[
  {
    "path": ".gitee/ISSUE_TEMPLATE.zh-CN.md",
    "content": "碰到问题，请在 <https://gitee.com/zhijiantianya/yudao-cloud/issues> 搜索是否存在相似的 issue。\n\n不按照模板提交的 issue，会被系统自动删除。\n\n### 基本信息\n\n- ruoyi-vue-pro 版本：\n- 操作系统：\n- 数据库：\n\n### 你猜测可能的原因\n\n（必填）我花费了 2-4 小时自查，发现可能的原因是：xxxxxx\n\n### 复现步骤\n\n第一步，\n\n第二步，\n\n第三步，\n\n### 报错信息\n\n带上必要的截图\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: 问题反馈\nabout: 请详细描述，以便更高快的获得到解决\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n碰到问题，请在 <https://github.com/YunaiV/yudao-cloud/issues> 搜索是否存在相似的 issue。\n\n不按照模板提交的 issue，会被系统自动删除。\n\n### 基本信息\n\n- ruoyi-vue-pro 版本：\n- 操作系统：\n- 数据库：\n\n### 你猜测可能的原因\n\n（必填）我花费了 2-4 小时自查，发现可能的原因是：xxxxxx\n\n### 复现步骤\n\n第一步，\n\n第二步，\n\n第三步，\n\n### 报错信息\n\n带上必要的截图\n"
  },
  {
    "path": ".github/workflows/maven.yml",
    "content": "# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time\n# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven\n\nname: Java CI with Maven\n\non:\n  push:\n    branches: [ master ]\n  # pull_request:\n  #   branches: [ master ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        java: [ '8', '11', '17' ]\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up JDK ${{ matrix.Java }}\n      uses: actions/setup-java@v2\n      with:\n        java-version: ${{ matrix.java }}\n        distribution: 'temurin'\n        cache: maven\n    - name: Build with Maven\n      run: mvn -B package --file pom.xml -Dmaven.test.skip=true\n"
  },
  {
    "path": ".gitignore",
    "content": "\n# 查看更多 .gitignore 配置 -> https://help.github.com/articles/ignoring-files/\n\ntarget/\n!.mvn/wrapper/maven-wrapper.jar\n\n.flattened-pom.xml\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n.sts4-cache\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n*.class\ntarget/*\n\n### NetBeans ###\n/nbproject/private/\n/nbbuild/\n/dist/\n/nbdist/\n/.nb-gradle/\n/build/\n\n\n\n### admin-web ###\n\n# dependencies\n**/node_modules\n\n# roadhog-api-doc ignore\n/src/utils/request-temp.js\n_roadhog-api-doc\n\n# production\n/dist\n/.vscode\n\n# misc\n.DS_Store\nnpm-debug.log*\nyarn-error.log\n\n/coverage\n.idea\nyarn.lock\npackage-lock.json\n*bak\n.vscode\n\n# visual studio code\n.history\n*.log\n\nfunctions/mock\n.temp/**\n\n# umi\n.umi\n.umi-production\n\n# screenshot\nscreenshot\n.firebase\nsessionStore\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2021 yudao-cloud\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n <img src=\"https://img.shields.io/badge/Spring%20Cloud-2021-blue.svg\" alt=\"Coverage Status\">\n <img src=\"https://img.shields.io/badge/Spring%20Boot-2.7.18-blue.svg\" alt=\"Downloads\">\n <img src=\"https://img.shields.io/badge/Vue-3.2-blue.svg\" alt=\"Downloads\">\n <img src=\"https://img.shields.io/github/license/YunaiV/yudao-cloud\" alt=\"Downloads\" />\n</p>\n\n**严肃声明：现在、未来都不会有商业版本，所有代码全部开源!！**\n\n**「我喜欢写代码，乐此不疲」**  \n**「我喜欢做开源，以此为乐」**\n\n我 🐶 在上海艰苦奋斗，早中晚在 top3 大厂认真搬砖，夜里为开源做贡献。\n\n如果这个项目让你有所收获，记得 Star 关注哦，这对我是非常不错的鼓励与支持。\n\n可参考 [《迁移文档》](https://cloud.iocoder.cn/migrate-module/) ，只需要 5-10 分钟，即可将【完整版】按需迁移到【精简版】\n\n## 🐶 新手必读\n\n* 演示地址【Vue3 + element-plus】：<http://dashboard-vue3.yudao.iocoder.cn>\n* 演示地址【Vue3 + vben(ant-design-vue)】：<http://dashboard-vben.yudao.iocoder.cn>\n* 演示地址【Vue2 + element-ui】：<http://dashboard.yudao.iocoder.cn>\n* 启动文档：<https://cloud.iocoder.cn/quick-start/>\n* 视频教程：<https://cloud.iocoder.cn/video/>\n\n## 🐰 版本说明\n\n| 版本                                                                    | JDK 8 + Spring Boot 2.7                                                  | JDK 17/21 + Spring Boot 3.2                                                          |\n|-----------------------------------------------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------|\n| 【完整版】[yudao-cloud](https://gitee.com/zhijiantianya/yudao-cloud)       | [`master`](https://gitee.com/zhijiantianya/yudao-cloud/tree/master/) 分支  | [`master-jdk17`](https://gitee.com/zhijiantianya/yudao-cloud/tree/master-jdk17/) 分支  |\n| 【精简版】[yudao-cloud-mini](https://gitee.com/yudaocode/yudao-cloud-mini) | [`master`](https://gitee.com/yudaocode/yudao-cloud-mini/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/yudaocode/yudao-cloud-mini/tree/master-jdk17/) 分支 |\n\n* 【完整版】：包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能\n* 【精简版】：只包括系统功能、基础设施功能，不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能\n\n可参考 [《迁移文档》](https://cloud.iocoder.cn/migrate-module/) ，只需要 5-10 分钟，即可将【完整版】按需迁移到【精简版】\n\n## 🐯 平台简介\n\n**芋道**，以开发者为中心，打造中国第一流的快速开发平台，全部开源，个人与企业可 100% 免费使用。\n\n> 有任何问题，或者想要的功能，可以在 _Issues_ 中提给艿艿。\n>\n> 😜 给项目点点 Star 吧，这对我们真的很重要！\n\n![架构图](/.image/common/yudao-cloud-architecture.png)\n\n* Java 后端：`master` 分支为 JDK 8 + Spring Boot 2.7，`master-jdk17` 分支为 JDK 17/21 + Spring Boot 3.2\n* 管理后台的电脑端：Vue3 提供 [element-plus](https://gitee.com/yudaocode/yudao-ui-admin-vue3)、[vben(ant-design-vue)](https://gitee.com/yudaocode/yudao-ui-admin-vben) 两个版本，Vue2 提供 [element-ui](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-admin) 版本\n* 管理后台的移动端：采用 [uni-app](https://github.com/dcloudio/uni-app) 方案，一份代码多终端适配，同时支持 APP、小程序、H5！\n* 后端采用 Spring Cloud Alibaba 微服务架构，注册中心 + 配置中心 Nacos，定时任务 XXL-Job，服务保障 Sentinel，服务网关 Gateway，分布式事务 Seata\n* 数据库可使用 MySQL、Oracle、PostgreSQL、SQL Server、MariaDB、国产达梦 DM、TiDB 等，基于 MyBatis Plus、Redis + Redisson 操作\n* 消息队列可使用 Event、Redis、RabbitMQ、Kafka、RocketMQ 等\n* 权限认证使用 Spring Security & Token & Redis，支持多终端、多种用户的认证系统，支持 SSO 单点登录\n* 支持加载动态权限菜单，按钮级别权限控制，Redis 缓存提升性能\n* 支持 SaaS 多租户，可自定义每个租户的权限，提供透明化的多租户底层封装\n* 工作流使用 Flowable，支持动态表单、在线设计流程、会签 / 或签、多种任务分配方式\n* 高效率开发，使用代码生成器可以一键生成 Java、Vue 前后端代码、SQL 脚本、接口文档，支持单表、树表、主子表\n* 实时通信，采用 Spring WebSocket 实现，内置 Token 身份校验，支持 WebSocket 集群\n* 集成微信小程序、微信公众号、企业微信、钉钉等三方登陆，集成支付宝、微信等支付与退款\n* 集成阿里云、腾讯云等短信渠道，集成 MinIO、阿里云、腾讯云、七牛云等云存储服务\n* 集成报表设计器、大屏设计器，通过拖拽即可生成酷炫的报表与大屏\n\n##  🐳 项目关系\n\n![架构演进](/.image/common/yudao-roadmap.png)\n\n三个项目的功能对比，可见社区共同整理的 [国产开源项目对比](https://www.yuque.com/xiatian-bsgny/lm0ec1/wqf8mn) 表格。\n\n### 后端项目\n\n| 项目                                                              | Star                                                                                                                                                                                                                                                                                             | 简介                          |\n|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|\n| [ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro)  | [![Gitee star](https://gitee.com/zhijiantianya/ruoyi-vue-pro/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/ruoyi-vue-pro) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/ruoyi-vue-pro.svg?style=social&label=Stars)](https://github.com/YunaiV/ruoyi-vue-pro)       | 基于 Spring Boot 多模块架构        |\n| [yudao-cloud](https://gitee.com/zhijiantianya/yudao-cloud)      | [![Gitee star](https://gitee.com/zhijiantianya/yudao-cloud/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/yudao-cloud) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/yudao-cloud.svg?style=social&label=Stars)](https://github.com/YunaiV/yudao-cloud)               | 基于 Spring Cloud 微服务架构       |\n| [Spring-Boot-Labs](https://gitee.com/yudaocode/SpringBoot-Labs) | [![Gitee star](https://gitee.com/yudaocode/SpringBoot-Labs/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/yudao-cloud) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/SpringBoot-Labs.svg?style=social&label=Stars)](https://github.com/yudaocode/SpringBoot-Labs) | 系统学习 Spring Boot & Cloud 专栏 |\n\n### 前端项目\n\n| 项目                                                                         | Star                                                                                                                                                                                                                                                                                                                     | 简介                                     |\n|----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|\n| [yudao-ui-admin-vue3](https://gitee.com/yudaocode/yudao-ui-admin-vue3)     | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-admin-vue3/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-admin-vue3) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-admin-vue3.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-admin-vue3)         | 基于 Vue3 + element-plus 实现的管理后台         |\n| [yudao-ui-admin-vben](https://gitee.com/yudaocode/yudao-ui-admin-vben)     | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-admin-vben/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-admin-vben) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-admin-vben.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-admin-vben)         | 基于 Vue3 + vben(ant-design-vue) 实现的管理后台 |\n| [yudao-mall-uniapp](https://gitee.com/yudaocode/yudao-mall-uniapp)         | [![Gitee star](https://gitee.com/yudaocode/yudao-mall-uniapp/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-mall-uniapp) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-mall-uniapp.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-mall-uniapp)                 | 基于 uni-app 实现的商城小程序                    |\n| [yudao-ui-admin-vue2](https://gitee.com/yudaocode/yudao-ui-admin-vue2)     | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-admin-vue2/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-admin-vue2) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-admin-vue2.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-admin-vue2)         | 基于 Vue2 + element-ui 实现的管理后台           |\n| [yudao-ui-admin-uniapp](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-admin-uniapp/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-admin-uniapp.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-admin-uniapp) | 基于 Vue2 + element-ui 实现的管理后台           |\n| [yudao-ui-go-view](https://gitee.com/yudaocode/yudao-ui-go-view)           | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-go-view/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-go-view) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-go-view.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-go-view)                     | 基于 Vue3 + naive-ui 实现的大屏报表             |\n\n## 😎 开源协议\n\n**为什么推荐使用本项目？**\n\n① 本项目采用比 Apache 2.0 更宽松的 [MIT License](https://gitee.com/zhijiantianya/ruoyi-vue-pro/blob/master/LICENSE) 开源协议，个人与企业可 100% 免费使用，不用保留类作者、Copyright 信息。\n\n② 代码全部开源，不会像其他项目一样，只开源部分代码，让你无法了解整个项目的架构设计。[国产开源项目对比](https://www.yuque.com/xiatian-bsgny/lm0ec1/wqf8mn)\n\n![开源项目对比](/.image/common/project-vs.png)\n\n③ 代码整洁、架构整洁，遵循《阿里巴巴 Java 开发手册》规范，代码注释详细，113770 行 Java 代码，42462 行代码注释。\n\n## 🤝 项目外包\n\n我们也是接外包滴，如果你有项目想要外包，可以微信联系【**Aix9975**】。\n\n团队包含专业的项目经理、架构师、前端工程师、后端工程师、测试工程师、运维工程师，可以提供全流程的外包服务。\n\n项目可以是商城、SCRM 系统、OA 系统、物流系统、ERP 系统、CMS 系统、HIS 系统、支付系统、IM 聊天、微信公众号、微信小程序等等。\n\n## 🐼 内置功能\n\n系统内置多种多种业务功能，可以用于快速你的业务系统：\n\n![功能分层](/.image/common/ruoyi-vue-pro-biz.png)\n\n* 通用模块（必选）：系统功能、基础设施\n* 通用模块（可选）：工作流程、支付系统、数据报表、会员中心\n* 业务系统（按需）：ERP 系统、CRM 系统、商城系统、微信公众号、AI 大模型\n\n> 友情提示：本项目基于 RuoYi-Vue 修改，**重构优化**后端的代码，**美化**前端的界面。\n>\n> * 额外新增的功能，我们使用 🚀 标记。\n> * 重新实现的功能，我们使用 ⭐️ 标记。\n\n🙂 所有功能，都通过 **单元测试** 保证高质量。\n\n### 系统功能\n\n|     | 功能    | 描述                              |\n|-----|-------|---------------------------------|\n|     | 用户管理  | 用户是系统操作者，该功能主要完成系统用户配置          |\n| ⭐️  | 在线用户  | 当前系统中活跃用户状态监控，支持手动踢下线           |\n|     | 角色管理  | 角色菜单权限分配、设置角色按机构进行数据范围权限划分      |\n|     | 菜单管理  | 配置系统菜单、操作权限、按钮权限标识等，本地缓存提供性能    |\n|     | 部门管理  | 配置系统组织机构（公司、部门、小组），树结构展现支持数据权限  |\n|     | 岗位管理  | 配置系统用户所属担任职务                    |\n| 🚀  | 租户管理  | 配置系统租户，支持 SaaS 场景下的多租户功能        |\n| 🚀  | 租户套餐  | 配置租户套餐，自定每个租户的菜单、操作、按钮的权限       |\n|     | 字典管理  | 对系统中经常使用的一些较为固定的数据进行维护          |\n| 🚀  | 短信管理  | 短信渠道、短息模板、短信日志，对接阿里云、腾讯云等主流短信平台 |\n| 🚀  | 邮件管理  | 邮箱账号、邮件模版、邮件发送日志，支持所有邮件平台       |\n| 🚀  | 站内信   | 系统内的消息通知，提供站内信模版、站内信消息          |\n| 🚀  | 操作日志  | 系统正常操作日志记录和查询，集成 Swagger 生成日志内容 |\n| ⭐️  | 登录日志  | 系统登录日志记录查询，包含登录异常               |\n| 🚀  | 错误码管理 | 系统所有错误码的管理，可在线修改错误提示，无需重启服务     |\n|     | 通知公告  | 系统通知公告信息发布维护                    |\n| 🚀  | 敏感词   | 配置系统敏感词，支持标签分组                  |\n| 🚀  | 应用管理  | 管理 SSO 单点登录的应用，支持多种 OAuth2 授权方式 |\n| 🚀  | 地区管理  | 展示省份、城市、区镇等城市信息，支持 IP 对应城市      |\n\n![功能图](/.image/common/system-feature.png)\n\n### 工作流程\n\n![功能图](/.image/common/bpm-feature.png)\n\n基于 Flowable 构建，可支持信创（国产）数据库，满足中国特色流程操作：\n\n| BPMN 设计器                     | 钉钉/飞书设计器                       |\n|------------------------------|--------------------------------|\n| ![](/.image/工作流设计器-bpmn.jpg) | ![](/.image/工作流设计器-simple.jpg) |\n\n> 历经头部企业生产验证，工作流引擎须标配仿钉钉/飞书 + BPMN 双设计器！！！\n>\n> 前者支持轻量配置简单流程，后者实现复杂场景深度编排\n\n| 功能列表       | 功能描述                                                                                | 是否完成 |\n|------------|-------------------------------------------------------------------------------------|------|\n| SIMPLE 设计器 | 仿钉钉/飞书设计器，支持拖拽搭建表单流程，10 分钟快速完成审批流程配置                                                | ✅    |\n| BPMN 设计器   | 基于 BPMN 标准开发，适配复杂业务场景，满足多层级审批及流程自动化需求                                               | ✅    |\n| 会签         | 同一个审批节点设置多个人（如 A、B、C 三人，三人会同时收到待办任务），需全部同意之后，审批才可到下一审批节点                            | ✅    |\n| 或签         | 同一个审批节点设置多个人，任意一个人处理后，就能进入下一个节点                                                     | ✅    |\n| 依次审批       | （顺序会签）同一个审批节点设置多个人（如 A、B、C 三人），三人按顺序依次收到待办，即 A 先审批，A 提交后 B 才能审批，需全部同意之后，审批才可到下一审批节点 | ✅    |\n| 抄送         | 将审批结果通知给抄送人，同一个审批默认排重，不重复抄送给同一人                                                     | ✅    |\n| 驳回         | （退回）将审批重置发送给某节点，重新审批。可驳回至发起人、上一节点、任意节点                                              | ✅    |\n| 转办         | A 转给其 B 审批，B 审批后，进入下一节点                                                             | ✅    |\n| 委派         | A 转给其 B 审批，B 审批后，转给 A，A 继续审批后进入下一节点                                                 | ✅    |\n| 加签         | 允许当前审批人根据需要，自行增加当前节点的审批人，支持向前、向后加签                                                  | ✅    |\n| 减签         | （取消加签）在当前审批人操作之前，减少审批人                                                              | ✅    |\n| 撤销         | （取消流程）流程发起人，可以对流程进行撤销处理                                                             | ✅    |\n| 终止         | 系统管理员，在任意节点终止流程实例                                                                   | ✅    |\n| 表单权限       | 支持拖拉拽配置表单，每个审批节点可配置只读、编辑、隐藏权限                                                       | ✅    |\n| 超时审批       | 配置超时审批时间，超时后自动触发审批通过、不通过、驳回等操作                                                      | ✅    |\n| 自动提醒       | 配置提醒时间，到达时间后自动触发短信、邮箱、站内信等通知提醒，支持自定义重复提醒频次                                          | ✅    |\n| 父子流程       | 主流程设置子流程节点，子流程节点会自动触发子流程。子流程结束后，主流程才会执行（继续往下下执行），支持同步子流程、异步子流程                      | ✅    |\n| 条件分支       | （排它分支）用于在流程中实现决策，即根据条件选择一个分支执行                                                      | ✅    |\n| 并行分支       | 允许将流程分成多条分支，不进行条件判断，所有分支都会执行                                                        | ✅    |\n| 包容分支       | （条件分支 + 并行分支的结合体）允许基于条件选择多条分支执行，但如果没有任何一个分支满足条件，则可以选择默认分支                           | ✅    |\n| 路由分支       | 根据条件选择一个分支执行（重定向到指定配置节点），也可以选择默认分支执行（继续往下执行）                                        | ✅    |\n| 触发节点       | 执行到该节点，触发 HTTP 请求、HTTP 回调、更新数据、删除数据等                                                | ✅    |\n| 延迟节点       | 执行到该节点，审批等待一段时间再执行，支持固定时长、固定日期等                                                     | ✅    |\n| 拓展设置       | 流程前置/后置通知，节点（任务）前置、后置通知，流程报表，自动审批去重，自定流程编号、标题、摘要，流程报表等                              | ✅    |\n\n### 支付系统\n\n|     | 功能   | 描述                        |\n|-----|------|---------------------------|\n| 🚀  | 应用信息 | 配置商户的应用信息，对接支付宝、微信等多个支付渠道 |\n| 🚀  | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单     |\n| 🚀  | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单     |\n| 🚀  | 回调通知 | 查看支付回调业务的【支付】【退款】的通知结果    |\n| 🚀  | 接入示例 | 提供接入支付系统的【支付】【退款】的功能实战    |\n\n### 基础设施\n\n|     | 功能        | 描述                                           |\n|-----|-----------|----------------------------------------------|\n| 🚀  | 代码生成      | 前后端代码的生成（Java、Vue、SQL、单元测试），支持 CRUD 下载       |\n| 🚀  | 系统接口      | 基于 Swagger 自动生成相关的 RESTful API 接口文档          |\n| 🚀  | 数据库文档     | 基于 Screw 自动生成数据库文档，支持导出 Word、HTML、MD 格式      |\n|     | 表单构建      | 拖动表单元素生成相应的 HTML 代码，支持导出 JSON、Vue 文件         |\n| 🚀  | 配置管理      | 对系统动态配置常用参数，支持 SpringBoot 加载                 |\n| ⭐️  | 定时任务      | 在线（添加、修改、删除)任务调度包含执行结果日志                     |\n| 🚀  | 文件服务      | 支持将文件存储到 S3（MinIO、阿里云、腾讯云、七牛云）、本地、FTP、数据库等   | \n| 🚀  | WebSocket | 提供 WebSocket 接入示例，支持一对一、一对多发送方式              | \n| 🚀  | API 日志    | 包括 RESTful API 访问日志、异常日志两部分，方便排查 API 相关的问题   |\n|     | MySQL 监控  | 监视当前系统数据库连接池状态，可进行分析SQL找出系统性能瓶颈              |\n|     | Redis 监控  | 监控 Redis 数据库的使用情况，使用的 Redis Key 管理           |\n| 🚀  | 消息队列      | 基于 Redis 实现消息队列，Stream 提供集群消费，Pub/Sub 提供广播消费 |\n| 🚀  | Java 监控   | 基于 Spring Boot Admin 实现 Java 应用的监控           |\n| 🚀  | 链路追踪      | 接入 SkyWalking 组件，实现链路追踪                      |\n| 🚀  | 日志中心      | 接入 SkyWalking 组件，实现日志中心                      |\n| 🚀  | 服务保障      | 基于 Redis 实现分布式锁、幂等、限流功能，满足高并发场景              |\n| 🚀  | 日志服务      | 轻量级日志中心，查看远程服务器的日志                           |\n| 🚀  | 单元测试      | 基于 JUnit + Mockito 实现单元测试，保证功能的正确性、代码的质量等    |\n\n![功能图](/.image/common/infra-feature.png)\n\n### 数据报表\n\n|     | 功能    | 描述                 |\n|-----|-------|--------------------|\n| 🚀  | 报表设计器 | 支持数据报表、图形报表、打印设计等  |\n| 🚀  | 大屏设计器 | 拖拽生成数据大屏，内置几十种图表组件 |\n\n### 微信公众号\n\n|    | 功能     | 描述                            |\n|----|--------|-------------------------------|\n| 🚀 | 账号管理   | 配置接入的微信公众号，可支持多个公众号           |\n| 🚀 | 数据统计   | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据  |\n| 🚀 | 粉丝管理   | 查看已关注、取关的粉丝列表，可对粉丝进行同步、打标签等操作 |\n| 🚀 | 消息管理   | 查看粉丝发送的消息列表，可主动回复粉丝消息         |\n| 🚀 | 模版消息   | 配置和发送模版消息，用于向粉丝推送通知类消息        |\n| 🚀 | 自动回复   | 自动回复粉丝发送的消息，支持关注回复、消息回复、关键字回复 |\n| 🚀 | 标签管理   | 对公众号的标签进行创建、查询、修改、删除等操作       |\n| 🚀 | 菜单管理   | 自定义公众号的菜单，也可以从公众号同步菜单         |\n| 🚀 | 素材管理   | 管理公众号的图片、语音、视频等素材，支持在线播放语音、视频 |\n| 🚀 | 图文草稿箱  | 新增常用的图文素材到草稿箱，可发布到公众号         |\n| 🚀 | 图文发表记录 | 查看已发布成功的图文素材，支持删除操作           |\n\n### 商城系统\n\n演示地址：<https://cloud.iocoder.cn/mall-preview/>\n\n![功能图](/.image/common/mall-feature.png)\n\n![功能图](/.image/common/mall-preview.png)\n\n### 会员中心\n\n|     | 功能   | 描述                               |\n|-----|------|----------------------------------|\n| 🚀  | 会员管理 | 会员是 C 端的消费者，该功能用于会员的搜索与管理        |\n| 🚀  | 会员标签 | 对会员的标签进行创建、查询、修改、删除等操作           |\n| 🚀  | 会员等级 | 对会员的等级、成长值进行管理，可用于订单折扣等会员权益      |\n| 🚀  | 会员分组 | 对会员进行分组，用于用户画像、内容推送等运营手段         |\n| 🚀  | 积分签到 | 回馈给签到、消费等行为的积分，会员可订单抵现、积分兑换等途径消耗 |\n\n### ERP 系统\n\n演示地址：<https://cloud.iocoder.cn/erp-preview/>\n\n![功能图](/.image/common/erp-feature.png)\n\n### CRM 系统\n\n演示地址：<https://cloud.iocoder.cn/crm-preview/>\n\n![功能图](/.image/common/crm-feature.png)\n\n### AI 大模型\n\n演示地址：<https://cloud.iocoder.cn/ai-preview/>\n\n![功能图](/.image/common/ai-feature.png)\n\n![功能图](/.image/common/ai-preview.gif)\n\n## 🐨 技术栈\n\n### 微服务\n\n| 项目                    | 说明                 |\n|-----------------------|--------------------|\n| `yudao-dependencies`  | Maven 依赖版本管理       |\n| `yudao-framework`     | Java 框架拓展          |\n| `yudao-server`        | 管理后台 + 用户 APP 的服务端 |\n| `yudao-module-system` | 系统功能的 Module 模块    |\n| `yudao-module-member` | 会员中心的 Module 模块    |\n| `yudao-module-infra`  | 基础设施的 Module 模块    |\n| `yudao-module-bpm`    | 工作流程的 Module 模块    |\n| `yudao-module-pay`    | 支付系统的 Module 模块    |\n| `yudao-module-mall`   | 商城系统的 Module 模块    |\n| `yudao-module-erp`    | ERP 系统的 Module 模块  |\n| `yudao-module-crm`    | CRM 系统的 Module 模块  |\n| `yudao-module-ai`     | AI 大模型的 Module 模块  |\n| `yudao-module-mp`     | 微信公众号的 Module 模块   |\n| `yudao-module-report` | 大屏报表 Module 模块     |\n\n### 框架\n\n| 框架                                                                                          | 说明               | 版本         | 学习指南                                                                |\n|---------------------------------------------------------------------------------------------|------------------|------------|---------------------------------------------------------------------|\n| [Spring Cloud Alibaba](https://github.com/alibaba/spring-cloud-alibaba)                     | 微服务框架            | 2021.0.4.0 | [文档](https://github.com/YunaiV/SpringBoot-Labs)                     |\n| [Nacos](https://github.com/alibaba/nacos)                                                   | 配置中心 & 注册中心      | 2.3.2      | [文档](https://www.iocoder.cn/categories/Nacos/?yudao)                |\n| [RocketMQ](https://github.com/apache/rocketmq)                                              | 消息队列             | 5.2.0      | [文档](https://www.iocoder.cn/categories/RocketMQ/?yudao)             |\n| [Sentinel](https://github.com/alibaba/sentinel)                                             | 服务保障             | 1.8.6      | [文档](https://www.iocoder.cn/categories/Sentinel/?yudao)             |\n| [XXL Job](https://github.com/xuxueli/xxl-job)                                               | 定时任务             | 2.3.1      | [文档](https://www.iocoder.cn/XXL-JOB/good-collection/?yudao)         |\n| [Spring Cloud Gateway](https://github.com/spring-cloud/spring-cloud-gateway)                | 服务网关             | 3.4.1      | [文档](https://www.iocoder.cn/categories/Spring-Cloud-Gateway/?yudao) |\n| [Seata](https://github.com/seata/seata)                                                     | 分布式事务            | 1.6.1      | [文档](https://www.iocoder.cn/categories/Seata/?yudao)                |\n| [MySQL](https://www.mysql.com/cn/)                                                          | 数据库服务器           | 5.7 / 8.0+ |                                                                     |\n| [Druid](https://github.com/alibaba/druid)                                                   | JDBC 连接池、监控组件    | 1.2.23     | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao)      |\n| [MyBatis Plus](https://mp.baomidou.com/)                                                    | MyBatis 增强工具包    | 3.5.7      | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao)              |\n| [Dynamic Datasource](https://dynamic-datasource.com/)                                       | 动态数据源            | 4.3.1      | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao)      |\n| [Redis](https://redis.io/)                                                                  | key-value 数据库    | 5.0 / 6.0  |                                                                     |\n| [Redisson](https://github.com/redisson/redisson)                                            | Redis 客户端        | 3.32.0     | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao)                |\n| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架           | 5.3.24     | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao)                    |\n| [Spring Security](https://github.com/spring-projects/spring-security)                       | Spring 安全框架      | 5.7.5      | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao)      |\n| [Hibernate Validator](https://github.com/hibernate/hibernate-validator)                     | 参数校验组件           | 6.2.5      | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao)           |\n| [Flowable](https://github.com/flowable/flowable-engine)                                     | 工作流引擎            | 6.8.0      | [文档](https://doc.iocoder.cn/bpm/)                                   |\n| [Knife4j](https://gitee.com/xiaoym/knife4j)                                                 | Swagger 增强 UI 实现 | 4.5.0      | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao)              |\n| [SkyWalking](https://skywalking.apache.org/)                                                | 分布式应用追踪系统        | 8.12.0     | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao)           |\n| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin)                       | Spring Boot 监控平台 | 2.7.10     | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao)                |\n| [Jackson](https://github.com/FasterXML/jackson)                                             | JSON 工具库         | 2.13.3     |                                                                     |\n| [MapStruct](https://mapstruct.org/)                                                         | Java Bean 转换     | 1.6.3      | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao)            |\n| [Lombok](https://projectlombok.org/)                                                        | 消除冗长的 Java 代码    | 1.18.34    | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao)               |\n| [JUnit](https://junit.org/junit5/)                                                          | Java 单元测试框架      | 5.8.2      | -                                                                   |\n| [Mockito](https://github.com/mockito/mockito)                                               | Java Mock 框架     | 4.8.0      | -                                                                   |\n\n## 🐷 演示图\n\n### 系统功能\n\n| 模块       | biu                         | biu                       | biu                      |\n|----------|-----------------------------|---------------------------|--------------------------|\n| 登录 & 首页  | ![登录](/.image/登录.jpg)       | ![首页](/.image/首页.jpg)     | ![个人中心](/.image/个人中心.jpg) |\n| 用户 & 应用  | ![用户管理](/.image/用户管理.jpg)   | ![令牌管理](/.image/令牌管理.jpg) | ![应用管理](/.image/应用管理.jpg) |\n| 租户 & 套餐  | ![租户管理](/.image/租户管理.jpg)   | ![租户套餐](/.image/租户套餐.png) | -                        |\n| 部门 & 岗位  | ![部门管理](/.image/部门管理.jpg)   | ![岗位管理](/.image/岗位管理.jpg) | -                        |\n| 菜单 & 角色  | ![菜单管理](/.image/菜单管理.jpg)   | ![角色管理](/.image/角色管理.jpg) | -                        |\n| 审计日志     | ![操作日志](/.image/操作日志.jpg)   | ![登录日志](/.image/登录日志.jpg) | -                        |\n| 短信       | ![短信渠道](/.image/短信渠道.jpg)   | ![短信模板](/.image/短信模板.jpg) | ![短信日志](/.image/短信日志.jpg) |\n| 字典 & 敏感词 | ![字典类型](/.image/字典类型.jpg)   | ![字典数据](/.image/字典数据.jpg) | ![敏感词](/.image/敏感词.jpg)  |\n| 错误码 & 通知 | ![错误码管理](/.image/错误码管理.jpg) | ![通知公告](/.image/通知公告.jpg) | -                        |\n\n### 工作流程\n\n| 模块      | biu                             | biu                             | biu                             |\n|---------|---------------------------------|---------------------------------|---------------------------------|\n| 流程模型    | ![流程模型-列表](/.image/流程模型-列表.jpg) | ![流程模型-设计](/.image/流程模型-设计.jpg) | ![流程模型-定义](/.image/流程模型-定义.jpg) |\n| 表单 & 分组 | ![流程表单](/.image/流程表单.jpg)       | ![用户分组](/.image/用户分组.jpg)       | -                               |\n| 我的流程    | ![我的流程-列表](/.image/我的流程-列表.jpg) | ![我的流程-发起](/.image/我的流程-发起.jpg) | ![我的流程-详情](/.image/我的流程-详情.jpg) |\n| 待办 & 已办 | ![任务列表-审批](/.image/任务列表-审批.jpg) | ![任务列表-待办](/.image/任务列表-待办.jpg) | ![任务列表-已办](/.image/任务列表-已办.jpg) |\n| OA 请假   | ![OA请假-列表](/.image/OA请假-列表.jpg) | ![OA请假-发起](/.image/OA请假-发起.jpg) | ![OA请假-详情](/.image/OA请假-详情.jpg) |\n\n### 基础设施\n\n| 模块            | biu                           | biu                         | biu                       |\n|---------------|-------------------------------|-----------------------------|---------------------------|\n| 代码生成          | ![代码生成](/.image/代码生成.jpg)     | ![生成效果](/.image/生成效果.jpg)   | -                         |\n| 文档            | ![系统接口](/.image/系统接口.jpg)     | ![数据库文档](/.image/数据库文档.jpg) | -                         |\n| 文件 & 配置       | ![文件配置](/.image/文件配置.jpg)     | ![文件管理](/.image/文件管理2.jpg)  | ![配置管理](/.image/配置管理.jpg) |\n| 定时任务          | ![定时任务](/.image/定时任务.jpg)     | ![任务日志](/.image/任务日志.jpg)   | -                         |\n| API 日志        | ![访问日志](/.image/访问日志.jpg)     | ![错误日志](/.image/错误日志.jpg)   | -                         |\n| MySQL & Redis | ![MySQL](/.image/MySQL.jpg)   | ![Redis](/.image/Redis.jpg) | -                         |\n| 监控平台          | ![Java监控](/.image/Java监控.jpg) | ![链路追踪](/.image/链路追踪.jpg)   | ![日志中心](/.image/日志中心.jpg) |\n\n### 支付系统\n\n| 模块      | biu                       | biu                             | biu                             |\n|---------|---------------------------|---------------------------------|---------------------------------|\n| 商家 & 应用 | ![商户信息](/.image/商户信息.jpg) | ![应用信息-列表](/.image/应用信息-列表.jpg) | ![应用信息-编辑](/.image/应用信息-编辑.jpg) |\n| 支付 & 退款 | ![支付订单](/.image/支付订单.jpg) | ![退款订单](/.image/退款订单.jpg)       | ---                             |\n### 数据报表\n\n| 模块    | biu                             | biu                             | biu                                   |\n|-------|---------------------------------|---------------------------------|---------------------------------------|\n| 报表设计器 | ![数据报表](/.image/报表设计器-数据报表.jpg) | ![图形报表](/.image/报表设计器-图形报表.jpg) | ![报表设计器-打印设计](/.image/报表设计器-打印设计.jpg) |\n| 大屏设计器 | ![大屏列表](/.image/大屏设计器-列表.jpg)   | ![大屏预览](/.image/大屏设计器-预览.jpg)   | ![大屏编辑](/.image/大屏设计器-编辑.jpg)         |\n\n### 移动端（管理后台）\n\n| biu                              | biu                              | biu                              |\n|----------------------------------|----------------------------------|----------------------------------|\n| ![](/.image/admin-uniapp/01.png) | ![](/.image/admin-uniapp/02.png) | ![](/.image/admin-uniapp/03.png) |\n| ![](/.image/admin-uniapp/04.png) | ![](/.image/admin-uniapp/05.png) | ![](/.image/admin-uniapp/06.png) |\n| ![](/.image/admin-uniapp/07.png) | ![](/.image/admin-uniapp/08.png) | ![](/.image/admin-uniapp/09.png) |\n\n目前已经实现登录、我的、工作台、编辑资料、头像修改、密码修改、常见问题、关于我们等基础功能。\n"
  },
  {
    "path": "lombok.config",
    "content": "config.stopBubbling = true\nlombok.tostring.callsuper=CALL\nlombok.equalsandhashcode.callsuper=CALL\nlombok.accessors.chain=true\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>cn.iocoder.cloud</groupId>\n    <artifactId>yudao</artifactId>\n    <version>${revision}</version>\n    <packaging>pom</packaging>\n    <modules>\n        <module>yudao-dependencies</module>\n        <module>yudao-gateway</module>\n        <module>yudao-framework</module>\n        <!-- Server 主项目 -->\n        <module>yudao-server</module>\n        <!--  各种 module 拓展 -->\n        <module>yudao-module-system</module>\n        <module>yudao-module-infra</module>\n        <module>yudao-module-member</module>\n        <module>yudao-module-bpm</module>\n        <module>yudao-module-pay</module>\n        <module>yudao-module-report</module>\n        <module>yudao-module-mp</module>\n        <module>yudao-module-mall</module>\n        <module>yudao-module-erp</module>\n        <module>yudao-module-crm</module>\n        <!-- 友情提示：基于 Spring AI 实现 LLM 大模型的接入，需要使用 JDK17 版本，详细可见 https://doc.iocoder.cn/ai/build/ -->\n<!--        <module>yudao-module-ai</module>-->\n        <module>yudao-module-iot</module>\n    </modules>\n\n    <name>${project.artifactId}</name>\n    <description>芋道项目基础脚手架</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <properties>\n        <revision>2026.01-jdk8-SNAPSHOT</revision>\n        <!-- Maven 相关 -->\n        <java.version>1.8</java.version>\n        <maven.compiler.source>${java.version}</maven.compiler.source>\n        <maven.compiler.target>${java.version}</maven.compiler.target>\n        <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>\n        <maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>\n        <flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>\n        <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取（兼容老版本 IDEA 2024 及以前版本） -->\n        <lombok.version>1.18.42</lombok.version>\n        <spring.boot.version>2.7.18</spring.boot.version>\n        <mapstruct.version>1.6.3</mapstruct.version>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-dependencies</artifactId>\n                <version>${revision}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <!-- maven-surefire-plugin 插件，用于运行单元测试。 -->\n                <!-- 注意，需要使用 3.0.X+，因为要支持 Junit 5 版本 -->\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-surefire-plugin</artifactId>\n                    <version>${maven-surefire-plugin.version}</version>\n                </plugin>\n                <!-- maven-compiler-plugin 插件，解决 Lombok + MapStruct 组合 -->\n                <!-- https://stackoverflow.com/questions/33483697/re-run-spring-boot-configuration-annotation-processor-to-update-generated-metada -->\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>${maven-compiler-plugin.version}</version>\n                    <configuration>\n                        <annotationProcessorPaths>\n                            <path>\n                                <groupId>org.springframework.boot</groupId>\n                                <artifactId>spring-boot-configuration-processor</artifactId>\n                                <version>${spring.boot.version}</version>\n                            </path>\n                            <path>\n                                <groupId>org.projectlombok</groupId>\n                                <artifactId>lombok</artifactId>\n                                <version>${lombok.version}</version>\n                            </path>\n                            <path>\n                                <!-- 确保 Lombok 生成的 getter/setter 方法能被 MapStruct 正确识别，\n                                     避免出现 No property named “xxx\" exists 的编译错误 -->\n                                <groupId>org.projectlombok</groupId>\n                                <artifactId>lombok-mapstruct-binding</artifactId>\n                                <version>0.2.0</version>\n                            </path>\n                            <path>\n                                <groupId>org.mapstruct</groupId>\n                                <artifactId>mapstruct-processor</artifactId>\n                                <version>${mapstruct.version}</version>\n                            </path>\n                        </annotationProcessorPaths>\n                    </configuration>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n\n        <plugins>\n            <!-- 统一 revision 版本 -->\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>flatten-maven-plugin</artifactId>\n                <version>${flatten-maven-plugin.version}</version>\n                <configuration>\n                    <flattenMode>oss</flattenMode>\n                    <updatePomFile>true</updatePomFile>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>flatten</goal>\n                        </goals>\n                        <id>flatten</id>\n                        <phase>process-resources</phase>\n                    </execution>\n                    <execution>\n                        <goals>\n                            <goal>clean</goal>\n                        </goals>\n                        <id>flatten.clean</id>\n                        <phase>clean</phase>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n    <!-- 使用 huawei / aliyun 的 Maven 源，提升下载速度 -->\n    <repositories>\n        <repository>\n            <id>huaweicloud</id>\n            <name>huawei</name>\n            <url>https://mirrors.huaweicloud.com/repository/maven/</url>\n        </repository>\n        <repository>\n            <id>aliyunmaven</id>\n            <name>aliyun</name>\n            <url>https://maven.aliyun.com/repository/public</url>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "script/docker/docker-compose.yml",
    "content": "version: '3'\nservices:\n  yudao-gateway:\n    image: yudao-gateway\n    container_name: yudao-gateway\n    environment:\n      - TZ=Asia/Shanghai # 配置程序默认时区为上海（中国标准时间）\n      - JAVA_TOOL_OPTIONS=-javaagent:/data/skywalking/skywalking-agent/skywalking-agent.jar # 配置skywalking\n      - SW_AGENT_NAME=yudao-gateway\n      - SW_AGENT_TRACE_IGNORE_PATH=Redisson/PING,/actuator/**,/admin/**\n      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=[YOUR_SKYWALKING_ADDR] # 请替换 your.skywalking.addr 为你的 skywalking 地址\n      - SPRING_PROFILES_ACTIVE=test # 指定程序运行环境\n      - SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR=[YOUR_NACOS_ADDR] # 配置中心地址\n      - SPRING_CLOUD_NACOS_CONFIG_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n      - SPRING_CLOUD_NACOS_SERVER_ADDR=[YOUR_NACOS_ADDR]  # 注册中心地址\n      - SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n    volumes:\n      - /docker/yudao-cloud/logs:/root/logs/\n      - /data/skywalking/skywalking-agent:/data/skywalking/skywalking-agent\n    restart: always\n    network_mode: host # 以主机网络环境运行\n  yudao-system:\n    image: yudao-module-system-biz\n    container_name: yudao-system\n    environment:\n      - TZ=Asia/Shanghai # 配置程序默认时区为上海（中国标准时间）\n      - JAVA_TOOL_OPTIONS=-javaagent:/data/skywalking/skywalking-agent/skywalking-agent.jar # 配置skywalking\n      - SW_AGENT_NAME=yudao-gateway\n      - SW_AGENT_TRACE_IGNORE_PATH=Redisson/PING,/actuator/**,/admin/**\n      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=[YOUR_SKYWALKING_ADDR] # 请替换 your.skywalking.addr 为你的 skywalking 地址\n      - SPRING_PROFILES_ACTIVE=test # 指定程序运行环境\n      - SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR=[YOUR_NACOS_ADDR] # 配置中心地址\n      - SPRING_CLOUD_NACOS_CONFIG_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n      - SPRING_CLOUD_NACOS_SERVER_ADDR=[YOUR_NACOS_ADDR]  # 注册中心地址\n      - SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n    volumes:\n      - /docker/yudao-cloud/logs:/root/logs/\n      - /data/skywalking/skywalking-agent:/data/skywalking/skywalking-agent\n    healthcheck:\n      test: [ \"CMD\",\"curl\",\"-f\",\"http://localhost:48081\" ]\n      interval: 30s\n      timeout: 10s\n      retries: 5\n      start_period: 60s\n    restart: always\n    network_mode: host\n  yudao-infra:\n    image: yudao-module-infra-biz\n    container_name: yudao-infra\n    environment:\n      - TZ=Asia/Shanghai # 配置程序默认时区为上海（中国标准时间）\n      - JAVA_TOOL_OPTIONS=-javaagent:/data/skywalking/skywalking-agent/skywalking-agent.jar # 配置skywalking\n      - SW_AGENT_NAME=yudao-gateway\n      - SW_AGENT_TRACE_IGNORE_PATH=Redisson/PING,/actuator/**,/admin/**\n      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=[YOUR_SKYWALKING_ADDR] # 请替换 your.skywalking.addr 为你的 skywalking 地址\n      - SPRING_PROFILES_ACTIVE=test # 指定程序运行环境\n      - SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR=[YOUR_NACOS_ADDR] # 配置中心地址\n      - SPRING_CLOUD_NACOS_CONFIG_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n      - SPRING_CLOUD_NACOS_SERVER_ADDR=[YOUR_NACOS_ADDR]  # 注册中心地址\n      - SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n    volumes:\n      - /docker/yudao-cloud/logs:/root/logs/\n      - /data/skywalking/skywalking-agent:/data/skywalking/skywalking-agent\n    restart: always\n    network_mode: host\n    healthcheck:\n      test: [ \"CMD\",\"curl\",\"-f\",\"http://localhost:48082\" ]\n      interval: 30s\n      timeout: 10s\n      retries: 5\n      start_period: 60s\n    depends_on:\n      yudao-system:\n        condition: service_healthy\n  yudao-report:\n    image: yudao-module-report-biz\n    container_name: yudao-report\n    environment:\n      - TZ=Asia/Shanghai # 配置程序默认时区为上海（中国标准时间）\n      - JAVA_TOOL_OPTIONS=-javaagent:/data/skywalking/skywalking-agent/skywalking-agent.jar # 配置skywalking\n      - SW_AGENT_NAME=yudao-gateway\n      - SW_AGENT_TRACE_IGNORE_PATH=Redisson/PING,/actuator/**,/admin/**\n      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=[YOUR_SKYWALKING_ADDR] # 请替换 your.skywalking.addr 为你的 skywalking 地址\n      - SPRING_PROFILES_ACTIVE=test # 指定程序运行环境\n      - SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR=[YOUR_NACOS_ADDR] # 配置中心地址\n      - SPRING_CLOUD_NACOS_CONFIG_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n      - SPRING_CLOUD_NACOS_SERVER_ADDR=[YOUR_NACOS_ADDR]  # 注册中心地址\n      - SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n    volumes:\n      - /docker/yudao-cloud/logs:/root/logs/\n      - /data/skywalking/skywalking-agent:/data/skywalking/skywalking-agent\n    restart: always\n    network_mode: host\n    depends_on:\n      yudao-infra:\n        condition: service_healthy\n  yudao-bpm:\n    image: yudao-module-bpm-biz\n    container_name: yudao-bpm\n    environment:\n      - TZ=Asia/Shanghai # 配置程序默认时区为上海（中国标准时间）\n      - JAVA_TOOL_OPTIONS=-javaagent:/data/skywalking/skywalking-agent/skywalking-agent.jar # 配置skywalking\n      - SW_AGENT_NAME=yudao-gateway\n      - SW_AGENT_TRACE_IGNORE_PATH=Redisson/PING,/actuator/**,/admin/**\n      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=[YOUR_SKYWALKING_ADDR] # 请替换 your.skywalking.addr 为你的 skywalking 地址\n      - SPRING_PROFILES_ACTIVE=test # 指定程序运行环境\n      - SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR=[YOUR_NACOS_ADDR] # 配置中心地址\n      - SPRING_CLOUD_NACOS_CONFIG_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n      - SPRING_CLOUD_NACOS_SERVER_ADDR=[YOUR_NACOS_ADDR]  # 注册中心地址\n      - SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n    volumes:\n      - /docker/yudao-cloud/logs:/root/logs/\n      - /data/skywalking/skywalking-agent:/data/skywalking/skywalking-agent\n    restart: always\n    network_mode: host\n    depends_on:\n      yudao-infra:\n        condition: service_healthy\n  yudao-pay:\n    image: yudao-module-pay-biz\n    container_name: yudao-pay\n    environment:\n      - TZ=Asia/Shanghai # 配置程序默认时区为上海（中国标准时间）\n      - JAVA_TOOL_OPTIONS=-javaagent:/data/skywalking/skywalking-agent/skywalking-agent.jar # 配置skywalking\n      - SW_AGENT_NAME=yudao-gateway\n      - SW_AGENT_TRACE_IGNORE_PATH=Redisson/PING,/actuator/**,/admin/**\n      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=[YOUR_SKYWALKING_ADDR] # 请替换 your.skywalking.addr 为你的 skywalking 地址\n      - SPRING_PROFILES_ACTIVE=test # 指定程序运行环境\n      - SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR=[YOUR_NACOS_ADDR] # 配置中心地址\n      - SPRING_CLOUD_NACOS_CONFIG_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n      - SPRING_CLOUD_NACOS_SERVER_ADDR=[YOUR_NACOS_ADDR]  # 注册中心地址\n      - SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n    volumes:\n      - /docker/yudao-cloud/logs:/root/logs/\n      - /data/skywalking/skywalking-agent:/data/skywalking/skywalking-agent\n    restart: always\n    network_mode: host\n    depends_on:\n      yudao-infra:\n        condition: service_healthy\n  yudao-mp:\n    image: yudao-module-mp-biz\n    container_name: yudao-mp\n    environment:\n      - TZ=Asia/Shanghai # 配置程序默认时区为上海（中国标准时间）\n      - JAVA_TOOL_OPTIONS=-javaagent:/data/skywalking/skywalking-agent/skywalking-agent.jar # 配置skywalking\n      - SW_AGENT_NAME=yudao-gateway\n      - SW_AGENT_TRACE_IGNORE_PATH=Redisson/PING,/actuator/**,/admin/**\n      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=[YOUR_SKYWALKING_ADDR] # 请替换 your.skywalking.addr 为你的 skywalking 地址\n      - SPRING_PROFILES_ACTIVE=test # 指定程序运行环境\n      - SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR=[YOUR_NACOS_ADDR] # 配置中心地址\n      - SPRING_CLOUD_NACOS_CONFIG_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n      - SPRING_CLOUD_NACOS_SERVER_ADDR=[YOUR_NACOS_ADDR]  # 注册中心地址\n      - SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE=[YOUR_NAMESPACE] # 命名空间\n    volumes:\n      - /docker/yudao-cloud/logs:/root/logs/\n      - /data/skywalking/skywalking-agent:/data/skywalking/skywalking-agent\n    restart: always\n    network_mode: host\n    depends_on:\n      yudao-infra:\n        condition: service_healthy"
  },
  {
    "path": "script/idea/http-client.env.json",
    "content": "{\n  \"local\": {\n    \"baseUrl\": \"http://127.0.0.1:48080/admin-api\",\n    \"systemBaseUrl\": \"http://127.0.0.1:48081/admin-api\",\n    \"infaBaseUrl\": \"http://127.0.0.1:48082/admin-api\",\n\n    \"token\": \"test1\",\n    \"adminTenantId\": \"1\",\n    \"tag\": \"${HOSTNAME}\",\n\n    \"appApi\": \"http://127.0.0.1:48080/app-api\",\n    \"appToken\": \"test1\",\n    \"appTenentId\": \"1\"\n  },\n  \"gateway\": {\n    \"baseUrl\": \"http://127.0.0.1:48080/admin-api\",\n    \"systemBaseUrl\": \"http://127.0.0.1:48080/admin-api\",\n    \"infaBaseUrl\": \"http://127.0.0.1:48080/admin-api\",\n\n    \"token\": \"test1\",\n    \"adminTenantId\": \"1\",\n    \"tag\": \"${HOSTNAME}\",\n\n    \"appApi\": \"http://127.0.0.1:8888/app-api\",\n    \"appToken\": \"test1\",\n    \"appTenentId\": \"1\"\n  }\n}\n"
  },
  {
    "path": "sql/db2/README.md",
    "content": "暂未适配 IBM DB2 数据库，如果你有需要，可以微信联系 wangwenbin-server 一起建设。\n\n你需要把表结构与数据导入到 DM 数据库，我来测试与适配代码。\n"
  },
  {
    "path": "sql/dm/flowable-patch/src/main/java/liquibase/database/core/DmDatabase.java",
    "content": "package liquibase.database.core;\n\nimport java.lang.reflect.Method;\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport liquibase.CatalogAndSchema;\nimport liquibase.Scope;\nimport liquibase.database.AbstractJdbcDatabase;\nimport liquibase.database.DatabaseConnection;\nimport liquibase.database.OfflineConnection;\nimport liquibase.database.jvm.JdbcConnection;\nimport liquibase.exception.DatabaseException;\nimport liquibase.exception.UnexpectedLiquibaseException;\nimport liquibase.exception.ValidationErrors;\nimport liquibase.executor.ExecutorService;\nimport liquibase.statement.DatabaseFunction;\nimport liquibase.statement.SequenceCurrentValueFunction;\nimport liquibase.statement.SequenceNextValueFunction;\nimport liquibase.statement.core.RawCallStatement;\nimport liquibase.statement.core.RawSqlStatement;\nimport liquibase.structure.DatabaseObject;\nimport liquibase.structure.core.Catalog;\nimport liquibase.structure.core.Index;\nimport liquibase.structure.core.PrimaryKey;\nimport liquibase.structure.core.Schema;\nimport liquibase.util.JdbcUtils;\nimport liquibase.util.StringUtil;\n\npublic class DmDatabase extends AbstractJdbcDatabase {\n    private static final String PRODUCT_NAME = \"DM DBMS\";\n\n    @Override\n    protected String getDefaultDatabaseProductName() {\n        return PRODUCT_NAME;\n    }\n\n    /**\n     * Is this AbstractDatabase subclass the correct one to use for the given connection.\n     *\n     * @param conn\n     */\n    @Override\n    public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {\n        return PRODUCT_NAME.equalsIgnoreCase(conn.getDatabaseProductName());\n    }\n\n    /**\n     * If this database understands the given url, return the default driver class name.  Otherwise return null.\n     *\n     * @param url\n     */\n    @Override\n    public String getDefaultDriver(String url) {\n        if(url.startsWith(\"jdbc:dm\")) {\n            return \"dm.jdbc.driver.DmDriver\";\n        }\n\n        return null;\n    }\n\n    /**\n     * Returns an all-lower-case short name of the product.  Used for end-user selecting of database type\n     * such as the DBMS precondition.\n     */\n    @Override\n    public String getShortName() {\n        return \"dm\";\n    }\n\n    @Override\n    public Integer getDefaultPort() {\n        return 5236;\n    }\n\n    /**\n     * Returns whether this database support initially deferrable columns.\n     */\n    @Override\n    public boolean supportsInitiallyDeferrableColumns() {\n        return true;\n    }\n\n    @Override\n    public boolean supportsTablespaces() {\n        return true;\n    }\n\n    @Override\n    public int getPriority() {\n        return PRIORITY_DEFAULT;\n    }\n\n    private static final Pattern PROXY_USER = Pattern.compile(\".*(?:thin|oci)\\\\:(.+)/@.*\");\n\n    protected final int SHORT_IDENTIFIERS_LENGTH = 30;\n    protected final int LONG_IDENTIFIERS_LEGNTH = 128;\n    public static final int ORACLE_12C_MAJOR_VERSION = 12;\n\n    private Set<String> reservedWords = new HashSet<>();\n    private Set<String> userDefinedTypes;\n    private Map<String, String> savedSessionNlsSettings;\n\n    private Boolean canAccessDbaRecycleBin;\n    private Integer databaseMajorVersion;\n    private Integer databaseMinorVersion;\n\n    /**\n     * Default constructor for an object that represents the Oracle Database DBMS.\n     */\n    public DmDatabase() {\n        super.unquotedObjectsAreUppercased = true;\n        //noinspection HardCodedStringLiteral\n        super.setCurrentDateTimeFunction(\"SYSTIMESTAMP\");\n        // Setting list of Oracle's native functions\n        //noinspection HardCodedStringLiteral\n        dateFunctions.add(new DatabaseFunction(\"SYSDATE\"));\n        //noinspection HardCodedStringLiteral\n        dateFunctions.add(new DatabaseFunction(\"SYSTIMESTAMP\"));\n        //noinspection HardCodedStringLiteral\n        dateFunctions.add(new DatabaseFunction(\"CURRENT_TIMESTAMP\"));\n        //noinspection HardCodedStringLiteral\n        super.sequenceNextValueFunction = \"%s.nextval\";\n        //noinspection HardCodedStringLiteral\n        super.sequenceCurrentValueFunction = \"%s.currval\";\n    }\n\n    private void tryProxySession(final String url, final Connection con) {\n        Matcher m = PROXY_USER.matcher(url);\n        if (m.matches()) {\n            Properties props = new Properties();\n            props.put(\"PROXY_USER_NAME\", m.group(1));\n            try {\n                Method method = con.getClass().getMethod(\"openProxySession\", int.class, Properties.class);\n                method.setAccessible(true);\n                method.invoke(con, 1, props);\n            } catch (Exception e) {\n                Scope.getCurrentScope().getLog(getClass()).info(\"Could not open proxy session on OracleDatabase: \" + e.getCause().getMessage());\n            }\n        }\n    }\n\n    @Override\n    public int getDatabaseMajorVersion() throws DatabaseException {\n        if (databaseMajorVersion == null) {\n            return super.getDatabaseMajorVersion();\n        } else {\n            return databaseMajorVersion;\n        }\n    }\n\n    @Override\n    public int getDatabaseMinorVersion() throws DatabaseException {\n        if (databaseMinorVersion == null) {\n            return super.getDatabaseMinorVersion();\n        } else {\n            return databaseMinorVersion;\n        }\n    }\n\n    @Override\n    public String getJdbcCatalogName(CatalogAndSchema schema) {\n        return null;\n    }\n\n    @Override\n    public String getJdbcSchemaName(CatalogAndSchema schema) {\n        return correctObjectName((schema.getCatalogName() == null) ? schema.getSchemaName() : schema.getCatalogName(), Schema.class);\n    }\n\n    @Override\n    protected String getAutoIncrementClause(final String generationType, final Boolean defaultOnNull) {\n        if (StringUtil.isEmpty(generationType)) {\n            return super.getAutoIncrementClause();\n        }\n\n        String autoIncrementClause = \"GENERATED %s AS IDENTITY\"; // %s -- [ ALWAYS | BY DEFAULT [ ON NULL ] ]\n        String generationStrategy = generationType;\n        if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals(\"BY DEFAULT\")) {\n            generationStrategy += \" ON NULL\";\n        }\n        return String.format(autoIncrementClause, generationStrategy);\n    }\n\n    @Override\n    public String generatePrimaryKeyName(String tableName) {\n        if (tableName.length() > 27) {\n            //noinspection HardCodedStringLiteral\n            return \"PK_\" + tableName.toUpperCase(Locale.US).substring(0, 27);\n        } else {\n            //noinspection HardCodedStringLiteral\n            return \"PK_\" + tableName.toUpperCase(Locale.US);\n        }\n    }\n\n    @Override\n    public boolean isReservedWord(String objectName) {\n        return reservedWords.contains(objectName.toUpperCase());\n    }\n\n    @Override\n    public boolean supportsSequences() {\n        return true;\n    }\n\n    /**\n     * Oracle supports catalogs in liquibase terms\n     *\n     * @return false\n     */\n    @Override\n    public boolean supportsSchemas() {\n        return false;\n    }\n\n    @Override\n    protected String getConnectionCatalogName() throws DatabaseException {\n        if (getConnection() instanceof OfflineConnection) {\n            return getConnection().getCatalog();\n        }\n        try {\n            //noinspection HardCodedStringLiteral\n            return Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor(\"jdbc\", this).queryForObject(new RawCallStatement(\"select sys_context( 'userenv', 'current_schema' ) from dual\"), String.class);\n        } catch (Exception e) {\n            //noinspection HardCodedStringLiteral\n            Scope.getCurrentScope().getLog(getClass()).info(\"Error getting default schema\", e);\n        }\n        return null;\n    }\n\n    @Override\n    public String getDefaultCatalogName() {//NOPMD\n        return (super.getDefaultCatalogName() == null) ? null : super.getDefaultCatalogName().toUpperCase(Locale.US);\n    }\n\n    /**\n     * <p>Returns an Oracle date literal with the same value as a string formatted using ISO 8601.</p>\n     *\n     * <p>Convert an ISO8601 date string to one of the following results:\n     * to_date('1995-05-23', 'YYYY-MM-DD')\n     * to_date('1995-05-23 09:23:59', 'YYYY-MM-DD HH24:MI:SS')</p>\n     * <p>\n     * Implementation restriction:<br>\n     * Currently, only the following subsets of ISO8601 are supported:<br>\n     * <ul>\n     * <li>YYYY-MM-DD</li>\n     * <li>YYYY-MM-DDThh:mm:ss</li>\n     * </ul>\n     */\n    @Override\n    public String getDateLiteral(String isoDate) {\n        String normalLiteral = super.getDateLiteral(isoDate);\n\n        if (isDateOnly(isoDate)) {\n            return \"TO_DATE(\" + normalLiteral + \", 'YYYY-MM-DD')\";\n        } else if (isTimeOnly(isoDate)) {\n            return \"TO_DATE(\" + normalLiteral + \", 'HH24:MI:SS')\";\n        } else if (isTimestamp(isoDate)) {\n            return \"TO_TIMESTAMP(\" + normalLiteral + \", 'YYYY-MM-DD HH24:MI:SS.FF')\";\n        } else if (isDateTime(isoDate)) {\n            int seppos = normalLiteral.lastIndexOf('.');\n            if (seppos != -1) {\n                normalLiteral = normalLiteral.substring(0, seppos) + \"'\";\n            }\n            return \"TO_DATE(\" + normalLiteral + \", 'YYYY-MM-DD HH24:MI:SS')\";\n        }\n        return \"UNSUPPORTED:\" + isoDate;\n    }\n\n    @Override\n    public boolean isSystemObject(DatabaseObject example) {\n        if (example == null) {\n            return false;\n        }\n\n        if (this.isLiquibaseObject(example)) {\n            return false;\n        }\n\n        if (example instanceof Schema) {\n            //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral\n            if (\"SYSTEM\".equals(example.getName()) || \"SYS\".equals(example.getName()) || \"CTXSYS\".equals(example.getName()) || \"XDB\".equals(example.getName())) {\n                return true;\n            }\n            //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral\n            if (\"SYSTEM\".equals(example.getSchema().getCatalogName()) || \"SYS\".equals(example.getSchema().getCatalogName()) || \"CTXSYS\".equals(example.getSchema().getCatalogName()) || \"XDB\".equals(example.getSchema().getCatalogName())) {\n                return true;\n            }\n        } else if (isSystemObject(example.getSchema())) {\n            return true;\n        }\n        if (example instanceof Catalog) {\n            //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral\n            if ((\"SYSTEM\".equals(example.getName()) || \"SYS\".equals(example.getName()) || \"CTXSYS\".equals(example.getName()) || \"XDB\".equals(example.getName()))) {\n                return true;\n            }\n        } else if (example.getName() != null) {\n            //noinspection HardCodedStringLiteral\n            if (example.getName().startsWith(\"BIN$\")) { //oracle deleted table\n                boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin();\n                if (!filteredInOriginalQuery) {\n                    filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName());\n                }\n\n                if (filteredInOriginalQuery) {\n                    return !((example instanceof PrimaryKey) || (example instanceof Index) || (example instanceof\n                            liquibase.statement.UniqueConstraint));\n                } else {\n                    return true;\n                }\n            } else //noinspection HardCodedStringLiteral\n                if (example.getName().startsWith(\"AQ$\")) { //oracle AQ tables\n                    return true;\n                } else //noinspection HardCodedStringLiteral\n                    if (example.getName().startsWith(\"DR$\")) { //oracle index tables\n                        return true;\n                    } else //noinspection HardCodedStringLiteral\n                        if (example.getName().startsWith(\"SYS_IOT_OVER\")) { //oracle system table\n                            return true;\n                        } else //noinspection HardCodedStringLiteral,HardCodedStringLiteral\n                            if ((example.getName().startsWith(\"MDRT_\") || example.getName().startsWith(\"MDRS_\")) && example.getName().endsWith(\"$\")) {\n                                // CORE-1768 - Oracle creates these for spatial indices and will remove them when the index is removed.\n                                return true;\n                            } else //noinspection HardCodedStringLiteral\n                                if (example.getName().startsWith(\"MLOG$_\")) { //Created by materliaized view logs for every table that is part of a materialized view. Not available for DDL operations.\n                                    return true;\n                                } else //noinspection HardCodedStringLiteral\n                                    if (example.getName().startsWith(\"RUPD$_\")) { //Created by materialized view log tables using primary keys. Not available for DDL operations.\n                                        return true;\n                                    } else //noinspection HardCodedStringLiteral\n                                        if (example.getName().startsWith(\"WM$_\")) { //Workspace Manager backup tables.\n                                            return true;\n                                        } else //noinspection HardCodedStringLiteral\n                                            if (\"CREATE$JAVA$LOB$TABLE\".equals(example.getName())) { //This table contains the name of the Java object, the date it was loaded, and has a BLOB column to store the Java object.\n                                                return true;\n                                            } else //noinspection HardCodedStringLiteral\n                                                if (\"JAVA$CLASS$MD5$TABLE\".equals(example.getName())) { //This is a hash table that tracks the loading of Java objects into a schema.\n                                                    return true;\n                                                } else //noinspection HardCodedStringLiteral\n                                                    if (example.getName().startsWith(\"ISEQ$$_\")) { //System-generated sequence\n                                                        return true;\n                                                    } else //noinspection HardCodedStringLiteral\n                                                        if (example.getName().startsWith(\"USLOG$\")) { //for update materialized view\n                                                            return true;\n                                                        } else if (example.getName().startsWith(\"SYS_FBA\")) { //for Flashback tables\n                                                            return true;\n                                                        }\n        }\n\n        return super.isSystemObject(example);\n    }\n\n    @Override\n    public boolean supportsAutoIncrement() {\n        // Oracle supports Identity beginning with version 12c\n        boolean isAutoIncrementSupported = false;\n\n        try {\n            if (getDatabaseMajorVersion() >= 12) {\n                isAutoIncrementSupported = true;\n            }\n\n            // Returning true will generate create table command with 'IDENTITY' clause, example:\n            // CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) GENERATED BY DEFAULT AS IDENTITY NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey));\n\n            // While returning false will continue to generate create table command without 'IDENTITY' clause, example:\n            // CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey));\n\n        } catch (DatabaseException ex) {\n            isAutoIncrementSupported = false;\n        }\n\n        return isAutoIncrementSupported;\n    }\n\n\n//    public Set<UniqueConstraint> findUniqueConstraints(String schema) throws DatabaseException {\n//        Set<UniqueConstraint> returnSet = new HashSet<UniqueConstraint>();\n//\n//        List<Map> maps = new Executor(this).queryForList(new RawSqlStatement(\"SELECT UC.CONSTRAINT_NAME, UCC.TABLE_NAME, UCC.COLUMN_NAME FROM USER_CONSTRAINTS UC, USER_CONS_COLUMNS UCC WHERE UC.CONSTRAINT_NAME=UCC.CONSTRAINT_NAME AND CONSTRAINT_TYPE='U' ORDER BY UC.CONSTRAINT_NAME\"));\n//\n//        UniqueConstraint constraint = null;\n//        for (Map map : maps) {\n//            if (constraint == null || !constraint.getName().equals(constraint.getName())) {\n//                returnSet.add(constraint);\n//                Table table = new Table((String) map.get(\"TABLE_NAME\"));\n//                constraint = new UniqueConstraint(map.get(\"CONSTRAINT_NAME\").toString(), table);\n//            }\n//        }\n//        if (constraint != null) {\n//            returnSet.add(constraint);\n//        }\n//\n//        return returnSet;\n//    }\n\n    @Override\n    public boolean supportsRestrictForeignKeys() {\n        return false;\n    }\n\n    @Override\n    public int getDataTypeMaxParameters(String dataTypeName) {\n        //noinspection HardCodedStringLiteral\n        if (\"BINARY_FLOAT\".equals(dataTypeName.toUpperCase())) {\n            return 0;\n        }\n        //noinspection HardCodedStringLiteral\n        if (\"BINARY_DOUBLE\".equals(dataTypeName.toUpperCase())) {\n            return 0;\n        }\n        return super.getDataTypeMaxParameters(dataTypeName);\n    }\n\n    public String getSystemTableWhereClause(String tableNameColumn) {\n        List<String> clauses = new ArrayList<String>(Arrays.asList(\"BIN$\",\n                \"AQ$\",\n                \"DR$\",\n                \"SYS_IOT_OVER\",\n                \"MLOG$_\",\n                \"RUPD$_\",\n                \"WM$_\",\n                \"ISEQ$$_\",\n                \"USLOG$\",\n                \"SYS_FBA\"));\n\n        for (int i = 0;i<clauses.size(); i++) {\n            clauses.set(i, tableNameColumn+\" NOT LIKE '\"+clauses.get(i)+\"%'\");\n        }\n        return \"(\"+ StringUtil.join(clauses, \" AND \") + \")\";\n    }\n\n    @Override\n    public boolean jdbcCallsCatalogsSchemas() {\n        return true;\n    }\n\n    public Set<String> getUserDefinedTypes() {\n        if (userDefinedTypes == null) {\n            userDefinedTypes = new HashSet<>();\n            if ((getConnection() != null) && !(getConnection() instanceof OfflineConnection)) {\n                try {\n                    try {\n                        //noinspection HardCodedStringLiteral\n                        userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor(\"jdbc\", this).queryForList(new RawSqlStatement(\"SELECT DISTINCT TYPE_NAME FROM ALL_TYPES\"), String.class));\n                    } catch (DatabaseException e) { //fall back to USER_TYPES if the user cannot see ALL_TYPES\n                        //noinspection HardCodedStringLiteral\n                        userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor(\"jdbc\", this).queryForList(new RawSqlStatement(\"SELECT TYPE_NAME FROM USER_TYPES\"), String.class));\n                    }\n                } catch (DatabaseException e) {\n                    //ignore error\n                }\n            }\n        }\n\n        return userDefinedTypes;\n    }\n\n    @Override\n    public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) {\n        //noinspection HardCodedStringLiteral\n        if ((databaseFunction != null) && \"current_timestamp\".equalsIgnoreCase(databaseFunction.toString())) {\n            return databaseFunction.toString();\n        }\n        if ((databaseFunction instanceof SequenceNextValueFunction) || (databaseFunction instanceof\n                SequenceCurrentValueFunction)) {\n            String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction);\n            // replace \"myschema.my_seq\".nextval with \"myschema\".\"my_seq\".nextval\n            return quotedSeq.replaceFirst(\"\\\"([^\\\\.\\\"]+)\\\\.([^\\\\.\\\"]+)\\\"\", \"\\\"$1\\\".\\\"$2\\\"\");\n\n        }\n\n        return super.generateDatabaseFunctionValue(databaseFunction);\n    }\n\n    @Override\n    public ValidationErrors validate() {\n        ValidationErrors errors = super.validate();\n        DatabaseConnection connection = getConnection();\n        if ((connection == null) || (connection instanceof OfflineConnection)) {\n            //noinspection HardCodedStringLiteral\n            Scope.getCurrentScope().getLog(getClass()).info(\"Cannot validate offline database\");\n            return errors;\n        }\n\n        if (!canAccessDbaRecycleBin()) {\n            errors.addWarning(getDbaRecycleBinWarning());\n        }\n\n        return errors;\n\n    }\n\n    public String getDbaRecycleBinWarning() {\n        //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,\n        // HardCodedStringLiteral\n        //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral\n        return \"Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where \" +\n                \"constraints are deleted and restored. Since Oracle doesn't properly restore the original table names \" +\n                \"referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this\" +\n                \" issue.\\n\" +\n                \"\\n\" +\n                \"The user you used to connect to the database (\" + getConnection().getConnectionUserName() +\n                \") needs to have \\\"SELECT ON SYS.DBA_RECYCLEBIN\\\" permissions set before we can perform this operation. \" +\n                \"Please run the following SQL to set the appropriate permissions, and try running the command again.\\n\" +\n                \"\\n\" +\n                \"     GRANT SELECT ON SYS.DBA_RECYCLEBIN TO \" + getConnection().getConnectionUserName() + \";\";\n    }\n\n    public boolean canAccessDbaRecycleBin() {\n        if (canAccessDbaRecycleBin == null) {\n            DatabaseConnection connection = getConnection();\n            if ((connection == null) || (connection instanceof OfflineConnection)) {\n                return false;\n            }\n\n            Statement statement = null;\n            try {\n                statement = ((JdbcConnection) connection).createStatement();\n                @SuppressWarnings(\"HardCodedStringLiteral\") ResultSet resultSet = statement.executeQuery(\"select 1 from dba_recyclebin where 0=1\");\n                resultSet.close(); //don't need to do anything with the result set, just make sure statement ran.\n                this.canAccessDbaRecycleBin = true;\n            } catch (Exception e) {\n                //noinspection HardCodedStringLiteral\n                if ((e instanceof SQLException) && e.getMessage().startsWith(\"ORA-00942\")) { //ORA-00942: table or view does not exist\n                    this.canAccessDbaRecycleBin = false;\n                } else {\n                    //noinspection HardCodedStringLiteral\n                    Scope.getCurrentScope().getLog(getClass()).warning(\"Cannot check dba_recyclebin access\", e);\n                    this.canAccessDbaRecycleBin = false;\n                }\n            } finally {\n                JdbcUtils.close(null, statement);\n            }\n        }\n\n        return canAccessDbaRecycleBin;\n    }\n\n    @Override\n    public boolean supportsNotNullConstraintNames() {\n        return true;\n    }\n\n    /**\n     * Tests if the given String would be a valid identifier in Oracle DBMS. In Oracle, a valid identifier has\n     * the following form (case-insensitive comparison):\n     * 1st character: A-Z\n     * 2..n characters: A-Z0-9$_#\n     * The maximum length of an identifier differs by Oracle version and object type.\n     */\n    public boolean isValidOracleIdentifier(String identifier, Class<? extends DatabaseObject> type) {\n        if ((identifier == null) || (identifier.length() < 1))\n            return false;\n\n        if (!identifier.matches(\"^(i?)[A-Z][A-Z0-9\\\\$\\\\_\\\\#]*$\"))\n            return false;\n\n        /*\n         * @todo It seems we currently do not have a class for tablespace identifiers, and all other classes\n         * we do know seem to be supported as 12cR2 long identifiers, so:\n         */\n        return (identifier.length() <= LONG_IDENTIFIERS_LEGNTH);\n    }\n\n    /**\n     * Returns the maximum number of bytes (NOT: characters) for an identifier. For Oracle <=12c Release 20, this\n     * is 30 bytes, and starting from 12cR2, up to 128 (except for tablespaces, PDB names and some other rather rare\n     * object types).\n     *\n     * @return the maximum length of an object identifier, in bytes\n     */\n    public int getIdentifierMaximumLength() {\n        try {\n            if (getDatabaseMajorVersion() < ORACLE_12C_MAJOR_VERSION) {\n                return SHORT_IDENTIFIERS_LENGTH;\n            } else if ((getDatabaseMajorVersion() == ORACLE_12C_MAJOR_VERSION) && (getDatabaseMinorVersion() <= 1)) {\n                return SHORT_IDENTIFIERS_LENGTH;\n            } else {\n                return LONG_IDENTIFIERS_LEGNTH;\n            }\n        } catch (DatabaseException ex) {\n            throw new UnexpectedLiquibaseException(\"Cannot determine the Oracle database version number\", ex);\n        }\n\n    }\n}\n"
  },
  {
    "path": "sql/dm/flowable-patch/src/main/java/liquibase/datatype/core/BooleanType.java",
    "content": "package liquibase.datatype.core;\n\nimport liquibase.change.core.LoadDataChange;\nimport liquibase.database.Database;\nimport liquibase.database.core.*;\nimport liquibase.datatype.DataTypeInfo;\nimport liquibase.datatype.DatabaseDataType;\nimport liquibase.datatype.LiquibaseDataType;\nimport liquibase.exception.UnexpectedLiquibaseException;\nimport liquibase.statement.DatabaseFunction;\nimport liquibase.util.StringUtil;\n\nimport java.util.Locale;\nimport java.util.regex.Pattern;\n\n@DataTypeInfo(name = \"boolean\", aliases = {\"java.sql.Types.BOOLEAN\", \"java.lang.Boolean\", \"bit\", \"bool\"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT)\npublic class BooleanType extends LiquibaseDataType {\n\n    @Override\n    public DatabaseDataType toDatabaseDataType(Database database) {\n        String originalDefinition = StringUtil.trimToEmpty(getRawDefinition());\n        if ((database instanceof Firebird3Database)) {\n            return new DatabaseDataType(\"BOOLEAN\");\n        }\n\n        if ((database instanceof Db2zDatabase) || (database instanceof FirebirdDatabase)) {\n            return new DatabaseDataType(\"SMALLINT\");\n        } else if (database instanceof MSSQLDatabase) {\n            return new DatabaseDataType(database.escapeDataTypeName(\"bit\"));\n        } else if (database instanceof MySQLDatabase) {\n            if (originalDefinition.toLowerCase(Locale.US).startsWith(\"bit\")) {\n                return new DatabaseDataType(\"BIT\", getParameters());\n            }\n            return new DatabaseDataType(\"BIT\", 1);\n        } else if (database instanceof OracleDatabase) {\n            return new DatabaseDataType(\"NUMBER\", 1);\n        } else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) {\n            return new DatabaseDataType(\"BIT\");\n        } else if (database instanceof DerbyDatabase) {\n            if (((DerbyDatabase) database).supportsBooleanDataType()) {\n                return new DatabaseDataType(\"BOOLEAN\");\n            } else {\n                return new DatabaseDataType(\"SMALLINT\");\n            }\n        } else if (database instanceof DB2Database) {\n            if (((DB2Database) database).supportsBooleanDataType())\n                return new DatabaseDataType(\"BOOLEAN\");\n            else\n                return new DatabaseDataType(\"SMALLINT\");\n        } else if (database instanceof HsqlDatabase) {\n            return new DatabaseDataType(\"BOOLEAN\");\n        } else if (database instanceof PostgresDatabase) {\n            if (originalDefinition.toLowerCase(Locale.US).startsWith(\"bit\")) {\n                return new DatabaseDataType(\"BIT\", getParameters());\n            }\n        } else if (database instanceof DmDatabase) { // dhb52: DM Support\n            return new DatabaseDataType(\"bit\");\n        }\n\n        return super.toDatabaseDataType(database);\n    }\n\n    @Override\n    public String objectToSql(Object value, Database database) {\n        if ((value == null) || \"null\".equals(value.toString().toLowerCase(Locale.US))) {\n            return null;\n        }\n\n        String returnValue;\n        if (value instanceof String) {\n            value = ((String) value).replaceAll(\"'\", \"\");\n            if (\"true\".equals(((String) value).toLowerCase(Locale.US)) || \"1\".equals(value) || \"b'1'\".equals(((String) value).toLowerCase(Locale.US)) || \"t\".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) {\n                returnValue = this.getTrueBooleanValue(database);\n            } else if (\"false\".equals(((String) value).toLowerCase(Locale.US)) || \"0\".equals(value) || \"b'0'\".equals(\n                ((String) value).toLowerCase(Locale.US)) || \"f\".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) {\n                returnValue = this.getFalseBooleanValue(database);\n            } else if (database instanceof PostgresDatabase && Pattern.matches(\"b?([01])\\\\1*(::bit|::\\\"bit\\\")?\", (String) value)) {\n                returnValue = \"b'\"\n                    + value.toString()\n                    .replace(\"b\", \"\")\n                    .replace(\"\\\"\", \"\")\n                    .replace(\"::it\", \"\")\n                    + \"'::\\\"bit\\\"\";\n            } else {\n                throw new UnexpectedLiquibaseException(\"Unknown boolean value: \" + value);\n            }\n        } else if (value instanceof Long) {\n            if (Long.valueOf(1).equals(value)) {\n                returnValue = this.getTrueBooleanValue(database);\n            } else {\n                returnValue = this.getFalseBooleanValue(database);\n            }\n        } else if (value instanceof Number) {\n            if (value.equals(1) || \"1\".equals(value.toString()) || \"1.0\".equals(value.toString())) {\n                returnValue = this.getTrueBooleanValue(database);\n            } else {\n                returnValue = this.getFalseBooleanValue(database);\n            }\n        } else if (value instanceof DatabaseFunction) {\n            return value.toString();\n        } else if (value instanceof Boolean) {\n            if (((Boolean) value)) {\n                returnValue = this.getTrueBooleanValue(database);\n            } else {\n                returnValue = this.getFalseBooleanValue(database);\n            }\n        } else {\n            throw new UnexpectedLiquibaseException(\"Cannot convert type \" + value.getClass() + \" to a boolean value\");\n        }\n\n        return returnValue;\n    }\n\n    protected boolean isNumericBoolean(Database database) {\n        if (database instanceof Firebird3Database) {\n            return false;\n        }\n        if (database instanceof DerbyDatabase) {\n            return !((DerbyDatabase) database).supportsBooleanDataType();\n        } else if (database instanceof DB2Database) {\n            return !((DB2Database) database).supportsBooleanDataType();\n        }\n        return (database instanceof Db2zDatabase)\n            || (database instanceof FirebirdDatabase)\n            || (database instanceof MSSQLDatabase)\n            || (database instanceof MySQLDatabase)\n            || (database instanceof OracleDatabase)\n            || (database instanceof SQLiteDatabase)\n            || (database instanceof SybaseASADatabase)\n            || (database instanceof SybaseDatabase)\n            || (database instanceof DmDatabase); // dhb52: DM Support\n    }\n\n    /**\n     * The database-specific value to use for \"false\" \"boolean\" columns.\n     */\n    public String getFalseBooleanValue(Database database) {\n        if (isNumericBoolean(database)) {\n            return \"0\";\n        }\n        if (database instanceof InformixDatabase) {\n            return \"'f'\";\n        }\n        return \"FALSE\";\n    }\n\n    /**\n     * The database-specific value to use for \"true\" \"boolean\" columns.\n     */\n    public String getTrueBooleanValue(Database database) {\n        if (isNumericBoolean(database)) {\n            return \"1\";\n        }\n        if (database instanceof InformixDatabase) {\n            return \"'t'\";\n        }\n        return \"TRUE\";\n    }\n\n    @Override\n    public LoadDataChange.LOAD_DATA_TYPE getLoadTypeName() {\n        return LoadDataChange.LOAD_DATA_TYPE.BOOLEAN;\n    }\n\n}\n"
  },
  {
    "path": "sql/dm/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java",
    "content": "/* Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.flowable.common.engine.impl;\n\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.ServiceLoader;\nimport java.util.Set;\n\nimport javax.naming.InitialContext;\nimport javax.sql.DataSource;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.ibatis.builder.xml.XMLConfigBuilder;\nimport org.apache.ibatis.builder.xml.XMLMapperBuilder;\nimport org.apache.ibatis.datasource.pooled.PooledDataSource;\nimport org.apache.ibatis.mapping.Environment;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;\nimport org.apache.ibatis.transaction.TransactionFactory;\nimport org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;\nimport org.apache.ibatis.transaction.managed.ManagedTransactionFactory;\nimport org.apache.ibatis.type.ArrayTypeHandler;\nimport org.apache.ibatis.type.BigDecimalTypeHandler;\nimport org.apache.ibatis.type.BlobInputStreamTypeHandler;\nimport org.apache.ibatis.type.BlobTypeHandler;\nimport org.apache.ibatis.type.BooleanTypeHandler;\nimport org.apache.ibatis.type.ByteTypeHandler;\nimport org.apache.ibatis.type.ClobTypeHandler;\nimport org.apache.ibatis.type.DateOnlyTypeHandler;\nimport org.apache.ibatis.type.DateTypeHandler;\nimport org.apache.ibatis.type.DoubleTypeHandler;\nimport org.apache.ibatis.type.FloatTypeHandler;\nimport org.apache.ibatis.type.IntegerTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.LongTypeHandler;\nimport org.apache.ibatis.type.NClobTypeHandler;\nimport org.apache.ibatis.type.NStringTypeHandler;\nimport org.apache.ibatis.type.ShortTypeHandler;\nimport org.apache.ibatis.type.SqlxmlTypeHandler;\nimport org.apache.ibatis.type.StringTypeHandler;\nimport org.apache.ibatis.type.TimeOnlyTypeHandler;\nimport org.apache.ibatis.type.TypeHandlerRegistry;\nimport org.flowable.common.engine.api.FlowableException;\nimport org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;\nimport org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;\nimport org.flowable.common.engine.api.delegate.event.FlowableEventListener;\nimport org.flowable.common.engine.api.engine.EngineLifecycleListener;\nimport org.flowable.common.engine.impl.agenda.AgendaOperationRunner;\nimport org.flowable.common.engine.impl.cfg.CommandExecutorImpl;\nimport org.flowable.common.engine.impl.cfg.IdGenerator;\nimport org.flowable.common.engine.impl.cfg.TransactionContextFactory;\nimport org.flowable.common.engine.impl.cfg.standalone.StandaloneMybatisTransactionContextFactory;\nimport org.flowable.common.engine.impl.db.CommonDbSchemaManager;\nimport org.flowable.common.engine.impl.db.DbSqlSessionFactory;\nimport org.flowable.common.engine.impl.db.LogSqlExecutionTimePlugin;\nimport org.flowable.common.engine.impl.db.MybatisTypeAliasConfigurator;\nimport org.flowable.common.engine.impl.db.MybatisTypeHandlerConfigurator;\nimport org.flowable.common.engine.impl.db.SchemaManager;\nimport org.flowable.common.engine.impl.event.EventDispatchAction;\nimport org.flowable.common.engine.impl.event.FlowableEventDispatcherImpl;\nimport org.flowable.common.engine.impl.interceptor.Command;\nimport org.flowable.common.engine.impl.interceptor.CommandConfig;\nimport org.flowable.common.engine.impl.interceptor.CommandContextFactory;\nimport org.flowable.common.engine.impl.interceptor.CommandContextInterceptor;\nimport org.flowable.common.engine.impl.interceptor.CommandExecutor;\nimport org.flowable.common.engine.impl.interceptor.CommandInterceptor;\nimport org.flowable.common.engine.impl.interceptor.CrDbRetryInterceptor;\nimport org.flowable.common.engine.impl.interceptor.DefaultCommandInvoker;\nimport org.flowable.common.engine.impl.interceptor.LogInterceptor;\nimport org.flowable.common.engine.impl.interceptor.SessionFactory;\nimport org.flowable.common.engine.impl.interceptor.TransactionContextInterceptor;\nimport org.flowable.common.engine.impl.lock.LockManager;\nimport org.flowable.common.engine.impl.lock.LockManagerImpl;\nimport org.flowable.common.engine.impl.logging.LoggingListener;\nimport org.flowable.common.engine.impl.logging.LoggingSession;\nimport org.flowable.common.engine.impl.logging.LoggingSessionFactory;\nimport org.flowable.common.engine.impl.persistence.GenericManagerFactory;\nimport org.flowable.common.engine.impl.persistence.StrongUuidGenerator;\nimport org.flowable.common.engine.impl.persistence.cache.EntityCache;\nimport org.flowable.common.engine.impl.persistence.cache.EntityCacheImpl;\nimport org.flowable.common.engine.impl.persistence.entity.ByteArrayEntityManager;\nimport org.flowable.common.engine.impl.persistence.entity.ByteArrayEntityManagerImpl;\nimport org.flowable.common.engine.impl.persistence.entity.Entity;\nimport org.flowable.common.engine.impl.persistence.entity.PropertyEntityManager;\nimport org.flowable.common.engine.impl.persistence.entity.PropertyEntityManagerImpl;\nimport org.flowable.common.engine.impl.persistence.entity.TableDataManager;\nimport org.flowable.common.engine.impl.persistence.entity.TableDataManagerImpl;\nimport org.flowable.common.engine.impl.persistence.entity.data.ByteArrayDataManager;\nimport org.flowable.common.engine.impl.persistence.entity.data.PropertyDataManager;\nimport org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisByteArrayDataManager;\nimport org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisPropertyDataManager;\nimport org.flowable.common.engine.impl.runtime.Clock;\nimport org.flowable.common.engine.impl.service.CommonEngineServiceImpl;\nimport org.flowable.common.engine.impl.util.DefaultClockImpl;\nimport org.flowable.common.engine.impl.util.IoUtil;\nimport org.flowable.common.engine.impl.util.ReflectUtil;\nimport org.flowable.eventregistry.api.EventRegistryEventConsumer;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.SerializationFeature;\n\npublic abstract class AbstractEngineConfiguration {\n\n    protected final Logger logger = LoggerFactory.getLogger(getClass());\n\n    /** The tenant id indicating 'no tenant' */\n    public static final String NO_TENANT_ID = \"\";\n\n    /**\n     * Checks the version of the DB schema against the library when the form engine is being created and throws an exception if the versions don't match.\n     */\n    public static final String DB_SCHEMA_UPDATE_FALSE = \"false\";\n    public static final String DB_SCHEMA_UPDATE_CREATE = \"create\";\n    public static final String DB_SCHEMA_UPDATE_CREATE_DROP = \"create-drop\";\n\n    /**\n     * Creates the schema when the form engine is being created and drops the schema when the form engine is being closed.\n     */\n    public static final String DB_SCHEMA_UPDATE_DROP_CREATE = \"drop-create\";\n\n    /**\n     * Upon building of the process engine, a check is performed and an update of the schema is performed if it is necessary.\n     */\n    public static final String DB_SCHEMA_UPDATE_TRUE = \"true\";\n\n    protected boolean forceCloseMybatisConnectionPool = true;\n\n    protected String databaseType;\n    protected String jdbcDriver = \"org.h2.Driver\";\n    protected String jdbcUrl = \"jdbc:h2:tcp://localhost/~/flowable\";\n    protected String jdbcUsername = \"sa\";\n    protected String jdbcPassword = \"\";\n    protected String dataSourceJndiName;\n    protected int jdbcMaxActiveConnections = 16;\n    protected int jdbcMaxIdleConnections = 8;\n    protected int jdbcMaxCheckoutTime;\n    protected int jdbcMaxWaitTime;\n    protected boolean jdbcPingEnabled;\n    protected String jdbcPingQuery;\n    protected int jdbcPingConnectionNotUsedFor;\n    protected int jdbcDefaultTransactionIsolationLevel;\n    protected DataSource dataSource;\n    protected SchemaManager commonSchemaManager;\n    protected SchemaManager schemaManager;\n    protected Command<Void> schemaManagementCmd;\n\n    protected String databaseSchemaUpdate = DB_SCHEMA_UPDATE_FALSE;\n\n    /**\n     * Whether to use a lock when performing the database schema create or update operations.\n     */\n    protected boolean useLockForDatabaseSchemaUpdate = false;\n\n    protected String xmlEncoding = \"UTF-8\";\n\n    // COMMAND EXECUTORS ///////////////////////////////////////////////\n\n    protected CommandExecutor commandExecutor;\n    protected Collection<? extends CommandInterceptor> defaultCommandInterceptors;\n    protected CommandConfig defaultCommandConfig;\n    protected CommandConfig schemaCommandConfig;\n    protected CommandContextFactory commandContextFactory;\n    protected CommandInterceptor commandInvoker;\n\n    protected AgendaOperationRunner agendaOperationRunner = (commandContext, runnable) -> runnable.run();\n\n    protected List<CommandInterceptor> customPreCommandInterceptors;\n    protected List<CommandInterceptor> customPostCommandInterceptors;\n    protected List<CommandInterceptor> commandInterceptors;\n\n    protected Map<String, AbstractEngineConfiguration> engineConfigurations = new HashMap<>();\n    protected Map<String, AbstractServiceConfiguration> serviceConfigurations = new HashMap<>();\n\n    protected ClassLoader classLoader;\n    /**\n     * Either use Class.forName or ClassLoader.loadClass for class loading. See http://forums.activiti.org/content/reflectutilloadclass-and-custom- classloader\n     */\n    protected boolean useClassForNameClassLoading = true;\n\n    protected List<EngineLifecycleListener> engineLifecycleListeners;\n\n    // Event Registry //////////////////////////////////////////////////\n    protected Map<String, EventRegistryEventConsumer> eventRegistryEventConsumers = new HashMap<>();\n\n    // MYBATIS SQL SESSION FACTORY /////////////////////////////////////\n\n    protected boolean isDbHistoryUsed = true;\n    protected DbSqlSessionFactory dbSqlSessionFactory;\n    protected SqlSessionFactory sqlSessionFactory;\n    protected TransactionFactory transactionFactory;\n    protected TransactionContextFactory transactionContextFactory;\n\n    /**\n     * If set to true, enables bulk insert (grouping sql inserts together). Default true.\n     * For some databases (eg DB2+z/OS) needs to be set to false.\n     */\n    protected boolean isBulkInsertEnabled = true;\n\n    /**\n     * Some databases have a limit of how many parameters one sql insert can have (eg SQL Server, 2000 params (!= insert statements) ). Tweak this parameter in case of exceptions indicating too much\n     * is being put into one bulk insert, or make it higher if your database can cope with it and there are inserts with a huge amount of data.\n     * <p>\n     * By default: 100 (55 for mssql server as it has a hard limit of 2000 parameters in a statement)\n     */\n    protected int maxNrOfStatementsInBulkInsert = 100;\n\n    public int DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER = 55; // currently Execution has most params (35). 2000 / 35 = 57.\n\n    protected String mybatisMappingFile;\n    protected Set<Class<?>> customMybatisMappers;\n    protected Set<String> customMybatisXMLMappers;\n    protected List<Interceptor> customMybatisInterceptors;\n\n    protected Set<String> dependentEngineMyBatisXmlMappers;\n    protected List<MybatisTypeAliasConfigurator> dependentEngineMybatisTypeAliasConfigs;\n    protected List<MybatisTypeHandlerConfigurator> dependentEngineMybatisTypeHandlerConfigs;\n\n    // SESSION FACTORIES ///////////////////////////////////////////////\n    protected List<SessionFactory> customSessionFactories;\n    protected Map<Class<?>, SessionFactory> sessionFactories;\n\n    protected boolean enableEventDispatcher = true;\n    protected FlowableEventDispatcher eventDispatcher;\n    protected List<FlowableEventListener> eventListeners;\n    protected Map<String, List<FlowableEventListener>> typedEventListeners;\n    protected List<EventDispatchAction> additionalEventDispatchActions;\n\n    protected LoggingListener loggingListener;\n\n    protected boolean transactionsExternallyManaged;\n\n    /**\n     * Flag that can be set to configure or not a relational database is used. This is useful for custom implementations that do not use relational databases at all.\n     *\n     * If true (default), the {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will be used to determine what needs to happen wrt the database schema.\n     *\n     * If false, no validation or schema creation will be done. That means that the database schema must have been created 'manually' before but the engine does not validate whether the schema is\n     * correct. The {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will not be used.\n     */\n    protected boolean usingRelationalDatabase = true;\n\n    /**\n     * Flag that can be set to configure whether or not a schema is used. This is useful for custom implementations that do not use relational databases at all.\n     * Setting {@link #usingRelationalDatabase} to true will automatically imply using a schema.\n     */\n    protected boolean usingSchemaMgmt = true;\n\n    /**\n     * Allows configuring a database table prefix which is used for all runtime operations of the process engine. For example, if you specify a prefix named 'PRE1.', Flowable will query for executions\n     * in a table named 'PRE1.ACT_RU_EXECUTION_'.\n     *\n     * <p>\n     * <strong>NOTE: the prefix is not respected by automatic database schema management. If you use {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_CREATE_DROP} or\n     * {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_TRUE}, Flowable will create the database tables using the default names, regardless of the prefix configured here.</strong>\n     */\n    protected String databaseTablePrefix = \"\";\n\n    /**\n     * Escape character for doing wildcard searches.\n     *\n     * This will be added at then end of queries that include for example a LIKE clause. For example: SELECT * FROM table WHERE column LIKE '%\\%%' ESCAPE '\\';\n     */\n    protected String databaseWildcardEscapeCharacter;\n\n    /**\n     * database catalog to use\n     */\n    protected String databaseCatalog = \"\";\n\n    /**\n     * In some situations you want to set the schema to use for table checks / generation if the database metadata doesn't return that correctly, see https://jira.codehaus.org/browse/ACT-1220,\n     * https://jira.codehaus.org/browse/ACT-1062\n     */\n    protected String databaseSchema;\n\n    /**\n     * Set to true in case the defined databaseTablePrefix is a schema-name, instead of an actual table name prefix. This is relevant for checking if Flowable-tables exist, the databaseTablePrefix\n     * will not be used here - since the schema is taken into account already, adding a prefix for the table-check will result in wrong table-names.\n     */\n    protected boolean tablePrefixIsSchema;\n\n    /**\n     * Set to true if the latest version of a definition should be retrieved, ignoring a possible parent deployment id value\n     */\n    protected boolean alwaysLookupLatestDefinitionVersion;\n\n    /**\n     * Set to true if by default lookups should fallback to the default tenant (an empty string by default or a defined tenant value)\n     */\n    protected boolean fallbackToDefaultTenant;\n\n    /**\n     * Default tenant provider that is executed when looking up definitions, in case the global or local fallback to default tenant value is true\n     */\n    protected DefaultTenantProvider defaultTenantProvider = (tenantId, scope, scopeKey) -> NO_TENANT_ID;\n\n    /**\n     * Enables the MyBatis plugin that logs the execution time of sql statements.\n     */\n    protected boolean enableLogSqlExecutionTime;\n\n    protected Properties databaseTypeMappings = getDefaultDatabaseTypeMappings();\n\n    /**\n     * Duration between the checks when acquiring a lock.\n     */\n    protected Duration lockPollRate = Duration.ofSeconds(10);\n\n    /**\n     * Duration to wait for the DB Schema lock before giving up.\n     */\n    protected Duration schemaLockWaitTime = Duration.ofMinutes(5);\n\n    // DATA MANAGERS //////////////////////////////////////////////////////////////////\n\n    protected PropertyDataManager propertyDataManager;\n    protected ByteArrayDataManager byteArrayDataManager;\n    protected TableDataManager tableDataManager;\n\n    // ENTITY MANAGERS ////////////////////////////////////////////////////////////////\n\n    protected PropertyEntityManager propertyEntityManager;\n    protected ByteArrayEntityManager byteArrayEntityManager;\n\n    protected List<EngineDeployer> customPreDeployers;\n    protected List<EngineDeployer> customPostDeployers;\n    protected List<EngineDeployer> deployers;\n\n    // CONFIGURATORS ////////////////////////////////////////////////////////////\n\n    protected boolean enableConfiguratorServiceLoader = true; // Enabled by default. In certain environments this should be set to false (eg osgi)\n    protected List<EngineConfigurator> configurators; // The injected configurators\n    protected List<EngineConfigurator> allConfigurators; // Including auto-discovered configurators\n    protected EngineConfigurator idmEngineConfigurator;\n    protected EngineConfigurator eventRegistryConfigurator;\n\n    public static final String PRODUCT_NAME_POSTGRES = \"PostgreSQL\";\n    public static final String PRODUCT_NAME_CRDB = \"CockroachDB\";\n\n    public static final String DATABASE_TYPE_H2 = \"h2\";\n    public static final String DATABASE_TYPE_HSQL = \"hsql\";\n    public static final String DATABASE_TYPE_MYSQL = \"mysql\";\n    public static final String DATABASE_TYPE_ORACLE = \"oracle\";\n    public static final String DATABASE_TYPE_POSTGRES = \"postgres\";\n    public static final String DATABASE_TYPE_MSSQL = \"mssql\";\n    public static final String DATABASE_TYPE_DB2 = \"db2\";\n    public static final String DATABASE_TYPE_COCKROACHDB = \"cockroachdb\";\n\n    public static Properties getDefaultDatabaseTypeMappings() {\n        Properties databaseTypeMappings = new Properties();\n        databaseTypeMappings.setProperty(\"H2\", DATABASE_TYPE_H2);\n        databaseTypeMappings.setProperty(\"HSQL Database Engine\", DATABASE_TYPE_HSQL);\n        databaseTypeMappings.setProperty(\"MySQL\", DATABASE_TYPE_MYSQL);\n        databaseTypeMappings.setProperty(\"MariaDB\", DATABASE_TYPE_MYSQL);\n        databaseTypeMappings.setProperty(\"Oracle\", DATABASE_TYPE_ORACLE);\n        databaseTypeMappings.setProperty(PRODUCT_NAME_POSTGRES, DATABASE_TYPE_POSTGRES);\n        databaseTypeMappings.setProperty(\"Microsoft SQL Server\", DATABASE_TYPE_MSSQL);\n        databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/NT\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/NT64\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2 UDP\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/LINUX\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/LINUX390\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/LINUXX8664\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/LINUXZ64\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/LINUXPPC64\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/LINUXPPC64LE\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/400 SQL\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/6000\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2 UDB iSeries\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/AIX64\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/HPUX\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/HP64\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/SUN\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/SUN64\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/PTX\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2/2\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(\"DB2 UDB AS400\", DATABASE_TYPE_DB2);\n        databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB);\n        databaseTypeMappings.setProperty(\"DM DBMS\", DATABASE_TYPE_ORACLE); // dhb52: DM support\n        return databaseTypeMappings;\n    }\n\n    protected Map<Object, Object> beans;\n\n    protected IdGenerator idGenerator;\n    protected boolean usePrefixId;\n\n    protected Clock clock;\n    protected ObjectMapper objectMapper;\n\n    // Variables\n\n    public static final int DEFAULT_GENERIC_MAX_LENGTH_STRING = 4000;\n    public static final int DEFAULT_ORACLE_MAX_LENGTH_STRING = 2000;\n\n    /**\n     * Define a max length for storing String variable types in the database. Mainly used for the Oracle NVARCHAR2 limit of 2000 characters\n     */\n    protected int maxLengthStringVariableType = -1;\n\n    protected void initEngineConfigurations() {\n        addEngineConfiguration(getEngineCfgKey(), getEngineScopeType(), this);\n    }\n\n    // DataSource\n    // ///////////////////////////////////////////////////////////////\n\n    protected void initDataSource() {\n        if (dataSource == null) {\n            if (dataSourceJndiName != null) {\n                try {\n                    dataSource = (DataSource) new InitialContext().lookup(dataSourceJndiName);\n                } catch (Exception e) {\n                    throw new FlowableException(\"couldn't lookup datasource from \" + dataSourceJndiName + \": \" + e.getMessage(), e);\n                }\n\n            } else if (jdbcUrl != null) {\n                if ((jdbcDriver == null) || (jdbcUsername == null)) {\n                    throw new FlowableException(\"DataSource or JDBC properties have to be specified in a process engine configuration\");\n                }\n\n                logger.debug(\"initializing datasource to db: {}\", jdbcUrl);\n\n                if (logger.isInfoEnabled()) {\n                    logger.info(\"Configuring Datasource with following properties (omitted password for security)\");\n                    logger.info(\"datasource driver : {}\", jdbcDriver);\n                    logger.info(\"datasource url : {}\", jdbcUrl);\n                    logger.info(\"datasource user name : {}\", jdbcUsername);\n                }\n\n                PooledDataSource pooledDataSource = new PooledDataSource(this.getClass().getClassLoader(), jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword);\n\n                if (jdbcMaxActiveConnections > 0) {\n                    pooledDataSource.setPoolMaximumActiveConnections(jdbcMaxActiveConnections);\n                }\n                if (jdbcMaxIdleConnections > 0) {\n                    pooledDataSource.setPoolMaximumIdleConnections(jdbcMaxIdleConnections);\n                }\n                if (jdbcMaxCheckoutTime > 0) {\n                    pooledDataSource.setPoolMaximumCheckoutTime(jdbcMaxCheckoutTime);\n                }\n                if (jdbcMaxWaitTime > 0) {\n                    pooledDataSource.setPoolTimeToWait(jdbcMaxWaitTime);\n                }\n                if (jdbcPingEnabled) {\n                    pooledDataSource.setPoolPingEnabled(true);\n                    if (jdbcPingQuery != null) {\n                        pooledDataSource.setPoolPingQuery(jdbcPingQuery);\n                    }\n                    pooledDataSource.setPoolPingConnectionsNotUsedFor(jdbcPingConnectionNotUsedFor);\n                }\n                if (jdbcDefaultTransactionIsolationLevel > 0) {\n                    pooledDataSource.setDefaultTransactionIsolationLevel(jdbcDefaultTransactionIsolationLevel);\n                }\n                dataSource = pooledDataSource;\n            }\n        }\n\n        if (databaseType == null) {\n            initDatabaseType();\n        }\n    }\n\n    public void initDatabaseType() {\n        Connection connection = null;\n        try {\n            connection = dataSource.getConnection();\n            DatabaseMetaData databaseMetaData = connection.getMetaData();\n            String databaseProductName = databaseMetaData.getDatabaseProductName();\n            logger.debug(\"database product name: '{}'\", databaseProductName);\n\n            // CRDB does not expose the version through the jdbc driver, so we need to fetch it through version().\n            if (PRODUCT_NAME_POSTGRES.equalsIgnoreCase(databaseProductName)) {\n                try (PreparedStatement preparedStatement = connection.prepareStatement(\"select version() as version;\");\n                     ResultSet resultSet = preparedStatement.executeQuery()) {\n                    String version = null;\n                    if (resultSet.next()) {\n                        version = resultSet.getString(\"version\");\n                    }\n\n                    if (StringUtils.isNotEmpty(version) && version.toLowerCase().startsWith(PRODUCT_NAME_CRDB.toLowerCase())) {\n                        databaseProductName = PRODUCT_NAME_CRDB;\n                        logger.info(\"CockroachDB version '{}' detected\", version);\n                    }\n                }\n            }\n\n            databaseType = databaseTypeMappings.getProperty(databaseProductName);\n            if (databaseType == null) {\n                throw new FlowableException(\"couldn't deduct database type from database product name '\" + databaseProductName + \"'\");\n            }\n            logger.debug(\"using database type: {}\", databaseType);\n\n        } catch (SQLException e) {\n            throw new RuntimeException(\"Exception while initializing Database connection\", e);\n        } finally {\n            try {\n                if (connection != null) {\n                    connection.close();\n                }\n            } catch (SQLException e) {\n                logger.error(\"Exception while closing the Database connection\", e);\n            }\n        }\n\n        // Special care for MSSQL, as it has a hard limit of 2000 params per statement (incl bulk statement).\n        // Especially with executions, with 100 as default, this limit is passed.\n        if (DATABASE_TYPE_MSSQL.equals(databaseType)) {\n            maxNrOfStatementsInBulkInsert = DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER;\n        }\n    }\n\n    public void initSchemaManager() {\n        if (this.commonSchemaManager == null) {\n            this.commonSchemaManager = new CommonDbSchemaManager();\n        }\n    }\n\n    // session factories ////////////////////////////////////////////////////////\n\n    public void addSessionFactory(SessionFactory sessionFactory) {\n        sessionFactories.put(sessionFactory.getSessionType(), sessionFactory);\n    }\n\n    public void initCommandContextFactory() {\n        if (commandContextFactory == null) {\n            commandContextFactory = new CommandContextFactory();\n        }\n    }\n\n    public void initTransactionContextFactory() {\n        if (transactionContextFactory == null) {\n            transactionContextFactory = new StandaloneMybatisTransactionContextFactory();\n        }\n    }\n\n    public void initCommandExecutors() {\n        initDefaultCommandConfig();\n        initSchemaCommandConfig();\n        initCommandInvoker();\n        initCommandInterceptors();\n        initCommandExecutor();\n    }\n\n\n    public void initDefaultCommandConfig() {\n        if (defaultCommandConfig == null) {\n            defaultCommandConfig = new CommandConfig();\n        }\n    }\n\n    public void initSchemaCommandConfig() {\n        if (schemaCommandConfig == null) {\n            schemaCommandConfig = new CommandConfig();\n        }\n    }\n\n    public void initCommandInvoker() {\n        if (commandInvoker == null) {\n            commandInvoker = new DefaultCommandInvoker();\n        }\n    }\n\n    public void initCommandInterceptors() {\n        if (commandInterceptors == null) {\n            commandInterceptors = new ArrayList<>();\n            if (customPreCommandInterceptors != null) {\n                commandInterceptors.addAll(customPreCommandInterceptors);\n            }\n            commandInterceptors.addAll(getDefaultCommandInterceptors());\n            if (customPostCommandInterceptors != null) {\n                commandInterceptors.addAll(customPostCommandInterceptors);\n            }\n            commandInterceptors.add(commandInvoker);\n        }\n    }\n\n    public Collection<? extends CommandInterceptor> getDefaultCommandInterceptors() {\n        if (defaultCommandInterceptors == null) {\n            List<CommandInterceptor> interceptors = new ArrayList<>();\n            interceptors.add(new LogInterceptor());\n\n            if (DATABASE_TYPE_COCKROACHDB.equals(databaseType)) {\n                interceptors.add(new CrDbRetryInterceptor());\n            }\n\n            CommandInterceptor transactionInterceptor = createTransactionInterceptor();\n            if (transactionInterceptor != null) {\n                interceptors.add(transactionInterceptor);\n            }\n\n            if (commandContextFactory != null) {\n                String engineCfgKey = getEngineCfgKey();\n                CommandContextInterceptor commandContextInterceptor = new CommandContextInterceptor(commandContextFactory,\n                    classLoader, useClassForNameClassLoading, clock, objectMapper);\n                engineConfigurations.put(engineCfgKey, this);\n                commandContextInterceptor.setEngineCfgKey(engineCfgKey);\n                commandContextInterceptor.setEngineConfigurations(engineConfigurations);\n                interceptors.add(commandContextInterceptor);\n            }\n\n            if (transactionContextFactory != null) {\n                interceptors.add(new TransactionContextInterceptor(transactionContextFactory));\n            }\n\n            List<CommandInterceptor> additionalCommandInterceptors = getAdditionalDefaultCommandInterceptors();\n            if (additionalCommandInterceptors != null) {\n                interceptors.addAll(additionalCommandInterceptors);\n            }\n\n            defaultCommandInterceptors = interceptors;\n        }\n        return defaultCommandInterceptors;\n    }\n\n    public abstract String getEngineCfgKey();\n\n    public abstract String getEngineScopeType();\n\n    public List<CommandInterceptor> getAdditionalDefaultCommandInterceptors() {\n        return null;\n    }\n\n    public void initCommandExecutor() {\n        if (commandExecutor == null) {\n            CommandInterceptor first = initInterceptorChain(commandInterceptors);\n            commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first);\n        }\n    }\n\n    public CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) {\n        if (chain == null || chain.isEmpty()) {\n            throw new FlowableException(\"invalid command interceptor chain configuration: \" + chain);\n        }\n        for (int i = 0; i < chain.size() - 1; i++) {\n            chain.get(i).setNext(chain.get(i + 1));\n        }\n        return chain.get(0);\n    }\n\n    public abstract CommandInterceptor createTransactionInterceptor();\n\n\n    public void initBeans() {\n        if (beans == null) {\n            beans = new HashMap<>();\n        }\n    }\n\n    // id generator\n    // /////////////////////////////////////////////////////////////\n\n    public void initIdGenerator() {\n        if (idGenerator == null) {\n            idGenerator = new StrongUuidGenerator();\n        }\n    }\n\n    public void initObjectMapper() {\n        if (objectMapper == null) {\n            objectMapper = new ObjectMapper();\n            objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);\n        }\n    }\n\n    public void initClock() {\n        if (clock == null) {\n            clock = new DefaultClockImpl();\n        }\n    }\n\n    // Data managers ///////////////////////////////////////////////////////////\n\n    public void initDataManagers() {\n        if (propertyDataManager == null) {\n            propertyDataManager = new MybatisPropertyDataManager(idGenerator);\n        }\n\n        if (byteArrayDataManager == null) {\n            byteArrayDataManager = new MybatisByteArrayDataManager(idGenerator);\n        }\n    }\n\n    // Entity managers //////////////////////////////////////////////////////////\n\n    public void initEntityManagers() {\n        if (propertyEntityManager == null) {\n            propertyEntityManager = new PropertyEntityManagerImpl(this, propertyDataManager);\n        }\n\n        if (byteArrayEntityManager == null) {\n            byteArrayEntityManager = new ByteArrayEntityManagerImpl(byteArrayDataManager, getEngineCfgKey(), this::getEventDispatcher);\n        }\n\n        if (tableDataManager == null) {\n            tableDataManager = new TableDataManagerImpl(this);\n        }\n    }\n\n    // services\n    // /////////////////////////////////////////////////////////////////\n\n    protected void initService(Object service) {\n        if (service instanceof CommonEngineServiceImpl) {\n            ((CommonEngineServiceImpl) service).setCommandExecutor(commandExecutor);\n        }\n    }\n\n    // myBatis SqlSessionFactory\n    // ////////////////////////////////////////////////\n\n    public void initSessionFactories() {\n        if (sessionFactories == null) {\n            sessionFactories = new HashMap<>();\n\n            if (usingRelationalDatabase) {\n                initDbSqlSessionFactory();\n            }\n\n            addSessionFactory(new GenericManagerFactory(EntityCache.class, EntityCacheImpl.class));\n\n            if (isLoggingSessionEnabled()) {\n                if (!sessionFactories.containsKey(LoggingSession.class)) {\n                    LoggingSessionFactory loggingSessionFactory = new LoggingSessionFactory();\n                    loggingSessionFactory.setLoggingListener(loggingListener);\n                    loggingSessionFactory.setObjectMapper(objectMapper);\n                    sessionFactories.put(LoggingSession.class, loggingSessionFactory);\n                }\n            }\n\n            commandContextFactory.setSessionFactories(sessionFactories);\n\n        } else {\n            if (usingRelationalDatabase) {\n                initDbSqlSessionFactoryEntitySettings();\n            }\n        }\n\n        if (customSessionFactories != null) {\n            for (SessionFactory sessionFactory : customSessionFactories) {\n                addSessionFactory(sessionFactory);\n            }\n        }\n    }\n\n    public void initDbSqlSessionFactory() {\n        if (dbSqlSessionFactory == null) {\n            dbSqlSessionFactory = createDbSqlSessionFactory();\n        }\n        dbSqlSessionFactory.setDatabaseType(databaseType);\n        dbSqlSessionFactory.setSqlSessionFactory(sqlSessionFactory);\n        dbSqlSessionFactory.setDbHistoryUsed(isDbHistoryUsed);\n        dbSqlSessionFactory.setDatabaseTablePrefix(databaseTablePrefix);\n        dbSqlSessionFactory.setTablePrefixIsSchema(tablePrefixIsSchema);\n        dbSqlSessionFactory.setDatabaseCatalog(databaseCatalog);\n        dbSqlSessionFactory.setDatabaseSchema(databaseSchema);\n        dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(maxNrOfStatementsInBulkInsert);\n\n        initDbSqlSessionFactoryEntitySettings();\n\n        addSessionFactory(dbSqlSessionFactory);\n    }\n\n    public DbSqlSessionFactory createDbSqlSessionFactory() {\n        return new DbSqlSessionFactory(usePrefixId);\n    }\n\n    protected abstract void initDbSqlSessionFactoryEntitySettings();\n\n    protected void defaultInitDbSqlSessionFactoryEntitySettings(List<Class<? extends Entity>> insertOrder, List<Class<? extends Entity>> deleteOrder) {\n        if (insertOrder != null) {\n            for (Class<? extends Entity> clazz : insertOrder) {\n                dbSqlSessionFactory.getInsertionOrder().add(clazz);\n\n                if (isBulkInsertEnabled) {\n                    dbSqlSessionFactory.getBulkInserteableEntityClasses().add(clazz);\n                }\n            }\n        }\n\n        if (deleteOrder != null) {\n            for (Class<? extends Entity> clazz : deleteOrder) {\n                dbSqlSessionFactory.getDeletionOrder().add(clazz);\n            }\n        }\n    }\n\n    public void initTransactionFactory() {\n        if (transactionFactory == null) {\n            if (transactionsExternallyManaged) {\n                transactionFactory = new ManagedTransactionFactory();\n                Properties properties = new Properties();\n                properties.put(\"closeConnection\", \"false\");\n                this.transactionFactory.setProperties(properties);\n            } else {\n                transactionFactory = new JdbcTransactionFactory();\n            }\n        }\n    }\n\n    public void initSqlSessionFactory() {\n        if (sqlSessionFactory == null) {\n            InputStream inputStream = null;\n            try {\n                inputStream = getMyBatisXmlConfigurationStream();\n\n                Environment environment = new Environment(\"default\", transactionFactory, dataSource);\n                Reader reader = new InputStreamReader(inputStream);\n                Properties properties = new Properties();\n                properties.put(\"prefix\", databaseTablePrefix);\n\n                String wildcardEscapeClause = \"\";\n                if ((databaseWildcardEscapeCharacter != null) && (databaseWildcardEscapeCharacter.length() != 0)) {\n                    wildcardEscapeClause = \" escape '\" + databaseWildcardEscapeCharacter + \"'\";\n                }\n                properties.put(\"wildcardEscapeClause\", wildcardEscapeClause);\n\n                // set default properties\n                properties.put(\"limitBefore\", \"\");\n                properties.put(\"limitAfter\", \"\");\n                properties.put(\"limitBetween\", \"\");\n                properties.put(\"limitBeforeNativeQuery\", \"\");\n                properties.put(\"limitAfterNativeQuery\", \"\");\n                properties.put(\"blobType\", \"BLOB\");\n                properties.put(\"boolValue\", \"TRUE\");\n\n                if (databaseType != null) {\n                    properties.load(getResourceAsStream(pathToEngineDbProperties()));\n                }\n\n                Configuration configuration = initMybatisConfiguration(environment, reader, properties);\n                sqlSessionFactory = new DefaultSqlSessionFactory(configuration);\n\n            } catch (Exception e) {\n                throw new FlowableException(\"Error while building ibatis SqlSessionFactory: \" + e.getMessage(), e);\n            } finally {\n                IoUtil.closeSilently(inputStream);\n            }\n        } else {\n            // This is needed when the SQL Session Factory is created by another engine.\n            // When custom XML Mappers are registered with this engine they need to be loaded in the configuration as well\n            applyCustomMybatisCustomizations(sqlSessionFactory.getConfiguration());\n        }\n    }\n\n    public String pathToEngineDbProperties() {\n        return \"org/flowable/common/db/properties/\" + databaseType + \".properties\";\n    }\n\n    public Configuration initMybatisConfiguration(Environment environment, Reader reader, Properties properties) {\n        XMLConfigBuilder parser = new XMLConfigBuilder(reader, \"\", properties);\n        Configuration configuration = parser.getConfiguration();\n\n        if (databaseType != null) {\n            configuration.setDatabaseId(databaseType);\n        }\n\n        configuration.setEnvironment(environment);\n\n        initMybatisTypeHandlers(configuration);\n        initCustomMybatisInterceptors(configuration);\n        if (isEnableLogSqlExecutionTime()) {\n            initMyBatisLogSqlExecutionTimePlugin(configuration);\n        }\n\n        configuration = parseMybatisConfiguration(parser);\n        return configuration;\n    }\n\n    public void initCustomMybatisMappers(Configuration configuration) {\n        if (getCustomMybatisMappers() != null) {\n            for (Class<?> clazz : getCustomMybatisMappers()) {\n                if (!configuration.hasMapper(clazz)) {\n                    configuration.addMapper(clazz);\n                }\n            }\n        }\n    }\n\n    public void initMybatisTypeHandlers(Configuration configuration) {\n        // When mapping into Map<String, Object> there is currently a problem with MyBatis.\n        // It will return objects which are driver specific.\n        // Therefore we are registering the mappings between Object.class and the specific jdbc type here.\n        // see https://github.com/mybatis/mybatis-3/issues/2216 for more info\n        TypeHandlerRegistry handlerRegistry = configuration.getTypeHandlerRegistry();\n\n        handlerRegistry.register(Object.class, JdbcType.BOOLEAN, new BooleanTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.BIT, new BooleanTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.TINYINT, new ByteTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.SMALLINT, new ShortTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.INTEGER, new IntegerTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.FLOAT, new FloatTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.DOUBLE, new DoubleTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.CHAR, new StringTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.CLOB, new ClobTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.VARCHAR, new StringTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.LONGVARCHAR, new StringTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.NVARCHAR, new NStringTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.NCHAR, new NStringTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.NCLOB, new NClobTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.BIGINT, new LongTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.REAL, new BigDecimalTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.DECIMAL, new BigDecimalTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.NUMERIC, new BigDecimalTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.BLOB, new BlobInputStreamTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.LONGVARBINARY, new BlobTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.DATE, new DateOnlyTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.TIME, new TimeOnlyTypeHandler());\n        handlerRegistry.register(Object.class, JdbcType.TIMESTAMP, new DateTypeHandler());\n\n        handlerRegistry.register(Object.class, JdbcType.SQLXML, new SqlxmlTypeHandler());\n    }\n\n    public void initCustomMybatisInterceptors(Configuration configuration) {\n        if (customMybatisInterceptors!=null){\n            for (Interceptor interceptor :customMybatisInterceptors){\n                configuration.addInterceptor(interceptor);\n            }\n        }\n    }\n\n    public void initMyBatisLogSqlExecutionTimePlugin(Configuration configuration) {\n        configuration.addInterceptor(new LogSqlExecutionTimePlugin());\n    }\n\n    public Configuration parseMybatisConfiguration(XMLConfigBuilder parser) {\n        Configuration configuration = parser.parse();\n\n        applyCustomMybatisCustomizations(configuration);\n        return configuration;\n    }\n\n    protected void applyCustomMybatisCustomizations(Configuration configuration) {\n        initCustomMybatisMappers(configuration);\n\n        if (dependentEngineMybatisTypeAliasConfigs != null) {\n            for (MybatisTypeAliasConfigurator typeAliasConfig : dependentEngineMybatisTypeAliasConfigs) {\n                typeAliasConfig.configure(configuration.getTypeAliasRegistry());\n            }\n        }\n        if (dependentEngineMybatisTypeHandlerConfigs != null) {\n            for (MybatisTypeHandlerConfigurator typeHandlerConfig : dependentEngineMybatisTypeHandlerConfigs) {\n                typeHandlerConfig.configure(configuration.getTypeHandlerRegistry());\n            }\n        }\n\n        parseDependentEngineMybatisXMLMappers(configuration);\n        parseCustomMybatisXMLMappers(configuration);\n    }\n\n    public void parseCustomMybatisXMLMappers(Configuration configuration) {\n        if (getCustomMybatisXMLMappers() != null) {\n            for (String resource : getCustomMybatisXMLMappers()) {\n                parseMybatisXmlMapping(configuration, resource);\n            }\n        }\n    }\n\n    public void parseDependentEngineMybatisXMLMappers(Configuration configuration) {\n        if (getDependentEngineMyBatisXmlMappers() != null) {\n            for (String resource : getDependentEngineMyBatisXmlMappers()) {\n                parseMybatisXmlMapping(configuration, resource);\n            }\n        }\n    }\n\n    protected void parseMybatisXmlMapping(Configuration configuration, String resource) {\n        // see XMLConfigBuilder.mapperElement()\n        XMLMapperBuilder mapperParser = new XMLMapperBuilder(getResourceAsStream(resource), configuration, resource, configuration.getSqlFragments());\n        mapperParser.parse();\n    }\n\n    protected InputStream getResourceAsStream(String resource) {\n        ClassLoader classLoader = getClassLoader();\n        if (classLoader != null) {\n            return getClassLoader().getResourceAsStream(resource);\n        } else {\n            return this.getClass().getClassLoader().getResourceAsStream(resource);\n        }\n    }\n\n    public void setMybatisMappingFile(String file) {\n        this.mybatisMappingFile = file;\n    }\n\n    public String getMybatisMappingFile() {\n        return mybatisMappingFile;\n    }\n\n    public abstract InputStream getMyBatisXmlConfigurationStream();\n\n    public void initConfigurators() {\n\n        allConfigurators = new ArrayList<>();\n        allConfigurators.addAll(getEngineSpecificEngineConfigurators());\n\n        // Configurators that are explicitly added to the config\n        if (configurators != null) {\n            allConfigurators.addAll(configurators);\n        }\n\n        // Auto discovery through ServiceLoader\n        if (enableConfiguratorServiceLoader) {\n            ClassLoader classLoader = getClassLoader();\n            if (classLoader == null) {\n                classLoader = ReflectUtil.getClassLoader();\n            }\n\n            ServiceLoader<EngineConfigurator> configuratorServiceLoader = ServiceLoader.load(EngineConfigurator.class, classLoader);\n            int nrOfServiceLoadedConfigurators = 0;\n            for (EngineConfigurator configurator : configuratorServiceLoader) {\n                allConfigurators.add(configurator);\n                nrOfServiceLoadedConfigurators++;\n            }\n\n            if (nrOfServiceLoadedConfigurators > 0) {\n                logger.info(\"Found {} auto-discoverable Process Engine Configurator{}\", nrOfServiceLoadedConfigurators, nrOfServiceLoadedConfigurators > 1 ? \"s\" : \"\");\n            }\n\n            if (!allConfigurators.isEmpty()) {\n\n                // Order them according to the priorities (useful for dependent\n                // configurator)\n                allConfigurators.sort(new Comparator<EngineConfigurator>() {\n\n                    @Override\n                    public int compare(EngineConfigurator configurator1, EngineConfigurator configurator2) {\n                        int priority1 = configurator1.getPriority();\n                        int priority2 = configurator2.getPriority();\n\n                        if (priority1 < priority2) {\n                            return -1;\n                        } else if (priority1 > priority2) {\n                            return 1;\n                        }\n                        return 0;\n                    }\n                });\n\n                // Execute the configurators\n                logger.info(\"Found {} Engine Configurators in total:\", allConfigurators.size());\n                for (EngineConfigurator configurator : allConfigurators) {\n                    logger.info(\"{} (priority:{})\", configurator.getClass(), configurator.getPriority());\n                }\n\n            }\n\n        }\n    }\n\n    public void close() {\n        if (forceCloseMybatisConnectionPool && dataSource instanceof PooledDataSource) {\n            /*\n             * When the datasource is created by a Flowable engine (i.e. it's an instance of PooledDataSource),\n             * the connection pool needs to be closed when closing the engine.\n             * Note that calling forceCloseAll() multiple times (as is the case when running with multiple engine) is ok.\n             */\n            ((PooledDataSource) dataSource).forceCloseAll();\n        }\n    }\n\n    protected List<EngineConfigurator> getEngineSpecificEngineConfigurators() {\n        // meant to be overridden if needed\n        return Collections.emptyList();\n    }\n\n    public void configuratorsBeforeInit() {\n        for (EngineConfigurator configurator : allConfigurators) {\n            logger.info(\"Executing beforeInit() of {} (priority:{})\", configurator.getClass(), configurator.getPriority());\n            configurator.beforeInit(this);\n        }\n    }\n\n    public void configuratorsAfterInit() {\n        for (EngineConfigurator configurator : allConfigurators) {\n            logger.info(\"Executing configure() of {} (priority:{})\", configurator.getClass(), configurator.getPriority());\n            configurator.configure(this);\n        }\n    }\n\n    public LockManager getLockManager(String lockName) {\n        return new LockManagerImpl(commandExecutor, lockName, getLockPollRate(), getEngineCfgKey());\n    }\n\n    // getters and setters\n    // //////////////////////////////////////////////////////\n\n    public abstract String getEngineName();\n\n    public ClassLoader getClassLoader() {\n        return classLoader;\n    }\n\n    public AbstractEngineConfiguration setClassLoader(ClassLoader classLoader) {\n        this.classLoader = classLoader;\n        return this;\n    }\n\n    public boolean isUseClassForNameClassLoading() {\n        return useClassForNameClassLoading;\n    }\n\n    public AbstractEngineConfiguration setUseClassForNameClassLoading(boolean useClassForNameClassLoading) {\n        this.useClassForNameClassLoading = useClassForNameClassLoading;\n        return this;\n    }\n\n    public void addEngineLifecycleListener(EngineLifecycleListener engineLifecycleListener) {\n        if (this.engineLifecycleListeners == null) {\n            this.engineLifecycleListeners = new ArrayList<>();\n        }\n        this.engineLifecycleListeners.add(engineLifecycleListener);\n    }\n\n    public List<EngineLifecycleListener> getEngineLifecycleListeners() {\n        return engineLifecycleListeners;\n    }\n\n    public AbstractEngineConfiguration setEngineLifecycleListeners(List<EngineLifecycleListener> engineLifecycleListeners) {\n        this.engineLifecycleListeners = engineLifecycleListeners;\n        return this;\n    }\n\n    public String getDatabaseType() {\n        return databaseType;\n    }\n\n    public AbstractEngineConfiguration setDatabaseType(String databaseType) {\n        this.databaseType = databaseType;\n        return this;\n    }\n\n    public DataSource getDataSource() {\n        return dataSource;\n    }\n\n    public AbstractEngineConfiguration setDataSource(DataSource dataSource) {\n        this.dataSource = dataSource;\n        return this;\n    }\n\n    public SchemaManager getSchemaManager() {\n        return schemaManager;\n    }\n\n    public AbstractEngineConfiguration setSchemaManager(SchemaManager schemaManager) {\n        this.schemaManager = schemaManager;\n        return this;\n    }\n\n    public SchemaManager getCommonSchemaManager() {\n        return commonSchemaManager;\n    }\n\n    public AbstractEngineConfiguration setCommonSchemaManager(SchemaManager commonSchemaManager) {\n        this.commonSchemaManager = commonSchemaManager;\n        return this;\n    }\n\n    public Command<Void> getSchemaManagementCmd() {\n        return schemaManagementCmd;\n    }\n\n    public AbstractEngineConfiguration setSchemaManagementCmd(Command<Void> schemaManagementCmd) {\n        this.schemaManagementCmd = schemaManagementCmd;\n        return this;\n    }\n\n    public String getJdbcDriver() {\n        return jdbcDriver;\n    }\n\n    public AbstractEngineConfiguration setJdbcDriver(String jdbcDriver) {\n        this.jdbcDriver = jdbcDriver;\n        return this;\n    }\n\n    public String getJdbcUrl() {\n        return jdbcUrl;\n    }\n\n    public AbstractEngineConfiguration setJdbcUrl(String jdbcUrl) {\n        this.jdbcUrl = jdbcUrl;\n        return this;\n    }\n\n    public String getJdbcUsername() {\n        return jdbcUsername;\n    }\n\n    public AbstractEngineConfiguration setJdbcUsername(String jdbcUsername) {\n        this.jdbcUsername = jdbcUsername;\n        return this;\n    }\n\n    public String getJdbcPassword() {\n        return jdbcPassword;\n    }\n\n    public AbstractEngineConfiguration setJdbcPassword(String jdbcPassword) {\n        this.jdbcPassword = jdbcPassword;\n        return this;\n    }\n\n    public int getJdbcMaxActiveConnections() {\n        return jdbcMaxActiveConnections;\n    }\n\n    public AbstractEngineConfiguration setJdbcMaxActiveConnections(int jdbcMaxActiveConnections) {\n        this.jdbcMaxActiveConnections = jdbcMaxActiveConnections;\n        return this;\n    }\n\n    public int getJdbcMaxIdleConnections() {\n        return jdbcMaxIdleConnections;\n    }\n\n    public AbstractEngineConfiguration setJdbcMaxIdleConnections(int jdbcMaxIdleConnections) {\n        this.jdbcMaxIdleConnections = jdbcMaxIdleConnections;\n        return this;\n    }\n\n    public int getJdbcMaxCheckoutTime() {\n        return jdbcMaxCheckoutTime;\n    }\n\n    public AbstractEngineConfiguration setJdbcMaxCheckoutTime(int jdbcMaxCheckoutTime) {\n        this.jdbcMaxCheckoutTime = jdbcMaxCheckoutTime;\n        return this;\n    }\n\n    public int getJdbcMaxWaitTime() {\n        return jdbcMaxWaitTime;\n    }\n\n    public AbstractEngineConfiguration setJdbcMaxWaitTime(int jdbcMaxWaitTime) {\n        this.jdbcMaxWaitTime = jdbcMaxWaitTime;\n        return this;\n    }\n\n    public boolean isJdbcPingEnabled() {\n        return jdbcPingEnabled;\n    }\n\n    public AbstractEngineConfiguration setJdbcPingEnabled(boolean jdbcPingEnabled) {\n        this.jdbcPingEnabled = jdbcPingEnabled;\n        return this;\n    }\n\n    public int getJdbcPingConnectionNotUsedFor() {\n        return jdbcPingConnectionNotUsedFor;\n    }\n\n    public AbstractEngineConfiguration setJdbcPingConnectionNotUsedFor(int jdbcPingConnectionNotUsedFor) {\n        this.jdbcPingConnectionNotUsedFor = jdbcPingConnectionNotUsedFor;\n        return this;\n    }\n\n    public int getJdbcDefaultTransactionIsolationLevel() {\n        return jdbcDefaultTransactionIsolationLevel;\n    }\n\n    public AbstractEngineConfiguration setJdbcDefaultTransactionIsolationLevel(int jdbcDefaultTransactionIsolationLevel) {\n        this.jdbcDefaultTransactionIsolationLevel = jdbcDefaultTransactionIsolationLevel;\n        return this;\n    }\n\n    public String getJdbcPingQuery() {\n        return jdbcPingQuery;\n    }\n\n    public AbstractEngineConfiguration setJdbcPingQuery(String jdbcPingQuery) {\n        this.jdbcPingQuery = jdbcPingQuery;\n        return this;\n    }\n\n    public String getDataSourceJndiName() {\n        return dataSourceJndiName;\n    }\n\n    public AbstractEngineConfiguration setDataSourceJndiName(String dataSourceJndiName) {\n        this.dataSourceJndiName = dataSourceJndiName;\n        return this;\n    }\n\n    public CommandConfig getSchemaCommandConfig() {\n        return schemaCommandConfig;\n    }\n\n    public AbstractEngineConfiguration setSchemaCommandConfig(CommandConfig schemaCommandConfig) {\n        this.schemaCommandConfig = schemaCommandConfig;\n        return this;\n    }\n\n    public boolean isTransactionsExternallyManaged() {\n        return transactionsExternallyManaged;\n    }\n\n    public AbstractEngineConfiguration setTransactionsExternallyManaged(boolean transactionsExternallyManaged) {\n        this.transactionsExternallyManaged = transactionsExternallyManaged;\n        return this;\n    }\n\n    public Map<Object, Object> getBeans() {\n        return beans;\n    }\n\n    public AbstractEngineConfiguration setBeans(Map<Object, Object> beans) {\n        this.beans = beans;\n        return this;\n    }\n\n    public IdGenerator getIdGenerator() {\n        return idGenerator;\n    }\n\n    public AbstractEngineConfiguration setIdGenerator(IdGenerator idGenerator) {\n        this.idGenerator = idGenerator;\n        return this;\n    }\n\n    public boolean isUsePrefixId() {\n        return usePrefixId;\n    }\n\n    public AbstractEngineConfiguration setUsePrefixId(boolean usePrefixId) {\n        this.usePrefixId = usePrefixId;\n        return this;\n    }\n\n    public String getXmlEncoding() {\n        return xmlEncoding;\n    }\n\n    public AbstractEngineConfiguration setXmlEncoding(String xmlEncoding) {\n        this.xmlEncoding = xmlEncoding;\n        return this;\n    }\n\n    public CommandConfig getDefaultCommandConfig() {\n        return defaultCommandConfig;\n    }\n\n    public AbstractEngineConfiguration setDefaultCommandConfig(CommandConfig defaultCommandConfig) {\n        this.defaultCommandConfig = defaultCommandConfig;\n        return this;\n    }\n\n    public CommandExecutor getCommandExecutor() {\n        return commandExecutor;\n    }\n\n    public AbstractEngineConfiguration setCommandExecutor(CommandExecutor commandExecutor) {\n        this.commandExecutor = commandExecutor;\n        return this;\n    }\n\n    public CommandContextFactory getCommandContextFactory() {\n        return commandContextFactory;\n    }\n\n    public AbstractEngineConfiguration setCommandContextFactory(CommandContextFactory commandContextFactory) {\n        this.commandContextFactory = commandContextFactory;\n        return this;\n    }\n\n    public CommandInterceptor getCommandInvoker() {\n        return commandInvoker;\n    }\n\n    public AbstractEngineConfiguration setCommandInvoker(CommandInterceptor commandInvoker) {\n        this.commandInvoker = commandInvoker;\n        return this;\n    }\n\n    public AgendaOperationRunner getAgendaOperationRunner() {\n        return agendaOperationRunner;\n    }\n\n    public AbstractEngineConfiguration setAgendaOperationRunner(AgendaOperationRunner agendaOperationRunner) {\n        this.agendaOperationRunner = agendaOperationRunner;\n        return this;\n    }\n\n    public List<CommandInterceptor> getCustomPreCommandInterceptors() {\n        return customPreCommandInterceptors;\n    }\n\n    public AbstractEngineConfiguration setCustomPreCommandInterceptors(List<CommandInterceptor> customPreCommandInterceptors) {\n        this.customPreCommandInterceptors = customPreCommandInterceptors;\n        return this;\n    }\n\n    public List<CommandInterceptor> getCustomPostCommandInterceptors() {\n        return customPostCommandInterceptors;\n    }\n\n    public AbstractEngineConfiguration setCustomPostCommandInterceptors(List<CommandInterceptor> customPostCommandInterceptors) {\n        this.customPostCommandInterceptors = customPostCommandInterceptors;\n        return this;\n    }\n\n    public List<CommandInterceptor> getCommandInterceptors() {\n        return commandInterceptors;\n    }\n\n    public AbstractEngineConfiguration setCommandInterceptors(List<CommandInterceptor> commandInterceptors) {\n        this.commandInterceptors = commandInterceptors;\n        return this;\n    }\n\n    public Map<String, AbstractEngineConfiguration> getEngineConfigurations() {\n        return engineConfigurations;\n    }\n\n    public AbstractEngineConfiguration setEngineConfigurations(Map<String, AbstractEngineConfiguration> engineConfigurations) {\n        this.engineConfigurations = engineConfigurations;\n        return this;\n    }\n\n    public void addEngineConfiguration(String key, String scopeType, AbstractEngineConfiguration engineConfiguration) {\n        if (engineConfigurations == null) {\n            engineConfigurations = new HashMap<>();\n        }\n        engineConfigurations.put(key, engineConfiguration);\n        engineConfigurations.put(scopeType, engineConfiguration);\n    }\n\n    public Map<String, AbstractServiceConfiguration> getServiceConfigurations() {\n        return serviceConfigurations;\n    }\n\n    public AbstractEngineConfiguration setServiceConfigurations(Map<String, AbstractServiceConfiguration> serviceConfigurations) {\n        this.serviceConfigurations = serviceConfigurations;\n        return this;\n    }\n\n    public void addServiceConfiguration(String key, AbstractServiceConfiguration serviceConfiguration) {\n        if (serviceConfigurations == null) {\n            serviceConfigurations = new HashMap<>();\n        }\n        serviceConfigurations.put(key, serviceConfiguration);\n    }\n\n    public Map<String, EventRegistryEventConsumer> getEventRegistryEventConsumers() {\n        return eventRegistryEventConsumers;\n    }\n\n    public AbstractEngineConfiguration setEventRegistryEventConsumers(Map<String, EventRegistryEventConsumer> eventRegistryEventConsumers) {\n        this.eventRegistryEventConsumers = eventRegistryEventConsumers;\n        return this;\n    }\n\n    public void addEventRegistryEventConsumer(String key, EventRegistryEventConsumer eventRegistryEventConsumer) {\n        if (eventRegistryEventConsumers == null) {\n            eventRegistryEventConsumers = new HashMap<>();\n        }\n        eventRegistryEventConsumers.put(key, eventRegistryEventConsumer);\n    }\n\n    public AbstractEngineConfiguration setDefaultCommandInterceptors(Collection<? extends CommandInterceptor> defaultCommandInterceptors) {\n        this.defaultCommandInterceptors = defaultCommandInterceptors;\n        return this;\n    }\n\n    public SqlSessionFactory getSqlSessionFactory() {\n        return sqlSessionFactory;\n    }\n\n    public AbstractEngineConfiguration setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {\n        this.sqlSessionFactory = sqlSessionFactory;\n        return this;\n    }\n\n    public boolean isDbHistoryUsed() {\n        return isDbHistoryUsed;\n    }\n\n    public AbstractEngineConfiguration setDbHistoryUsed(boolean isDbHistoryUsed) {\n        this.isDbHistoryUsed = isDbHistoryUsed;\n        return this;\n    }\n\n    public DbSqlSessionFactory getDbSqlSessionFactory() {\n        return dbSqlSessionFactory;\n    }\n\n    public AbstractEngineConfiguration setDbSqlSessionFactory(DbSqlSessionFactory dbSqlSessionFactory) {\n        this.dbSqlSessionFactory = dbSqlSessionFactory;\n        return this;\n    }\n\n    public TransactionFactory getTransactionFactory() {\n        return transactionFactory;\n    }\n\n    public AbstractEngineConfiguration setTransactionFactory(TransactionFactory transactionFactory) {\n        this.transactionFactory = transactionFactory;\n        return this;\n    }\n\n    public TransactionContextFactory getTransactionContextFactory() {\n        return transactionContextFactory;\n    }\n\n    public AbstractEngineConfiguration setTransactionContextFactory(TransactionContextFactory transactionContextFactory) {\n        this.transactionContextFactory = transactionContextFactory;\n        return this;\n    }\n\n    public int getMaxNrOfStatementsInBulkInsert() {\n        return maxNrOfStatementsInBulkInsert;\n    }\n\n    public AbstractEngineConfiguration setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) {\n        this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert;\n        return this;\n    }\n\n    public boolean isBulkInsertEnabled() {\n        return isBulkInsertEnabled;\n    }\n\n    public AbstractEngineConfiguration setBulkInsertEnabled(boolean isBulkInsertEnabled) {\n        this.isBulkInsertEnabled = isBulkInsertEnabled;\n        return this;\n    }\n\n    public Set<Class<?>> getCustomMybatisMappers() {\n        return customMybatisMappers;\n    }\n\n    public AbstractEngineConfiguration setCustomMybatisMappers(Set<Class<?>> customMybatisMappers) {\n        this.customMybatisMappers = customMybatisMappers;\n        return this;\n    }\n\n    public Set<String> getCustomMybatisXMLMappers() {\n        return customMybatisXMLMappers;\n    }\n\n    public AbstractEngineConfiguration setCustomMybatisXMLMappers(Set<String> customMybatisXMLMappers) {\n        this.customMybatisXMLMappers = customMybatisXMLMappers;\n        return this;\n    }\n\n    public Set<String> getDependentEngineMyBatisXmlMappers() {\n        return dependentEngineMyBatisXmlMappers;\n    }\n\n    public AbstractEngineConfiguration setCustomMybatisInterceptors(List<Interceptor> customMybatisInterceptors) {\n        this.customMybatisInterceptors = customMybatisInterceptors;\n        return  this;\n    }\n\n    public List<Interceptor> getCustomMybatisInterceptors() {\n        return customMybatisInterceptors;\n    }\n\n    public AbstractEngineConfiguration setDependentEngineMyBatisXmlMappers(Set<String> dependentEngineMyBatisXmlMappers) {\n        this.dependentEngineMyBatisXmlMappers = dependentEngineMyBatisXmlMappers;\n        return this;\n    }\n\n    public List<MybatisTypeAliasConfigurator> getDependentEngineMybatisTypeAliasConfigs() {\n        return dependentEngineMybatisTypeAliasConfigs;\n    }\n\n    public AbstractEngineConfiguration setDependentEngineMybatisTypeAliasConfigs(List<MybatisTypeAliasConfigurator> dependentEngineMybatisTypeAliasConfigs) {\n        this.dependentEngineMybatisTypeAliasConfigs = dependentEngineMybatisTypeAliasConfigs;\n        return this;\n    }\n\n    public List<MybatisTypeHandlerConfigurator> getDependentEngineMybatisTypeHandlerConfigs() {\n        return dependentEngineMybatisTypeHandlerConfigs;\n    }\n\n    public AbstractEngineConfiguration setDependentEngineMybatisTypeHandlerConfigs(List<MybatisTypeHandlerConfigurator> dependentEngineMybatisTypeHandlerConfigs) {\n        this.dependentEngineMybatisTypeHandlerConfigs = dependentEngineMybatisTypeHandlerConfigs;\n        return this;\n    }\n\n    public List<SessionFactory> getCustomSessionFactories() {\n        return customSessionFactories;\n    }\n\n    public AbstractEngineConfiguration addCustomSessionFactory(SessionFactory sessionFactory) {\n        if (customSessionFactories == null) {\n            customSessionFactories = new ArrayList<>();\n        }\n        customSessionFactories.add(sessionFactory);\n        return this;\n    }\n\n    public AbstractEngineConfiguration setCustomSessionFactories(List<SessionFactory> customSessionFactories) {\n        this.customSessionFactories = customSessionFactories;\n        return this;\n    }\n\n    public boolean isUsingRelationalDatabase() {\n        return usingRelationalDatabase;\n    }\n\n    public AbstractEngineConfiguration setUsingRelationalDatabase(boolean usingRelationalDatabase) {\n        this.usingRelationalDatabase = usingRelationalDatabase;\n        return this;\n    }\n\n    public boolean isUsingSchemaMgmt() {\n        return usingSchemaMgmt;\n    }\n\n    public AbstractEngineConfiguration setUsingSchemaMgmt(boolean usingSchema) {\n        this.usingSchemaMgmt = usingSchema;\n        return this;\n    }\n\n    public String getDatabaseTablePrefix() {\n        return databaseTablePrefix;\n    }\n\n    public AbstractEngineConfiguration setDatabaseTablePrefix(String databaseTablePrefix) {\n        this.databaseTablePrefix = databaseTablePrefix;\n        return this;\n    }\n\n    public String getDatabaseWildcardEscapeCharacter() {\n        return databaseWildcardEscapeCharacter;\n    }\n\n    public AbstractEngineConfiguration setDatabaseWildcardEscapeCharacter(String databaseWildcardEscapeCharacter) {\n        this.databaseWildcardEscapeCharacter = databaseWildcardEscapeCharacter;\n        return this;\n    }\n\n    public String getDatabaseCatalog() {\n        return databaseCatalog;\n    }\n\n    public AbstractEngineConfiguration setDatabaseCatalog(String databaseCatalog) {\n        this.databaseCatalog = databaseCatalog;\n        return this;\n    }\n\n    public String getDatabaseSchema() {\n        return databaseSchema;\n    }\n\n    public AbstractEngineConfiguration setDatabaseSchema(String databaseSchema) {\n        this.databaseSchema = databaseSchema;\n        return this;\n    }\n\n    public boolean isTablePrefixIsSchema() {\n        return tablePrefixIsSchema;\n    }\n\n    public AbstractEngineConfiguration setTablePrefixIsSchema(boolean tablePrefixIsSchema) {\n        this.tablePrefixIsSchema = tablePrefixIsSchema;\n        return this;\n    }\n\n    public boolean isAlwaysLookupLatestDefinitionVersion() {\n        return alwaysLookupLatestDefinitionVersion;\n    }\n\n    public AbstractEngineConfiguration setAlwaysLookupLatestDefinitionVersion(boolean alwaysLookupLatestDefinitionVersion) {\n        this.alwaysLookupLatestDefinitionVersion = alwaysLookupLatestDefinitionVersion;\n        return this;\n    }\n\n    public boolean isFallbackToDefaultTenant() {\n        return fallbackToDefaultTenant;\n    }\n\n    public AbstractEngineConfiguration setFallbackToDefaultTenant(boolean fallbackToDefaultTenant) {\n        this.fallbackToDefaultTenant = fallbackToDefaultTenant;\n        return this;\n    }\n\n    /**\n     * @return name of the default tenant\n     * @deprecated use {@link AbstractEngineConfiguration#getDefaultTenantProvider()} instead\n     */\n    @Deprecated\n    public String getDefaultTenantValue() {\n        return getDefaultTenantProvider().getDefaultTenant(null, null, null);\n    }\n\n    public AbstractEngineConfiguration setDefaultTenantValue(String defaultTenantValue) {\n        this.defaultTenantProvider = (tenantId, scope, scopeKey) -> defaultTenantValue;\n        return this;\n    }\n\n    public DefaultTenantProvider getDefaultTenantProvider() {\n        return defaultTenantProvider;\n    }\n\n    public AbstractEngineConfiguration setDefaultTenantProvider(DefaultTenantProvider defaultTenantProvider) {\n        this.defaultTenantProvider = defaultTenantProvider;\n        return this;\n    }\n\n    public boolean isEnableLogSqlExecutionTime() {\n        return enableLogSqlExecutionTime;\n    }\n\n    public void setEnableLogSqlExecutionTime(boolean enableLogSqlExecutionTime) {\n        this.enableLogSqlExecutionTime = enableLogSqlExecutionTime;\n    }\n\n    public Map<Class<?>, SessionFactory> getSessionFactories() {\n        return sessionFactories;\n    }\n\n    public AbstractEngineConfiguration setSessionFactories(Map<Class<?>, SessionFactory> sessionFactories) {\n        this.sessionFactories = sessionFactories;\n        return this;\n    }\n\n    public String getDatabaseSchemaUpdate() {\n        return databaseSchemaUpdate;\n    }\n\n    public AbstractEngineConfiguration setDatabaseSchemaUpdate(String databaseSchemaUpdate) {\n        this.databaseSchemaUpdate = databaseSchemaUpdate;\n        return this;\n    }\n\n    public boolean isUseLockForDatabaseSchemaUpdate() {\n        return useLockForDatabaseSchemaUpdate;\n    }\n\n    public AbstractEngineConfiguration setUseLockForDatabaseSchemaUpdate(boolean useLockForDatabaseSchemaUpdate) {\n        this.useLockForDatabaseSchemaUpdate = useLockForDatabaseSchemaUpdate;\n        return this;\n    }\n\n    public boolean isEnableEventDispatcher() {\n        return enableEventDispatcher;\n    }\n\n    public AbstractEngineConfiguration setEnableEventDispatcher(boolean enableEventDispatcher) {\n        this.enableEventDispatcher = enableEventDispatcher;\n        return this;\n    }\n\n    public FlowableEventDispatcher getEventDispatcher() {\n        return eventDispatcher;\n    }\n\n    public AbstractEngineConfiguration setEventDispatcher(FlowableEventDispatcher eventDispatcher) {\n        this.eventDispatcher = eventDispatcher;\n        return this;\n    }\n\n    public List<FlowableEventListener> getEventListeners() {\n        return eventListeners;\n    }\n\n    public AbstractEngineConfiguration setEventListeners(List<FlowableEventListener> eventListeners) {\n        this.eventListeners = eventListeners;\n        return this;\n    }\n\n    public Map<String, List<FlowableEventListener>> getTypedEventListeners() {\n        return typedEventListeners;\n    }\n\n    public AbstractEngineConfiguration setTypedEventListeners(Map<String, List<FlowableEventListener>> typedEventListeners) {\n        this.typedEventListeners = typedEventListeners;\n        return this;\n    }\n\n    public List<EventDispatchAction> getAdditionalEventDispatchActions() {\n        return additionalEventDispatchActions;\n    }\n\n    public AbstractEngineConfiguration setAdditionalEventDispatchActions(List<EventDispatchAction> additionalEventDispatchActions) {\n        this.additionalEventDispatchActions = additionalEventDispatchActions;\n        return this;\n    }\n\n    public void initEventDispatcher() {\n        if (this.eventDispatcher == null) {\n            this.eventDispatcher = new FlowableEventDispatcherImpl();\n        }\n\n        initAdditionalEventDispatchActions();\n\n        this.eventDispatcher.setEnabled(enableEventDispatcher);\n\n        initEventListeners();\n        initTypedEventListeners();\n    }\n\n    protected void initEventListeners() {\n        if (eventListeners != null) {\n            for (FlowableEventListener listenerToAdd : eventListeners) {\n                this.eventDispatcher.addEventListener(listenerToAdd);\n            }\n        }\n    }\n\n    protected void initAdditionalEventDispatchActions() {\n        if (this.additionalEventDispatchActions == null) {\n            this.additionalEventDispatchActions = new ArrayList<>();\n        }\n    }\n\n    protected void initTypedEventListeners() {\n        if (typedEventListeners != null) {\n            for (Map.Entry<String, List<FlowableEventListener>> listenersToAdd : typedEventListeners.entrySet()) {\n                // Extract types from the given string\n                FlowableEngineEventType[] types = FlowableEngineEventType.getTypesFromString(listenersToAdd.getKey());\n\n                for (FlowableEventListener listenerToAdd : listenersToAdd.getValue()) {\n                    this.eventDispatcher.addEventListener(listenerToAdd, types);\n                }\n            }\n        }\n    }\n\n    public boolean isLoggingSessionEnabled() {\n        return loggingListener != null;\n    }\n\n    public LoggingListener getLoggingListener() {\n        return loggingListener;\n    }\n\n    public void setLoggingListener(LoggingListener loggingListener) {\n        this.loggingListener = loggingListener;\n    }\n\n    public Clock getClock() {\n        return clock;\n    }\n\n    public AbstractEngineConfiguration setClock(Clock clock) {\n        this.clock = clock;\n        return this;\n    }\n\n    public ObjectMapper getObjectMapper() {\n        return objectMapper;\n    }\n\n    public AbstractEngineConfiguration setObjectMapper(ObjectMapper objectMapper) {\n        this.objectMapper = objectMapper;\n        return this;\n    }\n\n    public int getMaxLengthString() {\n        if (maxLengthStringVariableType == -1) {\n            if (\"oracle\".equalsIgnoreCase(databaseType)) {\n                return DEFAULT_ORACLE_MAX_LENGTH_STRING;\n            } else {\n                return DEFAULT_GENERIC_MAX_LENGTH_STRING;\n            }\n        } else {\n            return maxLengthStringVariableType;\n        }\n    }\n\n    public int getMaxLengthStringVariableType() {\n        return maxLengthStringVariableType;\n    }\n\n    public AbstractEngineConfiguration setMaxLengthStringVariableType(int maxLengthStringVariableType) {\n        this.maxLengthStringVariableType = maxLengthStringVariableType;\n        return this;\n    }\n\n    public PropertyDataManager getPropertyDataManager() {\n        return propertyDataManager;\n    }\n\n    public Duration getLockPollRate() {\n        return lockPollRate;\n    }\n\n    public AbstractEngineConfiguration setLockPollRate(Duration lockPollRate) {\n        this.lockPollRate = lockPollRate;\n        return this;\n    }\n\n    public Duration getSchemaLockWaitTime() {\n        return schemaLockWaitTime;\n    }\n\n    public void setSchemaLockWaitTime(Duration schemaLockWaitTime) {\n        this.schemaLockWaitTime = schemaLockWaitTime;\n    }\n\n    public AbstractEngineConfiguration setPropertyDataManager(PropertyDataManager propertyDataManager) {\n        this.propertyDataManager = propertyDataManager;\n        return this;\n    }\n\n    public PropertyEntityManager getPropertyEntityManager() {\n        return propertyEntityManager;\n    }\n\n    public AbstractEngineConfiguration setPropertyEntityManager(PropertyEntityManager propertyEntityManager) {\n        this.propertyEntityManager = propertyEntityManager;\n        return this;\n    }\n\n    public ByteArrayDataManager getByteArrayDataManager() {\n        return byteArrayDataManager;\n    }\n\n    public AbstractEngineConfiguration setByteArrayDataManager(ByteArrayDataManager byteArrayDataManager) {\n        this.byteArrayDataManager = byteArrayDataManager;\n        return this;\n    }\n\n    public ByteArrayEntityManager getByteArrayEntityManager() {\n        return byteArrayEntityManager;\n    }\n\n    public AbstractEngineConfiguration setByteArrayEntityManager(ByteArrayEntityManager byteArrayEntityManager) {\n        this.byteArrayEntityManager = byteArrayEntityManager;\n        return this;\n    }\n\n    public TableDataManager getTableDataManager() {\n        return tableDataManager;\n    }\n\n    public AbstractEngineConfiguration setTableDataManager(TableDataManager tableDataManager) {\n        this.tableDataManager = tableDataManager;\n        return this;\n    }\n\n    public List<EngineDeployer> getDeployers() {\n        return deployers;\n    }\n\n    public AbstractEngineConfiguration setDeployers(List<EngineDeployer> deployers) {\n        this.deployers = deployers;\n        return this;\n    }\n\n    public List<EngineDeployer> getCustomPreDeployers() {\n        return customPreDeployers;\n    }\n\n    public AbstractEngineConfiguration setCustomPreDeployers(List<EngineDeployer> customPreDeployers) {\n        this.customPreDeployers = customPreDeployers;\n        return this;\n    }\n\n    public List<EngineDeployer> getCustomPostDeployers() {\n        return customPostDeployers;\n    }\n\n    public AbstractEngineConfiguration setCustomPostDeployers(List<EngineDeployer> customPostDeployers) {\n        this.customPostDeployers = customPostDeployers;\n        return this;\n    }\n\n    public boolean isEnableConfiguratorServiceLoader() {\n        return enableConfiguratorServiceLoader;\n    }\n\n    public AbstractEngineConfiguration setEnableConfiguratorServiceLoader(boolean enableConfiguratorServiceLoader) {\n        this.enableConfiguratorServiceLoader = enableConfiguratorServiceLoader;\n        return this;\n    }\n\n    public List<EngineConfigurator> getConfigurators() {\n        return configurators;\n    }\n\n    public AbstractEngineConfiguration addConfigurator(EngineConfigurator configurator) {\n        if (configurators == null) {\n            configurators = new ArrayList<>();\n        }\n        configurators.add(configurator);\n        return this;\n    }\n\n    /**\n     * @return All {@link EngineConfigurator} instances. Will only contain values after init of the engine.\n     * Use the {@link #getConfigurators()} or {@link #addConfigurator(EngineConfigurator)} methods otherwise.\n     */\n    public List<EngineConfigurator> getAllConfigurators() {\n        return allConfigurators;\n    }\n\n    public AbstractEngineConfiguration setConfigurators(List<EngineConfigurator> configurators) {\n        this.configurators = configurators;\n        return this;\n    }\n\n    public EngineConfigurator getIdmEngineConfigurator() {\n        return idmEngineConfigurator;\n    }\n\n    public AbstractEngineConfiguration setIdmEngineConfigurator(EngineConfigurator idmEngineConfigurator) {\n        this.idmEngineConfigurator = idmEngineConfigurator;\n        return this;\n    }\n\n    public EngineConfigurator getEventRegistryConfigurator() {\n        return eventRegistryConfigurator;\n    }\n\n    public AbstractEngineConfiguration setEventRegistryConfigurator(EngineConfigurator eventRegistryConfigurator) {\n        this.eventRegistryConfigurator = eventRegistryConfigurator;\n        return this;\n    }\n\n    public AbstractEngineConfiguration setForceCloseMybatisConnectionPool(boolean forceCloseMybatisConnectionPool) {\n        this.forceCloseMybatisConnectionPool = forceCloseMybatisConnectionPool;\n        return this;\n    }\n\n    public boolean isForceCloseMybatisConnectionPool() {\n        return forceCloseMybatisConnectionPool;\n    }\n}\n"
  },
  {
    "path": "sql/dm/flowable-patch/src/main/resources/META-INF/package-info.md",
    "content": "防止IDEA将`.`和`/`混为一谈"
  },
  {
    "path": "sql/dm/flowable-patch/src/main/resources/META-INF/services/liquibase.database.Database",
    "content": "liquibase.database.core.CockroachDatabase\nliquibase.database.core.DB2Database\nliquibase.database.core.Db2zDatabase\nliquibase.database.core.DerbyDatabase\nliquibase.database.core.Firebird3Database\nliquibase.database.core.FirebirdDatabase\nliquibase.database.core.H2Database\nliquibase.database.core.HsqlDatabase\nliquibase.database.core.InformixDatabase\nliquibase.database.core.Ingres9Database\nliquibase.database.core.MSSQLDatabase\nliquibase.database.core.MariaDBDatabase\nliquibase.database.core.MockDatabase\nliquibase.database.core.MySQLDatabase\nliquibase.database.core.OracleDatabase\nliquibase.database.core.PostgresDatabase\nliquibase.database.core.SQLiteDatabase\nliquibase.database.core.SybaseASADatabase\nliquibase.database.core.SybaseDatabase\nliquibase.database.core.DmDatabase\nliquibase.database.core.UnsupportedDatabase\n"
  },
  {
    "path": "sql/dm/quartz.sql",
    "content": "--\n-- A hint submitted by a user: Oracle DB MUST be created as \"shared\" and the\n-- job_queue_processes parameter  must be greater than 2\n-- However, these settings are pretty much standard after any\n-- Oracle install, so most users need not worry about this.\n--\n-- Many other users (including the primary author of Quartz) have had success\n-- running in dedicated mode, so only consider the above as a hint ;-)\n--\n\ndrop table if exists qrtz_calendars;\ndrop table if exists qrtz_fired_triggers;\ndrop table if exists qrtz_blob_triggers;\ndrop table if exists qrtz_cron_triggers;\ndrop table if exists qrtz_simple_triggers;\ndrop table if exists qrtz_simprop_triggers;\ndrop table if exists qrtz_triggers;\ndrop table if exists qrtz_job_details;\ndrop table if exists qrtz_paused_trigger_grps;\ndrop table if exists qrtz_locks;\ndrop table if exists qrtz_scheduler_state;\n\nCREATE TABLE qrtz_job_details\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    JOB_NAME  VARCHAR2(200) NOT NULL,\n    JOB_GROUP VARCHAR2(200) NOT NULL,\n    DESCRIPTION VARCHAR2(250) NULL,\n    JOB_CLASS_NAME   VARCHAR2(250) NOT NULL,\n    IS_DURABLE VARCHAR2(1) NOT NULL,\n    IS_NONCONCURRENT VARCHAR2(1) NOT NULL,\n    IS_UPDATE_DATA VARCHAR2(1) NOT NULL,\n    REQUESTS_RECOVERY VARCHAR2(1) NOT NULL,\n    JOB_DATA BLOB NULL,\n    CONSTRAINT QRTZ_JOB_DETAILS_PK PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)\n);\nCREATE TABLE qrtz_triggers\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    TRIGGER_NAME VARCHAR2(200) NOT NULL,\n    TRIGGER_GROUP VARCHAR2(200) NOT NULL,\n    JOB_NAME  VARCHAR2(200) NOT NULL,\n    JOB_GROUP VARCHAR2(200) NOT NULL,\n    DESCRIPTION VARCHAR2(250) NULL,\n    NEXT_FIRE_TIME NUMBER(19) NULL,\n    PREV_FIRE_TIME NUMBER(19) NULL,\n    PRIORITY NUMBER(13) NULL,\n    TRIGGER_STATE VARCHAR2(16) NOT NULL,\n    TRIGGER_TYPE VARCHAR2(8) NOT NULL,\n    START_TIME NUMBER(19) NOT NULL,\n    END_TIME NUMBER(19) NULL,\n    CALENDAR_NAME VARCHAR2(200) NULL,\n    MISFIRE_INSTR NUMBER(2) NULL,\n    JOB_DATA BLOB NULL,\n    CONSTRAINT QRTZ_TRIGGERS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),\n    CONSTRAINT QRTZ_TRIGGER_TO_JOBS_FK FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)\n        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)\n);\nCREATE TABLE qrtz_simple_triggers\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    TRIGGER_NAME VARCHAR2(200) NOT NULL,\n    TRIGGER_GROUP VARCHAR2(200) NOT NULL,\n    REPEAT_COUNT NUMBER(7) NOT NULL,\n    REPEAT_INTERVAL NUMBER(12) NOT NULL,\n    TIMES_TRIGGERED NUMBER(10) NOT NULL,\n    CONSTRAINT QRTZ_SIMPLE_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),\n    CONSTRAINT QRTZ_SIMPLE_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)\n        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)\n);\nCREATE TABLE qrtz_cron_triggers\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    TRIGGER_NAME VARCHAR2(200) NOT NULL,\n    TRIGGER_GROUP VARCHAR2(200) NOT NULL,\n    CRON_EXPRESSION VARCHAR2(120) NOT NULL,\n    TIME_ZONE_ID VARCHAR2(80),\n    CONSTRAINT QRTZ_CRON_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),\n    CONSTRAINT QRTZ_CRON_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)\n        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)\n);\nCREATE TABLE qrtz_simprop_triggers\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    TRIGGER_NAME VARCHAR2(200) NOT NULL,\n    TRIGGER_GROUP VARCHAR2(200) NOT NULL,\n    STR_PROP_1 VARCHAR2(512) NULL,\n    STR_PROP_2 VARCHAR2(512) NULL,\n    STR_PROP_3 VARCHAR2(512) NULL,\n    INT_PROP_1 NUMBER(10) NULL,\n    INT_PROP_2 NUMBER(10) NULL,\n    LONG_PROP_1 NUMBER(19) NULL,\n    LONG_PROP_2 NUMBER(19) NULL,\n    DEC_PROP_1 NUMERIC(13,4) NULL,\n    DEC_PROP_2 NUMERIC(13,4) NULL,\n    BOOL_PROP_1 VARCHAR2(1) NULL,\n    BOOL_PROP_2 VARCHAR2(1) NULL,\n    TIME_ZONE_ID VARCHAR2(80) NULL,\n    CONSTRAINT QRTZ_SIMPROP_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),\n    CONSTRAINT QRTZ_SIMPROP_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)\n        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)\n);\nCREATE TABLE qrtz_blob_triggers\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    TRIGGER_NAME VARCHAR2(200) NOT NULL,\n    TRIGGER_GROUP VARCHAR2(200) NOT NULL,\n    BLOB_DATA BLOB NULL,\n    CONSTRAINT QRTZ_BLOB_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),\n    CONSTRAINT QRTZ_BLOB_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)\n        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)\n);\nCREATE TABLE qrtz_calendars\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    CALENDAR_NAME  VARCHAR2(200) NOT NULL,\n    CALENDAR BLOB NOT NULL,\n    CONSTRAINT QRTZ_CALENDARS_PK PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)\n);\nCREATE TABLE qrtz_paused_trigger_grps\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    TRIGGER_GROUP  VARCHAR2(200) NOT NULL,\n    CONSTRAINT QRTZ_PAUSED_TRIG_GRPS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)\n);\nCREATE TABLE qrtz_fired_triggers\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    ENTRY_ID VARCHAR2(140) NOT NULL,\n    TRIGGER_NAME VARCHAR2(200) NOT NULL,\n    TRIGGER_GROUP VARCHAR2(200) NOT NULL,\n    INSTANCE_NAME VARCHAR2(200) NOT NULL,\n    FIRED_TIME NUMBER(19) NOT NULL,\n    SCHED_TIME NUMBER(19) NOT NULL,\n    PRIORITY NUMBER(13) NOT NULL,\n    STATE VARCHAR2(16) NOT NULL,\n    JOB_NAME VARCHAR2(200) NULL,\n    JOB_GROUP VARCHAR2(200) NULL,\n    IS_NONCONCURRENT VARCHAR2(1) NULL,\n    REQUESTS_RECOVERY VARCHAR2(1) NULL,\n    CONSTRAINT QRTZ_FIRED_TRIGGER_PK PRIMARY KEY (SCHED_NAME,ENTRY_ID)\n);\nCREATE TABLE qrtz_scheduler_state\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    INSTANCE_NAME VARCHAR2(200) NOT NULL,\n    LAST_CHECKIN_TIME NUMBER(19) NOT NULL,\n    CHECKIN_INTERVAL NUMBER(13) NOT NULL,\n    CONSTRAINT QRTZ_SCHEDULER_STATE_PK PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)\n);\nCREATE TABLE qrtz_locks\n(\n    SCHED_NAME VARCHAR2(120) NOT NULL,\n    LOCK_NAME  VARCHAR2(40) NOT NULL,\n    CONSTRAINT QRTZ_LOCKS_PK PRIMARY KEY (SCHED_NAME,LOCK_NAME)\n);\n\ncreate index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY);\ncreate index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP);\n\ncreate index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);\ncreate index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP);\ncreate index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME);\ncreate index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP);\ncreate index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE);\ncreate index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);\ncreate index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);\ncreate index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME);\ncreate index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);\ncreate index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);\ncreate index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);\ncreate index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);\n\ncreate index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME);\ncreate index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);\ncreate index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);\ncreate index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP);\ncreate index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);\ncreate index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP);"
  },
  {
    "path": "sql/dm/ruoyi-vue-pro-dm8.sql",
    "content": "/*\n Yudao Database Transfer Tool\n\n Source Server Type    : MySQL\n\n Target Server Type    : DM8\n\n Date: 2025-05-22 21:49:51\n*/\n\n\n-- ----------------------------\n-- Table structure for infra_api_access_log\n-- ----------------------------\nCREATE TABLE infra_api_access_log\n(\n    id               bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    trace_id         varchar(64)  DEFAULT ''                NULL,\n    user_id          bigint       DEFAULT 0                 NOT NULL,\n    user_type        smallint     DEFAULT 0                 NOT NULL,\n    application_name varchar(50)                            NOT NULL,\n    request_method   varchar(16)  DEFAULT ''                NULL,\n    request_url      varchar(255) DEFAULT ''                NULL,\n    request_params   text                                   NULL,\n    response_body    text                                   NULL,\n    user_ip          varchar(50)                            NOT NULL,\n    user_agent       varchar(512)                           NOT NULL,\n    operate_module   varchar(50)  DEFAULT NULL              NULL,\n    operate_name     varchar(50)  DEFAULT NULL              NULL,\n    operate_type     smallint     DEFAULT 0                 NULL,\n    begin_time       datetime                               NOT NULL,\n    end_time         datetime                               NOT NULL,\n    duration         int                                    NOT NULL,\n    result_code      int          DEFAULT 0                 NOT NULL,\n    result_msg       varchar(512) DEFAULT ''                NULL,\n    creator          varchar(64)  DEFAULT ''                NULL,\n    create_time      datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater          varchar(64)  DEFAULT ''                NULL,\n    update_time      datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted          bit          DEFAULT '0'               NOT NULL,\n    tenant_id        bigint       DEFAULT 0                 NOT NULL\n);\n\nCREATE INDEX idx_infra_api_access_log_01 ON infra_api_access_log (create_time);\n\nCOMMENT ON COLUMN infra_api_access_log.id IS '日志主键';\nCOMMENT ON COLUMN infra_api_access_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_access_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_access_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_access_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_access_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_access_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_access_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_access_log.response_body IS '响应结果';\nCOMMENT ON COLUMN infra_api_access_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_access_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_access_log.operate_module IS '操作模块';\nCOMMENT ON COLUMN infra_api_access_log.operate_name IS '操作名';\nCOMMENT ON COLUMN infra_api_access_log.operate_type IS '操作分类';\nCOMMENT ON COLUMN infra_api_access_log.begin_time IS '开始请求时间';\nCOMMENT ON COLUMN infra_api_access_log.end_time IS '结束请求时间';\nCOMMENT ON COLUMN infra_api_access_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_api_access_log.result_code IS '结果码';\nCOMMENT ON COLUMN infra_api_access_log.result_msg IS '结果提示';\nCOMMENT ON COLUMN infra_api_access_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_access_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_access_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_access_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_access_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_access_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_access_log IS 'API 访问日志表';\n\n-- ----------------------------\n-- Table structure for infra_api_error_log\n-- ----------------------------\nCREATE TABLE infra_api_error_log\n(\n    id                           bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    trace_id                     varchar(64)                            NOT NULL,\n    user_id                      bigint       DEFAULT 0                 NOT NULL,\n    user_type                    smallint     DEFAULT 0                 NOT NULL,\n    application_name             varchar(50)                            NOT NULL,\n    request_method               varchar(16)                            NOT NULL,\n    request_url                  varchar(255)                           NOT NULL,\n    request_params               varchar(8000)                          NOT NULL,\n    user_ip                      varchar(50)                            NOT NULL,\n    user_agent                   varchar(512)                           NOT NULL,\n    exception_time               datetime                               NOT NULL,\n    exception_name               varchar(128) DEFAULT ''                NULL,\n    exception_message            text                                   NOT NULL,\n    exception_root_cause_message text                                   NOT NULL,\n    exception_stack_trace        text                                   NOT NULL,\n    exception_class_name         varchar(512)                           NOT NULL,\n    exception_file_name          varchar(512)                           NOT NULL,\n    exception_method_name        varchar(512)                           NOT NULL,\n    exception_line_number        int                                    NOT NULL,\n    process_status               smallint                               NOT NULL,\n    process_time                 datetime     DEFAULT NULL              NULL,\n    process_user_id              int          DEFAULT 0                 NULL,\n    creator                      varchar(64)  DEFAULT ''                NULL,\n    create_time                  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater                      varchar(64)  DEFAULT ''                NULL,\n    update_time                  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted                      bit          DEFAULT '0'               NOT NULL,\n    tenant_id                    bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN infra_api_error_log.id IS '编号';\nCOMMENT ON COLUMN infra_api_error_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_error_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_error_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_error_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_error_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_error_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_error_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_error_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_error_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_error_log.exception_time IS '异常发生时间';\nCOMMENT ON COLUMN infra_api_error_log.exception_name IS '异常名';\nCOMMENT ON COLUMN infra_api_error_log.exception_message IS '异常导致的消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_root_cause_message IS '异常导致的根消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_stack_trace IS '异常的栈轨迹';\nCOMMENT ON COLUMN infra_api_error_log.exception_class_name IS '异常发生的类全名';\nCOMMENT ON COLUMN infra_api_error_log.exception_file_name IS '异常发生的类文件';\nCOMMENT ON COLUMN infra_api_error_log.exception_method_name IS '异常发生的方法名';\nCOMMENT ON COLUMN infra_api_error_log.exception_line_number IS '异常发生的方法所在行';\nCOMMENT ON COLUMN infra_api_error_log.process_status IS '处理状态';\nCOMMENT ON COLUMN infra_api_error_log.process_time IS '处理时间';\nCOMMENT ON COLUMN infra_api_error_log.process_user_id IS '处理用户编号';\nCOMMENT ON COLUMN infra_api_error_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_error_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_error_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_error_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_error_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_error_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_error_log IS '系统异常日志';\n\n-- ----------------------------\n-- Table structure for infra_codegen_column\n-- ----------------------------\nCREATE TABLE infra_codegen_column\n(\n    id                       bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    table_id                 bigint                                 NOT NULL,\n    column_name              varchar(200)                           NOT NULL,\n    data_type                varchar(100)                           NOT NULL,\n    column_comment           varchar(500)                           NOT NULL,\n    nullable                 bit                                    NOT NULL,\n    primary_key              bit                                    NOT NULL,\n    ordinal_position         int                                    NOT NULL,\n    java_type                varchar(32)                            NOT NULL,\n    java_field               varchar(64)                            NOT NULL,\n    dict_type                varchar(200) DEFAULT ''                NULL,\n    example                  varchar(64)  DEFAULT NULL              NULL,\n    create_operation         bit                                    NOT NULL,\n    update_operation         bit                                    NOT NULL,\n    list_operation           bit                                    NOT NULL,\n    list_operation_condition varchar(32)  DEFAULT '='               NOT NULL,\n    list_operation_result    bit                                    NOT NULL,\n    html_type                varchar(32)                            NOT NULL,\n    creator                  varchar(64)  DEFAULT ''                NULL,\n    create_time              datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater                  varchar(64)  DEFAULT ''                NULL,\n    update_time              datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted                  bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN infra_codegen_column.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_column.table_id IS '表编号';\nCOMMENT ON COLUMN infra_codegen_column.column_name IS '字段名';\nCOMMENT ON COLUMN infra_codegen_column.data_type IS '字段类型';\nCOMMENT ON COLUMN infra_codegen_column.column_comment IS '字段描述';\nCOMMENT ON COLUMN infra_codegen_column.nullable IS '是否允许为空';\nCOMMENT ON COLUMN infra_codegen_column.primary_key IS '是否主键';\nCOMMENT ON COLUMN infra_codegen_column.ordinal_position IS '排序';\nCOMMENT ON COLUMN infra_codegen_column.java_type IS 'Java 属性类型';\nCOMMENT ON COLUMN infra_codegen_column.java_field IS 'Java 属性名';\nCOMMENT ON COLUMN infra_codegen_column.dict_type IS '字典类型';\nCOMMENT ON COLUMN infra_codegen_column.example IS '数据示例';\nCOMMENT ON COLUMN infra_codegen_column.create_operation IS '是否为 Create 创建操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.update_operation IS '是否为 Update 更新操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation IS '是否为 List 查询操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_condition IS 'List 查询操作的条件类型';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_result IS '是否为 List 查询操作的返回字段';\nCOMMENT ON COLUMN infra_codegen_column.html_type IS '显示类型';\nCOMMENT ON COLUMN infra_codegen_column.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_column.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_column.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_column.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_column.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_column IS '代码生成表字段定义';\n\n-- ----------------------------\n-- Table structure for infra_codegen_table\n-- ----------------------------\nCREATE TABLE infra_codegen_table\n(\n    id                    bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    data_source_config_id bigint                                 NOT NULL,\n    scene                 smallint     DEFAULT 1                 NOT NULL,\n    table_name            varchar(200) DEFAULT ''                NULL,\n    table_comment         varchar(500) DEFAULT ''                NULL,\n    remark                varchar(500) DEFAULT NULL              NULL,\n    module_name           varchar(30)                            NOT NULL,\n    business_name         varchar(30)                            NOT NULL,\n    class_name            varchar(100) DEFAULT ''                NULL,\n    class_comment         varchar(50)                            NOT NULL,\n    author                varchar(50)                            NOT NULL,\n    template_type         smallint     DEFAULT 1                 NOT NULL,\n    front_type            smallint                               NOT NULL,\n    parent_menu_id        bigint       DEFAULT NULL              NULL,\n    master_table_id       bigint       DEFAULT NULL              NULL,\n    sub_join_column_id    bigint       DEFAULT NULL              NULL,\n    sub_join_many         bit          DEFAULT NULL              NULL,\n    tree_parent_column_id bigint       DEFAULT NULL              NULL,\n    tree_name_column_id   bigint       DEFAULT NULL              NULL,\n    creator               varchar(64)  DEFAULT ''                NULL,\n    create_time           datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater               varchar(64)  DEFAULT ''                NULL,\n    update_time           datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted               bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN infra_codegen_table.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_table.data_source_config_id IS '数据源配置的编号';\nCOMMENT ON COLUMN infra_codegen_table.scene IS '生成场景';\nCOMMENT ON COLUMN infra_codegen_table.table_name IS '表名称';\nCOMMENT ON COLUMN infra_codegen_table.table_comment IS '表描述';\nCOMMENT ON COLUMN infra_codegen_table.remark IS '备注';\nCOMMENT ON COLUMN infra_codegen_table.module_name IS '模块名';\nCOMMENT ON COLUMN infra_codegen_table.business_name IS '业务名';\nCOMMENT ON COLUMN infra_codegen_table.class_name IS '类名称';\nCOMMENT ON COLUMN infra_codegen_table.class_comment IS '类描述';\nCOMMENT ON COLUMN infra_codegen_table.author IS '作者';\nCOMMENT ON COLUMN infra_codegen_table.template_type IS '模板类型';\nCOMMENT ON COLUMN infra_codegen_table.front_type IS '前端类型';\nCOMMENT ON COLUMN infra_codegen_table.parent_menu_id IS '父菜单编号';\nCOMMENT ON COLUMN infra_codegen_table.master_table_id IS '主表的编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_column_id IS '子表关联主表的字段编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_many IS '主表与子表是否一对多';\nCOMMENT ON COLUMN infra_codegen_table.tree_parent_column_id IS '树表的父字段编号';\nCOMMENT ON COLUMN infra_codegen_table.tree_name_column_id IS '树表的名字字段编号';\nCOMMENT ON COLUMN infra_codegen_table.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_table.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_table.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_table.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_table.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_table IS '代码生成表定义';\n\n-- ----------------------------\n-- Table structure for infra_config\n-- ----------------------------\nCREATE TABLE infra_config\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    category    varchar(50)                            NOT NULL,\n    type        smallint                               NOT NULL,\n    name        varchar(100) DEFAULT ''                NULL,\n    config_key  varchar(100) DEFAULT ''                NULL,\n    value       varchar(500) DEFAULT ''                NULL,\n    visible     bit                                    NOT NULL,\n    remark      varchar(500) DEFAULT NULL              NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN infra_config.id IS '参数主键';\nCOMMENT ON COLUMN infra_config.category IS '参数分组';\nCOMMENT ON COLUMN infra_config.type IS '参数类型';\nCOMMENT ON COLUMN infra_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_config.config_key IS '参数键名';\nCOMMENT ON COLUMN infra_config.value IS '参数键值';\nCOMMENT ON COLUMN infra_config.visible IS '是否可见';\nCOMMENT ON COLUMN infra_config.remark IS '备注';\nCOMMENT ON COLUMN infra_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_config IS '参数配置表';\n\n-- ----------------------------\n-- Records of infra_config\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT infra_config ON;\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'system.user.init-password', '123456', '0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-07-20 17:22:47', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:33:38', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:57:03', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:52:07', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (10, 'url', 2, 'Swagger 接口文档的地址', 'url.swagger', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:59:00', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (11, 'ui', 2, '腾讯地图 key', 'tencent.lbs.key', 'TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E', '1', '腾讯地图 key', '1', '2023-06-03 19:16:27', '1', '2023-06-03 19:16:27', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 'test2', 2, 'test3', 'test4', 'test5', '1', 'test6', '1', '2023-12-03 09:55:16', '1', '2025-04-06 21:00:09', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '用户管理-账号初始密码', 2, '用户管理-注册开关', 'system.user.register-enabled', 'true', '0', '', '1', '2025-04-26 17:23:41', '1', '2025-04-26 17:23:41', '0');\nCOMMIT;\nSET IDENTITY_INSERT infra_config OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_data_source_config\n-- ----------------------------\nCREATE TABLE infra_data_source_config\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name        varchar(100) DEFAULT ''                NULL,\n    url         varchar(1024)                          NOT NULL,\n    username    varchar(255)                           NOT NULL,\n    password    varchar(255) DEFAULT ''                NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN infra_data_source_config.id IS '主键编号';\nCOMMENT ON COLUMN infra_data_source_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_data_source_config.url IS '数据源连接';\nCOMMENT ON COLUMN infra_data_source_config.username IS '用户名';\nCOMMENT ON COLUMN infra_data_source_config.password IS '密码';\nCOMMENT ON COLUMN infra_data_source_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_data_source_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_data_source_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_data_source_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_data_source_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_data_source_config IS '数据源配置表';\n\n-- ----------------------------\n-- Table structure for infra_file\n-- ----------------------------\nCREATE TABLE infra_file\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    config_id   bigint       DEFAULT NULL              NULL,\n    name        varchar(256) DEFAULT NULL              NULL,\n    path        varchar(512)                           NOT NULL,\n    url         varchar(1024)                          NOT NULL,\n    type        varchar(128) DEFAULT NULL              NULL,\n    size        int                                    NOT NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN infra_file.id IS '文件编号';\nCOMMENT ON COLUMN infra_file.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file.name IS '文件名';\nCOMMENT ON COLUMN infra_file.path IS '文件路径';\nCOMMENT ON COLUMN infra_file.url IS '文件 URL';\nCOMMENT ON COLUMN infra_file.type IS '文件类型';\nCOMMENT ON COLUMN infra_file.size IS '文件大小';\nCOMMENT ON COLUMN infra_file.creator IS '创建者';\nCOMMENT ON COLUMN infra_file.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file.updater IS '更新者';\nCOMMENT ON COLUMN infra_file.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file IS '文件表';\n\n-- ----------------------------\n-- Table structure for infra_file_config\n-- ----------------------------\nCREATE TABLE infra_file_config\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name        varchar(63)                            NOT NULL,\n    storage     smallint                               NOT NULL,\n    remark      varchar(255) DEFAULT NULL              NULL,\n    master      bit                                    NOT NULL,\n    config      varchar(4096)                          NOT NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN infra_file_config.id IS '编号';\nCOMMENT ON COLUMN infra_file_config.name IS '配置名';\nCOMMENT ON COLUMN infra_file_config.storage IS '存储器';\nCOMMENT ON COLUMN infra_file_config.remark IS '备注';\nCOMMENT ON COLUMN infra_file_config.master IS '是否为主配置';\nCOMMENT ON COLUMN infra_file_config.config IS '存储配置';\nCOMMENT ON COLUMN infra_file_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_config IS '文件配置表';\n\n-- ----------------------------\n-- Records of infra_file_config\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT infra_file_config ON;\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (4, '数据库（示例）', 1, '我是数据库', '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-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (22, '七牛存储器（示例）', 20, '请换成你自己的密钥！！！', '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}', '1', '2024-01-13 22:11:12', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (24, '腾讯云存储（示例）', 20, '请换成你的密钥！！！', '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\"}', '1', '2024-11-09 16:03:22', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (25, '阿里云存储（示例）', 20, '', '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}', '1', '2024-11-09 16:47:08', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (26, '火山云存储（示例）', 20, '', '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}', '1', '2024-11-09 16:56:42', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (27, '华为云存储（示例）', 20, '', '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}', '1', '2024-11-09 17:18:41', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (28, 'MinIO 存储（示例）', 20, '', '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}', '1', '2024-11-09 17:43:10', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (29, '本地存储（示例）', 10, '仅适合 mac 或 windows', '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-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (30, 'SFTP 存储（示例）', 12, '', '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-05-02 18:30:28', '0');\nCOMMIT;\nSET IDENTITY_INSERT infra_file_config OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_file_content\n-- ----------------------------\nCREATE TABLE infra_file_content\n(\n    id          bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    config_id   bigint                                NOT NULL,\n    path        varchar(512)                          NOT NULL,\n    content     blob                                  NOT NULL,\n    creator     varchar(64) DEFAULT ''                NULL,\n    create_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64) DEFAULT ''                NULL,\n    update_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit         DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN infra_file_content.id IS '编号';\nCOMMENT ON COLUMN infra_file_content.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file_content.path IS '文件路径';\nCOMMENT ON COLUMN infra_file_content.content IS '文件内容';\nCOMMENT ON COLUMN infra_file_content.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_content.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_content.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_content.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_content.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_content IS '文件表';\n\n-- ----------------------------\n-- Table structure for infra_job\n-- ----------------------------\nCREATE TABLE infra_job\n(\n    id              bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name            varchar(32)                            NOT NULL,\n    status          smallint                               NOT NULL,\n    handler_name    varchar(64)                            NOT NULL,\n    handler_param   varchar(255) DEFAULT NULL              NULL,\n    cron_expression varchar(32)                            NOT NULL,\n    retry_count     int          DEFAULT 0                 NOT NULL,\n    retry_interval  int          DEFAULT 0                 NOT NULL,\n    monitor_timeout int          DEFAULT 0                 NOT NULL,\n    creator         varchar(64)  DEFAULT ''                NULL,\n    create_time     datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         varchar(64)  DEFAULT ''                NULL,\n    update_time     datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN infra_job.id IS '任务编号';\nCOMMENT ON COLUMN infra_job.name IS '任务名称';\nCOMMENT ON COLUMN infra_job.status IS '任务状态';\nCOMMENT ON COLUMN infra_job.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job.cron_expression IS 'CRON 表达式';\nCOMMENT ON COLUMN infra_job.retry_count IS '重试次数';\nCOMMENT ON COLUMN infra_job.retry_interval IS '重试间隔';\nCOMMENT ON COLUMN infra_job.monitor_timeout IS '监控超时时间';\nCOMMENT ON COLUMN infra_job.creator IS '创建者';\nCOMMENT ON COLUMN infra_job.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job.updater IS '更新者';\nCOMMENT ON COLUMN infra_job.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job IS '定时任务表';\n\n-- ----------------------------\n-- Records of infra_job\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT infra_job ON;\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (5, '支付通知 Job', 2, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', '2021-10-27 08:34:42', '1', '2024-09-12 13:32:48', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (17, '支付订单同步 Job', 2, 'payOrderSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 14:36:26', '1', '2023-07-22 15:39:08', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (18, '支付订单过期 Job', 2, 'payOrderExpireJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 15:36:23', '1', '2023-07-22 15:39:54', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (19, '退款订单的同步 Job', 2, 'payRefundSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-23 21:03:44', '1', '2023-07-23 21:09:00', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (21, '交易订单的自动过期 Job', 2, 'tradeOrderAutoCancelJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-25 23:43:26', '1', '2023-09-26 19:23:30', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (22, '交易订单的自动收货 Job', 2, 'tradeOrderAutoReceiveJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 19:23:53', '1', '2023-09-26 23:38:08', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (23, '交易订单的自动评论 Job', 2, 'tradeOrderAutoCommentJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 23:38:29', '1', '2023-09-27 11:03:10', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (24, '佣金解冻 Job', 2, 'brokerageRecordUnfreezeJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-28 22:01:46', '1', '2023-09-28 22:01:56', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (25, '访问日志清理 Job', 2, 'accessLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 10:59:41', '1', '2023-10-03 11:01:10', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (26, '错误日志清理 Job', 2, 'errorLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:00:43', '1', '2023-10-03 11:01:12', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (27, '任务日志清理 Job', 2, 'jobLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:01:33', '1', '2024-09-12 13:40:34', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (33, 'demoJob', 2, 'demoJob', '', '0 * * * * ?', 1, 1, 0, '1', '2024-10-27 19:38:46', '1', '2025-05-10 18:13:54', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (35, '转账订单的同步 Job', 2, 'payTransferSyncJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-05-10 17:35:54', '1', '2025-05-10 18:13:52', '0');\nCOMMIT;\nSET IDENTITY_INSERT infra_job OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_job_log\n-- ----------------------------\nCREATE TABLE infra_job_log\n(\n    id            bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    job_id        bigint                                  NOT NULL,\n    handler_name  varchar(64)                             NOT NULL,\n    handler_param varchar(255)  DEFAULT NULL              NULL,\n    execute_index smallint      DEFAULT 1                 NOT NULL,\n    begin_time    datetime                                NOT NULL,\n    end_time      datetime      DEFAULT NULL              NULL,\n    duration      int           DEFAULT NULL              NULL,\n    status        smallint                                NOT NULL,\n    result        varchar(4000) DEFAULT ''                NULL,\n    creator       varchar(64)   DEFAULT ''                NULL,\n    create_time   datetime      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       varchar(64)   DEFAULT ''                NULL,\n    update_time   datetime      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       bit           DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN infra_job_log.id IS '日志编号';\nCOMMENT ON COLUMN infra_job_log.job_id IS '任务编号';\nCOMMENT ON COLUMN infra_job_log.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job_log.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job_log.execute_index IS '第几次执行';\nCOMMENT ON COLUMN infra_job_log.begin_time IS '开始执行时间';\nCOMMENT ON COLUMN infra_job_log.end_time IS '结束执行时间';\nCOMMENT ON COLUMN infra_job_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_job_log.status IS '任务状态';\nCOMMENT ON COLUMN infra_job_log.result IS '结果数据';\nCOMMENT ON COLUMN infra_job_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_job_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_job_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job_log.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job_log IS '定时任务日志表';\n\n-- ----------------------------\n-- Table structure for system_dept\n-- ----------------------------\nCREATE TABLE system_dept\n(\n    id             bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    name           varchar(30) DEFAULT ''                NULL,\n    parent_id      bigint      DEFAULT 0                 NOT NULL,\n    sort           int         DEFAULT 0                 NOT NULL,\n    leader_user_id bigint      DEFAULT NULL              NULL,\n    phone          varchar(11) DEFAULT NULL              NULL,\n    email          varchar(50) DEFAULT NULL              NULL,\n    status         smallint                              NOT NULL,\n    creator        varchar(64) DEFAULT ''                NULL,\n    create_time    datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar(64) DEFAULT ''                NULL,\n    update_time    datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit         DEFAULT '0'               NOT NULL,\n    tenant_id      bigint      DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_dept.id IS '部门id';\nCOMMENT ON COLUMN system_dept.name IS '部门名称';\nCOMMENT ON COLUMN system_dept.parent_id IS '父部门id';\nCOMMENT ON COLUMN system_dept.sort IS '显示顺序';\nCOMMENT ON COLUMN system_dept.leader_user_id IS '负责人';\nCOMMENT ON COLUMN system_dept.phone IS '联系电话';\nCOMMENT ON COLUMN system_dept.email IS '邮箱';\nCOMMENT ON COLUMN system_dept.status IS '部门状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dept.creator IS '创建者';\nCOMMENT ON COLUMN system_dept.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dept.updater IS '更新者';\nCOMMENT ON COLUMN system_dept.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dept.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dept.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_dept IS '部门表';\n\n-- ----------------------------\n-- Records of system_dept\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_dept ON;\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, '芋道源码', 0, 0, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2025-03-29 15:47:53', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (101, '深圳总公司', 100, 1, 104, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2025-03-29 15:49:55', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (102, '长沙分公司', 100, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:40', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, '研发部门', 101, 1, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2024-10-02 10:22:03', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, '市场部门', 101, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:38', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (105, '测试部门', 101, 3, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-05-16 20:25:15', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (106, '财务部门', 101, 4, 103, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '103', '2022-01-15 21:32:22', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (107, '运维部门', 101, 5, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2023-12-02 09:28:22', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (108, '市场部门', 102, 1, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-02-16 08:35:45', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (109, '财务部门', 102, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:29', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (110, '新部门', 0, 1, NULL, NULL, NULL, 0, '110', '2022-02-23 20:46:30', '110', '2022-02-23 20:46:30', '0', 121);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (111, '顶级部门', 0, 1, NULL, NULL, NULL, 0, '113', '2022-03-07 21:44:50', '113', '2022-03-07 21:44:50', '0', 122);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, '产品部门', 101, 100, 1, NULL, NULL, 1, '1', '2023-12-02 09:45:13', '1', '2023-12-02 09:45:31', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, '支持部门', 102, 3, 104, NULL, NULL, 1, '1', '2023-12-02 09:47:38', '1', '2025-03-29 15:00:56', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT system_dept OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_dict_data\n-- ----------------------------\nCREATE TABLE system_dict_data\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    sort        int          DEFAULT 0                 NOT NULL,\n    label       varchar(100) DEFAULT ''                NULL,\n    value       varchar(100) DEFAULT ''                NULL,\n    dict_type   varchar(100) DEFAULT ''                NULL,\n    status      smallint     DEFAULT 0                 NOT NULL,\n    color_type  varchar(100) DEFAULT ''                NULL,\n    css_class   varchar(100) DEFAULT ''                NULL,\n    remark      varchar(500) DEFAULT NULL              NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_dict_data.id IS '字典编码';\nCOMMENT ON COLUMN system_dict_data.sort IS '字典排序';\nCOMMENT ON COLUMN system_dict_data.label IS '字典标签';\nCOMMENT ON COLUMN system_dict_data.value IS '字典键值';\nCOMMENT ON COLUMN system_dict_data.dict_type IS '字典类型';\nCOMMENT ON COLUMN system_dict_data.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_data.color_type IS '颜色类型';\nCOMMENT ON COLUMN system_dict_data.css_class IS 'css 样式';\nCOMMENT ON COLUMN system_dict_data.remark IS '备注';\nCOMMENT ON COLUMN system_dict_data.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_data.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_data.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_data.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_data.deleted IS '是否删除';\nCOMMENT ON TABLE system_dict_data IS '字典数据表';\n\n-- ----------------------------\n-- Records of system_dict_data\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_dict_data ON;\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1, 1, '男', '1', 'system_user_sex', 0, 'default', 'A', '性别男', 'admin', '2021-01-05 17:03:48', '1', '2022-03-29 00:14:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 2, '女', '2', 'system_user_sex', 0, 'success', '', '性别女', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 23:30:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 1, '正常', '1', 'infra_job_status', 0, 'success', '', '正常状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 2, '暂停', '2', 'infra_job_status', 0, 'danger', '', '停用状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 1, '系统内置', '1', 'infra_config_type', 0, 'danger', '', '参数类型 - 系统内置', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (13, 2, '自定义', '2', 'infra_config_type', 0, 'primary', '', '参数类型 - 自定义', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (14, 1, '通知', '1', 'system_notice_type', 0, 'success', '', '通知', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:05:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (15, 2, '公告', '2', 'system_notice_type', 0, 'info', '', '公告', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:06:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (16, 0, '其它', '0', 'infra_operate_type', 0, 'default', '', '其它操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (17, 1, '查询', '1', 'infra_operate_type', 0, 'info', '', '查询操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (18, 2, '新增', '2', 'infra_operate_type', 0, 'primary', '', '新增操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (19, 3, '修改', '3', 'infra_operate_type', 0, 'warning', '', '修改操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (20, 4, '删除', '4', 'infra_operate_type', 0, 'danger', '', '删除操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (22, 5, '导出', '5', 'infra_operate_type', 0, 'default', '', '导出操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (23, 6, '导入', '6', 'infra_operate_type', 0, 'default', '', '导入操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (27, 1, '开启', '0', 'common_status', 0, 'primary', '', '开启状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (28, 2, '关闭', '1', 'common_status', 0, 'info', '', '关闭状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (29, 1, '目录', '1', 'system_menu_type', 0, '', '', '目录', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (30, 2, '菜单', '2', 'system_menu_type', 0, '', '', '菜单', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (31, 3, '按钮', '3', 'system_menu_type', 0, '', '', '按钮', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (32, 1, '内置', '1', 'system_role_type', 0, 'danger', '', '内置角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (33, 2, '自定义', '2', 'system_role_type', 0, 'primary', '', '自定义角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (34, 1, '全部数据权限', '1', 'system_data_scope', 0, '', '', '全部数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (35, 2, '指定部门数据权限', '2', 'system_data_scope', 0, '', '', '指定部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (36, 3, '本部门数据权限', '3', 'system_data_scope', 0, '', '', '本部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (37, 4, '本部门及以下数据权限', '4', 'system_data_scope', 0, '', '', '本部门及以下数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (38, 5, '仅本人数据权限', '5', 'system_data_scope', 0, '', '', '仅本人数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (39, 0, '成功', '0', 'system_login_result', 0, 'success', '', '登陆结果 - 成功', '', '2021-01-18 06:17:36', '1', '2022-02-16 13:23:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (40, 10, '账号或密码不正确', '10', 'system_login_result', 0, 'primary', '', '登陆结果 - 账号或密码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (41, 20, '用户被禁用', '20', 'system_login_result', 0, 'warning', '', '登陆结果 - 用户被禁用', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:23:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (42, 30, '验证码不存在', '30', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不存在', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (43, 31, '验证码不正确', '31', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (44, 100, '未知异常', '100', 'system_login_result', 0, 'danger', '', '登陆结果 - 未知异常', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (45, 1, '是', 'true', 'infra_boolean_string', 0, 'danger', '', 'Boolean 是否类型 - 是', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:01:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (46, 1, '否', 'false', 'infra_boolean_string', 0, 'info', '', 'Boolean 是否类型 - 否', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:09:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (50, 1, '单表（增删改查）', '1', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:09:06', '', '2022-03-10 16:33:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (51, 2, '树表（增删改查）', '2', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:14:46', '', '2022-03-10 16:33:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (53, 0, '初始化中', '0', 'infra_job_status', 0, 'primary', '', NULL, '', '2021-02-07 07:46:49', '1', '2022-02-16 19:33:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (57, 0, '运行中', '0', 'infra_job_log_status', 0, 'primary', '', 'RUNNING', '', '2021-02-08 10:04:24', '1', '2022-02-16 19:07:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (58, 1, '成功', '1', 'infra_job_log_status', 0, 'success', '', NULL, '', '2021-02-08 10:06:57', '1', '2022-02-16 19:07:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (59, 2, '失败', '2', 'infra_job_log_status', 0, 'warning', '', '失败', '', '2021-02-08 10:07:38', '1', '2022-02-16 19:07:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (60, 1, '会员', '1', 'user_type', 0, 'primary', '', NULL, '', '2021-02-26 00:16:27', '1', '2022-02-16 10:22:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (61, 2, '管理员', '2', 'user_type', 0, 'success', '', NULL, '', '2021-02-26 00:16:34', '1', '2025-04-06 18:37:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (62, 0, '未处理', '0', 'infra_api_error_log_process_status', 0, 'primary', '', NULL, '', '2021-02-26 07:07:19', '1', '2022-02-16 20:14:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (63, 1, '已处理', '1', 'infra_api_error_log_process_status', 0, 'success', '', NULL, '', '2021-02-26 07:07:26', '1', '2022-02-16 20:14:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (64, 2, '已忽略', '2', 'infra_api_error_log_process_status', 0, 'danger', '', NULL, '', '2021-02-26 07:07:34', '1', '2022-02-16 20:14:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (66, 1, '阿里云', 'ALIYUN', 'system_sms_channel_code', 0, 'primary', '', NULL, '1', '2021-04-05 01:05:26', '1', '2024-07-22 22:23:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (67, 1, '验证码', '1', 'system_sms_template_type', 0, 'warning', '', NULL, '1', '2021-04-05 21:50:57', '1', '2022-02-16 12:48:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (68, 2, '通知', '2', 'system_sms_template_type', 0, 'primary', '', NULL, '1', '2021-04-05 21:51:08', '1', '2022-02-16 12:48:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (69, 0, '营销', '3', 'system_sms_template_type', 0, 'danger', '', NULL, '1', '2021-04-05 21:51:15', '1', '2022-02-16 12:48:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (70, 0, '初始化', '0', 'system_sms_send_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:18:33', '1', '2022-02-16 10:26:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (71, 1, '发送成功', '10', 'system_sms_send_status', 0, 'success', '', NULL, '1', '2021-04-11 20:18:43', '1', '2022-02-16 10:25:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (72, 2, '发送失败', '20', 'system_sms_send_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:18:49', '1', '2022-02-16 10:26:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (73, 3, '不发送', '30', 'system_sms_send_status', 0, 'info', '', NULL, '1', '2021-04-11 20:19:44', '1', '2022-02-16 10:26:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (74, 0, '等待结果', '0', 'system_sms_receive_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:27:43', '1', '2022-02-16 10:28:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (75, 1, '接收成功', '10', 'system_sms_receive_status', 0, 'success', '', NULL, '1', '2021-04-11 20:29:25', '1', '2022-02-16 10:28:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (76, 2, '接收失败', '20', 'system_sms_receive_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:29:31', '1', '2022-02-16 10:28:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (77, 0, '调试(钉钉)', 'DEBUG_DING_TALK', 'system_sms_channel_code', 0, 'info', '', NULL, '1', '2021-04-13 00:20:37', '1', '2022-02-16 10:10:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (80, 100, '账号登录', '100', 'system_login_type', 0, 'primary', '', '账号登录', '1', '2021-10-06 00:52:02', '1', '2022-02-16 13:11:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (81, 101, '社交登录', '101', 'system_login_type', 0, 'info', '', '社交登录', '1', '2021-10-06 00:52:17', '1', '2022-02-16 13:11:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (83, 200, '主动登出', '200', 'system_login_type', 0, 'primary', '', '主动登出', '1', '2021-10-06 00:52:58', '1', '2022-02-16 13:11:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (85, 202, '强制登出', '202', 'system_login_type', 0, 'danger', '', '强制退出', '1', '2021-10-06 00:53:41', '1', '2022-02-16 13:11:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (86, 0, '病假', '1', 'bpm_oa_leave_type', 0, 'primary', '', NULL, '1', '2021-09-21 22:35:28', '1', '2022-02-16 10:00:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (87, 1, '事假', '2', 'bpm_oa_leave_type', 0, 'info', '', NULL, '1', '2021-09-21 22:36:11', '1', '2022-02-16 10:00:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (88, 2, '婚假', '3', 'bpm_oa_leave_type', 0, 'warning', '', NULL, '1', '2021-09-21 22:36:38', '1', '2022-02-16 10:00:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (112, 0, '微信 Wap 网站支付', 'wx_wap', 'pay_channel_code', 0, 'success', '', '微信 Wap 网站支付', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (113, 1, '微信公众号支付', 'wx_pub', 'pay_channel_code', 0, 'success', '', '微信公众号支付', '1', '2021-12-03 10:40:24', '1', '2023-07-19 20:08:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (114, 2, '微信小程序支付', 'wx_lite', 'pay_channel_code', 0, 'success', '', '微信小程序支付', '1', '2021-12-03 10:41:06', '1', '2023-07-19 20:08:50', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (115, 3, '微信 App 支付', 'wx_app', 'pay_channel_code', 0, 'success', '', '微信 App 支付', '1', '2021-12-03 10:41:20', '1', '2023-07-19 20:08:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (116, 10, '支付宝 PC 网站支付', 'alipay_pc', 'pay_channel_code', 0, 'primary', '', '支付宝 PC 网站支付', '1', '2021-12-03 10:42:09', '1', '2023-07-19 20:09:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (117, 11, '支付宝 Wap 网站支付', 'alipay_wap', 'pay_channel_code', 0, 'primary', '', '支付宝 Wap 网站支付', '1', '2021-12-03 10:42:26', '1', '2023-07-19 20:09:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (118, 12, '支付宝 App 支付', 'alipay_app', 'pay_channel_code', 0, 'primary', '', '支付宝 App 支付', '1', '2021-12-03 10:42:55', '1', '2023-07-19 20:09:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (119, 14, '支付宝扫码支付', 'alipay_qr', 'pay_channel_code', 0, 'primary', '', '支付宝扫码支付', '1', '2021-12-03 10:43:10', '1', '2023-07-19 20:09:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (120, 10, '通知成功', '10', 'pay_notify_status', 0, 'success', '', '通知成功', '1', '2021-12-03 11:02:41', '1', '2023-07-19 10:08:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (121, 20, '通知失败', '20', 'pay_notify_status', 0, 'danger', '', '通知失败', '1', '2021-12-03 11:02:59', '1', '2023-07-19 10:08:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (122, 0, '等待通知', '0', 'pay_notify_status', 0, 'info', '', '未通知', '1', '2021-12-03 11:03:10', '1', '2023-07-19 10:08:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (123, 10, '支付成功', '10', 'pay_order_status', 0, 'success', '', '支付成功', '1', '2021-12-03 11:18:29', '1', '2023-07-19 18:04:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (124, 30, '支付关闭', '30', 'pay_order_status', 0, 'info', '', '支付关闭', '1', '2021-12-03 11:18:42', '1', '2023-07-19 18:05:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (125, 0, '等待支付', '0', 'pay_order_status', 0, 'info', '', '未支付', '1', '2021-12-03 11:18:18', '1', '2023-07-19 18:04:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (600, 5, '首页', '1', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (601, 4, '秒杀活动页', '2', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (602, 3, '砍价活动页', '3', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (603, 2, '限时折扣页', '4', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (604, 1, '满减送页', '5', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1118, 0, '等待退款', '0', 'pay_refund_status', 0, 'info', '', '等待退款', '1', '2021-12-10 16:44:59', '1', '2023-07-19 10:14:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1119, 20, '退款失败', '20', 'pay_refund_status', 0, 'danger', '', '退款失败', '1', '2021-12-10 16:45:10', '1', '2023-07-19 10:15:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1124, 10, '退款成功', '10', 'pay_refund_status', 0, 'success', '', '退款成功', '1', '2021-12-10 16:46:26', '1', '2023-07-19 10:15:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1127, 1, '审批中', '1', 'bpm_process_instance_status', 0, 'default', '', '流程实例的状态 - 进行中', '1', '2022-01-07 23:47:22', '1', '2024-03-16 16:11:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1128, 2, '审批通过', '2', 'bpm_process_instance_status', 0, 'success', '', '流程实例的状态 - 已完成', '1', '2022-01-07 23:47:49', '1', '2024-03-16 16:11:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1129, 1, '审批中', '1', 'bpm_task_status', 0, 'primary', '', '流程实例的结果 - 处理中', '1', '2022-01-07 23:48:32', '1', '2024-03-08 22:41:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1130, 2, '审批通过', '2', 'bpm_task_status', 0, 'success', '', '流程实例的结果 - 通过', '1', '2022-01-07 23:48:45', '1', '2024-03-08 22:41:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1131, 3, '审批不通过', '3', 'bpm_task_status', 0, 'danger', '', '流程实例的结果 - 不通过', '1', '2022-01-07 23:48:55', '1', '2024-03-08 22:41:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1132, 4, '已取消', '4', 'bpm_task_status', 0, 'info', '', '流程实例的结果 - 撤销', '1', '2022-01-07 23:49:06', '1', '2024-03-08 22:41:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1133, 10, '流程表单', '10', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 流程表单', '103', '2022-01-11 23:51:30', '103', '2022-01-11 23:51:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1134, 20, '业务表单', '20', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 业务表单', '103', '2022-01-11 23:51:47', '103', '2022-01-11 23:51:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1135, 10, '角色', '10', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 角色', '103', '2022-01-12 23:21:22', '1', '2024-03-06 02:53:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1136, 20, '部门的成员', '20', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的成员', '103', '2022-01-12 23:21:47', '1', '2024-03-06 02:53:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1137, 21, '部门的负责人', '21', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的负责人', '103', '2022-01-12 23:33:36', '1', '2024-03-06 02:53:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1138, 30, '用户', '30', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 用户', '103', '2022-01-12 23:34:02', '1', '2024-03-06 02:53:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1139, 40, '用户组', '40', 'bpm_task_candidate_strategy', 0, 'warning', '', '任务分配规则的类型 - 用户组', '103', '2022-01-12 23:34:21', '1', '2024-03-06 02:53:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1140, 60, '流程表达式', '60', 'bpm_task_candidate_strategy', 0, 'danger', '', '任务分配规则的类型 - 流程表达式', '103', '2022-01-12 23:34:43', '1', '2024-03-06 02:53:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1141, 22, '岗位', '22', 'bpm_task_candidate_strategy', 0, 'success', '', '任务分配规则的类型 - 岗位', '103', '2022-01-14 18:41:55', '1', '2024-03-06 02:53:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1145, 1, '管理后台', '1', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 管理后台', '1', '2022-02-02 13:15:06', '1', '2022-03-10 16:32:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1146, 2, '用户 APP', '2', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 用户 APP', '1', '2022-02-02 13:15:19', '1', '2022-03-10 16:33:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1150, 1, '数据库', '1', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:28', '1', '2022-03-15 00:25:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1151, 10, '本地磁盘', '10', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:41', '1', '2022-03-15 00:25:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1152, 11, 'FTP 服务器', '11', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:06', '1', '2022-03-15 00:26:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1153, 12, 'SFTP 服务器', '12', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:22', '1', '2022-03-15 00:26:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1154, 20, 'S3 对象存储', '20', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:31', '1', '2022-03-15 00:26:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1155, 103, '短信登录', '103', 'system_login_type', 0, 'default', '', NULL, '1', '2022-05-09 23:57:58', '1', '2022-05-09 23:58:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1156, 1, 'password', 'password', 'system_oauth2_grant_type', 0, 'default', '', '密码模式', '1', '2022-05-12 00:22:05', '1', '2022-05-11 16:26:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1157, 2, 'authorization_code', 'authorization_code', 'system_oauth2_grant_type', 0, 'primary', '', '授权码模式', '1', '2022-05-12 00:22:59', '1', '2022-05-11 16:26:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1158, 3, 'implicit', 'implicit', 'system_oauth2_grant_type', 0, 'success', '', '简化模式', '1', '2022-05-12 00:23:40', '1', '2022-05-11 16:26:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1159, 4, 'client_credentials', 'client_credentials', 'system_oauth2_grant_type', 0, 'default', '', '客户端模式', '1', '2022-05-12 00:23:51', '1', '2022-05-11 16:26:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1160, 5, 'refresh_token', 'refresh_token', 'system_oauth2_grant_type', 0, 'info', '', '刷新模式', '1', '2022-05-12 00:24:02', '1', '2022-05-11 16:26:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1162, 1, '销售中', '1', 'product_spu_status', 0, 'success', '', '商品 SPU 状态 - 销售中', '1', '2022-10-24 21:19:47', '1', '2022-10-24 21:20:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1163, 0, '仓库中', '0', 'product_spu_status', 0, 'info', '', '商品 SPU 状态 - 仓库中', '1', '2022-10-24 21:20:54', '1', '2022-10-24 21:21:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1164, 0, '回收站', '-1', 'product_spu_status', 0, 'default', '', '商品 SPU 状态 - 回收站', '1', '2022-10-24 21:21:11', '1', '2022-10-24 21:21:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1165, 1, '满减', '1', 'promotion_discount_type', 0, 'success', '', '优惠类型 - 满减', '1', '2022-11-01 12:46:41', '1', '2022-11-01 12:50:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1166, 2, '折扣', '2', 'promotion_discount_type', 0, 'primary', '', '优惠类型 - 折扣', '1', '2022-11-01 12:46:51', '1', '2022-11-01 12:50:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1167, 1, '固定日期', '1', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 固定日期', '1', '2022-11-02 00:07:34', '1', '2022-11-04 00:07:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1168, 2, '领取之后', '2', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 领取之后', '1', '2022-11-02 00:07:54', '1', '2022-11-04 00:07:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1169, 1, '通用劵', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', '2022-11-02 00:28:22', '1', '2023-09-28 00:27:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1170, 2, '商品劵', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', '2022-11-02 00:28:34', '1', '2023-09-28 00:27:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1171, 1, '未使用', '1', 'promotion_coupon_status', 0, 'primary', '', '优惠劵的状态 - 已领取', '1', '2022-11-04 00:15:08', '1', '2023-10-03 12:54:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1172, 2, '已使用', '2', 'promotion_coupon_status', 0, 'success', '', '优惠劵的状态 - 已使用', '1', '2022-11-04 00:15:21', '1', '2022-11-04 19:16:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1173, 3, '已过期', '3', 'promotion_coupon_status', 0, 'info', '', '优惠劵的状态 - 已过期', '1', '2022-11-04 00:15:43', '1', '2022-11-04 19:16:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1174, 1, '直接领取', '1', 'promotion_coupon_take_type', 0, 'primary', '', '优惠劵的领取方式 - 直接领取', '1', '2022-11-04 19:13:00', '1', '2022-11-04 19:13:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1175, 2, '指定发放', '2', 'promotion_coupon_take_type', 0, 'success', '', '优惠劵的领取方式 - 指定发放', '1', '2022-11-04 19:13:13', '1', '2022-11-04 19:14:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1176, 10, '未开始', '10', 'promotion_activity_status', 0, 'primary', '', '促销活动的状态枚举 - 未开始', '1', '2022-11-04 22:54:49', '1', '2022-11-04 22:55:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1177, 20, '进行中', '20', 'promotion_activity_status', 0, 'success', '', '促销活动的状态枚举 - 进行中', '1', '2022-11-04 22:55:06', '1', '2022-11-04 22:55:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1178, 30, '已结束', '30', 'promotion_activity_status', 0, 'info', '', '促销活动的状态枚举 - 已结束', '1', '2022-11-04 22:55:41', '1', '2022-11-04 22:55:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1179, 40, '已关闭', '40', 'promotion_activity_status', 0, 'warning', '', '促销活动的状态枚举 - 已关闭', '1', '2022-11-04 22:56:10', '1', '2022-11-04 22:56:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1180, 10, '满 N 元', '10', 'promotion_condition_type', 0, 'primary', '', '营销的条件类型 - 满 N 元', '1', '2022-11-04 22:59:45', '1', '2022-11-04 22:59:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1181, 20, '满 N 件', '20', 'promotion_condition_type', 0, 'success', '', '营销的条件类型 - 满 N 件', '1', '2022-11-04 23:00:02', '1', '2022-11-04 23:00:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1182, 10, '申请售后', '10', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 申请售后', '1', '2022-11-19 20:53:33', '1', '2022-11-19 20:54:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1183, 20, '商品待退货', '20', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商品待退货', '1', '2022-11-19 20:54:36', '1', '2022-11-19 20:58:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1184, 30, '商家待收货', '30', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商家待收货', '1', '2022-11-19 20:56:56', '1', '2022-11-19 20:59:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1185, 40, '等待退款', '40', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 等待退款', '1', '2022-11-19 20:59:54', '1', '2022-11-19 21:00:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1186, 50, '退款成功', '50', 'trade_after_sale_status', 0, 'default', '', '交易售后状态 - 退款成功', '1', '2022-11-19 21:00:33', '1', '2022-11-19 21:00:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1187, 61, '买家取消', '61', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 买家取消', '1', '2022-11-19 21:01:29', '1', '2022-11-19 21:01:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1188, 62, '商家拒绝', '62', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒绝', '1', '2022-11-19 21:02:17', '1', '2022-11-19 21:02:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1189, 63, '商家拒收货', '63', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒收货', '1', '2022-11-19 21:02:37', '1', '2022-11-19 21:03:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1190, 10, '售中退款', '10', 'trade_after_sale_type', 0, 'success', '', '交易售后的类型 - 售中退款', '1', '2022-11-19 21:05:05', '1', '2022-11-19 21:38:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1191, 20, '售后退款', '20', 'trade_after_sale_type', 0, 'primary', '', '交易售后的类型 - 售后退款', '1', '2022-11-19 21:05:32', '1', '2022-11-19 21:38:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1192, 10, '仅退款', '10', 'trade_after_sale_way', 0, 'primary', '', '交易售后的方式 - 仅退款', '1', '2022-11-19 21:39:19', '1', '2022-11-19 21:39:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1193, 20, '退货退款', '20', 'trade_after_sale_way', 0, 'success', '', '交易售后的方式 - 退货退款', '1', '2022-11-19 21:39:38', '1', '2022-11-19 21:39:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1194, 10, '微信小程序', '10', 'terminal', 0, 'default', '', '终端 - 微信小程序', '1', '2022-12-10 10:51:11', '1', '2022-12-10 10:51:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1195, 20, 'H5 网页', '20', 'terminal', 0, 'default', '', '终端 - H5 网页', '1', '2022-12-10 10:51:30', '1', '2022-12-10 10:51:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1196, 11, '微信公众号', '11', 'terminal', 0, 'default', '', '终端 - 微信公众号', '1', '2022-12-10 10:54:16', '1', '2022-12-10 10:52:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1197, 31, '苹果 App', '31', 'terminal', 0, 'default', '', '终端 - 苹果 App', '1', '2022-12-10 10:54:42', '1', '2022-12-10 10:52:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1198, 32, '安卓 App', '32', 'terminal', 0, 'default', '', '终端 - 安卓 App', '1', '2022-12-10 10:55:02', '1', '2022-12-10 10:59:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1199, 0, '普通订单', '0', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 普通订单', '1', '2022-12-10 16:34:14', '1', '2022-12-10 16:34:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1200, 1, '秒杀订单', '1', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 秒杀订单', '1', '2022-12-10 16:34:26', '1', '2022-12-10 16:34:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1201, 2, '砍价订单', '2', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 拼团订单', '1', '2022-12-10 16:34:36', '1', '2024-09-07 14:18:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1202, 3, '拼团订单', '3', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 砍价订单', '1', '2022-12-10 16:34:48', '1', '2024-09-07 14:18:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1203, 0, '待支付', '0', 'trade_order_status', 0, 'default', '', '交易订单状态 - 待支付', '1', '2022-12-10 16:49:29', '1', '2022-12-10 16:49:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1204, 10, '待发货', '10', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 待发货', '1', '2022-12-10 16:49:53', '1', '2022-12-10 16:51:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1205, 20, '已发货', '20', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 已发货', '1', '2022-12-10 16:50:13', '1', '2022-12-10 16:51:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1206, 30, '已完成', '30', 'trade_order_status', 0, 'success', '', '交易订单状态 - 已完成', '1', '2022-12-10 16:50:30', '1', '2022-12-10 16:51:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1207, 40, '已取消', '40', 'trade_order_status', 0, 'danger', '', '交易订单状态 - 已取消', '1', '2022-12-10 16:50:50', '1', '2022-12-10 16:51:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1208, 0, '未售后', '0', 'trade_order_item_after_sale_status', 0, 'info', '', '交易订单项的售后状态 - 未售后', '1', '2022-12-10 20:58:42', '1', '2022-12-10 20:59:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1209, 10, '售后中', '10', 'trade_order_item_after_sale_status', 0, 'primary', '', '交易订单项的售后状态 - 售后中', '1', '2022-12-10 20:59:21', '1', '2024-07-21 17:01:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1210, 20, '已退款', '20', 'trade_order_item_after_sale_status', 0, 'success', '', '交易订单项的售后状态 - 已退款', '1', '2022-12-10 20:59:46', '1', '2024-07-21 17:01:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1211, 1, '完全匹配', '1', 'mp_auto_reply_request_match', 0, 'primary', '', '公众号自动回复的请求关键字匹配模式 - 完全匹配', '1', '2023-01-16 23:30:39', '1', '2023-01-16 23:31:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1212, 2, '半匹配', '2', 'mp_auto_reply_request_match', 0, 'success', '', '公众号自动回复的请求关键字匹配模式 - 半匹配', '1', '2023-01-16 23:30:55', '1', '2023-01-16 23:31:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1213, 1, '文本', 'text', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 文本', '1', '2023-01-17 22:17:32', '1', '2023-01-17 22:17:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1214, 2, '图片', 'image', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图片', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1215, 3, '语音', 'voice', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 语音', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:20:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1216, 4, '视频', 'video', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:21:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1217, 5, '小视频', 'shortvideo', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 小视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1218, 6, '图文', 'news', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图文', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1219, 7, '音乐', 'music', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 音乐', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1220, 8, '地理位置', 'location', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 地理位置', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:23:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1221, 9, '链接', 'link', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 链接', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1222, 10, '事件', 'event', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 事件', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1223, 0, '初始化', '0', 'system_mail_send_status', 0, 'primary', '', '邮件发送状态 - 初始化\\n', '1', '2023-01-26 09:53:49', '1', '2023-01-26 16:36:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1224, 10, '发送成功', '10', 'system_mail_send_status', 0, 'success', '', '邮件发送状态 - 发送成功', '1', '2023-01-26 09:54:28', '1', '2023-01-26 16:36:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1225, 20, '发送失败', '20', 'system_mail_send_status', 0, 'danger', '', '邮件发送状态 - 发送失败', '1', '2023-01-26 09:54:50', '1', '2023-01-26 16:36:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1226, 30, '不发送', '30', 'system_mail_send_status', 0, 'info', '', '邮件发送状态 -  不发送', '1', '2023-01-26 09:55:06', '1', '2023-01-26 16:36:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1227, 1, '通知公告', '1', 'system_notify_template_type', 0, 'primary', '', '站内信模版的类型 - 通知公告', '1', '2023-01-28 10:35:59', '1', '2023-01-28 10:35:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1228, 2, '系统消息', '2', 'system_notify_template_type', 0, 'success', '', '站内信模版的类型 - 系统消息', '1', '2023-01-28 10:36:20', '1', '2023-01-28 10:36:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1230, 13, '支付宝条码支付', 'alipay_bar', 'pay_channel_code', 0, 'primary', '', '支付宝条码支付', '1', '2023-02-18 23:32:24', '1', '2023-07-19 20:09:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1231, 10, 'Vue2 Element UI 标准模版', '10', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:03:55', '1', '2023-04-13 00:03:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1232, 20, 'Vue3 Element Plus 标准模版', '20', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:08', '1', '2023-04-13 00:04:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1234, 30, 'Vben2.0 Ant Design Schema 模版', '30', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:26', '1', '2025-04-23 21:27:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1244, 0, '按件', '1', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:40', '1', '2023-05-21 22:46:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1245, 1, '按重量', '2', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:58', '1', '2023-05-21 22:46:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1246, 2, '按体积', '3', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:47:18', '1', '2023-05-21 22:47:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1335, 11, '订单积分抵扣', '11', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:27', '1', '2023-10-11 07:41:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1336, 1, '签到', '1', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:48', '1', '2023-08-20 11:59:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1341, 20, '已退款', '20', 'pay_order_status', 0, 'danger', '', '已退款', '1', '2023-07-19 18:05:37', '1', '2023-07-19 18:05:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1342, 21, '请求成功，但是结果失败', '21', 'pay_notify_status', 0, 'warning', '', '请求成功，但是结果失败', '1', '2023-07-19 18:10:47', '1', '2023-07-19 18:11:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1343, 22, '请求失败', '22', 'pay_notify_status', 0, 'warning', '', NULL, '1', '2023-07-19 18:11:05', '1', '2023-07-19 18:11:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1344, 4, '微信扫码支付', 'wx_native', 'pay_channel_code', 0, 'success', '', '微信扫码支付', '1', '2023-07-19 20:07:47', '1', '2023-07-19 20:09:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1345, 5, '微信条码支付', 'wx_bar', 'pay_channel_code', 0, 'success', '', '微信条码支付\\n', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1346, 1, '支付单', '1', 'pay_notify_type', 0, 'primary', '', '支付单', '1', '2023-07-20 12:23:17', '1', '2023-07-20 12:23:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1347, 2, '退款单', '2', 'pay_notify_type', 0, 'danger', '', NULL, '1', '2023-07-20 12:23:26', '1', '2023-07-20 12:23:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1348, 20, '模拟支付', 'mock', 'pay_channel_code', 0, 'default', '', '模拟支付', '1', '2023-07-29 11:10:51', '1', '2023-07-29 03:14:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1349, 12, '订单积分抵扣（整单取消）', '12', 'member_point_biz_type', 0, '', '', '', '1', '2023-08-20 12:00:03', '1', '2023-10-11 07:42:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1350, 0, '管理员调整', '0', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1351, 1, '邀新奖励', '1', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1352, 11, '下单奖励', '11', 'member_experience_biz_type', 0, 'success', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1353, 12, '下单奖励（整单取消）', '12', 'member_experience_biz_type', 0, 'warning', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1354, 4, '签到奖励', '4', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1355, 5, '抽奖奖励', '5', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1356, 1, '快递发货', '1', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:04:55', '1', '2023-08-23 00:04:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1357, 2, '用户自提', '2', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:05:05', '1', '2023-08-23 00:05:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1358, 3, '品类劵', '3', 'promotion_product_scope', 0, 'default', '', '', '1', '2023-09-01 23:43:07', '1', '2023-09-28 00:27:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1359, 1, '人人分销', '1', 'brokerage_enabled_condition', 0, '', '', '所有用户都可以分销', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1360, 2, '指定分销', '2', 'brokerage_enabled_condition', 0, '', '', '仅可后台手动设置推广员', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1361, 1, '首次绑定', '1', 'brokerage_bind_mode', 0, '', '', '只要用户没有推广人，随时都可以绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1362, 2, '注册绑定', '2', 'brokerage_bind_mode', 0, '', '', '仅新用户注册时才能绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1363, 3, '覆盖绑定', '3', 'brokerage_bind_mode', 0, '', '', '如果用户已经有推广人，推广人会被变更', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1364, 1, '钱包', '1', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1365, 2, '银行卡', '2', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1366, 3, '微信收款码', '3', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1367, 4, '支付宝收款码', '4', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1368, 1, '订单返佣', '1', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1369, 2, '申请提现', '2', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1370, 3, '申请提现驳回', '3', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1371, 0, '待结算', '0', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1372, 1, '已结算', '1', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1373, 2, '已取消', '2', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1374, 0, '审核中', '0', 'brokerage_withdraw_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1375, 10, '审核通过', '10', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1376, 11, '提现成功', '11', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1377, 20, '审核不通过', '20', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1378, 21, '提现失败', '21', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1379, 0, '工商银行', '0', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1380, 1, '建设银行', '1', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1381, 2, '农业银行', '2', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1382, 3, '中国银行', '3', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1383, 4, '交通银行', '4', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1384, 5, '招商银行', '5', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1385, 21, '钱包', 'wallet', 'pay_channel_code', 0, 'primary', '', '', '1', '2023-10-01 21:46:19', '1', '2023-10-01 21:48:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1386, 1, '砍价中', '1', 'promotion_bargain_record_status', 0, 'default', '', '', '1', '2023-10-05 10:41:26', '1', '2023-10-05 10:41:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1387, 2, '砍价成功', '2', 'promotion_bargain_record_status', 0, 'success', '', '', '1', '2023-10-05 10:41:39', '1', '2023-10-05 10:41:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1388, 3, '砍价失败', '3', 'promotion_bargain_record_status', 0, 'warning', '', '', '1', '2023-10-05 10:41:57', '1', '2023-10-05 10:41:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1389, 0, '拼团中', '0', 'promotion_combination_record_status', 0, '', '', '', '1', '2023-10-08 07:24:44', '1', '2024-10-13 10:08:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1390, 1, '拼团成功', '1', 'promotion_combination_record_status', 0, 'success', '', '', '1', '2023-10-08 07:24:56', '1', '2024-10-13 10:08:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1391, 2, '拼团失败', '2', 'promotion_combination_record_status', 0, 'warning', '', '', '1', '2023-10-08 07:25:11', '1', '2024-10-13 10:08:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1392, 2, '管理员修改', '2', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:41:34', '1', '2023-10-11 07:41:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1393, 13, '订单积分抵扣（单个退款）', '13', 'member_point_biz_type', 0, '', '', '', '1', '2023-10-11 07:42:29', '1', '2023-10-11 07:42:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1394, 21, '订单积分奖励', '21', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:44', '1', '2023-10-11 07:42:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1395, 22, '订单积分奖励（整单取消）', '22', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:55', '1', '2023-10-11 07:43:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1396, 23, '订单积分奖励（单个退款）', '23', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:43:16', '1', '2023-10-11 07:43:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1397, 13, '下单奖励（单个退款）', '13', 'member_experience_biz_type', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1398, 5, '网上转账', '5', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:24', '1', '2023-10-18 21:55:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1399, 6, '支付宝', '6', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:38', '1', '2023-10-18 21:55:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1400, 7, '微信支付', '7', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:53', '1', '2023-10-18 21:55:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1401, 8, '其他', '8', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:56:06', '1', '2023-10-18 21:56:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1402, 1, 'IT', '1', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:15', '1', '2024-02-18 23:30:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1403, 2, '金融业', '2', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:29', '1', '2024-02-18 23:30:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1404, 3, '房地产', '3', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:41', '1', '2024-02-18 23:30:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1405, 4, '商业服务', '4', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:54', '1', '2024-02-18 23:30:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1406, 5, '运输/物流', '5', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:03', '1', '2024-02-18 23:31:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1407, 6, '生产', '6', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:13', '1', '2024-02-18 23:31:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1408, 7, '政府', '7', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:27', '1', '2024-02-18 23:31:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1409, 8, '文化传媒', '8', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:37', '1', '2024-02-18 23:31:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1422, 1, 'A （重点客户）', '1', 'crm_customer_level', 0, 'primary', '', '', '1', '2023-10-28 23:07:13', '1', '2023-10-28 23:07:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1423, 2, 'B （普通客户）', '2', 'crm_customer_level', 0, 'info', '', '', '1', '2023-10-28 23:07:35', '1', '2023-10-28 23:07:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1424, 3, 'C （非优先客户）', '3', 'crm_customer_level', 0, 'default', '', '', '1', '2023-10-28 23:07:53', '1', '2023-10-28 23:07:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1425, 1, '促销', '1', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:29', '1', '2023-10-28 23:08:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1426, 2, '搜索引擎', '2', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:39', '1', '2023-10-28 23:08:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1427, 3, '广告', '3', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:47', '1', '2023-10-28 23:08:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1428, 4, '转介绍', '4', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:58', '1', '2023-10-28 23:08:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1429, 5, '线上注册', '5', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:12', '1', '2023-10-28 23:09:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1430, 6, '线上咨询', '6', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:22', '1', '2023-10-28 23:09:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1431, 7, '预约上门', '7', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:39', '1', '2023-10-28 23:09:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1432, 8, '陌拜', '8', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:04', '1', '2023-10-28 23:10:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1433, 9, '电话咨询', '9', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:18', '1', '2023-10-28 23:10:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1434, 10, '邮件咨询', '10', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:33', '1', '2023-10-28 23:10:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1435, 10, 'Gitee', '10', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:42', '1', '2023-11-04 13:04:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1436, 20, '钉钉', '20', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:54', '1', '2023-11-04 13:04:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1437, 30, '企业微信', '30', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:09', '1', '2023-11-04 13:05:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1438, 31, '微信公众平台', '31', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:18', '1', '2023-11-04 13:05:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1439, 32, '微信开放平台', '32', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:30', '1', '2023-11-04 13:05:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1440, 34, '微信小程序', '34', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1441, 1, '上架', '1', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:34', '1', '2023-10-30 21:49:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1442, 0, '下架', '0', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:13', '1', '2023-10-30 21:49:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1443, 15, '子表', '15', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-13 23:06:16', '1', '2023-11-13 23:06:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1444, 10, '主表（标准模式）', '10', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:32:49', '1', '2023-11-14 12:32:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1445, 11, '主表（ERP 模式）', '11', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:33:05', '1', '2023-11-14 12:33:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1446, 12, '主表（内嵌模式）', '12', 'infra_codegen_template_type', 0, '', '', '', '1', '2023-11-14 12:33:31', '1', '2023-11-14 12:33:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1447, 1, '负责人', '1', 'crm_permission_level', 0, 'default', '', '', '1', '2023-11-30 09:53:12', '1', '2023-11-30 09:53:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1448, 2, '只读', '2', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:29', '1', '2023-11-30 09:53:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1449, 3, '读写', '3', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:36', '1', '2023-11-30 09:53:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1450, 0, '未提交', '0', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:56:59', '1', '2023-11-30 18:56:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1451, 10, '审批中', '10', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:10', '1', '2023-11-30 18:57:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1452, 20, '审核通过', '20', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:24', '1', '2023-11-30 18:57:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1453, 30, '审核不通过', '30', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:32', '1', '2023-11-30 18:57:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1454, 40, '已取消', '40', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:42', '1', '2023-11-30 18:57:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1456, 1, '支票', '1', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:29', '1', '2023-10-18 21:54:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1457, 2, '现金', '2', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:41', '1', '2023-10-18 21:54:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1458, 3, '邮政汇款', '3', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:53', '1', '2023-10-18 21:54:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1459, 4, '电汇', '4', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:07', '1', '2023-10-18 21:55:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1461, 1, '个', '1', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:26', '1', '2023-12-05 23:02:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1462, 2, '块', '2', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:34', '1', '2023-12-05 23:02:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1463, 3, '只', '3', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:57', '1', '2023-12-05 23:02:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1464, 4, '把', '4', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:05', '1', '2023-12-05 23:03:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1465, 5, '枚', '5', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:14', '1', '2023-12-05 23:03:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1466, 6, '瓶', '6', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:20', '1', '2023-12-05 23:03:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1467, 7, '盒', '7', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:30', '1', '2023-12-05 23:03:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1468, 8, '台', '8', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:41', '1', '2023-12-05 23:03:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1469, 9, '吨', '9', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:48', '1', '2023-12-05 23:03:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1470, 10, '千克', '10', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:03', '1', '2023-12-05 23:04:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1471, 11, '米', '11', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:12', '1', '2023-12-05 23:04:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1472, 12, '箱', '12', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:25', '1', '2023-12-05 23:04:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1473, 13, '套', '13', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:34', '1', '2023-12-05 23:04:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1474, 1, '打电话', '1', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:20', '1', '2024-01-15 20:48:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1475, 2, '发短信', '2', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:31', '1', '2024-01-15 20:48:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1476, 3, '上门拜访', '3', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:07', '1', '2024-01-15 20:49:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1477, 4, '微信沟通', '4', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:15', '1', '2024-01-15 20:49:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1482, 4, '转账失败', '20', 'pay_transfer_status', 0, 'warning', '', '', '1', '2023-10-28 16:24:16', '1', '2025-05-08 12:59:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1483, 3, '转账成功', '10', 'pay_transfer_status', 0, 'success', '', '', '1', '2023-10-28 16:23:50', '1', '2025-05-08 12:58:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1484, 2, '转账进行中', '5', 'pay_transfer_status', 0, 'info', '', '', '1', '2023-10-28 16:23:12', '1', '2025-05-08 12:58:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1485, 1, '等待转账', '0', 'pay_transfer_status', 0, 'default', '', '', '1', '2023-10-28 16:21:43', '1', '2023-10-28 16:23:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1486, 10, '其它入库', '10', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:07:25', '1', '2024-02-05 18:07:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1487, 11, '其它入库（作废）', '11', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:08:07', '1', '2024-02-05 19:20:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1488, 20, '其它出库', '20', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:08:51', '1', '2024-02-05 18:08:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1489, 21, '其它出库（作废）', '21', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:09:00', '1', '2024-02-05 19:20:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1490, 10, '未审核', '10', 'erp_audit_status', 0, 'default', '', '', '1', '2024-02-06 00:00:21', '1', '2024-02-06 00:00:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1491, 20, '已审核', '20', 'erp_audit_status', 0, 'success', '', '', '1', '2024-02-06 00:00:35', '1', '2024-02-06 00:00:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1492, 30, '调拨入库', '30', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:19', '1', '2024-02-07 12:36:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1493, 31, '调拨入库（作废）', '31', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:29', '1', '2024-02-07 20:37:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1494, 32, '调拨出库', '32', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:38', '1', '2024-02-07 12:36:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1495, 33, '调拨出库（作废）', '33', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:49', '1', '2024-02-07 20:37:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1496, 40, '盘盈入库', '40', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:53:00', '1', '2024-02-08 08:53:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1497, 41, '盘盈入库（作废）', '41', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:53:39', '1', '2024-02-16 19:40:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1498, 42, '盘亏出库', '42', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:54:16', '1', '2024-02-08 08:54:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1499, 43, '盘亏出库（作废）', '43', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:54:31', '1', '2024-02-16 19:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1500, 50, '销售出库', '50', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-11 21:47:25', '1', '2024-02-11 21:50:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1501, 51, '销售出库（作废）', '51', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-11 21:47:37', '1', '2024-02-11 21:51:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1502, 60, '销售退货入库', '60', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-12 06:51:05', '1', '2024-02-12 06:51:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1503, 61, '销售退货入库（作废）', '61', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-12 06:51:18', '1', '2024-02-12 06:51:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1504, 70, '采购入库', '70', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:02', '1', '2024-02-16 13:10:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1505, 71, '采购入库（作废）', '71', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:10', '1', '2024-02-16 19:40:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1506, 80, '采购退货出库', '80', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:17', '1', '2024-02-16 13:10:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1507, 81, '采购退货出库（作废）', '81', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:26', '1', '2024-02-16 19:40:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1509, 3, '审批不通过', '3', 'bpm_process_instance_status', 0, 'danger', '', '', '1', '2024-03-16 16:12:06', '1', '2024-03-16 16:12:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1510, 4, '已取消', '4', 'bpm_process_instance_status', 0, 'warning', '', '', '1', '2024-03-16 16:12:22', '1', '2024-03-16 16:12:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1511, 5, '已退回', '5', 'bpm_task_status', 0, 'warning', '', '', '1', '2024-03-16 19:10:46', '1', '2024-03-08 22:41:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1512, 6, '委派中', '6', 'bpm_task_status', 0, 'primary', '', '', '1', '2024-03-17 10:06:22', '1', '2024-03-08 22:41:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1513, 7, '审批通过中', '7', 'bpm_task_status', 0, 'success', '', '', '1', '2024-03-17 10:06:47', '1', '2024-03-08 22:41:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1514, 0, '待审批', '0', 'bpm_task_status', 0, 'info', '', '', '1', '2024-03-17 10:07:11', '1', '2024-03-08 22:41:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1515, 35, '发起人自选', '35', 'bpm_task_candidate_strategy', 0, '', '', '', '1', '2024-03-22 19:45:16', '1', '2024-03-22 19:45:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1516, 1, '执行监听器', 'execution', 'bpm_process_listener_type', 0, 'primary', '', '', '1', '2024-03-23 12:54:03', '1', '2024-03-23 19:14:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1517, 1, '任务监听器', 'task', 'bpm_process_listener_type', 0, 'success', '', '', '1', '2024-03-23 12:54:13', '1', '2024-03-23 19:14:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1526, 1, 'Java 类', 'class', 'bpm_process_listener_value_type', 0, 'primary', '', '', '1', '2024-03-23 15:08:45', '1', '2024-03-23 19:14:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1527, 2, '表达式', 'expression', 'bpm_process_listener_value_type', 0, 'success', '', '', '1', '2024-03-23 15:09:06', '1', '2024-03-23 19:14:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1528, 3, '代理表达式', 'delegateExpression', 'bpm_process_listener_value_type', 0, 'info', '', '', '1', '2024-03-23 15:11:23', '1', '2024-03-23 19:14:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1529, 1, '天', '1', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:26', '1', '2024-03-29 22:50:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1530, 2, '周', '2', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:36', '1', '2024-03-29 22:50:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1531, 3, '月', '3', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:46', '1', '2024-03-29 22:50:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1532, 4, '季度', '4', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:01', '1', '2024-03-29 22:51:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1533, 5, '年', '5', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:07', '1', '2024-03-29 22:51:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1534, 1, '赢单', '1', 'crm_business_end_status_type', 0, 'success', '', '', '1', '2024-04-13 23:26:57', '1', '2024-04-13 23:26:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1535, 2, '输单', '2', 'crm_business_end_status_type', 0, 'primary', '', '', '1', '2024-04-13 23:27:31', '1', '2024-04-13 23:27:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1536, 3, '无效', '3', 'crm_business_end_status_type', 0, 'info', '', '', '1', '2024-04-13 23:27:59', '1', '2024-04-13 23:27:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1537, 1, 'OpenAI', 'OpenAI', 'ai_platform', 0, '', '', '', '1', '2024-05-09 22:33:47', '1', '2024-05-09 22:58:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1538, 2, 'Ollama', 'Ollama', 'ai_platform', 0, '', '', '', '1', '2024-05-17 23:02:55', '1', '2024-05-17 23:02:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1539, 3, '文心一言', 'YiYan', 'ai_platform', 0, '', '', '', '1', '2024-05-18 09:24:20', '1', '2024-05-18 09:29:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1540, 4, '讯飞星火', 'XingHuo', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:08:56', '1', '2024-05-18 10:08:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1541, 5, '通义千问', 'TongYi', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:32:29', '1', '2024-07-06 15:42:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1542, 6, 'StableDiffusion', 'StableDiffusion', 'ai_platform', 0, '', '', '', '1', '2024-06-01 15:09:31', '1', '2024-06-01 15:10:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1543, 10, '进行中', '10', 'ai_image_status', 0, 'primary', '', '', '1', '2024-06-26 20:51:41', '1', '2024-06-26 20:52:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1544, 20, '已完成', '20', 'ai_image_status', 0, 'success', '', '', '1', '2024-06-26 20:52:07', '1', '2024-06-26 20:52:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1545, 30, '已失败', '30', 'ai_image_status', 0, 'warning', '', '', '1', '2024-06-26 20:52:25', '1', '2024-06-26 20:52:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1546, 7, 'Midjourney', 'Midjourney', 'ai_platform', 0, '', '', '', '1', '2024-06-26 22:14:46', '1', '2024-06-26 22:14:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1547, 10, '进行中', '10', 'ai_music_status', 0, 'primary', '', '', '1', '2024-06-27 22:45:22', '1', '2024-06-28 00:56:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1548, 20, '已完成', '20', 'ai_music_status', 0, 'success', '', '', '1', '2024-06-27 22:45:33', '1', '2024-06-28 00:56:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1549, 30, '已失败', '30', 'ai_music_status', 0, 'danger', '', '', '1', '2024-06-27 22:45:44', '1', '2024-06-28 00:56:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1550, 1, '歌词模式', '1', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:31', '1', '2024-06-28 01:22:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1554, 13, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2025-02-24 20:18:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1558, 7, '博客文章', '7', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:23', '1', '2024-07-07 15:50:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1559, 8, '想法', '8', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:31', '1', '2024-07-07 15:50:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1560, 9, '大纲', '9', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:37', '1', '2024-07-07 15:50:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1561, 1, '自动', '1', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:06', '1', '2024-07-07 15:51:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1562, 2, '友善', '2', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:19', '1', '2024-07-07 15:51:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1563, 3, '随意', '3', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:27', '1', '2024-07-07 15:51:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1564, 4, '友好', '4', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:37', '1', '2024-07-07 15:51:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1565, 5, '专业', '5', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:49', '1', '2024-07-07 15:52:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1566, 6, '诙谐', '6', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:15', '1', '2024-07-07 15:52:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1567, 7, '有趣', '7', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:24', '1', '2024-07-07 15:52:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1568, 8, '正式', '8', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:54:33', '1', '2024-07-07 15:54:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1569, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1570, 1, '自动', '1', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:34', '1', '2024-07-07 15:19:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1571, 2, '电子邮件', '2', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:50', '1', '2024-07-07 15:49:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1572, 3, '消息', '3', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:01', '1', '2024-07-07 15:49:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1573, 4, '评论', '4', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:13', '1', '2024-07-07 15:49:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1574, 1, '自动', '1', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:18', '1', '2024-07-07 15:44:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1575, 2, '中文', '2', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:28', '1', '2024-07-07 15:44:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1576, 3, '英文', '3', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:37', '1', '2024-07-07 15:44:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1577, 4, '韩语', '4', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:28', '1', '2024-07-07 15:46:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1578, 5, '日语', '5', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:44', '1', '2024-07-07 15:46:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1579, 1, '自动', '1', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:34', '1', '2024-07-07 15:48:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1580, 2, '短', '2', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:44', '1', '2024-07-07 15:48:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1581, 3, '中等', '3', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:52', '1', '2024-07-07 15:48:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1582, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1584, 1, '撰写', '1', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:00', '1', '2024-07-10 21:26:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1585, 2, '回复', '2', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:06', '1', '2024-07-10 21:26:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1586, 2, '腾讯云', 'TENCENT', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:16', '1', '2024-07-22 22:23:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1587, 3, '华为云', 'HUAWEI', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:46', '1', '2024-07-22 22:23:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1588, 1, 'OpenAI 微软', 'AzureOpenAI', 'ai_platform', 0, '', '', '', '1', '2024-08-10 14:07:41', '1', '2024-08-10 14:07:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1589, 10, 'BPMN 设计器', '10', 'bpm_model_type', 0, 'primary', '', '', '1', '2024-08-26 15:22:17', '1', '2024-08-26 16:46:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1590, 20, 'SIMPLE 设计器', '20', 'bpm_model_type', 0, 'success', '', '', '1', '2024-08-26 15:22:27', '1', '2024-08-26 16:45:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1591, 4, '七牛云', 'QINIU', 'system_sms_channel_code', 0, '', '', '', '1', '2024-08-31 08:45:03', '1', '2024-08-31 08:45:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1592, 3, '新人券', '3', 'promotion_coupon_take_type', 0, 'info', '', '新人注册后，自动发放', '1', '2024-09-03 11:57:16', '1', '2024-09-03 11:57:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1593, 5, '微信零钱', '5', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2024-10-13 11:06:48', '1', '2025-05-10 08:24:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', '2025-02-23 19:51:40', '1', '2025-02-23 19:52:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', '2025-02-23 20:58:04', '1', '2025-02-23 20:58:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', '2025-02-24 20:19:09', '1', '2025-02-24 20:19:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1686, 1, '聊天', '1', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:26:34', '1', '2025-03-03 12:26:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1687, 2, '图像', '2', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:23', '1', '2025-03-03 12:27:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1688, 3, '音频', '3', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:51', '1', '2025-03-03 12:27:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1689, 4, '视频', '4', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:03', '1', '2025-03-03 12:28:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1690, 5, '向量', '5', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:15', '1', '2025-03-03 12:28:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1693, 15, '月之暗面', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-03-11 20:05:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2000, 0, '标准数据格式（JSON）', '0', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:26', '1', '2025-03-17 09:28:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2001, 1, '透传/自定义', '1', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:37', '1', '2025-03-17 09:28:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2025-03-17 09:28:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2025-03-17 09:28:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2025-03-17 09:28:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2025-03-17 09:28:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2025-03-17 09:28:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2007, 0, '弱校验', '0', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:05:48', '1', '2025-03-17 09:28:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2008, 1, '免校验', '1', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:06:03', '1', '2025-03-17 09:28:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2025-03-17 09:28:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2010, 1, '蜂窝（2G / 3G / 4G / 5G）', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2025-03-17 09:28:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2025-03-17 09:28:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2025-03-17 09:28:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2013, 0, '自定义', '0', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:10', '1', '2025-03-17 09:28:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2014, 1, 'Modbus', '1', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:21', '1', '2025-03-17 09:28:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2015, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2025-03-17 09:29:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2016, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2025-03-17 09:29:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2017, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2025-03-17 09:29:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-03-17 09:29:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-03-17 09:29:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-03-17 09:29:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2025-03-17 09:29:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2025-03-17 09:29:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2025-03-17 09:29:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2024, 1, 'JAR 部署', '0', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:32', '1', '2025-03-17 09:29:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2025, 2, '独立部署', '1', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:43', '1', '2025-03-17 09:29:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2026, 0, '停止', '0', 'iot_plugin_status', 0, 'danger', '', '', '1', '2024-12-13 11:07:37', '1', '2025-03-17 09:29:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2027, 1, '运行', '1', 'iot_plugin_status', 0, '', '', '', '1', '2024-12-13 11:07:45', '1', '2025-03-17 09:34:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2028, 0, '普通插件', '0', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:32', '1', '2025-03-17 09:34:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2029, 1, '设备插件', '1', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2033, 4, 'PH值', 'pH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2034, 5, '土壤EC值', 'dS/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2035, 6, '太阳总辐射', 'W/㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2036, 7, '降雨量', 'mm/hour', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2037, 8, '乏', 'var', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2038, 9, '厘泊', 'cP', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2039, 10, '饱和度', 'aw', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2040, 11, '个', 'pcs', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2041, 12, '厘斯', 'cst', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2042, 13, '巴', 'bar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2043, 14, '纳克每升', 'ppt', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2044, 15, '微克每升', 'ppb', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2045, 16, '微西每厘米', 'uS/cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2046, 17, '牛顿每库仑', 'N/C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2047, 18, '伏特每米', 'V/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2048, 19, '滴速', 'ml/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2049, 20, '毫米汞柱', 'mmHg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2050, 21, '血糖', 'mmol/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2051, 22, '毫米每秒', 'mm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2052, 23, '转每分钟', 'turn/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2053, 24, '次', 'count', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2054, 25, '档', 'gear', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2055, 26, '步', 'stepCount', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2056, 27, '标准立方米每小时', 'Nm3/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2057, 28, '千伏', 'kV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2058, 29, '千伏安', 'kVA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2060, 30, '千乏', 'kVar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2061, 31, '微瓦每平方厘米', 'uw/cm2', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2062, 32, '只', '只', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2063, 33, '相对湿度', '%RH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2064, 34, '立方米每秒', 'm³/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2065, 35, '公斤每秒', 'kg/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2066, 36, '转每分钟', 'r/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2067, 37, '吨每小时', 't/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2068, 38, '千卡每小时', 'KCL/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2069, 39, '升每秒', 'L/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2070, 40, '兆帕', 'Mpa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2071, 41, '立方米每小时', 'm³/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2072, 42, '千乏时', 'kvarh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2073, 43, '微克每升', 'μg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2074, 44, '千卡路里', 'kcal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2075, 45, '吉字节', 'GB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2076, 46, '兆字节', 'MB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2077, 47, '千字节', 'KB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2078, 48, '字节', 'B', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2079, 49, '微克每平方分米每天', 'μg/(d㎡·d)', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2080, 50, '无', '', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2081, 51, '百万分率', 'ppm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2082, 52, '像素', 'pixel', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2083, 53, '照度', 'Lux', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2084, 54, '重力加速度', 'grav', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2085, 55, '分贝', 'dB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2086, 56, '百分比', '%', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2087, 57, '流明', 'lm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2088, 58, '比特', 'bit', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2089, 59, '克每毫升', 'g/mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2090, 60, '克每升', 'g/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2091, 61, '毫克每升', 'mg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2092, 62, '微克每立方米', 'μg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2093, 63, '毫克每立方米', 'mg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2094, 64, '克每立方米', 'g/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2095, 65, '千克每立方米', 'kg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2096, 66, '纳法', 'nF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2097, 67, '皮法', 'pF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2098, 68, '微法', 'μF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2099, 69, '法拉', 'F', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2100, 70, '欧姆', 'Ω', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2101, 71, '微安', 'μA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2102, 72, '毫安', 'mA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2103, 73, '千安', 'kA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2104, 74, '安培', 'A', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2105, 75, '毫伏', 'mV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2106, 76, '伏特', 'V', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2107, 77, '毫秒', 'ms', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2108, 78, '秒', 's', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2109, 79, '分钟', 'min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2110, 80, '小时', 'h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2111, 81, '日', 'day', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2112, 82, '周', 'week', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2113, 83, '月', 'month', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2114, 84, '年', 'year', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2115, 85, '节', 'kn', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2116, 86, '千米每小时', 'km/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2117, 87, '米每秒', 'm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2118, 88, '秒', '″', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2119, 89, '分', '′', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2120, 90, '度', '°', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2121, 91, '弧度', 'rad', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2122, 92, '赫兹', 'Hz', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2123, 93, '微瓦', 'μW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2124, 94, '毫瓦', 'mW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2125, 95, '千瓦特', 'kW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2126, 96, '瓦特', 'W', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2127, 97, '卡路里', 'cal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2128, 98, '千瓦时', 'kW·h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2129, 99, '瓦时', 'Wh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2130, 100, '电子伏', 'eV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2131, 101, '千焦', 'kJ', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2132, 102, '焦耳', 'J', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2133, 103, '华氏度', '℉', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2134, 104, '开尔文', 'K', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2135, 105, '吨', 't', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2136, 106, '摄氏度', '°C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2137, 107, '毫帕', 'mPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2138, 108, '百帕', 'hPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2139, 109, '千帕', 'kPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2140, 110, '帕斯卡', 'Pa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2141, 111, '毫克', 'mg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2142, 112, '克', 'g', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2143, 113, '千克', 'kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2144, 114, '牛', 'N', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2145, 115, '毫升', 'mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2146, 116, '升', 'L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2147, 117, '立方毫米', 'mm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2148, 118, '立方厘米', 'cm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2149, 119, '立方千米', 'km³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2150, 120, '立方米', 'm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2151, 121, '公顷', 'h㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2152, 122, '平方厘米', 'c㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2153, 123, '平方毫米', 'm㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2154, 124, '平方千米', 'k㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2155, 125, '平方米', '㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2156, 126, '纳米', 'nm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2157, 127, '微米', 'μm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2158, 128, '毫米', 'mm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2159, 129, '厘米', 'cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2163, 1, '输入', '1', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:24', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2164, 2, '输出', '2', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:36', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2165, 1, 'HTTP', '1', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:39:54', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2166, 2, 'TCP', '2', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:06', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2167, 3, 'WEBSOCKET', '3', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:24', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2168, 10, 'MQTT', '10', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:37', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2169, 20, 'DATABASE', '20', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:05', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2170, 21, 'REDIS_STREAM', '21', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:18', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2171, 30, 'ROCKETMQ', '30', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:30', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2172, 31, 'RABBITMQ', '31', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:47', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2173, 32, 'KAFKA', '32', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:59', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3000, 16, '百川智能', 'BaiChuan', 'ai_platform', 0, '', '', '', '1', '2025-03-23 12:15:46', '1', '2025-03-23 12:15:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3001, 50, 'Vben5.0 Ant Design Schema 模版', '40', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-04-23 21:47:47', '1', '2025-05-02 12:01:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3002, 6, '支付宝余额', '6', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2025-05-10 08:24:49', '1', '2025-05-10 08:24:49', '0');\nINSERT 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');\nCOMMIT;\nSET IDENTITY_INSERT system_dict_data OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_dict_type\n-- ----------------------------\nCREATE TABLE system_dict_type\n(\n    id           bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name         varchar(100) DEFAULT ''                NULL,\n    type         varchar(100) DEFAULT ''                NULL,\n    status       smallint     DEFAULT 0                 NOT NULL,\n    remark       varchar(500) DEFAULT NULL              NULL,\n    creator      varchar(64)  DEFAULT ''                NULL,\n    create_time  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      varchar(64)  DEFAULT ''                NULL,\n    update_time  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      bit          DEFAULT '0'               NOT NULL,\n    deleted_time datetime     DEFAULT NULL              NULL\n);\n\nCOMMENT ON COLUMN system_dict_type.id IS '字典主键';\nCOMMENT ON COLUMN system_dict_type.name IS '字典名称';\nCOMMENT ON COLUMN system_dict_type.type IS '字典类型';\nCOMMENT ON COLUMN system_dict_type.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_type.remark IS '备注';\nCOMMENT ON COLUMN system_dict_type.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_type.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_type.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_type.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_type.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dict_type.deleted_time IS '删除时间';\nCOMMENT ON TABLE system_dict_type IS '字典类型表';\n\n-- ----------------------------\n-- Records of system_dict_type\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_dict_type ON;\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1, '用户性别', 'system_user_sex', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2022-05-16 20:29:32', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (6, '参数类型', 'infra_config_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:36:54', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (7, '通知类型', 'system_notice_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:35:26', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (9, '操作类型', 'infra_operate_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (10, '系统状态', 'common_status', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:21:28', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (11, 'Boolean 是否类型', 'infra_boolean_string', 0, 'boolean 转是否', '', '2021-01-19 03:20:08', '', '2022-02-01 16:37:10', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (104, '登陆结果', 'system_login_result', 0, '登陆结果', '', '2021-01-18 06:17:11', '', '2022-02-01 16:36:00', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (106, '代码生成模板类型', 'infra_codegen_template_type', 0, NULL, '', '2021-02-05 07:08:06', '1', '2022-05-16 20:26:50', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (107, '定时任务状态', 'infra_job_status', 0, NULL, '', '2021-02-07 07:44:16', '', '2022-02-01 16:51:11', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (108, '定时任务日志状态', 'infra_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2022-02-01 16:50:43', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (110, 'API 异常数据的处理状态', 'infra_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2022-02-01 16:50:53', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (111, '短信渠道编码', 'system_sms_channel_code', 0, NULL, '1', '2021-04-05 01:04:50', '1', '2022-02-16 02:09:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (112, '短信模板的类型', 'system_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:43', '1', '2022-02-01 16:35:06', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (113, '短信发送状态', 'system_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:03', '1', '2022-02-01 16:35:09', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (114, '短信接收状态', 'system_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:14', '1', '2022-02-01 16:35:14', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (116, '登陆日志的类型', 'system_login_type', 0, '登陆日志的类型', '1', '2021-10-06 00:50:46', '1', '2022-02-01 16:35:56', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (117, 'OA 请假类型', 'bpm_oa_leave_type', 0, NULL, '1', '2021-09-21 22:34:33', '1', '2022-01-22 10:41:37', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (130, '支付渠道编码类型', 'pay_channel_code', 0, '支付渠道的编码', '1', '2021-12-03 10:35:08', '1', '2023-07-10 10:11:39', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (131, '支付回调状态', 'pay_notify_status', 0, '支付回调状态（包括退款回调）', '1', '2021-12-03 10:53:29', '1', '2023-07-19 18:09:43', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (132, '支付订单状态', 'pay_order_status', 0, '支付订单状态', '1', '2021-12-03 11:17:50', '1', '2021-12-03 11:17:50', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (134, '退款订单状态', 'pay_refund_status', 0, '退款订单状态', '1', '2021-12-10 16:42:50', '1', '2023-07-19 10:13:17', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (139, '流程实例的状态', 'bpm_process_instance_status', 0, '流程实例的状态', '1', '2022-01-07 23:46:42', '1', '2022-01-07 23:46:42', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (140, '流程实例的结果', 'bpm_task_status', 0, '流程实例的结果', '1', '2022-01-07 23:48:10', '1', '2024-03-08 22:42:03', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (141, '流程的表单类型', 'bpm_model_form_type', 0, '流程的表单类型', '103', '2022-01-11 23:50:45', '103', '2022-01-11 23:50:45', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (142, '任务分配规则的类型', 'bpm_task_candidate_strategy', 0, 'BPM 任务的候选人的策略', '103', '2022-01-12 23:21:04', '103', '2024-03-06 02:53:59', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (144, '代码生成的场景枚举', 'infra_codegen_scene', 0, '代码生成的场景枚举', '1', '2022-02-02 13:14:45', '1', '2022-03-10 16:33:46', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (145, '角色类型', 'system_role_type', 0, '角色类型', '1', '2022-02-16 13:01:46', '1', '2022-02-16 13:01:46', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (146, '文件存储器', 'infra_file_storage', 0, '文件存储器', '1', '2022-03-15 00:24:38', '1', '2022-03-15 00:24:38', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (147, 'OAuth 2.0 授权类型', 'system_oauth2_grant_type', 0, 'OAuth 2.0 授权类型（模式）', '1', '2022-05-12 00:20:52', '1', '2022-05-11 16:25:49', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (149, '商品 SPU 状态', 'product_spu_status', 0, '商品 SPU 状态', '1', '2022-10-24 21:19:04', '1', '2022-10-24 21:19:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (150, '优惠类型', 'promotion_discount_type', 0, '优惠类型', '1', '2022-11-01 12:46:06', '1', '2022-11-01 12:46:06', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (151, '优惠劵模板的有限期类型', 'promotion_coupon_template_validity_type', 0, '优惠劵模板的有限期类型', '1', '2022-11-02 00:06:20', '1', '2022-11-04 00:08:26', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (152, '营销的商品范围', 'promotion_product_scope', 0, '营销的商品范围', '1', '2022-11-02 00:28:01', '1', '2022-11-02 00:28:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (153, '优惠劵的状态', 'promotion_coupon_status', 0, '优惠劵的状态', '1', '2022-11-04 00:14:49', '1', '2022-11-04 00:14:49', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (154, '优惠劵的领取方式', 'promotion_coupon_take_type', 0, '优惠劵的领取方式', '1', '2022-11-04 19:12:27', '1', '2022-11-04 19:12:27', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (155, '促销活动的状态', 'promotion_activity_status', 0, '促销活动的状态', '1', '2022-11-04 22:54:23', '1', '2022-11-04 22:54:23', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (156, '营销的条件类型', 'promotion_condition_type', 0, '营销的条件类型', '1', '2022-11-04 22:59:23', '1', '2022-11-04 22:59:23', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (157, '交易售后状态', 'trade_after_sale_status', 0, '交易售后状态', '1', '2022-11-19 20:52:56', '1', '2022-11-19 20:52:56', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (158, '交易售后的类型', 'trade_after_sale_type', 0, '交易售后的类型', '1', '2022-11-19 21:04:09', '1', '2022-11-19 21:04:09', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (159, '交易售后的方式', 'trade_after_sale_way', 0, '交易售后的方式', '1', '2022-11-19 21:39:04', '1', '2022-11-19 21:39:04', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (160, '终端', 'terminal', 0, '终端', '1', '2022-12-10 10:50:50', '1', '2022-12-10 10:53:11', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (161, '交易订单的类型', 'trade_order_type', 0, '交易订单的类型', '1', '2022-12-10 16:33:54', '1', '2022-12-10 16:33:54', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (162, '交易订单的状态', 'trade_order_status', 0, '交易订单的状态', '1', '2022-12-10 16:48:44', '1', '2022-12-10 16:48:44', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (163, '交易订单项的售后状态', 'trade_order_item_after_sale_status', 0, '交易订单项的售后状态', '1', '2022-12-10 20:58:08', '1', '2022-12-10 20:58:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (164, '公众号自动回复的请求关键字匹配模式', 'mp_auto_reply_request_match', 0, '公众号自动回复的请求关键字匹配模式', '1', '2023-01-16 23:29:56', '1', '2023-01-16 23:29:56', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (165, '公众号的消息类型', 'mp_message_type', 0, '公众号的消息类型', '1', '2023-01-17 22:17:09', '1', '2023-01-17 22:17:09', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (166, '邮件发送状态', 'system_mail_send_status', 0, '邮件发送状态', '1', '2023-01-26 09:53:13', '1', '2023-01-26 09:53:13', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (167, '站内信模版的类型', 'system_notify_template_type', 0, '站内信模版的类型', '1', '2023-01-28 10:35:10', '1', '2023-01-28 10:35:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (168, '代码生成的前端类型', 'infra_codegen_front_type', 0, '', '1', '2023-04-12 23:57:52', '1', '2023-04-12 23:57:52', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (170, '快递计费方式', 'trade_delivery_express_charge_mode', 0, '用于商城交易模块配送管理', '1', '2023-05-21 22:45:03', '1', '2023-05-21 22:45:03', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (171, '积分业务类型', 'member_point_biz_type', 0, '', '1', '2023-06-10 12:15:00', '1', '2023-06-28 13:48:20', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (173, '支付通知类型', 'pay_notify_type', 0, NULL, '1', '2023-07-20 12:23:03', '1', '2023-07-20 12:23:03', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (174, '会员经验业务类型', 'member_experience_biz_type', 0, NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (175, '交易配送类型', 'trade_delivery_type', 0, '', '1', '2023-08-23 00:03:14', '1', '2023-08-23 00:03:14', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (176, '分佣模式', 'brokerage_enabled_condition', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (177, '分销关系绑定模式', 'brokerage_bind_mode', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (178, '佣金提现类型', 'brokerage_withdraw_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (179, '佣金记录业务类型', 'brokerage_record_biz_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (180, '佣金记录状态', 'brokerage_record_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (181, '佣金提现状态', 'brokerage_withdraw_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (182, '佣金提现银行', 'brokerage_bank_name', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (183, '砍价记录的状态', 'promotion_bargain_record_status', 0, '', '1', '2023-10-05 10:41:08', '1', '2023-10-05 10:41:08', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (184, '拼团记录的状态', 'promotion_combination_record_status', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-10-08 07:24:25', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (185, '回款-回款方式', 'crm_receivable_return_type', 0, '回款-回款方式', '1', '2023-10-18 21:54:10', '1', '2023-10-18 21:54:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (186, 'CRM 客户行业', 'crm_customer_industry', 0, 'CRM 客户所属行业', '1', '2023-10-28 22:57:07', '1', '2024-02-18 23:30:22', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (187, '客户等级', 'crm_customer_level', 0, 'CRM 客户等级', '1', '2023-10-28 22:59:12', '1', '2023-10-28 15:11:16', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (188, '客户来源', 'crm_customer_source', 0, 'CRM 客户来源', '1', '2023-10-28 23:00:34', '1', '2023-10-28 15:11:16', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (600, 'Banner 位置', 'promotion_banner_position', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-11-04 13:04:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (601, '社交类型', 'system_social_type', 0, '', '1', '2023-11-04 13:03:54', '1', '2023-11-04 13:03:54', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (604, '产品状态', 'crm_product_status', 0, '', '1', '2023-10-30 21:47:59', '1', '2023-10-30 21:48:45', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (605, 'CRM 数据权限的级别', 'crm_permission_level', 0, '', '1', '2023-11-30 09:51:59', '1', '2023-11-30 09:51:59', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (606, 'CRM 审批状态', 'crm_audit_status', 0, '', '1', '2023-11-30 18:56:23', '1', '2023-11-30 18:56:23', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (607, 'CRM 产品单位', 'crm_product_unit', 0, '', '1', '2023-12-05 23:01:51', '1', '2023-12-05 23:01:51', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (608, 'CRM 跟进方式', 'crm_follow_up_type', 0, '', '1', '2024-01-15 20:48:05', '1', '2024-01-15 20:48:05', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (610, '转账订单状态', 'pay_transfer_status', 0, '', '1', '2023-10-28 16:18:32', '1', '2023-10-28 16:18:32', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (611, 'ERP 库存明细的业务类型', 'erp_stock_record_biz_type', 0, 'ERP 库存明细的业务类型', '1', '2024-02-05 18:07:02', '1', '2024-02-05 18:07:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (612, 'ERP 审批状态', 'erp_audit_status', 0, '', '1', '2024-02-06 00:00:07', '1', '2024-02-06 00:00:07', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (613, 'BPM 监听器类型', 'bpm_process_listener_type', 0, '', '1', '2024-03-23 12:52:24', '1', '2024-03-09 15:54:28', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (615, 'BPM 监听器值类型', 'bpm_process_listener_value_type', 0, '', '1', '2024-03-23 13:00:31', '1', '2024-03-23 13:00:31', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (616, '时间间隔', 'date_interval', 0, '', '1', '2024-03-29 22:50:09', '1', '2024-03-29 22:50:09', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (619, 'CRM 商机结束状态类型', 'crm_business_end_status_type', 0, '', '1', '2024-04-13 23:23:00', '1', '2024-04-13 23:23:00', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (620, 'AI 模型平台', 'ai_platform', 0, '', '1', '2024-05-09 22:27:38', '1', '2024-05-09 22:27:38', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (621, 'AI 绘画状态', 'ai_image_status', 0, '', '1', '2024-06-26 20:51:23', '1', '2024-06-26 20:51:23', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (622, 'AI 音乐状态', 'ai_music_status', 0, '', '1', '2024-06-27 22:45:07', '1', '2024-06-28 00:56:27', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (623, 'AI 音乐生成模式', 'ai_generate_mode', 0, '', '1', '2024-06-27 22:46:21', '1', '2024-06-28 01:22:29', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (624, '写作语气', 'ai_write_tone', 0, '', '1', '2024-07-07 15:19:02', '1', '2024-07-07 15:19:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (625, '写作语言', 'ai_write_language', 0, '', '1', '2024-07-07 15:18:52', '1', '2024-07-07 15:18:52', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (626, '写作长度', 'ai_write_length', 0, '', '1', '2024-07-07 15:18:41', '1', '2024-07-07 15:18:41', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (627, '写作格式', 'ai_write_format', 0, '', '1', '2024-07-07 15:14:34', '1', '2024-07-07 15:14:34', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1000, 'IoT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2025-03-17 09:25:06', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1001, 'IoT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2025-03-17 09:25:08', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1002, 'IoT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2025-03-17 09:25:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1003, 'IoT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2025-03-17 09:25:12', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1004, 'IoT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2025-03-17 09:25:14', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1005, 'IoT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2025-03-17 09:25:16', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1006, 'IoT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-03-17 09:25:19', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1007, 'IoT 物模型功能类型', 'iot_thing_model_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2025-03-17 09:25:24', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1008, 'IoT 插件部署方式', 'iot_plugin_deploy_type', 0, '', '1', '2024-12-13 10:55:13', '1', '2025-03-17 09:25:27', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1009, 'IoT 插件状态', 'iot_plugin_status', 0, '', '1', '2024-12-13 11:05:34', '1', '2025-03-17 09:25:30', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1010, 'IoT 插件类型', 'iot_plugin_type', 0, '', '1', '2024-12-13 11:08:19', '1', '2025-03-17 09:25:32', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1011, 'IoT 物模型单位', 'iot_thing_model_unit', 0, '', '1', '2024-12-25 17:36:46', '1', '2025-03-17 09:25:35', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1012, 'IoT 数据桥接的方向枚举', 'iot_data_bridge_direction_enum', 0, '', '1', '2025-03-09 12:37:40', '1', '2025-03-17 09:25:39', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1013, 'IoT 数据桥梁的类型枚举', 'iot_data_bridge_type_enum', 0, '', '1', '2025-03-09 12:39:36', '1', '2025-04-06 17:09:46', '0', '1970-01-01 00:00:00');\nCOMMIT;\nSET IDENTITY_INSERT system_dict_type OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_login_log\n-- ----------------------------\nCREATE TABLE system_login_log\n(\n    id          bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    log_type    bigint                                NOT NULL,\n    trace_id    varchar(64) DEFAULT ''                NULL,\n    user_id     bigint      DEFAULT 0                 NOT NULL,\n    user_type   smallint    DEFAULT 0                 NOT NULL,\n    username    varchar(50) DEFAULT ''                NULL,\n    result      smallint                              NOT NULL,\n    user_ip     varchar(50)                           NOT NULL,\n    user_agent  varchar(512)                          NOT NULL,\n    creator     varchar(64) DEFAULT ''                NULL,\n    create_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64) DEFAULT ''                NULL,\n    update_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit         DEFAULT '0'               NOT NULL,\n    tenant_id   bigint      DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_login_log.id IS '访问ID';\nCOMMENT ON COLUMN system_login_log.log_type IS '日志类型';\nCOMMENT ON COLUMN system_login_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_login_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_login_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_login_log.username IS '用户账号';\nCOMMENT ON COLUMN system_login_log.result IS '登陆结果';\nCOMMENT ON COLUMN system_login_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_login_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_login_log.creator IS '创建者';\nCOMMENT ON COLUMN system_login_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_login_log.updater IS '更新者';\nCOMMENT ON COLUMN system_login_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_login_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_login_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_login_log IS '系统访问记录';\n\n-- ----------------------------\n-- Table structure for system_mail_account\n-- ----------------------------\nCREATE TABLE system_mail_account\n(\n    id              bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    mail            varchar(255)                          NOT NULL,\n    username        varchar(255)                          NOT NULL,\n    password        varchar(255)                          NOT NULL,\n    host            varchar(255)                          NOT NULL,\n    port            int                                   NOT NULL,\n    ssl_enable      bit         DEFAULT '0'               NOT NULL,\n    starttls_enable bit         DEFAULT '0'               NOT NULL,\n    creator         varchar(64) DEFAULT ''                NULL,\n    create_time     datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         varchar(64) DEFAULT ''                NULL,\n    update_time     datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         bit         DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_mail_account.id IS '主键';\nCOMMENT ON COLUMN system_mail_account.mail IS '邮箱';\nCOMMENT ON COLUMN system_mail_account.username IS '用户名';\nCOMMENT ON COLUMN system_mail_account.password IS '密码';\nCOMMENT ON COLUMN system_mail_account.host IS 'SMTP 服务器域名';\nCOMMENT ON COLUMN system_mail_account.port IS 'SMTP 服务器端口';\nCOMMENT ON COLUMN system_mail_account.ssl_enable IS '是否开启 SSL';\nCOMMENT ON COLUMN system_mail_account.starttls_enable IS '是否开启 STARTTLS';\nCOMMENT ON COLUMN system_mail_account.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_account.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_account.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_account.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_account.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_account IS '邮箱账号表';\n\n-- ----------------------------\n-- Records of system_mail_account\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_mail_account ON;\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, '0', '0', '1', '2023-01-25 17:39:52', '1', '2025-04-04 16:34:40', '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, '1', '0', '1', '2023-01-26 01:26:03', '1', '2023-04-12 22:39:38', '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, '0', '0', '1', '2023-01-27 15:06:38', '1', '2023-01-27 07:08:36', '1');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (4, '7685413x@qq.com', '2', '3', '4', 5, '1', '0', '1', '2023-04-12 23:05:06', '1', '2023-04-12 15:05:11', '1');\nCOMMIT;\nSET IDENTITY_INSERT system_mail_account OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_mail_log\n-- ----------------------------\nCREATE TABLE system_mail_log\n(\n    id                bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    user_id           bigint        DEFAULT NULL              NULL,\n    user_type         smallint      DEFAULT NULL              NULL,\n    to_mail           varchar(255)                            NOT NULL,\n    account_id        bigint                                  NOT NULL,\n    from_mail         varchar(255)                            NOT NULL,\n    template_id       bigint                                  NOT NULL,\n    template_code     varchar(63)                             NOT NULL,\n    template_nickname varchar(255)  DEFAULT NULL              NULL,\n    template_title    varchar(255)                            NOT NULL,\n    template_content  varchar(10240)                          NOT NULL,\n    template_params   varchar(255)                            NOT NULL,\n    send_status       smallint      DEFAULT 0                 NOT NULL,\n    send_time         datetime      DEFAULT NULL              NULL,\n    send_message_id   varchar(255)  DEFAULT NULL              NULL,\n    send_exception    varchar(4096) DEFAULT NULL              NULL,\n    creator           varchar(64)   DEFAULT ''                NULL,\n    create_time       datetime      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater           varchar(64)   DEFAULT ''                NULL,\n    update_time       datetime      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted           bit           DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_mail_log.id IS '编号';\nCOMMENT ON COLUMN system_mail_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_mail_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_mail_log.to_mail IS '接收邮箱地址';\nCOMMENT ON COLUMN system_mail_log.account_id IS '邮箱账号编号';\nCOMMENT ON COLUMN system_mail_log.from_mail IS '发送邮箱地址';\nCOMMENT ON COLUMN system_mail_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_mail_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_mail_log.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_mail_log.template_title IS '邮件标题';\nCOMMENT ON COLUMN system_mail_log.template_content IS '邮件内容';\nCOMMENT ON COLUMN system_mail_log.template_params IS '邮件参数';\nCOMMENT ON COLUMN system_mail_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_mail_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_mail_log.send_message_id IS '发送返回的消息 ID';\nCOMMENT ON COLUMN system_mail_log.send_exception IS '发送异常';\nCOMMENT ON COLUMN system_mail_log.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_log.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_log IS '邮件日志表';\n\n-- ----------------------------\n-- Table structure for system_mail_template\n-- ----------------------------\nCREATE TABLE system_mail_template\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name        varchar(63)                            NOT NULL,\n    code        varchar(63)                            NOT NULL,\n    account_id  bigint                                 NOT NULL,\n    nickname    varchar(255) DEFAULT NULL              NULL,\n    title       varchar(255)                           NOT NULL,\n    content     varchar(10240)                         NOT NULL,\n    params      varchar(255)                           NOT NULL,\n    status      smallint                               NOT NULL,\n    remark      varchar(255) DEFAULT NULL              NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_mail_template.id IS '编号';\nCOMMENT ON COLUMN system_mail_template.name IS '模板名称';\nCOMMENT ON COLUMN system_mail_template.code IS '模板编码';\nCOMMENT ON COLUMN system_mail_template.account_id IS '发送的邮箱账号编号';\nCOMMENT ON COLUMN system_mail_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_mail_template.title IS '模板标题';\nCOMMENT ON COLUMN system_mail_template.content IS '模板内容';\nCOMMENT ON COLUMN system_mail_template.params IS '参数数组';\nCOMMENT ON COLUMN system_mail_template.status IS '开启状态';\nCOMMENT ON COLUMN system_mail_template.remark IS '备注';\nCOMMENT ON COLUMN system_mail_template.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_template.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_template IS '邮件模版表';\n\n-- ----------------------------\n-- Records of system_mail_template\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_mail_template ON;\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '后台用户短信登录', 'admin-sms-login', 1, '奥特曼', '你猜我猜', '<p>您的验证码是{code}，名字是{name}</p>', '[\"code\",\"name\"]', 0, '3', '1', '2021-10-11 08:10:00', '1', '2023-12-02 19:51:14', '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (14, '测试模版', 'test_01', 2, '芋艿', '一个标题', '<p>你是 {key01} 吗？</p><p><br></p><p>是的话，赶紧 {key02} 一下！</p>', '[\"key01\",\"key02\"]', 0, NULL, '1', '2023-01-26 01:27:40', '1', '2023-01-27 10:32:16', '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (15, '3', '2', 2, '7', '4', '<p>45</p>', '[]', 1, '80', '1', '2023-01-27 15:50:35', '1', '2023-01-27 16:34:49', '0');\nCOMMIT;\nSET IDENTITY_INSERT system_mail_template OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_menu\n-- ----------------------------\nCREATE TABLE system_menu\n(\n    id             bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name           varchar(50)                            NOT NULL,\n    permission     varchar(100) DEFAULT ''                NULL,\n    type           smallint                               NOT NULL,\n    sort           int          DEFAULT 0                 NOT NULL,\n    parent_id      bigint       DEFAULT 0                 NOT NULL,\n    path           varchar(200) DEFAULT ''                NULL,\n    icon           varchar(100) DEFAULT '#'               NULL,\n    component      varchar(255) DEFAULT NULL              NULL,\n    component_name varchar(255) DEFAULT NULL              NULL,\n    status         smallint     DEFAULT 0                 NOT NULL,\n    visible        bit          DEFAULT '1'               NOT NULL,\n    keep_alive     bit          DEFAULT '1'               NOT NULL,\n    always_show    bit          DEFAULT '1'               NOT NULL,\n    creator        varchar(64)  DEFAULT ''                NULL,\n    create_time    datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar(64)  DEFAULT ''                NULL,\n    update_time    datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_menu.id IS '菜单ID';\nCOMMENT ON COLUMN system_menu.name IS '菜单名称';\nCOMMENT ON COLUMN system_menu.permission IS '权限标识';\nCOMMENT ON COLUMN system_menu.type IS '菜单类型';\nCOMMENT ON COLUMN system_menu.sort IS '显示顺序';\nCOMMENT ON COLUMN system_menu.parent_id IS '父菜单ID';\nCOMMENT ON COLUMN system_menu.path IS '路由地址';\nCOMMENT ON COLUMN system_menu.icon IS '菜单图标';\nCOMMENT ON COLUMN system_menu.component IS '组件路径';\nCOMMENT ON COLUMN system_menu.component_name IS '组件名';\nCOMMENT ON COLUMN system_menu.status IS '菜单状态';\nCOMMENT ON COLUMN system_menu.visible IS '是否可见';\nCOMMENT ON COLUMN system_menu.keep_alive IS '是否缓存';\nCOMMENT ON COLUMN system_menu.always_show IS '是否总是显示';\nCOMMENT ON COLUMN system_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_menu.deleted IS '是否删除';\nCOMMENT ON TABLE system_menu IS '菜单权限表';\n\n-- ----------------------------\n-- Records of system_menu\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_menu ON;\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-03-01 08:28:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5, 'OA 示例', '', 1, 40, 1185, 'oa', 'fa:road', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-09-20 16:26:19', '1', '2024-02-29 12:38:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (101, '角色管理', '', 2, 2, 1, 'role', 'ep:user', 'system/role/index', 'SystemRole', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-05-01 18:35:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (102, '菜单管理', '', 2, 3, 1, 'menu', 'ep:menu', 'system/menu/index', 'SystemMenu', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:03:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (103, '部门管理', '', 2, 4, 1, 'dept', 'fa:address-card', 'system/dept/index', 'SystemDept', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (104, '岗位管理', '', 2, 5, 1, 'post', 'fa:address-book-o', 'system/post/index', 'SystemPost', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (105, '字典管理', '', 2, 6, 1, 'dict', 'ep:collection', 'system/dict/index', 'SystemDictType', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:07:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (106, '配置管理', '', 2, 8, 2, 'config', 'fa:connectdevelop', 'infra/config/index', 'InfraConfig', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:02:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (107, '通知公告', '', 2, 4, 2739, 'notice', 'ep:takeaway-box', 'system/notice/index', 'SystemNotice', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-22 23:56:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (108, '审计日志', '', 1, 9, 1, 'log', 'ep:document-copy', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:08:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (109, '令牌管理', '', 2, 2, 1261, 'token', 'fa:key', 'system/oauth2/token/index', 'SystemTokenClient', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:13:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (110, '定时任务', '', 2, 7, 2, 'job', 'fa-solid:tasks', 'infra/job/index', 'InfraJob', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:57:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (111, 'MySQL 监控', '', 2, 1, 2740, 'druid', 'fa-solid:box', 'infra/druid/index', 'InfraDruid', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:05:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (112, 'Java 监控', '', 2, 3, 2740, 'admin-server', 'ep:coffee-cup', 'infra/server/index', 'InfraAdminServer', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (113, 'Redis 监控', '', 2, 2, 2740, 'redis', 'fa:reddit-square', 'infra/redis/index', 'InfraRedis', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (114, '表单构建', 'infra:build:list', 2, 2, 2, 'build', 'fa:wpforms', 'infra/build/index', 'InfraBuild', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (115, '代码生成', 'infra:codegen:query', 2, 1, 2, 'codegen', 'ep:document-copy', 'infra/codegen/index', 'InfraCodegen', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (116, 'API 接口', 'infra:swagger:list', 2, 3, 2, 'swagger', 'fa:fighter-jet', 'infra/swagger/index', 'InfraSwagger', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:01:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (500, '操作日志', '', 2, 1, 108, 'operate-log', 'ep:position', 'system/operatelog/index', 'SystemOperateLog', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:09:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (501, '登录日志', '', 2, 2, 108, 'login-log', 'ep:promotion', 'system/loginlog/index', 'SystemLoginLog', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:10:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1001, '用户查询', 'system:user:query', 3, 1, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1002, '用户新增', 'system:user:create', 3, 2, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1003, '用户修改', 'system:user:update', 3, 3, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1004, '用户删除', 'system:user:delete', 3, 4, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1005, '用户导出', 'system:user:export', 3, 5, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1006, '用户导入', 'system:user:import', 3, 6, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1007, '重置密码', 'system:user:update-password', 3, 7, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1008, '角色查询', 'system:role:query', 3, 1, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1009, '角色新增', 'system:role:create', 3, 2, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1010, '角色修改', 'system:role:update', 3, 3, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1011, '角色删除', 'system:role:delete', 3, 4, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1012, '角色导出', 'system:role:export', 3, 5, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1013, '菜单查询', 'system:menu:query', 3, 1, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1014, '菜单新增', 'system:menu:create', 3, 2, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1015, '菜单修改', 'system:menu:update', 3, 3, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1016, '菜单删除', 'system:menu:delete', 3, 4, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1017, '部门查询', 'system:dept:query', 3, 1, 103, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1018, '部门新增', 'system:dept:create', 3, 2, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1019, '部门修改', 'system:dept:update', 3, 3, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1020, '部门删除', 'system:dept:delete', 3, 4, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1021, '岗位查询', 'system:post:query', 3, 1, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1022, '岗位新增', 'system:post:create', 3, 2, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1023, '岗位修改', 'system:post:update', 3, 3, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1024, '岗位删除', 'system:post:delete', 3, 4, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1025, '岗位导出', 'system:post:export', 3, 5, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1026, '字典查询', 'system:dict:query', 3, 1, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1027, '字典新增', 'system:dict:create', 3, 2, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1028, '字典修改', 'system:dict:update', 3, 3, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1029, '字典删除', 'system:dict:delete', 3, 4, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1030, '字典导出', 'system:dict:export', 3, 5, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1031, '配置查询', 'infra:config:query', 3, 1, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1032, '配置新增', 'infra:config:create', 3, 2, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1033, '配置修改', 'infra:config:update', 3, 3, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1034, '配置删除', 'infra:config:delete', 3, 4, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1035, '配置导出', 'infra:config:export', 3, 5, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1036, '公告查询', 'system:notice:query', 3, 1, 107, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1037, '公告新增', 'system:notice:create', 3, 2, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1038, '公告修改', 'system:notice:update', 3, 3, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1039, '公告删除', 'system:notice:delete', 3, 4, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1040, '操作查询', 'system:operate-log:query', 3, 1, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1042, '日志导出', 'system:operate-log:export', 3, 2, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1043, '登录查询', 'system:login-log:query', 3, 1, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1045, '日志导出', 'system:login-log:export', 3, 3, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1046, '令牌列表', 'system:oauth2-token:page', 3, 1, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1048, '令牌删除', 'system:oauth2-token:delete', 3, 2, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1050, '任务新增', 'infra:job:create', 3, 2, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1051, '任务修改', 'infra:job:update', 3, 3, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1052, '任务删除', 'infra:job:delete', 3, 4, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1053, '状态修改', 'infra:job:update', 3, 5, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1054, '任务导出', 'infra:job:export', 3, 7, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1056, '生成修改', 'infra:codegen:update', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1057, '生成删除', 'infra:codegen:delete', 3, 3, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1058, '导入代码', 'infra:codegen:create', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1059, '预览代码', 'infra:codegen:preview', 3, 4, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1060, '生成代码', 'infra:codegen:download', 3, 5, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1063, '设置角色菜单权限', 'system:permission:assign-role-menu', 3, 6, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-06 17:53:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1064, '设置角色数据权限', 'system:permission:assign-role-data-scope', 3, 7, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-06 17:56:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1065, '设置用户角色', 'system:permission:assign-user-role', 3, 8, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-07 10:23:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1066, '获得 Redis 监控信息', 'infra:redis:get-monitor-info', 3, 1, 113, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-26 01:02:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1067, '获得 Redis Key 列表', 'infra:redis:get-key-list', 3, 2, 113, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-26 01:02:52', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1070, '代码生成案例', '', 1, 1, 2, 'demo', 'ep:aim', 'infra/testDemo/index', NULL, 0, '1', '1', '1', '', '2021-02-06 12:42:49', '1', '2023-11-15 23:45:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1075, '任务触发', 'infra:job:trigger', 3, 8, 110, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-07 13:03:10', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1077, '链路追踪', '', 2, 4, 2740, 'skywalking', 'fa:eye', 'infra/skywalking/index', 'InfraSkyWalking', 0, '1', '1', '1', '', '2021-02-08 20:41:31', '1', '2024-04-23 00:07:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1078, '访问日志', '', 2, 1, 1083, 'api-access-log', 'ep:place', 'infra/apiAccessLog/index', 'InfraApiAccessLog', 0, '1', '1', '1', '', '2021-02-26 01:32:59', '1', '2024-02-29 08:54:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1082, '日志导出', 'infra:api-access-log:export', 3, 2, 1078, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 01:32:59', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1083, 'API 日志', '', 2, 4, 2, 'log', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '', '2021-02-26 02:18:24', '1', '2024-04-22 23:58:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1084, '错误日志', 'infra:api-error-log:query', 2, 2, 1083, 'api-error-log', 'ep:warning-filled', 'infra/apiErrorLog/index', 'InfraApiErrorLog', 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2024-02-29 08:55:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1085, '日志处理', 'infra:api-error-log:update-status', 3, 2, 1084, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1086, '日志导出', 'infra:api-error-log:export', 3, 3, 1084, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1087, '任务查询', 'infra:job:query', 3, 1, 110, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:26:19', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1088, '日志查询', 'infra:api-access-log:query', 3, 1, 1078, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:28:04', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1089, '日志查询', 'infra:api-error-log:query', 3, 1, 1084, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:29:09', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1090, '文件列表', '', 2, 5, 1243, 'file', 'ep:upload-filled', 'infra/file/index', 'InfraFile', 0, '1', '1', '1', '', '2021-03-12 20:16:20', '1', '2024-02-29 08:53:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', NULL, 0, '1', '1', '1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1092, '文件删除', 'infra:file:delete', 3, 4, 1090, '', '', '', NULL, 0, '1', '1', '1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1093, '短信管理', '', 1, 1, 2739, 'sms', 'ep:message', NULL, NULL, 0, '1', '1', '1', '1', '2021-04-05 01:10:16', '1', '2024-04-22 23:56:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1094, '短信渠道', '', 2, 0, 1093, 'sms-channel', 'fa:stack-exchange', 'system/sms/channel/index', 'SystemSmsChannel', 0, '1', '1', '1', '', '2021-04-01 11:07:15', '1', '2024-02-29 01:15:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1095, '短信渠道查询', 'system:sms-channel:query', 3, 1, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1096, '短信渠道创建', 'system:sms-channel:create', 3, 2, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1097, '短信渠道更新', 'system:sms-channel:update', 3, 3, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1098, '短信渠道删除', 'system:sms-channel:delete', 3, 4, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1100, '短信模板', '', 2, 1, 1093, 'sms-template', 'ep:connection', 'system/sms/template/index', 'SystemSmsTemplate', 0, '1', '1', '1', '', '2021-04-01 17:35:17', '1', '2024-02-29 01:16:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1101, '短信模板查询', 'system:sms-template:query', 3, 1, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1102, '短信模板创建', 'system:sms-template:create', 3, 2, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1103, '短信模板更新', 'system:sms-template:update', 3, 3, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1104, '短信模板删除', 'system:sms-template:delete', 3, 4, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1105, '短信模板导出', 'system:sms-template:export', 3, 5, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1106, '发送测试短信', 'system:sms-template:send-sms', 3, 6, 1100, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-04-11 00:26:40', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1107, '短信日志', '', 2, 2, 1093, 'sms-log', 'fa:edit', 'system/sms/log/index', 'SystemSmsLog', 0, '1', '1', '1', '', '2021-04-11 08:37:05', '1', '2024-02-29 08:49:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1108, '短信日志查询', 'system:sms-log:query', 3, 1, 1107, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1109, '短信日志导出', 'system:sms-log:export', 3, 5, 1107, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1117, '支付管理', '', 1, 30, 0, '/pay', 'ep:money', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-25 16:43:41', '1', '2024-02-29 08:58:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1118, '请假查询', '', 2, 0, 5, 'leave', 'fa:leanpub', 'bpm/oa/leave/index', 'BpmOALeave', 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2024-02-29 12:38:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1119, '请假申请查询', 'bpm:oa-leave:query', 3, 1, 1118, '', '', '', NULL, 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1120, '请假申请创建', 'bpm:oa-leave:create', 3, 2, 1118, '', '', '', NULL, 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1126, '应用信息', '', 2, 1, 1117, 'app', 'fa:apple', 'pay/app/index', 'PayApp', 0, '1', '1', '1', '', '2021-11-10 01:13:30', '1', '2024-02-29 08:59:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1127, '支付应用信息查询', 'pay:app:query', 3, 1, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1128, '支付应用信息创建', 'pay:app:create', 3, 2, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1129, '支付应用信息更新', 'pay:app:update', 3, 3, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1130, '支付应用信息删除', 'pay:app:delete', 3, 4, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1132, '秘钥解析', 'pay:channel:parsing', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1133, '支付商户信息查询', 'pay:merchant:query', 3, 1, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1134, '支付商户信息创建', 'pay:merchant:create', 3, 2, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1135, '支付商户信息更新', 'pay:merchant:update', 3, 3, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1136, '支付商户信息删除', 'pay:merchant:delete', 3, 4, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1137, '支付商户信息导出', 'pay:merchant:export', 3, 5, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1138, '租户列表', '', 2, 0, 1224, 'list', 'ep:house', 'system/tenant/index', 'SystemTenant', 0, '1', '1', '1', '', '2021-12-14 12:31:43', '1', '2024-02-29 01:01:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1139, '租户查询', 'system:tenant:query', 3, 1, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1140, '租户创建', 'system:tenant:create', 3, 2, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1141, '租户更新', 'system:tenant:update', 3, 3, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1142, '租户删除', 'system:tenant:delete', 3, 4, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1143, '租户导出', 'system:tenant:export', 3, 5, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1150, '秘钥解析', '', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1161, '退款订单', '', 2, 3, 1117, 'refund', 'fa:registered', 'pay/refund/index', 'PayRefund', 0, '1', '1', '1', '', '2021-12-25 08:29:07', '1', '2024-02-29 08:59:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1162, '退款订单查询', 'pay:refund:query', 3, 1, 1161, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1166, '退款订单导出', 'pay:refund:export', 3, 5, 1161, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1173, '支付订单', '', 2, 2, 1117, 'order', 'fa:cc-paypal', 'pay/order/index', 'PayOrder', 0, '1', '1', '1', '', '2021-12-25 08:49:43', '1', '2024-02-29 08:59:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1174, '支付订单查询', 'pay:order:query', 3, 1, 1173, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1178, '支付订单导出', 'pay:order:export', 3, 5, 1173, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1185, '工作流程', '', 1, 50, 0, '/bpm', 'fa:medium', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-30 20:26:36', '1', '2024-02-29 12:43:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1186, '流程管理', '', 1, 10, 1185, 'manager', 'fa:dedent', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-30 20:28:30', '1', '2024-02-29 12:36:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1187, '流程表单', '', 2, 2, 1186, 'form', 'fa:hdd-o', 'bpm/form/index', 'BpmForm', 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2024-03-19 12:25:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1188, '表单查询', 'bpm:form:query', 3, 1, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1189, '表单创建', 'bpm:form:create', 3, 2, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1190, '表单更新', 'bpm:form:update', 3, 3, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1191, '表单删除', 'bpm:form:delete', 3, 4, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1192, '表单导出', 'bpm:form:export', 3, 5, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1193, '流程模型', '', 2, 1, 1186, 'model', 'fa-solid:project-diagram', 'bpm/model/index', 'BpmModel', 0, '1', '1', '1', '1', '2021-12-31 23:24:58', '1', '2024-03-19 12:25:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1194, '模型查询', 'bpm:model:query', 3, 1, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:01:10', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1195, '模型创建', 'bpm:model:create', 3, 2, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:01:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1197, '模型更新', 'bpm:model:update', 3, 4, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:02:28', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1198, '模型删除', 'bpm:model:delete', 3, 5, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:02:43', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1199, '模型发布', 'bpm:model:deploy', 3, 6, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:03:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1200, '审批中心', '', 2, 20, 1185, 'task', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '1', '2022-01-07 23:51:48', '1', '2024-03-21 00:33:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1201, '我的流程', '', 2, 1, 1200, 'my', 'fa-solid:book', 'bpm/processInstance/index', 'BpmProcessInstanceMy', 0, '1', '1', '1', '', '2022-01-07 15:53:44', '1', '2024-03-21 23:52:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1202, '流程实例的查询', 'bpm:process-instance:query', 3, 1, 1201, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-07 15:53:44', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1207, '待办任务', '', 2, 10, 1200, 'todo', 'fa:slack', 'bpm/task/todo/index', 'BpmTodoTask', 0, '1', '1', '1', '1', '2022-01-08 10:33:37', '1', '2024-02-29 12:37:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1208, '已办任务', '', 2, 20, 1200, 'done', 'fa:delicious', 'bpm/task/done/index', 'BpmDoneTask', 0, '1', '1', '1', '1', '2022-01-08 10:34:13', '1', '2024-02-29 12:37:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1209, '用户分组', '', 2, 4, 1186, 'user-group', 'fa:user-secret', 'bpm/group/index', 'BpmUserGroup', 0, '1', '1', '1', '', '2022-01-14 02:14:20', '1', '2024-03-21 23:55:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1210, '用户组查询', 'bpm:user-group:query', 3, 1, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1211, '用户组创建', 'bpm:user-group:create', 3, 2, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1212, '用户组更新', 'bpm:user-group:update', 3, 3, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1213, '用户组删除', 'bpm:user-group:delete', 3, 4, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1215, '流程定义查询', 'bpm:process-definition:query', 3, 10, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:21:43', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1216, '流程任务分配规则查询', 'bpm:task-assign-rule:query', 3, 20, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:26:53', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1217, '流程任务分配规则创建', 'bpm:task-assign-rule:create', 3, 21, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:28:15', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1218, '流程任务分配规则更新', 'bpm:task-assign-rule:update', 3, 22, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:28:41', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1219, '流程实例的创建', 'bpm:process-instance:create', 3, 2, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:36:15', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1220, '流程实例的取消', 'bpm:process-instance:cancel', 3, 3, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:36:33', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1221, '流程任务的查询', 'bpm:task:query', 3, 1, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:38:52', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1222, '流程任务的更新', 'bpm:task:update', 3, 2, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:39:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1224, '租户管理', '', 2, 0, 1, 'tenant', 'fa-solid:house-user', NULL, NULL, 0, '1', '1', '1', '1', '2022-02-20 01:41:13', '1', '2024-02-29 00:59:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1225, '租户套餐', '', 2, 0, 1224, 'package', 'fa:bars', 'system/tenantPackage/index', 'SystemTenantPackage', 0, '1', '1', '1', '', '2022-02-19 17:44:06', '1', '2024-02-29 01:01:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1226, '租户套餐查询', 'system:tenant-package:query', 3, 1, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1227, '租户套餐创建', 'system:tenant-package:create', 3, 2, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1228, '租户套餐更新', 'system:tenant-package:update', 3, 3, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1229, '租户套餐删除', 'system:tenant-package:delete', 3, 4, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1237, '文件配置', '', 2, 0, 1243, 'file-config', 'fa-solid:file-signature', 'infra/fileConfig/index', 'InfraFileConfig', 0, '1', '1', '1', '', '2022-03-15 14:35:28', '1', '2024-02-29 08:52:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1238, '文件配置查询', 'infra:file-config:query', 3, 1, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1239, '文件配置创建', 'infra:file-config:create', 3, 2, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1240, '文件配置更新', 'infra:file-config:update', 3, 3, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, '1', '1', '1', '1', '2022-03-16 23:47:40', '1', '2024-04-23 00:02:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, '1', '1', '1', '1', '2022-04-23 01:03:15', '1', '2025-04-29 17:45:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, '1', '1', '1', '', '2022-04-27 14:37:32', '1', '2024-02-29 08:51:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1258, '数据源配置更新', 'infra:data-source-config:update', 3, 3, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1259, '数据源配置删除', 'infra:data-source-config:delete', 3, 4, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1260, '数据源配置导出', 'infra:data-source-config:export', 3, 5, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1261, 'OAuth 2.0', '', 2, 10, 1, 'oauth2', 'fa:dashcube', NULL, NULL, 0, '1', '1', '1', '1', '2022-05-09 23:38:17', '1', '2024-02-29 01:12:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1263, '应用管理', '', 2, 0, 1261, 'oauth2/application', 'fa:hdd-o', 'system/oauth2/client/index', 'SystemOAuth2Client', 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2024-02-29 01:13:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1264, '客户端查询', 'system:oauth2-client:query', 3, 1, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1265, '客户端创建', 'system:oauth2-client:create', 3, 2, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1266, '客户端更新', 'system:oauth2-client:update', 3, 3, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1267, '客户端删除', 'system:oauth2-client:delete', 3, 4, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1281, '报表管理', '', 2, 40, 0, '/report', 'ep:pie-chart', NULL, NULL, 0, '1', '1', '1', '1', '2022-07-10 20:22:15', '1', '2024-02-29 12:33:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1282, '报表设计器', '', 2, 1, 1281, 'jimu-report', 'ep:trend-charts', 'report/jmreport/index', 'JimuReport', 0, '1', '1', '1', '1', '2022-07-10 20:26:36', '1', '2025-05-03 09:57:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2000, '商品中心', '', 1, 60, 2362, 'product', 'fa:product-hunt', NULL, NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '1', '2023-09-30 11:52:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2002, '商品分类', '', 2, 2, 2000, 'category', 'ep:cellphone', 'mall/product/category/index', 'ProductCategory', 0, '1', '1', '1', '', '2022-07-29 15:53:53', '1', '2023-08-21 10:27:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2003, '分类查询', 'product:category:query', 3, 1, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2004, '分类创建', 'product:category:create', 3, 2, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2005, '分类更新', 'product:category:update', 3, 3, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2006, '分类删除', 'product:category:delete', 3, 4, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2008, '商品品牌', '', 2, 3, 2000, 'brand', 'ep:chicken', 'mall/product/brand/index', 'ProductBrand', 0, '1', '1', '1', '', '2022-07-30 13:52:44', '1', '2023-08-21 10:27:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2009, '品牌查询', 'product:brand:query', 3, 1, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2010, '品牌创建', 'product:brand:create', 3, 2, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2011, '品牌更新', 'product:brand:update', 3, 3, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2012, '品牌删除', 'product:brand:delete', 3, 4, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2014, '商品列表', '', 2, 1, 2000, 'spu', 'ep:apple', 'mall/product/spu/index', 'ProductSpu', 0, '1', '1', '1', '', '2022-07-30 14:22:58', '1', '2023-08-21 10:27:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2015, '商品查询', 'product:spu:query', 3, 1, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2016, '商品创建', 'product:spu:create', 3, 2, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2017, '商品更新', 'product:spu:update', 3, 3, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2018, '商品删除', 'product:spu:delete', 3, 4, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2019, '商品属性', '', 2, 4, 2000, 'property', 'ep:cold-drink', 'mall/product/property/index', 'ProductProperty', 0, '1', '1', '1', '', '2022-08-01 14:55:35', '1', '2023-08-26 11:01:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2020, '规格查询', 'product:property:query', 3, 1, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2021, '规格创建', 'product:property:create', 3, 2, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2022, '规格更新', 'product:property:update', 3, 3, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2023, '规格删除', 'product:property:delete', 3, 4, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2025, 'Banner', '', 2, 100, 2387, 'banner', 'fa:bandcamp', 'mall/promotion/banner/index', NULL, 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2026, 'Banner查询', 'promotion:banner:query', 3, 1, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2027, 'Banner创建', 'promotion:banner:create', 3, 2, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2028, 'Banner更新', 'promotion:banner:update', 3, 3, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2029, 'Banner删除', 'promotion:banner:delete', 3, 4, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2030, '营销中心', '', 1, 70, 2362, 'promotion', 'ep:present', NULL, NULL, 0, '1', '1', '1', '1', '2022-10-31 21:25:09', '1', '2023-09-30 11:54:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2032, '优惠劵列表', '', 2, 1, 2365, 'template', 'ep:discount', 'mall/promotion/coupon/template/index', 'PromotionCouponTemplate', 0, '1', '1', '1', '', '2022-10-31 22:27:14', '1', '2023-10-03 12:40:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2033, '优惠劵模板查询', 'promotion:coupon-template:query', 3, 1, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2034, '优惠劵模板创建', 'promotion:coupon-template:create', 3, 2, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2035, '优惠劵模板更新', 'promotion:coupon-template:update', 3, 3, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2036, '优惠劵模板删除', 'promotion:coupon-template:delete', 3, 4, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2038, '领取记录', '', 2, 2, 2365, 'list', 'ep:collection-tag', 'mall/promotion/coupon/index', 'PromotionCoupon', 0, '1', '1', '1', '', '2022-11-03 23:21:31', '1', '2023-10-03 12:55:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2039, '优惠劵查询', 'promotion:coupon:query', 3, 1, 2038, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2040, '优惠劵删除', 'promotion:coupon:delete', 3, 4, 2038, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2041, '满减送', '', 2, 10, 2390, 'reward-activity', 'ep:goblet-square-full', 'mall/promotion/rewardActivity/index', 'PromotionRewardActivity', 0, '1', '1', '1', '', '2022-11-04 23:47:49', '1', '2023-10-21 19:24:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2042, '满减送活动查询', 'promotion:reward-activity:query', 3, 1, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2043, '满减送活动创建', 'promotion:reward-activity:create', 3, 2, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2044, '满减送活动更新', 'promotion:reward-activity:update', 3, 3, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2045, '满减送活动删除', 'promotion:reward-activity:delete', 3, 4, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2046, '满减送活动关闭', 'promotion:reward-activity:close', 3, 5, 2041, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-11-05 10:42:53', '1', '2022-11-05 10:42:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2047, '限时折扣', '', 2, 7, 2390, 'discount-activity', 'ep:timer', 'mall/promotion/discountActivity/index', 'PromotionDiscountActivity', 0, '1', '1', '1', '', '2022-11-05 17:12:15', '1', '2023-10-21 19:24:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2048, '限时折扣活动查询', 'promotion:discount-activity:query', 3, 1, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2049, '限时折扣活动创建', 'promotion:discount-activity:create', 3, 2, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2050, '限时折扣活动更新', 'promotion:discount-activity:update', 3, 3, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2051, '限时折扣活动删除', 'promotion:discount-activity:delete', 3, 4, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2052, '限时折扣活动关闭', 'promotion:discount-activity:close', 3, 5, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2059, '秒杀商品', '', 2, 2, 2209, 'activity', 'ep:basketball', 'mall/promotion/seckill/activity/index', 'PromotionSeckillActivity', 0, '1', '1', '1', '', '2022-11-06 22:24:49', '1', '2023-06-24 18:57:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2060, '秒杀活动查询', 'promotion:seckill-activity:query', 3, 1, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2061, '秒杀活动创建', 'promotion:seckill-activity:create', 3, 2, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2062, '秒杀活动更新', 'promotion:seckill-activity:update', 3, 3, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2063, '秒杀活动删除', 'promotion:seckill-activity:delete', 3, 4, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2066, '秒杀时段', '', 2, 1, 2209, 'config', 'ep:baseball', 'mall/promotion/seckill/config/index', 'PromotionSeckillConfig', 0, '1', '1', '1', '', '2022-11-15 19:46:50', '1', '2023-06-24 18:57:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2067, '秒杀时段查询', 'promotion:seckill-config:query', 3, 1, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2068, '秒杀时段创建', 'promotion:seckill-config:create', 3, 2, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:48:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2069, '秒杀时段更新', 'promotion:seckill-config:update', 3, 3, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2070, '秒杀时段删除', 'promotion:seckill-config:delete', 3, 4, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2072, '订单中心', '', 1, 65, 2362, 'trade', 'ep:eleme', NULL, NULL, 0, '1', '1', '1', '1', '2022-11-19 18:57:19', '1', '2023-09-30 11:54:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2073, '售后退款', '', 2, 2, 2072, 'after-sale', 'ep:refrigerator', 'mall/trade/afterSale/index', 'TradeAfterSale', 0, '1', '1', '1', '', '2022-11-19 20:15:32', '1', '2023-10-01 21:42:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2074, '售后查询', 'trade:after-sale:query', 3, 1, 2073, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-19 20:15:33', '1', '2022-12-10 21:04:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2075, '秒杀活动关闭', 'promotion:seckill-activity:close', 3, 5, 2059, '', '', '', '', 0, '1', '1', '1', '1', '2022-11-28 20:20:15', '1', '2023-10-03 18:34:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2076, '订单列表', '', 2, 1, 2072, 'order', 'ep:list', 'mall/trade/order/index', 'TradeOrder', 0, '1', '1', '1', '1', '2022-12-10 21:05:44', '1', '2023-10-01 21:42:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2083, '地区管理', '', 2, 14, 1, 'area', 'fa:map-marker', 'system/area/index', 'SystemArea', 0, '1', '1', '1', '1', '2022-12-23 17:35:05', '1', '2024-02-29 08:50:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2084, '公众号管理', '', 1, 100, 0, '/mp', 'ep:compass', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-01 20:11:04', '1', '2024-02-29 12:39:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2085, '账号管理', '', 2, 1, 2084, 'account', 'fa:user', 'mp/account/index', 'MpAccount', 0, '1', '1', '1', '1', '2023-01-01 20:13:31', '1', '2024-02-29 12:42:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2086, '新增账号', 'mp:account:create', 3, 1, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-01 20:21:40', '1', '2023-01-07 17:32:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2087, '修改账号', 'mp:account:update', 3, 2, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:32:46', '1', '2023-01-07 17:32:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2088, '查询账号', 'mp:account:query', 3, 0, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:07', '1', '2023-01-07 17:33:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2089, '删除账号', 'mp:account:delete', 3, 3, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:21', '1', '2023-01-07 17:33:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2090, '生成二维码', 'mp:account:qr-code', 3, 4, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:58', '1', '2023-01-07 17:33:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2091, '清空 API 配额', 'mp:account:clear-quota', 3, 5, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 18:20:32', '1', '2023-01-07 18:20:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2092, '数据统计', 'mp:statistics:query', 2, 2, 2084, 'statistics', 'ep:trend-charts', 'mp/statistics/index', 'MpStatistics', 0, '1', '1', '1', '1', '2023-01-07 20:17:36', '1', '2024-02-29 12:42:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2093, '标签管理', '', 2, 3, 2084, 'tag', 'ep:collection-tag', 'mp/tag/index', 'MpTag', 0, '1', '1', '1', '1', '2023-01-08 11:37:32', '1', '2024-02-29 12:42:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2094, '查询标签', 'mp:tag:query', 3, 0, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:03', '1', '2023-01-08 11:59:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2095, '新增标签', 'mp:tag:create', 3, 1, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:23', '1', '2023-01-08 11:59:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2096, '修改标签', 'mp:tag:update', 3, 2, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:41', '1', '2023-01-08 11:59:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2097, '删除标签', 'mp:tag:delete', 3, 3, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 12:00:04', '1', '2023-01-08 12:00:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2098, '同步标签', 'mp:tag:sync', 3, 4, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 12:00:29', '1', '2023-01-08 12:00:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2099, '粉丝管理', '', 2, 4, 2084, 'user', 'fa:user-secret', 'mp/user/index', 'MpUser', 0, '1', '1', '1', '1', '2023-01-08 16:51:20', '1', '2024-02-29 12:42:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2100, '查询粉丝', 'mp:user:query', 3, 0, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:16:59', '1', '2023-01-08 17:17:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2101, '修改粉丝', 'mp:user:update', 3, 1, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:17:11', '1', '2023-01-08 17:17:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2102, '同步粉丝', 'mp:user:sync', 3, 2, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:17:40', '1', '2023-01-08 17:17:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2103, '消息管理', '', 2, 5, 2084, 'message', 'ep:message', 'mp/message/index', 'MpMessage', 0, '1', '1', '1', '1', '2023-01-08 18:44:19', '1', '2024-02-29 12:42:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2104, '图文发表记录', '', 2, 10, 2084, 'free-publish', 'ep:edit-pen', 'mp/freePublish/index', 'MpFreePublish', 0, '1', '1', '1', '1', '2023-01-13 00:30:50', '1', '2024-02-29 12:43:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2105, '查询发布列表', 'mp:free-publish:query', 3, 1, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:19:17', '1', '2023-01-13 07:19:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2106, '发布草稿', 'mp:free-publish:submit', 3, 2, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:19:46', '1', '2023-01-13 07:19:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2107, '删除发布记录', 'mp:free-publish:delete', 3, 3, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:20:01', '1', '2023-01-13 07:20:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2108, '图文草稿箱', '', 2, 9, 2084, 'draft', 'ep:edit', 'mp/draft/index', 'MpDraft', 0, '1', '1', '1', '1', '2023-01-13 07:40:21', '1', '2024-02-29 12:43:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2109, '新建草稿', 'mp:draft:create', 3, 1, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 23:15:30', '1', '2023-01-13 23:15:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2110, '修改草稿', 'mp:draft:update', 3, 2, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:08:47', '1', '2023-01-14 10:08:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2111, '查询草稿', 'mp:draft:query', 3, 0, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:09:01', '1', '2023-01-14 10:09:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2112, '删除草稿', 'mp:draft:delete', 3, 3, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:09:19', '1', '2023-01-14 10:09:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2113, '素材管理', '', 2, 8, 2084, 'material', 'ep:basketball', 'mp/material/index', 'MpMaterial', 0, '1', '1', '1', '1', '2023-01-14 14:12:07', '1', '2024-02-29 12:43:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2114, '上传临时素材', 'mp:material:upload-temporary', 3, 1, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:33:55', '1', '2023-01-14 15:33:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2115, '上传永久素材', 'mp:material:upload-permanent', 3, 2, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:34:14', '1', '2023-01-14 15:34:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2116, '删除素材', 'mp:material:delete', 3, 3, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:35:37', '1', '2023-01-14 15:35:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2117, '上传图文图片', 'mp:material:upload-news-image', 3, 4, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:36:31', '1', '2023-01-14 15:36:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2118, '查询素材', 'mp:material:query', 3, 5, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:39:22', '1', '2023-01-14 15:39:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2119, '菜单管理', '', 2, 6, 2084, 'menu', 'ep:menu', 'mp/menu/index', 'MpMenu', 0, '1', '1', '1', '1', '2023-01-14 17:43:54', '1', '2025-04-01 20:21:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2120, '自动回复', '', 2, 7, 2084, 'auto-reply', 'fa-solid:republican', 'mp/autoReply/index', 'MpAutoReply', 0, '1', '1', '1', '1', '2023-01-15 22:13:09', '1', '2024-02-29 12:43:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2121, '查询回复', 'mp:auto-reply:query', 3, 0, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:28:41', '1', '2023-01-16 22:28:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2122, '新增回复', 'mp:auto-reply:create', 3, 1, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:28:54', '1', '2023-01-16 22:28:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2123, '修改回复', 'mp:auto-reply:update', 3, 2, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:29:05', '1', '2023-01-16 22:29:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2124, '删除回复', 'mp:auto-reply:delete', 3, 3, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:29:34', '1', '2023-01-16 22:29:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2125, '查询菜单', 'mp:menu:query', 3, 0, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:05:41', '1', '2023-01-17 23:05:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2126, '保存菜单', 'mp:menu:save', 3, 1, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:06:01', '1', '2023-01-17 23:06:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2127, '删除菜单', 'mp:menu:delete', 3, 2, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:06:16', '1', '2023-01-17 23:06:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2128, '查询消息', 'mp:message:query', 3, 0, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:07:14', '1', '2023-01-17 23:07:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2129, '发送消息', 'mp:message:send', 3, 1, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:07:26', '1', '2023-01-17 23:07:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2130, '邮箱管理', '', 2, 2, 2739, 'mail', 'fa-solid:mail-bulk', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-25 17:27:44', '1', '2024-04-22 23:56:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2131, '邮箱账号', '', 2, 0, 2130, 'mail-account', 'fa:universal-access', 'system/mail/account/index', 'SystemMailAccount', 0, '1', '1', '1', '', '2023-01-25 09:33:48', '1', '2024-02-29 08:48:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2132, '账号查询', 'system:mail-account:query', 3, 1, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2133, '账号创建', 'system:mail-account:create', 3, 2, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2134, '账号更新', 'system:mail-account:update', 3, 3, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2135, '账号删除', 'system:mail-account:delete', 3, 4, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2136, '邮件模版', '', 2, 0, 2130, 'mail-template', 'fa:tag', 'system/mail/template/index', 'SystemMailTemplate', 0, '1', '1', '1', '', '2023-01-25 12:05:31', '1', '2024-02-29 08:48:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2137, '模版查询', 'system:mail-template:query', 3, 1, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2138, '模版创建', 'system:mail-template:create', 3, 2, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2139, '模版更新', 'system:mail-template:update', 3, 3, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2140, '模版删除', 'system:mail-template:delete', 3, 4, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2141, '邮件记录', '', 2, 0, 2130, 'mail-log', 'fa:edit', 'system/mail/log/index', 'SystemMailLog', 0, '1', '1', '1', '', '2023-01-26 02:16:50', '1', '2024-02-29 08:48:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2142, '日志查询', 'system:mail-log:query', 3, 1, 2141, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-26 02:16:50', '', '2023-01-26 02:16:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2143, '发送测试邮件', 'system:mail-template:send-mail', 3, 5, 2136, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-26 23:29:15', '1', '2023-01-26 23:29:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2144, '站内信管理', '', 1, 3, 2739, 'notify', 'ep:message-box', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-28 10:25:18', '1', '2024-04-22 23:56:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2145, '模板管理', '', 2, 0, 2144, 'notify-template', 'fa:archive', 'system/notify/template/index', 'SystemNotifyTemplate', 0, '1', '1', '1', '', '2023-01-28 02:26:42', '1', '2024-02-29 08:49:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2146, '站内信模板查询', 'system:notify-template:query', 3, 1, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2147, '站内信模板创建', 'system:notify-template:create', 3, 2, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2148, '站内信模板更新', 'system:notify-template:update', 3, 3, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2149, '站内信模板删除', 'system:notify-template:delete', 3, 4, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2150, '发送测试站内信', 'system:notify-template:send-notify', 3, 5, 2145, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-28 10:54:43', '1', '2023-01-28 10:54:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2151, '消息记录', '', 2, 0, 2144, 'notify-message', 'fa:edit', 'system/notify/message/index', 'SystemNotifyMessage', 0, '1', '1', '1', '', '2023-01-28 04:28:22', '1', '2024-02-29 08:49:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2152, '站内信消息查询', 'system:notify-message:query', 3, 1, 2151, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 04:28:22', '', '2023-01-28 04:28:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2153, '大屏设计器', '', 2, 2, 1281, 'go-view', 'fa:area-chart', 'report/goview/index', 'GoView', 0, '1', '1', '1', '1', '2023-02-07 00:03:19', '1', '2025-05-03 09:57:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2154, '创建项目', 'report:go-view-project:create', 3, 1, 2153, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-02-07 19:25:14', '1', '2023-02-07 19:25:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2155, '更新项目', 'report:go-view-project:update', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', '2023-02-07 19:25:34', '1', '2024-04-24 20:01:18', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:25:53', '1', '2023-02-07 19:25:53', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:26:15', '1', '2023-02-07 19:26:15', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:26:35', '1', '2023-02-07 19:26:35', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-10 22:46:28', '1', '2024-07-28 11:36:48', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-10 22:47:07', '1', '2023-12-02 21:32:29', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2023-02-11 14:21:42', '1', '2024-01-18 23:50:00', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2164, '配送管理', '', 1, 3, 2072, 'delivery', 'ep:shopping-cart', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:18:02', '1', '2023-09-28 10:58:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2165, '快递发货', '', 1, 0, 2164, 'express', 'ep:bicycle', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:22:06', '1', '2023-08-30 21:02:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2166, '门店自提', '', 1, 1, 2164, 'pick-up-store', 'ep:add-location', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:23:14', '1', '2023-08-30 21:03:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2167, '快递公司', '', 2, 0, 2165, 'express', 'ep:compass', 'mall/trade/delivery/express/index', 'Express', 0, '1', '1', '1', '1', '2023-05-18 09:27:21', '1', '2024-11-29 11:20:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2168, '快递公司查询', 'trade:delivery:express:query', 3, 1, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2169, '快递公司创建', 'trade:delivery:express:create', 3, 2, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2170, '快递公司更新', 'trade:delivery:express:update', 3, 3, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2171, '快递公司删除', 'trade:delivery:express:delete', 3, 4, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2172, '快递公司导出', 'trade:delivery:express:export', 3, 5, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2173, '运费模版', 'trade:delivery:express-template:query', 2, 1, 2165, 'express-template', 'ep:coordinate', 'mall/trade/delivery/expressTemplate/index', 'ExpressTemplate', 0, '1', '1', '1', '1', '2023-05-20 06:48:10', '1', '2023-08-30 21:03:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2174, '快递运费模板查询', 'trade:delivery:express-template:query', 3, 1, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2175, '快递运费模板创建', 'trade:delivery:express-template:create', 3, 2, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2176, '快递运费模板更新', 'trade:delivery:express-template:update', 3, 3, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2177, '快递运费模板删除', 'trade:delivery:express-template:delete', 3, 4, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2178, '快递运费模板导出', 'trade:delivery:express-template:export', 3, 5, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2179, '门店管理', '', 2, 1, 2166, 'pick-up-store', 'ep:basketball', 'mall/trade/delivery/pickUpStore/index', 'PickUpStore', 0, '1', '1', '1', '1', '2023-05-25 10:50:00', '1', '2023-08-30 21:03:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2180, '自提门店查询', 'trade:delivery:pick-up-store:query', 3, 1, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2181, '自提门店创建', 'trade:delivery:pick-up-store:create', 3, 2, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2182, '自提门店更新', 'trade:delivery:pick-up-store:update', 3, 3, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2183, '自提门店删除', 'trade:delivery:pick-up-store:delete', 3, 4, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2184, '自提门店导出', 'trade:delivery:pick-up-store:export', 3, 5, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2209, '秒杀活动', '', 2, 3, 2030, 'seckill', 'ep:place', '', '', 0, '1', '1', '1', '1', '2023-06-24 17:39:13', '1', '2023-06-24 18:55:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2262, '会员中心', '', 1, 55, 0, '/member', 'ep:bicycle', NULL, NULL, 0, '1', '1', '1', '1', '2023-06-10 00:42:03', '1', '2023-08-20 09:23:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2275, '会员配置', '', 2, 0, 2262, 'config', 'fa:archive', 'member/config/index', 'MemberConfig', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2023-10-01 23:41:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2276, '会员配置查询', 'member:config:query', 3, 1, 2275, '', '', '', '', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:48:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2277, '会员配置保存', 'member:config:save', 3, 2, 2275, '', '', '', '', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:49:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2281, '签到配置', '', 2, 2, 2300, 'config', 'ep:calendar', 'member/signin/config/index', 'SignInConfig', 0, '1', '1', '1', '', '2023-06-10 03:26:12', '1', '2023-08-20 19:25:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2282, '积分签到规则查询', 'point:sign-in-config:query', 3, 1, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2283, '积分签到规则创建', 'point:sign-in-config:create', 3, 2, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2284, '积分签到规则更新', 'point:sign-in-config:update', 3, 3, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2285, '积分签到规则删除', 'point:sign-in-config:delete', 3, 4, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2287, '会员积分', '', 2, 10, 2262, 'record', 'fa:asterisk', 'member/point/record/index', 'PointRecord', 0, '1', '1', '1', '', '2023-06-10 04:18:50', '1', '2023-10-01 23:42:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2288, '用户积分记录查询', 'point:record:query', 3, 1, 2287, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:18:50', '', '2023-06-10 04:18:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2293, '签到记录', '', 2, 3, 2300, 'record', 'ep:chicken', 'member/signin/record/index', 'SignInRecord', 0, '1', '1', '1', '', '2023-06-10 04:48:22', '1', '2023-08-20 19:26:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2294, '用户签到积分查询', 'point:sign-in-record:query', 3, 1, 2293, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2297, '用户签到积分删除', 'point:sign-in-record:delete', 3, 4, 2293, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2300, '会员签到', '', 1, 11, 2262, 'signin', 'ep:alarm-clock', '', '', 0, '1', '1', '1', '1', '2023-06-27 22:49:53', '1', '2023-08-20 09:23:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2301, '回调通知', '', 2, 5, 1117, 'notify', 'ep:mute-notification', 'pay/notify/index', 'PayNotify', 0, '1', '1', '1', '', '2023-07-20 04:41:32', '1', '2024-01-18 23:56:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2302, '支付通知查询', 'pay:notify:query', 3, 1, 2301, '', '', '', NULL, 0, '1', '1', '1', '', '2023-07-20 04:41:32', '', '2023-07-20 04:41:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2303, '拼团活动', '', 2, 3, 2030, 'combination', 'fa:group', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:19:54', '1', '2023-08-12 17:20:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2304, '拼团商品', '', 2, 1, 2303, 'acitivity', 'ep:apple', 'mall/promotion/combination/activity/index', 'PromotionCombinationActivity', 0, '1', '1', '1', '1', '2023-08-12 17:22:03', '1', '2023-08-12 17:22:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2305, '拼团活动查询', 'promotion:combination-activity:query', 3, 1, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:54:32', '1', '2023-11-24 11:57:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2306, '拼团活动创建', 'promotion:combination-activity:create', 3, 2, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:54:49', '1', '2023-08-12 17:54:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2307, '拼团活动更新', 'promotion:combination-activity:update', 3, 3, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:04', '1', '2023-08-12 17:55:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2308, '拼团活动删除', 'promotion:combination-activity:delete', 3, 4, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:23', '1', '2023-08-12 17:55:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2309, '拼团活动关闭', 'promotion:combination-activity:close', 3, 5, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:37', '1', '2023-10-06 10:51:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2310, '砍价活动', '', 2, 4, 2030, 'bargain', 'ep:box', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:27:25', '1', '2023-08-13 00:27:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2311, '砍价商品', '', 2, 1, 2310, 'activity', 'ep:burger', 'mall/promotion/bargain/activity/index', 'PromotionBargainActivity', 0, '1', '1', '1', '1', '2023-08-13 00:28:49', '1', '2023-10-05 01:16:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2312, '砍价活动查询', 'promotion:bargain-activity:query', 3, 1, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:30', '1', '2023-08-13 00:32:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2313, '砍价活动创建', 'promotion:bargain-activity:create', 3, 2, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:44', '1', '2023-08-13 00:32:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2314, '砍价活动更新', 'promotion:bargain-activity:update', 3, 3, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:55', '1', '2023-08-13 00:32:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2315, '砍价活动删除', 'promotion:bargain-activity:delete', 3, 4, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:34:50', '1', '2023-08-13 00:34:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2316, '砍价活动关闭', 'promotion:bargain-activity:close', 3, 5, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:35:02', '1', '2023-08-13 00:35:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2317, '会员管理', '', 2, 0, 2262, 'user', 'ep:avatar', 'member/user/index', 'MemberUser', 0, '1', '1', '1', '', '2023-08-19 04:12:15', '1', '2023-08-24 00:50:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2318, '会员用户查询', 'member:user:query', 3, 1, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2319, '会员用户更新', 'member:user:update', 3, 3, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2320, '会员标签', '', 2, 1, 2262, 'tag', 'ep:collection-tag', 'member/tag/index', 'MemberTag', 0, '1', '1', '1', '', '2023-08-20 01:03:08', '1', '2023-08-20 09:23:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2321, '会员标签查询', 'member:tag:query', 3, 1, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2322, '会员标签创建', 'member:tag:create', 3, 2, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2323, '会员标签更新', 'member:tag:update', 3, 3, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2324, '会员标签删除', 'member:tag:delete', 3, 4, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2325, '会员等级', '', 2, 2, 2262, 'level', 'fa:level-up', 'member/level/index', 'MemberLevel', 0, '1', '1', '1', '', '2023-08-22 12:41:01', '1', '2023-08-22 21:47:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2326, '会员等级查询', 'member:level:query', 3, 1, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2327, '会员等级创建', 'member:level:create', 3, 2, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2328, '会员等级更新', 'member:level:update', 3, 3, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2329, '会员等级删除', 'member:level:delete', 3, 4, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2330, '会员分组', '', 2, 3, 2262, 'group', 'fa:group', 'member/group/index', 'MemberGroup', 0, '1', '1', '1', '', '2023-08-22 13:50:06', '1', '2023-10-01 23:42:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2331, '用户分组查询', 'member:group:query', 3, 1, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2332, '用户分组创建', 'member:group:create', 3, 2, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2333, '用户分组更新', 'member:group:update', 3, 3, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2334, '用户分组删除', 'member:group:delete', 3, 4, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2335, '用户等级修改', 'member:user:update-level', 3, 5, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-23 16:49:05', '', '2023-08-23 16:50:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2336, '商品评论', '', 2, 5, 2000, 'comment', 'ep:comment', 'mall/product/comment/index', 'ProductComment', 0, '1', '1', '1', '1', '2023-08-26 11:03:00', '1', '2023-08-26 11:03:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2337, '评论查询', 'product:comment:query', 3, 1, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:01', '1', '2023-08-26 11:04:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2338, '添加自评', 'product:comment:create', 3, 2, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:23', '1', '2023-08-26 11:08:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2339, '商家回复', 'product:comment:update', 3, 3, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:37', '1', '2023-08-26 11:04:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2340, '显隐评论', 'product:comment:update', 3, 4, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:55', '1', '2023-08-26 11:04:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2341, '优惠劵发送', 'promotion:coupon:send', 3, 2, 2038, '', '', '', '', 0, '1', '1', '1', '1', '2023-09-02 00:03:14', '1', '2023-09-02 00:03:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2342, '交易配置', '', 2, 0, 2072, 'config', 'ep:setting', 'mall/trade/config/index', 'TradeConfig', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:30:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2343, '交易中心配置查询', 'trade:config:query', 3, 1, 2342, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2344, '交易中心配置保存', 'trade:config:save', 3, 2, 2342, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2345, '分销管理', '', 1, 4, 2072, 'brokerage', 'fa-solid:project-diagram', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2023-09-28 10:58:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2346, '分销用户', '', 2, 0, 2345, 'brokerage-user', 'fa-solid:user-tie', 'mall/trade/brokerage/user/index', 'TradeBrokerageUser', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2347, '分销用户查询', 'trade:brokerage-user:query', 3, 1, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2348, '分销用户推广人查询', 'trade:brokerage-user:user-query', 3, 2, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2349, '分销用户推广订单查询', 'trade:brokerage-user:order-query', 3, 3, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2350, '分销用户修改推广资格', 'trade:brokerage-user:update-brokerage-enable', 3, 4, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2351, '修改推广员', 'trade:brokerage-user:update-bind-user', 3, 5, 2346, '', '', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2352, '清除推广员', 'trade:brokerage-user:clear-bind-user', 3, 6, 2346, '', '', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2353, '佣金记录', '', 2, 1, 2345, 'brokerage-record', 'fa:money', 'mall/trade/brokerage/record/index', 'TradeBrokerageRecord', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2354, '佣金记录查询', 'trade:brokerage-record:query', 3, 1, 2353, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2355, '佣金提现', '', 2, 2, 2345, 'brokerage-withdraw', 'fa:credit-card', 'mall/trade/brokerage/withdraw/index', 'TradeBrokerageWithdraw', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2356, '佣金提现查询', 'trade:brokerage-withdraw:query', 3, 1, 2355, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2357, '佣金提现审核', 'trade:brokerage-withdraw:audit', 3, 2, 2355, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2358, '统计中心', '', 1, 75, 2362, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '', '2023-09-30 03:22:40', '1', '2023-09-30 11:54:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2359, '交易统计', '', 2, 4, 2358, 'trade', 'fa-solid:credit-card', 'mall/statistics/trade/index', 'TradeStatistics', 0, '1', '1', '1', '', '2023-09-30 03:22:40', '1', '2024-02-26 20:42:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2360, '交易统计查询', 'statistics:trade:query', 3, 1, 2359, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2361, '交易统计导出', 'statistics:trade:export', 3, 2, 2359, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2362, '商城系统', '', 1, 59, 0, '/mall', 'ep:shop', '', '', 0, '1', '1', '1', '1', '2023-09-30 11:52:02', '1', '2023-09-30 11:52:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2363, '用户积分修改', 'member:user:update-point', 3, 6, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-01 14:39:43', '', '2023-10-01 14:39:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2364, '用户余额修改', 'pay:wallet:update-balance', 3, 7, 2317, '', '', '', '', 0, '1', '1', '1', '', '2023-10-01 14:39:43', '1', '2024-10-01 09:42:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2365, '优惠劵', '', 1, 2, 2030, 'coupon', 'fa-solid:disease', '', '', 0, '1', '1', '1', '1', '2023-10-03 12:39:15', '1', '2023-10-05 00:16:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2366, '砍价记录', '', 2, 2, 2310, 'record', 'ep:list', 'mall/promotion/bargain/record/index', 'PromotionBargainRecord', 0, '1', '1', '1', '', '2023-10-05 02:49:06', '1', '2023-10-05 10:50:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2367, '砍价记录查询', 'promotion:bargain-record:query', 3, 1, 2366, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-05 02:49:06', '', '2023-10-05 02:49:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2368, '助力记录查询', 'promotion:bargain-help:query', 3, 2, 2366, '', '', '', '', 0, '1', '1', '1', '1', '2023-10-05 12:27:49', '1', '2023-10-05 12:27:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2369, '拼团记录', 'promotion:combination-record:query', 2, 2, 2303, 'record', 'ep:avatar', 'mall/promotion/combination/record/index.vue', 'PromotionCombinationRecord', 0, '1', '1', '1', '1', '2023-10-08 07:10:22', '1', '2023-10-08 07:34:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2374, '会员统计', '', 2, 2, 2358, 'member', 'ep:avatar', 'mall/statistics/member/index', 'MemberStatistics', 0, '1', '1', '1', '', '2023-10-11 04:39:24', '1', '2024-02-26 20:41:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2375, '会员统计查询', 'statistics:member:query', 3, 1, 2374, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-11 04:39:24', '', '2023-10-11 04:39:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2376, '订单核销', 'trade:order:pick-up', 3, 10, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2023-10-14 17:11:58', '1', '2023-10-14 17:11:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2377, '文章分类', '', 2, 0, 2387, 'article/category', 'fa:certificate', 'mall/promotion/article/category/index', 'ArticleCategory', 0, '1', '1', '1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:38:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2378, '分类查询', 'promotion:article-category:query', 3, 1, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2379, '分类创建', 'promotion:article-category:create', 3, 2, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2380, '分类更新', 'promotion:article-category:update', 3, 3, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2381, '分类删除', 'promotion:article-category:delete', 3, 4, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2382, '文章列表', '', 2, 2, 2387, 'article', 'ep:connection', 'mall/promotion/article/index', 'Article', 0, '1', '1', '1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:41:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2383, '文章管理查询', 'promotion:article:query', 3, 1, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2384, '文章管理创建', 'promotion:article:create', 3, 2, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2385, '文章管理更新', 'promotion:article:update', 3, 3, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2386, '文章管理删除', 'promotion:article:delete', 3, 4, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2387, '内容管理', '', 1, 1, 2030, 'content', 'ep:collection', '', '', 0, '1', '1', '1', '1', '2023-10-16 09:37:31', '1', '2023-10-16 09:37:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2388, '商城首页', '', 2, 1, 2362, 'home', 'ep:home-filled', 'mall/home/index', 'MallHome', 0, '1', '1', '1', '', '2023-10-16 12:10:33', '', '2023-10-16 12:10:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2389, '核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder', 0, '1', '1', '1', '', '2023-10-19 16:09:51', '', '2023-10-19 16:09:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2390, '优惠活动', '', 1, 99, 2030, 'youhui', 'ep:aim', '', '', 0, '1', '1', '1', '1', '2023-10-21 19:23:49', '1', '2023-10-21 19:23:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2391, '客户管理', '', 2, 10, 2397, 'customer', 'fa:address-book-o', 'crm/customer/index', 'CrmCustomer', 0, '1', '1', '1', '', '2023-10-29 09:04:21', '1', '2024-02-17 17:13:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2392, '客户查询', 'crm:customer:query', 3, 1, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2393, '客户创建', 'crm:customer:create', 3, 2, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2394, '客户更新', 'crm:customer:update', 3, 3, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2395, '客户删除', 'crm:customer:delete', 3, 4, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2396, '客户导出', 'crm:customer:export', 3, 5, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2397, 'CRM 系统', '', 1, 200, 0, '/crm', 'simple-icons:civicrm', '', '', 0, '1', '1', '1', '1', '2023-10-29 17:08:30', '1', '2025-04-19 18:56:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2398, '合同管理', '', 2, 50, 2397, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, '1', '1', '1', '', '2023-10-29 10:50:41', '1', '2024-02-17 17:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2399, '合同查询', 'crm:contract:query', 3, 1, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2400, '合同创建', 'crm:contract:create', 3, 2, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2401, '合同更新', 'crm:contract:update', 3, 3, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2402, '合同删除', 'crm:contract:delete', 3, 4, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2403, '合同导出', 'crm:contract:export', 3, 5, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2404, '线索管理', '', 2, 8, 2397, 'clue', 'fa:pagelines', 'crm/clue/index', 'CrmClue', 0, '1', '1', '1', '', '2023-10-29 11:06:29', '1', '2024-02-17 17:15:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2405, '线索查询', 'crm:clue:query', 3, 1, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2406, '线索创建', 'crm:clue:create', 3, 2, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2407, '线索更新', 'crm:clue:update', 3, 3, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2408, '线索删除', 'crm:clue:delete', 3, 4, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2409, '线索导出', 'crm:clue:export', 3, 5, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2410, '商机管理', '', 2, 40, 2397, 'business', 'fa:bus', 'crm/business/index', 'CrmBusiness', 0, '1', '1', '1', '', '2023-10-29 11:12:35', '1', '2024-02-17 17:14:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2411, '商机查询', 'crm:business:query', 3, 1, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2412, '商机创建', 'crm:business:create', 3, 2, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2413, '商机更新', 'crm:business:update', 3, 3, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2414, '商机删除', 'crm:business:delete', 3, 4, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2415, '商机导出', 'crm:business:export', 3, 5, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2416, '联系人管理', '', 2, 20, 2397, 'contact', 'fa:address-book-o', 'crm/contact/index', 'CrmContact', 0, '1', '1', '1', '', '2023-10-29 11:14:56', '1', '2024-02-17 17:13:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2417, '联系人查询', 'crm:contact:query', 3, 1, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2418, '联系人创建', 'crm:contact:create', 3, 2, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2419, '联系人更新', 'crm:contact:update', 3, 3, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2420, '联系人删除', 'crm:contact:delete', 3, 4, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2421, '联系人导出', 'crm:contact:export', 3, 5, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2422, '回款管理', '', 2, 60, 2397, 'receivable', 'ep:money', 'crm/receivable/index', 'CrmReceivable', 0, '1', '1', '1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2423, '回款管理查询', 'crm:receivable:query', 3, 1, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2424, '回款管理创建', 'crm:receivable:create', 3, 2, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2425, '回款管理更新', 'crm:receivable:update', 3, 3, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2426, '回款管理删除', 'crm:receivable:delete', 3, 4, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2427, '回款管理导出', 'crm:receivable:export', 3, 5, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2428, '回款计划', '', 2, 61, 2397, 'receivable-plan', 'fa:money', 'crm/receivable/plan/index', 'CrmReceivablePlan', 0, '1', '1', '1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2429, '回款计划查询', 'crm:receivable-plan:query', 3, 1, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2430, '回款计划创建', 'crm:receivable-plan:create', 3, 2, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2432, '回款计划删除', 'crm:receivable-plan:delete', 3, 4, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2433, '回款计划导出', 'crm:receivable-plan:export', 3, 5, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', '', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '1', '2025-03-15 21:34:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2436, '装修模板', '', 2, 1, 2435, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2437, '装修模板查询', 'promotion:diy-template:query', 3, 1, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2438, '装修模板创建', 'promotion:diy-template:create', 3, 2, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2439, '装修模板更新', 'promotion:diy-template:update', 3, 3, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2440, '装修模板删除', 'promotion:diy-template:delete', 3, 4, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2441, '装修模板使用', 'promotion:diy-template:use', 3, 5, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2442, '装修页面', '', 2, 2, 2435, 'diy-page', 'foundation:page-edit', 'mall/promotion/diy/page/index', 'DiyPage', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2443, '装修页面查询', 'promotion:diy-page:query', 3, 1, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2444, '装修页面创建', 'promotion:diy-page:create', 3, 2, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2445, '装修页面更新', 'promotion:diy-page:update', 3, 3, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2446, '装修页面删除', 'promotion:diy-page:delete', 3, 4, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2447, '三方登录', '', 1, 10, 1, 'social', 'fa:rocket', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:12:01', '1', '2024-02-29 01:14:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2448, '三方应用', '', 2, 1, 2447, 'client', 'ep:set-up', 'system/social/client/index.vue', 'SocialClient', 0, '1', '1', '1', '1', '2023-11-04 12:17:19', '1', '2024-05-04 19:09:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2449, '三方应用查询', 'system:social-client:query', 3, 1, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:43:12', '1', '2023-11-04 12:43:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2450, '三方应用创建', 'system:social-client:create', 3, 2, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:43:58', '1', '2023-11-04 12:43:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2451, '三方应用更新', 'system:social-client:update', 3, 3, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:44:27', '1', '2023-11-04 12:44:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2452, '三方应用删除', 'system:social-client:delete', 3, 4, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:44:43', '1', '2023-11-04 12:44:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2453, '三方用户', 'system:social-user:query', 2, 2, 2447, 'user', 'ep:avatar', 'system/social/user/index.vue', 'SocialUser', 0, '1', '1', '1', '1', '2023-11-04 14:01:05', '1', '2023-11-04 14:01:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2472, '主子表（内嵌）', '', 2, 12, 1070, 'demo03-inner', 'fa:power-off', 'infra/demo/demo03/inner/index', 'Demo03StudentInner', 0, '1', '1', '1', '', '2023-11-13 04:39:51', '1', '2023-11-16 23:53:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2478, '单表（增删改查）', '', 2, 1, 1070, 'demo01-contact', 'ep:bicycle', 'infra/demo/demo01/index', 'Demo01Contact', 0, '1', '1', '1', '', '2023-11-15 14:42:30', '1', '2023-11-16 20:34:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2479, '示例联系人查询', 'infra:demo01-contact:query', 3, 1, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2480, '示例联系人创建', 'infra:demo01-contact:create', 3, 2, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2481, '示例联系人更新', 'infra:demo01-contact:update', 3, 3, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2482, '示例联系人删除', 'infra:demo01-contact:delete', 3, 4, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2483, '示例联系人导出', 'infra:demo01-contact:export', 3, 5, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2484, '树表（增删改查）', '', 2, 2, 1070, 'demo02-category', 'fa:tree', 'infra/demo/demo02/index', 'Demo02Category', 0, '1', '1', '1', '', '2023-11-16 12:18:27', '1', '2023-11-16 20:35:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2485, '示例分类查询', 'infra:demo02-category:query', 3, 1, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2486, '示例分类创建', 'infra:demo02-category:create', 3, 2, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2487, '示例分类更新', 'infra:demo02-category:update', 3, 3, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2488, '示例分类删除', 'infra:demo02-category:delete', 3, 4, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2489, '示例分类导出', 'infra:demo02-category:export', 3, 5, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2490, '主子表（标准）', '', 2, 10, 1070, 'demo03-normal', 'fa:battery-3', 'infra/demo/demo03/normal/index', 'Demo03StudentNormal', 0, '1', '1', '1', '', '2023-11-16 12:53:37', '1', '2023-11-16 23:10:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2491, '学生查询', 'infra:demo03-student:query', 3, 1, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2492, '学生创建', 'infra:demo03-student:create', 3, 2, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2493, '学生更新', 'infra:demo03-student:update', 3, 3, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2494, '学生删除', 'infra:demo03-student:delete', 3, 4, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2495, '学生导出', 'infra:demo03-student:export', 3, 5, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2497, '主子表（ERP）', '', 2, 11, 1070, 'demo03-erp', 'ep:calendar', 'infra/demo/demo03/erp/index', 'Demo03StudentERP', 0, '1', '1', '1', '', '2023-11-16 15:50:59', '1', '2023-11-17 13:19:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2516, '客户公海配置', '', 2, 0, 2524, 'customer-pool-config', 'ep:data-analysis', 'crm/customer/poolConfig/index', 'CrmCustomerPoolConfig', 0, '1', '1', '1', '', '2023-11-18 13:33:31', '1', '2024-01-03 19:52:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2517, '客户公海配置保存', 'crm:customer-pool-config:update', 3, 1, 2516, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:31', '', '2023-11-18 13:33:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2518, '客户限制配置', '', 2, 1, 2524, 'customer-limit-config', 'ep:avatar', 'crm/customer/limitConfig/index', 'CrmCustomerLimitConfig', 0, '1', '1', '1', '', '2023-11-18 13:33:53', '1', '2024-02-24 16:43:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2519, '客户限制配置查询', 'crm:customer-limit-config:query', 3, 1, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2520, '客户限制配置创建', 'crm:customer-limit-config:create', 3, 2, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2521, '客户限制配置更新', 'crm:customer-limit-config:update', 3, 3, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2522, '客户限制配置删除', 'crm:customer-limit-config:delete', 3, 4, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2523, '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2524, '系统配置', '', 1, 999, 2397, 'config', 'ep:connection', '', '', 0, '1', '1', '1', '1', '2023-11-18 21:58:00', '1', '2024-02-17 17:14:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2525, 'WebSocket', '', 2, 5, 2, 'websocket', 'ep:connection', 'infra/webSocket/index', 'InfraWebSocket', 0, '1', '1', '1', '1', '2023-11-23 19:41:55', '1', '2024-04-23 00:02:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2526, '产品管理', '', 2, 80, 2397, 'product', 'fa:product-hunt', 'crm/product/index', 'CrmProduct', 0, '1', '1', '1', '1', '2023-12-05 22:45:26', '1', '2024-02-20 20:36:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2527, '产品查询', 'crm:product:query', 3, 1, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:47:16', '1', '2023-12-05 22:47:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2528, '产品创建', 'crm:product:create', 3, 2, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:47:41', '1', '2023-12-05 22:47:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2529, '产品更新', 'crm:product:update', 3, 3, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:03', '1', '2023-12-05 22:48:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2530, '产品删除', 'crm:product:delete', 3, 4, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:17', '1', '2023-12-05 22:48:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2531, '产品导出', 'crm:product:export', 3, 5, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:29', '1', '2023-12-05 22:48:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2532, '产品分类配置', '', 2, 3, 2524, 'product/category', 'fa-solid:window-restore', 'crm/product/category/index', 'CrmProductCategory', 0, '1', '1', '1', '1', '2023-12-06 12:52:36', '1', '2023-12-06 12:52:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2533, '产品分类查询', 'crm:product-category:query', 3, 1, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:23', '1', '2023-12-06 12:53:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2534, '产品分类创建', 'crm:product-category:create', 3, 2, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:41', '1', '2023-12-06 12:53:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2535, '产品分类更新', 'crm:product-category:update', 3, 3, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:59', '1', '2023-12-06 12:53:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2536, '产品分类删除', 'crm:product-category:delete', 3, 4, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:54:14', '1', '2023-12-06 12:54:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2543, '关联商机', 'crm:contact:create-business', 3, 10, 2416, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-02 17:28:25', '1', '2024-01-02 17:28:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2544, '取关商机', 'crm:contact:delete-business', 3, 11, 2416, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-02 17:28:43', '1', '2024-01-02 17:28:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2545, '商品统计', '', 2, 3, 2358, 'product', 'fa:product-hunt', 'mall/statistics/product/index', 'ProductStatistics', 0, '1', '1', '1', '', '2023-12-15 18:54:28', '1', '2024-02-26 20:41:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2546, '客户公海', '', 2, 30, 2397, 'customer/pool', 'fa-solid:swimming-pool', 'crm/customer/pool/index', 'CrmCustomerPool', 0, '1', '1', '1', '1', '2024-01-15 21:29:34', '1', '2024-02-17 17:14:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2547, '订单查询', 'trade:order:query', 3, 1, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-16 08:52:00', '1', '2024-01-16 08:52:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2548, '订单更新', 'trade:order:update', 3, 2, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-16 08:52:21', '1', '2024-01-16 08:52:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2549, '支付&退款案例', '', 2, 1, 2161, 'order', 'fa:paypal', 'pay/demo/order/index', '', 0, '1', '1', '1', '1', '2024-01-18 23:45:00', '1', '2024-01-18 23:47:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2550, '提现转账案例', '', 2, 2, 2161, 'transfer', 'fa:transgender-alt', 'pay/demo/withdraw/index', '', 0, '1', '1', '1', '1', '2024-01-18 23:51:16', '1', '2025-05-08 13:04:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2551, '钱包管理', '', 1, 4, 1117, 'wallet', 'ep:wallet', '', '', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '1', '2024-02-29 08:58:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2552, '充值套餐', '', 2, 2, 2551, 'wallet-recharge-package', 'fa:leaf', 'pay/wallet/rechargePackage/index', 'WalletRechargePackage', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2553, '钱包充值套餐查询', 'pay:wallet-recharge-package:query', 3, 1, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2554, '钱包充值套餐创建', 'pay:wallet-recharge-package:create', 3, 2, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2555, '钱包充值套餐更新', 'pay:wallet-recharge-package:update', 3, 3, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2556, '钱包充值套餐删除', 'pay:wallet-recharge-package:delete', 3, 4, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2557, '钱包余额', '', 2, 1, 2551, 'wallet-balance', 'fa:leaf', 'pay/wallet/balance/index', 'WalletBalance', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2558, '钱包余额查询', 'pay:wallet:query', 3, 1, 2557, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2559, '转账订单', '', 2, 3, 1117, 'transfer', 'ep:credit-card', 'pay/transfer/index', 'PayTransfer', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2560, '数据统计', '', 1, 200, 2397, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '1', '2024-01-26 22:50:35', '1', '2024-02-24 20:10:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2561, '排行榜', 'crm:statistics-rank:query', 2, 1, 2560, 'ranking', 'fa:area-chart', 'crm/statistics/rank/index', 'CrmStatisticsRank', 0, '1', '1', '1', '1', '2024-01-26 22:52:09', '1', '2024-04-24 19:39:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2562, '客户导入', 'crm:customer:import', 3, 6, 2391, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-01 13:09:00', '1', '2024-02-01 13:09:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2563, 'ERP 系统', '', 1, 300, 0, '/erp', 'simple-icons:erpnext', '', '', 0, '1', '1', '1', '1', '2024-02-04 15:37:25', '1', '2025-04-19 18:56:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2564, '产品管理', '', 1, 40, 2563, 'product', 'fa:product-hunt', '', '', 0, '1', '1', '1', '1', '2024-02-04 15:38:43', '1', '2024-02-04 15:38:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2565, '产品信息', '', 2, 0, 2564, 'product', 'fa-solid:apple-alt', 'erp/product/product/index', 'ErpProduct', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-05 14:42:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2566, '产品查询', 'erp:product:query', 3, 1, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:21:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2567, '产品创建', 'erp:product:create', 3, 2, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2568, '产品更新', 'erp:product:update', 3, 3, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2569, '产品删除', 'erp:product:delete', 3, 4, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2570, '产品导出', 'erp:product:export', 3, 5, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2571, '产品分类', '', 2, 1, 2564, 'product-category', 'fa:certificate', 'erp/product/category/index', 'ErpProductCategory', 0, '1', '1', '1', '', '2024-02-04 09:21:04', '1', '2024-02-04 17:24:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2572, '分类查询', 'erp:product-category:query', 3, 1, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2573, '分类创建', 'erp:product-category:create', 3, 2, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2574, '分类更新', 'erp:product-category:update', 3, 3, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2575, '分类删除', 'erp:product-category:delete', 3, 4, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2576, '分类导出', 'erp:product-category:export', 3, 5, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2577, '产品单位', '', 2, 2, 2564, 'unit', 'ep:opportunity', 'erp/product/unit/index', 'ErpProductUnit', 0, '1', '1', '1', '', '2024-02-04 11:54:08', '1', '2024-02-04 19:54:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2578, '单位查询', 'erp:product-unit:query', 3, 1, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2579, '单位创建', 'erp:product-unit:create', 3, 2, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2580, '单位更新', 'erp:product-unit:update', 3, 3, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2581, '单位删除', 'erp:product-unit:delete', 3, 4, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2582, '单位导出', 'erp:product-unit:export', 3, 5, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2583, '库存管理', '', 1, 30, 2563, 'stock', 'fa:window-restore', '', '', 0, '1', '1', '1', '1', '2024-02-05 00:29:37', '1', '2024-02-05 00:29:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2584, '仓库信息', '', 2, 0, 2583, 'warehouse', 'ep:house', 'erp/stock/warehouse/index', 'ErpWarehouse', 0, '1', '1', '1', '', '2024-02-04 17:12:09', '1', '2024-02-05 01:12:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2585, '仓库查询', 'erp:warehouse:query', 3, 1, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2586, '仓库创建', 'erp:warehouse:create', 3, 2, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2587, '仓库更新', 'erp:warehouse:update', 3, 3, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2588, '仓库删除', 'erp:warehouse:delete', 3, 4, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2589, '仓库导出', 'erp:warehouse:export', 3, 5, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2590, '产品库存', '', 2, 1, 2583, 'stock', 'ep:coffee', 'erp/stock/stock/index', 'ErpStock', 0, '1', '1', '1', '', '2024-02-05 06:40:50', '1', '2024-02-05 14:42:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2591, '库存查询', 'erp:stock:query', 3, 1, 2590, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2592, '库存导出', 'erp:stock:export', 3, 5, 2590, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2593, '出入库明细', '', 2, 2, 2583, 'record', 'fa-solid:blog', 'erp/stock/record/index', 'ErpStockRecord', 0, '1', '1', '1', '', '2024-02-05 10:27:21', '1', '2024-02-06 17:26:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2594, '库存明细查询', 'erp:stock-record:query', 3, 1, 2593, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2595, '库存明细导出', 'erp:stock-record:export', 3, 5, 2593, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2596, '其它入库', '', 2, 3, 2583, 'in', 'ep:zoom-in', 'erp/stock/in/index', 'ErpStockIn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2597, '其它入库单查询', 'erp:stock-in:query', 3, 1, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2598, '其它入库单创建', 'erp:stock-in:create', 3, 2, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2599, '其它入库单更新', 'erp:stock-in:update', 3, 3, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2600, '其它入库单删除', 'erp:stock-in:delete', 3, 4, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2601, '其它入库单导出', 'erp:stock-in:export', 3, 5, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2602, '采购管理', '', 1, 10, 2563, 'purchase', 'fa:buysellads', '', '', 0, '1', '1', '1', '1', '2024-02-06 16:01:01', '1', '2024-02-06 16:01:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2603, '供应商信息', '', 2, 4, 2602, 'supplier', 'fa:superpowers', 'erp/purchase/supplier/index', 'ErpSupplier', 0, '1', '1', '1', '', '2024-02-06 08:21:55', '1', '2024-02-06 16:22:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2604, '供应商查询', 'erp:supplier:query', 3, 1, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2605, '供应商创建', 'erp:supplier:create', 3, 2, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2606, '供应商更新', 'erp:supplier:update', 3, 3, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2607, '供应商删除', 'erp:supplier:delete', 3, 4, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2608, '供应商导出', 'erp:supplier:export', 3, 5, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2609, '其它入库单审批', 'erp:stock-in:update-status', 3, 6, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2610, '其它出库', '', 2, 4, 2583, 'out', 'ep:zoom-out', 'erp/stock/out/index', 'ErpStockOut', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2611, '其它出库单查询', 'erp:stock-out:query', 3, 1, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2612, '其它出库单创建', 'erp:stock-out:create', 3, 2, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2613, '其它出库单更新', 'erp:stock-out:update', 3, 3, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2614, '其它出库单删除', 'erp:stock-out:delete', 3, 4, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2615, '其它出库单导出', 'erp:stock-out:export', 3, 5, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2616, '其它出库单审批', 'erp:stock-out:update-status', 3, 6, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2617, '销售管理', '', 1, 20, 2563, 'sale', 'fa:sellsy', '', '', 0, '1', '1', '1', '1', '2024-02-07 15:12:32', '1', '2024-02-07 15:12:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2618, '客户信息', '', 2, 4, 2617, 'customer', 'ep:avatar', 'erp/sale/customer/index', 'ErpCustomer', 0, '1', '1', '1', '', '2024-02-07 07:21:45', '1', '2024-02-07 15:22:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2619, '客户查询', 'erp:customer:query', 3, 1, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2620, '客户创建', 'erp:customer:create', 3, 2, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2621, '客户更新', 'erp:customer:update', 3, 3, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2622, '客户删除', 'erp:customer:delete', 3, 4, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2623, '客户导出', 'erp:customer:export', 3, 5, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2624, '库存调拨', '', 2, 5, 2583, 'move', 'ep:folder-remove', 'erp/stock/move/index', 'ErpStockMove', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-16 18:53:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2625, '库存调度单查询', 'erp:stock-move:query', 3, 1, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2626, '库存调度单创建', 'erp:stock-move:create', 3, 2, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2627, '库存调度单更新', 'erp:stock-move:update', 3, 3, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2628, '库存调度单删除', 'erp:stock-move:delete', 3, 4, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2629, '库存调度单导出', 'erp:stock-move:export', 3, 5, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2630, '库存调度单审批', 'erp:stock-move:update-status', 3, 6, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2631, '库存盘点', '', 2, 6, 2583, 'check', 'ep:circle-check-filled', 'erp/stock/check/index', 'ErpStockCheck', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-08 08:31:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2632, '库存盘点单查询', 'erp:stock-check:query', 3, 1, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2633, '库存盘点单创建', 'erp:stock-check:create', 3, 2, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2634, '库存盘点单更新', 'erp:stock-check:update', 3, 3, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2635, '库存盘点单删除', 'erp:stock-check:delete', 3, 4, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2636, '库存盘点单导出', 'erp:stock-check:export', 3, 5, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2637, '库存盘点单审批', 'erp:stock-check:update-status', 3, 6, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2638, '销售订单', '', 2, 1, 2617, 'order', 'fa:first-order', 'erp/sale/order/index', 'ErpSaleOrder', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-10 21:59:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2639, '销售订单查询', 'erp:sale-order:query', 3, 1, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2640, '销售订单创建', 'erp:sale-order:create', 3, 2, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2641, '销售订单更新', 'erp:sale-order:update', 3, 3, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2642, '销售订单删除', 'erp:sale-order:delete', 3, 4, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2643, '销售订单导出', 'erp:sale-order:export', 3, 5, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2644, '销售订单审批', 'erp:sale-order:update-status', 3, 6, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2645, '财务管理', '', 1, 50, 2563, 'finance', 'ep:money', '', '', 0, '1', '1', '1', '1', '2024-02-10 08:05:58', '1', '2024-02-10 08:06:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2646, '结算账户', '', 2, 10, 2645, 'account', 'fa:universal-access', 'erp/finance/account/index', 'ErpAccount', 0, '1', '1', '1', '', '2024-02-10 00:15:07', '1', '2024-02-14 08:24:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2647, '结算账户查询', 'erp:account:query', 3, 1, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2648, '结算账户创建', 'erp:account:create', 3, 2, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2649, '结算账户更新', 'erp:account:update', 3, 3, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2650, '结算账户删除', 'erp:account:delete', 3, 4, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2651, '结算账户导出', 'erp:account:export', 3, 5, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2652, '销售出库', '', 2, 2, 2617, 'out', 'ep:sold-out', 'erp/sale/out/index', 'ErpSaleOut', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-10 22:02:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2653, '销售出库查询', 'erp:sale-out:query', 3, 1, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2654, '销售出库创建', 'erp:sale-out:create', 3, 2, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2655, '销售出库更新', 'erp:sale-out:update', 3, 3, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2656, '销售出库删除', 'erp:sale-out:delete', 3, 4, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2657, '销售出库导出', 'erp:sale-out:export', 3, 5, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2658, '销售出库审批', 'erp:sale-out:update-status', 3, 6, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2659, '销售退货', '', 2, 3, 2617, 'return', 'fa-solid:bone', 'erp/sale/return/index', 'ErpSaleReturn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 06:12:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2660, '销售退货查询', 'erp:sale-return:query', 3, 1, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2661, '销售退货创建', 'erp:sale-return:create', 3, 2, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2662, '销售退货更新', 'erp:sale-return:update', 3, 3, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2663, '销售退货删除', 'erp:sale-return:delete', 3, 4, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2664, '销售退货导出', 'erp:sale-return:export', 3, 5, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2665, '销售退货审批', 'erp:sale-return:update-status', 3, 6, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2666, '采购订单', '', 2, 1, 2602, 'order', 'fa-solid:border-all', 'erp/purchase/order/index', 'ErpPurchaseOrder', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 08:51:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2667, '采购订单查询', 'erp:purchase-order:query', 3, 1, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2668, '采购订单创建', 'erp:purchase-order:create', 3, 2, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2669, '采购订单更新', 'erp:purchase-order:update', 3, 3, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2670, '采购订单删除', 'erp:purchase-order:delete', 3, 4, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2671, '采购订单导出', 'erp:purchase-order:export', 3, 5, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2672, '采购订单审批', 'erp:purchase-order:update-status', 3, 6, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2673, '采购入库', '', 2, 2, 2602, 'in', 'fa-solid:gopuram', 'erp/purchase/in/index', 'ErpPurchaseIn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 11:19:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2674, '采购入库查询', 'erp:purchase-in:query', 3, 1, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2675, '采购入库创建', 'erp:purchase-in:create', 3, 2, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2676, '采购入库更新', 'erp:purchase-in:update', 3, 3, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2677, '采购入库删除', 'erp:purchase-in:delete', 3, 4, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2678, '采购入库导出', 'erp:purchase-in:export', 3, 5, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2679, '采购入库审批', 'erp:purchase-in:update-status', 3, 6, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2680, '采购退货', '', 2, 3, 2602, 'return', 'ep:minus', 'erp/purchase/return/index', 'ErpPurchaseReturn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 20:51:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2681, '采购退货查询', 'erp:purchase-return:query', 3, 1, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2682, '采购退货创建', 'erp:purchase-return:create', 3, 2, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2683, '采购退货更新', 'erp:purchase-return:update', 3, 3, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2684, '采购退货删除', 'erp:purchase-return:delete', 3, 4, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2685, '采购退货导出', 'erp:purchase-return:export', 3, 5, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2686, '采购退货审批', 'erp:purchase-return:update-status', 3, 6, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2687, '付款单', '', 2, 1, 2645, 'payment', 'ep:caret-right', 'erp/finance/payment/index', 'ErpFinancePayment', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-14 08:24:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2688, '付款单查询', 'erp:finance-payment:query', 3, 1, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2689, '付款单创建', 'erp:finance-payment:create', 3, 2, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2690, '付款单更新', 'erp:finance-payment:update', 3, 3, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2691, '付款单删除', 'erp:finance-payment:delete', 3, 4, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2692, '付款单导出', 'erp:finance-payment:export', 3, 5, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2693, '付款单审批', 'erp:finance-payment:update-status', 3, 6, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2694, '收款单', '', 2, 2, 2645, 'receipt', 'ep:expand', 'erp/finance/receipt/index', 'ErpFinanceReceipt', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-15 19:35:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2695, '收款单查询', 'erp:finance-receipt:query', 3, 1, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2696, '收款单创建', 'erp:finance-receipt:create', 3, 2, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2697, '收款单更新', 'erp:finance-receipt:update', 3, 3, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2698, '收款单删除', 'erp:finance-receipt:delete', 3, 4, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2699, '收款单导出', 'erp:finance-receipt:export', 3, 5, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2700, '收款单审批', 'erp:finance-receipt:update-status', 3, 6, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2701, '待办事项', '', 2, 0, 2397, 'backlog', 'fa-solid:tasks', 'crm/backlog/index', 'CrmBacklog', 0, '1', '1', '1', '1', '2024-02-17 17:17:11', '1', '2024-02-17 17:17:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2702, 'ERP 首页', 'erp:statistics:query', 2, 0, 2563, 'home', 'ep:home-filled', 'erp/home/index.vue', 'ErpHome', 0, '1', '1', '1', '1', '2024-02-18 16:49:40', '1', '2024-02-26 21:12:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2703, '商机状态配置', '', 2, 4, 2524, 'business-status', 'fa-solid:charging-station', 'crm/business/status/index', 'CrmBusinessStatus', 0, '1', '1', '1', '1', '2024-02-21 20:15:17', '1', '2024-02-21 20:15:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2704, '商机状态查询', 'crm:business-status:query', 3, 1, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:35:36', '1', '2024-02-21 20:36:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2705, '商机状态创建', 'crm:business-status:create', 3, 2, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:35:57', '1', '2024-02-21 20:35:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2706, '商机状态更新', 'crm:business-status:update', 3, 3, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:36:21', '1', '2024-02-21 20:36:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2707, '商机状态删除', 'crm:business-status:delete', 3, 4, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:36:36', '1', '2024-02-21 20:36:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2708, '合同配置', '', 2, 5, 2524, 'contract-config', 'ep:connection', 'crm/contract/config/index', 'CrmContractConfig', 0, '1', '1', '1', '1', '2024-02-24 16:44:40', '1', '2024-02-24 16:44:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2709, '客户公海配置查询', 'crm:customer-pool-config:query', 3, 2, 2516, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:45:19', '1', '2024-02-24 16:45:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2710, '合同配置更新', 'crm:contract-config:update', 3, 1, 2708, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:45:56', '1', '2024-02-24 16:45:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2711, '合同配置查询', 'crm:contract-config:query', 3, 2, 2708, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:46:16', '1', '2024-02-24 16:46:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2712, '客户分析', 'crm:statistics-customer:query', 2, 0, 2560, 'customer', 'ep:avatar', 'crm/statistics/customer/index.vue', 'CrmStatisticsCustomer', 0, '1', '1', '1', '1', '2024-03-09 16:43:56', '1', '2024-05-04 20:38:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2713, '抄送我的', 'bpm:process-instance-cc:query', 2, 30, 1200, 'copy', 'ep:copy-document', 'bpm/task/copy/index', 'BpmProcessInstanceCopy', 0, '1', '1', '1', '1', '2024-03-17 21:50:23', '1', '2024-04-24 19:55:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2714, '流程分类', '', 2, 3, 1186, 'category', 'fa:object-ungroup', 'bpm/category/index', 'BpmCategory', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-21 23:51:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2715, '分类查询', 'bpm:category:query', 3, 1, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2716, '分类创建', 'bpm:category:create', 3, 2, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2717, '分类更新', 'bpm:category:update', 3, 3, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2718, '分类删除', 'bpm:category:delete', 3, 4, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2720, '发起流程', '', 2, 0, 1200, 'create', 'fa-solid:grin-stars', 'bpm/processInstance/create/index', 'BpmProcessInstanceCreate', 0, '1', '0', '1', '1', '2024-03-19 19:46:05', '1', '2024-03-23 19:03:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2721, '流程实例', '', 2, 10, 1186, 'process-instance/manager', 'fa:square', 'bpm/processInstance/manager/index', 'BpmProcessInstanceManager', 0, '1', '1', '1', '1', '2024-03-21 23:57:30', '1', '2024-03-21 23:57:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2722, '流程实例的查询（管理员）', 'bpm:process-instance:manager-query', 3, 1, 2721, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:18:27', '1', '2024-03-22 08:19:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2723, '流程实例的取消（管理员）', 'bpm:process-instance:cancel-by-admin', 3, 2, 2721, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:19:25', '1', '2024-03-22 08:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2724, '流程任务', '', 2, 11, 1186, 'process-tasnk', 'ep:collection-tag', 'bpm/task/manager/index', 'BpmManagerTask', 0, '1', '1', '1', '1', '2024-03-22 08:43:22', '1', '2024-03-22 08:43:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2725, '流程任务的查询（管理员）', 'bpm:task:mananger-query', 3, 1, 2724, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:43:49', '1', '2024-03-22 08:43:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2726, '流程监听器', '', 2, 5, 1186, 'process-listener', 'fa:assistive-listening-systems', 'bpm/processListener/index', 'BpmProcessListener', 0, '1', '1', '1', '', '2024-03-09 16:05:34', '1', '2024-03-23 13:13:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2727, '流程监听器查询', 'bpm:process-listener:query', 3, 1, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2728, '流程监听器创建', 'bpm:process-listener:create', 3, 2, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2729, '流程监听器更新', 'bpm:process-listener:update', 3, 3, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2730, '流程监听器删除', 'bpm:process-listener:delete', 3, 4, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2731, '流程表达式', '', 2, 6, 1186, 'process-expression', 'fa:wpexplorer', 'bpm/processExpression/index', 'BpmProcessExpression', 0, '1', '1', '1', '', '2024-03-09 22:35:08', '1', '2024-03-23 19:43:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2732, '流程表达式查询', 'bpm:process-expression:query', 3, 1, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2733, '流程表达式创建', 'bpm:process-expression:create', 3, 2, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2734, '流程表达式更新', 'bpm:process-expression:update', 3, 3, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2735, '流程表达式删除', 'bpm:process-expression:delete', 3, 4, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2736, '员工业绩', 'crm:statistics-performance:query', 2, 3, 2560, 'performance', 'ep:dish-dot', 'crm/statistics/performance/index', 'CrmStatisticsPerformance', 0, '1', '1', '1', '1', '2024-04-05 13:49:20', '1', '2024-04-24 19:42:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2737, '客户画像', 'crm:statistics-portrait:query', 2, 4, 2560, 'portrait', 'ep:picture', 'crm/statistics/portrait/index', 'CrmStatisticsPortrait', 0, '1', '1', '1', '1', '2024-04-05 13:57:40', '1', '2024-04-24 19:42:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2738, '销售漏斗', 'crm:statistics-funnel:query', 2, 5, 2560, 'funnel', 'ep:grape', 'crm/statistics/funnel/index', 'CrmStatisticsFunnel', 0, '1', '1', '1', '1', '2024-04-13 10:53:26', '1', '2024-04-24 19:39:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2739, '消息中心', '', 1, 7, 1, 'messages', 'ep:chat-dot-round', '', '', 0, '1', '1', '1', '1', '2024-04-22 23:54:30', '1', '2024-04-23 09:36:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2740, '监控中心', '', 1, 10, 2, 'monitors', 'ep:monitor', '', '', 0, '1', '1', '1', '1', '2024-04-23 00:04:44', '1', '2024-04-23 00:04:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2741, '领取公海客户', 'crm:customer:receive', 3, 1, 2546, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:47:45', '1', '2024-04-24 19:47:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2742, '分配公海客户', 'crm:customer:distribute', 3, 2, 2546, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:48:05', '1', '2024-04-24 19:48:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2743, '商品统计查询', 'statistics:product:query', 3, 1, 2545, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:50:05', '1', '2024-04-24 19:50:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2744, '商品统计导出', 'statistics:product:export', 3, 2, 2545, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:50:26', '1', '2024-04-24 19:50:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2745, '支付渠道查询', 'pay:channel:query', 3, 10, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:01', '1', '2024-04-24 19:53:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2746, '支付渠道创建', 'pay:channel:create', 3, 11, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:18', '1', '2024-04-24 19:53:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2747, '支付渠道更新', 'pay:channel:update', 3, 12, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:32', '1', '2024-04-24 19:53:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2748, '支付渠道删除', 'pay:channel:delete', 3, 13, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:54:34', '1', '2024-04-24 19:54:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2749, '商品收藏查询', 'product:favorite:query', 3, 10, 2014, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:55:47', '1', '2024-04-24 19:55:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2750, '商品浏览查询', 'product:browse-history:query', 3, 20, 2014, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:57:43', '1', '2024-04-24 19:57:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2751, '售后同意', 'trade:after-sale:agree', 3, 2, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:58:40', '1', '2024-04-24 19:58:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2752, '售后不同意', 'trade:after-sale:disagree', 3, 3, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:59:03', '1', '2024-04-24 19:59:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2753, '售后确认退货', 'trade:after-sale:receive', 3, 4, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:00:07', '1', '2024-04-24 20:00:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2754, '售后确认退款', 'trade:after-sale:refund', 3, 5, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:00:24', '1', '2024-04-24 20:00:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2755, '删除项目', 'report:go-view-project:delete', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:01:37', '1', '2024-04-24 20:01:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2756, '会员等级记录查询', 'member:level-record:query', 3, 10, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:32', '1', '2024-04-24 20:02:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2757, '会员经验记录查询', 'member:experience-record:query', 3, 11, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:51', '1', '2024-04-24 20:02:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2758, 'AI 大模型', '', 1, 400, 0, '/ai', 'tabler:ai', '', '', 0, '1', '1', '1', '1', '2024-05-07 15:07:56', '1', '2025-04-19 18:57:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2759, 'AI 对话', '', 2, 1, 2758, 'chat', 'ep:message', 'ai/chat/index/index.vue', 'AiChat', 0, '1', '1', '1', '1', '2024-05-07 15:09:14', '1', '2024-07-07 17:15:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2760, '控制台', '', 1, 100, 2758, 'console', 'ep:setting', '', '', 0, '1', '1', '1', '1', '2024-05-09 22:39:09', '1', '2024-05-24 23:34:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2761, 'API 密钥', '', 2, 0, 2760, 'api-key', 'ep:key', 'ai/model/apiKey/index.vue', 'AiApiKey', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-10 22:44:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2762, 'API 密钥查询', 'ai:api-key:query', 3, 1, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2767, '模型配置', '', 2, 0, 2760, 'model', 'fa-solid:abacus', 'ai/model/model/index.vue', 'AiModel', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:57:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2768, '聊天模型查询', 'ai:model:query', 3, 1, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:19:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2769, '聊天模型创建', 'ai:model:create', 3, 2, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2770, '聊天模型更新', 'ai:model:update', 3, 3, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2771, '聊天模型删除', 'ai:model:delete', 3, 4, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, '1', '1', '1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2776, '聊天角色更新', 'ai:chat-role:update', 3, 3, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2777, '聊天角色删除', 'ai:chat-role:delete', 3, 4, 2773, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-13 21:43:38', '1', '2024-05-13 21:43:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2778, '聊天管理', '', 2, 10, 2760, 'chat-conversation', 'ep:chat-square', 'ai/chat/manager/index.vue', 'AiChatManager', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-06-26 21:36:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2779, '会话查询', 'ai:chat-conversation:query', 3, 1, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2780, '会话删除', 'ai:chat-conversation:delete', 3, 2, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2781, '消息查询', 'ai:chat-message:query', 3, 11, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:38:56', '1', '2024-05-25 08:38:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2782, '消息删除', 'ai:chat-message:delete', 3, 12, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:39:10', '1', '2024-05-25 08:39:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2783, 'AI 绘画', '', 2, 2, 2758, 'image', 'ep:picture-rounded', 'ai/image/index/index.vue', 'AiImage', 0, '1', '1', '1', '1', '2024-05-26 11:45:17', '1', '2024-07-07 17:18:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2784, '绘画管理', '', 2, 11, 2760, 'image', 'fa:file-image-o', 'ai/image/manager/index.vue', 'AiImageManager', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 21:37:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2785, '绘画查询', 'ai:image:query', 3, 1, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:21:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2786, '绘画删除', 'ai:image:delete', 3, 4, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:22:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2787, '绘图更新', 'ai:image:update', 3, 2, 2784, '', '', '', '', 0, '1', '1', '1', '1', '2024-06-26 22:47:56', '1', '2024-08-31 09:21:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2788, '音乐管理', '', 2, 12, 2760, 'music', 'fa:music', 'ai/music/manager/index.vue', 'AiMusicManager', 0, '1', '1', '1', '', '2024-06-27 15:03:33', '1', '2024-06-27 23:04:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2789, '音乐查询', 'ai:music:query', 3, 1, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2790, '音乐更新', 'ai:music:update', 3, 3, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2791, '音乐删除', 'ai:music:delete', 3, 4, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2792, 'AI 写作', '', 2, 3, 2758, 'write', 'fa-solid:book-reader', 'ai/write/index/index.vue', 'AiWrite', 0, '1', '1', '1', '1', '2024-07-08 09:26:44', '1', '2024-07-16 13:03:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2793, '写作管理', '', 2, 13, 2760, 'write', 'fa:bookmark-o', 'ai/write/manager/index.vue', 'AiWriteManager', 0, '1', '1', '1', '', '2024-07-10 13:24:34', '1', '2024-07-10 21:31:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2794, 'AI 写作查询', 'ai:write:query', 3, 1, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, '1', '1', '1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, '1', '1', '1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2798, 'AI 思维导图', '', 2, 6, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, '1', '1', '1', '1', '2024-07-29 21:31:59', '1', '2025-03-02 18:57:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, '1', '1', '1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2802, '会话查询', 'promotion:kefu-conversation:query', 3, 1, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:17:52', '1', '2024-08-31 09:18:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2803, '会话更新', 'promotion:kefu-conversation:update', 3, 2, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:15', '1', '2024-08-31 09:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2804, '消息查询', 'promotion:kefu-message:query', 3, 10, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:42', '1', '2024-08-31 09:18:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2805, '会话删除', 'promotion:kefu-conversation:delete', 3, 3, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:19:51', '1', '2024-08-31 09:20:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2806, '消息发送', 'promotion:kefu-message:send', 3, 12, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:06', '1', '2024-08-31 09:20:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2807, '消息更新', 'promotion:kefu-message:update', 3, 11, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:22', '1', '2024-08-31 09:20:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2808, '积分商城', '', 2, 5, 2030, 'point-activity', 'ep:bowl', 'mall/promotion/point/activity/index', 'PointActivity', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-23 09:14:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2809, '积分商城活动查询', 'promotion:point-activity:query', 3, 1, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2810, '积分商城活动创建', 'promotion:point-activity:create', 3, 2, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2811, '积分商城活动更新', 'promotion:point-activity:update', 3, 3, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2812, '积分商城活动删除', 'promotion:point-activity:delete', 3, 4, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2813, '积分商城活动导出', 'promotion:point-activity:export', 3, 5, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, '1', '1', '1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-17 19:32:06', '1', '2025-01-17 19:32:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-23 20:23:34', '1', '2025-01-23 20:23:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2915, 'AI 知识库', '', 2, 5, 2758, 'knowledge', 'ep:notebook', 'ai/knowledge/knowledge/index', 'AiKnowledge', 0, '1', '1', '1', '', '2025-02-28 07:04:21', '1', '2025-03-02 18:58:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2916, 'AI 知识库查询', 'ai:knowledge:query', 3, 1, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2917, 'AI 知识库创建', 'ai:knowledge:create', 3, 2, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2918, 'AI 知识库更新', 'ai:knowledge:update', 3, 3, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2919, 'AI 知识库删除', 'ai:knowledge:delete', 3, 4, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2920, '工具管理', '', 2, 0, 2760, 'tool', 'fa-solid:tools', 'ai/model/tool/index.vue', 'AiTool', 0, '1', '1', '1', '', '2025-03-14 11:19:29', '1', '2025-03-14 19:20:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2921, '工具查询', 'ai:tool:query', 3, 1, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4000, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, '1', '1', '1', '1', '2024-08-10 09:55:28', '1', '2024-12-07 15:58:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4001, '设备接入', '', 1, 2, 4000, 'device', 'ep:platform', '', '', 0, '1', '1', '1', '1', '2024-08-10 09:57:56', '1', '2025-02-27 08:39:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4002, '产品管理', '', 2, 2, 4001, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, '1', '1', '1', '', '2024-08-10 02:38:02', '1', '2024-12-07 18:47:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4003, '产品查询', 'iot:product:query', 3, 1, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4004, '产品创建', 'iot:product:create', 3, 2, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4005, '产品更新', 'iot:product:update', 3, 3, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4006, '产品删除', 'iot:product:delete', 3, 4, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4007, '产品导出', 'iot:product:export', 3, 5, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4008, '设备管理', '', 2, 4, 4001, 'device', 'fa:mobile', 'iot/device/device/index', 'IoTDevice', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-14 11:39:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4009, '设备查询', 'iot:device:query', 3, 1, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4010, '设备创建', 'iot:device:create', 3, 2, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4011, '设备更新', 'iot:device:update', 3, 3, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4012, '设备删除', 'iot:device:delete', 3, 4, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4013, '设备导出', 'iot:device:export', 3, 5, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4014, '产品分类', '', 2, 1, 4001, 'product-category', 'ep:notebook', 'iot/product/category/index', 'IotProductCategory', 0, '1', '1', '1', '', '2024-12-07 16:01:35', '1', '2024-12-07 16:31:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4015, '产品分类查询', 'iot:product-category:query', 3, 1, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4016, '产品分类创建', 'iot:product-category:create', 3, 2, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4017, '产品分类更新', 'iot:product-category:update', 3, 3, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4018, '产品分类删除', 'iot:product-category:delete', 3, 4, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4025, '插件管理', '', 2, 5, 4047, 'plugin-config', 'ep:folder-opened', 'iot/plugin/index', 'IoTPlugin', 0, '1', '1', '1', '', '2024-12-09 21:25:06', '1', '2025-02-05 22:23:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4026, '插件查询', 'iot:plugin-config:query', 3, 1, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4027, '插件创建', 'iot:plugin-config:create', 3, 2, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4028, '插件更新', 'iot:plugin-config:update', 3, 3, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4029, '插件删除', 'iot:plugin-config:delete', 3, 4, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4030, '插件导出', 'iot:plugin-config:export', 3, 5, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4031, '设备分组', '', 2, 3, 4001, 'device-group', 'fa-solid:layer-group', 'iot/device/group/index', 'IotDeviceGroup', 0, '1', '1', '1', '', '2024-12-14 17:08:29', '1', '2024-12-14 17:09:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4032, '设备分组查询', 'iot:device-group:query', 3, 1, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4033, '设备分组创建', 'iot:device-group:create', 3, 2, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4034, '设备分组更新', 'iot:device-group:update', 3, 3, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4035, '设备分组删除', 'iot:device-group:delete', 3, 4, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4036, '设备导入', 'iot:device:import', 3, 6, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2024-12-15 10:35:47', '1', '2024-12-15 10:35:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4037, '产品物模型', '', 2, 2, 4001, 'thing-model', 'ep:mostly-cloudy', 'iot/thingmodel/index', 'IoTThingModel', 0, '0', '0', '0', '', '2024-12-16 17:17:50', '1', '2024-12-27 11:03:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4038, '产品物模型功能查询', 'iot:thing-model:query', 3, 1, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:51', '', '2025-03-17 09:14:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4039, '产品物模型功能创建', 'iot:thing-model:create', 3, 2, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:14:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4040, '产品物模型功能更新', 'iot:thing-model:update', 3, 3, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4041, '产品物模型功能删除', 'iot:thing-model:delete', 3, 4, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4042, '产品物模型功能导出', 'iot:thing-model:export', 3, 5, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:53', '', '2025-03-17 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4043, '设备上行', 'iot:device:upstream', 3, 7, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 04:40:16', '1', '2025-01-31 22:45:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4044, '设备属性查询', 'iot:device:property-query', 3, 10, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 11:52:54', '1', '2025-01-28 11:52:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4045, '设备日志查询', 'iot:device:log-query', 3, 11, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 11:53:22', '1', '2025-01-28 11:53:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4046, '设备下行', 'iot:device:downstream', 3, 8, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-31 22:46:11', '1', '2025-01-31 22:46:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4047, '运维管理', '', 1, 2, 4000, 'operations', 'fa:cog', '', '', 0, '1', '1', '1', '1', '2025-02-05 22:21:37', '1', '2025-02-05 22:22:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4048, '规则引擎', '', 1, 3, 4000, 'rule', 'fa-solid:cogs', '', '', 0, '1', '1', '1', '1', '2025-02-11 14:10:54', '1', '2025-02-11 14:10:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4049, '场景联动', '', 2, 1, 4048, 'scene', 'ep:link', 'iot/rule/scene/index', 'Scene', 0, '1', '1', '1', '1', '2025-02-11 14:12:44', '1', '2025-02-12 10:15:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4050, 'IoT首页', '', 2, 1, 4000, 'home', 'ep:home-filled', 'iot/home/index', 'IotHome', 0, '1', '1', '1', '1', '2025-02-27 08:39:35', '1', '2025-02-27 08:40:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4051, '数据桥梁', '', 2, 0, 4048, 'data-bridge', 'ep:guide', 'iot/rule/databridge/index', 'IotDataBridge', 0, '1', '1', '1', '', '2025-03-09 13:47:11', '1', '2025-03-09 13:47:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4052, 'IoT 数据桥梁查询', 'iot:data-bridge:query', 3, 1, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4053, 'IoT 数据桥梁创建', 'iot:data-bridge:create', 3, 2, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4054, 'IoT 数据桥梁更新', 'iot:data-bridge:update', 3, 3, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4055, 'IoT 数据桥梁删除', 'iot:data-bridge:delete', 3, 4, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4056, 'IoT 数据桥梁导出', 'iot:data-bridge:export', 3, 5, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5000, 'AI 工作流', '', 2, 5, 2758, 'workflow', 'fa:hand-grab-o', 'ai/workflow/index.vue', 'AiWorkflow', 0, '1', '1', '1', '1', '2025-03-25 09:50:27', '1', '2025-05-03 18:55:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5001, 'AI 工作流查询', 'ai:workflow:query', 3, 1, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:11', '1', '2025-03-25 09:51:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5002, 'AI 工作流创建', 'ai:workflow:create', 3, 2, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:28', '1', '2025-03-25 09:51:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5003, 'AI 工作流更新', 'ai:workflow:update', 3, 3, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:42', '1', '2025-03-25 09:51:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5004, 'AI 工作流删除', 'ai:workflow:delete', 3, 4, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:55', '1', '2025-03-25 09:52:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5005, 'AI 工作流测试', 'ai:workflow:test', 3, 5, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-30 10:29:41', '1', '2025-03-30 10:29:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5009, '仪表盘设计器', '', 2, 1, 1281, 'jimu-bi', 'fa:y-combinator', 'report/jmreport/bi', 'JimuBI', 0, '1', '1', '1', '1', '2025-05-03 09:57:15', '1', '2025-05-03 10:02:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5010, '租户切换', 'system:tenant:visit', 3, 999, 1138, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-05 15:25:32', '1', '2025-05-05 15:25:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5011, '转账订单查询', 'pay:transfer:query', 3, 1, 2559, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-08 12:46:53', '1', '2025-05-08 12:46:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5012, '转账订单导出', 'pay:transfer:export', 3, 2, 2559, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-10 17:00:28', '1', '2025-05-10 17:00:28', '0');\nCOMMIT;\nSET IDENTITY_INSERT system_menu OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_notice\n-- ----------------------------\nCREATE TABLE system_notice\n(\n    id          bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    title       varchar(50)                           NOT NULL,\n    content     text                                  NOT NULL,\n    type        smallint                              NOT NULL,\n    status      smallint    DEFAULT 0                 NOT NULL,\n    creator     varchar(64) DEFAULT ''                NULL,\n    create_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64) DEFAULT ''                NULL,\n    update_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit         DEFAULT '0'               NOT NULL,\n    tenant_id   bigint      DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_notice.id IS '公告ID';\nCOMMENT ON COLUMN system_notice.title IS '公告标题';\nCOMMENT ON COLUMN system_notice.content IS '公告内容';\nCOMMENT ON COLUMN system_notice.type IS '公告类型（1通知 2公告）';\nCOMMENT ON COLUMN system_notice.status IS '公告状态（0正常 1关闭）';\nCOMMENT ON COLUMN system_notice.creator IS '创建者';\nCOMMENT ON COLUMN system_notice.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notice.updater IS '更新者';\nCOMMENT ON COLUMN system_notice.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notice.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notice.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notice IS '通知公告表';\n\n-- ----------------------------\n-- Records of system_notice\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_notice ON;\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '芋道的公众', '<p>新版本内容133</p>', 1, 0, 'admin', '2021-01-05 17:03:48', '1', '2022-05-04 21:00:20', '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '维护通知：2018-07-01 系统凌晨维护', '<p><img src=\"http://test.yudao.iocoder.cn/b7cb3cf49b4b3258bf7309a09dd2f4e5.jpg\" alt=\"\" data-href=\"\">11112222<img src=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\" alt=\"image\" data-href=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\">3333</p>', 2, 1, 'admin', '2021-01-05 17:03:48', '1', '2025-04-18 23:56:40', '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '我是测试标题', '<p>哈哈哈哈123</p>', 1, 0, '110', '2022-02-22 01:01:25', '110', '2022-02-22 01:01:46', '0', 121);\nCOMMIT;\nSET IDENTITY_INSERT system_notice OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_notify_message\n-- ----------------------------\nCREATE TABLE system_notify_message\n(\n    id                bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    user_id           bigint                                NOT NULL,\n    user_type         smallint                              NOT NULL,\n    template_id       bigint                                NOT NULL,\n    template_code     varchar(64)                           NOT NULL,\n    template_nickname varchar(63)                           NOT NULL,\n    template_content  varchar(1024)                         NOT NULL,\n    template_type     int                                   NOT NULL,\n    template_params   varchar(255)                          NOT NULL,\n    read_status       bit                                   NOT NULL,\n    read_time         datetime    DEFAULT NULL              NULL,\n    creator           varchar(64) DEFAULT ''                NULL,\n    create_time       datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater           varchar(64) DEFAULT ''                NULL,\n    update_time       datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted           bit         DEFAULT '0'               NOT NULL,\n    tenant_id         bigint      DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_notify_message.id IS '用户ID';\nCOMMENT ON COLUMN system_notify_message.user_id IS '用户id';\nCOMMENT ON COLUMN system_notify_message.user_type IS '用户类型';\nCOMMENT ON COLUMN system_notify_message.template_id IS '模版编号';\nCOMMENT ON COLUMN system_notify_message.template_code IS '模板编码';\nCOMMENT ON COLUMN system_notify_message.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_notify_message.template_content IS '模版内容';\nCOMMENT ON COLUMN system_notify_message.template_type IS '模版类型';\nCOMMENT ON COLUMN system_notify_message.template_params IS '模版参数';\nCOMMENT ON COLUMN system_notify_message.read_status IS '是否已读';\nCOMMENT ON COLUMN system_notify_message.read_time IS '阅读时间';\nCOMMENT ON COLUMN system_notify_message.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_message.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_message.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_message.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_message.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notify_message.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notify_message IS '站内信消息表';\n\n-- ----------------------------\n-- Records of system_notify_message\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_notify_message ON;\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 11:44:08', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 11:45:04', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 103, 2, 2, 'register', '系统消息', '你好，欢迎 哈哈 加入大家庭！', 2, '{\"name\":\"哈哈\"}', '0', NULL, '1', '2023-01-28 21:02:20', '1', '2023-01-28 21:02:20', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 22:21:42', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', '2025-04-21 14:59:36', '1', '2023-01-28 22:22:07', '1', '2025-04-21 14:59:36', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 1, 2, 1, 'test', '123', '我是 2，我开始 3 了', 1, '{\"name\":\"2\",\"what\":\"3\"}', '1', '2025-04-21 14:59:35', '1', '2023-01-28 23:45:21', '1', '2025-04-21 14:59:35', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 1, 2, 2, 'register', '系统消息', '你好，欢迎 123 加入大家庭！', 2, '{\"name\":\"123\"}', '1', '2025-04-21 14:59:35', '1', '2023-01-28 23:50:21', '1', '2025-04-21 14:59:35', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-28 08:35:46提现￥0.09元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-28 08:35:46\",\"price\":\"0.09\"}', '0', NULL, '1', '2023-09-28 16:36:22', '1', '2023-09-28 16:36:22', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-30 20:59:40提现￥1.00元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-30 20:59:40\",\"price\":\"1.00\"}', '0', NULL, '1', '2023-10-03 12:11:34', '1', '2023-10-03 12:11:34', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT system_notify_message OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_notify_template\n-- ----------------------------\nCREATE TABLE system_notify_template\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name        varchar(63)                            NOT NULL,\n    code        varchar(64)                            NOT NULL,\n    nickname    varchar(255)                           NOT NULL,\n    content     varchar(1024)                          NOT NULL,\n    type        smallint                               NOT NULL,\n    params      varchar(255) DEFAULT NULL              NULL,\n    status      smallint                               NOT NULL,\n    remark      varchar(255) DEFAULT NULL              NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_notify_template.id IS '主键';\nCOMMENT ON COLUMN system_notify_template.name IS '模板名称';\nCOMMENT ON COLUMN system_notify_template.code IS '模版编码';\nCOMMENT ON COLUMN system_notify_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_notify_template.content IS '模版内容';\nCOMMENT ON COLUMN system_notify_template.type IS '类型';\nCOMMENT ON COLUMN system_notify_template.params IS '参数数组';\nCOMMENT ON COLUMN system_notify_template.status IS '状态';\nCOMMENT ON COLUMN system_notify_template.remark IS '备注';\nCOMMENT ON COLUMN system_notify_template.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_template.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_notify_template IS '站内信模板表';\n\n-- ----------------------------\n-- Table structure for system_oauth2_access_token\n-- ----------------------------\nCREATE TABLE system_oauth2_access_token\n(\n    id            bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    user_id       bigint                                 NOT NULL,\n    user_type     smallint                               NOT NULL,\n    user_info     varchar(512)                           NOT NULL,\n    access_token  varchar(255)                           NOT NULL,\n    refresh_token varchar(32)                            NOT NULL,\n    client_id     varchar(255)                           NOT NULL,\n    scopes        varchar(255) DEFAULT NULL              NULL,\n    expires_time  datetime                               NOT NULL,\n    creator       varchar(64)  DEFAULT ''                NULL,\n    create_time   datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       varchar(64)  DEFAULT ''                NULL,\n    update_time   datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       bit          DEFAULT '0'               NOT NULL,\n    tenant_id     bigint       DEFAULT 0                 NOT NULL\n);\n\nCREATE INDEX idx_system_oauth2_access_token_01 ON system_oauth2_access_token (access_token);\nCREATE INDEX idx_system_oauth2_access_token_02 ON system_oauth2_access_token (refresh_token);\n\nCOMMENT ON COLUMN system_oauth2_access_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_access_token.user_info IS '用户信息';\nCOMMENT ON COLUMN system_oauth2_access_token.access_token IS '访问令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_access_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_access_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_access_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_access_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_access_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_access_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_access_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_access_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_access_token IS 'OAuth2 访问令牌';\n\n-- ----------------------------\n-- Table structure for system_oauth2_approve\n-- ----------------------------\nCREATE TABLE system_oauth2_approve\n(\n    id           bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    user_id      bigint                                 NOT NULL,\n    user_type    smallint                               NOT NULL,\n    client_id    varchar(255)                           NOT NULL,\n    scope        varchar(255) DEFAULT ''                NULL,\n    approved     bit          DEFAULT '0'               NOT NULL,\n    expires_time datetime                               NOT NULL,\n    creator      varchar(64)  DEFAULT ''                NULL,\n    create_time  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      varchar(64)  DEFAULT ''                NULL,\n    update_time  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      bit          DEFAULT '0'               NOT NULL,\n    tenant_id    bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_oauth2_approve.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_approve.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_approve.scope IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_approve.approved IS '是否接受';\nCOMMENT ON COLUMN system_oauth2_approve.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_approve.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_approve.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_approve.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_approve.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_approve.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_approve.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_approve IS 'OAuth2 批准表';\n\n-- ----------------------------\n-- Table structure for system_oauth2_client\n-- ----------------------------\nCREATE TABLE system_oauth2_client\n(\n    id                             bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    client_id                      varchar(255)                            NOT NULL,\n    secret                         varchar(255)                            NOT NULL,\n    name                           varchar(255)                            NOT NULL,\n    logo                           varchar(255)                            NOT NULL,\n    description                    varchar(255)  DEFAULT NULL              NULL,\n    status                         smallint                                NOT NULL,\n    access_token_validity_seconds  int                                     NOT NULL,\n    refresh_token_validity_seconds int                                     NOT NULL,\n    redirect_uris                  varchar(255)                            NOT NULL,\n    authorized_grant_types         varchar(255)                            NOT NULL,\n    scopes                         varchar(255)  DEFAULT NULL              NULL,\n    auto_approve_scopes            varchar(255)  DEFAULT NULL              NULL,\n    authorities                    varchar(255)  DEFAULT NULL              NULL,\n    resource_ids                   varchar(255)  DEFAULT NULL              NULL,\n    additional_information         varchar(4096) DEFAULT NULL              NULL,\n    creator                        varchar(64)   DEFAULT ''                NULL,\n    create_time                    datetime      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater                        varchar(64)   DEFAULT ''                NULL,\n    update_time                    datetime      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted                        bit           DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_oauth2_client.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_client.secret IS '客户端密钥';\nCOMMENT ON COLUMN system_oauth2_client.name IS '应用名';\nCOMMENT ON COLUMN system_oauth2_client.logo IS '应用图标';\nCOMMENT ON COLUMN system_oauth2_client.description IS '应用描述';\nCOMMENT ON COLUMN system_oauth2_client.status IS '状态';\nCOMMENT ON COLUMN system_oauth2_client.access_token_validity_seconds IS '访问令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.refresh_token_validity_seconds IS '刷新令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.redirect_uris IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_client.authorized_grant_types IS '授权类型';\nCOMMENT ON COLUMN system_oauth2_client.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_client.auto_approve_scopes IS '自动通过的授权范围';\nCOMMENT ON COLUMN system_oauth2_client.authorities IS '权限';\nCOMMENT ON COLUMN system_oauth2_client.resource_ids IS '资源';\nCOMMENT ON COLUMN system_oauth2_client.additional_information IS '附加信息';\nCOMMENT ON COLUMN system_oauth2_client.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_client.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_client.deleted IS '是否删除';\nCOMMENT ON TABLE system_oauth2_client IS 'OAuth2 客户端表';\n\n-- ----------------------------\n-- Records of system_oauth2_client\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_oauth2_client ON;\nINSERT 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\"]', '[\"user.read\",\"user.write\"]', '[]', '[\"user.read\",\"user.write\"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-05-02 20:42:22', '0');\nINSERT 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/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.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-05-02 19:58:08', '0');\nINSERT 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', '0');\nINSERT 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/604bdc695e13b3b22745be704d1f2aa8ee05c5f26f9fead6d1ca49005afbc857.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-05-04 16:00:46', '0');\nCOMMIT;\nSET IDENTITY_INSERT system_oauth2_client OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_oauth2_code\n-- ----------------------------\nCREATE TABLE system_oauth2_code\n(\n    id           bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    user_id      bigint                                 NOT NULL,\n    user_type    smallint                               NOT NULL,\n    code         varchar(32)                            NOT NULL,\n    client_id    varchar(255)                           NOT NULL,\n    scopes       varchar(255) DEFAULT ''                NULL,\n    expires_time datetime                               NOT NULL,\n    redirect_uri varchar(255) DEFAULT NULL              NULL,\n    state        varchar(255) DEFAULT ''                NULL,\n    creator      varchar(64)  DEFAULT ''                NULL,\n    create_time  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      varchar(64)  DEFAULT ''                NULL,\n    update_time  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      bit          DEFAULT '0'               NOT NULL,\n    tenant_id    bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_oauth2_code.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_code.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_code.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_code.code IS '授权码';\nCOMMENT ON COLUMN system_oauth2_code.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_code.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_code.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_code.redirect_uri IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_code.state IS '状态';\nCOMMENT ON COLUMN system_oauth2_code.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_code.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_code IS 'OAuth2 授权码表';\n\n-- ----------------------------\n-- Table structure for system_oauth2_refresh_token\n-- ----------------------------\nCREATE TABLE system_oauth2_refresh_token\n(\n    id            bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    user_id       bigint                                 NOT NULL,\n    refresh_token varchar(32)                            NOT NULL,\n    user_type     smallint                               NOT NULL,\n    client_id     varchar(255)                           NOT NULL,\n    scopes        varchar(255) DEFAULT NULL              NULL,\n    expires_time  datetime                               NOT NULL,\n    creator       varchar(64)  DEFAULT ''                NULL,\n    create_time   datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       varchar(64)  DEFAULT ''                NULL,\n    update_time   datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       bit          DEFAULT '0'               NOT NULL,\n    tenant_id     bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_oauth2_refresh_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_refresh_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_refresh_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_refresh_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_refresh_token IS 'OAuth2 刷新令牌';\n\n-- ----------------------------\n-- Table structure for system_operate_log\n-- ----------------------------\nCREATE TABLE system_operate_log\n(\n    id             bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    trace_id       varchar(64)   DEFAULT ''                NULL,\n    user_id        bigint                                  NOT NULL,\n    user_type      smallint      DEFAULT 0                 NOT NULL,\n    type           varchar(50)                             NOT NULL,\n    sub_type       varchar(50)                             NOT NULL,\n    biz_id         bigint                                  NOT NULL,\n    action         varchar(2000) DEFAULT ''                NULL,\n    success        bit           DEFAULT '1'               NOT NULL,\n    extra          varchar(2000) DEFAULT ''                NULL,\n    request_method varchar(16)   DEFAULT ''                NULL,\n    request_url    varchar(255)  DEFAULT ''                NULL,\n    user_ip        varchar(50)   DEFAULT NULL              NULL,\n    user_agent     varchar(512)  DEFAULT NULL              NULL,\n    creator        varchar(64)   DEFAULT ''                NULL,\n    create_time    datetime      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar(64)   DEFAULT ''                NULL,\n    update_time    datetime      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit           DEFAULT '0'               NOT NULL,\n    tenant_id      bigint        DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_operate_log.id IS '日志主键';\nCOMMENT ON COLUMN system_operate_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_operate_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_operate_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_operate_log.type IS '操作模块类型';\nCOMMENT ON COLUMN system_operate_log.sub_type IS '操作名';\nCOMMENT ON COLUMN system_operate_log.biz_id IS '操作数据模块编号';\nCOMMENT ON COLUMN system_operate_log.action IS '操作内容';\nCOMMENT ON COLUMN system_operate_log.success IS '操作结果';\nCOMMENT ON COLUMN system_operate_log.extra IS '拓展字段';\nCOMMENT ON COLUMN system_operate_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN system_operate_log.request_url IS '请求地址';\nCOMMENT ON COLUMN system_operate_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_operate_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_operate_log.creator IS '创建者';\nCOMMENT ON COLUMN system_operate_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_operate_log.updater IS '更新者';\nCOMMENT ON COLUMN system_operate_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_operate_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_operate_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_operate_log IS '操作日志记录 V2 版本';\n\n-- ----------------------------\n-- Table structure for system_post\n-- ----------------------------\nCREATE TABLE system_post\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    code        varchar(64)                            NOT NULL,\n    name        varchar(50)                            NOT NULL,\n    sort        int                                    NOT NULL,\n    status      smallint                               NOT NULL,\n    remark      varchar(500) DEFAULT NULL              NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_post.id IS '岗位ID';\nCOMMENT ON COLUMN system_post.code IS '岗位编码';\nCOMMENT ON COLUMN system_post.name IS '岗位名称';\nCOMMENT ON COLUMN system_post.sort IS '显示顺序';\nCOMMENT ON COLUMN system_post.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_post.remark IS '备注';\nCOMMENT ON COLUMN system_post.creator IS '创建者';\nCOMMENT ON COLUMN system_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_post.updater IS '更新者';\nCOMMENT ON COLUMN system_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_post IS '岗位信息表';\n\n-- ----------------------------\n-- Records of system_post\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_post ON;\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 'ceo', '董事长', 1, 0, '', 'admin', '2021-01-06 17:03:48', '1', '2023-02-11 15:19:04', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 'se', '项目经理', 2, 0, '', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 09:18:20', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 'user', '普通员工', 4, 0, '111222', 'admin', '2021-01-05 17:03:48', '1', '2025-03-24 21:32:40', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 'HR', '人力资源', 5, 0, '`', '1', '2024-03-24 20:45:40', '1', '2025-03-29 19:08:10', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT system_post OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_role\n-- ----------------------------\nCREATE TABLE system_role\n(\n    id                  bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name                varchar(30)                            NOT NULL,\n    code                varchar(100)                           NOT NULL,\n    sort                int                                    NOT NULL,\n    data_scope          smallint     DEFAULT 1                 NOT NULL,\n    data_scope_dept_ids varchar(500) DEFAULT ''                NULL,\n    status              smallint                               NOT NULL,\n    type                smallint                               NOT NULL,\n    remark              varchar(500) DEFAULT NULL              NULL,\n    creator             varchar(64)  DEFAULT ''                NULL,\n    create_time         datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater             varchar(64)  DEFAULT ''                NULL,\n    update_time         datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted             bit          DEFAULT '0'               NOT NULL,\n    tenant_id           bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_role.id IS '角色ID';\nCOMMENT ON COLUMN system_role.name IS '角色名称';\nCOMMENT ON COLUMN system_role.code IS '角色权限字符串';\nCOMMENT ON COLUMN system_role.sort IS '显示顺序';\nCOMMENT ON COLUMN system_role.data_scope IS '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）';\nCOMMENT ON COLUMN system_role.data_scope_dept_ids IS '数据范围 ( 指定部门数组)';\nCOMMENT ON COLUMN system_role.status IS '角色状态（0正常 1停用）';\nCOMMENT ON COLUMN system_role.type IS '角色类型';\nCOMMENT ON COLUMN system_role.remark IS '备注';\nCOMMENT ON COLUMN system_role.creator IS '创建者';\nCOMMENT ON COLUMN system_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role.updater IS '更新者';\nCOMMENT ON COLUMN system_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role IS '角色信息表';\n\n-- ----------------------------\n-- Records of system_role\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_role ON;\nINSERT 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 (1, '超级管理员', 'super_admin', 1, 1, '', 0, 1, '超级管理员', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:21', '0', 1);\nINSERT 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 (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:20', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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 (101, '测试账号', 'test', 0, 1, '[]', 0, 2, '123', '', '2021-01-06 13:49:35', '1', '2025-04-30 17:38:28', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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, '测试数据权限', 'test-dp', 3, 2, '[100,102,103,104,105,108]', 0, 2, '', '1', '2025-03-31 14:58:06', '1', '2025-04-17 23:07:44', '0', 1);\nINSERT 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 (158, '2', '3', 4, 1, '', 0, 2, NULL, '1', '2025-04-17 20:08:08', '1', '2025-04-17 23:05:31', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT system_role OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_role_menu\n-- ----------------------------\nCREATE TABLE system_role_menu\n(\n    id          bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    role_id     bigint                                NOT NULL,\n    menu_id     bigint                                NOT NULL,\n    creator     varchar(64) DEFAULT ''                NULL,\n    create_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64) DEFAULT ''                NULL,\n    update_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit         DEFAULT '0'               NOT NULL,\n    tenant_id   bigint      DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_role_menu.id IS '自增编号';\nCOMMENT ON COLUMN system_role_menu.role_id IS '角色ID';\nCOMMENT ON COLUMN system_role_menu.menu_id IS '菜单ID';\nCOMMENT ON COLUMN system_role_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_role_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_role_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role_menu.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role_menu.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role_menu IS '角色和菜单关联表';\n\n-- ----------------------------\n-- Records of system_role_menu\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_role_menu ON;\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (263, 109, 1, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (434, 2, 1, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (454, 2, 1093, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (455, 2, 1094, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (460, 2, 1100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (467, 2, 1107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (476, 2, 1117, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (477, 2, 100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (478, 2, 101, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (479, 2, 102, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (480, 2, 1126, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (481, 2, 103, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (483, 2, 104, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (485, 2, 105, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (488, 2, 107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (490, 2, 108, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (492, 2, 109, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (498, 2, 1138, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (523, 2, 1224, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (524, 2, 1225, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (541, 2, 500, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (543, 2, 501, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (675, 2, 2, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (689, 2, 1077, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (690, 2, 1078, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (692, 2, 1083, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (693, 2, 1084, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (699, 2, 1090, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (703, 2, 106, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (704, 2, 110, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (705, 2, 111, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1604, 101, 1216, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1605, 101, 1217, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1606, 101, 1218, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1607, 101, 1219, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1608, 101, 1220, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1609, 101, 1221, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1610, 101, 5, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1611, 101, 1222, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1612, 101, 1118, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1613, 101, 1119, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1614, 101, 1120, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1615, 101, 1185, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1616, 101, 1186, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1617, 101, 1187, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1618, 101, 1188, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1619, 101, 1189, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1620, 101, 1190, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1621, 101, 1191, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1622, 101, 1192, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1623, 101, 1193, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1624, 101, 1194, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1625, 101, 1195, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1627, 101, 1197, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1628, 101, 1198, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1629, 101, 1199, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1630, 101, 1200, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1631, 101, 1201, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1632, 101, 1202, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1633, 101, 1207, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1634, 101, 1208, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1635, 101, 1209, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1636, 101, 1210, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1637, 101, 1211, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1638, 101, 1212, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1639, 101, 1213, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1640, 101, 1215, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1641, 101, 2, '1', '2022-04-01 22:21:24', '1', '2022-04-01 22:21:24', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1642, 101, 1031, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1643, 101, 1032, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1644, 101, 1033, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1645, 101, 1034, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1646, 101, 1035, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1647, 101, 1050, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1648, 101, 1051, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1649, 101, 1052, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1650, 101, 1053, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1651, 101, 1054, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1652, 101, 1056, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1653, 101, 1057, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1654, 101, 1058, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1655, 101, 1059, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1656, 101, 1060, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1657, 101, 1066, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1658, 101, 1067, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1659, 101, 1070, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1664, 101, 1075, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1666, 101, 1077, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1667, 101, 1078, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1668, 101, 1082, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1669, 101, 1083, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1670, 101, 1084, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1671, 101, 1085, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1672, 101, 1086, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1673, 101, 1087, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1674, 101, 1088, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1675, 101, 1089, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1679, 101, 1237, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1680, 101, 1238, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1681, 101, 1239, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1682, 101, 1240, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1683, 101, 1241, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1684, 101, 1242, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1685, 101, 1243, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1687, 101, 106, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1688, 101, 110, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1689, 101, 111, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1690, 101, 112, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1691, 101, 113, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1692, 101, 114, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1693, 101, 115, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1694, 101, 116, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1732, 109, 1064, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1733, 109, 1001, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1734, 109, 1065, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1735, 109, 1002, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1736, 109, 1003, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1737, 109, 1004, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1738, 109, 1005, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1739, 109, 1006, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1740, 109, 1007, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1741, 109, 1008, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1742, 109, 1009, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1766, 109, 1064, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1767, 109, 1001, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1768, 109, 1065, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1769, 109, 1002, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1770, 109, 1003, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1771, 109, 1004, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1772, 109, 1005, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1773, 109, 1006, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1774, 109, 1007, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1775, 109, 1008, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1776, 109, 1009, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1800, 109, 1064, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1801, 109, 1001, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1802, 109, 1065, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1803, 109, 1002, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1804, 109, 1003, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1805, 109, 1004, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1806, 109, 1005, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1807, 109, 1006, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1808, 109, 1007, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1809, 109, 1008, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1810, 109, 1009, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1994, 2, 1027, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1995, 2, 1028, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1996, 2, 1029, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1997, 2, 1030, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1998, 2, 1031, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1999, 2, 1032, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2000, 2, 1033, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2001, 2, 1034, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2002, 2, 1035, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2003, 2, 1036, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2004, 2, 1037, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2005, 2, 1038, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2006, 2, 1039, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2007, 2, 1040, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2008, 2, 1042, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2009, 2, 1043, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2010, 2, 1045, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2011, 2, 1046, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2012, 2, 1048, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2013, 2, 1050, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2014, 2, 1051, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2015, 2, 1052, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2016, 2, 1053, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2017, 2, 1054, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2018, 2, 1056, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2019, 2, 1057, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2020, 2, 1058, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2021, 2, 2083, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2022, 2, 1059, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2023, 2, 1060, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2024, 2, 1063, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2025, 2, 1064, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2026, 2, 1065, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2027, 2, 1066, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2028, 2, 1067, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2029, 2, 1070, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2034, 2, 1075, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2036, 2, 1082, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2037, 2, 1085, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2038, 2, 1086, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2039, 2, 1087, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2040, 2, 1088, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2041, 2, 1089, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2042, 2, 1091, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2043, 2, 1092, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2044, 2, 1095, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2045, 2, 1096, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2046, 2, 1097, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2047, 2, 1098, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2048, 2, 1101, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2049, 2, 1102, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2050, 2, 1103, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2051, 2, 1104, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2052, 2, 1105, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2053, 2, 1106, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2054, 2, 1108, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2055, 2, 1109, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2061, 2, 1127, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2062, 2, 1128, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2063, 2, 1129, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2064, 2, 1130, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2066, 2, 1132, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2067, 2, 1133, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2068, 2, 1134, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2069, 2, 1135, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2070, 2, 1136, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2071, 2, 1137, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2072, 2, 114, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2073, 2, 1139, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2074, 2, 115, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2075, 2, 1140, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2076, 2, 116, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2077, 2, 1141, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2078, 2, 1142, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2079, 2, 1143, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2080, 2, 1150, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2081, 2, 1161, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2082, 2, 1162, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2086, 2, 1166, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2087, 2, 1173, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2088, 2, 1174, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2092, 2, 1178, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2099, 2, 1226, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2100, 2, 1227, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2101, 2, 1228, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2102, 2, 1229, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2103, 2, 1237, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2104, 2, 1238, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2105, 2, 1239, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2106, 2, 1240, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2107, 2, 1241, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2108, 2, 1242, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2109, 2, 1243, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2116, 2, 1254, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2117, 2, 1255, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2118, 2, 1256, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2119, 2, 1257, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2120, 2, 1258, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2121, 2, 1259, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2122, 2, 1260, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2123, 2, 1261, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2124, 2, 1263, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2125, 2, 1264, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2126, 2, 1265, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2127, 2, 1266, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2128, 2, 1267, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2129, 2, 1001, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2130, 2, 1002, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2131, 2, 1003, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2132, 2, 1004, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2133, 2, 1005, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2134, 2, 1006, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2135, 2, 1007, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2136, 2, 1008, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2137, 2, 1009, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2138, 2, 1010, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2148, 2, 1020, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2149, 2, 1021, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2150, 2, 1022, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2151, 2, 1023, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2152, 2, 1281, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2153, 2, 1282, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2154, 2, 2000, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2155, 2, 2002, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2156, 2, 2003, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2157, 2, 2004, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2158, 2, 2005, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2159, 2, 2006, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2160, 2, 2008, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2161, 2, 2009, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2162, 2, 2010, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2163, 2, 2011, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2164, 2, 2012, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2170, 2, 2019, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2171, 2, 2020, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2172, 2, 2021, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2173, 2, 2022, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2174, 2, 2023, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2175, 2, 2025, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2177, 2, 2027, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2178, 2, 2028, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2179, 2, 2029, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2180, 2, 2014, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2181, 2, 2015, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2182, 2, 2016, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2183, 2, 2017, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2184, 2, 2018, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2188, 101, 1024, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2189, 101, 1, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2190, 101, 1025, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2191, 101, 1026, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2192, 101, 1027, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2193, 101, 1028, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2194, 101, 1029, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2195, 101, 1030, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2196, 101, 1036, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2197, 101, 1037, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2198, 101, 1038, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2199, 101, 1039, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2200, 101, 1040, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2201, 101, 1042, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2202, 101, 1043, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2203, 101, 1045, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2204, 101, 1046, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2205, 101, 1048, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2206, 101, 2083, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2207, 101, 1063, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2208, 101, 1064, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2209, 101, 1065, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2210, 101, 1093, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2211, 101, 1094, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2212, 101, 1095, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2213, 101, 1096, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2214, 101, 1097, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2215, 101, 1098, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2216, 101, 1100, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2217, 101, 1101, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2218, 101, 1102, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2219, 101, 1103, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2220, 101, 1104, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2221, 101, 1105, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2222, 101, 1106, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2223, 101, 2130, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2224, 101, 1107, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2225, 101, 2131, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2226, 101, 1108, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2227, 101, 2132, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2228, 101, 1109, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2229, 101, 2133, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2230, 101, 2134, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2232, 101, 2135, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2234, 101, 2136, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2236, 101, 2137, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2238, 101, 2138, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2240, 101, 2139, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2242, 101, 2140, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2243, 101, 2141, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2244, 101, 2142, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2245, 101, 2143, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2246, 101, 2144, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2247, 101, 2145, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2248, 101, 2146, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2249, 101, 2147, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2250, 101, 100, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2251, 101, 2148, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2252, 101, 101, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2253, 101, 2149, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2254, 101, 102, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2255, 101, 2150, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2256, 101, 103, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2257, 101, 2151, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2258, 101, 104, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2259, 101, 2152, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2260, 101, 105, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2261, 101, 107, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2262, 101, 108, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2263, 101, 109, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2264, 101, 1138, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2265, 101, 1139, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2266, 101, 1140, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2267, 101, 1141, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2268, 101, 1142, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2269, 101, 1143, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2270, 101, 1224, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2271, 101, 1225, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2272, 101, 1226, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2273, 101, 1227, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2274, 101, 1228, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2275, 101, 1229, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2282, 101, 1261, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2283, 101, 1263, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2284, 101, 1264, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2285, 101, 1265, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2286, 101, 1266, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2287, 101, 1267, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2288, 101, 1001, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2289, 101, 1002, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2290, 101, 1003, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2291, 101, 1004, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2292, 101, 1005, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2293, 101, 1006, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2294, 101, 1007, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2295, 101, 1008, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2296, 101, 1009, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2297, 101, 1010, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2298, 101, 1011, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2299, 101, 1012, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2300, 101, 500, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2301, 101, 1013, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2302, 101, 501, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2303, 101, 1014, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2304, 101, 1015, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2305, 101, 1016, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2306, 101, 1017, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2307, 101, 1018, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2308, 101, 1019, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2309, 101, 1020, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2310, 101, 1021, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2311, 101, 1022, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2312, 101, 1023, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2929, 109, 1224, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2930, 109, 1225, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2931, 109, 1226, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2932, 109, 1227, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2933, 109, 1228, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2934, 109, 1229, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2935, 109, 1138, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2936, 109, 1139, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2937, 109, 1140, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2996, 109, 1033, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2997, 109, 1034, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2998, 109, 1035, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2999, 109, 1050, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3000, 109, 1051, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3001, 109, 1052, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3002, 109, 1053, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3003, 109, 1054, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3004, 109, 1056, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3005, 109, 1057, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3006, 109, 1058, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3007, 109, 1059, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3008, 109, 1060, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3009, 109, 1066, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3010, 109, 1067, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3011, 109, 1070, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3012, 109, 1075, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3014, 109, 1077, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3015, 109, 1078, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3016, 109, 1082, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3017, 109, 1083, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3018, 109, 1084, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3019, 109, 1085, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3020, 109, 1086, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3021, 109, 1087, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3022, 109, 1088, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3023, 109, 1089, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3024, 109, 1090, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3025, 109, 1091, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3026, 109, 1092, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3027, 109, 106, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3028, 109, 110, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3029, 109, 111, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3030, 109, 112, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3031, 109, 113, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3032, 109, 114, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3033, 109, 115, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3034, 109, 116, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3035, 109, 2472, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3036, 109, 2478, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3037, 109, 2479, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3038, 109, 2480, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3039, 109, 2481, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3040, 109, 2482, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3041, 109, 2483, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3042, 109, 2484, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3043, 109, 2485, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3044, 109, 2486, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3045, 109, 2487, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3046, 109, 2488, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3047, 109, 2489, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3048, 109, 2490, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3049, 109, 2491, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3050, 109, 2492, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3051, 109, 2493, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3052, 109, 2494, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3053, 109, 2495, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3054, 109, 2497, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3055, 109, 1237, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3056, 109, 1238, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3057, 109, 1239, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3058, 109, 1240, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3059, 109, 1241, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3060, 109, 1242, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3061, 109, 1243, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3062, 109, 2525, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3063, 109, 1255, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3064, 109, 1256, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3065, 109, 1257, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4166, 109, 1120, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4167, 109, 2713, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4168, 109, 2714, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4169, 109, 2715, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4170, 109, 2716, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4171, 109, 2717, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4172, 109, 2718, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4173, 109, 2720, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4174, 109, 1185, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4175, 109, 2721, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4176, 109, 1186, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4177, 109, 2722, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4178, 109, 1187, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4179, 109, 2723, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4180, 109, 1188, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4181, 109, 2724, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4182, 109, 1189, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4183, 109, 2725, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4184, 109, 1190, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4185, 109, 2726, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4186, 109, 1191, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4187, 109, 2727, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4188, 109, 1192, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4189, 109, 2728, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4190, 109, 1193, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4191, 109, 2729, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4192, 109, 1194, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4193, 109, 2730, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4194, 109, 1195, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4195, 109, 2731, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4196, 109, 1196, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4197, 109, 2732, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4198, 109, 1197, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4199, 109, 2733, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4200, 109, 1198, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4201, 109, 2734, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4202, 109, 1199, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4203, 109, 2735, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4204, 109, 1200, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4205, 109, 1201, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4206, 109, 1202, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4207, 109, 1207, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4208, 109, 1208, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4209, 109, 1209, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4210, 109, 1210, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4211, 109, 1211, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4212, 109, 1212, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4213, 109, 1213, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4214, 109, 1215, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4215, 109, 1216, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4216, 109, 1217, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4217, 109, 1218, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4218, 109, 1219, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4255, 111, 1196, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5777, 101, 2739, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5778, 101, 2740, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5782, 2, 2759, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5783, 2, 2362, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5784, 2, 2387, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5786, 101, 2758, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5787, 101, 2759, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5788, 101, 2783, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6105, 155, 4033, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6106, 155, 4034, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6107, 155, 4035, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6108, 155, 4036, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6109, 155, 4037, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6110, 155, 4038, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6111, 155, 4039, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6112, 155, 4040, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6113, 155, 4041, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6114, 155, 4042, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6115, 155, 4043, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6116, 155, 4044, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6117, 155, 4045, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6118, 155, 4046, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6119, 155, 4001, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6120, 155, 4002, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6121, 155, 4003, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6122, 155, 4004, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6123, 155, 4005, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6124, 155, 4006, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6125, 155, 4007, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6126, 155, 4008, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6127, 155, 4009, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6128, 155, 4010, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6129, 155, 4011, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6130, 155, 4012, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6131, 155, 4013, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6132, 155, 4014, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6133, 155, 4015, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6134, 155, 4016, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6135, 155, 4017, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6136, 155, 4018, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6137, 155, 4031, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6138, 101, 5010, '1', '2025-05-05 17:49:17', '1', '2025-05-05 17:49:17', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT system_role_menu OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_sms_channel\n-- ----------------------------\nCREATE TABLE system_sms_channel\n(\n    id           bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    signature    varchar(12)                            NOT NULL,\n    code         varchar(63)                            NOT NULL,\n    status       smallint                               NOT NULL,\n    remark       varchar(255) DEFAULT NULL              NULL,\n    api_key      varchar(128)                           NOT NULL,\n    api_secret   varchar(128) DEFAULT NULL              NULL,\n    callback_url varchar(255) DEFAULT NULL              NULL,\n    creator      varchar(64)  DEFAULT ''                NULL,\n    create_time  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      varchar(64)  DEFAULT ''                NULL,\n    update_time  datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_sms_channel.id IS '编号';\nCOMMENT ON COLUMN system_sms_channel.signature IS '短信签名';\nCOMMENT ON COLUMN system_sms_channel.code IS '渠道编码';\nCOMMENT ON COLUMN system_sms_channel.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_channel.remark IS '备注';\nCOMMENT ON COLUMN system_sms_channel.api_key IS '短信 API 的账号';\nCOMMENT ON COLUMN system_sms_channel.api_secret IS '短信 API 的秘钥';\nCOMMENT ON COLUMN system_sms_channel.callback_url IS '短信发送回调 URL';\nCOMMENT ON COLUMN system_sms_channel.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_channel.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_channel.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_channel.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_channel.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_channel IS '短信渠道';\n\n-- ----------------------------\n-- Records of system_sms_channel\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_sms_channel ON;\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (2, 'Ballcat', 'ALIYUN', 0, '你要改哦，只有我可以用！！！！', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', '2021-03-31 11:53:10', '1', '2024-08-04 08:53:26', '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (4, '测试渠道', 'DEBUG_DING_TALK', 0, '123', '696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859', 'SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67', NULL, '1', '2021-04-13 00:23:14', '1', '2022-03-27 20:29:49', '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (7, 'mock腾讯云', 'TENCENT', 0, '', '1 2', '2 3', '', '1', '2024-09-30 08:53:45', '1', '2024-09-30 08:55:01', '0');\nCOMMIT;\nSET IDENTITY_INSERT system_sms_channel OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_sms_code\n-- ----------------------------\nCREATE TABLE system_sms_code\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    mobile      varchar(11)                            NOT NULL,\n    code        varchar(6)                             NOT NULL,\n    create_ip   varchar(15)                            NOT NULL,\n    scene       smallint                               NOT NULL,\n    today_index smallint                               NOT NULL,\n    used        smallint                               NOT NULL,\n    used_time   datetime     DEFAULT NULL              NULL,\n    used_ip     varchar(255) DEFAULT NULL              NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n);\n\nCREATE INDEX idx_system_sms_code_01 ON system_sms_code (mobile);\n\nCOMMENT ON COLUMN system_sms_code.id IS '编号';\nCOMMENT ON COLUMN system_sms_code.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_code.code IS '验证码';\nCOMMENT ON COLUMN system_sms_code.create_ip IS '创建 IP';\nCOMMENT ON COLUMN system_sms_code.scene IS '发送场景';\nCOMMENT ON COLUMN system_sms_code.today_index IS '今日发送的第几条';\nCOMMENT ON COLUMN system_sms_code.used IS '是否使用';\nCOMMENT ON COLUMN system_sms_code.used_time IS '使用时间';\nCOMMENT ON COLUMN system_sms_code.used_ip IS '使用 IP';\nCOMMENT ON COLUMN system_sms_code.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_code.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_sms_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_sms_code IS '手机验证码';\n\n-- ----------------------------\n-- Table structure for system_sms_log\n-- ----------------------------\nCREATE TABLE system_sms_log\n(\n    id               bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    channel_id       bigint                                 NOT NULL,\n    channel_code     varchar(63)                            NOT NULL,\n    template_id      bigint                                 NOT NULL,\n    template_code    varchar(63)                            NOT NULL,\n    template_type    smallint                               NOT NULL,\n    template_content varchar(255)                           NOT NULL,\n    template_params  varchar(255)                           NOT NULL,\n    api_template_id  varchar(63)                            NOT NULL,\n    mobile           varchar(11)                            NOT NULL,\n    user_id          bigint       DEFAULT NULL              NULL,\n    user_type        smallint     DEFAULT NULL              NULL,\n    send_status      smallint     DEFAULT 0                 NOT NULL,\n    send_time        datetime     DEFAULT NULL              NULL,\n    api_send_code    varchar(63)  DEFAULT NULL              NULL,\n    api_send_msg     varchar(255) DEFAULT NULL              NULL,\n    api_request_id   varchar(255) DEFAULT NULL              NULL,\n    api_serial_no    varchar(255) DEFAULT NULL              NULL,\n    receive_status   smallint     DEFAULT 0                 NOT NULL,\n    receive_time     datetime     DEFAULT NULL              NULL,\n    api_receive_code varchar(63)  DEFAULT NULL              NULL,\n    api_receive_msg  varchar(255) DEFAULT NULL              NULL,\n    creator          varchar(64)  DEFAULT ''                NULL,\n    create_time      datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater          varchar(64)  DEFAULT ''                NULL,\n    update_time      datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted          bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_sms_log.id IS '编号';\nCOMMENT ON COLUMN system_sms_log.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_log.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_sms_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_sms_log.template_type IS '短信类型';\nCOMMENT ON COLUMN system_sms_log.template_content IS '短信内容';\nCOMMENT ON COLUMN system_sms_log.template_params IS '短信参数';\nCOMMENT ON COLUMN system_sms_log.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_log.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_sms_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_sms_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_sms_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_sms_log.api_send_code IS '短信 API 发送结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_send_msg IS '短信 API 发送失败的提示';\nCOMMENT ON COLUMN system_sms_log.api_request_id IS '短信 API 发送返回的唯一请求 ID';\nCOMMENT ON COLUMN system_sms_log.api_serial_no IS '短信 API 发送返回的序号';\nCOMMENT ON COLUMN system_sms_log.receive_status IS '接收状态';\nCOMMENT ON COLUMN system_sms_log.receive_time IS '接收时间';\nCOMMENT ON COLUMN system_sms_log.api_receive_code IS 'API 接收结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_receive_msg IS 'API 接收结果的说明';\nCOMMENT ON COLUMN system_sms_log.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_log.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_log IS '短信日志';\n\n-- ----------------------------\n-- Table structure for system_sms_template\n-- ----------------------------\nCREATE TABLE system_sms_template\n(\n    id              bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    type            smallint                               NOT NULL,\n    status          smallint                               NOT NULL,\n    code            varchar(63)                            NOT NULL,\n    name            varchar(63)                            NOT NULL,\n    content         varchar(255)                           NOT NULL,\n    params          varchar(255)                           NOT NULL,\n    remark          varchar(255) DEFAULT NULL              NULL,\n    api_template_id varchar(63)                            NOT NULL,\n    channel_id      bigint                                 NOT NULL,\n    channel_code    varchar(63)                            NOT NULL,\n    creator         varchar(64)  DEFAULT ''                NULL,\n    create_time     datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         varchar(64)  DEFAULT ''                NULL,\n    update_time     datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_sms_template.id IS '编号';\nCOMMENT ON COLUMN system_sms_template.type IS '模板类型';\nCOMMENT ON COLUMN system_sms_template.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_template.code IS '模板编码';\nCOMMENT ON COLUMN system_sms_template.name IS '模板名称';\nCOMMENT ON COLUMN system_sms_template.content IS '模板内容';\nCOMMENT ON COLUMN system_sms_template.params IS '参数数组';\nCOMMENT ON COLUMN system_sms_template.remark IS '备注';\nCOMMENT ON COLUMN system_sms_template.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_template.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_template.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_template.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_template.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_template IS '短信模板';\n\n-- ----------------------------\n-- Records of system_sms_template\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_sms_template ON;\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (2, 1, 0, 'test_01', '测试验证码短信', '正在进行登录操作{operation}，您的验证码是{code}', '[\"operation\",\"code\"]', '测试备注', '4383920', 4, 'DEBUG_DING_TALK', '', '2021-03-31 10:49:38', '1', '2024-08-18 11:57:18', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (3, 1, 0, 'test_02', '公告通知', '您的验证码{code}，该验证码5分钟内有效，请勿泄漏于他人！', '[\"code\"]', NULL, 'SMS_207945135', 2, 'ALIYUN', '', '2021-03-31 11:56:30', '1', '2021-04-10 01:22:02', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (6, 3, 0, 'test-01', '测试模板', '哈哈哈 {name}', '[\"name\"]', 'f哈哈哈', '4383920', 4, 'DEBUG_DING_TALK', '1', '2021-04-10 01:07:21', '1', '2024-08-18 11:57:07', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (7, 3, 0, 'test-04', '测试下', '老鸡{name}，牛逼{code}', '[\"name\",\"code\"]', '哈哈哈哈', 'suibian', 7, 'DEBUG_DING_TALK', '1', '2021-04-13 00:29:53', '1', '2024-09-30 00:56:24', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (8, 1, 0, 'user-sms-login', '前台用户短信登录', '您的验证码是{code}', '[\"code\"]', NULL, '4372216', 4, 'DEBUG_DING_TALK', '1', '2021-10-11 08:10:00', '1', '2024-08-18 11:57:06', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (9, 2, 0, 'bpm_task_assigned', '【工作流】任务被分配', '您收到了一条新的待办任务：{processInstanceName}-{taskName}，申请人：{startUserNickname}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"startUserNickname\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-21 22:31:19', '1', '2022-01-22 00:03:36', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (10, 2, 0, 'bpm_process_instance_reject', '【工作流】流程被不通过', '您的流程被审批不通过：{processInstanceName}，原因：{reason}，查看链接：{detailUrl}', '[\"processInstanceName\",\"reason\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:03:31', '1', '2022-05-01 12:33:14', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (11, 2, 0, 'bpm_process_instance_approve', '【工作流】流程被通过', '您的流程被审批通过：{processInstanceName}，查看链接：{detailUrl}', '[\"processInstanceName\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:04:31', '1', '2022-03-27 20:32:21', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (12, 2, 0, 'demo', '演示模板', '我就是测试一下下', '[]', NULL, 'biubiubiu', 4, 'DEBUG_DING_TALK', '1', '2022-04-10 23:22:49', '1', '2024-08-18 11:57:04', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (14, 1, 0, 'user-update-mobile', '会员用户 - 修改手机', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:04', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (15, 1, 0, 'user-update-password', '会员用户 - 修改密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:18', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (16, 1, 0, 'user-reset-password', '会员用户 - 重置密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-12-02 22:35:27', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (17, 2, 0, 'bpm_task_timeout', '【工作流】任务审批超时', '您收到了一条超时的待办任务：{processInstanceName}-{taskName}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"detailUrl\"]', '', 'X', 4, 'DEBUG_DING_TALK', '1', '2024-08-16 21:59:15', '1', '2024-08-16 21:59:34', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (18, 1, 0, 'admin-reset-password', '后台用户 - 忘记密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2025-03-16 14:19:34', '1', '2025-03-16 14:19:45', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (19, 1, 0, 'admin-sms-login', '后台用户短信登录', '您的验证码是{code}', '[\"code\"]', '', '4372216', 4, 'DEBUG_DING_TALK', '1', '2025-04-08 09:36:03', '1', '2025-04-08 09:36:17', '0');\nCOMMIT;\nSET IDENTITY_INSERT system_sms_template OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_social_client\n-- ----------------------------\nCREATE TABLE system_social_client\n(\n    id            bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name          varchar(255)                           NOT NULL,\n    social_type   smallint                               NOT NULL,\n    user_type     smallint                               NOT NULL,\n    client_id     varchar(255)                           NOT NULL,\n    client_secret varchar(2048)                           NOT NULL,\n    public_key    varchar(2048) DEFAULT NULL              NULL,\n    agent_id      varchar(255) DEFAULT NULL              NULL,\n    status        smallint                               NOT NULL,\n    creator       varchar(64)  DEFAULT ''                NULL,\n    create_time   datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       varchar(64)  DEFAULT ''                NULL,\n    update_time   datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       bit          DEFAULT '0'               NOT NULL,\n    tenant_id     bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_social_client.id IS '编号';\nCOMMENT ON COLUMN system_social_client.name IS '应用名';\nCOMMENT ON COLUMN system_social_client.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_client.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_social_client.public_key IS 'publicKey公钥';\nCOMMENT ON COLUMN system_social_client.client_secret IS '客户端密钥';\nCOMMENT ON COLUMN system_social_client.agent_id IS '代理编号';\nCOMMENT ON COLUMN system_social_client.status IS '状态';\nCOMMENT ON COLUMN system_social_client.creator IS '创建者';\nCOMMENT ON COLUMN system_social_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_client.updater IS '更新者';\nCOMMENT ON COLUMN system_social_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_client.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_client.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_client IS '社交客户端表';\n\n-- ----------------------------\n-- Records of system_social_client\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_social_client ON;\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '钉钉', 20, 2, 'dingvrnreaje3yqvzhxg', 'i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI', NULL, 0, '', '2023-10-18 11:21:18', '1', '2023-12-20 21:28:26', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '钉钉（王土豆）', 20, 2, 'dingtsu9hpepjkbmthhw', 'FP_bnSq_HAHKCSncmJjw5hxhnzs6vaVDSZZn3egj6rdqTQ_hu5tQVJyLMpgCakdP', NULL, 0, '', '2023-10-18 11:21:18', '', '2023-12-20 21:28:26', '1', 121);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '微信公众号', 31, 1, 'wx5b23ba7a5589ecbb', '2a7b3b20c537e52e74afd395eb85f61f', NULL, 0, '', '2023-10-18 16:07:46', '1', '2023-12-20 21:28:23', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (43, '微信小程序', 34, 1, 'wx63c280fe3248a3e7', '6f270509224a7ae1296bbf1c8cb97aed', NULL, 0, '', '2023-10-19 13:37:41', '1', '2023-12-20 21:28:25', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (44, '1', 10, 1, '2', '3', NULL, 0, '1', '2025-04-06 20:36:28', '1', '2025-04-06 20:43:12', '1', 1);\nCOMMIT;\nSET IDENTITY_INSERT system_social_client OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_social_user\n-- ----------------------------\nCREATE TABLE system_social_user\n(\n    id             bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    type           smallint                               NOT NULL,\n    openid         varchar(32)                            NOT NULL,\n    token          varchar(256) DEFAULT NULL              NULL,\n    raw_token_info varchar(1024)                          NOT NULL,\n    nickname       varchar(32)                            NOT NULL,\n    avatar         varchar(255) DEFAULT NULL              NULL,\n    raw_user_info  varchar(1024)                          NOT NULL,\n    code           varchar(256)                           NOT NULL,\n    state          varchar(256) DEFAULT NULL              NULL,\n    creator        varchar(64)  DEFAULT ''                NULL,\n    create_time    datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar(64)  DEFAULT ''                NULL,\n    update_time    datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit          DEFAULT '0'               NOT NULL,\n    tenant_id      bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_social_user.id IS '主键 ( 自增策略)';\nCOMMENT ON COLUMN system_social_user.type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user.openid IS '社交 openid';\nCOMMENT ON COLUMN system_social_user.token IS '社交 token';\nCOMMENT ON COLUMN system_social_user.raw_token_info IS '原始 Token 数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_social_user.avatar IS '用户头像';\nCOMMENT ON COLUMN system_social_user.raw_user_info IS '原始用户数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.code IS '最后一次的认证 code';\nCOMMENT ON COLUMN system_social_user.state IS '最后一次的认证 state';\nCOMMENT ON COLUMN system_social_user.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user IS '社交用户表';\n\n-- ----------------------------\n-- Table structure for system_social_user_bind\n-- ----------------------------\nCREATE TABLE system_social_user_bind\n(\n    id             bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    user_id        bigint                                NOT NULL,\n    user_type      smallint                              NOT NULL,\n    social_type    smallint                              NOT NULL,\n    social_user_id bigint                                NOT NULL,\n    creator        varchar(64) DEFAULT ''                NULL,\n    create_time    datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar(64) DEFAULT ''                NULL,\n    update_time    datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit         DEFAULT '0'               NOT NULL,\n    tenant_id      bigint      DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_social_user_bind.id IS '主键 ( 自增策略)';\nCOMMENT ON COLUMN system_social_user_bind.user_id IS '用户编号';\nCOMMENT ON COLUMN system_social_user_bind.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_user_bind.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user_bind.social_user_id IS '社交用户的编号';\nCOMMENT ON COLUMN system_social_user_bind.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user_bind.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user_bind.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user_bind.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user_bind.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user_bind.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user_bind IS '社交绑定表';\n\n-- ----------------------------\n-- Table structure for system_tenant\n-- ----------------------------\nCREATE TABLE system_tenant\n(\n    id              bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name            varchar(30)                            NOT NULL,\n    contact_user_id bigint       DEFAULT NULL              NULL,\n    contact_name    varchar(30)                            NOT NULL,\n    contact_mobile  varchar(500) DEFAULT NULL              NULL,\n    status          smallint     DEFAULT 0                 NOT NULL,\n    websites        varchar(256) DEFAULT ''                NULL,\n    package_id      bigint                                 NOT NULL,\n    expire_time     datetime                               NOT NULL,\n    account_count   int                                    NOT NULL,\n    creator         varchar(64)  DEFAULT ''                NULL,\n    create_time     datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         varchar(64)  DEFAULT ''                NULL,\n    update_time     datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_tenant.id IS '租户编号';\nCOMMENT ON COLUMN system_tenant.name IS '租户名';\nCOMMENT ON COLUMN system_tenant.contact_user_id IS '联系人的用户编号';\nCOMMENT ON COLUMN system_tenant.contact_name IS '联系人';\nCOMMENT ON COLUMN system_tenant.contact_mobile IS '联系手机';\nCOMMENT ON COLUMN system_tenant.status IS '租户状态（0正常 1停用）';\nCOMMENT ON COLUMN system_tenant.websites IS '绑定域名数组';\nCOMMENT ON COLUMN system_tenant.package_id IS '租户套餐编号';\nCOMMENT ON COLUMN system_tenant.expire_time IS '过期时间';\nCOMMENT ON COLUMN system_tenant.account_count IS '账号数量';\nCOMMENT ON COLUMN system_tenant.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant IS '租户表';\n\n-- ----------------------------\n-- Records of system_tenant\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_tenant ON;\nINSERT 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', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2023-11-06 11:41:41', '0');\nINSERT 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', 111, '2026-07-10 00:00:00', 30, '1', '2022-02-22 00:56:14', '1', '2025-04-03 21:33:01', '0');\nINSERT 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', 111, '2022-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2024-09-22 12:10:50', '0');\nCOMMIT;\nSET IDENTITY_INSERT system_tenant OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_tenant_package\n-- ----------------------------\nCREATE TABLE system_tenant_package\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name        varchar(30)                            NOT NULL,\n    status      smallint     DEFAULT 0                 NOT NULL,\n    remark      varchar(256) DEFAULT ''                NULL,\n    menu_ids    varchar(4096)                          NOT NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL\n);\n\nCOMMENT ON COLUMN system_tenant_package.id IS '套餐编号';\nCOMMENT ON COLUMN system_tenant_package.name IS '套餐名';\nCOMMENT ON COLUMN system_tenant_package.status IS '租户状态（0正常 1停用）';\nCOMMENT ON COLUMN system_tenant_package.remark IS '备注';\nCOMMENT ON COLUMN system_tenant_package.menu_ids IS '关联的菜单编号';\nCOMMENT ON COLUMN system_tenant_package.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant_package.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant_package.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant_package.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant_package.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant_package IS '租户套餐表';\n\n-- ----------------------------\n-- Records of system_tenant_package\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_tenant_package ON;\nINSERT 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,1118,1119,1120,100,101,102,103,106,107,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2713,2714,2715,2716,2717,2718,2720,1185,2721,1186,2722,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,2472,1192,2728,1193,2729,1194,2730,1195,2731,1196,2732,1197,2733,2478,1198,2734,2479,1199,2735,2480,1200,2481,1201,2482,1202,2483,2739,2484,2740,2485,2486,2487,1207,2488,1208,2489,1209,2490,1210,2491,1211,2492,1212,2493,1213,2494,2495,1215,1216,2497,1217,1218,1219,1220,1221,1222,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,2525,1255,1256,1001,1257,1002,1258,1003,1259,1004,1260,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020]', '1', '2022-02-22 00:54:00', '1', '2024-07-13 22:37:24', '0');\nINSERT INTO system_tenant_package (id, name, status, remark, menu_ids, creator, create_time, updater, update_time, deleted) VALUES (112, '再来一个套餐', 0, '1234', '[1024,1,1025,1026,2,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1042,1043,1045,1046,1048,1050,1051,1052,1053,1054,1056,1057,1058,2083,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1100,1101,1102,1103,1104,1105,1106,2130,1107,2131,1108,2132,1109,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,100,2148,101,2149,102,2150,103,2151,104,2152,105,106,107,108,109,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2739,2740,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,1255,1256,1257,1258,1259,1260,1261,1263,1264,1265,1266,1267,2447,2448,2449,2450,2451,2452,2453,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,500,1013,501,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023]', '1', '2025-04-04 08:15:02', '1', '2025-04-04 08:15:21', '0');\nCOMMIT;\nSET IDENTITY_INSERT system_tenant_package OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_user_post\n-- ----------------------------\nCREATE TABLE system_user_post\n(\n    id          bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    user_id     bigint      DEFAULT 0                 NOT NULL,\n    post_id     bigint      DEFAULT 0                 NOT NULL,\n    creator     varchar(64) DEFAULT ''                NULL,\n    create_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64) DEFAULT ''                NULL,\n    update_time datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit         DEFAULT '0'               NOT NULL,\n    tenant_id   bigint      DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_user_post.id IS 'id';\nCOMMENT ON COLUMN system_user_post.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_post.post_id IS '岗位ID';\nCOMMENT ON COLUMN system_user_post.creator IS '创建者';\nCOMMENT ON COLUMN system_user_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_post.updater IS '更新者';\nCOMMENT ON COLUMN system_user_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_post IS '用户岗位表';\n\n-- ----------------------------\n-- Records of system_user_post\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_user_post ON;\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, 1, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, 100, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (115, 104, 1, '1', '2022-05-16 19:36:28', '1', '2022-05-16 19:36:28', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (116, 117, 2, '1', '2022-07-09 17:40:26', '1', '2022-07-09 17:40:26', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (117, 118, 1, '1', '2022-07-09 17:44:44', '1', '2022-07-09 17:44:44', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (119, 114, 5, '1', '2024-03-24 20:45:51', '1', '2024-03-24 20:45:51', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (123, 115, 1, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (124, 115, 2, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (125, 1, 2, '1', '2024-07-13 22:31:39', '1', '2024-07-13 22:31:39', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT system_user_post OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_user_role\n-- ----------------------------\nCREATE TABLE system_user_role\n(\n    id          bigint                                NOT NULL PRIMARY KEY IDENTITY,\n    user_id     bigint                                NOT NULL,\n    role_id     bigint                                NOT NULL,\n    creator     varchar(64) DEFAULT ''                NULL,\n    create_time datetime    DEFAULT CURRENT_TIMESTAMP NULL,\n    updater     varchar(64) DEFAULT ''                NULL,\n    update_time datetime    DEFAULT CURRENT_TIMESTAMP NULL,\n    deleted     bit         DEFAULT '0'               NULL,\n    tenant_id   bigint      DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_user_role.id IS '自增编号';\nCOMMENT ON COLUMN system_user_role.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_role.role_id IS '角色ID';\nCOMMENT ON COLUMN system_user_role.creator IS '创建者';\nCOMMENT ON COLUMN system_user_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_role.updater IS '更新者';\nCOMMENT ON COLUMN system_user_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_role IS '用户和角色关联表';\n\n-- ----------------------------\n-- Records of system_user_role\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_user_role ON;\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 1, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:17', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 100, 101, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 100, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:12', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 100, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:11', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 103, 1, '1', '2022-01-11 13:19:45', '1', '2022-01-11 13:19:45', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 110, 109, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 111, 110, '110', '2022-02-23 13:14:38', '110', '2022-02-23 13:14:38', '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 113, 111, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', '0', 122);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (18, 1, 2, '1', '2022-05-12 20:39:29', '1', '2022-05-12 20:39:29', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (38, 114, 101, '1', '2024-03-24 22:23:03', '1', '2024-03-24 22:23:03', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (47, 104, 2, '1', '2025-01-04 10:40:33', '1', '2025-01-04 10:40:33', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (48, 100, 155, '1', '2025-04-04 10:41:14', '1', '2025-04-04 10:41:14', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT system_user_role OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_users\n-- ----------------------------\nCREATE TABLE system_users\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    username    varchar(30)                            NOT NULL,\n    password    varchar(100) DEFAULT ''                NULL,\n    nickname    varchar(30)                            NOT NULL,\n    remark      varchar(500) DEFAULT NULL              NULL,\n    dept_id     bigint       DEFAULT NULL              NULL,\n    post_ids    varchar(255) DEFAULT NULL              NULL,\n    email       varchar(50)  DEFAULT ''                NULL,\n    mobile      varchar(11)  DEFAULT ''                NULL,\n    sex         smallint     DEFAULT 0                 NULL,\n    avatar      varchar(512) DEFAULT ''                NULL,\n    status      smallint     DEFAULT 0                 NOT NULL,\n    login_ip    varchar(50)  DEFAULT ''                NULL,\n    login_date  datetime     DEFAULT NULL              NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN system_users.id IS '用户ID';\nCOMMENT ON COLUMN system_users.username IS '用户账号';\nCOMMENT ON COLUMN system_users.password IS '密码';\nCOMMENT ON COLUMN system_users.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_users.remark IS '备注';\nCOMMENT ON COLUMN system_users.dept_id IS '部门ID';\nCOMMENT ON COLUMN system_users.post_ids IS '岗位编号数组';\nCOMMENT ON COLUMN system_users.email IS '用户邮箱';\nCOMMENT ON COLUMN system_users.mobile IS '手机号码';\nCOMMENT ON COLUMN system_users.sex IS '用户性别';\nCOMMENT ON COLUMN system_users.avatar IS '头像地址';\nCOMMENT ON COLUMN system_users.status IS '帐号状态（0正常 1停用）';\nCOMMENT ON COLUMN system_users.login_ip IS '最后登录IP';\nCOMMENT ON COLUMN system_users.login_date IS '最后登录时间';\nCOMMENT ON COLUMN system_users.creator IS '创建者';\nCOMMENT ON COLUMN system_users.create_time IS '创建时间';\nCOMMENT ON COLUMN system_users.updater IS '更新者';\nCOMMENT ON COLUMN system_users.update_time IS '更新时间';\nCOMMENT ON COLUMN system_users.deleted IS '是否删除';\nCOMMENT ON COLUMN system_users.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_users IS '用户信息表';\n\n-- ----------------------------\n-- Records of system_users\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT system_users ON;\nINSERT 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$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '芋道源码', '管理员', 103, '[1,2]', '11aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/test/20250502/avatar_1746154660449.png', 0, '0:0:0:0:0:0:0:1', '2025-05-10 18:03:15', 'admin', '2021-01-05 17:03:47', NULL, '2025-05-10 18:03:15', '0', 1);\nINSERT 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$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '0', 118);\nINSERT 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', '0', 119);\nINSERT 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', '0', 120);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 1);\nINSERT 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', '0', 122);\nINSERT 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', '2024-03-24 22:21:05', '1', '2022-03-19 21:50:58', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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$jth0yOj8cSJq84D6vrzusOHDwW/LpBfgBnQ6bfFlD8zNZfM632Ta2', '狗蛋', NULL, 103, '[1]', '', '15601691239', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-17 09:10:27', '1', '2022-07-09 17:44:43', '1', '2025-04-21 14:23:08', '0', 1);\nINSERT 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 (131, 'hh', '$2a$04$jyH9h6.gaw8mpOjPfHIpx.8as2Rzfcmdlj5rlJFwgCw4rsv/MTb2K', '呵呵', NULL, 100, '[]', '777@qq.com', '15601882312', 1, NULL, 0, '', NULL, '1', '2024-04-27 08:45:56', '1', '2025-04-21 14:23:08', '0', 1);\nINSERT 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$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', '小秃头', NULL, NULL, NULL, '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-09-10 21:03:58', NULL, '2024-09-10 21:03:58', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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-04-08 13:09:07', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT system_users OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo01_contact\n-- ----------------------------\nCREATE TABLE yudao_demo01_contact\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name        varchar(100) DEFAULT ''                NULL,\n    sex         smallint                               NOT NULL,\n    birthday    datetime                               NOT NULL,\n    description varchar(255)                           NOT NULL,\n    avatar      varchar(512) DEFAULT NULL              NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN yudao_demo01_contact.id IS '编号';\nCOMMENT ON COLUMN yudao_demo01_contact.name IS '名字';\nCOMMENT ON COLUMN yudao_demo01_contact.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo01_contact.birthday IS '出生年';\nCOMMENT ON COLUMN yudao_demo01_contact.description IS '简介';\nCOMMENT ON COLUMN yudao_demo01_contact.avatar IS '头像';\nCOMMENT ON COLUMN yudao_demo01_contact.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo01_contact.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo01_contact.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo01_contact.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo01_contact.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo01_contact.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo01_contact IS '示例联系人表';\n\n-- ----------------------------\n-- Records of yudao_demo01_contact\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT yudao_demo01_contact ON;\nINSERT INTO yudao_demo01_contact (id, name, sex, birthday, description, avatar, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 2, '2023-11-07 00:00:00', '<p>天蚕土豆！呀</p>', 'http://127.0.0.1:48080/admin-api/infra/file/4/get/46f8fa1a37db3f3960d8910ff2fe3962ab3b2db87cf2f8ccb4dc8145b8bdf237.jpeg', '1', '2023-11-15 23:34:30', '1', '2023-11-15 23:47:39', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT yudao_demo01_contact OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo02_category\n-- ----------------------------\nCREATE TABLE yudao_demo02_category\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name        varchar(100) DEFAULT ''                NULL,\n    parent_id   bigint                                 NOT NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN yudao_demo02_category.id IS '编号';\nCOMMENT ON COLUMN yudao_demo02_category.name IS '名字';\nCOMMENT ON COLUMN yudao_demo02_category.parent_id IS '父级编号';\nCOMMENT ON COLUMN yudao_demo02_category.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo02_category.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo02_category.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo02_category.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo02_category.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo02_category.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo02_category IS '示例分类表';\n\n-- ----------------------------\n-- Records of yudao_demo02_category\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT yudao_demo02_category ON;\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 0, '1', '2023-11-15 23:34:30', '1', '2023-11-16 20:24:23', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '番茄', 0, '1', '2023-11-16 20:24:00', '1', '2023-11-16 20:24:15', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '怪怪', 0, '1', '2023-11-16 20:24:32', '1', '2023-11-16 20:24:32', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '小番茄', 2, '1', '2023-11-16 20:24:39', '1', '2023-11-16 20:24:39', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, '大番茄', 2, '1', '2023-11-16 20:24:46', '1', '2023-11-16 20:24:46', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, '11', 3, '1', '2023-11-24 19:29:34', '1', '2023-11-24 19:29:34', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT yudao_demo02_category OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo03_course\n-- ----------------------------\nCREATE TABLE yudao_demo03_course\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    student_id  bigint                                 NOT NULL,\n    name        varchar(100) DEFAULT ''                NULL,\n    score       smallint                               NOT NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN yudao_demo03_course.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_course.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_course.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_course.score IS '分数';\nCOMMENT ON COLUMN yudao_demo03_course.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_course.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_course.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_course.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_course.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_course.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_course IS '学生课程表';\n\n-- ----------------------------\n-- Records of yudao_demo03_course\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT yudao_demo03_course ON;\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, '语文', 66, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 2, '数学', 22, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (11, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (12, 2, '电脑', 33, '1', '2023-11-17 00:20:42', '1', '2023-11-16 16:20:45', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (13, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:26', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:49', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', '0', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '1', 1);\nINSERT 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', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT yudao_demo03_course OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo03_grade\n-- ----------------------------\nCREATE TABLE yudao_demo03_grade\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    student_id  bigint                                 NOT NULL,\n    name        varchar(100) DEFAULT ''                NULL,\n    teacher     varchar(255)                           NOT NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN yudao_demo03_grade.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_grade.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_grade.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_grade.teacher IS '班主任';\nCOMMENT ON COLUMN yudao_demo03_grade.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_grade.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_grade.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_grade.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_grade.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_grade.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_grade IS '学生班级表';\n\n-- ----------------------------\n-- Records of yudao_demo03_grade\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT yudao_demo03_grade ON;\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT yudao_demo03_grade OFF;\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo03_student\n-- ----------------------------\nCREATE TABLE yudao_demo03_student\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name        varchar(100) DEFAULT ''                NULL,\n    sex         smallint                               NOT NULL,\n    birthday    datetime                               NOT NULL,\n    description varchar(255)                           NOT NULL,\n    creator     varchar(64)  DEFAULT ''                NULL,\n    create_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar(64)  DEFAULT ''                NULL,\n    update_time datetime     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT '0'               NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n);\n\nCOMMENT ON COLUMN yudao_demo03_student.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_student.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_student.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo03_student.birthday IS '出生日期';\nCOMMENT ON COLUMN yudao_demo03_student.description IS '简介';\nCOMMENT ON COLUMN yudao_demo03_student.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_student.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_student.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_student.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_student.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_student.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_student IS '学生表';\n\n-- ----------------------------\n-- Records of yudao_demo03_student\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT yudao_demo03_student ON;\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nCOMMIT;\nSET IDENTITY_INSERT yudao_demo03_student OFF;\n-- @formatter:on\n\n"
  },
  {
    "path": "sql/kingbase/quartz.sql",
    "content": "set client_min_messages = WARNING;\nDROP TABLE IF EXISTS qrtz_fired_triggers;\nDROP TABLE IF EXISTS qrtz_paused_trigger_grps;\nDROP TABLE IF EXISTS qrtz_scheduler_state;\nDROP TABLE IF EXISTS qrtz_locks;\nDROP TABLE IF EXISTS qrtz_simprop_triggers;\nDROP TABLE IF EXISTS qrtz_simple_triggers;\nDROP TABLE IF EXISTS qrtz_cron_triggers;\nDROP TABLE IF EXISTS qrtz_blob_triggers;\nDROP TABLE IF EXISTS qrtz_triggers;\nDROP TABLE IF EXISTS qrtz_job_details;\nDROP TABLE IF EXISTS qrtz_calendars;\nset client_min_messages = NOTICE;\n\nCREATE TABLE qrtz_job_details\n  (\n    sched_name TEXT NOT NULL,\n\tjob_name  TEXT NOT NULL,\n    job_group TEXT NOT NULL,\n    description TEXT NULL,\n    job_class_name   TEXT NOT NULL, \n    is_durable BOOL NOT NULL,\n    is_nonconcurrent BOOL NOT NULL,\n    is_update_data BOOL NOT NULL,\n\trequests_recovery BOOL NOT NULL,\n    job_data BYTEA NULL,\n    PRIMARY KEY (sched_name,job_name,job_group)\n);\n\nCREATE TABLE qrtz_triggers\n  (\n    sched_name TEXT NOT NULL,\n\ttrigger_name TEXT NOT NULL,\n    trigger_group TEXT NOT NULL,\n    job_name  TEXT NOT NULL, \n    job_group TEXT NOT NULL,\n    description TEXT NULL,\n    next_fire_time BIGINT NULL,\n    prev_fire_time BIGINT NULL,\n    priority INTEGER NULL,\n    trigger_state TEXT NOT NULL,\n    trigger_type TEXT NOT NULL,\n    start_time BIGINT NOT NULL,\n    end_time BIGINT NULL,\n    calendar_name TEXT NULL,\n    misfire_instr SMALLINT NULL,\n    job_data BYTEA NULL,\n    PRIMARY KEY (sched_name,trigger_name,trigger_group),\n    FOREIGN KEY (sched_name,job_name,job_group) \n\t\tREFERENCES qrtz_job_details(sched_name,job_name,job_group) \n);\n\nCREATE TABLE qrtz_simple_triggers\n  (\n    sched_name TEXT NOT NULL,\n\ttrigger_name TEXT NOT NULL,\n    trigger_group TEXT NOT NULL,\n    repeat_count BIGINT NOT NULL,\n    repeat_interval BIGINT NOT NULL,\n    times_triggered BIGINT NOT NULL,\n    PRIMARY KEY (sched_name,trigger_name,trigger_group),\n    FOREIGN KEY (sched_name,trigger_name,trigger_group) \n\t\tREFERENCES qrtz_triggers(sched_name,trigger_name,trigger_group) ON DELETE CASCADE\n);\n\nCREATE TABLE QRTZ_SIMPROP_TRIGGERS \n  (\n    sched_name TEXT NOT NULL,\n    trigger_name TEXT NOT NULL ,\n    trigger_group TEXT NOT NULL ,\n    str_prop_1 TEXT NULL,\n    str_prop_2 TEXT NULL,\n    str_prop_3 TEXT NULL,\n    int_prop_1 INTEGER NULL,\n    int_prop_2 INTEGER NULL,\n    long_prop_1 BIGINT NULL,\n    long_prop_2 BIGINT NULL,\n    dec_prop_1 NUMERIC NULL,\n    dec_prop_2 NUMERIC NULL,\n    bool_prop_1 BOOL NULL,\n    bool_prop_2 BOOL NULL,\n\ttime_zone_id TEXT NULL,\n\tPRIMARY KEY (sched_name,trigger_name,trigger_group),\n    FOREIGN KEY (sched_name,trigger_name,trigger_group) \n\t\tREFERENCES qrtz_triggers(sched_name,trigger_name,trigger_group) ON DELETE CASCADE\n);\n\nCREATE TABLE qrtz_cron_triggers\n  (\n    sched_name TEXT NOT NULL,\n    trigger_name TEXT NOT NULL,\n    trigger_group TEXT NOT NULL,\n    cron_expression TEXT NOT NULL,\n    time_zone_id TEXT,\n    PRIMARY KEY (sched_name,trigger_name,trigger_group),\n    FOREIGN KEY (sched_name,trigger_name,trigger_group) \n\t\tREFERENCES qrtz_triggers(sched_name,trigger_name,trigger_group) ON DELETE CASCADE\n);\n\nCREATE TABLE qrtz_blob_triggers\n  (\n    sched_name TEXT NOT NULL,\n    trigger_name TEXT NOT NULL,\n    trigger_group TEXT NOT NULL,\n    blob_data BYTEA NULL,\n    PRIMARY KEY (sched_name,trigger_name,trigger_group),\n    FOREIGN KEY (sched_name,trigger_name,trigger_group) \n\t\tREFERENCES qrtz_triggers(sched_name,trigger_name,trigger_group) ON DELETE CASCADE\n);\n\nCREATE TABLE qrtz_calendars\n  (\n    sched_name TEXT NOT NULL,\n    calendar_name  TEXT NOT NULL, \n    calendar BYTEA NOT NULL,\n    PRIMARY KEY (sched_name,calendar_name)\n);\n\nCREATE TABLE qrtz_paused_trigger_grps\n  (\n    sched_name TEXT NOT NULL,\n    trigger_group TEXT NOT NULL, \n    PRIMARY KEY (sched_name,trigger_group)\n);\n\nCREATE TABLE qrtz_fired_triggers \n  (\n    sched_name TEXT NOT NULL,\n    entry_id TEXT NOT NULL,\n    trigger_name TEXT NOT NULL,\n    trigger_group TEXT NOT NULL,\n    instance_name TEXT NOT NULL,\n    fired_time BIGINT NOT NULL,\n\tsched_time BIGINT NOT NULL,\n    priority INTEGER NOT NULL,\n    state TEXT NOT NULL,\n    job_name TEXT NULL,\n    job_group TEXT NULL,\n    is_nonconcurrent BOOL NOT NULL,\n    requests_recovery BOOL NULL,\n    PRIMARY KEY (sched_name,entry_id)\n);\n\nCREATE TABLE qrtz_scheduler_state \n  (\n    sched_name TEXT NOT NULL,\n    instance_name TEXT NOT NULL,\n    last_checkin_time BIGINT NOT NULL,\n    checkin_interval BIGINT NOT NULL,\n    PRIMARY KEY (sched_name,instance_name)\n);\n\nCREATE TABLE qrtz_locks\n  (\n    sched_name TEXT NOT NULL,\n    lock_name  TEXT NOT NULL, \n    PRIMARY KEY (sched_name,lock_name)\n);\n\ncreate index idx_qrtz_j_req_recovery on qrtz_job_details(requests_recovery);\ncreate index idx_qrtz_t_next_fire_time on qrtz_triggers(next_fire_time);\ncreate index idx_qrtz_t_state on qrtz_triggers(trigger_state);\ncreate index idx_qrtz_t_nft_st on qrtz_triggers(next_fire_time,trigger_state);\ncreate index idx_qrtz_ft_trig_name on qrtz_fired_triggers(trigger_name);\ncreate index idx_qrtz_ft_trig_group on qrtz_fired_triggers(trigger_group);\ncreate index idx_qrtz_ft_trig_nm_gp on qrtz_fired_triggers(sched_name,trigger_name,trigger_group);\ncreate index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(instance_name);\ncreate index idx_qrtz_ft_job_name on qrtz_fired_triggers(job_name);\ncreate index idx_qrtz_ft_job_group on qrtz_fired_triggers(job_group);\ncreate index idx_qrtz_ft_job_req_recovery on qrtz_fired_triggers(requests_recovery);\n"
  },
  {
    "path": "sql/kingbase/ruoyi-vue-pro.sql",
    "content": "/*\n Yudao Database Transfer Tool\n\n Source Server Type    : MySQL\n\n Target Server Type    : Kingbase\n\n Date: 2025-05-22 21:03:59\n*/\n\n\n-- ----------------------------\n-- Table structure for dual\n-- ----------------------------\nDROP TABLE IF EXISTS dual;\nCREATE TABLE dual\n(\n    id int2\n);\n\nCOMMENT ON TABLE dual IS '数据库连接的表';\n\n-- ----------------------------\n-- Records of dual\n-- ----------------------------\n-- @formatter:off\nINSERT INTO dual VALUES (1);\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_api_access_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_api_access_log;\nCREATE TABLE infra_api_access_log\n(\n    id               int8         NOT NULL,\n    trace_id         varchar(64)  NULL     DEFAULT '',\n    user_id          int8         NOT NULL DEFAULT 0,\n    user_type        int2         NOT NULL DEFAULT 0,\n    application_name varchar(50)  NOT NULL,\n    request_method   varchar(16)  NULL     DEFAULT '',\n    request_url      varchar(255) NULL     DEFAULT '',\n    request_params   text         NULL,\n    response_body    text         NULL,\n    user_ip          varchar(50)  NOT NULL,\n    user_agent       varchar(512) NOT NULL,\n    operate_module   varchar(50)  NULL     DEFAULT NULL,\n    operate_name     varchar(50)  NULL     DEFAULT NULL,\n    operate_type     int2         NULL     DEFAULT 0,\n    begin_time       timestamp    NOT NULL,\n    end_time         timestamp    NOT NULL,\n    duration         int4         NOT NULL,\n    result_code      int4         NOT NULL DEFAULT 0,\n    result_msg       varchar(512) NULL     DEFAULT '',\n    creator          varchar(64)  NULL     DEFAULT '',\n    create_time      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater          varchar(64)  NULL     DEFAULT '',\n    update_time      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted          int2         NOT NULL DEFAULT 0,\n    tenant_id        int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_api_access_log\n    ADD CONSTRAINT pk_infra_api_access_log PRIMARY KEY (id);\n\nCREATE INDEX idx_infra_api_access_log_01 ON infra_api_access_log (create_time);\n\nCOMMENT ON COLUMN infra_api_access_log.id IS '日志主键';\nCOMMENT ON COLUMN infra_api_access_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_access_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_access_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_access_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_access_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_access_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_access_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_access_log.response_body IS '响应结果';\nCOMMENT ON COLUMN infra_api_access_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_access_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_access_log.operate_module IS '操作模块';\nCOMMENT ON COLUMN infra_api_access_log.operate_name IS '操作名';\nCOMMENT ON COLUMN infra_api_access_log.operate_type IS '操作分类';\nCOMMENT ON COLUMN infra_api_access_log.begin_time IS '开始请求时间';\nCOMMENT ON COLUMN infra_api_access_log.end_time IS '结束请求时间';\nCOMMENT ON COLUMN infra_api_access_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_api_access_log.result_code IS '结果码';\nCOMMENT ON COLUMN infra_api_access_log.result_msg IS '结果提示';\nCOMMENT ON COLUMN infra_api_access_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_access_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_access_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_access_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_access_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_access_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_access_log IS 'API 访问日志表';\n\nDROP SEQUENCE IF EXISTS infra_api_access_log_seq;\nCREATE SEQUENCE infra_api_access_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_api_error_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_api_error_log;\nCREATE TABLE infra_api_error_log\n(\n    id                           int8          NOT NULL,\n    trace_id                     varchar(64)   NOT NULL,\n    user_id                      int8          NOT NULL DEFAULT 0,\n    user_type                    int2          NOT NULL DEFAULT 0,\n    application_name             varchar(50)   NOT NULL,\n    request_method               varchar(16)   NOT NULL,\n    request_url                  varchar(255)  NOT NULL,\n    request_params               varchar(8000) NOT NULL,\n    user_ip                      varchar(50)   NOT NULL,\n    user_agent                   varchar(512)  NOT NULL,\n    exception_time               timestamp     NOT NULL,\n    exception_name               varchar(128)  NULL     DEFAULT '',\n    exception_message            text          NULL,\n    exception_root_cause_message text          NULL,\n    exception_stack_trace        text          NULL,\n    exception_class_name         varchar(512)  NOT NULL,\n    exception_file_name          varchar(512)  NOT NULL,\n    exception_method_name        varchar(512)  NOT NULL,\n    exception_line_number        int4          NOT NULL,\n    process_status               int2          NOT NULL,\n    process_time                 timestamp     NULL     DEFAULT NULL,\n    process_user_id              int4          NULL     DEFAULT 0,\n    creator                      varchar(64)   NULL     DEFAULT '',\n    create_time                  timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater                      varchar(64)   NULL     DEFAULT '',\n    update_time                  timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted                      int2          NOT NULL DEFAULT 0,\n    tenant_id                    int8          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_api_error_log\n    ADD CONSTRAINT pk_infra_api_error_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_api_error_log.id IS '编号';\nCOMMENT ON COLUMN infra_api_error_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_error_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_error_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_error_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_error_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_error_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_error_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_error_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_error_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_error_log.exception_time IS '异常发生时间';\nCOMMENT ON COLUMN infra_api_error_log.exception_name IS '异常名';\nCOMMENT ON COLUMN infra_api_error_log.exception_message IS '异常导致的消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_root_cause_message IS '异常导致的根消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_stack_trace IS '异常的栈轨迹';\nCOMMENT ON COLUMN infra_api_error_log.exception_class_name IS '异常发生的类全名';\nCOMMENT ON COLUMN infra_api_error_log.exception_file_name IS '异常发生的类文件';\nCOMMENT ON COLUMN infra_api_error_log.exception_method_name IS '异常发生的方法名';\nCOMMENT ON COLUMN infra_api_error_log.exception_line_number IS '异常发生的方法所在行';\nCOMMENT ON COLUMN infra_api_error_log.process_status IS '处理状态';\nCOMMENT ON COLUMN infra_api_error_log.process_time IS '处理时间';\nCOMMENT ON COLUMN infra_api_error_log.process_user_id IS '处理用户编号';\nCOMMENT ON COLUMN infra_api_error_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_error_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_error_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_error_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_error_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_error_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_error_log IS '系统异常日志';\n\nDROP SEQUENCE IF EXISTS infra_api_error_log_seq;\nCREATE SEQUENCE infra_api_error_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_codegen_column\n-- ----------------------------\nDROP TABLE IF EXISTS infra_codegen_column;\nCREATE TABLE infra_codegen_column\n(\n    id                       int8         NOT NULL,\n    table_id                 int8         NOT NULL,\n    column_name              varchar(200) NOT NULL,\n    data_type                varchar(100) NOT NULL,\n    column_comment           varchar(500) NOT NULL,\n    nullable                 bool         NOT NULL,\n    primary_key              bool         NOT NULL,\n    ordinal_position         int4         NOT NULL,\n    java_type                varchar(32)  NOT NULL,\n    java_field               varchar(64)  NOT NULL,\n    dict_type                varchar(200) NULL     DEFAULT '',\n    example                  varchar(64)  NULL     DEFAULT NULL,\n    create_operation         bool         NOT NULL,\n    update_operation         bool         NOT NULL,\n    list_operation           bool         NOT NULL,\n    list_operation_condition varchar(32)  NOT NULL DEFAULT '=',\n    list_operation_result    bool         NOT NULL,\n    html_type                varchar(32)  NOT NULL,\n    creator                  varchar(64)  NULL     DEFAULT '',\n    create_time              timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater                  varchar(64)  NULL     DEFAULT '',\n    update_time              timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted                  int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_codegen_column\n    ADD CONSTRAINT pk_infra_codegen_column PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_codegen_column.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_column.table_id IS '表编号';\nCOMMENT ON COLUMN infra_codegen_column.column_name IS '字段名';\nCOMMENT ON COLUMN infra_codegen_column.data_type IS '字段类型';\nCOMMENT ON COLUMN infra_codegen_column.column_comment IS '字段描述';\nCOMMENT ON COLUMN infra_codegen_column.nullable IS '是否允许为空';\nCOMMENT ON COLUMN infra_codegen_column.primary_key IS '是否主键';\nCOMMENT ON COLUMN infra_codegen_column.ordinal_position IS '排序';\nCOMMENT ON COLUMN infra_codegen_column.java_type IS 'Java 属性类型';\nCOMMENT ON COLUMN infra_codegen_column.java_field IS 'Java 属性名';\nCOMMENT ON COLUMN infra_codegen_column.dict_type IS '字典类型';\nCOMMENT ON COLUMN infra_codegen_column.example IS '数据示例';\nCOMMENT ON COLUMN infra_codegen_column.create_operation IS '是否为 Create 创建操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.update_operation IS '是否为 Update 更新操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation IS '是否为 List 查询操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_condition IS 'List 查询操作的条件类型';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_result IS '是否为 List 查询操作的返回字段';\nCOMMENT ON COLUMN infra_codegen_column.html_type IS '显示类型';\nCOMMENT ON COLUMN infra_codegen_column.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_column.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_column.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_column.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_column.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_column IS '代码生成表字段定义';\n\nDROP SEQUENCE IF EXISTS infra_codegen_column_seq;\nCREATE SEQUENCE infra_codegen_column_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_codegen_table\n-- ----------------------------\nDROP TABLE IF EXISTS infra_codegen_table;\nCREATE TABLE infra_codegen_table\n(\n    id                    int8         NOT NULL,\n    data_source_config_id int8         NOT NULL,\n    scene                 int2         NOT NULL DEFAULT 1,\n    table_name            varchar(200) NULL     DEFAULT '',\n    table_comment         varchar(500) NULL     DEFAULT '',\n    remark                varchar(500) NULL     DEFAULT NULL,\n    module_name           varchar(30)  NOT NULL,\n    business_name         varchar(30)  NOT NULL,\n    class_name            varchar(100) NULL     DEFAULT '',\n    class_comment         varchar(50)  NOT NULL,\n    author                varchar(50)  NOT NULL,\n    template_type         int2         NOT NULL DEFAULT 1,\n    front_type            int2         NOT NULL,\n    parent_menu_id        int8         NULL     DEFAULT NULL,\n    master_table_id       int8         NULL     DEFAULT NULL,\n    sub_join_column_id    int8         NULL     DEFAULT NULL,\n    sub_join_many         bool         NULL     DEFAULT NULL,\n    tree_parent_column_id int8         NULL     DEFAULT NULL,\n    tree_name_column_id   int8         NULL     DEFAULT NULL,\n    creator               varchar(64)  NULL     DEFAULT '',\n    create_time           timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater               varchar(64)  NULL     DEFAULT '',\n    update_time           timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted               int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_codegen_table\n    ADD CONSTRAINT pk_infra_codegen_table PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_codegen_table.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_table.data_source_config_id IS '数据源配置的编号';\nCOMMENT ON COLUMN infra_codegen_table.scene IS '生成场景';\nCOMMENT ON COLUMN infra_codegen_table.table_name IS '表名称';\nCOMMENT ON COLUMN infra_codegen_table.table_comment IS '表描述';\nCOMMENT ON COLUMN infra_codegen_table.remark IS '备注';\nCOMMENT ON COLUMN infra_codegen_table.module_name IS '模块名';\nCOMMENT ON COLUMN infra_codegen_table.business_name IS '业务名';\nCOMMENT ON COLUMN infra_codegen_table.class_name IS '类名称';\nCOMMENT ON COLUMN infra_codegen_table.class_comment IS '类描述';\nCOMMENT ON COLUMN infra_codegen_table.author IS '作者';\nCOMMENT ON COLUMN infra_codegen_table.template_type IS '模板类型';\nCOMMENT ON COLUMN infra_codegen_table.front_type IS '前端类型';\nCOMMENT ON COLUMN infra_codegen_table.parent_menu_id IS '父菜单编号';\nCOMMENT ON COLUMN infra_codegen_table.master_table_id IS '主表的编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_column_id IS '子表关联主表的字段编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_many IS '主表与子表是否一对多';\nCOMMENT ON COLUMN infra_codegen_table.tree_parent_column_id IS '树表的父字段编号';\nCOMMENT ON COLUMN infra_codegen_table.tree_name_column_id IS '树表的名字字段编号';\nCOMMENT ON COLUMN infra_codegen_table.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_table.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_table.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_table.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_table.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_table IS '代码生成表定义';\n\nDROP SEQUENCE IF EXISTS infra_codegen_table_seq;\nCREATE SEQUENCE infra_codegen_table_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_config;\nCREATE TABLE infra_config\n(\n    id          int8         NOT NULL,\n    category    varchar(50)  NOT NULL,\n    type        int2         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    config_key  varchar(100) NULL     DEFAULT '',\n    value       varchar(500) NULL     DEFAULT '',\n    visible     bool         NOT NULL,\n    remark      varchar(500) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_config\n    ADD CONSTRAINT pk_infra_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_config.id IS '参数主键';\nCOMMENT ON COLUMN infra_config.category IS '参数分组';\nCOMMENT ON COLUMN infra_config.type IS '参数类型';\nCOMMENT ON COLUMN infra_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_config.config_key IS '参数键名';\nCOMMENT ON COLUMN infra_config.value IS '参数键值';\nCOMMENT ON COLUMN infra_config.visible IS '是否可见';\nCOMMENT ON COLUMN infra_config.remark IS '备注';\nCOMMENT ON COLUMN infra_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_config IS '参数配置表';\n\n-- ----------------------------\n-- Records of infra_config\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'system.user.init-password', '123456', '0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-07-20 17:22:47', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:33:38', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:57:03', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:52:07', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (10, 'url', 2, 'Swagger 接口文档的地址', 'url.swagger', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:59:00', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (11, 'ui', 2, '腾讯地图 key', 'tencent.lbs.key', 'TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E', '1', '腾讯地图 key', '1', '2023-06-03 19:16:27', '1', '2023-06-03 19:16:27', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 'test2', 2, 'test3', 'test4', 'test5', '1', 'test6', '1', '2023-12-03 09:55:16', '1', '2025-04-06 21:00:09', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '用户管理-账号初始密码', 2, '用户管理-注册开关', 'system.user.register-enabled', 'true', '0', '', '1', '2025-04-26 17:23:41', '1', '2025-04-26 17:23:41', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS infra_config_seq;\nCREATE SEQUENCE infra_config_seq\n    START 14;\n\n-- ----------------------------\n-- Table structure for infra_data_source_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_data_source_config;\nCREATE TABLE infra_data_source_config\n(\n    id          int8          NOT NULL,\n    name        varchar(100)  NULL     DEFAULT '',\n    url         varchar(1024) NOT NULL,\n    username    varchar(255)  NOT NULL,\n    password    varchar(255)  NULL     DEFAULT '',\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_data_source_config\n    ADD CONSTRAINT pk_infra_data_source_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_data_source_config.id IS '主键编号';\nCOMMENT ON COLUMN infra_data_source_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_data_source_config.url IS '数据源连接';\nCOMMENT ON COLUMN infra_data_source_config.username IS '用户名';\nCOMMENT ON COLUMN infra_data_source_config.password IS '密码';\nCOMMENT ON COLUMN infra_data_source_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_data_source_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_data_source_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_data_source_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_data_source_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_data_source_config IS '数据源配置表';\n\nDROP SEQUENCE IF EXISTS infra_data_source_config_seq;\nCREATE SEQUENCE infra_data_source_config_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_file\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file;\nCREATE TABLE infra_file\n(\n    id          int8          NOT NULL,\n    config_id   int8          NULL     DEFAULT NULL,\n    name        varchar(256)  NULL     DEFAULT NULL,\n    path        varchar(512)  NOT NULL,\n    url         varchar(1024) NOT NULL,\n    type        varchar(128)  NULL     DEFAULT NULL,\n    size        int4          NOT NULL,\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_file\n    ADD CONSTRAINT pk_infra_file PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file.id IS '文件编号';\nCOMMENT ON COLUMN infra_file.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file.name IS '文件名';\nCOMMENT ON COLUMN infra_file.path IS '文件路径';\nCOMMENT ON COLUMN infra_file.url IS '文件 URL';\nCOMMENT ON COLUMN infra_file.type IS '文件类型';\nCOMMENT ON COLUMN infra_file.size IS '文件大小';\nCOMMENT ON COLUMN infra_file.creator IS '创建者';\nCOMMENT ON COLUMN infra_file.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file.updater IS '更新者';\nCOMMENT ON COLUMN infra_file.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file IS '文件表';\n\nDROP SEQUENCE IF EXISTS infra_file_seq;\nCREATE SEQUENCE infra_file_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_file_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file_config;\nCREATE TABLE infra_file_config\n(\n    id          int8          NOT NULL,\n    name        varchar(63)   NOT NULL,\n    storage     int2          NOT NULL,\n    remark      varchar(255)  NULL     DEFAULT NULL,\n    master      bool          NOT NULL,\n    config      varchar(4096) NOT NULL,\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_file_config\n    ADD CONSTRAINT pk_infra_file_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file_config.id IS '编号';\nCOMMENT ON COLUMN infra_file_config.name IS '配置名';\nCOMMENT ON COLUMN infra_file_config.storage IS '存储器';\nCOMMENT ON COLUMN infra_file_config.remark IS '备注';\nCOMMENT ON COLUMN infra_file_config.master IS '是否为主配置';\nCOMMENT ON COLUMN infra_file_config.config IS '存储配置';\nCOMMENT ON COLUMN infra_file_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_config IS '文件配置表';\n\n-- ----------------------------\n-- Records of infra_file_config\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (4, '数据库（示例）', 1, '我是数据库', '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-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (22, '七牛存储器（示例）', 20, '请换成你自己的密钥！！！', '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}', '1', '2024-01-13 22:11:12', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (24, '腾讯云存储（示例）', 20, '请换成你的密钥！！！', '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\"}', '1', '2024-11-09 16:03:22', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (25, '阿里云存储（示例）', 20, '', '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}', '1', '2024-11-09 16:47:08', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (26, '火山云存储（示例）', 20, '', '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}', '1', '2024-11-09 16:56:42', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (27, '华为云存储（示例）', 20, '', '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}', '1', '2024-11-09 17:18:41', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (28, 'MinIO 存储（示例）', 20, '', '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}', '1', '2024-11-09 17:43:10', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (29, '本地存储（示例）', 10, '仅适合 mac 或 windows', '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-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (30, 'SFTP 存储（示例）', 12, '', '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-05-02 18:30:28', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS infra_file_config_seq;\nCREATE SEQUENCE infra_file_config_seq\n    START 31;\n\n-- ----------------------------\n-- Table structure for infra_file_content\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file_content;\nCREATE TABLE infra_file_content\n(\n    id          int8         NOT NULL,\n    config_id   int8         NOT NULL,\n    path        varchar(512) NOT NULL,\n    content     bytea        NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_file_content\n    ADD CONSTRAINT pk_infra_file_content PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file_content.id IS '编号';\nCOMMENT ON COLUMN infra_file_content.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file_content.path IS '文件路径';\nCOMMENT ON COLUMN infra_file_content.content IS '文件内容';\nCOMMENT ON COLUMN infra_file_content.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_content.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_content.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_content.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_content.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_content IS '文件表';\n\nDROP SEQUENCE IF EXISTS infra_file_content_seq;\nCREATE SEQUENCE infra_file_content_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_job\n-- ----------------------------\nDROP TABLE IF EXISTS infra_job;\nCREATE TABLE infra_job\n(\n    id              int8         NOT NULL,\n    name            varchar(32)  NOT NULL,\n    status          int2         NOT NULL,\n    handler_name    varchar(64)  NOT NULL,\n    handler_param   varchar(255) NULL     DEFAULT NULL,\n    cron_expression varchar(32)  NOT NULL,\n    retry_count     int4         NOT NULL DEFAULT 0,\n    retry_interval  int4         NOT NULL DEFAULT 0,\n    monitor_timeout int4         NOT NULL DEFAULT 0,\n    creator         varchar(64)  NULL     DEFAULT '',\n    create_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater         varchar(64)  NULL     DEFAULT '',\n    update_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted         int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_job\n    ADD CONSTRAINT pk_infra_job PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_job.id IS '任务编号';\nCOMMENT ON COLUMN infra_job.name IS '任务名称';\nCOMMENT ON COLUMN infra_job.status IS '任务状态';\nCOMMENT ON COLUMN infra_job.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job.cron_expression IS 'CRON 表达式';\nCOMMENT ON COLUMN infra_job.retry_count IS '重试次数';\nCOMMENT ON COLUMN infra_job.retry_interval IS '重试间隔';\nCOMMENT ON COLUMN infra_job.monitor_timeout IS '监控超时时间';\nCOMMENT ON COLUMN infra_job.creator IS '创建者';\nCOMMENT ON COLUMN infra_job.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job.updater IS '更新者';\nCOMMENT ON COLUMN infra_job.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job IS '定时任务表';\n\n-- ----------------------------\n-- Records of infra_job\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (5, '支付通知 Job', 2, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', '2021-10-27 08:34:42', '1', '2024-09-12 13:32:48', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (17, '支付订单同步 Job', 2, 'payOrderSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 14:36:26', '1', '2023-07-22 15:39:08', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (18, '支付订单过期 Job', 2, 'payOrderExpireJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 15:36:23', '1', '2023-07-22 15:39:54', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (19, '退款订单的同步 Job', 2, 'payRefundSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-23 21:03:44', '1', '2023-07-23 21:09:00', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (21, '交易订单的自动过期 Job', 2, 'tradeOrderAutoCancelJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-25 23:43:26', '1', '2023-09-26 19:23:30', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (22, '交易订单的自动收货 Job', 2, 'tradeOrderAutoReceiveJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 19:23:53', '1', '2023-09-26 23:38:08', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (23, '交易订单的自动评论 Job', 2, 'tradeOrderAutoCommentJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 23:38:29', '1', '2023-09-27 11:03:10', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (24, '佣金解冻 Job', 2, 'brokerageRecordUnfreezeJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-28 22:01:46', '1', '2023-09-28 22:01:56', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (25, '访问日志清理 Job', 2, 'accessLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 10:59:41', '1', '2023-10-03 11:01:10', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (26, '错误日志清理 Job', 2, 'errorLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:00:43', '1', '2023-10-03 11:01:12', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (27, '任务日志清理 Job', 2, 'jobLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:01:33', '1', '2024-09-12 13:40:34', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (33, 'demoJob', 2, 'demoJob', '', '0 * * * * ?', 1, 1, 0, '1', '2024-10-27 19:38:46', '1', '2025-05-10 18:13:54', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (35, '转账订单的同步 Job', 2, 'payTransferSyncJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-05-10 17:35:54', '1', '2025-05-10 18:13:52', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS infra_job_seq;\nCREATE SEQUENCE infra_job_seq\n    START 36;\n\n-- ----------------------------\n-- Table structure for infra_job_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_job_log;\nCREATE TABLE infra_job_log\n(\n    id            int8          NOT NULL,\n    job_id        int8          NOT NULL,\n    handler_name  varchar(64)   NOT NULL,\n    handler_param varchar(255)  NULL     DEFAULT NULL,\n    execute_index int2          NOT NULL DEFAULT 1,\n    begin_time    timestamp     NOT NULL,\n    end_time      timestamp     NULL     DEFAULT NULL,\n    duration      int4          NULL     DEFAULT NULL,\n    status        int2          NOT NULL,\n    result        varchar(4000) NULL     DEFAULT '',\n    creator       varchar(64)   NULL     DEFAULT '',\n    create_time   timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater       varchar(64)   NULL     DEFAULT '',\n    update_time   timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted       int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_job_log\n    ADD CONSTRAINT pk_infra_job_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_job_log.id IS '日志编号';\nCOMMENT ON COLUMN infra_job_log.job_id IS '任务编号';\nCOMMENT ON COLUMN infra_job_log.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job_log.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job_log.execute_index IS '第几次执行';\nCOMMENT ON COLUMN infra_job_log.begin_time IS '开始执行时间';\nCOMMENT ON COLUMN infra_job_log.end_time IS '结束执行时间';\nCOMMENT ON COLUMN infra_job_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_job_log.status IS '任务状态';\nCOMMENT ON COLUMN infra_job_log.result IS '结果数据';\nCOMMENT ON COLUMN infra_job_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_job_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_job_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job_log.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job_log IS '定时任务日志表';\n\nDROP SEQUENCE IF EXISTS infra_job_log_seq;\nCREATE SEQUENCE infra_job_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_dept\n-- ----------------------------\nDROP TABLE IF EXISTS system_dept;\nCREATE TABLE system_dept\n(\n    id             int8        NOT NULL,\n    name           varchar(30) NULL     DEFAULT '',\n    parent_id      int8        NOT NULL DEFAULT 0,\n    sort           int4        NOT NULL DEFAULT 0,\n    leader_user_id int8        NULL     DEFAULT NULL,\n    phone          varchar(11) NULL     DEFAULT NULL,\n    email          varchar(50) NULL     DEFAULT NULL,\n    status         int2        NOT NULL,\n    creator        varchar(64) NULL     DEFAULT '',\n    create_time    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64) NULL     DEFAULT '',\n    update_time    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2        NOT NULL DEFAULT 0,\n    tenant_id      int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_dept\n    ADD CONSTRAINT pk_system_dept PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dept.id IS '部门id';\nCOMMENT ON COLUMN system_dept.name IS '部门名称';\nCOMMENT ON COLUMN system_dept.parent_id IS '父部门id';\nCOMMENT ON COLUMN system_dept.sort IS '显示顺序';\nCOMMENT ON COLUMN system_dept.leader_user_id IS '负责人';\nCOMMENT ON COLUMN system_dept.phone IS '联系电话';\nCOMMENT ON COLUMN system_dept.email IS '邮箱';\nCOMMENT ON COLUMN system_dept.status IS '部门状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dept.creator IS '创建者';\nCOMMENT ON COLUMN system_dept.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dept.updater IS '更新者';\nCOMMENT ON COLUMN system_dept.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dept.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dept.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_dept IS '部门表';\n\n-- ----------------------------\n-- Records of system_dept\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, '芋道源码', 0, 0, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2025-03-29 15:47:53', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (101, '深圳总公司', 100, 1, 104, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2025-03-29 15:49:55', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (102, '长沙分公司', 100, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:40', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, '研发部门', 101, 1, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2024-10-02 10:22:03', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, '市场部门', 101, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:38', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (105, '测试部门', 101, 3, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-05-16 20:25:15', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (106, '财务部门', 101, 4, 103, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '103', '2022-01-15 21:32:22', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (107, '运维部门', 101, 5, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2023-12-02 09:28:22', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (108, '市场部门', 102, 1, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-02-16 08:35:45', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (109, '财务部门', 102, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:29', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (110, '新部门', 0, 1, NULL, NULL, NULL, 0, '110', '2022-02-23 20:46:30', '110', '2022-02-23 20:46:30', '0', 121);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (111, '顶级部门', 0, 1, NULL, NULL, NULL, 0, '113', '2022-03-07 21:44:50', '113', '2022-03-07 21:44:50', '0', 122);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, '产品部门', 101, 100, 1, NULL, NULL, 1, '1', '2023-12-02 09:45:13', '1', '2023-12-02 09:45:31', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, '支持部门', 102, 3, 104, NULL, NULL, 1, '1', '2023-12-02 09:47:38', '1', '2025-03-29 15:00:56', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_dept_seq;\nCREATE SEQUENCE system_dept_seq\n    START 114;\n\n-- ----------------------------\n-- Table structure for system_dict_data\n-- ----------------------------\nDROP TABLE IF EXISTS system_dict_data;\nCREATE TABLE system_dict_data\n(\n    id          int8         NOT NULL,\n    sort        int4         NOT NULL DEFAULT 0,\n    label       varchar(100) NULL     DEFAULT '',\n    value       varchar(100) NULL     DEFAULT '',\n    dict_type   varchar(100) NULL     DEFAULT '',\n    status      int2         NOT NULL DEFAULT 0,\n    color_type  varchar(100) NULL     DEFAULT '',\n    css_class   varchar(100) NULL     DEFAULT '',\n    remark      varchar(500) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_dict_data\n    ADD CONSTRAINT pk_system_dict_data PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dict_data.id IS '字典编码';\nCOMMENT ON COLUMN system_dict_data.sort IS '字典排序';\nCOMMENT ON COLUMN system_dict_data.label IS '字典标签';\nCOMMENT ON COLUMN system_dict_data.value IS '字典键值';\nCOMMENT ON COLUMN system_dict_data.dict_type IS '字典类型';\nCOMMENT ON COLUMN system_dict_data.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_data.color_type IS '颜色类型';\nCOMMENT ON COLUMN system_dict_data.css_class IS 'css 样式';\nCOMMENT ON COLUMN system_dict_data.remark IS '备注';\nCOMMENT ON COLUMN system_dict_data.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_data.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_data.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_data.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_data.deleted IS '是否删除';\nCOMMENT ON TABLE system_dict_data IS '字典数据表';\n\n-- ----------------------------\n-- Records of system_dict_data\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1, 1, '男', '1', 'system_user_sex', 0, 'default', 'A', '性别男', 'admin', '2021-01-05 17:03:48', '1', '2022-03-29 00:14:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 2, '女', '2', 'system_user_sex', 0, 'success', '', '性别女', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 23:30:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 1, '正常', '1', 'infra_job_status', 0, 'success', '', '正常状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 2, '暂停', '2', 'infra_job_status', 0, 'danger', '', '停用状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 1, '系统内置', '1', 'infra_config_type', 0, 'danger', '', '参数类型 - 系统内置', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (13, 2, '自定义', '2', 'infra_config_type', 0, 'primary', '', '参数类型 - 自定义', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (14, 1, '通知', '1', 'system_notice_type', 0, 'success', '', '通知', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:05:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (15, 2, '公告', '2', 'system_notice_type', 0, 'info', '', '公告', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:06:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (16, 0, '其它', '0', 'infra_operate_type', 0, 'default', '', '其它操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (17, 1, '查询', '1', 'infra_operate_type', 0, 'info', '', '查询操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (18, 2, '新增', '2', 'infra_operate_type', 0, 'primary', '', '新增操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (19, 3, '修改', '3', 'infra_operate_type', 0, 'warning', '', '修改操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (20, 4, '删除', '4', 'infra_operate_type', 0, 'danger', '', '删除操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (22, 5, '导出', '5', 'infra_operate_type', 0, 'default', '', '导出操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (23, 6, '导入', '6', 'infra_operate_type', 0, 'default', '', '导入操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (27, 1, '开启', '0', 'common_status', 0, 'primary', '', '开启状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (28, 2, '关闭', '1', 'common_status', 0, 'info', '', '关闭状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (29, 1, '目录', '1', 'system_menu_type', 0, '', '', '目录', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (30, 2, '菜单', '2', 'system_menu_type', 0, '', '', '菜单', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (31, 3, '按钮', '3', 'system_menu_type', 0, '', '', '按钮', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (32, 1, '内置', '1', 'system_role_type', 0, 'danger', '', '内置角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (33, 2, '自定义', '2', 'system_role_type', 0, 'primary', '', '自定义角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (34, 1, '全部数据权限', '1', 'system_data_scope', 0, '', '', '全部数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (35, 2, '指定部门数据权限', '2', 'system_data_scope', 0, '', '', '指定部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (36, 3, '本部门数据权限', '3', 'system_data_scope', 0, '', '', '本部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (37, 4, '本部门及以下数据权限', '4', 'system_data_scope', 0, '', '', '本部门及以下数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (38, 5, '仅本人数据权限', '5', 'system_data_scope', 0, '', '', '仅本人数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (39, 0, '成功', '0', 'system_login_result', 0, 'success', '', '登陆结果 - 成功', '', '2021-01-18 06:17:36', '1', '2022-02-16 13:23:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (40, 10, '账号或密码不正确', '10', 'system_login_result', 0, 'primary', '', '登陆结果 - 账号或密码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (41, 20, '用户被禁用', '20', 'system_login_result', 0, 'warning', '', '登陆结果 - 用户被禁用', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:23:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (42, 30, '验证码不存在', '30', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不存在', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (43, 31, '验证码不正确', '31', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (44, 100, '未知异常', '100', 'system_login_result', 0, 'danger', '', '登陆结果 - 未知异常', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (45, 1, '是', 'true', 'infra_boolean_string', 0, 'danger', '', 'Boolean 是否类型 - 是', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:01:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (46, 1, '否', 'false', 'infra_boolean_string', 0, 'info', '', 'Boolean 是否类型 - 否', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:09:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (50, 1, '单表（增删改查）', '1', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:09:06', '', '2022-03-10 16:33:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (51, 2, '树表（增删改查）', '2', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:14:46', '', '2022-03-10 16:33:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (53, 0, '初始化中', '0', 'infra_job_status', 0, 'primary', '', NULL, '', '2021-02-07 07:46:49', '1', '2022-02-16 19:33:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (57, 0, '运行中', '0', 'infra_job_log_status', 0, 'primary', '', 'RUNNING', '', '2021-02-08 10:04:24', '1', '2022-02-16 19:07:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (58, 1, '成功', '1', 'infra_job_log_status', 0, 'success', '', NULL, '', '2021-02-08 10:06:57', '1', '2022-02-16 19:07:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (59, 2, '失败', '2', 'infra_job_log_status', 0, 'warning', '', '失败', '', '2021-02-08 10:07:38', '1', '2022-02-16 19:07:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (60, 1, '会员', '1', 'user_type', 0, 'primary', '', NULL, '', '2021-02-26 00:16:27', '1', '2022-02-16 10:22:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (61, 2, '管理员', '2', 'user_type', 0, 'success', '', NULL, '', '2021-02-26 00:16:34', '1', '2025-04-06 18:37:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (62, 0, '未处理', '0', 'infra_api_error_log_process_status', 0, 'primary', '', NULL, '', '2021-02-26 07:07:19', '1', '2022-02-16 20:14:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (63, 1, '已处理', '1', 'infra_api_error_log_process_status', 0, 'success', '', NULL, '', '2021-02-26 07:07:26', '1', '2022-02-16 20:14:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (64, 2, '已忽略', '2', 'infra_api_error_log_process_status', 0, 'danger', '', NULL, '', '2021-02-26 07:07:34', '1', '2022-02-16 20:14:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (66, 1, '阿里云', 'ALIYUN', 'system_sms_channel_code', 0, 'primary', '', NULL, '1', '2021-04-05 01:05:26', '1', '2024-07-22 22:23:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (67, 1, '验证码', '1', 'system_sms_template_type', 0, 'warning', '', NULL, '1', '2021-04-05 21:50:57', '1', '2022-02-16 12:48:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (68, 2, '通知', '2', 'system_sms_template_type', 0, 'primary', '', NULL, '1', '2021-04-05 21:51:08', '1', '2022-02-16 12:48:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (69, 0, '营销', '3', 'system_sms_template_type', 0, 'danger', '', NULL, '1', '2021-04-05 21:51:15', '1', '2022-02-16 12:48:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (70, 0, '初始化', '0', 'system_sms_send_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:18:33', '1', '2022-02-16 10:26:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (71, 1, '发送成功', '10', 'system_sms_send_status', 0, 'success', '', NULL, '1', '2021-04-11 20:18:43', '1', '2022-02-16 10:25:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (72, 2, '发送失败', '20', 'system_sms_send_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:18:49', '1', '2022-02-16 10:26:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (73, 3, '不发送', '30', 'system_sms_send_status', 0, 'info', '', NULL, '1', '2021-04-11 20:19:44', '1', '2022-02-16 10:26:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (74, 0, '等待结果', '0', 'system_sms_receive_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:27:43', '1', '2022-02-16 10:28:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (75, 1, '接收成功', '10', 'system_sms_receive_status', 0, 'success', '', NULL, '1', '2021-04-11 20:29:25', '1', '2022-02-16 10:28:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (76, 2, '接收失败', '20', 'system_sms_receive_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:29:31', '1', '2022-02-16 10:28:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (77, 0, '调试(钉钉)', 'DEBUG_DING_TALK', 'system_sms_channel_code', 0, 'info', '', NULL, '1', '2021-04-13 00:20:37', '1', '2022-02-16 10:10:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (80, 100, '账号登录', '100', 'system_login_type', 0, 'primary', '', '账号登录', '1', '2021-10-06 00:52:02', '1', '2022-02-16 13:11:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (81, 101, '社交登录', '101', 'system_login_type', 0, 'info', '', '社交登录', '1', '2021-10-06 00:52:17', '1', '2022-02-16 13:11:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (83, 200, '主动登出', '200', 'system_login_type', 0, 'primary', '', '主动登出', '1', '2021-10-06 00:52:58', '1', '2022-02-16 13:11:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (85, 202, '强制登出', '202', 'system_login_type', 0, 'danger', '', '强制退出', '1', '2021-10-06 00:53:41', '1', '2022-02-16 13:11:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (86, 0, '病假', '1', 'bpm_oa_leave_type', 0, 'primary', '', NULL, '1', '2021-09-21 22:35:28', '1', '2022-02-16 10:00:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (87, 1, '事假', '2', 'bpm_oa_leave_type', 0, 'info', '', NULL, '1', '2021-09-21 22:36:11', '1', '2022-02-16 10:00:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (88, 2, '婚假', '3', 'bpm_oa_leave_type', 0, 'warning', '', NULL, '1', '2021-09-21 22:36:38', '1', '2022-02-16 10:00:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (112, 0, '微信 Wap 网站支付', 'wx_wap', 'pay_channel_code', 0, 'success', '', '微信 Wap 网站支付', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (113, 1, '微信公众号支付', 'wx_pub', 'pay_channel_code', 0, 'success', '', '微信公众号支付', '1', '2021-12-03 10:40:24', '1', '2023-07-19 20:08:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (114, 2, '微信小程序支付', 'wx_lite', 'pay_channel_code', 0, 'success', '', '微信小程序支付', '1', '2021-12-03 10:41:06', '1', '2023-07-19 20:08:50', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (115, 3, '微信 App 支付', 'wx_app', 'pay_channel_code', 0, 'success', '', '微信 App 支付', '1', '2021-12-03 10:41:20', '1', '2023-07-19 20:08:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (116, 10, '支付宝 PC 网站支付', 'alipay_pc', 'pay_channel_code', 0, 'primary', '', '支付宝 PC 网站支付', '1', '2021-12-03 10:42:09', '1', '2023-07-19 20:09:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (117, 11, '支付宝 Wap 网站支付', 'alipay_wap', 'pay_channel_code', 0, 'primary', '', '支付宝 Wap 网站支付', '1', '2021-12-03 10:42:26', '1', '2023-07-19 20:09:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (118, 12, '支付宝 App 支付', 'alipay_app', 'pay_channel_code', 0, 'primary', '', '支付宝 App 支付', '1', '2021-12-03 10:42:55', '1', '2023-07-19 20:09:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (119, 14, '支付宝扫码支付', 'alipay_qr', 'pay_channel_code', 0, 'primary', '', '支付宝扫码支付', '1', '2021-12-03 10:43:10', '1', '2023-07-19 20:09:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (120, 10, '通知成功', '10', 'pay_notify_status', 0, 'success', '', '通知成功', '1', '2021-12-03 11:02:41', '1', '2023-07-19 10:08:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (121, 20, '通知失败', '20', 'pay_notify_status', 0, 'danger', '', '通知失败', '1', '2021-12-03 11:02:59', '1', '2023-07-19 10:08:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (122, 0, '等待通知', '0', 'pay_notify_status', 0, 'info', '', '未通知', '1', '2021-12-03 11:03:10', '1', '2023-07-19 10:08:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (123, 10, '支付成功', '10', 'pay_order_status', 0, 'success', '', '支付成功', '1', '2021-12-03 11:18:29', '1', '2023-07-19 18:04:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (124, 30, '支付关闭', '30', 'pay_order_status', 0, 'info', '', '支付关闭', '1', '2021-12-03 11:18:42', '1', '2023-07-19 18:05:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (125, 0, '等待支付', '0', 'pay_order_status', 0, 'info', '', '未支付', '1', '2021-12-03 11:18:18', '1', '2023-07-19 18:04:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (600, 5, '首页', '1', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (601, 4, '秒杀活动页', '2', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (602, 3, '砍价活动页', '3', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (603, 2, '限时折扣页', '4', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (604, 1, '满减送页', '5', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1118, 0, '等待退款', '0', 'pay_refund_status', 0, 'info', '', '等待退款', '1', '2021-12-10 16:44:59', '1', '2023-07-19 10:14:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1119, 20, '退款失败', '20', 'pay_refund_status', 0, 'danger', '', '退款失败', '1', '2021-12-10 16:45:10', '1', '2023-07-19 10:15:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1124, 10, '退款成功', '10', 'pay_refund_status', 0, 'success', '', '退款成功', '1', '2021-12-10 16:46:26', '1', '2023-07-19 10:15:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1127, 1, '审批中', '1', 'bpm_process_instance_status', 0, 'default', '', '流程实例的状态 - 进行中', '1', '2022-01-07 23:47:22', '1', '2024-03-16 16:11:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1128, 2, '审批通过', '2', 'bpm_process_instance_status', 0, 'success', '', '流程实例的状态 - 已完成', '1', '2022-01-07 23:47:49', '1', '2024-03-16 16:11:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1129, 1, '审批中', '1', 'bpm_task_status', 0, 'primary', '', '流程实例的结果 - 处理中', '1', '2022-01-07 23:48:32', '1', '2024-03-08 22:41:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1130, 2, '审批通过', '2', 'bpm_task_status', 0, 'success', '', '流程实例的结果 - 通过', '1', '2022-01-07 23:48:45', '1', '2024-03-08 22:41:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1131, 3, '审批不通过', '3', 'bpm_task_status', 0, 'danger', '', '流程实例的结果 - 不通过', '1', '2022-01-07 23:48:55', '1', '2024-03-08 22:41:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1132, 4, '已取消', '4', 'bpm_task_status', 0, 'info', '', '流程实例的结果 - 撤销', '1', '2022-01-07 23:49:06', '1', '2024-03-08 22:41:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1133, 10, '流程表单', '10', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 流程表单', '103', '2022-01-11 23:51:30', '103', '2022-01-11 23:51:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1134, 20, '业务表单', '20', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 业务表单', '103', '2022-01-11 23:51:47', '103', '2022-01-11 23:51:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1135, 10, '角色', '10', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 角色', '103', '2022-01-12 23:21:22', '1', '2024-03-06 02:53:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1136, 20, '部门的成员', '20', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的成员', '103', '2022-01-12 23:21:47', '1', '2024-03-06 02:53:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1137, 21, '部门的负责人', '21', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的负责人', '103', '2022-01-12 23:33:36', '1', '2024-03-06 02:53:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1138, 30, '用户', '30', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 用户', '103', '2022-01-12 23:34:02', '1', '2024-03-06 02:53:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1139, 40, '用户组', '40', 'bpm_task_candidate_strategy', 0, 'warning', '', '任务分配规则的类型 - 用户组', '103', '2022-01-12 23:34:21', '1', '2024-03-06 02:53:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1140, 60, '流程表达式', '60', 'bpm_task_candidate_strategy', 0, 'danger', '', '任务分配规则的类型 - 流程表达式', '103', '2022-01-12 23:34:43', '1', '2024-03-06 02:53:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1141, 22, '岗位', '22', 'bpm_task_candidate_strategy', 0, 'success', '', '任务分配规则的类型 - 岗位', '103', '2022-01-14 18:41:55', '1', '2024-03-06 02:53:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1145, 1, '管理后台', '1', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 管理后台', '1', '2022-02-02 13:15:06', '1', '2022-03-10 16:32:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1146, 2, '用户 APP', '2', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 用户 APP', '1', '2022-02-02 13:15:19', '1', '2022-03-10 16:33:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1150, 1, '数据库', '1', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:28', '1', '2022-03-15 00:25:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1151, 10, '本地磁盘', '10', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:41', '1', '2022-03-15 00:25:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1152, 11, 'FTP 服务器', '11', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:06', '1', '2022-03-15 00:26:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1153, 12, 'SFTP 服务器', '12', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:22', '1', '2022-03-15 00:26:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1154, 20, 'S3 对象存储', '20', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:31', '1', '2022-03-15 00:26:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1155, 103, '短信登录', '103', 'system_login_type', 0, 'default', '', NULL, '1', '2022-05-09 23:57:58', '1', '2022-05-09 23:58:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1156, 1, 'password', 'password', 'system_oauth2_grant_type', 0, 'default', '', '密码模式', '1', '2022-05-12 00:22:05', '1', '2022-05-11 16:26:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1157, 2, 'authorization_code', 'authorization_code', 'system_oauth2_grant_type', 0, 'primary', '', '授权码模式', '1', '2022-05-12 00:22:59', '1', '2022-05-11 16:26:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1158, 3, 'implicit', 'implicit', 'system_oauth2_grant_type', 0, 'success', '', '简化模式', '1', '2022-05-12 00:23:40', '1', '2022-05-11 16:26:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1159, 4, 'client_credentials', 'client_credentials', 'system_oauth2_grant_type', 0, 'default', '', '客户端模式', '1', '2022-05-12 00:23:51', '1', '2022-05-11 16:26:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1160, 5, 'refresh_token', 'refresh_token', 'system_oauth2_grant_type', 0, 'info', '', '刷新模式', '1', '2022-05-12 00:24:02', '1', '2022-05-11 16:26:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1162, 1, '销售中', '1', 'product_spu_status', 0, 'success', '', '商品 SPU 状态 - 销售中', '1', '2022-10-24 21:19:47', '1', '2022-10-24 21:20:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1163, 0, '仓库中', '0', 'product_spu_status', 0, 'info', '', '商品 SPU 状态 - 仓库中', '1', '2022-10-24 21:20:54', '1', '2022-10-24 21:21:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1164, 0, '回收站', '-1', 'product_spu_status', 0, 'default', '', '商品 SPU 状态 - 回收站', '1', '2022-10-24 21:21:11', '1', '2022-10-24 21:21:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1165, 1, '满减', '1', 'promotion_discount_type', 0, 'success', '', '优惠类型 - 满减', '1', '2022-11-01 12:46:41', '1', '2022-11-01 12:50:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1166, 2, '折扣', '2', 'promotion_discount_type', 0, 'primary', '', '优惠类型 - 折扣', '1', '2022-11-01 12:46:51', '1', '2022-11-01 12:50:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1167, 1, '固定日期', '1', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 固定日期', '1', '2022-11-02 00:07:34', '1', '2022-11-04 00:07:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1168, 2, '领取之后', '2', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 领取之后', '1', '2022-11-02 00:07:54', '1', '2022-11-04 00:07:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1169, 1, '通用劵', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', '2022-11-02 00:28:22', '1', '2023-09-28 00:27:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1170, 2, '商品劵', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', '2022-11-02 00:28:34', '1', '2023-09-28 00:27:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1171, 1, '未使用', '1', 'promotion_coupon_status', 0, 'primary', '', '优惠劵的状态 - 已领取', '1', '2022-11-04 00:15:08', '1', '2023-10-03 12:54:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1172, 2, '已使用', '2', 'promotion_coupon_status', 0, 'success', '', '优惠劵的状态 - 已使用', '1', '2022-11-04 00:15:21', '1', '2022-11-04 19:16:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1173, 3, '已过期', '3', 'promotion_coupon_status', 0, 'info', '', '优惠劵的状态 - 已过期', '1', '2022-11-04 00:15:43', '1', '2022-11-04 19:16:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1174, 1, '直接领取', '1', 'promotion_coupon_take_type', 0, 'primary', '', '优惠劵的领取方式 - 直接领取', '1', '2022-11-04 19:13:00', '1', '2022-11-04 19:13:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1175, 2, '指定发放', '2', 'promotion_coupon_take_type', 0, 'success', '', '优惠劵的领取方式 - 指定发放', '1', '2022-11-04 19:13:13', '1', '2022-11-04 19:14:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1176, 10, '未开始', '10', 'promotion_activity_status', 0, 'primary', '', '促销活动的状态枚举 - 未开始', '1', '2022-11-04 22:54:49', '1', '2022-11-04 22:55:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1177, 20, '进行中', '20', 'promotion_activity_status', 0, 'success', '', '促销活动的状态枚举 - 进行中', '1', '2022-11-04 22:55:06', '1', '2022-11-04 22:55:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1178, 30, '已结束', '30', 'promotion_activity_status', 0, 'info', '', '促销活动的状态枚举 - 已结束', '1', '2022-11-04 22:55:41', '1', '2022-11-04 22:55:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1179, 40, '已关闭', '40', 'promotion_activity_status', 0, 'warning', '', '促销活动的状态枚举 - 已关闭', '1', '2022-11-04 22:56:10', '1', '2022-11-04 22:56:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1180, 10, '满 N 元', '10', 'promotion_condition_type', 0, 'primary', '', '营销的条件类型 - 满 N 元', '1', '2022-11-04 22:59:45', '1', '2022-11-04 22:59:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1181, 20, '满 N 件', '20', 'promotion_condition_type', 0, 'success', '', '营销的条件类型 - 满 N 件', '1', '2022-11-04 23:00:02', '1', '2022-11-04 23:00:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1182, 10, '申请售后', '10', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 申请售后', '1', '2022-11-19 20:53:33', '1', '2022-11-19 20:54:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1183, 20, '商品待退货', '20', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商品待退货', '1', '2022-11-19 20:54:36', '1', '2022-11-19 20:58:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1184, 30, '商家待收货', '30', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商家待收货', '1', '2022-11-19 20:56:56', '1', '2022-11-19 20:59:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1185, 40, '等待退款', '40', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 等待退款', '1', '2022-11-19 20:59:54', '1', '2022-11-19 21:00:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1186, 50, '退款成功', '50', 'trade_after_sale_status', 0, 'default', '', '交易售后状态 - 退款成功', '1', '2022-11-19 21:00:33', '1', '2022-11-19 21:00:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1187, 61, '买家取消', '61', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 买家取消', '1', '2022-11-19 21:01:29', '1', '2022-11-19 21:01:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1188, 62, '商家拒绝', '62', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒绝', '1', '2022-11-19 21:02:17', '1', '2022-11-19 21:02:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1189, 63, '商家拒收货', '63', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒收货', '1', '2022-11-19 21:02:37', '1', '2022-11-19 21:03:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1190, 10, '售中退款', '10', 'trade_after_sale_type', 0, 'success', '', '交易售后的类型 - 售中退款', '1', '2022-11-19 21:05:05', '1', '2022-11-19 21:38:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1191, 20, '售后退款', '20', 'trade_after_sale_type', 0, 'primary', '', '交易售后的类型 - 售后退款', '1', '2022-11-19 21:05:32', '1', '2022-11-19 21:38:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1192, 10, '仅退款', '10', 'trade_after_sale_way', 0, 'primary', '', '交易售后的方式 - 仅退款', '1', '2022-11-19 21:39:19', '1', '2022-11-19 21:39:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1193, 20, '退货退款', '20', 'trade_after_sale_way', 0, 'success', '', '交易售后的方式 - 退货退款', '1', '2022-11-19 21:39:38', '1', '2022-11-19 21:39:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1194, 10, '微信小程序', '10', 'terminal', 0, 'default', '', '终端 - 微信小程序', '1', '2022-12-10 10:51:11', '1', '2022-12-10 10:51:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1195, 20, 'H5 网页', '20', 'terminal', 0, 'default', '', '终端 - H5 网页', '1', '2022-12-10 10:51:30', '1', '2022-12-10 10:51:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1196, 11, '微信公众号', '11', 'terminal', 0, 'default', '', '终端 - 微信公众号', '1', '2022-12-10 10:54:16', '1', '2022-12-10 10:52:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1197, 31, '苹果 App', '31', 'terminal', 0, 'default', '', '终端 - 苹果 App', '1', '2022-12-10 10:54:42', '1', '2022-12-10 10:52:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1198, 32, '安卓 App', '32', 'terminal', 0, 'default', '', '终端 - 安卓 App', '1', '2022-12-10 10:55:02', '1', '2022-12-10 10:59:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1199, 0, '普通订单', '0', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 普通订单', '1', '2022-12-10 16:34:14', '1', '2022-12-10 16:34:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1200, 1, '秒杀订单', '1', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 秒杀订单', '1', '2022-12-10 16:34:26', '1', '2022-12-10 16:34:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1201, 2, '砍价订单', '2', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 拼团订单', '1', '2022-12-10 16:34:36', '1', '2024-09-07 14:18:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1202, 3, '拼团订单', '3', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 砍价订单', '1', '2022-12-10 16:34:48', '1', '2024-09-07 14:18:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1203, 0, '待支付', '0', 'trade_order_status', 0, 'default', '', '交易订单状态 - 待支付', '1', '2022-12-10 16:49:29', '1', '2022-12-10 16:49:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1204, 10, '待发货', '10', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 待发货', '1', '2022-12-10 16:49:53', '1', '2022-12-10 16:51:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1205, 20, '已发货', '20', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 已发货', '1', '2022-12-10 16:50:13', '1', '2022-12-10 16:51:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1206, 30, '已完成', '30', 'trade_order_status', 0, 'success', '', '交易订单状态 - 已完成', '1', '2022-12-10 16:50:30', '1', '2022-12-10 16:51:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1207, 40, '已取消', '40', 'trade_order_status', 0, 'danger', '', '交易订单状态 - 已取消', '1', '2022-12-10 16:50:50', '1', '2022-12-10 16:51:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1208, 0, '未售后', '0', 'trade_order_item_after_sale_status', 0, 'info', '', '交易订单项的售后状态 - 未售后', '1', '2022-12-10 20:58:42', '1', '2022-12-10 20:59:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1209, 10, '售后中', '10', 'trade_order_item_after_sale_status', 0, 'primary', '', '交易订单项的售后状态 - 售后中', '1', '2022-12-10 20:59:21', '1', '2024-07-21 17:01:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1210, 20, '已退款', '20', 'trade_order_item_after_sale_status', 0, 'success', '', '交易订单项的售后状态 - 已退款', '1', '2022-12-10 20:59:46', '1', '2024-07-21 17:01:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1211, 1, '完全匹配', '1', 'mp_auto_reply_request_match', 0, 'primary', '', '公众号自动回复的请求关键字匹配模式 - 完全匹配', '1', '2023-01-16 23:30:39', '1', '2023-01-16 23:31:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1212, 2, '半匹配', '2', 'mp_auto_reply_request_match', 0, 'success', '', '公众号自动回复的请求关键字匹配模式 - 半匹配', '1', '2023-01-16 23:30:55', '1', '2023-01-16 23:31:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1213, 1, '文本', 'text', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 文本', '1', '2023-01-17 22:17:32', '1', '2023-01-17 22:17:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1214, 2, '图片', 'image', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图片', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1215, 3, '语音', 'voice', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 语音', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:20:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1216, 4, '视频', 'video', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:21:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1217, 5, '小视频', 'shortvideo', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 小视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1218, 6, '图文', 'news', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图文', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1219, 7, '音乐', 'music', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 音乐', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1220, 8, '地理位置', 'location', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 地理位置', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:23:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1221, 9, '链接', 'link', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 链接', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1222, 10, '事件', 'event', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 事件', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1223, 0, '初始化', '0', 'system_mail_send_status', 0, 'primary', '', '邮件发送状态 - 初始化\\n', '1', '2023-01-26 09:53:49', '1', '2023-01-26 16:36:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1224, 10, '发送成功', '10', 'system_mail_send_status', 0, 'success', '', '邮件发送状态 - 发送成功', '1', '2023-01-26 09:54:28', '1', '2023-01-26 16:36:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1225, 20, '发送失败', '20', 'system_mail_send_status', 0, 'danger', '', '邮件发送状态 - 发送失败', '1', '2023-01-26 09:54:50', '1', '2023-01-26 16:36:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1226, 30, '不发送', '30', 'system_mail_send_status', 0, 'info', '', '邮件发送状态 -  不发送', '1', '2023-01-26 09:55:06', '1', '2023-01-26 16:36:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1227, 1, '通知公告', '1', 'system_notify_template_type', 0, 'primary', '', '站内信模版的类型 - 通知公告', '1', '2023-01-28 10:35:59', '1', '2023-01-28 10:35:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1228, 2, '系统消息', '2', 'system_notify_template_type', 0, 'success', '', '站内信模版的类型 - 系统消息', '1', '2023-01-28 10:36:20', '1', '2023-01-28 10:36:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1230, 13, '支付宝条码支付', 'alipay_bar', 'pay_channel_code', 0, 'primary', '', '支付宝条码支付', '1', '2023-02-18 23:32:24', '1', '2023-07-19 20:09:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1231, 10, 'Vue2 Element UI 标准模版', '10', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:03:55', '1', '2023-04-13 00:03:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1232, 20, 'Vue3 Element Plus 标准模版', '20', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:08', '1', '2023-04-13 00:04:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1234, 30, 'Vben2.0 Ant Design Schema 模版', '30', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:26', '1', '2025-04-23 21:27:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1244, 0, '按件', '1', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:40', '1', '2023-05-21 22:46:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1245, 1, '按重量', '2', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:58', '1', '2023-05-21 22:46:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1246, 2, '按体积', '3', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:47:18', '1', '2023-05-21 22:47:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1335, 11, '订单积分抵扣', '11', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:27', '1', '2023-10-11 07:41:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1336, 1, '签到', '1', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:48', '1', '2023-08-20 11:59:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1341, 20, '已退款', '20', 'pay_order_status', 0, 'danger', '', '已退款', '1', '2023-07-19 18:05:37', '1', '2023-07-19 18:05:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1342, 21, '请求成功，但是结果失败', '21', 'pay_notify_status', 0, 'warning', '', '请求成功，但是结果失败', '1', '2023-07-19 18:10:47', '1', '2023-07-19 18:11:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1343, 22, '请求失败', '22', 'pay_notify_status', 0, 'warning', '', NULL, '1', '2023-07-19 18:11:05', '1', '2023-07-19 18:11:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1344, 4, '微信扫码支付', 'wx_native', 'pay_channel_code', 0, 'success', '', '微信扫码支付', '1', '2023-07-19 20:07:47', '1', '2023-07-19 20:09:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1345, 5, '微信条码支付', 'wx_bar', 'pay_channel_code', 0, 'success', '', '微信条码支付\\n', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1346, 1, '支付单', '1', 'pay_notify_type', 0, 'primary', '', '支付单', '1', '2023-07-20 12:23:17', '1', '2023-07-20 12:23:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1347, 2, '退款单', '2', 'pay_notify_type', 0, 'danger', '', NULL, '1', '2023-07-20 12:23:26', '1', '2023-07-20 12:23:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1348, 20, '模拟支付', 'mock', 'pay_channel_code', 0, 'default', '', '模拟支付', '1', '2023-07-29 11:10:51', '1', '2023-07-29 03:14:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1349, 12, '订单积分抵扣（整单取消）', '12', 'member_point_biz_type', 0, '', '', '', '1', '2023-08-20 12:00:03', '1', '2023-10-11 07:42:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1350, 0, '管理员调整', '0', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1351, 1, '邀新奖励', '1', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1352, 11, '下单奖励', '11', 'member_experience_biz_type', 0, 'success', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1353, 12, '下单奖励（整单取消）', '12', 'member_experience_biz_type', 0, 'warning', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1354, 4, '签到奖励', '4', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1355, 5, '抽奖奖励', '5', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1356, 1, '快递发货', '1', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:04:55', '1', '2023-08-23 00:04:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1357, 2, '用户自提', '2', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:05:05', '1', '2023-08-23 00:05:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1358, 3, '品类劵', '3', 'promotion_product_scope', 0, 'default', '', '', '1', '2023-09-01 23:43:07', '1', '2023-09-28 00:27:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1359, 1, '人人分销', '1', 'brokerage_enabled_condition', 0, '', '', '所有用户都可以分销', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1360, 2, '指定分销', '2', 'brokerage_enabled_condition', 0, '', '', '仅可后台手动设置推广员', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1361, 1, '首次绑定', '1', 'brokerage_bind_mode', 0, '', '', '只要用户没有推广人，随时都可以绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1362, 2, '注册绑定', '2', 'brokerage_bind_mode', 0, '', '', '仅新用户注册时才能绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1363, 3, '覆盖绑定', '3', 'brokerage_bind_mode', 0, '', '', '如果用户已经有推广人，推广人会被变更', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1364, 1, '钱包', '1', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1365, 2, '银行卡', '2', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1366, 3, '微信收款码', '3', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1367, 4, '支付宝收款码', '4', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1368, 1, '订单返佣', '1', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1369, 2, '申请提现', '2', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1370, 3, '申请提现驳回', '3', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1371, 0, '待结算', '0', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1372, 1, '已结算', '1', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1373, 2, '已取消', '2', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1374, 0, '审核中', '0', 'brokerage_withdraw_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1375, 10, '审核通过', '10', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1376, 11, '提现成功', '11', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1377, 20, '审核不通过', '20', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1378, 21, '提现失败', '21', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1379, 0, '工商银行', '0', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1380, 1, '建设银行', '1', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1381, 2, '农业银行', '2', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1382, 3, '中国银行', '3', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1383, 4, '交通银行', '4', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1384, 5, '招商银行', '5', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1385, 21, '钱包', 'wallet', 'pay_channel_code', 0, 'primary', '', '', '1', '2023-10-01 21:46:19', '1', '2023-10-01 21:48:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1386, 1, '砍价中', '1', 'promotion_bargain_record_status', 0, 'default', '', '', '1', '2023-10-05 10:41:26', '1', '2023-10-05 10:41:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1387, 2, '砍价成功', '2', 'promotion_bargain_record_status', 0, 'success', '', '', '1', '2023-10-05 10:41:39', '1', '2023-10-05 10:41:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1388, 3, '砍价失败', '3', 'promotion_bargain_record_status', 0, 'warning', '', '', '1', '2023-10-05 10:41:57', '1', '2023-10-05 10:41:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1389, 0, '拼团中', '0', 'promotion_combination_record_status', 0, '', '', '', '1', '2023-10-08 07:24:44', '1', '2024-10-13 10:08:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1390, 1, '拼团成功', '1', 'promotion_combination_record_status', 0, 'success', '', '', '1', '2023-10-08 07:24:56', '1', '2024-10-13 10:08:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1391, 2, '拼团失败', '2', 'promotion_combination_record_status', 0, 'warning', '', '', '1', '2023-10-08 07:25:11', '1', '2024-10-13 10:08:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1392, 2, '管理员修改', '2', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:41:34', '1', '2023-10-11 07:41:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1393, 13, '订单积分抵扣（单个退款）', '13', 'member_point_biz_type', 0, '', '', '', '1', '2023-10-11 07:42:29', '1', '2023-10-11 07:42:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1394, 21, '订单积分奖励', '21', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:44', '1', '2023-10-11 07:42:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1395, 22, '订单积分奖励（整单取消）', '22', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:55', '1', '2023-10-11 07:43:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1396, 23, '订单积分奖励（单个退款）', '23', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:43:16', '1', '2023-10-11 07:43:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1397, 13, '下单奖励（单个退款）', '13', 'member_experience_biz_type', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1398, 5, '网上转账', '5', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:24', '1', '2023-10-18 21:55:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1399, 6, '支付宝', '6', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:38', '1', '2023-10-18 21:55:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1400, 7, '微信支付', '7', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:53', '1', '2023-10-18 21:55:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1401, 8, '其他', '8', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:56:06', '1', '2023-10-18 21:56:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1402, 1, 'IT', '1', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:15', '1', '2024-02-18 23:30:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1403, 2, '金融业', '2', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:29', '1', '2024-02-18 23:30:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1404, 3, '房地产', '3', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:41', '1', '2024-02-18 23:30:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1405, 4, '商业服务', '4', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:54', '1', '2024-02-18 23:30:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1406, 5, '运输/物流', '5', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:03', '1', '2024-02-18 23:31:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1407, 6, '生产', '6', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:13', '1', '2024-02-18 23:31:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1408, 7, '政府', '7', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:27', '1', '2024-02-18 23:31:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1409, 8, '文化传媒', '8', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:37', '1', '2024-02-18 23:31:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1422, 1, 'A （重点客户）', '1', 'crm_customer_level', 0, 'primary', '', '', '1', '2023-10-28 23:07:13', '1', '2023-10-28 23:07:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1423, 2, 'B （普通客户）', '2', 'crm_customer_level', 0, 'info', '', '', '1', '2023-10-28 23:07:35', '1', '2023-10-28 23:07:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1424, 3, 'C （非优先客户）', '3', 'crm_customer_level', 0, 'default', '', '', '1', '2023-10-28 23:07:53', '1', '2023-10-28 23:07:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1425, 1, '促销', '1', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:29', '1', '2023-10-28 23:08:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1426, 2, '搜索引擎', '2', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:39', '1', '2023-10-28 23:08:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1427, 3, '广告', '3', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:47', '1', '2023-10-28 23:08:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1428, 4, '转介绍', '4', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:58', '1', '2023-10-28 23:08:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1429, 5, '线上注册', '5', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:12', '1', '2023-10-28 23:09:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1430, 6, '线上咨询', '6', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:22', '1', '2023-10-28 23:09:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1431, 7, '预约上门', '7', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:39', '1', '2023-10-28 23:09:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1432, 8, '陌拜', '8', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:04', '1', '2023-10-28 23:10:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1433, 9, '电话咨询', '9', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:18', '1', '2023-10-28 23:10:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1434, 10, '邮件咨询', '10', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:33', '1', '2023-10-28 23:10:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1435, 10, 'Gitee', '10', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:42', '1', '2023-11-04 13:04:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1436, 20, '钉钉', '20', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:54', '1', '2023-11-04 13:04:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1437, 30, '企业微信', '30', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:09', '1', '2023-11-04 13:05:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1438, 31, '微信公众平台', '31', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:18', '1', '2023-11-04 13:05:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1439, 32, '微信开放平台', '32', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:30', '1', '2023-11-04 13:05:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1440, 34, '微信小程序', '34', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1441, 1, '上架', '1', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:34', '1', '2023-10-30 21:49:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1442, 0, '下架', '0', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:13', '1', '2023-10-30 21:49:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1443, 15, '子表', '15', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-13 23:06:16', '1', '2023-11-13 23:06:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1444, 10, '主表（标准模式）', '10', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:32:49', '1', '2023-11-14 12:32:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1445, 11, '主表（ERP 模式）', '11', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:33:05', '1', '2023-11-14 12:33:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1446, 12, '主表（内嵌模式）', '12', 'infra_codegen_template_type', 0, '', '', '', '1', '2023-11-14 12:33:31', '1', '2023-11-14 12:33:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1447, 1, '负责人', '1', 'crm_permission_level', 0, 'default', '', '', '1', '2023-11-30 09:53:12', '1', '2023-11-30 09:53:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1448, 2, '只读', '2', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:29', '1', '2023-11-30 09:53:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1449, 3, '读写', '3', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:36', '1', '2023-11-30 09:53:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1450, 0, '未提交', '0', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:56:59', '1', '2023-11-30 18:56:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1451, 10, '审批中', '10', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:10', '1', '2023-11-30 18:57:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1452, 20, '审核通过', '20', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:24', '1', '2023-11-30 18:57:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1453, 30, '审核不通过', '30', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:32', '1', '2023-11-30 18:57:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1454, 40, '已取消', '40', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:42', '1', '2023-11-30 18:57:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1456, 1, '支票', '1', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:29', '1', '2023-10-18 21:54:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1457, 2, '现金', '2', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:41', '1', '2023-10-18 21:54:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1458, 3, '邮政汇款', '3', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:53', '1', '2023-10-18 21:54:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1459, 4, '电汇', '4', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:07', '1', '2023-10-18 21:55:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1461, 1, '个', '1', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:26', '1', '2023-12-05 23:02:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1462, 2, '块', '2', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:34', '1', '2023-12-05 23:02:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1463, 3, '只', '3', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:57', '1', '2023-12-05 23:02:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1464, 4, '把', '4', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:05', '1', '2023-12-05 23:03:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1465, 5, '枚', '5', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:14', '1', '2023-12-05 23:03:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1466, 6, '瓶', '6', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:20', '1', '2023-12-05 23:03:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1467, 7, '盒', '7', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:30', '1', '2023-12-05 23:03:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1468, 8, '台', '8', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:41', '1', '2023-12-05 23:03:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1469, 9, '吨', '9', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:48', '1', '2023-12-05 23:03:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1470, 10, '千克', '10', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:03', '1', '2023-12-05 23:04:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1471, 11, '米', '11', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:12', '1', '2023-12-05 23:04:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1472, 12, '箱', '12', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:25', '1', '2023-12-05 23:04:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1473, 13, '套', '13', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:34', '1', '2023-12-05 23:04:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1474, 1, '打电话', '1', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:20', '1', '2024-01-15 20:48:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1475, 2, '发短信', '2', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:31', '1', '2024-01-15 20:48:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1476, 3, '上门拜访', '3', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:07', '1', '2024-01-15 20:49:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1477, 4, '微信沟通', '4', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:15', '1', '2024-01-15 20:49:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1482, 4, '转账失败', '20', 'pay_transfer_status', 0, 'warning', '', '', '1', '2023-10-28 16:24:16', '1', '2025-05-08 12:59:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1483, 3, '转账成功', '10', 'pay_transfer_status', 0, 'success', '', '', '1', '2023-10-28 16:23:50', '1', '2025-05-08 12:58:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1484, 2, '转账进行中', '5', 'pay_transfer_status', 0, 'info', '', '', '1', '2023-10-28 16:23:12', '1', '2025-05-08 12:58:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1485, 1, '等待转账', '0', 'pay_transfer_status', 0, 'default', '', '', '1', '2023-10-28 16:21:43', '1', '2023-10-28 16:23:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1486, 10, '其它入库', '10', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:07:25', '1', '2024-02-05 18:07:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1487, 11, '其它入库（作废）', '11', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:08:07', '1', '2024-02-05 19:20:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1488, 20, '其它出库', '20', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:08:51', '1', '2024-02-05 18:08:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1489, 21, '其它出库（作废）', '21', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:09:00', '1', '2024-02-05 19:20:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1490, 10, '未审核', '10', 'erp_audit_status', 0, 'default', '', '', '1', '2024-02-06 00:00:21', '1', '2024-02-06 00:00:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1491, 20, '已审核', '20', 'erp_audit_status', 0, 'success', '', '', '1', '2024-02-06 00:00:35', '1', '2024-02-06 00:00:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1492, 30, '调拨入库', '30', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:19', '1', '2024-02-07 12:36:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1493, 31, '调拨入库（作废）', '31', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:29', '1', '2024-02-07 20:37:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1494, 32, '调拨出库', '32', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:38', '1', '2024-02-07 12:36:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1495, 33, '调拨出库（作废）', '33', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:49', '1', '2024-02-07 20:37:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1496, 40, '盘盈入库', '40', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:53:00', '1', '2024-02-08 08:53:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1497, 41, '盘盈入库（作废）', '41', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:53:39', '1', '2024-02-16 19:40:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1498, 42, '盘亏出库', '42', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:54:16', '1', '2024-02-08 08:54:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1499, 43, '盘亏出库（作废）', '43', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:54:31', '1', '2024-02-16 19:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1500, 50, '销售出库', '50', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-11 21:47:25', '1', '2024-02-11 21:50:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1501, 51, '销售出库（作废）', '51', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-11 21:47:37', '1', '2024-02-11 21:51:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1502, 60, '销售退货入库', '60', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-12 06:51:05', '1', '2024-02-12 06:51:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1503, 61, '销售退货入库（作废）', '61', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-12 06:51:18', '1', '2024-02-12 06:51:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1504, 70, '采购入库', '70', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:02', '1', '2024-02-16 13:10:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1505, 71, '采购入库（作废）', '71', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:10', '1', '2024-02-16 19:40:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1506, 80, '采购退货出库', '80', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:17', '1', '2024-02-16 13:10:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1507, 81, '采购退货出库（作废）', '81', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:26', '1', '2024-02-16 19:40:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1509, 3, '审批不通过', '3', 'bpm_process_instance_status', 0, 'danger', '', '', '1', '2024-03-16 16:12:06', '1', '2024-03-16 16:12:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1510, 4, '已取消', '4', 'bpm_process_instance_status', 0, 'warning', '', '', '1', '2024-03-16 16:12:22', '1', '2024-03-16 16:12:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1511, 5, '已退回', '5', 'bpm_task_status', 0, 'warning', '', '', '1', '2024-03-16 19:10:46', '1', '2024-03-08 22:41:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1512, 6, '委派中', '6', 'bpm_task_status', 0, 'primary', '', '', '1', '2024-03-17 10:06:22', '1', '2024-03-08 22:41:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1513, 7, '审批通过中', '7', 'bpm_task_status', 0, 'success', '', '', '1', '2024-03-17 10:06:47', '1', '2024-03-08 22:41:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1514, 0, '待审批', '0', 'bpm_task_status', 0, 'info', '', '', '1', '2024-03-17 10:07:11', '1', '2024-03-08 22:41:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1515, 35, '发起人自选', '35', 'bpm_task_candidate_strategy', 0, '', '', '', '1', '2024-03-22 19:45:16', '1', '2024-03-22 19:45:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1516, 1, '执行监听器', 'execution', 'bpm_process_listener_type', 0, 'primary', '', '', '1', '2024-03-23 12:54:03', '1', '2024-03-23 19:14:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1517, 1, '任务监听器', 'task', 'bpm_process_listener_type', 0, 'success', '', '', '1', '2024-03-23 12:54:13', '1', '2024-03-23 19:14:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1526, 1, 'Java 类', 'class', 'bpm_process_listener_value_type', 0, 'primary', '', '', '1', '2024-03-23 15:08:45', '1', '2024-03-23 19:14:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1527, 2, '表达式', 'expression', 'bpm_process_listener_value_type', 0, 'success', '', '', '1', '2024-03-23 15:09:06', '1', '2024-03-23 19:14:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1528, 3, '代理表达式', 'delegateExpression', 'bpm_process_listener_value_type', 0, 'info', '', '', '1', '2024-03-23 15:11:23', '1', '2024-03-23 19:14:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1529, 1, '天', '1', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:26', '1', '2024-03-29 22:50:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1530, 2, '周', '2', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:36', '1', '2024-03-29 22:50:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1531, 3, '月', '3', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:46', '1', '2024-03-29 22:50:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1532, 4, '季度', '4', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:01', '1', '2024-03-29 22:51:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1533, 5, '年', '5', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:07', '1', '2024-03-29 22:51:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1534, 1, '赢单', '1', 'crm_business_end_status_type', 0, 'success', '', '', '1', '2024-04-13 23:26:57', '1', '2024-04-13 23:26:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1535, 2, '输单', '2', 'crm_business_end_status_type', 0, 'primary', '', '', '1', '2024-04-13 23:27:31', '1', '2024-04-13 23:27:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1536, 3, '无效', '3', 'crm_business_end_status_type', 0, 'info', '', '', '1', '2024-04-13 23:27:59', '1', '2024-04-13 23:27:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1537, 1, 'OpenAI', 'OpenAI', 'ai_platform', 0, '', '', '', '1', '2024-05-09 22:33:47', '1', '2024-05-09 22:58:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1538, 2, 'Ollama', 'Ollama', 'ai_platform', 0, '', '', '', '1', '2024-05-17 23:02:55', '1', '2024-05-17 23:02:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1539, 3, '文心一言', 'YiYan', 'ai_platform', 0, '', '', '', '1', '2024-05-18 09:24:20', '1', '2024-05-18 09:29:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1540, 4, '讯飞星火', 'XingHuo', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:08:56', '1', '2024-05-18 10:08:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1541, 5, '通义千问', 'TongYi', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:32:29', '1', '2024-07-06 15:42:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1542, 6, 'StableDiffusion', 'StableDiffusion', 'ai_platform', 0, '', '', '', '1', '2024-06-01 15:09:31', '1', '2024-06-01 15:10:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1543, 10, '进行中', '10', 'ai_image_status', 0, 'primary', '', '', '1', '2024-06-26 20:51:41', '1', '2024-06-26 20:52:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1544, 20, '已完成', '20', 'ai_image_status', 0, 'success', '', '', '1', '2024-06-26 20:52:07', '1', '2024-06-26 20:52:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1545, 30, '已失败', '30', 'ai_image_status', 0, 'warning', '', '', '1', '2024-06-26 20:52:25', '1', '2024-06-26 20:52:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1546, 7, 'Midjourney', 'Midjourney', 'ai_platform', 0, '', '', '', '1', '2024-06-26 22:14:46', '1', '2024-06-26 22:14:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1547, 10, '进行中', '10', 'ai_music_status', 0, 'primary', '', '', '1', '2024-06-27 22:45:22', '1', '2024-06-28 00:56:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1548, 20, '已完成', '20', 'ai_music_status', 0, 'success', '', '', '1', '2024-06-27 22:45:33', '1', '2024-06-28 00:56:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1549, 30, '已失败', '30', 'ai_music_status', 0, 'danger', '', '', '1', '2024-06-27 22:45:44', '1', '2024-06-28 00:56:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1550, 1, '歌词模式', '1', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:31', '1', '2024-06-28 01:22:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1554, 13, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2025-02-24 20:18:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1558, 7, '博客文章', '7', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:23', '1', '2024-07-07 15:50:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1559, 8, '想法', '8', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:31', '1', '2024-07-07 15:50:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1560, 9, '大纲', '9', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:37', '1', '2024-07-07 15:50:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1561, 1, '自动', '1', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:06', '1', '2024-07-07 15:51:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1562, 2, '友善', '2', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:19', '1', '2024-07-07 15:51:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1563, 3, '随意', '3', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:27', '1', '2024-07-07 15:51:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1564, 4, '友好', '4', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:37', '1', '2024-07-07 15:51:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1565, 5, '专业', '5', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:49', '1', '2024-07-07 15:52:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1566, 6, '诙谐', '6', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:15', '1', '2024-07-07 15:52:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1567, 7, '有趣', '7', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:24', '1', '2024-07-07 15:52:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1568, 8, '正式', '8', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:54:33', '1', '2024-07-07 15:54:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1569, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1570, 1, '自动', '1', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:34', '1', '2024-07-07 15:19:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1571, 2, '电子邮件', '2', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:50', '1', '2024-07-07 15:49:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1572, 3, '消息', '3', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:01', '1', '2024-07-07 15:49:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1573, 4, '评论', '4', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:13', '1', '2024-07-07 15:49:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1574, 1, '自动', '1', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:18', '1', '2024-07-07 15:44:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1575, 2, '中文', '2', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:28', '1', '2024-07-07 15:44:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1576, 3, '英文', '3', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:37', '1', '2024-07-07 15:44:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1577, 4, '韩语', '4', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:28', '1', '2024-07-07 15:46:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1578, 5, '日语', '5', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:44', '1', '2024-07-07 15:46:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1579, 1, '自动', '1', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:34', '1', '2024-07-07 15:48:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1580, 2, '短', '2', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:44', '1', '2024-07-07 15:48:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1581, 3, '中等', '3', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:52', '1', '2024-07-07 15:48:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1582, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1584, 1, '撰写', '1', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:00', '1', '2024-07-10 21:26:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1585, 2, '回复', '2', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:06', '1', '2024-07-10 21:26:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1586, 2, '腾讯云', 'TENCENT', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:16', '1', '2024-07-22 22:23:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1587, 3, '华为云', 'HUAWEI', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:46', '1', '2024-07-22 22:23:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1588, 1, 'OpenAI 微软', 'AzureOpenAI', 'ai_platform', 0, '', '', '', '1', '2024-08-10 14:07:41', '1', '2024-08-10 14:07:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1589, 10, 'BPMN 设计器', '10', 'bpm_model_type', 0, 'primary', '', '', '1', '2024-08-26 15:22:17', '1', '2024-08-26 16:46:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1590, 20, 'SIMPLE 设计器', '20', 'bpm_model_type', 0, 'success', '', '', '1', '2024-08-26 15:22:27', '1', '2024-08-26 16:45:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1591, 4, '七牛云', 'QINIU', 'system_sms_channel_code', 0, '', '', '', '1', '2024-08-31 08:45:03', '1', '2024-08-31 08:45:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1592, 3, '新人券', '3', 'promotion_coupon_take_type', 0, 'info', '', '新人注册后，自动发放', '1', '2024-09-03 11:57:16', '1', '2024-09-03 11:57:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1593, 5, '微信零钱', '5', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2024-10-13 11:06:48', '1', '2025-05-10 08:24:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', '2025-02-23 19:51:40', '1', '2025-02-23 19:52:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', '2025-02-23 20:58:04', '1', '2025-02-23 20:58:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', '2025-02-24 20:19:09', '1', '2025-02-24 20:19:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1686, 1, '聊天', '1', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:26:34', '1', '2025-03-03 12:26:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1687, 2, '图像', '2', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:23', '1', '2025-03-03 12:27:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1688, 3, '音频', '3', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:51', '1', '2025-03-03 12:27:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1689, 4, '视频', '4', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:03', '1', '2025-03-03 12:28:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1690, 5, '向量', '5', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:15', '1', '2025-03-03 12:28:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1693, 15, '月之暗面', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-03-11 20:05:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2000, 0, '标准数据格式（JSON）', '0', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:26', '1', '2025-03-17 09:28:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2001, 1, '透传/自定义', '1', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:37', '1', '2025-03-17 09:28:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2025-03-17 09:28:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2025-03-17 09:28:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2025-03-17 09:28:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2025-03-17 09:28:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2025-03-17 09:28:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2007, 0, '弱校验', '0', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:05:48', '1', '2025-03-17 09:28:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2008, 1, '免校验', '1', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:06:03', '1', '2025-03-17 09:28:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2025-03-17 09:28:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2010, 1, '蜂窝（2G / 3G / 4G / 5G）', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2025-03-17 09:28:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2025-03-17 09:28:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2025-03-17 09:28:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2013, 0, '自定义', '0', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:10', '1', '2025-03-17 09:28:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2014, 1, 'Modbus', '1', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:21', '1', '2025-03-17 09:28:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2015, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2025-03-17 09:29:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2016, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2025-03-17 09:29:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2017, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2025-03-17 09:29:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-03-17 09:29:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-03-17 09:29:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-03-17 09:29:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2025-03-17 09:29:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2025-03-17 09:29:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2025-03-17 09:29:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2024, 1, 'JAR 部署', '0', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:32', '1', '2025-03-17 09:29:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2025, 2, '独立部署', '1', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:43', '1', '2025-03-17 09:29:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2026, 0, '停止', '0', 'iot_plugin_status', 0, 'danger', '', '', '1', '2024-12-13 11:07:37', '1', '2025-03-17 09:29:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2027, 1, '运行', '1', 'iot_plugin_status', 0, '', '', '', '1', '2024-12-13 11:07:45', '1', '2025-03-17 09:34:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2028, 0, '普通插件', '0', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:32', '1', '2025-03-17 09:34:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2029, 1, '设备插件', '1', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2033, 4, 'PH值', 'pH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2034, 5, '土壤EC值', 'dS/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2035, 6, '太阳总辐射', 'W/㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2036, 7, '降雨量', 'mm/hour', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2037, 8, '乏', 'var', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2038, 9, '厘泊', 'cP', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2039, 10, '饱和度', 'aw', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2040, 11, '个', 'pcs', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2041, 12, '厘斯', 'cst', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2042, 13, '巴', 'bar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2043, 14, '纳克每升', 'ppt', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2044, 15, '微克每升', 'ppb', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2045, 16, '微西每厘米', 'uS/cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2046, 17, '牛顿每库仑', 'N/C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2047, 18, '伏特每米', 'V/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2048, 19, '滴速', 'ml/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2049, 20, '毫米汞柱', 'mmHg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2050, 21, '血糖', 'mmol/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2051, 22, '毫米每秒', 'mm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2052, 23, '转每分钟', 'turn/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2053, 24, '次', 'count', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2054, 25, '档', 'gear', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2055, 26, '步', 'stepCount', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2056, 27, '标准立方米每小时', 'Nm3/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2057, 28, '千伏', 'kV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2058, 29, '千伏安', 'kVA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2060, 30, '千乏', 'kVar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2061, 31, '微瓦每平方厘米', 'uw/cm2', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2062, 32, '只', '只', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2063, 33, '相对湿度', '%RH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2064, 34, '立方米每秒', 'm³/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2065, 35, '公斤每秒', 'kg/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2066, 36, '转每分钟', 'r/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2067, 37, '吨每小时', 't/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2068, 38, '千卡每小时', 'KCL/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2069, 39, '升每秒', 'L/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2070, 40, '兆帕', 'Mpa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2071, 41, '立方米每小时', 'm³/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2072, 42, '千乏时', 'kvarh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2073, 43, '微克每升', 'μg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2074, 44, '千卡路里', 'kcal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2075, 45, '吉字节', 'GB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2076, 46, '兆字节', 'MB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2077, 47, '千字节', 'KB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2078, 48, '字节', 'B', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2079, 49, '微克每平方分米每天', 'μg/(d㎡·d)', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2080, 50, '无', '', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2081, 51, '百万分率', 'ppm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2082, 52, '像素', 'pixel', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2083, 53, '照度', 'Lux', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2084, 54, '重力加速度', 'grav', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2085, 55, '分贝', 'dB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2086, 56, '百分比', '%', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2087, 57, '流明', 'lm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2088, 58, '比特', 'bit', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2089, 59, '克每毫升', 'g/mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2090, 60, '克每升', 'g/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2091, 61, '毫克每升', 'mg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2092, 62, '微克每立方米', 'μg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2093, 63, '毫克每立方米', 'mg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2094, 64, '克每立方米', 'g/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2095, 65, '千克每立方米', 'kg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2096, 66, '纳法', 'nF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2097, 67, '皮法', 'pF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2098, 68, '微法', 'μF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2099, 69, '法拉', 'F', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2100, 70, '欧姆', 'Ω', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2101, 71, '微安', 'μA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2102, 72, '毫安', 'mA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2103, 73, '千安', 'kA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2104, 74, '安培', 'A', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2105, 75, '毫伏', 'mV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2106, 76, '伏特', 'V', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2107, 77, '毫秒', 'ms', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2108, 78, '秒', 's', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2109, 79, '分钟', 'min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2110, 80, '小时', 'h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2111, 81, '日', 'day', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2112, 82, '周', 'week', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2113, 83, '月', 'month', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2114, 84, '年', 'year', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2115, 85, '节', 'kn', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2116, 86, '千米每小时', 'km/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2117, 87, '米每秒', 'm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2118, 88, '秒', '″', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2119, 89, '分', '′', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2120, 90, '度', '°', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2121, 91, '弧度', 'rad', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2122, 92, '赫兹', 'Hz', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2123, 93, '微瓦', 'μW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2124, 94, '毫瓦', 'mW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2125, 95, '千瓦特', 'kW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2126, 96, '瓦特', 'W', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2127, 97, '卡路里', 'cal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2128, 98, '千瓦时', 'kW·h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2129, 99, '瓦时', 'Wh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2130, 100, '电子伏', 'eV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2131, 101, '千焦', 'kJ', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2132, 102, '焦耳', 'J', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2133, 103, '华氏度', '℉', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2134, 104, '开尔文', 'K', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2135, 105, '吨', 't', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2136, 106, '摄氏度', '°C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2137, 107, '毫帕', 'mPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2138, 108, '百帕', 'hPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2139, 109, '千帕', 'kPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2140, 110, '帕斯卡', 'Pa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2141, 111, '毫克', 'mg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2142, 112, '克', 'g', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2143, 113, '千克', 'kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2144, 114, '牛', 'N', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2145, 115, '毫升', 'mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2146, 116, '升', 'L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2147, 117, '立方毫米', 'mm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2148, 118, '立方厘米', 'cm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2149, 119, '立方千米', 'km³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2150, 120, '立方米', 'm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2151, 121, '公顷', 'h㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2152, 122, '平方厘米', 'c㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2153, 123, '平方毫米', 'm㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2154, 124, '平方千米', 'k㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2155, 125, '平方米', '㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2156, 126, '纳米', 'nm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2157, 127, '微米', 'μm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2158, 128, '毫米', 'mm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2159, 129, '厘米', 'cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2163, 1, '输入', '1', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:24', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2164, 2, '输出', '2', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:36', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2165, 1, 'HTTP', '1', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:39:54', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2166, 2, 'TCP', '2', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:06', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2167, 3, 'WEBSOCKET', '3', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:24', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2168, 10, 'MQTT', '10', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:37', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2169, 20, 'DATABASE', '20', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:05', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2170, 21, 'REDIS_STREAM', '21', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:18', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2171, 30, 'ROCKETMQ', '30', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:30', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2172, 31, 'RABBITMQ', '31', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:47', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2173, 32, 'KAFKA', '32', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:59', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3000, 16, '百川智能', 'BaiChuan', 'ai_platform', 0, '', '', '', '1', '2025-03-23 12:15:46', '1', '2025-03-23 12:15:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3001, 50, 'Vben5.0 Ant Design Schema 模版', '40', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-04-23 21:47:47', '1', '2025-05-02 12:01:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3002, 6, '支付宝余额', '6', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2025-05-10 08:24:49', '1', '2025-05-10 08:24:49', '0');\nINSERT 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, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_dict_data_seq;\nCREATE SEQUENCE system_dict_data_seq\n    START 3003;\n\n-- ----------------------------\n-- Table structure for system_dict_type\n-- ----------------------------\nDROP TABLE IF EXISTS system_dict_type;\nCREATE TABLE system_dict_type\n(\n    id           int8         NOT NULL,\n    name         varchar(100) NULL     DEFAULT '',\n    type         varchar(100) NULL     DEFAULT '',\n    status       int2         NOT NULL DEFAULT 0,\n    remark       varchar(500) NULL     DEFAULT NULL,\n    creator      varchar(64)  NULL     DEFAULT '',\n    create_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater      varchar(64)  NULL     DEFAULT '',\n    update_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted      int2         NOT NULL DEFAULT 0,\n    deleted_time timestamp    NULL     DEFAULT NULL\n);\n\nALTER TABLE system_dict_type\n    ADD CONSTRAINT pk_system_dict_type PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dict_type.id IS '字典主键';\nCOMMENT ON COLUMN system_dict_type.name IS '字典名称';\nCOMMENT ON COLUMN system_dict_type.type IS '字典类型';\nCOMMENT ON COLUMN system_dict_type.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_type.remark IS '备注';\nCOMMENT ON COLUMN system_dict_type.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_type.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_type.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_type.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_type.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dict_type.deleted_time IS '删除时间';\nCOMMENT ON TABLE system_dict_type IS '字典类型表';\n\n-- ----------------------------\n-- Records of system_dict_type\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1, '用户性别', 'system_user_sex', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2022-05-16 20:29:32', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (6, '参数类型', 'infra_config_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:36:54', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (7, '通知类型', 'system_notice_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:35:26', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (9, '操作类型', 'infra_operate_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (10, '系统状态', 'common_status', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:21:28', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (11, 'Boolean 是否类型', 'infra_boolean_string', 0, 'boolean 转是否', '', '2021-01-19 03:20:08', '', '2022-02-01 16:37:10', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (104, '登陆结果', 'system_login_result', 0, '登陆结果', '', '2021-01-18 06:17:11', '', '2022-02-01 16:36:00', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (106, '代码生成模板类型', 'infra_codegen_template_type', 0, NULL, '', '2021-02-05 07:08:06', '1', '2022-05-16 20:26:50', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (107, '定时任务状态', 'infra_job_status', 0, NULL, '', '2021-02-07 07:44:16', '', '2022-02-01 16:51:11', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (108, '定时任务日志状态', 'infra_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2022-02-01 16:50:43', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (110, 'API 异常数据的处理状态', 'infra_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2022-02-01 16:50:53', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (111, '短信渠道编码', 'system_sms_channel_code', 0, NULL, '1', '2021-04-05 01:04:50', '1', '2022-02-16 02:09:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (112, '短信模板的类型', 'system_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:43', '1', '2022-02-01 16:35:06', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (113, '短信发送状态', 'system_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:03', '1', '2022-02-01 16:35:09', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (114, '短信接收状态', 'system_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:14', '1', '2022-02-01 16:35:14', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (116, '登陆日志的类型', 'system_login_type', 0, '登陆日志的类型', '1', '2021-10-06 00:50:46', '1', '2022-02-01 16:35:56', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (117, 'OA 请假类型', 'bpm_oa_leave_type', 0, NULL, '1', '2021-09-21 22:34:33', '1', '2022-01-22 10:41:37', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (130, '支付渠道编码类型', 'pay_channel_code', 0, '支付渠道的编码', '1', '2021-12-03 10:35:08', '1', '2023-07-10 10:11:39', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (131, '支付回调状态', 'pay_notify_status', 0, '支付回调状态（包括退款回调）', '1', '2021-12-03 10:53:29', '1', '2023-07-19 18:09:43', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (132, '支付订单状态', 'pay_order_status', 0, '支付订单状态', '1', '2021-12-03 11:17:50', '1', '2021-12-03 11:17:50', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (134, '退款订单状态', 'pay_refund_status', 0, '退款订单状态', '1', '2021-12-10 16:42:50', '1', '2023-07-19 10:13:17', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (139, '流程实例的状态', 'bpm_process_instance_status', 0, '流程实例的状态', '1', '2022-01-07 23:46:42', '1', '2022-01-07 23:46:42', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (140, '流程实例的结果', 'bpm_task_status', 0, '流程实例的结果', '1', '2022-01-07 23:48:10', '1', '2024-03-08 22:42:03', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (141, '流程的表单类型', 'bpm_model_form_type', 0, '流程的表单类型', '103', '2022-01-11 23:50:45', '103', '2022-01-11 23:50:45', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (142, '任务分配规则的类型', 'bpm_task_candidate_strategy', 0, 'BPM 任务的候选人的策略', '103', '2022-01-12 23:21:04', '103', '2024-03-06 02:53:59', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (144, '代码生成的场景枚举', 'infra_codegen_scene', 0, '代码生成的场景枚举', '1', '2022-02-02 13:14:45', '1', '2022-03-10 16:33:46', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (145, '角色类型', 'system_role_type', 0, '角色类型', '1', '2022-02-16 13:01:46', '1', '2022-02-16 13:01:46', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (146, '文件存储器', 'infra_file_storage', 0, '文件存储器', '1', '2022-03-15 00:24:38', '1', '2022-03-15 00:24:38', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (147, 'OAuth 2.0 授权类型', 'system_oauth2_grant_type', 0, 'OAuth 2.0 授权类型（模式）', '1', '2022-05-12 00:20:52', '1', '2022-05-11 16:25:49', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (149, '商品 SPU 状态', 'product_spu_status', 0, '商品 SPU 状态', '1', '2022-10-24 21:19:04', '1', '2022-10-24 21:19:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (150, '优惠类型', 'promotion_discount_type', 0, '优惠类型', '1', '2022-11-01 12:46:06', '1', '2022-11-01 12:46:06', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (151, '优惠劵模板的有限期类型', 'promotion_coupon_template_validity_type', 0, '优惠劵模板的有限期类型', '1', '2022-11-02 00:06:20', '1', '2022-11-04 00:08:26', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (152, '营销的商品范围', 'promotion_product_scope', 0, '营销的商品范围', '1', '2022-11-02 00:28:01', '1', '2022-11-02 00:28:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (153, '优惠劵的状态', 'promotion_coupon_status', 0, '优惠劵的状态', '1', '2022-11-04 00:14:49', '1', '2022-11-04 00:14:49', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (154, '优惠劵的领取方式', 'promotion_coupon_take_type', 0, '优惠劵的领取方式', '1', '2022-11-04 19:12:27', '1', '2022-11-04 19:12:27', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (155, '促销活动的状态', 'promotion_activity_status', 0, '促销活动的状态', '1', '2022-11-04 22:54:23', '1', '2022-11-04 22:54:23', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (156, '营销的条件类型', 'promotion_condition_type', 0, '营销的条件类型', '1', '2022-11-04 22:59:23', '1', '2022-11-04 22:59:23', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (157, '交易售后状态', 'trade_after_sale_status', 0, '交易售后状态', '1', '2022-11-19 20:52:56', '1', '2022-11-19 20:52:56', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (158, '交易售后的类型', 'trade_after_sale_type', 0, '交易售后的类型', '1', '2022-11-19 21:04:09', '1', '2022-11-19 21:04:09', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (159, '交易售后的方式', 'trade_after_sale_way', 0, '交易售后的方式', '1', '2022-11-19 21:39:04', '1', '2022-11-19 21:39:04', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (160, '终端', 'terminal', 0, '终端', '1', '2022-12-10 10:50:50', '1', '2022-12-10 10:53:11', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (161, '交易订单的类型', 'trade_order_type', 0, '交易订单的类型', '1', '2022-12-10 16:33:54', '1', '2022-12-10 16:33:54', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (162, '交易订单的状态', 'trade_order_status', 0, '交易订单的状态', '1', '2022-12-10 16:48:44', '1', '2022-12-10 16:48:44', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (163, '交易订单项的售后状态', 'trade_order_item_after_sale_status', 0, '交易订单项的售后状态', '1', '2022-12-10 20:58:08', '1', '2022-12-10 20:58:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (164, '公众号自动回复的请求关键字匹配模式', 'mp_auto_reply_request_match', 0, '公众号自动回复的请求关键字匹配模式', '1', '2023-01-16 23:29:56', '1', '2023-01-16 23:29:56', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (165, '公众号的消息类型', 'mp_message_type', 0, '公众号的消息类型', '1', '2023-01-17 22:17:09', '1', '2023-01-17 22:17:09', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (166, '邮件发送状态', 'system_mail_send_status', 0, '邮件发送状态', '1', '2023-01-26 09:53:13', '1', '2023-01-26 09:53:13', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (167, '站内信模版的类型', 'system_notify_template_type', 0, '站内信模版的类型', '1', '2023-01-28 10:35:10', '1', '2023-01-28 10:35:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (168, '代码生成的前端类型', 'infra_codegen_front_type', 0, '', '1', '2023-04-12 23:57:52', '1', '2023-04-12 23:57:52', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (170, '快递计费方式', 'trade_delivery_express_charge_mode', 0, '用于商城交易模块配送管理', '1', '2023-05-21 22:45:03', '1', '2023-05-21 22:45:03', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (171, '积分业务类型', 'member_point_biz_type', 0, '', '1', '2023-06-10 12:15:00', '1', '2023-06-28 13:48:20', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (173, '支付通知类型', 'pay_notify_type', 0, NULL, '1', '2023-07-20 12:23:03', '1', '2023-07-20 12:23:03', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (174, '会员经验业务类型', 'member_experience_biz_type', 0, NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (175, '交易配送类型', 'trade_delivery_type', 0, '', '1', '2023-08-23 00:03:14', '1', '2023-08-23 00:03:14', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (176, '分佣模式', 'brokerage_enabled_condition', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (177, '分销关系绑定模式', 'brokerage_bind_mode', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (178, '佣金提现类型', 'brokerage_withdraw_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (179, '佣金记录业务类型', 'brokerage_record_biz_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (180, '佣金记录状态', 'brokerage_record_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (181, '佣金提现状态', 'brokerage_withdraw_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (182, '佣金提现银行', 'brokerage_bank_name', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (183, '砍价记录的状态', 'promotion_bargain_record_status', 0, '', '1', '2023-10-05 10:41:08', '1', '2023-10-05 10:41:08', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (184, '拼团记录的状态', 'promotion_combination_record_status', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-10-08 07:24:25', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (185, '回款-回款方式', 'crm_receivable_return_type', 0, '回款-回款方式', '1', '2023-10-18 21:54:10', '1', '2023-10-18 21:54:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (186, 'CRM 客户行业', 'crm_customer_industry', 0, 'CRM 客户所属行业', '1', '2023-10-28 22:57:07', '1', '2024-02-18 23:30:22', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (187, '客户等级', 'crm_customer_level', 0, 'CRM 客户等级', '1', '2023-10-28 22:59:12', '1', '2023-10-28 15:11:16', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (188, '客户来源', 'crm_customer_source', 0, 'CRM 客户来源', '1', '2023-10-28 23:00:34', '1', '2023-10-28 15:11:16', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (600, 'Banner 位置', 'promotion_banner_position', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-11-04 13:04:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (601, '社交类型', 'system_social_type', 0, '', '1', '2023-11-04 13:03:54', '1', '2023-11-04 13:03:54', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (604, '产品状态', 'crm_product_status', 0, '', '1', '2023-10-30 21:47:59', '1', '2023-10-30 21:48:45', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (605, 'CRM 数据权限的级别', 'crm_permission_level', 0, '', '1', '2023-11-30 09:51:59', '1', '2023-11-30 09:51:59', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (606, 'CRM 审批状态', 'crm_audit_status', 0, '', '1', '2023-11-30 18:56:23', '1', '2023-11-30 18:56:23', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (607, 'CRM 产品单位', 'crm_product_unit', 0, '', '1', '2023-12-05 23:01:51', '1', '2023-12-05 23:01:51', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (608, 'CRM 跟进方式', 'crm_follow_up_type', 0, '', '1', '2024-01-15 20:48:05', '1', '2024-01-15 20:48:05', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (610, '转账订单状态', 'pay_transfer_status', 0, '', '1', '2023-10-28 16:18:32', '1', '2023-10-28 16:18:32', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (611, 'ERP 库存明细的业务类型', 'erp_stock_record_biz_type', 0, 'ERP 库存明细的业务类型', '1', '2024-02-05 18:07:02', '1', '2024-02-05 18:07:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (612, 'ERP 审批状态', 'erp_audit_status', 0, '', '1', '2024-02-06 00:00:07', '1', '2024-02-06 00:00:07', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (613, 'BPM 监听器类型', 'bpm_process_listener_type', 0, '', '1', '2024-03-23 12:52:24', '1', '2024-03-09 15:54:28', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (615, 'BPM 监听器值类型', 'bpm_process_listener_value_type', 0, '', '1', '2024-03-23 13:00:31', '1', '2024-03-23 13:00:31', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (616, '时间间隔', 'date_interval', 0, '', '1', '2024-03-29 22:50:09', '1', '2024-03-29 22:50:09', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (619, 'CRM 商机结束状态类型', 'crm_business_end_status_type', 0, '', '1', '2024-04-13 23:23:00', '1', '2024-04-13 23:23:00', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (620, 'AI 模型平台', 'ai_platform', 0, '', '1', '2024-05-09 22:27:38', '1', '2024-05-09 22:27:38', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (621, 'AI 绘画状态', 'ai_image_status', 0, '', '1', '2024-06-26 20:51:23', '1', '2024-06-26 20:51:23', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (622, 'AI 音乐状态', 'ai_music_status', 0, '', '1', '2024-06-27 22:45:07', '1', '2024-06-28 00:56:27', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (623, 'AI 音乐生成模式', 'ai_generate_mode', 0, '', '1', '2024-06-27 22:46:21', '1', '2024-06-28 01:22:29', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (624, '写作语气', 'ai_write_tone', 0, '', '1', '2024-07-07 15:19:02', '1', '2024-07-07 15:19:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (625, '写作语言', 'ai_write_language', 0, '', '1', '2024-07-07 15:18:52', '1', '2024-07-07 15:18:52', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (626, '写作长度', 'ai_write_length', 0, '', '1', '2024-07-07 15:18:41', '1', '2024-07-07 15:18:41', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (627, '写作格式', 'ai_write_format', 0, '', '1', '2024-07-07 15:14:34', '1', '2024-07-07 15:14:34', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1000, 'IoT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2025-03-17 09:25:06', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1001, 'IoT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2025-03-17 09:25:08', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1002, 'IoT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2025-03-17 09:25:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1003, 'IoT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2025-03-17 09:25:12', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1004, 'IoT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2025-03-17 09:25:14', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1005, 'IoT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2025-03-17 09:25:16', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1006, 'IoT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-03-17 09:25:19', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1007, 'IoT 物模型功能类型', 'iot_thing_model_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2025-03-17 09:25:24', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1008, 'IoT 插件部署方式', 'iot_plugin_deploy_type', 0, '', '1', '2024-12-13 10:55:13', '1', '2025-03-17 09:25:27', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1009, 'IoT 插件状态', 'iot_plugin_status', 0, '', '1', '2024-12-13 11:05:34', '1', '2025-03-17 09:25:30', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1010, 'IoT 插件类型', 'iot_plugin_type', 0, '', '1', '2024-12-13 11:08:19', '1', '2025-03-17 09:25:32', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1011, 'IoT 物模型单位', 'iot_thing_model_unit', 0, '', '1', '2024-12-25 17:36:46', '1', '2025-03-17 09:25:35', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1012, 'IoT 数据桥接的方向枚举', 'iot_data_bridge_direction_enum', 0, '', '1', '2025-03-09 12:37:40', '1', '2025-03-17 09:25:39', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1013, 'IoT 数据桥梁的类型枚举', 'iot_data_bridge_type_enum', 0, '', '1', '2025-03-09 12:39:36', '1', '2025-04-06 17:09:46', '0', '1970-01-01 00:00:00');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_dict_type_seq;\nCREATE SEQUENCE system_dict_type_seq\n    START 1014;\n\n-- ----------------------------\n-- Table structure for system_login_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_login_log;\nCREATE TABLE system_login_log\n(\n    id          int8         NOT NULL,\n    log_type    int8         NOT NULL,\n    trace_id    varchar(64)  NULL     DEFAULT '',\n    user_id     int8         NOT NULL DEFAULT 0,\n    user_type   int2         NOT NULL DEFAULT 0,\n    username    varchar(50)  NULL     DEFAULT '',\n    result      int2         NOT NULL,\n    user_ip     varchar(50)  NOT NULL,\n    user_agent  varchar(512) NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_login_log\n    ADD CONSTRAINT pk_system_login_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_login_log.id IS '访问ID';\nCOMMENT ON COLUMN system_login_log.log_type IS '日志类型';\nCOMMENT ON COLUMN system_login_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_login_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_login_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_login_log.username IS '用户账号';\nCOMMENT ON COLUMN system_login_log.result IS '登陆结果';\nCOMMENT ON COLUMN system_login_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_login_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_login_log.creator IS '创建者';\nCOMMENT ON COLUMN system_login_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_login_log.updater IS '更新者';\nCOMMENT ON COLUMN system_login_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_login_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_login_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_login_log IS '系统访问记录';\n\nDROP SEQUENCE IF EXISTS system_login_log_seq;\nCREATE SEQUENCE system_login_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_mail_account\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_account;\nCREATE TABLE system_mail_account\n(\n    id              int8         NOT NULL,\n    mail            varchar(255) NOT NULL,\n    username        varchar(255) NOT NULL,\n    password        varchar(255) NOT NULL,\n    host            varchar(255) NOT NULL,\n    port            int4         NOT NULL,\n    ssl_enable      bool         NOT NULL DEFAULT '0',\n    starttls_enable bool         NOT NULL DEFAULT '0',\n    creator         varchar(64)  NULL     DEFAULT '',\n    create_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater         varchar(64)  NULL     DEFAULT '',\n    update_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted         int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_mail_account\n    ADD CONSTRAINT pk_system_mail_account PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_account.id IS '主键';\nCOMMENT ON COLUMN system_mail_account.mail IS '邮箱';\nCOMMENT ON COLUMN system_mail_account.username IS '用户名';\nCOMMENT ON COLUMN system_mail_account.password IS '密码';\nCOMMENT ON COLUMN system_mail_account.host IS 'SMTP 服务器域名';\nCOMMENT ON COLUMN system_mail_account.port IS 'SMTP 服务器端口';\nCOMMENT ON COLUMN system_mail_account.ssl_enable IS '是否开启 SSL';\nCOMMENT ON COLUMN system_mail_account.starttls_enable IS '是否开启 STARTTLS';\nCOMMENT ON COLUMN system_mail_account.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_account.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_account.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_account.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_account.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_account IS '邮箱账号表';\n\n-- ----------------------------\n-- Records of system_mail_account\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, '0', '0', '1', '2023-01-25 17:39:52', '1', '2025-04-04 16:34:40', '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, '1', '0', '1', '2023-01-26 01:26:03', '1', '2023-04-12 22:39:38', '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, '0', '0', '1', '2023-01-27 15:06:38', '1', '2023-01-27 07:08:36', '1');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (4, '7685413x@qq.com', '2', '3', '4', 5, '1', '0', '1', '2023-04-12 23:05:06', '1', '2023-04-12 15:05:11', '1');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_mail_account_seq;\nCREATE SEQUENCE system_mail_account_seq\n    START 5;\n\n-- ----------------------------\n-- Table structure for system_mail_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_log;\nCREATE TABLE system_mail_log\n(\n    id                int8           NOT NULL,\n    user_id           int8           NULL     DEFAULT NULL,\n    user_type         int2           NULL     DEFAULT NULL,\n    to_mail           varchar(255)   NOT NULL,\n    account_id        int8           NOT NULL,\n    from_mail         varchar(255)   NOT NULL,\n    template_id       int8           NOT NULL,\n    template_code     varchar(63)    NOT NULL,\n    template_nickname varchar(255)   NULL     DEFAULT NULL,\n    template_title    varchar(255)   NOT NULL,\n    template_content  varchar(10240) NOT NULL,\n    template_params   varchar(255)   NOT NULL,\n    send_status       int2           NOT NULL DEFAULT 0,\n    send_time         timestamp      NULL     DEFAULT NULL,\n    send_message_id   varchar(255)   NULL     DEFAULT NULL,\n    send_exception    varchar(4096)  NULL     DEFAULT NULL,\n    creator           varchar(64)    NULL     DEFAULT '',\n    create_time       timestamp      NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater           varchar(64)    NULL     DEFAULT '',\n    update_time       timestamp      NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted           int2           NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_mail_log\n    ADD CONSTRAINT pk_system_mail_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_log.id IS '编号';\nCOMMENT ON COLUMN system_mail_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_mail_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_mail_log.to_mail IS '接收邮箱地址';\nCOMMENT ON COLUMN system_mail_log.account_id IS '邮箱账号编号';\nCOMMENT ON COLUMN system_mail_log.from_mail IS '发送邮箱地址';\nCOMMENT ON COLUMN system_mail_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_mail_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_mail_log.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_mail_log.template_title IS '邮件标题';\nCOMMENT ON COLUMN system_mail_log.template_content IS '邮件内容';\nCOMMENT ON COLUMN system_mail_log.template_params IS '邮件参数';\nCOMMENT ON COLUMN system_mail_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_mail_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_mail_log.send_message_id IS '发送返回的消息 ID';\nCOMMENT ON COLUMN system_mail_log.send_exception IS '发送异常';\nCOMMENT ON COLUMN system_mail_log.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_log.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_log IS '邮件日志表';\n\nDROP SEQUENCE IF EXISTS system_mail_log_seq;\nCREATE SEQUENCE system_mail_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_mail_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_template;\nCREATE TABLE system_mail_template\n(\n    id          int8           NOT NULL,\n    name        varchar(63)    NOT NULL,\n    code        varchar(63)    NOT NULL,\n    account_id  int8           NOT NULL,\n    nickname    varchar(255)   NULL     DEFAULT NULL,\n    title       varchar(255)   NOT NULL,\n    content     varchar(10240) NOT NULL,\n    params      varchar(255)   NOT NULL,\n    status      int2           NOT NULL,\n    remark      varchar(255)   NULL     DEFAULT NULL,\n    creator     varchar(64)    NULL     DEFAULT '',\n    create_time timestamp      NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)    NULL     DEFAULT '',\n    update_time timestamp      NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2           NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_mail_template\n    ADD CONSTRAINT pk_system_mail_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_template.id IS '编号';\nCOMMENT ON COLUMN system_mail_template.name IS '模板名称';\nCOMMENT ON COLUMN system_mail_template.code IS '模板编码';\nCOMMENT ON COLUMN system_mail_template.account_id IS '发送的邮箱账号编号';\nCOMMENT ON COLUMN system_mail_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_mail_template.title IS '模板标题';\nCOMMENT ON COLUMN system_mail_template.content IS '模板内容';\nCOMMENT ON COLUMN system_mail_template.params IS '参数数组';\nCOMMENT ON COLUMN system_mail_template.status IS '开启状态';\nCOMMENT ON COLUMN system_mail_template.remark IS '备注';\nCOMMENT ON COLUMN system_mail_template.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_template.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_template IS '邮件模版表';\n\n-- ----------------------------\n-- Records of system_mail_template\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '后台用户短信登录', 'admin-sms-login', 1, '奥特曼', '你猜我猜', '<p>您的验证码是{code}，名字是{name}</p>', '[\"code\",\"name\"]', 0, '3', '1', '2021-10-11 08:10:00', '1', '2023-12-02 19:51:14', '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (14, '测试模版', 'test_01', 2, '芋艿', '一个标题', '<p>你是 {key01} 吗？</p><p><br></p><p>是的话，赶紧 {key02} 一下！</p>', '[\"key01\",\"key02\"]', 0, NULL, '1', '2023-01-26 01:27:40', '1', '2023-01-27 10:32:16', '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (15, '3', '2', 2, '7', '4', '<p>45</p>', '[]', 1, '80', '1', '2023-01-27 15:50:35', '1', '2023-01-27 16:34:49', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_mail_template_seq;\nCREATE SEQUENCE system_mail_template_seq\n    START 16;\n\n-- ----------------------------\n-- Table structure for system_menu\n-- ----------------------------\nDROP TABLE IF EXISTS system_menu;\nCREATE TABLE system_menu\n(\n    id             int8         NOT NULL,\n    name           varchar(50)  NOT NULL,\n    permission     varchar(100) NULL     DEFAULT '',\n    type           int2         NOT NULL,\n    sort           int4         NOT NULL DEFAULT 0,\n    parent_id      int8         NOT NULL DEFAULT 0,\n    path           varchar(200) NULL     DEFAULT '',\n    icon           varchar(100) NULL     DEFAULT '#',\n    component      varchar(255) NULL     DEFAULT NULL,\n    component_name varchar(255) NULL     DEFAULT NULL,\n    status         int2         NOT NULL DEFAULT 0,\n    visible        bool         NOT NULL DEFAULT '1',\n    keep_alive     bool         NOT NULL DEFAULT '1',\n    always_show    bool         NOT NULL DEFAULT '1',\n    creator        varchar(64)  NULL     DEFAULT '',\n    create_time    timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64)  NULL     DEFAULT '',\n    update_time    timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_menu\n    ADD CONSTRAINT pk_system_menu PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_menu.id IS '菜单ID';\nCOMMENT ON COLUMN system_menu.name IS '菜单名称';\nCOMMENT ON COLUMN system_menu.permission IS '权限标识';\nCOMMENT ON COLUMN system_menu.type IS '菜单类型';\nCOMMENT ON COLUMN system_menu.sort IS '显示顺序';\nCOMMENT ON COLUMN system_menu.parent_id IS '父菜单ID';\nCOMMENT ON COLUMN system_menu.path IS '路由地址';\nCOMMENT ON COLUMN system_menu.icon IS '菜单图标';\nCOMMENT ON COLUMN system_menu.component IS '组件路径';\nCOMMENT ON COLUMN system_menu.component_name IS '组件名';\nCOMMENT ON COLUMN system_menu.status IS '菜单状态';\nCOMMENT ON COLUMN system_menu.visible IS '是否可见';\nCOMMENT ON COLUMN system_menu.keep_alive IS '是否缓存';\nCOMMENT ON COLUMN system_menu.always_show IS '是否总是显示';\nCOMMENT ON COLUMN system_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_menu.deleted IS '是否删除';\nCOMMENT ON TABLE system_menu IS '菜单权限表';\n\n-- ----------------------------\n-- Records of system_menu\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-03-01 08:28:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5, 'OA 示例', '', 1, 40, 1185, 'oa', 'fa:road', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-09-20 16:26:19', '1', '2024-02-29 12:38:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (101, '角色管理', '', 2, 2, 1, 'role', 'ep:user', 'system/role/index', 'SystemRole', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-05-01 18:35:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (102, '菜单管理', '', 2, 3, 1, 'menu', 'ep:menu', 'system/menu/index', 'SystemMenu', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:03:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (103, '部门管理', '', 2, 4, 1, 'dept', 'fa:address-card', 'system/dept/index', 'SystemDept', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (104, '岗位管理', '', 2, 5, 1, 'post', 'fa:address-book-o', 'system/post/index', 'SystemPost', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (105, '字典管理', '', 2, 6, 1, 'dict', 'ep:collection', 'system/dict/index', 'SystemDictType', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:07:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (106, '配置管理', '', 2, 8, 2, 'config', 'fa:connectdevelop', 'infra/config/index', 'InfraConfig', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:02:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (107, '通知公告', '', 2, 4, 2739, 'notice', 'ep:takeaway-box', 'system/notice/index', 'SystemNotice', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-22 23:56:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (108, '审计日志', '', 1, 9, 1, 'log', 'ep:document-copy', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:08:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (109, '令牌管理', '', 2, 2, 1261, 'token', 'fa:key', 'system/oauth2/token/index', 'SystemTokenClient', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:13:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (110, '定时任务', '', 2, 7, 2, 'job', 'fa-solid:tasks', 'infra/job/index', 'InfraJob', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:57:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (111, 'MySQL 监控', '', 2, 1, 2740, 'druid', 'fa-solid:box', 'infra/druid/index', 'InfraDruid', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:05:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (112, 'Java 监控', '', 2, 3, 2740, 'admin-server', 'ep:coffee-cup', 'infra/server/index', 'InfraAdminServer', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (113, 'Redis 监控', '', 2, 2, 2740, 'redis', 'fa:reddit-square', 'infra/redis/index', 'InfraRedis', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (114, '表单构建', 'infra:build:list', 2, 2, 2, 'build', 'fa:wpforms', 'infra/build/index', 'InfraBuild', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (115, '代码生成', 'infra:codegen:query', 2, 1, 2, 'codegen', 'ep:document-copy', 'infra/codegen/index', 'InfraCodegen', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (116, 'API 接口', 'infra:swagger:list', 2, 3, 2, 'swagger', 'fa:fighter-jet', 'infra/swagger/index', 'InfraSwagger', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:01:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (500, '操作日志', '', 2, 1, 108, 'operate-log', 'ep:position', 'system/operatelog/index', 'SystemOperateLog', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:09:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (501, '登录日志', '', 2, 2, 108, 'login-log', 'ep:promotion', 'system/loginlog/index', 'SystemLoginLog', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:10:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1001, '用户查询', 'system:user:query', 3, 1, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1002, '用户新增', 'system:user:create', 3, 2, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1003, '用户修改', 'system:user:update', 3, 3, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1004, '用户删除', 'system:user:delete', 3, 4, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1005, '用户导出', 'system:user:export', 3, 5, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1006, '用户导入', 'system:user:import', 3, 6, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1007, '重置密码', 'system:user:update-password', 3, 7, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1008, '角色查询', 'system:role:query', 3, 1, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1009, '角色新增', 'system:role:create', 3, 2, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1010, '角色修改', 'system:role:update', 3, 3, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1011, '角色删除', 'system:role:delete', 3, 4, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1012, '角色导出', 'system:role:export', 3, 5, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1013, '菜单查询', 'system:menu:query', 3, 1, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1014, '菜单新增', 'system:menu:create', 3, 2, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1015, '菜单修改', 'system:menu:update', 3, 3, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1016, '菜单删除', 'system:menu:delete', 3, 4, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1017, '部门查询', 'system:dept:query', 3, 1, 103, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1018, '部门新增', 'system:dept:create', 3, 2, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1019, '部门修改', 'system:dept:update', 3, 3, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1020, '部门删除', 'system:dept:delete', 3, 4, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1021, '岗位查询', 'system:post:query', 3, 1, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1022, '岗位新增', 'system:post:create', 3, 2, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1023, '岗位修改', 'system:post:update', 3, 3, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1024, '岗位删除', 'system:post:delete', 3, 4, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1025, '岗位导出', 'system:post:export', 3, 5, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1026, '字典查询', 'system:dict:query', 3, 1, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1027, '字典新增', 'system:dict:create', 3, 2, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1028, '字典修改', 'system:dict:update', 3, 3, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1029, '字典删除', 'system:dict:delete', 3, 4, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1030, '字典导出', 'system:dict:export', 3, 5, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1031, '配置查询', 'infra:config:query', 3, 1, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1032, '配置新增', 'infra:config:create', 3, 2, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1033, '配置修改', 'infra:config:update', 3, 3, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1034, '配置删除', 'infra:config:delete', 3, 4, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1035, '配置导出', 'infra:config:export', 3, 5, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1036, '公告查询', 'system:notice:query', 3, 1, 107, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1037, '公告新增', 'system:notice:create', 3, 2, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1038, '公告修改', 'system:notice:update', 3, 3, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1039, '公告删除', 'system:notice:delete', 3, 4, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1040, '操作查询', 'system:operate-log:query', 3, 1, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1042, '日志导出', 'system:operate-log:export', 3, 2, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1043, '登录查询', 'system:login-log:query', 3, 1, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1045, '日志导出', 'system:login-log:export', 3, 3, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1046, '令牌列表', 'system:oauth2-token:page', 3, 1, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1048, '令牌删除', 'system:oauth2-token:delete', 3, 2, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1050, '任务新增', 'infra:job:create', 3, 2, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1051, '任务修改', 'infra:job:update', 3, 3, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1052, '任务删除', 'infra:job:delete', 3, 4, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1053, '状态修改', 'infra:job:update', 3, 5, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1054, '任务导出', 'infra:job:export', 3, 7, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1056, '生成修改', 'infra:codegen:update', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1057, '生成删除', 'infra:codegen:delete', 3, 3, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1058, '导入代码', 'infra:codegen:create', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1059, '预览代码', 'infra:codegen:preview', 3, 4, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1060, '生成代码', 'infra:codegen:download', 3, 5, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1063, '设置角色菜单权限', 'system:permission:assign-role-menu', 3, 6, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-06 17:53:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1064, '设置角色数据权限', 'system:permission:assign-role-data-scope', 3, 7, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-06 17:56:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1065, '设置用户角色', 'system:permission:assign-user-role', 3, 8, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-07 10:23:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1066, '获得 Redis 监控信息', 'infra:redis:get-monitor-info', 3, 1, 113, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-26 01:02:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1067, '获得 Redis Key 列表', 'infra:redis:get-key-list', 3, 2, 113, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-26 01:02:52', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1070, '代码生成案例', '', 1, 1, 2, 'demo', 'ep:aim', 'infra/testDemo/index', NULL, 0, '1', '1', '1', '', '2021-02-06 12:42:49', '1', '2023-11-15 23:45:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1075, '任务触发', 'infra:job:trigger', 3, 8, 110, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-07 13:03:10', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1077, '链路追踪', '', 2, 4, 2740, 'skywalking', 'fa:eye', 'infra/skywalking/index', 'InfraSkyWalking', 0, '1', '1', '1', '', '2021-02-08 20:41:31', '1', '2024-04-23 00:07:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1078, '访问日志', '', 2, 1, 1083, 'api-access-log', 'ep:place', 'infra/apiAccessLog/index', 'InfraApiAccessLog', 0, '1', '1', '1', '', '2021-02-26 01:32:59', '1', '2024-02-29 08:54:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1082, '日志导出', 'infra:api-access-log:export', 3, 2, 1078, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 01:32:59', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1083, 'API 日志', '', 2, 4, 2, 'log', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '', '2021-02-26 02:18:24', '1', '2024-04-22 23:58:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1084, '错误日志', 'infra:api-error-log:query', 2, 2, 1083, 'api-error-log', 'ep:warning-filled', 'infra/apiErrorLog/index', 'InfraApiErrorLog', 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2024-02-29 08:55:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1085, '日志处理', 'infra:api-error-log:update-status', 3, 2, 1084, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1086, '日志导出', 'infra:api-error-log:export', 3, 3, 1084, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1087, '任务查询', 'infra:job:query', 3, 1, 110, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:26:19', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1088, '日志查询', 'infra:api-access-log:query', 3, 1, 1078, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:28:04', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1089, '日志查询', 'infra:api-error-log:query', 3, 1, 1084, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:29:09', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1090, '文件列表', '', 2, 5, 1243, 'file', 'ep:upload-filled', 'infra/file/index', 'InfraFile', 0, '1', '1', '1', '', '2021-03-12 20:16:20', '1', '2024-02-29 08:53:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', NULL, 0, '1', '1', '1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1092, '文件删除', 'infra:file:delete', 3, 4, 1090, '', '', '', NULL, 0, '1', '1', '1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1093, '短信管理', '', 1, 1, 2739, 'sms', 'ep:message', NULL, NULL, 0, '1', '1', '1', '1', '2021-04-05 01:10:16', '1', '2024-04-22 23:56:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1094, '短信渠道', '', 2, 0, 1093, 'sms-channel', 'fa:stack-exchange', 'system/sms/channel/index', 'SystemSmsChannel', 0, '1', '1', '1', '', '2021-04-01 11:07:15', '1', '2024-02-29 01:15:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1095, '短信渠道查询', 'system:sms-channel:query', 3, 1, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1096, '短信渠道创建', 'system:sms-channel:create', 3, 2, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1097, '短信渠道更新', 'system:sms-channel:update', 3, 3, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1098, '短信渠道删除', 'system:sms-channel:delete', 3, 4, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1100, '短信模板', '', 2, 1, 1093, 'sms-template', 'ep:connection', 'system/sms/template/index', 'SystemSmsTemplate', 0, '1', '1', '1', '', '2021-04-01 17:35:17', '1', '2024-02-29 01:16:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1101, '短信模板查询', 'system:sms-template:query', 3, 1, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1102, '短信模板创建', 'system:sms-template:create', 3, 2, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1103, '短信模板更新', 'system:sms-template:update', 3, 3, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1104, '短信模板删除', 'system:sms-template:delete', 3, 4, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1105, '短信模板导出', 'system:sms-template:export', 3, 5, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1106, '发送测试短信', 'system:sms-template:send-sms', 3, 6, 1100, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-04-11 00:26:40', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1107, '短信日志', '', 2, 2, 1093, 'sms-log', 'fa:edit', 'system/sms/log/index', 'SystemSmsLog', 0, '1', '1', '1', '', '2021-04-11 08:37:05', '1', '2024-02-29 08:49:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1108, '短信日志查询', 'system:sms-log:query', 3, 1, 1107, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1109, '短信日志导出', 'system:sms-log:export', 3, 5, 1107, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1117, '支付管理', '', 1, 30, 0, '/pay', 'ep:money', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-25 16:43:41', '1', '2024-02-29 08:58:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1118, '请假查询', '', 2, 0, 5, 'leave', 'fa:leanpub', 'bpm/oa/leave/index', 'BpmOALeave', 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2024-02-29 12:38:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1119, '请假申请查询', 'bpm:oa-leave:query', 3, 1, 1118, '', '', '', NULL, 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1120, '请假申请创建', 'bpm:oa-leave:create', 3, 2, 1118, '', '', '', NULL, 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1126, '应用信息', '', 2, 1, 1117, 'app', 'fa:apple', 'pay/app/index', 'PayApp', 0, '1', '1', '1', '', '2021-11-10 01:13:30', '1', '2024-02-29 08:59:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1127, '支付应用信息查询', 'pay:app:query', 3, 1, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1128, '支付应用信息创建', 'pay:app:create', 3, 2, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1129, '支付应用信息更新', 'pay:app:update', 3, 3, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1130, '支付应用信息删除', 'pay:app:delete', 3, 4, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1132, '秘钥解析', 'pay:channel:parsing', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1133, '支付商户信息查询', 'pay:merchant:query', 3, 1, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1134, '支付商户信息创建', 'pay:merchant:create', 3, 2, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1135, '支付商户信息更新', 'pay:merchant:update', 3, 3, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1136, '支付商户信息删除', 'pay:merchant:delete', 3, 4, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1137, '支付商户信息导出', 'pay:merchant:export', 3, 5, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1138, '租户列表', '', 2, 0, 1224, 'list', 'ep:house', 'system/tenant/index', 'SystemTenant', 0, '1', '1', '1', '', '2021-12-14 12:31:43', '1', '2024-02-29 01:01:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1139, '租户查询', 'system:tenant:query', 3, 1, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1140, '租户创建', 'system:tenant:create', 3, 2, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1141, '租户更新', 'system:tenant:update', 3, 3, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1142, '租户删除', 'system:tenant:delete', 3, 4, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1143, '租户导出', 'system:tenant:export', 3, 5, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1150, '秘钥解析', '', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1161, '退款订单', '', 2, 3, 1117, 'refund', 'fa:registered', 'pay/refund/index', 'PayRefund', 0, '1', '1', '1', '', '2021-12-25 08:29:07', '1', '2024-02-29 08:59:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1162, '退款订单查询', 'pay:refund:query', 3, 1, 1161, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1166, '退款订单导出', 'pay:refund:export', 3, 5, 1161, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1173, '支付订单', '', 2, 2, 1117, 'order', 'fa:cc-paypal', 'pay/order/index', 'PayOrder', 0, '1', '1', '1', '', '2021-12-25 08:49:43', '1', '2024-02-29 08:59:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1174, '支付订单查询', 'pay:order:query', 3, 1, 1173, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1178, '支付订单导出', 'pay:order:export', 3, 5, 1173, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1185, '工作流程', '', 1, 50, 0, '/bpm', 'fa:medium', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-30 20:26:36', '1', '2024-02-29 12:43:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1186, '流程管理', '', 1, 10, 1185, 'manager', 'fa:dedent', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-30 20:28:30', '1', '2024-02-29 12:36:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1187, '流程表单', '', 2, 2, 1186, 'form', 'fa:hdd-o', 'bpm/form/index', 'BpmForm', 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2024-03-19 12:25:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1188, '表单查询', 'bpm:form:query', 3, 1, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1189, '表单创建', 'bpm:form:create', 3, 2, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1190, '表单更新', 'bpm:form:update', 3, 3, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1191, '表单删除', 'bpm:form:delete', 3, 4, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1192, '表单导出', 'bpm:form:export', 3, 5, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1193, '流程模型', '', 2, 1, 1186, 'model', 'fa-solid:project-diagram', 'bpm/model/index', 'BpmModel', 0, '1', '1', '1', '1', '2021-12-31 23:24:58', '1', '2024-03-19 12:25:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1194, '模型查询', 'bpm:model:query', 3, 1, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:01:10', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1195, '模型创建', 'bpm:model:create', 3, 2, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:01:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1197, '模型更新', 'bpm:model:update', 3, 4, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:02:28', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1198, '模型删除', 'bpm:model:delete', 3, 5, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:02:43', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1199, '模型发布', 'bpm:model:deploy', 3, 6, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:03:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1200, '审批中心', '', 2, 20, 1185, 'task', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '1', '2022-01-07 23:51:48', '1', '2024-03-21 00:33:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1201, '我的流程', '', 2, 1, 1200, 'my', 'fa-solid:book', 'bpm/processInstance/index', 'BpmProcessInstanceMy', 0, '1', '1', '1', '', '2022-01-07 15:53:44', '1', '2024-03-21 23:52:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1202, '流程实例的查询', 'bpm:process-instance:query', 3, 1, 1201, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-07 15:53:44', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1207, '待办任务', '', 2, 10, 1200, 'todo', 'fa:slack', 'bpm/task/todo/index', 'BpmTodoTask', 0, '1', '1', '1', '1', '2022-01-08 10:33:37', '1', '2024-02-29 12:37:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1208, '已办任务', '', 2, 20, 1200, 'done', 'fa:delicious', 'bpm/task/done/index', 'BpmDoneTask', 0, '1', '1', '1', '1', '2022-01-08 10:34:13', '1', '2024-02-29 12:37:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1209, '用户分组', '', 2, 4, 1186, 'user-group', 'fa:user-secret', 'bpm/group/index', 'BpmUserGroup', 0, '1', '1', '1', '', '2022-01-14 02:14:20', '1', '2024-03-21 23:55:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1210, '用户组查询', 'bpm:user-group:query', 3, 1, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1211, '用户组创建', 'bpm:user-group:create', 3, 2, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1212, '用户组更新', 'bpm:user-group:update', 3, 3, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1213, '用户组删除', 'bpm:user-group:delete', 3, 4, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1215, '流程定义查询', 'bpm:process-definition:query', 3, 10, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:21:43', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1216, '流程任务分配规则查询', 'bpm:task-assign-rule:query', 3, 20, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:26:53', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1217, '流程任务分配规则创建', 'bpm:task-assign-rule:create', 3, 21, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:28:15', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1218, '流程任务分配规则更新', 'bpm:task-assign-rule:update', 3, 22, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:28:41', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1219, '流程实例的创建', 'bpm:process-instance:create', 3, 2, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:36:15', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1220, '流程实例的取消', 'bpm:process-instance:cancel', 3, 3, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:36:33', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1221, '流程任务的查询', 'bpm:task:query', 3, 1, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:38:52', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1222, '流程任务的更新', 'bpm:task:update', 3, 2, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:39:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1224, '租户管理', '', 2, 0, 1, 'tenant', 'fa-solid:house-user', NULL, NULL, 0, '1', '1', '1', '1', '2022-02-20 01:41:13', '1', '2024-02-29 00:59:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1225, '租户套餐', '', 2, 0, 1224, 'package', 'fa:bars', 'system/tenantPackage/index', 'SystemTenantPackage', 0, '1', '1', '1', '', '2022-02-19 17:44:06', '1', '2024-02-29 01:01:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1226, '租户套餐查询', 'system:tenant-package:query', 3, 1, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1227, '租户套餐创建', 'system:tenant-package:create', 3, 2, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1228, '租户套餐更新', 'system:tenant-package:update', 3, 3, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1229, '租户套餐删除', 'system:tenant-package:delete', 3, 4, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1237, '文件配置', '', 2, 0, 1243, 'file-config', 'fa-solid:file-signature', 'infra/fileConfig/index', 'InfraFileConfig', 0, '1', '1', '1', '', '2022-03-15 14:35:28', '1', '2024-02-29 08:52:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1238, '文件配置查询', 'infra:file-config:query', 3, 1, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1239, '文件配置创建', 'infra:file-config:create', 3, 2, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1240, '文件配置更新', 'infra:file-config:update', 3, 3, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, '1', '1', '1', '1', '2022-03-16 23:47:40', '1', '2024-04-23 00:02:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, '1', '1', '1', '1', '2022-04-23 01:03:15', '1', '2025-04-29 17:45:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, '1', '1', '1', '', '2022-04-27 14:37:32', '1', '2024-02-29 08:51:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1258, '数据源配置更新', 'infra:data-source-config:update', 3, 3, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1259, '数据源配置删除', 'infra:data-source-config:delete', 3, 4, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1260, '数据源配置导出', 'infra:data-source-config:export', 3, 5, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1261, 'OAuth 2.0', '', 2, 10, 1, 'oauth2', 'fa:dashcube', NULL, NULL, 0, '1', '1', '1', '1', '2022-05-09 23:38:17', '1', '2024-02-29 01:12:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1263, '应用管理', '', 2, 0, 1261, 'oauth2/application', 'fa:hdd-o', 'system/oauth2/client/index', 'SystemOAuth2Client', 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2024-02-29 01:13:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1264, '客户端查询', 'system:oauth2-client:query', 3, 1, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1265, '客户端创建', 'system:oauth2-client:create', 3, 2, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1266, '客户端更新', 'system:oauth2-client:update', 3, 3, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1267, '客户端删除', 'system:oauth2-client:delete', 3, 4, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1281, '报表管理', '', 2, 40, 0, '/report', 'ep:pie-chart', NULL, NULL, 0, '1', '1', '1', '1', '2022-07-10 20:22:15', '1', '2024-02-29 12:33:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1282, '报表设计器', '', 2, 1, 1281, 'jimu-report', 'ep:trend-charts', 'report/jmreport/index', 'JimuReport', 0, '1', '1', '1', '1', '2022-07-10 20:26:36', '1', '2025-05-03 09:57:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2000, '商品中心', '', 1, 60, 2362, 'product', 'fa:product-hunt', NULL, NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '1', '2023-09-30 11:52:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2002, '商品分类', '', 2, 2, 2000, 'category', 'ep:cellphone', 'mall/product/category/index', 'ProductCategory', 0, '1', '1', '1', '', '2022-07-29 15:53:53', '1', '2023-08-21 10:27:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2003, '分类查询', 'product:category:query', 3, 1, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2004, '分类创建', 'product:category:create', 3, 2, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2005, '分类更新', 'product:category:update', 3, 3, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2006, '分类删除', 'product:category:delete', 3, 4, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2008, '商品品牌', '', 2, 3, 2000, 'brand', 'ep:chicken', 'mall/product/brand/index', 'ProductBrand', 0, '1', '1', '1', '', '2022-07-30 13:52:44', '1', '2023-08-21 10:27:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2009, '品牌查询', 'product:brand:query', 3, 1, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2010, '品牌创建', 'product:brand:create', 3, 2, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2011, '品牌更新', 'product:brand:update', 3, 3, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2012, '品牌删除', 'product:brand:delete', 3, 4, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2014, '商品列表', '', 2, 1, 2000, 'spu', 'ep:apple', 'mall/product/spu/index', 'ProductSpu', 0, '1', '1', '1', '', '2022-07-30 14:22:58', '1', '2023-08-21 10:27:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2015, '商品查询', 'product:spu:query', 3, 1, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2016, '商品创建', 'product:spu:create', 3, 2, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2017, '商品更新', 'product:spu:update', 3, 3, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2018, '商品删除', 'product:spu:delete', 3, 4, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2019, '商品属性', '', 2, 4, 2000, 'property', 'ep:cold-drink', 'mall/product/property/index', 'ProductProperty', 0, '1', '1', '1', '', '2022-08-01 14:55:35', '1', '2023-08-26 11:01:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2020, '规格查询', 'product:property:query', 3, 1, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2021, '规格创建', 'product:property:create', 3, 2, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2022, '规格更新', 'product:property:update', 3, 3, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2023, '规格删除', 'product:property:delete', 3, 4, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2025, 'Banner', '', 2, 100, 2387, 'banner', 'fa:bandcamp', 'mall/promotion/banner/index', NULL, 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2026, 'Banner查询', 'promotion:banner:query', 3, 1, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2027, 'Banner创建', 'promotion:banner:create', 3, 2, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2028, 'Banner更新', 'promotion:banner:update', 3, 3, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2029, 'Banner删除', 'promotion:banner:delete', 3, 4, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2030, '营销中心', '', 1, 70, 2362, 'promotion', 'ep:present', NULL, NULL, 0, '1', '1', '1', '1', '2022-10-31 21:25:09', '1', '2023-09-30 11:54:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2032, '优惠劵列表', '', 2, 1, 2365, 'template', 'ep:discount', 'mall/promotion/coupon/template/index', 'PromotionCouponTemplate', 0, '1', '1', '1', '', '2022-10-31 22:27:14', '1', '2023-10-03 12:40:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2033, '优惠劵模板查询', 'promotion:coupon-template:query', 3, 1, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2034, '优惠劵模板创建', 'promotion:coupon-template:create', 3, 2, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2035, '优惠劵模板更新', 'promotion:coupon-template:update', 3, 3, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2036, '优惠劵模板删除', 'promotion:coupon-template:delete', 3, 4, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2038, '领取记录', '', 2, 2, 2365, 'list', 'ep:collection-tag', 'mall/promotion/coupon/index', 'PromotionCoupon', 0, '1', '1', '1', '', '2022-11-03 23:21:31', '1', '2023-10-03 12:55:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2039, '优惠劵查询', 'promotion:coupon:query', 3, 1, 2038, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2040, '优惠劵删除', 'promotion:coupon:delete', 3, 4, 2038, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2041, '满减送', '', 2, 10, 2390, 'reward-activity', 'ep:goblet-square-full', 'mall/promotion/rewardActivity/index', 'PromotionRewardActivity', 0, '1', '1', '1', '', '2022-11-04 23:47:49', '1', '2023-10-21 19:24:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2042, '满减送活动查询', 'promotion:reward-activity:query', 3, 1, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2043, '满减送活动创建', 'promotion:reward-activity:create', 3, 2, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2044, '满减送活动更新', 'promotion:reward-activity:update', 3, 3, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2045, '满减送活动删除', 'promotion:reward-activity:delete', 3, 4, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2046, '满减送活动关闭', 'promotion:reward-activity:close', 3, 5, 2041, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-11-05 10:42:53', '1', '2022-11-05 10:42:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2047, '限时折扣', '', 2, 7, 2390, 'discount-activity', 'ep:timer', 'mall/promotion/discountActivity/index', 'PromotionDiscountActivity', 0, '1', '1', '1', '', '2022-11-05 17:12:15', '1', '2023-10-21 19:24:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2048, '限时折扣活动查询', 'promotion:discount-activity:query', 3, 1, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2049, '限时折扣活动创建', 'promotion:discount-activity:create', 3, 2, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2050, '限时折扣活动更新', 'promotion:discount-activity:update', 3, 3, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2051, '限时折扣活动删除', 'promotion:discount-activity:delete', 3, 4, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2052, '限时折扣活动关闭', 'promotion:discount-activity:close', 3, 5, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2059, '秒杀商品', '', 2, 2, 2209, 'activity', 'ep:basketball', 'mall/promotion/seckill/activity/index', 'PromotionSeckillActivity', 0, '1', '1', '1', '', '2022-11-06 22:24:49', '1', '2023-06-24 18:57:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2060, '秒杀活动查询', 'promotion:seckill-activity:query', 3, 1, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2061, '秒杀活动创建', 'promotion:seckill-activity:create', 3, 2, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2062, '秒杀活动更新', 'promotion:seckill-activity:update', 3, 3, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2063, '秒杀活动删除', 'promotion:seckill-activity:delete', 3, 4, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2066, '秒杀时段', '', 2, 1, 2209, 'config', 'ep:baseball', 'mall/promotion/seckill/config/index', 'PromotionSeckillConfig', 0, '1', '1', '1', '', '2022-11-15 19:46:50', '1', '2023-06-24 18:57:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2067, '秒杀时段查询', 'promotion:seckill-config:query', 3, 1, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2068, '秒杀时段创建', 'promotion:seckill-config:create', 3, 2, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:48:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2069, '秒杀时段更新', 'promotion:seckill-config:update', 3, 3, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2070, '秒杀时段删除', 'promotion:seckill-config:delete', 3, 4, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2072, '订单中心', '', 1, 65, 2362, 'trade', 'ep:eleme', NULL, NULL, 0, '1', '1', '1', '1', '2022-11-19 18:57:19', '1', '2023-09-30 11:54:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2073, '售后退款', '', 2, 2, 2072, 'after-sale', 'ep:refrigerator', 'mall/trade/afterSale/index', 'TradeAfterSale', 0, '1', '1', '1', '', '2022-11-19 20:15:32', '1', '2023-10-01 21:42:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2074, '售后查询', 'trade:after-sale:query', 3, 1, 2073, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-19 20:15:33', '1', '2022-12-10 21:04:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2075, '秒杀活动关闭', 'promotion:seckill-activity:close', 3, 5, 2059, '', '', '', '', 0, '1', '1', '1', '1', '2022-11-28 20:20:15', '1', '2023-10-03 18:34:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2076, '订单列表', '', 2, 1, 2072, 'order', 'ep:list', 'mall/trade/order/index', 'TradeOrder', 0, '1', '1', '1', '1', '2022-12-10 21:05:44', '1', '2023-10-01 21:42:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2083, '地区管理', '', 2, 14, 1, 'area', 'fa:map-marker', 'system/area/index', 'SystemArea', 0, '1', '1', '1', '1', '2022-12-23 17:35:05', '1', '2024-02-29 08:50:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2084, '公众号管理', '', 1, 100, 0, '/mp', 'ep:compass', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-01 20:11:04', '1', '2024-02-29 12:39:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2085, '账号管理', '', 2, 1, 2084, 'account', 'fa:user', 'mp/account/index', 'MpAccount', 0, '1', '1', '1', '1', '2023-01-01 20:13:31', '1', '2024-02-29 12:42:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2086, '新增账号', 'mp:account:create', 3, 1, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-01 20:21:40', '1', '2023-01-07 17:32:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2087, '修改账号', 'mp:account:update', 3, 2, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:32:46', '1', '2023-01-07 17:32:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2088, '查询账号', 'mp:account:query', 3, 0, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:07', '1', '2023-01-07 17:33:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2089, '删除账号', 'mp:account:delete', 3, 3, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:21', '1', '2023-01-07 17:33:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2090, '生成二维码', 'mp:account:qr-code', 3, 4, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:58', '1', '2023-01-07 17:33:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2091, '清空 API 配额', 'mp:account:clear-quota', 3, 5, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 18:20:32', '1', '2023-01-07 18:20:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2092, '数据统计', 'mp:statistics:query', 2, 2, 2084, 'statistics', 'ep:trend-charts', 'mp/statistics/index', 'MpStatistics', 0, '1', '1', '1', '1', '2023-01-07 20:17:36', '1', '2024-02-29 12:42:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2093, '标签管理', '', 2, 3, 2084, 'tag', 'ep:collection-tag', 'mp/tag/index', 'MpTag', 0, '1', '1', '1', '1', '2023-01-08 11:37:32', '1', '2024-02-29 12:42:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2094, '查询标签', 'mp:tag:query', 3, 0, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:03', '1', '2023-01-08 11:59:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2095, '新增标签', 'mp:tag:create', 3, 1, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:23', '1', '2023-01-08 11:59:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2096, '修改标签', 'mp:tag:update', 3, 2, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:41', '1', '2023-01-08 11:59:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2097, '删除标签', 'mp:tag:delete', 3, 3, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 12:00:04', '1', '2023-01-08 12:00:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2098, '同步标签', 'mp:tag:sync', 3, 4, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 12:00:29', '1', '2023-01-08 12:00:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2099, '粉丝管理', '', 2, 4, 2084, 'user', 'fa:user-secret', 'mp/user/index', 'MpUser', 0, '1', '1', '1', '1', '2023-01-08 16:51:20', '1', '2024-02-29 12:42:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2100, '查询粉丝', 'mp:user:query', 3, 0, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:16:59', '1', '2023-01-08 17:17:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2101, '修改粉丝', 'mp:user:update', 3, 1, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:17:11', '1', '2023-01-08 17:17:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2102, '同步粉丝', 'mp:user:sync', 3, 2, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:17:40', '1', '2023-01-08 17:17:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2103, '消息管理', '', 2, 5, 2084, 'message', 'ep:message', 'mp/message/index', 'MpMessage', 0, '1', '1', '1', '1', '2023-01-08 18:44:19', '1', '2024-02-29 12:42:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2104, '图文发表记录', '', 2, 10, 2084, 'free-publish', 'ep:edit-pen', 'mp/freePublish/index', 'MpFreePublish', 0, '1', '1', '1', '1', '2023-01-13 00:30:50', '1', '2024-02-29 12:43:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2105, '查询发布列表', 'mp:free-publish:query', 3, 1, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:19:17', '1', '2023-01-13 07:19:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2106, '发布草稿', 'mp:free-publish:submit', 3, 2, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:19:46', '1', '2023-01-13 07:19:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2107, '删除发布记录', 'mp:free-publish:delete', 3, 3, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:20:01', '1', '2023-01-13 07:20:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2108, '图文草稿箱', '', 2, 9, 2084, 'draft', 'ep:edit', 'mp/draft/index', 'MpDraft', 0, '1', '1', '1', '1', '2023-01-13 07:40:21', '1', '2024-02-29 12:43:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2109, '新建草稿', 'mp:draft:create', 3, 1, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 23:15:30', '1', '2023-01-13 23:15:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2110, '修改草稿', 'mp:draft:update', 3, 2, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:08:47', '1', '2023-01-14 10:08:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2111, '查询草稿', 'mp:draft:query', 3, 0, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:09:01', '1', '2023-01-14 10:09:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2112, '删除草稿', 'mp:draft:delete', 3, 3, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:09:19', '1', '2023-01-14 10:09:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2113, '素材管理', '', 2, 8, 2084, 'material', 'ep:basketball', 'mp/material/index', 'MpMaterial', 0, '1', '1', '1', '1', '2023-01-14 14:12:07', '1', '2024-02-29 12:43:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2114, '上传临时素材', 'mp:material:upload-temporary', 3, 1, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:33:55', '1', '2023-01-14 15:33:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2115, '上传永久素材', 'mp:material:upload-permanent', 3, 2, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:34:14', '1', '2023-01-14 15:34:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2116, '删除素材', 'mp:material:delete', 3, 3, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:35:37', '1', '2023-01-14 15:35:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2117, '上传图文图片', 'mp:material:upload-news-image', 3, 4, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:36:31', '1', '2023-01-14 15:36:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2118, '查询素材', 'mp:material:query', 3, 5, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:39:22', '1', '2023-01-14 15:39:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2119, '菜单管理', '', 2, 6, 2084, 'menu', 'ep:menu', 'mp/menu/index', 'MpMenu', 0, '1', '1', '1', '1', '2023-01-14 17:43:54', '1', '2025-04-01 20:21:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2120, '自动回复', '', 2, 7, 2084, 'auto-reply', 'fa-solid:republican', 'mp/autoReply/index', 'MpAutoReply', 0, '1', '1', '1', '1', '2023-01-15 22:13:09', '1', '2024-02-29 12:43:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2121, '查询回复', 'mp:auto-reply:query', 3, 0, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:28:41', '1', '2023-01-16 22:28:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2122, '新增回复', 'mp:auto-reply:create', 3, 1, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:28:54', '1', '2023-01-16 22:28:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2123, '修改回复', 'mp:auto-reply:update', 3, 2, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:29:05', '1', '2023-01-16 22:29:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2124, '删除回复', 'mp:auto-reply:delete', 3, 3, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:29:34', '1', '2023-01-16 22:29:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2125, '查询菜单', 'mp:menu:query', 3, 0, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:05:41', '1', '2023-01-17 23:05:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2126, '保存菜单', 'mp:menu:save', 3, 1, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:06:01', '1', '2023-01-17 23:06:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2127, '删除菜单', 'mp:menu:delete', 3, 2, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:06:16', '1', '2023-01-17 23:06:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2128, '查询消息', 'mp:message:query', 3, 0, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:07:14', '1', '2023-01-17 23:07:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2129, '发送消息', 'mp:message:send', 3, 1, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:07:26', '1', '2023-01-17 23:07:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2130, '邮箱管理', '', 2, 2, 2739, 'mail', 'fa-solid:mail-bulk', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-25 17:27:44', '1', '2024-04-22 23:56:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2131, '邮箱账号', '', 2, 0, 2130, 'mail-account', 'fa:universal-access', 'system/mail/account/index', 'SystemMailAccount', 0, '1', '1', '1', '', '2023-01-25 09:33:48', '1', '2024-02-29 08:48:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2132, '账号查询', 'system:mail-account:query', 3, 1, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2133, '账号创建', 'system:mail-account:create', 3, 2, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2134, '账号更新', 'system:mail-account:update', 3, 3, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2135, '账号删除', 'system:mail-account:delete', 3, 4, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2136, '邮件模版', '', 2, 0, 2130, 'mail-template', 'fa:tag', 'system/mail/template/index', 'SystemMailTemplate', 0, '1', '1', '1', '', '2023-01-25 12:05:31', '1', '2024-02-29 08:48:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2137, '模版查询', 'system:mail-template:query', 3, 1, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2138, '模版创建', 'system:mail-template:create', 3, 2, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2139, '模版更新', 'system:mail-template:update', 3, 3, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2140, '模版删除', 'system:mail-template:delete', 3, 4, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2141, '邮件记录', '', 2, 0, 2130, 'mail-log', 'fa:edit', 'system/mail/log/index', 'SystemMailLog', 0, '1', '1', '1', '', '2023-01-26 02:16:50', '1', '2024-02-29 08:48:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2142, '日志查询', 'system:mail-log:query', 3, 1, 2141, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-26 02:16:50', '', '2023-01-26 02:16:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2143, '发送测试邮件', 'system:mail-template:send-mail', 3, 5, 2136, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-26 23:29:15', '1', '2023-01-26 23:29:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2144, '站内信管理', '', 1, 3, 2739, 'notify', 'ep:message-box', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-28 10:25:18', '1', '2024-04-22 23:56:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2145, '模板管理', '', 2, 0, 2144, 'notify-template', 'fa:archive', 'system/notify/template/index', 'SystemNotifyTemplate', 0, '1', '1', '1', '', '2023-01-28 02:26:42', '1', '2024-02-29 08:49:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2146, '站内信模板查询', 'system:notify-template:query', 3, 1, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2147, '站内信模板创建', 'system:notify-template:create', 3, 2, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2148, '站内信模板更新', 'system:notify-template:update', 3, 3, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2149, '站内信模板删除', 'system:notify-template:delete', 3, 4, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2150, '发送测试站内信', 'system:notify-template:send-notify', 3, 5, 2145, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-28 10:54:43', '1', '2023-01-28 10:54:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2151, '消息记录', '', 2, 0, 2144, 'notify-message', 'fa:edit', 'system/notify/message/index', 'SystemNotifyMessage', 0, '1', '1', '1', '', '2023-01-28 04:28:22', '1', '2024-02-29 08:49:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2152, '站内信消息查询', 'system:notify-message:query', 3, 1, 2151, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 04:28:22', '', '2023-01-28 04:28:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2153, '大屏设计器', '', 2, 2, 1281, 'go-view', 'fa:area-chart', 'report/goview/index', 'GoView', 0, '1', '1', '1', '1', '2023-02-07 00:03:19', '1', '2025-05-03 09:57:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2154, '创建项目', 'report:go-view-project:create', 3, 1, 2153, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-02-07 19:25:14', '1', '2023-02-07 19:25:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2155, '更新项目', 'report:go-view-project:update', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', '2023-02-07 19:25:34', '1', '2024-04-24 20:01:18', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:25:53', '1', '2023-02-07 19:25:53', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:26:15', '1', '2023-02-07 19:26:15', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:26:35', '1', '2023-02-07 19:26:35', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-10 22:46:28', '1', '2024-07-28 11:36:48', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-10 22:47:07', '1', '2023-12-02 21:32:29', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2023-02-11 14:21:42', '1', '2024-01-18 23:50:00', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2164, '配送管理', '', 1, 3, 2072, 'delivery', 'ep:shopping-cart', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:18:02', '1', '2023-09-28 10:58:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2165, '快递发货', '', 1, 0, 2164, 'express', 'ep:bicycle', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:22:06', '1', '2023-08-30 21:02:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2166, '门店自提', '', 1, 1, 2164, 'pick-up-store', 'ep:add-location', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:23:14', '1', '2023-08-30 21:03:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2167, '快递公司', '', 2, 0, 2165, 'express', 'ep:compass', 'mall/trade/delivery/express/index', 'Express', 0, '1', '1', '1', '1', '2023-05-18 09:27:21', '1', '2024-11-29 11:20:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2168, '快递公司查询', 'trade:delivery:express:query', 3, 1, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2169, '快递公司创建', 'trade:delivery:express:create', 3, 2, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2170, '快递公司更新', 'trade:delivery:express:update', 3, 3, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2171, '快递公司删除', 'trade:delivery:express:delete', 3, 4, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2172, '快递公司导出', 'trade:delivery:express:export', 3, 5, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2173, '运费模版', 'trade:delivery:express-template:query', 2, 1, 2165, 'express-template', 'ep:coordinate', 'mall/trade/delivery/expressTemplate/index', 'ExpressTemplate', 0, '1', '1', '1', '1', '2023-05-20 06:48:10', '1', '2023-08-30 21:03:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2174, '快递运费模板查询', 'trade:delivery:express-template:query', 3, 1, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2175, '快递运费模板创建', 'trade:delivery:express-template:create', 3, 2, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2176, '快递运费模板更新', 'trade:delivery:express-template:update', 3, 3, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2177, '快递运费模板删除', 'trade:delivery:express-template:delete', 3, 4, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2178, '快递运费模板导出', 'trade:delivery:express-template:export', 3, 5, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2179, '门店管理', '', 2, 1, 2166, 'pick-up-store', 'ep:basketball', 'mall/trade/delivery/pickUpStore/index', 'PickUpStore', 0, '1', '1', '1', '1', '2023-05-25 10:50:00', '1', '2023-08-30 21:03:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2180, '自提门店查询', 'trade:delivery:pick-up-store:query', 3, 1, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2181, '自提门店创建', 'trade:delivery:pick-up-store:create', 3, 2, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2182, '自提门店更新', 'trade:delivery:pick-up-store:update', 3, 3, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2183, '自提门店删除', 'trade:delivery:pick-up-store:delete', 3, 4, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2184, '自提门店导出', 'trade:delivery:pick-up-store:export', 3, 5, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2209, '秒杀活动', '', 2, 3, 2030, 'seckill', 'ep:place', '', '', 0, '1', '1', '1', '1', '2023-06-24 17:39:13', '1', '2023-06-24 18:55:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2262, '会员中心', '', 1, 55, 0, '/member', 'ep:bicycle', NULL, NULL, 0, '1', '1', '1', '1', '2023-06-10 00:42:03', '1', '2023-08-20 09:23:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2275, '会员配置', '', 2, 0, 2262, 'config', 'fa:archive', 'member/config/index', 'MemberConfig', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2023-10-01 23:41:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2276, '会员配置查询', 'member:config:query', 3, 1, 2275, '', '', '', '', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:48:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2277, '会员配置保存', 'member:config:save', 3, 2, 2275, '', '', '', '', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:49:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2281, '签到配置', '', 2, 2, 2300, 'config', 'ep:calendar', 'member/signin/config/index', 'SignInConfig', 0, '1', '1', '1', '', '2023-06-10 03:26:12', '1', '2023-08-20 19:25:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2282, '积分签到规则查询', 'point:sign-in-config:query', 3, 1, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2283, '积分签到规则创建', 'point:sign-in-config:create', 3, 2, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2284, '积分签到规则更新', 'point:sign-in-config:update', 3, 3, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2285, '积分签到规则删除', 'point:sign-in-config:delete', 3, 4, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2287, '会员积分', '', 2, 10, 2262, 'record', 'fa:asterisk', 'member/point/record/index', 'PointRecord', 0, '1', '1', '1', '', '2023-06-10 04:18:50', '1', '2023-10-01 23:42:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2288, '用户积分记录查询', 'point:record:query', 3, 1, 2287, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:18:50', '', '2023-06-10 04:18:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2293, '签到记录', '', 2, 3, 2300, 'record', 'ep:chicken', 'member/signin/record/index', 'SignInRecord', 0, '1', '1', '1', '', '2023-06-10 04:48:22', '1', '2023-08-20 19:26:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2294, '用户签到积分查询', 'point:sign-in-record:query', 3, 1, 2293, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2297, '用户签到积分删除', 'point:sign-in-record:delete', 3, 4, 2293, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2300, '会员签到', '', 1, 11, 2262, 'signin', 'ep:alarm-clock', '', '', 0, '1', '1', '1', '1', '2023-06-27 22:49:53', '1', '2023-08-20 09:23:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2301, '回调通知', '', 2, 5, 1117, 'notify', 'ep:mute-notification', 'pay/notify/index', 'PayNotify', 0, '1', '1', '1', '', '2023-07-20 04:41:32', '1', '2024-01-18 23:56:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2302, '支付通知查询', 'pay:notify:query', 3, 1, 2301, '', '', '', NULL, 0, '1', '1', '1', '', '2023-07-20 04:41:32', '', '2023-07-20 04:41:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2303, '拼团活动', '', 2, 3, 2030, 'combination', 'fa:group', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:19:54', '1', '2023-08-12 17:20:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2304, '拼团商品', '', 2, 1, 2303, 'acitivity', 'ep:apple', 'mall/promotion/combination/activity/index', 'PromotionCombinationActivity', 0, '1', '1', '1', '1', '2023-08-12 17:22:03', '1', '2023-08-12 17:22:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2305, '拼团活动查询', 'promotion:combination-activity:query', 3, 1, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:54:32', '1', '2023-11-24 11:57:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2306, '拼团活动创建', 'promotion:combination-activity:create', 3, 2, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:54:49', '1', '2023-08-12 17:54:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2307, '拼团活动更新', 'promotion:combination-activity:update', 3, 3, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:04', '1', '2023-08-12 17:55:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2308, '拼团活动删除', 'promotion:combination-activity:delete', 3, 4, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:23', '1', '2023-08-12 17:55:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2309, '拼团活动关闭', 'promotion:combination-activity:close', 3, 5, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:37', '1', '2023-10-06 10:51:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2310, '砍价活动', '', 2, 4, 2030, 'bargain', 'ep:box', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:27:25', '1', '2023-08-13 00:27:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2311, '砍价商品', '', 2, 1, 2310, 'activity', 'ep:burger', 'mall/promotion/bargain/activity/index', 'PromotionBargainActivity', 0, '1', '1', '1', '1', '2023-08-13 00:28:49', '1', '2023-10-05 01:16:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2312, '砍价活动查询', 'promotion:bargain-activity:query', 3, 1, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:30', '1', '2023-08-13 00:32:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2313, '砍价活动创建', 'promotion:bargain-activity:create', 3, 2, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:44', '1', '2023-08-13 00:32:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2314, '砍价活动更新', 'promotion:bargain-activity:update', 3, 3, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:55', '1', '2023-08-13 00:32:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2315, '砍价活动删除', 'promotion:bargain-activity:delete', 3, 4, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:34:50', '1', '2023-08-13 00:34:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2316, '砍价活动关闭', 'promotion:bargain-activity:close', 3, 5, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:35:02', '1', '2023-08-13 00:35:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2317, '会员管理', '', 2, 0, 2262, 'user', 'ep:avatar', 'member/user/index', 'MemberUser', 0, '1', '1', '1', '', '2023-08-19 04:12:15', '1', '2023-08-24 00:50:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2318, '会员用户查询', 'member:user:query', 3, 1, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2319, '会员用户更新', 'member:user:update', 3, 3, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2320, '会员标签', '', 2, 1, 2262, 'tag', 'ep:collection-tag', 'member/tag/index', 'MemberTag', 0, '1', '1', '1', '', '2023-08-20 01:03:08', '1', '2023-08-20 09:23:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2321, '会员标签查询', 'member:tag:query', 3, 1, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2322, '会员标签创建', 'member:tag:create', 3, 2, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2323, '会员标签更新', 'member:tag:update', 3, 3, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2324, '会员标签删除', 'member:tag:delete', 3, 4, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2325, '会员等级', '', 2, 2, 2262, 'level', 'fa:level-up', 'member/level/index', 'MemberLevel', 0, '1', '1', '1', '', '2023-08-22 12:41:01', '1', '2023-08-22 21:47:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2326, '会员等级查询', 'member:level:query', 3, 1, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2327, '会员等级创建', 'member:level:create', 3, 2, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2328, '会员等级更新', 'member:level:update', 3, 3, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2329, '会员等级删除', 'member:level:delete', 3, 4, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2330, '会员分组', '', 2, 3, 2262, 'group', 'fa:group', 'member/group/index', 'MemberGroup', 0, '1', '1', '1', '', '2023-08-22 13:50:06', '1', '2023-10-01 23:42:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2331, '用户分组查询', 'member:group:query', 3, 1, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2332, '用户分组创建', 'member:group:create', 3, 2, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2333, '用户分组更新', 'member:group:update', 3, 3, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2334, '用户分组删除', 'member:group:delete', 3, 4, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2335, '用户等级修改', 'member:user:update-level', 3, 5, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-23 16:49:05', '', '2023-08-23 16:50:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2336, '商品评论', '', 2, 5, 2000, 'comment', 'ep:comment', 'mall/product/comment/index', 'ProductComment', 0, '1', '1', '1', '1', '2023-08-26 11:03:00', '1', '2023-08-26 11:03:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2337, '评论查询', 'product:comment:query', 3, 1, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:01', '1', '2023-08-26 11:04:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2338, '添加自评', 'product:comment:create', 3, 2, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:23', '1', '2023-08-26 11:08:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2339, '商家回复', 'product:comment:update', 3, 3, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:37', '1', '2023-08-26 11:04:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2340, '显隐评论', 'product:comment:update', 3, 4, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:55', '1', '2023-08-26 11:04:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2341, '优惠劵发送', 'promotion:coupon:send', 3, 2, 2038, '', '', '', '', 0, '1', '1', '1', '1', '2023-09-02 00:03:14', '1', '2023-09-02 00:03:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2342, '交易配置', '', 2, 0, 2072, 'config', 'ep:setting', 'mall/trade/config/index', 'TradeConfig', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:30:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2343, '交易中心配置查询', 'trade:config:query', 3, 1, 2342, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2344, '交易中心配置保存', 'trade:config:save', 3, 2, 2342, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2345, '分销管理', '', 1, 4, 2072, 'brokerage', 'fa-solid:project-diagram', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2023-09-28 10:58:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2346, '分销用户', '', 2, 0, 2345, 'brokerage-user', 'fa-solid:user-tie', 'mall/trade/brokerage/user/index', 'TradeBrokerageUser', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2347, '分销用户查询', 'trade:brokerage-user:query', 3, 1, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2348, '分销用户推广人查询', 'trade:brokerage-user:user-query', 3, 2, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2349, '分销用户推广订单查询', 'trade:brokerage-user:order-query', 3, 3, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2350, '分销用户修改推广资格', 'trade:brokerage-user:update-brokerage-enable', 3, 4, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2351, '修改推广员', 'trade:brokerage-user:update-bind-user', 3, 5, 2346, '', '', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2352, '清除推广员', 'trade:brokerage-user:clear-bind-user', 3, 6, 2346, '', '', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2353, '佣金记录', '', 2, 1, 2345, 'brokerage-record', 'fa:money', 'mall/trade/brokerage/record/index', 'TradeBrokerageRecord', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2354, '佣金记录查询', 'trade:brokerage-record:query', 3, 1, 2353, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2355, '佣金提现', '', 2, 2, 2345, 'brokerage-withdraw', 'fa:credit-card', 'mall/trade/brokerage/withdraw/index', 'TradeBrokerageWithdraw', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2356, '佣金提现查询', 'trade:brokerage-withdraw:query', 3, 1, 2355, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2357, '佣金提现审核', 'trade:brokerage-withdraw:audit', 3, 2, 2355, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2358, '统计中心', '', 1, 75, 2362, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '', '2023-09-30 03:22:40', '1', '2023-09-30 11:54:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2359, '交易统计', '', 2, 4, 2358, 'trade', 'fa-solid:credit-card', 'mall/statistics/trade/index', 'TradeStatistics', 0, '1', '1', '1', '', '2023-09-30 03:22:40', '1', '2024-02-26 20:42:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2360, '交易统计查询', 'statistics:trade:query', 3, 1, 2359, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2361, '交易统计导出', 'statistics:trade:export', 3, 2, 2359, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2362, '商城系统', '', 1, 59, 0, '/mall', 'ep:shop', '', '', 0, '1', '1', '1', '1', '2023-09-30 11:52:02', '1', '2023-09-30 11:52:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2363, '用户积分修改', 'member:user:update-point', 3, 6, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-01 14:39:43', '', '2023-10-01 14:39:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2364, '用户余额修改', 'pay:wallet:update-balance', 3, 7, 2317, '', '', '', '', 0, '1', '1', '1', '', '2023-10-01 14:39:43', '1', '2024-10-01 09:42:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2365, '优惠劵', '', 1, 2, 2030, 'coupon', 'fa-solid:disease', '', '', 0, '1', '1', '1', '1', '2023-10-03 12:39:15', '1', '2023-10-05 00:16:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2366, '砍价记录', '', 2, 2, 2310, 'record', 'ep:list', 'mall/promotion/bargain/record/index', 'PromotionBargainRecord', 0, '1', '1', '1', '', '2023-10-05 02:49:06', '1', '2023-10-05 10:50:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2367, '砍价记录查询', 'promotion:bargain-record:query', 3, 1, 2366, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-05 02:49:06', '', '2023-10-05 02:49:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2368, '助力记录查询', 'promotion:bargain-help:query', 3, 2, 2366, '', '', '', '', 0, '1', '1', '1', '1', '2023-10-05 12:27:49', '1', '2023-10-05 12:27:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2369, '拼团记录', 'promotion:combination-record:query', 2, 2, 2303, 'record', 'ep:avatar', 'mall/promotion/combination/record/index.vue', 'PromotionCombinationRecord', 0, '1', '1', '1', '1', '2023-10-08 07:10:22', '1', '2023-10-08 07:34:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2374, '会员统计', '', 2, 2, 2358, 'member', 'ep:avatar', 'mall/statistics/member/index', 'MemberStatistics', 0, '1', '1', '1', '', '2023-10-11 04:39:24', '1', '2024-02-26 20:41:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2375, '会员统计查询', 'statistics:member:query', 3, 1, 2374, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-11 04:39:24', '', '2023-10-11 04:39:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2376, '订单核销', 'trade:order:pick-up', 3, 10, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2023-10-14 17:11:58', '1', '2023-10-14 17:11:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2377, '文章分类', '', 2, 0, 2387, 'article/category', 'fa:certificate', 'mall/promotion/article/category/index', 'ArticleCategory', 0, '1', '1', '1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:38:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2378, '分类查询', 'promotion:article-category:query', 3, 1, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2379, '分类创建', 'promotion:article-category:create', 3, 2, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2380, '分类更新', 'promotion:article-category:update', 3, 3, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2381, '分类删除', 'promotion:article-category:delete', 3, 4, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2382, '文章列表', '', 2, 2, 2387, 'article', 'ep:connection', 'mall/promotion/article/index', 'Article', 0, '1', '1', '1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:41:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2383, '文章管理查询', 'promotion:article:query', 3, 1, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2384, '文章管理创建', 'promotion:article:create', 3, 2, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2385, '文章管理更新', 'promotion:article:update', 3, 3, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2386, '文章管理删除', 'promotion:article:delete', 3, 4, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2387, '内容管理', '', 1, 1, 2030, 'content', 'ep:collection', '', '', 0, '1', '1', '1', '1', '2023-10-16 09:37:31', '1', '2023-10-16 09:37:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2388, '商城首页', '', 2, 1, 2362, 'home', 'ep:home-filled', 'mall/home/index', 'MallHome', 0, '1', '1', '1', '', '2023-10-16 12:10:33', '', '2023-10-16 12:10:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2389, '核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder', 0, '1', '1', '1', '', '2023-10-19 16:09:51', '', '2023-10-19 16:09:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2390, '优惠活动', '', 1, 99, 2030, 'youhui', 'ep:aim', '', '', 0, '1', '1', '1', '1', '2023-10-21 19:23:49', '1', '2023-10-21 19:23:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2391, '客户管理', '', 2, 10, 2397, 'customer', 'fa:address-book-o', 'crm/customer/index', 'CrmCustomer', 0, '1', '1', '1', '', '2023-10-29 09:04:21', '1', '2024-02-17 17:13:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2392, '客户查询', 'crm:customer:query', 3, 1, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2393, '客户创建', 'crm:customer:create', 3, 2, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2394, '客户更新', 'crm:customer:update', 3, 3, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2395, '客户删除', 'crm:customer:delete', 3, 4, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2396, '客户导出', 'crm:customer:export', 3, 5, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2397, 'CRM 系统', '', 1, 200, 0, '/crm', 'simple-icons:civicrm', '', '', 0, '1', '1', '1', '1', '2023-10-29 17:08:30', '1', '2025-04-19 18:56:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2398, '合同管理', '', 2, 50, 2397, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, '1', '1', '1', '', '2023-10-29 10:50:41', '1', '2024-02-17 17:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2399, '合同查询', 'crm:contract:query', 3, 1, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2400, '合同创建', 'crm:contract:create', 3, 2, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2401, '合同更新', 'crm:contract:update', 3, 3, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2402, '合同删除', 'crm:contract:delete', 3, 4, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2403, '合同导出', 'crm:contract:export', 3, 5, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2404, '线索管理', '', 2, 8, 2397, 'clue', 'fa:pagelines', 'crm/clue/index', 'CrmClue', 0, '1', '1', '1', '', '2023-10-29 11:06:29', '1', '2024-02-17 17:15:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2405, '线索查询', 'crm:clue:query', 3, 1, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2406, '线索创建', 'crm:clue:create', 3, 2, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2407, '线索更新', 'crm:clue:update', 3, 3, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2408, '线索删除', 'crm:clue:delete', 3, 4, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2409, '线索导出', 'crm:clue:export', 3, 5, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2410, '商机管理', '', 2, 40, 2397, 'business', 'fa:bus', 'crm/business/index', 'CrmBusiness', 0, '1', '1', '1', '', '2023-10-29 11:12:35', '1', '2024-02-17 17:14:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2411, '商机查询', 'crm:business:query', 3, 1, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2412, '商机创建', 'crm:business:create', 3, 2, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2413, '商机更新', 'crm:business:update', 3, 3, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2414, '商机删除', 'crm:business:delete', 3, 4, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2415, '商机导出', 'crm:business:export', 3, 5, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2416, '联系人管理', '', 2, 20, 2397, 'contact', 'fa:address-book-o', 'crm/contact/index', 'CrmContact', 0, '1', '1', '1', '', '2023-10-29 11:14:56', '1', '2024-02-17 17:13:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2417, '联系人查询', 'crm:contact:query', 3, 1, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2418, '联系人创建', 'crm:contact:create', 3, 2, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2419, '联系人更新', 'crm:contact:update', 3, 3, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2420, '联系人删除', 'crm:contact:delete', 3, 4, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2421, '联系人导出', 'crm:contact:export', 3, 5, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2422, '回款管理', '', 2, 60, 2397, 'receivable', 'ep:money', 'crm/receivable/index', 'CrmReceivable', 0, '1', '1', '1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2423, '回款管理查询', 'crm:receivable:query', 3, 1, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2424, '回款管理创建', 'crm:receivable:create', 3, 2, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2425, '回款管理更新', 'crm:receivable:update', 3, 3, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2426, '回款管理删除', 'crm:receivable:delete', 3, 4, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2427, '回款管理导出', 'crm:receivable:export', 3, 5, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2428, '回款计划', '', 2, 61, 2397, 'receivable-plan', 'fa:money', 'crm/receivable/plan/index', 'CrmReceivablePlan', 0, '1', '1', '1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2429, '回款计划查询', 'crm:receivable-plan:query', 3, 1, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2430, '回款计划创建', 'crm:receivable-plan:create', 3, 2, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2432, '回款计划删除', 'crm:receivable-plan:delete', 3, 4, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2433, '回款计划导出', 'crm:receivable-plan:export', 3, 5, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', '', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '1', '2025-03-15 21:34:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2436, '装修模板', '', 2, 1, 2435, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2437, '装修模板查询', 'promotion:diy-template:query', 3, 1, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2438, '装修模板创建', 'promotion:diy-template:create', 3, 2, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2439, '装修模板更新', 'promotion:diy-template:update', 3, 3, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2440, '装修模板删除', 'promotion:diy-template:delete', 3, 4, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2441, '装修模板使用', 'promotion:diy-template:use', 3, 5, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2442, '装修页面', '', 2, 2, 2435, 'diy-page', 'foundation:page-edit', 'mall/promotion/diy/page/index', 'DiyPage', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2443, '装修页面查询', 'promotion:diy-page:query', 3, 1, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2444, '装修页面创建', 'promotion:diy-page:create', 3, 2, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2445, '装修页面更新', 'promotion:diy-page:update', 3, 3, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2446, '装修页面删除', 'promotion:diy-page:delete', 3, 4, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2447, '三方登录', '', 1, 10, 1, 'social', 'fa:rocket', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:12:01', '1', '2024-02-29 01:14:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2448, '三方应用', '', 2, 1, 2447, 'client', 'ep:set-up', 'system/social/client/index.vue', 'SocialClient', 0, '1', '1', '1', '1', '2023-11-04 12:17:19', '1', '2024-05-04 19:09:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2449, '三方应用查询', 'system:social-client:query', 3, 1, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:43:12', '1', '2023-11-04 12:43:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2450, '三方应用创建', 'system:social-client:create', 3, 2, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:43:58', '1', '2023-11-04 12:43:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2451, '三方应用更新', 'system:social-client:update', 3, 3, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:44:27', '1', '2023-11-04 12:44:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2452, '三方应用删除', 'system:social-client:delete', 3, 4, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:44:43', '1', '2023-11-04 12:44:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2453, '三方用户', 'system:social-user:query', 2, 2, 2447, 'user', 'ep:avatar', 'system/social/user/index.vue', 'SocialUser', 0, '1', '1', '1', '1', '2023-11-04 14:01:05', '1', '2023-11-04 14:01:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2472, '主子表（内嵌）', '', 2, 12, 1070, 'demo03-inner', 'fa:power-off', 'infra/demo/demo03/inner/index', 'Demo03StudentInner', 0, '1', '1', '1', '', '2023-11-13 04:39:51', '1', '2023-11-16 23:53:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2478, '单表（增删改查）', '', 2, 1, 1070, 'demo01-contact', 'ep:bicycle', 'infra/demo/demo01/index', 'Demo01Contact', 0, '1', '1', '1', '', '2023-11-15 14:42:30', '1', '2023-11-16 20:34:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2479, '示例联系人查询', 'infra:demo01-contact:query', 3, 1, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2480, '示例联系人创建', 'infra:demo01-contact:create', 3, 2, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2481, '示例联系人更新', 'infra:demo01-contact:update', 3, 3, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2482, '示例联系人删除', 'infra:demo01-contact:delete', 3, 4, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2483, '示例联系人导出', 'infra:demo01-contact:export', 3, 5, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2484, '树表（增删改查）', '', 2, 2, 1070, 'demo02-category', 'fa:tree', 'infra/demo/demo02/index', 'Demo02Category', 0, '1', '1', '1', '', '2023-11-16 12:18:27', '1', '2023-11-16 20:35:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2485, '示例分类查询', 'infra:demo02-category:query', 3, 1, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2486, '示例分类创建', 'infra:demo02-category:create', 3, 2, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2487, '示例分类更新', 'infra:demo02-category:update', 3, 3, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2488, '示例分类删除', 'infra:demo02-category:delete', 3, 4, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2489, '示例分类导出', 'infra:demo02-category:export', 3, 5, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2490, '主子表（标准）', '', 2, 10, 1070, 'demo03-normal', 'fa:battery-3', 'infra/demo/demo03/normal/index', 'Demo03StudentNormal', 0, '1', '1', '1', '', '2023-11-16 12:53:37', '1', '2023-11-16 23:10:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2491, '学生查询', 'infra:demo03-student:query', 3, 1, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2492, '学生创建', 'infra:demo03-student:create', 3, 2, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2493, '学生更新', 'infra:demo03-student:update', 3, 3, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2494, '学生删除', 'infra:demo03-student:delete', 3, 4, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2495, '学生导出', 'infra:demo03-student:export', 3, 5, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2497, '主子表（ERP）', '', 2, 11, 1070, 'demo03-erp', 'ep:calendar', 'infra/demo/demo03/erp/index', 'Demo03StudentERP', 0, '1', '1', '1', '', '2023-11-16 15:50:59', '1', '2023-11-17 13:19:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2516, '客户公海配置', '', 2, 0, 2524, 'customer-pool-config', 'ep:data-analysis', 'crm/customer/poolConfig/index', 'CrmCustomerPoolConfig', 0, '1', '1', '1', '', '2023-11-18 13:33:31', '1', '2024-01-03 19:52:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2517, '客户公海配置保存', 'crm:customer-pool-config:update', 3, 1, 2516, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:31', '', '2023-11-18 13:33:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2518, '客户限制配置', '', 2, 1, 2524, 'customer-limit-config', 'ep:avatar', 'crm/customer/limitConfig/index', 'CrmCustomerLimitConfig', 0, '1', '1', '1', '', '2023-11-18 13:33:53', '1', '2024-02-24 16:43:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2519, '客户限制配置查询', 'crm:customer-limit-config:query', 3, 1, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2520, '客户限制配置创建', 'crm:customer-limit-config:create', 3, 2, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2521, '客户限制配置更新', 'crm:customer-limit-config:update', 3, 3, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2522, '客户限制配置删除', 'crm:customer-limit-config:delete', 3, 4, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2523, '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2524, '系统配置', '', 1, 999, 2397, 'config', 'ep:connection', '', '', 0, '1', '1', '1', '1', '2023-11-18 21:58:00', '1', '2024-02-17 17:14:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2525, 'WebSocket', '', 2, 5, 2, 'websocket', 'ep:connection', 'infra/webSocket/index', 'InfraWebSocket', 0, '1', '1', '1', '1', '2023-11-23 19:41:55', '1', '2024-04-23 00:02:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2526, '产品管理', '', 2, 80, 2397, 'product', 'fa:product-hunt', 'crm/product/index', 'CrmProduct', 0, '1', '1', '1', '1', '2023-12-05 22:45:26', '1', '2024-02-20 20:36:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2527, '产品查询', 'crm:product:query', 3, 1, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:47:16', '1', '2023-12-05 22:47:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2528, '产品创建', 'crm:product:create', 3, 2, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:47:41', '1', '2023-12-05 22:47:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2529, '产品更新', 'crm:product:update', 3, 3, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:03', '1', '2023-12-05 22:48:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2530, '产品删除', 'crm:product:delete', 3, 4, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:17', '1', '2023-12-05 22:48:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2531, '产品导出', 'crm:product:export', 3, 5, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:29', '1', '2023-12-05 22:48:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2532, '产品分类配置', '', 2, 3, 2524, 'product/category', 'fa-solid:window-restore', 'crm/product/category/index', 'CrmProductCategory', 0, '1', '1', '1', '1', '2023-12-06 12:52:36', '1', '2023-12-06 12:52:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2533, '产品分类查询', 'crm:product-category:query', 3, 1, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:23', '1', '2023-12-06 12:53:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2534, '产品分类创建', 'crm:product-category:create', 3, 2, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:41', '1', '2023-12-06 12:53:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2535, '产品分类更新', 'crm:product-category:update', 3, 3, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:59', '1', '2023-12-06 12:53:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2536, '产品分类删除', 'crm:product-category:delete', 3, 4, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:54:14', '1', '2023-12-06 12:54:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2543, '关联商机', 'crm:contact:create-business', 3, 10, 2416, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-02 17:28:25', '1', '2024-01-02 17:28:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2544, '取关商机', 'crm:contact:delete-business', 3, 11, 2416, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-02 17:28:43', '1', '2024-01-02 17:28:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2545, '商品统计', '', 2, 3, 2358, 'product', 'fa:product-hunt', 'mall/statistics/product/index', 'ProductStatistics', 0, '1', '1', '1', '', '2023-12-15 18:54:28', '1', '2024-02-26 20:41:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2546, '客户公海', '', 2, 30, 2397, 'customer/pool', 'fa-solid:swimming-pool', 'crm/customer/pool/index', 'CrmCustomerPool', 0, '1', '1', '1', '1', '2024-01-15 21:29:34', '1', '2024-02-17 17:14:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2547, '订单查询', 'trade:order:query', 3, 1, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-16 08:52:00', '1', '2024-01-16 08:52:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2548, '订单更新', 'trade:order:update', 3, 2, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-16 08:52:21', '1', '2024-01-16 08:52:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2549, '支付&退款案例', '', 2, 1, 2161, 'order', 'fa:paypal', 'pay/demo/order/index', '', 0, '1', '1', '1', '1', '2024-01-18 23:45:00', '1', '2024-01-18 23:47:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2550, '提现转账案例', '', 2, 2, 2161, 'transfer', 'fa:transgender-alt', 'pay/demo/withdraw/index', '', 0, '1', '1', '1', '1', '2024-01-18 23:51:16', '1', '2025-05-08 13:04:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2551, '钱包管理', '', 1, 4, 1117, 'wallet', 'ep:wallet', '', '', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '1', '2024-02-29 08:58:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2552, '充值套餐', '', 2, 2, 2551, 'wallet-recharge-package', 'fa:leaf', 'pay/wallet/rechargePackage/index', 'WalletRechargePackage', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2553, '钱包充值套餐查询', 'pay:wallet-recharge-package:query', 3, 1, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2554, '钱包充值套餐创建', 'pay:wallet-recharge-package:create', 3, 2, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2555, '钱包充值套餐更新', 'pay:wallet-recharge-package:update', 3, 3, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2556, '钱包充值套餐删除', 'pay:wallet-recharge-package:delete', 3, 4, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2557, '钱包余额', '', 2, 1, 2551, 'wallet-balance', 'fa:leaf', 'pay/wallet/balance/index', 'WalletBalance', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2558, '钱包余额查询', 'pay:wallet:query', 3, 1, 2557, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2559, '转账订单', '', 2, 3, 1117, 'transfer', 'ep:credit-card', 'pay/transfer/index', 'PayTransfer', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2560, '数据统计', '', 1, 200, 2397, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '1', '2024-01-26 22:50:35', '1', '2024-02-24 20:10:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2561, '排行榜', 'crm:statistics-rank:query', 2, 1, 2560, 'ranking', 'fa:area-chart', 'crm/statistics/rank/index', 'CrmStatisticsRank', 0, '1', '1', '1', '1', '2024-01-26 22:52:09', '1', '2024-04-24 19:39:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2562, '客户导入', 'crm:customer:import', 3, 6, 2391, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-01 13:09:00', '1', '2024-02-01 13:09:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2563, 'ERP 系统', '', 1, 300, 0, '/erp', 'simple-icons:erpnext', '', '', 0, '1', '1', '1', '1', '2024-02-04 15:37:25', '1', '2025-04-19 18:56:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2564, '产品管理', '', 1, 40, 2563, 'product', 'fa:product-hunt', '', '', 0, '1', '1', '1', '1', '2024-02-04 15:38:43', '1', '2024-02-04 15:38:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2565, '产品信息', '', 2, 0, 2564, 'product', 'fa-solid:apple-alt', 'erp/product/product/index', 'ErpProduct', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-05 14:42:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2566, '产品查询', 'erp:product:query', 3, 1, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:21:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2567, '产品创建', 'erp:product:create', 3, 2, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2568, '产品更新', 'erp:product:update', 3, 3, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2569, '产品删除', 'erp:product:delete', 3, 4, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2570, '产品导出', 'erp:product:export', 3, 5, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2571, '产品分类', '', 2, 1, 2564, 'product-category', 'fa:certificate', 'erp/product/category/index', 'ErpProductCategory', 0, '1', '1', '1', '', '2024-02-04 09:21:04', '1', '2024-02-04 17:24:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2572, '分类查询', 'erp:product-category:query', 3, 1, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2573, '分类创建', 'erp:product-category:create', 3, 2, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2574, '分类更新', 'erp:product-category:update', 3, 3, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2575, '分类删除', 'erp:product-category:delete', 3, 4, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2576, '分类导出', 'erp:product-category:export', 3, 5, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2577, '产品单位', '', 2, 2, 2564, 'unit', 'ep:opportunity', 'erp/product/unit/index', 'ErpProductUnit', 0, '1', '1', '1', '', '2024-02-04 11:54:08', '1', '2024-02-04 19:54:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2578, '单位查询', 'erp:product-unit:query', 3, 1, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2579, '单位创建', 'erp:product-unit:create', 3, 2, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2580, '单位更新', 'erp:product-unit:update', 3, 3, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2581, '单位删除', 'erp:product-unit:delete', 3, 4, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2582, '单位导出', 'erp:product-unit:export', 3, 5, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2583, '库存管理', '', 1, 30, 2563, 'stock', 'fa:window-restore', '', '', 0, '1', '1', '1', '1', '2024-02-05 00:29:37', '1', '2024-02-05 00:29:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2584, '仓库信息', '', 2, 0, 2583, 'warehouse', 'ep:house', 'erp/stock/warehouse/index', 'ErpWarehouse', 0, '1', '1', '1', '', '2024-02-04 17:12:09', '1', '2024-02-05 01:12:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2585, '仓库查询', 'erp:warehouse:query', 3, 1, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2586, '仓库创建', 'erp:warehouse:create', 3, 2, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2587, '仓库更新', 'erp:warehouse:update', 3, 3, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2588, '仓库删除', 'erp:warehouse:delete', 3, 4, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2589, '仓库导出', 'erp:warehouse:export', 3, 5, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2590, '产品库存', '', 2, 1, 2583, 'stock', 'ep:coffee', 'erp/stock/stock/index', 'ErpStock', 0, '1', '1', '1', '', '2024-02-05 06:40:50', '1', '2024-02-05 14:42:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2591, '库存查询', 'erp:stock:query', 3, 1, 2590, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2592, '库存导出', 'erp:stock:export', 3, 5, 2590, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2593, '出入库明细', '', 2, 2, 2583, 'record', 'fa-solid:blog', 'erp/stock/record/index', 'ErpStockRecord', 0, '1', '1', '1', '', '2024-02-05 10:27:21', '1', '2024-02-06 17:26:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2594, '库存明细查询', 'erp:stock-record:query', 3, 1, 2593, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2595, '库存明细导出', 'erp:stock-record:export', 3, 5, 2593, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2596, '其它入库', '', 2, 3, 2583, 'in', 'ep:zoom-in', 'erp/stock/in/index', 'ErpStockIn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2597, '其它入库单查询', 'erp:stock-in:query', 3, 1, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2598, '其它入库单创建', 'erp:stock-in:create', 3, 2, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2599, '其它入库单更新', 'erp:stock-in:update', 3, 3, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2600, '其它入库单删除', 'erp:stock-in:delete', 3, 4, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2601, '其它入库单导出', 'erp:stock-in:export', 3, 5, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2602, '采购管理', '', 1, 10, 2563, 'purchase', 'fa:buysellads', '', '', 0, '1', '1', '1', '1', '2024-02-06 16:01:01', '1', '2024-02-06 16:01:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2603, '供应商信息', '', 2, 4, 2602, 'supplier', 'fa:superpowers', 'erp/purchase/supplier/index', 'ErpSupplier', 0, '1', '1', '1', '', '2024-02-06 08:21:55', '1', '2024-02-06 16:22:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2604, '供应商查询', 'erp:supplier:query', 3, 1, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2605, '供应商创建', 'erp:supplier:create', 3, 2, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2606, '供应商更新', 'erp:supplier:update', 3, 3, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2607, '供应商删除', 'erp:supplier:delete', 3, 4, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2608, '供应商导出', 'erp:supplier:export', 3, 5, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2609, '其它入库单审批', 'erp:stock-in:update-status', 3, 6, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2610, '其它出库', '', 2, 4, 2583, 'out', 'ep:zoom-out', 'erp/stock/out/index', 'ErpStockOut', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2611, '其它出库单查询', 'erp:stock-out:query', 3, 1, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2612, '其它出库单创建', 'erp:stock-out:create', 3, 2, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2613, '其它出库单更新', 'erp:stock-out:update', 3, 3, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2614, '其它出库单删除', 'erp:stock-out:delete', 3, 4, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2615, '其它出库单导出', 'erp:stock-out:export', 3, 5, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2616, '其它出库单审批', 'erp:stock-out:update-status', 3, 6, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2617, '销售管理', '', 1, 20, 2563, 'sale', 'fa:sellsy', '', '', 0, '1', '1', '1', '1', '2024-02-07 15:12:32', '1', '2024-02-07 15:12:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2618, '客户信息', '', 2, 4, 2617, 'customer', 'ep:avatar', 'erp/sale/customer/index', 'ErpCustomer', 0, '1', '1', '1', '', '2024-02-07 07:21:45', '1', '2024-02-07 15:22:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2619, '客户查询', 'erp:customer:query', 3, 1, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2620, '客户创建', 'erp:customer:create', 3, 2, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2621, '客户更新', 'erp:customer:update', 3, 3, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2622, '客户删除', 'erp:customer:delete', 3, 4, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2623, '客户导出', 'erp:customer:export', 3, 5, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2624, '库存调拨', '', 2, 5, 2583, 'move', 'ep:folder-remove', 'erp/stock/move/index', 'ErpStockMove', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-16 18:53:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2625, '库存调度单查询', 'erp:stock-move:query', 3, 1, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2626, '库存调度单创建', 'erp:stock-move:create', 3, 2, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2627, '库存调度单更新', 'erp:stock-move:update', 3, 3, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2628, '库存调度单删除', 'erp:stock-move:delete', 3, 4, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2629, '库存调度单导出', 'erp:stock-move:export', 3, 5, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2630, '库存调度单审批', 'erp:stock-move:update-status', 3, 6, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2631, '库存盘点', '', 2, 6, 2583, 'check', 'ep:circle-check-filled', 'erp/stock/check/index', 'ErpStockCheck', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-08 08:31:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2632, '库存盘点单查询', 'erp:stock-check:query', 3, 1, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2633, '库存盘点单创建', 'erp:stock-check:create', 3, 2, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2634, '库存盘点单更新', 'erp:stock-check:update', 3, 3, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2635, '库存盘点单删除', 'erp:stock-check:delete', 3, 4, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2636, '库存盘点单导出', 'erp:stock-check:export', 3, 5, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2637, '库存盘点单审批', 'erp:stock-check:update-status', 3, 6, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2638, '销售订单', '', 2, 1, 2617, 'order', 'fa:first-order', 'erp/sale/order/index', 'ErpSaleOrder', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-10 21:59:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2639, '销售订单查询', 'erp:sale-order:query', 3, 1, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2640, '销售订单创建', 'erp:sale-order:create', 3, 2, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2641, '销售订单更新', 'erp:sale-order:update', 3, 3, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2642, '销售订单删除', 'erp:sale-order:delete', 3, 4, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2643, '销售订单导出', 'erp:sale-order:export', 3, 5, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2644, '销售订单审批', 'erp:sale-order:update-status', 3, 6, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2645, '财务管理', '', 1, 50, 2563, 'finance', 'ep:money', '', '', 0, '1', '1', '1', '1', '2024-02-10 08:05:58', '1', '2024-02-10 08:06:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2646, '结算账户', '', 2, 10, 2645, 'account', 'fa:universal-access', 'erp/finance/account/index', 'ErpAccount', 0, '1', '1', '1', '', '2024-02-10 00:15:07', '1', '2024-02-14 08:24:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2647, '结算账户查询', 'erp:account:query', 3, 1, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2648, '结算账户创建', 'erp:account:create', 3, 2, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2649, '结算账户更新', 'erp:account:update', 3, 3, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2650, '结算账户删除', 'erp:account:delete', 3, 4, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2651, '结算账户导出', 'erp:account:export', 3, 5, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2652, '销售出库', '', 2, 2, 2617, 'out', 'ep:sold-out', 'erp/sale/out/index', 'ErpSaleOut', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-10 22:02:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2653, '销售出库查询', 'erp:sale-out:query', 3, 1, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2654, '销售出库创建', 'erp:sale-out:create', 3, 2, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2655, '销售出库更新', 'erp:sale-out:update', 3, 3, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2656, '销售出库删除', 'erp:sale-out:delete', 3, 4, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2657, '销售出库导出', 'erp:sale-out:export', 3, 5, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2658, '销售出库审批', 'erp:sale-out:update-status', 3, 6, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2659, '销售退货', '', 2, 3, 2617, 'return', 'fa-solid:bone', 'erp/sale/return/index', 'ErpSaleReturn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 06:12:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2660, '销售退货查询', 'erp:sale-return:query', 3, 1, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2661, '销售退货创建', 'erp:sale-return:create', 3, 2, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2662, '销售退货更新', 'erp:sale-return:update', 3, 3, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2663, '销售退货删除', 'erp:sale-return:delete', 3, 4, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2664, '销售退货导出', 'erp:sale-return:export', 3, 5, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2665, '销售退货审批', 'erp:sale-return:update-status', 3, 6, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2666, '采购订单', '', 2, 1, 2602, 'order', 'fa-solid:border-all', 'erp/purchase/order/index', 'ErpPurchaseOrder', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 08:51:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2667, '采购订单查询', 'erp:purchase-order:query', 3, 1, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2668, '采购订单创建', 'erp:purchase-order:create', 3, 2, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2669, '采购订单更新', 'erp:purchase-order:update', 3, 3, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2670, '采购订单删除', 'erp:purchase-order:delete', 3, 4, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2671, '采购订单导出', 'erp:purchase-order:export', 3, 5, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2672, '采购订单审批', 'erp:purchase-order:update-status', 3, 6, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2673, '采购入库', '', 2, 2, 2602, 'in', 'fa-solid:gopuram', 'erp/purchase/in/index', 'ErpPurchaseIn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 11:19:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2674, '采购入库查询', 'erp:purchase-in:query', 3, 1, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2675, '采购入库创建', 'erp:purchase-in:create', 3, 2, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2676, '采购入库更新', 'erp:purchase-in:update', 3, 3, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2677, '采购入库删除', 'erp:purchase-in:delete', 3, 4, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2678, '采购入库导出', 'erp:purchase-in:export', 3, 5, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2679, '采购入库审批', 'erp:purchase-in:update-status', 3, 6, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2680, '采购退货', '', 2, 3, 2602, 'return', 'ep:minus', 'erp/purchase/return/index', 'ErpPurchaseReturn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 20:51:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2681, '采购退货查询', 'erp:purchase-return:query', 3, 1, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2682, '采购退货创建', 'erp:purchase-return:create', 3, 2, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2683, '采购退货更新', 'erp:purchase-return:update', 3, 3, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2684, '采购退货删除', 'erp:purchase-return:delete', 3, 4, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2685, '采购退货导出', 'erp:purchase-return:export', 3, 5, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2686, '采购退货审批', 'erp:purchase-return:update-status', 3, 6, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2687, '付款单', '', 2, 1, 2645, 'payment', 'ep:caret-right', 'erp/finance/payment/index', 'ErpFinancePayment', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-14 08:24:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2688, '付款单查询', 'erp:finance-payment:query', 3, 1, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2689, '付款单创建', 'erp:finance-payment:create', 3, 2, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2690, '付款单更新', 'erp:finance-payment:update', 3, 3, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2691, '付款单删除', 'erp:finance-payment:delete', 3, 4, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2692, '付款单导出', 'erp:finance-payment:export', 3, 5, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2693, '付款单审批', 'erp:finance-payment:update-status', 3, 6, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2694, '收款单', '', 2, 2, 2645, 'receipt', 'ep:expand', 'erp/finance/receipt/index', 'ErpFinanceReceipt', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-15 19:35:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2695, '收款单查询', 'erp:finance-receipt:query', 3, 1, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2696, '收款单创建', 'erp:finance-receipt:create', 3, 2, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2697, '收款单更新', 'erp:finance-receipt:update', 3, 3, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2698, '收款单删除', 'erp:finance-receipt:delete', 3, 4, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2699, '收款单导出', 'erp:finance-receipt:export', 3, 5, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2700, '收款单审批', 'erp:finance-receipt:update-status', 3, 6, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2701, '待办事项', '', 2, 0, 2397, 'backlog', 'fa-solid:tasks', 'crm/backlog/index', 'CrmBacklog', 0, '1', '1', '1', '1', '2024-02-17 17:17:11', '1', '2024-02-17 17:17:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2702, 'ERP 首页', 'erp:statistics:query', 2, 0, 2563, 'home', 'ep:home-filled', 'erp/home/index.vue', 'ErpHome', 0, '1', '1', '1', '1', '2024-02-18 16:49:40', '1', '2024-02-26 21:12:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2703, '商机状态配置', '', 2, 4, 2524, 'business-status', 'fa-solid:charging-station', 'crm/business/status/index', 'CrmBusinessStatus', 0, '1', '1', '1', '1', '2024-02-21 20:15:17', '1', '2024-02-21 20:15:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2704, '商机状态查询', 'crm:business-status:query', 3, 1, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:35:36', '1', '2024-02-21 20:36:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2705, '商机状态创建', 'crm:business-status:create', 3, 2, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:35:57', '1', '2024-02-21 20:35:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2706, '商机状态更新', 'crm:business-status:update', 3, 3, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:36:21', '1', '2024-02-21 20:36:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2707, '商机状态删除', 'crm:business-status:delete', 3, 4, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:36:36', '1', '2024-02-21 20:36:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2708, '合同配置', '', 2, 5, 2524, 'contract-config', 'ep:connection', 'crm/contract/config/index', 'CrmContractConfig', 0, '1', '1', '1', '1', '2024-02-24 16:44:40', '1', '2024-02-24 16:44:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2709, '客户公海配置查询', 'crm:customer-pool-config:query', 3, 2, 2516, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:45:19', '1', '2024-02-24 16:45:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2710, '合同配置更新', 'crm:contract-config:update', 3, 1, 2708, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:45:56', '1', '2024-02-24 16:45:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2711, '合同配置查询', 'crm:contract-config:query', 3, 2, 2708, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:46:16', '1', '2024-02-24 16:46:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2712, '客户分析', 'crm:statistics-customer:query', 2, 0, 2560, 'customer', 'ep:avatar', 'crm/statistics/customer/index.vue', 'CrmStatisticsCustomer', 0, '1', '1', '1', '1', '2024-03-09 16:43:56', '1', '2024-05-04 20:38:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2713, '抄送我的', 'bpm:process-instance-cc:query', 2, 30, 1200, 'copy', 'ep:copy-document', 'bpm/task/copy/index', 'BpmProcessInstanceCopy', 0, '1', '1', '1', '1', '2024-03-17 21:50:23', '1', '2024-04-24 19:55:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2714, '流程分类', '', 2, 3, 1186, 'category', 'fa:object-ungroup', 'bpm/category/index', 'BpmCategory', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-21 23:51:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2715, '分类查询', 'bpm:category:query', 3, 1, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2716, '分类创建', 'bpm:category:create', 3, 2, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2717, '分类更新', 'bpm:category:update', 3, 3, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2718, '分类删除', 'bpm:category:delete', 3, 4, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2720, '发起流程', '', 2, 0, 1200, 'create', 'fa-solid:grin-stars', 'bpm/processInstance/create/index', 'BpmProcessInstanceCreate', 0, '1', '0', '1', '1', '2024-03-19 19:46:05', '1', '2024-03-23 19:03:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2721, '流程实例', '', 2, 10, 1186, 'process-instance/manager', 'fa:square', 'bpm/processInstance/manager/index', 'BpmProcessInstanceManager', 0, '1', '1', '1', '1', '2024-03-21 23:57:30', '1', '2024-03-21 23:57:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2722, '流程实例的查询（管理员）', 'bpm:process-instance:manager-query', 3, 1, 2721, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:18:27', '1', '2024-03-22 08:19:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2723, '流程实例的取消（管理员）', 'bpm:process-instance:cancel-by-admin', 3, 2, 2721, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:19:25', '1', '2024-03-22 08:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2724, '流程任务', '', 2, 11, 1186, 'process-tasnk', 'ep:collection-tag', 'bpm/task/manager/index', 'BpmManagerTask', 0, '1', '1', '1', '1', '2024-03-22 08:43:22', '1', '2024-03-22 08:43:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2725, '流程任务的查询（管理员）', 'bpm:task:mananger-query', 3, 1, 2724, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:43:49', '1', '2024-03-22 08:43:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2726, '流程监听器', '', 2, 5, 1186, 'process-listener', 'fa:assistive-listening-systems', 'bpm/processListener/index', 'BpmProcessListener', 0, '1', '1', '1', '', '2024-03-09 16:05:34', '1', '2024-03-23 13:13:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2727, '流程监听器查询', 'bpm:process-listener:query', 3, 1, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2728, '流程监听器创建', 'bpm:process-listener:create', 3, 2, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2729, '流程监听器更新', 'bpm:process-listener:update', 3, 3, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2730, '流程监听器删除', 'bpm:process-listener:delete', 3, 4, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2731, '流程表达式', '', 2, 6, 1186, 'process-expression', 'fa:wpexplorer', 'bpm/processExpression/index', 'BpmProcessExpression', 0, '1', '1', '1', '', '2024-03-09 22:35:08', '1', '2024-03-23 19:43:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2732, '流程表达式查询', 'bpm:process-expression:query', 3, 1, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2733, '流程表达式创建', 'bpm:process-expression:create', 3, 2, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2734, '流程表达式更新', 'bpm:process-expression:update', 3, 3, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2735, '流程表达式删除', 'bpm:process-expression:delete', 3, 4, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2736, '员工业绩', 'crm:statistics-performance:query', 2, 3, 2560, 'performance', 'ep:dish-dot', 'crm/statistics/performance/index', 'CrmStatisticsPerformance', 0, '1', '1', '1', '1', '2024-04-05 13:49:20', '1', '2024-04-24 19:42:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2737, '客户画像', 'crm:statistics-portrait:query', 2, 4, 2560, 'portrait', 'ep:picture', 'crm/statistics/portrait/index', 'CrmStatisticsPortrait', 0, '1', '1', '1', '1', '2024-04-05 13:57:40', '1', '2024-04-24 19:42:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2738, '销售漏斗', 'crm:statistics-funnel:query', 2, 5, 2560, 'funnel', 'ep:grape', 'crm/statistics/funnel/index', 'CrmStatisticsFunnel', 0, '1', '1', '1', '1', '2024-04-13 10:53:26', '1', '2024-04-24 19:39:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2739, '消息中心', '', 1, 7, 1, 'messages', 'ep:chat-dot-round', '', '', 0, '1', '1', '1', '1', '2024-04-22 23:54:30', '1', '2024-04-23 09:36:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2740, '监控中心', '', 1, 10, 2, 'monitors', 'ep:monitor', '', '', 0, '1', '1', '1', '1', '2024-04-23 00:04:44', '1', '2024-04-23 00:04:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2741, '领取公海客户', 'crm:customer:receive', 3, 1, 2546, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:47:45', '1', '2024-04-24 19:47:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2742, '分配公海客户', 'crm:customer:distribute', 3, 2, 2546, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:48:05', '1', '2024-04-24 19:48:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2743, '商品统计查询', 'statistics:product:query', 3, 1, 2545, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:50:05', '1', '2024-04-24 19:50:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2744, '商品统计导出', 'statistics:product:export', 3, 2, 2545, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:50:26', '1', '2024-04-24 19:50:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2745, '支付渠道查询', 'pay:channel:query', 3, 10, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:01', '1', '2024-04-24 19:53:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2746, '支付渠道创建', 'pay:channel:create', 3, 11, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:18', '1', '2024-04-24 19:53:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2747, '支付渠道更新', 'pay:channel:update', 3, 12, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:32', '1', '2024-04-24 19:53:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2748, '支付渠道删除', 'pay:channel:delete', 3, 13, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:54:34', '1', '2024-04-24 19:54:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2749, '商品收藏查询', 'product:favorite:query', 3, 10, 2014, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:55:47', '1', '2024-04-24 19:55:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2750, '商品浏览查询', 'product:browse-history:query', 3, 20, 2014, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:57:43', '1', '2024-04-24 19:57:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2751, '售后同意', 'trade:after-sale:agree', 3, 2, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:58:40', '1', '2024-04-24 19:58:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2752, '售后不同意', 'trade:after-sale:disagree', 3, 3, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:59:03', '1', '2024-04-24 19:59:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2753, '售后确认退货', 'trade:after-sale:receive', 3, 4, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:00:07', '1', '2024-04-24 20:00:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2754, '售后确认退款', 'trade:after-sale:refund', 3, 5, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:00:24', '1', '2024-04-24 20:00:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2755, '删除项目', 'report:go-view-project:delete', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:01:37', '1', '2024-04-24 20:01:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2756, '会员等级记录查询', 'member:level-record:query', 3, 10, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:32', '1', '2024-04-24 20:02:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2757, '会员经验记录查询', 'member:experience-record:query', 3, 11, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:51', '1', '2024-04-24 20:02:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2758, 'AI 大模型', '', 1, 400, 0, '/ai', 'tabler:ai', '', '', 0, '1', '1', '1', '1', '2024-05-07 15:07:56', '1', '2025-04-19 18:57:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2759, 'AI 对话', '', 2, 1, 2758, 'chat', 'ep:message', 'ai/chat/index/index.vue', 'AiChat', 0, '1', '1', '1', '1', '2024-05-07 15:09:14', '1', '2024-07-07 17:15:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2760, '控制台', '', 1, 100, 2758, 'console', 'ep:setting', '', '', 0, '1', '1', '1', '1', '2024-05-09 22:39:09', '1', '2024-05-24 23:34:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2761, 'API 密钥', '', 2, 0, 2760, 'api-key', 'ep:key', 'ai/model/apiKey/index.vue', 'AiApiKey', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-10 22:44:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2762, 'API 密钥查询', 'ai:api-key:query', 3, 1, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2767, '模型配置', '', 2, 0, 2760, 'model', 'fa-solid:abacus', 'ai/model/model/index.vue', 'AiModel', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:57:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2768, '聊天模型查询', 'ai:model:query', 3, 1, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:19:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2769, '聊天模型创建', 'ai:model:create', 3, 2, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2770, '聊天模型更新', 'ai:model:update', 3, 3, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2771, '聊天模型删除', 'ai:model:delete', 3, 4, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, '1', '1', '1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2776, '聊天角色更新', 'ai:chat-role:update', 3, 3, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2777, '聊天角色删除', 'ai:chat-role:delete', 3, 4, 2773, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-13 21:43:38', '1', '2024-05-13 21:43:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2778, '聊天管理', '', 2, 10, 2760, 'chat-conversation', 'ep:chat-square', 'ai/chat/manager/index.vue', 'AiChatManager', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-06-26 21:36:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2779, '会话查询', 'ai:chat-conversation:query', 3, 1, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2780, '会话删除', 'ai:chat-conversation:delete', 3, 2, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2781, '消息查询', 'ai:chat-message:query', 3, 11, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:38:56', '1', '2024-05-25 08:38:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2782, '消息删除', 'ai:chat-message:delete', 3, 12, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:39:10', '1', '2024-05-25 08:39:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2783, 'AI 绘画', '', 2, 2, 2758, 'image', 'ep:picture-rounded', 'ai/image/index/index.vue', 'AiImage', 0, '1', '1', '1', '1', '2024-05-26 11:45:17', '1', '2024-07-07 17:18:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2784, '绘画管理', '', 2, 11, 2760, 'image', 'fa:file-image-o', 'ai/image/manager/index.vue', 'AiImageManager', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 21:37:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2785, '绘画查询', 'ai:image:query', 3, 1, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:21:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2786, '绘画删除', 'ai:image:delete', 3, 4, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:22:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2787, '绘图更新', 'ai:image:update', 3, 2, 2784, '', '', '', '', 0, '1', '1', '1', '1', '2024-06-26 22:47:56', '1', '2024-08-31 09:21:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2788, '音乐管理', '', 2, 12, 2760, 'music', 'fa:music', 'ai/music/manager/index.vue', 'AiMusicManager', 0, '1', '1', '1', '', '2024-06-27 15:03:33', '1', '2024-06-27 23:04:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2789, '音乐查询', 'ai:music:query', 3, 1, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2790, '音乐更新', 'ai:music:update', 3, 3, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2791, '音乐删除', 'ai:music:delete', 3, 4, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2792, 'AI 写作', '', 2, 3, 2758, 'write', 'fa-solid:book-reader', 'ai/write/index/index.vue', 'AiWrite', 0, '1', '1', '1', '1', '2024-07-08 09:26:44', '1', '2024-07-16 13:03:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2793, '写作管理', '', 2, 13, 2760, 'write', 'fa:bookmark-o', 'ai/write/manager/index.vue', 'AiWriteManager', 0, '1', '1', '1', '', '2024-07-10 13:24:34', '1', '2024-07-10 21:31:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2794, 'AI 写作查询', 'ai:write:query', 3, 1, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, '1', '1', '1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, '1', '1', '1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2798, 'AI 思维导图', '', 2, 6, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, '1', '1', '1', '1', '2024-07-29 21:31:59', '1', '2025-03-02 18:57:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, '1', '1', '1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2802, '会话查询', 'promotion:kefu-conversation:query', 3, 1, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:17:52', '1', '2024-08-31 09:18:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2803, '会话更新', 'promotion:kefu-conversation:update', 3, 2, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:15', '1', '2024-08-31 09:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2804, '消息查询', 'promotion:kefu-message:query', 3, 10, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:42', '1', '2024-08-31 09:18:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2805, '会话删除', 'promotion:kefu-conversation:delete', 3, 3, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:19:51', '1', '2024-08-31 09:20:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2806, '消息发送', 'promotion:kefu-message:send', 3, 12, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:06', '1', '2024-08-31 09:20:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2807, '消息更新', 'promotion:kefu-message:update', 3, 11, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:22', '1', '2024-08-31 09:20:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2808, '积分商城', '', 2, 5, 2030, 'point-activity', 'ep:bowl', 'mall/promotion/point/activity/index', 'PointActivity', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-23 09:14:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2809, '积分商城活动查询', 'promotion:point-activity:query', 3, 1, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2810, '积分商城活动创建', 'promotion:point-activity:create', 3, 2, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2811, '积分商城活动更新', 'promotion:point-activity:update', 3, 3, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2812, '积分商城活动删除', 'promotion:point-activity:delete', 3, 4, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2813, '积分商城活动导出', 'promotion:point-activity:export', 3, 5, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, '1', '1', '1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-17 19:32:06', '1', '2025-01-17 19:32:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-23 20:23:34', '1', '2025-01-23 20:23:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2915, 'AI 知识库', '', 2, 5, 2758, 'knowledge', 'ep:notebook', 'ai/knowledge/knowledge/index', 'AiKnowledge', 0, '1', '1', '1', '', '2025-02-28 07:04:21', '1', '2025-03-02 18:58:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2916, 'AI 知识库查询', 'ai:knowledge:query', 3, 1, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2917, 'AI 知识库创建', 'ai:knowledge:create', 3, 2, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2918, 'AI 知识库更新', 'ai:knowledge:update', 3, 3, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2919, 'AI 知识库删除', 'ai:knowledge:delete', 3, 4, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2920, '工具管理', '', 2, 0, 2760, 'tool', 'fa-solid:tools', 'ai/model/tool/index.vue', 'AiTool', 0, '1', '1', '1', '', '2025-03-14 11:19:29', '1', '2025-03-14 19:20:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2921, '工具查询', 'ai:tool:query', 3, 1, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4000, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, '1', '1', '1', '1', '2024-08-10 09:55:28', '1', '2024-12-07 15:58:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4001, '设备接入', '', 1, 2, 4000, 'device', 'ep:platform', '', '', 0, '1', '1', '1', '1', '2024-08-10 09:57:56', '1', '2025-02-27 08:39:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4002, '产品管理', '', 2, 2, 4001, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, '1', '1', '1', '', '2024-08-10 02:38:02', '1', '2024-12-07 18:47:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4003, '产品查询', 'iot:product:query', 3, 1, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4004, '产品创建', 'iot:product:create', 3, 2, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4005, '产品更新', 'iot:product:update', 3, 3, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4006, '产品删除', 'iot:product:delete', 3, 4, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4007, '产品导出', 'iot:product:export', 3, 5, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4008, '设备管理', '', 2, 4, 4001, 'device', 'fa:mobile', 'iot/device/device/index', 'IoTDevice', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-14 11:39:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4009, '设备查询', 'iot:device:query', 3, 1, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4010, '设备创建', 'iot:device:create', 3, 2, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4011, '设备更新', 'iot:device:update', 3, 3, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4012, '设备删除', 'iot:device:delete', 3, 4, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4013, '设备导出', 'iot:device:export', 3, 5, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4014, '产品分类', '', 2, 1, 4001, 'product-category', 'ep:notebook', 'iot/product/category/index', 'IotProductCategory', 0, '1', '1', '1', '', '2024-12-07 16:01:35', '1', '2024-12-07 16:31:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4015, '产品分类查询', 'iot:product-category:query', 3, 1, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4016, '产品分类创建', 'iot:product-category:create', 3, 2, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4017, '产品分类更新', 'iot:product-category:update', 3, 3, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4018, '产品分类删除', 'iot:product-category:delete', 3, 4, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4025, '插件管理', '', 2, 5, 4047, 'plugin-config', 'ep:folder-opened', 'iot/plugin/index', 'IoTPlugin', 0, '1', '1', '1', '', '2024-12-09 21:25:06', '1', '2025-02-05 22:23:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4026, '插件查询', 'iot:plugin-config:query', 3, 1, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4027, '插件创建', 'iot:plugin-config:create', 3, 2, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4028, '插件更新', 'iot:plugin-config:update', 3, 3, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4029, '插件删除', 'iot:plugin-config:delete', 3, 4, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4030, '插件导出', 'iot:plugin-config:export', 3, 5, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4031, '设备分组', '', 2, 3, 4001, 'device-group', 'fa-solid:layer-group', 'iot/device/group/index', 'IotDeviceGroup', 0, '1', '1', '1', '', '2024-12-14 17:08:29', '1', '2024-12-14 17:09:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4032, '设备分组查询', 'iot:device-group:query', 3, 1, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4033, '设备分组创建', 'iot:device-group:create', 3, 2, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4034, '设备分组更新', 'iot:device-group:update', 3, 3, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4035, '设备分组删除', 'iot:device-group:delete', 3, 4, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4036, '设备导入', 'iot:device:import', 3, 6, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2024-12-15 10:35:47', '1', '2024-12-15 10:35:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4037, '产品物模型', '', 2, 2, 4001, 'thing-model', 'ep:mostly-cloudy', 'iot/thingmodel/index', 'IoTThingModel', 0, '0', '0', '0', '', '2024-12-16 17:17:50', '1', '2024-12-27 11:03:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4038, '产品物模型功能查询', 'iot:thing-model:query', 3, 1, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:51', '', '2025-03-17 09:14:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4039, '产品物模型功能创建', 'iot:thing-model:create', 3, 2, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:14:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4040, '产品物模型功能更新', 'iot:thing-model:update', 3, 3, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4041, '产品物模型功能删除', 'iot:thing-model:delete', 3, 4, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4042, '产品物模型功能导出', 'iot:thing-model:export', 3, 5, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:53', '', '2025-03-17 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4043, '设备上行', 'iot:device:upstream', 3, 7, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 04:40:16', '1', '2025-01-31 22:45:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4044, '设备属性查询', 'iot:device:property-query', 3, 10, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 11:52:54', '1', '2025-01-28 11:52:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4045, '设备日志查询', 'iot:device:log-query', 3, 11, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 11:53:22', '1', '2025-01-28 11:53:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4046, '设备下行', 'iot:device:downstream', 3, 8, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-31 22:46:11', '1', '2025-01-31 22:46:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4047, '运维管理', '', 1, 2, 4000, 'operations', 'fa:cog', '', '', 0, '1', '1', '1', '1', '2025-02-05 22:21:37', '1', '2025-02-05 22:22:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4048, '规则引擎', '', 1, 3, 4000, 'rule', 'fa-solid:cogs', '', '', 0, '1', '1', '1', '1', '2025-02-11 14:10:54', '1', '2025-02-11 14:10:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4049, '场景联动', '', 2, 1, 4048, 'scene', 'ep:link', 'iot/rule/scene/index', 'Scene', 0, '1', '1', '1', '1', '2025-02-11 14:12:44', '1', '2025-02-12 10:15:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4050, 'IoT首页', '', 2, 1, 4000, 'home', 'ep:home-filled', 'iot/home/index', 'IotHome', 0, '1', '1', '1', '1', '2025-02-27 08:39:35', '1', '2025-02-27 08:40:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4051, '数据桥梁', '', 2, 0, 4048, 'data-bridge', 'ep:guide', 'iot/rule/databridge/index', 'IotDataBridge', 0, '1', '1', '1', '', '2025-03-09 13:47:11', '1', '2025-03-09 13:47:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4052, 'IoT 数据桥梁查询', 'iot:data-bridge:query', 3, 1, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4053, 'IoT 数据桥梁创建', 'iot:data-bridge:create', 3, 2, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4054, 'IoT 数据桥梁更新', 'iot:data-bridge:update', 3, 3, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4055, 'IoT 数据桥梁删除', 'iot:data-bridge:delete', 3, 4, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4056, 'IoT 数据桥梁导出', 'iot:data-bridge:export', 3, 5, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5000, 'AI 工作流', '', 2, 5, 2758, 'workflow', 'fa:hand-grab-o', 'ai/workflow/index.vue', 'AiWorkflow', 0, '1', '1', '1', '1', '2025-03-25 09:50:27', '1', '2025-05-03 18:55:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5001, 'AI 工作流查询', 'ai:workflow:query', 3, 1, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:11', '1', '2025-03-25 09:51:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5002, 'AI 工作流创建', 'ai:workflow:create', 3, 2, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:28', '1', '2025-03-25 09:51:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5003, 'AI 工作流更新', 'ai:workflow:update', 3, 3, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:42', '1', '2025-03-25 09:51:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5004, 'AI 工作流删除', 'ai:workflow:delete', 3, 4, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:55', '1', '2025-03-25 09:52:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5005, 'AI 工作流测试', 'ai:workflow:test', 3, 5, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-30 10:29:41', '1', '2025-03-30 10:29:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5009, '仪表盘设计器', '', 2, 1, 1281, 'jimu-bi', 'fa:y-combinator', 'report/jmreport/bi', 'JimuBI', 0, '1', '1', '1', '1', '2025-05-03 09:57:15', '1', '2025-05-03 10:02:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5010, '租户切换', 'system:tenant:visit', 3, 999, 1138, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-05 15:25:32', '1', '2025-05-05 15:25:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5011, '转账订单查询', 'pay:transfer:query', 3, 1, 2559, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-08 12:46:53', '1', '2025-05-08 12:46:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5012, '转账订单导出', 'pay:transfer:export', 3, 2, 2559, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-10 17:00:28', '1', '2025-05-10 17:00:28', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_menu_seq;\nCREATE SEQUENCE system_menu_seq\n    START 5013;\n\n-- ----------------------------\n-- Table structure for system_notice\n-- ----------------------------\nDROP TABLE IF EXISTS system_notice;\nCREATE TABLE system_notice\n(\n    id          int8        NOT NULL,\n    title       varchar(50) NOT NULL,\n    content     text        NULL,\n    type        int2        NOT NULL,\n    status      int2        NOT NULL DEFAULT 0,\n    creator     varchar(64) NULL     DEFAULT '',\n    create_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64) NULL     DEFAULT '',\n    update_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2        NOT NULL DEFAULT 0,\n    tenant_id   int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_notice\n    ADD CONSTRAINT pk_system_notice PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notice.id IS '公告ID';\nCOMMENT ON COLUMN system_notice.title IS '公告标题';\nCOMMENT ON COLUMN system_notice.content IS '公告内容';\nCOMMENT ON COLUMN system_notice.type IS '公告类型（1通知 2公告）';\nCOMMENT ON COLUMN system_notice.status IS '公告状态（0正常 1关闭）';\nCOMMENT ON COLUMN system_notice.creator IS '创建者';\nCOMMENT ON COLUMN system_notice.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notice.updater IS '更新者';\nCOMMENT ON COLUMN system_notice.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notice.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notice.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notice IS '通知公告表';\n\n-- ----------------------------\n-- Records of system_notice\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '芋道的公众', '<p>新版本内容133</p>', 1, 0, 'admin', '2021-01-05 17:03:48', '1', '2022-05-04 21:00:20', '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '维护通知：2018-07-01 系统凌晨维护', '<p><img src=\"http://test.yudao.iocoder.cn/b7cb3cf49b4b3258bf7309a09dd2f4e5.jpg\" alt=\"\" data-href=\"\">11112222<img src=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\" alt=\"image\" data-href=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\">3333</p>', 2, 1, 'admin', '2021-01-05 17:03:48', '1', '2025-04-18 23:56:40', '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '我是测试标题', '<p>哈哈哈哈123</p>', 1, 0, '110', '2022-02-22 01:01:25', '110', '2022-02-22 01:01:46', '0', 121);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_notice_seq;\nCREATE SEQUENCE system_notice_seq\n    START 5;\n\n-- ----------------------------\n-- Table structure for system_notify_message\n-- ----------------------------\nDROP TABLE IF EXISTS system_notify_message;\nCREATE TABLE system_notify_message\n(\n    id                int8          NOT NULL,\n    user_id           int8          NOT NULL,\n    user_type         int2          NOT NULL,\n    template_id       int8          NOT NULL,\n    template_code     varchar(64)   NOT NULL,\n    template_nickname varchar(63)   NOT NULL,\n    template_content  varchar(1024) NOT NULL,\n    template_type     int4          NOT NULL,\n    template_params   varchar(255)  NOT NULL,\n    read_status       bool          NOT NULL,\n    read_time         timestamp     NULL     DEFAULT NULL,\n    creator           varchar(64)   NULL     DEFAULT '',\n    create_time       timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater           varchar(64)   NULL     DEFAULT '',\n    update_time       timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted           int2          NOT NULL DEFAULT 0,\n    tenant_id         int8          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_notify_message\n    ADD CONSTRAINT pk_system_notify_message PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notify_message.id IS '用户ID';\nCOMMENT ON COLUMN system_notify_message.user_id IS '用户id';\nCOMMENT ON COLUMN system_notify_message.user_type IS '用户类型';\nCOMMENT ON COLUMN system_notify_message.template_id IS '模版编号';\nCOMMENT ON COLUMN system_notify_message.template_code IS '模板编码';\nCOMMENT ON COLUMN system_notify_message.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_notify_message.template_content IS '模版内容';\nCOMMENT ON COLUMN system_notify_message.template_type IS '模版类型';\nCOMMENT ON COLUMN system_notify_message.template_params IS '模版参数';\nCOMMENT ON COLUMN system_notify_message.read_status IS '是否已读';\nCOMMENT ON COLUMN system_notify_message.read_time IS '阅读时间';\nCOMMENT ON COLUMN system_notify_message.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_message.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_message.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_message.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_message.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notify_message.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notify_message IS '站内信消息表';\n\n-- ----------------------------\n-- Records of system_notify_message\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 11:44:08', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 11:45:04', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 103, 2, 2, 'register', '系统消息', '你好，欢迎 哈哈 加入大家庭！', 2, '{\"name\":\"哈哈\"}', '0', NULL, '1', '2023-01-28 21:02:20', '1', '2023-01-28 21:02:20', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 22:21:42', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', '2025-04-21 14:59:36', '1', '2023-01-28 22:22:07', '1', '2025-04-21 14:59:36', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 1, 2, 1, 'test', '123', '我是 2，我开始 3 了', 1, '{\"name\":\"2\",\"what\":\"3\"}', '1', '2025-04-21 14:59:35', '1', '2023-01-28 23:45:21', '1', '2025-04-21 14:59:35', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 1, 2, 2, 'register', '系统消息', '你好，欢迎 123 加入大家庭！', 2, '{\"name\":\"123\"}', '1', '2025-04-21 14:59:35', '1', '2023-01-28 23:50:21', '1', '2025-04-21 14:59:35', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-28 08:35:46提现￥0.09元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-28 08:35:46\",\"price\":\"0.09\"}', '0', NULL, '1', '2023-09-28 16:36:22', '1', '2023-09-28 16:36:22', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-30 20:59:40提现￥1.00元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-30 20:59:40\",\"price\":\"1.00\"}', '0', NULL, '1', '2023-10-03 12:11:34', '1', '2023-10-03 12:11:34', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_notify_message_seq;\nCREATE SEQUENCE system_notify_message_seq\n    START 11;\n\n-- ----------------------------\n-- Table structure for system_notify_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_notify_template;\nCREATE TABLE system_notify_template\n(\n    id          int8          NOT NULL,\n    name        varchar(63)   NOT NULL,\n    code        varchar(64)   NOT NULL,\n    nickname    varchar(255)  NOT NULL,\n    content     varchar(1024) NOT NULL,\n    type        int2          NOT NULL,\n    params      varchar(255)  NULL     DEFAULT NULL,\n    status      int2          NOT NULL,\n    remark      varchar(255)  NULL     DEFAULT NULL,\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_notify_template\n    ADD CONSTRAINT pk_system_notify_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notify_template.id IS '主键';\nCOMMENT ON COLUMN system_notify_template.name IS '模板名称';\nCOMMENT ON COLUMN system_notify_template.code IS '模版编码';\nCOMMENT ON COLUMN system_notify_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_notify_template.content IS '模版内容';\nCOMMENT ON COLUMN system_notify_template.type IS '类型';\nCOMMENT ON COLUMN system_notify_template.params IS '参数数组';\nCOMMENT ON COLUMN system_notify_template.status IS '状态';\nCOMMENT ON COLUMN system_notify_template.remark IS '备注';\nCOMMENT ON COLUMN system_notify_template.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_template.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_notify_template IS '站内信模板表';\n\nDROP SEQUENCE IF EXISTS system_notify_template_seq;\nCREATE SEQUENCE system_notify_template_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_access_token\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_access_token;\nCREATE TABLE system_oauth2_access_token\n(\n    id            int8         NOT NULL,\n    user_id       int8         NOT NULL,\n    user_type     int2         NOT NULL,\n    user_info     varchar(512) NOT NULL,\n    access_token  varchar(255) NOT NULL,\n    refresh_token varchar(32)  NOT NULL,\n    client_id     varchar(255) NOT NULL,\n    scopes        varchar(255) NULL     DEFAULT NULL,\n    expires_time  timestamp    NOT NULL,\n    creator       varchar(64)  NULL     DEFAULT '',\n    create_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater       varchar(64)  NULL     DEFAULT '',\n    update_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted       int2         NOT NULL DEFAULT 0,\n    tenant_id     int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_access_token\n    ADD CONSTRAINT pk_system_oauth2_access_token PRIMARY KEY (id);\n\nCREATE INDEX idx_system_oauth2_access_token_01 ON system_oauth2_access_token (access_token);\nCREATE INDEX idx_system_oauth2_access_token_02 ON system_oauth2_access_token (refresh_token);\n\nCOMMENT ON COLUMN system_oauth2_access_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_access_token.user_info IS '用户信息';\nCOMMENT ON COLUMN system_oauth2_access_token.access_token IS '访问令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_access_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_access_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_access_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_access_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_access_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_access_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_access_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_access_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_access_token IS 'OAuth2 访问令牌';\n\nDROP SEQUENCE IF EXISTS system_oauth2_access_token_seq;\nCREATE SEQUENCE system_oauth2_access_token_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_approve\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_approve;\nCREATE TABLE system_oauth2_approve\n(\n    id           int8         NOT NULL,\n    user_id      int8         NOT NULL,\n    user_type    int2         NOT NULL,\n    client_id    varchar(255) NOT NULL,\n    scope        varchar(255) NULL     DEFAULT '',\n    approved     bool         NOT NULL DEFAULT '0',\n    expires_time timestamp    NOT NULL,\n    creator      varchar(64)  NULL     DEFAULT '',\n    create_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater      varchar(64)  NULL     DEFAULT '',\n    update_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted      int2         NOT NULL DEFAULT 0,\n    tenant_id    int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_approve\n    ADD CONSTRAINT pk_system_oauth2_approve PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_approve.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_approve.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_approve.scope IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_approve.approved IS '是否接受';\nCOMMENT ON COLUMN system_oauth2_approve.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_approve.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_approve.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_approve.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_approve.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_approve.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_approve.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_approve IS 'OAuth2 批准表';\n\nDROP SEQUENCE IF EXISTS system_oauth2_approve_seq;\nCREATE SEQUENCE system_oauth2_approve_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_client\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_client;\nCREATE TABLE system_oauth2_client\n(\n    id                             int8          NOT NULL,\n    client_id                      varchar(255)  NOT NULL,\n    secret                         varchar(255)  NOT NULL,\n    name                           varchar(255)  NOT NULL,\n    logo                           varchar(255)  NOT NULL,\n    description                    varchar(255)  NULL     DEFAULT NULL,\n    status                         int2          NOT NULL,\n    access_token_validity_seconds  int4          NOT NULL,\n    refresh_token_validity_seconds int4          NOT NULL,\n    redirect_uris                  varchar(255)  NOT NULL,\n    authorized_grant_types         varchar(255)  NOT NULL,\n    scopes                         varchar(255)  NULL     DEFAULT NULL,\n    auto_approve_scopes            varchar(255)  NULL     DEFAULT NULL,\n    authorities                    varchar(255)  NULL     DEFAULT NULL,\n    resource_ids                   varchar(255)  NULL     DEFAULT NULL,\n    additional_information         varchar(4096) NULL     DEFAULT NULL,\n    creator                        varchar(64)   NULL     DEFAULT '',\n    create_time                    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater                        varchar(64)   NULL     DEFAULT '',\n    update_time                    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted                        int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_client\n    ADD CONSTRAINT pk_system_oauth2_client PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_client.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_client.secret IS '客户端密钥';\nCOMMENT ON COLUMN system_oauth2_client.name IS '应用名';\nCOMMENT ON COLUMN system_oauth2_client.logo IS '应用图标';\nCOMMENT ON COLUMN system_oauth2_client.description IS '应用描述';\nCOMMENT ON COLUMN system_oauth2_client.status IS '状态';\nCOMMENT ON COLUMN system_oauth2_client.access_token_validity_seconds IS '访问令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.refresh_token_validity_seconds IS '刷新令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.redirect_uris IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_client.authorized_grant_types IS '授权类型';\nCOMMENT ON COLUMN system_oauth2_client.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_client.auto_approve_scopes IS '自动通过的授权范围';\nCOMMENT ON COLUMN system_oauth2_client.authorities IS '权限';\nCOMMENT ON COLUMN system_oauth2_client.resource_ids IS '资源';\nCOMMENT ON COLUMN system_oauth2_client.additional_information IS '附加信息';\nCOMMENT ON COLUMN system_oauth2_client.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_client.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_client.deleted IS '是否删除';\nCOMMENT ON TABLE system_oauth2_client IS 'OAuth2 客户端表';\n\n-- ----------------------------\n-- Records of system_oauth2_client\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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\"]', '[\"user.read\",\"user.write\"]', '[]', '[\"user.read\",\"user.write\"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-05-02 20:42:22', '0');\nINSERT 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/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.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-05-02 19:58:08', '0');\nINSERT 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', '0');\nINSERT 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/604bdc695e13b3b22745be704d1f2aa8ee05c5f26f9fead6d1ca49005afbc857.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-05-04 16:00:46', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_oauth2_client_seq;\nCREATE SEQUENCE system_oauth2_client_seq\n    START 43;\n\n-- ----------------------------\n-- Table structure for system_oauth2_code\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_code;\nCREATE TABLE system_oauth2_code\n(\n    id           int8         NOT NULL,\n    user_id      int8         NOT NULL,\n    user_type    int2         NOT NULL,\n    code         varchar(32)  NOT NULL,\n    client_id    varchar(255) NOT NULL,\n    scopes       varchar(255) NULL     DEFAULT '',\n    expires_time timestamp    NOT NULL,\n    redirect_uri varchar(255) NULL     DEFAULT NULL,\n    state        varchar(255) NULL     DEFAULT '',\n    creator      varchar(64)  NULL     DEFAULT '',\n    create_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater      varchar(64)  NULL     DEFAULT '',\n    update_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted      int2         NOT NULL DEFAULT 0,\n    tenant_id    int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_code\n    ADD CONSTRAINT pk_system_oauth2_code PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_code.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_code.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_code.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_code.code IS '授权码';\nCOMMENT ON COLUMN system_oauth2_code.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_code.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_code.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_code.redirect_uri IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_code.state IS '状态';\nCOMMENT ON COLUMN system_oauth2_code.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_code.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_code IS 'OAuth2 授权码表';\n\nDROP SEQUENCE IF EXISTS system_oauth2_code_seq;\nCREATE SEQUENCE system_oauth2_code_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_refresh_token\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_refresh_token;\nCREATE TABLE system_oauth2_refresh_token\n(\n    id            int8         NOT NULL,\n    user_id       int8         NOT NULL,\n    refresh_token varchar(32)  NOT NULL,\n    user_type     int2         NOT NULL,\n    client_id     varchar(255) NOT NULL,\n    scopes        varchar(255) NULL     DEFAULT NULL,\n    expires_time  timestamp    NOT NULL,\n    creator       varchar(64)  NULL     DEFAULT '',\n    create_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater       varchar(64)  NULL     DEFAULT '',\n    update_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted       int2         NOT NULL DEFAULT 0,\n    tenant_id     int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_refresh_token\n    ADD CONSTRAINT pk_system_oauth2_refresh_token PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_refresh_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_refresh_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_refresh_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_refresh_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_refresh_token IS 'OAuth2 刷新令牌';\n\nDROP SEQUENCE IF EXISTS system_oauth2_refresh_token_seq;\nCREATE SEQUENCE system_oauth2_refresh_token_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_operate_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_operate_log;\nCREATE TABLE system_operate_log\n(\n    id             int8          NOT NULL,\n    trace_id       varchar(64)   NULL     DEFAULT '',\n    user_id        int8          NOT NULL,\n    user_type      int2          NOT NULL DEFAULT 0,\n    type           varchar(50)   NOT NULL,\n    sub_type       varchar(50)   NOT NULL,\n    biz_id         int8          NOT NULL,\n    action         varchar(2000) NULL     DEFAULT '',\n    success        bool          NOT NULL DEFAULT '1',\n    extra          varchar(2000) NULL     DEFAULT '',\n    request_method varchar(16)   NULL     DEFAULT '',\n    request_url    varchar(255)  NULL     DEFAULT '',\n    user_ip        varchar(50)   NULL     DEFAULT NULL,\n    user_agent     varchar(512)  NULL     DEFAULT NULL,\n    creator        varchar(64)   NULL     DEFAULT '',\n    create_time    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64)   NULL     DEFAULT '',\n    update_time    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2          NOT NULL DEFAULT 0,\n    tenant_id      int8          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_operate_log\n    ADD CONSTRAINT pk_system_operate_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_operate_log.id IS '日志主键';\nCOMMENT ON COLUMN system_operate_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_operate_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_operate_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_operate_log.type IS '操作模块类型';\nCOMMENT ON COLUMN system_operate_log.sub_type IS '操作名';\nCOMMENT ON COLUMN system_operate_log.biz_id IS '操作数据模块编号';\nCOMMENT ON COLUMN system_operate_log.action IS '操作内容';\nCOMMENT ON COLUMN system_operate_log.success IS '操作结果';\nCOMMENT ON COLUMN system_operate_log.extra IS '拓展字段';\nCOMMENT ON COLUMN system_operate_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN system_operate_log.request_url IS '请求地址';\nCOMMENT ON COLUMN system_operate_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_operate_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_operate_log.creator IS '创建者';\nCOMMENT ON COLUMN system_operate_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_operate_log.updater IS '更新者';\nCOMMENT ON COLUMN system_operate_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_operate_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_operate_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_operate_log IS '操作日志记录 V2 版本';\n\nDROP SEQUENCE IF EXISTS system_operate_log_seq;\nCREATE SEQUENCE system_operate_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_post\n-- ----------------------------\nDROP TABLE IF EXISTS system_post;\nCREATE TABLE system_post\n(\n    id          int8         NOT NULL,\n    code        varchar(64)  NOT NULL,\n    name        varchar(50)  NOT NULL,\n    sort        int4         NOT NULL,\n    status      int2         NOT NULL,\n    remark      varchar(500) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_post\n    ADD CONSTRAINT pk_system_post PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_post.id IS '岗位ID';\nCOMMENT ON COLUMN system_post.code IS '岗位编码';\nCOMMENT ON COLUMN system_post.name IS '岗位名称';\nCOMMENT ON COLUMN system_post.sort IS '显示顺序';\nCOMMENT ON COLUMN system_post.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_post.remark IS '备注';\nCOMMENT ON COLUMN system_post.creator IS '创建者';\nCOMMENT ON COLUMN system_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_post.updater IS '更新者';\nCOMMENT ON COLUMN system_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_post IS '岗位信息表';\n\n-- ----------------------------\n-- Records of system_post\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 'ceo', '董事长', 1, 0, '', 'admin', '2021-01-06 17:03:48', '1', '2023-02-11 15:19:04', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 'se', '项目经理', 2, 0, '', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 09:18:20', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 'user', '普通员工', 4, 0, '111222', 'admin', '2021-01-05 17:03:48', '1', '2025-03-24 21:32:40', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 'HR', '人力资源', 5, 0, '`', '1', '2024-03-24 20:45:40', '1', '2025-03-29 19:08:10', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_post_seq;\nCREATE SEQUENCE system_post_seq\n    START 6;\n\n-- ----------------------------\n-- Table structure for system_role\n-- ----------------------------\nDROP TABLE IF EXISTS system_role;\nCREATE TABLE system_role\n(\n    id                  int8         NOT NULL,\n    name                varchar(30)  NOT NULL,\n    code                varchar(100) NOT NULL,\n    sort                int4         NOT NULL,\n    data_scope          int2         NOT NULL DEFAULT 1,\n    data_scope_dept_ids varchar(500) NULL     DEFAULT '',\n    status              int2         NOT NULL,\n    type                int2         NOT NULL,\n    remark              varchar(500) NULL     DEFAULT NULL,\n    creator             varchar(64)  NULL     DEFAULT '',\n    create_time         timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater             varchar(64)  NULL     DEFAULT '',\n    update_time         timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted             int2         NOT NULL DEFAULT 0,\n    tenant_id           int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_role\n    ADD CONSTRAINT pk_system_role PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_role.id IS '角色ID';\nCOMMENT ON COLUMN system_role.name IS '角色名称';\nCOMMENT ON COLUMN system_role.code IS '角色权限字符串';\nCOMMENT ON COLUMN system_role.sort IS '显示顺序';\nCOMMENT ON COLUMN system_role.data_scope IS '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）';\nCOMMENT ON COLUMN system_role.data_scope_dept_ids IS '数据范围 ( 指定部门数组)';\nCOMMENT ON COLUMN system_role.status IS '角色状态（0正常 1停用）';\nCOMMENT ON COLUMN system_role.type IS '角色类型';\nCOMMENT ON COLUMN system_role.remark IS '备注';\nCOMMENT ON COLUMN system_role.creator IS '创建者';\nCOMMENT ON COLUMN system_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role.updater IS '更新者';\nCOMMENT ON COLUMN system_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role IS '角色信息表';\n\n-- ----------------------------\n-- Records of system_role\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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 (1, '超级管理员', 'super_admin', 1, 1, '', 0, 1, '超级管理员', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:21', '0', 1);\nINSERT 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 (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:20', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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 (101, '测试账号', 'test', 0, 1, '[]', 0, 2, '123', '', '2021-01-06 13:49:35', '1', '2025-04-30 17:38:28', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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, '测试数据权限', 'test-dp', 3, 2, '[100,102,103,104,105,108]', 0, 2, '', '1', '2025-03-31 14:58:06', '1', '2025-04-17 23:07:44', '0', 1);\nINSERT 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 (158, '2', '3', 4, 1, '', 0, 2, NULL, '1', '2025-04-17 20:08:08', '1', '2025-04-17 23:05:31', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_role_seq;\nCREATE SEQUENCE system_role_seq\n    START 159;\n\n-- ----------------------------\n-- Table structure for system_role_menu\n-- ----------------------------\nDROP TABLE IF EXISTS system_role_menu;\nCREATE TABLE system_role_menu\n(\n    id          int8        NOT NULL,\n    role_id     int8        NOT NULL,\n    menu_id     int8        NOT NULL,\n    creator     varchar(64) NULL     DEFAULT '',\n    create_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64) NULL     DEFAULT '',\n    update_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2        NOT NULL DEFAULT 0,\n    tenant_id   int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_role_menu\n    ADD CONSTRAINT pk_system_role_menu PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_role_menu.id IS '自增编号';\nCOMMENT ON COLUMN system_role_menu.role_id IS '角色ID';\nCOMMENT ON COLUMN system_role_menu.menu_id IS '菜单ID';\nCOMMENT ON COLUMN system_role_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_role_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_role_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role_menu.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role_menu.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role_menu IS '角色和菜单关联表';\n\n-- ----------------------------\n-- Records of system_role_menu\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (263, 109, 1, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (434, 2, 1, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (454, 2, 1093, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (455, 2, 1094, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (460, 2, 1100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (467, 2, 1107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (476, 2, 1117, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (477, 2, 100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (478, 2, 101, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (479, 2, 102, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (480, 2, 1126, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (481, 2, 103, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (483, 2, 104, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (485, 2, 105, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (488, 2, 107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (490, 2, 108, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (492, 2, 109, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (498, 2, 1138, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (523, 2, 1224, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (524, 2, 1225, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (541, 2, 500, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (543, 2, 501, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (675, 2, 2, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (689, 2, 1077, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (690, 2, 1078, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (692, 2, 1083, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (693, 2, 1084, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (699, 2, 1090, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (703, 2, 106, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (704, 2, 110, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (705, 2, 111, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1604, 101, 1216, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1605, 101, 1217, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1606, 101, 1218, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1607, 101, 1219, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1608, 101, 1220, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1609, 101, 1221, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1610, 101, 5, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1611, 101, 1222, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1612, 101, 1118, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1613, 101, 1119, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1614, 101, 1120, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1615, 101, 1185, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1616, 101, 1186, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1617, 101, 1187, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1618, 101, 1188, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1619, 101, 1189, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1620, 101, 1190, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1621, 101, 1191, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1622, 101, 1192, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1623, 101, 1193, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1624, 101, 1194, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1625, 101, 1195, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1627, 101, 1197, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1628, 101, 1198, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1629, 101, 1199, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1630, 101, 1200, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1631, 101, 1201, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1632, 101, 1202, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1633, 101, 1207, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1634, 101, 1208, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1635, 101, 1209, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1636, 101, 1210, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1637, 101, 1211, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1638, 101, 1212, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1639, 101, 1213, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1640, 101, 1215, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1641, 101, 2, '1', '2022-04-01 22:21:24', '1', '2022-04-01 22:21:24', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1642, 101, 1031, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1643, 101, 1032, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1644, 101, 1033, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1645, 101, 1034, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1646, 101, 1035, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1647, 101, 1050, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1648, 101, 1051, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1649, 101, 1052, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1650, 101, 1053, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1651, 101, 1054, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1652, 101, 1056, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1653, 101, 1057, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1654, 101, 1058, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1655, 101, 1059, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1656, 101, 1060, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1657, 101, 1066, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1658, 101, 1067, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1659, 101, 1070, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1664, 101, 1075, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1666, 101, 1077, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1667, 101, 1078, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1668, 101, 1082, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1669, 101, 1083, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1670, 101, 1084, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1671, 101, 1085, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1672, 101, 1086, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1673, 101, 1087, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1674, 101, 1088, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1675, 101, 1089, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1679, 101, 1237, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1680, 101, 1238, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1681, 101, 1239, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1682, 101, 1240, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1683, 101, 1241, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1684, 101, 1242, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1685, 101, 1243, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1687, 101, 106, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1688, 101, 110, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1689, 101, 111, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1690, 101, 112, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1691, 101, 113, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1692, 101, 114, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1693, 101, 115, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1694, 101, 116, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1732, 109, 1064, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1733, 109, 1001, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1734, 109, 1065, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1735, 109, 1002, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1736, 109, 1003, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1737, 109, 1004, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1738, 109, 1005, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1739, 109, 1006, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1740, 109, 1007, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1741, 109, 1008, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1742, 109, 1009, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1766, 109, 1064, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1767, 109, 1001, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1768, 109, 1065, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1769, 109, 1002, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1770, 109, 1003, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1771, 109, 1004, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1772, 109, 1005, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1773, 109, 1006, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1774, 109, 1007, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1775, 109, 1008, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1776, 109, 1009, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1800, 109, 1064, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1801, 109, 1001, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1802, 109, 1065, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1803, 109, 1002, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1804, 109, 1003, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1805, 109, 1004, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1806, 109, 1005, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1807, 109, 1006, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1808, 109, 1007, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1809, 109, 1008, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1810, 109, 1009, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1994, 2, 1027, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1995, 2, 1028, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1996, 2, 1029, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1997, 2, 1030, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1998, 2, 1031, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1999, 2, 1032, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2000, 2, 1033, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2001, 2, 1034, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2002, 2, 1035, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2003, 2, 1036, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2004, 2, 1037, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2005, 2, 1038, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2006, 2, 1039, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2007, 2, 1040, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2008, 2, 1042, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2009, 2, 1043, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2010, 2, 1045, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2011, 2, 1046, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2012, 2, 1048, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2013, 2, 1050, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2014, 2, 1051, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2015, 2, 1052, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2016, 2, 1053, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2017, 2, 1054, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2018, 2, 1056, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2019, 2, 1057, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2020, 2, 1058, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2021, 2, 2083, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2022, 2, 1059, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2023, 2, 1060, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2024, 2, 1063, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2025, 2, 1064, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2026, 2, 1065, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2027, 2, 1066, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2028, 2, 1067, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2029, 2, 1070, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2034, 2, 1075, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2036, 2, 1082, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2037, 2, 1085, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2038, 2, 1086, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2039, 2, 1087, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2040, 2, 1088, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2041, 2, 1089, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2042, 2, 1091, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2043, 2, 1092, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2044, 2, 1095, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2045, 2, 1096, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2046, 2, 1097, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2047, 2, 1098, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2048, 2, 1101, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2049, 2, 1102, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2050, 2, 1103, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2051, 2, 1104, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2052, 2, 1105, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2053, 2, 1106, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2054, 2, 1108, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2055, 2, 1109, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2061, 2, 1127, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2062, 2, 1128, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2063, 2, 1129, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2064, 2, 1130, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2066, 2, 1132, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2067, 2, 1133, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2068, 2, 1134, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2069, 2, 1135, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2070, 2, 1136, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2071, 2, 1137, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2072, 2, 114, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2073, 2, 1139, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2074, 2, 115, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2075, 2, 1140, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2076, 2, 116, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2077, 2, 1141, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2078, 2, 1142, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2079, 2, 1143, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2080, 2, 1150, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2081, 2, 1161, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2082, 2, 1162, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2086, 2, 1166, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2087, 2, 1173, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2088, 2, 1174, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2092, 2, 1178, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2099, 2, 1226, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2100, 2, 1227, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2101, 2, 1228, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2102, 2, 1229, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2103, 2, 1237, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2104, 2, 1238, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2105, 2, 1239, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2106, 2, 1240, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2107, 2, 1241, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2108, 2, 1242, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2109, 2, 1243, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2116, 2, 1254, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2117, 2, 1255, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2118, 2, 1256, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2119, 2, 1257, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2120, 2, 1258, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2121, 2, 1259, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2122, 2, 1260, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2123, 2, 1261, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2124, 2, 1263, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2125, 2, 1264, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2126, 2, 1265, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2127, 2, 1266, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2128, 2, 1267, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2129, 2, 1001, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2130, 2, 1002, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2131, 2, 1003, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2132, 2, 1004, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2133, 2, 1005, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2134, 2, 1006, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2135, 2, 1007, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2136, 2, 1008, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2137, 2, 1009, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2138, 2, 1010, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2148, 2, 1020, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2149, 2, 1021, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2150, 2, 1022, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2151, 2, 1023, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2152, 2, 1281, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2153, 2, 1282, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2154, 2, 2000, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2155, 2, 2002, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2156, 2, 2003, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2157, 2, 2004, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2158, 2, 2005, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2159, 2, 2006, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2160, 2, 2008, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2161, 2, 2009, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2162, 2, 2010, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2163, 2, 2011, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2164, 2, 2012, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2170, 2, 2019, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2171, 2, 2020, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2172, 2, 2021, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2173, 2, 2022, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2174, 2, 2023, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2175, 2, 2025, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2177, 2, 2027, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2178, 2, 2028, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2179, 2, 2029, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2180, 2, 2014, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2181, 2, 2015, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2182, 2, 2016, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2183, 2, 2017, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2184, 2, 2018, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2188, 101, 1024, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2189, 101, 1, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2190, 101, 1025, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2191, 101, 1026, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2192, 101, 1027, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2193, 101, 1028, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2194, 101, 1029, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2195, 101, 1030, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2196, 101, 1036, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2197, 101, 1037, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2198, 101, 1038, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2199, 101, 1039, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2200, 101, 1040, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2201, 101, 1042, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2202, 101, 1043, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2203, 101, 1045, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2204, 101, 1046, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2205, 101, 1048, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2206, 101, 2083, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2207, 101, 1063, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2208, 101, 1064, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2209, 101, 1065, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2210, 101, 1093, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2211, 101, 1094, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2212, 101, 1095, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2213, 101, 1096, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2214, 101, 1097, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2215, 101, 1098, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2216, 101, 1100, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2217, 101, 1101, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2218, 101, 1102, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2219, 101, 1103, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2220, 101, 1104, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2221, 101, 1105, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2222, 101, 1106, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2223, 101, 2130, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2224, 101, 1107, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2225, 101, 2131, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2226, 101, 1108, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2227, 101, 2132, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2228, 101, 1109, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2229, 101, 2133, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2230, 101, 2134, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2232, 101, 2135, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2234, 101, 2136, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2236, 101, 2137, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2238, 101, 2138, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2240, 101, 2139, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2242, 101, 2140, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2243, 101, 2141, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2244, 101, 2142, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2245, 101, 2143, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2246, 101, 2144, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2247, 101, 2145, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2248, 101, 2146, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2249, 101, 2147, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2250, 101, 100, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2251, 101, 2148, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2252, 101, 101, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2253, 101, 2149, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2254, 101, 102, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2255, 101, 2150, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2256, 101, 103, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2257, 101, 2151, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2258, 101, 104, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2259, 101, 2152, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2260, 101, 105, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2261, 101, 107, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2262, 101, 108, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2263, 101, 109, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2264, 101, 1138, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2265, 101, 1139, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2266, 101, 1140, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2267, 101, 1141, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2268, 101, 1142, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2269, 101, 1143, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2270, 101, 1224, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2271, 101, 1225, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2272, 101, 1226, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2273, 101, 1227, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2274, 101, 1228, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2275, 101, 1229, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2282, 101, 1261, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2283, 101, 1263, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2284, 101, 1264, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2285, 101, 1265, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2286, 101, 1266, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2287, 101, 1267, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2288, 101, 1001, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2289, 101, 1002, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2290, 101, 1003, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2291, 101, 1004, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2292, 101, 1005, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2293, 101, 1006, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2294, 101, 1007, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2295, 101, 1008, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2296, 101, 1009, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2297, 101, 1010, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2298, 101, 1011, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2299, 101, 1012, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2300, 101, 500, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2301, 101, 1013, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2302, 101, 501, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2303, 101, 1014, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2304, 101, 1015, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2305, 101, 1016, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2306, 101, 1017, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2307, 101, 1018, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2308, 101, 1019, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2309, 101, 1020, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2310, 101, 1021, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2311, 101, 1022, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2312, 101, 1023, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2929, 109, 1224, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2930, 109, 1225, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2931, 109, 1226, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2932, 109, 1227, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2933, 109, 1228, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2934, 109, 1229, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2935, 109, 1138, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2936, 109, 1139, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2937, 109, 1140, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2996, 109, 1033, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2997, 109, 1034, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2998, 109, 1035, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2999, 109, 1050, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3000, 109, 1051, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3001, 109, 1052, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3002, 109, 1053, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3003, 109, 1054, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3004, 109, 1056, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3005, 109, 1057, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3006, 109, 1058, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3007, 109, 1059, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3008, 109, 1060, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3009, 109, 1066, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3010, 109, 1067, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3011, 109, 1070, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3012, 109, 1075, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3014, 109, 1077, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3015, 109, 1078, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3016, 109, 1082, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3017, 109, 1083, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3018, 109, 1084, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3019, 109, 1085, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3020, 109, 1086, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3021, 109, 1087, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3022, 109, 1088, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3023, 109, 1089, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3024, 109, 1090, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3025, 109, 1091, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3026, 109, 1092, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3027, 109, 106, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3028, 109, 110, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3029, 109, 111, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3030, 109, 112, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3031, 109, 113, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3032, 109, 114, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3033, 109, 115, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3034, 109, 116, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3035, 109, 2472, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3036, 109, 2478, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3037, 109, 2479, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3038, 109, 2480, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3039, 109, 2481, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3040, 109, 2482, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3041, 109, 2483, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3042, 109, 2484, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3043, 109, 2485, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3044, 109, 2486, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3045, 109, 2487, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3046, 109, 2488, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3047, 109, 2489, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3048, 109, 2490, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3049, 109, 2491, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3050, 109, 2492, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3051, 109, 2493, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3052, 109, 2494, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3053, 109, 2495, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3054, 109, 2497, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3055, 109, 1237, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3056, 109, 1238, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3057, 109, 1239, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3058, 109, 1240, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3059, 109, 1241, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3060, 109, 1242, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3061, 109, 1243, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3062, 109, 2525, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3063, 109, 1255, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3064, 109, 1256, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3065, 109, 1257, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4166, 109, 1120, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4167, 109, 2713, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4168, 109, 2714, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4169, 109, 2715, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4170, 109, 2716, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4171, 109, 2717, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4172, 109, 2718, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4173, 109, 2720, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4174, 109, 1185, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4175, 109, 2721, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4176, 109, 1186, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4177, 109, 2722, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4178, 109, 1187, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4179, 109, 2723, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4180, 109, 1188, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4181, 109, 2724, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4182, 109, 1189, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4183, 109, 2725, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4184, 109, 1190, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4185, 109, 2726, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4186, 109, 1191, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4187, 109, 2727, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4188, 109, 1192, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4189, 109, 2728, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4190, 109, 1193, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4191, 109, 2729, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4192, 109, 1194, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4193, 109, 2730, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4194, 109, 1195, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4195, 109, 2731, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4196, 109, 1196, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4197, 109, 2732, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4198, 109, 1197, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4199, 109, 2733, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4200, 109, 1198, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4201, 109, 2734, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4202, 109, 1199, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4203, 109, 2735, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4204, 109, 1200, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4205, 109, 1201, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4206, 109, 1202, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4207, 109, 1207, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4208, 109, 1208, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4209, 109, 1209, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4210, 109, 1210, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4211, 109, 1211, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4212, 109, 1212, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4213, 109, 1213, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4214, 109, 1215, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4215, 109, 1216, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4216, 109, 1217, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4217, 109, 1218, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4218, 109, 1219, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4255, 111, 1196, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5777, 101, 2739, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5778, 101, 2740, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5782, 2, 2759, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5783, 2, 2362, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5784, 2, 2387, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5786, 101, 2758, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5787, 101, 2759, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5788, 101, 2783, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6105, 155, 4033, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6106, 155, 4034, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6107, 155, 4035, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6108, 155, 4036, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6109, 155, 4037, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6110, 155, 4038, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6111, 155, 4039, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6112, 155, 4040, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6113, 155, 4041, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6114, 155, 4042, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6115, 155, 4043, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6116, 155, 4044, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6117, 155, 4045, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6118, 155, 4046, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6119, 155, 4001, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6120, 155, 4002, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6121, 155, 4003, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6122, 155, 4004, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6123, 155, 4005, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6124, 155, 4006, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6125, 155, 4007, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6126, 155, 4008, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6127, 155, 4009, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6128, 155, 4010, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6129, 155, 4011, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6130, 155, 4012, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6131, 155, 4013, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6132, 155, 4014, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6133, 155, 4015, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6134, 155, 4016, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6135, 155, 4017, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6136, 155, 4018, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6137, 155, 4031, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6138, 101, 5010, '1', '2025-05-05 17:49:17', '1', '2025-05-05 17:49:17', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_role_menu_seq;\nCREATE SEQUENCE system_role_menu_seq\n    START 6139;\n\n-- ----------------------------\n-- Table structure for system_sms_channel\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_channel;\nCREATE TABLE system_sms_channel\n(\n    id           int8         NOT NULL,\n    signature    varchar(12)  NOT NULL,\n    code         varchar(63)  NOT NULL,\n    status       int2         NOT NULL,\n    remark       varchar(255) NULL     DEFAULT NULL,\n    api_key      varchar(128) NOT NULL,\n    api_secret   varchar(128) NULL     DEFAULT NULL,\n    callback_url varchar(255) NULL     DEFAULT NULL,\n    creator      varchar(64)  NULL     DEFAULT '',\n    create_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater      varchar(64)  NULL     DEFAULT '',\n    update_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted      int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_channel\n    ADD CONSTRAINT pk_system_sms_channel PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_channel.id IS '编号';\nCOMMENT ON COLUMN system_sms_channel.signature IS '短信签名';\nCOMMENT ON COLUMN system_sms_channel.code IS '渠道编码';\nCOMMENT ON COLUMN system_sms_channel.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_channel.remark IS '备注';\nCOMMENT ON COLUMN system_sms_channel.api_key IS '短信 API 的账号';\nCOMMENT ON COLUMN system_sms_channel.api_secret IS '短信 API 的秘钥';\nCOMMENT ON COLUMN system_sms_channel.callback_url IS '短信发送回调 URL';\nCOMMENT ON COLUMN system_sms_channel.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_channel.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_channel.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_channel.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_channel.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_channel IS '短信渠道';\n\n-- ----------------------------\n-- Records of system_sms_channel\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (2, 'Ballcat', 'ALIYUN', 0, '你要改哦，只有我可以用！！！！', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', '2021-03-31 11:53:10', '1', '2024-08-04 08:53:26', '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (4, '测试渠道', 'DEBUG_DING_TALK', 0, '123', '696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859', 'SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67', NULL, '1', '2021-04-13 00:23:14', '1', '2022-03-27 20:29:49', '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (7, 'mock腾讯云', 'TENCENT', 0, '', '1 2', '2 3', '', '1', '2024-09-30 08:53:45', '1', '2024-09-30 08:55:01', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_sms_channel_seq;\nCREATE SEQUENCE system_sms_channel_seq\n    START 8;\n\n-- ----------------------------\n-- Table structure for system_sms_code\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_code;\nCREATE TABLE system_sms_code\n(\n    id          int8         NOT NULL,\n    mobile      varchar(11)  NOT NULL,\n    code        varchar(6)   NOT NULL,\n    create_ip   varchar(15)  NOT NULL,\n    scene       int2         NOT NULL,\n    today_index int2         NOT NULL,\n    used        int2         NOT NULL,\n    used_time   timestamp    NULL     DEFAULT NULL,\n    used_ip     varchar(255) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_code\n    ADD CONSTRAINT pk_system_sms_code PRIMARY KEY (id);\n\nCREATE INDEX idx_system_sms_code_01 ON system_sms_code (mobile);\n\nCOMMENT ON COLUMN system_sms_code.id IS '编号';\nCOMMENT ON COLUMN system_sms_code.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_code.code IS '验证码';\nCOMMENT ON COLUMN system_sms_code.create_ip IS '创建 IP';\nCOMMENT ON COLUMN system_sms_code.scene IS '发送场景';\nCOMMENT ON COLUMN system_sms_code.today_index IS '今日发送的第几条';\nCOMMENT ON COLUMN system_sms_code.used IS '是否使用';\nCOMMENT ON COLUMN system_sms_code.used_time IS '使用时间';\nCOMMENT ON COLUMN system_sms_code.used_ip IS '使用 IP';\nCOMMENT ON COLUMN system_sms_code.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_code.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_sms_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_sms_code IS '手机验证码';\n\nDROP SEQUENCE IF EXISTS system_sms_code_seq;\nCREATE SEQUENCE system_sms_code_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_sms_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_log;\nCREATE TABLE system_sms_log\n(\n    id               int8         NOT NULL,\n    channel_id       int8         NOT NULL,\n    channel_code     varchar(63)  NOT NULL,\n    template_id      int8         NOT NULL,\n    template_code    varchar(63)  NOT NULL,\n    template_type    int2         NOT NULL,\n    template_content varchar(255) NOT NULL,\n    template_params  varchar(255) NOT NULL,\n    api_template_id  varchar(63)  NOT NULL,\n    mobile           varchar(11)  NOT NULL,\n    user_id          int8         NULL     DEFAULT NULL,\n    user_type        int2         NULL     DEFAULT NULL,\n    send_status      int2         NOT NULL DEFAULT 0,\n    send_time        timestamp    NULL     DEFAULT NULL,\n    api_send_code    varchar(63)  NULL     DEFAULT NULL,\n    api_send_msg     varchar(255) NULL     DEFAULT NULL,\n    api_request_id   varchar(255) NULL     DEFAULT NULL,\n    api_serial_no    varchar(255) NULL     DEFAULT NULL,\n    receive_status   int2         NOT NULL DEFAULT 0,\n    receive_time     timestamp    NULL     DEFAULT NULL,\n    api_receive_code varchar(63)  NULL     DEFAULT NULL,\n    api_receive_msg  varchar(255) NULL     DEFAULT NULL,\n    creator          varchar(64)  NULL     DEFAULT '',\n    create_time      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater          varchar(64)  NULL     DEFAULT '',\n    update_time      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted          int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_log\n    ADD CONSTRAINT pk_system_sms_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_log.id IS '编号';\nCOMMENT ON COLUMN system_sms_log.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_log.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_sms_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_sms_log.template_type IS '短信类型';\nCOMMENT ON COLUMN system_sms_log.template_content IS '短信内容';\nCOMMENT ON COLUMN system_sms_log.template_params IS '短信参数';\nCOMMENT ON COLUMN system_sms_log.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_log.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_sms_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_sms_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_sms_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_sms_log.api_send_code IS '短信 API 发送结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_send_msg IS '短信 API 发送失败的提示';\nCOMMENT ON COLUMN system_sms_log.api_request_id IS '短信 API 发送返回的唯一请求 ID';\nCOMMENT ON COLUMN system_sms_log.api_serial_no IS '短信 API 发送返回的序号';\nCOMMENT ON COLUMN system_sms_log.receive_status IS '接收状态';\nCOMMENT ON COLUMN system_sms_log.receive_time IS '接收时间';\nCOMMENT ON COLUMN system_sms_log.api_receive_code IS 'API 接收结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_receive_msg IS 'API 接收结果的说明';\nCOMMENT ON COLUMN system_sms_log.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_log.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_log IS '短信日志';\n\nDROP SEQUENCE IF EXISTS system_sms_log_seq;\nCREATE SEQUENCE system_sms_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_sms_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_template;\nCREATE TABLE system_sms_template\n(\n    id              int8         NOT NULL,\n    type            int2         NOT NULL,\n    status          int2         NOT NULL,\n    code            varchar(63)  NOT NULL,\n    name            varchar(63)  NOT NULL,\n    content         varchar(255) NOT NULL,\n    params          varchar(255) NOT NULL,\n    remark          varchar(255) NULL     DEFAULT NULL,\n    api_template_id varchar(63)  NOT NULL,\n    channel_id      int8         NOT NULL,\n    channel_code    varchar(63)  NOT NULL,\n    creator         varchar(64)  NULL     DEFAULT '',\n    create_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater         varchar(64)  NULL     DEFAULT '',\n    update_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted         int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_template\n    ADD CONSTRAINT pk_system_sms_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_template.id IS '编号';\nCOMMENT ON COLUMN system_sms_template.type IS '模板类型';\nCOMMENT ON COLUMN system_sms_template.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_template.code IS '模板编码';\nCOMMENT ON COLUMN system_sms_template.name IS '模板名称';\nCOMMENT ON COLUMN system_sms_template.content IS '模板内容';\nCOMMENT ON COLUMN system_sms_template.params IS '参数数组';\nCOMMENT ON COLUMN system_sms_template.remark IS '备注';\nCOMMENT ON COLUMN system_sms_template.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_template.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_template.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_template.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_template.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_template IS '短信模板';\n\n-- ----------------------------\n-- Records of system_sms_template\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (2, 1, 0, 'test_01', '测试验证码短信', '正在进行登录操作{operation}，您的验证码是{code}', '[\"operation\",\"code\"]', '测试备注', '4383920', 4, 'DEBUG_DING_TALK', '', '2021-03-31 10:49:38', '1', '2024-08-18 11:57:18', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (3, 1, 0, 'test_02', '公告通知', '您的验证码{code}，该验证码5分钟内有效，请勿泄漏于他人！', '[\"code\"]', NULL, 'SMS_207945135', 2, 'ALIYUN', '', '2021-03-31 11:56:30', '1', '2021-04-10 01:22:02', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (6, 3, 0, 'test-01', '测试模板', '哈哈哈 {name}', '[\"name\"]', 'f哈哈哈', '4383920', 4, 'DEBUG_DING_TALK', '1', '2021-04-10 01:07:21', '1', '2024-08-18 11:57:07', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (7, 3, 0, 'test-04', '测试下', '老鸡{name}，牛逼{code}', '[\"name\",\"code\"]', '哈哈哈哈', 'suibian', 7, 'DEBUG_DING_TALK', '1', '2021-04-13 00:29:53', '1', '2024-09-30 00:56:24', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (8, 1, 0, 'user-sms-login', '前台用户短信登录', '您的验证码是{code}', '[\"code\"]', NULL, '4372216', 4, 'DEBUG_DING_TALK', '1', '2021-10-11 08:10:00', '1', '2024-08-18 11:57:06', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (9, 2, 0, 'bpm_task_assigned', '【工作流】任务被分配', '您收到了一条新的待办任务：{processInstanceName}-{taskName}，申请人：{startUserNickname}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"startUserNickname\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-21 22:31:19', '1', '2022-01-22 00:03:36', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (10, 2, 0, 'bpm_process_instance_reject', '【工作流】流程被不通过', '您的流程被审批不通过：{processInstanceName}，原因：{reason}，查看链接：{detailUrl}', '[\"processInstanceName\",\"reason\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:03:31', '1', '2022-05-01 12:33:14', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (11, 2, 0, 'bpm_process_instance_approve', '【工作流】流程被通过', '您的流程被审批通过：{processInstanceName}，查看链接：{detailUrl}', '[\"processInstanceName\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:04:31', '1', '2022-03-27 20:32:21', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (12, 2, 0, 'demo', '演示模板', '我就是测试一下下', '[]', NULL, 'biubiubiu', 4, 'DEBUG_DING_TALK', '1', '2022-04-10 23:22:49', '1', '2024-08-18 11:57:04', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (14, 1, 0, 'user-update-mobile', '会员用户 - 修改手机', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:04', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (15, 1, 0, 'user-update-password', '会员用户 - 修改密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:18', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (16, 1, 0, 'user-reset-password', '会员用户 - 重置密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-12-02 22:35:27', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (17, 2, 0, 'bpm_task_timeout', '【工作流】任务审批超时', '您收到了一条超时的待办任务：{processInstanceName}-{taskName}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"detailUrl\"]', '', 'X', 4, 'DEBUG_DING_TALK', '1', '2024-08-16 21:59:15', '1', '2024-08-16 21:59:34', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (18, 1, 0, 'admin-reset-password', '后台用户 - 忘记密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2025-03-16 14:19:34', '1', '2025-03-16 14:19:45', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (19, 1, 0, 'admin-sms-login', '后台用户短信登录', '您的验证码是{code}', '[\"code\"]', '', '4372216', 4, 'DEBUG_DING_TALK', '1', '2025-04-08 09:36:03', '1', '2025-04-08 09:36:17', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_sms_template_seq;\nCREATE SEQUENCE system_sms_template_seq\n    START 20;\n\n-- ----------------------------\n-- Table structure for system_social_client\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_client;\nCREATE TABLE system_social_client\n(\n    id            int8         NOT NULL,\n    name          varchar(255) NOT NULL,\n    social_type   int2         NOT NULL,\n    user_type     int2         NOT NULL,\n    client_id     varchar(255) NOT NULL,\n    client_secret varchar(2048) NOT NULL,\n    public_key    varchar(2048) NULL    DEFAULT NULL,\n    agent_id      varchar(255) NULL     DEFAULT NULL,\n    status        int2         NOT NULL,\n    creator       varchar(64)  NULL     DEFAULT '',\n    create_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater       varchar(64)  NULL     DEFAULT '',\n    update_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted       int2         NOT NULL DEFAULT 0,\n    tenant_id     int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_social_client\n    ADD CONSTRAINT pk_system_social_client PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_client.id IS '编号';\nCOMMENT ON COLUMN system_social_client.name IS '应用名';\nCOMMENT ON COLUMN system_social_client.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_client.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_social_client.client_secret IS '客户端密钥';\nCOMMENT ON COLUMN system_social_client.public_key IS 'publicKey公钥';\nCOMMENT ON COLUMN system_social_client.agent_id IS '代理编号';\nCOMMENT ON COLUMN system_social_client.status IS '状态';\nCOMMENT ON COLUMN system_social_client.creator IS '创建者';\nCOMMENT ON COLUMN system_social_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_client.updater IS '更新者';\nCOMMENT ON COLUMN system_social_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_client.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_client.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_client IS '社交客户端表';\n\n-- ----------------------------\n-- Records of system_social_client\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '钉钉', 20, 2, 'dingvrnreaje3yqvzhxg', 'i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI', NULL, 0, '', '2023-10-18 11:21:18', '1', '2023-12-20 21:28:26', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '钉钉（王土豆）', 20, 2, 'dingtsu9hpepjkbmthhw', 'FP_bnSq_HAHKCSncmJjw5hxhnzs6vaVDSZZn3egj6rdqTQ_hu5tQVJyLMpgCakdP', NULL, 0, '', '2023-10-18 11:21:18', '', '2023-12-20 21:28:26', '1', 121);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '微信公众号', 31, 1, 'wx5b23ba7a5589ecbb', '2a7b3b20c537e52e74afd395eb85f61f', NULL, 0, '', '2023-10-18 16:07:46', '1', '2023-12-20 21:28:23', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (43, '微信小程序', 34, 1, 'wx63c280fe3248a3e7', '6f270509224a7ae1296bbf1c8cb97aed', NULL, 0, '', '2023-10-19 13:37:41', '1', '2023-12-20 21:28:25', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (44, '1', 10, 1, '2', '3', NULL, 0, '1', '2025-04-06 20:36:28', '1', '2025-04-06 20:43:12', '1', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_social_client_seq;\nCREATE SEQUENCE system_social_client_seq\n    START 45;\n\n-- ----------------------------\n-- Table structure for system_social_user\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_user;\nCREATE TABLE system_social_user\n(\n    id             int8          NOT NULL,\n    type           int2          NOT NULL,\n    openid         varchar(32)   NOT NULL,\n    token          varchar(256)  NULL     DEFAULT NULL,\n    raw_token_info varchar(1024) NOT NULL,\n    nickname       varchar(32)   NOT NULL,\n    avatar         varchar(255)  NULL     DEFAULT NULL,\n    raw_user_info  varchar(1024) NOT NULL,\n    code           varchar(256)  NOT NULL,\n    state          varchar(256)  NULL     DEFAULT NULL,\n    creator        varchar(64)   NULL     DEFAULT '',\n    create_time    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64)   NULL     DEFAULT '',\n    update_time    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2          NOT NULL DEFAULT 0,\n    tenant_id      int8          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_social_user\n    ADD CONSTRAINT pk_system_social_user PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_user.id IS '主键 ( 自增策略)';\nCOMMENT ON COLUMN system_social_user.type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user.openid IS '社交 openid';\nCOMMENT ON COLUMN system_social_user.token IS '社交 token';\nCOMMENT ON COLUMN system_social_user.raw_token_info IS '原始 Token 数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_social_user.avatar IS '用户头像';\nCOMMENT ON COLUMN system_social_user.raw_user_info IS '原始用户数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.code IS '最后一次的认证 code';\nCOMMENT ON COLUMN system_social_user.state IS '最后一次的认证 state';\nCOMMENT ON COLUMN system_social_user.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user IS '社交用户表';\n\nDROP SEQUENCE IF EXISTS system_social_user_seq;\nCREATE SEQUENCE system_social_user_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_social_user_bind\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_user_bind;\nCREATE TABLE system_social_user_bind\n(\n    id             int8        NOT NULL,\n    user_id        int8        NOT NULL,\n    user_type      int2        NOT NULL,\n    social_type    int2        NOT NULL,\n    social_user_id int8        NOT NULL,\n    creator        varchar(64) NULL     DEFAULT '',\n    create_time    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64) NULL     DEFAULT '',\n    update_time    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2        NOT NULL DEFAULT 0,\n    tenant_id      int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_social_user_bind\n    ADD CONSTRAINT pk_system_social_user_bind PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_user_bind.id IS '主键 ( 自增策略)';\nCOMMENT ON COLUMN system_social_user_bind.user_id IS '用户编号';\nCOMMENT ON COLUMN system_social_user_bind.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_user_bind.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user_bind.social_user_id IS '社交用户的编号';\nCOMMENT ON COLUMN system_social_user_bind.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user_bind.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user_bind.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user_bind.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user_bind.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user_bind.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user_bind IS '社交绑定表';\n\nDROP SEQUENCE IF EXISTS system_social_user_bind_seq;\nCREATE SEQUENCE system_social_user_bind_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_tenant\n-- ----------------------------\nDROP TABLE IF EXISTS system_tenant;\nCREATE TABLE system_tenant\n(\n    id              int8         NOT NULL,\n    name            varchar(30)  NOT NULL,\n    contact_user_id int8         NULL     DEFAULT NULL,\n    contact_name    varchar(30)  NOT NULL,\n    contact_mobile  varchar(500) NULL     DEFAULT NULL,\n    status          int2         NOT NULL DEFAULT 0,\n    websites        varchar(256) NULL     DEFAULT '',\n    package_id      int8         NOT NULL,\n    expire_time     timestamp    NOT NULL,\n    account_count   int4         NOT NULL,\n    creator         varchar(64)  NULL     DEFAULT '',\n    create_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater         varchar(64)  NULL     DEFAULT '',\n    update_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted         int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_tenant\n    ADD CONSTRAINT pk_system_tenant PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_tenant.id IS '租户编号';\nCOMMENT ON COLUMN system_tenant.name IS '租户名';\nCOMMENT ON COLUMN system_tenant.contact_user_id IS '联系人的用户编号';\nCOMMENT ON COLUMN system_tenant.contact_name IS '联系人';\nCOMMENT ON COLUMN system_tenant.contact_mobile IS '联系手机';\nCOMMENT ON COLUMN system_tenant.status IS '租户状态（0正常 1停用）';\nCOMMENT ON COLUMN system_tenant.websites IS '绑定域名数组';\nCOMMENT ON COLUMN system_tenant.package_id IS '租户套餐编号';\nCOMMENT ON COLUMN system_tenant.expire_time IS '过期时间';\nCOMMENT ON COLUMN system_tenant.account_count IS '账号数量';\nCOMMENT ON COLUMN system_tenant.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant IS '租户表';\n\n-- ----------------------------\n-- Records of system_tenant\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2023-11-06 11:41:41', '0');\nINSERT 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', 111, '2026-07-10 00:00:00', 30, '1', '2022-02-22 00:56:14', '1', '2025-04-03 21:33:01', '0');\nINSERT 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', 111, '2022-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2024-09-22 12:10:50', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_tenant_seq;\nCREATE SEQUENCE system_tenant_seq\n    START 123;\n\n-- ----------------------------\n-- Table structure for system_tenant_package\n-- ----------------------------\nDROP TABLE IF EXISTS system_tenant_package;\nCREATE TABLE system_tenant_package\n(\n    id          int8          NOT NULL,\n    name        varchar(30)   NOT NULL,\n    status      int2          NOT NULL DEFAULT 0,\n    remark      varchar(256)  NULL     DEFAULT '',\n    menu_ids    varchar(4096) NOT NULL,\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_tenant_package\n    ADD CONSTRAINT pk_system_tenant_package PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_tenant_package.id IS '套餐编号';\nCOMMENT ON COLUMN system_tenant_package.name IS '套餐名';\nCOMMENT ON COLUMN system_tenant_package.status IS '租户状态（0正常 1停用）';\nCOMMENT ON COLUMN system_tenant_package.remark IS '备注';\nCOMMENT ON COLUMN system_tenant_package.menu_ids IS '关联的菜单编号';\nCOMMENT ON COLUMN system_tenant_package.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant_package.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant_package.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant_package.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant_package.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant_package IS '租户套餐表';\n\n-- ----------------------------\n-- Records of system_tenant_package\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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,1118,1119,1120,100,101,102,103,106,107,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2713,2714,2715,2716,2717,2718,2720,1185,2721,1186,2722,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,2472,1192,2728,1193,2729,1194,2730,1195,2731,1196,2732,1197,2733,2478,1198,2734,2479,1199,2735,2480,1200,2481,1201,2482,1202,2483,2739,2484,2740,2485,2486,2487,1207,2488,1208,2489,1209,2490,1210,2491,1211,2492,1212,2493,1213,2494,2495,1215,1216,2497,1217,1218,1219,1220,1221,1222,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,2525,1255,1256,1001,1257,1002,1258,1003,1259,1004,1260,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020]', '1', '2022-02-22 00:54:00', '1', '2024-07-13 22:37:24', '0');\nINSERT INTO system_tenant_package (id, name, status, remark, menu_ids, creator, create_time, updater, update_time, deleted) VALUES (112, '再来一个套餐', 0, '1234', '[1024,1,1025,1026,2,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1042,1043,1045,1046,1048,1050,1051,1052,1053,1054,1056,1057,1058,2083,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1100,1101,1102,1103,1104,1105,1106,2130,1107,2131,1108,2132,1109,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,100,2148,101,2149,102,2150,103,2151,104,2152,105,106,107,108,109,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2739,2740,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,1255,1256,1257,1258,1259,1260,1261,1263,1264,1265,1266,1267,2447,2448,2449,2450,2451,2452,2453,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,500,1013,501,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023]', '1', '2025-04-04 08:15:02', '1', '2025-04-04 08:15:21', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_tenant_package_seq;\nCREATE SEQUENCE system_tenant_package_seq\n    START 113;\n\n-- ----------------------------\n-- Table structure for system_user_post\n-- ----------------------------\nDROP TABLE IF EXISTS system_user_post;\nCREATE TABLE system_user_post\n(\n    id          int8        NOT NULL,\n    user_id     int8        NOT NULL DEFAULT 0,\n    post_id     int8        NOT NULL DEFAULT 0,\n    creator     varchar(64) NULL     DEFAULT '',\n    create_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64) NULL     DEFAULT '',\n    update_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2        NOT NULL DEFAULT 0,\n    tenant_id   int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_user_post\n    ADD CONSTRAINT pk_system_user_post PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_user_post.id IS 'id';\nCOMMENT ON COLUMN system_user_post.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_post.post_id IS '岗位ID';\nCOMMENT ON COLUMN system_user_post.creator IS '创建者';\nCOMMENT ON COLUMN system_user_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_post.updater IS '更新者';\nCOMMENT ON COLUMN system_user_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_post IS '用户岗位表';\n\n-- ----------------------------\n-- Records of system_user_post\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, 1, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, 100, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (115, 104, 1, '1', '2022-05-16 19:36:28', '1', '2022-05-16 19:36:28', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (116, 117, 2, '1', '2022-07-09 17:40:26', '1', '2022-07-09 17:40:26', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (117, 118, 1, '1', '2022-07-09 17:44:44', '1', '2022-07-09 17:44:44', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (119, 114, 5, '1', '2024-03-24 20:45:51', '1', '2024-03-24 20:45:51', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (123, 115, 1, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (124, 115, 2, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (125, 1, 2, '1', '2024-07-13 22:31:39', '1', '2024-07-13 22:31:39', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_user_post_seq;\nCREATE SEQUENCE system_user_post_seq\n    START 126;\n\n-- ----------------------------\n-- Table structure for system_user_role\n-- ----------------------------\nDROP TABLE IF EXISTS system_user_role;\nCREATE TABLE system_user_role\n(\n    id          int8        NOT NULL,\n    user_id     int8        NOT NULL,\n    role_id     int8        NOT NULL,\n    creator     varchar(64) NULL     DEFAULT '',\n    create_time timestamp   NULL     DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64) NULL     DEFAULT '',\n    update_time timestamp   NULL     DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2        NOT NULL DEFAULT 0,\n    tenant_id   int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_user_role\n    ADD CONSTRAINT pk_system_user_role PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_user_role.id IS '自增编号';\nCOMMENT ON COLUMN system_user_role.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_role.role_id IS '角色ID';\nCOMMENT ON COLUMN system_user_role.creator IS '创建者';\nCOMMENT ON COLUMN system_user_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_role.updater IS '更新者';\nCOMMENT ON COLUMN system_user_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_role IS '用户和角色关联表';\n\n-- ----------------------------\n-- Records of system_user_role\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 1, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:17', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 100, 101, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 100, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:12', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 100, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:11', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 103, 1, '1', '2022-01-11 13:19:45', '1', '2022-01-11 13:19:45', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 110, 109, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 111, 110, '110', '2022-02-23 13:14:38', '110', '2022-02-23 13:14:38', '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 113, 111, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', '0', 122);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (18, 1, 2, '1', '2022-05-12 20:39:29', '1', '2022-05-12 20:39:29', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (38, 114, 101, '1', '2024-03-24 22:23:03', '1', '2024-03-24 22:23:03', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (47, 104, 2, '1', '2025-01-04 10:40:33', '1', '2025-01-04 10:40:33', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (48, 100, 155, '1', '2025-04-04 10:41:14', '1', '2025-04-04 10:41:14', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_user_role_seq;\nCREATE SEQUENCE system_user_role_seq\n    START 49;\n\n-- ----------------------------\n-- Table structure for system_users\n-- ----------------------------\nDROP TABLE IF EXISTS system_users;\nCREATE TABLE system_users\n(\n    id          int8         NOT NULL,\n    username    varchar(30)  NOT NULL,\n    password    varchar(100) NULL     DEFAULT '',\n    nickname    varchar(30)  NOT NULL,\n    remark      varchar(500) NULL     DEFAULT NULL,\n    dept_id     int8         NULL     DEFAULT NULL,\n    post_ids    varchar(255) NULL     DEFAULT NULL,\n    email       varchar(50)  NULL     DEFAULT '',\n    mobile      varchar(11)  NULL     DEFAULT '',\n    sex         int2         NULL     DEFAULT 0,\n    avatar      varchar(512) NULL     DEFAULT '',\n    status      int2         NOT NULL DEFAULT 0,\n    login_ip    varchar(50)  NULL     DEFAULT '',\n    login_date  timestamp    NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_users\n    ADD CONSTRAINT pk_system_users PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_users.id IS '用户ID';\nCOMMENT ON COLUMN system_users.username IS '用户账号';\nCOMMENT ON COLUMN system_users.password IS '密码';\nCOMMENT ON COLUMN system_users.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_users.remark IS '备注';\nCOMMENT ON COLUMN system_users.dept_id IS '部门ID';\nCOMMENT ON COLUMN system_users.post_ids IS '岗位编号数组';\nCOMMENT ON COLUMN system_users.email IS '用户邮箱';\nCOMMENT ON COLUMN system_users.mobile IS '手机号码';\nCOMMENT ON COLUMN system_users.sex IS '用户性别';\nCOMMENT ON COLUMN system_users.avatar IS '头像地址';\nCOMMENT ON COLUMN system_users.status IS '帐号状态（0正常 1停用）';\nCOMMENT ON COLUMN system_users.login_ip IS '最后登录IP';\nCOMMENT ON COLUMN system_users.login_date IS '最后登录时间';\nCOMMENT ON COLUMN system_users.creator IS '创建者';\nCOMMENT ON COLUMN system_users.create_time IS '创建时间';\nCOMMENT ON COLUMN system_users.updater IS '更新者';\nCOMMENT ON COLUMN system_users.update_time IS '更新时间';\nCOMMENT ON COLUMN system_users.deleted IS '是否删除';\nCOMMENT ON COLUMN system_users.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_users IS '用户信息表';\n\n-- ----------------------------\n-- Records of system_users\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '芋道源码', '管理员', 103, '[1,2]', '11aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/test/20250502/avatar_1746154660449.png', 0, '0:0:0:0:0:0:0:1', '2025-05-10 18:03:15', 'admin', '2021-01-05 17:03:47', NULL, '2025-05-10 18:03:15', '0', 1);\nINSERT 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$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '0', 118);\nINSERT 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', '0', 119);\nINSERT 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', '0', 120);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 1);\nINSERT 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', '0', 122);\nINSERT 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', '2024-03-24 22:21:05', '1', '2022-03-19 21:50:58', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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$jth0yOj8cSJq84D6vrzusOHDwW/LpBfgBnQ6bfFlD8zNZfM632Ta2', '狗蛋', NULL, 103, '[1]', '', '15601691239', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-17 09:10:27', '1', '2022-07-09 17:44:43', '1', '2025-04-21 14:23:08', '0', 1);\nINSERT 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 (131, 'hh', '$2a$04$jyH9h6.gaw8mpOjPfHIpx.8as2Rzfcmdlj5rlJFwgCw4rsv/MTb2K', '呵呵', NULL, 100, '[]', '777@qq.com', '15601882312', 1, NULL, 0, '', NULL, '1', '2024-04-27 08:45:56', '1', '2025-04-21 14:23:08', '0', 1);\nINSERT 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$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', '小秃头', NULL, NULL, NULL, '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-09-10 21:03:58', NULL, '2024-09-10 21:03:58', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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-04-08 13:09:07', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_users_seq;\nCREATE SEQUENCE system_users_seq\n    START 142;\n\n-- ----------------------------\n-- Table structure for yudao_demo01_contact\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo01_contact;\nCREATE TABLE yudao_demo01_contact\n(\n    id          int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    sex         int2         NOT NULL,\n    birthday    timestamp    NOT NULL,\n    description varchar(255) NOT NULL,\n    avatar      varchar(512) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo01_contact\n    ADD CONSTRAINT pk_yudao_demo01_contact PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo01_contact.id IS '编号';\nCOMMENT ON COLUMN yudao_demo01_contact.name IS '名字';\nCOMMENT ON COLUMN yudao_demo01_contact.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo01_contact.birthday IS '出生年';\nCOMMENT ON COLUMN yudao_demo01_contact.description IS '简介';\nCOMMENT ON COLUMN yudao_demo01_contact.avatar IS '头像';\nCOMMENT ON COLUMN yudao_demo01_contact.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo01_contact.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo01_contact.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo01_contact.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo01_contact.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo01_contact.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo01_contact IS '示例联系人表';\n\n-- ----------------------------\n-- Records of yudao_demo01_contact\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO yudao_demo01_contact (id, name, sex, birthday, description, avatar, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 2, '2023-11-07 00:00:00', '<p>天蚕土豆！呀</p>', 'http://127.0.0.1:48080/admin-api/infra/file/4/get/46f8fa1a37db3f3960d8910ff2fe3962ab3b2db87cf2f8ccb4dc8145b8bdf237.jpeg', '1', '2023-11-15 23:34:30', '1', '2023-11-15 23:47:39', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo01_contact_seq;\nCREATE SEQUENCE yudao_demo01_contact_seq\n    START 2;\n\n-- ----------------------------\n-- Table structure for yudao_demo02_category\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo02_category;\nCREATE TABLE yudao_demo02_category\n(\n    id          int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    parent_id   int8         NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo02_category\n    ADD CONSTRAINT pk_yudao_demo02_category PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo02_category.id IS '编号';\nCOMMENT ON COLUMN yudao_demo02_category.name IS '名字';\nCOMMENT ON COLUMN yudao_demo02_category.parent_id IS '父级编号';\nCOMMENT ON COLUMN yudao_demo02_category.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo02_category.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo02_category.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo02_category.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo02_category.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo02_category.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo02_category IS '示例分类表';\n\n-- ----------------------------\n-- Records of yudao_demo02_category\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 0, '1', '2023-11-15 23:34:30', '1', '2023-11-16 20:24:23', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '番茄', 0, '1', '2023-11-16 20:24:00', '1', '2023-11-16 20:24:15', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '怪怪', 0, '1', '2023-11-16 20:24:32', '1', '2023-11-16 20:24:32', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '小番茄', 2, '1', '2023-11-16 20:24:39', '1', '2023-11-16 20:24:39', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, '大番茄', 2, '1', '2023-11-16 20:24:46', '1', '2023-11-16 20:24:46', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, '11', 3, '1', '2023-11-24 19:29:34', '1', '2023-11-24 19:29:34', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo02_category_seq;\nCREATE SEQUENCE yudao_demo02_category_seq\n    START 7;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_course\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_course;\nCREATE TABLE yudao_demo03_course\n(\n    id          int8         NOT NULL,\n    student_id  int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    score       int2         NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo03_course\n    ADD CONSTRAINT pk_yudao_demo03_course PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_course.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_course.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_course.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_course.score IS '分数';\nCOMMENT ON COLUMN yudao_demo03_course.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_course.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_course.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_course.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_course.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_course.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_course IS '学生课程表';\n\n-- ----------------------------\n-- Records of yudao_demo03_course\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, '语文', 66, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 2, '数学', 22, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (11, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (12, 2, '电脑', 33, '1', '2023-11-17 00:20:42', '1', '2023-11-16 16:20:45', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (13, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:26', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:49', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', '0', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '1', 1);\nINSERT 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', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo03_course_seq;\nCREATE SEQUENCE yudao_demo03_course_seq\n    START 21;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_grade\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_grade;\nCREATE TABLE yudao_demo03_grade\n(\n    id          int8         NOT NULL,\n    student_id  int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    teacher     varchar(255) NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo03_grade\n    ADD CONSTRAINT pk_yudao_demo03_grade PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_grade.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_grade.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_grade.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_grade.teacher IS '班主任';\nCOMMENT ON COLUMN yudao_demo03_grade.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_grade.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_grade.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_grade.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_grade.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_grade.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_grade IS '学生班级表';\n\n-- ----------------------------\n-- Records of yudao_demo03_grade\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo03_grade_seq;\nCREATE SEQUENCE yudao_demo03_grade_seq\n    START 10;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_student\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_student;\nCREATE TABLE yudao_demo03_student\n(\n    id          int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    sex         int2         NOT NULL,\n    birthday    timestamp    NOT NULL,\n    description varchar(255) NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo03_student\n    ADD CONSTRAINT pk_yudao_demo03_student PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_student.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_student.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_student.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo03_student.birthday IS '出生日期';\nCOMMENT ON COLUMN yudao_demo03_student.description IS '简介';\nCOMMENT ON COLUMN yudao_demo03_student.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_student.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_student.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_student.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_student.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_student.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_student IS '学生表';\n\n-- ----------------------------\n-- Records of yudao_demo03_student\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo03_student_seq;\nCREATE SEQUENCE yudao_demo03_student_seq\n    START 10;\n\n"
  },
  {
    "path": "sql/mysql/quartz.sql",
    "content": "/*\n Navicat Premium Data Transfer\n\n Source Server         : 127.0.0.1 MySQL\n Source Server Type    : MySQL\n Source Server Version : 80200 (8.2.0)\n Source Host           : 127.0.0.1:3306\n Source Schema         : ruoyi-vue-pro\n\n Target Server Type    : MySQL\n Target Server Version : 80200 (8.2.0)\n File Encoding         : 65001\n\n Date: 24/07/2024 08:52:41\n*/\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`;\nCREATE TABLE `QRTZ_BLOB_TRIGGERS`  (\n                                       `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                       `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                       `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                       `BLOB_DATA` blob NULL,\n                                       PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,\n                                       INDEX `SCHED_NAME`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE,\n                                       CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_CALENDARS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_CALENDARS`;\nCREATE TABLE `QRTZ_CALENDARS`  (\n                                   `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                   `CALENDAR_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                   `CALENDAR` blob NOT NULL,\n                                   PRIMARY KEY (`SCHED_NAME`, `CALENDAR_NAME`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_CALENDARS\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_CRON_TRIGGERS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`;\nCREATE TABLE `QRTZ_CRON_TRIGGERS`  (\n                                       `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                       `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                       `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                       `CRON_EXPRESSION` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                       `TIME_ZONE_ID` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                       PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,\n                                       CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_CRON_TRIGGERS\n-- ----------------------------\nBEGIN;\nINSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'accessLogCleanJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai'), ('schedulerName', 'brokerageRecordUnfreezeJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai'), ('schedulerName', 'errorLogCleanJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai'), ('schedulerName', 'jobLogCleanJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai'), ('schedulerName', 'payNotifyJob', 'DEFAULT', '* * * * * ?', 'Asia/Shanghai'), ('schedulerName', 'payOrderExpireJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai'), ('schedulerName', 'payOrderSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai'), ('schedulerName', 'payRefundSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai'), ('schedulerName', 'tradeOrderAutoCancelJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai'), ('schedulerName', 'tradeOrderAutoCommentJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai'), ('schedulerName', 'tradeOrderAutoReceiveJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`;\nCREATE TABLE `QRTZ_FIRED_TRIGGERS`  (\n                                        `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                        `ENTRY_ID` varchar(95) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                        `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                        `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                        `INSTANCE_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                        `FIRED_TIME` bigint NOT NULL,\n                                        `SCHED_TIME` bigint NOT NULL,\n                                        `PRIORITY` int NOT NULL,\n                                        `STATE` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                        `JOB_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                        `JOB_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                        `IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                        `REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                        PRIMARY KEY (`SCHED_NAME`, `ENTRY_ID`) USING BTREE,\n                                        INDEX `IDX_QRTZ_FT_TRIG_INST_NAME`(`SCHED_NAME` ASC, `INSTANCE_NAME` ASC) USING BTREE,\n                                        INDEX `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY`(`SCHED_NAME` ASC, `INSTANCE_NAME` ASC, `REQUESTS_RECOVERY` ASC) USING BTREE,\n                                        INDEX `IDX_QRTZ_FT_J_G`(`SCHED_NAME` ASC, `JOB_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,\n                                        INDEX `IDX_QRTZ_FT_JG`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,\n                                        INDEX `IDX_QRTZ_FT_T_G`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE,\n                                        INDEX `IDX_QRTZ_FT_TG`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_JOB_DETAILS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`;\nCREATE TABLE `QRTZ_JOB_DETAILS`  (\n                                     `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                     `JOB_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                     `JOB_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                     `DESCRIPTION` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                     `JOB_CLASS_NAME` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                     `IS_DURABLE` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                     `IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                     `IS_UPDATE_DATA` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                     `REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                     `JOB_DATA` blob NULL,\n                                     PRIMARY KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE,\n                                     INDEX `IDX_QRTZ_J_REQ_RECOVERY`(`SCHED_NAME` ASC, `REQUESTS_RECOVERY` ASC) USING BTREE,\n                                     INDEX `IDX_QRTZ_J_GRP`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_JOB_DETAILS\n-- ----------------------------\nBEGIN;\nINSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'accessLogCleanJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000197400104A4F425F48414E444C45525F4E414D457400116163636573734C6F67436C65616E4A6F627800), ('schedulerName', 'brokerageRecordUnfreezeJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000187400104A4F425F48414E444C45525F4E414D4574001A62726F6B65726167655265636F7264556E667265657A654A6F627800), ('schedulerName', 'errorLogCleanJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000000000000001A7400104A4F425F48414E444C45525F4E414D457400106572726F724C6F67436C65616E4A6F627800), ('schedulerName', 'jobLogCleanJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000000000000001B7400104A4F425F48414E444C45525F4E414D4574000E6A6F624C6F67436C65616E4A6F627800), ('schedulerName', 'payNotifyJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000057400104A4F425F48414E444C45525F4E414D4574000C7061794E6F746966794A6F627800), ('schedulerName', 'payOrderExpireJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000127400104A4F425F48414E444C45525F4E414D457400117061794F726465724578706972654A6F627800), ('schedulerName', 'payOrderSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000117400104A4F425F48414E444C45525F4E414D4574000F7061794F7264657253796E634A6F627800), ('schedulerName', 'payRefundSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000137400104A4F425F48414E444C45525F4E414D45740010706179526566756E6453796E634A6F627800), ('schedulerName', 'tradeOrderAutoCancelJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000157400104A4F425F48414E444C45525F4E414D4574001774726164654F726465724175746F43616E63656C4A6F627800), ('schedulerName', 'tradeOrderAutoCommentJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000177400104A4F425F48414E444C45525F4E414D4574001874726164654F726465724175746F436F6D6D656E744A6F627800), ('schedulerName', 'tradeOrderAutoReceiveJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000167400104A4F425F48414E444C45525F4E414D4574001874726164654F726465724175746F526563656976654A6F627800);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_LOCKS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_LOCKS`;\nCREATE TABLE `QRTZ_LOCKS`  (\n                               `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                               `LOCK_NAME` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                               PRIMARY KEY (`SCHED_NAME`, `LOCK_NAME`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_LOCKS\n-- ----------------------------\nBEGIN;\nINSERT INTO `QRTZ_LOCKS` (`SCHED_NAME`, `LOCK_NAME`) VALUES ('schedulerName', 'STATE_ACCESS'), ('schedulerName', 'TRIGGER_ACCESS');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_PAUSED_TRIGGER_GRPS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`;\nCREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS`  (\n                                             `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                             `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                             PRIMARY KEY (`SCHED_NAME`, `TRIGGER_GROUP`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_PAUSED_TRIGGER_GRPS\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_SCHEDULER_STATE\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;\nCREATE TABLE `QRTZ_SCHEDULER_STATE`  (\n                                         `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                         `INSTANCE_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                         `LAST_CHECKIN_TIME` bigint NOT NULL,\n                                         `CHECKIN_INTERVAL` bigint NOT NULL,\n                                         PRIMARY KEY (`SCHED_NAME`, `INSTANCE_NAME`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_SCHEDULER_STATE\n-- ----------------------------\nBEGIN;\nINSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('schedulerName', 'MacBook-Pro.local1713489703551', 1713742509534, 15000);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`;\nCREATE TABLE `QRTZ_SIMPLE_TRIGGERS`  (\n                                         `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                         `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                         `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                         `REPEAT_COUNT` bigint NOT NULL,\n                                         `REPEAT_INTERVAL` bigint NOT NULL,\n                                         `TIMES_TRIGGERED` bigint NOT NULL,\n                                         PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,\n                                         CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`;\nCREATE TABLE `QRTZ_SIMPROP_TRIGGERS`  (\n                                          `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                          `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                          `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                          `STR_PROP_1` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                          `STR_PROP_2` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                          `STR_PROP_3` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                          `INT_PROP_1` int NULL DEFAULT NULL,\n                                          `INT_PROP_2` int NULL DEFAULT NULL,\n                                          `LONG_PROP_1` bigint NULL DEFAULT NULL,\n                                          `LONG_PROP_2` bigint NULL DEFAULT NULL,\n                                          `DEC_PROP_1` decimal(13, 4) NULL DEFAULT NULL,\n                                          `DEC_PROP_2` decimal(13, 4) NULL DEFAULT NULL,\n                                          `BOOL_PROP_1` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                          `BOOL_PROP_2` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                          PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,\n                                          CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_TRIGGERS\n-- ----------------------------\nDROP TABLE IF EXISTS `QRTZ_TRIGGERS`;\nCREATE TABLE `QRTZ_TRIGGERS`  (\n                                  `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                  `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                  `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                  `JOB_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                  `JOB_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                  `DESCRIPTION` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                  `NEXT_FIRE_TIME` bigint NULL DEFAULT NULL,\n                                  `PREV_FIRE_TIME` bigint NULL DEFAULT NULL,\n                                  `PRIORITY` int NULL DEFAULT NULL,\n                                  `TRIGGER_STATE` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                  `TRIGGER_TYPE` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n                                  `START_TIME` bigint NOT NULL,\n                                  `END_TIME` bigint NULL DEFAULT NULL,\n                                  `CALENDAR_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,\n                                  `MISFIRE_INSTR` smallint NULL DEFAULT NULL,\n                                  `JOB_DATA` blob NULL,\n                                  PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_J`(`SCHED_NAME` ASC, `JOB_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_JG`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_C`(`SCHED_NAME` ASC, `CALENDAR_NAME` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_G`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_STATE`(`SCHED_NAME` ASC, `TRIGGER_STATE` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_N_STATE`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_N_G_STATE`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_NEXT_FIRE_TIME`(`SCHED_NAME` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_NFT_ST`(`SCHED_NAME` ASC, `TRIGGER_STATE` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_NFT_MISFIRE`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC, `TRIGGER_STATE` ASC) USING BTREE,\n                                  INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE,\n                                  CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of QRTZ_TRIGGERS\n-- ----------------------------\nBEGIN;\nINSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'accessLogCleanJob', 'DEFAULT', 'accessLogCleanJob', 'DEFAULT', NULL, 1696348800000, -1, 5, 'PAUSED', 'CRON', 1696301981000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800), ('schedulerName', 'brokerageRecordUnfreezeJob', 'DEFAULT', 'brokerageRecordUnfreezeJob', 'DEFAULT', NULL, 1695909720000, -1, 5, 'PAUSED', 'CRON', 1695909706000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800), ('schedulerName', 'errorLogCleanJob', 'DEFAULT', 'errorLogCleanJob', 'DEFAULT', NULL, 1696348800000, -1, 5, 'PAUSED', 'CRON', 1696302043000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800), ('schedulerName', 'jobLogCleanJob', 'DEFAULT', 'jobLogCleanJob', 'DEFAULT', NULL, 1696348800000, -1, 5, 'PAUSED', 'CRON', 1696302092000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800), ('schedulerName', 'payNotifyJob', 'DEFAULT', 'payNotifyJob', 'DEFAULT', NULL, 1688907102000, 1688907101000, 5, 'PAUSED', 'CRON', 1635294882000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800), ('schedulerName', 'payOrderExpireJob', 'DEFAULT', 'payOrderExpireJob', 'DEFAULT', NULL, 1690011600000, -1, 5, 'PAUSED', 'CRON', 1690011553000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800), ('schedulerName', 'payOrderSyncJob', 'DEFAULT', 'payOrderSyncJob', 'DEFAULT', NULL, 1690011600000, 1690011540000, 5, 'PAUSED', 'CRON', 1690007785000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800), ('schedulerName', 'payRefundSyncJob', 'DEFAULT', 'payRefundSyncJob', 'DEFAULT', NULL, 1690117560000, 1690117500000, 5, 'PAUSED', 'CRON', 1690117424000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800), ('schedulerName', 'tradeOrderAutoCancelJob', 'DEFAULT', 'tradeOrderAutoCancelJob', 'DEFAULT', NULL, 1695727440000, 1695727380000, 5, 'PAUSED', 'CRON', 1695656605000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800), ('schedulerName', 'tradeOrderAutoCommentJob', 'DEFAULT', 'tradeOrderAutoCommentJob', 'DEFAULT', NULL, 1695783840000, 1695783780000, 5, 'PAUSED', 'CRON', 1695742709000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800), ('schedulerName', 'tradeOrderAutoReceiveJob', 'DEFAULT', 'tradeOrderAutoReceiveJob', 'DEFAULT', NULL, 1695742740000, 1695742680000, 5, 'PAUSED', 'CRON', 1695727433000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);\nCOMMIT;\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "sql/mysql/ruoyi-vue-pro.sql",
    "content": "/*\n Navicat Premium Data Transfer\n\n Source Server         : 127.0.0.1 MySQL\n Source Server Type    : MySQL\n Source Server Version : 80200 (8.2.0)\n Source Host           : 127.0.0.1:3306\n Source Schema         : ruoyi-vue-pro\n\n Target Server Type    : MySQL\n Target Server Version : 80200 (8.2.0)\n File Encoding         : 65001\n\n Date: 14/02/2026 16:02:08\n*/\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for infra_api_access_log\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_api_access_log`;\nCREATE TABLE `infra_api_access_log`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',\n  `trace_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '链路追踪编号',\n  `user_id` bigint NOT NULL DEFAULT 0 COMMENT '用户编号',\n  `user_type` tinyint NOT NULL DEFAULT 0 COMMENT '用户类型',\n  `application_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名',\n  `request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求方法名',\n  `request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求地址',\n  `request_params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '请求参数',\n  `response_body` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '响应结果',\n  `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户 IP',\n  `user_agent` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '浏览器 UA',\n  `operate_module` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作模块',\n  `operate_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作名',\n  `operate_type` tinyint NULL DEFAULT 0 COMMENT '操作分类',\n  `begin_time` datetime NOT NULL COMMENT '开始请求时间',\n  `end_time` datetime NOT NULL COMMENT '结束请求时间',\n  `duration` int NOT NULL COMMENT '执行时长',\n  `result_code` int NOT NULL DEFAULT 0 COMMENT '结果码',\n  `result_msg` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '结果提示',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE,\n  INDEX `idx_create_time`(`create_time` ASC) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 36233 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';\n\n-- ----------------------------\n-- Records of infra_api_access_log\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_api_error_log\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_api_error_log`;\nCREATE TABLE `infra_api_error_log`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `trace_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '链路追踪编号',\n  `user_id` bigint NOT NULL DEFAULT 0 COMMENT '用户编号',\n  `user_type` tinyint NOT NULL DEFAULT 0 COMMENT '用户类型',\n  `application_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名',\n  `request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求方法名',\n  `request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求地址',\n  `request_params` varchar(8000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求参数',\n  `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户 IP',\n  `user_agent` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '浏览器 UA',\n  `exception_time` datetime NOT NULL COMMENT '异常发生时间',\n  `exception_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '异常名',\n  `exception_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常导致的消息',\n  `exception_root_cause_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常导致的根消息',\n  `exception_stack_trace` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常的栈轨迹',\n  `exception_class_name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常发生的类全名',\n  `exception_file_name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常发生的类文件',\n  `exception_method_name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常发生的方法名',\n  `exception_line_number` int NOT NULL COMMENT '异常发生的方法所在行',\n  `process_status` tinyint NOT NULL COMMENT '处理状态',\n  `process_time` datetime NULL DEFAULT NULL COMMENT '处理时间',\n  `process_user_id` int NULL DEFAULT 0 COMMENT '处理用户编号',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 23367 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';\n\n-- ----------------------------\n-- Records of infra_api_error_log\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_codegen_column\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_codegen_column`;\nCREATE TABLE `infra_codegen_column`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `table_id` bigint NOT NULL COMMENT '表编号',\n  `column_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '字段名',\n  `data_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '字段类型',\n  `column_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '字段描述',\n  `nullable` bit(1) NOT NULL COMMENT '是否允许为空',\n  `primary_key` bit(1) NOT NULL COMMENT '是否主键',\n  `ordinal_position` int NOT NULL COMMENT '排序',\n  `java_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Java 属性类型',\n  `java_field` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Java 属性名',\n  `dict_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '字典类型',\n  `example` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '数据示例',\n  `create_operation` bit(1) NOT NULL COMMENT '是否为 Create 创建操作的字段',\n  `update_operation` bit(1) NOT NULL COMMENT '是否为 Update 更新操作的字段',\n  `list_operation` bit(1) NOT NULL COMMENT '是否为 List 查询操作的字段',\n  `list_operation_condition` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '=' COMMENT 'List 查询操作的条件类型',\n  `list_operation_result` bit(1) NOT NULL COMMENT '是否为 List 查询操作的返回字段',\n  `html_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '显示类型',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 2880 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';\n\n-- ----------------------------\n-- Records of infra_codegen_column\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_codegen_table\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_codegen_table`;\nCREATE TABLE `infra_codegen_table`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `data_source_config_id` bigint NOT NULL COMMENT '数据源配置的编号',\n  `scene` tinyint NOT NULL DEFAULT 1 COMMENT '生成场景',\n  `table_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '表名称',\n  `table_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '表描述',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `module_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模块名',\n  `business_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务名',\n  `class_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '类名称',\n  `class_comment` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '类描述',\n  `author` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作者',\n  `template_type` tinyint NOT NULL DEFAULT 1 COMMENT '模板类型',\n  `front_type` tinyint NOT NULL COMMENT '前端类型',\n  `parent_menu_id` bigint NULL DEFAULT NULL COMMENT '父菜单编号',\n  `master_table_id` bigint NULL DEFAULT NULL COMMENT '主表的编号',\n  `sub_join_column_id` bigint NULL DEFAULT NULL COMMENT '子表关联主表的字段编号',\n  `sub_join_many` bit(1) NULL DEFAULT NULL COMMENT '主表与子表是否一对多',\n  `tree_parent_column_id` bigint NULL DEFAULT NULL COMMENT '树表的父字段编号',\n  `tree_name_column_id` bigint NULL DEFAULT NULL COMMENT '树表的名字字段编号',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 210 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';\n\n-- ----------------------------\n-- Records of infra_codegen_table\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_config\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_config`;\nCREATE TABLE `infra_config`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '参数主键',\n  `category` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '参数分组',\n  `type` tinyint NOT NULL COMMENT '参数类型',\n  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '参数名称',\n  `config_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '参数键名',\n  `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '参数键值',\n  `visible` bit(1) NOT NULL COMMENT '是否可见',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '参数配置表';\n\n-- ----------------------------\n-- Records of infra_config\n-- ----------------------------\nBEGIN;\nINSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'system.user.init-password', '123456', b'0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-07-20 17:22:47', b'0');\nINSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:33:38', b'0');\nINSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:57:03', b'0');\nINSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:52:07', b'0');\nINSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (10, 'url', 2, 'Swagger 接口文档的地址', 'url.swagger', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:59:00', b'0');\nINSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12, 'test2', 2, 'test3', 'test4', 'test5', b'1', 'test6', '1', '2023-12-03 09:55:16', '1', '2025-04-06 21:00:09', b'0');\nINSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (13, '用户管理-账号初始密码', 2, '用户管理-注册开关', 'system.user.register-enabled', 'true', b'0', '', '1', '2025-04-26 17:23:41', '1', '2025-04-26 17:23:41', b'0');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_data_source_config\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_data_source_config`;\nCREATE TABLE `infra_data_source_config`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键编号',\n  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '参数名称',\n  `url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '数据源连接',\n  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名',\n  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '密码',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '数据源配置表';\n\n-- ----------------------------\n-- Records of infra_data_source_config\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_file\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_file`;\nCREATE TABLE `infra_file`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '文件编号',\n  `config_id` bigint NULL DEFAULT NULL COMMENT '配置编号',\n  `name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件名',\n  `path` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件路径',\n  `url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件 URL',\n  `type` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件类型',\n  `size` int NOT NULL COMMENT '文件大小',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 2163 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';\n\n-- ----------------------------\n-- Records of infra_file\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_file_config\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_file_config`;\nCREATE TABLE `infra_file_config`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `name` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '配置名',\n  `storage` tinyint NOT NULL COMMENT '存储器',\n  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `master` bit(1) NOT NULL COMMENT '是否为主配置',\n  `config` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '存储配置',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 36 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件配置表';\n\n-- ----------------------------\n-- Records of infra_file_config\n-- ----------------------------\nBEGIN;\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_file_content\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_file_content`;\nCREATE TABLE `infra_file_content`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `config_id` bigint NOT NULL COMMENT '配置编号',\n  `path` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件路径',\n  `content` mediumblob NOT NULL COMMENT '文件内容',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 286 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';\n\n-- ----------------------------\n-- Records of infra_file_content\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_job\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_job`;\nCREATE TABLE `infra_job`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务编号',\n  `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务名称',\n  `status` tinyint NOT NULL COMMENT '任务状态',\n  `handler_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '处理器的名字',\n  `handler_param` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '处理器的参数',\n  `cron_expression` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'CRON 表达式',\n  `retry_count` int NOT NULL DEFAULT 0 COMMENT '重试次数',\n  `retry_interval` int NOT NULL DEFAULT 0 COMMENT '重试间隔',\n  `monitor_timeout` int NOT NULL DEFAULT 0 COMMENT '监控超时时间',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';\n\n-- ----------------------------\n-- Records of infra_job\n-- ----------------------------\nBEGIN;\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, '支付通知 Job', 2, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', '2021-10-27 08:34:42', '1', '2024-09-12 13:32:48', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, '支付订单同步 Job', 2, 'payOrderSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 14:36:26', '1', '2023-07-22 15:39:08', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (18, '支付订单过期 Job', 2, 'payOrderExpireJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 15:36:23', '1', '2023-07-22 15:39:54', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (19, '退款订单的同步 Job', 2, 'payRefundSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-23 21:03:44', '1', '2023-07-23 21:09:00', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (21, 'Mall 交易订单的自动过期 Job', 2, 'tradeOrderAutoCancelJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-25 23:43:26', '1', '2025-10-02 11:08:34', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, 'Mall 交易订单的自动收货 Job', 2, 'tradeOrderAutoReceiveJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 19:23:53', '1', '2025-10-02 11:08:36', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (23, 'Mall 交易订单的自动评论 Job', 2, 'tradeOrderAutoCommentJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 23:38:29', '1', '2025-10-02 11:08:38', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (24, 'Mall 佣金解冻 Job', 2, 'brokerageRecordUnfreezeJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-28 22:01:46', '1', '2025-10-02 11:08:04', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (25, '访问日志清理 Job', 2, 'accessLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 10:59:41', '1', '2023-10-03 11:01:10', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (26, '错误日志清理 Job', 2, 'errorLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:00:43', '1', '2023-10-03 11:01:12', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (27, '任务日志清理 Job', 2, 'jobLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:01:33', '1', '2024-09-12 13:40:34', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (33, 'demoJob', 2, 'demoJob', '', '0 * * * * ?', 1, 1, 0, '1', '2024-10-27 19:38:46', '1', '2025-05-10 18:13:54', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (35, '转账订单的同步 Job', 2, 'payTransferSyncJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-05-10 17:35:54', '1', '2025-05-10 18:13:52', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (36, 'IoT 设备离线检查 Job', 2, 'iotDeviceOfflineCheckJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-07-03 23:48:44', '\\\"1\\\"', '2025-07-03 23:48:47', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (37, 'IoT OTA 升级推送 Job', 2, 'iotOtaUpgradeJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-07-03 23:49:07', '\\\"1\\\"', '2025-07-03 23:49:13', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (38, 'Mall 拼团过期 Job', 2, 'combinationRecordExpireJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-10-02 11:07:11', '1', '2025-10-02 11:07:14', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (39, 'Mall 优惠券过期 Job', 2, 'couponExpireJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-10-02 11:07:34', '1', '2025-10-02 11:07:37', b'0');\nINSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (40, 'Mall 商品统计 Job', 2, 'productStatisticsJob', '', '0 0 0 * * ?', 0, 0, 0, '1', '2025-11-22 18:51:25', '1', '2025-11-22 18:56:21', b'0');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for infra_job_log\n-- ----------------------------\nDROP TABLE IF EXISTS `infra_job_log`;\nCREATE TABLE `infra_job_log`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志编号',\n  `job_id` bigint NOT NULL COMMENT '任务编号',\n  `handler_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '处理器的名字',\n  `handler_param` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '处理器的参数',\n  `execute_index` tinyint NOT NULL DEFAULT 1 COMMENT '第几次执行',\n  `begin_time` datetime NOT NULL COMMENT '开始执行时间',\n  `end_time` datetime NULL DEFAULT NULL COMMENT '结束执行时间',\n  `duration` int NULL DEFAULT NULL COMMENT '执行时长',\n  `status` tinyint NOT NULL COMMENT '任务状态',\n  `result` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '结果数据',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 987 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';\n\n-- ----------------------------\n-- Records of infra_job_log\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_dept\n-- ----------------------------\nDROP TABLE IF EXISTS `system_dept`;\nCREATE TABLE `system_dept`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '部门id',\n  `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '部门名称',\n  `parent_id` bigint NOT NULL DEFAULT 0 COMMENT '父部门id',\n  `sort` int NOT NULL DEFAULT 0 COMMENT '显示顺序',\n  `leader_user_id` bigint NULL DEFAULT NULL COMMENT '负责人',\n  `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '联系电话',\n  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '邮箱',\n  `status` tinyint NOT NULL COMMENT '部门状态（0正常 1停用）',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 118 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '部门表';\n\n-- ----------------------------\n-- Records of system_dept\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, '芋道源码', 0, 0, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2026-01-04 18:01:12', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (101, '深圳总公司', 100, 1, 104, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2025-03-29 15:49:55', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (102, '长沙分公司', 100, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:40', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, '研发部门', 101, 1, 104, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2026-01-04 18:01:24', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, '市场部门', 101, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:38', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (105, '测试部门', 101, 3, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-05-16 20:25:15', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (106, '财务部门', 101, 4, 103, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '103', '2022-01-15 21:32:22', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (107, '运维部门', 101, 5, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2023-12-02 09:28:22', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (108, '市场部门', 102, 1, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-02-16 08:35:45', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, '财务部门', 102, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:29', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (110, '新部门', 0, 1, NULL, NULL, NULL, 0, '110', '2022-02-23 20:46:30', '110', '2022-02-23 20:46:30', b'0', 121);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (111, '顶级部门', 0, 1, NULL, NULL, NULL, 0, '113', '2022-03-07 21:44:50', '113', '2022-03-07 21:44:50', b'0', 122);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (112, '产品部门', 101, 100, 1, NULL, NULL, 1, '1', '2023-12-02 09:45:13', '1', '2023-12-02 09:45:31', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (113, '支持部门', 102, 3, 104, NULL, NULL, 1, '1', '2023-12-02 09:47:38', '1', '2025-03-29 15:00:56', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (116, '某个子部门', 0, 1, NULL, NULL, NULL, 0, '1', '2025-12-08 14:51:12', '1', '2025-12-08 14:51:12', b'0', 1);\nINSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (117, '某个子部门 2', 0, 2, NULL, NULL, NULL, 0, '1', '2025-12-08 14:51:25', '1', '2025-12-08 14:51:25', b'0', 1);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_dict_data\n-- ----------------------------\nDROP TABLE IF EXISTS `system_dict_data`;\nCREATE TABLE `system_dict_data`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '字典编码',\n  `sort` int NOT NULL DEFAULT 0 COMMENT '字典排序',\n  `label` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '字典标签',\n  `value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '字典键值',\n  `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '字典类型',\n  `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态（0正常 1停用）',\n  `color_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '颜色类型',\n  `css_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT 'css 样式',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 3054 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';\n\n-- ----------------------------\n-- Records of system_dict_data\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 1, '男', '1', 'system_user_sex', 0, 'primary', 'A', '性别男', 'admin', '2021-01-05 17:03:48', '1', '2025-12-10 13:19:26', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 2, '女', '2', 'system_user_sex', 0, 'success', '', '性别女', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 23:30:37', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8, 1, '正常', '1', 'infra_job_status', 0, 'success', '', '正常状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (9, 2, '暂停', '2', 'infra_job_status', 0, 'danger', '', '停用状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12, 1, '系统内置', '1', 'infra_config_type', 0, 'danger', '', '参数类型 - 系统内置', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:02', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (13, 2, '自定义', '2', 'infra_config_type', 0, 'primary', '', '参数类型 - 自定义', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (14, 1, '通知', '1', 'system_notice_type', 0, 'success', '', '通知', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:05:57', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, 2, '公告', '2', 'system_notice_type', 0, 'info', '', '公告', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:06:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 0, '其它', '0', 'infra_operate_type', 0, 'default', '', '其它操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, 1, '查询', '1', 'infra_operate_type', 0, 'info', '', '查询操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (18, 2, '新增', '2', 'infra_operate_type', 0, 'primary', '', '新增操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:21', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (19, 3, '修改', '3', 'infra_operate_type', 0, 'warning', '', '修改操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (20, 4, '删除', '4', 'infra_operate_type', 0, 'danger', '', '删除操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:23', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, 5, '导出', '5', 'infra_operate_type', 0, 'default', '', '导出操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (23, 6, '导入', '6', 'infra_operate_type', 0, 'default', '', '导入操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:25', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (27, 1, '开启', '0', 'common_status', 0, 'primary', '', '开启状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (28, 2, '关闭', '1', 'common_status', 0, 'info', '', '关闭状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:44', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (29, 1, '目录', '1', 'system_menu_type', 0, '', '', '目录', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (30, 2, '菜单', '2', 'system_menu_type', 0, '', '', '菜单', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (31, 3, '按钮', '3', 'system_menu_type', 0, '', '', '按钮', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (32, 1, '内置', '1', 'system_role_type', 0, 'danger', '', '内置角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (33, 2, '自定义', '2', 'system_role_type', 0, 'primary', '', '自定义角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (34, 1, '全部数据权限', '1', 'system_data_scope', 0, '', '', '全部数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (35, 2, '指定部门数据权限', '2', 'system_data_scope', 0, '', '', '指定部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (36, 3, '本部门数据权限', '3', 'system_data_scope', 0, '', '', '本部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (37, 4, '本部门及以下数据权限', '4', 'system_data_scope', 0, '', '', '本部门及以下数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:21', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (38, 5, '仅本人数据权限', '5', 'system_data_scope', 0, '', '', '仅本人数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:23', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (39, 0, '成功', '0', 'system_login_result', 0, 'success', '', '登陆结果 - 成功', '', '2021-01-18 06:17:36', '1', '2022-02-16 13:23:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (40, 10, '账号或密码不正确', '10', 'system_login_result', 0, 'primary', '', '登陆结果 - 账号或密码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:27', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (41, 20, '用户被禁用', '20', 'system_login_result', 0, 'warning', '', '登陆结果 - 用户被禁用', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:23:57', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (42, 30, '验证码不存在', '30', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不存在', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (43, 31, '验证码不正确', '31', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:11', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (44, 100, '未知异常', '100', 'system_login_result', 0, 'danger', '', '登陆结果 - 未知异常', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:23', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (45, 1, '是', 'true', 'infra_boolean_string', 0, 'danger', '', 'Boolean 是否类型 - 是', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:01:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (46, 1, '否', 'false', 'infra_boolean_string', 0, 'info', '', 'Boolean 是否类型 - 否', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:09:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (50, 1, '单表（增删改查）', '1', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:09:06', '', '2022-03-10 16:33:15', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (51, 2, '树表（增删改查）', '2', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:14:46', '', '2022-03-10 16:33:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (53, 0, '初始化中', '0', 'infra_job_status', 0, 'primary', '', NULL, '', '2021-02-07 07:46:49', '1', '2022-02-16 19:33:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (57, 0, '运行中', '0', 'infra_job_log_status', 0, 'primary', '', 'RUNNING', '', '2021-02-08 10:04:24', '1', '2022-02-16 19:07:48', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (58, 1, '成功', '1', 'infra_job_log_status', 0, 'success', '', NULL, '', '2021-02-08 10:06:57', '1', '2022-02-16 19:07:52', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (59, 2, '失败', '2', 'infra_job_log_status', 0, 'warning', '', '失败', '', '2021-02-08 10:07:38', '1', '2022-02-16 19:07:56', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (60, 1, '会员', '1', 'user_type', 0, 'primary', '', NULL, '', '2021-02-26 00:16:27', '1', '2022-02-16 10:22:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (61, 2, '管理员', '2', 'user_type', 0, 'success', '', NULL, '', '2021-02-26 00:16:34', '1', '2025-04-06 18:37:43', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (62, 0, '未处理', '0', 'infra_api_error_log_process_status', 0, 'primary', '', NULL, '', '2021-02-26 07:07:19', '1', '2022-02-16 20:14:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (63, 1, '已处理', '1', 'infra_api_error_log_process_status', 0, 'success', '', NULL, '', '2021-02-26 07:07:26', '1', '2022-02-16 20:14:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (64, 2, '已忽略', '2', 'infra_api_error_log_process_status', 0, 'danger', '', NULL, '', '2021-02-26 07:07:34', '1', '2022-02-16 20:14:14', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (66, 1, '阿里云', 'ALIYUN', 'system_sms_channel_code', 0, 'primary', '', NULL, '1', '2021-04-05 01:05:26', '1', '2024-07-22 22:23:25', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (67, 1, '验证码', '1', 'system_sms_template_type', 0, 'warning', '', NULL, '1', '2021-04-05 21:50:57', '1', '2022-02-16 12:48:30', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (68, 2, '通知', '2', 'system_sms_template_type', 0, 'primary', '', NULL, '1', '2021-04-05 21:51:08', '1', '2022-02-16 12:48:27', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (69, 0, '营销', '3', 'system_sms_template_type', 0, 'danger', '', NULL, '1', '2021-04-05 21:51:15', '1', '2022-02-16 12:48:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (70, 0, '初始化', '0', 'system_sms_send_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:18:33', '1', '2022-02-16 10:26:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (71, 1, '发送成功', '10', 'system_sms_send_status', 0, 'success', '', NULL, '1', '2021-04-11 20:18:43', '1', '2022-02-16 10:25:56', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (72, 2, '发送失败', '20', 'system_sms_send_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:18:49', '1', '2022-02-16 10:26:03', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (73, 3, '不发送', '30', 'system_sms_send_status', 0, 'info', '', NULL, '1', '2021-04-11 20:19:44', '1', '2022-02-16 10:26:10', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (74, 0, '等待结果', '0', 'system_sms_receive_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:27:43', '1', '2022-02-16 10:28:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (75, 1, '接收成功', '10', 'system_sms_receive_status', 0, 'success', '', NULL, '1', '2021-04-11 20:29:25', '1', '2022-02-16 10:28:28', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (76, 2, '接收失败', '20', 'system_sms_receive_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:29:31', '1', '2022-02-16 10:28:32', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (77, 0, '调试(钉钉)', 'DEBUG_DING_TALK', 'system_sms_channel_code', 0, 'info', '', NULL, '1', '2021-04-13 00:20:37', '1', '2022-02-16 10:10:00', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (80, 100, '账号登录', '100', 'system_login_type', 0, 'primary', '', '账号登录', '1', '2021-10-06 00:52:02', '1', '2022-02-16 13:11:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (81, 101, '社交登录', '101', 'system_login_type', 0, 'info', '', '社交登录', '1', '2021-10-06 00:52:17', '1', '2022-02-16 13:11:40', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (83, 200, '主动登出', '200', 'system_login_type', 0, 'primary', '', '主动登出', '1', '2021-10-06 00:52:58', '1', '2022-02-16 13:11:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (85, 202, '强制登出', '202', 'system_login_type', 0, 'danger', '', '强制退出', '1', '2021-10-06 00:53:41', '1', '2022-02-16 13:11:57', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (86, 0, '病假', '1', 'bpm_oa_leave_type', 0, 'primary', '', NULL, '1', '2021-09-21 22:35:28', '1', '2022-02-16 10:00:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (87, 1, '事假', '2', 'bpm_oa_leave_type', 0, 'info', '', NULL, '1', '2021-09-21 22:36:11', '1', '2022-02-16 10:00:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (88, 2, '婚假', '3', 'bpm_oa_leave_type', 0, 'warning', '', NULL, '1', '2021-09-21 22:36:38', '1', '2022-02-16 10:00:53', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (112, 0, '微信 Wap 网站支付', 'wx_wap', 'pay_channel_code', 0, 'success', '', '微信 Wap 网站支付', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (113, 1, '微信公众号支付', 'wx_pub', 'pay_channel_code', 0, 'success', '', '微信公众号支付', '1', '2021-12-03 10:40:24', '1', '2023-07-19 20:08:47', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (114, 2, '微信小程序支付', 'wx_lite', 'pay_channel_code', 0, 'success', '', '微信小程序支付', '1', '2021-12-03 10:41:06', '1', '2023-07-19 20:08:50', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (115, 3, '微信 App 支付', 'wx_app', 'pay_channel_code', 0, 'success', '', '微信 App 支付', '1', '2021-12-03 10:41:20', '1', '2023-07-19 20:08:56', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (116, 10, '支付宝 PC 网站支付', 'alipay_pc', 'pay_channel_code', 0, 'primary', '', '支付宝 PC 网站支付', '1', '2021-12-03 10:42:09', '1', '2023-07-19 20:09:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (117, 11, '支付宝 Wap 网站支付', 'alipay_wap', 'pay_channel_code', 0, 'primary', '', '支付宝 Wap 网站支付', '1', '2021-12-03 10:42:26', '1', '2023-07-19 20:09:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (118, 12, '支付宝 App 支付', 'alipay_app', 'pay_channel_code', 0, 'primary', '', '支付宝 App 支付', '1', '2021-12-03 10:42:55', '1', '2023-07-19 20:09:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (119, 14, '支付宝扫码支付', 'alipay_qr', 'pay_channel_code', 0, 'primary', '', '支付宝扫码支付', '1', '2021-12-03 10:43:10', '1', '2023-07-19 20:09:28', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (120, 10, '通知成功', '10', 'pay_notify_status', 0, 'success', '', '通知成功', '1', '2021-12-03 11:02:41', '1', '2023-07-19 10:08:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, 20, '通知失败', '20', 'pay_notify_status', 0, 'danger', '', '通知失败', '1', '2021-12-03 11:02:59', '1', '2023-07-19 10:08:21', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, 0, '等待通知', '0', 'pay_notify_status', 0, 'info', '', '未通知', '1', '2021-12-03 11:03:10', '1', '2023-07-19 10:08:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (123, 10, '支付成功', '10', 'pay_order_status', 0, 'success', '', '支付成功', '1', '2021-12-03 11:18:29', '1', '2023-07-19 18:04:28', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (124, 30, '支付关闭', '30', 'pay_order_status', 0, 'info', '', '支付关闭', '1', '2021-12-03 11:18:42', '1', '2023-07-19 18:05:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (125, 0, '等待支付', '0', 'pay_order_status', 0, 'info', '', '未支付', '1', '2021-12-03 11:18:18', '1', '2023-07-19 18:04:15', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (600, 5, '首页', '1', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (601, 4, '秒杀活动页', '2', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (602, 3, '砍价活动页', '3', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (603, 2, '限时折扣页', '4', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (604, 1, '满减送页', '5', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1118, 0, '等待退款', '0', 'pay_refund_status', 0, 'info', '', '等待退款', '1', '2021-12-10 16:44:59', '1', '2023-07-19 10:14:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1119, 20, '退款失败', '20', 'pay_refund_status', 0, 'danger', '', '退款失败', '1', '2021-12-10 16:45:10', '1', '2023-07-19 10:15:10', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1124, 10, '退款成功', '10', 'pay_refund_status', 0, 'success', '', '退款成功', '1', '2021-12-10 16:46:26', '1', '2023-07-19 10:15:00', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1127, 1, '审批中', '1', 'bpm_process_instance_status', 0, 'default', '', '流程实例的状态 - 进行中', '1', '2022-01-07 23:47:22', '1', '2024-03-16 16:11:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1128, 2, '审批通过', '2', 'bpm_process_instance_status', 0, 'success', '', '流程实例的状态 - 已完成', '1', '2022-01-07 23:47:49', '1', '2024-03-16 16:11:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1129, 1, '审批中', '1', 'bpm_task_status', 0, 'primary', '', '流程实例的结果 - 处理中', '1', '2022-01-07 23:48:32', '1', '2024-03-08 22:41:37', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1130, 2, '审批通过', '2', 'bpm_task_status', 0, 'success', '', '流程实例的结果 - 通过', '1', '2022-01-07 23:48:45', '1', '2024-03-08 22:41:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1131, 3, '审批不通过', '3', 'bpm_task_status', 0, 'danger', '', '流程实例的结果 - 不通过', '1', '2022-01-07 23:48:55', '1', '2024-03-08 22:41:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1132, 4, '已取消', '4', 'bpm_task_status', 0, 'info', '', '流程实例的结果 - 撤销', '1', '2022-01-07 23:49:06', '1', '2024-03-08 22:41:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1133, 10, '流程表单', '10', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 流程表单', '103', '2022-01-11 23:51:30', '103', '2022-01-11 23:51:30', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1134, 20, '业务表单', '20', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 业务表单', '103', '2022-01-11 23:51:47', '103', '2022-01-11 23:51:47', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1135, 10, '角色', '10', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 角色', '103', '2022-01-12 23:21:22', '1', '2024-03-06 02:53:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1136, 20, '部门的成员', '20', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的成员', '103', '2022-01-12 23:21:47', '1', '2024-03-06 02:53:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1137, 21, '部门的负责人', '21', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的负责人', '103', '2022-01-12 23:33:36', '1', '2024-03-06 02:53:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1138, 30, '用户', '30', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 用户', '103', '2022-01-12 23:34:02', '1', '2024-03-06 02:53:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1139, 40, '用户组', '40', 'bpm_task_candidate_strategy', 0, 'warning', '', '任务分配规则的类型 - 用户组', '103', '2022-01-12 23:34:21', '1', '2024-03-06 02:53:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1140, 60, '流程表达式', '60', 'bpm_task_candidate_strategy', 0, 'danger', '', '任务分配规则的类型 - 流程表达式', '103', '2022-01-12 23:34:43', '1', '2024-03-06 02:53:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1141, 22, '岗位', '22', 'bpm_task_candidate_strategy', 0, 'success', '', '任务分配规则的类型 - 岗位', '103', '2022-01-14 18:41:55', '1', '2024-03-06 02:53:21', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1145, 1, '管理后台', '1', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 管理后台', '1', '2022-02-02 13:15:06', '1', '2022-03-10 16:32:59', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1146, 2, '用户 APP', '2', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 用户 APP', '1', '2022-02-02 13:15:19', '1', '2022-03-10 16:33:03', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1150, 1, '数据库', '1', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:28', '1', '2022-03-15 00:25:28', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1151, 10, '本地磁盘', '10', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:41', '1', '2022-03-15 00:25:56', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1152, 11, 'FTP 服务器', '11', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:06', '1', '2022-03-15 00:26:10', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1153, 12, 'SFTP 服务器', '12', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:22', '1', '2022-03-15 00:26:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1154, 20, 'S3 对象存储', '20', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:31', '1', '2022-03-15 00:26:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1155, 103, '短信登录', '103', 'system_login_type', 0, 'default', '', NULL, '1', '2022-05-09 23:57:58', '1', '2022-05-09 23:58:09', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1156, 1, 'password', 'password', 'system_oauth2_grant_type', 0, 'default', '', '密码模式', '1', '2022-05-12 00:22:05', '1', '2022-05-11 16:26:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1157, 2, 'authorization_code', 'authorization_code', 'system_oauth2_grant_type', 0, 'primary', '', '授权码模式', '1', '2022-05-12 00:22:59', '1', '2022-05-11 16:26:02', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1158, 3, 'implicit', 'implicit', 'system_oauth2_grant_type', 0, 'success', '', '简化模式', '1', '2022-05-12 00:23:40', '1', '2022-05-11 16:26:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1159, 4, 'client_credentials', 'client_credentials', 'system_oauth2_grant_type', 0, 'default', '', '客户端模式', '1', '2022-05-12 00:23:51', '1', '2022-05-11 16:26:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1160, 5, 'refresh_token', 'refresh_token', 'system_oauth2_grant_type', 0, 'info', '', '刷新模式', '1', '2022-05-12 00:24:02', '1', '2022-05-11 16:26:11', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1162, 1, '销售中', '1', 'product_spu_status', 0, 'success', '', '商品 SPU 状态 - 销售中', '1', '2022-10-24 21:19:47', '1', '2022-10-24 21:20:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1163, 0, '仓库中', '0', 'product_spu_status', 0, 'info', '', '商品 SPU 状态 - 仓库中', '1', '2022-10-24 21:20:54', '1', '2022-10-24 21:21:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1164, 0, '回收站', '-1', 'product_spu_status', 0, 'default', '', '商品 SPU 状态 - 回收站', '1', '2022-10-24 21:21:11', '1', '2022-10-24 21:21:11', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1165, 1, '满减', '1', 'promotion_discount_type', 0, 'success', '', '优惠类型 - 满减', '1', '2022-11-01 12:46:41', '1', '2022-11-01 12:50:11', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1166, 2, '折扣', '2', 'promotion_discount_type', 0, 'primary', '', '优惠类型 - 折扣', '1', '2022-11-01 12:46:51', '1', '2022-11-01 12:50:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1167, 1, '固定日期', '1', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 固定日期', '1', '2022-11-02 00:07:34', '1', '2022-11-04 00:07:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1168, 2, '领取之后', '2', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 领取之后', '1', '2022-11-02 00:07:54', '1', '2022-11-04 00:07:52', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1169, 1, '通用劵', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', '2022-11-02 00:28:22', '1', '2023-09-28 00:27:42', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1170, 2, '商品劵', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', '2022-11-02 00:28:34', '1', '2023-09-28 00:27:44', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1171, 1, '未使用', '1', 'promotion_coupon_status', 0, 'primary', '', '优惠劵的状态 - 已领取', '1', '2022-11-04 00:15:08', '1', '2023-10-03 12:54:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1172, 2, '已使用', '2', 'promotion_coupon_status', 0, 'success', '', '优惠劵的状态 - 已使用', '1', '2022-11-04 00:15:21', '1', '2022-11-04 19:16:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1173, 3, '已过期', '3', 'promotion_coupon_status', 0, 'info', '', '优惠劵的状态 - 已过期', '1', '2022-11-04 00:15:43', '1', '2022-11-04 19:16:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1174, 1, '直接领取', '1', 'promotion_coupon_take_type', 0, 'primary', '', '优惠劵的领取方式 - 直接领取', '1', '2022-11-04 19:13:00', '1', '2022-11-04 19:13:25', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1175, 2, '指定发放', '2', 'promotion_coupon_take_type', 0, 'success', '', '优惠劵的领取方式 - 指定发放', '1', '2022-11-04 19:13:13', '1', '2022-11-04 19:14:48', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1176, 10, '未开始', '10', 'promotion_activity_status', 0, 'primary', '', '促销活动的状态枚举 - 未开始', '1', '2022-11-04 22:54:49', '1', '2022-11-04 22:55:53', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1177, 20, '进行中', '20', 'promotion_activity_status', 0, 'success', '', '促销活动的状态枚举 - 进行中', '1', '2022-11-04 22:55:06', '1', '2022-11-04 22:55:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1178, 30, '已结束', '30', 'promotion_activity_status', 0, 'info', '', '促销活动的状态枚举 - 已结束', '1', '2022-11-04 22:55:41', '1', '2022-11-04 22:55:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1179, 40, '已关闭', '40', 'promotion_activity_status', 0, 'warning', '', '促销活动的状态枚举 - 已关闭', '1', '2022-11-04 22:56:10', '1', '2022-11-04 22:56:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1180, 10, '满 N 元', '10', 'promotion_condition_type', 0, 'primary', '', '营销的条件类型 - 满 N 元', '1', '2022-11-04 22:59:45', '1', '2022-11-04 22:59:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1181, 20, '满 N 件', '20', 'promotion_condition_type', 0, 'success', '', '营销的条件类型 - 满 N 件', '1', '2022-11-04 23:00:02', '1', '2022-11-04 23:00:02', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1182, 10, '申请售后', '10', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 申请售后', '1', '2022-11-19 20:53:33', '1', '2022-11-19 20:54:42', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1183, 20, '商品待退货', '20', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商品待退货', '1', '2022-11-19 20:54:36', '1', '2022-11-19 20:58:58', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1184, 30, '商家待收货', '30', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商家待收货', '1', '2022-11-19 20:56:56', '1', '2022-11-19 20:59:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1185, 40, '等待退款', '40', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 等待退款', '1', '2022-11-19 20:59:54', '1', '2022-11-19 21:00:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1186, 50, '退款成功', '50', 'trade_after_sale_status', 0, 'default', '', '交易售后状态 - 退款成功', '1', '2022-11-19 21:00:33', '1', '2022-11-19 21:00:33', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1187, 61, '买家取消', '61', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 买家取消', '1', '2022-11-19 21:01:29', '1', '2022-11-19 21:01:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1188, 62, '商家拒绝', '62', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒绝', '1', '2022-11-19 21:02:17', '1', '2022-11-19 21:02:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1189, 63, '商家拒收货', '63', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒收货', '1', '2022-11-19 21:02:37', '1', '2022-11-19 21:03:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1190, 10, '售中退款', '10', 'trade_after_sale_type', 0, 'success', '', '交易售后的类型 - 售中退款', '1', '2022-11-19 21:05:05', '1', '2022-11-19 21:38:23', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1191, 20, '售后退款', '20', 'trade_after_sale_type', 0, 'primary', '', '交易售后的类型 - 售后退款', '1', '2022-11-19 21:05:32', '1', '2022-11-19 21:38:32', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1192, 10, '仅退款', '10', 'trade_after_sale_way', 0, 'primary', '', '交易售后的方式 - 仅退款', '1', '2022-11-19 21:39:19', '1', '2022-11-19 21:39:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1193, 20, '退货退款', '20', 'trade_after_sale_way', 0, 'success', '', '交易售后的方式 - 退货退款', '1', '2022-11-19 21:39:38', '1', '2022-11-19 21:39:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1194, 10, '微信小程序', '10', 'terminal', 0, 'default', '', '终端 - 微信小程序', '1', '2022-12-10 10:51:11', '1', '2022-12-10 10:51:57', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1195, 20, 'H5 网页', '20', 'terminal', 0, 'default', '', '终端 - H5 网页', '1', '2022-12-10 10:51:30', '1', '2022-12-10 10:51:59', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1196, 11, '微信公众号', '11', 'terminal', 0, 'default', '', '终端 - 微信公众号', '1', '2022-12-10 10:54:16', '1', '2022-12-10 10:52:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1197, 31, '苹果 App', '31', 'terminal', 0, 'default', '', '终端 - 苹果 App', '1', '2022-12-10 10:54:42', '1', '2022-12-10 10:52:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1198, 32, '安卓 App', '32', 'terminal', 0, 'default', '', '终端 - 安卓 App', '1', '2022-12-10 10:55:02', '1', '2022-12-10 10:59:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1199, 0, '普通订单', '0', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 普通订单', '1', '2022-12-10 16:34:14', '1', '2022-12-10 16:34:14', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1200, 1, '秒杀订单', '1', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 秒杀订单', '1', '2022-12-10 16:34:26', '1', '2022-12-10 16:34:26', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1201, 2, '砍价订单', '2', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 拼团订单', '1', '2022-12-10 16:34:36', '1', '2024-09-07 14:18:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1202, 3, '拼团订单', '3', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 砍价订单', '1', '2022-12-10 16:34:48', '1', '2024-09-07 14:18:32', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1203, 0, '待支付', '0', 'trade_order_status', 0, 'default', '', '交易订单状态 - 待支付', '1', '2022-12-10 16:49:29', '1', '2022-12-10 16:49:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1204, 10, '待发货', '10', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 待发货', '1', '2022-12-10 16:49:53', '1', '2022-12-10 16:51:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1205, 20, '已发货', '20', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 已发货', '1', '2022-12-10 16:50:13', '1', '2022-12-10 16:51:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1206, 30, '已完成', '30', 'trade_order_status', 0, 'success', '', '交易订单状态 - 已完成', '1', '2022-12-10 16:50:30', '1', '2022-12-10 16:51:06', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1207, 40, '已取消', '40', 'trade_order_status', 0, 'danger', '', '交易订单状态 - 已取消', '1', '2022-12-10 16:50:50', '1', '2022-12-10 16:51:00', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1208, 0, '未售后', '0', 'trade_order_item_after_sale_status', 0, 'info', '', '交易订单项的售后状态 - 未售后', '1', '2022-12-10 20:58:42', '1', '2022-12-10 20:59:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1209, 10, '售后中', '10', 'trade_order_item_after_sale_status', 0, 'primary', '', '交易订单项的售后状态 - 售后中', '1', '2022-12-10 20:59:21', '1', '2024-07-21 17:01:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1210, 20, '已退款', '20', 'trade_order_item_after_sale_status', 0, 'success', '', '交易订单项的售后状态 - 已退款', '1', '2022-12-10 20:59:46', '1', '2024-07-21 17:01:35', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1211, 1, '完全匹配', '1', 'mp_auto_reply_request_match', 0, 'primary', '', '公众号自动回复的请求关键字匹配模式 - 完全匹配', '1', '2023-01-16 23:30:39', '1', '2023-01-16 23:31:00', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1212, 2, '半匹配', '2', 'mp_auto_reply_request_match', 0, 'success', '', '公众号自动回复的请求关键字匹配模式 - 半匹配', '1', '2023-01-16 23:30:55', '1', '2023-01-16 23:31:10', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1213, 1, '文本', 'text', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 文本', '1', '2023-01-17 22:17:32', '1', '2023-01-17 22:17:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1214, 2, '图片', 'image', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图片', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:47', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1215, 3, '语音', 'voice', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 语音', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:20:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1216, 4, '视频', 'video', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:21:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1217, 5, '小视频', 'shortvideo', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 小视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:59', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1218, 6, '图文', 'news', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图文', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1219, 7, '音乐', 'music', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 音乐', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1220, 8, '地理位置', 'location', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 地理位置', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:23:51', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1221, 9, '链接', 'link', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 链接', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1222, 10, '事件', 'event', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 事件', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1223, 0, '初始化', '0', 'system_mail_send_status', 0, 'primary', '', '邮件发送状态 - 初始化\\n', '1', '2023-01-26 09:53:49', '1', '2023-01-26 16:36:14', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1224, 10, '发送成功', '10', 'system_mail_send_status', 0, 'success', '', '邮件发送状态 - 发送成功', '1', '2023-01-26 09:54:28', '1', '2023-01-26 16:36:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1225, 20, '发送失败', '20', 'system_mail_send_status', 0, 'danger', '', '邮件发送状态 - 发送失败', '1', '2023-01-26 09:54:50', '1', '2023-01-26 16:36:26', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1226, 30, '不发送', '30', 'system_mail_send_status', 0, 'info', '', '邮件发送状态 -  不发送', '1', '2023-01-26 09:55:06', '1', '2023-01-26 16:36:36', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1227, 1, '通知公告', '1', 'system_notify_template_type', 0, 'primary', '', '站内信模版的类型 - 通知公告', '1', '2023-01-28 10:35:59', '1', '2023-01-28 10:35:59', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1228, 2, '系统消息', '2', 'system_notify_template_type', 0, 'success', '', '站内信模版的类型 - 系统消息', '1', '2023-01-28 10:36:20', '1', '2023-01-28 10:36:25', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1230, 13, '支付宝条码支付', 'alipay_bar', 'pay_channel_code', 0, 'primary', '', '支付宝条码支付', '1', '2023-02-18 23:32:24', '1', '2023-07-19 20:09:23', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1231, 10, 'Vue2 Element UI 标准模版', '10', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:03:55', '1', '2023-04-13 00:03:55', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1232, 20, 'Vue3 Element Plus 标准模版', '20', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:08', '1', '2023-04-13 00:04:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1234, 30, 'Vben2.0 Ant Design Schema 模版', '30', 'infra_codegen_front_type', 1, '', '', '', '1', '2023-04-13 00:04:26', '1', '2025-07-27 10:55:14', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1244, 0, '按件', '1', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:40', '1', '2023-05-21 22:46:40', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1245, 1, '按重量', '2', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:58', '1', '2023-05-21 22:46:58', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1246, 2, '按体积', '3', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:47:18', '1', '2023-05-21 22:47:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1335, 11, '订单积分抵扣', '11', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:27', '1', '2023-10-11 07:41:43', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1336, 1, '签到', '1', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:48', '1', '2023-08-20 11:59:53', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1341, 20, '已退款', '20', 'pay_order_status', 0, 'danger', '', '已退款', '1', '2023-07-19 18:05:37', '1', '2023-07-19 18:05:37', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1342, 21, '请求成功，但是结果失败', '21', 'pay_notify_status', 0, 'warning', '', '请求成功，但是结果失败', '1', '2023-07-19 18:10:47', '1', '2023-07-19 18:11:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1343, 22, '请求失败', '22', 'pay_notify_status', 0, 'warning', '', NULL, '1', '2023-07-19 18:11:05', '1', '2023-07-19 18:11:27', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1344, 4, '微信扫码支付', 'wx_native', 'pay_channel_code', 0, 'success', '', '微信扫码支付', '1', '2023-07-19 20:07:47', '1', '2023-07-19 20:09:03', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1345, 5, '微信条码支付', 'wx_bar', 'pay_channel_code', 0, 'success', '', '微信条码支付\\n', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1346, 1, '支付单', '1', 'pay_notify_type', 0, 'primary', '', '支付单', '1', '2023-07-20 12:23:17', '1', '2023-07-20 12:23:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1347, 2, '退款单', '2', 'pay_notify_type', 0, 'danger', '', NULL, '1', '2023-07-20 12:23:26', '1', '2023-07-20 12:23:26', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1348, 20, '模拟支付', 'mock', 'pay_channel_code', 0, 'default', '', '模拟支付', '1', '2023-07-29 11:10:51', '1', '2023-07-29 03:14:10', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1349, 12, '订单积分抵扣（整单取消）', '12', 'member_point_biz_type', 0, '', '', '', '1', '2023-08-20 12:00:03', '1', '2023-10-11 07:42:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1350, 0, '管理员调整', '0', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1351, 1, '邀新奖励', '1', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1352, 11, '下单奖励', '11', 'member_experience_biz_type', 0, 'success', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:09', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1353, 12, '下单奖励（整单取消）', '12', 'member_experience_biz_type', 0, 'warning', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1354, 4, '签到奖励', '4', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1355, 5, '抽奖奖励', '5', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1356, 1, '快递发货', '1', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:04:55', '1', '2023-08-23 00:04:55', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1357, 2, '用户自提', '2', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:05:05', '1', '2023-08-23 00:05:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1358, 3, '品类劵', '3', 'promotion_product_scope', 0, 'default', '', '', '1', '2023-09-01 23:43:07', '1', '2023-09-28 00:27:47', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1359, 1, '人人分销', '1', 'brokerage_enabled_condition', 0, '', '', '所有用户都可以分销', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1360, 2, '指定分销', '2', 'brokerage_enabled_condition', 0, '', '', '仅可后台手动设置推广员', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1361, 1, '首次绑定', '1', 'brokerage_bind_mode', 0, '', '', '只要用户没有推广人，随时都可以绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1362, 2, '注册绑定', '2', 'brokerage_bind_mode', 0, '', '', '仅新用户注册时才能绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1363, 3, '覆盖绑定', '3', 'brokerage_bind_mode', 0, '', '', '如果用户已经有推广人，推广人会被变更', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1364, 1, '钱包', '1', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1365, 2, '银行卡', '2', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1366, 3, '微信收款码', '3', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:25', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1367, 4, '支付宝收款码', '4', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:37', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1368, 1, '订单返佣', '1', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1369, 2, '申请提现', '2', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1370, 3, '申请提现驳回', '3', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1371, 0, '待结算', '0', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1372, 1, '已结算', '1', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1373, 2, '已取消', '2', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1374, 0, '审核中', '0', 'brokerage_withdraw_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1375, 10, '审核通过', '10', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1376, 11, '提现成功', '11', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1377, 20, '审核不通过', '20', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1378, 21, '提现失败', '21', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1379, 0, '工商银行', '0', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1380, 1, '建设银行', '1', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1381, 2, '农业银行', '2', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1382, 3, '中国银行', '3', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1383, 4, '交通银行', '4', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1384, 5, '招商银行', '5', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1385, 21, '钱包', 'wallet', 'pay_channel_code', 0, 'primary', '', '', '1', '2023-10-01 21:46:19', '1', '2023-10-01 21:48:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1386, 1, '砍价中', '1', 'promotion_bargain_record_status', 0, 'default', '', '', '1', '2023-10-05 10:41:26', '1', '2023-10-05 10:41:26', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1387, 2, '砍价成功', '2', 'promotion_bargain_record_status', 0, 'success', '', '', '1', '2023-10-05 10:41:39', '1', '2023-10-05 10:41:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1388, 3, '砍价失败', '3', 'promotion_bargain_record_status', 0, 'warning', '', '', '1', '2023-10-05 10:41:57', '1', '2023-10-05 10:41:57', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1389, 0, '拼团中', '0', 'promotion_combination_record_status', 0, '', '', '', '1', '2023-10-08 07:24:44', '1', '2024-10-13 10:08:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1390, 1, '拼团成功', '1', 'promotion_combination_record_status', 0, 'success', '', '', '1', '2023-10-08 07:24:56', '1', '2024-10-13 10:08:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1391, 2, '拼团失败', '2', 'promotion_combination_record_status', 0, 'warning', '', '', '1', '2023-10-08 07:25:11', '1', '2024-10-13 10:08:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1392, 2, '管理员修改', '2', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:41:34', '1', '2023-10-11 07:41:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1393, 13, '订单积分抵扣（单个退款）', '13', 'member_point_biz_type', 0, '', '', '', '1', '2023-10-11 07:42:29', '1', '2023-10-11 07:42:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1394, 21, '订单积分奖励', '21', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:44', '1', '2023-10-11 07:42:44', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1395, 22, '订单积分奖励（整单取消）', '22', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:55', '1', '2023-10-11 07:43:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1396, 23, '订单积分奖励（单个退款）', '23', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:43:16', '1', '2023-10-11 07:43:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1397, 13, '下单奖励（单个退款）', '13', 'member_experience_biz_type', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1398, 5, '网上转账', '5', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:24', '1', '2023-10-18 21:55:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1399, 6, '支付宝', '6', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:38', '1', '2023-10-18 21:55:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1400, 7, '微信支付', '7', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:53', '1', '2023-10-18 21:55:53', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1401, 8, '其他', '8', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:56:06', '1', '2023-10-18 21:56:06', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1402, 1, 'IT', '1', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:15', '1', '2024-02-18 23:30:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1403, 2, '金融业', '2', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:29', '1', '2024-02-18 23:30:43', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1404, 3, '房地产', '3', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:41', '1', '2024-02-18 23:30:48', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1405, 4, '商业服务', '4', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:54', '1', '2024-02-18 23:30:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1406, 5, '运输/物流', '5', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:03', '1', '2024-02-18 23:31:00', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1407, 6, '生产', '6', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:13', '1', '2024-02-18 23:31:08', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1408, 7, '政府', '7', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:27', '1', '2024-02-18 23:31:13', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1409, 8, '文化传媒', '8', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:37', '1', '2024-02-18 23:31:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1422, 1, 'A （重点客户）', '1', 'crm_customer_level', 0, 'primary', '', '', '1', '2023-10-28 23:07:13', '1', '2023-10-28 23:07:13', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1423, 2, 'B （普通客户）', '2', 'crm_customer_level', 0, 'info', '', '', '1', '2023-10-28 23:07:35', '1', '2023-10-28 23:07:35', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1424, 3, 'C （非优先客户）', '3', 'crm_customer_level', 0, 'default', '', '', '1', '2023-10-28 23:07:53', '1', '2023-10-28 23:07:53', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1425, 1, '促销', '1', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:29', '1', '2023-10-28 23:08:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1426, 2, '搜索引擎', '2', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:39', '1', '2023-10-28 23:08:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1427, 3, '广告', '3', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:47', '1', '2023-10-28 23:08:47', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1428, 4, '转介绍', '4', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:58', '1', '2023-10-28 23:08:58', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1429, 5, '线上注册', '5', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:12', '1', '2023-10-28 23:09:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1430, 6, '线上咨询', '6', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:22', '1', '2023-10-28 23:09:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1431, 7, '预约上门', '7', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:39', '1', '2023-10-28 23:09:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1432, 8, '陌拜', '8', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:04', '1', '2023-10-28 23:10:04', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1433, 9, '电话咨询', '9', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:18', '1', '2023-10-28 23:10:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1434, 10, '邮件咨询', '10', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:33', '1', '2023-10-28 23:10:33', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1435, 10, 'Gitee', '10', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:42', '1', '2023-11-04 13:04:42', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1436, 20, '钉钉', '20', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:54', '1', '2023-11-04 13:04:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1437, 30, '企业微信', '30', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:09', '1', '2023-11-04 13:05:09', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1438, 31, '微信公众平台', '31', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:18', '1', '2023-11-04 13:05:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1439, 32, '微信开放平台', '32', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:30', '1', '2023-11-04 13:05:30', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1440, 34, '微信小程序', '34', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1441, 1, '上架', '1', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:34', '1', '2023-10-30 21:49:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1442, 0, '下架', '0', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:13', '1', '2023-10-30 21:49:13', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1443, 15, '子表', '15', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-13 23:06:16', '1', '2023-11-13 23:06:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1444, 10, '主表（标准模式）', '10', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:32:49', '1', '2023-11-14 12:32:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1445, 11, '主表（ERP 模式）', '11', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:33:05', '1', '2023-11-14 12:33:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1446, 12, '主表（内嵌模式）', '12', 'infra_codegen_template_type', 0, '', '', '', '1', '2023-11-14 12:33:31', '1', '2023-11-14 12:33:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1447, 1, '负责人', '1', 'crm_permission_level', 0, 'default', '', '', '1', '2023-11-30 09:53:12', '1', '2023-11-30 09:53:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1448, 2, '只读', '2', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:29', '1', '2023-11-30 09:53:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1449, 3, '读写', '3', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:36', '1', '2023-11-30 09:53:36', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1450, 0, '未提交', '0', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:56:59', '1', '2023-11-30 18:56:59', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1451, 10, '审批中', '10', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:10', '1', '2023-11-30 18:57:10', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1452, 20, '审核通过', '20', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:24', '1', '2023-11-30 18:57:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1453, 30, '审核不通过', '30', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:32', '1', '2023-11-30 18:57:32', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1454, 40, '已取消', '40', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:42', '1', '2023-11-30 18:57:42', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1456, 1, '支票', '1', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:29', '1', '2023-10-18 21:54:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1457, 2, '现金', '2', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:41', '1', '2023-10-18 21:54:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1458, 3, '邮政汇款', '3', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:53', '1', '2023-10-18 21:54:53', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1459, 4, '电汇', '4', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:07', '1', '2023-10-18 21:55:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1461, 1, '个', '1', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:26', '1', '2023-12-05 23:02:26', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1462, 2, '块', '2', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:34', '1', '2023-12-05 23:02:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1463, 3, '只', '3', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:57', '1', '2023-12-05 23:02:57', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1464, 4, '把', '4', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:05', '1', '2023-12-05 23:03:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1465, 5, '枚', '5', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:14', '1', '2023-12-05 23:03:14', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1466, 6, '瓶', '6', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:20', '1', '2023-12-05 23:03:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1467, 7, '盒', '7', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:30', '1', '2023-12-05 23:03:30', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1468, 8, '台', '8', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:41', '1', '2023-12-05 23:03:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1469, 9, '吨', '9', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:48', '1', '2023-12-05 23:03:48', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1470, 10, '千克', '10', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:03', '1', '2023-12-05 23:04:03', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1471, 11, '米', '11', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:12', '1', '2023-12-05 23:04:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1472, 12, '箱', '12', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:25', '1', '2023-12-05 23:04:25', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1473, 13, '套', '13', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:34', '1', '2023-12-05 23:04:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1474, 1, '打电话', '1', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:20', '1', '2024-01-15 20:48:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1475, 2, '发短信', '2', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:31', '1', '2024-01-15 20:48:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1476, 3, '上门拜访', '3', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:07', '1', '2024-01-15 20:49:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1477, 4, '微信沟通', '4', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:15', '1', '2024-01-15 20:49:15', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1482, 4, '转账失败', '20', 'pay_transfer_status', 0, 'warning', '', '', '1', '2023-10-28 16:24:16', '1', '2025-05-08 12:59:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1483, 3, '转账成功', '10', 'pay_transfer_status', 0, 'success', '', '', '1', '2023-10-28 16:23:50', '1', '2025-05-08 12:58:58', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1484, 2, '转账进行中', '5', 'pay_transfer_status', 0, 'info', '', '', '1', '2023-10-28 16:23:12', '1', '2025-05-08 12:58:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1485, 1, '等待转账', '0', 'pay_transfer_status', 0, 'default', '', '', '1', '2023-10-28 16:21:43', '1', '2023-10-28 16:23:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1486, 10, '其它入库', '10', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:07:25', '1', '2024-02-05 18:07:43', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1487, 11, '其它入库（作废）', '11', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:08:07', '1', '2024-02-05 19:20:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1488, 20, '其它出库', '20', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:08:51', '1', '2024-02-05 18:08:51', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1489, 21, '其它出库（作废）', '21', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:09:00', '1', '2024-02-05 19:20:10', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1490, 10, '未审核', '10', 'erp_audit_status', 0, 'default', '', '', '1', '2024-02-06 00:00:21', '1', '2024-02-06 00:00:21', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1491, 20, '已审核', '20', 'erp_audit_status', 0, 'success', '', '', '1', '2024-02-06 00:00:35', '1', '2024-02-06 00:00:35', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1492, 30, '调拨入库', '30', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:19', '1', '2024-02-07 12:36:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1493, 31, '调拨入库（作废）', '31', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:29', '1', '2024-02-07 20:37:11', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1494, 32, '调拨出库', '32', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:38', '1', '2024-02-07 12:36:33', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1495, 33, '调拨出库（作废）', '33', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:49', '1', '2024-02-07 20:37:06', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1496, 40, '盘盈入库', '40', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:53:00', '1', '2024-02-08 08:53:09', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1497, 41, '盘盈入库（作废）', '41', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:53:39', '1', '2024-02-16 19:40:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1498, 42, '盘亏出库', '42', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:54:16', '1', '2024-02-08 08:54:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1499, 43, '盘亏出库（作废）', '43', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:54:31', '1', '2024-02-16 19:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1500, 50, '销售出库', '50', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-11 21:47:25', '1', '2024-02-11 21:50:40', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1501, 51, '销售出库（作废）', '51', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-11 21:47:37', '1', '2024-02-11 21:51:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1502, 60, '销售退货入库', '60', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-12 06:51:05', '1', '2024-02-12 06:51:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1503, 61, '销售退货入库（作废）', '61', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-12 06:51:18', '1', '2024-02-12 06:51:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1504, 70, '采购入库', '70', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:02', '1', '2024-02-16 13:10:02', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1505, 71, '采购入库（作废）', '71', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:10', '1', '2024-02-16 19:40:40', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1506, 80, '采购退货出库', '80', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:17', '1', '2024-02-16 13:10:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1507, 81, '采购退货出库（作废）', '81', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:26', '1', '2024-02-16 19:40:33', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1509, 3, '审批不通过', '3', 'bpm_process_instance_status', 0, 'danger', '', '', '1', '2024-03-16 16:12:06', '1', '2024-03-16 16:12:06', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1510, 4, '已取消', '4', 'bpm_process_instance_status', 0, 'warning', '', '', '1', '2024-03-16 16:12:22', '1', '2024-03-16 16:12:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1511, 5, '已退回', '5', 'bpm_task_status', 0, 'warning', '', '', '1', '2024-03-16 19:10:46', '1', '2024-03-08 22:41:40', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1512, 6, '委派中', '6', 'bpm_task_status', 0, 'primary', '', '', '1', '2024-03-17 10:06:22', '1', '2024-03-08 22:41:40', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1513, 7, '审批通过中', '7', 'bpm_task_status', 0, 'success', '', '', '1', '2024-03-17 10:06:47', '1', '2024-03-08 22:41:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1514, 0, '待审批', '0', 'bpm_task_status', 0, 'info', '', '', '1', '2024-03-17 10:07:11', '1', '2024-03-08 22:41:42', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1515, 35, '发起人自选', '35', 'bpm_task_candidate_strategy', 0, '', '', '', '1', '2024-03-22 19:45:16', '1', '2024-03-22 19:45:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1516, 1, '执行监听器', 'execution', 'bpm_process_listener_type', 0, 'primary', '', '', '1', '2024-03-23 12:54:03', '1', '2024-03-23 19:14:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1517, 1, '任务监听器', 'task', 'bpm_process_listener_type', 0, 'success', '', '', '1', '2024-03-23 12:54:13', '1', '2024-03-23 19:14:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1526, 1, 'Java 类', 'class', 'bpm_process_listener_value_type', 0, 'primary', '', '', '1', '2024-03-23 15:08:45', '1', '2024-03-23 19:14:32', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1527, 2, '表达式', 'expression', 'bpm_process_listener_value_type', 0, 'success', '', '', '1', '2024-03-23 15:09:06', '1', '2024-03-23 19:14:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1528, 3, '代理表达式', 'delegateExpression', 'bpm_process_listener_value_type', 0, 'info', '', '', '1', '2024-03-23 15:11:23', '1', '2024-03-23 19:14:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1529, 1, '天', '1', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:26', '1', '2024-03-29 22:50:26', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1530, 2, '周', '2', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:36', '1', '2024-03-29 22:50:36', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1531, 3, '月', '3', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:46', '1', '2024-03-29 22:50:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1532, 4, '季度', '4', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:01', '1', '2024-03-29 22:51:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1533, 5, '年', '5', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:07', '1', '2024-03-29 22:51:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1534, 1, '赢单', '1', 'crm_business_end_status_type', 0, 'success', '', '', '1', '2024-04-13 23:26:57', '1', '2024-04-13 23:26:57', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1535, 2, '输单', '2', 'crm_business_end_status_type', 0, 'primary', '', '', '1', '2024-04-13 23:27:31', '1', '2024-04-13 23:27:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1536, 3, '无效', '3', 'crm_business_end_status_type', 0, 'info', '', '', '1', '2024-04-13 23:27:59', '1', '2024-04-13 23:27:59', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1537, 1, 'OpenAI', 'OpenAI', 'ai_platform', 0, '', '', '', '1', '2024-05-09 22:33:47', '1', '2024-05-09 22:58:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1538, 2, 'Ollama', 'Ollama', 'ai_platform', 0, '', '', '', '1', '2024-05-17 23:02:55', '1', '2024-05-17 23:02:55', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1539, 3, '文心一言', 'YiYan', 'ai_platform', 0, '', '', '', '1', '2024-05-18 09:24:20', '1', '2024-05-18 09:29:01', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1540, 4, '讯飞星火', 'XingHuo', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:08:56', '1', '2024-05-18 10:08:56', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1541, 5, '通义千问', 'TongYi', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:32:29', '1', '2024-07-06 15:42:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1542, 6, 'StableDiffusion', 'StableDiffusion', 'ai_platform', 0, '', '', '', '1', '2024-06-01 15:09:31', '1', '2024-06-01 15:10:25', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1543, 10, '进行中', '10', 'ai_image_status', 0, 'primary', '', '', '1', '2024-06-26 20:51:41', '1', '2024-06-26 20:52:48', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1544, 20, '已完成', '20', 'ai_image_status', 0, 'success', '', '', '1', '2024-06-26 20:52:07', '1', '2024-06-26 20:52:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1545, 30, '已失败', '30', 'ai_image_status', 0, 'warning', '', '', '1', '2024-06-26 20:52:25', '1', '2024-06-26 20:52:35', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1546, 7, 'Midjourney', 'Midjourney', 'ai_platform', 0, '', '', '', '1', '2024-06-26 22:14:46', '1', '2024-06-26 22:14:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1547, 10, '进行中', '10', 'ai_music_status', 0, 'primary', '', '', '1', '2024-06-27 22:45:22', '1', '2024-06-28 00:56:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1548, 20, '已完成', '20', 'ai_music_status', 0, 'success', '', '', '1', '2024-06-27 22:45:33', '1', '2024-06-28 00:56:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1549, 30, '已失败', '30', 'ai_music_status', 0, 'danger', '', '', '1', '2024-06-27 22:45:44', '1', '2024-06-28 00:56:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1550, 1, '歌词模式', '1', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:31', '1', '2024-06-28 01:22:25', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1554, 13, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2025-02-24 20:18:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1558, 7, '博客文章', '7', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:23', '1', '2024-07-07 15:50:23', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1559, 8, '想法', '8', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:31', '1', '2024-07-07 15:50:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1560, 9, '大纲', '9', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:37', '1', '2024-07-07 15:50:37', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1561, 1, '自动', '1', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:06', '1', '2024-07-07 15:51:06', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1562, 2, '友善', '2', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:19', '1', '2024-07-07 15:51:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1563, 3, '随意', '3', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:27', '1', '2024-07-07 15:51:27', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1564, 4, '友好', '4', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:37', '1', '2024-07-07 15:51:37', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1565, 5, '专业', '5', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:49', '1', '2024-07-07 15:52:02', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1566, 6, '诙谐', '6', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:15', '1', '2024-07-07 15:52:15', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1567, 7, '有趣', '7', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:24', '1', '2024-07-07 15:52:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1568, 8, '正式', '8', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:54:33', '1', '2024-07-07 15:54:33', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1569, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1570, 1, '自动', '1', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:34', '1', '2024-07-07 15:19:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1571, 2, '电子邮件', '2', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:50', '1', '2024-07-07 15:49:30', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1572, 3, '消息', '3', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:01', '1', '2024-07-07 15:49:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1573, 4, '评论', '4', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:13', '1', '2024-07-07 15:49:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1574, 1, '自动', '1', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:18', '1', '2024-07-07 15:44:18', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1575, 2, '中文', '2', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:28', '1', '2024-07-07 15:44:28', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1576, 3, '英文', '3', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:37', '1', '2024-07-07 15:44:37', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1577, 4, '韩语', '4', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:28', '1', '2024-07-07 15:46:28', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1578, 5, '日语', '5', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:44', '1', '2024-07-07 15:46:44', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1579, 1, '自动', '1', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:34', '1', '2024-07-07 15:48:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1580, 2, '短', '2', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:44', '1', '2024-07-07 15:48:44', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1581, 3, '中等', '3', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:52', '1', '2024-07-07 15:48:52', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1582, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1584, 1, '撰写', '1', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:00', '1', '2024-07-10 21:26:00', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1585, 2, '回复', '2', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:06', '1', '2024-07-10 21:26:06', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1586, 2, '腾讯云', 'TENCENT', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:16', '1', '2024-07-22 22:23:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1587, 3, '华为云', 'HUAWEI', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:46', '1', '2024-07-22 22:23:53', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1588, 1, 'OpenAI 微软', 'AzureOpenAI', 'ai_platform', 0, '', '', '', '1', '2024-08-10 14:07:41', '1', '2024-08-10 14:07:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1589, 10, 'BPMN 设计器', '10', 'bpm_model_type', 0, 'primary', '', '', '1', '2024-08-26 15:22:17', '1', '2024-08-26 16:46:02', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1590, 20, 'SIMPLE 设计器', '20', 'bpm_model_type', 0, 'success', '', '', '1', '2024-08-26 15:22:27', '1', '2024-08-26 16:45:58', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1591, 4, '七牛云', 'QINIU', 'system_sms_channel_code', 0, '', '', '', '1', '2024-08-31 08:45:03', '1', '2024-08-31 08:45:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1592, 3, '新人券', '3', 'promotion_coupon_take_type', 0, 'info', '', '新人注册后，自动发放', '1', '2024-09-03 11:57:16', '1', '2024-09-03 11:57:28', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1593, 5, '微信零钱', '5', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2024-10-13 11:06:48', '1', '2025-05-10 08:24:55', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', '2025-02-23 19:51:40', '1', '2025-02-23 19:52:02', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', '2025-02-23 20:58:04', '1', '2025-02-23 20:58:04', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', '2025-02-24 20:19:09', '1', '2025-02-24 20:19:09', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1686, 1, '聊天', '1', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:26:34', '1', '2025-03-03 12:26:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1687, 2, '图像', '2', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:23', '1', '2025-03-03 12:27:23', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1688, 3, '音频', '3', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:51', '1', '2025-03-03 12:27:51', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1689, 4, '视频', '4', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:03', '1', '2025-03-03 12:28:03', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1690, 5, '向量', '5', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:15', '1', '2025-03-03 12:28:15', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1693, 15, '月之暗面', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-11-24 07:17:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2025-03-17 09:28:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2025-03-17 09:28:28', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2025-03-17 09:28:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2025-03-17 09:28:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2025-03-17 09:28:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2025-03-17 09:28:47', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2010, 1, '移动网络', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2025-06-12 23:27:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2025-03-17 09:28:51', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2025-03-17 09:28:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-03-17 09:29:09', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-03-17 09:29:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-03-17 09:29:14', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2025-03-17 09:29:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2025-03-17 09:29:27', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2025-03-17 09:29:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:27', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2033, 4, 'PH值', 'pH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:36', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2034, 5, '土壤EC值', 'dS/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:43', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2035, 6, '太阳总辐射', 'W/㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2036, 7, '降雨量', 'mm/hour', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2037, 8, '乏', 'var', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:27', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2038, 9, '厘泊', 'cP', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:33', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2039, 10, '饱和度', 'aw', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:11', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2040, 11, '个', 'pcs', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2041, 12, '厘斯', 'cst', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2042, 13, '巴', 'bar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2043, 14, '纳克每升', 'ppt', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:27', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2044, 15, '微克每升', 'ppb', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2045, 16, '微西每厘米', 'uS/cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:34', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2046, 17, '牛顿每库仑', 'N/C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2047, 18, '伏特每米', 'V/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:43', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2048, 19, '滴速', 'ml/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2049, 20, '毫米汞柱', 'mmHg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:48', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2050, 21, '血糖', 'mmol/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:54', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2051, 22, '毫米每秒', 'mm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:02', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2052, 23, '转每分钟', 'turn/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2053, 24, '次', 'count', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:09', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2054, 25, '档', 'gear', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:11', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2055, 26, '步', 'stepCount', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:13', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2056, 27, '标准立方米每小时', 'Nm3/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:15', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2057, 28, '千伏', 'kV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2058, 29, '千伏安', 'kVA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:24', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2060, 30, '千乏', 'kVar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2061, 31, '微瓦每平方厘米', 'uw/cm2', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2062, 32, '只', '只', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2063, 33, '相对湿度', '%RH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2064, 34, '立方米每秒', 'm³/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2065, 35, '公斤每秒', 'kg/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2066, 36, '转每分钟', 'r/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2067, 37, '吨每小时', 't/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2068, 38, '千卡每小时', 'KCL/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2069, 39, '升每秒', 'L/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2070, 40, '兆帕', 'Mpa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2071, 41, '立方米每小时', 'm³/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2072, 42, '千乏时', 'kvarh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2073, 43, '微克每升', 'μg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2074, 44, '千卡路里', 'kcal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2075, 45, '吉字节', 'GB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2076, 46, '兆字节', 'MB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2077, 47, '千字节', 'KB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2078, 48, '字节', 'B', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2079, 49, '微克每平方分米每天', 'μg/(d㎡·d)', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2080, 50, '无', '', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2081, 51, '百万分率', 'ppm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2082, 52, '像素', 'pixel', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2083, 53, '照度', 'Lux', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2084, 54, '重力加速度', 'grav', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2085, 55, '分贝', 'dB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2086, 56, '百分比', '%', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2087, 57, '流明', 'lm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2088, 58, '比特', 'bit', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2089, 59, '克每毫升', 'g/mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2090, 60, '克每升', 'g/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2091, 61, '毫克每升', 'mg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2092, 62, '微克每立方米', 'μg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2093, 63, '毫克每立方米', 'mg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2094, 64, '克每立方米', 'g/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2095, 65, '千克每立方米', 'kg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2096, 66, '纳法', 'nF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2097, 67, '皮法', 'pF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2098, 68, '微法', 'μF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2099, 69, '法拉', 'F', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2100, 70, '欧姆', 'Ω', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2101, 71, '微安', 'μA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2102, 72, '毫安', 'mA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2103, 73, '千安', 'kA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2104, 74, '安培', 'A', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2105, 75, '毫伏', 'mV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2106, 76, '伏特', 'V', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2107, 77, '毫秒', 'ms', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2108, 78, '秒', 's', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2109, 79, '分钟', 'min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2110, 80, '小时', 'h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2111, 81, '日', 'day', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2112, 82, '周', 'week', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2113, 83, '月', 'month', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2114, 84, '年', 'year', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2115, 85, '节', 'kn', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2116, 86, '千米每小时', 'km/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2117, 87, '米每秒', 'm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2118, 88, '秒', '″', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2119, 89, '分', '′', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2120, 90, '度', '°', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2121, 91, '弧度', 'rad', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2122, 92, '赫兹', 'Hz', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2123, 93, '微瓦', 'μW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2124, 94, '毫瓦', 'mW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2125, 95, '千瓦特', 'kW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2126, 96, '瓦特', 'W', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2127, 97, '卡路里', 'cal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2128, 98, '千瓦时', 'kW·h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2129, 99, '瓦时', 'Wh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2130, 100, '电子伏', 'eV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2131, 101, '千焦', 'kJ', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2132, 102, '焦耳', 'J', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2133, 103, '华氏度', '℉', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2134, 104, '开尔文', 'K', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2135, 105, '吨', 't', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2136, 106, '摄氏度', '°C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2137, 107, '毫帕', 'mPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2138, 108, '百帕', 'hPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2139, 109, '千帕', 'kPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2140, 110, '帕斯卡', 'Pa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2141, 111, '毫克', 'mg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2142, 112, '克', 'g', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2143, 113, '千克', 'kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2144, 114, '牛', 'N', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2145, 115, '毫升', 'mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2146, 116, '升', 'L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2147, 117, '立方毫米', 'mm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2148, 118, '立方厘米', 'cm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2149, 119, '立方千米', 'km³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2150, 120, '立方米', 'm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2151, 121, '公顷', 'h㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2152, 122, '平方厘米', 'c㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2153, 123, '平方毫米', 'm㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2154, 124, '平方千米', 'k㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2155, 125, '平方米', '㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2156, 126, '纳米', 'nm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2157, 127, '微米', 'μm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2158, 128, '毫米', 'mm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2159, 129, '厘米', 'cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2165, 1, 'HTTP', '1', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:39:54', '1', '2025-06-24 12:44:47', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2166, 2, 'TCP', '2', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:40:06', '1', '2025-06-24 12:44:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2167, 3, 'WebSocket', '3', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:40:24', '1', '2025-06-24 12:44:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2168, 10, 'MQTT', '10', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:40:37', '1', '2025-06-24 12:44:44', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2169, 20, 'Database', '20', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:05', '1', '2025-06-24 12:44:44', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2170, 21, 'Redis Stream', '21', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:18', '1', '2025-06-24 12:44:43', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2171, 30, 'RocketMQ', '30', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:30', '1', '2025-06-24 12:44:42', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2172, 31, 'RabbitMQ', '31', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:47', '1', '2025-06-24 12:44:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2173, 32, 'Kafka', '32', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:59', '1', '2025-06-24 12:44:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2174, 1, '设备上下线变更', '1', 'iot_rule_scene_trigger_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:00:01', '\\\"1\\\"', '2025-07-06 10:28:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2175, 2, '物模型属性上报', '2', 'iot_rule_scene_trigger_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:00:09', '\\\"1\\\"', '2025-07-06 10:28:22', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2176, 1, '设备状态', 'state', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:24:58', '1', '2025-03-20 15:24:58', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2177, 2, '设备属性', 'property', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:09', '1', '2025-03-20 15:25:09', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2178, 3, '设备事件', 'event', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:23', '1', '2025-03-20 15:25:23', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2179, 4, '设备服务', 'service', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:39', '1', '2025-03-20 15:25:39', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2180, 5, '设备配置', 'config', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:51', '1', '2025-03-20 15:25:57', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2181, 6, '设备 OTA', 'ota', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:26:17', '1', '2025-03-20 15:26:17', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2182, 7, '设备注册', 'register', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:26:35', '1', '2025-03-20 15:26:35', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2183, 8, '设备拓扑', 'topology', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:26:46', '1', '2025-03-20 15:26:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2184, 1, '设备属性设置', '1', 'iot_rule_scene_action_type_enum', 0, 'primary', '', '', '1', '2025-03-28 15:27:12', '\\\"1\\\"', '2025-07-06 10:37:33', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2185, 2, '设备服务调用', '2', 'iot_rule_scene_action_type_enum', 0, 'primary', '', '', '1', '2025-03-28 15:27:25', '\\\"1\\\"', '2025-07-06 10:37:41', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2186, 100, '告警触发', '100', 'iot_rule_scene_action_type_enum', 0, 'primary', '', '', '1', '2025-03-28 15:27:35', '\\\"1\\\"', '2025-07-06 10:37:50', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3000, 16, '百川智能', 'BaiChuan', 'ai_platform', 0, '', '', '', '1', '2025-03-23 12:15:46', '1', '2025-03-23 12:15:46', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3001, 40, 'Vben5.0 Ant Design Schema 模版', '40', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-04-23 21:47:47', '1', '2025-09-04 23:25:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3002, 6, '支付宝余额', '6', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2025-05-10 08:24:49', '1', '2025-05-10 08:24:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3004, 3, 'WARN', '3', 'iot_alert_level', 0, 'warning', '', '', '1', '2025-06-27 20:32:22', '1', '2025-06-27 20:34:31', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3005, 1, 'INFO', '1', 'iot_alert_level', 0, 'primary', '', '', '1', '2025-06-27 20:33:28', '1', '2025-06-27 20:34:35', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3006, 5, 'ERROR', '5', 'iot_alert_level', 0, 'danger', '', '', '1', '2025-06-27 20:33:50', '1', '2025-06-27 20:33:50', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3007, 1, '短信', '1', 'iot_alert_receive_type', 0, '', '', '', '1', '2025-06-27 22:49:30', '1', '2025-06-27 22:49:30', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3008, 2, '邮箱', '2', 'iot_alert_receive_type', 0, '', '', '', '1', '2025-06-27 22:49:39', '1', '2025-06-27 22:50:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3009, 3, '站内信', '3', 'iot_alert_receive_type', 0, '', '', '', '1', '2025-06-27 22:50:20', '1', '2025-06-27 22:50:20', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3010, 1, '全部设备', '1', 'iot_ota_task_device_scope', 0, '', '', '', '1', '2025-07-02 09:43:09', '1', '2025-07-02 09:43:09', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3011, 2, '指定设备', '2', 'iot_ota_task_device_scope', 0, '', '', '', '1', '2025-07-02 09:43:15', '1', '2025-07-02 09:43:15', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3012, 10, '进行中', '10', 'iot_ota_task_status', 0, 'primary', '', '', '1', '2025-07-02 09:44:01', '\\\"1\\\"', '2025-07-02 09:44:21', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3013, 20, '已结束', '20', 'iot_ota_task_status', 0, 'success', '', '', '1', '2025-07-02 09:44:14', '\\\"1\\\"', '2025-07-02 23:56:12', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3014, 30, '已取消', '30', 'iot_ota_task_status', 0, 'danger', '', '', '1', '2025-07-02 09:44:36', '1', '2025-07-02 09:44:36', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3015, 0, '待推送', '0', 'iot_ota_task_record_status', 0, '', '', '', '1', '2025-07-02 09:45:16', '1', '2025-07-02 09:45:16', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3016, 10, '已推送', '10', 'iot_ota_task_record_status', 0, '', '', '', '1', '2025-07-02 09:45:25', '1', '2025-07-02 09:45:25', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3017, 20, '升级中', '20', 'iot_ota_task_record_status', 0, 'primary', '', '', '1', '2025-07-02 09:45:37', '1', '2025-07-02 09:45:37', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3018, 30, '升级成功', '30', 'iot_ota_task_record_status', 0, 'success', '', '', '1', '2025-07-02 09:45:47', '1', '2025-07-02 09:45:47', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3019, 40, '升级失败', '40', 'iot_ota_task_record_status', 0, 'danger', '', '', '1', '2025-07-02 09:46:02', '1', '2025-07-02 09:46:02', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3020, 50, '升级取消', '50', 'iot_ota_task_record_status', 0, 'warning', '', '', '1', '2025-07-02 09:46:09', '\\\"1\\\"', '2025-07-02 09:46:27', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3024, 3, '设备事件上报', '3', 'iot_rule_scene_trigger_type_enum', 0, '', '', '', '1', '2025-07-06 10:28:29', '1', '2025-07-06 10:28:29', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3025, 4, '设备服务调用', '4', 'iot_rule_scene_trigger_type_enum', 0, '', '', '', '1', '2025-07-06 10:28:35', '1', '2025-07-06 10:28:35', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3026, 100, '定时触发', '100', 'iot_rule_scene_trigger_type_enum', 0, '', '', '', '1', '2025-07-06 10:28:48', '1', '2025-07-06 10:28:48', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3027, 101, '告警恢复', '101', 'iot_rule_scene_action_type_enum', 0, '', '', '', '1', '2025-07-06 10:37:57', '1', '2025-07-06 10:37:57', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3028, 2, 'Anthropic', 'Anthropic', 'ai_platform', 0, '', '', '', '1', '2025-08-21 22:54:24', '1', '2025-08-21 22:57:58', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3029, 2, '谷歌 Gemini', 'Gemini', 'ai_platform', 0, '', '', '', '1', '2025-08-22 22:39:35', '1', '2025-08-22 22:44:49', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3030, 1, '文件系统', 'filesystem', 'ai_mcp_client_name', 0, '', '', '', '1', '2025-08-28 13:58:43', '1', '2025-08-28 21:19:42', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3031, 41, 'Vben5.0 Ant Design 标准模版', '41', 'infra_codegen_front_type', 0, '', '', '', '1', '2025-09-04 23:26:07', '1', '2025-09-04 23:26:07', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3032, 50, 'Vben5.0 Element Plus Schema 模版', '50', 'infra_codegen_front_type', 0, '', '', '', '1', '2025-09-04 23:26:38', '1', '2025-09-04 23:26:38', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3033, 51, 'Vben5.0 Element Plus 标准模版', '51', 'infra_codegen_front_type', 0, '', '', '', '1', '2025-09-04 23:26:49', '1', '2025-09-04 23:26:49', b'0');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3043, 4, 'MQTT', 'mqtt', 'iot_protocol_type', 0, 'success', '', 'MQTT 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3044, 5, 'EMQX', 'emqx', 'iot_protocol_type', 0, 'success', '', 'EMQX 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3045, 6, 'CoAP', 'coap', 'iot_protocol_type', 0, '', '', 'CoAP 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3046, 7, 'Modbus TCP Server', 'modbus_tcp_server', 'iot_protocol_type', 0, '', '', 'Modbus TCP Server 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-12 15:16:45', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3047, 0, 'JSON', 'json', 'iot_serialize_type', 0, 'success', '', 'JSON 格式', '1', '2026-02-04 00:33:19', '1', '2026-02-04 00:33:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3048, 1, '二进制', 'binary', 'iot_serialize_type', 0, 'warning', '', '二进制格式', '1', '2026-02-04 00:33:19', '1', '2026-02-04 00:33:19', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3049, 8, 'Modbus TCP Client', 'modbus_tcp_client', 'iot_protocol_type', 0, '', '', 'Modbus TCP Client 协议', '1', '2026-02-08 18:29:46', '1', '2026-02-12 15:16:32', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3050, 2, '边缘采集', '2', 'iot_modbus_mode', 0, 'success', '', '设备主动上报数据，无需轮询', '1', '2025-06-12 22:56:06', '1', '2026-02-09 13:03:23', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3051, 1, 'Modbus TCP', '1', 'iot_modbus_frame_format', 0, 'default', '', 'MBAP 头部格式', '1', '2025-06-12 22:56:06', '1', '2025-06-12 22:56:06', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3052, 2, 'Modbus RTU', '2', 'iot_modbus_frame_format', 0, 'warning', '', 'CRC16 校验格式', '1', '2025-06-12 22:56:06', '1', '2025-06-12 22:56:06', b'0');\nINSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3053, 1, '云端轮询', '1', 'iot_modbus_mode', 0, 'primary', '', '网关主动轮询读取设备寄存器', '1', '2025-06-12 22:56:06', '1', '2025-06-12 22:56:06', b'0');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_dict_type\n-- ----------------------------\nDROP TABLE IF EXISTS `system_dict_type`;\nCREATE TABLE `system_dict_type`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '字典主键',\n  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '字典名称',\n  `type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '字典类型',\n  `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态（0正常 1停用）',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 2012 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';\n\n-- ----------------------------\n-- Records of system_dict_type\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1, '用户性别', 'system_user_sex', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2022-05-16 20:29:32', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (6, '参数类型', 'infra_config_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:36:54', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (7, '通知类型', 'system_notice_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:35:26', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (9, '操作类型', 'infra_operate_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:01', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (10, '系统状态', 'common_status', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:21:28', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (11, 'Boolean 是否类型', 'infra_boolean_string', 0, 'boolean 转是否', '', '2021-01-19 03:20:08', '', '2022-02-01 16:37:10', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (104, '登陆结果', 'system_login_result', 0, '登陆结果', '', '2021-01-18 06:17:11', '', '2022-02-01 16:36:00', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (106, '代码生成模板类型', 'infra_codegen_template_type', 0, NULL, '', '2021-02-05 07:08:06', '1', '2022-05-16 20:26:50', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (107, '定时任务状态', 'infra_job_status', 0, NULL, '', '2021-02-07 07:44:16', '', '2022-02-01 16:51:11', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (108, '定时任务日志状态', 'infra_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2022-02-01 16:50:43', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (110, 'API 异常数据的处理状态', 'infra_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2022-02-01 16:50:53', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (111, '短信渠道编码', 'system_sms_channel_code', 0, NULL, '1', '2021-04-05 01:04:50', '1', '2022-02-16 02:09:08', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (112, '短信模板的类型', 'system_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:43', '1', '2022-02-01 16:35:06', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (113, '短信发送状态', 'system_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:03', '1', '2022-02-01 16:35:09', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (114, '短信接收状态', 'system_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:14', '1', '2022-02-01 16:35:14', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (116, '登陆日志的类型', 'system_login_type', 0, '登陆日志的类型', '1', '2021-10-06 00:50:46', '1', '2022-02-01 16:35:56', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (117, 'OA 请假类型', 'bpm_oa_leave_type', 0, NULL, '1', '2021-09-21 22:34:33', '1', '2022-01-22 10:41:37', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (130, '支付渠道编码类型', 'pay_channel_code', 0, '支付渠道的编码', '1', '2021-12-03 10:35:08', '1', '2023-07-10 10:11:39', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (131, '支付回调状态', 'pay_notify_status', 0, '支付回调状态（包括退款回调）', '1', '2021-12-03 10:53:29', '1', '2023-07-19 18:09:43', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (132, '支付订单状态', 'pay_order_status', 0, '支付订单状态', '1', '2021-12-03 11:17:50', '1', '2021-12-03 11:17:50', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (134, '退款订单状态', 'pay_refund_status', 0, '退款订单状态', '1', '2021-12-10 16:42:50', '1', '2023-07-19 10:13:17', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (139, '流程实例的状态', 'bpm_process_instance_status', 0, '流程实例的状态', '1', '2022-01-07 23:46:42', '1', '2022-01-07 23:46:42', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (140, '流程实例的结果', 'bpm_task_status', 0, '流程实例的结果', '1', '2022-01-07 23:48:10', '1', '2024-03-08 22:42:03', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (141, '流程的表单类型', 'bpm_model_form_type', 0, '流程的表单类型', '103', '2022-01-11 23:50:45', '103', '2022-01-11 23:50:45', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (142, '任务分配规则的类型', 'bpm_task_candidate_strategy', 0, 'BPM 任务的候选人的策略', '103', '2022-01-12 23:21:04', '103', '2024-03-06 02:53:59', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (144, '代码生成的场景枚举', 'infra_codegen_scene', 0, '代码生成的场景枚举', '1', '2022-02-02 13:14:45', '1', '2022-03-10 16:33:46', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (145, '角色类型', 'system_role_type', 0, '角色类型', '1', '2022-02-16 13:01:46', '1', '2022-02-16 13:01:46', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (146, '文件存储器', 'infra_file_storage', 0, '文件存储器', '1', '2022-03-15 00:24:38', '1', '2022-03-15 00:24:38', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (147, 'OAuth 2.0 授权类型', 'system_oauth2_grant_type', 0, 'OAuth 2.0 授权类型（模式）', '1', '2022-05-12 00:20:52', '1', '2022-05-11 16:25:49', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (149, '商品 SPU 状态', 'product_spu_status', 0, '商品 SPU 状态', '1', '2022-10-24 21:19:04', '1', '2022-10-24 21:19:08', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (150, '优惠类型', 'promotion_discount_type', 0, '优惠类型', '1', '2022-11-01 12:46:06', '1', '2022-11-01 12:46:06', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (151, '优惠劵模板的有限期类型', 'promotion_coupon_template_validity_type', 0, '优惠劵模板的有限期类型', '1', '2022-11-02 00:06:20', '1', '2022-11-04 00:08:26', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (152, '营销的商品范围', 'promotion_product_scope', 0, '营销的商品范围', '1', '2022-11-02 00:28:01', '1', '2022-11-02 00:28:01', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (153, '优惠劵的状态', 'promotion_coupon_status', 0, '优惠劵的状态', '1', '2022-11-04 00:14:49', '1', '2022-11-04 00:14:49', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (154, '优惠劵的领取方式', 'promotion_coupon_take_type', 0, '优惠劵的领取方式', '1', '2022-11-04 19:12:27', '1', '2022-11-04 19:12:27', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (155, '促销活动的状态', 'promotion_activity_status', 0, '促销活动的状态', '1', '2022-11-04 22:54:23', '1', '2022-11-04 22:54:23', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (156, '营销的条件类型', 'promotion_condition_type', 0, '营销的条件类型', '1', '2022-11-04 22:59:23', '1', '2022-11-04 22:59:23', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (157, '交易售后状态', 'trade_after_sale_status', 0, '交易售后状态', '1', '2022-11-19 20:52:56', '1', '2022-11-19 20:52:56', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (158, '交易售后的类型', 'trade_after_sale_type', 0, '交易售后的类型', '1', '2022-11-19 21:04:09', '1', '2022-11-19 21:04:09', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (159, '交易售后的方式', 'trade_after_sale_way', 0, '交易售后的方式', '1', '2022-11-19 21:39:04', '1', '2022-11-19 21:39:04', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (160, '终端', 'terminal', 0, '终端', '1', '2022-12-10 10:50:50', '1', '2022-12-10 10:53:11', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (161, '交易订单的类型', 'trade_order_type', 0, '交易订单的类型', '1', '2022-12-10 16:33:54', '1', '2022-12-10 16:33:54', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (162, '交易订单的状态', 'trade_order_status', 0, '交易订单的状态', '1', '2022-12-10 16:48:44', '1', '2022-12-10 16:48:44', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (163, '交易订单项的售后状态', 'trade_order_item_after_sale_status', 0, '交易订单项的售后状态', '1', '2022-12-10 20:58:08', '1', '2022-12-10 20:58:08', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (164, '公众号自动回复的请求关键字匹配模式', 'mp_auto_reply_request_match', 0, '公众号自动回复的请求关键字匹配模式', '1', '2023-01-16 23:29:56', '1', '2023-01-16 23:29:56', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (165, '公众号的消息类型', 'mp_message_type', 0, '公众号的消息类型', '1', '2023-01-17 22:17:09', '1', '2023-01-17 22:17:09', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (166, '邮件发送状态', 'system_mail_send_status', 0, '邮件发送状态', '1', '2023-01-26 09:53:13', '1', '2023-01-26 09:53:13', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (167, '站内信模版的类型', 'system_notify_template_type', 0, '站内信模版的类型', '1', '2023-01-28 10:35:10', '1', '2023-01-28 10:35:10', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (168, '代码生成的前端类型', 'infra_codegen_front_type', 0, '', '1', '2023-04-12 23:57:52', '1', '2023-04-12 23:57:52', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (170, '快递计费方式', 'trade_delivery_express_charge_mode', 0, '用于商城交易模块配送管理', '1', '2023-05-21 22:45:03', '1', '2023-05-21 22:45:03', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (171, '积分业务类型', 'member_point_biz_type', 0, '', '1', '2023-06-10 12:15:00', '1', '2023-06-28 13:48:20', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (173, '支付通知类型', 'pay_notify_type', 0, NULL, '1', '2023-07-20 12:23:03', '1', '2023-07-20 12:23:03', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (174, '会员经验业务类型', 'member_experience_biz_type', 0, NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (175, '交易配送类型', 'trade_delivery_type', 0, '', '1', '2023-08-23 00:03:14', '1', '2023-08-23 00:03:14', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (176, '分佣模式', 'brokerage_enabled_condition', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (177, '分销关系绑定模式', 'brokerage_bind_mode', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (178, '佣金提现类型', 'brokerage_withdraw_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (179, '佣金记录业务类型', 'brokerage_record_biz_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (180, '佣金记录状态', 'brokerage_record_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (181, '佣金提现状态', 'brokerage_withdraw_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (182, '佣金提现银行', 'brokerage_bank_name', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (183, '砍价记录的状态', 'promotion_bargain_record_status', 0, '', '1', '2023-10-05 10:41:08', '1', '2023-10-05 10:41:08', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (184, '拼团记录的状态', 'promotion_combination_record_status', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-10-08 07:24:25', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (185, '回款-回款方式', 'crm_receivable_return_type', 0, '回款-回款方式', '1', '2023-10-18 21:54:10', '1', '2023-10-18 21:54:10', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (186, 'CRM 客户行业', 'crm_customer_industry', 0, 'CRM 客户所属行业', '1', '2023-10-28 22:57:07', '1', '2024-02-18 23:30:22', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (187, '客户等级', 'crm_customer_level', 0, 'CRM 客户等级', '1', '2023-10-28 22:59:12', '1', '2023-10-28 15:11:16', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (188, '客户来源', 'crm_customer_source', 0, 'CRM 客户来源', '1', '2023-10-28 23:00:34', '1', '2023-10-28 15:11:16', b'0', NULL);\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (600, 'Banner 位置', 'promotion_banner_position', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-11-04 13:04:02', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (601, '社交类型', 'system_social_type', 0, '', '1', '2023-11-04 13:03:54', '1', '2023-11-04 13:03:54', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (604, '产品状态', 'crm_product_status', 0, '', '1', '2023-10-30 21:47:59', '1', '2023-10-30 21:48:45', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (605, 'CRM 数据权限的级别', 'crm_permission_level', 0, '', '1', '2023-11-30 09:51:59', '1', '2023-11-30 09:51:59', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (606, 'CRM 审批状态', 'crm_audit_status', 0, '', '1', '2023-11-30 18:56:23', '1', '2023-11-30 18:56:23', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (607, 'CRM 产品单位', 'crm_product_unit', 0, '', '1', '2023-12-05 23:01:51', '1', '2023-12-05 23:01:51', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (608, 'CRM 跟进方式', 'crm_follow_up_type', 0, '', '1', '2024-01-15 20:48:05', '1', '2024-01-15 20:48:05', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (610, '转账订单状态', 'pay_transfer_status', 0, '', '1', '2023-10-28 16:18:32', '1', '2023-10-28 16:18:32', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (611, 'ERP 库存明细的业务类型', 'erp_stock_record_biz_type', 0, 'ERP 库存明细的业务类型', '1', '2024-02-05 18:07:02', '1', '2024-02-05 18:07:02', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (612, 'ERP 审批状态', 'erp_audit_status', 0, '', '1', '2024-02-06 00:00:07', '1', '2024-02-06 00:00:07', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (613, 'BPM 监听器类型', 'bpm_process_listener_type', 0, '', '1', '2024-03-23 12:52:24', '1', '2024-03-09 15:54:28', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (615, 'BPM 监听器值类型', 'bpm_process_listener_value_type', 0, '', '1', '2024-03-23 13:00:31', '1', '2024-03-23 13:00:31', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (616, '时间间隔', 'date_interval', 0, '', '1', '2024-03-29 22:50:09', '1', '2024-03-29 22:50:09', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (619, 'CRM 商机结束状态类型', 'crm_business_end_status_type', 0, '', '1', '2024-04-13 23:23:00', '1', '2024-04-13 23:23:00', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (620, 'AI 模型平台', 'ai_platform', 0, '', '1', '2024-05-09 22:27:38', '1', '2024-05-09 22:27:38', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (621, 'AI 绘画状态', 'ai_image_status', 0, '', '1', '2024-06-26 20:51:23', '1', '2024-06-26 20:51:23', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (622, 'AI 音乐状态', 'ai_music_status', 0, '', '1', '2024-06-27 22:45:07', '1', '2024-06-28 00:56:27', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (623, 'AI 音乐生成模式', 'ai_generate_mode', 0, '', '1', '2024-06-27 22:46:21', '1', '2024-06-28 01:22:29', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (624, '写作语气', 'ai_write_tone', 0, '', '1', '2024-07-07 15:19:02', '1', '2024-07-07 15:19:02', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (625, '写作语言', 'ai_write_language', 0, '', '1', '2024-07-07 15:18:52', '1', '2024-07-07 15:18:52', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (626, '写作长度', 'ai_write_length', 0, '', '1', '2024-07-07 15:18:41', '1', '2024-07-07 15:18:41', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (627, '写作格式', 'ai_write_format', 0, '', '1', '2024-07-07 15:14:34', '1', '2024-07-07 15:14:34', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1001, 'IoT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2025-03-17 09:25:08', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1002, 'IoT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2025-03-17 09:25:10', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1004, 'IoT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2025-03-17 09:25:14', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1006, 'IoT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-03-17 09:25:19', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1007, 'IoT 物模型功能类型', 'iot_thing_model_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2025-03-17 09:25:24', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1011, 'IoT 物模型单位', 'iot_thing_model_unit', 0, '', '1', '2024-12-25 17:36:46', '1', '2025-03-17 09:25:35', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1013, 'IoT 数据流转目的的类型枚举', 'iot_data_sink_type_enum', 0, '', '1', '2025-03-09 12:39:36', '1', '2025-06-24 12:45:24', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1014, 'IoT 场景流转的触发类型枚举', 'iot_rule_scene_trigger_type_enum', 0, '', '1', '2025-03-20 14:59:44', '1', '2025-03-20 14:59:44', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1015, 'IoT 设备消息类型枚举', 'iot_device_message_type_enum', 0, '', '1', '2025-03-20 15:01:15', '1', '2025-03-20 15:01:15', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1016, 'IoT 规则场景的触发类型枚举', 'iot_rule_scene_action_type_enum', 0, '', '1', '2025-03-28 15:26:54', '1', '2025-03-28 15:29:13', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2001, 'IoT 告警级别', 'iot_alert_level', 0, '', '1', '2025-06-27 20:30:57', '1', '2025-06-27 20:30:57', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2002, 'IoT 告警', 'iot_alert_receive_type', 0, '', '1', '2025-06-27 22:49:19', '1', '2025-06-27 22:49:19', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2003, 'IoT 固件设备范围', 'iot_ota_task_device_scope', 0, '', '1', '2025-07-02 09:42:49', '1', '2025-07-02 09:42:49', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2004, 'IoT 固件升级任务状态', 'iot_ota_task_status', 0, '', '1', '2025-07-02 09:43:43', '1', '2025-07-02 09:43:43', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2005, 'IoT 固件升级记录状态', 'iot_ota_task_record_status', 0, '', '1', '2025-07-02 09:45:02', '1', '2025-07-02 09:45:02', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2007, 'AI MCP 客户端名字', 'ai_mcp_client_name', 0, '', '1', '2025-08-28 13:57:40', '1', '2025-08-28 13:57:40', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2008, 'IoT 协议类型', 'iot_protocol_type', 0, 'IoT 设备接入协议类型', '1', '2026-02-04 00:31:33', '1', '2026-02-04 00:31:33', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2009, 'IoT 序列化类型', 'iot_serialize_type', 0, 'IoT 设备消息序列化类型', '1', '2026-02-04 00:33:16', '1', '2026-02-04 00:33:16', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2010, 'IoT Modbus 工作模式', 'iot_modbus_mode', 0, 'Modbus 设备数据采集模式', '1', '2025-06-12 22:55:46', '1', '2025-06-12 22:55:46', b'0', '1970-01-01 00:00:00');\nINSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2011, 'IoT Modbus 帧格式', 'iot_modbus_frame_format', 0, 'Modbus 数据帧协议格式', '1', '2025-06-12 22:55:46', '1', '2025-06-12 22:55:46', b'0', '1970-01-01 00:00:00');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_login_log\n-- ----------------------------\nDROP TABLE IF EXISTS `system_login_log`;\nCREATE TABLE `system_login_log`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '访问ID',\n  `log_type` bigint NOT NULL COMMENT '日志类型',\n  `trace_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '链路追踪编号',\n  `user_id` bigint NOT NULL DEFAULT 0 COMMENT '用户编号',\n  `user_type` tinyint NOT NULL DEFAULT 0 COMMENT '用户类型',\n  `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '用户账号',\n  `result` tinyint NOT NULL COMMENT '登陆结果',\n  `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户 IP',\n  `user_agent` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '浏览器 UA',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 4449 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';\n\n-- ----------------------------\n-- Records of system_login_log\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_mail_account\n-- ----------------------------\nDROP TABLE IF EXISTS `system_mail_account`;\nCREATE TABLE `system_mail_account`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮箱',\n  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名',\n  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码',\n  `host` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SMTP 服务器域名',\n  `port` int NOT NULL COMMENT 'SMTP 服务器端口',\n  `ssl_enable` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否开启 SSL',\n  `starttls_enable` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否开启 STARTTLS',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮箱账号表';\n\n-- ----------------------------\n-- Records of system_mail_account\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, b'0', b'0', '1', '2023-01-25 17:39:52', '1', '2025-04-04 16:34:40', b'0');\nINSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, b'1', b'0', '1', '2023-01-26 01:26:03', '1', '2025-12-20 18:09:32', b'0');\nINSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, b'0', b'0', '1', '2023-01-27 15:06:38', '1', '2023-01-27 07:08:36', b'1');\nINSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '7685413x@qq.com', '2', '3', '4', 5, b'1', b'0', '1', '2023-04-12 23:05:06', '1', '2023-04-12 15:05:11', b'1');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_mail_log\n-- ----------------------------\nDROP TABLE IF EXISTS `system_mail_log`;\nCREATE TABLE `system_mail_log`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `user_id` bigint NULL DEFAULT NULL COMMENT '用户编号',\n  `user_type` tinyint NULL DEFAULT NULL COMMENT '用户类型',\n  `to_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '接收邮箱地址',\n  `cc_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '抄送邮箱地址',\n  `bcc_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '密送邮箱地址',\n  `account_id` bigint NOT NULL COMMENT '邮箱账号编号',\n  `from_mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发送邮箱地址',\n  `template_id` bigint NOT NULL COMMENT '模板编号',\n  `template_code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板编码',\n  `template_nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '模版发送人名称',\n  `template_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮件标题',\n  `template_content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮件内容',\n  `template_params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮件参数',\n  `send_status` tinyint NOT NULL DEFAULT 0 COMMENT '发送状态',\n  `send_time` datetime NULL DEFAULT NULL COMMENT '发送时间',\n  `send_message_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送返回的消息 ID',\n  `send_exception` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送异常',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 368 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表';\n\n-- ----------------------------\n-- Records of system_mail_log\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_mail_template\n-- ----------------------------\nDROP TABLE IF EXISTS `system_mail_template`;\nCREATE TABLE `system_mail_template`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `name` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板名称',\n  `code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板编码',\n  `account_id` bigint NOT NULL COMMENT '发送的邮箱账号编号',\n  `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送人名称',\n  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板标题',\n  `content` varchar(10240) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板内容',\n  `params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '参数数组',\n  `status` tinyint NOT NULL COMMENT '开启状态',\n  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件模版表';\n\n-- ----------------------------\n-- Records of system_mail_template\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (13, '后台用户短信登录', 'admin-sms-login', 1, '奥特曼', '你猜我猜', '<p>您的验证码是{code}，名字是{name}</p>', '[\\\"code\\\",\\\"name\\\"]', 0, '3', '1', '2021-10-11 08:10:00', '1', '2023-12-02 19:51:14', b'0');\nINSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (14, '测试模版', 'test_01', 2, '芋艿', '一个标题', '<p>你是 {key01} 吗？</p><p><br></p><p>是的话，赶紧 {key02} 一下！</p>', '[\\\"key01\\\",\\\"key02\\\"]', 0, NULL, '1', '2023-01-26 01:27:40', '1', '2025-07-26 21:48:45', b'0');\nINSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, '3', '2', 2, '7', '4', '<p>45</p>', '[]', 1, '80', '1', '2023-01-27 15:50:35', '1', '2025-07-26 21:47:49', b'1');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `system_menu`;\nCREATE TABLE `system_menu`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单ID',\n  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '菜单名称',\n  `permission` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '权限标识',\n  `type` tinyint NOT NULL COMMENT '菜单类型',\n  `sort` int NOT NULL DEFAULT 0 COMMENT '显示顺序',\n  `parent_id` bigint NOT NULL DEFAULT 0 COMMENT '父菜单ID',\n  `path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '路由地址',\n  `icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '#' COMMENT '菜单图标',\n  `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '组件路径',\n  `component_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '组件名',\n  `status` tinyint NOT NULL DEFAULT 0 COMMENT '菜单状态',\n  `visible` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否可见',\n  `keep_alive` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否缓存',\n  `always_show` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否总是显示',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 5049 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';\n\n-- ----------------------------\n-- Records of system_menu\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-03-01 08:28:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, 'OA 示例', '', 1, 40, 1185, 'oa', 'fa:road', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-09-20 16:26:19', '1', '2024-02-29 12:38:13', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2026-01-01 18:43:01', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (101, '角色管理', '', 2, 2, 1, 'role', 'ep:user', 'system/role/index', 'SystemRole', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2026-01-05 19:30:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (102, '菜单管理', '', 2, 3, 1, 'menu', 'ep:menu', 'system/menu/index', 'SystemMenu', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:03:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (103, '部门管理', '', 2, 4, 1, 'dept', 'fa:address-card', 'system/dept/index', 'SystemDept', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (104, '岗位管理', '', 2, 5, 1, 'post', 'fa:address-book-o', 'system/post/index', 'SystemPost', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:39', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (105, '字典管理', '', 2, 6, 1, 'dict', 'ep:collection', 'system/dict/index', 'SystemDictType', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:07:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (106, '配置管理', '', 2, 8, 2, 'config', 'fa:connectdevelop', 'infra/config/index', 'InfraConfig', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:02:45', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (107, '通知公告', '', 2, 4, 2739, 'notice', 'ep:takeaway-box', 'system/notice/index', 'SystemNotice', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-22 23:56:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (108, '审计日志', '', 1, 9, 1, 'log', 'ep:document-copy', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:08:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (109, '令牌管理', '', 2, 2, 1261, 'token', 'fa:key', 'system/oauth2/token/index', 'SystemTokenClient', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:13:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (110, '定时任务', '', 2, 7, 2, 'job', 'fa-solid:tasks', 'infra/job/index', 'InfraJob', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:57:36', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (111, 'MySQL 监控', '', 2, 1, 2740, 'druid', 'fa-solid:box', 'infra/druid/index', 'InfraDruid', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:05:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (112, 'Java 监控', '', 2, 3, 2740, 'admin-server', 'ep:coffee-cup', 'infra/server/index', 'InfraAdminServer', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (113, 'Redis 监控', '', 2, 2, 2740, 'redis', 'fa:reddit-square', 'infra/redis/index', 'InfraRedis', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (114, '表单构建', 'infra:build:list', 2, 2, 2, 'build', 'fa:wpforms', 'infra/build/index', 'InfraBuild', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (115, '代码生成', 'infra:codegen:query', 2, 1, 2, 'codegen', 'ep:document-copy', 'infra/codegen/index', 'InfraCodegen', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (116, 'API 接口', 'infra:swagger:list', 2, 3, 2, 'swagger', 'fa:fighter-jet', 'infra/swagger/index', 'InfraSwagger', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:01:24', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (500, '操作日志', '', 2, 1, 108, 'operate-log', 'ep:position', 'system/operatelog/index', 'SystemOperateLog', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:09:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (501, '登录日志', '', 2, 2, 108, 'login-log', 'ep:promotion', 'system/loginlog/index', 'SystemLoginLog', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:10:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1001, '用户查询', 'system:user:query', 3, 1, 100, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1002, '用户新增', 'system:user:create', 3, 2, 100, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1003, '用户修改', 'system:user:update', 3, 3, 100, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1004, '用户删除', 'system:user:delete', 3, 4, 100, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1005, '用户导出', 'system:user:export', 3, 5, 100, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1006, '用户导入', 'system:user:import', 3, 6, 100, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1007, '重置密码', 'system:user:update-password', 3, 7, 100, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1008, '角色查询', 'system:role:query', 3, 1, 101, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1009, '角色新增', 'system:role:create', 3, 2, 101, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1010, '角色修改', 'system:role:update', 3, 3, 101, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1011, '角色删除', 'system:role:delete', 3, 4, 101, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1012, '角色导出', 'system:role:export', 3, 5, 101, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1013, '菜单查询', 'system:menu:query', 3, 1, 102, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1014, '菜单新增', 'system:menu:create', 3, 2, 102, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1015, '菜单修改', 'system:menu:update', 3, 3, 102, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1016, '菜单删除', 'system:menu:delete', 3, 4, 102, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1017, '部门查询', 'system:dept:query', 3, 1, 103, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1018, '部门新增', 'system:dept:create', 3, 2, 103, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1019, '部门修改', 'system:dept:update', 3, 3, 103, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1020, '部门删除', 'system:dept:delete', 3, 4, 103, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1021, '岗位查询', 'system:post:query', 3, 1, 104, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1022, '岗位新增', 'system:post:create', 3, 2, 104, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1023, '岗位修改', 'system:post:update', 3, 3, 104, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1024, '岗位删除', 'system:post:delete', 3, 4, 104, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1025, '岗位导出', 'system:post:export', 3, 5, 104, '', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1026, '字典查询', 'system:dict:query', 3, 1, 105, '#', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1027, '字典新增', 'system:dict:create', 3, 2, 105, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1028, '字典修改', 'system:dict:update', 3, 3, 105, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1029, '字典删除', 'system:dict:delete', 3, 4, 105, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1030, '字典导出', 'system:dict:export', 3, 5, 105, '#', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1031, '配置查询', 'infra:config:query', 3, 1, 106, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1032, '配置新增', 'infra:config:create', 3, 2, 106, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1033, '配置修改', 'infra:config:update', 3, 3, 106, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1034, '配置删除', 'infra:config:delete', 3, 4, 106, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1035, '配置导出', 'infra:config:export', 3, 5, 106, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1036, '公告查询', 'system:notice:query', 3, 1, 107, '#', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1037, '公告新增', 'system:notice:create', 3, 2, 107, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1038, '公告修改', 'system:notice:update', 3, 3, 107, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1039, '公告删除', 'system:notice:delete', 3, 4, 107, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1040, '操作查询', 'system:operate-log:query', 3, 1, 500, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1042, '日志导出', 'system:operate-log:export', 3, 2, 500, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1043, '登录查询', 'system:login-log:query', 3, 1, 501, '#', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1045, '日志导出', 'system:login-log:export', 3, 3, 501, '#', '#', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1046, '令牌列表', 'system:oauth2-token:page', 3, 1, 109, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1048, '令牌删除', 'system:oauth2-token:delete', 3, 2, 109, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1050, '任务新增', 'infra:job:create', 3, 2, 110, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1051, '任务修改', 'infra:job:update', 3, 3, 110, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1052, '任务删除', 'infra:job:delete', 3, 4, 110, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1053, '状态修改', 'infra:job:update', 3, 5, 110, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1054, '任务导出', 'infra:job:export', 3, 7, 110, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1056, '生成修改', 'infra:codegen:update', 3, 2, 115, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1057, '生成删除', 'infra:codegen:delete', 3, 3, 115, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1058, '导入代码', 'infra:codegen:create', 3, 2, 115, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1059, '预览代码', 'infra:codegen:preview', 3, 4, 115, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1060, '生成代码', 'infra:codegen:download', 3, 5, 115, '', '', '', NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1063, '设置角色菜单权限', 'system:permission:assign-role-menu', 3, 6, 101, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-01-06 17:53:44', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1064, '设置角色数据权限', 'system:permission:assign-role-data-scope', 3, 7, 101, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-01-06 17:56:31', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1065, '设置用户角色', 'system:permission:assign-user-role', 3, 8, 101, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-01-07 10:23:28', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1066, '获得 Redis 监控信息', 'infra:redis:get-monitor-info', 3, 1, 113, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-01-26 01:02:31', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1067, '获得 Redis Key 列表', 'infra:redis:get-key-list', 3, 2, 113, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-01-26 01:02:52', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1070, '代码生成案例', '', 1, 1, 2, 'demo', 'ep:aim', 'infra/testDemo/index', NULL, 0, b'1', b'1', b'1', '', '2021-02-06 12:42:49', '1', '2023-11-15 23:45:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1075, '任务触发', 'infra:job:trigger', 3, 8, 110, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-02-07 13:03:10', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1077, '链路追踪', '', 2, 4, 2740, 'skywalking', 'fa:eye', 'infra/skywalking/index', 'InfraSkyWalking', 0, b'1', b'1', b'1', '', '2021-02-08 20:41:31', '1', '2024-04-23 00:07:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1078, '访问日志', '', 2, 1, 1083, 'api-access-log', 'ep:place', 'infra/apiAccessLog/index', 'InfraApiAccessLog', 0, b'1', b'1', b'1', '', '2021-02-26 01:32:59', '1', '2024-02-29 08:54:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1082, '日志导出', 'infra:api-access-log:export', 3, 2, 1078, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-02-26 01:32:59', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1083, 'API 日志', '', 2, 4, 2, 'log', 'fa:tasks', NULL, NULL, 0, b'1', b'1', b'1', '', '2021-02-26 02:18:24', '1', '2024-04-22 23:58:36', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1084, '错误日志', 'infra:api-error-log:query', 2, 2, 1083, 'api-error-log', 'ep:warning-filled', 'infra/apiErrorLog/index', 'InfraApiErrorLog', 0, b'1', b'1', b'1', '', '2021-02-26 07:53:20', '1', '2024-02-29 08:55:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1085, '日志处理', 'infra:api-error-log:update-status', 3, 2, 1084, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1086, '日志导出', 'infra:api-error-log:export', 3, 3, 1084, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1087, '任务查询', 'infra:job:query', 3, 1, 110, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2021-03-10 01:26:19', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1088, '日志查询', 'infra:api-access-log:query', 3, 1, 1078, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2021-03-10 01:28:04', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1089, '日志查询', 'infra:api-error-log:query', 3, 1, 1084, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2021-03-10 01:29:09', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1090, '文件列表', '', 2, 5, 1243, 'file', 'ep:upload-filled', 'infra/file/index', 'InfraFile', 0, b'1', b'1', b'1', '', '2021-03-12 20:16:20', '1', '2024-02-29 08:53:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1092, '文件删除', 'infra:file:delete', 3, 4, 1090, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1093, '短信管理', '', 1, 1, 2739, 'sms', 'ep:message', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-04-05 01:10:16', '1', '2024-04-22 23:56:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1094, '短信渠道', '', 2, 0, 1093, 'sms-channel', 'fa:stack-exchange', 'system/sms/channel/index', 'SystemSmsChannel', 0, b'1', b'1', b'1', '', '2021-04-01 11:07:15', '1', '2024-02-29 01:15:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1095, '短信渠道查询', 'system:sms-channel:query', 3, 1, 1094, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1096, '短信渠道创建', 'system:sms-channel:create', 3, 2, 1094, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1097, '短信渠道更新', 'system:sms-channel:update', 3, 3, 1094, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1098, '短信渠道删除', 'system:sms-channel:delete', 3, 4, 1094, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1100, '短信模板', '', 2, 1, 1093, 'sms-template', 'ep:connection', 'system/sms/template/index', 'SystemSmsTemplate', 0, b'1', b'1', b'1', '', '2021-04-01 17:35:17', '1', '2024-02-29 01:16:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1101, '短信模板查询', 'system:sms-template:query', 3, 1, 1100, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1102, '短信模板创建', 'system:sms-template:create', 3, 2, 1100, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1103, '短信模板更新', 'system:sms-template:update', 3, 3, 1100, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1104, '短信模板删除', 'system:sms-template:delete', 3, 4, 1100, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1105, '短信模板导出', 'system:sms-template:export', 3, 5, 1100, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1106, '发送测试短信', 'system:sms-template:send-sms', 3, 6, 1100, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2021-04-11 00:26:40', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1107, '短信日志', '', 2, 2, 1093, 'sms-log', 'fa:edit', 'system/sms/log/index', 'SystemSmsLog', 0, b'1', b'1', b'1', '', '2021-04-11 08:37:05', '1', '2024-02-29 08:49:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1108, '短信日志查询', 'system:sms-log:query', 3, 1, 1107, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1109, '短信日志导出', 'system:sms-log:export', 3, 5, 1107, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1117, '支付管理', '', 1, 30, 0, '/pay', 'ep:money', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-25 16:43:41', '1', '2024-02-29 08:58:38', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1118, '请假查询', '', 2, 0, 5, 'leave', 'fa:leanpub', 'bpm/oa/leave/index', 'BpmOALeave', 0, b'1', b'1', b'1', '', '2021-09-20 08:51:03', '1', '2024-02-29 12:38:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1119, '请假申请查询', 'bpm:oa-leave:query', 3, 1, 1118, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1120, '请假申请创建', 'bpm:oa-leave:create', 3, 2, 1118, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1126, '应用信息', '', 2, 1, 1117, 'app', 'fa:apple', 'pay/app/index', 'PayApp', 0, b'1', b'1', b'1', '', '2021-11-10 01:13:30', '1', '2024-02-29 08:59:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1127, '支付应用信息查询', 'pay:app:query', 3, 1, 1126, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1128, '支付应用信息创建', 'pay:app:create', 3, 2, 1126, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1129, '支付应用信息更新', 'pay:app:update', 3, 3, 1126, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1130, '支付应用信息删除', 'pay:app:delete', 3, 4, 1126, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1132, '秘钥解析', 'pay:channel:parsing', 3, 6, 1129, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1133, '支付商户信息查询', 'pay:merchant:query', 3, 1, 1132, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1134, '支付商户信息创建', 'pay:merchant:create', 3, 2, 1132, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1135, '支付商户信息更新', 'pay:merchant:update', 3, 3, 1132, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1136, '支付商户信息删除', 'pay:merchant:delete', 3, 4, 1132, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1137, '支付商户信息导出', 'pay:merchant:export', 3, 5, 1132, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1138, '租户列表', '', 2, 0, 1224, 'list', 'ep:house', 'system/tenant/index', 'SystemTenant', 0, b'1', b'1', b'1', '', '2021-12-14 12:31:43', '1', '2024-02-29 01:01:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1139, '租户查询', 'system:tenant:query', 3, 1, 1138, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1140, '租户创建', 'system:tenant:create', 3, 2, 1138, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1141, '租户更新', 'system:tenant:update', 3, 3, 1138, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1142, '租户删除', 'system:tenant:delete', 3, 4, 1138, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1143, '租户导出', 'system:tenant:export', 3, 5, 1138, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1150, '秘钥解析', '', 3, 6, 1129, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1161, '退款订单', '', 2, 3, 1117, 'refund', 'fa:registered', 'pay/refund/index', 'PayRefund', 0, b'1', b'1', b'1', '', '2021-12-25 08:29:07', '1', '2024-02-29 08:59:20', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1162, '退款订单查询', 'pay:refund:query', 3, 1, 1161, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1166, '退款订单导出', 'pay:refund:export', 3, 5, 1161, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1173, '支付订单', '', 2, 2, 1117, 'order', 'fa:cc-paypal', 'pay/order/index', 'PayOrder', 0, b'1', b'1', b'1', '', '2021-12-25 08:49:43', '1', '2024-02-29 08:59:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1174, '支付订单查询', 'pay:order:query', 3, 1, 1173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1178, '支付订单导出', 'pay:order:export', 3, 5, 1173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1185, '工作流程', '', 1, 50, 0, '/bpm', 'fa:medium', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-30 20:26:36', '1', '2024-02-29 12:43:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1186, '流程管理', '', 1, 10, 1185, 'manager', 'fa:dedent', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-30 20:28:30', '1', '2024-02-29 12:36:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1187, '流程表单', '', 2, 2, 1186, 'form', 'fa:hdd-o', 'bpm/form/index', 'BpmForm', 0, b'1', b'1', b'1', '', '2021-12-30 12:38:22', '1', '2024-03-19 12:25:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1188, '表单查询', 'bpm:form:query', 3, 1, 1187, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1189, '表单创建', 'bpm:form:create', 3, 2, 1187, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1190, '表单更新', 'bpm:form:update', 3, 3, 1187, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1191, '表单删除', 'bpm:form:delete', 3, 4, 1187, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1192, '表单导出', 'bpm:form:export', 3, 5, 1187, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1193, '流程模型', '', 2, 1, 1186, 'model', 'fa-solid:project-diagram', 'bpm/model/index', 'BpmModel', 0, b'1', b'1', b'1', '1', '2021-12-31 23:24:58', '1', '2024-03-19 12:25:19', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1194, '模型查询', 'bpm:model:query', 3, 1, 1193, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-03 19:01:10', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1195, '模型创建', 'bpm:model:create', 3, 2, 1193, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-03 19:01:24', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1197, '模型更新', 'bpm:model:update', 3, 4, 1193, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-03 19:02:28', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1198, '模型删除', 'bpm:model:delete', 3, 5, 1193, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-03 19:02:43', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1199, '模型发布', 'bpm:model:deploy', 3, 6, 1193, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-03 19:03:24', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1200, '审批中心', '', 2, 20, 1185, 'task', 'fa:tasks', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-01-07 23:51:48', '1', '2024-03-21 00:33:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1201, '我的流程', '', 2, 1, 1200, 'my', 'fa-solid:book', 'bpm/processInstance/index', 'BpmProcessInstanceMy', 0, b'1', b'1', b'1', '', '2022-01-07 15:53:44', '1', '2024-03-21 23:52:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1202, '流程实例的查询', 'bpm:process-instance:query', 3, 1, 1201, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-01-07 15:53:44', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1207, '待办任务', '', 2, 10, 1200, 'todo', 'fa:slack', 'bpm/task/todo/index', 'BpmTodoTask', 0, b'1', b'1', b'1', '1', '2022-01-08 10:33:37', '1', '2024-02-29 12:37:39', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1208, '已办任务', '', 2, 20, 1200, 'done', 'fa:delicious', 'bpm/task/done/index', 'BpmDoneTask', 0, b'1', b'1', b'1', '1', '2022-01-08 10:34:13', '1', '2024-02-29 12:37:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1209, '用户分组', '', 2, 4, 1186, 'user-group', 'fa:user-secret', 'bpm/group/index', 'BpmUserGroup', 0, b'1', b'1', b'1', '', '2022-01-14 02:14:20', '1', '2024-03-21 23:55:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1210, '用户组查询', 'bpm:user-group:query', 3, 1, 1209, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1211, '用户组创建', 'bpm:user-group:create', 3, 2, 1209, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1212, '用户组更新', 'bpm:user-group:update', 3, 3, 1209, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1213, '用户组删除', 'bpm:user-group:delete', 3, 4, 1209, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1215, '流程定义查询', 'bpm:process-definition:query', 3, 10, 1193, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-23 00:21:43', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1216, '流程任务分配规则查询', 'bpm:task-assign-rule:query', 3, 20, 1193, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-23 00:26:53', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1217, '流程任务分配规则创建', 'bpm:task-assign-rule:create', 3, 21, 1193, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-23 00:28:15', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1218, '流程任务分配规则更新', 'bpm:task-assign-rule:update', 3, 22, 1193, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-23 00:28:41', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1219, '流程实例的创建', 'bpm:process-instance:create', 3, 2, 1201, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-23 00:36:15', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1220, '流程实例的取消', 'bpm:process-instance:cancel', 3, 3, 1201, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-23 00:36:33', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1221, '流程任务的查询', 'bpm:task:query', 3, 1, 1207, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-23 00:38:52', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1222, '流程任务的更新', 'bpm:task:update', 3, 2, 1207, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-01-23 00:39:24', '1', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1224, '租户管理', '', 2, 0, 1, 'tenant', 'fa-solid:house-user', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-02-20 01:41:13', '1', '2024-02-29 00:59:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1225, '租户套餐', '', 2, 0, 1224, 'package', 'fa:bars', 'system/tenantPackage/index', 'SystemTenantPackage', 0, b'1', b'1', b'1', '', '2022-02-19 17:44:06', '1', '2024-02-29 01:01:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1226, '租户套餐查询', 'system:tenant-package:query', 3, 1, 1225, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1227, '租户套餐创建', 'system:tenant-package:create', 3, 2, 1225, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1228, '租户套餐更新', 'system:tenant-package:update', 3, 3, 1225, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1229, '租户套餐删除', 'system:tenant-package:delete', 3, 4, 1225, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1237, '文件配置', '', 2, 0, 1243, 'file-config', 'fa-solid:file-signature', 'infra/fileConfig/index', 'InfraFileConfig', 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '1', '2024-02-29 08:52:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1238, '文件配置查询', 'infra:file-config:query', 3, 1, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1239, '文件配置创建', 'infra:file-config:create', 3, 2, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1240, '文件配置更新', 'infra:file-config:update', 3, 3, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, b'1', b'1', b'1', '1', '2022-03-16 23:47:40', '1', '2024-04-23 00:02:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-04-23 01:03:15', '1', '2025-04-29 17:45:38', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '1', '2024-02-29 08:51:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1258, '数据源配置更新', 'infra:data-source-config:update', 3, 3, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1259, '数据源配置删除', 'infra:data-source-config:delete', 3, 4, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1260, '数据源配置导出', 'infra:data-source-config:export', 3, 5, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1261, 'OAuth 2.0', '', 2, 10, 1, 'oauth2', 'fa:dashcube', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-05-09 23:38:17', '1', '2024-02-29 01:12:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1263, '应用管理', '', 2, 0, 1261, 'oauth2/application', 'fa:hdd-o', 'system/oauth2/client/index', 'SystemOAuth2Client', 0, b'1', b'1', b'1', '', '2022-05-10 16:26:33', '1', '2024-02-29 01:13:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1264, '客户端查询', 'system:oauth2-client:query', 3, 1, 1263, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1265, '客户端创建', 'system:oauth2-client:create', 3, 2, 1263, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1266, '客户端更新', 'system:oauth2-client:update', 3, 3, 1263, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1267, '客户端删除', 'system:oauth2-client:delete', 3, 4, 1263, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1281, '报表管理', '', 2, 40, 0, '/report', 'ep:pie-chart', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2024-02-29 12:33:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1282, '报表设计器', '', 2, 1, 1281, 'jimu-report', 'ep:trend-charts', 'report/jmreport/index', 'JimuReport', 0, b'1', b'1', b'1', '1', '2022-07-10 20:26:36', '1', '2025-05-03 09:57:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2000, '商品中心', '', 1, 60, 2362, 'product', 'fa:product-hunt', NULL, NULL, 0, b'1', b'1', b'1', '', '2022-07-29 15:53:53', '1', '2023-09-30 11:52:36', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2002, '商品分类', '', 2, 2, 2000, 'category', 'ep:cellphone', 'mall/product/category/index', 'ProductCategory', 0, b'1', b'1', b'1', '', '2022-07-29 15:53:53', '1', '2023-08-21 10:27:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2003, '分类查询', 'product:category:query', 3, 1, 2002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2004, '分类创建', 'product:category:create', 3, 2, 2002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2005, '分类更新', 'product:category:update', 3, 3, 2002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2006, '分类删除', 'product:category:delete', 3, 4, 2002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2008, '商品品牌', '', 2, 3, 2000, 'brand', 'ep:chicken', 'mall/product/brand/index', 'ProductBrand', 0, b'1', b'1', b'1', '', '2022-07-30 13:52:44', '1', '2023-08-21 10:27:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2009, '品牌查询', 'product:brand:query', 3, 1, 2008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2010, '品牌创建', 'product:brand:create', 3, 2, 2008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2011, '品牌更新', 'product:brand:update', 3, 3, 2008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2012, '品牌删除', 'product:brand:delete', 3, 4, 2008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2014, '商品列表', '', 2, 1, 2000, 'spu', 'ep:apple', 'mall/product/spu/index', 'ProductSpu', 0, b'1', b'1', b'1', '', '2022-07-30 14:22:58', '1', '2025-10-08 10:36:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2015, '商品查询', 'product:spu:query', 3, 1, 2014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2016, '商品创建', 'product:spu:create', 3, 2, 2014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2017, '商品更新', 'product:spu:update', 3, 3, 2014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, '商品删除', 'product:spu:delete', 3, 4, 2014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2019, '商品属性', '', 2, 4, 2000, 'property', 'ep:cold-drink', 'mall/product/property/index', 'ProductProperty', 0, b'1', b'1', b'1', '', '2022-08-01 14:55:35', '1', '2023-08-26 11:01:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2020, '规格查询', 'product:property:query', 3, 1, 2019, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:24', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2021, '规格创建', 'product:property:create', 3, 2, 2019, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2022, '规格更新', 'product:property:update', 3, 3, 2019, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2023, '规格删除', 'product:property:delete', 3, 4, 2019, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2025, 'Banner', '', 2, 100, 2387, 'banner', 'fa:bandcamp', 'mall/promotion/banner/index', NULL, 0, b'1', b'1', b'1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2026, 'Banner查询', 'promotion:banner:query', 3, 1, 2025, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 'Banner创建', 'promotion:banner:create', 3, 2, 2025, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 'Banner更新', 'promotion:banner:update', 3, 3, 2025, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2029, 'Banner删除', 'promotion:banner:delete', 3, 4, 2025, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:36', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2030, '营销中心', '', 1, 70, 2362, 'promotion', 'ep:present', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-10-31 21:25:09', '1', '2023-09-30 11:54:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2032, '优惠劵列表', '', 2, 1, 2365, 'template', 'ep:discount', 'mall/promotion/coupon/template/index', 'PromotionCouponTemplate', 0, b'1', b'1', b'1', '', '2022-10-31 22:27:14', '1', '2023-10-03 12:40:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2033, '优惠劵模板查询', 'promotion:coupon-template:query', 3, 1, 2032, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2034, '优惠劵模板创建', 'promotion:coupon-template:create', 3, 2, 2032, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2035, '优惠劵模板更新', 'promotion:coupon-template:update', 3, 3, 2032, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2036, '优惠劵模板删除', 'promotion:coupon-template:delete', 3, 4, 2032, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2038, '领取记录', '', 2, 2, 2365, 'list', 'ep:collection-tag', 'mall/promotion/coupon/index', 'PromotionCoupon', 0, b'1', b'1', b'1', '', '2022-11-03 23:21:31', '1', '2023-10-03 12:55:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2039, '优惠劵查询', 'promotion:coupon:query', 3, 1, 2038, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2040, '优惠劵删除', 'promotion:coupon:delete', 3, 4, 2038, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2041, '满减送', '', 2, 10, 2390, 'reward-activity', 'ep:goblet-square-full', 'mall/promotion/rewardActivity/index', 'PromotionRewardActivity', 0, b'1', b'1', b'1', '', '2022-11-04 23:47:49', '1', '2023-10-21 19:24:46', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2042, '满减送活动查询', 'promotion:reward-activity:query', 3, 1, 2041, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2043, '满减送活动创建', 'promotion:reward-activity:create', 3, 2, 2041, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2044, '满减送活动更新', 'promotion:reward-activity:update', 3, 3, 2041, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2045, '满减送活动删除', 'promotion:reward-activity:delete', 3, 4, 2041, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2046, '满减送活动关闭', 'promotion:reward-activity:close', 3, 5, 2041, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-11-05 10:42:53', '1', '2022-11-05 10:42:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2047, '限时折扣', '', 2, 7, 2390, 'discount-activity', 'ep:timer', 'mall/promotion/discountActivity/index', 'PromotionDiscountActivity', 0, b'1', b'1', b'1', '', '2022-11-05 17:12:15', '1', '2023-10-21 19:24:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2048, '限时折扣活动查询', 'promotion:discount-activity:query', 3, 1, 2047, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2049, '限时折扣活动创建', 'promotion:discount-activity:create', 3, 2, 2047, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2050, '限时折扣活动更新', 'promotion:discount-activity:update', 3, 3, 2047, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2051, '限时折扣活动删除', 'promotion:discount-activity:delete', 3, 4, 2047, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2052, '限时折扣活动关闭', 'promotion:discount-activity:close', 3, 5, 2047, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2059, '秒杀商品', '', 2, 2, 2209, 'activity', 'ep:basketball', 'mall/promotion/seckill/activity/index', 'PromotionSeckillActivity', 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '1', '2023-06-24 18:57:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2060, '秒杀活动查询', 'promotion:seckill-activity:query', 3, 1, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2061, '秒杀活动创建', 'promotion:seckill-activity:create', 3, 2, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2062, '秒杀活动更新', 'promotion:seckill-activity:update', 3, 3, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2063, '秒杀活动删除', 'promotion:seckill-activity:delete', 3, 4, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2066, '秒杀时段', '', 2, 1, 2209, 'config', 'ep:baseball', 'mall/promotion/seckill/config/index', 'PromotionSeckillConfig', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:50', '1', '2023-06-24 18:57:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2067, '秒杀时段查询', 'promotion:seckill-config:query', 3, 1, 2066, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2068, '秒杀时段创建', 'promotion:seckill-config:create', 3, 2, 2066, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:48:39', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2069, '秒杀时段更新', 'promotion:seckill-config:update', 3, 3, 2066, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2070, '秒杀时段删除', 'promotion:seckill-config:delete', 3, 4, 2066, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2072, '订单中心', '', 1, 65, 2362, 'trade', 'ep:eleme', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-11-19 18:57:19', '1', '2023-09-30 11:54:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2073, '售后退款', '', 2, 2, 2072, 'after-sale', 'ep:refrigerator', 'mall/trade/afterSale/index', 'TradeAfterSale', 0, b'1', b'1', b'1', '', '2022-11-19 20:15:32', '1', '2023-10-01 21:42:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2074, '售后查询', 'trade:after-sale:query', 3, 1, 2073, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-19 20:15:33', '1', '2022-12-10 21:04:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2075, '秒杀活动关闭', 'promotion:seckill-activity:close', 3, 5, 2059, '', '', '', '', 0, b'1', b'1', b'1', '1', '2022-11-28 20:20:15', '1', '2023-10-03 18:34:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2076, '订单列表', '', 2, 1, 2072, 'order', 'ep:list', 'mall/trade/order/index', 'TradeOrder', 0, b'1', b'1', b'1', '1', '2022-12-10 21:05:44', '1', '2023-10-01 21:42:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2083, '地区管理', '', 2, 14, 1, 'area', 'fa:map-marker', 'system/area/index', 'SystemArea', 0, b'1', b'1', b'1', '1', '2022-12-23 17:35:05', '1', '2024-02-29 08:50:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2084, '公众号管理', '', 1, 100, 0, '/mp', 'ep:compass', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-01-01 20:11:04', '1', '2024-02-29 12:39:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2085, '账号管理', '', 2, 1, 2084, 'account', 'fa:user', 'mp/account/index', 'MpAccount', 0, b'1', b'1', b'1', '1', '2023-01-01 20:13:31', '1', '2024-02-29 12:42:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2086, '新增账号', 'mp:account:create', 3, 1, 2085, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-01 20:21:40', '1', '2023-01-07 17:32:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2087, '修改账号', 'mp:account:update', 3, 2, 2085, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-07 17:32:46', '1', '2023-01-07 17:32:46', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2088, '查询账号', 'mp:account:query', 3, 0, 2085, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-07 17:33:07', '1', '2023-01-07 17:33:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2089, '删除账号', 'mp:account:delete', 3, 3, 2085, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-07 17:33:21', '1', '2023-01-07 17:33:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2090, '生成二维码', 'mp:account:qr-code', 3, 4, 2085, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-07 17:33:58', '1', '2023-01-07 17:33:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2091, '清空 API 配额', 'mp:account:clear-quota', 3, 5, 2085, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-07 18:20:32', '1', '2023-01-07 18:20:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2092, '数据统计', 'mp:statistics:query', 2, 2, 2084, 'statistics', 'ep:trend-charts', 'mp/statistics/index', 'MpStatistics', 0, b'1', b'1', b'1', '1', '2023-01-07 20:17:36', '1', '2024-02-29 12:42:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2093, '标签管理', '', 2, 3, 2084, 'tag', 'ep:collection-tag', 'mp/tag/index', 'MpTag', 0, b'1', b'1', b'1', '1', '2023-01-08 11:37:32', '1', '2024-02-29 12:42:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2094, '查询标签', 'mp:tag:query', 3, 0, 2093, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-08 11:59:03', '1', '2023-01-08 11:59:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2095, '新增标签', 'mp:tag:create', 3, 1, 2093, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-08 11:59:23', '1', '2023-01-08 11:59:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2096, '修改标签', 'mp:tag:update', 3, 2, 2093, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-08 11:59:41', '1', '2023-01-08 11:59:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2097, '删除标签', 'mp:tag:delete', 3, 3, 2093, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-08 12:00:04', '1', '2023-01-08 12:00:13', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2098, '同步标签', 'mp:tag:sync', 3, 4, 2093, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-08 12:00:29', '1', '2023-01-08 12:00:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2099, '粉丝管理', '', 2, 4, 2084, 'user', 'fa:user-secret', 'mp/user/index', 'MpUser', 0, b'1', b'1', b'1', '1', '2023-01-08 16:51:20', '1', '2024-02-29 12:42:39', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2100, '查询粉丝', 'mp:user:query', 3, 0, 2099, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-08 17:16:59', '1', '2023-01-08 17:17:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2101, '修改粉丝', 'mp:user:update', 3, 1, 2099, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-08 17:17:11', '1', '2023-01-08 17:17:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2102, '同步粉丝', 'mp:user:sync', 3, 2, 2099, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-08 17:17:40', '1', '2023-01-08 17:17:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2103, '消息管理', '', 2, 5, 2084, 'message', 'ep:message', 'mp/message/index', 'MpMessage', 0, b'1', b'1', b'1', '1', '2023-01-08 18:44:19', '1', '2024-02-29 12:42:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2104, '图文发表记录', '', 2, 10, 2084, 'free-publish', 'ep:edit-pen', 'mp/freePublish/index', 'MpFreePublish', 0, b'1', b'1', b'1', '1', '2023-01-13 00:30:50', '1', '2024-02-29 12:43:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2105, '查询发布列表', 'mp:free-publish:query', 3, 1, 2104, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-13 07:19:17', '1', '2023-01-13 07:19:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2106, '发布草稿', 'mp:free-publish:submit', 3, 2, 2104, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-13 07:19:46', '1', '2023-01-13 07:19:46', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2107, '删除发布记录', 'mp:free-publish:delete', 3, 3, 2104, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-13 07:20:01', '1', '2023-01-13 07:20:01', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2108, '图文草稿箱', '', 2, 9, 2084, 'draft', 'ep:edit', 'mp/draft/index', 'MpDraft', 0, b'1', b'1', b'1', '1', '2023-01-13 07:40:21', '1', '2024-02-29 12:43:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2109, '新建草稿', 'mp:draft:create', 3, 1, 2108, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-13 23:15:30', '1', '2023-01-13 23:15:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2110, '修改草稿', 'mp:draft:update', 3, 2, 2108, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-14 10:08:47', '1', '2023-01-14 10:08:47', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2111, '查询草稿', 'mp:draft:query', 3, 0, 2108, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-14 10:09:01', '1', '2023-01-14 10:09:01', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2112, '删除草稿', 'mp:draft:delete', 3, 3, 2108, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-14 10:09:19', '1', '2023-01-14 10:09:19', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2113, '素材管理', '', 2, 8, 2084, 'material', 'ep:basketball', 'mp/material/index', 'MpMaterial', 0, b'1', b'1', b'1', '1', '2023-01-14 14:12:07', '1', '2024-02-29 12:43:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2114, '上传临时素材', 'mp:material:upload-temporary', 3, 1, 2113, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-14 15:33:55', '1', '2023-01-14 15:33:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2115, '上传永久素材', 'mp:material:upload-permanent', 3, 2, 2113, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-14 15:34:14', '1', '2023-01-14 15:34:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2116, '删除素材', 'mp:material:delete', 3, 3, 2113, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-14 15:35:37', '1', '2023-01-14 15:35:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2117, '上传图文图片', 'mp:material:upload-news-image', 3, 4, 2113, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-14 15:36:31', '1', '2023-01-14 15:36:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2118, '查询素材', 'mp:material:query', 3, 5, 2113, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-14 15:39:22', '1', '2023-01-14 15:39:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2119, '菜单管理', '', 2, 6, 2084, 'menu', 'ep:menu', 'mp/menu/index', 'MpMenu', 0, b'1', b'1', b'1', '1', '2023-01-14 17:43:54', '1', '2025-04-01 20:21:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2120, '自动回复', '', 2, 7, 2084, 'auto-reply', 'fa-solid:republican', 'mp/autoReply/index', 'MpAutoReply', 0, b'1', b'1', b'1', '1', '2023-01-15 22:13:09', '1', '2024-02-29 12:43:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2121, '查询回复', 'mp:auto-reply:query', 3, 0, 2120, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-16 22:28:41', '1', '2023-01-16 22:28:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2122, '新增回复', 'mp:auto-reply:create', 3, 1, 2120, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-16 22:28:54', '1', '2023-01-16 22:28:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2123, '修改回复', 'mp:auto-reply:update', 3, 2, 2120, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-16 22:29:05', '1', '2023-01-16 22:29:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2124, '删除回复', 'mp:auto-reply:delete', 3, 3, 2120, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-16 22:29:34', '1', '2023-01-16 22:29:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2125, '查询菜单', 'mp:menu:query', 3, 0, 2119, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-17 23:05:41', '1', '2023-01-17 23:05:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2126, '保存菜单', 'mp:menu:save', 3, 1, 2119, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-17 23:06:01', '1', '2023-01-17 23:06:01', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2127, '删除菜单', 'mp:menu:delete', 3, 2, 2119, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-17 23:06:16', '1', '2023-01-17 23:06:16', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2128, '查询消息', 'mp:message:query', 3, 0, 2103, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-17 23:07:14', '1', '2023-01-17 23:07:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2129, '发送消息', 'mp:message:send', 3, 1, 2103, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-17 23:07:26', '1', '2023-01-17 23:07:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2130, '邮箱管理', '', 2, 2, 2739, 'mail', 'fa-solid:mail-bulk', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-01-25 17:27:44', '1', '2024-04-22 23:56:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2131, '邮箱账号', '', 2, 0, 2130, 'mail-account', 'fa:universal-access', 'system/mail/account/index', 'SystemMailAccount', 0, b'1', b'1', b'1', '', '2023-01-25 09:33:48', '1', '2024-02-29 08:48:16', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2132, '账号查询', 'system:mail-account:query', 3, 1, 2131, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2133, '账号创建', 'system:mail-account:create', 3, 2, 2131, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2134, '账号更新', 'system:mail-account:update', 3, 3, 2131, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2135, '账号删除', 'system:mail-account:delete', 3, 4, 2131, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2136, '邮件模版', '', 2, 0, 2130, 'mail-template', 'fa:tag', 'system/mail/template/index', 'SystemMailTemplate', 0, b'1', b'1', b'1', '', '2023-01-25 12:05:31', '1', '2024-02-29 08:48:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2137, '模版查询', 'system:mail-template:query', 3, 1, 2136, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2138, '模版创建', 'system:mail-template:create', 3, 2, 2136, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2139, '模版更新', 'system:mail-template:update', 3, 3, 2136, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2140, '模版删除', 'system:mail-template:delete', 3, 4, 2136, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2141, '邮件记录', '', 2, 0, 2130, 'mail-log', 'fa:edit', 'system/mail/log/index', 'SystemMailLog', 0, b'1', b'1', b'1', '', '2023-01-26 02:16:50', '1', '2024-02-29 08:48:51', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2142, '日志查询', 'system:mail-log:query', 3, 1, 2141, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-26 02:16:50', '', '2023-01-26 02:16:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2143, '发送测试邮件', 'system:mail-template:send-mail', 3, 5, 2136, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-26 23:29:15', '1', '2023-01-26 23:29:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2144, '站内信管理', '', 1, 3, 2739, 'notify', 'ep:message-box', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-01-28 10:25:18', '1', '2024-04-22 23:56:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2145, '模板管理', '', 2, 0, 2144, 'notify-template', 'fa:archive', 'system/notify/template/index', 'SystemNotifyTemplate', 0, b'1', b'1', b'1', '', '2023-01-28 02:26:42', '1', '2024-02-29 08:49:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2146, '站内信模板查询', 'system:notify-template:query', 3, 1, 2145, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2147, '站内信模板创建', 'system:notify-template:create', 3, 2, 2145, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2148, '站内信模板更新', 'system:notify-template:update', 3, 3, 2145, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2149, '站内信模板删除', 'system:notify-template:delete', 3, 4, 2145, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2150, '发送测试站内信', 'system:notify-template:send-notify', 3, 5, 2145, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-01-28 10:54:43', '1', '2023-01-28 10:54:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2151, '消息记录', '', 2, 0, 2144, 'notify-message', 'fa:edit', 'system/notify/message/index', 'SystemNotifyMessage', 0, b'1', b'1', b'1', '', '2023-01-28 04:28:22', '1', '2024-02-29 08:49:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2152, '站内信消息查询', 'system:notify-message:query', 3, 1, 2151, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-01-28 04:28:22', '', '2023-01-28 04:28:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2153, '大屏设计器', '', 2, 2, 1281, 'go-view', 'fa:area-chart', 'report/goview/index', 'GoView', 0, b'1', b'1', b'1', '1', '2023-02-07 00:03:19', '1', '2025-05-03 09:57:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2154, '创建项目', 'report:go-view-project:create', 3, 1, 2153, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:25:14', '1', '2023-02-07 19:25:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2155, '更新项目', 'report:go-view-project:update', 3, 2, 2153, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-02-07 19:25:34', '1', '2024-04-24 20:01:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2164, '配送管理', '', 1, 3, 2072, 'delivery', 'ep:shopping-cart', '', '', 0, b'1', b'1', b'1', '1', '2023-05-18 09:18:02', '1', '2023-09-28 10:58:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2165, '快递发货', '', 1, 0, 2164, 'express', 'ep:bicycle', '', '', 0, b'1', b'1', b'1', '1', '2023-05-18 09:22:06', '1', '2023-08-30 21:02:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2166, '门店自提', '', 1, 1, 2164, 'pick-up-store', 'ep:add-location', '', '', 0, b'1', b'1', b'1', '1', '2023-05-18 09:23:14', '1', '2023-08-30 21:03:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2167, '快递公司', '', 2, 0, 2165, 'express', 'ep:compass', 'mall/trade/delivery/express/index', 'Express', 0, b'1', b'1', b'1', '1', '2023-05-18 09:27:21', '1', '2024-11-29 11:20:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2168, '快递公司查询', 'trade:delivery:express:query', 3, 1, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2169, '快递公司创建', 'trade:delivery:express:create', 3, 2, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2170, '快递公司更新', 'trade:delivery:express:update', 3, 3, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2171, '快递公司删除', 'trade:delivery:express:delete', 3, 4, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2172, '快递公司导出', 'trade:delivery:express:export', 3, 5, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2173, '运费模版', 'trade:delivery:express-template:query', 2, 1, 2165, 'express-template', 'ep:coordinate', 'mall/trade/delivery/expressTemplate/index', 'ExpressTemplate', 0, b'1', b'1', b'1', '1', '2023-05-20 06:48:10', '1', '2023-08-30 21:03:13', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2174, '快递运费模板查询', 'trade:delivery:express-template:query', 3, 1, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2175, '快递运费模板创建', 'trade:delivery:express-template:create', 3, 2, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2176, '快递运费模板更新', 'trade:delivery:express-template:update', 3, 3, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2177, '快递运费模板删除', 'trade:delivery:express-template:delete', 3, 4, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2178, '快递运费模板导出', 'trade:delivery:express-template:export', 3, 5, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2179, '门店管理', '', 2, 1, 2166, 'pick-up-store', 'ep:basketball', 'mall/trade/delivery/pickUpStore/index', 'PickUpStore', 0, b'1', b'1', b'1', '1', '2023-05-25 10:50:00', '1', '2023-08-30 21:03:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2180, '自提门店查询', 'trade:delivery:pick-up-store:query', 3, 1, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2181, '自提门店创建', 'trade:delivery:pick-up-store:create', 3, 2, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2182, '自提门店更新', 'trade:delivery:pick-up-store:update', 3, 3, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2183, '自提门店删除', 'trade:delivery:pick-up-store:delete', 3, 4, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2184, '自提门店导出', 'trade:delivery:pick-up-store:export', 3, 5, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2209, '秒杀活动', '', 2, 3, 2030, 'seckill', 'ep:place', '', '', 0, b'1', b'1', b'1', '1', '2023-06-24 17:39:13', '1', '2023-06-24 18:55:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2262, '会员中心', '', 1, 55, 0, '/member', 'ep:bicycle', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-06-10 00:42:03', '1', '2023-08-20 09:23:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2275, '会员配置', '', 2, 0, 2262, 'config', 'fa:archive', 'member/config/index', 'MemberConfig', 0, b'1', b'1', b'1', '', '2023-06-10 02:07:44', '1', '2023-10-01 23:41:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2276, '会员配置查询', 'member:config:query', 3, 1, 2275, '', '', '', '', 0, b'1', b'1', b'1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:48:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2277, '会员配置保存', 'member:config:save', 3, 2, 2275, '', '', '', '', 0, b'1', b'1', b'1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:49:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2281, '签到配置', '', 2, 2, 2300, 'config', 'ep:calendar', 'member/signin/config/index', 'SignInConfig', 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '1', '2023-08-20 19:25:51', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2282, '积分签到规则查询', 'point:sign-in-config:query', 3, 1, 2281, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2283, '积分签到规则创建', 'point:sign-in-config:create', 3, 2, 2281, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2284, '积分签到规则更新', 'point:sign-in-config:update', 3, 3, 2281, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2285, '积分签到规则删除', 'point:sign-in-config:delete', 3, 4, 2281, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2287, '会员积分', '', 2, 10, 2262, 'record', 'fa:asterisk', 'member/point/record/index', 'PointRecord', 0, b'1', b'1', b'1', '', '2023-06-10 04:18:50', '1', '2023-10-01 23:42:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2288, '用户积分记录查询', 'point:record:query', 3, 1, 2287, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 04:18:50', '', '2023-06-10 04:18:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2293, '签到记录', '', 2, 3, 2300, 'record', 'ep:chicken', 'member/signin/record/index', 'SignInRecord', 0, b'1', b'1', b'1', '', '2023-06-10 04:48:22', '1', '2023-08-20 19:26:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2294, '用户签到积分查询', 'point:sign-in-record:query', 3, 1, 2293, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2297, '用户签到积分删除', 'point:sign-in-record:delete', 3, 4, 2293, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2300, '会员签到', '', 1, 11, 2262, 'signin', 'ep:alarm-clock', '', '', 0, b'1', b'1', b'1', '1', '2023-06-27 22:49:53', '1', '2023-08-20 09:23:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2301, '回调通知', '', 2, 5, 1117, 'notify', 'ep:mute-notification', 'pay/notify/index', 'PayNotify', 0, b'1', b'1', b'1', '', '2023-07-20 04:41:32', '1', '2024-01-18 23:56:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2302, '支付通知查询', 'pay:notify:query', 3, 1, 2301, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-07-20 04:41:32', '', '2023-07-20 04:41:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2303, '拼团活动', '', 2, 3, 2030, 'combination', 'fa:group', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:19:54', '1', '2023-08-12 17:20:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2304, '拼团商品', '', 2, 1, 2303, 'acitivity', 'ep:apple', 'mall/promotion/combination/activity/index', 'PromotionCombinationActivity', 0, b'1', b'1', b'1', '1', '2023-08-12 17:22:03', '1', '2023-08-12 17:22:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2305, '拼团活动查询', 'promotion:combination-activity:query', 3, 1, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:54:32', '1', '2023-11-24 11:57:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2306, '拼团活动创建', 'promotion:combination-activity:create', 3, 2, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:54:49', '1', '2023-08-12 17:54:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2307, '拼团活动更新', 'promotion:combination-activity:update', 3, 3, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:55:04', '1', '2023-08-12 17:55:04', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2308, '拼团活动删除', 'promotion:combination-activity:delete', 3, 4, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:55:23', '1', '2023-08-12 17:55:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2309, '拼团活动关闭', 'promotion:combination-activity:close', 3, 5, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:55:37', '1', '2023-10-06 10:51:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2310, '砍价活动', '', 2, 4, 2030, 'bargain', 'ep:box', '', '', 0, b'1', b'1', b'1', '1', '2023-08-13 00:27:25', '1', '2023-08-13 00:27:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2311, '砍价商品', '', 2, 1, 2310, 'activity', 'ep:burger', 'mall/promotion/bargain/activity/index', 'PromotionBargainActivity', 0, b'1', b'1', b'1', '1', '2023-08-13 00:28:49', '1', '2023-10-05 01:16:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2312, '砍价活动查询', 'promotion:bargain-activity:query', 3, 1, 2311, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-13 00:32:30', '1', '2023-08-13 00:32:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2313, '砍价活动创建', 'promotion:bargain-activity:create', 3, 2, 2311, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-13 00:32:44', '1', '2023-08-13 00:32:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2314, '砍价活动更新', 'promotion:bargain-activity:update', 3, 3, 2311, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-13 00:32:55', '1', '2023-08-13 00:32:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2315, '砍价活动删除', 'promotion:bargain-activity:delete', 3, 4, 2311, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-13 00:34:50', '1', '2023-08-13 00:34:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2316, '砍价活动关闭', 'promotion:bargain-activity:close', 3, 5, 2311, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-13 00:35:02', '1', '2023-08-13 00:35:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2317, '会员管理', '', 2, 0, 2262, 'user', 'ep:avatar', 'member/user/index', 'MemberUser', 0, b'1', b'1', b'1', '', '2023-08-19 04:12:15', '1', '2023-08-24 00:50:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2318, '会员用户查询', 'member:user:query', 3, 1, 2317, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2319, '会员用户更新', 'member:user:update', 3, 3, 2317, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2320, '会员标签', '', 2, 1, 2262, 'tag', 'ep:collection-tag', 'member/tag/index', 'MemberTag', 0, b'1', b'1', b'1', '', '2023-08-20 01:03:08', '1', '2023-08-20 09:23:19', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2321, '会员标签查询', 'member:tag:query', 3, 1, 2320, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2322, '会员标签创建', 'member:tag:create', 3, 2, 2320, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2323, '会员标签更新', 'member:tag:update', 3, 3, 2320, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2324, '会员标签删除', 'member:tag:delete', 3, 4, 2320, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2325, '会员等级', '', 2, 2, 2262, 'level', 'fa:level-up', 'member/level/index', 'MemberLevel', 0, b'1', b'1', b'1', '', '2023-08-22 12:41:01', '1', '2023-08-22 21:47:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2326, '会员等级查询', 'member:level:query', 3, 1, 2325, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2327, '会员等级创建', 'member:level:create', 3, 2, 2325, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2328, '会员等级更新', 'member:level:update', 3, 3, 2325, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2329, '会员等级删除', 'member:level:delete', 3, 4, 2325, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2330, '会员分组', '', 2, 3, 2262, 'group', 'fa:group', 'member/group/index', 'MemberGroup', 0, b'1', b'1', b'1', '', '2023-08-22 13:50:06', '1', '2023-10-01 23:42:01', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2331, '用户分组查询', 'member:group:query', 3, 1, 2330, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2332, '用户分组创建', 'member:group:create', 3, 2, 2330, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2333, '用户分组更新', 'member:group:update', 3, 3, 2330, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2334, '用户分组删除', 'member:group:delete', 3, 4, 2330, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2335, '用户等级修改', 'member:user:update-level', 3, 5, 2317, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-08-23 16:49:05', '', '2023-08-23 16:50:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2336, '商品评论', '', 2, 5, 2000, 'comment', 'ep:comment', 'mall/product/comment/index', 'ProductComment', 0, b'1', b'1', b'1', '1', '2023-08-26 11:03:00', '1', '2023-08-26 11:03:38', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2337, '评论查询', 'product:comment:query', 3, 1, 2336, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-26 11:04:01', '1', '2023-08-26 11:04:01', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2338, '添加自评', 'product:comment:create', 3, 2, 2336, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-26 11:04:23', '1', '2023-08-26 11:08:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2339, '商家回复', 'product:comment:update', 3, 3, 2336, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-26 11:04:37', '1', '2023-08-26 11:04:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2340, '显隐评论', 'product:comment:update', 3, 4, 2336, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-26 11:04:55', '1', '2023-08-26 11:04:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2341, '优惠劵发送', 'promotion:coupon:send', 3, 2, 2038, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-09-02 00:03:14', '1', '2023-09-02 00:03:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2342, '交易配置', '', 2, 0, 2072, 'config', 'ep:setting', 'mall/trade/config/index', 'TradeConfig', 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:30:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2343, '交易中心配置查询', 'trade:config:query', 3, 1, 2342, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2344, '交易中心配置保存', 'trade:config:save', 3, 2, 2342, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2345, '分销管理', '', 1, 4, 2072, 'brokerage', 'fa-solid:project-diagram', '', '', 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '1', '2023-09-28 10:58:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2346, '分销用户', '', 2, 0, 2345, 'brokerage-user', 'fa-solid:user-tie', 'mall/trade/brokerage/user/index', 'TradeBrokerageUser', 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2347, '分销用户查询', 'trade:brokerage-user:query', 3, 1, 2346, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2348, '分销用户推广人查询', 'trade:brokerage-user:user-query', 3, 2, 2346, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2349, '分销用户推广订单查询', 'trade:brokerage-user:order-query', 3, 3, 2346, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2350, '分销用户修改推广资格', 'trade:brokerage-user:update-brokerage-enable', 3, 4, 2346, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2351, '修改推广员', 'trade:brokerage-user:update-bind-user', 3, 5, 2346, '', '', '', '', 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2352, '清除推广员', 'trade:brokerage-user:clear-bind-user', 3, 6, 2346, '', '', '', '', 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2353, '佣金记录', '', 2, 1, 2345, 'brokerage-record', 'fa:money', 'mall/trade/brokerage/record/index', 'TradeBrokerageRecord', 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2354, '佣金记录查询', 'trade:brokerage-record:query', 3, 1, 2353, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2355, '佣金提现', '', 2, 2, 2345, 'brokerage-withdraw', 'fa:credit-card', 'mall/trade/brokerage/withdraw/index', 'TradeBrokerageWithdraw', 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2356, '佣金提现查询', 'trade:brokerage-withdraw:query', 3, 1, 2355, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2357, '佣金提现审核', 'trade:brokerage-withdraw:audit', 3, 2, 2355, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2358, '统计中心', '', 1, 75, 2362, 'statistics', 'ep:data-line', '', '', 0, b'1', b'1', b'1', '', '2023-09-30 03:22:40', '1', '2023-09-30 11:54:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2359, '交易统计', '', 2, 4, 2358, 'trade', 'fa-solid:credit-card', 'mall/statistics/trade/index', 'TradeStatistics', 0, b'1', b'1', b'1', '', '2023-09-30 03:22:40', '1', '2024-02-26 20:42:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2360, '交易统计查询', 'statistics:trade:query', 3, 1, 2359, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2361, '交易统计导出', 'statistics:trade:export', 3, 2, 2359, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2362, '商城系统', '', 1, 59, 0, '/mall', 'ep:shop', '', '', 0, b'1', b'1', b'1', '1', '2023-09-30 11:52:02', '1', '2023-09-30 11:52:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2363, '用户积分修改', 'member:user:update-point', 3, 6, 2317, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-01 14:39:43', '', '2023-10-01 14:39:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2364, '用户余额修改', 'pay:wallet:update-balance', 3, 7, 2317, '', '', '', '', 0, b'1', b'1', b'1', '', '2023-10-01 14:39:43', '1', '2024-10-01 09:42:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2365, '优惠劵', '', 1, 2, 2030, 'coupon', 'fa-solid:disease', '', '', 0, b'1', b'1', b'1', '1', '2023-10-03 12:39:15', '1', '2023-10-05 00:16:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2366, '砍价记录', '', 2, 2, 2310, 'record', 'ep:list', 'mall/promotion/bargain/record/index', 'PromotionBargainRecord', 0, b'1', b'1', b'1', '', '2023-10-05 02:49:06', '1', '2023-10-05 10:50:38', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2367, '砍价记录查询', 'promotion:bargain-record:query', 3, 1, 2366, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-05 02:49:06', '', '2023-10-05 02:49:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2368, '助力记录查询', 'promotion:bargain-help:query', 3, 2, 2366, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-10-05 12:27:49', '1', '2023-10-05 12:27:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2369, '拼团记录', 'promotion:combination-record:query', 2, 2, 2303, 'record', 'ep:avatar', 'mall/promotion/combination/record/index.vue', 'PromotionCombinationRecord', 0, b'1', b'1', b'1', '1', '2023-10-08 07:10:22', '1', '2023-10-08 07:34:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2374, '会员统计', '', 2, 2, 2358, 'member', 'ep:avatar', 'mall/statistics/member/index', 'MemberStatistics', 0, b'1', b'1', b'1', '', '2023-10-11 04:39:24', '1', '2024-02-26 20:41:46', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2375, '会员统计查询', 'statistics:member:query', 3, 1, 2374, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-11 04:39:24', '', '2023-10-11 04:39:24', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2376, '订单核销', 'trade:order:pick-up', 3, 10, 2076, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-10-14 17:11:58', '1', '2023-10-14 17:11:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2377, '文章分类', '', 2, 0, 2387, 'article/category', 'fa:certificate', 'mall/promotion/article/category/index', 'ArticleCategory', 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:38:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2378, '分类查询', 'promotion:article-category:query', 3, 1, 2377, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2379, '分类创建', 'promotion:article-category:create', 3, 2, 2377, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2380, '分类更新', 'promotion:article-category:update', 3, 3, 2377, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2381, '分类删除', 'promotion:article-category:delete', 3, 4, 2377, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2382, '文章列表', '', 2, 2, 2387, 'article', 'ep:connection', 'mall/promotion/article/index', 'Article', 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:41:19', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2383, '文章管理查询', 'promotion:article:query', 3, 1, 2382, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2384, '文章管理创建', 'promotion:article:create', 3, 2, 2382, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2385, '文章管理更新', 'promotion:article:update', 3, 3, 2382, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2386, '文章管理删除', 'promotion:article:delete', 3, 4, 2382, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2387, '内容管理', '', 1, 1, 2030, 'content', 'ep:collection', '', '', 0, b'1', b'1', b'1', '1', '2023-10-16 09:37:31', '1', '2023-10-16 09:37:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2388, '商城首页', '', 2, 1, 2362, 'home', 'ep:home-filled', 'mall/home/index', 'MallHome', 0, b'1', b'1', b'1', '', '2023-10-16 12:10:33', '', '2023-10-16 12:10:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2389, '核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder', 0, b'1', b'1', b'1', '', '2023-10-19 16:09:51', '', '2023-10-19 16:09:51', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2390, '优惠活动', '', 1, 99, 2030, 'youhui', 'ep:aim', '', '', 0, b'1', b'1', b'1', '1', '2023-10-21 19:23:49', '1', '2023-10-21 19:23:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2391, '客户管理', '', 2, 10, 2397, 'customer', 'fa:address-book-o', 'crm/customer/index', 'CrmCustomer', 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '1', '2024-02-17 17:13:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2392, '客户查询', 'crm:customer:query', 3, 1, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2393, '客户创建', 'crm:customer:create', 3, 2, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2394, '客户更新', 'crm:customer:update', 3, 3, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2395, '客户删除', 'crm:customer:delete', 3, 4, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2396, '客户导出', 'crm:customer:export', 3, 5, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2397, 'CRM 系统', '', 1, 200, 0, '/crm', 'simple-icons:civicrm', '', '', 0, b'1', b'1', b'1', '1', '2023-10-29 17:08:30', '1', '2025-04-19 18:56:38', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2398, '合同管理', '', 2, 50, 2397, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '1', '2024-02-17 17:15:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2399, '合同查询', 'crm:contract:query', 3, 1, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2400, '合同创建', 'crm:contract:create', 3, 2, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2401, '合同更新', 'crm:contract:update', 3, 3, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2402, '合同删除', 'crm:contract:delete', 3, 4, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2403, '合同导出', 'crm:contract:export', 3, 5, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2404, '线索管理', '', 2, 8, 2397, 'clue', 'fa:pagelines', 'crm/clue/index', 'CrmClue', 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '1', '2024-02-17 17:15:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2405, '线索查询', 'crm:clue:query', 3, 1, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2406, '线索创建', 'crm:clue:create', 3, 2, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2407, '线索更新', 'crm:clue:update', 3, 3, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2408, '线索删除', 'crm:clue:delete', 3, 4, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2409, '线索导出', 'crm:clue:export', 3, 5, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2410, '商机管理', '', 2, 40, 2397, 'business', 'fa:bus', 'crm/business/index', 'CrmBusiness', 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '1', '2024-02-17 17:14:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2411, '商机查询', 'crm:business:query', 3, 1, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2412, '商机创建', 'crm:business:create', 3, 2, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2413, '商机更新', 'crm:business:update', 3, 3, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2414, '商机删除', 'crm:business:delete', 3, 4, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2415, '商机导出', 'crm:business:export', 3, 5, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2416, '联系人管理', '', 2, 20, 2397, 'contact', 'fa:address-book-o', 'crm/contact/index', 'CrmContact', 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '1', '2024-02-17 17:13:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2417, '联系人查询', 'crm:contact:query', 3, 1, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2418, '联系人创建', 'crm:contact:create', 3, 2, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2419, '联系人更新', 'crm:contact:update', 3, 3, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2420, '联系人删除', 'crm:contact:delete', 3, 4, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2421, '联系人导出', 'crm:contact:export', 3, 5, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2422, '回款管理', '', 2, 60, 2397, 'receivable', 'ep:money', 'crm/receivable/index', 'CrmReceivable', 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2423, '回款管理查询', 'crm:receivable:query', 3, 1, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2424, '回款管理创建', 'crm:receivable:create', 3, 2, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2425, '回款管理更新', 'crm:receivable:update', 3, 3, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2426, '回款管理删除', 'crm:receivable:delete', 3, 4, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2427, '回款管理导出', 'crm:receivable:export', 3, 5, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2428, '回款计划', '', 2, 61, 2397, 'receivable-plan', 'fa:money', 'crm/receivable/plan/index', 'CrmReceivablePlan', 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2429, '回款计划查询', 'crm:receivable-plan:query', 3, 1, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2430, '回款计划创建', 'crm:receivable-plan:create', 3, 2, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2432, '回款计划删除', 'crm:receivable-plan:delete', 3, 4, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2433, '回款计划导出', 'crm:receivable-plan:export', 3, 5, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', '', 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '1', '2025-03-15 21:34:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2436, '装修模板', '', 2, 1, 2435, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2437, '装修模板查询', 'promotion:diy-template:query', 3, 1, 2436, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2438, '装修模板创建', 'promotion:diy-template:create', 3, 2, 2436, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2439, '装修模板更新', 'promotion:diy-template:update', 3, 3, 2436, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2440, '装修模板删除', 'promotion:diy-template:delete', 3, 4, 2436, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2441, '装修模板使用', 'promotion:diy-template:use', 3, 5, 2436, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2442, '装修页面', '', 2, 2, 2435, 'diy-page', 'foundation:page-edit', 'mall/promotion/diy/page/index', 'DiyPage', 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2443, '装修页面查询', 'promotion:diy-page:query', 3, 1, 2442, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2444, '装修页面创建', 'promotion:diy-page:create', 3, 2, 2442, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2445, '装修页面更新', 'promotion:diy-page:update', 3, 3, 2442, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2446, '装修页面删除', 'promotion:diy-page:delete', 3, 4, 2442, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2447, '三方登录', '', 1, 10, 1, 'social', 'fa:rocket', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:12:01', '1', '2024-02-29 01:14:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2448, '三方应用', '', 2, 1, 2447, 'client', 'ep:set-up', 'system/social/client/index.vue', 'SocialClient', 0, b'1', b'1', b'1', '1', '2023-11-04 12:17:19', '1', '2024-05-04 19:09:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2449, '三方应用查询', 'system:social-client:query', 3, 1, 2448, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:43:12', '1', '2023-11-04 12:43:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2450, '三方应用创建', 'system:social-client:create', 3, 2, 2448, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:43:58', '1', '2023-11-04 12:43:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2451, '三方应用更新', 'system:social-client:update', 3, 3, 2448, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:44:27', '1', '2023-11-04 12:44:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2452, '三方应用删除', 'system:social-client:delete', 3, 4, 2448, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:44:43', '1', '2023-11-04 12:44:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2453, '三方用户', 'system:social-user:query', 2, 2, 2447, 'user', 'ep:avatar', 'system/social/user/index.vue', 'SocialUser', 0, b'1', b'1', b'1', '1', '2023-11-04 14:01:05', '1', '2023-11-04 14:01:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2472, '主子表（内嵌）', '', 2, 12, 1070, 'demo03-inner', 'fa:power-off', 'infra/demo/demo03/inner/index', 'Demo03StudentInner', 0, b'1', b'1', b'1', '', '2023-11-13 04:39:51', '1', '2023-11-16 23:53:46', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2478, '单表（增删改查）', '', 2, 1, 1070, 'demo01-contact', 'ep:bicycle', 'infra/demo/demo01/index', 'Demo01Contact', 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '1', '2023-11-16 20:34:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2479, '示例联系人查询', 'infra:demo01-contact:query', 3, 1, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2480, '示例联系人创建', 'infra:demo01-contact:create', 3, 2, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2481, '示例联系人更新', 'infra:demo01-contact:update', 3, 3, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2482, '示例联系人删除', 'infra:demo01-contact:delete', 3, 4, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2483, '示例联系人导出', 'infra:demo01-contact:export', 3, 5, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2484, '树表（增删改查）', '', 2, 2, 1070, 'demo02-category', 'fa:tree', 'infra/demo/demo02/index', 'Demo02Category', 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '1', '2023-11-16 20:35:01', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2485, '示例分类查询', 'infra:demo02-category:query', 3, 1, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2486, '示例分类创建', 'infra:demo02-category:create', 3, 2, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2487, '示例分类更新', 'infra:demo02-category:update', 3, 3, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2488, '示例分类删除', 'infra:demo02-category:delete', 3, 4, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2489, '示例分类导出', 'infra:demo02-category:export', 3, 5, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2490, '主子表（标准）', '', 2, 10, 1070, 'demo03-normal', 'fa:battery-3', 'infra/demo/demo03/normal/index', 'Demo03StudentNormal', 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '1', '2023-11-16 23:10:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2491, '学生查询', 'infra:demo03-student:query', 3, 1, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2492, '学生创建', 'infra:demo03-student:create', 3, 2, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2493, '学生更新', 'infra:demo03-student:update', 3, 3, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2494, '学生删除', 'infra:demo03-student:delete', 3, 4, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2495, '学生导出', 'infra:demo03-student:export', 3, 5, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2497, '主子表（ERP）', '', 2, 11, 1070, 'demo03-erp', 'ep:calendar', 'infra/demo/demo03/erp/index', 'Demo03StudentERP', 0, b'1', b'1', b'1', '', '2023-11-16 15:50:59', '1', '2023-11-17 13:19:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2516, '客户公海配置', '', 2, 0, 2524, 'customer-pool-config', 'ep:data-analysis', 'crm/customer/poolConfig/index', 'CrmCustomerPoolConfig', 0, b'1', b'1', b'1', '', '2023-11-18 13:33:31', '1', '2024-01-03 19:52:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2517, '客户公海配置保存', 'crm:customer-pool-config:update', 3, 1, 2516, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:31', '', '2023-11-18 13:33:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2518, '客户限制配置', '', 2, 1, 2524, 'customer-limit-config', 'ep:avatar', 'crm/customer/limitConfig/index', 'CrmCustomerLimitConfig', 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '1', '2024-02-24 16:43:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2519, '客户限制配置查询', 'crm:customer-limit-config:query', 3, 1, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2520, '客户限制配置创建', 'crm:customer-limit-config:create', 3, 2, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2521, '客户限制配置更新', 'crm:customer-limit-config:update', 3, 3, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2522, '客户限制配置删除', 'crm:customer-limit-config:delete', 3, 4, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2523, '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2524, '系统配置', '', 1, 999, 2397, 'config', 'ep:connection', '', '', 0, b'1', b'1', b'1', '1', '2023-11-18 21:58:00', '1', '2024-02-17 17:14:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2525, 'WebSocket', '', 2, 5, 2, 'websocket', 'ep:connection', 'infra/webSocket/index', 'InfraWebSocket', 0, b'1', b'1', b'1', '1', '2023-11-23 19:41:55', '1', '2024-04-23 00:02:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2526, '产品管理', '', 2, 80, 2397, 'product', 'fa:product-hunt', 'crm/product/index', 'CrmProduct', 0, b'1', b'1', b'1', '1', '2023-12-05 22:45:26', '1', '2024-02-20 20:36:20', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2527, '产品查询', 'crm:product:query', 3, 1, 2526, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-05 22:47:16', '1', '2023-12-05 22:47:16', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2528, '产品创建', 'crm:product:create', 3, 2, 2526, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-05 22:47:41', '1', '2023-12-05 22:47:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2529, '产品更新', 'crm:product:update', 3, 3, 2526, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-05 22:48:03', '1', '2023-12-05 22:48:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2530, '产品删除', 'crm:product:delete', 3, 4, 2526, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-05 22:48:17', '1', '2023-12-05 22:48:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2531, '产品导出', 'crm:product:export', 3, 5, 2526, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-05 22:48:29', '1', '2023-12-05 22:48:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2532, '产品分类配置', '', 2, 3, 2524, 'product/category', 'fa-solid:window-restore', 'crm/product/category/index', 'CrmProductCategory', 0, b'1', b'1', b'1', '1', '2023-12-06 12:52:36', '1', '2023-12-06 12:52:51', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2533, '产品分类查询', 'crm:product-category:query', 3, 1, 2532, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-06 12:53:23', '1', '2023-12-06 12:53:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2534, '产品分类创建', 'crm:product-category:create', 3, 2, 2532, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-06 12:53:41', '1', '2023-12-06 12:53:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2535, '产品分类更新', 'crm:product-category:update', 3, 3, 2532, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-06 12:53:59', '1', '2023-12-06 12:53:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2536, '产品分类删除', 'crm:product-category:delete', 3, 4, 2532, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-06 12:54:14', '1', '2023-12-06 12:54:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2543, '关联商机', 'crm:contact:create-business', 3, 10, 2416, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-01-02 17:28:25', '1', '2024-01-02 17:28:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2544, '取关商机', 'crm:contact:delete-business', 3, 11, 2416, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-01-02 17:28:43', '1', '2024-01-02 17:28:51', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2545, '商品统计', '', 2, 3, 2358, 'product', 'fa:product-hunt', 'mall/statistics/product/index', 'ProductStatistics', 0, b'1', b'1', b'1', '', '2023-12-15 18:54:28', '1', '2024-02-26 20:41:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2546, '客户公海', '', 2, 30, 2397, 'customer/pool', 'fa-solid:swimming-pool', 'crm/customer/pool/index', 'CrmCustomerPool', 0, b'1', b'1', b'1', '1', '2024-01-15 21:29:34', '1', '2024-02-17 17:14:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2547, '订单查询', 'trade:order:query', 3, 1, 2076, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-01-16 08:52:00', '1', '2024-01-16 08:52:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2548, '订单更新', 'trade:order:update', 3, 2, 2076, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-01-16 08:52:21', '1', '2024-01-16 08:52:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2549, '支付&退款案例', '', 2, 1, 2161, 'order', 'fa:paypal', 'pay/demo/order/index', '', 0, b'1', b'1', b'1', '1', '2024-01-18 23:45:00', '1', '2024-01-18 23:47:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2550, '提现转账案例', '', 2, 2, 2161, 'transfer', 'fa:transgender-alt', 'pay/demo/withdraw/index', '', 0, b'1', b'1', b'1', '1', '2024-01-18 23:51:16', '1', '2025-05-08 13:04:36', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2551, '钱包管理', '', 1, 4, 1117, 'wallet', 'ep:wallet', '', '', 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '1', '2024-02-29 08:58:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2552, '充值套餐', '', 2, 2, 2551, 'wallet-recharge-package', 'fa:leaf', 'pay/wallet/rechargePackage/index', 'WalletRechargePackage', 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2553, '钱包充值套餐查询', 'pay:wallet-recharge-package:query', 3, 1, 2552, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2554, '钱包充值套餐创建', 'pay:wallet-recharge-package:create', 3, 2, 2552, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2555, '钱包充值套餐更新', 'pay:wallet-recharge-package:update', 3, 3, 2552, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2556, '钱包充值套餐删除', 'pay:wallet-recharge-package:delete', 3, 4, 2552, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2557, '钱包余额', '', 2, 1, 2551, 'wallet-balance', 'fa:leaf', 'pay/wallet/balance/index', 'WalletBalance', 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2558, '钱包余额查询', 'pay:wallet:query', 3, 1, 2557, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2559, '转账订单', '', 2, 3, 1117, 'transfer', 'ep:credit-card', 'pay/transfer/index', 'PayTransfer', 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2560, '数据统计', '', 1, 200, 2397, 'statistics', 'ep:data-line', '', '', 0, b'1', b'1', b'1', '1', '2024-01-26 22:50:35', '1', '2024-02-24 20:10:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2561, '排行榜', 'crm:statistics-rank:query', 2, 1, 2560, 'ranking', 'fa:area-chart', 'crm/statistics/rank/index', 'CrmStatisticsRank', 0, b'1', b'1', b'1', '1', '2024-01-26 22:52:09', '1', '2024-04-24 19:39:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2562, '客户导入', 'crm:customer:import', 3, 6, 2391, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-01 13:09:00', '1', '2024-02-01 13:09:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2563, 'ERP 系统', '', 1, 300, 0, '/erp', 'simple-icons:erpnext', '', '', 0, b'1', b'1', b'1', '1', '2024-02-04 15:37:25', '1', '2025-04-19 18:56:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2564, '产品管理', '', 1, 40, 2563, 'product', 'fa:product-hunt', '', '', 0, b'1', b'1', b'1', '1', '2024-02-04 15:38:43', '1', '2024-02-04 15:38:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2565, '产品信息', '', 2, 0, 2564, 'product', 'fa-solid:apple-alt', 'erp/product/product/index', 'ErpProduct', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-05 14:42:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2566, '产品查询', 'erp:product:query', 3, 1, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:21:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2567, '产品创建', 'erp:product:create', 3, 2, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2568, '产品更新', 'erp:product:update', 3, 3, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:16', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2569, '产品删除', 'erp:product:delete', 3, 4, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2570, '产品导出', 'erp:product:export', 3, 5, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2571, '产品分类', '', 2, 1, 2564, 'product-category', 'fa:certificate', 'erp/product/category/index', 'ErpProductCategory', 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '1', '2024-02-04 17:24:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2572, '分类查询', 'erp:product-category:query', 3, 1, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2573, '分类创建', 'erp:product-category:create', 3, 2, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2574, '分类更新', 'erp:product-category:update', 3, 3, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2575, '分类删除', 'erp:product-category:delete', 3, 4, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2576, '分类导出', 'erp:product-category:export', 3, 5, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2577, '产品单位', '', 2, 2, 2564, 'unit', 'ep:opportunity', 'erp/product/unit/index', 'ErpProductUnit', 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '1', '2024-02-04 19:54:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2578, '单位查询', 'erp:product-unit:query', 3, 1, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2579, '单位创建', 'erp:product-unit:create', 3, 2, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2580, '单位更新', 'erp:product-unit:update', 3, 3, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2581, '单位删除', 'erp:product-unit:delete', 3, 4, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2582, '单位导出', 'erp:product-unit:export', 3, 5, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2583, '库存管理', '', 1, 30, 2563, 'stock', 'fa:window-restore', '', '', 0, b'1', b'1', b'1', '1', '2024-02-05 00:29:37', '1', '2024-02-05 00:29:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2584, '仓库信息', '', 2, 0, 2583, 'warehouse', 'ep:house', 'erp/stock/warehouse/index', 'ErpWarehouse', 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '1', '2024-02-05 01:12:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2585, '仓库查询', 'erp:warehouse:query', 3, 1, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2586, '仓库创建', 'erp:warehouse:create', 3, 2, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2587, '仓库更新', 'erp:warehouse:update', 3, 3, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2588, '仓库删除', 'erp:warehouse:delete', 3, 4, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2589, '仓库导出', 'erp:warehouse:export', 3, 5, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2590, '产品库存', '', 2, 1, 2583, 'stock', 'ep:coffee', 'erp/stock/stock/index', 'ErpStock', 0, b'1', b'1', b'1', '', '2024-02-05 06:40:50', '1', '2024-02-05 14:42:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2591, '库存查询', 'erp:stock:query', 3, 1, 2590, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2592, '库存导出', 'erp:stock:export', 3, 5, 2590, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2593, '出入库明细', '', 2, 2, 2583, 'record', 'fa-solid:blog', 'erp/stock/record/index', 'ErpStockRecord', 0, b'1', b'1', b'1', '', '2024-02-05 10:27:21', '1', '2024-02-06 17:26:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2594, '库存明细查询', 'erp:stock-record:query', 3, 1, 2593, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2595, '库存明细导出', 'erp:stock-record:export', 3, 5, 2593, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2596, '其它入库', '', 2, 3, 2583, 'in', 'ep:zoom-in', 'erp/stock/in/index', 'ErpStockIn', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:51', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2597, '其它入库单查询', 'erp:stock-in:query', 3, 1, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2598, '其它入库单创建', 'erp:stock-in:create', 3, 2, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2599, '其它入库单更新', 'erp:stock-in:update', 3, 3, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2600, '其它入库单删除', 'erp:stock-in:delete', 3, 4, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2601, '其它入库单导出', 'erp:stock-in:export', 3, 5, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2602, '采购管理', '', 1, 10, 2563, 'purchase', 'fa:buysellads', '', '', 0, b'1', b'1', b'1', '1', '2024-02-06 16:01:01', '1', '2024-02-06 16:01:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2603, '供应商信息', '', 2, 4, 2602, 'supplier', 'fa:superpowers', 'erp/purchase/supplier/index', 'ErpSupplier', 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '1', '2024-02-06 16:22:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2604, '供应商查询', 'erp:supplier:query', 3, 1, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2605, '供应商创建', 'erp:supplier:create', 3, 2, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2606, '供应商更新', 'erp:supplier:update', 3, 3, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2607, '供应商删除', 'erp:supplier:delete', 3, 4, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2608, '供应商导出', 'erp:supplier:export', 3, 5, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2609, '其它入库单审批', 'erp:stock-in:update-status', 3, 6, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2610, '其它出库', '', 2, 4, 2583, 'out', 'ep:zoom-out', 'erp/stock/out/index', 'ErpStockOut', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2611, '其它出库单查询', 'erp:stock-out:query', 3, 1, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:39', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2612, '其它出库单创建', 'erp:stock-out:create', 3, 2, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2613, '其它出库单更新', 'erp:stock-out:update', 3, 3, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2614, '其它出库单删除', 'erp:stock-out:delete', 3, 4, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2615, '其它出库单导出', 'erp:stock-out:export', 3, 5, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2616, '其它出库单审批', 'erp:stock-out:update-status', 3, 6, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2617, '销售管理', '', 1, 20, 2563, 'sale', 'fa:sellsy', '', '', 0, b'1', b'1', b'1', '1', '2024-02-07 15:12:32', '1', '2024-02-07 15:12:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2618, '客户信息', '', 2, 4, 2617, 'customer', 'ep:avatar', 'erp/sale/customer/index', 'ErpCustomer', 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '1', '2024-02-07 15:22:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2619, '客户查询', 'erp:customer:query', 3, 1, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2620, '客户创建', 'erp:customer:create', 3, 2, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2621, '客户更新', 'erp:customer:update', 3, 3, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2622, '客户删除', 'erp:customer:delete', 3, 4, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2623, '客户导出', 'erp:customer:export', 3, 5, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2624, '库存调拨', '', 2, 5, 2583, 'move', 'ep:folder-remove', 'erp/stock/move/index', 'ErpStockMove', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-16 18:53:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2625, '库存调度单查询', 'erp:stock-move:query', 3, 1, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2626, '库存调度单创建', 'erp:stock-move:create', 3, 2, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2627, '库存调度单更新', 'erp:stock-move:update', 3, 3, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2628, '库存调度单删除', 'erp:stock-move:delete', 3, 4, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2629, '库存调度单导出', 'erp:stock-move:export', 3, 5, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2630, '库存调度单审批', 'erp:stock-move:update-status', 3, 6, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2631, '库存盘点', '', 2, 6, 2583, 'check', 'ep:circle-check-filled', 'erp/stock/check/index', 'ErpStockCheck', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-08 08:31:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2632, '库存盘点单查询', 'erp:stock-check:query', 3, 1, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2633, '库存盘点单创建', 'erp:stock-check:create', 3, 2, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2634, '库存盘点单更新', 'erp:stock-check:update', 3, 3, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2635, '库存盘点单删除', 'erp:stock-check:delete', 3, 4, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2636, '库存盘点单导出', 'erp:stock-check:export', 3, 5, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2637, '库存盘点单审批', 'erp:stock-check:update-status', 3, 6, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2638, '销售订单', '', 2, 1, 2617, 'order', 'fa:first-order', 'erp/sale/order/index', 'ErpSaleOrder', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-10 21:59:20', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2639, '销售订单查询', 'erp:sale-order:query', 3, 1, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2640, '销售订单创建', 'erp:sale-order:create', 3, 2, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2641, '销售订单更新', 'erp:sale-order:update', 3, 3, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2642, '销售订单删除', 'erp:sale-order:delete', 3, 4, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2643, '销售订单导出', 'erp:sale-order:export', 3, 5, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2644, '销售订单审批', 'erp:sale-order:update-status', 3, 6, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2645, '财务管理', '', 1, 50, 2563, 'finance', 'ep:money', '', '', 0, b'1', b'1', b'1', '1', '2024-02-10 08:05:58', '1', '2024-02-10 08:06:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2646, '结算账户', '', 2, 10, 2645, 'account', 'fa:universal-access', 'erp/finance/account/index', 'ErpAccount', 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '1', '2024-02-14 08:24:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2647, '结算账户查询', 'erp:account:query', 3, 1, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2648, '结算账户创建', 'erp:account:create', 3, 2, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2649, '结算账户更新', 'erp:account:update', 3, 3, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2650, '结算账户删除', 'erp:account:delete', 3, 4, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2651, '结算账户导出', 'erp:account:export', 3, 5, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2652, '销售出库', '', 2, 2, 2617, 'out', 'ep:sold-out', 'erp/sale/out/index', 'ErpSaleOut', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-10 22:02:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2653, '销售出库查询', 'erp:sale-out:query', 3, 1, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2654, '销售出库创建', 'erp:sale-out:create', 3, 2, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2655, '销售出库更新', 'erp:sale-out:update', 3, 3, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2656, '销售出库删除', 'erp:sale-out:delete', 3, 4, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2657, '销售出库导出', 'erp:sale-out:export', 3, 5, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2658, '销售出库审批', 'erp:sale-out:update-status', 3, 6, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2659, '销售退货', '', 2, 3, 2617, 'return', 'fa-solid:bone', 'erp/sale/return/index', 'ErpSaleReturn', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-12 06:12:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2660, '销售退货查询', 'erp:sale-return:query', 3, 1, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2661, '销售退货创建', 'erp:sale-return:create', 3, 2, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2662, '销售退货更新', 'erp:sale-return:update', 3, 3, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2663, '销售退货删除', 'erp:sale-return:delete', 3, 4, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2664, '销售退货导出', 'erp:sale-return:export', 3, 5, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2665, '销售退货审批', 'erp:sale-return:update-status', 3, 6, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2666, '采购订单', '', 2, 1, 2602, 'order', 'fa-solid:border-all', 'erp/purchase/order/index', 'ErpPurchaseOrder', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-12 08:51:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2667, '采购订单查询', 'erp:purchase-order:query', 3, 1, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2668, '采购订单创建', 'erp:purchase-order:create', 3, 2, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2669, '采购订单更新', 'erp:purchase-order:update', 3, 3, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2670, '采购订单删除', 'erp:purchase-order:delete', 3, 4, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2671, '采购订单导出', 'erp:purchase-order:export', 3, 5, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2672, '采购订单审批', 'erp:purchase-order:update-status', 3, 6, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2673, '采购入库', '', 2, 2, 2602, 'in', 'fa-solid:gopuram', 'erp/purchase/in/index', 'ErpPurchaseIn', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-12 11:19:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2674, '采购入库查询', 'erp:purchase-in:query', 3, 1, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2675, '采购入库创建', 'erp:purchase-in:create', 3, 2, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2676, '采购入库更新', 'erp:purchase-in:update', 3, 3, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2677, '采购入库删除', 'erp:purchase-in:delete', 3, 4, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2678, '采购入库导出', 'erp:purchase-in:export', 3, 5, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2679, '采购入库审批', 'erp:purchase-in:update-status', 3, 6, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2680, '采购退货', '', 2, 3, 2602, 'return', 'ep:minus', 'erp/purchase/return/index', 'ErpPurchaseReturn', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-12 20:51:02', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2681, '采购退货查询', 'erp:purchase-return:query', 3, 1, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2682, '采购退货创建', 'erp:purchase-return:create', 3, 2, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2683, '采购退货更新', 'erp:purchase-return:update', 3, 3, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2684, '采购退货删除', 'erp:purchase-return:delete', 3, 4, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2685, '采购退货导出', 'erp:purchase-return:export', 3, 5, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2686, '采购退货审批', 'erp:purchase-return:update-status', 3, 6, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2687, '付款单', '', 2, 1, 2645, 'payment', 'ep:caret-right', 'erp/finance/payment/index', 'ErpFinancePayment', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-14 08:24:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2688, '付款单查询', 'erp:finance-payment:query', 3, 1, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2689, '付款单创建', 'erp:finance-payment:create', 3, 2, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2690, '付款单更新', 'erp:finance-payment:update', 3, 3, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2691, '付款单删除', 'erp:finance-payment:delete', 3, 4, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2692, '付款单导出', 'erp:finance-payment:export', 3, 5, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2693, '付款单审批', 'erp:finance-payment:update-status', 3, 6, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2694, '收款单', '', 2, 2, 2645, 'receipt', 'ep:expand', 'erp/finance/receipt/index', 'ErpFinanceReceipt', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-15 19:35:45', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2695, '收款单查询', 'erp:finance-receipt:query', 3, 1, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2696, '收款单创建', 'erp:finance-receipt:create', 3, 2, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2697, '收款单更新', 'erp:finance-receipt:update', 3, 3, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2698, '收款单删除', 'erp:finance-receipt:delete', 3, 4, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2699, '收款单导出', 'erp:finance-receipt:export', 3, 5, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2700, '收款单审批', 'erp:finance-receipt:update-status', 3, 6, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2701, '待办事项', '', 2, 0, 2397, 'backlog', 'fa-solid:tasks', 'crm/backlog/index', 'CrmBacklog', 0, b'1', b'1', b'1', '1', '2024-02-17 17:17:11', '1', '2024-02-17 17:17:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2702, 'ERP 首页', 'erp:statistics:query', 2, 0, 2563, 'home', 'ep:home-filled', 'erp/home/index.vue', 'ErpHome', 0, b'1', b'1', b'1', '1', '2024-02-18 16:49:40', '1', '2024-02-26 21:12:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2703, '商机状态配置', '', 2, 4, 2524, 'business-status', 'fa-solid:charging-station', 'crm/business/status/index', 'CrmBusinessStatus', 0, b'1', b'1', b'1', '1', '2024-02-21 20:15:17', '1', '2024-02-21 20:15:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2704, '商机状态查询', 'crm:business-status:query', 3, 1, 2703, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-21 20:35:36', '1', '2024-02-21 20:36:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2705, '商机状态创建', 'crm:business-status:create', 3, 2, 2703, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-21 20:35:57', '1', '2024-02-21 20:35:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2706, '商机状态更新', 'crm:business-status:update', 3, 3, 2703, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-21 20:36:21', '1', '2024-02-21 20:36:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2707, '商机状态删除', 'crm:business-status:delete', 3, 4, 2703, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-21 20:36:36', '1', '2024-02-21 20:36:36', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2708, '合同配置', '', 2, 5, 2524, 'contract-config', 'ep:connection', 'crm/contract/config/index', 'CrmContractConfig', 0, b'1', b'1', b'1', '1', '2024-02-24 16:44:40', '1', '2024-02-24 16:44:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2709, '客户公海配置查询', 'crm:customer-pool-config:query', 3, 2, 2516, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-24 16:45:19', '1', '2024-02-24 16:45:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2710, '合同配置更新', 'crm:contract-config:update', 3, 1, 2708, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-24 16:45:56', '1', '2024-02-24 16:45:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2711, '合同配置查询', 'crm:contract-config:query', 3, 2, 2708, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-24 16:46:16', '1', '2024-02-24 16:46:16', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2712, '客户分析', 'crm:statistics-customer:query', 2, 0, 2560, 'customer', 'ep:avatar', 'crm/statistics/customer/index.vue', 'CrmStatisticsCustomer', 0, b'1', b'1', b'1', '1', '2024-03-09 16:43:56', '1', '2024-05-04 20:38:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2713, '抄送我的', 'bpm:process-instance-cc:query', 2, 30, 1200, 'copy', 'ep:copy-document', 'bpm/task/copy/index', 'BpmProcessInstanceCopy', 0, b'1', b'1', b'1', '1', '2024-03-17 21:50:23', '1', '2024-04-24 19:55:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2714, '流程分类', '', 2, 3, 1186, 'category', 'fa:object-ungroup', 'bpm/category/index', 'BpmCategory', 0, b'1', b'1', b'1', '', '2024-03-08 02:00:51', '1', '2024-03-21 23:51:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2715, '分类查询', 'bpm:category:query', 3, 1, 2714, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2716, '分类创建', 'bpm:category:create', 3, 2, 2714, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2717, '分类更新', 'bpm:category:update', 3, 3, 2714, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2718, '分类删除', 'bpm:category:delete', 3, 4, 2714, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2720, '发起流程', '', 2, 0, 1200, 'create', 'fa-solid:grin-stars', 'bpm/processInstance/create/index', 'BpmProcessInstanceCreate', 0, b'1', b'0', b'1', '1', '2024-03-19 19:46:05', '1', '2024-03-23 19:03:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2721, '流程实例', '', 2, 10, 1186, 'process-instance/manager', 'fa:square', 'bpm/processInstance/manager/index', 'BpmProcessInstanceManager', 0, b'1', b'1', b'1', '1', '2024-03-21 23:57:30', '1', '2024-03-21 23:57:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2722, '流程实例的查询（管理员）', 'bpm:process-instance:manager-query', 3, 1, 2721, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-03-22 08:18:27', '1', '2024-03-22 08:19:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2723, '流程实例的取消（管理员）', 'bpm:process-instance:cancel-by-admin', 3, 2, 2721, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-03-22 08:19:25', '1', '2024-03-22 08:19:25', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2724, '流程任务', '', 2, 11, 1186, 'process-tasnk', 'ep:collection-tag', 'bpm/task/manager/index', 'BpmManagerTask', 0, b'1', b'1', b'1', '1', '2024-03-22 08:43:22', '1', '2024-03-22 08:43:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2725, '流程任务的查询（管理员）', 'bpm:task:manager-query', 3, 1, 2724, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-03-22 08:43:49', '1', '2025-12-23 23:04:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2726, '流程监听器', '', 2, 5, 1186, 'process-listener', 'fa:assistive-listening-systems', 'bpm/processListener/index', 'BpmProcessListener', 0, b'1', b'1', b'1', '', '2024-03-09 16:05:34', '1', '2024-03-23 13:13:38', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2727, '流程监听器查询', 'bpm:process-listener:query', 3, 1, 2726, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2728, '流程监听器创建', 'bpm:process-listener:create', 3, 2, 2726, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2729, '流程监听器更新', 'bpm:process-listener:update', 3, 3, 2726, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2730, '流程监听器删除', 'bpm:process-listener:delete', 3, 4, 2726, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2731, '流程表达式', '', 2, 6, 1186, 'process-expression', 'fa:wpexplorer', 'bpm/processExpression/index', 'BpmProcessExpression', 0, b'1', b'1', b'1', '', '2024-03-09 22:35:08', '1', '2024-03-23 19:43:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2732, '流程表达式查询', 'bpm:process-expression:query', 3, 1, 2731, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2733, '流程表达式创建', 'bpm:process-expression:create', 3, 2, 2731, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2734, '流程表达式更新', 'bpm:process-expression:update', 3, 3, 2731, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2735, '流程表达式删除', 'bpm:process-expression:delete', 3, 4, 2731, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2736, '员工业绩', 'crm:statistics-performance:query', 2, 3, 2560, 'performance', 'ep:dish-dot', 'crm/statistics/performance/index', 'CrmStatisticsPerformance', 0, b'1', b'1', b'1', '1', '2024-04-05 13:49:20', '1', '2024-04-24 19:42:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2737, '客户画像', 'crm:statistics-portrait:query', 2, 4, 2560, 'portrait', 'ep:picture', 'crm/statistics/portrait/index', 'CrmStatisticsPortrait', 0, b'1', b'1', b'1', '1', '2024-04-05 13:57:40', '1', '2024-04-24 19:42:24', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2738, '销售漏斗', 'crm:statistics-funnel:query', 2, 5, 2560, 'funnel', 'ep:grape', 'crm/statistics/funnel/index', 'CrmStatisticsFunnel', 0, b'1', b'1', b'1', '1', '2024-04-13 10:53:26', '1', '2024-04-24 19:39:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2739, '消息中心', '', 1, 7, 1, 'messages', 'ep:chat-dot-round', '', '', 0, b'1', b'1', b'1', '1', '2024-04-22 23:54:30', '1', '2024-04-23 09:36:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2740, '监控中心', '', 1, 10, 2, 'monitors', 'ep:monitor', '', '', 0, b'1', b'1', b'1', '1', '2024-04-23 00:04:44', '1', '2024-04-23 00:04:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2741, '领取公海客户', 'crm:customer:receive', 3, 1, 2546, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:47:45', '1', '2024-04-24 19:47:45', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2742, '分配公海客户', 'crm:customer:distribute', 3, 2, 2546, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:48:05', '1', '2024-04-24 19:48:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2743, '商品统计查询', 'statistics:product:query', 3, 1, 2545, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:50:05', '1', '2024-04-24 19:50:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2744, '商品统计导出', 'statistics:product:export', 3, 2, 2545, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:50:26', '1', '2024-04-24 19:50:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2745, '支付渠道查询', 'pay:channel:query', 3, 10, 1126, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:53:01', '1', '2024-04-24 19:53:01', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2746, '支付渠道创建', 'pay:channel:create', 3, 11, 1126, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:53:18', '1', '2024-04-24 19:53:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2747, '支付渠道更新', 'pay:channel:update', 3, 12, 1126, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:53:32', '1', '2024-04-24 19:53:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2748, '支付渠道删除', 'pay:channel:delete', 3, 13, 1126, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:54:34', '1', '2024-04-24 19:54:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2749, '商品收藏查询', 'product:favorite:query', 3, 10, 2014, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:55:47', '1', '2024-04-24 19:55:47', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2750, '商品浏览查询', 'product:browse-history:query', 3, 20, 2014, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:57:43', '1', '2024-04-24 19:57:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2751, '售后同意', 'trade:after-sale:agree', 3, 2, 2073, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:58:40', '1', '2024-04-24 19:58:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2752, '售后不同意', 'trade:after-sale:disagree', 3, 3, 2073, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 19:59:03', '1', '2024-04-24 19:59:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2753, '售后确认退货', 'trade:after-sale:receive', 3, 4, 2073, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:00:07', '1', '2024-04-24 20:00:07', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2754, '售后确认退款', 'trade:after-sale:refund', 3, 5, 2073, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:00:24', '1', '2024-04-24 20:00:24', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2755, '删除项目', 'report:go-view-project:delete', 3, 2, 2153, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:01:37', '1', '2024-04-24 20:01:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2756, '会员等级记录查询', 'member:level-record:query', 3, 10, 2325, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:02:32', '1', '2024-04-24 20:02:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2757, '会员经验记录查询', 'member:experience-record:query', 3, 11, 2325, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:02:51', '1', '2024-04-24 20:02:51', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2758, 'AI 大模型', '', 1, 400, 0, '/ai', 'tabler:ai', '', '', 0, b'1', b'1', b'1', '1', '2024-05-07 15:07:56', '1', '2025-04-19 18:57:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2759, 'AI 对话', '', 2, 1, 2758, 'chat', 'ep:message', 'ai/chat/index/index.vue', 'AiChat', 0, b'1', b'1', b'1', '1', '2024-05-07 15:09:14', '1', '2024-07-07 17:15:36', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2760, '控制台', '', 1, 100, 2758, 'console', 'ep:setting', '', '', 0, b'1', b'1', b'1', '1', '2024-05-09 22:39:09', '1', '2024-05-24 23:34:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2761, 'API 密钥', '', 2, 0, 2760, 'api-key', 'ep:key', 'ai/model/apiKey/index.vue', 'AiApiKey', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-10 22:44:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2762, 'API 密钥查询', 'ai:api-key:query', 3, 1, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2767, '模型配置', '', 2, 0, 2760, 'model', 'fa-solid:abacus', 'ai/model/model/index.vue', 'AiModel', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:57:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2768, '聊天模型查询', 'ai:model:query', 3, 1, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:19:46', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2769, '聊天模型创建', 'ai:model:create', 3, 2, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2770, '聊天模型更新', 'ai:model:update', 3, 3, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:14', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2771, '聊天模型删除', 'ai:model:delete', 3, 4, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2776, '聊天角色更新', 'ai:chat-role:update', 3, 3, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2777, '聊天角色删除', 'ai:chat-role:delete', 3, 4, 2773, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-05-13 21:43:38', '1', '2024-05-13 21:43:38', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2778, '聊天管理', '', 2, 10, 2760, 'chat-conversation', 'ep:chat-square', 'ai/chat/manager/index.vue', 'AiChatManager', 0, b'1', b'1', b'1', '', '2024-05-24 15:39:18', '1', '2024-06-26 21:36:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2779, '会话查询', 'ai:chat-conversation:query', 3, 1, 2778, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:30', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2780, '会话删除', 'ai:chat-conversation:delete', 3, 2, 2778, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2781, '消息查询', 'ai:chat-message:query', 3, 11, 2778, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-05-25 08:38:56', '1', '2024-05-25 08:38:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2782, '消息删除', 'ai:chat-message:delete', 3, 12, 2778, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-05-25 08:39:10', '1', '2024-05-25 08:39:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2783, 'AI 绘画', '', 2, 2, 2758, 'image', 'ep:picture-rounded', 'ai/image/index/index.vue', 'AiImage', 0, b'1', b'1', b'1', '1', '2024-05-26 11:45:17', '1', '2024-07-07 17:18:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2784, '绘画管理', '', 2, 11, 2760, 'image', 'fa:file-image-o', 'ai/image/manager/index.vue', 'AiImageManager', 0, b'1', b'1', b'1', '', '2024-06-26 13:32:31', '1', '2024-06-26 21:37:13', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2785, '绘画查询', 'ai:image:query', 3, 1, 2784, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:21:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2786, '绘画删除', 'ai:image:delete', 3, 4, 2784, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:22:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2787, '绘图更新', 'ai:image:update', 3, 2, 2784, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-06-26 22:47:56', '1', '2024-08-31 09:21:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2788, '音乐管理', '', 2, 12, 2760, 'music', 'fa:music', 'ai/music/manager/index.vue', 'AiMusicManager', 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '1', '2024-06-27 23:04:19', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2789, '音乐查询', 'ai:music:query', 3, 1, 2788, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2790, '音乐更新', 'ai:music:update', 3, 3, 2788, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2791, '音乐删除', 'ai:music:delete', 3, 4, 2788, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2792, 'AI 写作', '', 2, 3, 2758, 'write', 'fa-solid:book-reader', 'ai/write/index/index.vue', 'AiWrite', 0, b'1', b'1', b'1', '1', '2024-07-08 09:26:44', '1', '2024-07-16 13:03:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2793, '写作管理', '', 2, 13, 2760, 'write', 'fa:bookmark-o', 'ai/write/manager/index.vue', 'AiWriteManager', 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '1', '2024-07-10 21:31:59', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2794, 'AI 写作查询', 'ai:write:query', 3, 1, 2793, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, b'1', b'1', b'1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, b'1', b'1', b'1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2798, 'AI 思维导图', '', 2, 6, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, b'1', b'1', b'1', '1', '2024-07-29 21:31:59', '1', '2025-03-02 18:57:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2802, '会话查询', 'promotion:kefu-conversation:query', 3, 1, 2797, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-08-31 09:17:52', '1', '2024-08-31 09:18:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2803, '会话更新', 'promotion:kefu-conversation:update', 3, 2, 2797, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-08-31 09:18:15', '1', '2024-08-31 09:19:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2804, '消息查询', 'promotion:kefu-message:query', 3, 10, 2797, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-08-31 09:18:42', '1', '2024-08-31 09:18:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2805, '会话删除', 'promotion:kefu-conversation:delete', 3, 3, 2797, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-08-31 09:19:51', '1', '2024-08-31 09:20:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2806, '消息发送', 'promotion:kefu-message:send', 3, 12, 2797, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-08-31 09:20:06', '1', '2024-08-31 09:20:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2807, '消息更新', 'promotion:kefu-message:update', 3, 11, 2797, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-08-31 09:20:22', '1', '2024-08-31 09:20:22', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2808, '积分商城', '', 2, 5, 2030, 'point-activity', 'ep:bowl', 'mall/promotion/point/activity/index', 'PointActivity', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-23 09:14:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2809, '积分商城活动查询', 'promotion:point-activity:query', 3, 1, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2810, '积分商城活动创建', 'promotion:point-activity:create', 3, 2, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2811, '积分商城活动更新', 'promotion:point-activity:update', 3, 3, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2812, '积分商城活动删除', 'promotion:point-activity:delete', 3, 4, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2813, '积分商城活动导出', 'promotion:point-activity:export', 3, 5, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-17 19:32:06', '1', '2025-01-17 19:32:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-23 20:23:34', '1', '2025-01-23 20:23:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2915, 'AI 知识库', '', 2, 5, 2758, 'knowledge', 'ep:notebook', 'ai/knowledge/knowledge/index', 'AiKnowledge', 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '1', '2025-03-02 18:58:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2916, 'AI 知识库查询', 'ai:knowledge:query', 3, 1, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2917, 'AI 知识库创建', 'ai:knowledge:create', 3, 2, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2918, 'AI 知识库更新', 'ai:knowledge:update', 3, 3, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2919, 'AI 知识库删除', 'ai:knowledge:delete', 3, 4, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2920, '工具管理', '', 2, 0, 2760, 'tool', 'fa-solid:tools', 'ai/model/tool/index.vue', 'AiTool', 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '1', '2025-03-14 19:20:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2921, '工具查询', 'ai:tool:query', 3, 1, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4000, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:55:28', '1', '2024-12-07 15:58:34', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4001, '设备接入', '', 1, 2, 4000, 'device', 'ep:platform', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:57:56', '1', '2025-02-27 08:39:49', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4002, '产品管理', '', 2, 1, 4001, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '1', '2025-06-15 20:56:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4003, '产品查询', 'iot:product:query', 3, 1, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4004, '产品创建', 'iot:product:create', 3, 2, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4005, '产品更新', 'iot:product:update', 3, 3, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4006, '产品删除', 'iot:product:delete', 3, 4, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4007, '产品导出', 'iot:product:export', 3, 5, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:13', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4008, '设备管理', '', 2, 2, 4001, 'device', 'fa:mobile', 'iot/device/device/index', 'IoTDevice', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2025-06-15 20:56:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4009, '设备查询', 'iot:device:query', 3, 1, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4010, '设备创建', 'iot:device:create', 3, 2, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4011, '设备更新', 'iot:device:update', 3, 3, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4012, '设备删除', 'iot:device:delete', 3, 4, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4013, '设备导出', 'iot:device:export', 3, 5, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:44', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4014, '产品分类', '', 2, 3, 4001, 'product-category', 'ep:notebook', 'iot/product/category/index', 'IotProductCategory', 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '1', '2025-06-15 20:56:39', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4015, '产品分类查询', 'iot:product-category:query', 3, 1, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4016, '产品分类创建', 'iot:product-category:create', 3, 2, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4017, '产品分类更新', 'iot:product-category:update', 3, 3, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4018, '产品分类删除', 'iot:product-category:delete', 3, 4, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4031, '设备分组', '', 2, 3, 4001, 'device-group', 'fa-solid:layer-group', 'iot/device/group/index', 'IotDeviceGroup', 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '1', '2024-12-14 17:09:17', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4032, '设备分组查询', 'iot:device-group:query', 3, 1, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4033, '设备分组创建', 'iot:device-group:create', 3, 2, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4034, '设备分组更新', 'iot:device-group:update', 3, 3, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4035, '设备分组删除', 'iot:device-group:delete', 3, 4, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4036, '设备导入', 'iot:device:import', 3, 6, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-15 10:35:47', '1', '2024-12-15 10:35:47', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4037, '产品物模型', '', 2, 99, 4001, 'thing-model', 'ep:mostly-cloudy', 'iot/thingmodel/index', 'IoTThingModel', 0, b'0', b'0', b'0', '', '2024-12-16 17:17:50', '1', '2025-06-15 20:56:19', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4038, '产品物模型功能查询', 'iot:thing-model:query', 3, 1, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:51', '', '2025-03-17 09:14:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4039, '产品物模型功能创建', 'iot:thing-model:create', 3, 2, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:14:58', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4040, '产品物模型功能更新', 'iot:thing-model:update', 3, 3, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4041, '产品物模型功能删除', 'iot:thing-model:delete', 3, 4, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:06', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4042, '产品物模型功能导出', 'iot:thing-model:export', 3, 5, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:53', '', '2025-03-17 09:15:09', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4043, '设备消息发送', 'iot:device:message-send', 3, 12, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 04:40:16', '1', '2025-06-14 14:09:26', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4044, '设备属性查询', 'iot:device:property-query', 3, 10, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 11:52:54', '1', '2025-01-28 11:52:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4045, '设备消息查询', 'iot:device:message-query', 3, 11, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 11:53:22', '1', '2025-06-14 11:11:20', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4047, '运维管理', '', 1, 4, 4000, 'operation', 'fa:align-center', '', '', 0, b'1', b'1', b'1', '1', '2025-02-05 22:21:37', '\\\"1\\\"', '2025-06-30 20:12:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4048, '规则引擎', '', 1, 3, 4000, 'rule', 'fa-solid:cogs', '', '', 0, b'1', b'1', b'1', '1', '2025-02-11 14:10:54', '1', '2025-02-11 14:10:54', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4049, '场景联动', '', 2, 1, 4048, 'scene', 'ep:link', 'iot/rule/scene/index', 'IoTSceneRule', 0, b'1', b'1', b'1', '1', '2025-02-11 14:12:44', '\\\"1\\\"', '2025-08-09 15:38:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4050, 'IoT 首页', '', 2, 1, 4000, 'home', 'ep:home-filled', 'iot/home/index', 'IotHome', 0, b'1', b'1', b'1', '1', '2025-02-27 08:39:35', '1', '2025-06-24 14:22:50', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4051, '数据流转', '', 2, 2, 4048, 'data-rule', 'ep:guide', 'iot/rule/data/index', 'IoTDataRule', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '\\\"1\\\"', '2025-08-09 15:38:37', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4052, '数据流转规则查询', 'iot:data-rule:query', 3, 1, 4051, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '1', '2025-06-24 20:48:04', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4053, '数据流转规则创建', 'iot:data-rule:create', 3, 2, 4051, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '1', '2025-06-24 20:48:08', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4054, '数据流转规则更新', 'iot:data-rule:update', 3, 3, 4051, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '1', '2025-06-24 20:48:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4055, '数据流转规则删除', 'iot:data-rule:delete', 3, 4, 4051, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:12', '1', '2025-06-24 20:48:15', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5000, 'AI 工作流', '', 2, 5, 2758, 'workflow', 'fa:hand-grab-o', 'ai/workflow/index.vue', 'AiWorkflow', 0, b'1', b'1', b'1', '1', '2025-03-25 09:50:27', '1', '2025-05-03 18:55:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5001, 'AI 工作流查询', 'ai:workflow:query', 3, 1, 5000, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-03-25 09:51:11', '1', '2025-03-25 09:51:11', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5002, 'AI 工作流创建', 'ai:workflow:create', 3, 2, 5000, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-03-25 09:51:28', '1', '2025-03-25 09:51:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5003, 'AI 工作流更新', 'ai:workflow:update', 3, 3, 5000, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-03-25 09:51:42', '1', '2025-03-25 09:51:42', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5004, 'AI 工作流删除', 'ai:workflow:delete', 3, 4, 5000, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-03-25 09:51:55', '1', '2025-03-25 09:52:03', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5005, 'AI 工作流测试', 'ai:workflow:test', 3, 5, 5000, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-03-30 10:29:41', '1', '2025-03-30 10:29:41', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5009, '仪表盘设计器', '', 2, 1, 1281, 'jimu-bi', 'fa:y-combinator', 'report/jmreport/bi', 'JimuBI', 0, b'1', b'1', b'1', '1', '2025-05-03 09:57:15', '1', '2025-05-03 10:02:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5010, '租户切换', 'system:tenant:visit', 3, 999, 1138, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-05-05 15:25:32', '1', '2025-05-05 15:25:32', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5011, '转账订单查询', 'pay:transfer:query', 3, 1, 2559, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-05-08 12:46:53', '1', '2025-05-08 12:46:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5012, '转账订单导出', 'pay:transfer:export', 3, 2, 2559, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-05-10 17:00:28', '1', '2025-05-10 17:00:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5013, '场景联动查询', 'iot:rule-scene:query', 3, 1, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:53:01', '1', '2025-06-20 16:53:01', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5014, '场景联动创建', 'iot:rule-scene:create', 3, 2, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:54:31', '1', '2025-06-20 16:54:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5015, '场景联动更新', 'iot:rule-scene:update', 3, 3, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:54:47', '1', '2025-06-20 16:54:47', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5016, '场景联动删除', 'iot:rule-scene:delete', 3, 4, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:55:04', '1', '2025-06-20 16:55:27', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5017, '场景联动导出', 'iot:rule-scene:export', 3, 5, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:57:56', '1', '2025-06-20 16:57:56', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5018, '数据流转目的查询', 'iot:data-sink:query', 3, 11, 4051, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-24 20:48:40', '1', '2025-06-24 20:48:40', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5019, '数据流转目的创建', 'iot:data-sink:create', 3, 12, 4051, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-24 20:48:57', '1', '2025-06-24 20:48:57', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5020, '数据流转目的更新', 'iot:data-sink:update', 3, 13, 4051, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-24 20:49:10', '1', '2025-06-24 20:49:10', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5021, '数据流转目的删除', 'iot:data-sink:delete', 3, 14, 4051, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-24 20:49:23', '1', '2025-06-24 20:49:23', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5022, '告警配置', '', 2, 1, 5028, 'config', 'fa:connectdevelop', 'iot/alert/config/index', 'IotAlertConfig', 0, b'1', b'1', b'1', '', '2025-06-27 14:28:59', '1', '2025-06-27 22:31:19', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5023, '告警配置查询', 'iot:alert-config:query', 3, 1, 5022, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-27 14:28:59', '1', '2025-06-28 16:00:31', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5024, '告警配置创建', 'iot:alert-config:create', 3, 2, 5022, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-27 14:28:59', '1', '2025-06-28 16:00:35', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5025, '告警配置更新', 'iot:alert-config:update', 3, 3, 5022, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-27 14:28:59', '1', '2025-06-28 16:00:43', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5026, '告警配置删除', 'iot:alert-config:delete', 3, 4, 5022, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-27 14:29:00', '1', '2025-06-28 16:00:39', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5028, '告警中心', '', 1, 3, 4000, 'alert', 'fa:soundcloud', '', '', 0, b'1', b'1', b'1', '1', '2025-06-27 22:30:04', '1', '2025-06-27 22:30:19', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5029, '告警记录', '', 2, 2, 5028, 'record', 'fa-solid:record-vinyl', 'iot/alert/record/index', 'IotAlertRecord', 0, b'1', b'1', b'1', '', '2025-06-28 07:59:32', '1', '2025-06-28 16:01:48', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5030, '告警记录查询', 'iot:alert-record:query', 3, 1, 5029, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-28 07:59:32', '1', '2025-06-28 16:00:53', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5031, '告警记录处理', 'iot:alert-record:process', 3, 2, 5029, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-28 07:59:32', '1', '2025-06-28 16:01:04', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5032, 'OTA 固件', '', 2, 1, 4047, 'ota/firmware', 'fa-solid:award', 'iot/ota/firmware/index', 'IoTOtaFirmware', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\\\"1\\\"', '2025-06-30 20:13:28', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5033, 'OTA 固件查询', 'iot:ota-firmware:query', 3, 1, 5032, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\\\"1\\\"', '2025-06-30 17:38:12', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `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');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5041, 'OTA 升级记录取消', 'iot:ota-task-record:cancel', 3, 23, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:59:18', '1', '2025-07-02 23:59:18', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5042, '模版消息', '', 2, 5, 2084, 'message-template', 'ep:notebook', 'mp/messageTemplate/index', 'MpMessageTemplate', 0, b'1', b'1', b'1', '1', '2025-11-26 16:45:35', '1', '2025-11-26 18:44:52', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5043, '查询模版消息', 'mp:message-template:query', 3, 1, 5042, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-11-26 17:00:15', '1', '2025-11-26 18:45:00', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5044, '删除模版消息', 'mp:message-template:delete', 3, 2, 5042, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-11-26 17:00:31', '1', '2025-11-26 18:45:05', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5045, '同步公众号模板', 'mp:message-template:sync', 3, 3, 5042, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-11-26 17:00:55', '1', '2025-11-26 17:00:55', b'0');\nINSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5046, '给粉丝发送模版消息', 'mp:message-template:send', 3, 4, 5042, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-11-26 17:01:11', '1', '2025-11-26 17:01:11', b'0');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_notice\n-- ----------------------------\nDROP TABLE IF EXISTS `system_notice`;\nCREATE TABLE `system_notice`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '公告ID',\n  `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '公告标题',\n  `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '公告内容',\n  `type` tinyint NOT NULL COMMENT '公告类型（1通知 2公告）',\n  `status` tinyint NOT NULL DEFAULT 0 COMMENT '公告状态（0正常 1关闭）',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '通知公告表';\n\n-- ----------------------------\n-- Records of system_notice\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '芋道的公众', '<p>新版本内容133222</p>', 1, 0, 'admin', '2021-01-05 17:03:48', '\\\"1\\\"', '2025-08-31 09:38:22', b'0', 1);\nINSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '维护通知：2018-07-01 系统凌晨维护', '<p><img src=\\\"http://test.yudao.iocoder.cn/b7cb3cf49b4b3258bf7309a09dd2f4e5.jpg\\\" alt=\\\"\\\" data-href=\\\"\\\">11112222<img src=\\\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\\\" alt=\\\"image\\\" data-href=\\\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\\\">3333</p>', 2, 1, 'admin', '2021-01-05 17:03:48', '1', '2025-04-18 23:56:40', b'0', 1);\nINSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, '我是测试标题', '<p>哈哈哈哈123</p>', 1, 0, '110', '2022-02-22 01:01:25', '110', '2022-02-22 01:01:46', b'0', 121);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_notify_message\n-- ----------------------------\nDROP TABLE IF EXISTS `system_notify_message`;\nCREATE TABLE `system_notify_message`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',\n  `user_id` bigint NOT NULL COMMENT '用户id',\n  `user_type` tinyint NOT NULL COMMENT '用户类型',\n  `template_id` bigint NOT NULL COMMENT '模版编号',\n  `template_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板编码',\n  `template_nickname` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模版发送人名称',\n  `template_content` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模版内容',\n  `template_type` int NOT NULL COMMENT '模版类型',\n  `template_params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模版参数',\n  `read_status` bit(1) NOT NULL COMMENT '是否已读',\n  `read_time` datetime NULL DEFAULT NULL COMMENT '阅读时间',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '站内信消息表';\n\n-- ----------------------------\n-- Records of system_notify_message\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\\\"name\\\":\\\"1\\\",\\\"what\\\":\\\"2\\\"}', b'1', '2025-12-15 21:24:36', '1', '2023-01-28 11:44:08', '1', '2025-12-15 21:24:36', b'0', 1);\nINSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\\\"name\\\":\\\"1\\\",\\\"what\\\":\\\"2\\\"}', b'1', '2025-12-15 21:24:36', '1', '2023-01-28 11:45:04', '1', '2025-12-15 21:24:36', b'0', 1);\nINSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, 103, 2, 2, 'register', '系统消息', '你好，欢迎 哈哈 加入大家庭！', 2, '{\\\"name\\\":\\\"哈哈\\\"}', b'0', NULL, '1', '2023-01-28 21:02:20', '1', '2023-01-28 21:02:20', b'0', 1);\nINSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\\\"name\\\":\\\"芋艿\\\",\\\"what\\\":\\\"写代码\\\"}', b'1', '2025-12-08 17:25:28', '1', '2023-01-28 22:21:42', '1', '2025-12-08 17:25:28', b'0', 1);\nINSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\\\"name\\\":\\\"芋艿\\\",\\\"what\\\":\\\"写代码\\\"}', b'1', '2025-12-08 17:25:30', '1', '2023-01-28 22:22:07', '1', '2025-12-08 17:25:30', b'0', 1);\nINSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, 1, 2, 1, 'test', '123', '我是 2，我开始 3 了', 1, '{\\\"name\\\":\\\"2\\\",\\\"what\\\":\\\"3\\\"}', b'1', '2025-12-08 17:25:22', '1', '2023-01-28 23:45:21', '1', '2025-12-08 17:25:22', b'0', 1);\nINSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8, 1, 2, 2, 'register', '系统消息', '你好，欢迎 123 加入大家庭！', 2, '{\\\"name\\\":\\\"123\\\"}', b'1', '2025-12-08 16:46:01', '1', '2023-01-28 23:50:21', '1', '2025-12-08 16:46:01', b'0', 1);\nINSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-28 08:35:46提现￥0.09元的申请已通过审核', 2, '{\\\"reason\\\":null,\\\"createTime\\\":\\\"2023-09-28 08:35:46\\\",\\\"price\\\":\\\"0.09\\\"}', b'0', NULL, '1', '2023-09-28 16:36:22', '1', '2023-09-28 16:36:22', b'0', 1);\nINSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (10, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-30 20:59:40提现￥1.00元的申请已通过审核', 2, '{\\\"reason\\\":null,\\\"createTime\\\":\\\"2023-09-30 20:59:40\\\",\\\"price\\\":\\\"1.00\\\"}', b'0', NULL, '1', '2023-10-03 12:11:34', '1', '2023-10-03 12:11:34', b'0', 1);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_notify_template\n-- ----------------------------\nDROP TABLE IF EXISTS `system_notify_template`;\nCREATE TABLE `system_notify_template`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `name` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板名称',\n  `code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模版编码',\n  `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发送人名称',\n  `content` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模版内容',\n  `type` tinyint NOT NULL COMMENT '类型',\n  `params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '参数数组',\n  `status` tinyint NOT NULL COMMENT '状态',\n  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '站内信模板表';\n\n-- ----------------------------\n-- Records of system_notify_template\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_oauth2_access_token\n-- ----------------------------\nDROP TABLE IF EXISTS `system_oauth2_access_token`;\nCREATE TABLE `system_oauth2_access_token`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `user_id` bigint NOT NULL COMMENT '用户编号',\n  `user_type` tinyint NOT NULL COMMENT '用户类型',\n  `user_info` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户信息',\n  `access_token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '访问令牌',\n  `refresh_token` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '刷新令牌',\n  `client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端编号',\n  `scopes` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '授权范围',\n  `expires_time` datetime NOT NULL COMMENT '过期时间',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE,\n  INDEX `idx_access_token`(`access_token` ASC) USING BTREE,\n  INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 47630 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';\n\n-- ----------------------------\n-- Records of system_oauth2_access_token\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_oauth2_approve\n-- ----------------------------\nDROP TABLE IF EXISTS `system_oauth2_approve`;\nCREATE TABLE `system_oauth2_approve`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `user_id` bigint NOT NULL COMMENT '用户编号',\n  `user_type` tinyint NOT NULL COMMENT '用户类型',\n  `client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端编号',\n  `scope` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '授权范围',\n  `approved` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否接受',\n  `expires_time` datetime NOT NULL COMMENT '过期时间',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 84 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 批准表';\n\n-- ----------------------------\n-- Records of system_oauth2_approve\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_oauth2_client\n-- ----------------------------\nDROP TABLE IF EXISTS `system_oauth2_client`;\nCREATE TABLE `system_oauth2_client`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端编号',\n  `secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端密钥',\n  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名',\n  `logo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用图标',\n  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '应用描述',\n  `status` tinyint NOT NULL COMMENT '状态',\n  `access_token_validity_seconds` int NOT NULL COMMENT '访问令牌的有效期',\n  `refresh_token_validity_seconds` int NOT NULL COMMENT '刷新令牌的有效期',\n  `redirect_uris` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '可重定向的 URI 地址',\n  `authorized_grant_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '授权类型',\n  `scopes` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '授权范围',\n  `auto_approve_scopes` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '自动通过的授权范围',\n  `authorities` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '权限',\n  `resource_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '资源',\n  `additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '附加信息',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 43 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 客户端表';\n\n-- ----------------------------\n-- Records of system_oauth2_client\n-- ----------------------------\nBEGIN;\nINSERT 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');\nINSERT 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');\nINSERT 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');\nINSERT 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');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_oauth2_code\n-- ----------------------------\nDROP TABLE IF EXISTS `system_oauth2_code`;\nCREATE TABLE `system_oauth2_code`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `user_id` bigint NOT NULL COMMENT '用户编号',\n  `user_type` tinyint NOT NULL COMMENT '用户类型',\n  `code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '授权码',\n  `client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端编号',\n  `scopes` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '授权范围',\n  `expires_time` datetime NOT NULL COMMENT '过期时间',\n  `redirect_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '可重定向的 URI 地址',\n  `state` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '状态',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 155 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 授权码表';\n\n-- ----------------------------\n-- Records of system_oauth2_code\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_oauth2_refresh_token\n-- ----------------------------\nDROP TABLE IF EXISTS `system_oauth2_refresh_token`;\nCREATE TABLE `system_oauth2_refresh_token`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `user_id` bigint NOT NULL COMMENT '用户编号',\n  `refresh_token` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '刷新令牌',\n  `user_type` tinyint NOT NULL COMMENT '用户类型',\n  `client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端编号',\n  `scopes` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '授权范围',\n  `expires_time` datetime NOT NULL COMMENT '过期时间',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 2501 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';\n\n-- ----------------------------\n-- Records of system_oauth2_refresh_token\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_operate_log\n-- ----------------------------\nDROP TABLE IF EXISTS `system_operate_log`;\nCREATE TABLE `system_operate_log`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',\n  `trace_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '链路追踪编号',\n  `user_id` bigint NOT NULL COMMENT '用户编号',\n  `user_type` tinyint NOT NULL DEFAULT 0 COMMENT '用户类型',\n  `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作模块类型',\n  `sub_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作名',\n  `biz_id` bigint NOT NULL COMMENT '操作数据模块编号',\n  `action` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '操作内容',\n  `success` bit(1) NOT NULL DEFAULT b'1' COMMENT '操作结果',\n  `extra` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '拓展字段',\n  `request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '请求方法名',\n  `request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '请求地址',\n  `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户 IP',\n  `user_agent` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '浏览器 UA',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 9193 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本';\n\n-- ----------------------------\n-- Records of system_operate_log\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_post\n-- ----------------------------\nDROP TABLE IF EXISTS `system_post`;\nCREATE TABLE `system_post`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '岗位ID',\n  `code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '岗位编码',\n  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '岗位名称',\n  `sort` int NOT NULL COMMENT '显示顺序',\n  `status` tinyint NOT NULL COMMENT '状态（0正常 1停用）',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '岗位信息表';\n\n-- ----------------------------\n-- Records of system_post\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_post` (`id`, `code`, `name`, `sort`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 'se', '项目经理', 2, 0, '', 'admin', '2021-01-05 17:03:48', '1', '2025-12-15 22:38:43', b'0', 1);\nINSERT INTO `system_post` (`id`, `code`, `name`, `sort`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, 'user', '普通员工', 4, 0, '111222', 'admin', '2021-01-05 17:03:48', '1', '2025-03-24 21:32:40', b'0', 1);\nINSERT INTO `system_post` (`id`, `code`, `name`, `sort`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, 'HR', '人力资源', 5, 0, '`', '1', '2024-03-24 20:45:40', '1', '2025-03-29 19:08:10', b'0', 1);\nINSERT INTO `system_post` (`id`, `code`, `name`, `sort`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, 'test', '测试', 10, 0, NULL, '1', '2025-09-02 08:45:57', '1', '2025-09-02 08:45:57', b'0', 1);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_role\n-- ----------------------------\nDROP TABLE IF EXISTS `system_role`;\nCREATE TABLE `system_role`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',\n  `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色名称',\n  `code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色权限字符串',\n  `sort` int NOT NULL COMMENT '显示顺序',\n  `data_scope` tinyint NOT NULL DEFAULT 1 COMMENT '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）',\n  `data_scope_dept_ids` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '数据范围(指定部门数组)',\n  `status` tinyint NOT NULL COMMENT '角色状态（0正常 1停用）',\n  `type` tinyint NOT NULL COMMENT '角色类型',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 160 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色信息表';\n\n-- ----------------------------\n-- Records of system_role\n-- ----------------------------\nBEGIN;\nINSERT 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 (1, '超级管理员', 'super_admin', 1, 1, '', 0, 1, '超级管理员', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:21', b'0', 1);\nINSERT 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 (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:20', b'0', 1);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_role_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `system_role_menu`;\nCREATE TABLE `system_role_menu`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增编号',\n  `role_id` bigint NOT NULL COMMENT '角色ID',\n  `menu_id` bigint NOT NULL COMMENT '菜单ID',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 6352 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';\n\n-- ----------------------------\n-- Records of system_role_menu\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (263, 109, 1, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (434, 2, 1, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (454, 2, 1093, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (455, 2, 1094, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (460, 2, 1100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (467, 2, 1107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (476, 2, 1117, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (477, 2, 100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (478, 2, 101, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (479, 2, 102, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (480, 2, 1126, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (481, 2, 103, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (483, 2, 104, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (485, 2, 105, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (488, 2, 107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (490, 2, 108, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (492, 2, 109, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (498, 2, 1138, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (523, 2, 1224, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (524, 2, 1225, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (541, 2, 500, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (543, 2, 501, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (675, 2, 2, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (689, 2, 1077, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (690, 2, 1078, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (692, 2, 1083, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (693, 2, 1084, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (699, 2, 1090, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (703, 2, 106, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (704, 2, 110, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (705, 2, 111, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1732, 109, 1064, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1733, 109, 1001, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1734, 109, 1065, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1735, 109, 1002, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1736, 109, 1003, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1737, 109, 1004, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1738, 109, 1005, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1739, 109, 1006, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1740, 109, 1007, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1741, 109, 1008, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1742, 109, 1009, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1766, 109, 1064, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1767, 109, 1001, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1768, 109, 1065, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1769, 109, 1002, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1770, 109, 1003, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1771, 109, 1004, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1772, 109, 1005, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1773, 109, 1006, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1774, 109, 1007, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1775, 109, 1008, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1776, 109, 1009, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1800, 109, 1064, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1801, 109, 1001, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1802, 109, 1065, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1803, 109, 1002, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1804, 109, 1003, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1805, 109, 1004, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1806, 109, 1005, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1807, 109, 1006, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1808, 109, 1007, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1809, 109, 1008, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1810, 109, 1009, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1994, 2, 1027, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1995, 2, 1028, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1996, 2, 1029, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1997, 2, 1030, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1998, 2, 1031, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1999, 2, 1032, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2000, 2, 1033, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2001, 2, 1034, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2002, 2, 1035, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2003, 2, 1036, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2004, 2, 1037, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2005, 2, 1038, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2006, 2, 1039, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2007, 2, 1040, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2008, 2, 1042, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2009, 2, 1043, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2010, 2, 1045, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2011, 2, 1046, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2012, 2, 1048, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2013, 2, 1050, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2014, 2, 1051, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2015, 2, 1052, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2016, 2, 1053, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2017, 2, 1054, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2018, 2, 1056, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2019, 2, 1057, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2020, 2, 1058, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2021, 2, 2083, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2022, 2, 1059, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2023, 2, 1060, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2024, 2, 1063, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2025, 2, 1064, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2026, 2, 1065, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2027, 2, 1066, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2028, 2, 1067, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2029, 2, 1070, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2034, 2, 1075, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2036, 2, 1082, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2037, 2, 1085, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2038, 2, 1086, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2039, 2, 1087, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2040, 2, 1088, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2041, 2, 1089, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2042, 2, 1091, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2043, 2, 1092, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2044, 2, 1095, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2045, 2, 1096, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2046, 2, 1097, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2047, 2, 1098, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2048, 2, 1101, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2049, 2, 1102, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2050, 2, 1103, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2051, 2, 1104, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2052, 2, 1105, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2053, 2, 1106, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2054, 2, 1108, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2055, 2, 1109, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2061, 2, 1127, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2062, 2, 1128, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2063, 2, 1129, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2064, 2, 1130, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2066, 2, 1132, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2067, 2, 1133, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2068, 2, 1134, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2069, 2, 1135, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2070, 2, 1136, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2071, 2, 1137, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2072, 2, 114, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2073, 2, 1139, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2074, 2, 115, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2075, 2, 1140, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2076, 2, 116, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2077, 2, 1141, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2078, 2, 1142, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2079, 2, 1143, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2080, 2, 1150, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2081, 2, 1161, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2082, 2, 1162, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2086, 2, 1166, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2087, 2, 1173, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2088, 2, 1174, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2092, 2, 1178, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2099, 2, 1226, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2100, 2, 1227, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2101, 2, 1228, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2102, 2, 1229, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2103, 2, 1237, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2104, 2, 1238, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2105, 2, 1239, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2106, 2, 1240, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2107, 2, 1241, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2108, 2, 1242, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2109, 2, 1243, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2116, 2, 1254, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2117, 2, 1255, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2118, 2, 1256, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2119, 2, 1257, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2120, 2, 1258, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2121, 2, 1259, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2122, 2, 1260, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2123, 2, 1261, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2124, 2, 1263, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2125, 2, 1264, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2126, 2, 1265, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2127, 2, 1266, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2128, 2, 1267, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2129, 2, 1001, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2130, 2, 1002, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2131, 2, 1003, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2132, 2, 1004, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2133, 2, 1005, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2134, 2, 1006, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2135, 2, 1007, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2136, 2, 1008, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2137, 2, 1009, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2138, 2, 1010, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2148, 2, 1020, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2149, 2, 1021, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2150, 2, 1022, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2151, 2, 1023, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2152, 2, 1281, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2153, 2, 1282, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2154, 2, 2000, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2155, 2, 2002, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2156, 2, 2003, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2157, 2, 2004, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2158, 2, 2005, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2159, 2, 2006, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2160, 2, 2008, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2161, 2, 2009, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2162, 2, 2010, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2163, 2, 2011, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2164, 2, 2012, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2170, 2, 2019, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2171, 2, 2020, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2172, 2, 2021, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2173, 2, 2022, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2174, 2, 2023, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2175, 2, 2025, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2177, 2, 2027, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2178, 2, 2028, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2179, 2, 2029, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2180, 2, 2014, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2181, 2, 2015, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2182, 2, 2016, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2183, 2, 2017, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2184, 2, 2018, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2929, 109, 1224, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2930, 109, 1225, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2931, 109, 1226, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2932, 109, 1227, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2933, 109, 1228, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2934, 109, 1229, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2935, 109, 1138, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2936, 109, 1139, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2937, 109, 1140, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2996, 109, 1033, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2997, 109, 1034, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2998, 109, 1035, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2999, 109, 1050, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3000, 109, 1051, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3001, 109, 1052, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3002, 109, 1053, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3003, 109, 1054, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3004, 109, 1056, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3005, 109, 1057, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3006, 109, 1058, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3007, 109, 1059, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3008, 109, 1060, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3009, 109, 1066, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3010, 109, 1067, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3011, 109, 1070, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3012, 109, 1075, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3014, 109, 1077, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3015, 109, 1078, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3016, 109, 1082, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3017, 109, 1083, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3018, 109, 1084, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3019, 109, 1085, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3020, 109, 1086, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3021, 109, 1087, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3022, 109, 1088, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3023, 109, 1089, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3024, 109, 1090, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3025, 109, 1091, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3026, 109, 1092, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3027, 109, 106, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3028, 109, 110, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3029, 109, 111, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3030, 109, 112, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3031, 109, 113, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3032, 109, 114, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3033, 109, 115, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3034, 109, 116, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3035, 109, 2472, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3036, 109, 2478, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3037, 109, 2479, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3038, 109, 2480, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3039, 109, 2481, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3040, 109, 2482, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3041, 109, 2483, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3042, 109, 2484, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3043, 109, 2485, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3044, 109, 2486, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3045, 109, 2487, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3046, 109, 2488, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3047, 109, 2489, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3048, 109, 2490, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3049, 109, 2491, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3050, 109, 2492, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3051, 109, 2493, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3052, 109, 2494, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3053, 109, 2495, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3054, 109, 2497, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3055, 109, 1237, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3056, 109, 1238, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3057, 109, 1239, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3058, 109, 1240, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3059, 109, 1241, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3060, 109, 1242, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3061, 109, 1243, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3062, 109, 2525, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3063, 109, 1255, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3064, 109, 1256, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3065, 109, 1257, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4166, 109, 1120, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4167, 109, 2713, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4168, 109, 2714, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4169, 109, 2715, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4170, 109, 2716, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4171, 109, 2717, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4172, 109, 2718, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4173, 109, 2720, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4174, 109, 1185, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4175, 109, 2721, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4176, 109, 1186, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4177, 109, 2722, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4178, 109, 1187, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4179, 109, 2723, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4180, 109, 1188, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4181, 109, 2724, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4182, 109, 1189, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4183, 109, 2725, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4184, 109, 1190, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4185, 109, 2726, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4186, 109, 1191, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4187, 109, 2727, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4188, 109, 1192, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4189, 109, 2728, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4190, 109, 1193, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4191, 109, 2729, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4192, 109, 1194, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4193, 109, 2730, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4194, 109, 1195, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4195, 109, 2731, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4197, 109, 2732, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4198, 109, 1197, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4199, 109, 2733, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4200, 109, 1198, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4201, 109, 2734, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4202, 109, 1199, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4203, 109, 2735, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4204, 109, 1200, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4205, 109, 1201, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4206, 109, 1202, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4207, 109, 1207, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4208, 109, 1208, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4209, 109, 1209, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4210, 109, 1210, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4211, 109, 1211, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4212, 109, 1212, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4213, 109, 1213, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4214, 109, 1215, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4215, 109, 1216, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4216, 109, 1217, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4217, 109, 1218, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4218, 109, 1219, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5782, 2, 2759, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5783, 2, 2362, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5784, 2, 2387, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6105, 155, 4033, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6106, 155, 4034, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6107, 155, 4035, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6108, 155, 4036, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6109, 155, 4037, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6110, 155, 4038, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6111, 155, 4039, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6112, 155, 4040, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6113, 155, 4041, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6114, 155, 4042, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6115, 155, 4043, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6116, 155, 4044, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6117, 155, 4045, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6119, 155, 4001, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6120, 155, 4002, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6121, 155, 4003, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6122, 155, 4004, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6123, 155, 4005, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6124, 155, 4006, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6125, 155, 4007, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6126, 155, 4008, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6127, 155, 4009, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6128, 155, 4010, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6129, 155, 4011, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6130, 155, 4012, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6131, 155, 4013, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6132, 155, 4014, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6133, 155, 4015, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6134, 155, 4016, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6135, 155, 4017, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6136, 155, 4018, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6137, 155, 4031, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6139, 109, 1117, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6140, 109, 1126, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6141, 109, 1127, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6142, 109, 1128, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6143, 109, 1129, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6144, 109, 1130, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6145, 109, 1132, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6146, 109, 1133, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6147, 109, 1134, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6148, 109, 1135, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6149, 109, 1136, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6150, 109, 1137, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6151, 109, 2161, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6152, 109, 1150, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6153, 109, 1161, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6154, 109, 1162, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6155, 109, 1166, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6156, 109, 1173, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6157, 109, 1174, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6158, 109, 1178, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6159, 109, 2745, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6160, 109, 2746, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6161, 109, 2747, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6162, 109, 2748, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6163, 109, 2301, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6164, 109, 2302, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6165, 109, 5011, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6166, 109, 5012, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6167, 109, 2549, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6168, 109, 2550, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6169, 109, 2551, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6170, 109, 2552, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6171, 109, 2553, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6172, 109, 2554, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6173, 109, 2555, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6174, 109, 2556, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6220, 109, 2275, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6221, 109, 2276, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6222, 109, 2277, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6223, 109, 2281, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6224, 109, 2282, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6225, 109, 2283, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6226, 109, 2284, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6227, 109, 2285, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6228, 109, 2287, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6229, 109, 2288, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6230, 109, 2293, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6231, 109, 2294, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6232, 109, 2297, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6233, 109, 2300, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6234, 109, 2317, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6235, 109, 2318, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6236, 109, 2319, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6237, 109, 2320, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6238, 109, 2321, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6239, 109, 2322, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6240, 109, 2323, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6241, 109, 2324, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6242, 109, 2325, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6243, 109, 2326, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6244, 109, 2327, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6245, 109, 2328, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6246, 109, 2329, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6247, 109, 2330, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6248, 109, 2331, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6249, 109, 2332, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6250, 109, 2333, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6251, 109, 2334, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6296, 2, 1120, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6297, 2, 2713, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6298, 2, 2714, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6299, 2, 2715, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6300, 2, 2716, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6301, 2, 2717, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6302, 2, 2718, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6303, 2, 2720, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6304, 2, 1185, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6305, 2, 2721, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6306, 2, 1186, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6307, 2, 2722, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6308, 2, 1187, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6309, 2, 2723, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6310, 2, 1188, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6311, 2, 2724, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6312, 2, 1189, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6313, 2, 2725, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6314, 2, 1190, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6315, 2, 2726, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6316, 2, 1191, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6317, 2, 2727, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6318, 2, 1192, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6319, 2, 2728, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6320, 2, 1193, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6321, 2, 2729, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6322, 2, 1194, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6323, 2, 2730, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6324, 2, 1195, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6325, 2, 2731, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6326, 2, 2732, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6327, 2, 1197, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6328, 2, 2733, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6329, 2, 1198, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6330, 2, 2734, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6331, 2, 1199, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6332, 2, 2735, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6333, 2, 1200, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6334, 2, 1201, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6335, 2, 1202, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6336, 2, 1207, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6337, 2, 1208, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6338, 2, 1209, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6339, 2, 1210, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6340, 2, 1211, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6341, 2, 1212, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6342, 2, 1213, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6343, 2, 1215, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6344, 2, 1216, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6345, 2, 1217, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6346, 2, 1218, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6347, 2, 1219, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6348, 2, 1220, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6349, 2, 1221, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6350, 2, 1222, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nINSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6351, 2, 2913, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_sms_channel\n-- ----------------------------\nDROP TABLE IF EXISTS `system_sms_channel`;\nCREATE TABLE `system_sms_channel`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `signature` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信签名',\n  `code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '渠道编码',\n  `status` tinyint NOT NULL COMMENT '开启状态',\n  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `api_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信 API 的账号',\n  `api_secret` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '短信 API 的秘钥',\n  `callback_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '短信发送回调 URL',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信渠道';\n\n-- ----------------------------\n-- Records of system_sms_channel\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_sms_channel` (`id`, `signature`, `code`, `status`, `remark`, `api_key`, `api_secret`, `callback_url`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'Ballcat', 'ALIYUN', 0, '你要改哦，只有我可以用！！！！', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', '2021-03-31 11:53:10', '1', '2024-08-04 08:53:26', b'0');\nINSERT INTO `system_sms_channel` (`id`, `signature`, `code`, `status`, `remark`, `api_key`, `api_secret`, `callback_url`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '测试渠道', 'DEBUG_DING_TALK', 0, '123', '696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859', 'SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67', NULL, '1', '2021-04-13 00:23:14', '1', '2022-03-27 20:29:49', b'0');\nINSERT INTO `system_sms_channel` (`id`, `signature`, `code`, `status`, `remark`, `api_key`, `api_secret`, `callback_url`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7, 'mock腾讯云', 'TENCENT', 0, '123', '1 2', '2 3', '', '1', '2024-09-30 08:53:45', '1', '2025-12-20 11:30:18', b'0');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_sms_code\n-- ----------------------------\nDROP TABLE IF EXISTS `system_sms_code`;\nCREATE TABLE `system_sms_code`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '手机号',\n  `code` varchar(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '验证码',\n  `create_ip` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建 IP',\n  `scene` tinyint NOT NULL COMMENT '发送场景',\n  `today_index` tinyint NOT NULL COMMENT '今日发送的第几条',\n  `used` tinyint NOT NULL COMMENT '是否使用',\n  `used_time` datetime NULL DEFAULT NULL COMMENT '使用时间',\n  `used_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '使用 IP',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE,\n  INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'\n) ENGINE = InnoDB AUTO_INCREMENT = 690 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';\n\n-- ----------------------------\n-- Records of system_sms_code\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_sms_log\n-- ----------------------------\nDROP TABLE IF EXISTS `system_sms_log`;\nCREATE TABLE `system_sms_log`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `channel_id` bigint NOT NULL COMMENT '短信渠道编号',\n  `channel_code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信渠道编码',\n  `template_id` bigint NOT NULL COMMENT '模板编号',\n  `template_code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板编码',\n  `template_type` tinyint NOT NULL COMMENT '短信类型',\n  `template_content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信内容',\n  `template_params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信参数',\n  `api_template_id` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信 API 的模板编号',\n  `mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '手机号',\n  `user_id` bigint NULL DEFAULT NULL COMMENT '用户编号',\n  `user_type` tinyint NULL DEFAULT NULL COMMENT '用户类型',\n  `send_status` tinyint NOT NULL DEFAULT 0 COMMENT '发送状态',\n  `send_time` datetime NULL DEFAULT NULL COMMENT '发送时间',\n  `api_send_code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '短信 API 发送结果的编码',\n  `api_send_msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '短信 API 发送失败的提示',\n  `api_request_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '短信 API 发送返回的唯一请求 ID',\n  `api_serial_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '短信 API 发送返回的序号',\n  `receive_status` tinyint NOT NULL DEFAULT 0 COMMENT '接收状态',\n  `receive_time` datetime NULL DEFAULT NULL COMMENT '接收时间',\n  `api_receive_code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'API 接收结果的编码',\n  `api_receive_msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'API 接收结果的说明',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 1549 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';\n\n-- ----------------------------\n-- Records of system_sms_log\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_sms_template\n-- ----------------------------\nDROP TABLE IF EXISTS `system_sms_template`;\nCREATE TABLE `system_sms_template`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `type` tinyint NOT NULL COMMENT '模板类型',\n  `status` tinyint NOT NULL COMMENT '开启状态',\n  `code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板编码',\n  `name` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板名称',\n  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板内容',\n  `params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '参数数组',\n  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `api_template_id` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信 API 的模板编号',\n  `channel_id` bigint NOT NULL COMMENT '短信渠道编号',\n  `channel_code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信渠道编码',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信模板';\n\n-- ----------------------------\n-- Records of system_sms_template\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 1, 0, 'test_01', '测试验证码短信', '正在进行登录操作{operation}，您的验证码是{code}', '[\\\"operation\\\",\\\"code\\\"]', '测试备注', '4383920', 4, 'DEBUG_DING_TALK', '', '2021-03-31 10:49:38', '1', '2024-08-18 11:57:18', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3, 1, 0, 'test_02', '公告通知', '您的验证码{code}，该验证码5分钟内有效，请勿泄漏于他人！', '[\\\"code\\\"]', NULL, 'SMS_207945135', 2, 'ALIYUN', '', '2021-03-31 11:56:30', '1', '2021-04-10 01:22:02', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6, 3, 0, 'test-01', '测试模板', '哈哈哈 {name}', '[\\\"name\\\"]', 'f哈哈哈', '4383920', 4, 'DEBUG_DING_TALK', '1', '2021-04-10 01:07:21', '1', '2024-08-18 11:57:07', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7, 3, 0, 'test-04', '测试下', '老鸡{name}，牛逼{code}', '[\\\"name\\\",\\\"code\\\"]', '哈哈哈哈', 'suibian', 7, 'DEBUG_DING_TALK', '1', '2021-04-13 00:29:53', '1', '2024-09-30 00:56:24', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8, 1, 0, 'user-sms-login', '前台用户短信登录', '您的验证码是{code}', '[\\\"code\\\"]', NULL, '4372216', 4, 'DEBUG_DING_TALK', '1', '2021-10-11 08:10:00', '1', '2024-08-18 11:57:06', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (9, 2, 0, 'bpm_task_assigned', '【工作流】任务被分配', '您收到了一条新的待办任务：{processInstanceName}-{taskName}，申请人：{startUserNickname}，处理链接：{detailUrl}', '[\\\"processInstanceName\\\",\\\"taskName\\\",\\\"startUserNickname\\\",\\\"detailUrl\\\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-21 22:31:19', '1', '2022-01-22 00:03:36', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (10, 2, 0, 'bpm_process_instance_reject', '【工作流】流程被不通过', '您的流程被审批不通过：{processInstanceName}，原因：{reason}，查看链接：{detailUrl}', '[\\\"processInstanceName\\\",\\\"reason\\\",\\\"detailUrl\\\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:03:31', '1', '2022-05-01 12:33:14', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (11, 2, 0, 'bpm_process_instance_approve', '【工作流】流程被通过', '您的流程被审批通过：{processInstanceName}，查看链接：{detailUrl}', '[\\\"processInstanceName\\\",\\\"detailUrl\\\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:04:31', '1', '2022-03-27 20:32:21', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12, 2, 0, 'demo', '演示模板', '我就是测试一下下', '[]', NULL, 'biubiubiu', 4, 'DEBUG_DING_TALK', '1', '2022-04-10 23:22:49', '1', '2024-08-18 11:57:04', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (14, 1, 0, 'user-update-mobile', '会员用户 - 修改手机', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\\\"code\\\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:04', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, 1, 0, 'user-update-password', '会员用户 - 修改密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\\\"code\\\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:18', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 1, 0, 'user-reset-password', '会员用户 - 重置密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\\\"code\\\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-12-02 22:35:27', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, 2, 0, 'bpm_task_timeout', '【工作流】任务审批超时', '您收到了一条超时的待办任务：{processInstanceName}-{taskName}，处理链接：{detailUrl}', '[\\\"processInstanceName\\\",\\\"taskName\\\",\\\"detailUrl\\\"]', '', 'X', 4, 'DEBUG_DING_TALK', '1', '2024-08-16 21:59:15', '1', '2024-08-16 21:59:34', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (18, 1, 0, 'admin-reset-password', '后台用户 - 忘记密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\\\"code\\\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2025-03-16 14:19:34', '1', '2025-03-16 14:19:45', b'0');\nINSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (19, 1, 0, 'admin-sms-login', '后台用户短信登录', '您的验证码是{code}', '[\\\"code\\\"]', '', '4372216', 4, 'DEBUG_DING_TALK', '1', '2025-04-08 09:36:03', '1', '2025-04-08 09:36:17', b'0');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_social_client\n-- ----------------------------\nDROP TABLE IF EXISTS `system_social_client`;\nCREATE TABLE `system_social_client`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名',\n  `social_type` tinyint NOT NULL COMMENT '社交平台的类型',\n  `user_type` tinyint NOT NULL COMMENT '用户类型',\n  `client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端编号',\n  `client_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端密钥',\n  `agent_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '代理编号',\n  `public_key` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'publicKey 公钥',\n  `status` tinyint NOT NULL COMMENT '状态',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 48 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交客户端表';\n\n-- ----------------------------\n-- Records of system_social_client\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_social_client` (`id`, `name`, `social_type`, `user_type`, `client_id`, `client_secret`, `agent_id`, `public_key`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '钉钉', 20, 2, 'dingvrnreaje3yqvzhxg', 'i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI', NULL, NULL, 0, '', '2023-10-18 11:21:18', '1', '2023-12-20 21:28:26', b'1', 1);\nINSERT INTO `system_social_client` (`id`, `name`, `social_type`, `user_type`, `client_id`, `client_secret`, `agent_id`, `public_key`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '钉钉（王土豆）', 20, 2, 'dingtsu9hpepjkbmthhw', 'FP_bnSq_HAHKCSncmJjw5hxhnzs6vaVDSZZn3egj6rdqTQ_hu5tQVJyLMpgCakdP', NULL, NULL, 0, '', '2023-10-18 11:21:18', '', '2023-12-20 21:28:26', b'1', 121);\nINSERT INTO `system_social_client` (`id`, `name`, `social_type`, `user_type`, `client_id`, `client_secret`, `agent_id`, `public_key`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, '微信公众号', 31, 1, 'wx5b23ba7a5589ecbb', '2a7b3b20c537e52e74afd395eb85f61f', NULL, NULL, 0, '', '2023-10-18 16:07:46', '1', '2023-12-20 21:28:23', b'1', 1);\nINSERT INTO `system_social_client` (`id`, `name`, `social_type`, `user_type`, `client_id`, `client_secret`, `agent_id`, `public_key`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (43, '微信小程序', 34, 1, 'wx63c280fe3248a3e7', '6f270509224a7ae1296bbf1c8cb97aed', NULL, NULL, 0, '', '2023-10-19 13:37:41', '1', '2023-12-20 21:28:25', b'1', 1);\nINSERT INTO `system_social_client` (`id`, `name`, `social_type`, `user_type`, `client_id`, `client_secret`, `agent_id`, `public_key`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (44, '1', 10, 1, '2', '3', NULL, NULL, 0, '1', '2025-04-06 20:36:28', '1', '2025-04-06 20:43:12', b'1', 1);\nINSERT INTO `system_social_client` (`id`, `name`, `social_type`, `user_type`, `client_id`, `client_secret`, `agent_id`, `public_key`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (45, '1', 10, 1, '2', '3', NULL, NULL, 1, '1', '2025-09-06 20:26:15', '1', '2025-09-06 20:27:55', b'1', 1);\nINSERT INTO `system_social_client` (`id`, `name`, `social_type`, `user_type`, `client_id`, `client_secret`, `agent_id`, `public_key`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (46, '1', 10, 1, '2', '3', NULL, NULL, 0, '1', '2025-11-29 16:04:23', '1', '2025-11-29 16:04:26', b'1', 1);\nINSERT INTO `system_social_client` (`id`, `name`, `social_type`, `user_type`, `client_id`, `client_secret`, `agent_id`, `public_key`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (47, '123', 10, 1, '1', '2', '3', NULL, 0, '1', '2025-12-21 10:27:02', '1', '2025-12-21 10:27:20', b'1', 1);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_social_user\n-- ----------------------------\nDROP TABLE IF EXISTS `system_social_user`;\nCREATE TABLE `system_social_user`  (\n  `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键(自增策略)',\n  `type` tinyint NOT NULL COMMENT '社交平台的类型',\n  `openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '社交 openid',\n  `token` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '社交 token',\n  `raw_token_info` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '原始 Token 数据，一般是 JSON 格式',\n  `nickname` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户昵称',\n  `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户头像',\n  `raw_user_info` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '原始用户数据，一般是 JSON 格式',\n  `code` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '最后一次的认证 code',\n  `state` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '最后一次的认证 state',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交用户表';\n\n-- ----------------------------\n-- Records of system_social_user\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_social_user_bind\n-- ----------------------------\nDROP TABLE IF EXISTS `system_social_user_bind`;\nCREATE TABLE `system_social_user_bind`  (\n  `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键(自增策略)',\n  `user_id` bigint NOT NULL COMMENT '用户编号',\n  `user_type` tinyint NOT NULL COMMENT '用户类型',\n  `social_type` tinyint NOT NULL COMMENT '社交平台的类型',\n  `social_user_id` bigint NOT NULL COMMENT '社交用户的编号',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 165 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交绑定表';\n\n-- ----------------------------\n-- Records of system_social_user_bind\n-- ----------------------------\nBEGIN;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_tenant\n-- ----------------------------\nDROP TABLE IF EXISTS `system_tenant`;\nCREATE TABLE `system_tenant`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '租户编号',\n  `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '租户名',\n  `contact_user_id` bigint NULL DEFAULT NULL COMMENT '联系人的用户编号',\n  `contact_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系人',\n  `contact_mobile` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '联系手机',\n  `status` tinyint NOT NULL DEFAULT 0 COMMENT '租户状态',\n  `websites` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '绑定域名数组',\n  `package_id` bigint NOT NULL COMMENT '租户套餐编号',\n  `expire_time` datetime NOT NULL COMMENT '过期时间',\n  `account_count` int NOT NULL COMMENT '账号数量',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 162 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '租户表';\n\n-- ----------------------------\n-- Records of system_tenant\n-- ----------------------------\nBEGIN;\nINSERT 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');\nINSERT 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');\nINSERT 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');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_tenant_package\n-- ----------------------------\nDROP TABLE IF EXISTS `system_tenant_package`;\nCREATE TABLE `system_tenant_package`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '套餐编号',\n  `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '套餐名',\n  `status` tinyint NOT NULL DEFAULT 0 COMMENT '租户状态（0正常 1停用）',\n  `remark` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '备注',\n  `menu_ids` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联的菜单编号',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 113 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '租户套餐表';\n\n-- ----------------------------\n-- Records of system_tenant_package\n-- ----------------------------\nBEGIN;\nINSERT 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');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_user_post\n-- ----------------------------\nDROP TABLE IF EXISTS `system_user_post`;\nCREATE TABLE `system_user_post`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `user_id` bigint NOT NULL DEFAULT 0 COMMENT '用户ID',\n  `post_id` bigint NOT NULL DEFAULT 0 COMMENT '岗位ID',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 130 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户岗位表';\n\n-- ----------------------------\n-- Records of system_user_post\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (112, 1, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (113, 100, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (115, 104, 1, '1', '2022-05-16 19:36:28', '1', '2022-05-16 19:36:28', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (116, 117, 2, '1', '2022-07-09 17:40:26', '1', '2022-07-09 17:40:26', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (117, 118, 1, '1', '2022-07-09 17:44:44', '1', '2022-07-09 17:44:44', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (119, 114, 5, '1', '2024-03-24 20:45:51', '1', '2024-03-24 20:45:51', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (123, 115, 1, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (124, 115, 2, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (125, 1, 2, '1', '2024-07-13 22:31:39', '1', '2024-07-13 22:31:39', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (128, 139, 2, '1', '2025-12-05 21:43:27', '1', '2025-12-05 21:43:27', b'0', 1);\nINSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (129, 139, 4, '1', '2025-12-05 21:43:27', '1', '2025-12-05 21:43:27', b'0', 1);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_user_role\n-- ----------------------------\nDROP TABLE IF EXISTS `system_user_role`;\nCREATE TABLE `system_user_role`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增编号',\n  `user_id` bigint NOT NULL COMMENT '用户ID',\n  `role_id` bigint NOT NULL COMMENT '角色ID',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 55 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表';\n\n-- ----------------------------\n-- Records of system_user_role\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 1, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:17', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 2, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, 100, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:12', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, 100, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:11', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (10, 103, 1, '1', '2022-01-11 13:19:45', '1', '2022-01-11 13:19:45', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (14, 110, 109, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', b'0', 121);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (15, 111, 110, '110', '2022-02-23 13:14:38', '110', '2022-02-23 13:14:38', b'0', 121);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (16, 113, 111, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (18, 1, 2, '1', '2022-05-12 20:39:29', '1', '2022-05-12 20:39:29', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (47, 104, 2, '1', '2025-01-04 10:40:33', '1', '2025-01-04 10:40:33', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (48, 100, 155, '1', '2025-04-04 10:41:14', '1', '2025-04-04 10:41:14', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (49, 142, 1, '1', '2025-07-23 09:11:42', '1', '2025-07-23 09:11:42', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (50, 142, 2, '1', '2025-10-07 20:50:37', '1', '2025-10-07 20:50:37', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (51, 139, 1, '1', '2025-12-05 22:36:57', '1', '2025-12-05 22:36:57', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (52, 139, 2, '1', '2025-12-05 22:37:00', '1', '2025-12-05 22:37:00', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (53, 114, 2, '1', '2026-01-04 18:15:40', '1', '2026-01-04 18:15:40', b'0', 1);\nINSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (54, 114, 3, '1', '2026-01-04 18:16:19', '1', '2026-01-04 18:16:19', b'0', 1);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for system_users\n-- ----------------------------\nDROP TABLE IF EXISTS `system_users`;\nCREATE TABLE `system_users`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',\n  `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户账号',\n  `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '密码',\n  `nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户昵称',\n  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',\n  `dept_id` bigint NULL DEFAULT NULL COMMENT '部门ID',\n  `post_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '岗位编号数组',\n  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '用户邮箱',\n  `mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '手机号码',\n  `sex` tinyint NULL DEFAULT 0 COMMENT '用户性别',\n  `avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '头像地址',\n  `status` tinyint NOT NULL DEFAULT 0 COMMENT '帐号状态（0正常 1停用）',\n  `login_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '最后登录IP',\n  `login_date` datetime NULL DEFAULT NULL COMMENT '最后登录时间',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 145 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户信息表';\n\n-- ----------------------------\n-- Records of system_users\n-- ----------------------------\nBEGIN;\nINSERT 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-02-14 09:07:33', 'admin', '2021-01-05 17:03:47', NULL, '2026-02-14 09:07:33', b'0', 1);\nINSERT 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$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-12-15 21:47:26', '', '2021-01-07 09:07:17', NULL, '2025-12-15 21:47:26', b'0', 1);\nINSERT 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$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', '1', '2025-07-09 23:41:58', b'0', 1);\nINSERT 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-01-04 18:09:54', '', '2021-01-21 02:13:53', NULL, '2026-01-04 18:09:54', b'0', 1);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for yudao_demo01_contact\n-- ----------------------------\nDROP TABLE IF EXISTS `yudao_demo01_contact`;\nCREATE TABLE `yudao_demo01_contact`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',\n  `sex` tinyint(1) NOT NULL COMMENT '性别',\n  `birthday` datetime NOT NULL COMMENT '出生年',\n  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '简介',\n  `avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '头像',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '示例联系人表';\n\n-- ----------------------------\n-- Records of yudao_demo01_contact\n-- ----------------------------\nBEGIN;\nINSERT INTO `yudao_demo01_contact` (`id`, `name`, `sex`, `birthday`, `description`, `avatar`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '土豆', 2, '2023-11-07 00:00:00', '<p>天蚕土豆！呀</p>', 'http://127.0.0.1:48080/admin-api/infra/file/4/get/46f8fa1a37db3f3960d8910ff2fe3962ab3b2db87cf2f8ccb4dc8145b8bdf237.jpeg', '1', '2023-11-15 23:34:30', '1', '2023-11-15 23:47:39', b'0', 1);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for yudao_demo02_category\n-- ----------------------------\nDROP TABLE IF EXISTS `yudao_demo02_category`;\nCREATE TABLE `yudao_demo02_category`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',\n  `parent_id` bigint NOT NULL COMMENT '父级编号',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '示例分类表';\n\n-- ----------------------------\n-- Records of yudao_demo02_category\n-- ----------------------------\nBEGIN;\nINSERT INTO `yudao_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '土豆', 0, '1', '2023-11-15 23:34:30', '1', '2023-11-16 20:24:23', b'0', 1);\nINSERT INTO `yudao_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '番茄', 0, '1', '2023-11-16 20:24:00', '1', '2023-11-16 20:24:15', b'0', 1);\nINSERT INTO `yudao_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, '怪怪', 0, '1', '2023-11-16 20:24:32', '1', '2023-11-16 20:24:32', b'0', 1);\nINSERT INTO `yudao_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, '小番茄', 2, '1', '2023-11-16 20:24:39', '1', '2023-11-16 20:24:39', b'0', 1);\nINSERT INTO `yudao_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, '大番茄', 2, '1', '2023-11-16 20:24:46', '1', '2023-11-16 20:24:46', b'0', 1);\nINSERT INTO `yudao_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, '11', 3, '1', '2023-11-24 19:29:34', '1', '2023-11-24 19:29:34', b'0', 1);\nINSERT INTO `yudao_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, '1', 0, '1', '2025-10-01 09:19:20', '1', '2025-10-01 09:19:20', b'0', 1);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_course\n-- ----------------------------\nDROP TABLE IF EXISTS `yudao_demo03_course`;\nCREATE TABLE `yudao_demo03_course`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `student_id` bigint NOT NULL COMMENT '学生编号',\n  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',\n  `score` tinyint NOT NULL COMMENT '分数',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '学生课程表';\n\n-- ----------------------------\n-- Records of yudao_demo03_course\n-- ----------------------------\nBEGIN;\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 2, '语文', 66, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 2, '数学', 22, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (10, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (11, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (12, 2, '电脑', 33, '1', '2023-11-17 00:20:42', '1', '2023-11-16 16:20:45', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (13, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:26', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (14, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:49', b'1', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (15, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', b'0', 1);\nINSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (16, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', b'0', 1);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_grade\n-- ----------------------------\nDROP TABLE IF EXISTS `yudao_demo03_grade`;\nCREATE TABLE `yudao_demo03_grade`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `student_id` bigint NOT NULL COMMENT '学生编号',\n  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',\n  `teacher` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '班主任',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '学生班级表';\n\n-- ----------------------------\n-- Records of yudao_demo03_grade\n-- ----------------------------\nBEGIN;\nINSERT 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);\nINSERT 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);\nINSERT 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);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_student\n-- ----------------------------\nDROP TABLE IF EXISTS `yudao_demo03_student`;\nCREATE TABLE `yudao_demo03_student`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',\n  `sex` tinyint NOT NULL COMMENT '性别',\n  `birthday` datetime NOT NULL COMMENT '出生日期',\n  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '简介',\n  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '学生表';\n\n-- ----------------------------\n-- Records of yudao_demo03_student\n-- ----------------------------\nBEGIN;\nINSERT 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);\nINSERT 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);\nINSERT 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);\nCOMMIT;\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "sql/opengauss/quartz.sql",
    "content": "-- ----------------------------\n-- qrtz_blob_triggers\n-- ----------------------------\nCREATE TABLE qrtz_blob_triggers\n(\n    sched_name    varchar(120) NOT NULL,\n    trigger_name  varchar(190) NOT NULL,\n    trigger_group varchar(190) NOT NULL,\n    blob_data     bytea        NULL,\n    PRIMARY KEY (sched_name, trigger_name, trigger_group)\n);\n\nCREATE INDEX idx_qrtz_blob_triggers_sched_name ON qrtz_blob_triggers (sched_name, trigger_name, trigger_group);\n\n-- ----------------------------\n-- qrtz_calendars\n-- ----------------------------\nCREATE TABLE qrtz_calendars\n(\n    sched_name    varchar(120) NOT NULL,\n    calendar_name varchar(190) NOT NULL,\n    calendar      bytea        NOT NULL,\n    PRIMARY KEY (sched_name, calendar_name)\n);\n\n\n-- ----------------------------\n-- qrtz_cron_triggers\n-- ----------------------------\nCREATE TABLE qrtz_cron_triggers\n(\n    sched_name      varchar(120) NOT NULL,\n    trigger_name    varchar(190) NOT NULL,\n    trigger_group   varchar(190) NOT NULL,\n    cron_expression varchar(120) NOT NULL,\n    time_zone_id    varchar(80)  NULL DEFAULT NULL,\n    PRIMARY KEY (sched_name, trigger_name, trigger_group)\n);\n\n-- @formatter:off\nBEGIN;\nCOMMIT;\n-- @formatter:on\n\n-- ----------------------------\n-- qrtz_fired_triggers\n-- ----------------------------\nCREATE TABLE qrtz_fired_triggers\n(\n    sched_name        varchar(120) NOT NULL,\n    entry_id          varchar(95)  NOT NULL,\n    trigger_name      varchar(190) NOT NULL,\n    trigger_group     varchar(190) NOT NULL,\n    instance_name     varchar(190) NOT NULL,\n    fired_time        int8         NOT NULL,\n    sched_time        int8         NOT NULL,\n    priority          int4         NOT NULL,\n    state             varchar(16)  NOT NULL,\n    job_name          varchar(190) NULL DEFAULT NULL,\n    job_group         varchar(190) NULL DEFAULT NULL,\n    is_nonconcurrent  varchar(1)   NULL DEFAULT NULL,\n    requests_recovery varchar(1)   NULL DEFAULT NULL,\n    PRIMARY KEY (sched_name, entry_id)\n);\n\nCREATE INDEX idx_qrtz_ft_trig_inst_name ON qrtz_fired_triggers (sched_name, instance_name);\nCREATE INDEX idx_qrtz_ft_inst_job_req_rcvry ON qrtz_fired_triggers (sched_name, instance_name, requests_recovery);\nCREATE INDEX idx_qrtz_ft_j_g ON qrtz_fired_triggers (sched_name, job_name, job_group);\nCREATE INDEX idx_qrtz_ft_jg ON qrtz_fired_triggers (sched_name, job_group);\nCREATE INDEX idx_qrtz_ft_t_g ON qrtz_fired_triggers (sched_name, trigger_name, trigger_group);\nCREATE INDEX idx_qrtz_ft_tg ON qrtz_fired_triggers (sched_name, trigger_group);\n\n-- ----------------------------\n-- qrtz_job_details\n-- ----------------------------\nCREATE TABLE qrtz_job_details\n(\n    sched_name        varchar(120) NOT NULL,\n    job_name          varchar(190) NOT NULL,\n    job_group         varchar(190) NOT NULL,\n    description       varchar(250) NULL DEFAULT NULL,\n    job_class_name    varchar(250) NOT NULL,\n    is_durable        varchar(1)   NOT NULL,\n    is_nonconcurrent  varchar(1)   NOT NULL,\n    is_update_data    varchar(1)   NOT NULL,\n    requests_recovery varchar(1)   NOT NULL,\n    job_data          bytea        NULL,\n    PRIMARY KEY (sched_name, job_name, job_group)\n);\n\nCREATE INDEX idx_qrtz_j_req_recovery ON qrtz_job_details (sched_name, requests_recovery);\nCREATE INDEX idx_qrtz_j_grp ON qrtz_job_details (sched_name, job_group);\n\n-- @formatter:off\nBEGIN;\nCOMMIT;\n-- @formatter:on\n\n-- ----------------------------\n-- qrtz_locks\n-- ----------------------------\nCREATE TABLE qrtz_locks\n(\n    sched_name varchar(120) NOT NULL,\n    lock_name  varchar(40)  NOT NULL,\n    PRIMARY KEY (sched_name, lock_name)\n);\n\n-- @formatter:off\nBEGIN;\nCOMMIT;\n-- @formatter:on\n\n-- ----------------------------\n-- qrtz_paused_trigger_grps\n-- ----------------------------\nCREATE TABLE qrtz_paused_trigger_grps\n(\n    sched_name    varchar(120) NOT NULL,\n    trigger_group varchar(190) NOT NULL,\n    PRIMARY KEY (sched_name, trigger_group)\n);\n\n-- ----------------------------\n-- qrtz_scheduler_state\n-- ----------------------------\nCREATE TABLE qrtz_scheduler_state\n(\n    sched_name        varchar(120) NOT NULL,\n    instance_name     varchar(190) NOT NULL,\n    last_checkin_time int8         NOT NULL,\n    checkin_interval  int8         NOT NULL,\n    PRIMARY KEY (sched_name, instance_name)\n);\n\n-- @formatter:off\nBEGIN;\nCOMMIT;\n-- @formatter:on\n\n-- ----------------------------\n-- qrtz_simple_triggers\n-- ----------------------------\nCREATE TABLE qrtz_simple_triggers\n(\n    sched_name      varchar(120) NOT NULL,\n    trigger_name    varchar(190) NOT NULL,\n    trigger_group   varchar(190) NOT NULL,\n    repeat_count    int8         NOT NULL,\n    repeat_interval int8         NOT NULL,\n    times_triggered int8         NOT NULL,\n    PRIMARY KEY (sched_name, trigger_name, trigger_group)\n);\n\n-- ----------------------------\n-- qrtz_simprop_triggers\n-- ----------------------------\nCREATE TABLE qrtz_simprop_triggers\n(\n    sched_name    varchar(120)   NOT NULL,\n    trigger_name  varchar(190)   NOT NULL,\n    trigger_group varchar(190)   NOT NULL,\n    str_prop_1    varchar(512)   NULL DEFAULT NULL,\n    str_prop_2    varchar(512)   NULL DEFAULT NULL,\n    str_prop_3    varchar(512)   NULL DEFAULT NULL,\n    int_prop_1    int4           NULL DEFAULT NULL,\n    int_prop_2    int4           NULL DEFAULT NULL,\n    long_prop_1   int8           NULL DEFAULT NULL,\n    long_prop_2   int8           NULL DEFAULT NULL,\n    dec_prop_1    numeric(13, 4) NULL DEFAULT NULL,\n    dec_prop_2    numeric(13, 4) NULL DEFAULT NULL,\n    bool_prop_1   varchar(1)     NULL DEFAULT NULL,\n    bool_prop_2   varchar(1)     NULL DEFAULT NULL,\n    PRIMARY KEY (sched_name, trigger_name, trigger_group)\n);\n\n-- ----------------------------\n-- qrtz_triggers\n-- ----------------------------\nCREATE TABLE qrtz_triggers\n(\n    sched_name     varchar(120) NOT NULL,\n    trigger_name   varchar(190) NOT NULL,\n    trigger_group  varchar(190) NOT NULL,\n    job_name       varchar(190) NOT NULL,\n    job_group      varchar(190) NOT NULL,\n    description    varchar(250) NULL DEFAULT NULL,\n    next_fire_time int8         NULL DEFAULT NULL,\n    prev_fire_time int8         NULL DEFAULT NULL,\n    priority       int4         NULL DEFAULT NULL,\n    trigger_state  varchar(16)  NOT NULL,\n    trigger_type   varchar(8)   NOT NULL,\n    start_time     int8         NOT NULL,\n    end_time       int8         NULL DEFAULT NULL,\n    calendar_name  varchar(190) NULL DEFAULT NULL,\n    misfire_instr  int2         NULL DEFAULT NULL,\n    job_data       bytea        NULL,\n    PRIMARY KEY (sched_name, trigger_name, trigger_group)\n);\n\nCREATE INDEX idx_qrtz_t_j ON qrtz_triggers (sched_name, job_name, job_group);\nCREATE INDEX idx_qrtz_t_jg ON qrtz_triggers (sched_name, job_group);\nCREATE INDEX idx_qrtz_t_c ON qrtz_triggers (sched_name, calendar_name);\nCREATE INDEX idx_qrtz_t_g ON qrtz_triggers (sched_name, trigger_group);\nCREATE INDEX idx_qrtz_t_state ON qrtz_triggers (sched_name, trigger_state);\nCREATE INDEX idx_qrtz_t_n_state ON qrtz_triggers (sched_name, trigger_name, trigger_group, trigger_state);\nCREATE INDEX idx_qrtz_t_n_g_state ON qrtz_triggers (sched_name, trigger_group, trigger_state);\nCREATE INDEX idx_qrtz_t_next_fire_time ON qrtz_triggers (sched_name, next_fire_time);\nCREATE INDEX idx_qrtz_t_nft_st ON qrtz_triggers (sched_name, trigger_state, next_fire_time);\nCREATE INDEX idx_qrtz_t_nft_misfire ON qrtz_triggers (sched_name, misfire_instr, next_fire_time);\nCREATE INDEX idx_qrtz_t_nft_st_misfire ON qrtz_triggers (sched_name, misfire_instr, next_fire_time, trigger_state);\nCREATE INDEX idx_qrtz_t_nft_st_misfire_grp ON qrtz_triggers (sched_name, misfire_instr, next_fire_time, trigger_group,\n                                                             trigger_state);\n\n-- @formatter:off\nBEGIN;\nCOMMIT;\n-- @formatter:on\n\n\n-- ----------------------------\n-- FK: qrtz_blob_triggers\n-- ----------------------------\nALTER TABLE qrtz_blob_triggers\n    ADD CONSTRAINT qrtz_blob_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name,\n                                                                                                                             trigger_name,\n                                                                                                                             trigger_group);\n\n-- ----------------------------\n-- FK: qrtz_cron_triggers\n-- ----------------------------\nALTER TABLE qrtz_cron_triggers\n    ADD CONSTRAINT qrtz_cron_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, trigger_name, trigger_group);\n\n-- ----------------------------\n-- FK: qrtz_simple_triggers\n-- ----------------------------\nALTER TABLE qrtz_simple_triggers\n    ADD CONSTRAINT qrtz_simple_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name,\n                                                                                                                               trigger_name,\n                                                                                                                               trigger_group);\n\n-- ----------------------------\n-- FK: qrtz_simprop_triggers\n-- ----------------------------\nALTER TABLE qrtz_simprop_triggers\n    ADD CONSTRAINT qrtz_simprop_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, trigger_name, trigger_group);\n\n-- ----------------------------\n-- FK: qrtz_triggers\n-- ----------------------------\nALTER TABLE qrtz_triggers\n    ADD CONSTRAINT qrtz_triggers_ibfk_1 FOREIGN KEY (sched_name, job_name, job_group) REFERENCES qrtz_job_details (sched_name, job_name, job_group);\n"
  },
  {
    "path": "sql/opengauss/ruoyi-vue-pro.sql",
    "content": "/*\n Yudao Database Transfer Tool\n\n Source Server Type    : MySQL\n\n Target Server Type    : OpenGauss\n\n Date: 2025-05-22 21:40:02\n*/\n\n\n-- ----------------------------\n-- Table structure for dual\n-- ----------------------------\nDROP TABLE IF EXISTS dual;\nCREATE TABLE dual\n(\n    id int2\n);\n\nCOMMENT ON TABLE dual IS '数据库连接的表';\n\n-- ----------------------------\n-- Records of dual\n-- ----------------------------\n-- @formatter:off\nINSERT INTO dual VALUES (1);\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_api_access_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_api_access_log;\nCREATE TABLE infra_api_access_log\n(\n    id               int8         NOT NULL,\n    trace_id         varchar(64)  NULL     DEFAULT '',\n    user_id          int8         NOT NULL DEFAULT 0,\n    user_type        int2         NOT NULL DEFAULT 0,\n    application_name varchar(50)  NOT NULL,\n    request_method   varchar(16)  NULL     DEFAULT '',\n    request_url      varchar(255) NULL     DEFAULT '',\n    request_params   text         NULL,\n    response_body    text         NULL,\n    user_ip          varchar(50)  NOT NULL,\n    user_agent       varchar(512) NOT NULL,\n    operate_module   varchar(50)  NULL     DEFAULT NULL,\n    operate_name     varchar(50)  NULL     DEFAULT NULL,\n    operate_type     int2         NULL     DEFAULT 0,\n    begin_time       timestamp    NOT NULL,\n    end_time         timestamp    NOT NULL,\n    duration         int4         NOT NULL,\n    result_code      int4         NOT NULL DEFAULT 0,\n    result_msg       varchar(512) NULL     DEFAULT '',\n    creator          varchar(64)  NULL     DEFAULT '',\n    create_time      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater          varchar(64)  NULL     DEFAULT '',\n    update_time      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted          int2         NOT NULL DEFAULT 0,\n    tenant_id        int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_api_access_log\n    ADD CONSTRAINT pk_infra_api_access_log PRIMARY KEY (id);\n\nCREATE INDEX idx_infra_api_access_log_01 ON infra_api_access_log (create_time);\n\nCOMMENT ON COLUMN infra_api_access_log.id IS '日志主键';\nCOMMENT ON COLUMN infra_api_access_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_access_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_access_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_access_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_access_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_access_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_access_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_access_log.response_body IS '响应结果';\nCOMMENT ON COLUMN infra_api_access_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_access_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_access_log.operate_module IS '操作模块';\nCOMMENT ON COLUMN infra_api_access_log.operate_name IS '操作名';\nCOMMENT ON COLUMN infra_api_access_log.operate_type IS '操作分类';\nCOMMENT ON COLUMN infra_api_access_log.begin_time IS '开始请求时间';\nCOMMENT ON COLUMN infra_api_access_log.end_time IS '结束请求时间';\nCOMMENT ON COLUMN infra_api_access_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_api_access_log.result_code IS '结果码';\nCOMMENT ON COLUMN infra_api_access_log.result_msg IS '结果提示';\nCOMMENT ON COLUMN infra_api_access_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_access_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_access_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_access_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_access_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_access_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_access_log IS 'API 访问日志表';\n\nDROP SEQUENCE IF EXISTS infra_api_access_log_seq;\nCREATE SEQUENCE infra_api_access_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_api_error_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_api_error_log;\nCREATE TABLE infra_api_error_log\n(\n    id                           int8          NOT NULL,\n    trace_id                     varchar(64)   NOT NULL,\n    user_id                      int8          NOT NULL DEFAULT 0,\n    user_type                    int2          NOT NULL DEFAULT 0,\n    application_name             varchar(50)   NOT NULL,\n    request_method               varchar(16)   NOT NULL,\n    request_url                  varchar(255)  NOT NULL,\n    request_params               varchar(8000) NOT NULL,\n    user_ip                      varchar(50)   NOT NULL,\n    user_agent                   varchar(512)  NOT NULL,\n    exception_time               timestamp     NOT NULL,\n    exception_name               varchar(128)  NULL     DEFAULT '',\n    exception_message            text          NULL,\n    exception_root_cause_message text          NULL,\n    exception_stack_trace        text          NULL,\n    exception_class_name         varchar(512)  NOT NULL,\n    exception_file_name          varchar(512)  NOT NULL,\n    exception_method_name        varchar(512)  NOT NULL,\n    exception_line_number        int4          NOT NULL,\n    process_status               int2          NOT NULL,\n    process_time                 timestamp     NULL     DEFAULT NULL,\n    process_user_id              int4          NULL     DEFAULT 0,\n    creator                      varchar(64)   NULL     DEFAULT '',\n    create_time                  timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater                      varchar(64)   NULL     DEFAULT '',\n    update_time                  timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted                      int2          NOT NULL DEFAULT 0,\n    tenant_id                    int8          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_api_error_log\n    ADD CONSTRAINT pk_infra_api_error_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_api_error_log.id IS '编号';\nCOMMENT ON COLUMN infra_api_error_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_error_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_error_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_error_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_error_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_error_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_error_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_error_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_error_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_error_log.exception_time IS '异常发生时间';\nCOMMENT ON COLUMN infra_api_error_log.exception_name IS '异常名';\nCOMMENT ON COLUMN infra_api_error_log.exception_message IS '异常导致的消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_root_cause_message IS '异常导致的根消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_stack_trace IS '异常的栈轨迹';\nCOMMENT ON COLUMN infra_api_error_log.exception_class_name IS '异常发生的类全名';\nCOMMENT ON COLUMN infra_api_error_log.exception_file_name IS '异常发生的类文件';\nCOMMENT ON COLUMN infra_api_error_log.exception_method_name IS '异常发生的方法名';\nCOMMENT ON COLUMN infra_api_error_log.exception_line_number IS '异常发生的方法所在行';\nCOMMENT ON COLUMN infra_api_error_log.process_status IS '处理状态';\nCOMMENT ON COLUMN infra_api_error_log.process_time IS '处理时间';\nCOMMENT ON COLUMN infra_api_error_log.process_user_id IS '处理用户编号';\nCOMMENT ON COLUMN infra_api_error_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_error_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_error_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_error_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_error_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_error_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_error_log IS '系统异常日志';\n\nDROP SEQUENCE IF EXISTS infra_api_error_log_seq;\nCREATE SEQUENCE infra_api_error_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_codegen_column\n-- ----------------------------\nDROP TABLE IF EXISTS infra_codegen_column;\nCREATE TABLE infra_codegen_column\n(\n    id                       int8         NOT NULL,\n    table_id                 int8         NOT NULL,\n    column_name              varchar(200) NOT NULL,\n    data_type                varchar(100) NOT NULL,\n    column_comment           varchar(500) NOT NULL,\n    nullable                 bool         NOT NULL,\n    primary_key              bool         NOT NULL,\n    ordinal_position         int4         NOT NULL,\n    java_type                varchar(32)  NOT NULL,\n    java_field               varchar(64)  NOT NULL,\n    dict_type                varchar(200) NULL     DEFAULT '',\n    example                  varchar(64)  NULL     DEFAULT NULL,\n    create_operation         bool         NOT NULL,\n    update_operation         bool         NOT NULL,\n    list_operation           bool         NOT NULL,\n    list_operation_condition varchar(32)  NOT NULL DEFAULT '=',\n    list_operation_result    bool         NOT NULL,\n    html_type                varchar(32)  NOT NULL,\n    creator                  varchar(64)  NULL     DEFAULT '',\n    create_time              timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater                  varchar(64)  NULL     DEFAULT '',\n    update_time              timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted                  int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_codegen_column\n    ADD CONSTRAINT pk_infra_codegen_column PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_codegen_column.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_column.table_id IS '表编号';\nCOMMENT ON COLUMN infra_codegen_column.column_name IS '字段名';\nCOMMENT ON COLUMN infra_codegen_column.data_type IS '字段类型';\nCOMMENT ON COLUMN infra_codegen_column.column_comment IS '字段描述';\nCOMMENT ON COLUMN infra_codegen_column.nullable IS '是否允许为空';\nCOMMENT ON COLUMN infra_codegen_column.primary_key IS '是否主键';\nCOMMENT ON COLUMN infra_codegen_column.ordinal_position IS '排序';\nCOMMENT ON COLUMN infra_codegen_column.java_type IS 'Java 属性类型';\nCOMMENT ON COLUMN infra_codegen_column.java_field IS 'Java 属性名';\nCOMMENT ON COLUMN infra_codegen_column.dict_type IS '字典类型';\nCOMMENT ON COLUMN infra_codegen_column.example IS '数据示例';\nCOMMENT ON COLUMN infra_codegen_column.create_operation IS '是否为 Create 创建操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.update_operation IS '是否为 Update 更新操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation IS '是否为 List 查询操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_condition IS 'List 查询操作的条件类型';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_result IS '是否为 List 查询操作的返回字段';\nCOMMENT ON COLUMN infra_codegen_column.html_type IS '显示类型';\nCOMMENT ON COLUMN infra_codegen_column.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_column.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_column.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_column.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_column.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_column IS '代码生成表字段定义';\n\nDROP SEQUENCE IF EXISTS infra_codegen_column_seq;\nCREATE SEQUENCE infra_codegen_column_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_codegen_table\n-- ----------------------------\nDROP TABLE IF EXISTS infra_codegen_table;\nCREATE TABLE infra_codegen_table\n(\n    id                    int8         NOT NULL,\n    data_source_config_id int8         NOT NULL,\n    scene                 int2         NOT NULL DEFAULT 1,\n    table_name            varchar(200) NULL     DEFAULT '',\n    table_comment         varchar(500) NULL     DEFAULT '',\n    remark                varchar(500) NULL     DEFAULT NULL,\n    module_name           varchar(30)  NOT NULL,\n    business_name         varchar(30)  NOT NULL,\n    class_name            varchar(100) NULL     DEFAULT '',\n    class_comment         varchar(50)  NOT NULL,\n    author                varchar(50)  NOT NULL,\n    template_type         int2         NOT NULL DEFAULT 1,\n    front_type            int2         NOT NULL,\n    parent_menu_id        int8         NULL     DEFAULT NULL,\n    master_table_id       int8         NULL     DEFAULT NULL,\n    sub_join_column_id    int8         NULL     DEFAULT NULL,\n    sub_join_many         bool         NULL     DEFAULT NULL,\n    tree_parent_column_id int8         NULL     DEFAULT NULL,\n    tree_name_column_id   int8         NULL     DEFAULT NULL,\n    creator               varchar(64)  NULL     DEFAULT '',\n    create_time           timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater               varchar(64)  NULL     DEFAULT '',\n    update_time           timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted               int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_codegen_table\n    ADD CONSTRAINT pk_infra_codegen_table PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_codegen_table.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_table.data_source_config_id IS '数据源配置的编号';\nCOMMENT ON COLUMN infra_codegen_table.scene IS '生成场景';\nCOMMENT ON COLUMN infra_codegen_table.table_name IS '表名称';\nCOMMENT ON COLUMN infra_codegen_table.table_comment IS '表描述';\nCOMMENT ON COLUMN infra_codegen_table.remark IS '备注';\nCOMMENT ON COLUMN infra_codegen_table.module_name IS '模块名';\nCOMMENT ON COLUMN infra_codegen_table.business_name IS '业务名';\nCOMMENT ON COLUMN infra_codegen_table.class_name IS '类名称';\nCOMMENT ON COLUMN infra_codegen_table.class_comment IS '类描述';\nCOMMENT ON COLUMN infra_codegen_table.author IS '作者';\nCOMMENT ON COLUMN infra_codegen_table.template_type IS '模板类型';\nCOMMENT ON COLUMN infra_codegen_table.front_type IS '前端类型';\nCOMMENT ON COLUMN infra_codegen_table.parent_menu_id IS '父菜单编号';\nCOMMENT ON COLUMN infra_codegen_table.master_table_id IS '主表的编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_column_id IS '子表关联主表的字段编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_many IS '主表与子表是否一对多';\nCOMMENT ON COLUMN infra_codegen_table.tree_parent_column_id IS '树表的父字段编号';\nCOMMENT ON COLUMN infra_codegen_table.tree_name_column_id IS '树表的名字字段编号';\nCOMMENT ON COLUMN infra_codegen_table.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_table.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_table.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_table.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_table.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_table IS '代码生成表定义';\n\nDROP SEQUENCE IF EXISTS infra_codegen_table_seq;\nCREATE SEQUENCE infra_codegen_table_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_config;\nCREATE TABLE infra_config\n(\n    id          int8         NOT NULL,\n    category    varchar(50)  NOT NULL,\n    type        int2         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    config_key  varchar(100) NULL     DEFAULT '',\n    value       varchar(500) NULL     DEFAULT '',\n    visible     bool         NOT NULL,\n    remark      varchar(500) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_config\n    ADD CONSTRAINT pk_infra_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_config.id IS '参数主键';\nCOMMENT ON COLUMN infra_config.category IS '参数分组';\nCOMMENT ON COLUMN infra_config.type IS '参数类型';\nCOMMENT ON COLUMN infra_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_config.config_key IS '参数键名';\nCOMMENT ON COLUMN infra_config.value IS '参数键值';\nCOMMENT ON COLUMN infra_config.visible IS '是否可见';\nCOMMENT ON COLUMN infra_config.remark IS '备注';\nCOMMENT ON COLUMN infra_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_config IS '参数配置表';\n\n-- ----------------------------\n-- Records of infra_config\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'system.user.init-password', '123456', '0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-07-20 17:22:47', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:33:38', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:57:03', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:52:07', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (10, 'url', 2, 'Swagger 接口文档的地址', 'url.swagger', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:59:00', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (11, 'ui', 2, '腾讯地图 key', 'tencent.lbs.key', 'TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E', '1', '腾讯地图 key', '1', '2023-06-03 19:16:27', '1', '2023-06-03 19:16:27', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 'test2', 2, 'test3', 'test4', 'test5', '1', 'test6', '1', '2023-12-03 09:55:16', '1', '2025-04-06 21:00:09', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '用户管理-账号初始密码', 2, '用户管理-注册开关', 'system.user.register-enabled', 'true', '0', '', '1', '2025-04-26 17:23:41', '1', '2025-04-26 17:23:41', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS infra_config_seq;\nCREATE SEQUENCE infra_config_seq\n    START 14;\n\n-- ----------------------------\n-- Table structure for infra_data_source_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_data_source_config;\nCREATE TABLE infra_data_source_config\n(\n    id          int8          NOT NULL,\n    name        varchar(100)  NULL     DEFAULT '',\n    url         varchar(1024) NOT NULL,\n    username    varchar(255)  NOT NULL,\n    password    varchar(255)  NULL     DEFAULT '',\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_data_source_config\n    ADD CONSTRAINT pk_infra_data_source_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_data_source_config.id IS '主键编号';\nCOMMENT ON COLUMN infra_data_source_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_data_source_config.url IS '数据源连接';\nCOMMENT ON COLUMN infra_data_source_config.username IS '用户名';\nCOMMENT ON COLUMN infra_data_source_config.password IS '密码';\nCOMMENT ON COLUMN infra_data_source_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_data_source_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_data_source_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_data_source_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_data_source_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_data_source_config IS '数据源配置表';\n\nDROP SEQUENCE IF EXISTS infra_data_source_config_seq;\nCREATE SEQUENCE infra_data_source_config_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_file\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file;\nCREATE TABLE infra_file\n(\n    id          int8          NOT NULL,\n    config_id   int8          NULL     DEFAULT NULL,\n    name        varchar(256)  NULL     DEFAULT NULL,\n    path        varchar(512)  NOT NULL,\n    url         varchar(1024) NOT NULL,\n    type        varchar(128)  NULL     DEFAULT NULL,\n    size        int4          NOT NULL,\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_file\n    ADD CONSTRAINT pk_infra_file PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file.id IS '文件编号';\nCOMMENT ON COLUMN infra_file.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file.name IS '文件名';\nCOMMENT ON COLUMN infra_file.path IS '文件路径';\nCOMMENT ON COLUMN infra_file.url IS '文件 URL';\nCOMMENT ON COLUMN infra_file.type IS '文件类型';\nCOMMENT ON COLUMN infra_file.size IS '文件大小';\nCOMMENT ON COLUMN infra_file.creator IS '创建者';\nCOMMENT ON COLUMN infra_file.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file.updater IS '更新者';\nCOMMENT ON COLUMN infra_file.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file IS '文件表';\n\nDROP SEQUENCE IF EXISTS infra_file_seq;\nCREATE SEQUENCE infra_file_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_file_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file_config;\nCREATE TABLE infra_file_config\n(\n    id          int8          NOT NULL,\n    name        varchar(63)   NOT NULL,\n    storage     int2          NOT NULL,\n    remark      varchar(255)  NULL     DEFAULT NULL,\n    master      bool          NOT NULL,\n    config      varchar(4096) NOT NULL,\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_file_config\n    ADD CONSTRAINT pk_infra_file_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file_config.id IS '编号';\nCOMMENT ON COLUMN infra_file_config.name IS '配置名';\nCOMMENT ON COLUMN infra_file_config.storage IS '存储器';\nCOMMENT ON COLUMN infra_file_config.remark IS '备注';\nCOMMENT ON COLUMN infra_file_config.master IS '是否为主配置';\nCOMMENT ON COLUMN infra_file_config.config IS '存储配置';\nCOMMENT ON COLUMN infra_file_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_config IS '文件配置表';\n\n-- ----------------------------\n-- Records of infra_file_config\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (4, '数据库（示例）', 1, '我是数据库', '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-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (22, '七牛存储器（示例）', 20, '请换成你自己的密钥！！！', '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}', '1', '2024-01-13 22:11:12', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (24, '腾讯云存储（示例）', 20, '请换成你的密钥！！！', '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\"}', '1', '2024-11-09 16:03:22', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (25, '阿里云存储（示例）', 20, '', '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}', '1', '2024-11-09 16:47:08', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (26, '火山云存储（示例）', 20, '', '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}', '1', '2024-11-09 16:56:42', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (27, '华为云存储（示例）', 20, '', '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}', '1', '2024-11-09 17:18:41', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (28, 'MinIO 存储（示例）', 20, '', '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}', '1', '2024-11-09 17:43:10', '1', '2025-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (29, '本地存储（示例）', 10, '仅适合 mac 或 windows', '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-05-02 18:30:28', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (30, 'SFTP 存储（示例）', 12, '', '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-05-02 18:30:28', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS infra_file_config_seq;\nCREATE SEQUENCE infra_file_config_seq\n    START 31;\n\n-- ----------------------------\n-- Table structure for infra_file_content\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file_content;\nCREATE TABLE infra_file_content\n(\n    id          int8         NOT NULL,\n    config_id   int8         NOT NULL,\n    path        varchar(512) NOT NULL,\n    content     bytea        NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_file_content\n    ADD CONSTRAINT pk_infra_file_content PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file_content.id IS '编号';\nCOMMENT ON COLUMN infra_file_content.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file_content.path IS '文件路径';\nCOMMENT ON COLUMN infra_file_content.content IS '文件内容';\nCOMMENT ON COLUMN infra_file_content.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_content.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_content.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_content.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_content.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_content IS '文件表';\n\nDROP SEQUENCE IF EXISTS infra_file_content_seq;\nCREATE SEQUENCE infra_file_content_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_job\n-- ----------------------------\nDROP TABLE IF EXISTS infra_job;\nCREATE TABLE infra_job\n(\n    id              int8         NOT NULL,\n    name            varchar(32)  NOT NULL,\n    status          int2         NOT NULL,\n    handler_name    varchar(64)  NOT NULL,\n    handler_param   varchar(255) NULL     DEFAULT NULL,\n    cron_expression varchar(32)  NOT NULL,\n    retry_count     int4         NOT NULL DEFAULT 0,\n    retry_interval  int4         NOT NULL DEFAULT 0,\n    monitor_timeout int4         NOT NULL DEFAULT 0,\n    creator         varchar(64)  NULL     DEFAULT '',\n    create_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater         varchar(64)  NULL     DEFAULT '',\n    update_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted         int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_job\n    ADD CONSTRAINT pk_infra_job PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_job.id IS '任务编号';\nCOMMENT ON COLUMN infra_job.name IS '任务名称';\nCOMMENT ON COLUMN infra_job.status IS '任务状态';\nCOMMENT ON COLUMN infra_job.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job.cron_expression IS 'CRON 表达式';\nCOMMENT ON COLUMN infra_job.retry_count IS '重试次数';\nCOMMENT ON COLUMN infra_job.retry_interval IS '重试间隔';\nCOMMENT ON COLUMN infra_job.monitor_timeout IS '监控超时时间';\nCOMMENT ON COLUMN infra_job.creator IS '创建者';\nCOMMENT ON COLUMN infra_job.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job.updater IS '更新者';\nCOMMENT ON COLUMN infra_job.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job IS '定时任务表';\n\n-- ----------------------------\n-- Records of infra_job\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (5, '支付通知 Job', 2, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', '2021-10-27 08:34:42', '1', '2024-09-12 13:32:48', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (17, '支付订单同步 Job', 2, 'payOrderSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 14:36:26', '1', '2023-07-22 15:39:08', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (18, '支付订单过期 Job', 2, 'payOrderExpireJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 15:36:23', '1', '2023-07-22 15:39:54', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (19, '退款订单的同步 Job', 2, 'payRefundSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-23 21:03:44', '1', '2023-07-23 21:09:00', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (21, '交易订单的自动过期 Job', 2, 'tradeOrderAutoCancelJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-25 23:43:26', '1', '2023-09-26 19:23:30', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (22, '交易订单的自动收货 Job', 2, 'tradeOrderAutoReceiveJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 19:23:53', '1', '2023-09-26 23:38:08', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (23, '交易订单的自动评论 Job', 2, 'tradeOrderAutoCommentJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 23:38:29', '1', '2023-09-27 11:03:10', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (24, '佣金解冻 Job', 2, 'brokerageRecordUnfreezeJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-28 22:01:46', '1', '2023-09-28 22:01:56', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (25, '访问日志清理 Job', 2, 'accessLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 10:59:41', '1', '2023-10-03 11:01:10', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (26, '错误日志清理 Job', 2, 'errorLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:00:43', '1', '2023-10-03 11:01:12', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (27, '任务日志清理 Job', 2, 'jobLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:01:33', '1', '2024-09-12 13:40:34', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (33, 'demoJob', 2, 'demoJob', '', '0 * * * * ?', 1, 1, 0, '1', '2024-10-27 19:38:46', '1', '2025-05-10 18:13:54', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (35, '转账订单的同步 Job', 2, 'payTransferSyncJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-05-10 17:35:54', '1', '2025-05-10 18:13:52', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS infra_job_seq;\nCREATE SEQUENCE infra_job_seq\n    START 36;\n\n-- ----------------------------\n-- Table structure for infra_job_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_job_log;\nCREATE TABLE infra_job_log\n(\n    id            int8          NOT NULL,\n    job_id        int8          NOT NULL,\n    handler_name  varchar(64)   NOT NULL,\n    handler_param varchar(255)  NULL     DEFAULT NULL,\n    execute_index int2          NOT NULL DEFAULT 1,\n    begin_time    timestamp     NOT NULL,\n    end_time      timestamp     NULL     DEFAULT NULL,\n    duration      int4          NULL     DEFAULT NULL,\n    status        int2          NOT NULL,\n    result        varchar(4000) NULL     DEFAULT '',\n    creator       varchar(64)   NULL     DEFAULT '',\n    create_time   timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater       varchar(64)   NULL     DEFAULT '',\n    update_time   timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted       int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_job_log\n    ADD CONSTRAINT pk_infra_job_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_job_log.id IS '日志编号';\nCOMMENT ON COLUMN infra_job_log.job_id IS '任务编号';\nCOMMENT ON COLUMN infra_job_log.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job_log.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job_log.execute_index IS '第几次执行';\nCOMMENT ON COLUMN infra_job_log.begin_time IS '开始执行时间';\nCOMMENT ON COLUMN infra_job_log.end_time IS '结束执行时间';\nCOMMENT ON COLUMN infra_job_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_job_log.status IS '任务状态';\nCOMMENT ON COLUMN infra_job_log.result IS '结果数据';\nCOMMENT ON COLUMN infra_job_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_job_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_job_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job_log.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job_log IS '定时任务日志表';\n\nDROP SEQUENCE IF EXISTS infra_job_log_seq;\nCREATE SEQUENCE infra_job_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_dept\n-- ----------------------------\nDROP TABLE IF EXISTS system_dept;\nCREATE TABLE system_dept\n(\n    id             int8        NOT NULL,\n    name           varchar(30) NULL     DEFAULT '',\n    parent_id      int8        NOT NULL DEFAULT 0,\n    sort           int4        NOT NULL DEFAULT 0,\n    leader_user_id int8        NULL     DEFAULT NULL,\n    phone          varchar(11) NULL     DEFAULT NULL,\n    email          varchar(50) NULL     DEFAULT NULL,\n    status         int2        NOT NULL,\n    creator        varchar(64) NULL     DEFAULT '',\n    create_time    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64) NULL     DEFAULT '',\n    update_time    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2        NOT NULL DEFAULT 0,\n    tenant_id      int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_dept\n    ADD CONSTRAINT pk_system_dept PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dept.id IS '部门id';\nCOMMENT ON COLUMN system_dept.name IS '部门名称';\nCOMMENT ON COLUMN system_dept.parent_id IS '父部门id';\nCOMMENT ON COLUMN system_dept.sort IS '显示顺序';\nCOMMENT ON COLUMN system_dept.leader_user_id IS '负责人';\nCOMMENT ON COLUMN system_dept.phone IS '联系电话';\nCOMMENT ON COLUMN system_dept.email IS '邮箱';\nCOMMENT ON COLUMN system_dept.status IS '部门状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dept.creator IS '创建者';\nCOMMENT ON COLUMN system_dept.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dept.updater IS '更新者';\nCOMMENT ON COLUMN system_dept.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dept.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dept.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_dept IS '部门表';\n\n-- ----------------------------\n-- Records of system_dept\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, '芋道源码', 0, 0, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2025-03-29 15:47:53', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (101, '深圳总公司', 100, 1, 104, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2025-03-29 15:49:55', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (102, '长沙分公司', 100, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:40', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, '研发部门', 101, 1, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2024-10-02 10:22:03', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, '市场部门', 101, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:38', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (105, '测试部门', 101, 3, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-05-16 20:25:15', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (106, '财务部门', 101, 4, 103, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '103', '2022-01-15 21:32:22', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (107, '运维部门', 101, 5, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2023-12-02 09:28:22', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (108, '市场部门', 102, 1, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-02-16 08:35:45', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (109, '财务部门', 102, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:29', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (110, '新部门', 0, 1, NULL, NULL, NULL, 0, '110', '2022-02-23 20:46:30', '110', '2022-02-23 20:46:30', '0', 121);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (111, '顶级部门', 0, 1, NULL, NULL, NULL, 0, '113', '2022-03-07 21:44:50', '113', '2022-03-07 21:44:50', '0', 122);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, '产品部门', 101, 100, 1, NULL, NULL, 1, '1', '2023-12-02 09:45:13', '1', '2023-12-02 09:45:31', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, '支持部门', 102, 3, 104, NULL, NULL, 1, '1', '2023-12-02 09:47:38', '1', '2025-03-29 15:00:56', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_dept_seq;\nCREATE SEQUENCE system_dept_seq\n    START 114;\n\n-- ----------------------------\n-- Table structure for system_dict_data\n-- ----------------------------\nDROP TABLE IF EXISTS system_dict_data;\nCREATE TABLE system_dict_data\n(\n    id          int8         NOT NULL,\n    sort        int4         NOT NULL DEFAULT 0,\n    label       varchar(100) NULL     DEFAULT '',\n    value       varchar(100) NULL     DEFAULT '',\n    dict_type   varchar(100) NULL     DEFAULT '',\n    status      int2         NOT NULL DEFAULT 0,\n    color_type  varchar(100) NULL     DEFAULT '',\n    css_class   varchar(100) NULL     DEFAULT '',\n    remark      varchar(500) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_dict_data\n    ADD CONSTRAINT pk_system_dict_data PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dict_data.id IS '字典编码';\nCOMMENT ON COLUMN system_dict_data.sort IS '字典排序';\nCOMMENT ON COLUMN system_dict_data.label IS '字典标签';\nCOMMENT ON COLUMN system_dict_data.value IS '字典键值';\nCOMMENT ON COLUMN system_dict_data.dict_type IS '字典类型';\nCOMMENT ON COLUMN system_dict_data.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_data.color_type IS '颜色类型';\nCOMMENT ON COLUMN system_dict_data.css_class IS 'css 样式';\nCOMMENT ON COLUMN system_dict_data.remark IS '备注';\nCOMMENT ON COLUMN system_dict_data.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_data.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_data.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_data.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_data.deleted IS '是否删除';\nCOMMENT ON TABLE system_dict_data IS '字典数据表';\n\n-- ----------------------------\n-- Records of system_dict_data\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1, 1, '男', '1', 'system_user_sex', 0, 'default', 'A', '性别男', 'admin', '2021-01-05 17:03:48', '1', '2022-03-29 00:14:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 2, '女', '2', 'system_user_sex', 0, 'success', '', '性别女', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 23:30:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 1, '正常', '1', 'infra_job_status', 0, 'success', '', '正常状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 2, '暂停', '2', 'infra_job_status', 0, 'danger', '', '停用状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 1, '系统内置', '1', 'infra_config_type', 0, 'danger', '', '参数类型 - 系统内置', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (13, 2, '自定义', '2', 'infra_config_type', 0, 'primary', '', '参数类型 - 自定义', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (14, 1, '通知', '1', 'system_notice_type', 0, 'success', '', '通知', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:05:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (15, 2, '公告', '2', 'system_notice_type', 0, 'info', '', '公告', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:06:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (16, 0, '其它', '0', 'infra_operate_type', 0, 'default', '', '其它操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (17, 1, '查询', '1', 'infra_operate_type', 0, 'info', '', '查询操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (18, 2, '新增', '2', 'infra_operate_type', 0, 'primary', '', '新增操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (19, 3, '修改', '3', 'infra_operate_type', 0, 'warning', '', '修改操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (20, 4, '删除', '4', 'infra_operate_type', 0, 'danger', '', '删除操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (22, 5, '导出', '5', 'infra_operate_type', 0, 'default', '', '导出操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (23, 6, '导入', '6', 'infra_operate_type', 0, 'default', '', '导入操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (27, 1, '开启', '0', 'common_status', 0, 'primary', '', '开启状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (28, 2, '关闭', '1', 'common_status', 0, 'info', '', '关闭状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (29, 1, '目录', '1', 'system_menu_type', 0, '', '', '目录', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (30, 2, '菜单', '2', 'system_menu_type', 0, '', '', '菜单', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (31, 3, '按钮', '3', 'system_menu_type', 0, '', '', '按钮', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (32, 1, '内置', '1', 'system_role_type', 0, 'danger', '', '内置角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (33, 2, '自定义', '2', 'system_role_type', 0, 'primary', '', '自定义角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (34, 1, '全部数据权限', '1', 'system_data_scope', 0, '', '', '全部数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (35, 2, '指定部门数据权限', '2', 'system_data_scope', 0, '', '', '指定部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (36, 3, '本部门数据权限', '3', 'system_data_scope', 0, '', '', '本部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (37, 4, '本部门及以下数据权限', '4', 'system_data_scope', 0, '', '', '本部门及以下数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (38, 5, '仅本人数据权限', '5', 'system_data_scope', 0, '', '', '仅本人数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (39, 0, '成功', '0', 'system_login_result', 0, 'success', '', '登陆结果 - 成功', '', '2021-01-18 06:17:36', '1', '2022-02-16 13:23:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (40, 10, '账号或密码不正确', '10', 'system_login_result', 0, 'primary', '', '登陆结果 - 账号或密码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (41, 20, '用户被禁用', '20', 'system_login_result', 0, 'warning', '', '登陆结果 - 用户被禁用', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:23:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (42, 30, '验证码不存在', '30', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不存在', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (43, 31, '验证码不正确', '31', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (44, 100, '未知异常', '100', 'system_login_result', 0, 'danger', '', '登陆结果 - 未知异常', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (45, 1, '是', 'true', 'infra_boolean_string', 0, 'danger', '', 'Boolean 是否类型 - 是', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:01:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (46, 1, '否', 'false', 'infra_boolean_string', 0, 'info', '', 'Boolean 是否类型 - 否', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:09:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (50, 1, '单表（增删改查）', '1', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:09:06', '', '2022-03-10 16:33:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (51, 2, '树表（增删改查）', '2', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:14:46', '', '2022-03-10 16:33:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (53, 0, '初始化中', '0', 'infra_job_status', 0, 'primary', '', NULL, '', '2021-02-07 07:46:49', '1', '2022-02-16 19:33:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (57, 0, '运行中', '0', 'infra_job_log_status', 0, 'primary', '', 'RUNNING', '', '2021-02-08 10:04:24', '1', '2022-02-16 19:07:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (58, 1, '成功', '1', 'infra_job_log_status', 0, 'success', '', NULL, '', '2021-02-08 10:06:57', '1', '2022-02-16 19:07:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (59, 2, '失败', '2', 'infra_job_log_status', 0, 'warning', '', '失败', '', '2021-02-08 10:07:38', '1', '2022-02-16 19:07:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (60, 1, '会员', '1', 'user_type', 0, 'primary', '', NULL, '', '2021-02-26 00:16:27', '1', '2022-02-16 10:22:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (61, 2, '管理员', '2', 'user_type', 0, 'success', '', NULL, '', '2021-02-26 00:16:34', '1', '2025-04-06 18:37:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (62, 0, '未处理', '0', 'infra_api_error_log_process_status', 0, 'primary', '', NULL, '', '2021-02-26 07:07:19', '1', '2022-02-16 20:14:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (63, 1, '已处理', '1', 'infra_api_error_log_process_status', 0, 'success', '', NULL, '', '2021-02-26 07:07:26', '1', '2022-02-16 20:14:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (64, 2, '已忽略', '2', 'infra_api_error_log_process_status', 0, 'danger', '', NULL, '', '2021-02-26 07:07:34', '1', '2022-02-16 20:14:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (66, 1, '阿里云', 'ALIYUN', 'system_sms_channel_code', 0, 'primary', '', NULL, '1', '2021-04-05 01:05:26', '1', '2024-07-22 22:23:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (67, 1, '验证码', '1', 'system_sms_template_type', 0, 'warning', '', NULL, '1', '2021-04-05 21:50:57', '1', '2022-02-16 12:48:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (68, 2, '通知', '2', 'system_sms_template_type', 0, 'primary', '', NULL, '1', '2021-04-05 21:51:08', '1', '2022-02-16 12:48:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (69, 0, '营销', '3', 'system_sms_template_type', 0, 'danger', '', NULL, '1', '2021-04-05 21:51:15', '1', '2022-02-16 12:48:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (70, 0, '初始化', '0', 'system_sms_send_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:18:33', '1', '2022-02-16 10:26:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (71, 1, '发送成功', '10', 'system_sms_send_status', 0, 'success', '', NULL, '1', '2021-04-11 20:18:43', '1', '2022-02-16 10:25:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (72, 2, '发送失败', '20', 'system_sms_send_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:18:49', '1', '2022-02-16 10:26:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (73, 3, '不发送', '30', 'system_sms_send_status', 0, 'info', '', NULL, '1', '2021-04-11 20:19:44', '1', '2022-02-16 10:26:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (74, 0, '等待结果', '0', 'system_sms_receive_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:27:43', '1', '2022-02-16 10:28:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (75, 1, '接收成功', '10', 'system_sms_receive_status', 0, 'success', '', NULL, '1', '2021-04-11 20:29:25', '1', '2022-02-16 10:28:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (76, 2, '接收失败', '20', 'system_sms_receive_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:29:31', '1', '2022-02-16 10:28:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (77, 0, '调试(钉钉)', 'DEBUG_DING_TALK', 'system_sms_channel_code', 0, 'info', '', NULL, '1', '2021-04-13 00:20:37', '1', '2022-02-16 10:10:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (80, 100, '账号登录', '100', 'system_login_type', 0, 'primary', '', '账号登录', '1', '2021-10-06 00:52:02', '1', '2022-02-16 13:11:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (81, 101, '社交登录', '101', 'system_login_type', 0, 'info', '', '社交登录', '1', '2021-10-06 00:52:17', '1', '2022-02-16 13:11:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (83, 200, '主动登出', '200', 'system_login_type', 0, 'primary', '', '主动登出', '1', '2021-10-06 00:52:58', '1', '2022-02-16 13:11:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (85, 202, '强制登出', '202', 'system_login_type', 0, 'danger', '', '强制退出', '1', '2021-10-06 00:53:41', '1', '2022-02-16 13:11:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (86, 0, '病假', '1', 'bpm_oa_leave_type', 0, 'primary', '', NULL, '1', '2021-09-21 22:35:28', '1', '2022-02-16 10:00:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (87, 1, '事假', '2', 'bpm_oa_leave_type', 0, 'info', '', NULL, '1', '2021-09-21 22:36:11', '1', '2022-02-16 10:00:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (88, 2, '婚假', '3', 'bpm_oa_leave_type', 0, 'warning', '', NULL, '1', '2021-09-21 22:36:38', '1', '2022-02-16 10:00:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (112, 0, '微信 Wap 网站支付', 'wx_wap', 'pay_channel_code', 0, 'success', '', '微信 Wap 网站支付', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (113, 1, '微信公众号支付', 'wx_pub', 'pay_channel_code', 0, 'success', '', '微信公众号支付', '1', '2021-12-03 10:40:24', '1', '2023-07-19 20:08:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (114, 2, '微信小程序支付', 'wx_lite', 'pay_channel_code', 0, 'success', '', '微信小程序支付', '1', '2021-12-03 10:41:06', '1', '2023-07-19 20:08:50', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (115, 3, '微信 App 支付', 'wx_app', 'pay_channel_code', 0, 'success', '', '微信 App 支付', '1', '2021-12-03 10:41:20', '1', '2023-07-19 20:08:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (116, 10, '支付宝 PC 网站支付', 'alipay_pc', 'pay_channel_code', 0, 'primary', '', '支付宝 PC 网站支付', '1', '2021-12-03 10:42:09', '1', '2023-07-19 20:09:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (117, 11, '支付宝 Wap 网站支付', 'alipay_wap', 'pay_channel_code', 0, 'primary', '', '支付宝 Wap 网站支付', '1', '2021-12-03 10:42:26', '1', '2023-07-19 20:09:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (118, 12, '支付宝 App 支付', 'alipay_app', 'pay_channel_code', 0, 'primary', '', '支付宝 App 支付', '1', '2021-12-03 10:42:55', '1', '2023-07-19 20:09:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (119, 14, '支付宝扫码支付', 'alipay_qr', 'pay_channel_code', 0, 'primary', '', '支付宝扫码支付', '1', '2021-12-03 10:43:10', '1', '2023-07-19 20:09:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (120, 10, '通知成功', '10', 'pay_notify_status', 0, 'success', '', '通知成功', '1', '2021-12-03 11:02:41', '1', '2023-07-19 10:08:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (121, 20, '通知失败', '20', 'pay_notify_status', 0, 'danger', '', '通知失败', '1', '2021-12-03 11:02:59', '1', '2023-07-19 10:08:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (122, 0, '等待通知', '0', 'pay_notify_status', 0, 'info', '', '未通知', '1', '2021-12-03 11:03:10', '1', '2023-07-19 10:08:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (123, 10, '支付成功', '10', 'pay_order_status', 0, 'success', '', '支付成功', '1', '2021-12-03 11:18:29', '1', '2023-07-19 18:04:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (124, 30, '支付关闭', '30', 'pay_order_status', 0, 'info', '', '支付关闭', '1', '2021-12-03 11:18:42', '1', '2023-07-19 18:05:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (125, 0, '等待支付', '0', 'pay_order_status', 0, 'info', '', '未支付', '1', '2021-12-03 11:18:18', '1', '2023-07-19 18:04:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (600, 5, '首页', '1', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (601, 4, '秒杀活动页', '2', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (602, 3, '砍价活动页', '3', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (603, 2, '限时折扣页', '4', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (604, 1, '满减送页', '5', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1118, 0, '等待退款', '0', 'pay_refund_status', 0, 'info', '', '等待退款', '1', '2021-12-10 16:44:59', '1', '2023-07-19 10:14:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1119, 20, '退款失败', '20', 'pay_refund_status', 0, 'danger', '', '退款失败', '1', '2021-12-10 16:45:10', '1', '2023-07-19 10:15:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1124, 10, '退款成功', '10', 'pay_refund_status', 0, 'success', '', '退款成功', '1', '2021-12-10 16:46:26', '1', '2023-07-19 10:15:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1127, 1, '审批中', '1', 'bpm_process_instance_status', 0, 'default', '', '流程实例的状态 - 进行中', '1', '2022-01-07 23:47:22', '1', '2024-03-16 16:11:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1128, 2, '审批通过', '2', 'bpm_process_instance_status', 0, 'success', '', '流程实例的状态 - 已完成', '1', '2022-01-07 23:47:49', '1', '2024-03-16 16:11:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1129, 1, '审批中', '1', 'bpm_task_status', 0, 'primary', '', '流程实例的结果 - 处理中', '1', '2022-01-07 23:48:32', '1', '2024-03-08 22:41:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1130, 2, '审批通过', '2', 'bpm_task_status', 0, 'success', '', '流程实例的结果 - 通过', '1', '2022-01-07 23:48:45', '1', '2024-03-08 22:41:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1131, 3, '审批不通过', '3', 'bpm_task_status', 0, 'danger', '', '流程实例的结果 - 不通过', '1', '2022-01-07 23:48:55', '1', '2024-03-08 22:41:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1132, 4, '已取消', '4', 'bpm_task_status', 0, 'info', '', '流程实例的结果 - 撤销', '1', '2022-01-07 23:49:06', '1', '2024-03-08 22:41:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1133, 10, '流程表单', '10', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 流程表单', '103', '2022-01-11 23:51:30', '103', '2022-01-11 23:51:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1134, 20, '业务表单', '20', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 业务表单', '103', '2022-01-11 23:51:47', '103', '2022-01-11 23:51:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1135, 10, '角色', '10', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 角色', '103', '2022-01-12 23:21:22', '1', '2024-03-06 02:53:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1136, 20, '部门的成员', '20', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的成员', '103', '2022-01-12 23:21:47', '1', '2024-03-06 02:53:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1137, 21, '部门的负责人', '21', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的负责人', '103', '2022-01-12 23:33:36', '1', '2024-03-06 02:53:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1138, 30, '用户', '30', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 用户', '103', '2022-01-12 23:34:02', '1', '2024-03-06 02:53:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1139, 40, '用户组', '40', 'bpm_task_candidate_strategy', 0, 'warning', '', '任务分配规则的类型 - 用户组', '103', '2022-01-12 23:34:21', '1', '2024-03-06 02:53:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1140, 60, '流程表达式', '60', 'bpm_task_candidate_strategy', 0, 'danger', '', '任务分配规则的类型 - 流程表达式', '103', '2022-01-12 23:34:43', '1', '2024-03-06 02:53:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1141, 22, '岗位', '22', 'bpm_task_candidate_strategy', 0, 'success', '', '任务分配规则的类型 - 岗位', '103', '2022-01-14 18:41:55', '1', '2024-03-06 02:53:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1145, 1, '管理后台', '1', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 管理后台', '1', '2022-02-02 13:15:06', '1', '2022-03-10 16:32:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1146, 2, '用户 APP', '2', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 用户 APP', '1', '2022-02-02 13:15:19', '1', '2022-03-10 16:33:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1150, 1, '数据库', '1', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:28', '1', '2022-03-15 00:25:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1151, 10, '本地磁盘', '10', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:41', '1', '2022-03-15 00:25:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1152, 11, 'FTP 服务器', '11', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:06', '1', '2022-03-15 00:26:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1153, 12, 'SFTP 服务器', '12', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:22', '1', '2022-03-15 00:26:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1154, 20, 'S3 对象存储', '20', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:31', '1', '2022-03-15 00:26:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1155, 103, '短信登录', '103', 'system_login_type', 0, 'default', '', NULL, '1', '2022-05-09 23:57:58', '1', '2022-05-09 23:58:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1156, 1, 'password', 'password', 'system_oauth2_grant_type', 0, 'default', '', '密码模式', '1', '2022-05-12 00:22:05', '1', '2022-05-11 16:26:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1157, 2, 'authorization_code', 'authorization_code', 'system_oauth2_grant_type', 0, 'primary', '', '授权码模式', '1', '2022-05-12 00:22:59', '1', '2022-05-11 16:26:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1158, 3, 'implicit', 'implicit', 'system_oauth2_grant_type', 0, 'success', '', '简化模式', '1', '2022-05-12 00:23:40', '1', '2022-05-11 16:26:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1159, 4, 'client_credentials', 'client_credentials', 'system_oauth2_grant_type', 0, 'default', '', '客户端模式', '1', '2022-05-12 00:23:51', '1', '2022-05-11 16:26:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1160, 5, 'refresh_token', 'refresh_token', 'system_oauth2_grant_type', 0, 'info', '', '刷新模式', '1', '2022-05-12 00:24:02', '1', '2022-05-11 16:26:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1162, 1, '销售中', '1', 'product_spu_status', 0, 'success', '', '商品 SPU 状态 - 销售中', '1', '2022-10-24 21:19:47', '1', '2022-10-24 21:20:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1163, 0, '仓库中', '0', 'product_spu_status', 0, 'info', '', '商品 SPU 状态 - 仓库中', '1', '2022-10-24 21:20:54', '1', '2022-10-24 21:21:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1164, 0, '回收站', '-1', 'product_spu_status', 0, 'default', '', '商品 SPU 状态 - 回收站', '1', '2022-10-24 21:21:11', '1', '2022-10-24 21:21:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1165, 1, '满减', '1', 'promotion_discount_type', 0, 'success', '', '优惠类型 - 满减', '1', '2022-11-01 12:46:41', '1', '2022-11-01 12:50:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1166, 2, '折扣', '2', 'promotion_discount_type', 0, 'primary', '', '优惠类型 - 折扣', '1', '2022-11-01 12:46:51', '1', '2022-11-01 12:50:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1167, 1, '固定日期', '1', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 固定日期', '1', '2022-11-02 00:07:34', '1', '2022-11-04 00:07:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1168, 2, '领取之后', '2', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 领取之后', '1', '2022-11-02 00:07:54', '1', '2022-11-04 00:07:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1169, 1, '通用劵', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', '2022-11-02 00:28:22', '1', '2023-09-28 00:27:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1170, 2, '商品劵', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', '2022-11-02 00:28:34', '1', '2023-09-28 00:27:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1171, 1, '未使用', '1', 'promotion_coupon_status', 0, 'primary', '', '优惠劵的状态 - 已领取', '1', '2022-11-04 00:15:08', '1', '2023-10-03 12:54:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1172, 2, '已使用', '2', 'promotion_coupon_status', 0, 'success', '', '优惠劵的状态 - 已使用', '1', '2022-11-04 00:15:21', '1', '2022-11-04 19:16:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1173, 3, '已过期', '3', 'promotion_coupon_status', 0, 'info', '', '优惠劵的状态 - 已过期', '1', '2022-11-04 00:15:43', '1', '2022-11-04 19:16:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1174, 1, '直接领取', '1', 'promotion_coupon_take_type', 0, 'primary', '', '优惠劵的领取方式 - 直接领取', '1', '2022-11-04 19:13:00', '1', '2022-11-04 19:13:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1175, 2, '指定发放', '2', 'promotion_coupon_take_type', 0, 'success', '', '优惠劵的领取方式 - 指定发放', '1', '2022-11-04 19:13:13', '1', '2022-11-04 19:14:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1176, 10, '未开始', '10', 'promotion_activity_status', 0, 'primary', '', '促销活动的状态枚举 - 未开始', '1', '2022-11-04 22:54:49', '1', '2022-11-04 22:55:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1177, 20, '进行中', '20', 'promotion_activity_status', 0, 'success', '', '促销活动的状态枚举 - 进行中', '1', '2022-11-04 22:55:06', '1', '2022-11-04 22:55:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1178, 30, '已结束', '30', 'promotion_activity_status', 0, 'info', '', '促销活动的状态枚举 - 已结束', '1', '2022-11-04 22:55:41', '1', '2022-11-04 22:55:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1179, 40, '已关闭', '40', 'promotion_activity_status', 0, 'warning', '', '促销活动的状态枚举 - 已关闭', '1', '2022-11-04 22:56:10', '1', '2022-11-04 22:56:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1180, 10, '满 N 元', '10', 'promotion_condition_type', 0, 'primary', '', '营销的条件类型 - 满 N 元', '1', '2022-11-04 22:59:45', '1', '2022-11-04 22:59:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1181, 20, '满 N 件', '20', 'promotion_condition_type', 0, 'success', '', '营销的条件类型 - 满 N 件', '1', '2022-11-04 23:00:02', '1', '2022-11-04 23:00:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1182, 10, '申请售后', '10', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 申请售后', '1', '2022-11-19 20:53:33', '1', '2022-11-19 20:54:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1183, 20, '商品待退货', '20', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商品待退货', '1', '2022-11-19 20:54:36', '1', '2022-11-19 20:58:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1184, 30, '商家待收货', '30', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商家待收货', '1', '2022-11-19 20:56:56', '1', '2022-11-19 20:59:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1185, 40, '等待退款', '40', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 等待退款', '1', '2022-11-19 20:59:54', '1', '2022-11-19 21:00:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1186, 50, '退款成功', '50', 'trade_after_sale_status', 0, 'default', '', '交易售后状态 - 退款成功', '1', '2022-11-19 21:00:33', '1', '2022-11-19 21:00:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1187, 61, '买家取消', '61', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 买家取消', '1', '2022-11-19 21:01:29', '1', '2022-11-19 21:01:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1188, 62, '商家拒绝', '62', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒绝', '1', '2022-11-19 21:02:17', '1', '2022-11-19 21:02:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1189, 63, '商家拒收货', '63', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒收货', '1', '2022-11-19 21:02:37', '1', '2022-11-19 21:03:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1190, 10, '售中退款', '10', 'trade_after_sale_type', 0, 'success', '', '交易售后的类型 - 售中退款', '1', '2022-11-19 21:05:05', '1', '2022-11-19 21:38:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1191, 20, '售后退款', '20', 'trade_after_sale_type', 0, 'primary', '', '交易售后的类型 - 售后退款', '1', '2022-11-19 21:05:32', '1', '2022-11-19 21:38:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1192, 10, '仅退款', '10', 'trade_after_sale_way', 0, 'primary', '', '交易售后的方式 - 仅退款', '1', '2022-11-19 21:39:19', '1', '2022-11-19 21:39:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1193, 20, '退货退款', '20', 'trade_after_sale_way', 0, 'success', '', '交易售后的方式 - 退货退款', '1', '2022-11-19 21:39:38', '1', '2022-11-19 21:39:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1194, 10, '微信小程序', '10', 'terminal', 0, 'default', '', '终端 - 微信小程序', '1', '2022-12-10 10:51:11', '1', '2022-12-10 10:51:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1195, 20, 'H5 网页', '20', 'terminal', 0, 'default', '', '终端 - H5 网页', '1', '2022-12-10 10:51:30', '1', '2022-12-10 10:51:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1196, 11, '微信公众号', '11', 'terminal', 0, 'default', '', '终端 - 微信公众号', '1', '2022-12-10 10:54:16', '1', '2022-12-10 10:52:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1197, 31, '苹果 App', '31', 'terminal', 0, 'default', '', '终端 - 苹果 App', '1', '2022-12-10 10:54:42', '1', '2022-12-10 10:52:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1198, 32, '安卓 App', '32', 'terminal', 0, 'default', '', '终端 - 安卓 App', '1', '2022-12-10 10:55:02', '1', '2022-12-10 10:59:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1199, 0, '普通订单', '0', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 普通订单', '1', '2022-12-10 16:34:14', '1', '2022-12-10 16:34:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1200, 1, '秒杀订单', '1', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 秒杀订单', '1', '2022-12-10 16:34:26', '1', '2022-12-10 16:34:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1201, 2, '砍价订单', '2', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 拼团订单', '1', '2022-12-10 16:34:36', '1', '2024-09-07 14:18:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1202, 3, '拼团订单', '3', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 砍价订单', '1', '2022-12-10 16:34:48', '1', '2024-09-07 14:18:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1203, 0, '待支付', '0', 'trade_order_status', 0, 'default', '', '交易订单状态 - 待支付', '1', '2022-12-10 16:49:29', '1', '2022-12-10 16:49:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1204, 10, '待发货', '10', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 待发货', '1', '2022-12-10 16:49:53', '1', '2022-12-10 16:51:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1205, 20, '已发货', '20', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 已发货', '1', '2022-12-10 16:50:13', '1', '2022-12-10 16:51:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1206, 30, '已完成', '30', 'trade_order_status', 0, 'success', '', '交易订单状态 - 已完成', '1', '2022-12-10 16:50:30', '1', '2022-12-10 16:51:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1207, 40, '已取消', '40', 'trade_order_status', 0, 'danger', '', '交易订单状态 - 已取消', '1', '2022-12-10 16:50:50', '1', '2022-12-10 16:51:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1208, 0, '未售后', '0', 'trade_order_item_after_sale_status', 0, 'info', '', '交易订单项的售后状态 - 未售后', '1', '2022-12-10 20:58:42', '1', '2022-12-10 20:59:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1209, 10, '售后中', '10', 'trade_order_item_after_sale_status', 0, 'primary', '', '交易订单项的售后状态 - 售后中', '1', '2022-12-10 20:59:21', '1', '2024-07-21 17:01:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1210, 20, '已退款', '20', 'trade_order_item_after_sale_status', 0, 'success', '', '交易订单项的售后状态 - 已退款', '1', '2022-12-10 20:59:46', '1', '2024-07-21 17:01:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1211, 1, '完全匹配', '1', 'mp_auto_reply_request_match', 0, 'primary', '', '公众号自动回复的请求关键字匹配模式 - 完全匹配', '1', '2023-01-16 23:30:39', '1', '2023-01-16 23:31:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1212, 2, '半匹配', '2', 'mp_auto_reply_request_match', 0, 'success', '', '公众号自动回复的请求关键字匹配模式 - 半匹配', '1', '2023-01-16 23:30:55', '1', '2023-01-16 23:31:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1213, 1, '文本', 'text', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 文本', '1', '2023-01-17 22:17:32', '1', '2023-01-17 22:17:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1214, 2, '图片', 'image', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图片', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1215, 3, '语音', 'voice', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 语音', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:20:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1216, 4, '视频', 'video', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:21:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1217, 5, '小视频', 'shortvideo', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 小视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1218, 6, '图文', 'news', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图文', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1219, 7, '音乐', 'music', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 音乐', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1220, 8, '地理位置', 'location', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 地理位置', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:23:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1221, 9, '链接', 'link', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 链接', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1222, 10, '事件', 'event', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 事件', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1223, 0, '初始化', '0', 'system_mail_send_status', 0, 'primary', '', '邮件发送状态 - 初始化\\n', '1', '2023-01-26 09:53:49', '1', '2023-01-26 16:36:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1224, 10, '发送成功', '10', 'system_mail_send_status', 0, 'success', '', '邮件发送状态 - 发送成功', '1', '2023-01-26 09:54:28', '1', '2023-01-26 16:36:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1225, 20, '发送失败', '20', 'system_mail_send_status', 0, 'danger', '', '邮件发送状态 - 发送失败', '1', '2023-01-26 09:54:50', '1', '2023-01-26 16:36:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1226, 30, '不发送', '30', 'system_mail_send_status', 0, 'info', '', '邮件发送状态 -  不发送', '1', '2023-01-26 09:55:06', '1', '2023-01-26 16:36:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1227, 1, '通知公告', '1', 'system_notify_template_type', 0, 'primary', '', '站内信模版的类型 - 通知公告', '1', '2023-01-28 10:35:59', '1', '2023-01-28 10:35:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1228, 2, '系统消息', '2', 'system_notify_template_type', 0, 'success', '', '站内信模版的类型 - 系统消息', '1', '2023-01-28 10:36:20', '1', '2023-01-28 10:36:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1230, 13, '支付宝条码支付', 'alipay_bar', 'pay_channel_code', 0, 'primary', '', '支付宝条码支付', '1', '2023-02-18 23:32:24', '1', '2023-07-19 20:09:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1231, 10, 'Vue2 Element UI 标准模版', '10', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:03:55', '1', '2023-04-13 00:03:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1232, 20, 'Vue3 Element Plus 标准模版', '20', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:08', '1', '2023-04-13 00:04:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1234, 30, 'Vben2.0 Ant Design Schema 模版', '30', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:26', '1', '2025-04-23 21:27:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1244, 0, '按件', '1', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:40', '1', '2023-05-21 22:46:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1245, 1, '按重量', '2', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:58', '1', '2023-05-21 22:46:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1246, 2, '按体积', '3', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:47:18', '1', '2023-05-21 22:47:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1335, 11, '订单积分抵扣', '11', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:27', '1', '2023-10-11 07:41:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1336, 1, '签到', '1', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:48', '1', '2023-08-20 11:59:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1341, 20, '已退款', '20', 'pay_order_status', 0, 'danger', '', '已退款', '1', '2023-07-19 18:05:37', '1', '2023-07-19 18:05:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1342, 21, '请求成功，但是结果失败', '21', 'pay_notify_status', 0, 'warning', '', '请求成功，但是结果失败', '1', '2023-07-19 18:10:47', '1', '2023-07-19 18:11:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1343, 22, '请求失败', '22', 'pay_notify_status', 0, 'warning', '', NULL, '1', '2023-07-19 18:11:05', '1', '2023-07-19 18:11:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1344, 4, '微信扫码支付', 'wx_native', 'pay_channel_code', 0, 'success', '', '微信扫码支付', '1', '2023-07-19 20:07:47', '1', '2023-07-19 20:09:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1345, 5, '微信条码支付', 'wx_bar', 'pay_channel_code', 0, 'success', '', '微信条码支付\\n', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1346, 1, '支付单', '1', 'pay_notify_type', 0, 'primary', '', '支付单', '1', '2023-07-20 12:23:17', '1', '2023-07-20 12:23:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1347, 2, '退款单', '2', 'pay_notify_type', 0, 'danger', '', NULL, '1', '2023-07-20 12:23:26', '1', '2023-07-20 12:23:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1348, 20, '模拟支付', 'mock', 'pay_channel_code', 0, 'default', '', '模拟支付', '1', '2023-07-29 11:10:51', '1', '2023-07-29 03:14:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1349, 12, '订单积分抵扣（整单取消）', '12', 'member_point_biz_type', 0, '', '', '', '1', '2023-08-20 12:00:03', '1', '2023-10-11 07:42:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1350, 0, '管理员调整', '0', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1351, 1, '邀新奖励', '1', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1352, 11, '下单奖励', '11', 'member_experience_biz_type', 0, 'success', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1353, 12, '下单奖励（整单取消）', '12', 'member_experience_biz_type', 0, 'warning', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1354, 4, '签到奖励', '4', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1355, 5, '抽奖奖励', '5', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1356, 1, '快递发货', '1', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:04:55', '1', '2023-08-23 00:04:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1357, 2, '用户自提', '2', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:05:05', '1', '2023-08-23 00:05:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1358, 3, '品类劵', '3', 'promotion_product_scope', 0, 'default', '', '', '1', '2023-09-01 23:43:07', '1', '2023-09-28 00:27:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1359, 1, '人人分销', '1', 'brokerage_enabled_condition', 0, '', '', '所有用户都可以分销', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1360, 2, '指定分销', '2', 'brokerage_enabled_condition', 0, '', '', '仅可后台手动设置推广员', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1361, 1, '首次绑定', '1', 'brokerage_bind_mode', 0, '', '', '只要用户没有推广人，随时都可以绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1362, 2, '注册绑定', '2', 'brokerage_bind_mode', 0, '', '', '仅新用户注册时才能绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1363, 3, '覆盖绑定', '3', 'brokerage_bind_mode', 0, '', '', '如果用户已经有推广人，推广人会被变更', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1364, 1, '钱包', '1', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1365, 2, '银行卡', '2', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1366, 3, '微信收款码', '3', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1367, 4, '支付宝收款码', '4', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1368, 1, '订单返佣', '1', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1369, 2, '申请提现', '2', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1370, 3, '申请提现驳回', '3', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1371, 0, '待结算', '0', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1372, 1, '已结算', '1', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1373, 2, '已取消', '2', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1374, 0, '审核中', '0', 'brokerage_withdraw_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1375, 10, '审核通过', '10', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1376, 11, '提现成功', '11', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1377, 20, '审核不通过', '20', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1378, 21, '提现失败', '21', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1379, 0, '工商银行', '0', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1380, 1, '建设银行', '1', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1381, 2, '农业银行', '2', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1382, 3, '中国银行', '3', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1383, 4, '交通银行', '4', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1384, 5, '招商银行', '5', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1385, 21, '钱包', 'wallet', 'pay_channel_code', 0, 'primary', '', '', '1', '2023-10-01 21:46:19', '1', '2023-10-01 21:48:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1386, 1, '砍价中', '1', 'promotion_bargain_record_status', 0, 'default', '', '', '1', '2023-10-05 10:41:26', '1', '2023-10-05 10:41:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1387, 2, '砍价成功', '2', 'promotion_bargain_record_status', 0, 'success', '', '', '1', '2023-10-05 10:41:39', '1', '2023-10-05 10:41:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1388, 3, '砍价失败', '3', 'promotion_bargain_record_status', 0, 'warning', '', '', '1', '2023-10-05 10:41:57', '1', '2023-10-05 10:41:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1389, 0, '拼团中', '0', 'promotion_combination_record_status', 0, '', '', '', '1', '2023-10-08 07:24:44', '1', '2024-10-13 10:08:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1390, 1, '拼团成功', '1', 'promotion_combination_record_status', 0, 'success', '', '', '1', '2023-10-08 07:24:56', '1', '2024-10-13 10:08:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1391, 2, '拼团失败', '2', 'promotion_combination_record_status', 0, 'warning', '', '', '1', '2023-10-08 07:25:11', '1', '2024-10-13 10:08:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1392, 2, '管理员修改', '2', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:41:34', '1', '2023-10-11 07:41:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1393, 13, '订单积分抵扣（单个退款）', '13', 'member_point_biz_type', 0, '', '', '', '1', '2023-10-11 07:42:29', '1', '2023-10-11 07:42:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1394, 21, '订单积分奖励', '21', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:44', '1', '2023-10-11 07:42:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1395, 22, '订单积分奖励（整单取消）', '22', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:55', '1', '2023-10-11 07:43:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1396, 23, '订单积分奖励（单个退款）', '23', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:43:16', '1', '2023-10-11 07:43:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1397, 13, '下单奖励（单个退款）', '13', 'member_experience_biz_type', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1398, 5, '网上转账', '5', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:24', '1', '2023-10-18 21:55:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1399, 6, '支付宝', '6', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:38', '1', '2023-10-18 21:55:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1400, 7, '微信支付', '7', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:53', '1', '2023-10-18 21:55:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1401, 8, '其他', '8', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:56:06', '1', '2023-10-18 21:56:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1402, 1, 'IT', '1', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:15', '1', '2024-02-18 23:30:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1403, 2, '金融业', '2', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:29', '1', '2024-02-18 23:30:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1404, 3, '房地产', '3', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:41', '1', '2024-02-18 23:30:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1405, 4, '商业服务', '4', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:54', '1', '2024-02-18 23:30:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1406, 5, '运输/物流', '5', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:03', '1', '2024-02-18 23:31:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1407, 6, '生产', '6', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:13', '1', '2024-02-18 23:31:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1408, 7, '政府', '7', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:27', '1', '2024-02-18 23:31:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1409, 8, '文化传媒', '8', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:37', '1', '2024-02-18 23:31:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1422, 1, 'A （重点客户）', '1', 'crm_customer_level', 0, 'primary', '', '', '1', '2023-10-28 23:07:13', '1', '2023-10-28 23:07:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1423, 2, 'B （普通客户）', '2', 'crm_customer_level', 0, 'info', '', '', '1', '2023-10-28 23:07:35', '1', '2023-10-28 23:07:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1424, 3, 'C （非优先客户）', '3', 'crm_customer_level', 0, 'default', '', '', '1', '2023-10-28 23:07:53', '1', '2023-10-28 23:07:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1425, 1, '促销', '1', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:29', '1', '2023-10-28 23:08:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1426, 2, '搜索引擎', '2', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:39', '1', '2023-10-28 23:08:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1427, 3, '广告', '3', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:47', '1', '2023-10-28 23:08:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1428, 4, '转介绍', '4', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:58', '1', '2023-10-28 23:08:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1429, 5, '线上注册', '5', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:12', '1', '2023-10-28 23:09:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1430, 6, '线上咨询', '6', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:22', '1', '2023-10-28 23:09:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1431, 7, '预约上门', '7', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:39', '1', '2023-10-28 23:09:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1432, 8, '陌拜', '8', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:04', '1', '2023-10-28 23:10:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1433, 9, '电话咨询', '9', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:18', '1', '2023-10-28 23:10:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1434, 10, '邮件咨询', '10', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:33', '1', '2023-10-28 23:10:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1435, 10, 'Gitee', '10', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:42', '1', '2023-11-04 13:04:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1436, 20, '钉钉', '20', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:54', '1', '2023-11-04 13:04:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1437, 30, '企业微信', '30', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:09', '1', '2023-11-04 13:05:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1438, 31, '微信公众平台', '31', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:18', '1', '2023-11-04 13:05:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1439, 32, '微信开放平台', '32', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:30', '1', '2023-11-04 13:05:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1440, 34, '微信小程序', '34', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1441, 1, '上架', '1', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:34', '1', '2023-10-30 21:49:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1442, 0, '下架', '0', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:13', '1', '2023-10-30 21:49:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1443, 15, '子表', '15', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-13 23:06:16', '1', '2023-11-13 23:06:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1444, 10, '主表（标准模式）', '10', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:32:49', '1', '2023-11-14 12:32:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1445, 11, '主表（ERP 模式）', '11', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:33:05', '1', '2023-11-14 12:33:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1446, 12, '主表（内嵌模式）', '12', 'infra_codegen_template_type', 0, '', '', '', '1', '2023-11-14 12:33:31', '1', '2023-11-14 12:33:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1447, 1, '负责人', '1', 'crm_permission_level', 0, 'default', '', '', '1', '2023-11-30 09:53:12', '1', '2023-11-30 09:53:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1448, 2, '只读', '2', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:29', '1', '2023-11-30 09:53:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1449, 3, '读写', '3', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:36', '1', '2023-11-30 09:53:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1450, 0, '未提交', '0', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:56:59', '1', '2023-11-30 18:56:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1451, 10, '审批中', '10', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:10', '1', '2023-11-30 18:57:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1452, 20, '审核通过', '20', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:24', '1', '2023-11-30 18:57:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1453, 30, '审核不通过', '30', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:32', '1', '2023-11-30 18:57:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1454, 40, '已取消', '40', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:42', '1', '2023-11-30 18:57:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1456, 1, '支票', '1', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:29', '1', '2023-10-18 21:54:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1457, 2, '现金', '2', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:41', '1', '2023-10-18 21:54:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1458, 3, '邮政汇款', '3', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:53', '1', '2023-10-18 21:54:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1459, 4, '电汇', '4', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:07', '1', '2023-10-18 21:55:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1461, 1, '个', '1', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:26', '1', '2023-12-05 23:02:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1462, 2, '块', '2', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:34', '1', '2023-12-05 23:02:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1463, 3, '只', '3', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:57', '1', '2023-12-05 23:02:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1464, 4, '把', '4', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:05', '1', '2023-12-05 23:03:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1465, 5, '枚', '5', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:14', '1', '2023-12-05 23:03:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1466, 6, '瓶', '6', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:20', '1', '2023-12-05 23:03:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1467, 7, '盒', '7', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:30', '1', '2023-12-05 23:03:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1468, 8, '台', '8', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:41', '1', '2023-12-05 23:03:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1469, 9, '吨', '9', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:48', '1', '2023-12-05 23:03:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1470, 10, '千克', '10', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:03', '1', '2023-12-05 23:04:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1471, 11, '米', '11', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:12', '1', '2023-12-05 23:04:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1472, 12, '箱', '12', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:25', '1', '2023-12-05 23:04:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1473, 13, '套', '13', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:34', '1', '2023-12-05 23:04:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1474, 1, '打电话', '1', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:20', '1', '2024-01-15 20:48:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1475, 2, '发短信', '2', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:31', '1', '2024-01-15 20:48:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1476, 3, '上门拜访', '3', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:07', '1', '2024-01-15 20:49:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1477, 4, '微信沟通', '4', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:15', '1', '2024-01-15 20:49:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1482, 4, '转账失败', '20', 'pay_transfer_status', 0, 'warning', '', '', '1', '2023-10-28 16:24:16', '1', '2025-05-08 12:59:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1483, 3, '转账成功', '10', 'pay_transfer_status', 0, 'success', '', '', '1', '2023-10-28 16:23:50', '1', '2025-05-08 12:58:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1484, 2, '转账进行中', '5', 'pay_transfer_status', 0, 'info', '', '', '1', '2023-10-28 16:23:12', '1', '2025-05-08 12:58:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1485, 1, '等待转账', '0', 'pay_transfer_status', 0, 'default', '', '', '1', '2023-10-28 16:21:43', '1', '2023-10-28 16:23:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1486, 10, '其它入库', '10', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:07:25', '1', '2024-02-05 18:07:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1487, 11, '其它入库（作废）', '11', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:08:07', '1', '2024-02-05 19:20:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1488, 20, '其它出库', '20', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:08:51', '1', '2024-02-05 18:08:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1489, 21, '其它出库（作废）', '21', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:09:00', '1', '2024-02-05 19:20:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1490, 10, '未审核', '10', 'erp_audit_status', 0, 'default', '', '', '1', '2024-02-06 00:00:21', '1', '2024-02-06 00:00:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1491, 20, '已审核', '20', 'erp_audit_status', 0, 'success', '', '', '1', '2024-02-06 00:00:35', '1', '2024-02-06 00:00:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1492, 30, '调拨入库', '30', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:19', '1', '2024-02-07 12:36:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1493, 31, '调拨入库（作废）', '31', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:29', '1', '2024-02-07 20:37:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1494, 32, '调拨出库', '32', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:38', '1', '2024-02-07 12:36:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1495, 33, '调拨出库（作废）', '33', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:49', '1', '2024-02-07 20:37:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1496, 40, '盘盈入库', '40', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:53:00', '1', '2024-02-08 08:53:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1497, 41, '盘盈入库（作废）', '41', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:53:39', '1', '2024-02-16 19:40:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1498, 42, '盘亏出库', '42', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:54:16', '1', '2024-02-08 08:54:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1499, 43, '盘亏出库（作废）', '43', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:54:31', '1', '2024-02-16 19:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1500, 50, '销售出库', '50', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-11 21:47:25', '1', '2024-02-11 21:50:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1501, 51, '销售出库（作废）', '51', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-11 21:47:37', '1', '2024-02-11 21:51:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1502, 60, '销售退货入库', '60', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-12 06:51:05', '1', '2024-02-12 06:51:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1503, 61, '销售退货入库（作废）', '61', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-12 06:51:18', '1', '2024-02-12 06:51:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1504, 70, '采购入库', '70', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:02', '1', '2024-02-16 13:10:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1505, 71, '采购入库（作废）', '71', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:10', '1', '2024-02-16 19:40:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1506, 80, '采购退货出库', '80', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:17', '1', '2024-02-16 13:10:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1507, 81, '采购退货出库（作废）', '81', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:26', '1', '2024-02-16 19:40:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1509, 3, '审批不通过', '3', 'bpm_process_instance_status', 0, 'danger', '', '', '1', '2024-03-16 16:12:06', '1', '2024-03-16 16:12:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1510, 4, '已取消', '4', 'bpm_process_instance_status', 0, 'warning', '', '', '1', '2024-03-16 16:12:22', '1', '2024-03-16 16:12:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1511, 5, '已退回', '5', 'bpm_task_status', 0, 'warning', '', '', '1', '2024-03-16 19:10:46', '1', '2024-03-08 22:41:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1512, 6, '委派中', '6', 'bpm_task_status', 0, 'primary', '', '', '1', '2024-03-17 10:06:22', '1', '2024-03-08 22:41:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1513, 7, '审批通过中', '7', 'bpm_task_status', 0, 'success', '', '', '1', '2024-03-17 10:06:47', '1', '2024-03-08 22:41:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1514, 0, '待审批', '0', 'bpm_task_status', 0, 'info', '', '', '1', '2024-03-17 10:07:11', '1', '2024-03-08 22:41:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1515, 35, '发起人自选', '35', 'bpm_task_candidate_strategy', 0, '', '', '', '1', '2024-03-22 19:45:16', '1', '2024-03-22 19:45:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1516, 1, '执行监听器', 'execution', 'bpm_process_listener_type', 0, 'primary', '', '', '1', '2024-03-23 12:54:03', '1', '2024-03-23 19:14:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1517, 1, '任务监听器', 'task', 'bpm_process_listener_type', 0, 'success', '', '', '1', '2024-03-23 12:54:13', '1', '2024-03-23 19:14:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1526, 1, 'Java 类', 'class', 'bpm_process_listener_value_type', 0, 'primary', '', '', '1', '2024-03-23 15:08:45', '1', '2024-03-23 19:14:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1527, 2, '表达式', 'expression', 'bpm_process_listener_value_type', 0, 'success', '', '', '1', '2024-03-23 15:09:06', '1', '2024-03-23 19:14:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1528, 3, '代理表达式', 'delegateExpression', 'bpm_process_listener_value_type', 0, 'info', '', '', '1', '2024-03-23 15:11:23', '1', '2024-03-23 19:14:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1529, 1, '天', '1', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:26', '1', '2024-03-29 22:50:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1530, 2, '周', '2', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:36', '1', '2024-03-29 22:50:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1531, 3, '月', '3', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:46', '1', '2024-03-29 22:50:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1532, 4, '季度', '4', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:01', '1', '2024-03-29 22:51:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1533, 5, '年', '5', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:07', '1', '2024-03-29 22:51:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1534, 1, '赢单', '1', 'crm_business_end_status_type', 0, 'success', '', '', '1', '2024-04-13 23:26:57', '1', '2024-04-13 23:26:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1535, 2, '输单', '2', 'crm_business_end_status_type', 0, 'primary', '', '', '1', '2024-04-13 23:27:31', '1', '2024-04-13 23:27:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1536, 3, '无效', '3', 'crm_business_end_status_type', 0, 'info', '', '', '1', '2024-04-13 23:27:59', '1', '2024-04-13 23:27:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1537, 1, 'OpenAI', 'OpenAI', 'ai_platform', 0, '', '', '', '1', '2024-05-09 22:33:47', '1', '2024-05-09 22:58:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1538, 2, 'Ollama', 'Ollama', 'ai_platform', 0, '', '', '', '1', '2024-05-17 23:02:55', '1', '2024-05-17 23:02:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1539, 3, '文心一言', 'YiYan', 'ai_platform', 0, '', '', '', '1', '2024-05-18 09:24:20', '1', '2024-05-18 09:29:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1540, 4, '讯飞星火', 'XingHuo', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:08:56', '1', '2024-05-18 10:08:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1541, 5, '通义千问', 'TongYi', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:32:29', '1', '2024-07-06 15:42:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1542, 6, 'StableDiffusion', 'StableDiffusion', 'ai_platform', 0, '', '', '', '1', '2024-06-01 15:09:31', '1', '2024-06-01 15:10:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1543, 10, '进行中', '10', 'ai_image_status', 0, 'primary', '', '', '1', '2024-06-26 20:51:41', '1', '2024-06-26 20:52:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1544, 20, '已完成', '20', 'ai_image_status', 0, 'success', '', '', '1', '2024-06-26 20:52:07', '1', '2024-06-26 20:52:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1545, 30, '已失败', '30', 'ai_image_status', 0, 'warning', '', '', '1', '2024-06-26 20:52:25', '1', '2024-06-26 20:52:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1546, 7, 'Midjourney', 'Midjourney', 'ai_platform', 0, '', '', '', '1', '2024-06-26 22:14:46', '1', '2024-06-26 22:14:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1547, 10, '进行中', '10', 'ai_music_status', 0, 'primary', '', '', '1', '2024-06-27 22:45:22', '1', '2024-06-28 00:56:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1548, 20, '已完成', '20', 'ai_music_status', 0, 'success', '', '', '1', '2024-06-27 22:45:33', '1', '2024-06-28 00:56:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1549, 30, '已失败', '30', 'ai_music_status', 0, 'danger', '', '', '1', '2024-06-27 22:45:44', '1', '2024-06-28 00:56:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1550, 1, '歌词模式', '1', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:31', '1', '2024-06-28 01:22:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1554, 13, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2025-02-24 20:18:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1558, 7, '博客文章', '7', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:23', '1', '2024-07-07 15:50:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1559, 8, '想法', '8', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:31', '1', '2024-07-07 15:50:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1560, 9, '大纲', '9', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:37', '1', '2024-07-07 15:50:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1561, 1, '自动', '1', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:06', '1', '2024-07-07 15:51:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1562, 2, '友善', '2', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:19', '1', '2024-07-07 15:51:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1563, 3, '随意', '3', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:27', '1', '2024-07-07 15:51:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1564, 4, '友好', '4', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:37', '1', '2024-07-07 15:51:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1565, 5, '专业', '5', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:49', '1', '2024-07-07 15:52:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1566, 6, '诙谐', '6', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:15', '1', '2024-07-07 15:52:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1567, 7, '有趣', '7', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:24', '1', '2024-07-07 15:52:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1568, 8, '正式', '8', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:54:33', '1', '2024-07-07 15:54:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1569, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1570, 1, '自动', '1', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:34', '1', '2024-07-07 15:19:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1571, 2, '电子邮件', '2', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:50', '1', '2024-07-07 15:49:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1572, 3, '消息', '3', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:01', '1', '2024-07-07 15:49:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1573, 4, '评论', '4', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:13', '1', '2024-07-07 15:49:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1574, 1, '自动', '1', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:18', '1', '2024-07-07 15:44:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1575, 2, '中文', '2', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:28', '1', '2024-07-07 15:44:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1576, 3, '英文', '3', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:37', '1', '2024-07-07 15:44:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1577, 4, '韩语', '4', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:28', '1', '2024-07-07 15:46:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1578, 5, '日语', '5', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:44', '1', '2024-07-07 15:46:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1579, 1, '自动', '1', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:34', '1', '2024-07-07 15:48:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1580, 2, '短', '2', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:44', '1', '2024-07-07 15:48:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1581, 3, '中等', '3', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:52', '1', '2024-07-07 15:48:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1582, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1584, 1, '撰写', '1', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:00', '1', '2024-07-10 21:26:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1585, 2, '回复', '2', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:06', '1', '2024-07-10 21:26:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1586, 2, '腾讯云', 'TENCENT', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:16', '1', '2024-07-22 22:23:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1587, 3, '华为云', 'HUAWEI', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:46', '1', '2024-07-22 22:23:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1588, 1, 'OpenAI 微软', 'AzureOpenAI', 'ai_platform', 0, '', '', '', '1', '2024-08-10 14:07:41', '1', '2024-08-10 14:07:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1589, 10, 'BPMN 设计器', '10', 'bpm_model_type', 0, 'primary', '', '', '1', '2024-08-26 15:22:17', '1', '2024-08-26 16:46:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1590, 20, 'SIMPLE 设计器', '20', 'bpm_model_type', 0, 'success', '', '', '1', '2024-08-26 15:22:27', '1', '2024-08-26 16:45:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1591, 4, '七牛云', 'QINIU', 'system_sms_channel_code', 0, '', '', '', '1', '2024-08-31 08:45:03', '1', '2024-08-31 08:45:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1592, 3, '新人券', '3', 'promotion_coupon_take_type', 0, 'info', '', '新人注册后，自动发放', '1', '2024-09-03 11:57:16', '1', '2024-09-03 11:57:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1593, 5, '微信零钱', '5', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2024-10-13 11:06:48', '1', '2025-05-10 08:24:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', '2025-02-23 19:51:40', '1', '2025-02-23 19:52:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', '2025-02-23 20:58:04', '1', '2025-02-23 20:58:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', '2025-02-24 20:19:09', '1', '2025-02-24 20:19:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1686, 1, '聊天', '1', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:26:34', '1', '2025-03-03 12:26:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1687, 2, '图像', '2', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:23', '1', '2025-03-03 12:27:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1688, 3, '音频', '3', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:51', '1', '2025-03-03 12:27:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1689, 4, '视频', '4', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:03', '1', '2025-03-03 12:28:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1690, 5, '向量', '5', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:15', '1', '2025-03-03 12:28:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1693, 15, '月之暗面', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-03-11 20:05:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2000, 0, '标准数据格式（JSON）', '0', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:26', '1', '2025-03-17 09:28:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2001, 1, '透传/自定义', '1', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:37', '1', '2025-03-17 09:28:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2025-03-17 09:28:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2025-03-17 09:28:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2025-03-17 09:28:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2025-03-17 09:28:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2025-03-17 09:28:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2007, 0, '弱校验', '0', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:05:48', '1', '2025-03-17 09:28:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2008, 1, '免校验', '1', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:06:03', '1', '2025-03-17 09:28:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2025-03-17 09:28:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2010, 1, '蜂窝（2G / 3G / 4G / 5G）', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2025-03-17 09:28:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2025-03-17 09:28:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2025-03-17 09:28:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2013, 0, '自定义', '0', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:10', '1', '2025-03-17 09:28:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2014, 1, 'Modbus', '1', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:21', '1', '2025-03-17 09:28:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2015, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2025-03-17 09:29:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2016, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2025-03-17 09:29:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2017, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2025-03-17 09:29:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-03-17 09:29:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-03-17 09:29:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-03-17 09:29:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2025-03-17 09:29:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2025-03-17 09:29:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2025-03-17 09:29:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2024, 1, 'JAR 部署', '0', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:32', '1', '2025-03-17 09:29:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2025, 2, '独立部署', '1', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:43', '1', '2025-03-17 09:29:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2026, 0, '停止', '0', 'iot_plugin_status', 0, 'danger', '', '', '1', '2024-12-13 11:07:37', '1', '2025-03-17 09:29:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2027, 1, '运行', '1', 'iot_plugin_status', 0, '', '', '', '1', '2024-12-13 11:07:45', '1', '2025-03-17 09:34:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2028, 0, '普通插件', '0', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:32', '1', '2025-03-17 09:34:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2029, 1, '设备插件', '1', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2033, 4, 'PH值', 'pH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2034, 5, '土壤EC值', 'dS/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2035, 6, '太阳总辐射', 'W/㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2036, 7, '降雨量', 'mm/hour', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2037, 8, '乏', 'var', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2038, 9, '厘泊', 'cP', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2039, 10, '饱和度', 'aw', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2040, 11, '个', 'pcs', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2041, 12, '厘斯', 'cst', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2042, 13, '巴', 'bar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2043, 14, '纳克每升', 'ppt', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2044, 15, '微克每升', 'ppb', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2045, 16, '微西每厘米', 'uS/cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2046, 17, '牛顿每库仑', 'N/C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2047, 18, '伏特每米', 'V/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2048, 19, '滴速', 'ml/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2049, 20, '毫米汞柱', 'mmHg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2050, 21, '血糖', 'mmol/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2051, 22, '毫米每秒', 'mm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2052, 23, '转每分钟', 'turn/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2053, 24, '次', 'count', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2054, 25, '档', 'gear', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2055, 26, '步', 'stepCount', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2056, 27, '标准立方米每小时', 'Nm3/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2057, 28, '千伏', 'kV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2058, 29, '千伏安', 'kVA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2060, 30, '千乏', 'kVar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2061, 31, '微瓦每平方厘米', 'uw/cm2', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2062, 32, '只', '只', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2063, 33, '相对湿度', '%RH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2064, 34, '立方米每秒', 'm³/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2065, 35, '公斤每秒', 'kg/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2066, 36, '转每分钟', 'r/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2067, 37, '吨每小时', 't/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2068, 38, '千卡每小时', 'KCL/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2069, 39, '升每秒', 'L/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2070, 40, '兆帕', 'Mpa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2071, 41, '立方米每小时', 'm³/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2072, 42, '千乏时', 'kvarh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2073, 43, '微克每升', 'μg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2074, 44, '千卡路里', 'kcal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2075, 45, '吉字节', 'GB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2076, 46, '兆字节', 'MB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2077, 47, '千字节', 'KB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2078, 48, '字节', 'B', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2079, 49, '微克每平方分米每天', 'μg/(d㎡·d)', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2080, 50, '无', '', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2081, 51, '百万分率', 'ppm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2082, 52, '像素', 'pixel', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2083, 53, '照度', 'Lux', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2084, 54, '重力加速度', 'grav', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2085, 55, '分贝', 'dB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2086, 56, '百分比', '%', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2087, 57, '流明', 'lm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2088, 58, '比特', 'bit', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2089, 59, '克每毫升', 'g/mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2090, 60, '克每升', 'g/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2091, 61, '毫克每升', 'mg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2092, 62, '微克每立方米', 'μg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2093, 63, '毫克每立方米', 'mg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2094, 64, '克每立方米', 'g/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2095, 65, '千克每立方米', 'kg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2096, 66, '纳法', 'nF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2097, 67, '皮法', 'pF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2098, 68, '微法', 'μF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2099, 69, '法拉', 'F', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2100, 70, '欧姆', 'Ω', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2101, 71, '微安', 'μA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2102, 72, '毫安', 'mA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2103, 73, '千安', 'kA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2104, 74, '安培', 'A', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2105, 75, '毫伏', 'mV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2106, 76, '伏特', 'V', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2107, 77, '毫秒', 'ms', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2108, 78, '秒', 's', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2109, 79, '分钟', 'min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2110, 80, '小时', 'h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2111, 81, '日', 'day', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2112, 82, '周', 'week', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2113, 83, '月', 'month', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2114, 84, '年', 'year', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2115, 85, '节', 'kn', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2116, 86, '千米每小时', 'km/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2117, 87, '米每秒', 'm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2118, 88, '秒', '″', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2119, 89, '分', '′', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2120, 90, '度', '°', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2121, 91, '弧度', 'rad', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2122, 92, '赫兹', 'Hz', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2123, 93, '微瓦', 'μW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2124, 94, '毫瓦', 'mW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2125, 95, '千瓦特', 'kW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2126, 96, '瓦特', 'W', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2127, 97, '卡路里', 'cal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2128, 98, '千瓦时', 'kW·h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2129, 99, '瓦时', 'Wh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2130, 100, '电子伏', 'eV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2131, 101, '千焦', 'kJ', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2132, 102, '焦耳', 'J', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2133, 103, '华氏度', '℉', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2134, 104, '开尔文', 'K', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2135, 105, '吨', 't', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2136, 106, '摄氏度', '°C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2137, 107, '毫帕', 'mPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2138, 108, '百帕', 'hPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2139, 109, '千帕', 'kPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2140, 110, '帕斯卡', 'Pa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2141, 111, '毫克', 'mg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2142, 112, '克', 'g', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2143, 113, '千克', 'kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2144, 114, '牛', 'N', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2145, 115, '毫升', 'mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2146, 116, '升', 'L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2147, 117, '立方毫米', 'mm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2148, 118, '立方厘米', 'cm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2149, 119, '立方千米', 'km³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2150, 120, '立方米', 'm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2151, 121, '公顷', 'h㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2152, 122, '平方厘米', 'c㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2153, 123, '平方毫米', 'm㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2154, 124, '平方千米', 'k㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2155, 125, '平方米', '㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2156, 126, '纳米', 'nm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2157, 127, '微米', 'μm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2158, 128, '毫米', 'mm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2159, 129, '厘米', 'cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2163, 1, '输入', '1', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:24', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2164, 2, '输出', '2', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:36', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2165, 1, 'HTTP', '1', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:39:54', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2166, 2, 'TCP', '2', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:06', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2167, 3, 'WEBSOCKET', '3', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:24', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2168, 10, 'MQTT', '10', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:37', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2169, 20, 'DATABASE', '20', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:05', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2170, 21, 'REDIS_STREAM', '21', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:18', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2171, 30, 'ROCKETMQ', '30', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:30', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2172, 31, 'RABBITMQ', '31', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:47', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2173, 32, 'KAFKA', '32', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:59', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3000, 16, '百川智能', 'BaiChuan', 'ai_platform', 0, '', '', '', '1', '2025-03-23 12:15:46', '1', '2025-03-23 12:15:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3001, 50, 'Vben5.0 Ant Design Schema 模版', '40', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-04-23 21:47:47', '1', '2025-05-02 12:01:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3002, 6, '支付宝余额', '6', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2025-05-10 08:24:49', '1', '2025-05-10 08:24:49', '0');\nINSERT 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');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_dict_data_seq;\nCREATE SEQUENCE system_dict_data_seq\n    START 3003;\n\n-- ----------------------------\n-- Table structure for system_dict_type\n-- ----------------------------\nDROP TABLE IF EXISTS system_dict_type;\nCREATE TABLE system_dict_type\n(\n    id           int8         NOT NULL,\n    name         varchar(100) NULL     DEFAULT '',\n    type         varchar(100) NULL     DEFAULT '',\n    status       int2         NOT NULL DEFAULT 0,\n    remark       varchar(500) NULL     DEFAULT NULL,\n    creator      varchar(64)  NULL     DEFAULT '',\n    create_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater      varchar(64)  NULL     DEFAULT '',\n    update_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted      int2         NOT NULL DEFAULT 0,\n    deleted_time timestamp    NULL     DEFAULT NULL\n);\n\nALTER TABLE system_dict_type\n    ADD CONSTRAINT pk_system_dict_type PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dict_type.id IS '字典主键';\nCOMMENT ON COLUMN system_dict_type.name IS '字典名称';\nCOMMENT ON COLUMN system_dict_type.type IS '字典类型';\nCOMMENT ON COLUMN system_dict_type.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_type.remark IS '备注';\nCOMMENT ON COLUMN system_dict_type.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_type.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_type.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_type.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_type.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dict_type.deleted_time IS '删除时间';\nCOMMENT ON TABLE system_dict_type IS '字典类型表';\n\n-- ----------------------------\n-- Records of system_dict_type\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1, '用户性别', 'system_user_sex', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2022-05-16 20:29:32', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (6, '参数类型', 'infra_config_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:36:54', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (7, '通知类型', 'system_notice_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:35:26', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (9, '操作类型', 'infra_operate_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (10, '系统状态', 'common_status', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:21:28', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (11, 'Boolean 是否类型', 'infra_boolean_string', 0, 'boolean 转是否', '', '2021-01-19 03:20:08', '', '2022-02-01 16:37:10', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (104, '登陆结果', 'system_login_result', 0, '登陆结果', '', '2021-01-18 06:17:11', '', '2022-02-01 16:36:00', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (106, '代码生成模板类型', 'infra_codegen_template_type', 0, NULL, '', '2021-02-05 07:08:06', '1', '2022-05-16 20:26:50', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (107, '定时任务状态', 'infra_job_status', 0, NULL, '', '2021-02-07 07:44:16', '', '2022-02-01 16:51:11', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (108, '定时任务日志状态', 'infra_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2022-02-01 16:50:43', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (110, 'API 异常数据的处理状态', 'infra_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2022-02-01 16:50:53', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (111, '短信渠道编码', 'system_sms_channel_code', 0, NULL, '1', '2021-04-05 01:04:50', '1', '2022-02-16 02:09:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (112, '短信模板的类型', 'system_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:43', '1', '2022-02-01 16:35:06', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (113, '短信发送状态', 'system_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:03', '1', '2022-02-01 16:35:09', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (114, '短信接收状态', 'system_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:14', '1', '2022-02-01 16:35:14', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (116, '登陆日志的类型', 'system_login_type', 0, '登陆日志的类型', '1', '2021-10-06 00:50:46', '1', '2022-02-01 16:35:56', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (117, 'OA 请假类型', 'bpm_oa_leave_type', 0, NULL, '1', '2021-09-21 22:34:33', '1', '2022-01-22 10:41:37', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (130, '支付渠道编码类型', 'pay_channel_code', 0, '支付渠道的编码', '1', '2021-12-03 10:35:08', '1', '2023-07-10 10:11:39', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (131, '支付回调状态', 'pay_notify_status', 0, '支付回调状态（包括退款回调）', '1', '2021-12-03 10:53:29', '1', '2023-07-19 18:09:43', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (132, '支付订单状态', 'pay_order_status', 0, '支付订单状态', '1', '2021-12-03 11:17:50', '1', '2021-12-03 11:17:50', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (134, '退款订单状态', 'pay_refund_status', 0, '退款订单状态', '1', '2021-12-10 16:42:50', '1', '2023-07-19 10:13:17', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (139, '流程实例的状态', 'bpm_process_instance_status', 0, '流程实例的状态', '1', '2022-01-07 23:46:42', '1', '2022-01-07 23:46:42', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (140, '流程实例的结果', 'bpm_task_status', 0, '流程实例的结果', '1', '2022-01-07 23:48:10', '1', '2024-03-08 22:42:03', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (141, '流程的表单类型', 'bpm_model_form_type', 0, '流程的表单类型', '103', '2022-01-11 23:50:45', '103', '2022-01-11 23:50:45', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (142, '任务分配规则的类型', 'bpm_task_candidate_strategy', 0, 'BPM 任务的候选人的策略', '103', '2022-01-12 23:21:04', '103', '2024-03-06 02:53:59', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (144, '代码生成的场景枚举', 'infra_codegen_scene', 0, '代码生成的场景枚举', '1', '2022-02-02 13:14:45', '1', '2022-03-10 16:33:46', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (145, '角色类型', 'system_role_type', 0, '角色类型', '1', '2022-02-16 13:01:46', '1', '2022-02-16 13:01:46', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (146, '文件存储器', 'infra_file_storage', 0, '文件存储器', '1', '2022-03-15 00:24:38', '1', '2022-03-15 00:24:38', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (147, 'OAuth 2.0 授权类型', 'system_oauth2_grant_type', 0, 'OAuth 2.0 授权类型（模式）', '1', '2022-05-12 00:20:52', '1', '2022-05-11 16:25:49', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (149, '商品 SPU 状态', 'product_spu_status', 0, '商品 SPU 状态', '1', '2022-10-24 21:19:04', '1', '2022-10-24 21:19:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (150, '优惠类型', 'promotion_discount_type', 0, '优惠类型', '1', '2022-11-01 12:46:06', '1', '2022-11-01 12:46:06', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (151, '优惠劵模板的有限期类型', 'promotion_coupon_template_validity_type', 0, '优惠劵模板的有限期类型', '1', '2022-11-02 00:06:20', '1', '2022-11-04 00:08:26', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (152, '营销的商品范围', 'promotion_product_scope', 0, '营销的商品范围', '1', '2022-11-02 00:28:01', '1', '2022-11-02 00:28:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (153, '优惠劵的状态', 'promotion_coupon_status', 0, '优惠劵的状态', '1', '2022-11-04 00:14:49', '1', '2022-11-04 00:14:49', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (154, '优惠劵的领取方式', 'promotion_coupon_take_type', 0, '优惠劵的领取方式', '1', '2022-11-04 19:12:27', '1', '2022-11-04 19:12:27', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (155, '促销活动的状态', 'promotion_activity_status', 0, '促销活动的状态', '1', '2022-11-04 22:54:23', '1', '2022-11-04 22:54:23', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (156, '营销的条件类型', 'promotion_condition_type', 0, '营销的条件类型', '1', '2022-11-04 22:59:23', '1', '2022-11-04 22:59:23', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (157, '交易售后状态', 'trade_after_sale_status', 0, '交易售后状态', '1', '2022-11-19 20:52:56', '1', '2022-11-19 20:52:56', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (158, '交易售后的类型', 'trade_after_sale_type', 0, '交易售后的类型', '1', '2022-11-19 21:04:09', '1', '2022-11-19 21:04:09', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (159, '交易售后的方式', 'trade_after_sale_way', 0, '交易售后的方式', '1', '2022-11-19 21:39:04', '1', '2022-11-19 21:39:04', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (160, '终端', 'terminal', 0, '终端', '1', '2022-12-10 10:50:50', '1', '2022-12-10 10:53:11', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (161, '交易订单的类型', 'trade_order_type', 0, '交易订单的类型', '1', '2022-12-10 16:33:54', '1', '2022-12-10 16:33:54', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (162, '交易订单的状态', 'trade_order_status', 0, '交易订单的状态', '1', '2022-12-10 16:48:44', '1', '2022-12-10 16:48:44', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (163, '交易订单项的售后状态', 'trade_order_item_after_sale_status', 0, '交易订单项的售后状态', '1', '2022-12-10 20:58:08', '1', '2022-12-10 20:58:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (164, '公众号自动回复的请求关键字匹配模式', 'mp_auto_reply_request_match', 0, '公众号自动回复的请求关键字匹配模式', '1', '2023-01-16 23:29:56', '1', '2023-01-16 23:29:56', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (165, '公众号的消息类型', 'mp_message_type', 0, '公众号的消息类型', '1', '2023-01-17 22:17:09', '1', '2023-01-17 22:17:09', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (166, '邮件发送状态', 'system_mail_send_status', 0, '邮件发送状态', '1', '2023-01-26 09:53:13', '1', '2023-01-26 09:53:13', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (167, '站内信模版的类型', 'system_notify_template_type', 0, '站内信模版的类型', '1', '2023-01-28 10:35:10', '1', '2023-01-28 10:35:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (168, '代码生成的前端类型', 'infra_codegen_front_type', 0, '', '1', '2023-04-12 23:57:52', '1', '2023-04-12 23:57:52', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (170, '快递计费方式', 'trade_delivery_express_charge_mode', 0, '用于商城交易模块配送管理', '1', '2023-05-21 22:45:03', '1', '2023-05-21 22:45:03', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (171, '积分业务类型', 'member_point_biz_type', 0, '', '1', '2023-06-10 12:15:00', '1', '2023-06-28 13:48:20', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (173, '支付通知类型', 'pay_notify_type', 0, NULL, '1', '2023-07-20 12:23:03', '1', '2023-07-20 12:23:03', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (174, '会员经验业务类型', 'member_experience_biz_type', 0, NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (175, '交易配送类型', 'trade_delivery_type', 0, '', '1', '2023-08-23 00:03:14', '1', '2023-08-23 00:03:14', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (176, '分佣模式', 'brokerage_enabled_condition', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (177, '分销关系绑定模式', 'brokerage_bind_mode', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (178, '佣金提现类型', 'brokerage_withdraw_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (179, '佣金记录业务类型', 'brokerage_record_biz_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (180, '佣金记录状态', 'brokerage_record_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (181, '佣金提现状态', 'brokerage_withdraw_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (182, '佣金提现银行', 'brokerage_bank_name', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (183, '砍价记录的状态', 'promotion_bargain_record_status', 0, '', '1', '2023-10-05 10:41:08', '1', '2023-10-05 10:41:08', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (184, '拼团记录的状态', 'promotion_combination_record_status', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-10-08 07:24:25', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (185, '回款-回款方式', 'crm_receivable_return_type', 0, '回款-回款方式', '1', '2023-10-18 21:54:10', '1', '2023-10-18 21:54:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (186, 'CRM 客户行业', 'crm_customer_industry', 0, 'CRM 客户所属行业', '1', '2023-10-28 22:57:07', '1', '2024-02-18 23:30:22', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (187, '客户等级', 'crm_customer_level', 0, 'CRM 客户等级', '1', '2023-10-28 22:59:12', '1', '2023-10-28 15:11:16', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (188, '客户来源', 'crm_customer_source', 0, 'CRM 客户来源', '1', '2023-10-28 23:00:34', '1', '2023-10-28 15:11:16', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (600, 'Banner 位置', 'promotion_banner_position', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-11-04 13:04:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (601, '社交类型', 'system_social_type', 0, '', '1', '2023-11-04 13:03:54', '1', '2023-11-04 13:03:54', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (604, '产品状态', 'crm_product_status', 0, '', '1', '2023-10-30 21:47:59', '1', '2023-10-30 21:48:45', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (605, 'CRM 数据权限的级别', 'crm_permission_level', 0, '', '1', '2023-11-30 09:51:59', '1', '2023-11-30 09:51:59', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (606, 'CRM 审批状态', 'crm_audit_status', 0, '', '1', '2023-11-30 18:56:23', '1', '2023-11-30 18:56:23', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (607, 'CRM 产品单位', 'crm_product_unit', 0, '', '1', '2023-12-05 23:01:51', '1', '2023-12-05 23:01:51', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (608, 'CRM 跟进方式', 'crm_follow_up_type', 0, '', '1', '2024-01-15 20:48:05', '1', '2024-01-15 20:48:05', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (610, '转账订单状态', 'pay_transfer_status', 0, '', '1', '2023-10-28 16:18:32', '1', '2023-10-28 16:18:32', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (611, 'ERP 库存明细的业务类型', 'erp_stock_record_biz_type', 0, 'ERP 库存明细的业务类型', '1', '2024-02-05 18:07:02', '1', '2024-02-05 18:07:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (612, 'ERP 审批状态', 'erp_audit_status', 0, '', '1', '2024-02-06 00:00:07', '1', '2024-02-06 00:00:07', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (613, 'BPM 监听器类型', 'bpm_process_listener_type', 0, '', '1', '2024-03-23 12:52:24', '1', '2024-03-09 15:54:28', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (615, 'BPM 监听器值类型', 'bpm_process_listener_value_type', 0, '', '1', '2024-03-23 13:00:31', '1', '2024-03-23 13:00:31', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (616, '时间间隔', 'date_interval', 0, '', '1', '2024-03-29 22:50:09', '1', '2024-03-29 22:50:09', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (619, 'CRM 商机结束状态类型', 'crm_business_end_status_type', 0, '', '1', '2024-04-13 23:23:00', '1', '2024-04-13 23:23:00', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (620, 'AI 模型平台', 'ai_platform', 0, '', '1', '2024-05-09 22:27:38', '1', '2024-05-09 22:27:38', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (621, 'AI 绘画状态', 'ai_image_status', 0, '', '1', '2024-06-26 20:51:23', '1', '2024-06-26 20:51:23', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (622, 'AI 音乐状态', 'ai_music_status', 0, '', '1', '2024-06-27 22:45:07', '1', '2024-06-28 00:56:27', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (623, 'AI 音乐生成模式', 'ai_generate_mode', 0, '', '1', '2024-06-27 22:46:21', '1', '2024-06-28 01:22:29', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (624, '写作语气', 'ai_write_tone', 0, '', '1', '2024-07-07 15:19:02', '1', '2024-07-07 15:19:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (625, '写作语言', 'ai_write_language', 0, '', '1', '2024-07-07 15:18:52', '1', '2024-07-07 15:18:52', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (626, '写作长度', 'ai_write_length', 0, '', '1', '2024-07-07 15:18:41', '1', '2024-07-07 15:18:41', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (627, '写作格式', 'ai_write_format', 0, '', '1', '2024-07-07 15:14:34', '1', '2024-07-07 15:14:34', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1000, 'IoT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2025-03-17 09:25:06', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1001, 'IoT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2025-03-17 09:25:08', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1002, 'IoT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2025-03-17 09:25:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1003, 'IoT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2025-03-17 09:25:12', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1004, 'IoT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2025-03-17 09:25:14', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1005, 'IoT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2025-03-17 09:25:16', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1006, 'IoT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-03-17 09:25:19', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1007, 'IoT 物模型功能类型', 'iot_thing_model_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2025-03-17 09:25:24', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1008, 'IoT 插件部署方式', 'iot_plugin_deploy_type', 0, '', '1', '2024-12-13 10:55:13', '1', '2025-03-17 09:25:27', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1009, 'IoT 插件状态', 'iot_plugin_status', 0, '', '1', '2024-12-13 11:05:34', '1', '2025-03-17 09:25:30', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1010, 'IoT 插件类型', 'iot_plugin_type', 0, '', '1', '2024-12-13 11:08:19', '1', '2025-03-17 09:25:32', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1011, 'IoT 物模型单位', 'iot_thing_model_unit', 0, '', '1', '2024-12-25 17:36:46', '1', '2025-03-17 09:25:35', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1012, 'IoT 数据桥接的方向枚举', 'iot_data_bridge_direction_enum', 0, '', '1', '2025-03-09 12:37:40', '1', '2025-03-17 09:25:39', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1013, 'IoT 数据桥梁的类型枚举', 'iot_data_bridge_type_enum', 0, '', '1', '2025-03-09 12:39:36', '1', '2025-04-06 17:09:46', '0', '1970-01-01 00:00:00');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_dict_type_seq;\nCREATE SEQUENCE system_dict_type_seq\n    START 1014;\n\n-- ----------------------------\n-- Table structure for system_login_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_login_log;\nCREATE TABLE system_login_log\n(\n    id          int8         NOT NULL,\n    log_type    int8         NOT NULL,\n    trace_id    varchar(64)  NULL     DEFAULT '',\n    user_id     int8         NOT NULL DEFAULT 0,\n    user_type   int2         NOT NULL DEFAULT 0,\n    username    varchar(50)  NULL     DEFAULT '',\n    result      int2         NOT NULL,\n    user_ip     varchar(50)  NOT NULL,\n    user_agent  varchar(512) NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_login_log\n    ADD CONSTRAINT pk_system_login_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_login_log.id IS '访问ID';\nCOMMENT ON COLUMN system_login_log.log_type IS '日志类型';\nCOMMENT ON COLUMN system_login_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_login_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_login_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_login_log.username IS '用户账号';\nCOMMENT ON COLUMN system_login_log.result IS '登陆结果';\nCOMMENT ON COLUMN system_login_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_login_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_login_log.creator IS '创建者';\nCOMMENT ON COLUMN system_login_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_login_log.updater IS '更新者';\nCOMMENT ON COLUMN system_login_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_login_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_login_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_login_log IS '系统访问记录';\n\nDROP SEQUENCE IF EXISTS system_login_log_seq;\nCREATE SEQUENCE system_login_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_mail_account\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_account;\nCREATE TABLE system_mail_account\n(\n    id              int8         NOT NULL,\n    mail            varchar(255) NOT NULL,\n    username        varchar(255) NOT NULL,\n    password        varchar(255) NOT NULL,\n    host            varchar(255) NOT NULL,\n    port            int4         NOT NULL,\n    ssl_enable      bool         NOT NULL DEFAULT '0',\n    starttls_enable bool         NOT NULL DEFAULT '0',\n    creator         varchar(64)  NULL     DEFAULT '',\n    create_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater         varchar(64)  NULL     DEFAULT '',\n    update_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted         int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_mail_account\n    ADD CONSTRAINT pk_system_mail_account PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_account.id IS '主键';\nCOMMENT ON COLUMN system_mail_account.mail IS '邮箱';\nCOMMENT ON COLUMN system_mail_account.username IS '用户名';\nCOMMENT ON COLUMN system_mail_account.password IS '密码';\nCOMMENT ON COLUMN system_mail_account.host IS 'SMTP 服务器域名';\nCOMMENT ON COLUMN system_mail_account.port IS 'SMTP 服务器端口';\nCOMMENT ON COLUMN system_mail_account.ssl_enable IS '是否开启 SSL';\nCOMMENT ON COLUMN system_mail_account.starttls_enable IS '是否开启 STARTTLS';\nCOMMENT ON COLUMN system_mail_account.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_account.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_account.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_account.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_account.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_account IS '邮箱账号表';\n\n-- ----------------------------\n-- Records of system_mail_account\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, '0', '0', '1', '2023-01-25 17:39:52', '1', '2025-04-04 16:34:40', '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, '1', '0', '1', '2023-01-26 01:26:03', '1', '2023-04-12 22:39:38', '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, '0', '0', '1', '2023-01-27 15:06:38', '1', '2023-01-27 07:08:36', '1');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (4, '7685413x@qq.com', '2', '3', '4', 5, '1', '0', '1', '2023-04-12 23:05:06', '1', '2023-04-12 15:05:11', '1');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_mail_account_seq;\nCREATE SEQUENCE system_mail_account_seq\n    START 5;\n\n-- ----------------------------\n-- Table structure for system_mail_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_log;\nCREATE TABLE system_mail_log\n(\n    id                int8           NOT NULL,\n    user_id           int8           NULL     DEFAULT NULL,\n    user_type         int2           NULL     DEFAULT NULL,\n    to_mail           varchar(255)   NOT NULL,\n    account_id        int8           NOT NULL,\n    from_mail         varchar(255)   NOT NULL,\n    template_id       int8           NOT NULL,\n    template_code     varchar(63)    NOT NULL,\n    template_nickname varchar(255)   NULL     DEFAULT NULL,\n    template_title    varchar(255)   NOT NULL,\n    template_content  varchar(10240) NOT NULL,\n    template_params   varchar(255)   NOT NULL,\n    send_status       int2           NOT NULL DEFAULT 0,\n    send_time         timestamp      NULL     DEFAULT NULL,\n    send_message_id   varchar(255)   NULL     DEFAULT NULL,\n    send_exception    varchar(4096)  NULL     DEFAULT NULL,\n    creator           varchar(64)    NULL     DEFAULT '',\n    create_time       timestamp      NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater           varchar(64)    NULL     DEFAULT '',\n    update_time       timestamp      NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted           int2           NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_mail_log\n    ADD CONSTRAINT pk_system_mail_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_log.id IS '编号';\nCOMMENT ON COLUMN system_mail_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_mail_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_mail_log.to_mail IS '接收邮箱地址';\nCOMMENT ON COLUMN system_mail_log.account_id IS '邮箱账号编号';\nCOMMENT ON COLUMN system_mail_log.from_mail IS '发送邮箱地址';\nCOMMENT ON COLUMN system_mail_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_mail_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_mail_log.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_mail_log.template_title IS '邮件标题';\nCOMMENT ON COLUMN system_mail_log.template_content IS '邮件内容';\nCOMMENT ON COLUMN system_mail_log.template_params IS '邮件参数';\nCOMMENT ON COLUMN system_mail_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_mail_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_mail_log.send_message_id IS '发送返回的消息 ID';\nCOMMENT ON COLUMN system_mail_log.send_exception IS '发送异常';\nCOMMENT ON COLUMN system_mail_log.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_log.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_log IS '邮件日志表';\n\nDROP SEQUENCE IF EXISTS system_mail_log_seq;\nCREATE SEQUENCE system_mail_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_mail_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_template;\nCREATE TABLE system_mail_template\n(\n    id          int8           NOT NULL,\n    name        varchar(63)    NOT NULL,\n    code        varchar(63)    NOT NULL,\n    account_id  int8           NOT NULL,\n    nickname    varchar(255)   NULL     DEFAULT NULL,\n    title       varchar(255)   NOT NULL,\n    content     varchar(10240) NOT NULL,\n    params      varchar(255)   NOT NULL,\n    status      int2           NOT NULL,\n    remark      varchar(255)   NULL     DEFAULT NULL,\n    creator     varchar(64)    NULL     DEFAULT '',\n    create_time timestamp      NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)    NULL     DEFAULT '',\n    update_time timestamp      NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2           NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_mail_template\n    ADD CONSTRAINT pk_system_mail_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_template.id IS '编号';\nCOMMENT ON COLUMN system_mail_template.name IS '模板名称';\nCOMMENT ON COLUMN system_mail_template.code IS '模板编码';\nCOMMENT ON COLUMN system_mail_template.account_id IS '发送的邮箱账号编号';\nCOMMENT ON COLUMN system_mail_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_mail_template.title IS '模板标题';\nCOMMENT ON COLUMN system_mail_template.content IS '模板内容';\nCOMMENT ON COLUMN system_mail_template.params IS '参数数组';\nCOMMENT ON COLUMN system_mail_template.status IS '开启状态';\nCOMMENT ON COLUMN system_mail_template.remark IS '备注';\nCOMMENT ON COLUMN system_mail_template.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_template.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_template IS '邮件模版表';\n\n-- ----------------------------\n-- Records of system_mail_template\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '后台用户短信登录', 'admin-sms-login', 1, '奥特曼', '你猜我猜', '<p>您的验证码是{code}，名字是{name}</p>', '[\"code\",\"name\"]', 0, '3', '1', '2021-10-11 08:10:00', '1', '2023-12-02 19:51:14', '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (14, '测试模版', 'test_01', 2, '芋艿', '一个标题', '<p>你是 {key01} 吗？</p><p><br></p><p>是的话，赶紧 {key02} 一下！</p>', '[\"key01\",\"key02\"]', 0, NULL, '1', '2023-01-26 01:27:40', '1', '2023-01-27 10:32:16', '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (15, '3', '2', 2, '7', '4', '<p>45</p>', '[]', 1, '80', '1', '2023-01-27 15:50:35', '1', '2023-01-27 16:34:49', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_mail_template_seq;\nCREATE SEQUENCE system_mail_template_seq\n    START 16;\n\n-- ----------------------------\n-- Table structure for system_menu\n-- ----------------------------\nDROP TABLE IF EXISTS system_menu;\nCREATE TABLE system_menu\n(\n    id             int8         NOT NULL,\n    name           varchar(50)  NOT NULL,\n    permission     varchar(100) NULL     DEFAULT '',\n    type           int2         NOT NULL,\n    sort           int4         NOT NULL DEFAULT 0,\n    parent_id      int8         NOT NULL DEFAULT 0,\n    path           varchar(200) NULL     DEFAULT '',\n    icon           varchar(100) NULL     DEFAULT '#',\n    component      varchar(255) NULL     DEFAULT NULL,\n    component_name varchar(255) NULL     DEFAULT NULL,\n    status         int2         NOT NULL DEFAULT 0,\n    visible        bool         NOT NULL DEFAULT '1',\n    keep_alive     bool         NOT NULL DEFAULT '1',\n    always_show    bool         NOT NULL DEFAULT '1',\n    creator        varchar(64)  NULL     DEFAULT '',\n    create_time    timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64)  NULL     DEFAULT '',\n    update_time    timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_menu\n    ADD CONSTRAINT pk_system_menu PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_menu.id IS '菜单ID';\nCOMMENT ON COLUMN system_menu.name IS '菜单名称';\nCOMMENT ON COLUMN system_menu.permission IS '权限标识';\nCOMMENT ON COLUMN system_menu.type IS '菜单类型';\nCOMMENT ON COLUMN system_menu.sort IS '显示顺序';\nCOMMENT ON COLUMN system_menu.parent_id IS '父菜单ID';\nCOMMENT ON COLUMN system_menu.path IS '路由地址';\nCOMMENT ON COLUMN system_menu.icon IS '菜单图标';\nCOMMENT ON COLUMN system_menu.component IS '组件路径';\nCOMMENT ON COLUMN system_menu.component_name IS '组件名';\nCOMMENT ON COLUMN system_menu.status IS '菜单状态';\nCOMMENT ON COLUMN system_menu.visible IS '是否可见';\nCOMMENT ON COLUMN system_menu.keep_alive IS '是否缓存';\nCOMMENT ON COLUMN system_menu.always_show IS '是否总是显示';\nCOMMENT ON COLUMN system_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_menu.deleted IS '是否删除';\nCOMMENT ON TABLE system_menu IS '菜单权限表';\n\n-- ----------------------------\n-- Records of system_menu\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-03-01 08:28:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5, 'OA 示例', '', 1, 40, 1185, 'oa', 'fa:road', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-09-20 16:26:19', '1', '2024-02-29 12:38:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (101, '角色管理', '', 2, 2, 1, 'role', 'ep:user', 'system/role/index', 'SystemRole', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-05-01 18:35:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (102, '菜单管理', '', 2, 3, 1, 'menu', 'ep:menu', 'system/menu/index', 'SystemMenu', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:03:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (103, '部门管理', '', 2, 4, 1, 'dept', 'fa:address-card', 'system/dept/index', 'SystemDept', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (104, '岗位管理', '', 2, 5, 1, 'post', 'fa:address-book-o', 'system/post/index', 'SystemPost', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (105, '字典管理', '', 2, 6, 1, 'dict', 'ep:collection', 'system/dict/index', 'SystemDictType', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:07:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (106, '配置管理', '', 2, 8, 2, 'config', 'fa:connectdevelop', 'infra/config/index', 'InfraConfig', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:02:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (107, '通知公告', '', 2, 4, 2739, 'notice', 'ep:takeaway-box', 'system/notice/index', 'SystemNotice', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-22 23:56:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (108, '审计日志', '', 1, 9, 1, 'log', 'ep:document-copy', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:08:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (109, '令牌管理', '', 2, 2, 1261, 'token', 'fa:key', 'system/oauth2/token/index', 'SystemTokenClient', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:13:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (110, '定时任务', '', 2, 7, 2, 'job', 'fa-solid:tasks', 'infra/job/index', 'InfraJob', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:57:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (111, 'MySQL 监控', '', 2, 1, 2740, 'druid', 'fa-solid:box', 'infra/druid/index', 'InfraDruid', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:05:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (112, 'Java 监控', '', 2, 3, 2740, 'admin-server', 'ep:coffee-cup', 'infra/server/index', 'InfraAdminServer', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (113, 'Redis 监控', '', 2, 2, 2740, 'redis', 'fa:reddit-square', 'infra/redis/index', 'InfraRedis', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (114, '表单构建', 'infra:build:list', 2, 2, 2, 'build', 'fa:wpforms', 'infra/build/index', 'InfraBuild', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (115, '代码生成', 'infra:codegen:query', 2, 1, 2, 'codegen', 'ep:document-copy', 'infra/codegen/index', 'InfraCodegen', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (116, 'API 接口', 'infra:swagger:list', 2, 3, 2, 'swagger', 'fa:fighter-jet', 'infra/swagger/index', 'InfraSwagger', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:01:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (500, '操作日志', '', 2, 1, 108, 'operate-log', 'ep:position', 'system/operatelog/index', 'SystemOperateLog', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:09:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (501, '登录日志', '', 2, 2, 108, 'login-log', 'ep:promotion', 'system/loginlog/index', 'SystemLoginLog', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:10:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1001, '用户查询', 'system:user:query', 3, 1, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1002, '用户新增', 'system:user:create', 3, 2, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1003, '用户修改', 'system:user:update', 3, 3, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1004, '用户删除', 'system:user:delete', 3, 4, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1005, '用户导出', 'system:user:export', 3, 5, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1006, '用户导入', 'system:user:import', 3, 6, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1007, '重置密码', 'system:user:update-password', 3, 7, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1008, '角色查询', 'system:role:query', 3, 1, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1009, '角色新增', 'system:role:create', 3, 2, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1010, '角色修改', 'system:role:update', 3, 3, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1011, '角色删除', 'system:role:delete', 3, 4, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1012, '角色导出', 'system:role:export', 3, 5, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1013, '菜单查询', 'system:menu:query', 3, 1, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1014, '菜单新增', 'system:menu:create', 3, 2, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1015, '菜单修改', 'system:menu:update', 3, 3, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1016, '菜单删除', 'system:menu:delete', 3, 4, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1017, '部门查询', 'system:dept:query', 3, 1, 103, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1018, '部门新增', 'system:dept:create', 3, 2, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1019, '部门修改', 'system:dept:update', 3, 3, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1020, '部门删除', 'system:dept:delete', 3, 4, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1021, '岗位查询', 'system:post:query', 3, 1, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1022, '岗位新增', 'system:post:create', 3, 2, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1023, '岗位修改', 'system:post:update', 3, 3, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1024, '岗位删除', 'system:post:delete', 3, 4, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1025, '岗位导出', 'system:post:export', 3, 5, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1026, '字典查询', 'system:dict:query', 3, 1, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1027, '字典新增', 'system:dict:create', 3, 2, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1028, '字典修改', 'system:dict:update', 3, 3, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1029, '字典删除', 'system:dict:delete', 3, 4, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1030, '字典导出', 'system:dict:export', 3, 5, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1031, '配置查询', 'infra:config:query', 3, 1, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1032, '配置新增', 'infra:config:create', 3, 2, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1033, '配置修改', 'infra:config:update', 3, 3, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1034, '配置删除', 'infra:config:delete', 3, 4, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1035, '配置导出', 'infra:config:export', 3, 5, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1036, '公告查询', 'system:notice:query', 3, 1, 107, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1037, '公告新增', 'system:notice:create', 3, 2, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1038, '公告修改', 'system:notice:update', 3, 3, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1039, '公告删除', 'system:notice:delete', 3, 4, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1040, '操作查询', 'system:operate-log:query', 3, 1, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1042, '日志导出', 'system:operate-log:export', 3, 2, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1043, '登录查询', 'system:login-log:query', 3, 1, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1045, '日志导出', 'system:login-log:export', 3, 3, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1046, '令牌列表', 'system:oauth2-token:page', 3, 1, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1048, '令牌删除', 'system:oauth2-token:delete', 3, 2, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1050, '任务新增', 'infra:job:create', 3, 2, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1051, '任务修改', 'infra:job:update', 3, 3, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1052, '任务删除', 'infra:job:delete', 3, 4, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1053, '状态修改', 'infra:job:update', 3, 5, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1054, '任务导出', 'infra:job:export', 3, 7, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1056, '生成修改', 'infra:codegen:update', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1057, '生成删除', 'infra:codegen:delete', 3, 3, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1058, '导入代码', 'infra:codegen:create', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1059, '预览代码', 'infra:codegen:preview', 3, 4, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1060, '生成代码', 'infra:codegen:download', 3, 5, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1063, '设置角色菜单权限', 'system:permission:assign-role-menu', 3, 6, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-06 17:53:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1064, '设置角色数据权限', 'system:permission:assign-role-data-scope', 3, 7, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-06 17:56:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1065, '设置用户角色', 'system:permission:assign-user-role', 3, 8, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-07 10:23:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1066, '获得 Redis 监控信息', 'infra:redis:get-monitor-info', 3, 1, 113, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-26 01:02:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1067, '获得 Redis Key 列表', 'infra:redis:get-key-list', 3, 2, 113, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-26 01:02:52', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1070, '代码生成案例', '', 1, 1, 2, 'demo', 'ep:aim', 'infra/testDemo/index', NULL, 0, '1', '1', '1', '', '2021-02-06 12:42:49', '1', '2023-11-15 23:45:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1075, '任务触发', 'infra:job:trigger', 3, 8, 110, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-07 13:03:10', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1077, '链路追踪', '', 2, 4, 2740, 'skywalking', 'fa:eye', 'infra/skywalking/index', 'InfraSkyWalking', 0, '1', '1', '1', '', '2021-02-08 20:41:31', '1', '2024-04-23 00:07:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1078, '访问日志', '', 2, 1, 1083, 'api-access-log', 'ep:place', 'infra/apiAccessLog/index', 'InfraApiAccessLog', 0, '1', '1', '1', '', '2021-02-26 01:32:59', '1', '2024-02-29 08:54:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1082, '日志导出', 'infra:api-access-log:export', 3, 2, 1078, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 01:32:59', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1083, 'API 日志', '', 2, 4, 2, 'log', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '', '2021-02-26 02:18:24', '1', '2024-04-22 23:58:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1084, '错误日志', 'infra:api-error-log:query', 2, 2, 1083, 'api-error-log', 'ep:warning-filled', 'infra/apiErrorLog/index', 'InfraApiErrorLog', 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2024-02-29 08:55:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1085, '日志处理', 'infra:api-error-log:update-status', 3, 2, 1084, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1086, '日志导出', 'infra:api-error-log:export', 3, 3, 1084, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1087, '任务查询', 'infra:job:query', 3, 1, 110, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:26:19', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1088, '日志查询', 'infra:api-access-log:query', 3, 1, 1078, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:28:04', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1089, '日志查询', 'infra:api-error-log:query', 3, 1, 1084, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:29:09', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1090, '文件列表', '', 2, 5, 1243, 'file', 'ep:upload-filled', 'infra/file/index', 'InfraFile', 0, '1', '1', '1', '', '2021-03-12 20:16:20', '1', '2024-02-29 08:53:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', NULL, 0, '1', '1', '1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1092, '文件删除', 'infra:file:delete', 3, 4, 1090, '', '', '', NULL, 0, '1', '1', '1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1093, '短信管理', '', 1, 1, 2739, 'sms', 'ep:message', NULL, NULL, 0, '1', '1', '1', '1', '2021-04-05 01:10:16', '1', '2024-04-22 23:56:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1094, '短信渠道', '', 2, 0, 1093, 'sms-channel', 'fa:stack-exchange', 'system/sms/channel/index', 'SystemSmsChannel', 0, '1', '1', '1', '', '2021-04-01 11:07:15', '1', '2024-02-29 01:15:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1095, '短信渠道查询', 'system:sms-channel:query', 3, 1, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1096, '短信渠道创建', 'system:sms-channel:create', 3, 2, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1097, '短信渠道更新', 'system:sms-channel:update', 3, 3, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1098, '短信渠道删除', 'system:sms-channel:delete', 3, 4, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1100, '短信模板', '', 2, 1, 1093, 'sms-template', 'ep:connection', 'system/sms/template/index', 'SystemSmsTemplate', 0, '1', '1', '1', '', '2021-04-01 17:35:17', '1', '2024-02-29 01:16:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1101, '短信模板查询', 'system:sms-template:query', 3, 1, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1102, '短信模板创建', 'system:sms-template:create', 3, 2, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1103, '短信模板更新', 'system:sms-template:update', 3, 3, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1104, '短信模板删除', 'system:sms-template:delete', 3, 4, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1105, '短信模板导出', 'system:sms-template:export', 3, 5, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1106, '发送测试短信', 'system:sms-template:send-sms', 3, 6, 1100, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-04-11 00:26:40', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1107, '短信日志', '', 2, 2, 1093, 'sms-log', 'fa:edit', 'system/sms/log/index', 'SystemSmsLog', 0, '1', '1', '1', '', '2021-04-11 08:37:05', '1', '2024-02-29 08:49:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1108, '短信日志查询', 'system:sms-log:query', 3, 1, 1107, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1109, '短信日志导出', 'system:sms-log:export', 3, 5, 1107, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1117, '支付管理', '', 1, 30, 0, '/pay', 'ep:money', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-25 16:43:41', '1', '2024-02-29 08:58:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1118, '请假查询', '', 2, 0, 5, 'leave', 'fa:leanpub', 'bpm/oa/leave/index', 'BpmOALeave', 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2024-02-29 12:38:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1119, '请假申请查询', 'bpm:oa-leave:query', 3, 1, 1118, '', '', '', NULL, 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1120, '请假申请创建', 'bpm:oa-leave:create', 3, 2, 1118, '', '', '', NULL, 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1126, '应用信息', '', 2, 1, 1117, 'app', 'fa:apple', 'pay/app/index', 'PayApp', 0, '1', '1', '1', '', '2021-11-10 01:13:30', '1', '2024-02-29 08:59:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1127, '支付应用信息查询', 'pay:app:query', 3, 1, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1128, '支付应用信息创建', 'pay:app:create', 3, 2, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1129, '支付应用信息更新', 'pay:app:update', 3, 3, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1130, '支付应用信息删除', 'pay:app:delete', 3, 4, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1132, '秘钥解析', 'pay:channel:parsing', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1133, '支付商户信息查询', 'pay:merchant:query', 3, 1, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1134, '支付商户信息创建', 'pay:merchant:create', 3, 2, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1135, '支付商户信息更新', 'pay:merchant:update', 3, 3, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1136, '支付商户信息删除', 'pay:merchant:delete', 3, 4, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1137, '支付商户信息导出', 'pay:merchant:export', 3, 5, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1138, '租户列表', '', 2, 0, 1224, 'list', 'ep:house', 'system/tenant/index', 'SystemTenant', 0, '1', '1', '1', '', '2021-12-14 12:31:43', '1', '2024-02-29 01:01:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1139, '租户查询', 'system:tenant:query', 3, 1, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1140, '租户创建', 'system:tenant:create', 3, 2, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1141, '租户更新', 'system:tenant:update', 3, 3, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1142, '租户删除', 'system:tenant:delete', 3, 4, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1143, '租户导出', 'system:tenant:export', 3, 5, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1150, '秘钥解析', '', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1161, '退款订单', '', 2, 3, 1117, 'refund', 'fa:registered', 'pay/refund/index', 'PayRefund', 0, '1', '1', '1', '', '2021-12-25 08:29:07', '1', '2024-02-29 08:59:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1162, '退款订单查询', 'pay:refund:query', 3, 1, 1161, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1166, '退款订单导出', 'pay:refund:export', 3, 5, 1161, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1173, '支付订单', '', 2, 2, 1117, 'order', 'fa:cc-paypal', 'pay/order/index', 'PayOrder', 0, '1', '1', '1', '', '2021-12-25 08:49:43', '1', '2024-02-29 08:59:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1174, '支付订单查询', 'pay:order:query', 3, 1, 1173, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1178, '支付订单导出', 'pay:order:export', 3, 5, 1173, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1185, '工作流程', '', 1, 50, 0, '/bpm', 'fa:medium', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-30 20:26:36', '1', '2024-02-29 12:43:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1186, '流程管理', '', 1, 10, 1185, 'manager', 'fa:dedent', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-30 20:28:30', '1', '2024-02-29 12:36:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1187, '流程表单', '', 2, 2, 1186, 'form', 'fa:hdd-o', 'bpm/form/index', 'BpmForm', 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2024-03-19 12:25:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1188, '表单查询', 'bpm:form:query', 3, 1, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1189, '表单创建', 'bpm:form:create', 3, 2, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1190, '表单更新', 'bpm:form:update', 3, 3, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1191, '表单删除', 'bpm:form:delete', 3, 4, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1192, '表单导出', 'bpm:form:export', 3, 5, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1193, '流程模型', '', 2, 1, 1186, 'model', 'fa-solid:project-diagram', 'bpm/model/index', 'BpmModel', 0, '1', '1', '1', '1', '2021-12-31 23:24:58', '1', '2024-03-19 12:25:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1194, '模型查询', 'bpm:model:query', 3, 1, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:01:10', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1195, '模型创建', 'bpm:model:create', 3, 2, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:01:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1197, '模型更新', 'bpm:model:update', 3, 4, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:02:28', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1198, '模型删除', 'bpm:model:delete', 3, 5, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:02:43', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1199, '模型发布', 'bpm:model:deploy', 3, 6, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:03:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1200, '审批中心', '', 2, 20, 1185, 'task', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '1', '2022-01-07 23:51:48', '1', '2024-03-21 00:33:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1201, '我的流程', '', 2, 1, 1200, 'my', 'fa-solid:book', 'bpm/processInstance/index', 'BpmProcessInstanceMy', 0, '1', '1', '1', '', '2022-01-07 15:53:44', '1', '2024-03-21 23:52:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1202, '流程实例的查询', 'bpm:process-instance:query', 3, 1, 1201, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-07 15:53:44', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1207, '待办任务', '', 2, 10, 1200, 'todo', 'fa:slack', 'bpm/task/todo/index', 'BpmTodoTask', 0, '1', '1', '1', '1', '2022-01-08 10:33:37', '1', '2024-02-29 12:37:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1208, '已办任务', '', 2, 20, 1200, 'done', 'fa:delicious', 'bpm/task/done/index', 'BpmDoneTask', 0, '1', '1', '1', '1', '2022-01-08 10:34:13', '1', '2024-02-29 12:37:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1209, '用户分组', '', 2, 4, 1186, 'user-group', 'fa:user-secret', 'bpm/group/index', 'BpmUserGroup', 0, '1', '1', '1', '', '2022-01-14 02:14:20', '1', '2024-03-21 23:55:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1210, '用户组查询', 'bpm:user-group:query', 3, 1, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1211, '用户组创建', 'bpm:user-group:create', 3, 2, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1212, '用户组更新', 'bpm:user-group:update', 3, 3, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1213, '用户组删除', 'bpm:user-group:delete', 3, 4, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1215, '流程定义查询', 'bpm:process-definition:query', 3, 10, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:21:43', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1216, '流程任务分配规则查询', 'bpm:task-assign-rule:query', 3, 20, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:26:53', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1217, '流程任务分配规则创建', 'bpm:task-assign-rule:create', 3, 21, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:28:15', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1218, '流程任务分配规则更新', 'bpm:task-assign-rule:update', 3, 22, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:28:41', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1219, '流程实例的创建', 'bpm:process-instance:create', 3, 2, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:36:15', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1220, '流程实例的取消', 'bpm:process-instance:cancel', 3, 3, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:36:33', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1221, '流程任务的查询', 'bpm:task:query', 3, 1, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:38:52', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1222, '流程任务的更新', 'bpm:task:update', 3, 2, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:39:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1224, '租户管理', '', 2, 0, 1, 'tenant', 'fa-solid:house-user', NULL, NULL, 0, '1', '1', '1', '1', '2022-02-20 01:41:13', '1', '2024-02-29 00:59:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1225, '租户套餐', '', 2, 0, 1224, 'package', 'fa:bars', 'system/tenantPackage/index', 'SystemTenantPackage', 0, '1', '1', '1', '', '2022-02-19 17:44:06', '1', '2024-02-29 01:01:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1226, '租户套餐查询', 'system:tenant-package:query', 3, 1, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1227, '租户套餐创建', 'system:tenant-package:create', 3, 2, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1228, '租户套餐更新', 'system:tenant-package:update', 3, 3, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1229, '租户套餐删除', 'system:tenant-package:delete', 3, 4, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1237, '文件配置', '', 2, 0, 1243, 'file-config', 'fa-solid:file-signature', 'infra/fileConfig/index', 'InfraFileConfig', 0, '1', '1', '1', '', '2022-03-15 14:35:28', '1', '2024-02-29 08:52:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1238, '文件配置查询', 'infra:file-config:query', 3, 1, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1239, '文件配置创建', 'infra:file-config:create', 3, 2, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1240, '文件配置更新', 'infra:file-config:update', 3, 3, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, '1', '1', '1', '1', '2022-03-16 23:47:40', '1', '2024-04-23 00:02:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, '1', '1', '1', '1', '2022-04-23 01:03:15', '1', '2025-04-29 17:45:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, '1', '1', '1', '', '2022-04-27 14:37:32', '1', '2024-02-29 08:51:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1258, '数据源配置更新', 'infra:data-source-config:update', 3, 3, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1259, '数据源配置删除', 'infra:data-source-config:delete', 3, 4, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1260, '数据源配置导出', 'infra:data-source-config:export', 3, 5, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1261, 'OAuth 2.0', '', 2, 10, 1, 'oauth2', 'fa:dashcube', NULL, NULL, 0, '1', '1', '1', '1', '2022-05-09 23:38:17', '1', '2024-02-29 01:12:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1263, '应用管理', '', 2, 0, 1261, 'oauth2/application', 'fa:hdd-o', 'system/oauth2/client/index', 'SystemOAuth2Client', 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2024-02-29 01:13:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1264, '客户端查询', 'system:oauth2-client:query', 3, 1, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1265, '客户端创建', 'system:oauth2-client:create', 3, 2, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1266, '客户端更新', 'system:oauth2-client:update', 3, 3, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1267, '客户端删除', 'system:oauth2-client:delete', 3, 4, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1281, '报表管理', '', 2, 40, 0, '/report', 'ep:pie-chart', NULL, NULL, 0, '1', '1', '1', '1', '2022-07-10 20:22:15', '1', '2024-02-29 12:33:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1282, '报表设计器', '', 2, 1, 1281, 'jimu-report', 'ep:trend-charts', 'report/jmreport/index', 'JimuReport', 0, '1', '1', '1', '1', '2022-07-10 20:26:36', '1', '2025-05-03 09:57:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2000, '商品中心', '', 1, 60, 2362, 'product', 'fa:product-hunt', NULL, NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '1', '2023-09-30 11:52:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2002, '商品分类', '', 2, 2, 2000, 'category', 'ep:cellphone', 'mall/product/category/index', 'ProductCategory', 0, '1', '1', '1', '', '2022-07-29 15:53:53', '1', '2023-08-21 10:27:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2003, '分类查询', 'product:category:query', 3, 1, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2004, '分类创建', 'product:category:create', 3, 2, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2005, '分类更新', 'product:category:update', 3, 3, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2006, '分类删除', 'product:category:delete', 3, 4, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2008, '商品品牌', '', 2, 3, 2000, 'brand', 'ep:chicken', 'mall/product/brand/index', 'ProductBrand', 0, '1', '1', '1', '', '2022-07-30 13:52:44', '1', '2023-08-21 10:27:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2009, '品牌查询', 'product:brand:query', 3, 1, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2010, '品牌创建', 'product:brand:create', 3, 2, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2011, '品牌更新', 'product:brand:update', 3, 3, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2012, '品牌删除', 'product:brand:delete', 3, 4, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2014, '商品列表', '', 2, 1, 2000, 'spu', 'ep:apple', 'mall/product/spu/index', 'ProductSpu', 0, '1', '1', '1', '', '2022-07-30 14:22:58', '1', '2023-08-21 10:27:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2015, '商品查询', 'product:spu:query', 3, 1, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2016, '商品创建', 'product:spu:create', 3, 2, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2017, '商品更新', 'product:spu:update', 3, 3, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2018, '商品删除', 'product:spu:delete', 3, 4, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2019, '商品属性', '', 2, 4, 2000, 'property', 'ep:cold-drink', 'mall/product/property/index', 'ProductProperty', 0, '1', '1', '1', '', '2022-08-01 14:55:35', '1', '2023-08-26 11:01:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2020, '规格查询', 'product:property:query', 3, 1, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2021, '规格创建', 'product:property:create', 3, 2, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2022, '规格更新', 'product:property:update', 3, 3, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2023, '规格删除', 'product:property:delete', 3, 4, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2025, 'Banner', '', 2, 100, 2387, 'banner', 'fa:bandcamp', 'mall/promotion/banner/index', NULL, 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2026, 'Banner查询', 'promotion:banner:query', 3, 1, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2027, 'Banner创建', 'promotion:banner:create', 3, 2, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2028, 'Banner更新', 'promotion:banner:update', 3, 3, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2029, 'Banner删除', 'promotion:banner:delete', 3, 4, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2030, '营销中心', '', 1, 70, 2362, 'promotion', 'ep:present', NULL, NULL, 0, '1', '1', '1', '1', '2022-10-31 21:25:09', '1', '2023-09-30 11:54:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2032, '优惠劵列表', '', 2, 1, 2365, 'template', 'ep:discount', 'mall/promotion/coupon/template/index', 'PromotionCouponTemplate', 0, '1', '1', '1', '', '2022-10-31 22:27:14', '1', '2023-10-03 12:40:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2033, '优惠劵模板查询', 'promotion:coupon-template:query', 3, 1, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2034, '优惠劵模板创建', 'promotion:coupon-template:create', 3, 2, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2035, '优惠劵模板更新', 'promotion:coupon-template:update', 3, 3, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2036, '优惠劵模板删除', 'promotion:coupon-template:delete', 3, 4, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2038, '领取记录', '', 2, 2, 2365, 'list', 'ep:collection-tag', 'mall/promotion/coupon/index', 'PromotionCoupon', 0, '1', '1', '1', '', '2022-11-03 23:21:31', '1', '2023-10-03 12:55:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2039, '优惠劵查询', 'promotion:coupon:query', 3, 1, 2038, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2040, '优惠劵删除', 'promotion:coupon:delete', 3, 4, 2038, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2041, '满减送', '', 2, 10, 2390, 'reward-activity', 'ep:goblet-square-full', 'mall/promotion/rewardActivity/index', 'PromotionRewardActivity', 0, '1', '1', '1', '', '2022-11-04 23:47:49', '1', '2023-10-21 19:24:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2042, '满减送活动查询', 'promotion:reward-activity:query', 3, 1, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2043, '满减送活动创建', 'promotion:reward-activity:create', 3, 2, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2044, '满减送活动更新', 'promotion:reward-activity:update', 3, 3, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2045, '满减送活动删除', 'promotion:reward-activity:delete', 3, 4, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2046, '满减送活动关闭', 'promotion:reward-activity:close', 3, 5, 2041, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-11-05 10:42:53', '1', '2022-11-05 10:42:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2047, '限时折扣', '', 2, 7, 2390, 'discount-activity', 'ep:timer', 'mall/promotion/discountActivity/index', 'PromotionDiscountActivity', 0, '1', '1', '1', '', '2022-11-05 17:12:15', '1', '2023-10-21 19:24:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2048, '限时折扣活动查询', 'promotion:discount-activity:query', 3, 1, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2049, '限时折扣活动创建', 'promotion:discount-activity:create', 3, 2, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2050, '限时折扣活动更新', 'promotion:discount-activity:update', 3, 3, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2051, '限时折扣活动删除', 'promotion:discount-activity:delete', 3, 4, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2052, '限时折扣活动关闭', 'promotion:discount-activity:close', 3, 5, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2059, '秒杀商品', '', 2, 2, 2209, 'activity', 'ep:basketball', 'mall/promotion/seckill/activity/index', 'PromotionSeckillActivity', 0, '1', '1', '1', '', '2022-11-06 22:24:49', '1', '2023-06-24 18:57:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2060, '秒杀活动查询', 'promotion:seckill-activity:query', 3, 1, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2061, '秒杀活动创建', 'promotion:seckill-activity:create', 3, 2, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2062, '秒杀活动更新', 'promotion:seckill-activity:update', 3, 3, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2063, '秒杀活动删除', 'promotion:seckill-activity:delete', 3, 4, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2066, '秒杀时段', '', 2, 1, 2209, 'config', 'ep:baseball', 'mall/promotion/seckill/config/index', 'PromotionSeckillConfig', 0, '1', '1', '1', '', '2022-11-15 19:46:50', '1', '2023-06-24 18:57:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2067, '秒杀时段查询', 'promotion:seckill-config:query', 3, 1, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2068, '秒杀时段创建', 'promotion:seckill-config:create', 3, 2, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:48:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2069, '秒杀时段更新', 'promotion:seckill-config:update', 3, 3, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2070, '秒杀时段删除', 'promotion:seckill-config:delete', 3, 4, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2072, '订单中心', '', 1, 65, 2362, 'trade', 'ep:eleme', NULL, NULL, 0, '1', '1', '1', '1', '2022-11-19 18:57:19', '1', '2023-09-30 11:54:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2073, '售后退款', '', 2, 2, 2072, 'after-sale', 'ep:refrigerator', 'mall/trade/afterSale/index', 'TradeAfterSale', 0, '1', '1', '1', '', '2022-11-19 20:15:32', '1', '2023-10-01 21:42:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2074, '售后查询', 'trade:after-sale:query', 3, 1, 2073, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-19 20:15:33', '1', '2022-12-10 21:04:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2075, '秒杀活动关闭', 'promotion:seckill-activity:close', 3, 5, 2059, '', '', '', '', 0, '1', '1', '1', '1', '2022-11-28 20:20:15', '1', '2023-10-03 18:34:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2076, '订单列表', '', 2, 1, 2072, 'order', 'ep:list', 'mall/trade/order/index', 'TradeOrder', 0, '1', '1', '1', '1', '2022-12-10 21:05:44', '1', '2023-10-01 21:42:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2083, '地区管理', '', 2, 14, 1, 'area', 'fa:map-marker', 'system/area/index', 'SystemArea', 0, '1', '1', '1', '1', '2022-12-23 17:35:05', '1', '2024-02-29 08:50:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2084, '公众号管理', '', 1, 100, 0, '/mp', 'ep:compass', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-01 20:11:04', '1', '2024-02-29 12:39:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2085, '账号管理', '', 2, 1, 2084, 'account', 'fa:user', 'mp/account/index', 'MpAccount', 0, '1', '1', '1', '1', '2023-01-01 20:13:31', '1', '2024-02-29 12:42:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2086, '新增账号', 'mp:account:create', 3, 1, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-01 20:21:40', '1', '2023-01-07 17:32:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2087, '修改账号', 'mp:account:update', 3, 2, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:32:46', '1', '2023-01-07 17:32:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2088, '查询账号', 'mp:account:query', 3, 0, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:07', '1', '2023-01-07 17:33:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2089, '删除账号', 'mp:account:delete', 3, 3, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:21', '1', '2023-01-07 17:33:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2090, '生成二维码', 'mp:account:qr-code', 3, 4, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:58', '1', '2023-01-07 17:33:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2091, '清空 API 配额', 'mp:account:clear-quota', 3, 5, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 18:20:32', '1', '2023-01-07 18:20:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2092, '数据统计', 'mp:statistics:query', 2, 2, 2084, 'statistics', 'ep:trend-charts', 'mp/statistics/index', 'MpStatistics', 0, '1', '1', '1', '1', '2023-01-07 20:17:36', '1', '2024-02-29 12:42:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2093, '标签管理', '', 2, 3, 2084, 'tag', 'ep:collection-tag', 'mp/tag/index', 'MpTag', 0, '1', '1', '1', '1', '2023-01-08 11:37:32', '1', '2024-02-29 12:42:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2094, '查询标签', 'mp:tag:query', 3, 0, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:03', '1', '2023-01-08 11:59:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2095, '新增标签', 'mp:tag:create', 3, 1, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:23', '1', '2023-01-08 11:59:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2096, '修改标签', 'mp:tag:update', 3, 2, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:41', '1', '2023-01-08 11:59:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2097, '删除标签', 'mp:tag:delete', 3, 3, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 12:00:04', '1', '2023-01-08 12:00:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2098, '同步标签', 'mp:tag:sync', 3, 4, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 12:00:29', '1', '2023-01-08 12:00:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2099, '粉丝管理', '', 2, 4, 2084, 'user', 'fa:user-secret', 'mp/user/index', 'MpUser', 0, '1', '1', '1', '1', '2023-01-08 16:51:20', '1', '2024-02-29 12:42:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2100, '查询粉丝', 'mp:user:query', 3, 0, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:16:59', '1', '2023-01-08 17:17:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2101, '修改粉丝', 'mp:user:update', 3, 1, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:17:11', '1', '2023-01-08 17:17:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2102, '同步粉丝', 'mp:user:sync', 3, 2, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:17:40', '1', '2023-01-08 17:17:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2103, '消息管理', '', 2, 5, 2084, 'message', 'ep:message', 'mp/message/index', 'MpMessage', 0, '1', '1', '1', '1', '2023-01-08 18:44:19', '1', '2024-02-29 12:42:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2104, '图文发表记录', '', 2, 10, 2084, 'free-publish', 'ep:edit-pen', 'mp/freePublish/index', 'MpFreePublish', 0, '1', '1', '1', '1', '2023-01-13 00:30:50', '1', '2024-02-29 12:43:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2105, '查询发布列表', 'mp:free-publish:query', 3, 1, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:19:17', '1', '2023-01-13 07:19:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2106, '发布草稿', 'mp:free-publish:submit', 3, 2, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:19:46', '1', '2023-01-13 07:19:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2107, '删除发布记录', 'mp:free-publish:delete', 3, 3, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:20:01', '1', '2023-01-13 07:20:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2108, '图文草稿箱', '', 2, 9, 2084, 'draft', 'ep:edit', 'mp/draft/index', 'MpDraft', 0, '1', '1', '1', '1', '2023-01-13 07:40:21', '1', '2024-02-29 12:43:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2109, '新建草稿', 'mp:draft:create', 3, 1, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 23:15:30', '1', '2023-01-13 23:15:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2110, '修改草稿', 'mp:draft:update', 3, 2, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:08:47', '1', '2023-01-14 10:08:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2111, '查询草稿', 'mp:draft:query', 3, 0, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:09:01', '1', '2023-01-14 10:09:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2112, '删除草稿', 'mp:draft:delete', 3, 3, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:09:19', '1', '2023-01-14 10:09:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2113, '素材管理', '', 2, 8, 2084, 'material', 'ep:basketball', 'mp/material/index', 'MpMaterial', 0, '1', '1', '1', '1', '2023-01-14 14:12:07', '1', '2024-02-29 12:43:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2114, '上传临时素材', 'mp:material:upload-temporary', 3, 1, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:33:55', '1', '2023-01-14 15:33:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2115, '上传永久素材', 'mp:material:upload-permanent', 3, 2, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:34:14', '1', '2023-01-14 15:34:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2116, '删除素材', 'mp:material:delete', 3, 3, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:35:37', '1', '2023-01-14 15:35:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2117, '上传图文图片', 'mp:material:upload-news-image', 3, 4, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:36:31', '1', '2023-01-14 15:36:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2118, '查询素材', 'mp:material:query', 3, 5, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:39:22', '1', '2023-01-14 15:39:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2119, '菜单管理', '', 2, 6, 2084, 'menu', 'ep:menu', 'mp/menu/index', 'MpMenu', 0, '1', '1', '1', '1', '2023-01-14 17:43:54', '1', '2025-04-01 20:21:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2120, '自动回复', '', 2, 7, 2084, 'auto-reply', 'fa-solid:republican', 'mp/autoReply/index', 'MpAutoReply', 0, '1', '1', '1', '1', '2023-01-15 22:13:09', '1', '2024-02-29 12:43:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2121, '查询回复', 'mp:auto-reply:query', 3, 0, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:28:41', '1', '2023-01-16 22:28:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2122, '新增回复', 'mp:auto-reply:create', 3, 1, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:28:54', '1', '2023-01-16 22:28:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2123, '修改回复', 'mp:auto-reply:update', 3, 2, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:29:05', '1', '2023-01-16 22:29:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2124, '删除回复', 'mp:auto-reply:delete', 3, 3, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:29:34', '1', '2023-01-16 22:29:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2125, '查询菜单', 'mp:menu:query', 3, 0, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:05:41', '1', '2023-01-17 23:05:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2126, '保存菜单', 'mp:menu:save', 3, 1, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:06:01', '1', '2023-01-17 23:06:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2127, '删除菜单', 'mp:menu:delete', 3, 2, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:06:16', '1', '2023-01-17 23:06:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2128, '查询消息', 'mp:message:query', 3, 0, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:07:14', '1', '2023-01-17 23:07:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2129, '发送消息', 'mp:message:send', 3, 1, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:07:26', '1', '2023-01-17 23:07:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2130, '邮箱管理', '', 2, 2, 2739, 'mail', 'fa-solid:mail-bulk', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-25 17:27:44', '1', '2024-04-22 23:56:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2131, '邮箱账号', '', 2, 0, 2130, 'mail-account', 'fa:universal-access', 'system/mail/account/index', 'SystemMailAccount', 0, '1', '1', '1', '', '2023-01-25 09:33:48', '1', '2024-02-29 08:48:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2132, '账号查询', 'system:mail-account:query', 3, 1, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2133, '账号创建', 'system:mail-account:create', 3, 2, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2134, '账号更新', 'system:mail-account:update', 3, 3, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2135, '账号删除', 'system:mail-account:delete', 3, 4, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2136, '邮件模版', '', 2, 0, 2130, 'mail-template', 'fa:tag', 'system/mail/template/index', 'SystemMailTemplate', 0, '1', '1', '1', '', '2023-01-25 12:05:31', '1', '2024-02-29 08:48:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2137, '模版查询', 'system:mail-template:query', 3, 1, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2138, '模版创建', 'system:mail-template:create', 3, 2, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2139, '模版更新', 'system:mail-template:update', 3, 3, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2140, '模版删除', 'system:mail-template:delete', 3, 4, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2141, '邮件记录', '', 2, 0, 2130, 'mail-log', 'fa:edit', 'system/mail/log/index', 'SystemMailLog', 0, '1', '1', '1', '', '2023-01-26 02:16:50', '1', '2024-02-29 08:48:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2142, '日志查询', 'system:mail-log:query', 3, 1, 2141, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-26 02:16:50', '', '2023-01-26 02:16:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2143, '发送测试邮件', 'system:mail-template:send-mail', 3, 5, 2136, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-26 23:29:15', '1', '2023-01-26 23:29:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2144, '站内信管理', '', 1, 3, 2739, 'notify', 'ep:message-box', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-28 10:25:18', '1', '2024-04-22 23:56:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2145, '模板管理', '', 2, 0, 2144, 'notify-template', 'fa:archive', 'system/notify/template/index', 'SystemNotifyTemplate', 0, '1', '1', '1', '', '2023-01-28 02:26:42', '1', '2024-02-29 08:49:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2146, '站内信模板查询', 'system:notify-template:query', 3, 1, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2147, '站内信模板创建', 'system:notify-template:create', 3, 2, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2148, '站内信模板更新', 'system:notify-template:update', 3, 3, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2149, '站内信模板删除', 'system:notify-template:delete', 3, 4, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2150, '发送测试站内信', 'system:notify-template:send-notify', 3, 5, 2145, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-28 10:54:43', '1', '2023-01-28 10:54:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2151, '消息记录', '', 2, 0, 2144, 'notify-message', 'fa:edit', 'system/notify/message/index', 'SystemNotifyMessage', 0, '1', '1', '1', '', '2023-01-28 04:28:22', '1', '2024-02-29 08:49:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2152, '站内信消息查询', 'system:notify-message:query', 3, 1, 2151, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 04:28:22', '', '2023-01-28 04:28:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2153, '大屏设计器', '', 2, 2, 1281, 'go-view', 'fa:area-chart', 'report/goview/index', 'GoView', 0, '1', '1', '1', '1', '2023-02-07 00:03:19', '1', '2025-05-03 09:57:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2154, '创建项目', 'report:go-view-project:create', 3, 1, 2153, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-02-07 19:25:14', '1', '2023-02-07 19:25:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2155, '更新项目', 'report:go-view-project:update', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', '2023-02-07 19:25:34', '1', '2024-04-24 20:01:18', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:25:53', '1', '2023-02-07 19:25:53', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:26:15', '1', '2023-02-07 19:26:15', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:26:35', '1', '2023-02-07 19:26:35', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-10 22:46:28', '1', '2024-07-28 11:36:48', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-10 22:47:07', '1', '2023-12-02 21:32:29', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2023-02-11 14:21:42', '1', '2024-01-18 23:50:00', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2164, '配送管理', '', 1, 3, 2072, 'delivery', 'ep:shopping-cart', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:18:02', '1', '2023-09-28 10:58:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2165, '快递发货', '', 1, 0, 2164, 'express', 'ep:bicycle', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:22:06', '1', '2023-08-30 21:02:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2166, '门店自提', '', 1, 1, 2164, 'pick-up-store', 'ep:add-location', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:23:14', '1', '2023-08-30 21:03:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2167, '快递公司', '', 2, 0, 2165, 'express', 'ep:compass', 'mall/trade/delivery/express/index', 'Express', 0, '1', '1', '1', '1', '2023-05-18 09:27:21', '1', '2024-11-29 11:20:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2168, '快递公司查询', 'trade:delivery:express:query', 3, 1, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2169, '快递公司创建', 'trade:delivery:express:create', 3, 2, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2170, '快递公司更新', 'trade:delivery:express:update', 3, 3, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2171, '快递公司删除', 'trade:delivery:express:delete', 3, 4, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2172, '快递公司导出', 'trade:delivery:express:export', 3, 5, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2173, '运费模版', 'trade:delivery:express-template:query', 2, 1, 2165, 'express-template', 'ep:coordinate', 'mall/trade/delivery/expressTemplate/index', 'ExpressTemplate', 0, '1', '1', '1', '1', '2023-05-20 06:48:10', '1', '2023-08-30 21:03:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2174, '快递运费模板查询', 'trade:delivery:express-template:query', 3, 1, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2175, '快递运费模板创建', 'trade:delivery:express-template:create', 3, 2, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2176, '快递运费模板更新', 'trade:delivery:express-template:update', 3, 3, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2177, '快递运费模板删除', 'trade:delivery:express-template:delete', 3, 4, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2178, '快递运费模板导出', 'trade:delivery:express-template:export', 3, 5, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2179, '门店管理', '', 2, 1, 2166, 'pick-up-store', 'ep:basketball', 'mall/trade/delivery/pickUpStore/index', 'PickUpStore', 0, '1', '1', '1', '1', '2023-05-25 10:50:00', '1', '2023-08-30 21:03:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2180, '自提门店查询', 'trade:delivery:pick-up-store:query', 3, 1, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2181, '自提门店创建', 'trade:delivery:pick-up-store:create', 3, 2, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2182, '自提门店更新', 'trade:delivery:pick-up-store:update', 3, 3, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2183, '自提门店删除', 'trade:delivery:pick-up-store:delete', 3, 4, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2184, '自提门店导出', 'trade:delivery:pick-up-store:export', 3, 5, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2209, '秒杀活动', '', 2, 3, 2030, 'seckill', 'ep:place', '', '', 0, '1', '1', '1', '1', '2023-06-24 17:39:13', '1', '2023-06-24 18:55:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2262, '会员中心', '', 1, 55, 0, '/member', 'ep:bicycle', NULL, NULL, 0, '1', '1', '1', '1', '2023-06-10 00:42:03', '1', '2023-08-20 09:23:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2275, '会员配置', '', 2, 0, 2262, 'config', 'fa:archive', 'member/config/index', 'MemberConfig', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2023-10-01 23:41:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2276, '会员配置查询', 'member:config:query', 3, 1, 2275, '', '', '', '', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:48:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2277, '会员配置保存', 'member:config:save', 3, 2, 2275, '', '', '', '', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:49:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2281, '签到配置', '', 2, 2, 2300, 'config', 'ep:calendar', 'member/signin/config/index', 'SignInConfig', 0, '1', '1', '1', '', '2023-06-10 03:26:12', '1', '2023-08-20 19:25:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2282, '积分签到规则查询', 'point:sign-in-config:query', 3, 1, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2283, '积分签到规则创建', 'point:sign-in-config:create', 3, 2, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2284, '积分签到规则更新', 'point:sign-in-config:update', 3, 3, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2285, '积分签到规则删除', 'point:sign-in-config:delete', 3, 4, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2287, '会员积分', '', 2, 10, 2262, 'record', 'fa:asterisk', 'member/point/record/index', 'PointRecord', 0, '1', '1', '1', '', '2023-06-10 04:18:50', '1', '2023-10-01 23:42:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2288, '用户积分记录查询', 'point:record:query', 3, 1, 2287, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:18:50', '', '2023-06-10 04:18:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2293, '签到记录', '', 2, 3, 2300, 'record', 'ep:chicken', 'member/signin/record/index', 'SignInRecord', 0, '1', '1', '1', '', '2023-06-10 04:48:22', '1', '2023-08-20 19:26:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2294, '用户签到积分查询', 'point:sign-in-record:query', 3, 1, 2293, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2297, '用户签到积分删除', 'point:sign-in-record:delete', 3, 4, 2293, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2300, '会员签到', '', 1, 11, 2262, 'signin', 'ep:alarm-clock', '', '', 0, '1', '1', '1', '1', '2023-06-27 22:49:53', '1', '2023-08-20 09:23:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2301, '回调通知', '', 2, 5, 1117, 'notify', 'ep:mute-notification', 'pay/notify/index', 'PayNotify', 0, '1', '1', '1', '', '2023-07-20 04:41:32', '1', '2024-01-18 23:56:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2302, '支付通知查询', 'pay:notify:query', 3, 1, 2301, '', '', '', NULL, 0, '1', '1', '1', '', '2023-07-20 04:41:32', '', '2023-07-20 04:41:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2303, '拼团活动', '', 2, 3, 2030, 'combination', 'fa:group', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:19:54', '1', '2023-08-12 17:20:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2304, '拼团商品', '', 2, 1, 2303, 'acitivity', 'ep:apple', 'mall/promotion/combination/activity/index', 'PromotionCombinationActivity', 0, '1', '1', '1', '1', '2023-08-12 17:22:03', '1', '2023-08-12 17:22:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2305, '拼团活动查询', 'promotion:combination-activity:query', 3, 1, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:54:32', '1', '2023-11-24 11:57:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2306, '拼团活动创建', 'promotion:combination-activity:create', 3, 2, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:54:49', '1', '2023-08-12 17:54:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2307, '拼团活动更新', 'promotion:combination-activity:update', 3, 3, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:04', '1', '2023-08-12 17:55:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2308, '拼团活动删除', 'promotion:combination-activity:delete', 3, 4, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:23', '1', '2023-08-12 17:55:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2309, '拼团活动关闭', 'promotion:combination-activity:close', 3, 5, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:37', '1', '2023-10-06 10:51:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2310, '砍价活动', '', 2, 4, 2030, 'bargain', 'ep:box', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:27:25', '1', '2023-08-13 00:27:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2311, '砍价商品', '', 2, 1, 2310, 'activity', 'ep:burger', 'mall/promotion/bargain/activity/index', 'PromotionBargainActivity', 0, '1', '1', '1', '1', '2023-08-13 00:28:49', '1', '2023-10-05 01:16:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2312, '砍价活动查询', 'promotion:bargain-activity:query', 3, 1, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:30', '1', '2023-08-13 00:32:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2313, '砍价活动创建', 'promotion:bargain-activity:create', 3, 2, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:44', '1', '2023-08-13 00:32:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2314, '砍价活动更新', 'promotion:bargain-activity:update', 3, 3, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:55', '1', '2023-08-13 00:32:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2315, '砍价活动删除', 'promotion:bargain-activity:delete', 3, 4, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:34:50', '1', '2023-08-13 00:34:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2316, '砍价活动关闭', 'promotion:bargain-activity:close', 3, 5, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:35:02', '1', '2023-08-13 00:35:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2317, '会员管理', '', 2, 0, 2262, 'user', 'ep:avatar', 'member/user/index', 'MemberUser', 0, '1', '1', '1', '', '2023-08-19 04:12:15', '1', '2023-08-24 00:50:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2318, '会员用户查询', 'member:user:query', 3, 1, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2319, '会员用户更新', 'member:user:update', 3, 3, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2320, '会员标签', '', 2, 1, 2262, 'tag', 'ep:collection-tag', 'member/tag/index', 'MemberTag', 0, '1', '1', '1', '', '2023-08-20 01:03:08', '1', '2023-08-20 09:23:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2321, '会员标签查询', 'member:tag:query', 3, 1, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2322, '会员标签创建', 'member:tag:create', 3, 2, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2323, '会员标签更新', 'member:tag:update', 3, 3, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2324, '会员标签删除', 'member:tag:delete', 3, 4, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2325, '会员等级', '', 2, 2, 2262, 'level', 'fa:level-up', 'member/level/index', 'MemberLevel', 0, '1', '1', '1', '', '2023-08-22 12:41:01', '1', '2023-08-22 21:47:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2326, '会员等级查询', 'member:level:query', 3, 1, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2327, '会员等级创建', 'member:level:create', 3, 2, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2328, '会员等级更新', 'member:level:update', 3, 3, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2329, '会员等级删除', 'member:level:delete', 3, 4, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2330, '会员分组', '', 2, 3, 2262, 'group', 'fa:group', 'member/group/index', 'MemberGroup', 0, '1', '1', '1', '', '2023-08-22 13:50:06', '1', '2023-10-01 23:42:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2331, '用户分组查询', 'member:group:query', 3, 1, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2332, '用户分组创建', 'member:group:create', 3, 2, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2333, '用户分组更新', 'member:group:update', 3, 3, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2334, '用户分组删除', 'member:group:delete', 3, 4, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2335, '用户等级修改', 'member:user:update-level', 3, 5, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-23 16:49:05', '', '2023-08-23 16:50:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2336, '商品评论', '', 2, 5, 2000, 'comment', 'ep:comment', 'mall/product/comment/index', 'ProductComment', 0, '1', '1', '1', '1', '2023-08-26 11:03:00', '1', '2023-08-26 11:03:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2337, '评论查询', 'product:comment:query', 3, 1, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:01', '1', '2023-08-26 11:04:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2338, '添加自评', 'product:comment:create', 3, 2, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:23', '1', '2023-08-26 11:08:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2339, '商家回复', 'product:comment:update', 3, 3, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:37', '1', '2023-08-26 11:04:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2340, '显隐评论', 'product:comment:update', 3, 4, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:55', '1', '2023-08-26 11:04:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2341, '优惠劵发送', 'promotion:coupon:send', 3, 2, 2038, '', '', '', '', 0, '1', '1', '1', '1', '2023-09-02 00:03:14', '1', '2023-09-02 00:03:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2342, '交易配置', '', 2, 0, 2072, 'config', 'ep:setting', 'mall/trade/config/index', 'TradeConfig', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:30:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2343, '交易中心配置查询', 'trade:config:query', 3, 1, 2342, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2344, '交易中心配置保存', 'trade:config:save', 3, 2, 2342, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2345, '分销管理', '', 1, 4, 2072, 'brokerage', 'fa-solid:project-diagram', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2023-09-28 10:58:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2346, '分销用户', '', 2, 0, 2345, 'brokerage-user', 'fa-solid:user-tie', 'mall/trade/brokerage/user/index', 'TradeBrokerageUser', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2347, '分销用户查询', 'trade:brokerage-user:query', 3, 1, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2348, '分销用户推广人查询', 'trade:brokerage-user:user-query', 3, 2, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2349, '分销用户推广订单查询', 'trade:brokerage-user:order-query', 3, 3, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2350, '分销用户修改推广资格', 'trade:brokerage-user:update-brokerage-enable', 3, 4, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2351, '修改推广员', 'trade:brokerage-user:update-bind-user', 3, 5, 2346, '', '', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2352, '清除推广员', 'trade:brokerage-user:clear-bind-user', 3, 6, 2346, '', '', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2353, '佣金记录', '', 2, 1, 2345, 'brokerage-record', 'fa:money', 'mall/trade/brokerage/record/index', 'TradeBrokerageRecord', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2354, '佣金记录查询', 'trade:brokerage-record:query', 3, 1, 2353, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2355, '佣金提现', '', 2, 2, 2345, 'brokerage-withdraw', 'fa:credit-card', 'mall/trade/brokerage/withdraw/index', 'TradeBrokerageWithdraw', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2356, '佣金提现查询', 'trade:brokerage-withdraw:query', 3, 1, 2355, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2357, '佣金提现审核', 'trade:brokerage-withdraw:audit', 3, 2, 2355, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2358, '统计中心', '', 1, 75, 2362, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '', '2023-09-30 03:22:40', '1', '2023-09-30 11:54:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2359, '交易统计', '', 2, 4, 2358, 'trade', 'fa-solid:credit-card', 'mall/statistics/trade/index', 'TradeStatistics', 0, '1', '1', '1', '', '2023-09-30 03:22:40', '1', '2024-02-26 20:42:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2360, '交易统计查询', 'statistics:trade:query', 3, 1, 2359, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2361, '交易统计导出', 'statistics:trade:export', 3, 2, 2359, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2362, '商城系统', '', 1, 59, 0, '/mall', 'ep:shop', '', '', 0, '1', '1', '1', '1', '2023-09-30 11:52:02', '1', '2023-09-30 11:52:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2363, '用户积分修改', 'member:user:update-point', 3, 6, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-01 14:39:43', '', '2023-10-01 14:39:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2364, '用户余额修改', 'pay:wallet:update-balance', 3, 7, 2317, '', '', '', '', 0, '1', '1', '1', '', '2023-10-01 14:39:43', '1', '2024-10-01 09:42:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2365, '优惠劵', '', 1, 2, 2030, 'coupon', 'fa-solid:disease', '', '', 0, '1', '1', '1', '1', '2023-10-03 12:39:15', '1', '2023-10-05 00:16:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2366, '砍价记录', '', 2, 2, 2310, 'record', 'ep:list', 'mall/promotion/bargain/record/index', 'PromotionBargainRecord', 0, '1', '1', '1', '', '2023-10-05 02:49:06', '1', '2023-10-05 10:50:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2367, '砍价记录查询', 'promotion:bargain-record:query', 3, 1, 2366, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-05 02:49:06', '', '2023-10-05 02:49:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2368, '助力记录查询', 'promotion:bargain-help:query', 3, 2, 2366, '', '', '', '', 0, '1', '1', '1', '1', '2023-10-05 12:27:49', '1', '2023-10-05 12:27:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2369, '拼团记录', 'promotion:combination-record:query', 2, 2, 2303, 'record', 'ep:avatar', 'mall/promotion/combination/record/index.vue', 'PromotionCombinationRecord', 0, '1', '1', '1', '1', '2023-10-08 07:10:22', '1', '2023-10-08 07:34:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2374, '会员统计', '', 2, 2, 2358, 'member', 'ep:avatar', 'mall/statistics/member/index', 'MemberStatistics', 0, '1', '1', '1', '', '2023-10-11 04:39:24', '1', '2024-02-26 20:41:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2375, '会员统计查询', 'statistics:member:query', 3, 1, 2374, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-11 04:39:24', '', '2023-10-11 04:39:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2376, '订单核销', 'trade:order:pick-up', 3, 10, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2023-10-14 17:11:58', '1', '2023-10-14 17:11:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2377, '文章分类', '', 2, 0, 2387, 'article/category', 'fa:certificate', 'mall/promotion/article/category/index', 'ArticleCategory', 0, '1', '1', '1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:38:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2378, '分类查询', 'promotion:article-category:query', 3, 1, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2379, '分类创建', 'promotion:article-category:create', 3, 2, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2380, '分类更新', 'promotion:article-category:update', 3, 3, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2381, '分类删除', 'promotion:article-category:delete', 3, 4, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2382, '文章列表', '', 2, 2, 2387, 'article', 'ep:connection', 'mall/promotion/article/index', 'Article', 0, '1', '1', '1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:41:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2383, '文章管理查询', 'promotion:article:query', 3, 1, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2384, '文章管理创建', 'promotion:article:create', 3, 2, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2385, '文章管理更新', 'promotion:article:update', 3, 3, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2386, '文章管理删除', 'promotion:article:delete', 3, 4, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2387, '内容管理', '', 1, 1, 2030, 'content', 'ep:collection', '', '', 0, '1', '1', '1', '1', '2023-10-16 09:37:31', '1', '2023-10-16 09:37:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2388, '商城首页', '', 2, 1, 2362, 'home', 'ep:home-filled', 'mall/home/index', 'MallHome', 0, '1', '1', '1', '', '2023-10-16 12:10:33', '', '2023-10-16 12:10:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2389, '核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder', 0, '1', '1', '1', '', '2023-10-19 16:09:51', '', '2023-10-19 16:09:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2390, '优惠活动', '', 1, 99, 2030, 'youhui', 'ep:aim', '', '', 0, '1', '1', '1', '1', '2023-10-21 19:23:49', '1', '2023-10-21 19:23:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2391, '客户管理', '', 2, 10, 2397, 'customer', 'fa:address-book-o', 'crm/customer/index', 'CrmCustomer', 0, '1', '1', '1', '', '2023-10-29 09:04:21', '1', '2024-02-17 17:13:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2392, '客户查询', 'crm:customer:query', 3, 1, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2393, '客户创建', 'crm:customer:create', 3, 2, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2394, '客户更新', 'crm:customer:update', 3, 3, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2395, '客户删除', 'crm:customer:delete', 3, 4, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2396, '客户导出', 'crm:customer:export', 3, 5, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2397, 'CRM 系统', '', 1, 200, 0, '/crm', 'simple-icons:civicrm', '', '', 0, '1', '1', '1', '1', '2023-10-29 17:08:30', '1', '2025-04-19 18:56:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2398, '合同管理', '', 2, 50, 2397, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, '1', '1', '1', '', '2023-10-29 10:50:41', '1', '2024-02-17 17:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2399, '合同查询', 'crm:contract:query', 3, 1, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2400, '合同创建', 'crm:contract:create', 3, 2, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2401, '合同更新', 'crm:contract:update', 3, 3, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2402, '合同删除', 'crm:contract:delete', 3, 4, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2403, '合同导出', 'crm:contract:export', 3, 5, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2404, '线索管理', '', 2, 8, 2397, 'clue', 'fa:pagelines', 'crm/clue/index', 'CrmClue', 0, '1', '1', '1', '', '2023-10-29 11:06:29', '1', '2024-02-17 17:15:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2405, '线索查询', 'crm:clue:query', 3, 1, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2406, '线索创建', 'crm:clue:create', 3, 2, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2407, '线索更新', 'crm:clue:update', 3, 3, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2408, '线索删除', 'crm:clue:delete', 3, 4, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2409, '线索导出', 'crm:clue:export', 3, 5, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2410, '商机管理', '', 2, 40, 2397, 'business', 'fa:bus', 'crm/business/index', 'CrmBusiness', 0, '1', '1', '1', '', '2023-10-29 11:12:35', '1', '2024-02-17 17:14:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2411, '商机查询', 'crm:business:query', 3, 1, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2412, '商机创建', 'crm:business:create', 3, 2, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2413, '商机更新', 'crm:business:update', 3, 3, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2414, '商机删除', 'crm:business:delete', 3, 4, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2415, '商机导出', 'crm:business:export', 3, 5, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2416, '联系人管理', '', 2, 20, 2397, 'contact', 'fa:address-book-o', 'crm/contact/index', 'CrmContact', 0, '1', '1', '1', '', '2023-10-29 11:14:56', '1', '2024-02-17 17:13:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2417, '联系人查询', 'crm:contact:query', 3, 1, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2418, '联系人创建', 'crm:contact:create', 3, 2, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2419, '联系人更新', 'crm:contact:update', 3, 3, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2420, '联系人删除', 'crm:contact:delete', 3, 4, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2421, '联系人导出', 'crm:contact:export', 3, 5, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2422, '回款管理', '', 2, 60, 2397, 'receivable', 'ep:money', 'crm/receivable/index', 'CrmReceivable', 0, '1', '1', '1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2423, '回款管理查询', 'crm:receivable:query', 3, 1, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2424, '回款管理创建', 'crm:receivable:create', 3, 2, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2425, '回款管理更新', 'crm:receivable:update', 3, 3, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2426, '回款管理删除', 'crm:receivable:delete', 3, 4, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2427, '回款管理导出', 'crm:receivable:export', 3, 5, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2428, '回款计划', '', 2, 61, 2397, 'receivable-plan', 'fa:money', 'crm/receivable/plan/index', 'CrmReceivablePlan', 0, '1', '1', '1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2429, '回款计划查询', 'crm:receivable-plan:query', 3, 1, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2430, '回款计划创建', 'crm:receivable-plan:create', 3, 2, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2432, '回款计划删除', 'crm:receivable-plan:delete', 3, 4, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2433, '回款计划导出', 'crm:receivable-plan:export', 3, 5, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', '', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '1', '2025-03-15 21:34:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2436, '装修模板', '', 2, 1, 2435, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2437, '装修模板查询', 'promotion:diy-template:query', 3, 1, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2438, '装修模板创建', 'promotion:diy-template:create', 3, 2, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2439, '装修模板更新', 'promotion:diy-template:update', 3, 3, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2440, '装修模板删除', 'promotion:diy-template:delete', 3, 4, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2441, '装修模板使用', 'promotion:diy-template:use', 3, 5, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2442, '装修页面', '', 2, 2, 2435, 'diy-page', 'foundation:page-edit', 'mall/promotion/diy/page/index', 'DiyPage', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2443, '装修页面查询', 'promotion:diy-page:query', 3, 1, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2444, '装修页面创建', 'promotion:diy-page:create', 3, 2, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2445, '装修页面更新', 'promotion:diy-page:update', 3, 3, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2446, '装修页面删除', 'promotion:diy-page:delete', 3, 4, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2447, '三方登录', '', 1, 10, 1, 'social', 'fa:rocket', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:12:01', '1', '2024-02-29 01:14:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2448, '三方应用', '', 2, 1, 2447, 'client', 'ep:set-up', 'system/social/client/index.vue', 'SocialClient', 0, '1', '1', '1', '1', '2023-11-04 12:17:19', '1', '2024-05-04 19:09:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2449, '三方应用查询', 'system:social-client:query', 3, 1, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:43:12', '1', '2023-11-04 12:43:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2450, '三方应用创建', 'system:social-client:create', 3, 2, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:43:58', '1', '2023-11-04 12:43:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2451, '三方应用更新', 'system:social-client:update', 3, 3, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:44:27', '1', '2023-11-04 12:44:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2452, '三方应用删除', 'system:social-client:delete', 3, 4, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:44:43', '1', '2023-11-04 12:44:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2453, '三方用户', 'system:social-user:query', 2, 2, 2447, 'user', 'ep:avatar', 'system/social/user/index.vue', 'SocialUser', 0, '1', '1', '1', '1', '2023-11-04 14:01:05', '1', '2023-11-04 14:01:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2472, '主子表（内嵌）', '', 2, 12, 1070, 'demo03-inner', 'fa:power-off', 'infra/demo/demo03/inner/index', 'Demo03StudentInner', 0, '1', '1', '1', '', '2023-11-13 04:39:51', '1', '2023-11-16 23:53:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2478, '单表（增删改查）', '', 2, 1, 1070, 'demo01-contact', 'ep:bicycle', 'infra/demo/demo01/index', 'Demo01Contact', 0, '1', '1', '1', '', '2023-11-15 14:42:30', '1', '2023-11-16 20:34:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2479, '示例联系人查询', 'infra:demo01-contact:query', 3, 1, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2480, '示例联系人创建', 'infra:demo01-contact:create', 3, 2, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2481, '示例联系人更新', 'infra:demo01-contact:update', 3, 3, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2482, '示例联系人删除', 'infra:demo01-contact:delete', 3, 4, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2483, '示例联系人导出', 'infra:demo01-contact:export', 3, 5, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2484, '树表（增删改查）', '', 2, 2, 1070, 'demo02-category', 'fa:tree', 'infra/demo/demo02/index', 'Demo02Category', 0, '1', '1', '1', '', '2023-11-16 12:18:27', '1', '2023-11-16 20:35:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2485, '示例分类查询', 'infra:demo02-category:query', 3, 1, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2486, '示例分类创建', 'infra:demo02-category:create', 3, 2, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2487, '示例分类更新', 'infra:demo02-category:update', 3, 3, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2488, '示例分类删除', 'infra:demo02-category:delete', 3, 4, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2489, '示例分类导出', 'infra:demo02-category:export', 3, 5, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2490, '主子表（标准）', '', 2, 10, 1070, 'demo03-normal', 'fa:battery-3', 'infra/demo/demo03/normal/index', 'Demo03StudentNormal', 0, '1', '1', '1', '', '2023-11-16 12:53:37', '1', '2023-11-16 23:10:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2491, '学生查询', 'infra:demo03-student:query', 3, 1, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2492, '学生创建', 'infra:demo03-student:create', 3, 2, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2493, '学生更新', 'infra:demo03-student:update', 3, 3, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2494, '学生删除', 'infra:demo03-student:delete', 3, 4, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2495, '学生导出', 'infra:demo03-student:export', 3, 5, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2497, '主子表（ERP）', '', 2, 11, 1070, 'demo03-erp', 'ep:calendar', 'infra/demo/demo03/erp/index', 'Demo03StudentERP', 0, '1', '1', '1', '', '2023-11-16 15:50:59', '1', '2023-11-17 13:19:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2516, '客户公海配置', '', 2, 0, 2524, 'customer-pool-config', 'ep:data-analysis', 'crm/customer/poolConfig/index', 'CrmCustomerPoolConfig', 0, '1', '1', '1', '', '2023-11-18 13:33:31', '1', '2024-01-03 19:52:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2517, '客户公海配置保存', 'crm:customer-pool-config:update', 3, 1, 2516, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:31', '', '2023-11-18 13:33:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2518, '客户限制配置', '', 2, 1, 2524, 'customer-limit-config', 'ep:avatar', 'crm/customer/limitConfig/index', 'CrmCustomerLimitConfig', 0, '1', '1', '1', '', '2023-11-18 13:33:53', '1', '2024-02-24 16:43:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2519, '客户限制配置查询', 'crm:customer-limit-config:query', 3, 1, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2520, '客户限制配置创建', 'crm:customer-limit-config:create', 3, 2, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2521, '客户限制配置更新', 'crm:customer-limit-config:update', 3, 3, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2522, '客户限制配置删除', 'crm:customer-limit-config:delete', 3, 4, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2523, '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2524, '系统配置', '', 1, 999, 2397, 'config', 'ep:connection', '', '', 0, '1', '1', '1', '1', '2023-11-18 21:58:00', '1', '2024-02-17 17:14:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2525, 'WebSocket', '', 2, 5, 2, 'websocket', 'ep:connection', 'infra/webSocket/index', 'InfraWebSocket', 0, '1', '1', '1', '1', '2023-11-23 19:41:55', '1', '2024-04-23 00:02:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2526, '产品管理', '', 2, 80, 2397, 'product', 'fa:product-hunt', 'crm/product/index', 'CrmProduct', 0, '1', '1', '1', '1', '2023-12-05 22:45:26', '1', '2024-02-20 20:36:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2527, '产品查询', 'crm:product:query', 3, 1, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:47:16', '1', '2023-12-05 22:47:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2528, '产品创建', 'crm:product:create', 3, 2, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:47:41', '1', '2023-12-05 22:47:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2529, '产品更新', 'crm:product:update', 3, 3, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:03', '1', '2023-12-05 22:48:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2530, '产品删除', 'crm:product:delete', 3, 4, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:17', '1', '2023-12-05 22:48:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2531, '产品导出', 'crm:product:export', 3, 5, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:29', '1', '2023-12-05 22:48:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2532, '产品分类配置', '', 2, 3, 2524, 'product/category', 'fa-solid:window-restore', 'crm/product/category/index', 'CrmProductCategory', 0, '1', '1', '1', '1', '2023-12-06 12:52:36', '1', '2023-12-06 12:52:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2533, '产品分类查询', 'crm:product-category:query', 3, 1, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:23', '1', '2023-12-06 12:53:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2534, '产品分类创建', 'crm:product-category:create', 3, 2, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:41', '1', '2023-12-06 12:53:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2535, '产品分类更新', 'crm:product-category:update', 3, 3, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:59', '1', '2023-12-06 12:53:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2536, '产品分类删除', 'crm:product-category:delete', 3, 4, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:54:14', '1', '2023-12-06 12:54:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2543, '关联商机', 'crm:contact:create-business', 3, 10, 2416, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-02 17:28:25', '1', '2024-01-02 17:28:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2544, '取关商机', 'crm:contact:delete-business', 3, 11, 2416, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-02 17:28:43', '1', '2024-01-02 17:28:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2545, '商品统计', '', 2, 3, 2358, 'product', 'fa:product-hunt', 'mall/statistics/product/index', 'ProductStatistics', 0, '1', '1', '1', '', '2023-12-15 18:54:28', '1', '2024-02-26 20:41:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2546, '客户公海', '', 2, 30, 2397, 'customer/pool', 'fa-solid:swimming-pool', 'crm/customer/pool/index', 'CrmCustomerPool', 0, '1', '1', '1', '1', '2024-01-15 21:29:34', '1', '2024-02-17 17:14:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2547, '订单查询', 'trade:order:query', 3, 1, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-16 08:52:00', '1', '2024-01-16 08:52:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2548, '订单更新', 'trade:order:update', 3, 2, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-16 08:52:21', '1', '2024-01-16 08:52:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2549, '支付&退款案例', '', 2, 1, 2161, 'order', 'fa:paypal', 'pay/demo/order/index', '', 0, '1', '1', '1', '1', '2024-01-18 23:45:00', '1', '2024-01-18 23:47:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2550, '提现转账案例', '', 2, 2, 2161, 'transfer', 'fa:transgender-alt', 'pay/demo/withdraw/index', '', 0, '1', '1', '1', '1', '2024-01-18 23:51:16', '1', '2025-05-08 13:04:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2551, '钱包管理', '', 1, 4, 1117, 'wallet', 'ep:wallet', '', '', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '1', '2024-02-29 08:58:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2552, '充值套餐', '', 2, 2, 2551, 'wallet-recharge-package', 'fa:leaf', 'pay/wallet/rechargePackage/index', 'WalletRechargePackage', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2553, '钱包充值套餐查询', 'pay:wallet-recharge-package:query', 3, 1, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2554, '钱包充值套餐创建', 'pay:wallet-recharge-package:create', 3, 2, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2555, '钱包充值套餐更新', 'pay:wallet-recharge-package:update', 3, 3, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2556, '钱包充值套餐删除', 'pay:wallet-recharge-package:delete', 3, 4, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2557, '钱包余额', '', 2, 1, 2551, 'wallet-balance', 'fa:leaf', 'pay/wallet/balance/index', 'WalletBalance', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2558, '钱包余额查询', 'pay:wallet:query', 3, 1, 2557, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2559, '转账订单', '', 2, 3, 1117, 'transfer', 'ep:credit-card', 'pay/transfer/index', 'PayTransfer', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2560, '数据统计', '', 1, 200, 2397, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '1', '2024-01-26 22:50:35', '1', '2024-02-24 20:10:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2561, '排行榜', 'crm:statistics-rank:query', 2, 1, 2560, 'ranking', 'fa:area-chart', 'crm/statistics/rank/index', 'CrmStatisticsRank', 0, '1', '1', '1', '1', '2024-01-26 22:52:09', '1', '2024-04-24 19:39:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2562, '客户导入', 'crm:customer:import', 3, 6, 2391, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-01 13:09:00', '1', '2024-02-01 13:09:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2563, 'ERP 系统', '', 1, 300, 0, '/erp', 'simple-icons:erpnext', '', '', 0, '1', '1', '1', '1', '2024-02-04 15:37:25', '1', '2025-04-19 18:56:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2564, '产品管理', '', 1, 40, 2563, 'product', 'fa:product-hunt', '', '', 0, '1', '1', '1', '1', '2024-02-04 15:38:43', '1', '2024-02-04 15:38:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2565, '产品信息', '', 2, 0, 2564, 'product', 'fa-solid:apple-alt', 'erp/product/product/index', 'ErpProduct', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-05 14:42:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2566, '产品查询', 'erp:product:query', 3, 1, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:21:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2567, '产品创建', 'erp:product:create', 3, 2, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2568, '产品更新', 'erp:product:update', 3, 3, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2569, '产品删除', 'erp:product:delete', 3, 4, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2570, '产品导出', 'erp:product:export', 3, 5, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2571, '产品分类', '', 2, 1, 2564, 'product-category', 'fa:certificate', 'erp/product/category/index', 'ErpProductCategory', 0, '1', '1', '1', '', '2024-02-04 09:21:04', '1', '2024-02-04 17:24:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2572, '分类查询', 'erp:product-category:query', 3, 1, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2573, '分类创建', 'erp:product-category:create', 3, 2, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2574, '分类更新', 'erp:product-category:update', 3, 3, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2575, '分类删除', 'erp:product-category:delete', 3, 4, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2576, '分类导出', 'erp:product-category:export', 3, 5, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2577, '产品单位', '', 2, 2, 2564, 'unit', 'ep:opportunity', 'erp/product/unit/index', 'ErpProductUnit', 0, '1', '1', '1', '', '2024-02-04 11:54:08', '1', '2024-02-04 19:54:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2578, '单位查询', 'erp:product-unit:query', 3, 1, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2579, '单位创建', 'erp:product-unit:create', 3, 2, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2580, '单位更新', 'erp:product-unit:update', 3, 3, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2581, '单位删除', 'erp:product-unit:delete', 3, 4, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2582, '单位导出', 'erp:product-unit:export', 3, 5, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2583, '库存管理', '', 1, 30, 2563, 'stock', 'fa:window-restore', '', '', 0, '1', '1', '1', '1', '2024-02-05 00:29:37', '1', '2024-02-05 00:29:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2584, '仓库信息', '', 2, 0, 2583, 'warehouse', 'ep:house', 'erp/stock/warehouse/index', 'ErpWarehouse', 0, '1', '1', '1', '', '2024-02-04 17:12:09', '1', '2024-02-05 01:12:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2585, '仓库查询', 'erp:warehouse:query', 3, 1, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2586, '仓库创建', 'erp:warehouse:create', 3, 2, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2587, '仓库更新', 'erp:warehouse:update', 3, 3, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2588, '仓库删除', 'erp:warehouse:delete', 3, 4, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2589, '仓库导出', 'erp:warehouse:export', 3, 5, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2590, '产品库存', '', 2, 1, 2583, 'stock', 'ep:coffee', 'erp/stock/stock/index', 'ErpStock', 0, '1', '1', '1', '', '2024-02-05 06:40:50', '1', '2024-02-05 14:42:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2591, '库存查询', 'erp:stock:query', 3, 1, 2590, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2592, '库存导出', 'erp:stock:export', 3, 5, 2590, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2593, '出入库明细', '', 2, 2, 2583, 'record', 'fa-solid:blog', 'erp/stock/record/index', 'ErpStockRecord', 0, '1', '1', '1', '', '2024-02-05 10:27:21', '1', '2024-02-06 17:26:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2594, '库存明细查询', 'erp:stock-record:query', 3, 1, 2593, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2595, '库存明细导出', 'erp:stock-record:export', 3, 5, 2593, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2596, '其它入库', '', 2, 3, 2583, 'in', 'ep:zoom-in', 'erp/stock/in/index', 'ErpStockIn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2597, '其它入库单查询', 'erp:stock-in:query', 3, 1, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2598, '其它入库单创建', 'erp:stock-in:create', 3, 2, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2599, '其它入库单更新', 'erp:stock-in:update', 3, 3, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2600, '其它入库单删除', 'erp:stock-in:delete', 3, 4, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2601, '其它入库单导出', 'erp:stock-in:export', 3, 5, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2602, '采购管理', '', 1, 10, 2563, 'purchase', 'fa:buysellads', '', '', 0, '1', '1', '1', '1', '2024-02-06 16:01:01', '1', '2024-02-06 16:01:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2603, '供应商信息', '', 2, 4, 2602, 'supplier', 'fa:superpowers', 'erp/purchase/supplier/index', 'ErpSupplier', 0, '1', '1', '1', '', '2024-02-06 08:21:55', '1', '2024-02-06 16:22:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2604, '供应商查询', 'erp:supplier:query', 3, 1, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2605, '供应商创建', 'erp:supplier:create', 3, 2, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2606, '供应商更新', 'erp:supplier:update', 3, 3, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2607, '供应商删除', 'erp:supplier:delete', 3, 4, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2608, '供应商导出', 'erp:supplier:export', 3, 5, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2609, '其它入库单审批', 'erp:stock-in:update-status', 3, 6, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2610, '其它出库', '', 2, 4, 2583, 'out', 'ep:zoom-out', 'erp/stock/out/index', 'ErpStockOut', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2611, '其它出库单查询', 'erp:stock-out:query', 3, 1, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2612, '其它出库单创建', 'erp:stock-out:create', 3, 2, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2613, '其它出库单更新', 'erp:stock-out:update', 3, 3, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2614, '其它出库单删除', 'erp:stock-out:delete', 3, 4, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2615, '其它出库单导出', 'erp:stock-out:export', 3, 5, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2616, '其它出库单审批', 'erp:stock-out:update-status', 3, 6, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2617, '销售管理', '', 1, 20, 2563, 'sale', 'fa:sellsy', '', '', 0, '1', '1', '1', '1', '2024-02-07 15:12:32', '1', '2024-02-07 15:12:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2618, '客户信息', '', 2, 4, 2617, 'customer', 'ep:avatar', 'erp/sale/customer/index', 'ErpCustomer', 0, '1', '1', '1', '', '2024-02-07 07:21:45', '1', '2024-02-07 15:22:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2619, '客户查询', 'erp:customer:query', 3, 1, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2620, '客户创建', 'erp:customer:create', 3, 2, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2621, '客户更新', 'erp:customer:update', 3, 3, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2622, '客户删除', 'erp:customer:delete', 3, 4, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2623, '客户导出', 'erp:customer:export', 3, 5, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2624, '库存调拨', '', 2, 5, 2583, 'move', 'ep:folder-remove', 'erp/stock/move/index', 'ErpStockMove', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-16 18:53:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2625, '库存调度单查询', 'erp:stock-move:query', 3, 1, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2626, '库存调度单创建', 'erp:stock-move:create', 3, 2, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2627, '库存调度单更新', 'erp:stock-move:update', 3, 3, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2628, '库存调度单删除', 'erp:stock-move:delete', 3, 4, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2629, '库存调度单导出', 'erp:stock-move:export', 3, 5, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2630, '库存调度单审批', 'erp:stock-move:update-status', 3, 6, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2631, '库存盘点', '', 2, 6, 2583, 'check', 'ep:circle-check-filled', 'erp/stock/check/index', 'ErpStockCheck', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-08 08:31:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2632, '库存盘点单查询', 'erp:stock-check:query', 3, 1, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2633, '库存盘点单创建', 'erp:stock-check:create', 3, 2, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2634, '库存盘点单更新', 'erp:stock-check:update', 3, 3, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2635, '库存盘点单删除', 'erp:stock-check:delete', 3, 4, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2636, '库存盘点单导出', 'erp:stock-check:export', 3, 5, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2637, '库存盘点单审批', 'erp:stock-check:update-status', 3, 6, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2638, '销售订单', '', 2, 1, 2617, 'order', 'fa:first-order', 'erp/sale/order/index', 'ErpSaleOrder', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-10 21:59:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2639, '销售订单查询', 'erp:sale-order:query', 3, 1, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2640, '销售订单创建', 'erp:sale-order:create', 3, 2, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2641, '销售订单更新', 'erp:sale-order:update', 3, 3, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2642, '销售订单删除', 'erp:sale-order:delete', 3, 4, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2643, '销售订单导出', 'erp:sale-order:export', 3, 5, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2644, '销售订单审批', 'erp:sale-order:update-status', 3, 6, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2645, '财务管理', '', 1, 50, 2563, 'finance', 'ep:money', '', '', 0, '1', '1', '1', '1', '2024-02-10 08:05:58', '1', '2024-02-10 08:06:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2646, '结算账户', '', 2, 10, 2645, 'account', 'fa:universal-access', 'erp/finance/account/index', 'ErpAccount', 0, '1', '1', '1', '', '2024-02-10 00:15:07', '1', '2024-02-14 08:24:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2647, '结算账户查询', 'erp:account:query', 3, 1, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2648, '结算账户创建', 'erp:account:create', 3, 2, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2649, '结算账户更新', 'erp:account:update', 3, 3, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2650, '结算账户删除', 'erp:account:delete', 3, 4, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2651, '结算账户导出', 'erp:account:export', 3, 5, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2652, '销售出库', '', 2, 2, 2617, 'out', 'ep:sold-out', 'erp/sale/out/index', 'ErpSaleOut', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-10 22:02:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2653, '销售出库查询', 'erp:sale-out:query', 3, 1, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2654, '销售出库创建', 'erp:sale-out:create', 3, 2, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2655, '销售出库更新', 'erp:sale-out:update', 3, 3, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2656, '销售出库删除', 'erp:sale-out:delete', 3, 4, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2657, '销售出库导出', 'erp:sale-out:export', 3, 5, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2658, '销售出库审批', 'erp:sale-out:update-status', 3, 6, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2659, '销售退货', '', 2, 3, 2617, 'return', 'fa-solid:bone', 'erp/sale/return/index', 'ErpSaleReturn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 06:12:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2660, '销售退货查询', 'erp:sale-return:query', 3, 1, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2661, '销售退货创建', 'erp:sale-return:create', 3, 2, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2662, '销售退货更新', 'erp:sale-return:update', 3, 3, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2663, '销售退货删除', 'erp:sale-return:delete', 3, 4, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2664, '销售退货导出', 'erp:sale-return:export', 3, 5, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2665, '销售退货审批', 'erp:sale-return:update-status', 3, 6, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2666, '采购订单', '', 2, 1, 2602, 'order', 'fa-solid:border-all', 'erp/purchase/order/index', 'ErpPurchaseOrder', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 08:51:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2667, '采购订单查询', 'erp:purchase-order:query', 3, 1, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2668, '采购订单创建', 'erp:purchase-order:create', 3, 2, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2669, '采购订单更新', 'erp:purchase-order:update', 3, 3, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2670, '采购订单删除', 'erp:purchase-order:delete', 3, 4, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2671, '采购订单导出', 'erp:purchase-order:export', 3, 5, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2672, '采购订单审批', 'erp:purchase-order:update-status', 3, 6, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2673, '采购入库', '', 2, 2, 2602, 'in', 'fa-solid:gopuram', 'erp/purchase/in/index', 'ErpPurchaseIn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 11:19:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2674, '采购入库查询', 'erp:purchase-in:query', 3, 1, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2675, '采购入库创建', 'erp:purchase-in:create', 3, 2, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2676, '采购入库更新', 'erp:purchase-in:update', 3, 3, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2677, '采购入库删除', 'erp:purchase-in:delete', 3, 4, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2678, '采购入库导出', 'erp:purchase-in:export', 3, 5, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2679, '采购入库审批', 'erp:purchase-in:update-status', 3, 6, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2680, '采购退货', '', 2, 3, 2602, 'return', 'ep:minus', 'erp/purchase/return/index', 'ErpPurchaseReturn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 20:51:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2681, '采购退货查询', 'erp:purchase-return:query', 3, 1, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2682, '采购退货创建', 'erp:purchase-return:create', 3, 2, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2683, '采购退货更新', 'erp:purchase-return:update', 3, 3, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2684, '采购退货删除', 'erp:purchase-return:delete', 3, 4, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2685, '采购退货导出', 'erp:purchase-return:export', 3, 5, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2686, '采购退货审批', 'erp:purchase-return:update-status', 3, 6, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2687, '付款单', '', 2, 1, 2645, 'payment', 'ep:caret-right', 'erp/finance/payment/index', 'ErpFinancePayment', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-14 08:24:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2688, '付款单查询', 'erp:finance-payment:query', 3, 1, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2689, '付款单创建', 'erp:finance-payment:create', 3, 2, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2690, '付款单更新', 'erp:finance-payment:update', 3, 3, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2691, '付款单删除', 'erp:finance-payment:delete', 3, 4, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2692, '付款单导出', 'erp:finance-payment:export', 3, 5, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2693, '付款单审批', 'erp:finance-payment:update-status', 3, 6, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2694, '收款单', '', 2, 2, 2645, 'receipt', 'ep:expand', 'erp/finance/receipt/index', 'ErpFinanceReceipt', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-15 19:35:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2695, '收款单查询', 'erp:finance-receipt:query', 3, 1, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2696, '收款单创建', 'erp:finance-receipt:create', 3, 2, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2697, '收款单更新', 'erp:finance-receipt:update', 3, 3, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2698, '收款单删除', 'erp:finance-receipt:delete', 3, 4, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2699, '收款单导出', 'erp:finance-receipt:export', 3, 5, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2700, '收款单审批', 'erp:finance-receipt:update-status', 3, 6, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2701, '待办事项', '', 2, 0, 2397, 'backlog', 'fa-solid:tasks', 'crm/backlog/index', 'CrmBacklog', 0, '1', '1', '1', '1', '2024-02-17 17:17:11', '1', '2024-02-17 17:17:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2702, 'ERP 首页', 'erp:statistics:query', 2, 0, 2563, 'home', 'ep:home-filled', 'erp/home/index.vue', 'ErpHome', 0, '1', '1', '1', '1', '2024-02-18 16:49:40', '1', '2024-02-26 21:12:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2703, '商机状态配置', '', 2, 4, 2524, 'business-status', 'fa-solid:charging-station', 'crm/business/status/index', 'CrmBusinessStatus', 0, '1', '1', '1', '1', '2024-02-21 20:15:17', '1', '2024-02-21 20:15:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2704, '商机状态查询', 'crm:business-status:query', 3, 1, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:35:36', '1', '2024-02-21 20:36:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2705, '商机状态创建', 'crm:business-status:create', 3, 2, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:35:57', '1', '2024-02-21 20:35:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2706, '商机状态更新', 'crm:business-status:update', 3, 3, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:36:21', '1', '2024-02-21 20:36:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2707, '商机状态删除', 'crm:business-status:delete', 3, 4, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:36:36', '1', '2024-02-21 20:36:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2708, '合同配置', '', 2, 5, 2524, 'contract-config', 'ep:connection', 'crm/contract/config/index', 'CrmContractConfig', 0, '1', '1', '1', '1', '2024-02-24 16:44:40', '1', '2024-02-24 16:44:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2709, '客户公海配置查询', 'crm:customer-pool-config:query', 3, 2, 2516, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:45:19', '1', '2024-02-24 16:45:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2710, '合同配置更新', 'crm:contract-config:update', 3, 1, 2708, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:45:56', '1', '2024-02-24 16:45:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2711, '合同配置查询', 'crm:contract-config:query', 3, 2, 2708, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:46:16', '1', '2024-02-24 16:46:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2712, '客户分析', 'crm:statistics-customer:query', 2, 0, 2560, 'customer', 'ep:avatar', 'crm/statistics/customer/index.vue', 'CrmStatisticsCustomer', 0, '1', '1', '1', '1', '2024-03-09 16:43:56', '1', '2024-05-04 20:38:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2713, '抄送我的', 'bpm:process-instance-cc:query', 2, 30, 1200, 'copy', 'ep:copy-document', 'bpm/task/copy/index', 'BpmProcessInstanceCopy', 0, '1', '1', '1', '1', '2024-03-17 21:50:23', '1', '2024-04-24 19:55:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2714, '流程分类', '', 2, 3, 1186, 'category', 'fa:object-ungroup', 'bpm/category/index', 'BpmCategory', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-21 23:51:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2715, '分类查询', 'bpm:category:query', 3, 1, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2716, '分类创建', 'bpm:category:create', 3, 2, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2717, '分类更新', 'bpm:category:update', 3, 3, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2718, '分类删除', 'bpm:category:delete', 3, 4, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2720, '发起流程', '', 2, 0, 1200, 'create', 'fa-solid:grin-stars', 'bpm/processInstance/create/index', 'BpmProcessInstanceCreate', 0, '1', '0', '1', '1', '2024-03-19 19:46:05', '1', '2024-03-23 19:03:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2721, '流程实例', '', 2, 10, 1186, 'process-instance/manager', 'fa:square', 'bpm/processInstance/manager/index', 'BpmProcessInstanceManager', 0, '1', '1', '1', '1', '2024-03-21 23:57:30', '1', '2024-03-21 23:57:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2722, '流程实例的查询（管理员）', 'bpm:process-instance:manager-query', 3, 1, 2721, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:18:27', '1', '2024-03-22 08:19:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2723, '流程实例的取消（管理员）', 'bpm:process-instance:cancel-by-admin', 3, 2, 2721, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:19:25', '1', '2024-03-22 08:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2724, '流程任务', '', 2, 11, 1186, 'process-tasnk', 'ep:collection-tag', 'bpm/task/manager/index', 'BpmManagerTask', 0, '1', '1', '1', '1', '2024-03-22 08:43:22', '1', '2024-03-22 08:43:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2725, '流程任务的查询（管理员）', 'bpm:task:mananger-query', 3, 1, 2724, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:43:49', '1', '2024-03-22 08:43:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2726, '流程监听器', '', 2, 5, 1186, 'process-listener', 'fa:assistive-listening-systems', 'bpm/processListener/index', 'BpmProcessListener', 0, '1', '1', '1', '', '2024-03-09 16:05:34', '1', '2024-03-23 13:13:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2727, '流程监听器查询', 'bpm:process-listener:query', 3, 1, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2728, '流程监听器创建', 'bpm:process-listener:create', 3, 2, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2729, '流程监听器更新', 'bpm:process-listener:update', 3, 3, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2730, '流程监听器删除', 'bpm:process-listener:delete', 3, 4, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2731, '流程表达式', '', 2, 6, 1186, 'process-expression', 'fa:wpexplorer', 'bpm/processExpression/index', 'BpmProcessExpression', 0, '1', '1', '1', '', '2024-03-09 22:35:08', '1', '2024-03-23 19:43:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2732, '流程表达式查询', 'bpm:process-expression:query', 3, 1, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2733, '流程表达式创建', 'bpm:process-expression:create', 3, 2, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2734, '流程表达式更新', 'bpm:process-expression:update', 3, 3, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2735, '流程表达式删除', 'bpm:process-expression:delete', 3, 4, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2736, '员工业绩', 'crm:statistics-performance:query', 2, 3, 2560, 'performance', 'ep:dish-dot', 'crm/statistics/performance/index', 'CrmStatisticsPerformance', 0, '1', '1', '1', '1', '2024-04-05 13:49:20', '1', '2024-04-24 19:42:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2737, '客户画像', 'crm:statistics-portrait:query', 2, 4, 2560, 'portrait', 'ep:picture', 'crm/statistics/portrait/index', 'CrmStatisticsPortrait', 0, '1', '1', '1', '1', '2024-04-05 13:57:40', '1', '2024-04-24 19:42:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2738, '销售漏斗', 'crm:statistics-funnel:query', 2, 5, 2560, 'funnel', 'ep:grape', 'crm/statistics/funnel/index', 'CrmStatisticsFunnel', 0, '1', '1', '1', '1', '2024-04-13 10:53:26', '1', '2024-04-24 19:39:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2739, '消息中心', '', 1, 7, 1, 'messages', 'ep:chat-dot-round', '', '', 0, '1', '1', '1', '1', '2024-04-22 23:54:30', '1', '2024-04-23 09:36:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2740, '监控中心', '', 1, 10, 2, 'monitors', 'ep:monitor', '', '', 0, '1', '1', '1', '1', '2024-04-23 00:04:44', '1', '2024-04-23 00:04:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2741, '领取公海客户', 'crm:customer:receive', 3, 1, 2546, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:47:45', '1', '2024-04-24 19:47:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2742, '分配公海客户', 'crm:customer:distribute', 3, 2, 2546, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:48:05', '1', '2024-04-24 19:48:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2743, '商品统计查询', 'statistics:product:query', 3, 1, 2545, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:50:05', '1', '2024-04-24 19:50:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2744, '商品统计导出', 'statistics:product:export', 3, 2, 2545, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:50:26', '1', '2024-04-24 19:50:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2745, '支付渠道查询', 'pay:channel:query', 3, 10, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:01', '1', '2024-04-24 19:53:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2746, '支付渠道创建', 'pay:channel:create', 3, 11, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:18', '1', '2024-04-24 19:53:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2747, '支付渠道更新', 'pay:channel:update', 3, 12, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:32', '1', '2024-04-24 19:53:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2748, '支付渠道删除', 'pay:channel:delete', 3, 13, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:54:34', '1', '2024-04-24 19:54:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2749, '商品收藏查询', 'product:favorite:query', 3, 10, 2014, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:55:47', '1', '2024-04-24 19:55:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2750, '商品浏览查询', 'product:browse-history:query', 3, 20, 2014, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:57:43', '1', '2024-04-24 19:57:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2751, '售后同意', 'trade:after-sale:agree', 3, 2, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:58:40', '1', '2024-04-24 19:58:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2752, '售后不同意', 'trade:after-sale:disagree', 3, 3, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:59:03', '1', '2024-04-24 19:59:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2753, '售后确认退货', 'trade:after-sale:receive', 3, 4, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:00:07', '1', '2024-04-24 20:00:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2754, '售后确认退款', 'trade:after-sale:refund', 3, 5, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:00:24', '1', '2024-04-24 20:00:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2755, '删除项目', 'report:go-view-project:delete', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:01:37', '1', '2024-04-24 20:01:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2756, '会员等级记录查询', 'member:level-record:query', 3, 10, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:32', '1', '2024-04-24 20:02:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2757, '会员经验记录查询', 'member:experience-record:query', 3, 11, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:51', '1', '2024-04-24 20:02:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2758, 'AI 大模型', '', 1, 400, 0, '/ai', 'tabler:ai', '', '', 0, '1', '1', '1', '1', '2024-05-07 15:07:56', '1', '2025-04-19 18:57:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2759, 'AI 对话', '', 2, 1, 2758, 'chat', 'ep:message', 'ai/chat/index/index.vue', 'AiChat', 0, '1', '1', '1', '1', '2024-05-07 15:09:14', '1', '2024-07-07 17:15:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2760, '控制台', '', 1, 100, 2758, 'console', 'ep:setting', '', '', 0, '1', '1', '1', '1', '2024-05-09 22:39:09', '1', '2024-05-24 23:34:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2761, 'API 密钥', '', 2, 0, 2760, 'api-key', 'ep:key', 'ai/model/apiKey/index.vue', 'AiApiKey', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-10 22:44:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2762, 'API 密钥查询', 'ai:api-key:query', 3, 1, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2767, '模型配置', '', 2, 0, 2760, 'model', 'fa-solid:abacus', 'ai/model/model/index.vue', 'AiModel', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:57:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2768, '聊天模型查询', 'ai:model:query', 3, 1, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:19:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2769, '聊天模型创建', 'ai:model:create', 3, 2, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2770, '聊天模型更新', 'ai:model:update', 3, 3, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2771, '聊天模型删除', 'ai:model:delete', 3, 4, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, '1', '1', '1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2776, '聊天角色更新', 'ai:chat-role:update', 3, 3, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2777, '聊天角色删除', 'ai:chat-role:delete', 3, 4, 2773, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-13 21:43:38', '1', '2024-05-13 21:43:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2778, '聊天管理', '', 2, 10, 2760, 'chat-conversation', 'ep:chat-square', 'ai/chat/manager/index.vue', 'AiChatManager', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-06-26 21:36:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2779, '会话查询', 'ai:chat-conversation:query', 3, 1, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2780, '会话删除', 'ai:chat-conversation:delete', 3, 2, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2781, '消息查询', 'ai:chat-message:query', 3, 11, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:38:56', '1', '2024-05-25 08:38:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2782, '消息删除', 'ai:chat-message:delete', 3, 12, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:39:10', '1', '2024-05-25 08:39:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2783, 'AI 绘画', '', 2, 2, 2758, 'image', 'ep:picture-rounded', 'ai/image/index/index.vue', 'AiImage', 0, '1', '1', '1', '1', '2024-05-26 11:45:17', '1', '2024-07-07 17:18:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2784, '绘画管理', '', 2, 11, 2760, 'image', 'fa:file-image-o', 'ai/image/manager/index.vue', 'AiImageManager', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 21:37:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2785, '绘画查询', 'ai:image:query', 3, 1, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:21:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2786, '绘画删除', 'ai:image:delete', 3, 4, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:22:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2787, '绘图更新', 'ai:image:update', 3, 2, 2784, '', '', '', '', 0, '1', '1', '1', '1', '2024-06-26 22:47:56', '1', '2024-08-31 09:21:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2788, '音乐管理', '', 2, 12, 2760, 'music', 'fa:music', 'ai/music/manager/index.vue', 'AiMusicManager', 0, '1', '1', '1', '', '2024-06-27 15:03:33', '1', '2024-06-27 23:04:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2789, '音乐查询', 'ai:music:query', 3, 1, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2790, '音乐更新', 'ai:music:update', 3, 3, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2791, '音乐删除', 'ai:music:delete', 3, 4, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2792, 'AI 写作', '', 2, 3, 2758, 'write', 'fa-solid:book-reader', 'ai/write/index/index.vue', 'AiWrite', 0, '1', '1', '1', '1', '2024-07-08 09:26:44', '1', '2024-07-16 13:03:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2793, '写作管理', '', 2, 13, 2760, 'write', 'fa:bookmark-o', 'ai/write/manager/index.vue', 'AiWriteManager', 0, '1', '1', '1', '', '2024-07-10 13:24:34', '1', '2024-07-10 21:31:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2794, 'AI 写作查询', 'ai:write:query', 3, 1, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, '1', '1', '1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, '1', '1', '1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2798, 'AI 思维导图', '', 2, 6, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, '1', '1', '1', '1', '2024-07-29 21:31:59', '1', '2025-03-02 18:57:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, '1', '1', '1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2802, '会话查询', 'promotion:kefu-conversation:query', 3, 1, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:17:52', '1', '2024-08-31 09:18:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2803, '会话更新', 'promotion:kefu-conversation:update', 3, 2, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:15', '1', '2024-08-31 09:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2804, '消息查询', 'promotion:kefu-message:query', 3, 10, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:42', '1', '2024-08-31 09:18:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2805, '会话删除', 'promotion:kefu-conversation:delete', 3, 3, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:19:51', '1', '2024-08-31 09:20:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2806, '消息发送', 'promotion:kefu-message:send', 3, 12, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:06', '1', '2024-08-31 09:20:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2807, '消息更新', 'promotion:kefu-message:update', 3, 11, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:22', '1', '2024-08-31 09:20:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2808, '积分商城', '', 2, 5, 2030, 'point-activity', 'ep:bowl', 'mall/promotion/point/activity/index', 'PointActivity', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-23 09:14:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2809, '积分商城活动查询', 'promotion:point-activity:query', 3, 1, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2810, '积分商城活动创建', 'promotion:point-activity:create', 3, 2, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2811, '积分商城活动更新', 'promotion:point-activity:update', 3, 3, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2812, '积分商城活动删除', 'promotion:point-activity:delete', 3, 4, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2813, '积分商城活动导出', 'promotion:point-activity:export', 3, 5, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, '1', '1', '1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-17 19:32:06', '1', '2025-01-17 19:32:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-23 20:23:34', '1', '2025-01-23 20:23:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2915, 'AI 知识库', '', 2, 5, 2758, 'knowledge', 'ep:notebook', 'ai/knowledge/knowledge/index', 'AiKnowledge', 0, '1', '1', '1', '', '2025-02-28 07:04:21', '1', '2025-03-02 18:58:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2916, 'AI 知识库查询', 'ai:knowledge:query', 3, 1, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2917, 'AI 知识库创建', 'ai:knowledge:create', 3, 2, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2918, 'AI 知识库更新', 'ai:knowledge:update', 3, 3, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2919, 'AI 知识库删除', 'ai:knowledge:delete', 3, 4, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2920, '工具管理', '', 2, 0, 2760, 'tool', 'fa-solid:tools', 'ai/model/tool/index.vue', 'AiTool', 0, '1', '1', '1', '', '2025-03-14 11:19:29', '1', '2025-03-14 19:20:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2921, '工具查询', 'ai:tool:query', 3, 1, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4000, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, '1', '1', '1', '1', '2024-08-10 09:55:28', '1', '2024-12-07 15:58:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4001, '设备接入', '', 1, 2, 4000, 'device', 'ep:platform', '', '', 0, '1', '1', '1', '1', '2024-08-10 09:57:56', '1', '2025-02-27 08:39:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4002, '产品管理', '', 2, 2, 4001, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, '1', '1', '1', '', '2024-08-10 02:38:02', '1', '2024-12-07 18:47:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4003, '产品查询', 'iot:product:query', 3, 1, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4004, '产品创建', 'iot:product:create', 3, 2, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4005, '产品更新', 'iot:product:update', 3, 3, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4006, '产品删除', 'iot:product:delete', 3, 4, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4007, '产品导出', 'iot:product:export', 3, 5, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4008, '设备管理', '', 2, 4, 4001, 'device', 'fa:mobile', 'iot/device/device/index', 'IoTDevice', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-14 11:39:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4009, '设备查询', 'iot:device:query', 3, 1, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4010, '设备创建', 'iot:device:create', 3, 2, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4011, '设备更新', 'iot:device:update', 3, 3, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4012, '设备删除', 'iot:device:delete', 3, 4, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4013, '设备导出', 'iot:device:export', 3, 5, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4014, '产品分类', '', 2, 1, 4001, 'product-category', 'ep:notebook', 'iot/product/category/index', 'IotProductCategory', 0, '1', '1', '1', '', '2024-12-07 16:01:35', '1', '2024-12-07 16:31:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4015, '产品分类查询', 'iot:product-category:query', 3, 1, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4016, '产品分类创建', 'iot:product-category:create', 3, 2, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4017, '产品分类更新', 'iot:product-category:update', 3, 3, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4018, '产品分类删除', 'iot:product-category:delete', 3, 4, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4025, '插件管理', '', 2, 5, 4047, 'plugin-config', 'ep:folder-opened', 'iot/plugin/index', 'IoTPlugin', 0, '1', '1', '1', '', '2024-12-09 21:25:06', '1', '2025-02-05 22:23:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4026, '插件查询', 'iot:plugin-config:query', 3, 1, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4027, '插件创建', 'iot:plugin-config:create', 3, 2, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4028, '插件更新', 'iot:plugin-config:update', 3, 3, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4029, '插件删除', 'iot:plugin-config:delete', 3, 4, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4030, '插件导出', 'iot:plugin-config:export', 3, 5, 4025, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4031, '设备分组', '', 2, 3, 4001, 'device-group', 'fa-solid:layer-group', 'iot/device/group/index', 'IotDeviceGroup', 0, '1', '1', '1', '', '2024-12-14 17:08:29', '1', '2024-12-14 17:09:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4032, '设备分组查询', 'iot:device-group:query', 3, 1, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4033, '设备分组创建', 'iot:device-group:create', 3, 2, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4034, '设备分组更新', 'iot:device-group:update', 3, 3, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4035, '设备分组删除', 'iot:device-group:delete', 3, 4, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4036, '设备导入', 'iot:device:import', 3, 6, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2024-12-15 10:35:47', '1', '2024-12-15 10:35:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4037, '产品物模型', '', 2, 2, 4001, 'thing-model', 'ep:mostly-cloudy', 'iot/thingmodel/index', 'IoTThingModel', 0, '0', '0', '0', '', '2024-12-16 17:17:50', '1', '2024-12-27 11:03:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4038, '产品物模型功能查询', 'iot:thing-model:query', 3, 1, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:51', '', '2025-03-17 09:14:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4039, '产品物模型功能创建', 'iot:thing-model:create', 3, 2, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:14:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4040, '产品物模型功能更新', 'iot:thing-model:update', 3, 3, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4041, '产品物模型功能删除', 'iot:thing-model:delete', 3, 4, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4042, '产品物模型功能导出', 'iot:thing-model:export', 3, 5, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:53', '', '2025-03-17 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4043, '设备上行', 'iot:device:upstream', 3, 7, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 04:40:16', '1', '2025-01-31 22:45:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4044, '设备属性查询', 'iot:device:property-query', 3, 10, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 11:52:54', '1', '2025-01-28 11:52:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4045, '设备日志查询', 'iot:device:log-query', 3, 11, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 11:53:22', '1', '2025-01-28 11:53:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4046, '设备下行', 'iot:device:downstream', 3, 8, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-31 22:46:11', '1', '2025-01-31 22:46:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4047, '运维管理', '', 1, 2, 4000, 'operations', 'fa:cog', '', '', 0, '1', '1', '1', '1', '2025-02-05 22:21:37', '1', '2025-02-05 22:22:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4048, '规则引擎', '', 1, 3, 4000, 'rule', 'fa-solid:cogs', '', '', 0, '1', '1', '1', '1', '2025-02-11 14:10:54', '1', '2025-02-11 14:10:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4049, '场景联动', '', 2, 1, 4048, 'scene', 'ep:link', 'iot/rule/scene/index', 'Scene', 0, '1', '1', '1', '1', '2025-02-11 14:12:44', '1', '2025-02-12 10:15:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4050, 'IoT首页', '', 2, 1, 4000, 'home', 'ep:home-filled', 'iot/home/index', 'IotHome', 0, '1', '1', '1', '1', '2025-02-27 08:39:35', '1', '2025-02-27 08:40:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4051, '数据桥梁', '', 2, 0, 4048, 'data-bridge', 'ep:guide', 'iot/rule/databridge/index', 'IotDataBridge', 0, '1', '1', '1', '', '2025-03-09 13:47:11', '1', '2025-03-09 13:47:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4052, 'IoT 数据桥梁查询', 'iot:data-bridge:query', 3, 1, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4053, 'IoT 数据桥梁创建', 'iot:data-bridge:create', 3, 2, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4054, 'IoT 数据桥梁更新', 'iot:data-bridge:update', 3, 3, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4055, 'IoT 数据桥梁删除', 'iot:data-bridge:delete', 3, 4, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4056, 'IoT 数据桥梁导出', 'iot:data-bridge:export', 3, 5, 4051, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5000, 'AI 工作流', '', 2, 5, 2758, 'workflow', 'fa:hand-grab-o', 'ai/workflow/index.vue', 'AiWorkflow', 0, '1', '1', '1', '1', '2025-03-25 09:50:27', '1', '2025-05-03 18:55:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5001, 'AI 工作流查询', 'ai:workflow:query', 3, 1, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:11', '1', '2025-03-25 09:51:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5002, 'AI 工作流创建', 'ai:workflow:create', 3, 2, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:28', '1', '2025-03-25 09:51:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5003, 'AI 工作流更新', 'ai:workflow:update', 3, 3, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:42', '1', '2025-03-25 09:51:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5004, 'AI 工作流删除', 'ai:workflow:delete', 3, 4, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:55', '1', '2025-03-25 09:52:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5005, 'AI 工作流测试', 'ai:workflow:test', 3, 5, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-30 10:29:41', '1', '2025-03-30 10:29:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5009, '仪表盘设计器', '', 2, 1, 1281, 'jimu-bi', 'fa:y-combinator', 'report/jmreport/bi', 'JimuBI', 0, '1', '1', '1', '1', '2025-05-03 09:57:15', '1', '2025-05-03 10:02:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5010, '租户切换', 'system:tenant:visit', 3, 999, 1138, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-05 15:25:32', '1', '2025-05-05 15:25:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5011, '转账订单查询', 'pay:transfer:query', 3, 1, 2559, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-08 12:46:53', '1', '2025-05-08 12:46:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5012, '转账订单导出', 'pay:transfer:export', 3, 2, 2559, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-10 17:00:28', '1', '2025-05-10 17:00:28', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_menu_seq;\nCREATE SEQUENCE system_menu_seq\n    START 5013;\n\n-- ----------------------------\n-- Table structure for system_notice\n-- ----------------------------\nDROP TABLE IF EXISTS system_notice;\nCREATE TABLE system_notice\n(\n    id          int8        NOT NULL,\n    title       varchar(50) NOT NULL,\n    content     text        NULL,\n    type        int2        NOT NULL,\n    status      int2        NOT NULL DEFAULT 0,\n    creator     varchar(64) NULL     DEFAULT '',\n    create_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64) NULL     DEFAULT '',\n    update_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2        NOT NULL DEFAULT 0,\n    tenant_id   int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_notice\n    ADD CONSTRAINT pk_system_notice PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notice.id IS '公告ID';\nCOMMENT ON COLUMN system_notice.title IS '公告标题';\nCOMMENT ON COLUMN system_notice.content IS '公告内容';\nCOMMENT ON COLUMN system_notice.type IS '公告类型（1通知 2公告）';\nCOMMENT ON COLUMN system_notice.status IS '公告状态（0正常 1关闭）';\nCOMMENT ON COLUMN system_notice.creator IS '创建者';\nCOMMENT ON COLUMN system_notice.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notice.updater IS '更新者';\nCOMMENT ON COLUMN system_notice.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notice.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notice.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notice IS '通知公告表';\n\n-- ----------------------------\n-- Records of system_notice\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '芋道的公众', '<p>新版本内容133</p>', 1, 0, 'admin', '2021-01-05 17:03:48', '1', '2022-05-04 21:00:20', '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '维护通知：2018-07-01 系统凌晨维护', '<p><img src=\"http://test.yudao.iocoder.cn/b7cb3cf49b4b3258bf7309a09dd2f4e5.jpg\" alt=\"\" data-href=\"\">11112222<img src=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\" alt=\"image\" data-href=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\">3333</p>', 2, 1, 'admin', '2021-01-05 17:03:48', '1', '2025-04-18 23:56:40', '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '我是测试标题', '<p>哈哈哈哈123</p>', 1, 0, '110', '2022-02-22 01:01:25', '110', '2022-02-22 01:01:46', '0', 121);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_notice_seq;\nCREATE SEQUENCE system_notice_seq\n    START 5;\n\n-- ----------------------------\n-- Table structure for system_notify_message\n-- ----------------------------\nDROP TABLE IF EXISTS system_notify_message;\nCREATE TABLE system_notify_message\n(\n    id                int8          NOT NULL,\n    user_id           int8          NOT NULL,\n    user_type         int2          NOT NULL,\n    template_id       int8          NOT NULL,\n    template_code     varchar(64)   NOT NULL,\n    template_nickname varchar(63)   NOT NULL,\n    template_content  varchar(1024) NOT NULL,\n    template_type     int4          NOT NULL,\n    template_params   varchar(255)  NOT NULL,\n    read_status       bool          NOT NULL,\n    read_time         timestamp     NULL     DEFAULT NULL,\n    creator           varchar(64)   NULL     DEFAULT '',\n    create_time       timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater           varchar(64)   NULL     DEFAULT '',\n    update_time       timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted           int2          NOT NULL DEFAULT 0,\n    tenant_id         int8          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_notify_message\n    ADD CONSTRAINT pk_system_notify_message PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notify_message.id IS '用户ID';\nCOMMENT ON COLUMN system_notify_message.user_id IS '用户id';\nCOMMENT ON COLUMN system_notify_message.user_type IS '用户类型';\nCOMMENT ON COLUMN system_notify_message.template_id IS '模版编号';\nCOMMENT ON COLUMN system_notify_message.template_code IS '模板编码';\nCOMMENT ON COLUMN system_notify_message.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_notify_message.template_content IS '模版内容';\nCOMMENT ON COLUMN system_notify_message.template_type IS '模版类型';\nCOMMENT ON COLUMN system_notify_message.template_params IS '模版参数';\nCOMMENT ON COLUMN system_notify_message.read_status IS '是否已读';\nCOMMENT ON COLUMN system_notify_message.read_time IS '阅读时间';\nCOMMENT ON COLUMN system_notify_message.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_message.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_message.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_message.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_message.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notify_message.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notify_message IS '站内信消息表';\n\n-- ----------------------------\n-- Records of system_notify_message\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 11:44:08', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 11:45:04', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 103, 2, 2, 'register', '系统消息', '你好，欢迎 哈哈 加入大家庭！', 2, '{\"name\":\"哈哈\"}', '0', NULL, '1', '2023-01-28 21:02:20', '1', '2023-01-28 21:02:20', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 22:21:42', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', '2025-04-21 14:59:36', '1', '2023-01-28 22:22:07', '1', '2025-04-21 14:59:36', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 1, 2, 1, 'test', '123', '我是 2，我开始 3 了', 1, '{\"name\":\"2\",\"what\":\"3\"}', '1', '2025-04-21 14:59:35', '1', '2023-01-28 23:45:21', '1', '2025-04-21 14:59:35', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 1, 2, 2, 'register', '系统消息', '你好，欢迎 123 加入大家庭！', 2, '{\"name\":\"123\"}', '1', '2025-04-21 14:59:35', '1', '2023-01-28 23:50:21', '1', '2025-04-21 14:59:35', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-28 08:35:46提现￥0.09元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-28 08:35:46\",\"price\":\"0.09\"}', '0', NULL, '1', '2023-09-28 16:36:22', '1', '2023-09-28 16:36:22', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-30 20:59:40提现￥1.00元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-30 20:59:40\",\"price\":\"1.00\"}', '0', NULL, '1', '2023-10-03 12:11:34', '1', '2023-10-03 12:11:34', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_notify_message_seq;\nCREATE SEQUENCE system_notify_message_seq\n    START 11;\n\n-- ----------------------------\n-- Table structure for system_notify_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_notify_template;\nCREATE TABLE system_notify_template\n(\n    id          int8          NOT NULL,\n    name        varchar(63)   NOT NULL,\n    code        varchar(64)   NOT NULL,\n    nickname    varchar(255)  NOT NULL,\n    content     varchar(1024) NOT NULL,\n    type        int2          NOT NULL,\n    params      varchar(255)  NULL     DEFAULT NULL,\n    status      int2          NOT NULL,\n    remark      varchar(255)  NULL     DEFAULT NULL,\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_notify_template\n    ADD CONSTRAINT pk_system_notify_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notify_template.id IS '主键';\nCOMMENT ON COLUMN system_notify_template.name IS '模板名称';\nCOMMENT ON COLUMN system_notify_template.code IS '模版编码';\nCOMMENT ON COLUMN system_notify_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_notify_template.content IS '模版内容';\nCOMMENT ON COLUMN system_notify_template.type IS '类型';\nCOMMENT ON COLUMN system_notify_template.params IS '参数数组';\nCOMMENT ON COLUMN system_notify_template.status IS '状态';\nCOMMENT ON COLUMN system_notify_template.remark IS '备注';\nCOMMENT ON COLUMN system_notify_template.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_template.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_notify_template IS '站内信模板表';\n\nDROP SEQUENCE IF EXISTS system_notify_template_seq;\nCREATE SEQUENCE system_notify_template_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_access_token\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_access_token;\nCREATE TABLE system_oauth2_access_token\n(\n    id            int8         NOT NULL,\n    user_id       int8         NOT NULL,\n    user_type     int2         NOT NULL,\n    user_info     varchar(512) NOT NULL,\n    access_token  varchar(255) NOT NULL,\n    refresh_token varchar(32)  NOT NULL,\n    client_id     varchar(255) NOT NULL,\n    scopes        varchar(255) NULL     DEFAULT NULL,\n    expires_time  timestamp    NOT NULL,\n    creator       varchar(64)  NULL     DEFAULT '',\n    create_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater       varchar(64)  NULL     DEFAULT '',\n    update_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted       int2         NOT NULL DEFAULT 0,\n    tenant_id     int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_access_token\n    ADD CONSTRAINT pk_system_oauth2_access_token PRIMARY KEY (id);\n\nCREATE INDEX idx_system_oauth2_access_token_01 ON system_oauth2_access_token (access_token);\nCREATE INDEX idx_system_oauth2_access_token_02 ON system_oauth2_access_token (refresh_token);\n\nCOMMENT ON COLUMN system_oauth2_access_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_access_token.user_info IS '用户信息';\nCOMMENT ON COLUMN system_oauth2_access_token.access_token IS '访问令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_access_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_access_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_access_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_access_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_access_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_access_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_access_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_access_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_access_token IS 'OAuth2 访问令牌';\n\nDROP SEQUENCE IF EXISTS system_oauth2_access_token_seq;\nCREATE SEQUENCE system_oauth2_access_token_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_approve\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_approve;\nCREATE TABLE system_oauth2_approve\n(\n    id           int8         NOT NULL,\n    user_id      int8         NOT NULL,\n    user_type    int2         NOT NULL,\n    client_id    varchar(255) NOT NULL,\n    scope        varchar(255) NULL     DEFAULT '',\n    approved     bool         NOT NULL DEFAULT '0',\n    expires_time timestamp    NOT NULL,\n    creator      varchar(64)  NULL     DEFAULT '',\n    create_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater      varchar(64)  NULL     DEFAULT '',\n    update_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted      int2         NOT NULL DEFAULT 0,\n    tenant_id    int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_approve\n    ADD CONSTRAINT pk_system_oauth2_approve PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_approve.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_approve.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_approve.scope IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_approve.approved IS '是否接受';\nCOMMENT ON COLUMN system_oauth2_approve.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_approve.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_approve.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_approve.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_approve.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_approve.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_approve.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_approve IS 'OAuth2 批准表';\n\nDROP SEQUENCE IF EXISTS system_oauth2_approve_seq;\nCREATE SEQUENCE system_oauth2_approve_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_client\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_client;\nCREATE TABLE system_oauth2_client\n(\n    id                             int8          NOT NULL,\n    client_id                      varchar(255)  NOT NULL,\n    secret                         varchar(255)  NOT NULL,\n    name                           varchar(255)  NOT NULL,\n    logo                           varchar(255)  NOT NULL,\n    description                    varchar(255)  NULL     DEFAULT NULL,\n    status                         int2          NOT NULL,\n    access_token_validity_seconds  int4          NOT NULL,\n    refresh_token_validity_seconds int4          NOT NULL,\n    redirect_uris                  varchar(255)  NOT NULL,\n    authorized_grant_types         varchar(255)  NOT NULL,\n    scopes                         varchar(255)  NULL     DEFAULT NULL,\n    auto_approve_scopes            varchar(255)  NULL     DEFAULT NULL,\n    authorities                    varchar(255)  NULL     DEFAULT NULL,\n    resource_ids                   varchar(255)  NULL     DEFAULT NULL,\n    additional_information         varchar(4096) NULL     DEFAULT NULL,\n    creator                        varchar(64)   NULL     DEFAULT '',\n    create_time                    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater                        varchar(64)   NULL     DEFAULT '',\n    update_time                    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted                        int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_client\n    ADD CONSTRAINT pk_system_oauth2_client PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_client.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_client.secret IS '客户端密钥';\nCOMMENT ON COLUMN system_oauth2_client.name IS '应用名';\nCOMMENT ON COLUMN system_oauth2_client.logo IS '应用图标';\nCOMMENT ON COLUMN system_oauth2_client.description IS '应用描述';\nCOMMENT ON COLUMN system_oauth2_client.status IS '状态';\nCOMMENT ON COLUMN system_oauth2_client.access_token_validity_seconds IS '访问令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.refresh_token_validity_seconds IS '刷新令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.redirect_uris IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_client.authorized_grant_types IS '授权类型';\nCOMMENT ON COLUMN system_oauth2_client.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_client.auto_approve_scopes IS '自动通过的授权范围';\nCOMMENT ON COLUMN system_oauth2_client.authorities IS '权限';\nCOMMENT ON COLUMN system_oauth2_client.resource_ids IS '资源';\nCOMMENT ON COLUMN system_oauth2_client.additional_information IS '附加信息';\nCOMMENT ON COLUMN system_oauth2_client.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_client.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_client.deleted IS '是否删除';\nCOMMENT ON TABLE system_oauth2_client IS 'OAuth2 客户端表';\n\n-- ----------------------------\n-- Records of system_oauth2_client\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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\"]', '[\"user.read\",\"user.write\"]', '[]', '[\"user.read\",\"user.write\"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-05-02 20:42:22', '0');\nINSERT 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/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.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-05-02 19:58:08', '0');\nINSERT 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', '0');\nINSERT 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/604bdc695e13b3b22745be704d1f2aa8ee05c5f26f9fead6d1ca49005afbc857.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-05-04 16:00:46', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_oauth2_client_seq;\nCREATE SEQUENCE system_oauth2_client_seq\n    START 43;\n\n-- ----------------------------\n-- Table structure for system_oauth2_code\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_code;\nCREATE TABLE system_oauth2_code\n(\n    id           int8         NOT NULL,\n    user_id      int8         NOT NULL,\n    user_type    int2         NOT NULL,\n    code         varchar(32)  NOT NULL,\n    client_id    varchar(255) NOT NULL,\n    scopes       varchar(255) NULL     DEFAULT '',\n    expires_time timestamp    NOT NULL,\n    redirect_uri varchar(255) NULL     DEFAULT NULL,\n    state        varchar(255) NULL     DEFAULT '',\n    creator      varchar(64)  NULL     DEFAULT '',\n    create_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater      varchar(64)  NULL     DEFAULT '',\n    update_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted      int2         NOT NULL DEFAULT 0,\n    tenant_id    int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_code\n    ADD CONSTRAINT pk_system_oauth2_code PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_code.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_code.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_code.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_code.code IS '授权码';\nCOMMENT ON COLUMN system_oauth2_code.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_code.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_code.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_code.redirect_uri IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_code.state IS '状态';\nCOMMENT ON COLUMN system_oauth2_code.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_code.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_code IS 'OAuth2 授权码表';\n\nDROP SEQUENCE IF EXISTS system_oauth2_code_seq;\nCREATE SEQUENCE system_oauth2_code_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_refresh_token\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_refresh_token;\nCREATE TABLE system_oauth2_refresh_token\n(\n    id            int8         NOT NULL,\n    user_id       int8         NOT NULL,\n    refresh_token varchar(32)  NOT NULL,\n    user_type     int2         NOT NULL,\n    client_id     varchar(255) NOT NULL,\n    scopes        varchar(255) NULL     DEFAULT NULL,\n    expires_time  timestamp    NOT NULL,\n    creator       varchar(64)  NULL     DEFAULT '',\n    create_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater       varchar(64)  NULL     DEFAULT '',\n    update_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted       int2         NOT NULL DEFAULT 0,\n    tenant_id     int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_refresh_token\n    ADD CONSTRAINT pk_system_oauth2_refresh_token PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_refresh_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_refresh_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_refresh_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_refresh_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_refresh_token IS 'OAuth2 刷新令牌';\n\nDROP SEQUENCE IF EXISTS system_oauth2_refresh_token_seq;\nCREATE SEQUENCE system_oauth2_refresh_token_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_operate_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_operate_log;\nCREATE TABLE system_operate_log\n(\n    id             int8          NOT NULL,\n    trace_id       varchar(64)   NULL     DEFAULT '',\n    user_id        int8          NOT NULL,\n    user_type      int2          NOT NULL DEFAULT 0,\n    type           varchar(50)   NOT NULL,\n    sub_type       varchar(50)   NOT NULL,\n    biz_id         int8          NOT NULL,\n    action         varchar(2000) NULL     DEFAULT '',\n    success        bool          NOT NULL DEFAULT '1',\n    extra          varchar(2000) NULL     DEFAULT '',\n    request_method varchar(16)   NULL     DEFAULT '',\n    request_url    varchar(255)  NULL     DEFAULT '',\n    user_ip        varchar(50)   NULL     DEFAULT NULL,\n    user_agent     varchar(512)  NULL     DEFAULT NULL,\n    creator        varchar(64)   NULL     DEFAULT '',\n    create_time    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64)   NULL     DEFAULT '',\n    update_time    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2          NOT NULL DEFAULT 0,\n    tenant_id      int8          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_operate_log\n    ADD CONSTRAINT pk_system_operate_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_operate_log.id IS '日志主键';\nCOMMENT ON COLUMN system_operate_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_operate_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_operate_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_operate_log.type IS '操作模块类型';\nCOMMENT ON COLUMN system_operate_log.sub_type IS '操作名';\nCOMMENT ON COLUMN system_operate_log.biz_id IS '操作数据模块编号';\nCOMMENT ON COLUMN system_operate_log.action IS '操作内容';\nCOMMENT ON COLUMN system_operate_log.success IS '操作结果';\nCOMMENT ON COLUMN system_operate_log.extra IS '拓展字段';\nCOMMENT ON COLUMN system_operate_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN system_operate_log.request_url IS '请求地址';\nCOMMENT ON COLUMN system_operate_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_operate_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_operate_log.creator IS '创建者';\nCOMMENT ON COLUMN system_operate_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_operate_log.updater IS '更新者';\nCOMMENT ON COLUMN system_operate_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_operate_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_operate_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_operate_log IS '操作日志记录 V2 版本';\n\nDROP SEQUENCE IF EXISTS system_operate_log_seq;\nCREATE SEQUENCE system_operate_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_post\n-- ----------------------------\nDROP TABLE IF EXISTS system_post;\nCREATE TABLE system_post\n(\n    id          int8         NOT NULL,\n    code        varchar(64)  NOT NULL,\n    name        varchar(50)  NOT NULL,\n    sort        int4         NOT NULL,\n    status      int2         NOT NULL,\n    remark      varchar(500) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_post\n    ADD CONSTRAINT pk_system_post PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_post.id IS '岗位ID';\nCOMMENT ON COLUMN system_post.code IS '岗位编码';\nCOMMENT ON COLUMN system_post.name IS '岗位名称';\nCOMMENT ON COLUMN system_post.sort IS '显示顺序';\nCOMMENT ON COLUMN system_post.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_post.remark IS '备注';\nCOMMENT ON COLUMN system_post.creator IS '创建者';\nCOMMENT ON COLUMN system_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_post.updater IS '更新者';\nCOMMENT ON COLUMN system_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_post IS '岗位信息表';\n\n-- ----------------------------\n-- Records of system_post\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 'ceo', '董事长', 1, 0, '', 'admin', '2021-01-06 17:03:48', '1', '2023-02-11 15:19:04', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 'se', '项目经理', 2, 0, '', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 09:18:20', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 'user', '普通员工', 4, 0, '111222', 'admin', '2021-01-05 17:03:48', '1', '2025-03-24 21:32:40', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 'HR', '人力资源', 5, 0, '`', '1', '2024-03-24 20:45:40', '1', '2025-03-29 19:08:10', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_post_seq;\nCREATE SEQUENCE system_post_seq\n    START 6;\n\n-- ----------------------------\n-- Table structure for system_role\n-- ----------------------------\nDROP TABLE IF EXISTS system_role;\nCREATE TABLE system_role\n(\n    id                  int8         NOT NULL,\n    name                varchar(30)  NOT NULL,\n    code                varchar(100) NOT NULL,\n    sort                int4         NOT NULL,\n    data_scope          int2         NOT NULL DEFAULT 1,\n    data_scope_dept_ids varchar(500) NULL     DEFAULT '',\n    status              int2         NOT NULL,\n    type                int2         NOT NULL,\n    remark              varchar(500) NULL     DEFAULT NULL,\n    creator             varchar(64)  NULL     DEFAULT '',\n    create_time         timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater             varchar(64)  NULL     DEFAULT '',\n    update_time         timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted             int2         NOT NULL DEFAULT 0,\n    tenant_id           int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_role\n    ADD CONSTRAINT pk_system_role PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_role.id IS '角色ID';\nCOMMENT ON COLUMN system_role.name IS '角色名称';\nCOMMENT ON COLUMN system_role.code IS '角色权限字符串';\nCOMMENT ON COLUMN system_role.sort IS '显示顺序';\nCOMMENT ON COLUMN system_role.data_scope IS '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）';\nCOMMENT ON COLUMN system_role.data_scope_dept_ids IS '数据范围 ( 指定部门数组)';\nCOMMENT ON COLUMN system_role.status IS '角色状态（0正常 1停用）';\nCOMMENT ON COLUMN system_role.type IS '角色类型';\nCOMMENT ON COLUMN system_role.remark IS '备注';\nCOMMENT ON COLUMN system_role.creator IS '创建者';\nCOMMENT ON COLUMN system_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role.updater IS '更新者';\nCOMMENT ON COLUMN system_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role IS '角色信息表';\n\n-- ----------------------------\n-- Records of system_role\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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 (1, '超级管理员', 'super_admin', 1, 1, '', 0, 1, '超级管理员', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:21', '0', 1);\nINSERT 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 (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:20', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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 (101, '测试账号', 'test', 0, 1, '[]', 0, 2, '123', '', '2021-01-06 13:49:35', '1', '2025-04-30 17:38:28', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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, '测试数据权限', 'test-dp', 3, 2, '[100,102,103,104,105,108]', 0, 2, '', '1', '2025-03-31 14:58:06', '1', '2025-04-17 23:07:44', '0', 1);\nINSERT 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 (158, '2', '3', 4, 1, '', 0, 2, NULL, '1', '2025-04-17 20:08:08', '1', '2025-04-17 23:05:31', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_role_seq;\nCREATE SEQUENCE system_role_seq\n    START 159;\n\n-- ----------------------------\n-- Table structure for system_role_menu\n-- ----------------------------\nDROP TABLE IF EXISTS system_role_menu;\nCREATE TABLE system_role_menu\n(\n    id          int8        NOT NULL,\n    role_id     int8        NOT NULL,\n    menu_id     int8        NOT NULL,\n    creator     varchar(64) NULL     DEFAULT '',\n    create_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64) NULL     DEFAULT '',\n    update_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2        NOT NULL DEFAULT 0,\n    tenant_id   int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_role_menu\n    ADD CONSTRAINT pk_system_role_menu PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_role_menu.id IS '自增编号';\nCOMMENT ON COLUMN system_role_menu.role_id IS '角色ID';\nCOMMENT ON COLUMN system_role_menu.menu_id IS '菜单ID';\nCOMMENT ON COLUMN system_role_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_role_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_role_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role_menu.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role_menu.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role_menu IS '角色和菜单关联表';\n\n-- ----------------------------\n-- Records of system_role_menu\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (263, 109, 1, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (434, 2, 1, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (454, 2, 1093, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (455, 2, 1094, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (460, 2, 1100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (467, 2, 1107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (476, 2, 1117, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (477, 2, 100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (478, 2, 101, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (479, 2, 102, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (480, 2, 1126, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (481, 2, 103, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (483, 2, 104, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (485, 2, 105, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (488, 2, 107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (490, 2, 108, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (492, 2, 109, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (498, 2, 1138, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (523, 2, 1224, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (524, 2, 1225, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (541, 2, 500, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (543, 2, 501, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (675, 2, 2, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (689, 2, 1077, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (690, 2, 1078, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (692, 2, 1083, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (693, 2, 1084, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (699, 2, 1090, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (703, 2, 106, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (704, 2, 110, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (705, 2, 111, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1604, 101, 1216, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1605, 101, 1217, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1606, 101, 1218, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1607, 101, 1219, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1608, 101, 1220, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1609, 101, 1221, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1610, 101, 5, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1611, 101, 1222, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1612, 101, 1118, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1613, 101, 1119, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1614, 101, 1120, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1615, 101, 1185, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1616, 101, 1186, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1617, 101, 1187, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1618, 101, 1188, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1619, 101, 1189, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1620, 101, 1190, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1621, 101, 1191, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1622, 101, 1192, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1623, 101, 1193, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1624, 101, 1194, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1625, 101, 1195, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1627, 101, 1197, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1628, 101, 1198, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1629, 101, 1199, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1630, 101, 1200, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1631, 101, 1201, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1632, 101, 1202, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1633, 101, 1207, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1634, 101, 1208, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1635, 101, 1209, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1636, 101, 1210, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1637, 101, 1211, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1638, 101, 1212, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1639, 101, 1213, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1640, 101, 1215, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1641, 101, 2, '1', '2022-04-01 22:21:24', '1', '2022-04-01 22:21:24', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1642, 101, 1031, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1643, 101, 1032, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1644, 101, 1033, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1645, 101, 1034, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1646, 101, 1035, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1647, 101, 1050, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1648, 101, 1051, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1649, 101, 1052, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1650, 101, 1053, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1651, 101, 1054, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1652, 101, 1056, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1653, 101, 1057, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1654, 101, 1058, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1655, 101, 1059, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1656, 101, 1060, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1657, 101, 1066, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1658, 101, 1067, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1659, 101, 1070, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1664, 101, 1075, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1666, 101, 1077, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1667, 101, 1078, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1668, 101, 1082, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1669, 101, 1083, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1670, 101, 1084, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1671, 101, 1085, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1672, 101, 1086, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1673, 101, 1087, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1674, 101, 1088, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1675, 101, 1089, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1679, 101, 1237, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1680, 101, 1238, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1681, 101, 1239, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1682, 101, 1240, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1683, 101, 1241, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1684, 101, 1242, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1685, 101, 1243, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1687, 101, 106, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1688, 101, 110, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1689, 101, 111, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1690, 101, 112, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1691, 101, 113, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1692, 101, 114, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1693, 101, 115, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1694, 101, 116, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1732, 109, 1064, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1733, 109, 1001, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1734, 109, 1065, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1735, 109, 1002, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1736, 109, 1003, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1737, 109, 1004, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1738, 109, 1005, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1739, 109, 1006, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1740, 109, 1007, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1741, 109, 1008, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1742, 109, 1009, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1766, 109, 1064, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1767, 109, 1001, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1768, 109, 1065, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1769, 109, 1002, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1770, 109, 1003, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1771, 109, 1004, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1772, 109, 1005, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1773, 109, 1006, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1774, 109, 1007, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1775, 109, 1008, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1776, 109, 1009, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1800, 109, 1064, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1801, 109, 1001, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1802, 109, 1065, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1803, 109, 1002, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1804, 109, 1003, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1805, 109, 1004, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1806, 109, 1005, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1807, 109, 1006, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1808, 109, 1007, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1809, 109, 1008, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1810, 109, 1009, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1994, 2, 1027, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1995, 2, 1028, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1996, 2, 1029, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1997, 2, 1030, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1998, 2, 1031, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1999, 2, 1032, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2000, 2, 1033, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2001, 2, 1034, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2002, 2, 1035, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2003, 2, 1036, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2004, 2, 1037, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2005, 2, 1038, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2006, 2, 1039, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2007, 2, 1040, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2008, 2, 1042, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2009, 2, 1043, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2010, 2, 1045, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2011, 2, 1046, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2012, 2, 1048, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2013, 2, 1050, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2014, 2, 1051, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2015, 2, 1052, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2016, 2, 1053, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2017, 2, 1054, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2018, 2, 1056, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2019, 2, 1057, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2020, 2, 1058, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2021, 2, 2083, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2022, 2, 1059, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2023, 2, 1060, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2024, 2, 1063, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2025, 2, 1064, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2026, 2, 1065, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2027, 2, 1066, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2028, 2, 1067, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2029, 2, 1070, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2034, 2, 1075, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2036, 2, 1082, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2037, 2, 1085, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2038, 2, 1086, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2039, 2, 1087, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2040, 2, 1088, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2041, 2, 1089, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2042, 2, 1091, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2043, 2, 1092, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2044, 2, 1095, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2045, 2, 1096, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2046, 2, 1097, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2047, 2, 1098, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2048, 2, 1101, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2049, 2, 1102, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2050, 2, 1103, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2051, 2, 1104, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2052, 2, 1105, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2053, 2, 1106, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2054, 2, 1108, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2055, 2, 1109, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2061, 2, 1127, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2062, 2, 1128, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2063, 2, 1129, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2064, 2, 1130, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2066, 2, 1132, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2067, 2, 1133, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2068, 2, 1134, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2069, 2, 1135, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2070, 2, 1136, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2071, 2, 1137, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2072, 2, 114, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2073, 2, 1139, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2074, 2, 115, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2075, 2, 1140, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2076, 2, 116, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2077, 2, 1141, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2078, 2, 1142, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2079, 2, 1143, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2080, 2, 1150, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2081, 2, 1161, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2082, 2, 1162, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2086, 2, 1166, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2087, 2, 1173, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2088, 2, 1174, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2092, 2, 1178, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2099, 2, 1226, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2100, 2, 1227, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2101, 2, 1228, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2102, 2, 1229, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2103, 2, 1237, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2104, 2, 1238, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2105, 2, 1239, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2106, 2, 1240, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2107, 2, 1241, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2108, 2, 1242, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2109, 2, 1243, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2116, 2, 1254, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2117, 2, 1255, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2118, 2, 1256, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2119, 2, 1257, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2120, 2, 1258, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2121, 2, 1259, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2122, 2, 1260, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2123, 2, 1261, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2124, 2, 1263, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2125, 2, 1264, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2126, 2, 1265, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2127, 2, 1266, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2128, 2, 1267, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2129, 2, 1001, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2130, 2, 1002, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2131, 2, 1003, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2132, 2, 1004, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2133, 2, 1005, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2134, 2, 1006, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2135, 2, 1007, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2136, 2, 1008, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2137, 2, 1009, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2138, 2, 1010, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2148, 2, 1020, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2149, 2, 1021, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2150, 2, 1022, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2151, 2, 1023, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2152, 2, 1281, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2153, 2, 1282, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2154, 2, 2000, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2155, 2, 2002, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2156, 2, 2003, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2157, 2, 2004, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2158, 2, 2005, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2159, 2, 2006, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2160, 2, 2008, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2161, 2, 2009, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2162, 2, 2010, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2163, 2, 2011, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2164, 2, 2012, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2170, 2, 2019, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2171, 2, 2020, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2172, 2, 2021, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2173, 2, 2022, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2174, 2, 2023, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2175, 2, 2025, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2177, 2, 2027, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2178, 2, 2028, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2179, 2, 2029, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2180, 2, 2014, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2181, 2, 2015, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2182, 2, 2016, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2183, 2, 2017, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2184, 2, 2018, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2188, 101, 1024, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2189, 101, 1, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2190, 101, 1025, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2191, 101, 1026, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2192, 101, 1027, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2193, 101, 1028, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2194, 101, 1029, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2195, 101, 1030, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2196, 101, 1036, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2197, 101, 1037, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2198, 101, 1038, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2199, 101, 1039, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2200, 101, 1040, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2201, 101, 1042, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2202, 101, 1043, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2203, 101, 1045, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2204, 101, 1046, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2205, 101, 1048, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2206, 101, 2083, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2207, 101, 1063, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2208, 101, 1064, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2209, 101, 1065, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2210, 101, 1093, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2211, 101, 1094, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2212, 101, 1095, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2213, 101, 1096, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2214, 101, 1097, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2215, 101, 1098, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2216, 101, 1100, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2217, 101, 1101, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2218, 101, 1102, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2219, 101, 1103, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2220, 101, 1104, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2221, 101, 1105, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2222, 101, 1106, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2223, 101, 2130, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2224, 101, 1107, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2225, 101, 2131, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2226, 101, 1108, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2227, 101, 2132, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2228, 101, 1109, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2229, 101, 2133, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2230, 101, 2134, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2232, 101, 2135, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2234, 101, 2136, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2236, 101, 2137, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2238, 101, 2138, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2240, 101, 2139, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2242, 101, 2140, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2243, 101, 2141, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2244, 101, 2142, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2245, 101, 2143, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2246, 101, 2144, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2247, 101, 2145, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2248, 101, 2146, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2249, 101, 2147, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2250, 101, 100, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2251, 101, 2148, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2252, 101, 101, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2253, 101, 2149, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2254, 101, 102, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2255, 101, 2150, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2256, 101, 103, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2257, 101, 2151, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2258, 101, 104, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2259, 101, 2152, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2260, 101, 105, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2261, 101, 107, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2262, 101, 108, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2263, 101, 109, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2264, 101, 1138, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2265, 101, 1139, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2266, 101, 1140, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2267, 101, 1141, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2268, 101, 1142, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2269, 101, 1143, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2270, 101, 1224, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2271, 101, 1225, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2272, 101, 1226, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2273, 101, 1227, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2274, 101, 1228, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2275, 101, 1229, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2282, 101, 1261, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2283, 101, 1263, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2284, 101, 1264, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2285, 101, 1265, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2286, 101, 1266, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2287, 101, 1267, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2288, 101, 1001, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2289, 101, 1002, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2290, 101, 1003, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2291, 101, 1004, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2292, 101, 1005, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2293, 101, 1006, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2294, 101, 1007, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2295, 101, 1008, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2296, 101, 1009, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2297, 101, 1010, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2298, 101, 1011, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2299, 101, 1012, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2300, 101, 500, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2301, 101, 1013, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2302, 101, 501, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2303, 101, 1014, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2304, 101, 1015, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2305, 101, 1016, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2306, 101, 1017, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2307, 101, 1018, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2308, 101, 1019, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2309, 101, 1020, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2310, 101, 1021, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2311, 101, 1022, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2312, 101, 1023, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2929, 109, 1224, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2930, 109, 1225, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2931, 109, 1226, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2932, 109, 1227, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2933, 109, 1228, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2934, 109, 1229, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2935, 109, 1138, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2936, 109, 1139, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2937, 109, 1140, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2996, 109, 1033, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2997, 109, 1034, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2998, 109, 1035, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2999, 109, 1050, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3000, 109, 1051, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3001, 109, 1052, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3002, 109, 1053, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3003, 109, 1054, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3004, 109, 1056, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3005, 109, 1057, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3006, 109, 1058, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3007, 109, 1059, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3008, 109, 1060, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3009, 109, 1066, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3010, 109, 1067, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3011, 109, 1070, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3012, 109, 1075, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3014, 109, 1077, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3015, 109, 1078, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3016, 109, 1082, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3017, 109, 1083, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3018, 109, 1084, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3019, 109, 1085, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3020, 109, 1086, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3021, 109, 1087, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3022, 109, 1088, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3023, 109, 1089, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3024, 109, 1090, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3025, 109, 1091, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3026, 109, 1092, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3027, 109, 106, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3028, 109, 110, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3029, 109, 111, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3030, 109, 112, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3031, 109, 113, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3032, 109, 114, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3033, 109, 115, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3034, 109, 116, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3035, 109, 2472, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3036, 109, 2478, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3037, 109, 2479, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3038, 109, 2480, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3039, 109, 2481, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3040, 109, 2482, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3041, 109, 2483, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3042, 109, 2484, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3043, 109, 2485, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3044, 109, 2486, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3045, 109, 2487, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3046, 109, 2488, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3047, 109, 2489, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3048, 109, 2490, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3049, 109, 2491, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3050, 109, 2492, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3051, 109, 2493, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3052, 109, 2494, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3053, 109, 2495, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3054, 109, 2497, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3055, 109, 1237, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3056, 109, 1238, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3057, 109, 1239, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3058, 109, 1240, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3059, 109, 1241, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3060, 109, 1242, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3061, 109, 1243, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3062, 109, 2525, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3063, 109, 1255, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3064, 109, 1256, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3065, 109, 1257, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4166, 109, 1120, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4167, 109, 2713, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4168, 109, 2714, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4169, 109, 2715, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4170, 109, 2716, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4171, 109, 2717, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4172, 109, 2718, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4173, 109, 2720, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4174, 109, 1185, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4175, 109, 2721, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4176, 109, 1186, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4177, 109, 2722, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4178, 109, 1187, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4179, 109, 2723, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4180, 109, 1188, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4181, 109, 2724, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4182, 109, 1189, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4183, 109, 2725, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4184, 109, 1190, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4185, 109, 2726, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4186, 109, 1191, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4187, 109, 2727, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4188, 109, 1192, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4189, 109, 2728, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4190, 109, 1193, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4191, 109, 2729, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4192, 109, 1194, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4193, 109, 2730, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4194, 109, 1195, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4195, 109, 2731, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4196, 109, 1196, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4197, 109, 2732, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4198, 109, 1197, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4199, 109, 2733, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4200, 109, 1198, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4201, 109, 2734, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4202, 109, 1199, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4203, 109, 2735, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4204, 109, 1200, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4205, 109, 1201, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4206, 109, 1202, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4207, 109, 1207, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4208, 109, 1208, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4209, 109, 1209, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4210, 109, 1210, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4211, 109, 1211, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4212, 109, 1212, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4213, 109, 1213, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4214, 109, 1215, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4215, 109, 1216, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4216, 109, 1217, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4217, 109, 1218, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4218, 109, 1219, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4255, 111, 1196, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5777, 101, 2739, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5778, 101, 2740, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5782, 2, 2759, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5783, 2, 2362, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5784, 2, 2387, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5786, 101, 2758, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5787, 101, 2759, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5788, 101, 2783, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6105, 155, 4033, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6106, 155, 4034, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6107, 155, 4035, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6108, 155, 4036, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6109, 155, 4037, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6110, 155, 4038, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6111, 155, 4039, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6112, 155, 4040, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6113, 155, 4041, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6114, 155, 4042, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6115, 155, 4043, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6116, 155, 4044, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6117, 155, 4045, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6118, 155, 4046, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6119, 155, 4001, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6120, 155, 4002, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6121, 155, 4003, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6122, 155, 4004, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6123, 155, 4005, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6124, 155, 4006, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6125, 155, 4007, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6126, 155, 4008, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6127, 155, 4009, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6128, 155, 4010, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6129, 155, 4011, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6130, 155, 4012, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6131, 155, 4013, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6132, 155, 4014, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6133, 155, 4015, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6134, 155, 4016, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6135, 155, 4017, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6136, 155, 4018, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6137, 155, 4031, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6138, 101, 5010, '1', '2025-05-05 17:49:17', '1', '2025-05-05 17:49:17', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_role_menu_seq;\nCREATE SEQUENCE system_role_menu_seq\n    START 6139;\n\n-- ----------------------------\n-- Table structure for system_sms_channel\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_channel;\nCREATE TABLE system_sms_channel\n(\n    id           int8         NOT NULL,\n    signature    varchar(12)  NOT NULL,\n    code         varchar(63)  NOT NULL,\n    status       int2         NOT NULL,\n    remark       varchar(255) NULL     DEFAULT NULL,\n    api_key      varchar(128) NOT NULL,\n    api_secret   varchar(128) NULL     DEFAULT NULL,\n    callback_url varchar(255) NULL     DEFAULT NULL,\n    creator      varchar(64)  NULL     DEFAULT '',\n    create_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater      varchar(64)  NULL     DEFAULT '',\n    update_time  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted      int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_channel\n    ADD CONSTRAINT pk_system_sms_channel PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_channel.id IS '编号';\nCOMMENT ON COLUMN system_sms_channel.signature IS '短信签名';\nCOMMENT ON COLUMN system_sms_channel.code IS '渠道编码';\nCOMMENT ON COLUMN system_sms_channel.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_channel.remark IS '备注';\nCOMMENT ON COLUMN system_sms_channel.api_key IS '短信 API 的账号';\nCOMMENT ON COLUMN system_sms_channel.api_secret IS '短信 API 的秘钥';\nCOMMENT ON COLUMN system_sms_channel.callback_url IS '短信发送回调 URL';\nCOMMENT ON COLUMN system_sms_channel.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_channel.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_channel.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_channel.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_channel.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_channel IS '短信渠道';\n\n-- ----------------------------\n-- Records of system_sms_channel\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (2, 'Ballcat', 'ALIYUN', 0, '你要改哦，只有我可以用！！！！', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', '2021-03-31 11:53:10', '1', '2024-08-04 08:53:26', '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (4, '测试渠道', 'DEBUG_DING_TALK', 0, '123', '696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859', 'SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67', NULL, '1', '2021-04-13 00:23:14', '1', '2022-03-27 20:29:49', '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (7, 'mock腾讯云', 'TENCENT', 0, '', '1 2', '2 3', '', '1', '2024-09-30 08:53:45', '1', '2024-09-30 08:55:01', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_sms_channel_seq;\nCREATE SEQUENCE system_sms_channel_seq\n    START 8;\n\n-- ----------------------------\n-- Table structure for system_sms_code\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_code;\nCREATE TABLE system_sms_code\n(\n    id          int8         NOT NULL,\n    mobile      varchar(11)  NOT NULL,\n    code        varchar(6)   NOT NULL,\n    create_ip   varchar(15)  NOT NULL,\n    scene       int2         NOT NULL,\n    today_index int2         NOT NULL,\n    used        int2         NOT NULL,\n    used_time   timestamp    NULL     DEFAULT NULL,\n    used_ip     varchar(255) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_code\n    ADD CONSTRAINT pk_system_sms_code PRIMARY KEY (id);\n\nCREATE INDEX idx_system_sms_code_01 ON system_sms_code (mobile);\n\nCOMMENT ON COLUMN system_sms_code.id IS '编号';\nCOMMENT ON COLUMN system_sms_code.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_code.code IS '验证码';\nCOMMENT ON COLUMN system_sms_code.create_ip IS '创建 IP';\nCOMMENT ON COLUMN system_sms_code.scene IS '发送场景';\nCOMMENT ON COLUMN system_sms_code.today_index IS '今日发送的第几条';\nCOMMENT ON COLUMN system_sms_code.used IS '是否使用';\nCOMMENT ON COLUMN system_sms_code.used_time IS '使用时间';\nCOMMENT ON COLUMN system_sms_code.used_ip IS '使用 IP';\nCOMMENT ON COLUMN system_sms_code.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_code.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_sms_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_sms_code IS '手机验证码';\n\nDROP SEQUENCE IF EXISTS system_sms_code_seq;\nCREATE SEQUENCE system_sms_code_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_sms_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_log;\nCREATE TABLE system_sms_log\n(\n    id               int8         NOT NULL,\n    channel_id       int8         NOT NULL,\n    channel_code     varchar(63)  NOT NULL,\n    template_id      int8         NOT NULL,\n    template_code    varchar(63)  NOT NULL,\n    template_type    int2         NOT NULL,\n    template_content varchar(255) NOT NULL,\n    template_params  varchar(255) NOT NULL,\n    api_template_id  varchar(63)  NOT NULL,\n    mobile           varchar(11)  NOT NULL,\n    user_id          int8         NULL     DEFAULT NULL,\n    user_type        int2         NULL     DEFAULT NULL,\n    send_status      int2         NOT NULL DEFAULT 0,\n    send_time        timestamp    NULL     DEFAULT NULL,\n    api_send_code    varchar(63)  NULL     DEFAULT NULL,\n    api_send_msg     varchar(255) NULL     DEFAULT NULL,\n    api_request_id   varchar(255) NULL     DEFAULT NULL,\n    api_serial_no    varchar(255) NULL     DEFAULT NULL,\n    receive_status   int2         NOT NULL DEFAULT 0,\n    receive_time     timestamp    NULL     DEFAULT NULL,\n    api_receive_code varchar(63)  NULL     DEFAULT NULL,\n    api_receive_msg  varchar(255) NULL     DEFAULT NULL,\n    creator          varchar(64)  NULL     DEFAULT '',\n    create_time      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater          varchar(64)  NULL     DEFAULT '',\n    update_time      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted          int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_log\n    ADD CONSTRAINT pk_system_sms_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_log.id IS '编号';\nCOMMENT ON COLUMN system_sms_log.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_log.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_sms_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_sms_log.template_type IS '短信类型';\nCOMMENT ON COLUMN system_sms_log.template_content IS '短信内容';\nCOMMENT ON COLUMN system_sms_log.template_params IS '短信参数';\nCOMMENT ON COLUMN system_sms_log.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_log.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_sms_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_sms_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_sms_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_sms_log.api_send_code IS '短信 API 发送结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_send_msg IS '短信 API 发送失败的提示';\nCOMMENT ON COLUMN system_sms_log.api_request_id IS '短信 API 发送返回的唯一请求 ID';\nCOMMENT ON COLUMN system_sms_log.api_serial_no IS '短信 API 发送返回的序号';\nCOMMENT ON COLUMN system_sms_log.receive_status IS '接收状态';\nCOMMENT ON COLUMN system_sms_log.receive_time IS '接收时间';\nCOMMENT ON COLUMN system_sms_log.api_receive_code IS 'API 接收结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_receive_msg IS 'API 接收结果的说明';\nCOMMENT ON COLUMN system_sms_log.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_log.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_log IS '短信日志';\n\nDROP SEQUENCE IF EXISTS system_sms_log_seq;\nCREATE SEQUENCE system_sms_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_sms_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_template;\nCREATE TABLE system_sms_template\n(\n    id              int8         NOT NULL,\n    type            int2         NOT NULL,\n    status          int2         NOT NULL,\n    code            varchar(63)  NOT NULL,\n    name            varchar(63)  NOT NULL,\n    content         varchar(255) NOT NULL,\n    params          varchar(255) NOT NULL,\n    remark          varchar(255) NULL     DEFAULT NULL,\n    api_template_id varchar(63)  NOT NULL,\n    channel_id      int8         NOT NULL,\n    channel_code    varchar(63)  NOT NULL,\n    creator         varchar(64)  NULL     DEFAULT '',\n    create_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater         varchar(64)  NULL     DEFAULT '',\n    update_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted         int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_template\n    ADD CONSTRAINT pk_system_sms_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_template.id IS '编号';\nCOMMENT ON COLUMN system_sms_template.type IS '模板类型';\nCOMMENT ON COLUMN system_sms_template.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_template.code IS '模板编码';\nCOMMENT ON COLUMN system_sms_template.name IS '模板名称';\nCOMMENT ON COLUMN system_sms_template.content IS '模板内容';\nCOMMENT ON COLUMN system_sms_template.params IS '参数数组';\nCOMMENT ON COLUMN system_sms_template.remark IS '备注';\nCOMMENT ON COLUMN system_sms_template.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_template.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_template.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_template.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_template.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_template IS '短信模板';\n\n-- ----------------------------\n-- Records of system_sms_template\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (2, 1, 0, 'test_01', '测试验证码短信', '正在进行登录操作{operation}，您的验证码是{code}', '[\"operation\",\"code\"]', '测试备注', '4383920', 4, 'DEBUG_DING_TALK', '', '2021-03-31 10:49:38', '1', '2024-08-18 11:57:18', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (3, 1, 0, 'test_02', '公告通知', '您的验证码{code}，该验证码5分钟内有效，请勿泄漏于他人！', '[\"code\"]', NULL, 'SMS_207945135', 2, 'ALIYUN', '', '2021-03-31 11:56:30', '1', '2021-04-10 01:22:02', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (6, 3, 0, 'test-01', '测试模板', '哈哈哈 {name}', '[\"name\"]', 'f哈哈哈', '4383920', 4, 'DEBUG_DING_TALK', '1', '2021-04-10 01:07:21', '1', '2024-08-18 11:57:07', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (7, 3, 0, 'test-04', '测试下', '老鸡{name}，牛逼{code}', '[\"name\",\"code\"]', '哈哈哈哈', 'suibian', 7, 'DEBUG_DING_TALK', '1', '2021-04-13 00:29:53', '1', '2024-09-30 00:56:24', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (8, 1, 0, 'user-sms-login', '前台用户短信登录', '您的验证码是{code}', '[\"code\"]', NULL, '4372216', 4, 'DEBUG_DING_TALK', '1', '2021-10-11 08:10:00', '1', '2024-08-18 11:57:06', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (9, 2, 0, 'bpm_task_assigned', '【工作流】任务被分配', '您收到了一条新的待办任务：{processInstanceName}-{taskName}，申请人：{startUserNickname}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"startUserNickname\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-21 22:31:19', '1', '2022-01-22 00:03:36', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (10, 2, 0, 'bpm_process_instance_reject', '【工作流】流程被不通过', '您的流程被审批不通过：{processInstanceName}，原因：{reason}，查看链接：{detailUrl}', '[\"processInstanceName\",\"reason\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:03:31', '1', '2022-05-01 12:33:14', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (11, 2, 0, 'bpm_process_instance_approve', '【工作流】流程被通过', '您的流程被审批通过：{processInstanceName}，查看链接：{detailUrl}', '[\"processInstanceName\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:04:31', '1', '2022-03-27 20:32:21', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (12, 2, 0, 'demo', '演示模板', '我就是测试一下下', '[]', NULL, 'biubiubiu', 4, 'DEBUG_DING_TALK', '1', '2022-04-10 23:22:49', '1', '2024-08-18 11:57:04', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (14, 1, 0, 'user-update-mobile', '会员用户 - 修改手机', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:04', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (15, 1, 0, 'user-update-password', '会员用户 - 修改密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:18', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (16, 1, 0, 'user-reset-password', '会员用户 - 重置密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-12-02 22:35:27', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (17, 2, 0, 'bpm_task_timeout', '【工作流】任务审批超时', '您收到了一条超时的待办任务：{processInstanceName}-{taskName}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"detailUrl\"]', '', 'X', 4, 'DEBUG_DING_TALK', '1', '2024-08-16 21:59:15', '1', '2024-08-16 21:59:34', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (18, 1, 0, 'admin-reset-password', '后台用户 - 忘记密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2025-03-16 14:19:34', '1', '2025-03-16 14:19:45', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (19, 1, 0, 'admin-sms-login', '后台用户短信登录', '您的验证码是{code}', '[\"code\"]', '', '4372216', 4, 'DEBUG_DING_TALK', '1', '2025-04-08 09:36:03', '1', '2025-04-08 09:36:17', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_sms_template_seq;\nCREATE SEQUENCE system_sms_template_seq\n    START 20;\n\n-- ----------------------------\n-- Table structure for system_social_client\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_client;\nCREATE TABLE system_social_client\n(\n    id            int8         NOT NULL,\n    name          varchar(255) NOT NULL,\n    social_type   int2         NOT NULL,\n    user_type     int2         NOT NULL,\n    client_id     varchar(255) NOT NULL,\n    client_secret varchar(2048) NOT NULL,\n    public_key    varchar(2048) NULL     DEFAULT NULL,\n    agent_id      varchar(255) NULL     DEFAULT NULL,\n    status        int2         NOT NULL,\n    creator       varchar(64)  NULL     DEFAULT '',\n    create_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater       varchar(64)  NULL     DEFAULT '',\n    update_time   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted       int2         NOT NULL DEFAULT 0,\n    tenant_id     int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_social_client\n    ADD CONSTRAINT pk_system_social_client PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_client.id IS '编号';\nCOMMENT ON COLUMN system_social_client.name IS '应用名';\nCOMMENT ON COLUMN system_social_client.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_client.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_social_client.client_secret IS '客户端密钥';\nCOMMENT ON COLUMN system_social_client.public_key IS 'publicKey公钥';\nCOMMENT ON COLUMN system_social_client.agent_id IS '代理编号';\nCOMMENT ON COLUMN system_social_client.status IS '状态';\nCOMMENT ON COLUMN system_social_client.creator IS '创建者';\nCOMMENT ON COLUMN system_social_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_client.updater IS '更新者';\nCOMMENT ON COLUMN system_social_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_client.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_client.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_client IS '社交客户端表';\n\n-- ----------------------------\n-- Records of system_social_client\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '钉钉', 20, 2, 'dingvrnreaje3yqvzhxg', 'i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI', NULL, 0, '', '2023-10-18 11:21:18', '1', '2023-12-20 21:28:26', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '钉钉（王土豆）', 20, 2, 'dingtsu9hpepjkbmthhw', 'FP_bnSq_HAHKCSncmJjw5hxhnzs6vaVDSZZn3egj6rdqTQ_hu5tQVJyLMpgCakdP', NULL, 0, '', '2023-10-18 11:21:18', '', '2023-12-20 21:28:26', '1', 121);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '微信公众号', 31, 1, 'wx5b23ba7a5589ecbb', '2a7b3b20c537e52e74afd395eb85f61f', NULL, 0, '', '2023-10-18 16:07:46', '1', '2023-12-20 21:28:23', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (43, '微信小程序', 34, 1, 'wx63c280fe3248a3e7', '6f270509224a7ae1296bbf1c8cb97aed', NULL, 0, '', '2023-10-19 13:37:41', '1', '2023-12-20 21:28:25', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (44, '1', 10, 1, '2', '3', NULL, 0, '1', '2025-04-06 20:36:28', '1', '2025-04-06 20:43:12', '1', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_social_client_seq;\nCREATE SEQUENCE system_social_client_seq\n    START 45;\n\n-- ----------------------------\n-- Table structure for system_social_user\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_user;\nCREATE TABLE system_social_user\n(\n    id             int8          NOT NULL,\n    type           int2          NOT NULL,\n    openid         varchar(32)   NOT NULL,\n    token          varchar(256)  NULL     DEFAULT NULL,\n    raw_token_info varchar(1024) NOT NULL,\n    nickname       varchar(32)   NOT NULL,\n    avatar         varchar(255)  NULL     DEFAULT NULL,\n    raw_user_info  varchar(1024) NOT NULL,\n    code           varchar(256)  NOT NULL,\n    state          varchar(256)  NULL     DEFAULT NULL,\n    creator        varchar(64)   NULL     DEFAULT '',\n    create_time    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64)   NULL     DEFAULT '',\n    update_time    timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2          NOT NULL DEFAULT 0,\n    tenant_id      int8          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_social_user\n    ADD CONSTRAINT pk_system_social_user PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_user.id IS '主键 ( 自增策略)';\nCOMMENT ON COLUMN system_social_user.type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user.openid IS '社交 openid';\nCOMMENT ON COLUMN system_social_user.token IS '社交 token';\nCOMMENT ON COLUMN system_social_user.raw_token_info IS '原始 Token 数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_social_user.avatar IS '用户头像';\nCOMMENT ON COLUMN system_social_user.raw_user_info IS '原始用户数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.code IS '最后一次的认证 code';\nCOMMENT ON COLUMN system_social_user.state IS '最后一次的认证 state';\nCOMMENT ON COLUMN system_social_user.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user IS '社交用户表';\n\nDROP SEQUENCE IF EXISTS system_social_user_seq;\nCREATE SEQUENCE system_social_user_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_social_user_bind\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_user_bind;\nCREATE TABLE system_social_user_bind\n(\n    id             int8        NOT NULL,\n    user_id        int8        NOT NULL,\n    user_type      int2        NOT NULL,\n    social_type    int2        NOT NULL,\n    social_user_id int8        NOT NULL,\n    creator        varchar(64) NULL     DEFAULT '',\n    create_time    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater        varchar(64) NULL     DEFAULT '',\n    update_time    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted        int2        NOT NULL DEFAULT 0,\n    tenant_id      int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_social_user_bind\n    ADD CONSTRAINT pk_system_social_user_bind PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_user_bind.id IS '主键 ( 自增策略)';\nCOMMENT ON COLUMN system_social_user_bind.user_id IS '用户编号';\nCOMMENT ON COLUMN system_social_user_bind.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_user_bind.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user_bind.social_user_id IS '社交用户的编号';\nCOMMENT ON COLUMN system_social_user_bind.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user_bind.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user_bind.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user_bind.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user_bind.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user_bind.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user_bind IS '社交绑定表';\n\nDROP SEQUENCE IF EXISTS system_social_user_bind_seq;\nCREATE SEQUENCE system_social_user_bind_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_tenant\n-- ----------------------------\nDROP TABLE IF EXISTS system_tenant;\nCREATE TABLE system_tenant\n(\n    id              int8         NOT NULL,\n    name            varchar(30)  NOT NULL,\n    contact_user_id int8         NULL     DEFAULT NULL,\n    contact_name    varchar(30)  NOT NULL,\n    contact_mobile  varchar(500) NULL     DEFAULT NULL,\n    status          int2         NOT NULL DEFAULT 0,\n    websites        varchar(256) NULL     DEFAULT '',\n    package_id      int8         NOT NULL,\n    expire_time     timestamp    NOT NULL,\n    account_count   int4         NOT NULL,\n    creator         varchar(64)  NULL     DEFAULT '',\n    create_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater         varchar(64)  NULL     DEFAULT '',\n    update_time     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted         int2         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_tenant\n    ADD CONSTRAINT pk_system_tenant PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_tenant.id IS '租户编号';\nCOMMENT ON COLUMN system_tenant.name IS '租户名';\nCOMMENT ON COLUMN system_tenant.contact_user_id IS '联系人的用户编号';\nCOMMENT ON COLUMN system_tenant.contact_name IS '联系人';\nCOMMENT ON COLUMN system_tenant.contact_mobile IS '联系手机';\nCOMMENT ON COLUMN system_tenant.status IS '租户状态（0正常 1停用）';\nCOMMENT ON COLUMN system_tenant.websites IS '绑定域名数组';\nCOMMENT ON COLUMN system_tenant.package_id IS '租户套餐编号';\nCOMMENT ON COLUMN system_tenant.expire_time IS '过期时间';\nCOMMENT ON COLUMN system_tenant.account_count IS '账号数量';\nCOMMENT ON COLUMN system_tenant.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant IS '租户表';\n\n-- ----------------------------\n-- Records of system_tenant\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2023-11-06 11:41:41', '0');\nINSERT 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', 111, '2026-07-10 00:00:00', 30, '1', '2022-02-22 00:56:14', '1', '2025-04-03 21:33:01', '0');\nINSERT 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', 111, '2022-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2024-09-22 12:10:50', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_tenant_seq;\nCREATE SEQUENCE system_tenant_seq\n    START 123;\n\n-- ----------------------------\n-- Table structure for system_tenant_package\n-- ----------------------------\nDROP TABLE IF EXISTS system_tenant_package;\nCREATE TABLE system_tenant_package\n(\n    id          int8          NOT NULL,\n    name        varchar(30)   NOT NULL,\n    status      int2          NOT NULL DEFAULT 0,\n    remark      varchar(256)  NULL     DEFAULT '',\n    menu_ids    varchar(4096) NOT NULL,\n    creator     varchar(64)   NULL     DEFAULT '',\n    create_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)   NULL     DEFAULT '',\n    update_time timestamp     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2          NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_tenant_package\n    ADD CONSTRAINT pk_system_tenant_package PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_tenant_package.id IS '套餐编号';\nCOMMENT ON COLUMN system_tenant_package.name IS '套餐名';\nCOMMENT ON COLUMN system_tenant_package.status IS '租户状态（0正常 1停用）';\nCOMMENT ON COLUMN system_tenant_package.remark IS '备注';\nCOMMENT ON COLUMN system_tenant_package.menu_ids IS '关联的菜单编号';\nCOMMENT ON COLUMN system_tenant_package.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant_package.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant_package.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant_package.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant_package.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant_package IS '租户套餐表';\n\n-- ----------------------------\n-- Records of system_tenant_package\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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,1118,1119,1120,100,101,102,103,106,107,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2713,2714,2715,2716,2717,2718,2720,1185,2721,1186,2722,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,2472,1192,2728,1193,2729,1194,2730,1195,2731,1196,2732,1197,2733,2478,1198,2734,2479,1199,2735,2480,1200,2481,1201,2482,1202,2483,2739,2484,2740,2485,2486,2487,1207,2488,1208,2489,1209,2490,1210,2491,1211,2492,1212,2493,1213,2494,2495,1215,1216,2497,1217,1218,1219,1220,1221,1222,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,2525,1255,1256,1001,1257,1002,1258,1003,1259,1004,1260,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020]', '1', '2022-02-22 00:54:00', '1', '2024-07-13 22:37:24', '0');\nINSERT INTO system_tenant_package (id, name, status, remark, menu_ids, creator, create_time, updater, update_time, deleted) VALUES (112, '再来一个套餐', 0, '1234', '[1024,1,1025,1026,2,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1042,1043,1045,1046,1048,1050,1051,1052,1053,1054,1056,1057,1058,2083,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1100,1101,1102,1103,1104,1105,1106,2130,1107,2131,1108,2132,1109,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,100,2148,101,2149,102,2150,103,2151,104,2152,105,106,107,108,109,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2739,2740,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,1255,1256,1257,1258,1259,1260,1261,1263,1264,1265,1266,1267,2447,2448,2449,2450,2451,2452,2453,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,500,1013,501,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023]', '1', '2025-04-04 08:15:02', '1', '2025-04-04 08:15:21', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_tenant_package_seq;\nCREATE SEQUENCE system_tenant_package_seq\n    START 113;\n\n-- ----------------------------\n-- Table structure for system_user_post\n-- ----------------------------\nDROP TABLE IF EXISTS system_user_post;\nCREATE TABLE system_user_post\n(\n    id          int8        NOT NULL,\n    user_id     int8        NOT NULL DEFAULT 0,\n    post_id     int8        NOT NULL DEFAULT 0,\n    creator     varchar(64) NULL     DEFAULT '',\n    create_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64) NULL     DEFAULT '',\n    update_time timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2        NOT NULL DEFAULT 0,\n    tenant_id   int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_user_post\n    ADD CONSTRAINT pk_system_user_post PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_user_post.id IS 'id';\nCOMMENT ON COLUMN system_user_post.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_post.post_id IS '岗位ID';\nCOMMENT ON COLUMN system_user_post.creator IS '创建者';\nCOMMENT ON COLUMN system_user_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_post.updater IS '更新者';\nCOMMENT ON COLUMN system_user_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_post IS '用户岗位表';\n\n-- ----------------------------\n-- Records of system_user_post\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, 1, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, 100, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (115, 104, 1, '1', '2022-05-16 19:36:28', '1', '2022-05-16 19:36:28', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (116, 117, 2, '1', '2022-07-09 17:40:26', '1', '2022-07-09 17:40:26', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (117, 118, 1, '1', '2022-07-09 17:44:44', '1', '2022-07-09 17:44:44', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (119, 114, 5, '1', '2024-03-24 20:45:51', '1', '2024-03-24 20:45:51', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (123, 115, 1, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (124, 115, 2, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (125, 1, 2, '1', '2024-07-13 22:31:39', '1', '2024-07-13 22:31:39', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_user_post_seq;\nCREATE SEQUENCE system_user_post_seq\n    START 126;\n\n-- ----------------------------\n-- Table structure for system_user_role\n-- ----------------------------\nDROP TABLE IF EXISTS system_user_role;\nCREATE TABLE system_user_role\n(\n    id          int8        NOT NULL,\n    user_id     int8        NOT NULL,\n    role_id     int8        NOT NULL,\n    creator     varchar(64) NULL     DEFAULT '',\n    create_time timestamp   NULL     DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64) NULL     DEFAULT '',\n    update_time timestamp   NULL     DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2        NOT NULL DEFAULT 0,\n    tenant_id   int8        NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_user_role\n    ADD CONSTRAINT pk_system_user_role PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_user_role.id IS '自增编号';\nCOMMENT ON COLUMN system_user_role.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_role.role_id IS '角色ID';\nCOMMENT ON COLUMN system_user_role.creator IS '创建者';\nCOMMENT ON COLUMN system_user_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_role.updater IS '更新者';\nCOMMENT ON COLUMN system_user_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_role IS '用户和角色关联表';\n\n-- ----------------------------\n-- Records of system_user_role\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 1, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:17', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 100, 101, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 100, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:12', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 100, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:11', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 103, 1, '1', '2022-01-11 13:19:45', '1', '2022-01-11 13:19:45', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 110, 109, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 111, 110, '110', '2022-02-23 13:14:38', '110', '2022-02-23 13:14:38', '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 113, 111, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', '0', 122);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (18, 1, 2, '1', '2022-05-12 20:39:29', '1', '2022-05-12 20:39:29', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (38, 114, 101, '1', '2024-03-24 22:23:03', '1', '2024-03-24 22:23:03', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (47, 104, 2, '1', '2025-01-04 10:40:33', '1', '2025-01-04 10:40:33', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (48, 100, 155, '1', '2025-04-04 10:41:14', '1', '2025-04-04 10:41:14', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_user_role_seq;\nCREATE SEQUENCE system_user_role_seq\n    START 49;\n\n-- ----------------------------\n-- Table structure for system_users\n-- ----------------------------\nDROP TABLE IF EXISTS system_users;\nCREATE TABLE system_users\n(\n    id          int8         NOT NULL,\n    username    varchar(30)  NOT NULL,\n    password    varchar(100) NULL     DEFAULT '',\n    nickname    varchar(30)  NOT NULL,\n    remark      varchar(500) NULL     DEFAULT NULL,\n    dept_id     int8         NULL     DEFAULT NULL,\n    post_ids    varchar(255) NULL     DEFAULT NULL,\n    email       varchar(50)  NULL     DEFAULT '',\n    mobile      varchar(11)  NULL     DEFAULT '',\n    sex         int2         NULL     DEFAULT 0,\n    avatar      varchar(512) NULL     DEFAULT '',\n    status      int2         NOT NULL DEFAULT 0,\n    login_ip    varchar(50)  NULL     DEFAULT '',\n    login_date  timestamp    NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_users\n    ADD CONSTRAINT pk_system_users PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_users.id IS '用户ID';\nCOMMENT ON COLUMN system_users.username IS '用户账号';\nCOMMENT ON COLUMN system_users.password IS '密码';\nCOMMENT ON COLUMN system_users.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_users.remark IS '备注';\nCOMMENT ON COLUMN system_users.dept_id IS '部门ID';\nCOMMENT ON COLUMN system_users.post_ids IS '岗位编号数组';\nCOMMENT ON COLUMN system_users.email IS '用户邮箱';\nCOMMENT ON COLUMN system_users.mobile IS '手机号码';\nCOMMENT ON COLUMN system_users.sex IS '用户性别';\nCOMMENT ON COLUMN system_users.avatar IS '头像地址';\nCOMMENT ON COLUMN system_users.status IS '帐号状态（0正常 1停用）';\nCOMMENT ON COLUMN system_users.login_ip IS '最后登录IP';\nCOMMENT ON COLUMN system_users.login_date IS '最后登录时间';\nCOMMENT ON COLUMN system_users.creator IS '创建者';\nCOMMENT ON COLUMN system_users.create_time IS '创建时间';\nCOMMENT ON COLUMN system_users.updater IS '更新者';\nCOMMENT ON COLUMN system_users.update_time IS '更新时间';\nCOMMENT ON COLUMN system_users.deleted IS '是否删除';\nCOMMENT ON COLUMN system_users.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_users IS '用户信息表';\n\n-- ----------------------------\n-- Records of system_users\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '芋道源码', '管理员', 103, '[1,2]', '11aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/test/20250502/avatar_1746154660449.png', 0, '0:0:0:0:0:0:0:1', '2025-05-10 18:03:15', 'admin', '2021-01-05 17:03:47', NULL, '2025-05-10 18:03:15', '0', 1);\nINSERT 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$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '0', 118);\nINSERT 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', '0', 119);\nINSERT 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', '0', 120);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 1);\nINSERT 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', '0', 122);\nINSERT 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', '2024-03-24 22:21:05', '1', '2022-03-19 21:50:58', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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$jth0yOj8cSJq84D6vrzusOHDwW/LpBfgBnQ6bfFlD8zNZfM632Ta2', '狗蛋', NULL, 103, '[1]', '', '15601691239', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-17 09:10:27', '1', '2022-07-09 17:44:43', '1', '2025-04-21 14:23:08', '0', 1);\nINSERT 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 (131, 'hh', '$2a$04$jyH9h6.gaw8mpOjPfHIpx.8as2Rzfcmdlj5rlJFwgCw4rsv/MTb2K', '呵呵', NULL, 100, '[]', '777@qq.com', '15601882312', 1, NULL, 0, '', NULL, '1', '2024-04-27 08:45:56', '1', '2025-04-21 14:23:08', '0', 1);\nINSERT 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$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', '小秃头', NULL, NULL, NULL, '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-09-10 21:03:58', NULL, '2024-09-10 21:03:58', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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-04-08 13:09:07', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_users_seq;\nCREATE SEQUENCE system_users_seq\n    START 142;\n\n-- ----------------------------\n-- Table structure for yudao_demo01_contact\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo01_contact;\nCREATE TABLE yudao_demo01_contact\n(\n    id          int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    sex         int2         NOT NULL,\n    birthday    timestamp    NOT NULL,\n    description varchar(255) NOT NULL,\n    avatar      varchar(512) NULL     DEFAULT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo01_contact\n    ADD CONSTRAINT pk_yudao_demo01_contact PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo01_contact.id IS '编号';\nCOMMENT ON COLUMN yudao_demo01_contact.name IS '名字';\nCOMMENT ON COLUMN yudao_demo01_contact.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo01_contact.birthday IS '出生年';\nCOMMENT ON COLUMN yudao_demo01_contact.description IS '简介';\nCOMMENT ON COLUMN yudao_demo01_contact.avatar IS '头像';\nCOMMENT ON COLUMN yudao_demo01_contact.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo01_contact.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo01_contact.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo01_contact.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo01_contact.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo01_contact.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo01_contact IS '示例联系人表';\n\n-- ----------------------------\n-- Records of yudao_demo01_contact\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO yudao_demo01_contact (id, name, sex, birthday, description, avatar, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 2, '2023-11-07 00:00:00', '<p>天蚕土豆！呀</p>', 'http://127.0.0.1:48080/admin-api/infra/file/4/get/46f8fa1a37db3f3960d8910ff2fe3962ab3b2db87cf2f8ccb4dc8145b8bdf237.jpeg', '1', '2023-11-15 23:34:30', '1', '2023-11-15 23:47:39', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo01_contact_seq;\nCREATE SEQUENCE yudao_demo01_contact_seq\n    START 2;\n\n-- ----------------------------\n-- Table structure for yudao_demo02_category\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo02_category;\nCREATE TABLE yudao_demo02_category\n(\n    id          int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    parent_id   int8         NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo02_category\n    ADD CONSTRAINT pk_yudao_demo02_category PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo02_category.id IS '编号';\nCOMMENT ON COLUMN yudao_demo02_category.name IS '名字';\nCOMMENT ON COLUMN yudao_demo02_category.parent_id IS '父级编号';\nCOMMENT ON COLUMN yudao_demo02_category.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo02_category.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo02_category.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo02_category.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo02_category.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo02_category.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo02_category IS '示例分类表';\n\n-- ----------------------------\n-- Records of yudao_demo02_category\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 0, '1', '2023-11-15 23:34:30', '1', '2023-11-16 20:24:23', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '番茄', 0, '1', '2023-11-16 20:24:00', '1', '2023-11-16 20:24:15', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '怪怪', 0, '1', '2023-11-16 20:24:32', '1', '2023-11-16 20:24:32', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '小番茄', 2, '1', '2023-11-16 20:24:39', '1', '2023-11-16 20:24:39', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, '大番茄', 2, '1', '2023-11-16 20:24:46', '1', '2023-11-16 20:24:46', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, '11', 3, '1', '2023-11-24 19:29:34', '1', '2023-11-24 19:29:34', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo02_category_seq;\nCREATE SEQUENCE yudao_demo02_category_seq\n    START 7;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_course\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_course;\nCREATE TABLE yudao_demo03_course\n(\n    id          int8         NOT NULL,\n    student_id  int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    score       int2         NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo03_course\n    ADD CONSTRAINT pk_yudao_demo03_course PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_course.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_course.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_course.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_course.score IS '分数';\nCOMMENT ON COLUMN yudao_demo03_course.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_course.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_course.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_course.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_course.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_course.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_course IS '学生课程表';\n\n-- ----------------------------\n-- Records of yudao_demo03_course\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, '语文', 66, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 2, '数学', 22, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (11, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (12, 2, '电脑', 33, '1', '2023-11-17 00:20:42', '1', '2023-11-16 16:20:45', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (13, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:26', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:49', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', '0', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '1', 1);\nINSERT 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', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo03_course_seq;\nCREATE SEQUENCE yudao_demo03_course_seq\n    START 21;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_grade\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_grade;\nCREATE TABLE yudao_demo03_grade\n(\n    id          int8         NOT NULL,\n    student_id  int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    teacher     varchar(255) NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo03_grade\n    ADD CONSTRAINT pk_yudao_demo03_grade PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_grade.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_grade.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_grade.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_grade.teacher IS '班主任';\nCOMMENT ON COLUMN yudao_demo03_grade.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_grade.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_grade.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_grade.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_grade.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_grade.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_grade IS '学生班级表';\n\n-- ----------------------------\n-- Records of yudao_demo03_grade\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo03_grade_seq;\nCREATE SEQUENCE yudao_demo03_grade_seq\n    START 10;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_student\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_student;\nCREATE TABLE yudao_demo03_student\n(\n    id          int8         NOT NULL,\n    name        varchar(100) NULL     DEFAULT '',\n    sex         int2         NOT NULL,\n    birthday    timestamp    NOT NULL,\n    description varchar(255) NOT NULL,\n    creator     varchar(64)  NULL     DEFAULT '',\n    create_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updater     varchar(64)  NULL     DEFAULT '',\n    update_time timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    deleted     int2         NOT NULL DEFAULT 0,\n    tenant_id   int8         NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo03_student\n    ADD CONSTRAINT pk_yudao_demo03_student PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_student.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_student.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_student.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo03_student.birthday IS '出生日期';\nCOMMENT ON COLUMN yudao_demo03_student.description IS '简介';\nCOMMENT ON COLUMN yudao_demo03_student.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_student.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_student.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_student.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_student.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_student.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_student IS '学生表';\n\n-- ----------------------------\n-- Records of yudao_demo03_student\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo03_student_seq;\nCREATE SEQUENCE yudao_demo03_student_seq\n    START 10;\n\n"
  },
  {
    "path": "sql/oracle/quartz.sql",
    "content": "/*\n 注意：仅仅需要 Quartz 定时任务的场景，可选！！！\n\n Date: 15/06/2022 08:20:08\n*/\n\n-- ----------------------------\n-- Table structure for QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nDROP TABLE \"QRTZ_BLOB_TRIGGERS\";\nCREATE TABLE \"QRTZ_BLOB_TRIGGERS\" (\n                                      \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                      \"TRIGGER_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                      \"TRIGGER_GROUP\" VARCHAR2(200 BYTE) NOT NULL,\n                                      \"BLOB_DATA\" BLOB\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_CALENDARS\n-- ----------------------------\nDROP TABLE \"QRTZ_CALENDARS\";\nCREATE TABLE \"QRTZ_CALENDARS\" (\n                                  \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                  \"CALENDAR_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                  \"CALENDAR\" BLOB NOT NULL\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_CALENDARS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_CRON_TRIGGERS\n-- ----------------------------\nDROP TABLE \"QRTZ_CRON_TRIGGERS\";\nCREATE TABLE \"QRTZ_CRON_TRIGGERS\" (\n                                      \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                      \"TRIGGER_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                      \"TRIGGER_GROUP\" VARCHAR2(200 BYTE) NOT NULL,\n                                      \"CRON_EXPRESSION\" VARCHAR2(120 BYTE) NOT NULL,\n                                      \"TIME_ZONE_ID\" VARCHAR2(80 BYTE)\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_CRON_TRIGGERS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nDROP TABLE \"QRTZ_FIRED_TRIGGERS\";\nCREATE TABLE \"QRTZ_FIRED_TRIGGERS\" (\n                                       \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                       \"ENTRY_ID\" VARCHAR2(95 BYTE) NOT NULL,\n                                       \"TRIGGER_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                       \"TRIGGER_GROUP\" VARCHAR2(200 BYTE) NOT NULL,\n                                       \"INSTANCE_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                       \"FIRED_TIME\" NUMBER(13,0) NOT NULL,\n                                       \"SCHED_TIME\" NUMBER(13,0) NOT NULL,\n                                       \"PRIORITY\" NUMBER(13,0) NOT NULL,\n                                       \"STATE\" VARCHAR2(16 BYTE) NOT NULL,\n                                       \"JOB_NAME\" VARCHAR2(200 BYTE),\n                                       \"JOB_GROUP\" VARCHAR2(200 BYTE),\n                                       \"IS_NONCONCURRENT\" VARCHAR2(1 BYTE),\n                                       \"REQUESTS_RECOVERY\" VARCHAR2(1 BYTE)\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_JOB_DETAILS\n-- ----------------------------\nDROP TABLE \"QRTZ_JOB_DETAILS\";\nCREATE TABLE \"QRTZ_JOB_DETAILS\" (\n                                    \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                    \"JOB_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                    \"JOB_GROUP\" VARCHAR2(200 BYTE) NOT NULL,\n                                    \"DESCRIPTION\" VARCHAR2(250 BYTE),\n                                    \"JOB_CLASS_NAME\" VARCHAR2(250 BYTE) NOT NULL,\n                                    \"IS_DURABLE\" VARCHAR2(1 BYTE) NOT NULL,\n                                    \"IS_NONCONCURRENT\" VARCHAR2(1 BYTE) NOT NULL,\n                                    \"IS_UPDATE_DATA\" VARCHAR2(1 BYTE) NOT NULL,\n                                    \"REQUESTS_RECOVERY\" VARCHAR2(1 BYTE) NOT NULL,\n                                    \"JOB_DATA\" BLOB\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_JOB_DETAILS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_LOCKS\n-- ----------------------------\nDROP TABLE \"QRTZ_LOCKS\";\nCREATE TABLE \"QRTZ_LOCKS\" (\n                              \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                              \"LOCK_NAME\" VARCHAR2(40 BYTE) NOT NULL\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_LOCKS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_PAUSED_TRIGGER_GRPS\n-- ----------------------------\nDROP TABLE \"QRTZ_PAUSED_TRIGGER_GRPS\";\nCREATE TABLE \"QRTZ_PAUSED_TRIGGER_GRPS\" (\n                                            \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                            \"TRIGGER_GROUP\" VARCHAR2(200 BYTE) NOT NULL\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_PAUSED_TRIGGER_GRPS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_SCHEDULER_STATE\n-- ----------------------------\nDROP TABLE \"QRTZ_SCHEDULER_STATE\";\nCREATE TABLE \"QRTZ_SCHEDULER_STATE\" (\n                                        \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                        \"INSTANCE_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                        \"LAST_CHECKIN_TIME\" NUMBER(13,0) NOT NULL,\n                                        \"CHECKIN_INTERVAL\" NUMBER(13,0) NOT NULL\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_SCHEDULER_STATE\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nDROP TABLE \"QRTZ_SIMPLE_TRIGGERS\";\nCREATE TABLE \"QRTZ_SIMPLE_TRIGGERS\" (\n                                        \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                        \"TRIGGER_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                        \"TRIGGER_GROUP\" VARCHAR2(200 BYTE) NOT NULL,\n                                        \"REPEAT_COUNT\" NUMBER(7,0) NOT NULL,\n                                        \"REPEAT_INTERVAL\" NUMBER(12,0) NOT NULL,\n                                        \"TIMES_TRIGGERED\" NUMBER(10,0) NOT NULL\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nDROP TABLE \"QRTZ_SIMPROP_TRIGGERS\";\nCREATE TABLE \"QRTZ_SIMPROP_TRIGGERS\" (\n                                         \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                         \"TRIGGER_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                         \"TRIGGER_GROUP\" VARCHAR2(200 BYTE) NOT NULL,\n                                         \"STR_PROP_1\" VARCHAR2(512 BYTE),\n                                         \"STR_PROP_2\" VARCHAR2(512 BYTE),\n                                         \"STR_PROP_3\" VARCHAR2(512 BYTE),\n                                         \"INT_PROP_1\" NUMBER(10,0),\n                                         \"INT_PROP_2\" NUMBER(10,0),\n                                         \"LONG_PROP_1\" NUMBER(13,0),\n                                         \"LONG_PROP_2\" NUMBER(13,0),\n                                         \"DEC_PROP_1\" NUMBER(13,4),\n                                         \"DEC_PROP_2\" NUMBER(13,4),\n                                         \"BOOL_PROP_1\" VARCHAR2(1 BYTE),\n                                         \"BOOL_PROP_2\" VARCHAR2(1 BYTE)\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for QRTZ_TRIGGERS\n-- ----------------------------\nDROP TABLE \"QRTZ_TRIGGERS\";\nCREATE TABLE \"QRTZ_TRIGGERS\" (\n                                 \"SCHED_NAME\" VARCHAR2(120 BYTE) NOT NULL,\n                                 \"TRIGGER_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                 \"TRIGGER_GROUP\" VARCHAR2(200 BYTE) NOT NULL,\n                                 \"JOB_NAME\" VARCHAR2(200 BYTE) NOT NULL,\n                                 \"JOB_GROUP\" VARCHAR2(200 BYTE) NOT NULL,\n                                 \"DESCRIPTION\" VARCHAR2(250 BYTE),\n                                 \"NEXT_FIRE_TIME\" NUMBER(13,0),\n                                 \"PREV_FIRE_TIME\" NUMBER(13,0),\n                                 \"PRIORITY\" NUMBER(13,0),\n                                 \"TRIGGER_STATE\" VARCHAR2(16 BYTE) NOT NULL,\n                                 \"TRIGGER_TYPE\" VARCHAR2(8 BYTE) NOT NULL,\n                                 \"START_TIME\" NUMBER(13,0) NOT NULL,\n                                 \"END_TIME\" NUMBER(13,0),\n                                 \"CALENDAR_NAME\" VARCHAR2(200 BYTE),\n                                 \"MISFIRE_INSTR\" NUMBER(2,0),\n                                 \"JOB_DATA\" BLOB\n)\n    LOGGING\nNOCOMPRESS\nPCTFREE 10\nINITRANS 1\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n)\nPARALLEL 1\nNOCACHE\nDISABLE ROW MOVEMENT\n;\n\n-- ----------------------------\n-- Records of QRTZ_TRIGGERS\n-- ----------------------------\nCOMMIT;\nCOMMIT;\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_BLOB_TRIGGERS\" ADD CONSTRAINT \"QRTZ_BLOB_TRIG_PK\" PRIMARY KEY (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_BLOB_TRIGGERS\" ADD CONSTRAINT \"SYS_C008266\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_BLOB_TRIGGERS\" ADD CONSTRAINT \"SYS_C008267\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_BLOB_TRIGGERS\" ADD CONSTRAINT \"SYS_C008268\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_BLOB_TRIGGERS\" ADD CONSTRAINT \"SYS_C008653\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_BLOB_TRIGGERS\" ADD CONSTRAINT \"SYS_C008654\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_BLOB_TRIGGERS\" ADD CONSTRAINT \"SYS_C008655\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_CALENDARS\n-- ----------------------------\nALTER TABLE \"QRTZ_CALENDARS\" ADD CONSTRAINT \"QRTZ_CALENDARS_PK\" PRIMARY KEY (\"SCHED_NAME\", \"CALENDAR_NAME\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_CALENDARS\n-- ----------------------------\nALTER TABLE \"QRTZ_CALENDARS\" ADD CONSTRAINT \"SYS_C008271\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CALENDARS\" ADD CONSTRAINT \"SYS_C008272\" CHECK (\"CALENDAR_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CALENDARS\" ADD CONSTRAINT \"SYS_C008273\" CHECK (\"CALENDAR\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CALENDARS\" ADD CONSTRAINT \"SYS_C008656\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CALENDARS\" ADD CONSTRAINT \"SYS_C008657\" CHECK (\"CALENDAR_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CALENDARS\" ADD CONSTRAINT \"SYS_C008658\" CHECK (\"CALENDAR\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_CRON_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"QRTZ_CRON_TRIG_PK\" PRIMARY KEY (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_CRON_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"SYS_C008255\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"SYS_C008256\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"SYS_C008257\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"SYS_C008258\" CHECK (\"CRON_EXPRESSION\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"SYS_C008659\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"SYS_C008660\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"SYS_C008661\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"SYS_C008662\" CHECK (\"CRON_EXPRESSION\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"QRTZ_FIRED_TRIGGER_PK\" PRIMARY KEY (\"SCHED_NAME\", \"ENTRY_ID\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008278\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008279\" CHECK (\"ENTRY_ID\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008280\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008281\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008282\" CHECK (\"INSTANCE_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008283\" CHECK (\"FIRED_TIME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008284\" CHECK (\"SCHED_TIME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008285\" CHECK (\"PRIORITY\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008286\" CHECK (\"STATE\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008663\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008664\" CHECK (\"ENTRY_ID\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008665\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008666\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008667\" CHECK (\"INSTANCE_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008668\" CHECK (\"FIRED_TIME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008669\" CHECK (\"SCHED_TIME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008670\" CHECK (\"PRIORITY\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_FIRED_TRIGGERS\" ADD CONSTRAINT \"SYS_C008671\" CHECK (\"STATE\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Indexes structure for table QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nCREATE INDEX \"IDX_QRTZ_FT_INST_JOB_REQ_RCVRY\"\n    ON \"QRTZ_FIRED_TRIGGERS\" (\"SCHED_NAME\" ASC, \"INSTANCE_NAME\" ASC, \"REQUESTS_RECOVERY\" ASC)\n    LOGGING\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_FT_JG\"\n    ON \"QRTZ_FIRED_TRIGGERS\" (\"SCHED_NAME\" ASC, \"JOB_GROUP\" ASC)\n    LOGGING\n  ONLINE\n  NOSORT\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_FT_J_G\"\n    ON \"QRTZ_FIRED_TRIGGERS\" (\"SCHED_NAME\" ASC, \"JOB_NAME\" ASC, \"JOB_GROUP\" ASC)\n    LOGGING\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_FT_TG\"\n    ON \"QRTZ_FIRED_TRIGGERS\" (\"SCHED_NAME\" ASC, \"TRIGGER_GROUP\" ASC) LOCAL\n  LOGGING\n  NOSORT\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_JOB_DETAILS\n-- ----------------------------\nALTER TABLE \"QRTZ_JOB_DETAILS\" ADD CONSTRAINT \"QRTZ_JOB_DETAILS_PK\" PRIMARY KEY (\"SCHED_NAME\", \"JOB_NAME\", \"JOB_GROUP\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_JOB_DETAILS\n-- ----------------------------\nALTER TABLE \"QRTZ_JOB_DETAILS\" ADD CONSTRAINT \"SYS_C008228\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_JOB_DETAILS\" ADD CONSTRAINT \"SYS_C008229\" CHECK (\"JOB_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_JOB_DETAILS\" ADD CONSTRAINT \"SYS_C008230\" CHECK (\"JOB_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_JOB_DETAILS\" ADD CONSTRAINT \"SYS_C008231\" CHECK (\"JOB_CLASS_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_JOB_DETAILS\" ADD CONSTRAINT \"SYS_C008232\" CHECK (\"IS_DURABLE\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_JOB_DETAILS\" ADD CONSTRAINT \"SYS_C008233\" CHECK (\"IS_NONCONCURRENT\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_JOB_DETAILS\" ADD CONSTRAINT \"SYS_C008234\" CHECK (\"IS_UPDATE_DATA\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_JOB_DETAILS\" ADD CONSTRAINT \"SYS_C008235\" CHECK (\"REQUESTS_RECOVERY\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Indexes structure for table QRTZ_JOB_DETAILS\n-- ----------------------------\nCREATE INDEX \"IDX_QRTZ_J_GRP\"\n    ON \"QRTZ_JOB_DETAILS\" (\"SCHED_NAME\" ASC, \"JOB_GROUP\" ASC)\n    LOGGING\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_J_REQ_RECOVERY\"\n    ON \"QRTZ_JOB_DETAILS\" (\"SCHED_NAME\" ASC, \"REQUESTS_RECOVERY\" ASC) LOCAL\n  LOGGING\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_LOCKS\n-- ----------------------------\nALTER TABLE \"QRTZ_LOCKS\" ADD CONSTRAINT \"QRTZ_LOCKS_PK\" PRIMARY KEY (\"SCHED_NAME\", \"LOCK_NAME\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_LOCKS\n-- ----------------------------\nALTER TABLE \"QRTZ_LOCKS\" ADD CONSTRAINT \"SYS_C008293\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_LOCKS\" ADD CONSTRAINT \"SYS_C008294\" CHECK (\"LOCK_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_LOCKS\" ADD CONSTRAINT \"SYS_C008672\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_LOCKS\" ADD CONSTRAINT \"SYS_C008673\" CHECK (\"LOCK_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_PAUSED_TRIGGER_GRPS\n-- ----------------------------\nALTER TABLE \"QRTZ_PAUSED_TRIGGER_GRPS\" ADD CONSTRAINT \"QRTZ_PAUSED_TRIG_GRPS_PK\" PRIMARY KEY (\"SCHED_NAME\", \"TRIGGER_GROUP\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_PAUSED_TRIGGER_GRPS\n-- ----------------------------\nALTER TABLE \"QRTZ_PAUSED_TRIGGER_GRPS\" ADD CONSTRAINT \"SYS_C008275\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_PAUSED_TRIGGER_GRPS\" ADD CONSTRAINT \"SYS_C008276\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_PAUSED_TRIGGER_GRPS\" ADD CONSTRAINT \"SYS_C008674\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_PAUSED_TRIGGER_GRPS\" ADD CONSTRAINT \"SYS_C008675\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_SCHEDULER_STATE\n-- ----------------------------\nALTER TABLE \"QRTZ_SCHEDULER_STATE\" ADD CONSTRAINT \"QRTZ_SCHEDULER_STATE_PK\" PRIMARY KEY (\"SCHED_NAME\", \"INSTANCE_NAME\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_SCHEDULER_STATE\n-- ----------------------------\nALTER TABLE \"QRTZ_SCHEDULER_STATE\" ADD CONSTRAINT \"SYS_C008288\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SCHEDULER_STATE\" ADD CONSTRAINT \"SYS_C008289\" CHECK (\"INSTANCE_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SCHEDULER_STATE\" ADD CONSTRAINT \"SYS_C008290\" CHECK (\"LAST_CHECKIN_TIME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SCHEDULER_STATE\" ADD CONSTRAINT \"SYS_C008291\" CHECK (\"CHECKIN_INTERVAL\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SCHEDULER_STATE\" ADD CONSTRAINT \"SYS_C008676\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SCHEDULER_STATE\" ADD CONSTRAINT \"SYS_C008677\" CHECK (\"INSTANCE_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SCHEDULER_STATE\" ADD CONSTRAINT \"SYS_C008678\" CHECK (\"LAST_CHECKIN_TIME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SCHEDULER_STATE\" ADD CONSTRAINT \"SYS_C008679\" CHECK (\"CHECKIN_INTERVAL\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"QRTZ_SIMPLE_TRIG_PK\" PRIMARY KEY (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008247\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008248\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008249\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008250\" CHECK (\"REPEAT_COUNT\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008251\" CHECK (\"REPEAT_INTERVAL\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008252\" CHECK (\"TIMES_TRIGGERED\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008680\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008681\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008682\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008683\" CHECK (\"REPEAT_COUNT\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008684\" CHECK (\"REPEAT_INTERVAL\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"SYS_C008685\" CHECK (\"TIMES_TRIGGERED\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_SIMPROP_TRIGGERS\" ADD CONSTRAINT \"QRTZ_SIMPROP_TRIG_PK\" PRIMARY KEY (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_SIMPROP_TRIGGERS\" ADD CONSTRAINT \"SYS_C008261\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPROP_TRIGGERS\" ADD CONSTRAINT \"SYS_C008262\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPROP_TRIGGERS\" ADD CONSTRAINT \"SYS_C008263\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPROP_TRIGGERS\" ADD CONSTRAINT \"SYS_C008686\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPROP_TRIGGERS\" ADD CONSTRAINT \"SYS_C008687\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_SIMPROP_TRIGGERS\" ADD CONSTRAINT \"SYS_C008688\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"QRTZ_TRIGGERS_PK\" PRIMARY KEY (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\");\n\n-- ----------------------------\n-- Checks structure for table QRTZ_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008237\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008238\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008239\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008240\" CHECK (\"JOB_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008241\" CHECK (\"JOB_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008242\" CHECK (\"TRIGGER_STATE\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008243\" CHECK (\"TRIGGER_TYPE\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008244\" CHECK (\"START_TIME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008689\" CHECK (\"SCHED_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008690\" CHECK (\"TRIGGER_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008691\" CHECK (\"TRIGGER_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008692\" CHECK (\"JOB_NAME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008693\" CHECK (\"JOB_GROUP\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008694\" CHECK (\"TRIGGER_STATE\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008695\" CHECK (\"TRIGGER_TYPE\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\nALTER TABLE \"QRTZ_TRIGGERS\" ADD CONSTRAINT \"SYS_C008696\" CHECK (\"START_TIME\" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Indexes structure for table QRTZ_TRIGGERS\n-- ----------------------------\nCREATE INDEX \"IDX_QRTZ_T_C\"\n    ON \"QRTZ_TRIGGERS\" (\"SCHED_NAME\" ASC, \"CALENDAR_NAME\" ASC) LOCAL\n  LOGGING\n  ONLINE\n  NOSORT\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_T_J\"\n    ON \"QRTZ_TRIGGERS\" (\"SCHED_NAME\" ASC, \"JOB_NAME\" ASC, \"JOB_GROUP\" ASC)\n    LOGGING\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_T_JG\"\n    ON \"QRTZ_TRIGGERS\" (\"SCHED_NAME\" ASC, \"JOB_GROUP\" ASC) LOCAL\n  LOGGING\n  ONLINE\n  NOSORT\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_T_NEXT_FIRE_TIME\"\n    ON \"QRTZ_TRIGGERS\" (\"SCHED_NAME\" ASC, \"NEXT_FIRE_TIME\" ASC)\n    LOGGING\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_T_NFT_ST\"\n    ON \"QRTZ_TRIGGERS\" (\"SCHED_NAME\" ASC, \"TRIGGER_STATE\" ASC, \"NEXT_FIRE_TIME\" ASC) LOCAL\n  LOGGING\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_T_NFT_ST_MISFIRE\"\n    ON \"QRTZ_TRIGGERS\" (\"SCHED_NAME\" ASC, \"MISFIRE_INSTR\" ASC, \"NEXT_FIRE_TIME\" ASC, \"TRIGGER_STATE\" ASC) LOCAL\n  LOGGING\n  ONLINE\n  NOSORT\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\nCREATE INDEX \"IDX_QRTZ_T_STATE\"\n    ON \"QRTZ_TRIGGERS\" (\"SCHED_NAME\" ASC, \"TRIGGER_STATE\" ASC)\n    LOGGING\n  VISIBLE\nPCTFREE 10\nINITRANS 2\nSTORAGE (\n  INITIAL 65536\n  NEXT 1048576\n  MINEXTENTS 1\n  MAXEXTENTS 2147483645\n  FREELISTS 1\n  FREELIST GROUPS 1\n  BUFFER_POOL DEFAULT\n);\n\n-- ----------------------------\n-- Foreign Keys structure for table QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_BLOB_TRIGGERS\" ADD CONSTRAINT \"QRTZ_BLOB_TRIG_TO_TRIG_FK\" FOREIGN KEY (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\") REFERENCES \"QRTZ_TRIGGERS\" (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Foreign Keys structure for table QRTZ_CRON_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_CRON_TRIGGERS\" ADD CONSTRAINT \"QRTZ_CRON_TRIG_TO_TRIG_FK\" FOREIGN KEY (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\") REFERENCES \"QRTZ_TRIGGERS\" (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Foreign Keys structure for table QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_SIMPLE_TRIGGERS\" ADD CONSTRAINT \"QRTZ_SIMPLE_TRIG_TO_TRIG_FK\" FOREIGN KEY (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\") REFERENCES \"QRTZ_TRIGGERS\" (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;\n\n-- ----------------------------\n-- Foreign Keys structure for table QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nALTER TABLE \"QRTZ_SIMPROP_TRIGGERS\" ADD CONSTRAINT \"QRTZ_SIMPROP_TRIG_TO_TRIG_FK\" FOREIGN KEY (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\") REFERENCES \"QRTZ_TRIGGERS\" (\"SCHED_NAME\", \"TRIGGER_NAME\", \"TRIGGER_GROUP\") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;"
  },
  {
    "path": "sql/oracle/ruoyi-vue-pro.sql",
    "content": "/*\n Yudao Database Transfer Tool\n\n Source Server Type    : MySQL\n\n Target Server Type    : Oracle\n\n Date: 2025-05-22 21:48:09\n*/\n\n\n-- ----------------------------\n-- Table structure for infra_api_access_log\n-- ----------------------------\nCREATE TABLE infra_api_access_log\n(\n    id               number                                  NOT NULL,\n    trace_id         varchar2(64)  DEFAULT ''                NULL,\n    user_id          number        DEFAULT 0                 NOT NULL,\n    user_type        smallint      DEFAULT 0                 NOT NULL,\n    application_name varchar2(50)                            NULL,\n    request_method   varchar2(16)  DEFAULT ''                NULL,\n    request_url      varchar2(255) DEFAULT ''                NULL,\n    request_params   clob                                    NULL,\n    response_body    clob                                    NULL,\n    user_ip          varchar2(50)                            NULL,\n    user_agent       varchar2(512)                           NULL,\n    operate_module   varchar2(50)  DEFAULT NULL              NULL,\n    operate_name     varchar2(50)  DEFAULT NULL              NULL,\n    operate_type     smallint      DEFAULT 0                 NULL,\n    begin_time       date                                    NOT NULL,\n    end_time         date                                    NOT NULL,\n    duration         number                                  NOT NULL,\n    result_code      number        DEFAULT 0                 NOT NULL,\n    result_msg       varchar2(512) DEFAULT ''                NULL,\n    creator          varchar2(64)  DEFAULT ''                NULL,\n    create_time      date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater          varchar2(64)  DEFAULT ''                NULL,\n    update_time      date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted          number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id        number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_api_access_log\n    ADD CONSTRAINT pk_infra_api_access_log PRIMARY KEY (id);\n\nCREATE INDEX idx_infra_api_access_log_01 ON infra_api_access_log (create_time);\n\nCOMMENT ON COLUMN infra_api_access_log.id IS '日志主键';\nCOMMENT ON COLUMN infra_api_access_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_access_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_access_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_access_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_access_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_access_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_access_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_access_log.response_body IS '响应结果';\nCOMMENT ON COLUMN infra_api_access_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_access_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_access_log.operate_module IS '操作模块';\nCOMMENT ON COLUMN infra_api_access_log.operate_name IS '操作名';\nCOMMENT ON COLUMN infra_api_access_log.operate_type IS '操作分类';\nCOMMENT ON COLUMN infra_api_access_log.begin_time IS '开始请求时间';\nCOMMENT ON COLUMN infra_api_access_log.end_time IS '结束请求时间';\nCOMMENT ON COLUMN infra_api_access_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_api_access_log.result_code IS '结果码';\nCOMMENT ON COLUMN infra_api_access_log.result_msg IS '结果提示';\nCOMMENT ON COLUMN infra_api_access_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_access_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_access_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_access_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_access_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_access_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_access_log IS 'API 访问日志表';\n\nCREATE SEQUENCE infra_api_access_log_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for infra_api_error_log\n-- ----------------------------\nCREATE TABLE infra_api_error_log\n(\n    id                           number                                  NOT NULL,\n    trace_id                     varchar2(64)                            NULL,\n    user_id                      number        DEFAULT 0                 NOT NULL,\n    user_type                    smallint      DEFAULT 0                 NOT NULL,\n    application_name             varchar2(50)                            NULL,\n    request_method               varchar2(16)                            NULL,\n    request_url                  varchar2(255)                           NULL,\n    request_params               varchar2(4000)                          NULL,\n    user_ip                      varchar2(50)                            NULL,\n    user_agent                   varchar2(512)                           NULL,\n    exception_time               date                                    NOT NULL,\n    exception_name               varchar2(128) DEFAULT ''                NULL,\n    exception_message            clob                                    NULL,\n    exception_root_cause_message clob                                    NULL,\n    exception_stack_trace        clob                                    NULL,\n    exception_class_name         varchar2(512)                           NULL,\n    exception_file_name          varchar2(512)                           NULL,\n    exception_method_name        varchar2(512)                           NULL,\n    exception_line_number        number                                  NOT NULL,\n    process_status               smallint                                NOT NULL,\n    process_time                 date          DEFAULT NULL              NULL,\n    process_user_id              number        DEFAULT 0                 NULL,\n    creator                      varchar2(64)  DEFAULT ''                NULL,\n    create_time                  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater                      varchar2(64)  DEFAULT ''                NULL,\n    update_time                  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted                      number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id                    number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_api_error_log\n    ADD CONSTRAINT pk_infra_api_error_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_api_error_log.id IS '编号';\nCOMMENT ON COLUMN infra_api_error_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_error_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_error_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_error_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_error_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_error_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_error_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_error_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_error_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_error_log.exception_time IS '异常发生时间';\nCOMMENT ON COLUMN infra_api_error_log.exception_name IS '异常名';\nCOMMENT ON COLUMN infra_api_error_log.exception_message IS '异常导致的消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_root_cause_message IS '异常导致的根消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_stack_trace IS '异常的栈轨迹';\nCOMMENT ON COLUMN infra_api_error_log.exception_class_name IS '异常发生的类全名';\nCOMMENT ON COLUMN infra_api_error_log.exception_file_name IS '异常发生的类文件';\nCOMMENT ON COLUMN infra_api_error_log.exception_method_name IS '异常发生的方法名';\nCOMMENT ON COLUMN infra_api_error_log.exception_line_number IS '异常发生的方法所在行';\nCOMMENT ON COLUMN infra_api_error_log.process_status IS '处理状态';\nCOMMENT ON COLUMN infra_api_error_log.process_time IS '处理时间';\nCOMMENT ON COLUMN infra_api_error_log.process_user_id IS '处理用户编号';\nCOMMENT ON COLUMN infra_api_error_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_error_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_error_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_error_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_error_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_error_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_error_log IS '系统异常日志';\n\nCREATE SEQUENCE infra_api_error_log_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for infra_codegen_column\n-- ----------------------------\nCREATE TABLE infra_codegen_column\n(\n    id                       number                                  NOT NULL,\n    table_id                 number                                  NOT NULL,\n    column_name              varchar2(200)                           NULL,\n    data_type                varchar2(100)                           NULL,\n    column_comment           varchar2(500)                           NULL,\n    nullable                 number(1, 0)                            NOT NULL,\n    primary_key              number(1, 0)                            NOT NULL,\n    ordinal_position         number                                  NOT NULL,\n    java_type                varchar2(32)                            NULL,\n    java_field               varchar2(64)                            NULL,\n    dict_type                varchar2(200) DEFAULT ''                NULL,\n    example                  varchar2(64)  DEFAULT NULL              NULL,\n    create_operation         number(1, 0)                            NOT NULL,\n    update_operation         number(1, 0)                            NOT NULL,\n    list_operation           number(1, 0)                            NOT NULL,\n    list_operation_condition varchar2(32)  DEFAULT '='               NULL,\n    list_operation_result    number(1, 0)                            NOT NULL,\n    html_type                varchar2(32)                            NULL,\n    creator                  varchar2(64)  DEFAULT ''                NULL,\n    create_time              date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater                  varchar2(64)  DEFAULT ''                NULL,\n    update_time              date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted                  number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_codegen_column\n    ADD CONSTRAINT pk_infra_codegen_column PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_codegen_column.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_column.table_id IS '表编号';\nCOMMENT ON COLUMN infra_codegen_column.column_name IS '字段名';\nCOMMENT ON COLUMN infra_codegen_column.data_type IS '字段类型';\nCOMMENT ON COLUMN infra_codegen_column.column_comment IS '字段描述';\nCOMMENT ON COLUMN infra_codegen_column.nullable IS '是否允许为空';\nCOMMENT ON COLUMN infra_codegen_column.primary_key IS '是否主键';\nCOMMENT ON COLUMN infra_codegen_column.ordinal_position IS '排序';\nCOMMENT ON COLUMN infra_codegen_column.java_type IS 'Java 属性类型';\nCOMMENT ON COLUMN infra_codegen_column.java_field IS 'Java 属性名';\nCOMMENT ON COLUMN infra_codegen_column.dict_type IS '字典类型';\nCOMMENT ON COLUMN infra_codegen_column.example IS '数据示例';\nCOMMENT ON COLUMN infra_codegen_column.create_operation IS '是否为 Create 创建操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.update_operation IS '是否为 Update 更新操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation IS '是否为 List 查询操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_condition IS 'List 查询操作的条件类型';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_result IS '是否为 List 查询操作的返回字段';\nCOMMENT ON COLUMN infra_codegen_column.html_type IS '显示类型';\nCOMMENT ON COLUMN infra_codegen_column.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_column.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_column.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_column.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_column.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_column IS '代码生成表字段定义';\n\nCREATE SEQUENCE infra_codegen_column_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for infra_codegen_table\n-- ----------------------------\nCREATE TABLE infra_codegen_table\n(\n    id                    number                                  NOT NULL,\n    data_source_config_id number                                  NOT NULL,\n    scene                 smallint      DEFAULT 1                 NOT NULL,\n    table_name            varchar2(200) DEFAULT ''                NULL,\n    table_comment         varchar2(500) DEFAULT ''                NULL,\n    remark                varchar2(500) DEFAULT NULL              NULL,\n    module_name           varchar2(30)                            NULL,\n    business_name         varchar2(30)                            NULL,\n    class_name            varchar2(100) DEFAULT ''                NULL,\n    class_comment         varchar2(50)                            NULL,\n    author                varchar2(50)                            NULL,\n    template_type         smallint      DEFAULT 1                 NOT NULL,\n    front_type            smallint                                NOT NULL,\n    parent_menu_id        number        DEFAULT NULL              NULL,\n    master_table_id       number        DEFAULT NULL              NULL,\n    sub_join_column_id    number        DEFAULT NULL              NULL,\n    sub_join_many         number(1, 0)  DEFAULT NULL              NULL,\n    tree_parent_column_id number        DEFAULT NULL              NULL,\n    tree_name_column_id   number        DEFAULT NULL              NULL,\n    creator               varchar2(64)  DEFAULT ''                NULL,\n    create_time           date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater               varchar2(64)  DEFAULT ''                NULL,\n    update_time           date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted               number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_codegen_table\n    ADD CONSTRAINT pk_infra_codegen_table PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_codegen_table.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_table.data_source_config_id IS '数据源配置的编号';\nCOMMENT ON COLUMN infra_codegen_table.scene IS '生成场景';\nCOMMENT ON COLUMN infra_codegen_table.table_name IS '表名称';\nCOMMENT ON COLUMN infra_codegen_table.table_comment IS '表描述';\nCOMMENT ON COLUMN infra_codegen_table.remark IS '备注';\nCOMMENT ON COLUMN infra_codegen_table.module_name IS '模块名';\nCOMMENT ON COLUMN infra_codegen_table.business_name IS '业务名';\nCOMMENT ON COLUMN infra_codegen_table.class_name IS '类名称';\nCOMMENT ON COLUMN infra_codegen_table.class_comment IS '类描述';\nCOMMENT ON COLUMN infra_codegen_table.author IS '作者';\nCOMMENT ON COLUMN infra_codegen_table.template_type IS '模板类型';\nCOMMENT ON COLUMN infra_codegen_table.front_type IS '前端类型';\nCOMMENT ON COLUMN infra_codegen_table.parent_menu_id IS '父菜单编号';\nCOMMENT ON COLUMN infra_codegen_table.master_table_id IS '主表的编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_column_id IS '子表关联主表的字段编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_many IS '主表与子表是否一对多';\nCOMMENT ON COLUMN infra_codegen_table.tree_parent_column_id IS '树表的父字段编号';\nCOMMENT ON COLUMN infra_codegen_table.tree_name_column_id IS '树表的名字字段编号';\nCOMMENT ON COLUMN infra_codegen_table.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_table.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_table.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_table.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_table.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_table IS '代码生成表定义';\n\nCREATE SEQUENCE infra_codegen_table_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for infra_config\n-- ----------------------------\nCREATE TABLE infra_config\n(\n    id          number                                  NOT NULL,\n    category    varchar2(50)                            NULL,\n    type        smallint                                NOT NULL,\n    name        varchar2(100) DEFAULT ''                NULL,\n    config_key  varchar2(100) DEFAULT ''                NULL,\n    value       varchar2(500) DEFAULT ''                NULL,\n    visible     number(1, 0)                            NOT NULL,\n    remark      varchar2(500) DEFAULT NULL              NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_config\n    ADD CONSTRAINT pk_infra_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_config.id IS '参数主键';\nCOMMENT ON COLUMN infra_config.category IS '参数分组';\nCOMMENT ON COLUMN infra_config.type IS '参数类型';\nCOMMENT ON COLUMN infra_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_config.config_key IS '参数键名';\nCOMMENT ON COLUMN infra_config.value IS '参数键值';\nCOMMENT ON COLUMN infra_config.visible IS '是否可见';\nCOMMENT ON COLUMN infra_config.remark IS '备注';\nCOMMENT ON COLUMN infra_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_config IS '参数配置表';\n\n-- ----------------------------\n-- Records of infra_config\n-- ----------------------------\n-- @formatter:off\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'system.user.init-password', '123456', '0', '初始化密码 123456', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-20 17:22:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', '1', '', '1', to_date('2023-04-07 13:41:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-04-07 14:33:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', '1', '', '1', to_date('2023-04-07 13:41:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-04-07 14:57:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', '1', '', '1', to_date('2023-04-07 13:41:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-04-07 14:52:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (10, 'url', 2, 'Swagger 接口文档的地址', 'url.swagger', '', '1', '', '1', to_date('2023-04-07 13:41:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-04-07 14:59:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (11, 'ui', 2, '腾讯地图 key', 'tencent.lbs.key', 'TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E', '1', '腾讯地图 key', '1', to_date('2023-06-03 19:16:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-06-03 19:16:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 'test2', 2, 'test3', 'test4', 'test5', '1', 'test6', '1', to_date('2023-12-03 09:55:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-06 21:00:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '用户管理-账号初始密码', 2, '用户管理-注册开关', 'system.user.register-enabled', 'true', '0', '', '1', to_date('2025-04-26 17:23:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-26 17:23:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE infra_config_seq\n    START WITH 14;\n\n-- ----------------------------\n-- Table structure for infra_data_source_config\n-- ----------------------------\nCREATE TABLE infra_data_source_config\n(\n    id          number                                  NOT NULL,\n    name        varchar2(100) DEFAULT ''                NULL,\n    url         varchar2(1024)                          NULL,\n    username    varchar2(255)                           NULL,\n    password    varchar2(255) DEFAULT ''                NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_data_source_config\n    ADD CONSTRAINT pk_infra_data_source_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_data_source_config.id IS '主键编号';\nCOMMENT ON COLUMN infra_data_source_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_data_source_config.url IS '数据源连接';\nCOMMENT ON COLUMN infra_data_source_config.username IS '用户名';\nCOMMENT ON COLUMN infra_data_source_config.password IS '密码';\nCOMMENT ON COLUMN infra_data_source_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_data_source_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_data_source_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_data_source_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_data_source_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_data_source_config IS '数据源配置表';\n\nCREATE SEQUENCE infra_data_source_config_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for infra_file\n-- ----------------------------\nCREATE TABLE infra_file\n(\n    id          number                                  NOT NULL,\n    config_id   number        DEFAULT NULL              NULL,\n    name        varchar2(256) DEFAULT NULL              NULL,\n    path        varchar2(512)                           NULL,\n    url         varchar2(1024)                          NULL,\n    type        varchar2(128) DEFAULT NULL              NULL,\n    \"size\"      number                                  NOT NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_file\n    ADD CONSTRAINT pk_infra_file PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file.id IS '文件编号';\nCOMMENT ON COLUMN infra_file.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file.name IS '文件名';\nCOMMENT ON COLUMN infra_file.path IS '文件路径';\nCOMMENT ON COLUMN infra_file.url IS '文件 URL';\nCOMMENT ON COLUMN infra_file.type IS '文件类型';\nCOMMENT ON COLUMN infra_file.size IS '文件大小';\nCOMMENT ON COLUMN infra_file.creator IS '创建者';\nCOMMENT ON COLUMN infra_file.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file.updater IS '更新者';\nCOMMENT ON COLUMN infra_file.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file IS '文件表';\n\nCREATE SEQUENCE infra_file_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for infra_file_config\n-- ----------------------------\nCREATE TABLE infra_file_config\n(\n    id          number                                  NOT NULL,\n    name        varchar2(63)                            NULL,\n    storage     smallint                                NOT NULL,\n    remark      varchar2(255) DEFAULT NULL              NULL,\n    master      number(1, 0)                            NOT NULL,\n    config      varchar2(4000)                          NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_file_config\n    ADD CONSTRAINT pk_infra_file_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file_config.id IS '编号';\nCOMMENT ON COLUMN infra_file_config.name IS '配置名';\nCOMMENT ON COLUMN infra_file_config.storage IS '存储器';\nCOMMENT ON COLUMN infra_file_config.remark IS '备注';\nCOMMENT ON COLUMN infra_file_config.master IS '是否为主配置';\nCOMMENT ON COLUMN infra_file_config.config IS '存储配置';\nCOMMENT ON COLUMN infra_file_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_config IS '文件配置表';\n\n-- ----------------------------\n-- Records of infra_file_config\n-- ----------------------------\n-- @formatter:off\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (4, '数据库（示例）', 1, '我是数据库', '0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', to_date('2022-03-15 23:56:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:30:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (22, '七牛存储器（示例）', 20, '请换成你自己的密钥！！！', '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}', '1', to_date('2024-01-13 22:11:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:30:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (24, '腾讯云存储（示例）', 20, '请换成你的密钥！！！', '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\"}', '1', to_date('2024-11-09 16:03:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:30:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (25, '阿里云存储（示例）', 20, '', '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}', '1', to_date('2024-11-09 16:47:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:30:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (26, '火山云存储（示例）', 20, '', '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}', '1', to_date('2024-11-09 16:56:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:30:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (27, '华为云存储（示例）', 20, '', '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}', '1', to_date('2024-11-09 17:18:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:30:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (28, 'MinIO 存储（示例）', 20, '', '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}', '1', to_date('2024-11-09 17:43:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:30:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (29, '本地存储（示例）', 10, '仅适合 mac 或 windows', '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', to_date('2025-05-02 11:25:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:30:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (30, 'SFTP 存储（示例）', 12, '', '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', to_date('2025-05-02 16:34:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:30:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE infra_file_config_seq\n    START WITH 31;\n\n-- ----------------------------\n-- Table structure for infra_file_content\n-- ----------------------------\nCREATE TABLE infra_file_content\n(\n    id          number                                 NOT NULL,\n    config_id   number                                 NOT NULL,\n    path        varchar2(512)                          NULL,\n    content     blob                                   NOT NULL,\n    creator     varchar2(64) DEFAULT ''                NULL,\n    create_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64) DEFAULT ''                NULL,\n    update_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0) DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_file_content\n    ADD CONSTRAINT pk_infra_file_content PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file_content.id IS '编号';\nCOMMENT ON COLUMN infra_file_content.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file_content.path IS '文件路径';\nCOMMENT ON COLUMN infra_file_content.content IS '文件内容';\nCOMMENT ON COLUMN infra_file_content.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_content.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_content.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_content.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_content.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_content IS '文件表';\n\nCREATE SEQUENCE infra_file_content_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for infra_job\n-- ----------------------------\nCREATE TABLE infra_job\n(\n    id              number                                  NOT NULL,\n    name            varchar2(32)                            NULL,\n    status          smallint                                NOT NULL,\n    handler_name    varchar2(64)                            NULL,\n    handler_param   varchar2(255) DEFAULT NULL              NULL,\n    cron_expression varchar2(32)                            NULL,\n    retry_count     number        DEFAULT 0                 NOT NULL,\n    retry_interval  number        DEFAULT 0                 NOT NULL,\n    monitor_timeout number        DEFAULT 0                 NOT NULL,\n    creator         varchar2(64)  DEFAULT ''                NULL,\n    create_time     date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         varchar2(64)  DEFAULT ''                NULL,\n    update_time     date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_job\n    ADD CONSTRAINT pk_infra_job PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_job.id IS '任务编号';\nCOMMENT ON COLUMN infra_job.name IS '任务名称';\nCOMMENT ON COLUMN infra_job.status IS '任务状态';\nCOMMENT ON COLUMN infra_job.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job.cron_expression IS 'CRON 表达式';\nCOMMENT ON COLUMN infra_job.retry_count IS '重试次数';\nCOMMENT ON COLUMN infra_job.retry_interval IS '重试间隔';\nCOMMENT ON COLUMN infra_job.monitor_timeout IS '监控超时时间';\nCOMMENT ON COLUMN infra_job.creator IS '创建者';\nCOMMENT ON COLUMN infra_job.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job.updater IS '更新者';\nCOMMENT ON COLUMN infra_job.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job IS '定时任务表';\n\n-- ----------------------------\n-- Records of infra_job\n-- ----------------------------\n-- @formatter:off\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (5, '支付通知 Job', 2, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', to_date('2021-10-27 08:34:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-12 13:32:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (17, '支付订单同步 Job', 2, 'payOrderSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', to_date('2023-07-22 14:36:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-22 15:39:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (18, '支付订单过期 Job', 2, 'payOrderExpireJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', to_date('2023-07-22 15:36:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-22 15:39:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (19, '退款订单的同步 Job', 2, 'payRefundSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', to_date('2023-07-23 21:03:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-23 21:09:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (21, '交易订单的自动过期 Job', 2, 'tradeOrderAutoCancelJob', '', '0 * * * * ?', 3, 0, 0, '1', to_date('2023-09-25 23:43:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-26 19:23:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (22, '交易订单的自动收货 Job', 2, 'tradeOrderAutoReceiveJob', '', '0 * * * * ?', 3, 0, 0, '1', to_date('2023-09-26 19:23:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-26 23:38:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (23, '交易订单的自动评论 Job', 2, 'tradeOrderAutoCommentJob', '', '0 * * * * ?', 3, 0, 0, '1', to_date('2023-09-26 23:38:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-27 11:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (24, '佣金解冻 Job', 2, 'brokerageRecordUnfreezeJob', '', '0 * * * * ?', 3, 0, 0, '1', to_date('2023-09-28 22:01:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-28 22:01:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (25, '访问日志清理 Job', 2, 'accessLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', to_date('2023-10-03 10:59:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-03 11:01:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (26, '错误日志清理 Job', 2, 'errorLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', to_date('2023-10-03 11:00:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-03 11:01:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (27, '任务日志清理 Job', 2, 'jobLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', to_date('2023-10-03 11:01:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-12 13:40:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (33, 'demoJob', 2, 'demoJob', '', '0 * * * * ?', 1, 1, 0, '1', to_date('2024-10-27 19:38:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-10 18:13:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (35, '转账订单的同步 Job', 2, 'payTransferSyncJob', '', '0 * * * * ?', 0, 0, 0, '1', to_date('2025-05-10 17:35:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-10 18:13:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE infra_job_seq\n    START WITH 36;\n\n-- ----------------------------\n-- Table structure for infra_job_log\n-- ----------------------------\nCREATE TABLE infra_job_log\n(\n    id            number                                   NOT NULL,\n    job_id        number                                   NOT NULL,\n    handler_name  varchar2(64)                             NULL,\n    handler_param varchar2(255)  DEFAULT NULL              NULL,\n    execute_index smallint       DEFAULT 1                 NOT NULL,\n    begin_time    date                                     NOT NULL,\n    end_time      date           DEFAULT NULL              NULL,\n    duration      number         DEFAULT NULL              NULL,\n    status        smallint                                 NOT NULL,\n    result        varchar2(4000) DEFAULT ''                NULL,\n    creator       varchar2(64)   DEFAULT ''                NULL,\n    create_time   date           DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       varchar2(64)   DEFAULT ''                NULL,\n    update_time   date           DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       number(1, 0)   DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE infra_job_log\n    ADD CONSTRAINT pk_infra_job_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_job_log.id IS '日志编号';\nCOMMENT ON COLUMN infra_job_log.job_id IS '任务编号';\nCOMMENT ON COLUMN infra_job_log.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job_log.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job_log.execute_index IS '第几次执行';\nCOMMENT ON COLUMN infra_job_log.begin_time IS '开始执行时间';\nCOMMENT ON COLUMN infra_job_log.end_time IS '结束执行时间';\nCOMMENT ON COLUMN infra_job_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_job_log.status IS '任务状态';\nCOMMENT ON COLUMN infra_job_log.result IS '结果数据';\nCOMMENT ON COLUMN infra_job_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_job_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_job_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job_log.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job_log IS '定时任务日志表';\n\nCREATE SEQUENCE infra_job_log_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_dept\n-- ----------------------------\nCREATE TABLE system_dept\n(\n    id             number                                 NOT NULL,\n    name           varchar2(30) DEFAULT ''                NULL,\n    parent_id      number       DEFAULT 0                 NOT NULL,\n    sort           number       DEFAULT 0                 NOT NULL,\n    leader_user_id number       DEFAULT NULL              NULL,\n    phone          varchar2(11) DEFAULT NULL              NULL,\n    email          varchar2(50) DEFAULT NULL              NULL,\n    status         smallint                               NOT NULL,\n    creator        varchar2(64) DEFAULT ''                NULL,\n    create_time    date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar2(64) DEFAULT ''                NULL,\n    update_time    date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        number(1, 0) DEFAULT 0                 NOT NULL,\n    tenant_id      number       DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_dept\n    ADD CONSTRAINT pk_system_dept PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dept.id IS '部门id';\nCOMMENT ON COLUMN system_dept.name IS '部门名称';\nCOMMENT ON COLUMN system_dept.parent_id IS '父部门id';\nCOMMENT ON COLUMN system_dept.sort IS '显示顺序';\nCOMMENT ON COLUMN system_dept.leader_user_id IS '负责人';\nCOMMENT ON COLUMN system_dept.phone IS '联系电话';\nCOMMENT ON COLUMN system_dept.email IS '邮箱';\nCOMMENT ON COLUMN system_dept.status IS '部门状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dept.creator IS '创建者';\nCOMMENT ON COLUMN system_dept.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dept.updater IS '更新者';\nCOMMENT ON COLUMN system_dept.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dept.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dept.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_dept IS '部门表';\n\n-- ----------------------------\n-- Records of system_dept\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, '芋道源码', 0, 0, 1, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-29 15:47:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (101, '深圳总公司', 100, 1, 104, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-29 15:49:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (102, '长沙分公司', 100, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2021-12-15 05:01:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, '研发部门', 101, 1, 1, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-10-02 10:22:03', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, '市场部门', 101, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2021-12-15 05:01:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (105, '测试部门', 101, 3, NULL, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-16 20:25:15', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (106, '财务部门', 101, 4, 103, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '103', to_date('2022-01-15 21:32:22', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (107, '运维部门', 101, 5, 1, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 09:28:22', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (108, '市场部门', 102, 1, NULL, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 08:35:45', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (109, '财务部门', 102, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2021-12-15 05:01:29', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (110, '新部门', 0, 1, NULL, NULL, NULL, 0, '110', to_date('2022-02-23 20:46:30', 'SYYYY-MM-DD HH24:MI:SS'), '110', to_date('2022-02-23 20:46:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (111, '顶级部门', 0, 1, NULL, NULL, NULL, 0, '113', to_date('2022-03-07 21:44:50', 'SYYYY-MM-DD HH24:MI:SS'), '113', to_date('2022-03-07 21:44:50', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, '产品部门', 101, 100, 1, NULL, NULL, 1, '1', to_date('2023-12-02 09:45:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 09:45:31', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, '支持部门', 102, 3, 104, NULL, NULL, 1, '1', to_date('2023-12-02 09:47:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-29 15:00:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_dept_seq\n    START WITH 114;\n\n-- ----------------------------\n-- Table structure for system_dict_data\n-- ----------------------------\nCREATE TABLE system_dict_data\n(\n    id          number                                  NOT NULL,\n    sort        number        DEFAULT 0                 NOT NULL,\n    label       varchar2(100) DEFAULT ''                NULL,\n    value       varchar2(100) DEFAULT ''                NULL,\n    dict_type   varchar2(100) DEFAULT ''                NULL,\n    status      smallint      DEFAULT 0                 NOT NULL,\n    color_type  varchar2(100) DEFAULT ''                NULL,\n    css_class   varchar2(100) DEFAULT ''                NULL,\n    remark      varchar2(500) DEFAULT NULL              NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_dict_data\n    ADD CONSTRAINT pk_system_dict_data PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dict_data.id IS '字典编码';\nCOMMENT ON COLUMN system_dict_data.sort IS '字典排序';\nCOMMENT ON COLUMN system_dict_data.label IS '字典标签';\nCOMMENT ON COLUMN system_dict_data.value IS '字典键值';\nCOMMENT ON COLUMN system_dict_data.dict_type IS '字典类型';\nCOMMENT ON COLUMN system_dict_data.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_data.color_type IS '颜色类型';\nCOMMENT ON COLUMN system_dict_data.css_class IS 'css 样式';\nCOMMENT ON COLUMN system_dict_data.remark IS '备注';\nCOMMENT ON COLUMN system_dict_data.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_data.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_data.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_data.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_data.deleted IS '是否删除';\nCOMMENT ON TABLE system_dict_data IS '字典数据表';\n\n-- ----------------------------\n-- Records of system_dict_data\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1, 1, '男', '1', 'system_user_sex', 0, 'default', 'A', '性别男', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-29 00:14:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 2, '女', '2', 'system_user_sex', 0, 'success', '', '性别女', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-15 23:30:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 1, '正常', '1', 'infra_job_status', 0, 'success', '', '正常状态', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 19:33:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 2, '暂停', '2', 'infra_job_status', 0, 'danger', '', '停用状态', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 19:33:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 1, '系统内置', '1', 'infra_config_type', 0, 'danger', '', '参数类型 - 系统内置', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 19:06:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (13, 2, '自定义', '2', 'infra_config_type', 0, 'primary', '', '参数类型 - 自定义', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 19:06:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (14, 1, '通知', '1', 'system_notice_type', 0, 'success', '', '通知', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:05:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (15, 2, '公告', '2', 'system_notice_type', 0, 'info', '', '公告', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:06:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (16, 0, '其它', '0', 'infra_operate_type', 0, 'default', '', '其它操作', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-14 12:44:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (17, 1, '查询', '1', 'infra_operate_type', 0, 'info', '', '查询操作', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-14 12:44:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (18, 2, '新增', '2', 'infra_operate_type', 0, 'primary', '', '新增操作', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-14 12:44:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (19, 3, '修改', '3', 'infra_operate_type', 0, 'warning', '', '修改操作', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-14 12:44:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (20, 4, '删除', '4', 'infra_operate_type', 0, 'danger', '', '删除操作', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-14 12:44:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (22, 5, '导出', '5', 'infra_operate_type', 0, 'default', '', '导出操作', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-14 12:44:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (23, 6, '导入', '6', 'infra_operate_type', 0, 'default', '', '导入操作', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-14 12:44:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (27, 1, '开启', '0', 'common_status', 0, 'primary', '', '开启状态', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 08:00:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (28, 2, '关闭', '1', 'common_status', 0, 'info', '', '关闭状态', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 08:00:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (29, 1, '目录', '1', 'system_menu_type', 0, '', '', '目录', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:43:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (30, 2, '菜单', '2', 'system_menu_type', 0, '', '', '菜单', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:43:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (31, 3, '按钮', '3', 'system_menu_type', 0, '', '', '按钮', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:43:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (32, 1, '内置', '1', 'system_role_type', 0, 'danger', '', '内置角色', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:02:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (33, 2, '自定义', '2', 'system_role_type', 0, 'primary', '', '自定义角色', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:02:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (34, 1, '全部数据权限', '1', 'system_data_scope', 0, '', '', '全部数据权限', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:47:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (35, 2, '指定部门数据权限', '2', 'system_data_scope', 0, '', '', '指定部门数据权限', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:47:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (36, 3, '本部门数据权限', '3', 'system_data_scope', 0, '', '', '本部门数据权限', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:47:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (37, 4, '本部门及以下数据权限', '4', 'system_data_scope', 0, '', '', '本部门及以下数据权限', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:47:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (38, 5, '仅本人数据权限', '5', 'system_data_scope', 0, '', '', '仅本人数据权限', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:47:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (39, 0, '成功', '0', 'system_login_result', 0, 'success', '', '登陆结果 - 成功', '', to_date('2021-01-18 06:17:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:23:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (40, 10, '账号或密码不正确', '10', 'system_login_result', 0, 'primary', '', '登陆结果 - 账号或密码不正确', '', to_date('2021-01-18 06:17:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:24:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (41, 20, '用户被禁用', '20', 'system_login_result', 0, 'warning', '', '登陆结果 - 用户被禁用', '', to_date('2021-01-18 06:17:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:23:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (42, 30, '验证码不存在', '30', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不存在', '', to_date('2021-01-18 06:17:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:24:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (43, 31, '验证码不正确', '31', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不正确', '', to_date('2021-01-18 06:17:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:24:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (44, 100, '未知异常', '100', 'system_login_result', 0, 'danger', '', '登陆结果 - 未知异常', '', to_date('2021-01-18 06:17:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:24:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (45, 1, '是', 'true', 'infra_boolean_string', 0, 'danger', '', 'Boolean 是否类型 - 是', '', to_date('2021-01-19 03:20:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-15 23:01:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (46, 1, '否', 'false', 'infra_boolean_string', 0, 'info', '', 'Boolean 是否类型 - 否', '', to_date('2021-01-19 03:20:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-15 23:09:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (50, 1, '单表（增删改查）', '1', 'infra_codegen_template_type', 0, '', '', NULL, '', to_date('2021-02-05 07:09:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-03-10 16:33:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (51, 2, '树表（增删改查）', '2', 'infra_codegen_template_type', 0, '', '', NULL, '', to_date('2021-02-05 07:14:46', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-03-10 16:33:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (53, 0, '初始化中', '0', 'infra_job_status', 0, 'primary', '', NULL, '', to_date('2021-02-07 07:46:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 19:33:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (57, 0, '运行中', '0', 'infra_job_log_status', 0, 'primary', '', 'RUNNING', '', to_date('2021-02-08 10:04:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 19:07:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (58, 1, '成功', '1', 'infra_job_log_status', 0, 'success', '', NULL, '', to_date('2021-02-08 10:06:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 19:07:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (59, 2, '失败', '2', 'infra_job_log_status', 0, 'warning', '', '失败', '', to_date('2021-02-08 10:07:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 19:07:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (60, 1, '会员', '1', 'user_type', 0, 'primary', '', NULL, '', to_date('2021-02-26 00:16:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:22:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (61, 2, '管理员', '2', 'user_type', 0, 'success', '', NULL, '', to_date('2021-02-26 00:16:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-06 18:37:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (62, 0, '未处理', '0', 'infra_api_error_log_process_status', 0, 'primary', '', NULL, '', to_date('2021-02-26 07:07:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 20:14:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (63, 1, '已处理', '1', 'infra_api_error_log_process_status', 0, 'success', '', NULL, '', to_date('2021-02-26 07:07:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 20:14:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (64, 2, '已忽略', '2', 'infra_api_error_log_process_status', 0, 'danger', '', NULL, '', to_date('2021-02-26 07:07:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 20:14:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (66, 1, '阿里云', 'ALIYUN', 'system_sms_channel_code', 0, 'primary', '', NULL, '1', to_date('2021-04-05 01:05:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-22 22:23:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (67, 1, '验证码', '1', 'system_sms_template_type', 0, 'warning', '', NULL, '1', to_date('2021-04-05 21:50:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 12:48:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (68, 2, '通知', '2', 'system_sms_template_type', 0, 'primary', '', NULL, '1', to_date('2021-04-05 21:51:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 12:48:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (69, 0, '营销', '3', 'system_sms_template_type', 0, 'danger', '', NULL, '1', to_date('2021-04-05 21:51:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 12:48:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (70, 0, '初始化', '0', 'system_sms_send_status', 0, 'primary', '', NULL, '1', to_date('2021-04-11 20:18:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:26:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (71, 1, '发送成功', '10', 'system_sms_send_status', 0, 'success', '', NULL, '1', to_date('2021-04-11 20:18:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:25:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (72, 2, '发送失败', '20', 'system_sms_send_status', 0, 'danger', '', NULL, '1', to_date('2021-04-11 20:18:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:26:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (73, 3, '不发送', '30', 'system_sms_send_status', 0, 'info', '', NULL, '1', to_date('2021-04-11 20:19:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:26:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (74, 0, '等待结果', '0', 'system_sms_receive_status', 0, 'primary', '', NULL, '1', to_date('2021-04-11 20:27:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:28:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (75, 1, '接收成功', '10', 'system_sms_receive_status', 0, 'success', '', NULL, '1', to_date('2021-04-11 20:29:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:28:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (76, 2, '接收失败', '20', 'system_sms_receive_status', 0, 'danger', '', NULL, '1', to_date('2021-04-11 20:29:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:28:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (77, 0, '调试(钉钉)', 'DEBUG_DING_TALK', 'system_sms_channel_code', 0, 'info', '', NULL, '1', to_date('2021-04-13 00:20:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:10:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (80, 100, '账号登录', '100', 'system_login_type', 0, 'primary', '', '账号登录', '1', to_date('2021-10-06 00:52:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:11:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (81, 101, '社交登录', '101', 'system_login_type', 0, 'info', '', '社交登录', '1', to_date('2021-10-06 00:52:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:11:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (83, 200, '主动登出', '200', 'system_login_type', 0, 'primary', '', '主动登出', '1', to_date('2021-10-06 00:52:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:11:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (85, 202, '强制登出', '202', 'system_login_type', 0, 'danger', '', '强制退出', '1', to_date('2021-10-06 00:53:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:11:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (86, 0, '病假', '1', 'bpm_oa_leave_type', 0, 'primary', '', NULL, '1', to_date('2021-09-21 22:35:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:00:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (87, 1, '事假', '2', 'bpm_oa_leave_type', 0, 'info', '', NULL, '1', to_date('2021-09-21 22:36:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:00:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (88, 2, '婚假', '3', 'bpm_oa_leave_type', 0, 'warning', '', NULL, '1', to_date('2021-09-21 22:36:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 10:00:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (112, 0, '微信 Wap 网站支付', 'wx_wap', 'pay_channel_code', 0, 'success', '', '微信 Wap 网站支付', '1', to_date('2023-07-19 20:08:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:09:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (113, 1, '微信公众号支付', 'wx_pub', 'pay_channel_code', 0, 'success', '', '微信公众号支付', '1', to_date('2021-12-03 10:40:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:08:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (114, 2, '微信小程序支付', 'wx_lite', 'pay_channel_code', 0, 'success', '', '微信小程序支付', '1', to_date('2021-12-03 10:41:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:08:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (115, 3, '微信 App 支付', 'wx_app', 'pay_channel_code', 0, 'success', '', '微信 App 支付', '1', to_date('2021-12-03 10:41:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (116, 10, '支付宝 PC 网站支付', 'alipay_pc', 'pay_channel_code', 0, 'primary', '', '支付宝 PC 网站支付', '1', to_date('2021-12-03 10:42:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (117, 11, '支付宝 Wap 网站支付', 'alipay_wap', 'pay_channel_code', 0, 'primary', '', '支付宝 Wap 网站支付', '1', to_date('2021-12-03 10:42:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:09:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (118, 12, '支付宝 App 支付', 'alipay_app', 'pay_channel_code', 0, 'primary', '', '支付宝 App 支付', '1', to_date('2021-12-03 10:42:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:09:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (119, 14, '支付宝扫码支付', 'alipay_qr', 'pay_channel_code', 0, 'primary', '', '支付宝扫码支付', '1', to_date('2021-12-03 10:43:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:09:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (120, 10, '通知成功', '10', 'pay_notify_status', 0, 'success', '', '通知成功', '1', to_date('2021-12-03 11:02:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 10:08:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (121, 20, '通知失败', '20', 'pay_notify_status', 0, 'danger', '', '通知失败', '1', to_date('2021-12-03 11:02:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 10:08:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (122, 0, '等待通知', '0', 'pay_notify_status', 0, 'info', '', '未通知', '1', to_date('2021-12-03 11:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 10:08:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (123, 10, '支付成功', '10', 'pay_order_status', 0, 'success', '', '支付成功', '1', to_date('2021-12-03 11:18:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 18:04:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (124, 30, '支付关闭', '30', 'pay_order_status', 0, 'info', '', '支付关闭', '1', to_date('2021-12-03 11:18:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 18:05:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (125, 0, '等待支付', '0', 'pay_order_status', 0, 'info', '', '未支付', '1', to_date('2021-12-03 11:18:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 18:04:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (600, 5, '首页', '1', 'promotion_banner_position', 0, 'warning', '', '', '1', to_date('2023-10-11 07:45:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:45:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (601, 4, '秒杀活动页', '2', 'promotion_banner_position', 0, 'warning', '', '', '1', to_date('2023-10-11 07:45:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:45:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (602, 3, '砍价活动页', '3', 'promotion_banner_position', 0, 'warning', '', '', '1', to_date('2023-10-11 07:45:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:45:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (603, 2, '限时折扣页', '4', 'promotion_banner_position', 0, 'warning', '', '', '1', to_date('2023-10-11 07:45:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:45:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (604, 1, '满减送页', '5', 'promotion_banner_position', 0, 'warning', '', '', '1', to_date('2023-10-11 07:45:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:45:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1118, 0, '等待退款', '0', 'pay_refund_status', 0, 'info', '', '等待退款', '1', to_date('2021-12-10 16:44:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 10:14:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1119, 20, '退款失败', '20', 'pay_refund_status', 0, 'danger', '', '退款失败', '1', to_date('2021-12-10 16:45:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 10:15:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1124, 10, '退款成功', '10', 'pay_refund_status', 0, 'success', '', '退款成功', '1', to_date('2021-12-10 16:46:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 10:15:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1127, 1, '审批中', '1', 'bpm_process_instance_status', 0, 'default', '', '流程实例的状态 - 进行中', '1', to_date('2022-01-07 23:47:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-16 16:11:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1128, 2, '审批通过', '2', 'bpm_process_instance_status', 0, 'success', '', '流程实例的状态 - 已完成', '1', to_date('2022-01-07 23:47:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-16 16:11:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1129, 1, '审批中', '1', 'bpm_task_status', 0, 'primary', '', '流程实例的结果 - 处理中', '1', to_date('2022-01-07 23:48:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-08 22:41:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1130, 2, '审批通过', '2', 'bpm_task_status', 0, 'success', '', '流程实例的结果 - 通过', '1', to_date('2022-01-07 23:48:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-08 22:41:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1131, 3, '审批不通过', '3', 'bpm_task_status', 0, 'danger', '', '流程实例的结果 - 不通过', '1', to_date('2022-01-07 23:48:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-08 22:41:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1132, 4, '已取消', '4', 'bpm_task_status', 0, 'info', '', '流程实例的结果 - 撤销', '1', to_date('2022-01-07 23:49:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-08 22:41:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1133, 10, '流程表单', '10', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 流程表单', '103', to_date('2022-01-11 23:51:30', 'SYYYY-MM-DD HH24:MI:SS'), '103', to_date('2022-01-11 23:51:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1134, 20, '业务表单', '20', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 业务表单', '103', to_date('2022-01-11 23:51:47', 'SYYYY-MM-DD HH24:MI:SS'), '103', to_date('2022-01-11 23:51:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1135, 10, '角色', '10', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 角色', '103', to_date('2022-01-12 23:21:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-06 02:53:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1136, 20, '部门的成员', '20', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的成员', '103', to_date('2022-01-12 23:21:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-06 02:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1137, 21, '部门的负责人', '21', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的负责人', '103', to_date('2022-01-12 23:33:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-06 02:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1138, 30, '用户', '30', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 用户', '103', to_date('2022-01-12 23:34:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-06 02:53:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1139, 40, '用户组', '40', 'bpm_task_candidate_strategy', 0, 'warning', '', '任务分配规则的类型 - 用户组', '103', to_date('2022-01-12 23:34:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-06 02:53:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1140, 60, '流程表达式', '60', 'bpm_task_candidate_strategy', 0, 'danger', '', '任务分配规则的类型 - 流程表达式', '103', to_date('2022-01-12 23:34:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-06 02:53:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1141, 22, '岗位', '22', 'bpm_task_candidate_strategy', 0, 'success', '', '任务分配规则的类型 - 岗位', '103', to_date('2022-01-14 18:41:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-06 02:53:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1145, 1, '管理后台', '1', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 管理后台', '1', to_date('2022-02-02 13:15:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-10 16:32:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1146, 2, '用户 APP', '2', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 用户 APP', '1', to_date('2022-02-02 13:15:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-10 16:33:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1150, 1, '数据库', '1', 'infra_file_storage', 0, 'default', '', NULL, '1', to_date('2022-03-15 00:25:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-15 00:25:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1151, 10, '本地磁盘', '10', 'infra_file_storage', 0, 'default', '', NULL, '1', to_date('2022-03-15 00:25:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-15 00:25:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1152, 11, 'FTP 服务器', '11', 'infra_file_storage', 0, 'default', '', NULL, '1', to_date('2022-03-15 00:26:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-15 00:26:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1153, 12, 'SFTP 服务器', '12', 'infra_file_storage', 0, 'default', '', NULL, '1', to_date('2022-03-15 00:26:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-15 00:26:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1154, 20, 'S3 对象存储', '20', 'infra_file_storage', 0, 'default', '', NULL, '1', to_date('2022-03-15 00:26:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-15 00:26:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1155, 103, '短信登录', '103', 'system_login_type', 0, 'default', '', NULL, '1', to_date('2022-05-09 23:57:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-09 23:58:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1156, 1, 'password', 'password', 'system_oauth2_grant_type', 0, 'default', '', '密码模式', '1', to_date('2022-05-12 00:22:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 16:26:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1157, 2, 'authorization_code', 'authorization_code', 'system_oauth2_grant_type', 0, 'primary', '', '授权码模式', '1', to_date('2022-05-12 00:22:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 16:26:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1158, 3, 'implicit', 'implicit', 'system_oauth2_grant_type', 0, 'success', '', '简化模式', '1', to_date('2022-05-12 00:23:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 16:26:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1159, 4, 'client_credentials', 'client_credentials', 'system_oauth2_grant_type', 0, 'default', '', '客户端模式', '1', to_date('2022-05-12 00:23:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 16:26:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1160, 5, 'refresh_token', 'refresh_token', 'system_oauth2_grant_type', 0, 'info', '', '刷新模式', '1', to_date('2022-05-12 00:24:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 16:26:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1162, 1, '销售中', '1', 'product_spu_status', 0, 'success', '', '商品 SPU 状态 - 销售中', '1', to_date('2022-10-24 21:19:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-10-24 21:20:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1163, 0, '仓库中', '0', 'product_spu_status', 0, 'info', '', '商品 SPU 状态 - 仓库中', '1', to_date('2022-10-24 21:20:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-10-24 21:21:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1164, 0, '回收站', '-1', 'product_spu_status', 0, 'default', '', '商品 SPU 状态 - 回收站', '1', to_date('2022-10-24 21:21:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-10-24 21:21:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1165, 1, '满减', '1', 'promotion_discount_type', 0, 'success', '', '优惠类型 - 满减', '1', to_date('2022-11-01 12:46:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-01 12:50:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1166, 2, '折扣', '2', 'promotion_discount_type', 0, 'primary', '', '优惠类型 - 折扣', '1', to_date('2022-11-01 12:46:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-01 12:50:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1167, 1, '固定日期', '1', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 固定日期', '1', to_date('2022-11-02 00:07:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 00:07:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1168, 2, '领取之后', '2', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 领取之后', '1', to_date('2022-11-02 00:07:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 00:07:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1169, 1, '通用劵', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', to_date('2022-11-02 00:28:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-28 00:27:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1170, 2, '商品劵', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', to_date('2022-11-02 00:28:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-28 00:27:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1171, 1, '未使用', '1', 'promotion_coupon_status', 0, 'primary', '', '优惠劵的状态 - 已领取', '1', to_date('2022-11-04 00:15:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-03 12:54:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1172, 2, '已使用', '2', 'promotion_coupon_status', 0, 'success', '', '优惠劵的状态 - 已使用', '1', to_date('2022-11-04 00:15:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 19:16:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1173, 3, '已过期', '3', 'promotion_coupon_status', 0, 'info', '', '优惠劵的状态 - 已过期', '1', to_date('2022-11-04 00:15:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 19:16:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1174, 1, '直接领取', '1', 'promotion_coupon_take_type', 0, 'primary', '', '优惠劵的领取方式 - 直接领取', '1', to_date('2022-11-04 19:13:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 19:13:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1175, 2, '指定发放', '2', 'promotion_coupon_take_type', 0, 'success', '', '优惠劵的领取方式 - 指定发放', '1', to_date('2022-11-04 19:13:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 19:14:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1176, 10, '未开始', '10', 'promotion_activity_status', 0, 'primary', '', '促销活动的状态枚举 - 未开始', '1', to_date('2022-11-04 22:54:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 22:55:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1177, 20, '进行中', '20', 'promotion_activity_status', 0, 'success', '', '促销活动的状态枚举 - 进行中', '1', to_date('2022-11-04 22:55:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 22:55:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1178, 30, '已结束', '30', 'promotion_activity_status', 0, 'info', '', '促销活动的状态枚举 - 已结束', '1', to_date('2022-11-04 22:55:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 22:55:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1179, 40, '已关闭', '40', 'promotion_activity_status', 0, 'warning', '', '促销活动的状态枚举 - 已关闭', '1', to_date('2022-11-04 22:56:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 22:56:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1180, 10, '满 N 元', '10', 'promotion_condition_type', 0, 'primary', '', '营销的条件类型 - 满 N 元', '1', to_date('2022-11-04 22:59:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 22:59:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1181, 20, '满 N 件', '20', 'promotion_condition_type', 0, 'success', '', '营销的条件类型 - 满 N 件', '1', to_date('2022-11-04 23:00:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 23:00:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1182, 10, '申请售后', '10', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 申请售后', '1', to_date('2022-11-19 20:53:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 20:54:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1183, 20, '商品待退货', '20', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商品待退货', '1', to_date('2022-11-19 20:54:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 20:58:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1184, 30, '商家待收货', '30', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商家待收货', '1', to_date('2022-11-19 20:56:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 20:59:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1185, 40, '等待退款', '40', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 等待退款', '1', to_date('2022-11-19 20:59:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:00:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1186, 50, '退款成功', '50', 'trade_after_sale_status', 0, 'default', '', '交易售后状态 - 退款成功', '1', to_date('2022-11-19 21:00:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:00:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1187, 61, '买家取消', '61', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 买家取消', '1', to_date('2022-11-19 21:01:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:01:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1188, 62, '商家拒绝', '62', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒绝', '1', to_date('2022-11-19 21:02:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:02:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1189, 63, '商家拒收货', '63', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒收货', '1', to_date('2022-11-19 21:02:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:03:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1190, 10, '售中退款', '10', 'trade_after_sale_type', 0, 'success', '', '交易售后的类型 - 售中退款', '1', to_date('2022-11-19 21:05:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:38:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1191, 20, '售后退款', '20', 'trade_after_sale_type', 0, 'primary', '', '交易售后的类型 - 售后退款', '1', to_date('2022-11-19 21:05:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:38:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1192, 10, '仅退款', '10', 'trade_after_sale_way', 0, 'primary', '', '交易售后的方式 - 仅退款', '1', to_date('2022-11-19 21:39:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:39:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1193, 20, '退货退款', '20', 'trade_after_sale_way', 0, 'success', '', '交易售后的方式 - 退货退款', '1', to_date('2022-11-19 21:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:39:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1194, 10, '微信小程序', '10', 'terminal', 0, 'default', '', '终端 - 微信小程序', '1', to_date('2022-12-10 10:51:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 10:51:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1195, 20, 'H5 网页', '20', 'terminal', 0, 'default', '', '终端 - H5 网页', '1', to_date('2022-12-10 10:51:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 10:51:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1196, 11, '微信公众号', '11', 'terminal', 0, 'default', '', '终端 - 微信公众号', '1', to_date('2022-12-10 10:54:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 10:52:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1197, 31, '苹果 App', '31', 'terminal', 0, 'default', '', '终端 - 苹果 App', '1', to_date('2022-12-10 10:54:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 10:52:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1198, 32, '安卓 App', '32', 'terminal', 0, 'default', '', '终端 - 安卓 App', '1', to_date('2022-12-10 10:55:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 10:59:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1199, 0, '普通订单', '0', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 普通订单', '1', to_date('2022-12-10 16:34:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 16:34:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1200, 1, '秒杀订单', '1', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 秒杀订单', '1', to_date('2022-12-10 16:34:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 16:34:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1201, 2, '砍价订单', '2', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 拼团订单', '1', to_date('2022-12-10 16:34:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-07 14:18:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1202, 3, '拼团订单', '3', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 砍价订单', '1', to_date('2022-12-10 16:34:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-07 14:18:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1203, 0, '待支付', '0', 'trade_order_status', 0, 'default', '', '交易订单状态 - 待支付', '1', to_date('2022-12-10 16:49:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 16:49:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1204, 10, '待发货', '10', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 待发货', '1', to_date('2022-12-10 16:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 16:51:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1205, 20, '已发货', '20', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 已发货', '1', to_date('2022-12-10 16:50:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 16:51:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1206, 30, '已完成', '30', 'trade_order_status', 0, 'success', '', '交易订单状态 - 已完成', '1', to_date('2022-12-10 16:50:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 16:51:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1207, 40, '已取消', '40', 'trade_order_status', 0, 'danger', '', '交易订单状态 - 已取消', '1', to_date('2022-12-10 16:50:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 16:51:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1208, 0, '未售后', '0', 'trade_order_item_after_sale_status', 0, 'info', '', '交易订单项的售后状态 - 未售后', '1', to_date('2022-12-10 20:58:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 20:59:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1209, 10, '售后中', '10', 'trade_order_item_after_sale_status', 0, 'primary', '', '交易订单项的售后状态 - 售后中', '1', to_date('2022-12-10 20:59:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-21 17:01:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1210, 20, '已退款', '20', 'trade_order_item_after_sale_status', 0, 'success', '', '交易订单项的售后状态 - 已退款', '1', to_date('2022-12-10 20:59:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-21 17:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1211, 1, '完全匹配', '1', 'mp_auto_reply_request_match', 0, 'primary', '', '公众号自动回复的请求关键字匹配模式 - 完全匹配', '1', to_date('2023-01-16 23:30:39', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-16 23:31:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1212, 2, '半匹配', '2', 'mp_auto_reply_request_match', 0, 'success', '', '公众号自动回复的请求关键字匹配模式 - 半匹配', '1', to_date('2023-01-16 23:30:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-16 23:31:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1213, 1, '文本', 'text', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 文本', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 22:17:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1214, 2, '图片', 'image', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图片', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 14:19:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1215, 3, '语音', 'voice', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 语音', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 14:20:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1216, 4, '视频', 'video', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 视频', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 14:21:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1217, 5, '小视频', 'shortvideo', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 小视频', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 14:19:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1218, 6, '图文', 'news', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图文', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 14:22:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1219, 7, '音乐', 'music', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 音乐', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 14:22:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1220, 8, '地理位置', 'location', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 地理位置', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 14:23:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1221, 9, '链接', 'link', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 链接', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 14:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1222, 10, '事件', 'event', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 事件', '1', to_date('2023-01-17 22:17:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 14:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1223, 0, '初始化', '0', 'system_mail_send_status', 0, 'primary', '', '邮件发送状态 - 初始化\\n', '1', to_date('2023-01-26 09:53:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-26 16:36:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1224, 10, '发送成功', '10', 'system_mail_send_status', 0, 'success', '', '邮件发送状态 - 发送成功', '1', to_date('2023-01-26 09:54:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-26 16:36:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1225, 20, '发送失败', '20', 'system_mail_send_status', 0, 'danger', '', '邮件发送状态 - 发送失败', '1', to_date('2023-01-26 09:54:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-26 16:36:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1226, 30, '不发送', '30', 'system_mail_send_status', 0, 'info', '', '邮件发送状态 -  不发送', '1', to_date('2023-01-26 09:55:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-26 16:36:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1227, 1, '通知公告', '1', 'system_notify_template_type', 0, 'primary', '', '站内信模版的类型 - 通知公告', '1', to_date('2023-01-28 10:35:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 10:35:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1228, 2, '系统消息', '2', 'system_notify_template_type', 0, 'success', '', '站内信模版的类型 - 系统消息', '1', to_date('2023-01-28 10:36:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 10:36:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1230, 13, '支付宝条码支付', 'alipay_bar', 'pay_channel_code', 0, 'primary', '', '支付宝条码支付', '1', to_date('2023-02-18 23:32:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:09:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1231, 10, 'Vue2 Element UI 标准模版', '10', 'infra_codegen_front_type', 0, '', '', '', '1', to_date('2023-04-13 00:03:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-04-13 00:03:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1232, 20, 'Vue3 Element Plus 标准模版', '20', 'infra_codegen_front_type', 0, '', '', '', '1', to_date('2023-04-13 00:04:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-04-13 00:04:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1234, 30, 'Vben2.0 Ant Design Schema 模版', '30', 'infra_codegen_front_type', 0, '', '', '', '1', to_date('2023-04-13 00:04:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-23 21:27:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1244, 0, '按件', '1', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', to_date('2023-05-21 22:46:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-05-21 22:46:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1245, 1, '按重量', '2', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', to_date('2023-05-21 22:46:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-05-21 22:46:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1246, 2, '按体积', '3', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', to_date('2023-05-21 22:47:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-05-21 22:47:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1335, 11, '订单积分抵扣', '11', 'member_point_biz_type', 0, '', '', '', '1', to_date('2023-06-10 12:15:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:41:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1336, 1, '签到', '1', 'member_point_biz_type', 0, '', '', '', '1', to_date('2023-06-10 12:15:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-20 11:59:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1341, 20, '已退款', '20', 'pay_order_status', 0, 'danger', '', '已退款', '1', to_date('2023-07-19 18:05:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 18:05:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1342, 21, '请求成功，但是结果失败', '21', 'pay_notify_status', 0, 'warning', '', '请求成功，但是结果失败', '1', to_date('2023-07-19 18:10:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 18:11:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1343, 22, '请求失败', '22', 'pay_notify_status', 0, 'warning', '', NULL, '1', to_date('2023-07-19 18:11:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 18:11:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1344, 4, '微信扫码支付', 'wx_native', 'pay_channel_code', 0, 'success', '', '微信扫码支付', '1', to_date('2023-07-19 20:07:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:09:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1345, 5, '微信条码支付', 'wx_bar', 'pay_channel_code', 0, 'success', '', '微信条码支付\\n', '1', to_date('2023-07-19 20:08:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 20:09:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1346, 1, '支付单', '1', 'pay_notify_type', 0, 'primary', '', '支付单', '1', to_date('2023-07-20 12:23:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-20 12:23:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1347, 2, '退款单', '2', 'pay_notify_type', 0, 'danger', '', NULL, '1', to_date('2023-07-20 12:23:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-20 12:23:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1348, 20, '模拟支付', 'mock', 'pay_channel_code', 0, 'default', '', '模拟支付', '1', to_date('2023-07-29 11:10:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-29 03:14:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1349, 12, '订单积分抵扣（整单取消）', '12', 'member_point_biz_type', 0, '', '', '', '1', to_date('2023-08-20 12:00:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:42:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1350, 0, '管理员调整', '0', 'member_experience_biz_type', 0, '', '', NULL, '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1351, 1, '邀新奖励', '1', 'member_experience_biz_type', 0, '', '', NULL, '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1352, 11, '下单奖励', '11', 'member_experience_biz_type', 0, 'success', '', NULL, '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:45:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1353, 12, '下单奖励（整单取消）', '12', 'member_experience_biz_type', 0, 'warning', '', NULL, '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:45:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1354, 4, '签到奖励', '4', 'member_experience_biz_type', 0, '', '', NULL, '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1355, 5, '抽奖奖励', '5', 'member_experience_biz_type', 0, '', '', NULL, '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1356, 1, '快递发货', '1', 'trade_delivery_type', 0, '', '', '', '1', to_date('2023-08-23 00:04:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-23 00:04:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1357, 2, '用户自提', '2', 'trade_delivery_type', 0, '', '', '', '1', to_date('2023-08-23 00:05:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-23 00:05:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1358, 3, '品类劵', '3', 'promotion_product_scope', 0, 'default', '', '', '1', to_date('2023-09-01 23:43:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-28 00:27:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1359, 1, '人人分销', '1', 'brokerage_enabled_condition', 0, '', '', '所有用户都可以分销', '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1360, 2, '指定分销', '2', 'brokerage_enabled_condition', 0, '', '', '仅可后台手动设置推广员', '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1361, 1, '首次绑定', '1', 'brokerage_bind_mode', 0, '', '', '只要用户没有推广人，随时都可以绑定推广关系', '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1362, 2, '注册绑定', '2', 'brokerage_bind_mode', 0, '', '', '仅新用户注册时才能绑定推广关系', '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1363, 3, '覆盖绑定', '3', 'brokerage_bind_mode', 0, '', '', '如果用户已经有推广人，推广人会被变更', '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1364, 1, '钱包', '1', 'brokerage_withdraw_type', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1365, 2, '银行卡', '2', 'brokerage_withdraw_type', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1366, 3, '微信收款码', '3', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-10 08:24:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1367, 4, '支付宝收款码', '4', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-10 08:24:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1368, 1, '订单返佣', '1', 'brokerage_record_biz_type', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1369, 2, '申请提现', '2', 'brokerage_record_biz_type', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1370, 3, '申请提现驳回', '3', 'brokerage_record_biz_type', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1371, 0, '待结算', '0', 'brokerage_record_status', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1372, 1, '已结算', '1', 'brokerage_record_status', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1373, 2, '已取消', '2', 'brokerage_record_status', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1374, 0, '审核中', '0', 'brokerage_withdraw_status', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1375, 10, '审核通过', '10', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1376, 11, '提现成功', '11', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1377, 20, '审核不通过', '20', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1378, 21, '提现失败', '21', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1379, 0, '工商银行', '0', 'brokerage_bank_name', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1380, 1, '建设银行', '1', 'brokerage_bank_name', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1381, 2, '农业银行', '2', 'brokerage_bank_name', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1382, 3, '中国银行', '3', 'brokerage_bank_name', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1383, 4, '交通银行', '4', 'brokerage_bank_name', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1384, 5, '招商银行', '5', 'brokerage_bank_name', 0, '', '', NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1385, 21, '钱包', 'wallet', 'pay_channel_code', 0, 'primary', '', '', '1', to_date('2023-10-01 21:46:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-01 21:48:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1386, 1, '砍价中', '1', 'promotion_bargain_record_status', 0, 'default', '', '', '1', to_date('2023-10-05 10:41:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-05 10:41:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1387, 2, '砍价成功', '2', 'promotion_bargain_record_status', 0, 'success', '', '', '1', to_date('2023-10-05 10:41:39', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-05 10:41:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1388, 3, '砍价失败', '3', 'promotion_bargain_record_status', 0, 'warning', '', '', '1', to_date('2023-10-05 10:41:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-05 10:41:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1389, 0, '拼团中', '0', 'promotion_combination_record_status', 0, '', '', '', '1', to_date('2023-10-08 07:24:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-10-13 10:08:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1390, 1, '拼团成功', '1', 'promotion_combination_record_status', 0, 'success', '', '', '1', to_date('2023-10-08 07:24:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-10-13 10:08:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1391, 2, '拼团失败', '2', 'promotion_combination_record_status', 0, 'warning', '', '', '1', to_date('2023-10-08 07:25:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-10-13 10:08:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1392, 2, '管理员修改', '2', 'member_point_biz_type', 0, 'default', '', '', '1', to_date('2023-10-11 07:41:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:41:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1393, 13, '订单积分抵扣（单个退款）', '13', 'member_point_biz_type', 0, '', '', '', '1', to_date('2023-10-11 07:42:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:42:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1394, 21, '订单积分奖励', '21', 'member_point_biz_type', 0, 'default', '', '', '1', to_date('2023-10-11 07:42:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:42:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1395, 22, '订单积分奖励（整单取消）', '22', 'member_point_biz_type', 0, 'default', '', '', '1', to_date('2023-10-11 07:42:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:43:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1396, 23, '订单积分奖励（单个退款）', '23', 'member_point_biz_type', 0, 'default', '', '', '1', to_date('2023-10-11 07:43:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:43:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1397, 13, '下单奖励（单个退款）', '13', 'member_experience_biz_type', 0, 'warning', '', '', '1', to_date('2023-10-11 07:45:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-11 07:45:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1398, 5, '网上转账', '5', 'crm_receivable_return_type', 0, 'default', '', '', '1', to_date('2023-10-18 21:55:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-18 21:55:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1399, 6, '支付宝', '6', 'crm_receivable_return_type', 0, 'default', '', '', '1', to_date('2023-10-18 21:55:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-18 21:55:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1400, 7, '微信支付', '7', 'crm_receivable_return_type', 0, 'default', '', '', '1', to_date('2023-10-18 21:55:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-18 21:55:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1401, 8, '其他', '8', 'crm_receivable_return_type', 0, 'default', '', '', '1', to_date('2023-10-18 21:56:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-18 21:56:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1402, 1, 'IT', '1', 'crm_customer_industry', 0, 'default', '', '', '1', to_date('2023-10-28 23:02:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-18 23:30:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1403, 2, '金融业', '2', 'crm_customer_industry', 0, 'default', '', '', '1', to_date('2023-10-28 23:02:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-18 23:30:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1404, 3, '房地产', '3', 'crm_customer_industry', 0, 'default', '', '', '1', to_date('2023-10-28 23:02:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-18 23:30:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1405, 4, '商业服务', '4', 'crm_customer_industry', 0, 'default', '', '', '1', to_date('2023-10-28 23:02:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-18 23:30:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1406, 5, '运输/物流', '5', 'crm_customer_industry', 0, 'default', '', '', '1', to_date('2023-10-28 23:03:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-18 23:31:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1407, 6, '生产', '6', 'crm_customer_industry', 0, 'default', '', '', '1', to_date('2023-10-28 23:03:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-18 23:31:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1408, 7, '政府', '7', 'crm_customer_industry', 0, 'default', '', '', '1', to_date('2023-10-28 23:03:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-18 23:31:13', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1409, 8, '文化传媒', '8', 'crm_customer_industry', 0, 'default', '', '', '1', to_date('2023-10-28 23:03:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-18 23:31:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1422, 1, 'A （重点客户）', '1', 'crm_customer_level', 0, 'primary', '', '', '1', to_date('2023-10-28 23:07:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:07:13', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1423, 2, 'B （普通客户）', '2', 'crm_customer_level', 0, 'info', '', '', '1', to_date('2023-10-28 23:07:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:07:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1424, 3, 'C （非优先客户）', '3', 'crm_customer_level', 0, 'default', '', '', '1', to_date('2023-10-28 23:07:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:07:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1425, 1, '促销', '1', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1426, 2, '搜索引擎', '2', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:08:39', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:08:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1427, 3, '广告', '3', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:08:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:08:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1428, 4, '转介绍', '4', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:08:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:08:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1429, 5, '线上注册', '5', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1430, 6, '线上咨询', '6', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:09:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:09:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1431, 7, '预约上门', '7', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:09:39', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:09:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1432, 8, '陌拜', '8', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:10:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:10:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1433, 9, '电话咨询', '9', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:10:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:10:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1434, 10, '邮件咨询', '10', 'crm_customer_source', 0, 'default', '', '', '1', to_date('2023-10-28 23:10:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 23:10:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1435, 10, 'Gitee', '10', 'system_social_type', 0, '', '', '', '1', to_date('2023-11-04 13:04:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 13:04:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1436, 20, '钉钉', '20', 'system_social_type', 0, '', '', '', '1', to_date('2023-11-04 13:04:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 13:04:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1437, 30, '企业微信', '30', 'system_social_type', 0, '', '', '', '1', to_date('2023-11-04 13:05:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 13:05:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1438, 31, '微信公众平台', '31', 'system_social_type', 0, '', '', '', '1', to_date('2023-11-04 13:05:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 13:05:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1439, 32, '微信开放平台', '32', 'system_social_type', 0, '', '', '', '1', to_date('2023-11-04 13:05:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 13:05:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1440, 34, '微信小程序', '34', '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');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1441, 1, '上架', '1', 'crm_product_status', 0, 'success', '', '', '1', to_date('2023-10-30 21:49:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-30 21:49:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1442, 0, '下架', '0', 'crm_product_status', 0, 'success', '', '', '1', to_date('2023-10-30 21:49:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-30 21:49:13', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1443, 15, '子表', '15', 'infra_codegen_template_type', 0, 'default', '', '', '1', to_date('2023-11-13 23:06:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-13 23:06:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1444, 10, '主表（标准模式）', '10', 'infra_codegen_template_type', 0, 'default', '', '', '1', to_date('2023-11-14 12:32:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-14 12:32:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1445, 11, '主表（ERP 模式）', '11', 'infra_codegen_template_type', 0, 'default', '', '', '1', to_date('2023-11-14 12:33:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-14 12:33:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1446, 12, '主表（内嵌模式）', '12', 'infra_codegen_template_type', 0, '', '', '', '1', to_date('2023-11-14 12:33:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-14 12:33:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1447, 1, '负责人', '1', 'crm_permission_level', 0, 'default', '', '', '1', to_date('2023-11-30 09:53:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 09:53:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1448, 2, '只读', '2', 'crm_permission_level', 0, '', '', '', '1', to_date('2023-11-30 09:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 09:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1449, 3, '读写', '3', 'crm_permission_level', 0, '', '', '', '1', to_date('2023-11-30 09:53:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 09:53:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1450, 0, '未提交', '0', 'crm_audit_status', 0, '', '', '', '1', to_date('2023-11-30 18:56:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 18:56:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1451, 10, '审批中', '10', 'crm_audit_status', 0, '', '', '', '1', to_date('2023-11-30 18:57:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 18:57:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1452, 20, '审核通过', '20', 'crm_audit_status', 0, '', '', '', '1', to_date('2023-11-30 18:57:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 18:57:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1453, 30, '审核不通过', '30', 'crm_audit_status', 0, '', '', '', '1', to_date('2023-11-30 18:57:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 18:57:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1454, 40, '已取消', '40', 'crm_audit_status', 0, '', '', '', '1', to_date('2023-11-30 18:57:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 18:57:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1456, 1, '支票', '1', 'crm_receivable_return_type', 0, 'default', '', '', '1', to_date('2023-10-18 21:54:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-18 21:54:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1457, 2, '现金', '2', 'crm_receivable_return_type', 0, 'default', '', '', '1', to_date('2023-10-18 21:54:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-18 21:54:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1458, 3, '邮政汇款', '3', 'crm_receivable_return_type', 0, 'default', '', '', '1', to_date('2023-10-18 21:54:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-18 21:54:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1459, 4, '电汇', '4', 'crm_receivable_return_type', 0, 'default', '', '', '1', to_date('2023-10-18 21:55:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-18 21:55:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1461, 1, '个', '1', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:02:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:02:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1462, 2, '块', '2', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:02:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:02:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1463, 3, '只', '3', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:02:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:02:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1464, 4, '把', '4', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:03:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:03:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1465, 5, '枚', '5', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:03:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:03:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1466, 6, '瓶', '6', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:03:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:03:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1467, 7, '盒', '7', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:03:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:03:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1468, 8, '台', '8', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:03:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:03:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1469, 9, '吨', '9', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1470, 10, '千克', '10', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:04:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:04:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1471, 11, '米', '11', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:04:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:04:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1472, 12, '箱', '12', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:04:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:04:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1473, 13, '套', '13', 'crm_product_unit', 0, '', '', '', '1', to_date('2023-12-05 23:04:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:04:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1474, 1, '打电话', '1', 'crm_follow_up_type', 0, '', '', '', '1', to_date('2024-01-15 20:48:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-15 20:48:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1475, 2, '发短信', '2', 'crm_follow_up_type', 0, '', '', '', '1', to_date('2024-01-15 20:48:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-15 20:48:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1476, 3, '上门拜访', '3', 'crm_follow_up_type', 0, '', '', '', '1', to_date('2024-01-15 20:49:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-15 20:49:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1477, 4, '微信沟通', '4', 'crm_follow_up_type', 0, '', '', '', '1', to_date('2024-01-15 20:49:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-15 20:49:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1482, 4, '转账失败', '20', 'pay_transfer_status', 0, 'warning', '', '', '1', to_date('2023-10-28 16:24:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-08 12:59:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1483, 3, '转账成功', '10', 'pay_transfer_status', 0, 'success', '', '', '1', to_date('2023-10-28 16:23:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-08 12:58:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1484, 2, '转账进行中', '5', 'pay_transfer_status', 0, 'info', '', '', '1', to_date('2023-10-28 16:23:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-08 12:58:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1485, 1, '等待转账', '0', 'pay_transfer_status', 0, 'default', '', '', '1', to_date('2023-10-28 16:21:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 16:23:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1486, 10, '其它入库', '10', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-05 18:07:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-05 18:07:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1487, 11, '其它入库（作废）', '11', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-05 18:08:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-05 19:20:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1488, 20, '其它出库', '20', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-05 18:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-05 18:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1489, 21, '其它出库（作废）', '21', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-05 18:09:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-05 19:20:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1490, 10, '未审核', '10', 'erp_audit_status', 0, 'default', '', '', '1', to_date('2024-02-06 00:00:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-06 00:00:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1491, 20, '已审核', '20', 'erp_audit_status', 0, 'success', '', '', '1', to_date('2024-02-06 00:00:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-06 00:00:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1492, 30, '调拨入库', '30', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-07 20:34:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-07 12:36:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1493, 31, '调拨入库（作废）', '31', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-07 20:34:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-07 20:37:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1494, 32, '调拨出库', '32', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-07 20:34:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-07 12:36:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1495, 33, '调拨出库（作废）', '33', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-07 20:34:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-07 20:37:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1496, 40, '盘盈入库', '40', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-08 08:53:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-08 08:53:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1497, 41, '盘盈入库（作废）', '41', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-08 08:53:39', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-16 19:40:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1498, 42, '盘亏出库', '42', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-08 08:54:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-08 08:54:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1499, 43, '盘亏出库（作废）', '43', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-08 08:54:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-16 19:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1500, 50, '销售出库', '50', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-11 21:47:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-11 21:50:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1501, 51, '销售出库（作废）', '51', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-11 21:47:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-11 21:51:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1502, 60, '销售退货入库', '60', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-12 06:51:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-12 06:51:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1503, 61, '销售退货入库（作废）', '61', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-12 06:51:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-12 06:51:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1504, 70, '采购入库', '70', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-16 13:10:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-16 13:10:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1505, 71, '采购入库（作废）', '71', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-16 13:10:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-16 19:40:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1506, 80, '采购退货出库', '80', 'erp_stock_record_biz_type', 0, '', '', '', '1', to_date('2024-02-16 13:10:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-16 13:10:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1507, 81, '采购退货出库（作废）', '81', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', to_date('2024-02-16 13:10:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-16 19:40:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1509, 3, '审批不通过', '3', 'bpm_process_instance_status', 0, 'danger', '', '', '1', to_date('2024-03-16 16:12:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-16 16:12:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1510, 4, '已取消', '4', 'bpm_process_instance_status', 0, 'warning', '', '', '1', to_date('2024-03-16 16:12:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-16 16:12:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1511, 5, '已退回', '5', 'bpm_task_status', 0, 'warning', '', '', '1', to_date('2024-03-16 19:10:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-08 22:41:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1512, 6, '委派中', '6', 'bpm_task_status', 0, 'primary', '', '', '1', to_date('2024-03-17 10:06:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-08 22:41:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1513, 7, '审批通过中', '7', 'bpm_task_status', 0, 'success', '', '', '1', to_date('2024-03-17 10:06:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-08 22:41:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1514, 0, '待审批', '0', 'bpm_task_status', 0, 'info', '', '', '1', to_date('2024-03-17 10:07:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-08 22:41:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1515, 35, '发起人自选', '35', 'bpm_task_candidate_strategy', 0, '', '', '', '1', to_date('2024-03-22 19:45:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-22 19:45:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1516, 1, '执行监听器', 'execution', 'bpm_process_listener_type', 0, 'primary', '', '', '1', to_date('2024-03-23 12:54:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-23 19:14:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1517, 1, '任务监听器', 'task', 'bpm_process_listener_type', 0, 'success', '', '', '1', to_date('2024-03-23 12:54:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-23 19:14:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1526, 1, 'Java 类', 'class', 'bpm_process_listener_value_type', 0, 'primary', '', '', '1', to_date('2024-03-23 15:08:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-23 19:14:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1527, 2, '表达式', 'expression', 'bpm_process_listener_value_type', 0, 'success', '', '', '1', to_date('2024-03-23 15:09:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-23 19:14:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1528, 3, '代理表达式', 'delegateExpression', 'bpm_process_listener_value_type', 0, 'info', '', '', '1', to_date('2024-03-23 15:11:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-23 19:14:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1529, 1, '天', '1', 'date_interval', 0, '', '', '', '1', to_date('2024-03-29 22:50:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-29 22:50:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1530, 2, '周', '2', 'date_interval', 0, '', '', '', '1', to_date('2024-03-29 22:50:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-29 22:50:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1531, 3, '月', '3', 'date_interval', 0, '', '', '', '1', to_date('2024-03-29 22:50:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-29 22:50:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1532, 4, '季度', '4', 'date_interval', 0, '', '', '', '1', to_date('2024-03-29 22:51:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-29 22:51:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1533, 5, '年', '5', 'date_interval', 0, '', '', '', '1', to_date('2024-03-29 22:51:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-29 22:51:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1534, 1, '赢单', '1', 'crm_business_end_status_type', 0, 'success', '', '', '1', to_date('2024-04-13 23:26:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-13 23:26:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1535, 2, '输单', '2', 'crm_business_end_status_type', 0, 'primary', '', '', '1', to_date('2024-04-13 23:27:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-13 23:27:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1536, 3, '无效', '3', 'crm_business_end_status_type', 0, 'info', '', '', '1', to_date('2024-04-13 23:27:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-13 23:27:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1537, 1, 'OpenAI', 'OpenAI', 'ai_platform', 0, '', '', '', '1', to_date('2024-05-09 22:33:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-09 22:58:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1538, 2, 'Ollama', 'Ollama', 'ai_platform', 0, '', '', '', '1', to_date('2024-05-17 23:02:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-17 23:02:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1539, 3, '文心一言', 'YiYan', 'ai_platform', 0, '', '', '', '1', to_date('2024-05-18 09:24:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-18 09:29:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1540, 4, '讯飞星火', 'XingHuo', 'ai_platform', 0, '', '', '', '1', to_date('2024-05-18 10:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-18 10:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1541, 5, '通义千问', 'TongYi', 'ai_platform', 0, '', '', '', '1', to_date('2024-05-18 10:32:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-06 15:42:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1542, 6, 'StableDiffusion', 'StableDiffusion', 'ai_platform', 0, '', '', '', '1', to_date('2024-06-01 15:09:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-01 15:10:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1543, 10, '进行中', '10', 'ai_image_status', 0, 'primary', '', '', '1', to_date('2024-06-26 20:51:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-26 20:52:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1544, 20, '已完成', '20', 'ai_image_status', 0, 'success', '', '', '1', to_date('2024-06-26 20:52:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-26 20:52:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1545, 30, '已失败', '30', 'ai_image_status', 0, 'warning', '', '', '1', to_date('2024-06-26 20:52:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-26 20:52:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1546, 7, 'Midjourney', 'Midjourney', 'ai_platform', 0, '', '', '', '1', to_date('2024-06-26 22:14:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-26 22:14:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1547, 10, '进行中', '10', 'ai_music_status', 0, 'primary', '', '', '1', to_date('2024-06-27 22:45:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-28 00:56:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1548, 20, '已完成', '20', 'ai_music_status', 0, 'success', '', '', '1', to_date('2024-06-27 22:45:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-28 00:56:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1549, 30, '已失败', '30', 'ai_music_status', 0, 'danger', '', '', '1', to_date('2024-06-27 22:45:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-28 00:56:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1550, 1, '歌词模式', '1', 'ai_generate_mode', 0, '', '', '', '1', to_date('2024-06-27 22:46:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-28 01:22:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', to_date('2024-06-27 22:46:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-28 01:22:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', to_date('2024-06-29 09:13:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-29 09:13:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', to_date('2024-07-06 12:04:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-06 12:05:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1554, 13, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', to_date('2024-07-06 18:00:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-24 20:18:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', to_date('2024-07-07 15:49:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:49:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:49:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:49:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:50:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:50:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1558, 7, '博客文章', '7', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:50:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:50:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1559, 8, '想法', '8', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:50:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:50:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1560, 9, '大纲', '9', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:50:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:50:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1561, 1, '自动', '1', 'ai_write_tone', 0, '', '', '', '1', to_date('2024-07-07 15:51:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:51:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1562, 2, '友善', '2', 'ai_write_tone', 0, '', '', '', '1', to_date('2024-07-07 15:51:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:51:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1563, 3, '随意', '3', 'ai_write_tone', 0, '', '', '', '1', to_date('2024-07-07 15:51:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:51:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1564, 4, '友好', '4', 'ai_write_tone', 0, '', '', '', '1', to_date('2024-07-07 15:51:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:51:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1565, 5, '专业', '5', 'ai_write_tone', 0, '', '', '', '1', to_date('2024-07-07 15:51:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:52:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1566, 6, '诙谐', '6', 'ai_write_tone', 0, '', '', '', '1', to_date('2024-07-07 15:52:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:52:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1567, 7, '有趣', '7', 'ai_write_tone', 0, '', '', '', '1', to_date('2024-07-07 15:52:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:52:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1568, 8, '正式', '8', 'ai_write_tone', 0, '', '', '', '1', to_date('2024-07-07 15:54:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:54:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1569, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:49:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:49:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1570, 1, '自动', '1', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:19:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:19:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1571, 2, '电子邮件', '2', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:19:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1572, 3, '消息', '3', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:20:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:49:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1573, 4, '评论', '4', 'ai_write_format', 0, '', '', '', '1', to_date('2024-07-07 15:20:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:49:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1574, 1, '自动', '1', 'ai_write_language', 0, '', '', '', '1', to_date('2024-07-07 15:44:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:44:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1575, 2, '中文', '2', 'ai_write_language', 0, '', '', '', '1', to_date('2024-07-07 15:44:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:44:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1576, 3, '英文', '3', 'ai_write_language', 0, '', '', '', '1', to_date('2024-07-07 15:44:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:44:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1577, 4, '韩语', '4', 'ai_write_language', 0, '', '', '', '1', to_date('2024-07-07 15:46:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:46:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1578, 5, '日语', '5', 'ai_write_language', 0, '', '', '', '1', to_date('2024-07-07 15:46:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:46:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1579, 1, '自动', '1', 'ai_write_length', 0, '', '', '', '1', to_date('2024-07-07 15:48:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:48:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1580, 2, '短', '2', 'ai_write_length', 0, '', '', '', '1', to_date('2024-07-07 15:48:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:48:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1581, 3, '中等', '3', 'ai_write_length', 0, '', '', '', '1', to_date('2024-07-07 15:48:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:48:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1582, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', to_date('2024-07-07 15:49:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:49:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1584, 1, '撰写', '1', 'ai_write_type', 0, '', '', '', '1', to_date('2024-07-10 21:26:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-10 21:26:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1585, 2, '回复', '2', 'ai_write_type', 0, '', '', '', '1', to_date('2024-07-10 21:26:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-10 21:26:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1586, 2, '腾讯云', 'TENCENT', 'system_sms_channel_code', 0, '', '', '', '1', to_date('2024-07-22 22:23:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-22 22:23:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1587, 3, '华为云', 'HUAWEI', 'system_sms_channel_code', 0, '', '', '', '1', to_date('2024-07-22 22:23:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-22 22:23:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1588, 1, 'OpenAI 微软', 'AzureOpenAI', 'ai_platform', 0, '', '', '', '1', to_date('2024-08-10 14:07:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-10 14:07:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1589, 10, 'BPMN 设计器', '10', 'bpm_model_type', 0, 'primary', '', '', '1', to_date('2024-08-26 15:22:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-26 16:46:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1590, 20, 'SIMPLE 设计器', '20', 'bpm_model_type', 0, 'success', '', '', '1', to_date('2024-08-26 15:22:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-26 16:45:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1591, 4, '七牛云', 'QINIU', 'system_sms_channel_code', 0, '', '', '', '1', to_date('2024-08-31 08:45:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-31 08:45:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1592, 3, '新人券', '3', 'promotion_coupon_take_type', 0, 'info', '', '新人注册后，自动发放', '1', to_date('2024-09-03 11:57:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-03 11:57:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1593, 5, '微信零钱', '5', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', to_date('2024-10-13 11:06:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-10 08:24:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', to_date('2025-02-23 19:51:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-23 19:52:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', to_date('2025-02-23 20:58:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-23 20:58:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', to_date('2025-02-24 20:19:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-24 20:19:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1686, 1, '聊天', '1', 'ai_model_type', 0, '', '', '', '1', to_date('2025-03-03 12:26:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 12:26:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1687, 2, '图像', '2', 'ai_model_type', 0, '', '', '', '1', to_date('2025-03-03 12:27:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 12:27:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1688, 3, '音频', '3', 'ai_model_type', 0, '', '', '', '1', to_date('2025-03-03 12:27:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 12:27:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1689, 4, '视频', '4', 'ai_model_type', 0, '', '', '', '1', to_date('2025-03-03 12:28:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 12:28:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1690, 5, '向量', '5', 'ai_model_type', 0, '', '', '', '1', to_date('2025-03-03 12:28:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 12:28:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', to_date('2025-03-03 12:28:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 12:28:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', to_date('2025-03-11 20:04:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-11 20:04:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1693, 15, '月之暗面', 'Moonshot', 'ai_platform', 0, '', '', '', '1', to_date('2025-03-11 20:05:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-11 20:05:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2000, 0, '标准数据格式（JSON）', '0', 'iot_data_format', 0, 'default', '', '', '1', to_date('2024-08-10 11:53:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2001, 1, '透传/自定义', '1', 'iot_data_format', 0, 'default', '', '', '1', to_date('2024-08-10 11:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', to_date('2024-08-10 11:54:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', to_date('2024-08-10 11:55:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', to_date('2024-08-10 11:55:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', to_date('2024-08-10 12:10:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', to_date('2024-08-10 14:19:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2007, 0, '弱校验', '0', 'iot_validate_type', 0, '', '', '', '1', to_date('2024-09-06 20:05:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2008, 1, '免校验', '1', 'iot_validate_type', 0, '', '', '', '1', to_date('2024-09-06 20:06:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', to_date('2024-09-06 22:04:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2010, 1, '蜂窝（2G / 3G / 4G / 5G）', '1', 'iot_net_type', 0, '', '', '', '1', to_date('2024-09-06 22:05:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', to_date('2024-09-06 22:05:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', to_date('2024-09-06 22:05:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2013, 0, '自定义', '0', 'iot_protocol_type', 0, '', '', '', '1', to_date('2024-09-06 22:26:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2014, 1, 'Modbus', '1', 'iot_protocol_type', 0, '', '', '', '1', to_date('2024-09-06 22:26:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:28:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2015, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', to_date('2024-09-06 22:26:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2016, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', to_date('2024-09-06 22:26:39', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2017, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', to_date('2024-09-06 22:26:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', to_date('2024-09-21 08:13:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', to_date('2024-09-21 08:13:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', to_date('2024-09-21 08:13:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', to_date('2024-09-29 20:03:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', to_date('2024-09-29 20:03:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', to_date('2024-09-29 20:03:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2024, 1, 'JAR 部署', '0', 'iot_plugin_deploy_type', 0, '', '', '', '1', to_date('2024-12-13 10:55:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2025, 2, '独立部署', '1', 'iot_plugin_deploy_type', 0, '', '', '', '1', to_date('2024-12-13 10:55:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2026, 0, '停止', '0', 'iot_plugin_status', 0, 'danger', '', '', '1', to_date('2024-12-13 11:07:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:29:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2027, 1, '运行', '1', 'iot_plugin_status', 0, '', '', '', '1', to_date('2024-12-13 11:07:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:34:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2028, 0, '普通插件', '0', 'iot_plugin_type', 0, '', '', '', '1', to_date('2024-12-13 11:08:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:34:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2029, 1, '设备插件', '1', 'iot_plugin_type', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:34:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:34:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:34:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:34:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2033, 4, 'PH值', 'pH', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:34:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2034, 5, '土壤EC值', 'dS/m', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:34:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2035, 6, '太阳总辐射', 'W/㎡', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:36:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2036, 7, '降雨量', 'mm/hour', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:36:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2037, 8, '乏', 'var', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:36:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2038, 9, '厘泊', 'cP', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:36:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2039, 10, '饱和度', 'aw', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2040, 11, '个', 'pcs', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2041, 12, '厘斯', 'cst', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2042, 13, '巴', 'bar', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2043, 14, '纳克每升', 'ppt', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2044, 15, '微克每升', 'ppb', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2045, 16, '微西每厘米', 'uS/cm', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2046, 17, '牛顿每库仑', 'N/C', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2047, 18, '伏特每米', 'V/m', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2048, 19, '滴速', 'ml/min', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2049, 20, '毫米汞柱', 'mmHg', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2050, 21, '血糖', 'mmol/L', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:37:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2051, 22, '毫米每秒', 'mm/s', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:38:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2052, 23, '转每分钟', 'turn/m', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:38:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2053, 24, '次', 'count', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:38:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2054, 25, '档', 'gear', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:38:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2055, 26, '步', 'stepCount', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:38:13', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2056, 27, '标准立方米每小时', 'Nm3/h', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:38:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2057, 28, '千伏', 'kV', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:38:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2058, 29, '千伏安', 'kVA', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:38:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2060, 30, '千乏', 'kVar', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2061, 31, '微瓦每平方厘米', 'uw/cm2', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2062, 32, '只', '只', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2063, 33, '相对湿度', '%RH', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2064, 34, '立方米每秒', 'm³/s', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2065, 35, '公斤每秒', 'kg/s', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2066, 36, '转每分钟', 'r/min', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2067, 37, '吨每小时', 't/h', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2068, 38, '千卡每小时', 'KCL/h', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2069, 39, '升每秒', 'L/s', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2070, 40, '兆帕', 'Mpa', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2071, 41, '立方米每小时', 'm³/h', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2072, 42, '千乏时', 'kvarh', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2073, 43, '微克每升', 'μg/L', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2074, 44, '千卡路里', 'kcal', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2075, 45, '吉字节', 'GB', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2076, 46, '兆字节', 'MB', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2077, 47, '千字节', 'KB', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2078, 48, '字节', 'B', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2079, 49, '微克每平方分米每天', 'μg/(d㎡·d)', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2080, 50, '无', '', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2081, 51, '百万分率', 'ppm', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2082, 52, '像素', 'pixel', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2083, 53, '照度', 'Lux', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2084, 54, '重力加速度', 'grav', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2085, 55, '分贝', 'dB', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2086, 56, '百分比', '%', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2087, 57, '流明', 'lm', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2088, 58, '比特', 'bit', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2089, 59, '克每毫升', 'g/mL', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2090, 60, '克每升', 'g/L', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2091, 61, '毫克每升', 'mg/L', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2092, 62, '微克每立方米', 'μg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2093, 63, '毫克每立方米', 'mg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2094, 64, '克每立方米', 'g/m³', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2095, 65, '千克每立方米', 'kg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2096, 66, '纳法', 'nF', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2097, 67, '皮法', 'pF', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2098, 68, '微法', 'μF', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2099, 69, '法拉', 'F', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2100, 70, '欧姆', 'Ω', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2101, 71, '微安', 'μA', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2102, 72, '毫安', 'mA', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2103, 73, '千安', 'kA', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2104, 74, '安培', 'A', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2105, 75, '毫伏', 'mV', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2106, 76, '伏特', 'V', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2107, 77, '毫秒', 'ms', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2108, 78, '秒', 's', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2109, 79, '分钟', 'min', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2110, 80, '小时', 'h', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2111, 81, '日', 'day', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2112, 82, '周', 'week', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2113, 83, '月', 'month', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2114, 84, '年', 'year', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2115, 85, '节', 'kn', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2116, 86, '千米每小时', 'km/h', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2117, 87, '米每秒', 'm/s', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2118, 88, '秒', '″', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2119, 89, '分', '′', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2120, 90, '度', '°', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2121, 91, '弧度', 'rad', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2122, 92, '赫兹', 'Hz', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2123, 93, '微瓦', 'μW', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2124, 94, '毫瓦', 'mW', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2125, 95, '千瓦特', 'kW', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2126, 96, '瓦特', 'W', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2127, 97, '卡路里', 'cal', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2128, 98, '千瓦时', 'kW·h', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2129, 99, '瓦时', 'Wh', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2130, 100, '电子伏', 'eV', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2131, 101, '千焦', 'kJ', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2132, 102, '焦耳', 'J', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2133, 103, '华氏度', '℉', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2134, 104, '开尔文', 'K', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2135, 105, '吨', 't', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2136, 106, '摄氏度', '°C', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2137, 107, '毫帕', 'mPa', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2138, 108, '百帕', 'hPa', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2139, 109, '千帕', 'kPa', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2140, 110, '帕斯卡', 'Pa', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2141, 111, '毫克', 'mg', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2142, 112, '克', 'g', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2143, 113, '千克', 'kg', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2144, 114, '牛', 'N', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2145, 115, '毫升', 'mL', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2146, 116, '升', 'L', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2147, 117, '立方毫米', 'mm³', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2148, 118, '立方厘米', 'cm³', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2149, 119, '立方千米', 'km³', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2150, 120, '立方米', 'm³', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2151, 121, '公顷', 'h㎡', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2152, 122, '平方厘米', 'c㎡', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2153, 123, '平方毫米', 'm㎡', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2154, 124, '平方千米', 'k㎡', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2155, 125, '平方米', '㎡', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2156, 126, '纳米', 'nm', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2157, 127, '微米', 'μm', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2158, 128, '毫米', 'mm', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2159, 129, '厘米', 'cm', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', to_date('2024-12-13 11:08:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2163, 1, '输入', '1', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:38:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2164, 2, '输出', '2', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:38:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2165, 1, 'HTTP', '1', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:39:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2166, 2, 'TCP', '2', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:40:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2167, 3, 'WEBSOCKET', '3', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:40:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2168, 10, 'MQTT', '10', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:40:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2169, 20, 'DATABASE', '20', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:41:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2170, 21, 'REDIS_STREAM', '21', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:41:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2171, 30, 'ROCKETMQ', '30', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:41:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2172, 31, 'RABBITMQ', '31', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:41:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2173, 32, 'KAFKA', '32', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', to_date('2025-03-09 12:41:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:40:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3000, 16, '百川智能', 'BaiChuan', 'ai_platform', 0, '', '', '', '1', to_date('2025-03-23 12:15:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-23 12:15:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3001, 50, 'Vben5.0 Ant Design Schema 模版', '40', 'infra_codegen_front_type', 0, '', '', NULL, '1', to_date('2025-04-23 21:47:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 12:01:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3002, 6, '支付宝余额', '6', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', to_date('2025-05-10 08:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-10 08:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT 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');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_dict_data_seq\n    START WITH 3003;\n\n-- ----------------------------\n-- Table structure for system_dict_type\n-- ----------------------------\nCREATE TABLE system_dict_type\n(\n    id           number                                  NOT NULL,\n    name         varchar2(100) DEFAULT ''                NULL,\n    type         varchar2(100) DEFAULT ''                NULL,\n    status       smallint      DEFAULT 0                 NOT NULL,\n    remark       varchar2(500) DEFAULT NULL              NULL,\n    creator      varchar2(64)  DEFAULT ''                NULL,\n    create_time  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      varchar2(64)  DEFAULT ''                NULL,\n    update_time  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      number(1, 0)  DEFAULT 0                 NOT NULL,\n    deleted_time date          DEFAULT NULL              NULL\n);\n\nALTER TABLE system_dict_type\n    ADD CONSTRAINT pk_system_dict_type PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dict_type.id IS '字典主键';\nCOMMENT ON COLUMN system_dict_type.name IS '字典名称';\nCOMMENT ON COLUMN system_dict_type.type IS '字典类型';\nCOMMENT ON COLUMN system_dict_type.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_type.remark IS '备注';\nCOMMENT ON COLUMN system_dict_type.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_type.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_type.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_type.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_type.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dict_type.deleted_time IS '删除时间';\nCOMMENT ON TABLE system_dict_type IS '字典类型表';\n\n-- ----------------------------\n-- Records of system_dict_type\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1, '用户性别', 'system_user_sex', 0, NULL, 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-16 20:29:32', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (6, '参数类型', 'infra_config_type', 0, NULL, 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:36:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (7, '通知类型', 'system_notice_type', 0, NULL, 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:35:26', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (9, '操作类型', 'infra_operate_type', 0, NULL, 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-14 12:44:01', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (10, '系统状态', 'common_status', 0, NULL, 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:21:28', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (11, 'Boolean 是否类型', 'infra_boolean_string', 0, 'boolean 转是否', '', to_date('2021-01-19 03:20:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:37:10', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (104, '登陆结果', 'system_login_result', 0, '登陆结果', '', to_date('2021-01-18 06:17:11', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:36:00', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (106, '代码生成模板类型', 'infra_codegen_template_type', 0, NULL, '', to_date('2021-02-05 07:08:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-16 20:26:50', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (107, '定时任务状态', 'infra_job_status', 0, NULL, '', to_date('2021-02-07 07:44:16', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:51:11', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (108, '定时任务日志状态', 'infra_job_log_status', 0, NULL, '', to_date('2021-02-08 10:03:51', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:50:43', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (109, '用户类型', 'user_type', 0, NULL, '', to_date('2021-02-26 00:15:51', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2021-02-26 00:15:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (110, 'API 异常数据的处理状态', 'infra_api_error_log_process_status', 0, NULL, '', to_date('2021-02-26 07:07:01', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-01 16:50:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (111, '短信渠道编码', 'system_sms_channel_code', 0, NULL, '1', to_date('2021-04-05 01:04:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 02:09:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (112, '短信模板的类型', 'system_sms_template_type', 0, NULL, '1', to_date('2021-04-05 21:50:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-01 16:35:06', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (113, '短信发送状态', 'system_sms_send_status', 0, NULL, '1', to_date('2021-04-11 20:18:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-01 16:35:09', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (114, '短信接收状态', 'system_sms_receive_status', 0, NULL, '1', to_date('2021-04-11 20:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-01 16:35:14', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (116, '登陆日志的类型', 'system_login_type', 0, '登陆日志的类型', '1', to_date('2021-10-06 00:50:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-01 16:35:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (117, 'OA 请假类型', 'bpm_oa_leave_type', 0, NULL, '1', to_date('2021-09-21 22:34:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-01-22 10:41:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (130, '支付渠道编码类型', 'pay_channel_code', 0, '支付渠道的编码', '1', to_date('2021-12-03 10:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-10 10:11:39', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (131, '支付回调状态', 'pay_notify_status', 0, '支付回调状态（包括退款回调）', '1', to_date('2021-12-03 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 18:09:43', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (132, '支付订单状态', 'pay_order_status', 0, '支付订单状态', '1', to_date('2021-12-03 11:17:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2021-12-03 11:17:50', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (134, '退款订单状态', 'pay_refund_status', 0, '退款订单状态', '1', to_date('2021-12-10 16:42:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-19 10:13:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (139, '流程实例的状态', 'bpm_process_instance_status', 0, '流程实例的状态', '1', to_date('2022-01-07 23:46:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-01-07 23:46:42', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (140, '流程实例的结果', 'bpm_task_status', 0, '流程实例的结果', '1', to_date('2022-01-07 23:48:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-08 22:42:03', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (141, '流程的表单类型', 'bpm_model_form_type', 0, '流程的表单类型', '103', to_date('2022-01-11 23:50:45', 'SYYYY-MM-DD HH24:MI:SS'), '103', to_date('2022-01-11 23:50:45', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (142, '任务分配规则的类型', 'bpm_task_candidate_strategy', 0, 'BPM 任务的候选人的策略', '103', to_date('2022-01-12 23:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '103', to_date('2024-03-06 02:53:59', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (144, '代码生成的场景枚举', 'infra_codegen_scene', 0, '代码生成的场景枚举', '1', to_date('2022-02-02 13:14:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-10 16:33:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (145, '角色类型', 'system_role_type', 0, '角色类型', '1', to_date('2022-02-16 13:01:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-16 13:01:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (146, '文件存储器', 'infra_file_storage', 0, '文件存储器', '1', to_date('2022-03-15 00:24:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-15 00:24:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (147, 'OAuth 2.0 授权类型', 'system_oauth2_grant_type', 0, 'OAuth 2.0 授权类型（模式）', '1', to_date('2022-05-12 00:20:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 16:25:49', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (149, '商品 SPU 状态', 'product_spu_status', 0, '商品 SPU 状态', '1', to_date('2022-10-24 21:19:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-10-24 21:19:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (150, '优惠类型', 'promotion_discount_type', 0, '优惠类型', '1', to_date('2022-11-01 12:46:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-01 12:46:06', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (151, '优惠劵模板的有限期类型', 'promotion_coupon_template_validity_type', 0, '优惠劵模板的有限期类型', '1', to_date('2022-11-02 00:06:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 00:08:26', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (152, '营销的商品范围', 'promotion_product_scope', 0, '营销的商品范围', '1', to_date('2022-11-02 00:28:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-02 00:28:01', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (153, '优惠劵的状态', 'promotion_coupon_status', 0, '优惠劵的状态', '1', to_date('2022-11-04 00:14:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 00:14:49', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (154, '优惠劵的领取方式', 'promotion_coupon_take_type', 0, '优惠劵的领取方式', '1', to_date('2022-11-04 19:12:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 19:12:27', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (155, '促销活动的状态', 'promotion_activity_status', 0, '促销活动的状态', '1', to_date('2022-11-04 22:54:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 22:54:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (156, '营销的条件类型', 'promotion_condition_type', 0, '营销的条件类型', '1', to_date('2022-11-04 22:59:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-04 22:59:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (157, '交易售后状态', 'trade_after_sale_status', 0, '交易售后状态', '1', to_date('2022-11-19 20:52:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 20:52:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (158, '交易售后的类型', 'trade_after_sale_type', 0, '交易售后的类型', '1', to_date('2022-11-19 21:04:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:04:09', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (159, '交易售后的方式', 'trade_after_sale_way', 0, '交易售后的方式', '1', to_date('2022-11-19 21:39:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-19 21:39:04', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (160, '终端', 'terminal', 0, '终端', '1', to_date('2022-12-10 10:50:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 10:53:11', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (161, '交易订单的类型', 'trade_order_type', 0, '交易订单的类型', '1', to_date('2022-12-10 16:33:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 16:33:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (162, '交易订单的状态', 'trade_order_status', 0, '交易订单的状态', '1', to_date('2022-12-10 16:48:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 16:48:44', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (163, '交易订单项的售后状态', 'trade_order_item_after_sale_status', 0, '交易订单项的售后状态', '1', to_date('2022-12-10 20:58:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 20:58:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (164, '公众号自动回复的请求关键字匹配模式', 'mp_auto_reply_request_match', 0, '公众号自动回复的请求关键字匹配模式', '1', to_date('2023-01-16 23:29:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-16 23:29:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (165, '公众号的消息类型', 'mp_message_type', 0, '公众号的消息类型', '1', to_date('2023-01-17 22:17:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 22:17:09', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (166, '邮件发送状态', 'system_mail_send_status', 0, '邮件发送状态', '1', to_date('2023-01-26 09:53:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-26 09:53:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (167, '站内信模版的类型', 'system_notify_template_type', 0, '站内信模版的类型', '1', to_date('2023-01-28 10:35:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 10:35:10', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (168, '代码生成的前端类型', 'infra_codegen_front_type', 0, '', '1', to_date('2023-04-12 23:57:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-04-12 23:57:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (170, '快递计费方式', 'trade_delivery_express_charge_mode', 0, '用于商城交易模块配送管理', '1', to_date('2023-05-21 22:45:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-05-21 22:45:03', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (171, '积分业务类型', 'member_point_biz_type', 0, '', '1', to_date('2023-06-10 12:15:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-06-28 13:48:20', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (173, '支付通知类型', 'pay_notify_type', 0, NULL, '1', to_date('2023-07-20 12:23:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-07-20 12:23:03', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (174, '会员经验业务类型', 'member_experience_biz_type', 0, NULL, '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (175, '交易配送类型', 'trade_delivery_type', 0, '', '1', to_date('2023-08-23 00:03:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-23 00:03:14', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (176, '分佣模式', 'brokerage_enabled_condition', 0, NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (177, '分销关系绑定模式', 'brokerage_bind_mode', 0, NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (178, '佣金提现类型', 'brokerage_withdraw_type', 0, NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (179, '佣金记录业务类型', 'brokerage_record_biz_type', 0, NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (180, '佣金记录状态', 'brokerage_record_status', 0, NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (181, '佣金提现状态', 'brokerage_withdraw_status', 0, NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (182, '佣金提现银行', 'brokerage_bank_name', 0, NULL, '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (183, '砍价记录的状态', 'promotion_bargain_record_status', 0, '', '1', to_date('2023-10-05 10:41:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-05 10:41:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (184, '拼团记录的状态', 'promotion_combination_record_status', 0, '', '1', to_date('2023-10-08 07:24:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-08 07:24:25', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (185, '回款-回款方式', 'crm_receivable_return_type', 0, '回款-回款方式', '1', to_date('2023-10-18 21:54:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-18 21:54:10', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (186, 'CRM 客户行业', 'crm_customer_industry', 0, 'CRM 客户所属行业', '1', to_date('2023-10-28 22:57:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-18 23:30:22', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (187, '客户等级', 'crm_customer_level', 0, 'CRM 客户等级', '1', to_date('2023-10-28 22:59:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 15:11:16', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (188, '客户来源', 'crm_customer_source', 0, 'CRM 客户来源', '1', to_date('2023-10-28 23:00:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 15:11:16', 'SYYYY-MM-DD HH24:MI:SS'), '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (600, 'Banner 位置', 'promotion_banner_position', 0, '', '1', to_date('2023-10-08 07:24:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 13:04:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (601, '社交类型', 'system_social_type', 0, '', '1', to_date('2023-11-04 13:03:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 13:03:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (604, '产品状态', 'crm_product_status', 0, '', '1', to_date('2023-10-30 21:47:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-30 21:48:45', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (605, 'CRM 数据权限的级别', 'crm_permission_level', 0, '', '1', to_date('2023-11-30 09:51:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 09:51:59', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (606, 'CRM 审批状态', 'crm_audit_status', 0, '', '1', to_date('2023-11-30 18:56:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-30 18:56:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (607, 'CRM 产品单位', 'crm_product_unit', 0, '', '1', to_date('2023-12-05 23:01:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 23:01:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (608, 'CRM 跟进方式', 'crm_follow_up_type', 0, '', '1', to_date('2024-01-15 20:48:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-15 20:48:05', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (610, '转账订单状态', 'pay_transfer_status', 0, '', '1', to_date('2023-10-28 16:18:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-28 16:18:32', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (611, 'ERP 库存明细的业务类型', 'erp_stock_record_biz_type', 0, 'ERP 库存明细的业务类型', '1', to_date('2024-02-05 18:07:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-05 18:07:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (612, 'ERP 审批状态', 'erp_audit_status', 0, '', '1', to_date('2024-02-06 00:00:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-06 00:00:07', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (613, 'BPM 监听器类型', 'bpm_process_listener_type', 0, '', '1', to_date('2024-03-23 12:52:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-09 15:54:28', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (615, 'BPM 监听器值类型', 'bpm_process_listener_value_type', 0, '', '1', to_date('2024-03-23 13:00:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-23 13:00:31', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (616, '时间间隔', 'date_interval', 0, '', '1', to_date('2024-03-29 22:50:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-29 22:50:09', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (619, 'CRM 商机结束状态类型', 'crm_business_end_status_type', 0, '', '1', to_date('2024-04-13 23:23:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-13 23:23:00', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (620, 'AI 模型平台', 'ai_platform', 0, '', '1', to_date('2024-05-09 22:27:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-09 22:27:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (621, 'AI 绘画状态', 'ai_image_status', 0, '', '1', to_date('2024-06-26 20:51:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-26 20:51:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (622, 'AI 音乐状态', 'ai_music_status', 0, '', '1', to_date('2024-06-27 22:45:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-28 00:56:27', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (623, 'AI 音乐生成模式', 'ai_generate_mode', 0, '', '1', to_date('2024-06-27 22:46:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-28 01:22:29', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (624, '写作语气', 'ai_write_tone', 0, '', '1', to_date('2024-07-07 15:19:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:19:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (625, '写作语言', 'ai_write_language', 0, '', '1', to_date('2024-07-07 15:18:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:18:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (626, '写作长度', 'ai_write_length', 0, '', '1', to_date('2024-07-07 15:18:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:18:41', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (627, '写作格式', 'ai_write_format', 0, '', '1', to_date('2024-07-07 15:14:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 15:14:34', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', to_date('2024-07-10 21:25:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-10 21:25:29', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', to_date('2024-08-26 15:21:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-26 15:21:43', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', to_date('2025-03-03 12:24:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 12:24:07', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1000, 'IoT 数据格式', 'iot_data_format', 0, '', '1', to_date('2024-08-10 11:52:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:06', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1001, 'IoT 产品设备类型', 'iot_product_device_type', 0, '', '1', to_date('2024-08-10 11:54:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1002, 'IoT 产品状态', 'iot_product_status', 0, '', '1', to_date('2024-08-10 12:06:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:10', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1003, 'IoT 数据校验级别', 'iot_validate_type', 0, '', '1', to_date('2024-09-06 20:05:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1004, 'IoT 联网方式', 'iot_net_type', 0, '', '1', to_date('2024-09-06 22:04:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:14', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1005, 'IoT 接入网关协议', 'iot_protocol_type', 0, '', '1', to_date('2024-09-06 22:20:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:16', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1006, 'IoT 设备状态', 'iot_device_state', 0, '', '1', to_date('2024-09-21 08:12:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:19', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1007, 'IoT 物模型功能类型', 'iot_thing_model_type', 0, '', '1', to_date('2024-09-29 20:02:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1008, 'IoT 插件部署方式', 'iot_plugin_deploy_type', 0, '', '1', to_date('2024-12-13 10:55:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:27', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1009, 'IoT 插件状态', 'iot_plugin_status', 0, '', '1', to_date('2024-12-13 11:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1010, 'IoT 插件类型', 'iot_plugin_type', 0, '', '1', to_date('2024-12-13 11:08:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:32', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1011, 'IoT 物模型单位', 'iot_thing_model_unit', 0, '', '1', to_date('2024-12-25 17:36:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:35', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1012, 'IoT 数据桥接的方向枚举', 'iot_data_bridge_direction_enum', 0, '', '1', to_date('2025-03-09 12:37:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-17 09:25:39', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1013, 'IoT 数据桥梁的类型枚举', 'iot_data_bridge_type_enum', 0, '', '1', to_date('2025-03-09 12:39:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-06 17:09:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', to_date('1970-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'));\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_dict_type_seq\n    START WITH 1014;\n\n-- ----------------------------\n-- Table structure for system_login_log\n-- ----------------------------\nCREATE TABLE system_login_log\n(\n    id          number                                 NOT NULL,\n    log_type    number                                 NOT NULL,\n    trace_id    varchar2(64) DEFAULT ''                NULL,\n    user_id     number       DEFAULT 0                 NOT NULL,\n    user_type   smallint     DEFAULT 0                 NOT NULL,\n    username    varchar2(50) DEFAULT ''                NULL,\n    result      smallint                               NOT NULL,\n    user_ip     varchar2(50)                           NULL,\n    user_agent  varchar2(512)                          NULL,\n    creator     varchar2(64) DEFAULT ''                NULL,\n    create_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64) DEFAULT ''                NULL,\n    update_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0) DEFAULT 0                 NOT NULL,\n    tenant_id   number       DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_login_log\n    ADD CONSTRAINT pk_system_login_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_login_log.id IS '访问ID';\nCOMMENT ON COLUMN system_login_log.log_type IS '日志类型';\nCOMMENT ON COLUMN system_login_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_login_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_login_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_login_log.username IS '用户账号';\nCOMMENT ON COLUMN system_login_log.result IS '登陆结果';\nCOMMENT ON COLUMN system_login_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_login_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_login_log.creator IS '创建者';\nCOMMENT ON COLUMN system_login_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_login_log.updater IS '更新者';\nCOMMENT ON COLUMN system_login_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_login_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_login_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_login_log IS '系统访问记录';\n\nCREATE SEQUENCE system_login_log_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_mail_account\n-- ----------------------------\nCREATE TABLE system_mail_account\n(\n    id              number                                 NOT NULL,\n    mail            varchar2(255)                          NULL,\n    username        varchar2(255)                          NULL,\n    password        varchar2(255)                          NULL,\n    host            varchar2(255)                          NULL,\n    port            number                                 NOT NULL,\n    ssl_enable      number(1, 0) DEFAULT '0'               NOT NULL,\n    starttls_enable number(1, 0) DEFAULT '0'               NOT NULL,\n    creator         varchar2(64) DEFAULT ''                NULL,\n    create_time     date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         varchar2(64) DEFAULT ''                NULL,\n    update_time     date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         number(1, 0) DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_mail_account\n    ADD CONSTRAINT pk_system_mail_account PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_account.id IS '主键';\nCOMMENT ON COLUMN system_mail_account.mail IS '邮箱';\nCOMMENT ON COLUMN system_mail_account.username IS '用户名';\nCOMMENT ON COLUMN system_mail_account.password IS '密码';\nCOMMENT ON COLUMN system_mail_account.host IS 'SMTP 服务器域名';\nCOMMENT ON COLUMN system_mail_account.port IS 'SMTP 服务器端口';\nCOMMENT ON COLUMN system_mail_account.ssl_enable IS '是否开启 SSL';\nCOMMENT ON COLUMN system_mail_account.starttls_enable IS '是否开启 STARTTLS';\nCOMMENT ON COLUMN system_mail_account.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_account.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_account.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_account.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_account.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_account IS '邮箱账号表';\n\n-- ----------------------------\n-- Records of system_mail_account\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, '0', '0', '1', to_date('2023-01-25 17:39:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-04 16:34:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, '1', '0', '1', to_date('2023-01-26 01:26:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-04-12 22:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, '0', '0', '1', to_date('2023-01-27 15:06:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-27 07:08:36', 'SYYYY-MM-DD HH24:MI:SS'), '1');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (4, '7685413x@qq.com', '2', '3', '4', 5, '1', '0', '1', to_date('2023-04-12 23:05:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-04-12 15:05:11', 'SYYYY-MM-DD HH24:MI:SS'), '1');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_mail_account_seq\n    START WITH 5;\n\n-- ----------------------------\n-- Table structure for system_mail_log\n-- ----------------------------\nCREATE TABLE system_mail_log\n(\n    id                number                                   NOT NULL,\n    user_id           number         DEFAULT NULL              NULL,\n    user_type         smallint       DEFAULT NULL              NULL,\n    to_mail           varchar2(255)                            NULL,\n    account_id        number                                   NOT NULL,\n    from_mail         varchar2(255)                            NULL,\n    template_id       number                                   NOT NULL,\n    template_code     varchar2(63)                             NULL,\n    template_nickname varchar2(255)  DEFAULT NULL              NULL,\n    template_title    varchar2(255)                            NULL,\n    template_content  varchar2(4000)                           NULL,\n    template_params   varchar2(255)                            NULL,\n    send_status       smallint       DEFAULT 0                 NOT NULL,\n    send_time         date           DEFAULT NULL              NULL,\n    send_message_id   varchar2(255)  DEFAULT NULL              NULL,\n    send_exception    varchar2(4000) DEFAULT NULL              NULL,\n    creator           varchar2(64)   DEFAULT ''                NULL,\n    create_time       date           DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater           varchar2(64)   DEFAULT ''                NULL,\n    update_time       date           DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted           number(1, 0)   DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_mail_log\n    ADD CONSTRAINT pk_system_mail_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_log.id IS '编号';\nCOMMENT ON COLUMN system_mail_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_mail_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_mail_log.to_mail IS '接收邮箱地址';\nCOMMENT ON COLUMN system_mail_log.account_id IS '邮箱账号编号';\nCOMMENT ON COLUMN system_mail_log.from_mail IS '发送邮箱地址';\nCOMMENT ON COLUMN system_mail_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_mail_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_mail_log.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_mail_log.template_title IS '邮件标题';\nCOMMENT ON COLUMN system_mail_log.template_content IS '邮件内容';\nCOMMENT ON COLUMN system_mail_log.template_params IS '邮件参数';\nCOMMENT ON COLUMN system_mail_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_mail_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_mail_log.send_message_id IS '发送返回的消息 ID';\nCOMMENT ON COLUMN system_mail_log.send_exception IS '发送异常';\nCOMMENT ON COLUMN system_mail_log.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_log.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_log IS '邮件日志表';\n\nCREATE SEQUENCE system_mail_log_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_mail_template\n-- ----------------------------\nCREATE TABLE system_mail_template\n(\n    id          number                                  NOT NULL,\n    name        varchar2(63)                            NULL,\n    code        varchar2(63)                            NULL,\n    account_id  number                                  NOT NULL,\n    nickname    varchar2(255) DEFAULT NULL              NULL,\n    title       varchar2(255)                           NULL,\n    content     varchar2(4000)                          NULL,\n    params      varchar2(255)                           NULL,\n    status      smallint                                NOT NULL,\n    remark      varchar2(255) DEFAULT NULL              NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_mail_template\n    ADD CONSTRAINT pk_system_mail_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_template.id IS '编号';\nCOMMENT ON COLUMN system_mail_template.name IS '模板名称';\nCOMMENT ON COLUMN system_mail_template.code IS '模板编码';\nCOMMENT ON COLUMN system_mail_template.account_id IS '发送的邮箱账号编号';\nCOMMENT ON COLUMN system_mail_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_mail_template.title IS '模板标题';\nCOMMENT ON COLUMN system_mail_template.content IS '模板内容';\nCOMMENT ON COLUMN system_mail_template.params IS '参数数组';\nCOMMENT ON COLUMN system_mail_template.status IS '开启状态';\nCOMMENT ON COLUMN system_mail_template.remark IS '备注';\nCOMMENT ON COLUMN system_mail_template.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_template.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_template IS '邮件模版表';\n\n-- ----------------------------\n-- Records of system_mail_template\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '后台用户短信登录', 'admin-sms-login', 1, '奥特曼', '你猜我猜', '<p>您的验证码是{code}，名字是{name}</p>', '[\"code\",\"name\"]', 0, '3', '1', to_date('2021-10-11 08:10:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 19:51:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (14, '测试模版', 'test_01', 2, '芋艿', '一个标题', '<p>你是 {key01} 吗？</p><p><br></p><p>是的话，赶紧 {key02} 一下！</p>', '[\"key01\",\"key02\"]', 0, NULL, '1', to_date('2023-01-26 01:27:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-27 10:32:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (15, '3', '2', 2, '7', '4', '<p>45</p>', '[]', 1, '80', '1', to_date('2023-01-27 15:50:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-27 16:34:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_mail_template_seq\n    START WITH 16;\n\n-- ----------------------------\n-- Table structure for system_menu\n-- ----------------------------\nCREATE TABLE system_menu\n(\n    id             number                                  NOT NULL,\n    name           varchar2(50)                            NULL,\n    permission     varchar2(100) DEFAULT ''                NULL,\n    type           smallint                                NOT NULL,\n    sort           number        DEFAULT 0                 NOT NULL,\n    parent_id      number        DEFAULT 0                 NOT NULL,\n    path           varchar2(200) DEFAULT ''                NULL,\n    icon           varchar2(100) DEFAULT '#'               NULL,\n    component      varchar2(255) DEFAULT NULL              NULL,\n    component_name varchar2(255) DEFAULT NULL              NULL,\n    status         smallint      DEFAULT 0                 NOT NULL,\n    visible        number(1, 0)  DEFAULT '1'               NOT NULL,\n    keep_alive     number(1, 0)  DEFAULT '1'               NOT NULL,\n    always_show    number(1, 0)  DEFAULT '1'               NOT NULL,\n    creator        varchar2(64)  DEFAULT ''                NULL,\n    create_time    date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar2(64)  DEFAULT ''                NULL,\n    update_time    date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_menu\n    ADD CONSTRAINT pk_system_menu PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_menu.id IS '菜单ID';\nCOMMENT ON COLUMN system_menu.name IS '菜单名称';\nCOMMENT ON COLUMN system_menu.permission IS '权限标识';\nCOMMENT ON COLUMN system_menu.type IS '菜单类型';\nCOMMENT ON COLUMN system_menu.sort IS '显示顺序';\nCOMMENT ON COLUMN system_menu.parent_id IS '父菜单ID';\nCOMMENT ON COLUMN system_menu.path IS '路由地址';\nCOMMENT ON COLUMN system_menu.icon IS '菜单图标';\nCOMMENT ON COLUMN system_menu.component IS '组件路径';\nCOMMENT ON COLUMN system_menu.component_name IS '组件名';\nCOMMENT ON COLUMN system_menu.status IS '菜单状态';\nCOMMENT ON COLUMN system_menu.visible IS '是否可见';\nCOMMENT ON COLUMN system_menu.keep_alive IS '是否缓存';\nCOMMENT ON COLUMN system_menu.always_show IS '是否总是显示';\nCOMMENT ON COLUMN system_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_menu.deleted IS '是否删除';\nCOMMENT ON TABLE system_menu IS '菜单权限表';\n\n-- ----------------------------\n-- Records of system_menu\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-15 21:30:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-01 08:28:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5, 'OA 示例', '', 1, 40, 1185, 'oa', 'fa:road', NULL, NULL, 0, '1', '1', '1', 'admin', to_date('2021-09-20 16:26:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:38:13', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-15 21:30:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (101, '角色管理', '', 2, 2, 1, 'role', 'ep:user', 'system/role/index', 'SystemRole', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-01 18:35:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (102, '菜单管理', '', 2, 3, 1, 'menu', 'ep:menu', 'system/menu/index', 'SystemMenu', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:03:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (103, '部门管理', '', 2, 4, 1, 'dept', 'fa:address-card', 'system/dept/index', 'SystemDept', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:06:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (104, '岗位管理', '', 2, 5, 1, 'post', 'fa:address-book-o', 'system/post/index', 'SystemPost', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:06:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (105, '字典管理', '', 2, 6, 1, 'dict', 'ep:collection', 'system/dict/index', 'SystemDictType', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:07:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (106, '配置管理', '', 2, 8, 2, 'config', 'fa:connectdevelop', 'infra/config/index', 'InfraConfig', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 00:02:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (107, '通知公告', '', 2, 4, 2739, 'notice', 'ep:takeaway-box', 'system/notice/index', 'SystemNotice', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-22 23:56:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (108, '审计日志', '', 1, 9, 1, 'log', 'ep:document-copy', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:08:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (109, '令牌管理', '', 2, 2, 1261, 'token', 'fa:key', 'system/oauth2/token/index', 'SystemTokenClient', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:13:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (110, '定时任务', '', 2, 7, 2, 'job', 'fa-solid:tasks', 'infra/job/index', 'InfraJob', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:57:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (111, 'MySQL 监控', '', 2, 1, 2740, 'druid', 'fa-solid:box', 'infra/druid/index', 'InfraDruid', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 00:05:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (112, 'Java 监控', '', 2, 3, 2740, 'admin-server', 'ep:coffee-cup', 'infra/server/index', 'InfraAdminServer', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 00:06:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (113, 'Redis 监控', '', 2, 2, 2740, 'redis', 'fa:reddit-square', 'infra/redis/index', 'InfraRedis', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 00:06:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (114, '表单构建', 'infra:build:list', 2, 2, 2, 'build', 'fa:wpforms', 'infra/build/index', 'InfraBuild', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:51:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (115, '代码生成', 'infra:codegen:query', 2, 1, 2, 'codegen', 'ep:document-copy', 'infra/codegen/index', 'InfraCodegen', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:51:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (116, 'API 接口', 'infra:swagger:list', 2, 3, 2, 'swagger', 'fa:fighter-jet', 'infra/swagger/index', 'InfraSwagger', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 00:01:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (500, '操作日志', '', 2, 1, 108, 'operate-log', 'ep:position', 'system/operatelog/index', 'SystemOperateLog', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:09:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (501, '登录日志', '', 2, 2, 108, 'login-log', 'ep:promotion', 'system/loginlog/index', 'SystemLoginLog', 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:10:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1001, '用户查询', 'system:user:query', 3, 1, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1002, '用户新增', 'system:user:create', 3, 2, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1003, '用户修改', 'system:user:update', 3, 3, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1004, '用户删除', 'system:user:delete', 3, 4, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1005, '用户导出', 'system:user:export', 3, 5, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1006, '用户导入', 'system:user:import', 3, 6, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1007, '重置密码', 'system:user:update-password', 3, 7, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1008, '角色查询', 'system:role:query', 3, 1, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1009, '角色新增', 'system:role:create', 3, 2, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1010, '角色修改', 'system:role:update', 3, 3, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1011, '角色删除', 'system:role:delete', 3, 4, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1012, '角色导出', 'system:role:export', 3, 5, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1013, '菜单查询', 'system:menu:query', 3, 1, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1014, '菜单新增', 'system:menu:create', 3, 2, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1015, '菜单修改', 'system:menu:update', 3, 3, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1016, '菜单删除', 'system:menu:delete', 3, 4, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1017, '部门查询', 'system:dept:query', 3, 1, 103, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1018, '部门新增', 'system:dept:create', 3, 2, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1019, '部门修改', 'system:dept:update', 3, 3, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1020, '部门删除', 'system:dept:delete', 3, 4, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1021, '岗位查询', 'system:post:query', 3, 1, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1022, '岗位新增', 'system:post:create', 3, 2, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1023, '岗位修改', 'system:post:update', 3, 3, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1024, '岗位删除', 'system:post:delete', 3, 4, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1025, '岗位导出', 'system:post:export', 3, 5, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1026, '字典查询', 'system:dict:query', 3, 1, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1027, '字典新增', 'system:dict:create', 3, 2, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1028, '字典修改', 'system:dict:update', 3, 3, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1029, '字典删除', 'system:dict:delete', 3, 4, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1030, '字典导出', 'system:dict:export', 3, 5, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1031, '配置查询', 'infra:config:query', 3, 1, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1032, '配置新增', 'infra:config:create', 3, 2, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1033, '配置修改', 'infra:config:update', 3, 3, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1034, '配置删除', 'infra:config:delete', 3, 4, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1035, '配置导出', 'infra:config:export', 3, 5, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1036, '公告查询', 'system:notice:query', 3, 1, 107, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1037, '公告新增', 'system:notice:create', 3, 2, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1038, '公告修改', 'system:notice:update', 3, 3, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1039, '公告删除', 'system:notice:delete', 3, 4, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1040, '操作查询', 'system:operate-log:query', 3, 1, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1042, '日志导出', 'system:operate-log:export', 3, 2, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1043, '登录查询', 'system:login-log:query', 3, 1, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1045, '日志导出', 'system:login-log:export', 3, 3, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1046, '令牌列表', 'system:oauth2-token:page', 3, 1, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-09 23:54:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1048, '令牌删除', 'system:oauth2-token:delete', 3, 2, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-09 23:54:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1050, '任务新增', 'infra:job:create', 3, 2, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1051, '任务修改', 'infra:job:update', 3, 3, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1052, '任务删除', 'infra:job:delete', 3, 4, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1053, '状态修改', 'infra:job:update', 3, 5, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1054, '任务导出', 'infra:job:export', 3, 7, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1056, '生成修改', 'infra:codegen:update', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1057, '生成删除', 'infra:codegen:delete', 3, 3, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1058, '导入代码', 'infra:codegen:create', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1059, '预览代码', 'infra:codegen:preview', 3, 4, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1060, '生成代码', 'infra:codegen:download', 3, 5, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1063, '设置角色菜单权限', 'system:permission:assign-role-menu', 3, 6, 101, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-01-06 17:53:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1064, '设置角色数据权限', 'system:permission:assign-role-data-scope', 3, 7, 101, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-01-06 17:56:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1065, '设置用户角色', 'system:permission:assign-user-role', 3, 8, 101, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-01-07 10:23:28', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1066, '获得 Redis 监控信息', 'infra:redis:get-monitor-info', 3, 1, 113, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-01-26 01:02:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1067, '获得 Redis Key 列表', 'infra:redis:get-key-list', 3, 2, 113, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-01-26 01:02:52', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1070, '代码生成案例', '', 1, 1, 2, 'demo', 'ep:aim', 'infra/testDemo/index', NULL, 0, '1', '1', '1', '', to_date('2021-02-06 12:42:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-15 23:45:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1075, '任务触发', 'infra:job:trigger', 3, 8, 110, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-02-07 13:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1077, '链路追踪', '', 2, 4, 2740, 'skywalking', 'fa:eye', 'infra/skywalking/index', 'InfraSkyWalking', 0, '1', '1', '1', '', to_date('2021-02-08 20:41:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 00:07:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1078, '访问日志', '', 2, 1, 1083, 'api-access-log', 'ep:place', 'infra/apiAccessLog/index', 'InfraApiAccessLog', 0, '1', '1', '1', '', to_date('2021-02-26 01:32:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:54:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1082, '日志导出', 'infra:api-access-log:export', 3, 2, 1078, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-02-26 01:32:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1083, 'API 日志', '', 2, 4, 2, 'log', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '', to_date('2021-02-26 02:18:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-22 23:58:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1084, '错误日志', 'infra:api-error-log:query', 2, 2, 1083, 'api-error-log', 'ep:warning-filled', 'infra/apiErrorLog/index', 'InfraApiErrorLog', 0, '1', '1', '1', '', to_date('2021-02-26 07:53:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:55:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1085, '日志处理', 'infra:api-error-log:update-status', 3, 2, 1084, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-02-26 07:53:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1086, '日志导出', 'infra:api-error-log:export', 3, 3, 1084, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-02-26 07:53:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1087, '任务查询', 'infra:job:query', 3, 1, 110, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2021-03-10 01:26:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1088, '日志查询', 'infra:api-access-log:query', 3, 1, 1078, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2021-03-10 01:28:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1089, '日志查询', 'infra:api-error-log:query', 3, 1, 1084, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2021-03-10 01:29:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1090, '文件列表', '', 2, 5, 1243, 'file', 'ep:upload-filled', 'infra/file/index', 'InfraFile', 0, '1', '1', '1', '', to_date('2021-03-12 20:16:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:53:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-03-12 20:16:20', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1092, '文件删除', 'infra:file:delete', 3, 4, 1090, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-03-12 20:16:20', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1093, '短信管理', '', 1, 1, 2739, 'sms', 'ep:message', NULL, NULL, 0, '1', '1', '1', '1', to_date('2021-04-05 01:10:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-22 23:56:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1094, '短信渠道', '', 2, 0, 1093, 'sms-channel', 'fa:stack-exchange', 'system/sms/channel/index', 'SystemSmsChannel', 0, '1', '1', '1', '', to_date('2021-04-01 11:07:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:15:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1095, '短信渠道查询', 'system:sms-channel:query', 3, 1, 1094, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-01 11:07:15', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1096, '短信渠道创建', 'system:sms-channel:create', 3, 2, 1094, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-01 11:07:15', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1097, '短信渠道更新', 'system:sms-channel:update', 3, 3, 1094, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-01 11:07:15', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1098, '短信渠道删除', 'system:sms-channel:delete', 3, 4, 1094, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-01 11:07:15', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1100, '短信模板', '', 2, 1, 1093, 'sms-template', 'ep:connection', 'system/sms/template/index', 'SystemSmsTemplate', 0, '1', '1', '1', '', to_date('2021-04-01 17:35:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:16:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1101, '短信模板查询', 'system:sms-template:query', 3, 1, 1100, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-01 17:35:17', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1102, '短信模板创建', 'system:sms-template:create', 3, 2, 1100, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-01 17:35:17', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1103, '短信模板更新', 'system:sms-template:update', 3, 3, 1100, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-01 17:35:17', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1104, '短信模板删除', 'system:sms-template:delete', 3, 4, 1100, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-01 17:35:17', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1105, '短信模板导出', 'system:sms-template:export', 3, 5, 1100, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-01 17:35:17', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1106, '发送测试短信', 'system:sms-template:send-sms', 3, 6, 1100, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2021-04-11 00:26:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1107, '短信日志', '', 2, 2, 1093, 'sms-log', 'fa:edit', 'system/sms/log/index', 'SystemSmsLog', 0, '1', '1', '1', '', to_date('2021-04-11 08:37:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:49:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1108, '短信日志查询', 'system:sms-log:query', 3, 1, 1107, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-11 08:37:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1109, '短信日志导出', 'system:sms-log:export', 3, 5, 1107, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-04-11 08:37:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1117, '支付管理', '', 1, 30, 0, '/pay', 'ep:money', NULL, NULL, 0, '1', '1', '1', '1', to_date('2021-12-25 16:43:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:58:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1118, '请假查询', '', 2, 0, 5, 'leave', 'fa:leanpub', 'bpm/oa/leave/index', 'BpmOALeave', 0, '1', '1', '1', '', to_date('2021-09-20 08:51:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:38:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1119, '请假申请查询', 'bpm:oa-leave:query', 3, 1, 1118, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-09-20 08:51:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1120, '请假申请创建', 'bpm:oa-leave:create', 3, 2, 1118, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-09-20 08:51:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1126, '应用信息', '', 2, 1, 1117, 'app', 'fa:apple', 'pay/app/index', 'PayApp', 0, '1', '1', '1', '', to_date('2021-11-10 01:13:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:59:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1127, '支付应用信息查询', 'pay:app:query', 3, 1, 1126, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-11-10 01:13:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1128, '支付应用信息创建', 'pay:app:create', 3, 2, 1126, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-11-10 01:13:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1129, '支付应用信息更新', 'pay:app:update', 3, 3, 1126, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-11-10 01:13:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1130, '支付应用信息删除', 'pay:app:delete', 3, 4, 1126, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-11-10 01:13:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1132, '秘钥解析', 'pay:channel:parsing', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2021-11-08 15:15:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1133, '支付商户信息查询', 'pay:merchant:query', 3, 1, 1132, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-11-10 01:13:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1134, '支付商户信息创建', 'pay:merchant:create', 3, 2, 1132, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-11-10 01:13:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1135, '支付商户信息更新', 'pay:merchant:update', 3, 3, 1132, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-11-10 01:13:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1136, '支付商户信息删除', 'pay:merchant:delete', 3, 4, 1132, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-11-10 01:13:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1137, '支付商户信息导出', 'pay:merchant:export', 3, 5, 1132, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-11-10 01:13:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1138, '租户列表', '', 2, 0, 1224, 'list', 'ep:house', 'system/tenant/index', 'SystemTenant', 0, '1', '1', '1', '', to_date('2021-12-14 12:31:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:01:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1139, '租户查询', 'system:tenant:query', 3, 1, 1138, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-14 12:31:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1140, '租户创建', 'system:tenant:create', 3, 2, 1138, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-14 12:31:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1141, '租户更新', 'system:tenant:update', 3, 3, 1138, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-14 12:31:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1142, '租户删除', 'system:tenant:delete', 3, 4, 1138, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-14 12:31:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1143, '租户导出', 'system:tenant:export', 3, 5, 1138, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-14 12:31:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1150, '秘钥解析', '', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2021-11-08 15:15:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1161, '退款订单', '', 2, 3, 1117, 'refund', 'fa:registered', 'pay/refund/index', 'PayRefund', 0, '1', '1', '1', '', to_date('2021-12-25 08:29:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:59:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1162, '退款订单查询', 'pay:refund:query', 3, 1, 1161, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-25 08:29:07', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1166, '退款订单导出', 'pay:refund:export', 3, 5, 1161, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-25 08:29:07', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1173, '支付订单', '', 2, 2, 1117, 'order', 'fa:cc-paypal', 'pay/order/index', 'PayOrder', 0, '1', '1', '1', '', to_date('2021-12-25 08:49:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:59:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1174, '支付订单查询', 'pay:order:query', 3, 1, 1173, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-25 08:49:43', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1178, '支付订单导出', 'pay:order:export', 3, 5, 1173, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-25 08:49:43', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1185, '工作流程', '', 1, 50, 0, '/bpm', 'fa:medium', NULL, NULL, 0, '1', '1', '1', '1', to_date('2021-12-30 20:26:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:43:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1186, '流程管理', '', 1, 10, 1185, 'manager', 'fa:dedent', NULL, NULL, 0, '1', '1', '1', '1', to_date('2021-12-30 20:28:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:36:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1187, '流程表单', '', 2, 2, 1186, 'form', 'fa:hdd-o', 'bpm/form/index', 'BpmForm', 0, '1', '1', '1', '', to_date('2021-12-30 12:38:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-19 12:25:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1188, '表单查询', 'bpm:form:query', 3, 1, 1187, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-30 12:38:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1189, '表单创建', 'bpm:form:create', 3, 2, 1187, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-30 12:38:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1190, '表单更新', 'bpm:form:update', 3, 3, 1187, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-30 12:38:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1191, '表单删除', 'bpm:form:delete', 3, 4, 1187, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-30 12:38:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1192, '表单导出', 'bpm:form:export', 3, 5, 1187, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2021-12-30 12:38:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1193, '流程模型', '', 2, 1, 1186, 'model', 'fa-solid:project-diagram', 'bpm/model/index', 'BpmModel', 0, '1', '1', '1', '1', to_date('2021-12-31 23:24:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-19 12:25:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1194, '模型查询', 'bpm:model:query', 3, 1, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-03 19:01:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1195, '模型创建', 'bpm:model:create', 3, 2, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-03 19:01:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1197, '模型更新', 'bpm:model:update', 3, 4, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-03 19:02:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1198, '模型删除', 'bpm:model:delete', 3, 5, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-03 19:02:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1199, '模型发布', 'bpm:model:deploy', 3, 6, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-03 19:03:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1200, '审批中心', '', 2, 20, 1185, 'task', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '1', to_date('2022-01-07 23:51:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-21 00:33:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1201, '我的流程', '', 2, 1, 1200, 'my', 'fa-solid:book', 'bpm/processInstance/index', 'BpmProcessInstanceMy', 0, '1', '1', '1', '', to_date('2022-01-07 15:53:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-21 23:52:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1202, '流程实例的查询', 'bpm:process-instance:query', 3, 1, 1201, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-01-07 15:53:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1207, '待办任务', '', 2, 10, 1200, 'todo', 'fa:slack', 'bpm/task/todo/index', 'BpmTodoTask', 0, '1', '1', '1', '1', to_date('2022-01-08 10:33:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:37:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1208, '已办任务', '', 2, 20, 1200, 'done', 'fa:delicious', 'bpm/task/done/index', 'BpmDoneTask', 0, '1', '1', '1', '1', to_date('2022-01-08 10:34:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:37:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1209, '用户分组', '', 2, 4, 1186, 'user-group', 'fa:user-secret', 'bpm/group/index', 'BpmUserGroup', 0, '1', '1', '1', '', to_date('2022-01-14 02:14:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-21 23:55:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1210, '用户组查询', 'bpm:user-group:query', 3, 1, 1209, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-01-14 02:14:20', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1211, '用户组创建', 'bpm:user-group:create', 3, 2, 1209, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-01-14 02:14:20', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1212, '用户组更新', 'bpm:user-group:update', 3, 3, 1209, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-01-14 02:14:20', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1213, '用户组删除', 'bpm:user-group:delete', 3, 4, 1209, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-01-14 02:14:20', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1215, '流程定义查询', 'bpm:process-definition:query', 3, 10, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-23 00:21:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1216, '流程任务分配规则查询', 'bpm:task-assign-rule:query', 3, 20, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-23 00:26:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1217, '流程任务分配规则创建', 'bpm:task-assign-rule:create', 3, 21, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-23 00:28:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1218, '流程任务分配规则更新', 'bpm:task-assign-rule:update', 3, 22, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-23 00:28:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1219, '流程实例的创建', 'bpm:process-instance:create', 3, 2, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-23 00:36:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1220, '流程实例的取消', 'bpm:process-instance:cancel', 3, 3, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-23 00:36:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1221, '流程任务的查询', 'bpm:task:query', 3, 1, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-23 00:38:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1222, '流程任务的更新', 'bpm:task:update', 3, 2, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-01-23 00:39:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1224, '租户管理', '', 2, 0, 1, 'tenant', 'fa-solid:house-user', NULL, NULL, 0, '1', '1', '1', '1', to_date('2022-02-20 01:41:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 00:59:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1225, '租户套餐', '', 2, 0, 1224, 'package', 'fa:bars', 'system/tenantPackage/index', 'SystemTenantPackage', 0, '1', '1', '1', '', to_date('2022-02-19 17:44:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:01:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1226, '租户套餐查询', 'system:tenant-package:query', 3, 1, 1225, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-02-19 17:44:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1227, '租户套餐创建', 'system:tenant-package:create', 3, 2, 1225, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-02-19 17:44:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1228, '租户套餐更新', 'system:tenant-package:update', 3, 3, 1225, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-02-19 17:44:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1229, '租户套餐删除', 'system:tenant-package:delete', 3, 4, 1225, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-02-19 17:44:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1237, '文件配置', '', 2, 0, 1243, 'file-config', 'fa-solid:file-signature', 'infra/fileConfig/index', 'InfraFileConfig', 0, '1', '1', '1', '', to_date('2022-03-15 14:35:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:52:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1238, '文件配置查询', 'infra:file-config:query', 3, 1, 1237, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-03-15 14:35:28', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1239, '文件配置创建', 'infra:file-config:create', 3, 2, 1237, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-03-15 14:35:28', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1240, '文件配置更新', 'infra:file-config:update', 3, 3, 1237, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-03-15 14:35:28', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-03-15 14:35:28', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-03-15 14:35:28', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-20 17:03:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, '1', '1', '1', '1', to_date('2022-03-16 23:47:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 00:02:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, '1', '1', '1', '1', to_date('2022-04-23 01:03:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-29 17:45:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, '1', '1', '1', '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:51:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1258, '数据源配置更新', 'infra:data-source-config:update', 3, 3, 1255, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1259, '数据源配置删除', 'infra:data-source-config:delete', 3, 4, 1255, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1260, '数据源配置导出', 'infra:data-source-config:export', 3, 5, 1255, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-04-27 14:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1261, 'OAuth 2.0', '', 2, 10, 1, 'oauth2', 'fa:dashcube', NULL, NULL, 0, '1', '1', '1', '1', to_date('2022-05-09 23:38:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:12:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1263, '应用管理', '', 2, 0, 1261, 'oauth2/application', 'fa:hdd-o', 'system/oauth2/client/index', 'SystemOAuth2Client', 0, '1', '1', '1', '', to_date('2022-05-10 16:26:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:13:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1264, '客户端查询', 'system:oauth2-client:query', 3, 1, 1263, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-05-10 16:26:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 00:31:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1265, '客户端创建', 'system:oauth2-client:create', 3, 2, 1263, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-05-10 16:26:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 00:31:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1266, '客户端更新', 'system:oauth2-client:update', 3, 3, 1263, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-05-10 16:26:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 00:31:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1267, '客户端删除', 'system:oauth2-client:delete', 3, 4, 1263, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-05-10 16:26:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-11 00:31:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1281, '报表管理', '', 2, 40, 0, '/report', 'ep:pie-chart', NULL, NULL, 0, '1', '1', '1', '1', to_date('2022-07-10 20:22:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:33:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1282, '报表设计器', '', 2, 1, 1281, 'jimu-report', 'ep:trend-charts', 'report/jmreport/index', 'JimuReport', 0, '1', '1', '1', '1', to_date('2022-07-10 20:26:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-03 09:57:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2000, '商品中心', '', 1, 60, 2362, 'product', 'fa:product-hunt', NULL, NULL, 0, '1', '1', '1', '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-30 11:52:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2002, '商品分类', '', 2, 2, 2000, 'category', 'ep:cellphone', 'mall/product/category/index', 'ProductCategory', 0, '1', '1', '1', '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-21 10:27:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2003, '分类查询', 'product:category:query', 3, 1, 2002, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2004, '分类创建', 'product:category:create', 3, 2, 2002, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2005, '分类更新', 'product:category:update', 3, 3, 2002, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2006, '分类删除', 'product:category:delete', 3, 4, 2002, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-29 15:53:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2008, '商品品牌', '', 2, 3, 2000, 'brand', 'ep:chicken', 'mall/product/brand/index', 'ProductBrand', 0, '1', '1', '1', '', to_date('2022-07-30 13:52:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-21 10:27:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2009, '品牌查询', 'product:brand:query', 3, 1, 2008, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-30 13:52:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-30 13:52:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2010, '品牌创建', 'product:brand:create', 3, 2, 2008, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-30 13:52:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-30 13:52:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2011, '品牌更新', 'product:brand:update', 3, 3, 2008, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-30 13:52:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-30 13:52:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2012, '品牌删除', 'product:brand:delete', 3, 4, 2008, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-30 13:52:44', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-30 13:52:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2014, '商品列表', '', 2, 1, 2000, 'spu', 'ep:apple', 'mall/product/spu/index', 'ProductSpu', 0, '1', '1', '1', '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-21 10:27:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2015, '商品查询', 'product:spu:query', 3, 1, 2014, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2016, '商品创建', 'product:spu:create', 3, 2, 2014, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2017, '商品更新', 'product:spu:update', 3, 3, 2014, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2018, '商品删除', 'product:spu:delete', 3, 4, 2014, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2019, '商品属性', '', 2, 4, 2000, 'property', 'ep:cold-drink', 'mall/product/property/index', 'ProductProperty', 0, '1', '1', '1', '', to_date('2022-08-01 14:55:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-26 11:01:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2020, '规格查询', 'product:property:query', 3, 1, 2019, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-08-01 14:55:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-12-12 20:26:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2021, '规格创建', 'product:property:create', 3, 2, 2019, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-08-01 14:55:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-12-12 20:26:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2022, '规格更新', 'product:property:update', 3, 3, 2019, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-08-01 14:55:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-12-12 20:26:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2023, '规格删除', 'product:property:delete', 3, 4, 2019, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-08-01 14:55:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-12-12 20:26:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2025, 'Banner', '', 2, 100, 2387, 'banner', 'fa:bandcamp', 'mall/promotion/banner/index', NULL, 0, '1', '1', '1', '', to_date('2022-08-01 14:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-24 20:20:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2026, 'Banner查询', 'promotion:banner:query', 3, 1, 2025, '', '', '', '', 0, '1', '1', '1', '', to_date('2022-08-01 14:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-24 20:20:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2027, 'Banner创建', 'promotion:banner:create', 3, 2, 2025, '', '', '', '', 0, '1', '1', '1', '', to_date('2022-08-01 14:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-24 20:20:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2028, 'Banner更新', 'promotion:banner:update', 3, 3, 2025, '', '', '', '', 0, '1', '1', '1', '', to_date('2022-08-01 14:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-24 20:20:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2029, 'Banner删除', 'promotion:banner:delete', 3, 4, 2025, '', '', '', '', 0, '1', '1', '1', '', to_date('2022-08-01 14:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-24 20:20:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2030, '营销中心', '', 1, 70, 2362, 'promotion', 'ep:present', NULL, NULL, 0, '1', '1', '1', '1', to_date('2022-10-31 21:25:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-30 11:54:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2032, '优惠劵列表', '', 2, 1, 2365, 'template', 'ep:discount', 'mall/promotion/coupon/template/index', 'PromotionCouponTemplate', 0, '1', '1', '1', '', to_date('2022-10-31 22:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-03 12:40:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2033, '优惠劵模板查询', 'promotion:coupon-template:query', 3, 1, 2032, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-10-31 22:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-10-31 22:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2034, '优惠劵模板创建', 'promotion:coupon-template:create', 3, 2, 2032, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-10-31 22:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-10-31 22:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2035, '优惠劵模板更新', 'promotion:coupon-template:update', 3, 3, 2032, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-10-31 22:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-10-31 22:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2036, '优惠劵模板删除', 'promotion:coupon-template:delete', 3, 4, 2032, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-10-31 22:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-10-31 22:27:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2038, '领取记录', '', 2, 2, 2365, 'list', 'ep:collection-tag', 'mall/promotion/coupon/index', 'PromotionCoupon', 0, '1', '1', '1', '', to_date('2022-11-03 23:21:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-03 12:55:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2039, '优惠劵查询', 'promotion:coupon:query', 3, 1, 2038, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-03 23:21:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-03 23:21:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2040, '优惠劵删除', 'promotion:coupon:delete', 3, 4, 2038, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-03 23:21:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-03 23:21:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2041, '满减送', '', 2, 10, 2390, 'reward-activity', 'ep:goblet-square-full', 'mall/promotion/rewardActivity/index', 'PromotionRewardActivity', 0, '1', '1', '1', '', to_date('2022-11-04 23:47:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-21 19:24:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2042, '满减送活动查询', 'promotion:reward-activity:query', 3, 1, 2041, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-04 23:47:49', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-04 23:47:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2043, '满减送活动创建', 'promotion:reward-activity:create', 3, 2, 2041, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-04 23:47:49', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-04 23:47:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2044, '满减送活动更新', 'promotion:reward-activity:update', 3, 3, 2041, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-04 23:47:50', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-04 23:47:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2045, '满减送活动删除', 'promotion:reward-activity:delete', 3, 4, 2041, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-04 23:47:50', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-04 23:47:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2046, '满减送活动关闭', 'promotion:reward-activity:close', 3, 5, 2041, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2022-11-05 10:42:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-11-05 10:42:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2047, '限时折扣', '', 2, 7, 2390, 'discount-activity', 'ep:timer', 'mall/promotion/discountActivity/index', 'PromotionDiscountActivity', 0, '1', '1', '1', '', to_date('2022-11-05 17:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-21 19:24:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2048, '限时折扣活动查询', 'promotion:discount-activity:query', 3, 1, 2047, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-05 17:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-05 17:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2049, '限时折扣活动创建', 'promotion:discount-activity:create', 3, 2, 2047, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-05 17:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-05 17:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2050, '限时折扣活动更新', 'promotion:discount-activity:update', 3, 3, 2047, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-05 17:12:16', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-05 17:12:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2051, '限时折扣活动删除', 'promotion:discount-activity:delete', 3, 4, 2047, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-05 17:12:16', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-05 17:12:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2052, '限时折扣活动关闭', 'promotion:discount-activity:close', 3, 5, 2047, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-05 17:12:16', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-05 17:12:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2059, '秒杀商品', '', 2, 2, 2209, 'activity', 'ep:basketball', 'mall/promotion/seckill/activity/index', 'PromotionSeckillActivity', 0, '1', '1', '1', '', to_date('2022-11-06 22:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-06-24 18:57:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2060, '秒杀活动查询', 'promotion:seckill-activity:query', 3, 1, 2059, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-06 22:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-06 22:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2061, '秒杀活动创建', 'promotion:seckill-activity:create', 3, 2, 2059, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-06 22:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-06 22:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2062, '秒杀活动更新', 'promotion:seckill-activity:update', 3, 3, 2059, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-06 22:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-06 22:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2063, '秒杀活动删除', 'promotion:seckill-activity:delete', 3, 4, 2059, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-06 22:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-11-06 22:24:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2066, '秒杀时段', '', 2, 1, 2209, 'config', 'ep:baseball', 'mall/promotion/seckill/config/index', 'PromotionSeckillConfig', 0, '1', '1', '1', '', to_date('2022-11-15 19:46:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-06-24 18:57:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2067, '秒杀时段查询', 'promotion:seckill-config:query', 3, 1, 2066, '', '', '', '', 0, '1', '1', '1', '', to_date('2022-11-15 19:46:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-06-24 17:50:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2068, '秒杀时段创建', 'promotion:seckill-config:create', 3, 2, 2066, '', '', '', '', 0, '1', '1', '1', '', to_date('2022-11-15 19:46:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-06-24 17:48:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2069, '秒杀时段更新', 'promotion:seckill-config:update', 3, 3, 2066, '', '', '', '', 0, '1', '1', '1', '', to_date('2022-11-15 19:46:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-06-24 17:50:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2070, '秒杀时段删除', 'promotion:seckill-config:delete', 3, 4, 2066, '', '', '', '', 0, '1', '1', '1', '', to_date('2022-11-15 19:46:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-06-24 17:50:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2072, '订单中心', '', 1, 65, 2362, 'trade', 'ep:eleme', NULL, NULL, 0, '1', '1', '1', '1', to_date('2022-11-19 18:57:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-30 11:54:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2073, '售后退款', '', 2, 2, 2072, 'after-sale', 'ep:refrigerator', 'mall/trade/afterSale/index', 'TradeAfterSale', 0, '1', '1', '1', '', to_date('2022-11-19 20:15:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-01 21:42:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2074, '售后查询', 'trade:after-sale:query', 3, 1, 2073, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2022-11-19 20:15:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-12-10 21:04:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2075, '秒杀活动关闭', 'promotion:seckill-activity:close', 3, 5, 2059, '', '', '', '', 0, '1', '1', '1', '1', to_date('2022-11-28 20:20:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-03 18:34:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2076, '订单列表', '', 2, 1, 2072, 'order', 'ep:list', 'mall/trade/order/index', 'TradeOrder', 0, '1', '1', '1', '1', to_date('2022-12-10 21:05:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-01 21:42:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2083, '地区管理', '', 2, 14, 1, 'area', 'fa:map-marker', 'system/area/index', 'SystemArea', 0, '1', '1', '1', '1', to_date('2022-12-23 17:35:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:50:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2084, '公众号管理', '', 1, 100, 0, '/mp', 'ep:compass', NULL, NULL, 0, '1', '1', '1', '1', to_date('2023-01-01 20:11:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:39:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2085, '账号管理', '', 2, 1, 2084, 'account', 'fa:user', 'mp/account/index', 'MpAccount', 0, '1', '1', '1', '1', to_date('2023-01-01 20:13:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:42:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2086, '新增账号', 'mp:account:create', 3, 1, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-01 20:21:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-07 17:32:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2087, '修改账号', 'mp:account:update', 3, 2, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-07 17:32:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-07 17:32:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2088, '查询账号', 'mp:account:query', 3, 0, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-07 17:33:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-07 17:33:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2089, '删除账号', 'mp:account:delete', 3, 3, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-07 17:33:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-07 17:33:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2090, '生成二维码', 'mp:account:qr-code', 3, 4, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-07 17:33:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-07 17:33:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2091, '清空 API 配额', 'mp:account:clear-quota', 3, 5, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-07 18:20:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-07 18:20:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2092, '数据统计', 'mp:statistics:query', 2, 2, 2084, 'statistics', 'ep:trend-charts', 'mp/statistics/index', 'MpStatistics', 0, '1', '1', '1', '1', to_date('2023-01-07 20:17:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:42:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2093, '标签管理', '', 2, 3, 2084, 'tag', 'ep:collection-tag', 'mp/tag/index', 'MpTag', 0, '1', '1', '1', '1', to_date('2023-01-08 11:37:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:42:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2094, '查询标签', 'mp:tag:query', 3, 0, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-08 11:59:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-08 11:59:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2095, '新增标签', 'mp:tag:create', 3, 1, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-08 11:59:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-08 11:59:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2096, '修改标签', 'mp:tag:update', 3, 2, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-08 11:59:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-08 11:59:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2097, '删除标签', 'mp:tag:delete', 3, 3, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-08 12:00:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-08 12:00:13', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2098, '同步标签', 'mp:tag:sync', 3, 4, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-08 12:00:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-08 12:00:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2099, '粉丝管理', '', 2, 4, 2084, 'user', 'fa:user-secret', 'mp/user/index', 'MpUser', 0, '1', '1', '1', '1', to_date('2023-01-08 16:51:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:42:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2100, '查询粉丝', 'mp:user:query', 3, 0, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-08 17:16:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-08 17:17:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2101, '修改粉丝', 'mp:user:update', 3, 1, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-08 17:17:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-08 17:17:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2102, '同步粉丝', 'mp:user:sync', 3, 2, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-08 17:17:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-08 17:17:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2103, '消息管理', '', 2, 5, 2084, 'message', 'ep:message', 'mp/message/index', 'MpMessage', 0, '1', '1', '1', '1', to_date('2023-01-08 18:44:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:42:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2104, '图文发表记录', '', 2, 10, 2084, 'free-publish', 'ep:edit-pen', 'mp/freePublish/index', 'MpFreePublish', 0, '1', '1', '1', '1', to_date('2023-01-13 00:30:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:43:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2105, '查询发布列表', 'mp:free-publish:query', 3, 1, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-13 07:19:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-13 07:19:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2106, '发布草稿', 'mp:free-publish:submit', 3, 2, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-13 07:19:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-13 07:19:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2107, '删除发布记录', 'mp:free-publish:delete', 3, 3, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-13 07:20:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-13 07:20:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2108, '图文草稿箱', '', 2, 9, 2084, 'draft', 'ep:edit', 'mp/draft/index', 'MpDraft', 0, '1', '1', '1', '1', to_date('2023-01-13 07:40:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:43:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2109, '新建草稿', 'mp:draft:create', 3, 1, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-13 23:15:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-13 23:15:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2110, '修改草稿', 'mp:draft:update', 3, 2, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-14 10:08:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-14 10:08:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2111, '查询草稿', 'mp:draft:query', 3, 0, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-14 10:09:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-14 10:09:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2112, '删除草稿', 'mp:draft:delete', 3, 3, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-14 10:09:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-14 10:09:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2113, '素材管理', '', 2, 8, 2084, 'material', 'ep:basketball', 'mp/material/index', 'MpMaterial', 0, '1', '1', '1', '1', to_date('2023-01-14 14:12:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:43:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2114, '上传临时素材', 'mp:material:upload-temporary', 3, 1, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-14 15:33:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-14 15:33:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2115, '上传永久素材', 'mp:material:upload-permanent', 3, 2, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-14 15:34:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-14 15:34:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2116, '删除素材', 'mp:material:delete', 3, 3, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-14 15:35:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-14 15:35:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2117, '上传图文图片', 'mp:material:upload-news-image', 3, 4, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-14 15:36:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-14 15:36:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2118, '查询素材', 'mp:material:query', 3, 5, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-14 15:39:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-14 15:39:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2119, '菜单管理', '', 2, 6, 2084, 'menu', 'ep:menu', 'mp/menu/index', 'MpMenu', 0, '1', '1', '1', '1', to_date('2023-01-14 17:43:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 20:21:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2120, '自动回复', '', 2, 7, 2084, 'auto-reply', 'fa-solid:republican', 'mp/autoReply/index', 'MpAutoReply', 0, '1', '1', '1', '1', to_date('2023-01-15 22:13:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 12:43:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2121, '查询回复', 'mp:auto-reply:query', 3, 0, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-16 22:28:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-16 22:28:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2122, '新增回复', 'mp:auto-reply:create', 3, 1, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-16 22:28:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-16 22:28:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2123, '修改回复', 'mp:auto-reply:update', 3, 2, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-16 22:29:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-16 22:29:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2124, '删除回复', 'mp:auto-reply:delete', 3, 3, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-16 22:29:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-16 22:29:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2125, '查询菜单', 'mp:menu:query', 3, 0, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-17 23:05:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 23:05:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2126, '保存菜单', 'mp:menu:save', 3, 1, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-17 23:06:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 23:06:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2127, '删除菜单', 'mp:menu:delete', 3, 2, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-17 23:06:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 23:06:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2128, '查询消息', 'mp:message:query', 3, 0, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-17 23:07:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 23:07:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2129, '发送消息', 'mp:message:send', 3, 1, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-17 23:07:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-17 23:07:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2130, '邮箱管理', '', 2, 2, 2739, 'mail', 'fa-solid:mail-bulk', NULL, NULL, 0, '1', '1', '1', '1', to_date('2023-01-25 17:27:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-22 23:56:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2131, '邮箱账号', '', 2, 0, 2130, 'mail-account', 'fa:universal-access', 'system/mail/account/index', 'SystemMailAccount', 0, '1', '1', '1', '', to_date('2023-01-25 09:33:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:48:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2132, '账号查询', 'system:mail-account:query', 3, 1, 2131, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-25 09:33:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-25 09:33:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2133, '账号创建', 'system:mail-account:create', 3, 2, 2131, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-25 09:33:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-25 09:33:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2134, '账号更新', 'system:mail-account:update', 3, 3, 2131, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-25 09:33:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-25 09:33:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2135, '账号删除', 'system:mail-account:delete', 3, 4, 2131, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-25 09:33:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-25 09:33:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2136, '邮件模版', '', 2, 0, 2130, 'mail-template', 'fa:tag', 'system/mail/template/index', 'SystemMailTemplate', 0, '1', '1', '1', '', to_date('2023-01-25 12:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:48:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2137, '模版查询', 'system:mail-template:query', 3, 1, 2136, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-25 12:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-25 12:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2138, '模版创建', 'system:mail-template:create', 3, 2, 2136, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-25 12:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-25 12:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2139, '模版更新', 'system:mail-template:update', 3, 3, 2136, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-25 12:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-25 12:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2140, '模版删除', 'system:mail-template:delete', 3, 4, 2136, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-25 12:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-25 12:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2141, '邮件记录', '', 2, 0, 2130, 'mail-log', 'fa:edit', 'system/mail/log/index', 'SystemMailLog', 0, '1', '1', '1', '', to_date('2023-01-26 02:16:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:48:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2142, '日志查询', 'system:mail-log:query', 3, 1, 2141, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-26 02:16:50', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-26 02:16:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2143, '发送测试邮件', 'system:mail-template:send-mail', 3, 5, 2136, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-26 23:29:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-26 23:29:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2144, '站内信管理', '', 1, 3, 2739, 'notify', 'ep:message-box', NULL, NULL, 0, '1', '1', '1', '1', to_date('2023-01-28 10:25:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-22 23:56:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2145, '模板管理', '', 2, 0, 2144, 'notify-template', 'fa:archive', 'system/notify/template/index', 'SystemNotifyTemplate', 0, '1', '1', '1', '', to_date('2023-01-28 02:26:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:49:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2146, '站内信模板查询', 'system:notify-template:query', 3, 1, 2145, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-28 02:26:42', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-28 02:26:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2147, '站内信模板创建', 'system:notify-template:create', 3, 2, 2145, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-28 02:26:42', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-28 02:26:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2148, '站内信模板更新', 'system:notify-template:update', 3, 3, 2145, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-28 02:26:42', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-28 02:26:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2149, '站内信模板删除', 'system:notify-template:delete', 3, 4, 2145, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-28 02:26:42', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-28 02:26:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2150, '发送测试站内信', 'system:notify-template:send-notify', 3, 5, 2145, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-01-28 10:54:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 10:54:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2151, '消息记录', '', 2, 0, 2144, 'notify-message', 'fa:edit', 'system/notify/message/index', 'SystemNotifyMessage', 0, '1', '1', '1', '', to_date('2023-01-28 04:28:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:49:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2152, '站内信消息查询', 'system:notify-message:query', 3, 1, 2151, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-01-28 04:28:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-01-28 04:28:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2153, '大屏设计器', '', 2, 2, 1281, 'go-view', 'fa:area-chart', 'report/goview/index', 'GoView', 0, '1', '1', '1', '1', to_date('2023-02-07 00:03:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-03 09:57:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2154, '创建项目', 'report:go-view-project:create', 3, 1, 2153, '', '', '', NULL, 0, '1', '1', '1', '1', to_date('2023-02-07 19:25:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-07 19:25:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2155, '更新项目', 'report:go-view-project:update', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-02-07 19:25:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 20:01:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', to_date('2023-02-07 19:25:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-07 19:25:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', to_date('2023-02-07 19:26:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-07 19:26:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', to_date('2023-02-07 19:26:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-07 19:26:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', to_date('2023-02-10 22:46:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-28 11:36:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', to_date('2023-02-10 22:47:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 21:32:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', to_date('2023-02-11 14:21:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-18 23:50:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-07-30 14:22:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2164, '配送管理', '', 1, 3, 2072, 'delivery', 'ep:shopping-cart', '', '', 0, '1', '1', '1', '1', to_date('2023-05-18 09:18:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-28 10:58:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2165, '快递发货', '', 1, 0, 2164, 'express', 'ep:bicycle', '', '', 0, '1', '1', '1', '1', to_date('2023-05-18 09:22:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-30 21:02:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2166, '门店自提', '', 1, 1, 2164, 'pick-up-store', 'ep:add-location', '', '', 0, '1', '1', '1', '1', to_date('2023-05-18 09:23:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-30 21:03:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2167, '快递公司', '', 2, 0, 2165, 'express', 'ep:compass', 'mall/trade/delivery/express/index', 'Express', 0, '1', '1', '1', '1', to_date('2023-05-18 09:27:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-11-29 11:20:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2168, '快递公司查询', 'trade:delivery:express:query', 3, 1, 2167, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2169, '快递公司创建', 'trade:delivery:express:create', 3, 2, 2167, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2170, '快递公司更新', 'trade:delivery:express:update', 3, 3, 2167, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2171, '快递公司删除', 'trade:delivery:express:delete', 3, 4, 2167, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2172, '快递公司导出', 'trade:delivery:express:export', 3, 5, 2167, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-18 09:37:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2173, '运费模版', 'trade:delivery:express-template:query', 2, 1, 2165, 'express-template', 'ep:coordinate', 'mall/trade/delivery/expressTemplate/index', 'ExpressTemplate', 0, '1', '1', '1', '1', to_date('2023-05-20 06:48:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-30 21:03:13', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2174, '快递运费模板查询', 'trade:delivery:express-template:query', 3, 1, 2173, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2175, '快递运费模板创建', 'trade:delivery:express-template:create', 3, 2, 2173, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2176, '快递运费模板更新', 'trade:delivery:express-template:update', 3, 3, 2173, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2177, '快递运费模板删除', 'trade:delivery:express-template:delete', 3, 4, 2173, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2178, '快递运费模板导出', 'trade:delivery:express-template:export', 3, 5, 2173, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-20 06:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2179, '门店管理', '', 2, 1, 2166, 'pick-up-store', 'ep:basketball', 'mall/trade/delivery/pickUpStore/index', 'PickUpStore', 0, '1', '1', '1', '1', to_date('2023-05-25 10:50:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-30 21:03:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2180, '自提门店查询', 'trade:delivery:pick-up-store:query', 3, 1, 2179, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2181, '自提门店创建', 'trade:delivery:pick-up-store:create', 3, 2, 2179, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2182, '自提门店更新', 'trade:delivery:pick-up-store:update', 3, 3, 2179, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2183, '自提门店删除', 'trade:delivery:pick-up-store:delete', 3, 4, 2179, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2184, '自提门店导出', 'trade:delivery:pick-up-store:export', 3, 5, 2179, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-05-25 10:53:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2209, '秒杀活动', '', 2, 3, 2030, 'seckill', 'ep:place', '', '', 0, '1', '1', '1', '1', to_date('2023-06-24 17:39:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-06-24 18:55:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2262, '会员中心', '', 1, 55, 0, '/member', 'ep:bicycle', NULL, NULL, 0, '1', '1', '1', '1', to_date('2023-06-10 00:42:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-20 09:23:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2275, '会员配置', '', 2, 0, 2262, 'config', 'fa:archive', 'member/config/index', 'MemberConfig', 0, '1', '1', '1', '', to_date('2023-06-10 02:07:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-01 23:41:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2276, '会员配置查询', 'member:config:query', 3, 1, 2275, '', '', '', '', 0, '1', '1', '1', '', to_date('2023-06-10 02:07:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:48:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2277, '会员配置保存', 'member:config:save', 3, 2, 2275, '', '', '', '', 0, '1', '1', '1', '', to_date('2023-06-10 02:07:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:49:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2281, '签到配置', '', 2, 2, 2300, 'config', 'ep:calendar', 'member/signin/config/index', 'SignInConfig', 0, '1', '1', '1', '', to_date('2023-06-10 03:26:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-20 19:25:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2282, '积分签到规则查询', 'point:sign-in-config:query', 3, 1, 2281, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-06-10 03:26:12', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-06-10 03:26:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2283, '积分签到规则创建', 'point:sign-in-config:create', 3, 2, 2281, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-06-10 03:26:12', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-06-10 03:26:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2284, '积分签到规则更新', 'point:sign-in-config:update', 3, 3, 2281, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-06-10 03:26:12', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-06-10 03:26:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2285, '积分签到规则删除', 'point:sign-in-config:delete', 3, 4, 2281, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-06-10 03:26:12', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-06-10 03:26:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2287, '会员积分', '', 2, 10, 2262, 'record', 'fa:asterisk', 'member/point/record/index', 'PointRecord', 0, '1', '1', '1', '', to_date('2023-06-10 04:18:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-01 23:42:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2288, '用户积分记录查询', 'point:record:query', 3, 1, 2287, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-06-10 04:18:50', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-06-10 04:18:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2293, '签到记录', '', 2, 3, 2300, 'record', 'ep:chicken', 'member/signin/record/index', 'SignInRecord', 0, '1', '1', '1', '', to_date('2023-06-10 04:48:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-20 19:26:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2294, '用户签到积分查询', 'point:sign-in-record:query', 3, 1, 2293, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-06-10 04:48:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-06-10 04:48:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2297, '用户签到积分删除', 'point:sign-in-record:delete', 3, 4, 2293, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-06-10 04:48:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-06-10 04:48:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2300, '会员签到', '', 1, 11, 2262, 'signin', 'ep:alarm-clock', '', '', 0, '1', '1', '1', '1', to_date('2023-06-27 22:49:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-20 09:23:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2301, '回调通知', '', 2, 5, 1117, 'notify', 'ep:mute-notification', 'pay/notify/index', 'PayNotify', 0, '1', '1', '1', '', to_date('2023-07-20 04:41:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-18 23:56:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2302, '支付通知查询', 'pay:notify:query', 3, 1, 2301, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-07-20 04:41:32', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-07-20 04:41:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2303, '拼团活动', '', 2, 3, 2030, 'combination', 'fa:group', '', '', 0, '1', '1', '1', '1', to_date('2023-08-12 17:19:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-12 17:20:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2304, '拼团商品', '', 2, 1, 2303, 'acitivity', 'ep:apple', 'mall/promotion/combination/activity/index', 'PromotionCombinationActivity', 0, '1', '1', '1', '1', to_date('2023-08-12 17:22:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-12 17:22:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2305, '拼团活动查询', 'promotion:combination-activity:query', 3, 1, 2304, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-12 17:54:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-24 11:57:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2306, '拼团活动创建', 'promotion:combination-activity:create', 3, 2, 2304, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-12 17:54:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-12 17:54:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2307, '拼团活动更新', 'promotion:combination-activity:update', 3, 3, 2304, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-12 17:55:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-12 17:55:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2308, '拼团活动删除', 'promotion:combination-activity:delete', 3, 4, 2304, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-12 17:55:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-12 17:55:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2309, '拼团活动关闭', 'promotion:combination-activity:close', 3, 5, 2304, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-12 17:55:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-06 10:51:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2310, '砍价活动', '', 2, 4, 2030, 'bargain', 'ep:box', '', '', 0, '1', '1', '1', '1', to_date('2023-08-13 00:27:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-13 00:27:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2311, '砍价商品', '', 2, 1, 2310, 'activity', 'ep:burger', 'mall/promotion/bargain/activity/index', 'PromotionBargainActivity', 0, '1', '1', '1', '1', to_date('2023-08-13 00:28:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-05 01:16:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2312, '砍价活动查询', 'promotion:bargain-activity:query', 3, 1, 2311, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-13 00:32:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-13 00:32:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2313, '砍价活动创建', 'promotion:bargain-activity:create', 3, 2, 2311, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-13 00:32:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-13 00:32:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2314, '砍价活动更新', 'promotion:bargain-activity:update', 3, 3, 2311, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-13 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-13 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2315, '砍价活动删除', 'promotion:bargain-activity:delete', 3, 4, 2311, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-13 00:34:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-13 00:34:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2316, '砍价活动关闭', 'promotion:bargain-activity:close', 3, 5, 2311, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-13 00:35:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-13 00:35:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2317, '会员管理', '', 2, 0, 2262, 'user', 'ep:avatar', 'member/user/index', 'MemberUser', 0, '1', '1', '1', '', to_date('2023-08-19 04:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-24 00:50:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2318, '会员用户查询', 'member:user:query', 3, 1, 2317, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-19 04:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-19 04:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2319, '会员用户更新', 'member:user:update', 3, 3, 2317, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-19 04:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-19 04:12:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2320, '会员标签', '', 2, 1, 2262, 'tag', 'ep:collection-tag', 'member/tag/index', 'MemberTag', 0, '1', '1', '1', '', to_date('2023-08-20 01:03:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-20 09:23:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2321, '会员标签查询', 'member:tag:query', 3, 1, 2320, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-20 01:03:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-20 01:03:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2322, '会员标签创建', 'member:tag:create', 3, 2, 2320, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-20 01:03:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-20 01:03:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2323, '会员标签更新', 'member:tag:update', 3, 3, 2320, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-20 01:03:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-20 01:03:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2324, '会员标签删除', 'member:tag:delete', 3, 4, 2320, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-20 01:03:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-20 01:03:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2325, '会员等级', '', 2, 2, 2262, 'level', 'fa:level-up', 'member/level/index', 'MemberLevel', 0, '1', '1', '1', '', to_date('2023-08-22 12:41:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-22 21:47:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2326, '会员等级查询', 'member:level:query', 3, 1, 2325, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-22 12:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 12:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2327, '会员等级创建', 'member:level:create', 3, 2, 2325, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-22 12:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 12:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2328, '会员等级更新', 'member:level:update', 3, 3, 2325, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-22 12:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 12:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2329, '会员等级删除', 'member:level:delete', 3, 4, 2325, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-22 12:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 12:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2330, '会员分组', '', 2, 3, 2262, 'group', 'fa:group', 'member/group/index', 'MemberGroup', 0, '1', '1', '1', '', to_date('2023-08-22 13:50:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-01 23:42:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2331, '用户分组查询', 'member:group:query', 3, 1, 2330, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-22 13:50:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 13:50:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2332, '用户分组创建', 'member:group:create', 3, 2, 2330, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-22 13:50:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 13:50:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2333, '用户分组更新', 'member:group:update', 3, 3, 2330, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-22 13:50:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 13:50:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2334, '用户分组删除', 'member:group:delete', 3, 4, 2330, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-22 13:50:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-22 13:50:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2335, '用户等级修改', 'member:user:update-level', 3, 5, 2317, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-08-23 16:49:05', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-08-23 16:50:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2336, '商品评论', '', 2, 5, 2000, 'comment', 'ep:comment', 'mall/product/comment/index', 'ProductComment', 0, '1', '1', '1', '1', to_date('2023-08-26 11:03:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-26 11:03:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2337, '评论查询', 'product:comment:query', 3, 1, 2336, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-26 11:04:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-26 11:04:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2338, '添加自评', 'product:comment:create', 3, 2, 2336, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-26 11:04:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-26 11:08:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2339, '商家回复', 'product:comment:update', 3, 3, 2336, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-26 11:04:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-26 11:04:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2340, '显隐评论', 'product:comment:update', 3, 4, 2336, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-08-26 11:04:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-26 11:04:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2341, '优惠劵发送', 'promotion:coupon:send', 3, 2, 2038, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-09-02 00:03:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-02 00:03:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2342, '交易配置', '', 2, 0, 2072, 'config', 'ep:setting', 'mall/trade/config/index', 'TradeConfig', 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-26 20:30:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2343, '交易中心配置查询', 'trade:config:query', 3, 1, 2342, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2344, '交易中心配置保存', 'trade:config:save', 3, 2, 2342, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2345, '分销管理', '', 1, 4, 2072, 'brokerage', 'fa-solid:project-diagram', '', '', 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-28 10:58:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2346, '分销用户', '', 2, 0, 2345, 'brokerage-user', 'fa-solid:user-tie', 'mall/trade/brokerage/user/index', 'TradeBrokerageUser', 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-26 20:33:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2347, '分销用户查询', 'trade:brokerage-user:query', 3, 1, 2346, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2348, '分销用户推广人查询', 'trade:brokerage-user:user-query', 3, 2, 2346, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2349, '分销用户推广订单查询', 'trade:brokerage-user:order-query', 3, 3, 2346, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2350, '分销用户修改推广资格', 'trade:brokerage-user:update-brokerage-enable', 3, 4, 2346, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2351, '修改推广员', 'trade:brokerage-user:update-bind-user', 3, 5, 2346, '', '', '', '', 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-01 14:33:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2352, '清除推广员', 'trade:brokerage-user:clear-bind-user', 3, 6, 2346, '', '', '', '', 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-01 14:33:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2353, '佣金记录', '', 2, 1, 2345, 'brokerage-record', 'fa:money', 'mall/trade/brokerage/record/index', 'TradeBrokerageRecord', 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-26 20:33:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2354, '佣金记录查询', 'trade:brokerage-record:query', 3, 1, 2353, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2355, '佣金提现', '', 2, 2, 2345, 'brokerage-withdraw', 'fa:credit-card', 'mall/trade/brokerage/withdraw/index', 'TradeBrokerageWithdraw', 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-26 20:33:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2356, '佣金提现查询', 'trade:brokerage-withdraw:query', 3, 1, 2355, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2357, '佣金提现审核', 'trade:brokerage-withdraw:audit', 3, 2, 2355, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-28 02:46:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2358, '统计中心', '', 1, 75, 2362, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '', to_date('2023-09-30 03:22:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-30 11:54:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2359, '交易统计', '', 2, 4, 2358, 'trade', 'fa-solid:credit-card', 'mall/statistics/trade/index', 'TradeStatistics', 0, '1', '1', '1', '', to_date('2023-09-30 03:22:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-26 20:42:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2360, '交易统计查询', 'statistics:trade:query', 3, 1, 2359, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-30 03:22:40', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-30 03:22:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2361, '交易统计导出', 'statistics:trade:export', 3, 2, 2359, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-09-30 03:22:40', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-09-30 03:22:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2362, '商城系统', '', 1, 59, 0, '/mall', 'ep:shop', '', '', 0, '1', '1', '1', '1', to_date('2023-09-30 11:52:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-30 11:52:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2363, '用户积分修改', 'member:user:update-point', 3, 6, 2317, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-01 14:39:43', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-01 14:39:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2364, '用户余额修改', 'pay:wallet:update-balance', 3, 7, 2317, '', '', '', '', 0, '1', '1', '1', '', to_date('2023-10-01 14:39:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-10-01 09:42:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2365, '优惠劵', '', 1, 2, 2030, 'coupon', 'fa-solid:disease', '', '', 0, '1', '1', '1', '1', to_date('2023-10-03 12:39:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-05 00:16:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2366, '砍价记录', '', 2, 2, 2310, 'record', 'ep:list', 'mall/promotion/bargain/record/index', 'PromotionBargainRecord', 0, '1', '1', '1', '', to_date('2023-10-05 02:49:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-05 10:50:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2367, '砍价记录查询', 'promotion:bargain-record:query', 3, 1, 2366, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-05 02:49:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-05 02:49:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2368, '助力记录查询', 'promotion:bargain-help:query', 3, 2, 2366, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-10-05 12:27:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-05 12:27:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2369, '拼团记录', 'promotion:combination-record:query', 2, 2, 2303, 'record', 'ep:avatar', 'mall/promotion/combination/record/index.vue', 'PromotionCombinationRecord', 0, '1', '1', '1', '1', to_date('2023-10-08 07:10:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-08 07:34:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2374, '会员统计', '', 2, 2, 2358, 'member', 'ep:avatar', 'mall/statistics/member/index', 'MemberStatistics', 0, '1', '1', '1', '', to_date('2023-10-11 04:39:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-26 20:41:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2375, '会员统计查询', 'statistics:member:query', 3, 1, 2374, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-11 04:39:24', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-11 04:39:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2376, '订单核销', 'trade:order:pick-up', 3, 10, 2076, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-10-14 17:11:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-14 17:11:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2377, '文章分类', '', 2, 0, 2387, 'article/category', 'fa:certificate', 'mall/promotion/article/category/index', 'ArticleCategory', 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-16 09:38:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2378, '分类查询', 'promotion:article-category:query', 3, 1, 2377, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2379, '分类创建', 'promotion:article-category:create', 3, 2, 2377, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2380, '分类更新', 'promotion:article-category:update', 3, 3, 2377, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2381, '分类删除', 'promotion:article-category:delete', 3, 4, 2377, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2382, '文章列表', '', 2, 2, 2387, 'article', 'ep:connection', 'mall/promotion/article/index', 'Article', 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-16 09:41:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2383, '文章管理查询', 'promotion:article:query', 3, 1, 2382, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2384, '文章管理创建', 'promotion:article:create', 3, 2, 2382, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2385, '文章管理更新', 'promotion:article:update', 3, 3, 2382, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2386, '文章管理删除', 'promotion:article:delete', 3, 4, 2382, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-16 01:26:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2387, '内容管理', '', 1, 1, 2030, 'content', 'ep:collection', '', '', 0, '1', '1', '1', '1', to_date('2023-10-16 09:37:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-16 09:37:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2388, '商城首页', '', 2, 1, 2362, 'home', 'ep:home-filled', 'mall/home/index', 'MallHome', 0, '1', '1', '1', '', to_date('2023-10-16 12:10:33', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-16 12:10:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2389, '核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder', 0, '1', '1', '1', '', to_date('2023-10-19 16:09:51', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-19 16:09:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2390, '优惠活动', '', 1, 99, 2030, 'youhui', 'ep:aim', '', '', 0, '1', '1', '1', '1', to_date('2023-10-21 19:23:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-21 19:23:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2391, '客户管理', '', 2, 10, 2397, 'customer', 'fa:address-book-o', 'crm/customer/index', 'CrmCustomer', 0, '1', '1', '1', '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:13:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2392, '客户查询', 'crm:customer:query', 3, 1, 2391, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2393, '客户创建', 'crm:customer:create', 3, 2, 2391, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2394, '客户更新', 'crm:customer:update', 3, 3, 2391, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2395, '客户删除', 'crm:customer:delete', 3, 4, 2391, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2396, '客户导出', 'crm:customer:export', 3, 5, 2391, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 09:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2397, 'CRM 系统', '', 1, 200, 0, '/crm', 'simple-icons:civicrm', '', '', 0, '1', '1', '1', '1', to_date('2023-10-29 17:08:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-19 18:56:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2398, '合同管理', '', 2, 50, 2397, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, '1', '1', '1', '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:15:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2399, '合同查询', 'crm:contract:query', 3, 1, 2398, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2400, '合同创建', 'crm:contract:create', 3, 2, 2398, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2401, '合同更新', 'crm:contract:update', 3, 3, 2398, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2402, '合同删除', 'crm:contract:delete', 3, 4, 2398, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2403, '合同导出', 'crm:contract:export', 3, 5, 2398, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 10:50:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2404, '线索管理', '', 2, 8, 2397, 'clue', 'fa:pagelines', 'crm/clue/index', 'CrmClue', 0, '1', '1', '1', '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:15:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2405, '线索查询', 'crm:clue:query', 3, 1, 2404, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2406, '线索创建', 'crm:clue:create', 3, 2, 2404, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2407, '线索更新', 'crm:clue:update', 3, 3, 2404, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2408, '线索删除', 'crm:clue:delete', 3, 4, 2404, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2409, '线索导出', 'crm:clue:export', 3, 5, 2404, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:06:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2410, '商机管理', '', 2, 40, 2397, 'business', 'fa:bus', 'crm/business/index', 'CrmBusiness', 0, '1', '1', '1', '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:14:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2411, '商机查询', 'crm:business:query', 3, 1, 2410, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2412, '商机创建', 'crm:business:create', 3, 2, 2410, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2413, '商机更新', 'crm:business:update', 3, 3, 2410, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2414, '商机删除', 'crm:business:delete', 3, 4, 2410, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2415, '商机导出', 'crm:business:export', 3, 5, 2410, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:12:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2416, '联系人管理', '', 2, 20, 2397, 'contact', 'fa:address-book-o', 'crm/contact/index', 'CrmContact', 0, '1', '1', '1', '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:13:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2417, '联系人查询', 'crm:contact:query', 3, 1, 2416, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2418, '联系人创建', 'crm:contact:create', 3, 2, 2416, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2419, '联系人更新', 'crm:contact:update', 3, 3, 2416, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2420, '联系人删除', 'crm:contact:delete', 3, 4, 2416, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2421, '联系人导出', 'crm:contact:export', 3, 5, 2416, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:14:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2422, '回款管理', '', 2, 60, 2397, 'receivable', 'ep:money', 'crm/receivable/index', 'CrmReceivable', 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:16:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2423, '回款管理查询', 'crm:receivable:query', 3, 1, 2422, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2424, '回款管理创建', 'crm:receivable:create', 3, 2, 2422, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2425, '回款管理更新', 'crm:receivable:update', 3, 3, 2422, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2426, '回款管理删除', 'crm:receivable:delete', 3, 4, 2422, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2427, '回款管理导出', 'crm:receivable:export', 3, 5, 2422, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2428, '回款计划', '', 2, 61, 2397, 'receivable-plan', 'fa:money', 'crm/receivable/plan/index', 'CrmReceivablePlan', 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:16:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2429, '回款计划查询', 'crm:receivable-plan:query', 3, 1, 2428, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2430, '回款计划创建', 'crm:receivable-plan:create', 3, 2, 2428, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2432, '回款计划删除', 'crm:receivable-plan:delete', 3, 4, 2428, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2433, '回款计划导出', 'crm:receivable-plan:export', 3, 5, 2428, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 11:18:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', '', 0, '1', '1', '1', '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-15 21:34:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2436, '装修模板', '', 2, 1, 2435, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, '1', '1', '1', '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2437, '装修模板查询', 'promotion:diy-template:query', 3, 1, 2436, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2438, '装修模板创建', 'promotion:diy-template:create', 3, 2, 2436, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2439, '装修模板更新', 'promotion:diy-template:update', 3, 3, 2436, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2440, '装修模板删除', 'promotion:diy-template:delete', 3, 4, 2436, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2441, '装修模板使用', 'promotion:diy-template:use', 3, 5, 2436, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2442, '装修页面', '', 2, 2, 2435, 'diy-page', 'foundation:page-edit', 'mall/promotion/diy/page/index', 'DiyPage', 0, '1', '1', '1', '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2443, '装修页面查询', 'promotion:diy-page:query', 3, 1, 2442, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2444, '装修页面创建', 'promotion:diy-page:create', 3, 2, 2442, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 14:19:26', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2445, '装修页面更新', 'promotion:diy-page:update', 3, 3, 2442, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 14:19:26', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2446, '装修页面删除', 'promotion:diy-page:delete', 3, 4, 2442, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-10-29 14:19:26', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-10-29 14:19:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2447, '三方登录', '', 1, 10, 1, 'social', 'fa:rocket', '', '', 0, '1', '1', '1', '1', to_date('2023-11-04 12:12:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 01:14:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2448, '三方应用', '', 2, 1, 2447, 'client', 'ep:set-up', 'system/social/client/index.vue', 'SocialClient', 0, '1', '1', '1', '1', to_date('2023-11-04 12:17:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-04 19:09:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2449, '三方应用查询', 'system:social-client:query', 3, 1, 2448, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-11-04 12:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 12:43:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2450, '三方应用创建', 'system:social-client:create', 3, 2, 2448, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-11-04 12:43:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 12:43:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2451, '三方应用更新', 'system:social-client:update', 3, 3, 2448, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-11-04 12:44:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 12:44:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2452, '三方应用删除', 'system:social-client:delete', 3, 4, 2448, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-11-04 12:44:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 12:44:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2453, '三方用户', 'system:social-user:query', 2, 2, 2447, 'user', 'ep:avatar', 'system/social/user/index.vue', 'SocialUser', 0, '1', '1', '1', '1', to_date('2023-11-04 14:01:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 14:01:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2472, '主子表（内嵌）', '', 2, 12, 1070, 'demo03-inner', 'fa:power-off', 'infra/demo/demo03/inner/index', 'Demo03StudentInner', 0, '1', '1', '1', '', to_date('2023-11-13 04:39:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 23:53:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2478, '单表（增删改查）', '', 2, 1, 1070, 'demo01-contact', 'ep:bicycle', 'infra/demo/demo01/index', 'Demo01Contact', 0, '1', '1', '1', '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 20:34:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2479, '示例联系人查询', 'infra:demo01-contact:query', 3, 1, 2478, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2480, '示例联系人创建', 'infra:demo01-contact:create', 3, 2, 2478, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2481, '示例联系人更新', 'infra:demo01-contact:update', 3, 3, 2478, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2482, '示例联系人删除', 'infra:demo01-contact:delete', 3, 4, 2478, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2483, '示例联系人导出', 'infra:demo01-contact:export', 3, 5, 2478, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-15 14:42:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2484, '树表（增删改查）', '', 2, 2, 1070, 'demo02-category', 'fa:tree', 'infra/demo/demo02/index', 'Demo02Category', 0, '1', '1', '1', '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 20:35:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2485, '示例分类查询', 'infra:demo02-category:query', 3, 1, 2484, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2486, '示例分类创建', 'infra:demo02-category:create', 3, 2, 2484, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2487, '示例分类更新', 'infra:demo02-category:update', 3, 3, 2484, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2488, '示例分类删除', 'infra:demo02-category:delete', 3, 4, 2484, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2489, '示例分类导出', 'infra:demo02-category:export', 3, 5, 2484, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2490, '主子表（标准）', '', 2, 10, 1070, 'demo03-normal', 'fa:battery-3', 'infra/demo/demo03/normal/index', 'Demo03StudentNormal', 0, '1', '1', '1', '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 23:10:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2491, '学生查询', 'infra:demo03-student:query', 3, 1, 2490, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2492, '学生创建', 'infra:demo03-student:create', 3, 2, 2490, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2493, '学生更新', 'infra:demo03-student:update', 3, 3, 2490, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2494, '学生删除', 'infra:demo03-student:delete', 3, 4, 2490, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2495, '学生导出', 'infra:demo03-student:export', 3, 5, 2490, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-16 12:53:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2497, '主子表（ERP）', '', 2, 11, 1070, 'demo03-erp', 'ep:calendar', 'infra/demo/demo03/erp/index', 'Demo03StudentERP', 0, '1', '1', '1', '', to_date('2023-11-16 15:50:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-17 13:19:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2516, '客户公海配置', '', 2, 0, 2524, 'customer-pool-config', 'ep:data-analysis', 'crm/customer/poolConfig/index', 'CrmCustomerPoolConfig', 0, '1', '1', '1', '', to_date('2023-11-18 13:33:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-03 19:52:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2517, '客户公海配置保存', 'crm:customer-pool-config:update', 3, 1, 2516, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-18 13:33:31', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-18 13:33:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2518, '客户限制配置', '', 2, 1, 2524, 'customer-limit-config', 'ep:avatar', 'crm/customer/limitConfig/index', 'CrmCustomerLimitConfig', 0, '1', '1', '1', '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-24 16:43:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2519, '客户限制配置查询', 'crm:customer-limit-config:query', 3, 1, 2518, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2520, '客户限制配置创建', 'crm:customer-limit-config:create', 3, 2, 2518, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2521, '客户限制配置更新', 'crm:customer-limit-config:update', 3, 3, 2518, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2522, '客户限制配置删除', 'crm:customer-limit-config:delete', 3, 4, 2518, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2523, '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, 2518, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-11-18 13:33:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2524, '系统配置', '', 1, 999, 2397, 'config', 'ep:connection', '', '', 0, '1', '1', '1', '1', to_date('2023-11-18 21:58:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:14:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2525, 'WebSocket', '', 2, 5, 2, 'websocket', 'ep:connection', 'infra/webSocket/index', 'InfraWebSocket', 0, '1', '1', '1', '1', to_date('2023-11-23 19:41:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 00:02:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2526, '产品管理', '', 2, 80, 2397, 'product', 'fa:product-hunt', 'crm/product/index', 'CrmProduct', 0, '1', '1', '1', '1', to_date('2023-12-05 22:45:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-20 20:36:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2527, '产品查询', 'crm:product:query', 3, 1, 2526, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-12-05 22:47:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 22:47:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2528, '产品创建', 'crm:product:create', 3, 2, 2526, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-12-05 22:47:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 22:47:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2529, '产品更新', 'crm:product:update', 3, 3, 2526, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-12-05 22:48:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 22:48:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2530, '产品删除', 'crm:product:delete', 3, 4, 2526, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-12-05 22:48:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 22:48:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2531, '产品导出', 'crm:product:export', 3, 5, 2526, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-12-05 22:48:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-05 22:48:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2532, '产品分类配置', '', 2, 3, 2524, 'product/category', 'fa-solid:window-restore', 'crm/product/category/index', 'CrmProductCategory', 0, '1', '1', '1', '1', to_date('2023-12-06 12:52:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-06 12:52:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2533, '产品分类查询', 'crm:product-category:query', 3, 1, 2532, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-12-06 12:53:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-06 12:53:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2534, '产品分类创建', 'crm:product-category:create', 3, 2, 2532, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-12-06 12:53:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-06 12:53:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2535, '产品分类更新', 'crm:product-category:update', 3, 3, 2532, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-12-06 12:53:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-06 12:53:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2536, '产品分类删除', 'crm:product-category:delete', 3, 4, 2532, '', '', '', '', 0, '1', '1', '1', '1', to_date('2023-12-06 12:54:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-06 12:54:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2543, '关联商机', 'crm:contact:create-business', 3, 10, 2416, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-01-02 17:28:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-02 17:28:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2544, '取关商机', 'crm:contact:delete-business', 3, 11, 2416, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-01-02 17:28:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-02 17:28:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2545, '商品统计', '', 2, 3, 2358, 'product', 'fa:product-hunt', 'mall/statistics/product/index', 'ProductStatistics', 0, '1', '1', '1', '', to_date('2023-12-15 18:54:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-26 20:41:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2546, '客户公海', '', 2, 30, 2397, 'customer/pool', 'fa-solid:swimming-pool', 'crm/customer/pool/index', 'CrmCustomerPool', 0, '1', '1', '1', '1', to_date('2024-01-15 21:29:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:14:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2547, '订单查询', 'trade:order:query', 3, 1, 2076, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-01-16 08:52:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-16 08:52:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2548, '订单更新', 'trade:order:update', 3, 2, 2076, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-01-16 08:52:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-16 08:52:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2549, '支付&退款案例', '', 2, 1, 2161, 'order', 'fa:paypal', 'pay/demo/order/index', '', 0, '1', '1', '1', '1', to_date('2024-01-18 23:45:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-01-18 23:47:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2550, '提现转账案例', '', 2, 2, 2161, 'transfer', 'fa:transgender-alt', 'pay/demo/withdraw/index', '', 0, '1', '1', '1', '1', to_date('2024-01-18 23:51:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-08 13:04:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2551, '钱包管理', '', 1, 4, 1117, 'wallet', 'ep:wallet', '', '', 0, '1', '1', '1', '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-29 08:58:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2552, '充值套餐', '', 2, 2, 2551, 'wallet-recharge-package', 'fa:leaf', 'pay/wallet/rechargePackage/index', 'WalletRechargePackage', 0, '1', '1', '1', '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2553, '钱包充值套餐查询', 'pay:wallet-recharge-package:query', 3, 1, 2552, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2554, '钱包充值套餐创建', 'pay:wallet-recharge-package:create', 3, 2, 2552, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2555, '钱包充值套餐更新', 'pay:wallet-recharge-package:update', 3, 3, 2552, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2556, '钱包充值套餐删除', 'pay:wallet-recharge-package:delete', 3, 4, 2552, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2557, '钱包余额', '', 2, 1, 2551, 'wallet-balance', 'fa:leaf', 'pay/wallet/balance/index', 'WalletBalance', 0, '1', '1', '1', '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2558, '钱包余额查询', 'pay:wallet:query', 3, 1, 2557, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2559, '转账订单', '', 2, 3, 1117, 'transfer', 'ep:credit-card', 'pay/transfer/index', 'PayTransfer', 0, '1', '1', '1', '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-12-29 02:32:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2560, '数据统计', '', 1, 200, 2397, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '1', to_date('2024-01-26 22:50:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-24 20:10:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2561, '排行榜', 'crm:statistics-rank:query', 2, 1, 2560, 'ranking', 'fa:area-chart', 'crm/statistics/rank/index', 'CrmStatisticsRank', 0, '1', '1', '1', '1', to_date('2024-01-26 22:52:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:39:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2562, '客户导入', 'crm:customer:import', 3, 6, 2391, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-02-01 13:09:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-01 13:09:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2563, 'ERP 系统', '', 1, 300, 0, '/erp', 'simple-icons:erpnext', '', '', 0, '1', '1', '1', '1', to_date('2024-02-04 15:37:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-19 18:56:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2564, '产品管理', '', 1, 40, 2563, 'product', 'fa:product-hunt', '', '', 0, '1', '1', '1', '1', to_date('2024-02-04 15:38:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-04 15:38:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2565, '产品信息', '', 2, 0, 2564, 'product', 'fa-solid:apple-alt', 'erp/product/product/index', 'ErpProduct', 0, '1', '1', '1', '', to_date('2024-02-04 07:52:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-05 14:42:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2566, '产品查询', 'erp:product:query', 3, 1, 2565, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-02-04 07:52:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-04 17:21:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2567, '产品创建', 'erp:product:create', 3, 2, 2565, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-02-04 07:52:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-04 17:22:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2568, '产品更新', 'erp:product:update', 3, 3, 2565, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-02-04 07:52:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-04 17:22:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2569, '产品删除', 'erp:product:delete', 3, 4, 2565, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-02-04 07:52:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-04 17:22:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2570, '产品导出', 'erp:product:export', 3, 5, 2565, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-02-04 07:52:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-04 17:22:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2571, '产品分类', '', 2, 1, 2564, 'product-category', 'fa:certificate', 'erp/product/category/index', 'ErpProductCategory', 0, '1', '1', '1', '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-04 17:24:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2572, '分类查询', 'erp:product-category:query', 3, 1, 2571, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2573, '分类创建', 'erp:product-category:create', 3, 2, 2571, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2574, '分类更新', 'erp:product-category:update', 3, 3, 2571, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2575, '分类删除', 'erp:product-category:delete', 3, 4, 2571, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2576, '分类导出', 'erp:product-category:export', 3, 5, 2571, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 09:21:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2577, '产品单位', '', 2, 2, 2564, 'unit', 'ep:opportunity', 'erp/product/unit/index', 'ErpProductUnit', 0, '1', '1', '1', '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-04 19:54:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2578, '单位查询', 'erp:product-unit:query', 3, 1, 2577, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2579, '单位创建', 'erp:product-unit:create', 3, 2, 2577, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2580, '单位更新', 'erp:product-unit:update', 3, 3, 2577, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2581, '单位删除', 'erp:product-unit:delete', 3, 4, 2577, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2582, '单位导出', 'erp:product-unit:export', 3, 5, 2577, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 11:54:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2583, '库存管理', '', 1, 30, 2563, 'stock', 'fa:window-restore', '', '', 0, '1', '1', '1', '1', to_date('2024-02-05 00:29:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-05 00:29:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2584, '仓库信息', '', 2, 0, 2583, 'warehouse', 'ep:house', 'erp/stock/warehouse/index', 'ErpWarehouse', 0, '1', '1', '1', '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-05 01:12:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2585, '仓库查询', 'erp:warehouse:query', 3, 1, 2584, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2586, '仓库创建', 'erp:warehouse:create', 3, 2, 2584, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2587, '仓库更新', 'erp:warehouse:update', 3, 3, 2584, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2588, '仓库删除', 'erp:warehouse:delete', 3, 4, 2584, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2589, '仓库导出', 'erp:warehouse:export', 3, 5, 2584, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-04 17:12:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2590, '产品库存', '', 2, 1, 2583, 'stock', 'ep:coffee', 'erp/stock/stock/index', 'ErpStock', 0, '1', '1', '1', '', to_date('2024-02-05 06:40:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-05 14:42:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2591, '库存查询', 'erp:stock:query', 3, 1, 2590, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 06:40:50', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 06:40:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2592, '库存导出', 'erp:stock:export', 3, 5, 2590, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 06:40:50', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 06:40:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2593, '出入库明细', '', 2, 2, 2583, 'record', 'fa-solid:blog', 'erp/stock/record/index', 'ErpStockRecord', 0, '1', '1', '1', '', to_date('2024-02-05 10:27:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-06 17:26:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2594, '库存明细查询', 'erp:stock-record:query', 3, 1, 2593, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 10:27:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 10:27:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2595, '库存明细导出', 'erp:stock-record:export', 3, 5, 2593, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 10:27:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 10:27:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2596, '其它入库', '', 2, 3, 2583, 'in', 'ep:zoom-in', 'erp/stock/in/index', 'ErpStockIn', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-07 19:06:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2597, '其它入库单查询', 'erp:stock-in:query', 3, 1, 2596, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2598, '其它入库单创建', 'erp:stock-in:create', 3, 2, 2596, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2599, '其它入库单更新', 'erp:stock-in:update', 3, 3, 2596, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2600, '其它入库单删除', 'erp:stock-in:delete', 3, 4, 2596, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2601, '其它入库单导出', 'erp:stock-in:export', 3, 5, 2596, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2602, '采购管理', '', 1, 10, 2563, 'purchase', 'fa:buysellads', '', '', 0, '1', '1', '1', '1', to_date('2024-02-06 16:01:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-06 16:01:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2603, '供应商信息', '', 2, 4, 2602, 'supplier', 'fa:superpowers', 'erp/purchase/supplier/index', 'ErpSupplier', 0, '1', '1', '1', '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-06 16:22:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2604, '供应商查询', 'erp:supplier:query', 3, 1, 2603, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2605, '供应商创建', 'erp:supplier:create', 3, 2, 2603, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2606, '供应商更新', 'erp:supplier:update', 3, 3, 2603, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2607, '供应商删除', 'erp:supplier:delete', 3, 4, 2603, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2608, '供应商导出', 'erp:supplier:export', 3, 5, 2603, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-06 08:21:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2609, '其它入库单审批', 'erp:stock-in:update-status', 3, 6, 2596, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2610, '其它出库', '', 2, 4, 2583, 'out', 'ep:zoom-out', 'erp/stock/out/index', 'ErpStockOut', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-07 19:06:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2611, '其它出库单查询', 'erp:stock-out:query', 3, 1, 2610, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 06:43:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2612, '其它出库单创建', 'erp:stock-out:create', 3, 2, 2610, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 06:43:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2613, '其它出库单更新', 'erp:stock-out:update', 3, 3, 2610, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 06:43:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2614, '其它出库单删除', 'erp:stock-out:delete', 3, 4, 2610, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 06:43:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2615, '其它出库单导出', 'erp:stock-out:export', 3, 5, 2610, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 06:43:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2616, '其它出库单审批', 'erp:stock-out:update-status', 3, 6, 2610, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 06:43:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2617, '销售管理', '', 1, 20, 2563, 'sale', 'fa:sellsy', '', '', 0, '1', '1', '1', '1', to_date('2024-02-07 15:12:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-07 15:12:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2618, '客户信息', '', 2, 4, 2617, 'customer', 'ep:avatar', 'erp/sale/customer/index', 'ErpCustomer', 0, '1', '1', '1', '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-07 15:22:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2619, '客户查询', 'erp:customer:query', 3, 1, 2618, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2620, '客户创建', 'erp:customer:create', 3, 2, 2618, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2621, '客户更新', 'erp:customer:update', 3, 3, 2618, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2622, '客户删除', 'erp:customer:delete', 3, 4, 2618, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2623, '客户导出', 'erp:customer:export', 3, 5, 2618, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 07:21:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2624, '库存调拨', '', 2, 5, 2583, 'move', 'ep:folder-remove', 'erp/stock/move/index', 'ErpStockMove', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-16 18:53:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2625, '库存调度单查询', 'erp:stock-move:query', 3, 1, 2624, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2626, '库存调度单创建', 'erp:stock-move:create', 3, 2, 2624, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2627, '库存调度单更新', 'erp:stock-move:update', 3, 3, 2624, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2628, '库存调度单删除', 'erp:stock-move:delete', 3, 4, 2624, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2629, '库存调度单导出', 'erp:stock-move:export', 3, 5, 2624, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2630, '库存调度单审批', 'erp:stock-move:update-status', 3, 6, 2624, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:13:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2631, '库存盘点', '', 2, 6, 2583, 'check', 'ep:circle-check-filled', 'erp/stock/check/index', 'ErpStockCheck', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-08 08:31:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2632, '库存盘点单查询', 'erp:stock-check:query', 3, 1, 2631, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2633, '库存盘点单创建', 'erp:stock-check:create', 3, 2, 2631, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2634, '库存盘点单更新', 'erp:stock-check:update', 3, 3, 2631, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2635, '库存盘点单删除', 'erp:stock-check:delete', 3, 4, 2631, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2636, '库存盘点单导出', 'erp:stock-check:export', 3, 5, 2631, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2637, '库存盘点单审批', 'erp:stock-check:update-status', 3, 6, 2631, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:13:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2638, '销售订单', '', 2, 1, 2617, 'order', 'fa:first-order', 'erp/sale/order/index', 'ErpSaleOrder', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-10 21:59:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2639, '销售订单查询', 'erp:sale-order:query', 3, 1, 2638, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2640, '销售订单创建', 'erp:sale-order:create', 3, 2, 2638, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2641, '销售订单更新', 'erp:sale-order:update', 3, 3, 2638, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2642, '销售订单删除', 'erp:sale-order:delete', 3, 4, 2638, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2643, '销售订单导出', 'erp:sale-order:export', 3, 5, 2638, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2644, '销售订单审批', 'erp:sale-order:update-status', 3, 6, 2638, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:13:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2645, '财务管理', '', 1, 50, 2563, 'finance', 'ep:money', '', '', 0, '1', '1', '1', '1', to_date('2024-02-10 08:05:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-10 08:06:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2646, '结算账户', '', 2, 10, 2645, 'account', 'fa:universal-access', 'erp/finance/account/index', 'ErpAccount', 0, '1', '1', '1', '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-14 08:24:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2647, '结算账户查询', 'erp:account:query', 3, 1, 2646, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2648, '结算账户创建', 'erp:account:create', 3, 2, 2646, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2649, '结算账户更新', 'erp:account:update', 3, 3, 2646, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2650, '结算账户删除', 'erp:account:delete', 3, 4, 2646, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2651, '结算账户导出', 'erp:account:export', 3, 5, 2646, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-10 00:15:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2652, '销售出库', '', 2, 2, 2617, 'out', 'ep:sold-out', 'erp/sale/out/index', 'ErpSaleOut', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-10 22:02:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2653, '销售出库查询', 'erp:sale-out:query', 3, 1, 2652, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2654, '销售出库创建', 'erp:sale-out:create', 3, 2, 2652, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2655, '销售出库更新', 'erp:sale-out:update', 3, 3, 2652, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2656, '销售出库删除', 'erp:sale-out:delete', 3, 4, 2652, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2657, '销售出库导出', 'erp:sale-out:export', 3, 5, 2652, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2658, '销售出库审批', 'erp:sale-out:update-status', 3, 6, 2652, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:13:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2659, '销售退货', '', 2, 3, 2617, 'return', 'fa-solid:bone', 'erp/sale/return/index', 'ErpSaleReturn', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-12 06:12:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2660, '销售退货查询', 'erp:sale-return:query', 3, 1, 2659, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2661, '销售退货创建', 'erp:sale-return:create', 3, 2, 2659, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2662, '销售退货更新', 'erp:sale-return:update', 3, 3, 2659, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2663, '销售退货删除', 'erp:sale-return:delete', 3, 4, 2659, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2664, '销售退货导出', 'erp:sale-return:export', 3, 5, 2659, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:12:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2665, '销售退货审批', 'erp:sale-return:update-status', 3, 6, 2659, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-07 11:13:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2666, '采购订单', '', 2, 1, 2602, 'order', 'fa-solid:border-all', 'erp/purchase/order/index', 'ErpPurchaseOrder', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-12 08:51:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2667, '采购订单查询', 'erp:purchase-order:query', 3, 1, 2666, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2668, '采购订单创建', 'erp:purchase-order:create', 3, 2, 2666, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2669, '采购订单更新', 'erp:purchase-order:update', 3, 3, 2666, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2670, '采购订单删除', 'erp:purchase-order:delete', 3, 4, 2666, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2671, '采购订单导出', 'erp:purchase-order:export', 3, 5, 2666, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2672, '采购订单审批', 'erp:purchase-order:update-status', 3, 6, 2666, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2673, '采购入库', '', 2, 2, 2602, 'in', 'fa-solid:gopuram', 'erp/purchase/in/index', 'ErpPurchaseIn', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-12 11:19:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2674, '采购入库查询', 'erp:purchase-in:query', 3, 1, 2673, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2675, '采购入库创建', 'erp:purchase-in:create', 3, 2, 2673, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2676, '采购入库更新', 'erp:purchase-in:update', 3, 3, 2673, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2677, '采购入库删除', 'erp:purchase-in:delete', 3, 4, 2673, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2678, '采购入库导出', 'erp:purchase-in:export', 3, 5, 2673, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2679, '采购入库审批', 'erp:purchase-in:update-status', 3, 6, 2673, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2680, '采购退货', '', 2, 3, 2602, 'return', 'ep:minus', 'erp/purchase/return/index', 'ErpPurchaseReturn', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-12 20:51:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2681, '采购退货查询', 'erp:purchase-return:query', 3, 1, 2680, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2682, '采购退货创建', 'erp:purchase-return:create', 3, 2, 2680, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2683, '采购退货更新', 'erp:purchase-return:update', 3, 3, 2680, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2684, '采购退货删除', 'erp:purchase-return:delete', 3, 4, 2680, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2685, '采购退货导出', 'erp:purchase-return:export', 3, 5, 2680, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2686, '采购退货审批', 'erp:purchase-return:update-status', 3, 6, 2680, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2687, '付款单', '', 2, 1, 2645, 'payment', 'ep:caret-right', 'erp/finance/payment/index', 'ErpFinancePayment', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-14 08:24:23', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2688, '付款单查询', 'erp:finance-payment:query', 3, 1, 2687, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2689, '付款单创建', 'erp:finance-payment:create', 3, 2, 2687, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2690, '付款单更新', 'erp:finance-payment:update', 3, 3, 2687, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2691, '付款单删除', 'erp:finance-payment:delete', 3, 4, 2687, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2692, '付款单导出', 'erp:finance-payment:export', 3, 5, 2687, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2693, '付款单审批', 'erp:finance-payment:update-status', 3, 6, 2687, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2694, '收款单', '', 2, 2, 2645, 'receipt', 'ep:expand', 'erp/finance/receipt/index', 'ErpFinanceReceipt', 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-15 19:35:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2695, '收款单查询', 'erp:finance-receipt:query', 3, 1, 2694, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2696, '收款单创建', 'erp:finance-receipt:create', 3, 2, 2694, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2697, '收款单更新', 'erp:finance-receipt:update', 3, 3, 2694, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:44:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2698, '收款单删除', 'erp:finance-receipt:delete', 3, 4, 2694, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2699, '收款单导出', 'erp:finance-receipt:export', 3, 5, 2694, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2700, '收款单审批', 'erp:finance-receipt:update-status', 3, 6, 2694, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-02-05 16:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-02-12 00:45:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2701, '待办事项', '', 2, 0, 2397, 'backlog', 'fa-solid:tasks', 'crm/backlog/index', 'CrmBacklog', 0, '1', '1', '1', '1', to_date('2024-02-17 17:17:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-17 17:17:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2702, 'ERP 首页', 'erp:statistics:query', 2, 0, 2563, 'home', 'ep:home-filled', 'erp/home/index.vue', 'ErpHome', 0, '1', '1', '1', '1', to_date('2024-02-18 16:49:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-26 21:12:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2703, '商机状态配置', '', 2, 4, 2524, 'business-status', 'fa-solid:charging-station', 'crm/business/status/index', 'CrmBusinessStatus', 0, '1', '1', '1', '1', to_date('2024-02-21 20:15:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-21 20:15:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2704, '商机状态查询', 'crm:business-status:query', 3, 1, 2703, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-02-21 20:35:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-21 20:36:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2705, '商机状态创建', 'crm:business-status:create', 3, 2, 2703, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-02-21 20:35:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-21 20:35:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2706, '商机状态更新', 'crm:business-status:update', 3, 3, 2703, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-02-21 20:36:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-21 20:36:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2707, '商机状态删除', 'crm:business-status:delete', 3, 4, 2703, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-02-21 20:36:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-21 20:36:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2708, '合同配置', '', 2, 5, 2524, 'contract-config', 'ep:connection', 'crm/contract/config/index', 'CrmContractConfig', 0, '1', '1', '1', '1', to_date('2024-02-24 16:44:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-24 16:44:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2709, '客户公海配置查询', 'crm:customer-pool-config:query', 3, 2, 2516, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-02-24 16:45:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-24 16:45:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2710, '合同配置更新', 'crm:contract-config:update', 3, 1, 2708, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-02-24 16:45:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-24 16:45:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2711, '合同配置查询', 'crm:contract-config:query', 3, 2, 2708, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-02-24 16:46:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-24 16:46:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2712, '客户分析', 'crm:statistics-customer:query', 2, 0, 2560, 'customer', 'ep:avatar', 'crm/statistics/customer/index.vue', 'CrmStatisticsCustomer', 0, '1', '1', '1', '1', to_date('2024-03-09 16:43:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-04 20:38:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2713, '抄送我的', 'bpm:process-instance-cc:query', 2, 30, 1200, 'copy', 'ep:copy-document', 'bpm/task/copy/index', 'BpmProcessInstanceCopy', 0, '1', '1', '1', '1', to_date('2024-03-17 21:50:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:55:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2714, '流程分类', '', 2, 3, 1186, 'category', 'fa:object-ungroup', 'bpm/category/index', 'BpmCategory', 0, '1', '1', '1', '', to_date('2024-03-08 02:00:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-21 23:51:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2715, '分类查询', 'bpm:category:query', 3, 1, 2714, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-03-08 02:00:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-19 14:36:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2716, '分类创建', 'bpm:category:create', 3, 2, 2714, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-03-08 02:00:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-19 14:36:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2717, '分类更新', 'bpm:category:update', 3, 3, 2714, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-03-08 02:00:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-19 14:36:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2718, '分类删除', 'bpm:category:delete', 3, 4, 2714, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-03-08 02:00:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-19 14:36:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2720, '发起流程', '', 2, 0, 1200, 'create', 'fa-solid:grin-stars', 'bpm/processInstance/create/index', 'BpmProcessInstanceCreate', 0, '1', '0', '1', '1', to_date('2024-03-19 19:46:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-23 19:03:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2721, '流程实例', '', 2, 10, 1186, 'process-instance/manager', 'fa:square', 'bpm/processInstance/manager/index', 'BpmProcessInstanceManager', 0, '1', '1', '1', '1', to_date('2024-03-21 23:57:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-21 23:57:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2722, '流程实例的查询（管理员）', 'bpm:process-instance:manager-query', 3, 1, 2721, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-03-22 08:18:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-22 08:19:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2723, '流程实例的取消（管理员）', 'bpm:process-instance:cancel-by-admin', 3, 2, 2721, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-03-22 08:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-22 08:19:25', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2724, '流程任务', '', 2, 11, 1186, 'process-tasnk', 'ep:collection-tag', 'bpm/task/manager/index', 'BpmManagerTask', 0, '1', '1', '1', '1', to_date('2024-03-22 08:43:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-22 08:43:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2725, '流程任务的查询（管理员）', 'bpm:task:mananger-query', 3, 1, 2724, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-03-22 08:43:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-22 08:43:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2726, '流程监听器', '', 2, 5, 1186, 'process-listener', 'fa:assistive-listening-systems', 'bpm/processListener/index', 'BpmProcessListener', 0, '1', '1', '1', '', to_date('2024-03-09 16:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-23 13:13:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2727, '流程监听器查询', 'bpm:process-listener:query', 3, 1, 2726, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-03-09 16:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-03-09 16:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2728, '流程监听器创建', 'bpm:process-listener:create', 3, 2, 2726, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-03-09 16:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-03-09 16:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2729, '流程监听器更新', 'bpm:process-listener:update', 3, 3, 2726, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-03-09 16:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-03-09 16:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2730, '流程监听器删除', 'bpm:process-listener:delete', 3, 4, 2726, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-03-09 16:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-03-09 16:05:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2731, '流程表达式', '', 2, 6, 1186, 'process-expression', 'fa:wpexplorer', 'bpm/processExpression/index', 'BpmProcessExpression', 0, '1', '1', '1', '', to_date('2024-03-09 22:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-23 19:43:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2732, '流程表达式查询', 'bpm:process-expression:query', 3, 1, 2731, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-03-09 22:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-03-09 22:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2733, '流程表达式创建', 'bpm:process-expression:create', 3, 2, 2731, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-03-09 22:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-03-09 22:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2734, '流程表达式更新', 'bpm:process-expression:update', 3, 3, 2731, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-03-09 22:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-03-09 22:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2735, '流程表达式删除', 'bpm:process-expression:delete', 3, 4, 2731, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-03-09 22:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-03-09 22:35:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2736, '员工业绩', 'crm:statistics-performance:query', 2, 3, 2560, 'performance', 'ep:dish-dot', 'crm/statistics/performance/index', 'CrmStatisticsPerformance', 0, '1', '1', '1', '1', to_date('2024-04-05 13:49:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:42:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2737, '客户画像', 'crm:statistics-portrait:query', 2, 4, 2560, 'portrait', 'ep:picture', 'crm/statistics/portrait/index', 'CrmStatisticsPortrait', 0, '1', '1', '1', '1', to_date('2024-04-05 13:57:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:42:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2738, '销售漏斗', 'crm:statistics-funnel:query', 2, 5, 2560, 'funnel', 'ep:grape', 'crm/statistics/funnel/index', 'CrmStatisticsFunnel', 0, '1', '1', '1', '1', to_date('2024-04-13 10:53:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:39:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2739, '消息中心', '', 1, 7, 1, 'messages', 'ep:chat-dot-round', '', '', 0, '1', '1', '1', '1', to_date('2024-04-22 23:54:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 09:36:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2740, '监控中心', '', 1, 10, 2, 'monitors', 'ep:monitor', '', '', 0, '1', '1', '1', '1', to_date('2024-04-23 00:04:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-23 00:04:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2741, '领取公海客户', 'crm:customer:receive', 3, 1, 2546, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:47:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:47:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2742, '分配公海客户', 'crm:customer:distribute', 3, 2, 2546, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:48:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:48:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2743, '商品统计查询', 'statistics:product:query', 3, 1, 2545, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:50:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:50:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2744, '商品统计导出', 'statistics:product:export', 3, 2, 2545, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:50:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:50:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2745, '支付渠道查询', 'pay:channel:query', 3, 10, 1126, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:53:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:53:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2746, '支付渠道创建', 'pay:channel:create', 3, 11, 1126, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2747, '支付渠道更新', 'pay:channel:update', 3, 12, 1126, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:53:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:53:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2748, '支付渠道删除', 'pay:channel:delete', 3, 13, 1126, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:54:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:54:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2749, '商品收藏查询', 'product:favorite:query', 3, 10, 2014, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:55:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:55:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2750, '商品浏览查询', 'product:browse-history:query', 3, 20, 2014, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:57:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:57:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2751, '售后同意', 'trade:after-sale:agree', 3, 2, 2073, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:58:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:58:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2752, '售后不同意', 'trade:after-sale:disagree', 3, 3, 2073, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 19:59:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 19:59:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2753, '售后确认退货', 'trade:after-sale:receive', 3, 4, 2073, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 20:00:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 20:00:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2754, '售后确认退款', 'trade:after-sale:refund', 3, 5, 2073, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 20:00:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 20:00:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2755, '删除项目', 'report:go-view-project:delete', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 20:01:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 20:01:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2756, '会员等级记录查询', 'member:level-record:query', 3, 10, 2325, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 20:02:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 20:02:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2757, '会员经验记录查询', 'member:experience-record:query', 3, 11, 2325, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-04-24 20:02:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-24 20:02:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2758, 'AI 大模型', '', 1, 400, 0, '/ai', 'tabler:ai', '', '', 0, '1', '1', '1', '1', to_date('2024-05-07 15:07:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-19 18:57:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2759, 'AI 对话', '', 2, 1, 2758, 'chat', 'ep:message', 'ai/chat/index/index.vue', 'AiChat', 0, '1', '1', '1', '1', to_date('2024-05-07 15:09:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 17:15:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2760, '控制台', '', 1, 100, 2758, 'console', 'ep:setting', '', '', 0, '1', '1', '1', '1', to_date('2024-05-09 22:39:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-24 23:34:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2761, 'API 密钥', '', 2, 0, 2760, 'api-key', 'ep:key', 'ai/model/apiKey/index.vue', 'AiApiKey', 0, '1', '1', '1', '', to_date('2024-05-09 14:52:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-10 22:44:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2762, 'API 密钥查询', 'ai:api-key:query', 3, 1, 2761, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-09 14:52:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-13 20:36:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-09 14:52:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-13 20:36:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-09 14:52:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-13 20:36:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-09 14:52:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-13 20:36:48', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2767, '模型配置', '', 2, 0, 2760, 'model', 'fa-solid:abacus', 'ai/model/model/index.vue', 'AiModel', 0, '1', '1', '1', '', to_date('2024-05-10 14:42:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 09:57:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2768, '聊天模型查询', 'ai:model:query', 3, 1, 2767, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-10 14:42:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 09:19:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2769, '聊天模型创建', 'ai:model:create', 3, 2, 2767, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-10 14:42:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 09:20:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2770, '聊天模型更新', 'ai:model:update', 3, 3, 2767, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-10 14:42:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 09:20:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2771, '聊天模型删除', 'ai:model:delete', 3, 4, 2767, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-10 14:42:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-03 09:20:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, '1', '1', '1', '', to_date('2024-05-13 12:39:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-13 20:41:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-05-13 12:39:28', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-05-13 12:39:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-05-13 12:39:28', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-05-13 12:39:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2776, '聊天角色更新', 'ai:chat-role:update', 3, 3, 2773, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-05-13 12:39:28', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-05-13 12:39:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2777, '聊天角色删除', 'ai:chat-role:delete', 3, 4, 2773, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-05-13 21:43:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-13 21:43:38', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2778, '聊天管理', '', 2, 10, 2760, 'chat-conversation', 'ep:chat-square', 'ai/chat/manager/index.vue', 'AiChatManager', 0, '1', '1', '1', '', to_date('2024-05-24 15:39:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-26 21:36:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2779, '会话查询', 'ai:chat-conversation:query', 3, 1, 2778, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-24 15:39:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-25 08:38:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2780, '会话删除', 'ai:chat-conversation:delete', 3, 2, 2778, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-05-24 15:39:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-25 08:38:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2781, '消息查询', 'ai:chat-message:query', 3, 11, 2778, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-05-25 08:38:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-25 08:38:56', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2782, '消息删除', 'ai:chat-message:delete', 3, 12, 2778, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-05-25 08:39:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-05-25 08:39:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2783, 'AI 绘画', '', 2, 2, 2758, 'image', 'ep:picture-rounded', 'ai/image/index/index.vue', 'AiImage', 0, '1', '1', '1', '1', to_date('2024-05-26 11:45:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 17:18:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2784, '绘画管理', '', 2, 11, 2760, 'image', 'fa:file-image-o', 'ai/image/manager/index.vue', 'AiImageManager', 0, '1', '1', '1', '', to_date('2024-06-26 13:32:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-26 21:37:13', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2785, '绘画查询', 'ai:image:query', 3, 1, 2784, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-06-26 13:32:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-26 22:21:57', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2786, '绘画删除', 'ai:image:delete', 3, 4, 2784, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-06-26 13:32:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-26 22:22:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2787, '绘图更新', 'ai:image:update', 3, 2, 2784, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-06-26 22:47:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-31 09:21:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2788, '音乐管理', '', 2, 12, 2760, 'music', 'fa:music', 'ai/music/manager/index.vue', 'AiMusicManager', 0, '1', '1', '1', '', to_date('2024-06-27 15:03:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-06-27 23:04:19', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2789, '音乐查询', 'ai:music:query', 3, 1, 2788, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-06-27 15:03:33', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-06-27 15:03:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2790, '音乐更新', 'ai:music:update', 3, 3, 2788, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-06-27 15:03:33', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-06-27 15:03:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2791, '音乐删除', 'ai:music:delete', 3, 4, 2788, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-06-27 15:03:33', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-06-27 15:03:33', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2792, 'AI 写作', '', 2, 3, 2758, 'write', 'fa-solid:book-reader', 'ai/write/index/index.vue', 'AiWrite', 0, '1', '1', '1', '1', to_date('2024-07-08 09:26:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-16 13:03:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2793, '写作管理', '', 2, 13, 2760, 'write', 'fa:bookmark-o', 'ai/write/manager/index.vue', 'AiWriteManager', 0, '1', '1', '1', '', to_date('2024-07-10 13:24:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-10 21:31:59', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2794, 'AI 写作查询', 'ai:write:query', 3, 1, 2793, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-07-10 13:24:34', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-07-10 13:24:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-07-10 13:24:34', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-07-10 13:24:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, '1', '1', '1', '1', to_date('2024-07-17 09:21:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-29 21:11:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, '1', '1', '1', '1', to_date('2024-07-17 23:49:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-17 23:49:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2798, 'AI 思维导图', '', 2, 6, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, '1', '1', '1', '1', to_date('2024-07-29 21:31:59', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-02 18:57:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, '1', '1', '1', '', to_date('2024-08-10 09:15:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-10 17:24:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-08-10 09:15:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-08-10 09:15:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-08-10 09:15:09', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-08-10 09:15:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2802, '会话查询', 'promotion:kefu-conversation:query', 3, 1, 2797, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-08-31 09:17:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-31 09:18:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2803, '会话更新', 'promotion:kefu-conversation:update', 3, 2, 2797, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-08-31 09:18:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-31 09:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2804, '消息查询', 'promotion:kefu-message:query', 3, 10, 2797, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-08-31 09:18:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-31 09:18:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2805, '会话删除', 'promotion:kefu-conversation:delete', 3, 3, 2797, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-08-31 09:19:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-31 09:20:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2806, '消息发送', 'promotion:kefu-message:send', 3, 12, 2797, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-08-31 09:20:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-31 09:20:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2807, '消息更新', 'promotion:kefu-message:update', 3, 11, 2797, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-08-31 09:20:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-31 09:20:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2808, '积分商城', '', 2, 5, 2030, 'point-activity', 'ep:bowl', 'mall/promotion/point/activity/index', 'PointActivity', 0, '1', '1', '1', '', to_date('2024-09-21 05:36:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-23 09:14:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2809, '积分商城活动查询', 'promotion:point-activity:query', 3, 1, 2808, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-21 05:36:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-22 14:49:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2810, '积分商城活动创建', 'promotion:point-activity:create', 3, 2, 2808, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-21 05:36:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-22 14:49:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2811, '积分商城活动更新', 'promotion:point-activity:update', 3, 3, 2808, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-21 05:36:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-22 14:49:10', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2812, '积分商城活动删除', 'promotion:point-activity:delete', 3, 4, 2808, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-21 05:36:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-22 14:49:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2813, '积分商城活动导出', 'promotion:point-activity:export', 3, 5, 2808, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-21 05:36:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-22 14:49:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-12-01 14:32:39', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-01 14:32:39', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-01-17 19:32:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-01-17 19:32:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-01-23 20:23:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-01-23 20:23:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2915, 'AI 知识库', '', 2, 5, 2758, 'knowledge', 'ep:notebook', 'ai/knowledge/knowledge/index', 'AiKnowledge', 0, '1', '1', '1', '', to_date('2025-02-28 07:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-02 18:58:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2916, 'AI 知识库查询', 'ai:knowledge:query', 3, 1, 2915, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-02-28 07:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-02-28 07:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2917, 'AI 知识库创建', 'ai:knowledge:create', 3, 2, 2915, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-02-28 07:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-02-28 07:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2918, 'AI 知识库更新', 'ai:knowledge:update', 3, 3, 2915, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-02-28 07:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-02-28 07:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2919, 'AI 知识库删除', 'ai:knowledge:delete', 3, 4, 2915, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-02-28 07:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-02-28 07:04:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2920, '工具管理', '', 2, 0, 2760, 'tool', 'fa-solid:tools', 'ai/model/tool/index.vue', 'AiTool', 0, '1', '1', '1', '', to_date('2025-03-14 11:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-14 19:20:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2921, '工具查询', 'ai:tool:query', 3, 1, 2920, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-03-14 11:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-14 11:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-03-14 11:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-14 11:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-03-14 11:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-14 11:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-03-14 11:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-14 11:19:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4000, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, '1', '1', '1', '1', to_date('2024-08-10 09:55:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-07 15:58:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4001, '设备接入', '', 1, 2, 4000, 'device', 'ep:platform', '', '', 0, '1', '1', '1', '1', to_date('2024-08-10 09:57:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-27 08:39:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4002, '产品管理', '', 2, 2, 4001, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, '1', '1', '1', '', to_date('2024-08-10 02:38:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-07 18:47:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4003, '产品查询', 'iot:product:query', 3, 1, 4002, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-08-10 02:38:02', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-07 15:55:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4004, '产品创建', 'iot:product:create', 3, 2, 4002, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-08-10 02:38:02', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-07 15:55:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4005, '产品更新', 'iot:product:update', 3, 3, 4002, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-08-10 02:38:02', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-07 15:55:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4006, '产品删除', 'iot:product:delete', 3, 4, 4002, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-08-10 02:38:02', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-07 15:55:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4007, '产品导出', 'iot:product:export', 3, 5, 4002, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-08-10 02:38:02', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-07 15:55:13', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4008, '设备管理', '', 2, 4, 4001, 'device', 'fa:mobile', 'iot/device/device/index', 'IoTDevice', 0, '1', '1', '1', '', to_date('2024-09-16 18:48:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-14 11:39:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4009, '设备查询', 'iot:device:query', 3, 1, 4008, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-16 18:48:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-07 15:55:40', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4010, '设备创建', 'iot:device:create', 3, 2, 4008, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-16 18:48:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-07 15:55:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4011, '设备更新', 'iot:device:update', 3, 3, 4008, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-16 18:48:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-07 15:55:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4012, '设备删除', 'iot:device:delete', 3, 4, 4008, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-16 18:48:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-07 15:55:43', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4013, '设备导出', 'iot:device:export', 3, 5, 4008, '', '', '', '', 0, '1', '1', '1', '', to_date('2024-09-16 18:48:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-07 15:55:44', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4014, '产品分类', '', 2, 1, 4001, 'product-category', 'ep:notebook', 'iot/product/category/index', 'IotProductCategory', 0, '1', '1', '1', '', to_date('2024-12-07 16:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-07 16:31:52', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4015, '产品分类查询', 'iot:product-category:query', 3, 1, 4014, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-07 16:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-07 16:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4016, '产品分类创建', 'iot:product-category:create', 3, 2, 4014, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-07 16:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-07 16:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4017, '产品分类更新', 'iot:product-category:update', 3, 3, 4014, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-07 16:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-07 16:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4018, '产品分类删除', 'iot:product-category:delete', 3, 4, 4014, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-07 16:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-07 16:01:35', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4025, '插件管理', '', 2, 5, 4047, 'plugin-config', 'ep:folder-opened', 'iot/plugin/index', 'IoTPlugin', 0, '1', '1', '1', '', to_date('2024-12-09 21:25:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-05 22:23:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4026, '插件查询', 'iot:plugin-config:query', 3, 1, 4025, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-09 21:25:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-02-05 21:23:20', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4027, '插件创建', 'iot:plugin-config:create', 3, 2, 4025, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-09 21:25:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-02-05 21:23:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4028, '插件更新', 'iot:plugin-config:update', 3, 3, 4025, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-09 21:25:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-02-05 21:23:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4029, '插件删除', 'iot:plugin-config:delete', 3, 4, 4025, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-09 21:25:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-02-05 21:23:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4030, '插件导出', 'iot:plugin-config:export', 3, 5, 4025, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-09 21:25:06', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-02-05 21:23:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4031, '设备分组', '', 2, 3, 4001, 'device-group', 'fa-solid:layer-group', 'iot/device/group/index', 'IotDeviceGroup', 0, '1', '1', '1', '', to_date('2024-12-14 17:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-14 17:09:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4032, '设备分组查询', 'iot:device-group:query', 3, 1, 4031, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-14 17:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-14 17:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4033, '设备分组创建', 'iot:device-group:create', 3, 2, 4031, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-14 17:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-14 17:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4034, '设备分组更新', 'iot:device-group:update', 3, 3, 4031, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-14 17:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-14 17:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4035, '设备分组删除', 'iot:device-group:delete', 3, 4, 4031, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-14 17:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2024-12-14 17:08:29', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4036, '设备导入', 'iot:device:import', 3, 6, 4008, '', '', '', '', 0, '1', '1', '1', '1', to_date('2024-12-15 10:35:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-15 10:35:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4037, '产品物模型', '', 2, 2, 4001, 'thing-model', 'ep:mostly-cloudy', 'iot/thingmodel/index', 'IoTThingModel', 0, '0', '0', '0', '', to_date('2024-12-16 17:17:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-12-27 11:03:37', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4038, '产品物模型功能查询', 'iot:thing-model:query', 3, 1, 4037, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-16 17:17:51', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-17 09:14:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4039, '产品物模型功能创建', 'iot:thing-model:create', 3, 2, 4037, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-16 17:17:52', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-17 09:14:58', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4040, '产品物模型功能更新', 'iot:thing-model:update', 3, 3, 4037, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-16 17:17:52', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-17 09:15:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4041, '产品物模型功能删除', 'iot:thing-model:delete', 3, 4, 4037, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-16 17:17:52', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-17 09:15:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4042, '产品物模型功能导出', 'iot:thing-model:export', 3, 5, 4037, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2024-12-16 17:17:53', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-17 09:15:09', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4043, '设备上行', 'iot:device:upstream', 3, 7, 4008, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-01-28 04:40:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-01-31 22:45:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4044, '设备属性查询', 'iot:device:property-query', 3, 10, 4008, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-01-28 11:52:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-01-28 11:52:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4045, '设备日志查询', 'iot:device:log-query', 3, 11, 4008, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-01-28 11:53:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-01-28 11:53:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4046, '设备下行', 'iot:device:downstream', 3, 8, 4008, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-01-31 22:46:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-01-31 22:46:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4047, '运维管理', '', 1, 2, 4000, 'operations', 'fa:cog', '', '', 0, '1', '1', '1', '1', to_date('2025-02-05 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-05 22:22:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4048, '规则引擎', '', 1, 3, 4000, 'rule', 'fa-solid:cogs', '', '', 0, '1', '1', '1', '1', to_date('2025-02-11 14:10:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-11 14:10:54', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4049, '场景联动', '', 2, 1, 4048, 'scene', 'ep:link', 'iot/rule/scene/index', 'Scene', 0, '1', '1', '1', '1', to_date('2025-02-11 14:12:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-12 10:15:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4050, 'IoT首页', '', 2, 1, 4000, 'home', 'ep:home-filled', 'iot/home/index', 'IotHome', 0, '1', '1', '1', '1', to_date('2025-02-27 08:39:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-02-27 08:40:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4051, '数据桥梁', '', 2, 0, 4048, 'data-bridge', 'ep:guide', 'iot/rule/databridge/index', 'IotDataBridge', 0, '1', '1', '1', '', to_date('2025-03-09 13:47:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-09 13:47:51', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4052, 'IoT 数据桥梁查询', 'iot:data-bridge:query', 3, 1, 4051, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-03-09 13:47:11', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-09 13:47:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4053, 'IoT 数据桥梁创建', 'iot:data-bridge:create', 3, 2, 4051, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-03-09 13:47:11', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-09 13:47:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4054, 'IoT 数据桥梁更新', 'iot:data-bridge:update', 3, 3, 4051, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-03-09 13:47:11', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-09 13:47:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4055, 'IoT 数据桥梁删除', 'iot:data-bridge:delete', 3, 4, 4051, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-03-09 13:47:12', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-09 13:47:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4056, 'IoT 数据桥梁导出', 'iot:data-bridge:export', 3, 5, 4051, '', '', '', NULL, 0, '1', '1', '1', '', to_date('2025-03-09 13:47:12', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2025-03-09 13:47:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5000, 'AI 工作流', '', 2, 5, 2758, 'workflow', 'fa:hand-grab-o', 'ai/workflow/index.vue', 'AiWorkflow', 0, '1', '1', '1', '1', to_date('2025-03-25 09:50:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-03 18:55:12', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5001, 'AI 工作流查询', 'ai:workflow:query', 3, 1, 5000, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-03-25 09:51:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-25 09:51:11', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5002, 'AI 工作流创建', 'ai:workflow:create', 3, 2, 5000, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-03-25 09:51:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-25 09:51:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5003, 'AI 工作流更新', 'ai:workflow:update', 3, 3, 5000, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-03-25 09:51:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-25 09:51:42', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5004, 'AI 工作流删除', 'ai:workflow:delete', 3, 4, 5000, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-03-25 09:51:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-25 09:52:03', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5005, 'AI 工作流测试', 'ai:workflow:test', 3, 5, 5000, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-03-30 10:29:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-30 10:29:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5009, '仪表盘设计器', '', 2, 1, 1281, 'jimu-bi', 'fa:y-combinator', 'report/jmreport/bi', 'JimuBI', 0, '1', '1', '1', '1', to_date('2025-05-03 09:57:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-03 10:02:05', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5010, '租户切换', 'system:tenant:visit', 3, 999, 1138, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-05-05 15:25:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-05 15:25:32', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5011, '转账订单查询', 'pay:transfer:query', 3, 1, 2559, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-05-08 12:46:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-08 12:46:53', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5012, '转账订单导出', 'pay:transfer:export', 3, 2, 2559, '', '', '', '', 0, '1', '1', '1', '1', to_date('2025-05-10 17:00:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-10 17:00:28', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_menu_seq\n    START WITH 5013;\n\n-- ----------------------------\n-- Table structure for system_notice\n-- ----------------------------\nCREATE TABLE system_notice\n(\n    id          number                                 NOT NULL,\n    title       varchar2(50)                           NULL,\n    content     clob                                   NULL,\n    type        smallint                               NOT NULL,\n    status      smallint     DEFAULT 0                 NOT NULL,\n    creator     varchar2(64) DEFAULT ''                NULL,\n    create_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64) DEFAULT ''                NULL,\n    update_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0) DEFAULT 0                 NOT NULL,\n    tenant_id   number       DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_notice\n    ADD CONSTRAINT pk_system_notice PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notice.id IS '公告ID';\nCOMMENT ON COLUMN system_notice.title IS '公告标题';\nCOMMENT ON COLUMN system_notice.content IS '公告内容';\nCOMMENT ON COLUMN system_notice.type IS '公告类型（1通知 2公告）';\nCOMMENT ON COLUMN system_notice.status IS '公告状态（0正常 1关闭）';\nCOMMENT ON COLUMN system_notice.creator IS '创建者';\nCOMMENT ON COLUMN system_notice.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notice.updater IS '更新者';\nCOMMENT ON COLUMN system_notice.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notice.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notice.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notice IS '通知公告表';\n\n-- ----------------------------\n-- Records of system_notice\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '芋道的公众', '<p>新版本内容133</p>', 1, 0, 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-04 21:00:20', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '维护通知：2018-07-01 系统凌晨维护', '<p><img src=\"http://test.yudao.iocoder.cn/b7cb3cf49b4b3258bf7309a09dd2f4e5.jpg\" alt=\"\" data-href=\"\">11112222<img src=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\" alt=\"image\" data-href=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\">3333</p>', 2, 1, 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-18 23:56:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '我是测试标题', '<p>哈哈哈哈123</p>', 1, 0, '110', to_date('2022-02-22 01:01:25', 'SYYYY-MM-DD HH24:MI:SS'), '110', to_date('2022-02-22 01:01:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_notice_seq\n    START WITH 5;\n\n-- ----------------------------\n-- Table structure for system_notify_message\n-- ----------------------------\nCREATE TABLE system_notify_message\n(\n    id                number                                 NOT NULL,\n    user_id           number                                 NOT NULL,\n    user_type         smallint                               NOT NULL,\n    template_id       number                                 NOT NULL,\n    template_code     varchar2(64)                           NULL,\n    template_nickname varchar2(63)                           NULL,\n    template_content  varchar2(1024)                         NULL,\n    template_type     number                                 NOT NULL,\n    template_params   varchar2(255)                          NULL,\n    read_status       number(1, 0)                           NOT NULL,\n    read_time         date         DEFAULT NULL              NULL,\n    creator           varchar2(64) DEFAULT ''                NULL,\n    create_time       date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater           varchar2(64) DEFAULT ''                NULL,\n    update_time       date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted           number(1, 0) DEFAULT 0                 NOT NULL,\n    tenant_id         number       DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_notify_message\n    ADD CONSTRAINT pk_system_notify_message PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notify_message.id IS '用户ID';\nCOMMENT ON COLUMN system_notify_message.user_id IS '用户id';\nCOMMENT ON COLUMN system_notify_message.user_type IS '用户类型';\nCOMMENT ON COLUMN system_notify_message.template_id IS '模版编号';\nCOMMENT ON COLUMN system_notify_message.template_code IS '模板编码';\nCOMMENT ON COLUMN system_notify_message.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_notify_message.template_content IS '模版内容';\nCOMMENT ON COLUMN system_notify_message.template_type IS '模版类型';\nCOMMENT ON COLUMN system_notify_message.template_params IS '模版参数';\nCOMMENT ON COLUMN system_notify_message.read_status IS '是否已读';\nCOMMENT ON COLUMN system_notify_message.read_time IS '阅读时间';\nCOMMENT ON COLUMN system_notify_message.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_message.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_message.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_message.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_message.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notify_message.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notify_message IS '站内信消息表';\n\n-- ----------------------------\n-- Records of system_notify_message\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', to_date('2025-04-21 14:59:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 11:44:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:59:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', to_date('2025-04-21 14:59:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 11:45:04', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:59:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 103, 2, 2, 'register', '系统消息', '你好，欢迎 哈哈 加入大家庭！', 2, '{\"name\":\"哈哈\"}', '0', NULL, '1', to_date('2023-01-28 21:02:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 21:02:20', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', to_date('2025-04-21 14:59:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 22:21:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:59:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', to_date('2025-04-21 14:59:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 22:22:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:59:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 1, 2, 1, 'test', '123', '我是 2，我开始 3 了', 1, '{\"name\":\"2\",\"what\":\"3\"}', '1', to_date('2025-04-21 14:59:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 23:45:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:59:35', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 1, 2, 2, 'register', '系统消息', '你好，欢迎 123 加入大家庭！', 2, '{\"name\":\"123\"}', '1', to_date('2025-04-21 14:59:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-28 23:50:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:59:35', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-28 08:35:46提现￥0.09元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-28 08:35:46\",\"price\":\"0.09\"}', '0', NULL, '1', to_date('2023-09-28 16:36:22', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-09-28 16:36:22', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-30 20:59:40提现￥1.00元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-30 20:59:40\",\"price\":\"1.00\"}', '0', NULL, '1', to_date('2023-10-03 12:11:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-10-03 12:11:34', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_notify_message_seq\n    START WITH 11;\n\n-- ----------------------------\n-- Table structure for system_notify_template\n-- ----------------------------\nCREATE TABLE system_notify_template\n(\n    id          number                                  NOT NULL,\n    name        varchar2(63)                            NULL,\n    code        varchar2(64)                            NULL,\n    nickname    varchar2(255)                           NULL,\n    content     varchar2(1024)                          NULL,\n    type        smallint                                NOT NULL,\n    params      varchar2(255) DEFAULT NULL              NULL,\n    status      smallint                                NOT NULL,\n    remark      varchar2(255) DEFAULT NULL              NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_notify_template\n    ADD CONSTRAINT pk_system_notify_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notify_template.id IS '主键';\nCOMMENT ON COLUMN system_notify_template.name IS '模板名称';\nCOMMENT ON COLUMN system_notify_template.code IS '模版编码';\nCOMMENT ON COLUMN system_notify_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_notify_template.content IS '模版内容';\nCOMMENT ON COLUMN system_notify_template.type IS '类型';\nCOMMENT ON COLUMN system_notify_template.params IS '参数数组';\nCOMMENT ON COLUMN system_notify_template.status IS '状态';\nCOMMENT ON COLUMN system_notify_template.remark IS '备注';\nCOMMENT ON COLUMN system_notify_template.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_template.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_notify_template IS '站内信模板表';\n\nCREATE SEQUENCE system_notify_template_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_access_token\n-- ----------------------------\nCREATE TABLE system_oauth2_access_token\n(\n    id            number                                  NOT NULL,\n    user_id       number                                  NOT NULL,\n    user_type     smallint                                NOT NULL,\n    user_info     varchar2(512)                           NULL,\n    access_token  varchar2(255)                           NULL,\n    refresh_token varchar2(32)                            NULL,\n    client_id     varchar2(255)                           NULL,\n    scopes        varchar2(255) DEFAULT NULL              NULL,\n    expires_time  date                                    NOT NULL,\n    creator       varchar2(64)  DEFAULT ''                NULL,\n    create_time   date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       varchar2(64)  DEFAULT ''                NULL,\n    update_time   date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id     number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_oauth2_access_token\n    ADD CONSTRAINT pk_system_oauth2_access_token PRIMARY KEY (id);\n\nCREATE INDEX idx_system_oauth2_access_token_01 ON system_oauth2_access_token (access_token);\nCREATE INDEX idx_system_oauth2_access_token_02 ON system_oauth2_access_token (refresh_token);\n\nCOMMENT ON COLUMN system_oauth2_access_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_access_token.user_info IS '用户信息';\nCOMMENT ON COLUMN system_oauth2_access_token.access_token IS '访问令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_access_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_access_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_access_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_access_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_access_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_access_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_access_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_access_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_access_token IS 'OAuth2 访问令牌';\n\nCREATE SEQUENCE system_oauth2_access_token_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_approve\n-- ----------------------------\nCREATE TABLE system_oauth2_approve\n(\n    id           number                                  NOT NULL,\n    user_id      number                                  NOT NULL,\n    user_type    smallint                                NOT NULL,\n    client_id    varchar2(255)                           NULL,\n    scope        varchar2(255) DEFAULT ''                NULL,\n    approved     number(1, 0)  DEFAULT '0'               NOT NULL,\n    expires_time date                                    NOT NULL,\n    creator      varchar2(64)  DEFAULT ''                NULL,\n    create_time  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      varchar2(64)  DEFAULT ''                NULL,\n    update_time  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id    number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_oauth2_approve\n    ADD CONSTRAINT pk_system_oauth2_approve PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_approve.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_approve.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_approve.scope IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_approve.approved IS '是否接受';\nCOMMENT ON COLUMN system_oauth2_approve.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_approve.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_approve.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_approve.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_approve.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_approve.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_approve.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_approve IS 'OAuth2 批准表';\n\nCREATE SEQUENCE system_oauth2_approve_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_client\n-- ----------------------------\nCREATE TABLE system_oauth2_client\n(\n    id                             number                                   NOT NULL,\n    client_id                      varchar2(255)                            NULL,\n    secret                         varchar2(255)                            NULL,\n    name                           varchar2(255)                            NULL,\n    logo                           varchar2(255)                            NULL,\n    description                    varchar2(255)  DEFAULT NULL              NULL,\n    status                         smallint                                 NOT NULL,\n    access_token_validity_seconds  number                                   NOT NULL,\n    refresh_token_validity_seconds number                                   NOT NULL,\n    redirect_uris                  varchar2(255)                            NULL,\n    authorized_grant_types         varchar2(255)                            NULL,\n    scopes                         varchar2(255)  DEFAULT NULL              NULL,\n    auto_approve_scopes            varchar2(255)  DEFAULT NULL              NULL,\n    authorities                    varchar2(255)  DEFAULT NULL              NULL,\n    resource_ids                   varchar2(255)  DEFAULT NULL              NULL,\n    additional_information         varchar2(4000) DEFAULT NULL              NULL,\n    creator                        varchar2(64)   DEFAULT ''                NULL,\n    create_time                    date           DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater                        varchar2(64)   DEFAULT ''                NULL,\n    update_time                    date           DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted                        number(1, 0)   DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_oauth2_client\n    ADD CONSTRAINT pk_system_oauth2_client PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_client.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_client.secret IS '客户端密钥';\nCOMMENT ON COLUMN system_oauth2_client.name IS '应用名';\nCOMMENT ON COLUMN system_oauth2_client.logo IS '应用图标';\nCOMMENT ON COLUMN system_oauth2_client.description IS '应用描述';\nCOMMENT ON COLUMN system_oauth2_client.status IS '状态';\nCOMMENT ON COLUMN system_oauth2_client.access_token_validity_seconds IS '访问令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.refresh_token_validity_seconds IS '刷新令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.redirect_uris IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_client.authorized_grant_types IS '授权类型';\nCOMMENT ON COLUMN system_oauth2_client.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_client.auto_approve_scopes IS '自动通过的授权范围';\nCOMMENT ON COLUMN system_oauth2_client.authorities IS '权限';\nCOMMENT ON COLUMN system_oauth2_client.resource_ids IS '资源';\nCOMMENT ON COLUMN system_oauth2_client.additional_information IS '附加信息';\nCOMMENT ON COLUMN system_oauth2_client.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_client.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_client.deleted IS '是否删除';\nCOMMENT ON TABLE system_oauth2_client IS 'OAuth2 客户端表';\n\n-- ----------------------------\n-- Records of system_oauth2_client\n-- ----------------------------\n-- @formatter:off\nINSERT 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\"]', '[\"user.read\",\"user.write\"]', '[]', '[\"user.read\",\"user.write\"]', '[]', '{}', '1', to_date('2022-05-11 21:47:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 20:42:22', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT 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/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.jpg', '啦啦啦啦', 0, 1800, 43200, '[\"https://www.iocoder.cn\"]', '[\"password\",\"authorization_code\",\"implicit\"]', '[\"user_info\",\"projects\"]', '[\"user_info\"]', '[]', '[]', '{}', '1', to_date('2022-05-12 00:28:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 19:58:08', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT 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', to_date('2022-09-29 13:28:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-02 18:32:30', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT 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/604bdc695e13b3b22745be704d1f2aa8ee05c5f26f9fead6d1ca49005afbc857.jpeg', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"password\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', to_date('2022-10-04 17:40:16', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-04 16:00:46', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_oauth2_client_seq\n    START WITH 43;\n\n-- ----------------------------\n-- Table structure for system_oauth2_code\n-- ----------------------------\nCREATE TABLE system_oauth2_code\n(\n    id           number                                  NOT NULL,\n    user_id      number                                  NOT NULL,\n    user_type    smallint                                NOT NULL,\n    code         varchar2(32)                            NULL,\n    client_id    varchar2(255)                           NULL,\n    scopes       varchar2(255) DEFAULT ''                NULL,\n    expires_time date                                    NOT NULL,\n    redirect_uri varchar2(255) DEFAULT NULL              NULL,\n    state        varchar2(255) DEFAULT ''                NULL,\n    creator      varchar2(64)  DEFAULT ''                NULL,\n    create_time  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      varchar2(64)  DEFAULT ''                NULL,\n    update_time  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id    number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_oauth2_code\n    ADD CONSTRAINT pk_system_oauth2_code PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_code.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_code.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_code.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_code.code IS '授权码';\nCOMMENT ON COLUMN system_oauth2_code.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_code.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_code.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_code.redirect_uri IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_code.state IS '状态';\nCOMMENT ON COLUMN system_oauth2_code.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_code.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_code IS 'OAuth2 授权码表';\n\nCREATE SEQUENCE system_oauth2_code_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_refresh_token\n-- ----------------------------\nCREATE TABLE system_oauth2_refresh_token\n(\n    id            number                                  NOT NULL,\n    user_id       number                                  NOT NULL,\n    refresh_token varchar2(32)                            NULL,\n    user_type     smallint                                NOT NULL,\n    client_id     varchar2(255)                           NULL,\n    scopes        varchar2(255) DEFAULT NULL              NULL,\n    expires_time  date                                    NOT NULL,\n    creator       varchar2(64)  DEFAULT ''                NULL,\n    create_time   date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       varchar2(64)  DEFAULT ''                NULL,\n    update_time   date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id     number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_oauth2_refresh_token\n    ADD CONSTRAINT pk_system_oauth2_refresh_token PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_refresh_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_refresh_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_refresh_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_refresh_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_refresh_token IS 'OAuth2 刷新令牌';\n\nCREATE SEQUENCE system_oauth2_refresh_token_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_operate_log\n-- ----------------------------\nCREATE TABLE system_operate_log\n(\n    id             number                                   NOT NULL,\n    trace_id       varchar2(64)   DEFAULT ''                NULL,\n    user_id        number                                   NOT NULL,\n    user_type      smallint       DEFAULT 0                 NOT NULL,\n    type           varchar2(50)                             NULL,\n    sub_type       varchar2(50)                             NULL,\n    biz_id         number                                   NOT NULL,\n    action         varchar2(2000) DEFAULT ''                NULL,\n    success        number(1, 0)   DEFAULT '1'               NOT NULL,\n    extra          varchar2(2000) DEFAULT ''                NULL,\n    request_method varchar2(16)   DEFAULT ''                NULL,\n    request_url    varchar2(255)  DEFAULT ''                NULL,\n    user_ip        varchar2(50)   DEFAULT NULL              NULL,\n    user_agent     varchar2(512)  DEFAULT NULL              NULL,\n    creator        varchar2(64)   DEFAULT ''                NULL,\n    create_time    date           DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar2(64)   DEFAULT ''                NULL,\n    update_time    date           DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        number(1, 0)   DEFAULT 0                 NOT NULL,\n    tenant_id      number         DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_operate_log\n    ADD CONSTRAINT pk_system_operate_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_operate_log.id IS '日志主键';\nCOMMENT ON COLUMN system_operate_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_operate_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_operate_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_operate_log.type IS '操作模块类型';\nCOMMENT ON COLUMN system_operate_log.sub_type IS '操作名';\nCOMMENT ON COLUMN system_operate_log.biz_id IS '操作数据模块编号';\nCOMMENT ON COLUMN system_operate_log.action IS '操作内容';\nCOMMENT ON COLUMN system_operate_log.success IS '操作结果';\nCOMMENT ON COLUMN system_operate_log.extra IS '拓展字段';\nCOMMENT ON COLUMN system_operate_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN system_operate_log.request_url IS '请求地址';\nCOMMENT ON COLUMN system_operate_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_operate_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_operate_log.creator IS '创建者';\nCOMMENT ON COLUMN system_operate_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_operate_log.updater IS '更新者';\nCOMMENT ON COLUMN system_operate_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_operate_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_operate_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_operate_log IS '操作日志记录 V2 版本';\n\nCREATE SEQUENCE system_operate_log_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_post\n-- ----------------------------\nCREATE TABLE system_post\n(\n    id          number                                  NOT NULL,\n    code        varchar2(64)                            NULL,\n    name        varchar2(50)                            NULL,\n    sort        number                                  NOT NULL,\n    status      smallint                                NOT NULL,\n    remark      varchar2(500) DEFAULT NULL              NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id   number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_post\n    ADD CONSTRAINT pk_system_post PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_post.id IS '岗位ID';\nCOMMENT ON COLUMN system_post.code IS '岗位编码';\nCOMMENT ON COLUMN system_post.name IS '岗位名称';\nCOMMENT ON COLUMN system_post.sort IS '显示顺序';\nCOMMENT ON COLUMN system_post.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_post.remark IS '备注';\nCOMMENT ON COLUMN system_post.creator IS '创建者';\nCOMMENT ON COLUMN system_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_post.updater IS '更新者';\nCOMMENT ON COLUMN system_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_post IS '岗位信息表';\n\n-- ----------------------------\n-- Records of system_post\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 'ceo', '董事长', 1, 0, '', 'admin', to_date('2021-01-06 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-11 15:19:04', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 'se', '项目经理', 2, 0, '', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-15 09:18:20', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 'user', '普通员工', 4, 0, '111222', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-24 21:32:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 'HR', '人力资源', 5, 0, '`', '1', to_date('2024-03-24 20:45:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-29 19:08:10', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_post_seq\n    START WITH 6;\n\n-- ----------------------------\n-- Table structure for system_role\n-- ----------------------------\nCREATE TABLE system_role\n(\n    id                  number                                  NOT NULL,\n    name                varchar2(30)                            NULL,\n    code                varchar2(100)                           NULL,\n    sort                number                                  NOT NULL,\n    data_scope          smallint      DEFAULT 1                 NOT NULL,\n    data_scope_dept_ids varchar2(500) DEFAULT ''                NULL,\n    status              smallint                                NOT NULL,\n    type                smallint                                NOT NULL,\n    remark              varchar2(500) DEFAULT NULL              NULL,\n    creator             varchar2(64)  DEFAULT ''                NULL,\n    create_time         date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater             varchar2(64)  DEFAULT ''                NULL,\n    update_time         date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted             number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id           number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_role\n    ADD CONSTRAINT pk_system_role PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_role.id IS '角色ID';\nCOMMENT ON COLUMN system_role.name IS '角色名称';\nCOMMENT ON COLUMN system_role.code IS '角色权限字符串';\nCOMMENT ON COLUMN system_role.sort IS '显示顺序';\nCOMMENT ON COLUMN system_role.data_scope IS '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）';\nCOMMENT ON COLUMN system_role.data_scope_dept_ids IS '数据范围 ( 指定部门数组)';\nCOMMENT ON COLUMN system_role.status IS '角色状态（0正常 1停用）';\nCOMMENT ON COLUMN system_role.type IS '角色类型';\nCOMMENT ON COLUMN system_role.remark IS '备注';\nCOMMENT ON COLUMN system_role.creator IS '创建者';\nCOMMENT ON COLUMN system_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role.updater IS '更新者';\nCOMMENT ON COLUMN system_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role IS '角色信息表';\n\n-- ----------------------------\n-- Records of system_role\n-- ----------------------------\n-- @formatter:off\nINSERT 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 (1, '超级管理员', 'super_admin', 1, 1, '', 0, 1, '超级管理员', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-22 05:08:21', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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 (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', to_date('2021-01-05 17:03:48', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-02-22 05:08:20', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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', to_date('2024-02-24 10:51:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-02-24 02:51:32', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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 (101, '测试账号', 'test', 0, 1, '[]', 0, 2, '123', '', to_date('2021-01-06 13:49:35', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-30 17:38:28', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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', to_date('2022-02-22 00:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 00:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT 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', to_date('2022-03-07 21:37:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-07 21:37:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT 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, '测试数据权限', 'test-dp', 3, 2, '[100,102,103,104,105,108]', 0, 2, '', '1', to_date('2025-03-31 14:58:06', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-17 23:07:44', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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 (158, '2', '3', 4, 1, '', 0, 2, NULL, '1', to_date('2025-04-17 20:08:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-17 23:05:31', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_role_seq\n    START WITH 159;\n\n-- ----------------------------\n-- Table structure for system_role_menu\n-- ----------------------------\nCREATE TABLE system_role_menu\n(\n    id          number                                 NOT NULL,\n    role_id     number                                 NOT NULL,\n    menu_id     number                                 NOT NULL,\n    creator     varchar2(64) DEFAULT ''                NULL,\n    create_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64) DEFAULT ''                NULL,\n    update_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0) DEFAULT 0                 NOT NULL,\n    tenant_id   number       DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_role_menu\n    ADD CONSTRAINT pk_system_role_menu PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_role_menu.id IS '自增编号';\nCOMMENT ON COLUMN system_role_menu.role_id IS '角色ID';\nCOMMENT ON COLUMN system_role_menu.menu_id IS '菜单ID';\nCOMMENT ON COLUMN system_role_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_role_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_role_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role_menu.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role_menu.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role_menu IS '角色和菜单关联表';\n\n-- ----------------------------\n-- Records of system_role_menu\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (263, 109, 1, '1', to_date('2022-02-22 00:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 00:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (434, 2, 1, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (454, 2, 1093, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (455, 2, 1094, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (460, 2, 1100, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (467, 2, 1107, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (476, 2, 1117, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (477, 2, 100, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (478, 2, 101, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (479, 2, 102, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (480, 2, 1126, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (481, 2, 103, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (483, 2, 104, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (485, 2, 105, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (488, 2, 107, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (490, 2, 108, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (492, 2, 109, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (498, 2, 1138, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (523, 2, 1224, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (524, 2, 1225, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (541, 2, 500, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (543, 2, 501, '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:09:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (675, 2, 2, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (689, 2, 1077, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (690, 2, 1078, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (692, 2, 1083, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (693, 2, 1084, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (699, 2, 1090, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (703, 2, 106, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (704, 2, 110, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (705, 2, 111, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (706, 2, 112, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (707, 2, 113, '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 13:16:57', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1296, 110, 1, '110', to_date('2022-02-23 00:23:55', 'SYYYY-MM-DD HH24:MI:SS'), '110', to_date('2022-02-23 00:23:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1578, 111, 1, '1', to_date('2022-03-07 21:37:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-07 21:37:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1604, 101, 1216, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1605, 101, 1217, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1606, 101, 1218, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1607, 101, 1219, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1608, 101, 1220, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1609, 101, 1221, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1610, 101, 5, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1611, 101, 1222, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1612, 101, 1118, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1613, 101, 1119, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1614, 101, 1120, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1615, 101, 1185, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1616, 101, 1186, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1617, 101, 1187, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1618, 101, 1188, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1619, 101, 1189, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1620, 101, 1190, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1621, 101, 1191, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1622, 101, 1192, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1623, 101, 1193, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1624, 101, 1194, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1625, 101, 1195, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1627, 101, 1197, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1628, 101, 1198, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1629, 101, 1199, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1630, 101, 1200, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1631, 101, 1201, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1632, 101, 1202, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1633, 101, 1207, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1634, 101, 1208, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1635, 101, 1209, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1636, 101, 1210, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1637, 101, 1211, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1638, 101, 1212, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1639, 101, 1213, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1640, 101, 1215, '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:45:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1641, 101, 2, '1', to_date('2022-04-01 22:21:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1642, 101, 1031, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1643, 101, 1032, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1644, 101, 1033, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1645, 101, 1034, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1646, 101, 1035, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1647, 101, 1050, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1648, 101, 1051, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1649, 101, 1052, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1650, 101, 1053, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1651, 101, 1054, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1652, 101, 1056, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1653, 101, 1057, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1654, 101, 1058, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1655, 101, 1059, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1656, 101, 1060, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1657, 101, 1066, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1658, 101, 1067, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1659, 101, 1070, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1664, 101, 1075, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1666, 101, 1077, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1667, 101, 1078, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1668, 101, 1082, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1669, 101, 1083, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1670, 101, 1084, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1671, 101, 1085, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1672, 101, 1086, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1673, 101, 1087, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1674, 101, 1088, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1675, 101, 1089, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1679, 101, 1237, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1680, 101, 1238, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1681, 101, 1239, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1682, 101, 1240, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1683, 101, 1241, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1684, 101, 1242, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1685, 101, 1243, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1687, 101, 106, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1688, 101, 110, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1689, 101, 111, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1690, 101, 112, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1691, 101, 113, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1692, 101, 114, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1693, 101, 115, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1694, 101, 116, '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-04-01 22:21:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1729, 109, 100, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1730, 109, 101, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1731, 109, 1063, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1732, 109, 1064, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1733, 109, 1001, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1734, 109, 1065, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1735, 109, 1002, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1736, 109, 1003, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1737, 109, 1004, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1738, 109, 1005, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1739, 109, 1006, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1740, 109, 1007, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1741, 109, 1008, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1742, 109, 1009, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1743, 109, 1010, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1744, 109, 1011, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1745, 109, 1012, '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1746, 111, 100, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1747, 111, 101, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1748, 111, 1063, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1749, 111, 1064, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1750, 111, 1001, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1751, 111, 1065, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1752, 111, 1002, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1753, 111, 1003, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1754, 111, 1004, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1755, 111, 1005, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1756, 111, 1006, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1757, 111, 1007, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1758, 111, 1008, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1759, 111, 1009, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1760, 111, 1010, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1761, 111, 1011, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1762, 111, 1012, '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1763, 109, 100, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1764, 109, 101, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1765, 109, 1063, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1766, 109, 1064, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1767, 109, 1001, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1768, 109, 1065, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1769, 109, 1002, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1770, 109, 1003, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1771, 109, 1004, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1772, 109, 1005, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1773, 109, 1006, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1774, 109, 1007, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1775, 109, 1008, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1776, 109, 1009, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1777, 109, 1010, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1778, 109, 1011, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1779, 109, 1012, '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1780, 111, 100, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1781, 111, 101, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1782, 111, 1063, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1783, 111, 1064, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1784, 111, 1001, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1785, 111, 1065, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1786, 111, 1002, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1787, 111, 1003, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1788, 111, 1004, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1789, 111, 1005, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1790, 111, 1006, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1791, 111, 1007, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1792, 111, 1008, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1793, 111, 1009, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1794, 111, 1010, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1795, 111, 1011, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1796, 111, 1012, '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:54', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1797, 109, 100, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1798, 109, 101, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1799, 109, 1063, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1800, 109, 1064, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1801, 109, 1001, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1802, 109, 1065, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1803, 109, 1002, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1804, 109, 1003, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1805, 109, 1004, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1806, 109, 1005, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1807, 109, 1006, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1808, 109, 1007, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1809, 109, 1008, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1810, 109, 1009, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1811, 109, 1010, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1812, 109, 1011, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1813, 109, 1012, '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1814, 111, 100, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1815, 111, 101, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1816, 111, 1063, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1817, 111, 1064, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1818, 111, 1001, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1819, 111, 1065, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1820, 111, 1002, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1821, 111, 1003, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1822, 111, 1004, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1823, 111, 1005, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1824, 111, 1006, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1825, 111, 1007, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1826, 111, 1008, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1827, 111, 1009, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1828, 111, 1010, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1829, 111, 1011, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1830, 111, 1012, '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:08:56', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1831, 109, 103, '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1832, 109, 1017, '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1833, 109, 1018, '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1834, 109, 1019, '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1835, 109, 1020, '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1836, 111, 103, '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1837, 111, 1017, '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1838, 111, 1018, '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1839, 111, 1019, '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1840, 111, 1020, '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:43:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1841, 109, 1036, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1842, 109, 1037, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1843, 109, 1038, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1844, 109, 1039, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1845, 109, 107, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1846, 111, 1036, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1847, 111, 1037, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1848, 111, 1038, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1849, 111, 1039, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1850, 111, 107, '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-09-21 22:48:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1991, 2, 1024, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1992, 2, 1025, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1993, 2, 1026, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1994, 2, 1027, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1995, 2, 1028, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1996, 2, 1029, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1997, 2, 1030, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1998, 2, 1031, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1999, 2, 1032, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2000, 2, 1033, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2001, 2, 1034, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2002, 2, 1035, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2003, 2, 1036, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2004, 2, 1037, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2005, 2, 1038, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2006, 2, 1039, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2007, 2, 1040, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2008, 2, 1042, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2009, 2, 1043, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2010, 2, 1045, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2011, 2, 1046, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2012, 2, 1048, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2013, 2, 1050, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2014, 2, 1051, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2015, 2, 1052, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2016, 2, 1053, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2017, 2, 1054, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2018, 2, 1056, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2019, 2, 1057, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2020, 2, 1058, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2021, 2, 2083, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2022, 2, 1059, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2023, 2, 1060, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2024, 2, 1063, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2025, 2, 1064, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2026, 2, 1065, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2027, 2, 1066, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2028, 2, 1067, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2029, 2, 1070, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2034, 2, 1075, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2036, 2, 1082, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2037, 2, 1085, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2038, 2, 1086, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2039, 2, 1087, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2040, 2, 1088, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2041, 2, 1089, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2042, 2, 1091, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2043, 2, 1092, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2044, 2, 1095, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2045, 2, 1096, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2046, 2, 1097, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2047, 2, 1098, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2048, 2, 1101, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2049, 2, 1102, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2050, 2, 1103, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2051, 2, 1104, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2052, 2, 1105, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2053, 2, 1106, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2054, 2, 1108, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2055, 2, 1109, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2061, 2, 1127, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2062, 2, 1128, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2063, 2, 1129, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2064, 2, 1130, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2066, 2, 1132, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2067, 2, 1133, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2068, 2, 1134, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2069, 2, 1135, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2070, 2, 1136, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2071, 2, 1137, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2072, 2, 114, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2073, 2, 1139, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2074, 2, 115, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2075, 2, 1140, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2076, 2, 116, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2077, 2, 1141, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2078, 2, 1142, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2079, 2, 1143, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2080, 2, 1150, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2081, 2, 1161, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2082, 2, 1162, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2086, 2, 1166, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2087, 2, 1173, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2088, 2, 1174, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2092, 2, 1178, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2099, 2, 1226, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2100, 2, 1227, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2101, 2, 1228, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2102, 2, 1229, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2103, 2, 1237, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2104, 2, 1238, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2105, 2, 1239, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2106, 2, 1240, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2107, 2, 1241, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2108, 2, 1242, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2109, 2, 1243, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2116, 2, 1254, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2117, 2, 1255, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2118, 2, 1256, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2119, 2, 1257, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2120, 2, 1258, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2121, 2, 1259, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2122, 2, 1260, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2123, 2, 1261, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2124, 2, 1263, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2125, 2, 1264, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2126, 2, 1265, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2127, 2, 1266, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2128, 2, 1267, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2129, 2, 1001, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2130, 2, 1002, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2131, 2, 1003, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2132, 2, 1004, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2133, 2, 1005, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2134, 2, 1006, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2135, 2, 1007, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2136, 2, 1008, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2137, 2, 1009, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2138, 2, 1010, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2139, 2, 1011, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2140, 2, 1012, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2141, 2, 1013, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2143, 2, 1015, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2145, 2, 1017, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2146, 2, 1018, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2147, 2, 1019, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2148, 2, 1020, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2149, 2, 1021, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2150, 2, 1022, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2151, 2, 1023, '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:52', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2152, 2, 1281, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2153, 2, 1282, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2154, 2, 2000, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2155, 2, 2002, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2156, 2, 2003, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2157, 2, 2004, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2158, 2, 2005, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2159, 2, 2006, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2160, 2, 2008, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2161, 2, 2009, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2162, 2, 2010, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2163, 2, 2011, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2164, 2, 2012, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2170, 2, 2019, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2171, 2, 2020, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2172, 2, 2021, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2173, 2, 2022, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2174, 2, 2023, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2175, 2, 2025, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2177, 2, 2027, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2178, 2, 2028, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2179, 2, 2029, '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:42:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2180, 2, 2014, '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2181, 2, 2015, '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2182, 2, 2016, '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2183, 2, 2017, '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2184, 2, 2018, '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-01-25 08:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2188, 101, 1024, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2189, 101, 1, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2190, 101, 1025, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2191, 101, 1026, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2192, 101, 1027, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2193, 101, 1028, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2194, 101, 1029, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2195, 101, 1030, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2196, 101, 1036, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2197, 101, 1037, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2198, 101, 1038, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2199, 101, 1039, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2200, 101, 1040, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2201, 101, 1042, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2202, 101, 1043, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2203, 101, 1045, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2204, 101, 1046, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2205, 101, 1048, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2206, 101, 2083, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2207, 101, 1063, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2208, 101, 1064, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2209, 101, 1065, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2210, 101, 1093, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2211, 101, 1094, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2212, 101, 1095, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2213, 101, 1096, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2214, 101, 1097, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2215, 101, 1098, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2216, 101, 1100, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2217, 101, 1101, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2218, 101, 1102, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2219, 101, 1103, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2220, 101, 1104, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2221, 101, 1105, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2222, 101, 1106, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2223, 101, 2130, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2224, 101, 1107, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2225, 101, 2131, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2226, 101, 1108, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2227, 101, 2132, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2228, 101, 1109, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2229, 101, 2133, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2230, 101, 2134, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2232, 101, 2135, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2234, 101, 2136, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2236, 101, 2137, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2238, 101, 2138, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2240, 101, 2139, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2242, 101, 2140, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2243, 101, 2141, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2244, 101, 2142, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2245, 101, 2143, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2246, 101, 2144, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2247, 101, 2145, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2248, 101, 2146, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2249, 101, 2147, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2250, 101, 100, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2251, 101, 2148, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2252, 101, 101, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2253, 101, 2149, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2254, 101, 102, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2255, 101, 2150, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2256, 101, 103, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2257, 101, 2151, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2258, 101, 104, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2259, 101, 2152, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2260, 101, 105, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2261, 101, 107, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2262, 101, 108, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2263, 101, 109, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2264, 101, 1138, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2265, 101, 1139, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2266, 101, 1140, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2267, 101, 1141, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2268, 101, 1142, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2269, 101, 1143, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2270, 101, 1224, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2271, 101, 1225, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2272, 101, 1226, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2273, 101, 1227, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2274, 101, 1228, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2275, 101, 1229, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2282, 101, 1261, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2283, 101, 1263, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2284, 101, 1264, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2285, 101, 1265, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2286, 101, 1266, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2287, 101, 1267, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2288, 101, 1001, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2289, 101, 1002, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2290, 101, 1003, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2291, 101, 1004, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2292, 101, 1005, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2293, 101, 1006, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2294, 101, 1007, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2295, 101, 1008, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2296, 101, 1009, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2297, 101, 1010, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2298, 101, 1011, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2299, 101, 1012, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2300, 101, 500, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2301, 101, 1013, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2302, 101, 501, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2303, 101, 1014, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2304, 101, 1015, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2305, 101, 1016, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2306, 101, 1017, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2307, 101, 1018, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2308, 101, 1019, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2309, 101, 1020, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2310, 101, 1021, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2311, 101, 1022, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2312, 101, 1023, '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-02-09 23:49:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2929, 109, 1224, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2930, 109, 1225, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2931, 109, 1226, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2932, 109, 1227, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2933, 109, 1228, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2934, 109, 1229, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2935, 109, 1138, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2936, 109, 1139, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2937, 109, 1140, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2938, 109, 1141, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2939, 109, 1142, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2940, 109, 1143, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2941, 111, 1224, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2942, 111, 1225, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2943, 111, 1226, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2944, 111, 1227, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2945, 111, 1228, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2946, 111, 1229, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2947, 111, 1138, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2948, 111, 1139, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2949, 111, 1140, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2950, 111, 1141, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2951, 111, 1142, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2952, 111, 1143, '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:19:40', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2993, 109, 2, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2994, 109, 1031, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2995, 109, 1032, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2996, 109, 1033, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2997, 109, 1034, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2998, 109, 1035, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2999, 109, 1050, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3000, 109, 1051, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3001, 109, 1052, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3002, 109, 1053, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3003, 109, 1054, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3004, 109, 1056, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3005, 109, 1057, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3006, 109, 1058, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3007, 109, 1059, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3008, 109, 1060, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3009, 109, 1066, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3010, 109, 1067, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3011, 109, 1070, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3012, 109, 1075, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3014, 109, 1077, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3015, 109, 1078, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3016, 109, 1082, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3017, 109, 1083, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3018, 109, 1084, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3019, 109, 1085, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3020, 109, 1086, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3021, 109, 1087, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3022, 109, 1088, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3023, 109, 1089, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3024, 109, 1090, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3025, 109, 1091, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3026, 109, 1092, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3027, 109, 106, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3028, 109, 110, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3029, 109, 111, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3030, 109, 112, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3031, 109, 113, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3032, 109, 114, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3033, 109, 115, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3034, 109, 116, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3035, 109, 2472, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3036, 109, 2478, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3037, 109, 2479, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3038, 109, 2480, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3039, 109, 2481, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3040, 109, 2482, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3041, 109, 2483, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3042, 109, 2484, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3043, 109, 2485, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3044, 109, 2486, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3045, 109, 2487, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3046, 109, 2488, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3047, 109, 2489, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3048, 109, 2490, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3049, 109, 2491, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3050, 109, 2492, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3051, 109, 2493, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3052, 109, 2494, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3053, 109, 2495, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3054, 109, 2497, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3055, 109, 1237, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3056, 109, 1238, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3057, 109, 1239, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3058, 109, 1240, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3059, 109, 1241, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3060, 109, 1242, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3061, 109, 1243, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3062, 109, 2525, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3063, 109, 1255, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3064, 109, 1256, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3065, 109, 1257, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3066, 109, 1258, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3067, 109, 1259, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3068, 109, 1260, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3069, 111, 2, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3070, 111, 1031, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3071, 111, 1032, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3072, 111, 1033, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3073, 111, 1034, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3074, 111, 1035, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3075, 111, 1050, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3076, 111, 1051, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3077, 111, 1052, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3078, 111, 1053, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3079, 111, 1054, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3080, 111, 1056, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3081, 111, 1057, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3082, 111, 1058, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3083, 111, 1059, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3084, 111, 1060, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3085, 111, 1066, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3086, 111, 1067, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3087, 111, 1070, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3088, 111, 1075, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3090, 111, 1077, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3091, 111, 1078, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3092, 111, 1082, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3093, 111, 1083, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3094, 111, 1084, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3095, 111, 1085, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3096, 111, 1086, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3097, 111, 1087, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3098, 111, 1088, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3099, 111, 1089, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3100, 111, 1090, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3101, 111, 1091, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3102, 111, 1092, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3103, 111, 106, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3104, 111, 110, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3105, 111, 111, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3106, 111, 112, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3107, 111, 113, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3108, 111, 114, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3109, 111, 115, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3110, 111, 116, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3111, 111, 2472, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3112, 111, 2478, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3113, 111, 2479, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3114, 111, 2480, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3115, 111, 2481, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3116, 111, 2482, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3117, 111, 2483, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3118, 111, 2484, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3119, 111, 2485, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3120, 111, 2486, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3121, 111, 2487, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3122, 111, 2488, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3123, 111, 2489, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3124, 111, 2490, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3125, 111, 2491, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3126, 111, 2492, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3127, 111, 2493, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3128, 111, 2494, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3129, 111, 2495, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3130, 111, 2497, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3131, 111, 1237, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3132, 111, 1238, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3133, 111, 1239, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3134, 111, 1240, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3135, 111, 1241, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3136, 111, 1242, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3137, 111, 1243, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3138, 111, 2525, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3139, 111, 1255, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3140, 111, 1256, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3141, 111, 1257, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3142, 111, 1258, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3143, 111, 1259, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3144, 111, 1260, '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 23:41:02', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3221, 109, 102, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3222, 109, 1013, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3223, 109, 1014, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3224, 109, 1015, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3225, 109, 1016, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3226, 111, 102, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3227, 111, 1013, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3228, 111, 1014, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3229, 111, 1015, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3230, 111, 1016, '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-30 11:42:36', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4163, 109, 5, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4164, 109, 1118, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4165, 109, 1119, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4166, 109, 1120, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4167, 109, 2713, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4168, 109, 2714, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4169, 109, 2715, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4170, 109, 2716, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4171, 109, 2717, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4172, 109, 2718, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4173, 109, 2720, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4174, 109, 1185, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4175, 109, 2721, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4176, 109, 1186, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4177, 109, 2722, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4178, 109, 1187, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4179, 109, 2723, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4180, 109, 1188, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4181, 109, 2724, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4182, 109, 1189, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4183, 109, 2725, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4184, 109, 1190, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4185, 109, 2726, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4186, 109, 1191, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4187, 109, 2727, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4188, 109, 1192, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4189, 109, 2728, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4190, 109, 1193, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4191, 109, 2729, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4192, 109, 1194, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4193, 109, 2730, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4194, 109, 1195, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4195, 109, 2731, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4196, 109, 1196, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4197, 109, 2732, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4198, 109, 1197, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4199, 109, 2733, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4200, 109, 1198, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4201, 109, 2734, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4202, 109, 1199, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4203, 109, 2735, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4204, 109, 1200, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4205, 109, 1201, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4206, 109, 1202, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4207, 109, 1207, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4208, 109, 1208, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4209, 109, 1209, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4210, 109, 1210, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4211, 109, 1211, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4212, 109, 1212, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4213, 109, 1213, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4214, 109, 1215, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4215, 109, 1216, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4216, 109, 1217, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4217, 109, 1218, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4218, 109, 1219, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4219, 109, 1220, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4220, 109, 1221, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4221, 109, 1222, '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4222, 111, 5, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4223, 111, 1118, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4224, 111, 1119, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4225, 111, 1120, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4226, 111, 2713, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4227, 111, 2714, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4228, 111, 2715, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4229, 111, 2716, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4230, 111, 2717, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4231, 111, 2718, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4232, 111, 2720, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4233, 111, 1185, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4234, 111, 2721, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4235, 111, 1186, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4236, 111, 2722, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4237, 111, 1187, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4238, 111, 2723, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4239, 111, 1188, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4240, 111, 2724, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4241, 111, 1189, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4242, 111, 2725, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4243, 111, 1190, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4244, 111, 2726, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4245, 111, 1191, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4246, 111, 2727, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4247, 111, 1192, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4248, 111, 2728, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4249, 111, 1193, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4250, 111, 2729, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4251, 111, 1194, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4252, 111, 2730, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4253, 111, 1195, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4254, 111, 2731, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4255, 111, 1196, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4256, 111, 2732, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4257, 111, 1197, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4258, 111, 2733, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4259, 111, 1198, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4260, 111, 2734, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4261, 111, 1199, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4262, 111, 2735, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4263, 111, 1200, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4264, 111, 1201, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4265, 111, 1202, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4266, 111, 1207, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4267, 111, 1208, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4268, 111, 1209, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4269, 111, 1210, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4270, 111, 1211, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4271, 111, 1212, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4272, 111, 1213, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4273, 111, 1215, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4274, 111, 1216, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4275, 111, 1217, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4276, 111, 1218, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4277, 111, 1219, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4278, 111, 1220, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4279, 111, 1221, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4280, 111, 1222, '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-30 17:53:18', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5777, 101, 2739, '1', to_date('2024-04-30 09:38:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-30 09:38:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5778, 101, 2740, '1', to_date('2024-04-30 09:38:37', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-30 09:38:37', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5779, 2, 2739, '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5780, 2, 2740, '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5781, 2, 2758, '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5782, 2, 2759, '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5783, 2, 2362, '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5784, 2, 2387, '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5785, 2, 2030, '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5786, 101, 2758, '1', to_date('2024-07-07 20:39:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5787, 101, 2759, '1', to_date('2024-07-07 20:39:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5788, 101, 2783, '1', to_date('2024-07-07 20:39:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-07 20:39:55', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5789, 109, 2739, '1', to_date('2024-07-13 22:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-13 22:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5790, 109, 2740, '1', to_date('2024-07-13 22:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-13 22:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5791, 111, 2739, '1', to_date('2024-07-13 22:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-13 22:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5792, 111, 2740, '1', to_date('2024-07-13 22:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-13 22:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6053, 155, 4000, '1', to_date('2025-04-01 13:48:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:48:26', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6097, 155, 4050, '1', to_date('2025-04-01 13:48:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:48:26', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6104, 155, 4032, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6105, 155, 4033, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6106, 155, 4034, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6107, 155, 4035, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6108, 155, 4036, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6109, 155, 4037, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6110, 155, 4038, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6111, 155, 4039, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6112, 155, 4040, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6113, 155, 4041, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6114, 155, 4042, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6115, 155, 4043, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6116, 155, 4044, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6117, 155, 4045, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6118, 155, 4046, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6119, 155, 4001, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6120, 155, 4002, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6121, 155, 4003, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6122, 155, 4004, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6123, 155, 4005, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6124, 155, 4006, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6125, 155, 4007, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6126, 155, 4008, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6127, 155, 4009, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6128, 155, 4010, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6129, 155, 4011, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6130, 155, 4012, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6131, 155, 4013, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6132, 155, 4014, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6133, 155, 4015, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6134, 155, 4016, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6135, 155, 4017, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6136, 155, 4018, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6137, 155, 4031, '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-01 13:49:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6138, 101, 5010, '1', to_date('2025-05-05 17:49:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-05 17:49:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_role_menu_seq\n    START WITH 6139;\n\n-- ----------------------------\n-- Table structure for system_sms_channel\n-- ----------------------------\nCREATE TABLE system_sms_channel\n(\n    id           number                                  NOT NULL,\n    signature    varchar2(12)                            NULL,\n    code         varchar2(63)                            NULL,\n    status       smallint                                NOT NULL,\n    remark       varchar2(255) DEFAULT NULL              NULL,\n    api_key      varchar2(128)                           NULL,\n    api_secret   varchar2(128) DEFAULT NULL              NULL,\n    callback_url varchar2(255) DEFAULT NULL              NULL,\n    creator      varchar2(64)  DEFAULT ''                NULL,\n    create_time  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      varchar2(64)  DEFAULT ''                NULL,\n    update_time  date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_sms_channel\n    ADD CONSTRAINT pk_system_sms_channel PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_channel.id IS '编号';\nCOMMENT ON COLUMN system_sms_channel.signature IS '短信签名';\nCOMMENT ON COLUMN system_sms_channel.code IS '渠道编码';\nCOMMENT ON COLUMN system_sms_channel.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_channel.remark IS '备注';\nCOMMENT ON COLUMN system_sms_channel.api_key IS '短信 API 的账号';\nCOMMENT ON COLUMN system_sms_channel.api_secret IS '短信 API 的秘钥';\nCOMMENT ON COLUMN system_sms_channel.callback_url IS '短信发送回调 URL';\nCOMMENT ON COLUMN system_sms_channel.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_channel.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_channel.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_channel.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_channel.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_channel IS '短信渠道';\n\n-- ----------------------------\n-- Records of system_sms_channel\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (2, 'Ballcat', 'ALIYUN', 0, '你要改哦，只有我可以用！！！！', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', to_date('2021-03-31 11:53:10', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-04 08:53:26', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (4, '测试渠道', 'DEBUG_DING_TALK', 0, '123', '696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859', 'SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67', NULL, '1', to_date('2021-04-13 00:23:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-27 20:29:49', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (7, 'mock腾讯云', 'TENCENT', 0, '', '1 2', '2 3', '', '1', to_date('2024-09-30 08:53:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-30 08:55:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_sms_channel_seq\n    START WITH 8;\n\n-- ----------------------------\n-- Table structure for system_sms_code\n-- ----------------------------\nCREATE TABLE system_sms_code\n(\n    id          number                                  NOT NULL,\n    mobile      varchar2(11)                            NULL,\n    code        varchar2(6)                             NULL,\n    create_ip   varchar2(15)                            NULL,\n    scene       smallint                                NOT NULL,\n    today_index smallint                                NOT NULL,\n    used        smallint                                NOT NULL,\n    used_time   date          DEFAULT NULL              NULL,\n    used_ip     varchar2(255) DEFAULT NULL              NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id   number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_sms_code\n    ADD CONSTRAINT pk_system_sms_code PRIMARY KEY (id);\n\nCREATE INDEX idx_system_sms_code_01 ON system_sms_code (mobile);\n\nCOMMENT ON COLUMN system_sms_code.id IS '编号';\nCOMMENT ON COLUMN system_sms_code.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_code.code IS '验证码';\nCOMMENT ON COLUMN system_sms_code.create_ip IS '创建 IP';\nCOMMENT ON COLUMN system_sms_code.scene IS '发送场景';\nCOMMENT ON COLUMN system_sms_code.today_index IS '今日发送的第几条';\nCOMMENT ON COLUMN system_sms_code.used IS '是否使用';\nCOMMENT ON COLUMN system_sms_code.used_time IS '使用时间';\nCOMMENT ON COLUMN system_sms_code.used_ip IS '使用 IP';\nCOMMENT ON COLUMN system_sms_code.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_code.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_sms_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_sms_code IS '手机验证码';\n\nCREATE SEQUENCE system_sms_code_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_sms_log\n-- ----------------------------\nCREATE TABLE system_sms_log\n(\n    id               number                                  NOT NULL,\n    channel_id       number                                  NOT NULL,\n    channel_code     varchar2(63)                            NULL,\n    template_id      number                                  NOT NULL,\n    template_code    varchar2(63)                            NULL,\n    template_type    smallint                                NOT NULL,\n    template_content varchar2(255)                           NULL,\n    template_params  varchar2(255)                           NULL,\n    api_template_id  varchar2(63)                            NULL,\n    mobile           varchar2(11)                            NULL,\n    user_id          number        DEFAULT NULL              NULL,\n    user_type        smallint      DEFAULT NULL              NULL,\n    send_status      smallint      DEFAULT 0                 NOT NULL,\n    send_time        date          DEFAULT NULL              NULL,\n    api_send_code    varchar2(63)  DEFAULT NULL              NULL,\n    api_send_msg     varchar2(255) DEFAULT NULL              NULL,\n    api_request_id   varchar2(255) DEFAULT NULL              NULL,\n    api_serial_no    varchar2(255) DEFAULT NULL              NULL,\n    receive_status   smallint      DEFAULT 0                 NOT NULL,\n    receive_time     date          DEFAULT NULL              NULL,\n    api_receive_code varchar2(63)  DEFAULT NULL              NULL,\n    api_receive_msg  varchar2(255) DEFAULT NULL              NULL,\n    creator          varchar2(64)  DEFAULT ''                NULL,\n    create_time      date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater          varchar2(64)  DEFAULT ''                NULL,\n    update_time      date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted          number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_sms_log\n    ADD CONSTRAINT pk_system_sms_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_log.id IS '编号';\nCOMMENT ON COLUMN system_sms_log.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_log.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_sms_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_sms_log.template_type IS '短信类型';\nCOMMENT ON COLUMN system_sms_log.template_content IS '短信内容';\nCOMMENT ON COLUMN system_sms_log.template_params IS '短信参数';\nCOMMENT ON COLUMN system_sms_log.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_log.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_sms_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_sms_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_sms_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_sms_log.api_send_code IS '短信 API 发送结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_send_msg IS '短信 API 发送失败的提示';\nCOMMENT ON COLUMN system_sms_log.api_request_id IS '短信 API 发送返回的唯一请求 ID';\nCOMMENT ON COLUMN system_sms_log.api_serial_no IS '短信 API 发送返回的序号';\nCOMMENT ON COLUMN system_sms_log.receive_status IS '接收状态';\nCOMMENT ON COLUMN system_sms_log.receive_time IS '接收时间';\nCOMMENT ON COLUMN system_sms_log.api_receive_code IS 'API 接收结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_receive_msg IS 'API 接收结果的说明';\nCOMMENT ON COLUMN system_sms_log.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_log.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_log IS '短信日志';\n\nCREATE SEQUENCE system_sms_log_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_sms_template\n-- ----------------------------\nCREATE TABLE system_sms_template\n(\n    id              number                                  NOT NULL,\n    type            smallint                                NOT NULL,\n    status          smallint                                NOT NULL,\n    code            varchar2(63)                            NULL,\n    name            varchar2(63)                            NULL,\n    content         varchar2(255)                           NULL,\n    params          varchar2(255)                           NULL,\n    remark          varchar2(255) DEFAULT NULL              NULL,\n    api_template_id varchar2(63)                            NULL,\n    channel_id      number                                  NOT NULL,\n    channel_code    varchar2(63)                            NULL,\n    creator         varchar2(64)  DEFAULT ''                NULL,\n    create_time     date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         varchar2(64)  DEFAULT ''                NULL,\n    update_time     date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_sms_template\n    ADD CONSTRAINT pk_system_sms_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_template.id IS '编号';\nCOMMENT ON COLUMN system_sms_template.type IS '模板类型';\nCOMMENT ON COLUMN system_sms_template.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_template.code IS '模板编码';\nCOMMENT ON COLUMN system_sms_template.name IS '模板名称';\nCOMMENT ON COLUMN system_sms_template.content IS '模板内容';\nCOMMENT ON COLUMN system_sms_template.params IS '参数数组';\nCOMMENT ON COLUMN system_sms_template.remark IS '备注';\nCOMMENT ON COLUMN system_sms_template.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_template.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_template.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_template.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_template.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_template IS '短信模板';\n\n-- ----------------------------\n-- Records of system_sms_template\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (2, 1, 0, 'test_01', '测试验证码短信', '正在进行登录操作{operation}，您的验证码是{code}', '[\"operation\",\"code\"]', '测试备注', '4383920', 4, 'DEBUG_DING_TALK', '', to_date('2021-03-31 10:49:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-18 11:57:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (3, 1, 0, 'test_02', '公告通知', '您的验证码{code}，该验证码5分钟内有效，请勿泄漏于他人！', '[\"code\"]', NULL, 'SMS_207945135', 2, 'ALIYUN', '', to_date('2021-03-31 11:56:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2021-04-10 01:22:02', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (6, 3, 0, 'test-01', '测试模板', '哈哈哈 {name}', '[\"name\"]', 'f哈哈哈', '4383920', 4, 'DEBUG_DING_TALK', '1', to_date('2021-04-10 01:07:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-18 11:57:07', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (7, 3, 0, 'test-04', '测试下', '老鸡{name}，牛逼{code}', '[\"name\",\"code\"]', '哈哈哈哈', 'suibian', 7, 'DEBUG_DING_TALK', '1', to_date('2021-04-13 00:29:53', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-30 00:56:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (8, 1, 0, 'user-sms-login', '前台用户短信登录', '您的验证码是{code}', '[\"code\"]', NULL, '4372216', 4, 'DEBUG_DING_TALK', '1', to_date('2021-10-11 08:10:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-18 11:57:06', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (9, 2, 0, 'bpm_task_assigned', '【工作流】任务被分配', '您收到了一条新的待办任务：{processInstanceName}-{taskName}，申请人：{startUserNickname}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"startUserNickname\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', to_date('2022-01-21 22:31:19', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-01-22 00:03:36', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (10, 2, 0, 'bpm_process_instance_reject', '【工作流】流程被不通过', '您的流程被审批不通过：{processInstanceName}，原因：{reason}，查看链接：{detailUrl}', '[\"processInstanceName\",\"reason\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', to_date('2022-01-22 00:03:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-01 12:33:14', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (11, 2, 0, 'bpm_process_instance_approve', '【工作流】流程被通过', '您的流程被审批通过：{processInstanceName}，查看链接：{detailUrl}', '[\"processInstanceName\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', to_date('2022-01-22 00:04:31', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-27 20:32:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (12, 2, 0, 'demo', '演示模板', '我就是测试一下下', '[]', NULL, 'biubiubiu', 4, 'DEBUG_DING_TALK', '1', to_date('2022-04-10 23:22:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-18 11:57:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (14, 1, 0, 'user-update-mobile', '会员用户 - 修改手机', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', to_date('2023-08-19 18:58:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-19 11:34:04', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (15, 1, 0, 'user-update-password', '会员用户 - 修改密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', to_date('2023-08-19 18:58:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-08-19 11:34:18', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (16, 1, 0, 'user-reset-password', '会员用户 - 重置密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', to_date('2023-08-19 18:58:01', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-02 22:35:27', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (17, 2, 0, 'bpm_task_timeout', '【工作流】任务审批超时', '您收到了一条超时的待办任务：{processInstanceName}-{taskName}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"detailUrl\"]', '', 'X', 4, 'DEBUG_DING_TALK', '1', to_date('2024-08-16 21:59:15', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-08-16 21:59:34', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (18, 1, 0, 'admin-reset-password', '后台用户 - 忘记密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', to_date('2025-03-16 14:19:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-03-16 14:19:45', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (19, 1, 0, 'admin-sms-login', '后台用户短信登录', '您的验证码是{code}', '[\"code\"]', '', '4372216', 4, 'DEBUG_DING_TALK', '1', to_date('2025-04-08 09:36:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-08 09:36:17', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_sms_template_seq\n    START WITH 20;\n\n-- ----------------------------\n-- Table structure for system_social_client\n-- ----------------------------\nCREATE TABLE system_social_client\n(\n    id            number                                  NOT NULL,\n    name          varchar2(255)                           NULL,\n    social_type   smallint                                NOT NULL,\n    user_type     smallint                                NOT NULL,\n    client_id     varchar2(255)                           NULL,\n    client_secret varchar2(2048)                           NULL,\n    public_key    varchar2(2048) DEFAULT NULL              NULL,\n    agent_id      varchar2(255) DEFAULT NULL              NULL,\n    status        smallint                                NOT NULL,\n    creator       varchar2(64)  DEFAULT ''                NULL,\n    create_time   date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       varchar2(64)  DEFAULT ''                NULL,\n    update_time   date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id     number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_social_client\n    ADD CONSTRAINT pk_system_social_client PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_client.id IS '编号';\nCOMMENT ON COLUMN system_social_client.name IS '应用名';\nCOMMENT ON COLUMN system_social_client.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_client.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_social_client.client_secret IS '客户端密钥';\nCOMMENT ON COLUMN system_social_client.public_key IS 'publicKey公钥';\nCOMMENT ON COLUMN system_social_client.agent_id IS '代理编号';\nCOMMENT ON COLUMN system_social_client.status IS '状态';\nCOMMENT ON COLUMN system_social_client.creator IS '创建者';\nCOMMENT ON COLUMN system_social_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_client.updater IS '更新者';\nCOMMENT ON COLUMN system_social_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_client.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_client.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_client IS '社交客户端表';\n\n-- ----------------------------\n-- Records of system_social_client\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '钉钉', 20, 2, 'dingvrnreaje3yqvzhxg', 'i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI', NULL, 0, '', to_date('2023-10-18 11:21:18', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-20 21:28:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '钉钉（王土豆）', 20, 2, 'dingtsu9hpepjkbmthhw', 'FP_bnSq_HAHKCSncmJjw5hxhnzs6vaVDSZZn3egj6rdqTQ_hu5tQVJyLMpgCakdP', NULL, 0, '', to_date('2023-10-18 11:21:18', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2023-12-20 21:28:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', 121);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '微信公众号', 31, 1, 'wx5b23ba7a5589ecbb', '2a7b3b20c537e52e74afd395eb85f61f', NULL, 0, '', to_date('2023-10-18 16:07:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-20 21:28:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (43, '微信小程序', 34, 1, 'wx63c280fe3248a3e7', '6f270509224a7ae1296bbf1c8cb97aed', NULL, 0, '', to_date('2023-10-19 13:37:41', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-12-20 21:28:25', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (44, '1', 10, 1, '2', '3', NULL, 0, '1', to_date('2025-04-06 20:36:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-06 20:43:12', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_social_client_seq\n    START WITH 45;\n\n-- ----------------------------\n-- Table structure for system_social_user\n-- ----------------------------\nCREATE TABLE system_social_user\n(\n    id             number                                  NOT NULL,\n    type           smallint                                NOT NULL,\n    openid         varchar2(32)                            NULL,\n    token          varchar2(256) DEFAULT NULL              NULL,\n    raw_token_info varchar2(1024)                          NULL,\n    nickname       varchar2(32)                            NULL,\n    avatar         varchar2(255) DEFAULT NULL              NULL,\n    raw_user_info  varchar2(1024)                          NULL,\n    code           varchar2(256)                           NULL,\n    state          varchar2(256) DEFAULT NULL              NULL,\n    creator        varchar2(64)  DEFAULT ''                NULL,\n    create_time    date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar2(64)  DEFAULT ''                NULL,\n    update_time    date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id      number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_social_user\n    ADD CONSTRAINT pk_system_social_user PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_user.id IS '主键 ( 自增策略)';\nCOMMENT ON COLUMN system_social_user.type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user.openid IS '社交 openid';\nCOMMENT ON COLUMN system_social_user.token IS '社交 token';\nCOMMENT ON COLUMN system_social_user.raw_token_info IS '原始 Token 数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_social_user.avatar IS '用户头像';\nCOMMENT ON COLUMN system_social_user.raw_user_info IS '原始用户数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.code IS '最后一次的认证 code';\nCOMMENT ON COLUMN system_social_user.state IS '最后一次的认证 state';\nCOMMENT ON COLUMN system_social_user.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user IS '社交用户表';\n\nCREATE SEQUENCE system_social_user_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_social_user_bind\n-- ----------------------------\nCREATE TABLE system_social_user_bind\n(\n    id             number                                 NOT NULL,\n    user_id        number                                 NOT NULL,\n    user_type      smallint                               NOT NULL,\n    social_type    smallint                               NOT NULL,\n    social_user_id number                                 NOT NULL,\n    creator        varchar2(64) DEFAULT ''                NULL,\n    create_time    date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        varchar2(64) DEFAULT ''                NULL,\n    update_time    date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        number(1, 0) DEFAULT 0                 NOT NULL,\n    tenant_id      number       DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_social_user_bind\n    ADD CONSTRAINT pk_system_social_user_bind PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_user_bind.id IS '主键 ( 自增策略)';\nCOMMENT ON COLUMN system_social_user_bind.user_id IS '用户编号';\nCOMMENT ON COLUMN system_social_user_bind.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_user_bind.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user_bind.social_user_id IS '社交用户的编号';\nCOMMENT ON COLUMN system_social_user_bind.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user_bind.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user_bind.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user_bind.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user_bind.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user_bind.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user_bind IS '社交绑定表';\n\nCREATE SEQUENCE system_social_user_bind_seq\n    START WITH 1;\n\n-- ----------------------------\n-- Table structure for system_tenant\n-- ----------------------------\nCREATE TABLE system_tenant\n(\n    id              number                                  NOT NULL,\n    name            varchar2(30)                            NULL,\n    contact_user_id number        DEFAULT NULL              NULL,\n    contact_name    varchar2(30)                            NULL,\n    contact_mobile  varchar2(500) DEFAULT NULL              NULL,\n    status          smallint      DEFAULT 0                 NOT NULL,\n    websites        varchar2(256) DEFAULT ''                NULL,\n    package_id      number                                  NOT NULL,\n    expire_time     date                                    NOT NULL,\n    account_count   number                                  NOT NULL,\n    creator         varchar2(64)  DEFAULT ''                NULL,\n    create_time     date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         varchar2(64)  DEFAULT ''                NULL,\n    update_time     date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_tenant\n    ADD CONSTRAINT pk_system_tenant PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_tenant.id IS '租户编号';\nCOMMENT ON COLUMN system_tenant.name IS '租户名';\nCOMMENT ON COLUMN system_tenant.contact_user_id IS '联系人的用户编号';\nCOMMENT ON COLUMN system_tenant.contact_name IS '联系人';\nCOMMENT ON COLUMN system_tenant.contact_mobile IS '联系手机';\nCOMMENT ON COLUMN system_tenant.status IS '租户状态（0正常 1停用）';\nCOMMENT ON COLUMN system_tenant.websites IS '绑定域名数组';\nCOMMENT ON COLUMN system_tenant.package_id IS '租户套餐编号';\nCOMMENT ON COLUMN system_tenant.expire_time IS '过期时间';\nCOMMENT ON COLUMN system_tenant.account_count IS '账号数量';\nCOMMENT ON COLUMN system_tenant.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant IS '租户表';\n\n-- ----------------------------\n-- Records of system_tenant\n-- ----------------------------\n-- @formatter:off\nINSERT 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', 0, to_date('2099-02-19 17:14:16', 'SYYYY-MM-DD HH24:MI:SS'), 9999, '1', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-06 11:41:41', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT 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', 111, to_date('2026-07-10 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), 30, '1', to_date('2022-02-22 00:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-03 21:33:01', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT 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', 111, to_date('2022-04-29 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), 50, '1', to_date('2022-03-07 21:37:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-22 12:10:50', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_tenant_seq\n    START WITH 123;\n\n-- ----------------------------\n-- Table structure for system_tenant_package\n-- ----------------------------\nCREATE TABLE system_tenant_package\n(\n    id          number                                  NOT NULL,\n    name        varchar2(30)                            NULL,\n    status      smallint      DEFAULT 0                 NOT NULL,\n    remark      varchar2(256) DEFAULT ''                NULL,\n    menu_ids    varchar2(4000)                          NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_tenant_package\n    ADD CONSTRAINT pk_system_tenant_package PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_tenant_package.id IS '套餐编号';\nCOMMENT ON COLUMN system_tenant_package.name IS '套餐名';\nCOMMENT ON COLUMN system_tenant_package.status IS '租户状态（0正常 1停用）';\nCOMMENT ON COLUMN system_tenant_package.remark IS '备注';\nCOMMENT ON COLUMN system_tenant_package.menu_ids IS '关联的菜单编号';\nCOMMENT ON COLUMN system_tenant_package.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant_package.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant_package.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant_package.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant_package.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant_package IS '租户套餐表';\n\n-- ----------------------------\n-- Records of system_tenant_package\n-- ----------------------------\n-- @formatter:off\nINSERT 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,1118,1119,1120,100,101,102,103,106,107,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2713,2714,2715,2716,2717,2718,2720,1185,2721,1186,2722,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,2472,1192,2728,1193,2729,1194,2730,1195,2731,1196,2732,1197,2733,2478,1198,2734,2479,1199,2735,2480,1200,2481,1201,2482,1202,2483,2739,2484,2740,2485,2486,2487,1207,2488,1208,2489,1209,2490,1210,2491,1211,2492,1212,2493,1213,2494,2495,1215,1216,2497,1217,1218,1219,1220,1221,1222,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,2525,1255,1256,1001,1257,1002,1258,1003,1259,1004,1260,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020]', '1', to_date('2022-02-22 00:54:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-13 22:37:24', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO system_tenant_package (id, name, status, remark, menu_ids, creator, create_time, updater, update_time, deleted) VALUES (112, '再来一个套餐', 0, '1234', '[1024,1,1025,1026,2,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1042,1043,1045,1046,1048,1050,1051,1052,1053,1054,1056,1057,1058,2083,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1100,1101,1102,1103,1104,1105,1106,2130,1107,2131,1108,2132,1109,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,100,2148,101,2149,102,2150,103,2151,104,2152,105,106,107,108,109,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2739,2740,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,1255,1256,1257,1258,1259,1260,1261,1263,1264,1265,1266,1267,2447,2448,2449,2450,2451,2452,2453,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,500,1013,501,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023]', '1', to_date('2025-04-04 08:15:02', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-04 08:15:21', 'SYYYY-MM-DD HH24:MI:SS'), '0');\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_tenant_package_seq\n    START WITH 113;\n\n-- ----------------------------\n-- Table structure for system_user_post\n-- ----------------------------\nCREATE TABLE system_user_post\n(\n    id          number                                 NOT NULL,\n    user_id     number       DEFAULT 0                 NOT NULL,\n    post_id     number       DEFAULT 0                 NOT NULL,\n    creator     varchar2(64) DEFAULT ''                NULL,\n    create_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64) DEFAULT ''                NULL,\n    update_time date         DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0) DEFAULT 0                 NOT NULL,\n    tenant_id   number       DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_user_post\n    ADD CONSTRAINT pk_system_user_post PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_user_post.id IS 'id';\nCOMMENT ON COLUMN system_user_post.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_post.post_id IS '岗位ID';\nCOMMENT ON COLUMN system_user_post.creator IS '创建者';\nCOMMENT ON COLUMN system_user_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_post.updater IS '更新者';\nCOMMENT ON COLUMN system_user_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_post IS '用户岗位表';\n\n-- ----------------------------\n-- Records of system_user_post\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, 1, 1, 'admin', to_date('2022-05-02 07:25:24', 'SYYYY-MM-DD HH24:MI:SS'), 'admin', to_date('2022-05-02 07:25:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, 100, 1, 'admin', to_date('2022-05-02 07:25:24', 'SYYYY-MM-DD HH24:MI:SS'), 'admin', to_date('2022-05-02 07:25:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (115, 104, 1, '1', to_date('2022-05-16 19:36:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-16 19:36:28', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (116, 117, 2, '1', to_date('2022-07-09 17:40:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-07-09 17:40:26', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (117, 118, 1, '1', to_date('2022-07-09 17:44:44', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-07-09 17:44:44', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (119, 114, 5, '1', to_date('2024-03-24 20:45:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-24 20:45:51', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (123, 115, 1, '1', to_date('2024-04-04 09:37:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-04 09:37:14', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (124, 115, 2, '1', to_date('2024-04-04 09:37:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-04-04 09:37:14', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (125, 1, 2, '1', to_date('2024-07-13 22:31:39', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-07-13 22:31:39', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_user_post_seq\n    START WITH 126;\n\n-- ----------------------------\n-- Table structure for system_user_role\n-- ----------------------------\nCREATE TABLE system_user_role\n(\n    id          number                                 NOT NULL,\n    user_id     number                                 NOT NULL,\n    role_id     number                                 NOT NULL,\n    creator     varchar2(64) DEFAULT ''                NULL,\n    create_time date         DEFAULT CURRENT_TIMESTAMP NULL,\n    updater     varchar2(64) DEFAULT ''                NULL,\n    update_time date         DEFAULT CURRENT_TIMESTAMP NULL,\n    deleted     number(1, 0) DEFAULT 0                 NOT NULL,\n    tenant_id   number       DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_user_role\n    ADD CONSTRAINT pk_system_user_role PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_user_role.id IS '自增编号';\nCOMMENT ON COLUMN system_user_role.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_role.role_id IS '角色ID';\nCOMMENT ON COLUMN system_user_role.creator IS '创建者';\nCOMMENT ON COLUMN system_user_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_role.updater IS '更新者';\nCOMMENT ON COLUMN system_user_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_role IS '用户和角色关联表';\n\n-- ----------------------------\n-- Records of system_user_role\n-- ----------------------------\n-- @formatter:off\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 1, 1, '', to_date('2022-01-11 13:19:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-05-12 12:35:17', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, 2, '', to_date('2022-01-11 13:19:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-05-12 12:35:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 100, 101, '', to_date('2022-01-11 13:19:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-05-12 12:35:13', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 100, 1, '', to_date('2022-01-11 13:19:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-05-12 12:35:12', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 100, 2, '', to_date('2022-01-11 13:19:45', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2022-05-12 12:35:11', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 103, 1, '1', to_date('2022-01-11 13:19:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-01-11 13:19:45', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 110, 109, '1', to_date('2022-02-22 00:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 00:56:14', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 111, 110, '110', to_date('2022-02-23 13:14:38', 'SYYYY-MM-DD HH24:MI:SS'), '110', to_date('2022-02-23 13:14:38', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 113, 111, '1', to_date('2022-03-07 21:37:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-07 21:37:58', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (18, 1, 2, '1', to_date('2022-05-12 20:39:29', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-05-12 20:39:29', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (22, 115, 2, '1', to_date('2022-07-21 22:08:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-07-21 22:08:30', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (35, 112, 1, '1', to_date('2024-03-15 20:00:24', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-15 20:00:24', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (36, 118, 1, '1', to_date('2024-03-17 09:12:08', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-17 09:12:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (38, 114, 101, '1', to_date('2024-03-24 22:23:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-03-24 22:23:03', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (46, 117, 1, '1', to_date('2024-10-02 10:16:11', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-10-02 10:16:11', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (47, 104, 2, '1', to_date('2025-01-04 10:40:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-01-04 10:40:33', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (48, 100, 155, '1', to_date('2025-04-04 10:41:14', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-04 10:41:14', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_user_role_seq\n    START WITH 49;\n\n-- ----------------------------\n-- Table structure for system_users\n-- ----------------------------\nCREATE TABLE system_users\n(\n    id          number                                  NOT NULL,\n    username    varchar2(30)                            NULL,\n    password    varchar2(100) DEFAULT ''                NULL,\n    nickname    varchar2(30)                            NULL,\n    remark      varchar2(500) DEFAULT NULL              NULL,\n    dept_id     number        DEFAULT NULL              NULL,\n    post_ids    varchar2(255) DEFAULT NULL              NULL,\n    email       varchar2(50)  DEFAULT ''                NULL,\n    mobile      varchar2(11)  DEFAULT ''                NULL,\n    sex         smallint      DEFAULT 0                 NULL,\n    avatar      varchar2(512) DEFAULT ''                NULL,\n    status      smallint      DEFAULT 0                 NOT NULL,\n    login_ip    varchar2(50)  DEFAULT ''                NULL,\n    login_date  date          DEFAULT NULL              NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id   number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE system_users\n    ADD CONSTRAINT pk_system_users PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_users.id IS '用户ID';\nCOMMENT ON COLUMN system_users.username IS '用户账号';\nCOMMENT ON COLUMN system_users.password IS '密码';\nCOMMENT ON COLUMN system_users.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_users.remark IS '备注';\nCOMMENT ON COLUMN system_users.dept_id IS '部门ID';\nCOMMENT ON COLUMN system_users.post_ids IS '岗位编号数组';\nCOMMENT ON COLUMN system_users.email IS '用户邮箱';\nCOMMENT ON COLUMN system_users.mobile IS '手机号码';\nCOMMENT ON COLUMN system_users.sex IS '用户性别';\nCOMMENT ON COLUMN system_users.avatar IS '头像地址';\nCOMMENT ON COLUMN system_users.status IS '帐号状态（0正常 1停用）';\nCOMMENT ON COLUMN system_users.login_ip IS '最后登录IP';\nCOMMENT ON COLUMN system_users.login_date IS '最后登录时间';\nCOMMENT ON COLUMN system_users.creator IS '创建者';\nCOMMENT ON COLUMN system_users.create_time IS '创建时间';\nCOMMENT ON COLUMN system_users.updater IS '更新者';\nCOMMENT ON COLUMN system_users.update_time IS '更新时间';\nCOMMENT ON COLUMN system_users.deleted IS '是否删除';\nCOMMENT ON COLUMN system_users.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_users IS '用户信息表';\n\n-- ----------------------------\n-- Records of system_users\n-- ----------------------------\n-- @formatter:off\nINSERT 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$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '芋道源码', '管理员', 103, '[1,2]', '11aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/test/20250502/avatar_1746154660449.png', 0, '0:0:0:0:0:0:0:1', to_date('2025-05-10 18:03:15', 'SYYYY-MM-DD HH24:MI:SS'), 'admin', to_date('2021-01-05 17:03:47', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-05-10 18:03:15', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', to_date('2025-04-08 09:36:40', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2021-01-07 09:07:17', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', to_date('2024-08-11 17:48:12', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2021-01-13 23:50:35', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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', to_date('2025-03-28 20:01:16', 'SYYYY-MM-DD HH24:MI:SS'), '', to_date('2021-01-21 02:13:53', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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', to_date('2022-02-20 22:59:33', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 118);\nINSERT 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', to_date('2022-02-20 23:00:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 119);\nINSERT 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', to_date('2022-02-20 23:11:50', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 120);\nINSERT 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', to_date('2024-07-20 22:23:17', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-22 00:56:14', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT 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', to_date('2023-12-30 11:42:17', 'SYYYY-MM-DD HH24:MI:SS'), '110', to_date('2022-02-23 13:14:33', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 121);\nINSERT 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', to_date('2024-03-16 23:11:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-02-23 19:08:03', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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', to_date('2022-03-19 18:38:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-07 21:37:58', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-05-05 15:30:53', 'SYYYY-MM-DD HH24:MI:SS'), '0', 122);\nINSERT 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', to_date('2024-03-24 22:21:05', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-03-19 21:50:58', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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', to_date('2022-04-30 02:55:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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', to_date('2024-10-02 10:16:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-07-09 17:40:26', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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$jth0yOj8cSJq84D6vrzusOHDwW/LpBfgBnQ6bfFlD8zNZfM632Ta2', '狗蛋', NULL, 103, '[1]', '', '15601691239', 1, NULL, 0, '0:0:0:0:0:0:0:1', to_date('2024-03-17 09:10:27', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2022-07-09 17:44:43', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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 (131, 'hh', '$2a$04$jyH9h6.gaw8mpOjPfHIpx.8as2Rzfcmdlj5rlJFwgCw4rsv/MTb2K', '呵呵', NULL, 100, '[]', '777@qq.com', '15601882312', 1, NULL, 0, '', NULL, '1', to_date('2024-04-27 08:45:56', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', '小秃头', NULL, NULL, NULL, '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', to_date('2024-09-10 21:03:58', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2024-09-10 21:03:58', 'SYYYY-MM-DD HH24:MI:SS'), NULL, to_date('2025-04-21 14:23:08', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT 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', to_date('2025-04-08 13:09:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-08 13:09:07', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-08 13:09:07', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE system_users_seq\n    START WITH 142;\n\n-- ----------------------------\n-- Table structure for yudao_demo01_contact\n-- ----------------------------\nCREATE TABLE yudao_demo01_contact\n(\n    id          number                                  NOT NULL,\n    name        varchar2(100) DEFAULT ''                NULL,\n    sex         smallint                                NOT NULL,\n    birthday    date                                    NOT NULL,\n    description varchar2(255)                           NULL,\n    avatar      varchar2(512) DEFAULT NULL              NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id   number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE yudao_demo01_contact\n    ADD CONSTRAINT pk_yudao_demo01_contact PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo01_contact.id IS '编号';\nCOMMENT ON COLUMN yudao_demo01_contact.name IS '名字';\nCOMMENT ON COLUMN yudao_demo01_contact.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo01_contact.birthday IS '出生年';\nCOMMENT ON COLUMN yudao_demo01_contact.description IS '简介';\nCOMMENT ON COLUMN yudao_demo01_contact.avatar IS '头像';\nCOMMENT ON COLUMN yudao_demo01_contact.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo01_contact.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo01_contact.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo01_contact.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo01_contact.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo01_contact.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo01_contact IS '示例联系人表';\n\n-- ----------------------------\n-- Records of yudao_demo01_contact\n-- ----------------------------\n-- @formatter:off\nINSERT INTO yudao_demo01_contact (id, name, sex, birthday, description, avatar, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 2, to_date('2023-11-07 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '<p>天蚕土豆！呀</p>', 'http://127.0.0.1:48080/admin-api/infra/file/4/get/46f8fa1a37db3f3960d8910ff2fe3962ab3b2db87cf2f8ccb4dc8145b8bdf237.jpeg', '1', to_date('2023-11-15 23:34:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-15 23:47:39', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE yudao_demo01_contact_seq\n    START WITH 2;\n\n-- ----------------------------\n-- Table structure for yudao_demo02_category\n-- ----------------------------\nCREATE TABLE yudao_demo02_category\n(\n    id          number                                  NOT NULL,\n    name        varchar2(100) DEFAULT ''                NULL,\n    parent_id   number                                  NOT NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id   number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE yudao_demo02_category\n    ADD CONSTRAINT pk_yudao_demo02_category PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo02_category.id IS '编号';\nCOMMENT ON COLUMN yudao_demo02_category.name IS '名字';\nCOMMENT ON COLUMN yudao_demo02_category.parent_id IS '父级编号';\nCOMMENT ON COLUMN yudao_demo02_category.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo02_category.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo02_category.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo02_category.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo02_category.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo02_category.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo02_category IS '示例分类表';\n\n-- ----------------------------\n-- Records of yudao_demo02_category\n-- ----------------------------\n-- @formatter:off\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 0, '1', to_date('2023-11-15 23:34:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 20:24:23', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '番茄', 0, '1', to_date('2023-11-16 20:24:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 20:24:15', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '怪怪', 0, '1', to_date('2023-11-16 20:24:32', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 20:24:32', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '小番茄', 2, '1', to_date('2023-11-16 20:24:39', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 20:24:39', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, '大番茄', 2, '1', to_date('2023-11-16 20:24:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 20:24:46', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, '11', 3, '1', to_date('2023-11-24 19:29:34', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-24 19:29:34', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE yudao_demo02_category_seq\n    START WITH 7;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_course\n-- ----------------------------\nCREATE TABLE yudao_demo03_course\n(\n    id          number                                  NOT NULL,\n    student_id  number                                  NOT NULL,\n    name        varchar2(100) DEFAULT ''                NULL,\n    score       smallint                                NOT NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id   number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE yudao_demo03_course\n    ADD CONSTRAINT pk_yudao_demo03_course PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_course.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_course.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_course.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_course.score IS '分数';\nCOMMENT ON COLUMN yudao_demo03_course.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_course.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_course.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_course.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_course.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_course.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_course IS '学生课程表';\n\n-- ----------------------------\n-- Records of yudao_demo03_course\n-- ----------------------------\n-- @formatter:off\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, '语文', 66, '1', to_date('2023-11-16 23:21:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 10:55:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 2, '数学', 22, '1', to_date('2023-11-16 23:21:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 10:55:30', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 5, '体育', 23, '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 15:44:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 5, '计算机', 11, '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 15:44:40', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 5, '体育', 23, '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 15:47:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 5, '计算机', 11, '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 15:47:09', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 5, '体育', 23, '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 10:55:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (11, 5, '计算机', 11, '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 10:55:28', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (12, 2, '电脑', 33, '1', to_date('2023-11-17 00:20:42', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-16 16:20:45', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (13, 9, '滑雪', 12, '1', to_date('2023-11-17 13:13:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 10:55:26', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 9, '滑雪', 12, '1', to_date('2023-11-17 13:13:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 10:55:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 5, '体育', 23, '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 18:55:29', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 5, '计算机', 11, '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 18:55:29', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (17, 2, '语文', 66, '1', to_date('2023-11-16 23:21:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 18:55:31', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (18, 2, '数学', 22, '1', to_date('2023-11-16 23:21:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 18:55:31', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (19, 9, '滑雪', 12, '1', to_date('2023-11-17 13:13:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-19 02:49:03', 'SYYYY-MM-DD HH24:MI:SS'), '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (20, 9, '滑雪', 12, '1', to_date('2023-11-17 13:13:20', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-19 10:49:04', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE yudao_demo03_course_seq\n    START WITH 21;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_grade\n-- ----------------------------\nCREATE TABLE yudao_demo03_grade\n(\n    id          number                                  NOT NULL,\n    student_id  number                                  NOT NULL,\n    name        varchar2(100) DEFAULT ''                NULL,\n    teacher     varchar2(255)                           NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id   number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE yudao_demo03_grade\n    ADD CONSTRAINT pk_yudao_demo03_grade PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_grade.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_grade.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_grade.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_grade.teacher IS '班主任';\nCOMMENT ON COLUMN yudao_demo03_grade.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_grade.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_grade.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_grade.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_grade.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_grade.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_grade IS '学生班级表';\n\n-- ----------------------------\n-- Records of yudao_demo03_grade\n-- ----------------------------\n-- @formatter:off\nINSERT INTO yudao_demo03_grade (id, student_id, name, teacher, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 2, '三年 2 班', '周杰伦', '1', to_date('2023-11-16 23:21:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 18:55:31', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo03_grade (id, student_id, name, teacher, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 5, '华为', '遥遥领先', '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 18:55:29', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo03_grade (id, student_id, name, teacher, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 9, '小图', '小娃111', '1', to_date('2023-11-17 13:10:23', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-19 10:49:04', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE yudao_demo03_grade_seq\n    START WITH 10;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_student\n-- ----------------------------\nCREATE TABLE yudao_demo03_student\n(\n    id          number                                  NOT NULL,\n    name        varchar2(100) DEFAULT ''                NULL,\n    sex         smallint                                NOT NULL,\n    birthday    date                                    NOT NULL,\n    description varchar2(255)                           NULL,\n    creator     varchar2(64)  DEFAULT ''                NULL,\n    create_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     varchar2(64)  DEFAULT ''                NULL,\n    update_time date          DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     number(1, 0)  DEFAULT 0                 NOT NULL,\n    tenant_id   number        DEFAULT 0                 NOT NULL\n);\n\nALTER TABLE yudao_demo03_student\n    ADD CONSTRAINT pk_yudao_demo03_student PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_student.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_student.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_student.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo03_student.birthday IS '出生日期';\nCOMMENT ON COLUMN yudao_demo03_student.description IS '简介';\nCOMMENT ON COLUMN yudao_demo03_student.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_student.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_student.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_student.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_student.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_student.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_student IS '学生表';\n\n-- ----------------------------\n-- Records of yudao_demo03_student\n-- ----------------------------\n-- @formatter:off\nINSERT INTO yudao_demo03_student (id, name, sex, birthday, description, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '小白', 1, to_date('2023-11-16 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '<p>厉害</p>', '1', to_date('2023-11-16 23:21:49', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 18:55:31', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo03_student (id, name, sex, birthday, description, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, '大黑', 2, to_date('2023-11-13 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '<p>你在教我做事?</p>', '1', to_date('2023-11-16 23:22:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2024-09-17 18:55:29', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nINSERT INTO yudao_demo03_student (id, name, sex, birthday, description, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, '小花', 1, to_date('2023-11-07 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '<p>哈哈哈</p>', '1', to_date('2023-11-17 00:04:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-04-19 10:49:04', 'SYYYY-MM-DD HH24:MI:SS'), '0', 1);\nCOMMIT;\n-- @formatter:on\n\nCREATE SEQUENCE yudao_demo03_student_seq\n    START WITH 10;\n\n"
  },
  {
    "path": "sql/postgresql/quartz.sql",
    "content": "-- https://github.com/quartz-scheduler/quartz/blob/main/quartz/src/main/resources/org/quartz/impl/jdbcjobstore/tables_postgres.sql\n-- Thanks to Patrick Lightbody for submitting this...\n--\n-- In your Quartz properties file, you'll need to set\n-- org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate\n\nDROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;\nDROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;\nDROP TABLE IF EXISTS QRTZ_LOCKS;\nDROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_JOB_DETAILS;\nDROP TABLE IF EXISTS QRTZ_CALENDARS;\n\nCREATE TABLE QRTZ_JOB_DETAILS\n(\n    SCHED_NAME        VARCHAR(120) NOT NULL,\n    JOB_NAME          VARCHAR(200) NOT NULL,\n    JOB_GROUP         VARCHAR(200) NOT NULL,\n    DESCRIPTION       VARCHAR(250) NULL,\n    JOB_CLASS_NAME    VARCHAR(250) NOT NULL,\n    IS_DURABLE        BOOL         NOT NULL,\n    IS_NONCONCURRENT  BOOL         NOT NULL,\n    IS_UPDATE_DATA    BOOL         NOT NULL,\n    REQUESTS_RECOVERY BOOL         NOT NULL,\n    JOB_DATA          BYTEA        NULL,\n    PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)\n);\n\nCREATE TABLE QRTZ_TRIGGERS\n(\n    SCHED_NAME     VARCHAR(120) NOT NULL,\n    TRIGGER_NAME   VARCHAR(200) NOT NULL,\n    TRIGGER_GROUP  VARCHAR(200) NOT NULL,\n    JOB_NAME       VARCHAR(200) NOT NULL,\n    JOB_GROUP      VARCHAR(200) NOT NULL,\n    DESCRIPTION    VARCHAR(250) NULL,\n    NEXT_FIRE_TIME BIGINT       NULL,\n    PREV_FIRE_TIME BIGINT       NULL,\n    PRIORITY       INTEGER      NULL,\n    TRIGGER_STATE  VARCHAR(16)  NOT NULL,\n    TRIGGER_TYPE   VARCHAR(8)   NOT NULL,\n    START_TIME     BIGINT       NOT NULL,\n    END_TIME       BIGINT       NULL,\n    CALENDAR_NAME  VARCHAR(200) NULL,\n    MISFIRE_INSTR  SMALLINT     NULL,\n    JOB_DATA       BYTEA        NULL,\n    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),\n    FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)\n        REFERENCES QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP)\n);\n\nCREATE TABLE QRTZ_SIMPLE_TRIGGERS\n(\n    SCHED_NAME      VARCHAR(120) NOT NULL,\n    TRIGGER_NAME    VARCHAR(200) NOT NULL,\n    TRIGGER_GROUP   VARCHAR(200) NOT NULL,\n    REPEAT_COUNT    BIGINT       NOT NULL,\n    REPEAT_INTERVAL BIGINT       NOT NULL,\n    TIMES_TRIGGERED BIGINT       NOT NULL,\n    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),\n    FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)\n        REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)\n);\n\nCREATE TABLE QRTZ_CRON_TRIGGERS\n(\n    SCHED_NAME      VARCHAR(120) NOT NULL,\n    TRIGGER_NAME    VARCHAR(200) NOT NULL,\n    TRIGGER_GROUP   VARCHAR(200) NOT NULL,\n    CRON_EXPRESSION VARCHAR(120) NOT NULL,\n    TIME_ZONE_ID    VARCHAR(80),\n    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),\n    FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)\n        REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)\n);\n\nCREATE TABLE QRTZ_SIMPROP_TRIGGERS\n(\n    SCHED_NAME    VARCHAR(120)   NOT NULL,\n    TRIGGER_NAME  VARCHAR(200)   NOT NULL,\n    TRIGGER_GROUP VARCHAR(200)   NOT NULL,\n    STR_PROP_1    VARCHAR(512)   NULL,\n    STR_PROP_2    VARCHAR(512)   NULL,\n    STR_PROP_3    VARCHAR(512)   NULL,\n    INT_PROP_1    INT            NULL,\n    INT_PROP_2    INT            NULL,\n    LONG_PROP_1   BIGINT         NULL,\n    LONG_PROP_2   BIGINT         NULL,\n    DEC_PROP_1    NUMERIC(13, 4) NULL,\n    DEC_PROP_2    NUMERIC(13, 4) NULL,\n    BOOL_PROP_1   BOOL           NULL,\n    BOOL_PROP_2   BOOL           NULL,\n    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),\n    FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)\n        REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)\n);\n\nCREATE TABLE QRTZ_BLOB_TRIGGERS\n(\n    SCHED_NAME    VARCHAR(120) NOT NULL,\n    TRIGGER_NAME  VARCHAR(200) NOT NULL,\n    TRIGGER_GROUP VARCHAR(200) NOT NULL,\n    BLOB_DATA     BYTEA        NULL,\n    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),\n    FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)\n        REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)\n);\n\nCREATE TABLE QRTZ_CALENDARS\n(\n    SCHED_NAME    VARCHAR(120) NOT NULL,\n    CALENDAR_NAME VARCHAR(200) NOT NULL,\n    CALENDAR      BYTEA        NOT NULL,\n    PRIMARY KEY (SCHED_NAME, CALENDAR_NAME)\n);\n\n\nCREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS\n(\n    SCHED_NAME    VARCHAR(120) NOT NULL,\n    TRIGGER_GROUP VARCHAR(200) NOT NULL,\n    PRIMARY KEY (SCHED_NAME, TRIGGER_GROUP)\n);\n\nCREATE TABLE QRTZ_FIRED_TRIGGERS\n(\n    SCHED_NAME        VARCHAR(120) NOT NULL,\n    ENTRY_ID          VARCHAR(95)  NOT NULL,\n    TRIGGER_NAME      VARCHAR(200) NOT NULL,\n    TRIGGER_GROUP     VARCHAR(200) NOT NULL,\n    INSTANCE_NAME     VARCHAR(200) NOT NULL,\n    FIRED_TIME        BIGINT       NOT NULL,\n    SCHED_TIME        BIGINT       NOT NULL,\n    PRIORITY          INTEGER      NOT NULL,\n    STATE             VARCHAR(16)  NOT NULL,\n    JOB_NAME          VARCHAR(200) NULL,\n    JOB_GROUP         VARCHAR(200) NULL,\n    IS_NONCONCURRENT  BOOL         NULL,\n    REQUESTS_RECOVERY BOOL         NULL,\n    PRIMARY KEY (SCHED_NAME, ENTRY_ID)\n);\n\nCREATE TABLE QRTZ_SCHEDULER_STATE\n(\n    SCHED_NAME        VARCHAR(120) NOT NULL,\n    INSTANCE_NAME     VARCHAR(200) NOT NULL,\n    LAST_CHECKIN_TIME BIGINT       NOT NULL,\n    CHECKIN_INTERVAL  BIGINT       NOT NULL,\n    PRIMARY KEY (SCHED_NAME, INSTANCE_NAME)\n);\n\nCREATE TABLE QRTZ_LOCKS\n(\n    SCHED_NAME VARCHAR(120) NOT NULL,\n    LOCK_NAME  VARCHAR(40)  NOT NULL,\n    PRIMARY KEY (SCHED_NAME, LOCK_NAME)\n);\n\nCREATE INDEX IDX_QRTZ_J_REQ_RECOVERY\n    ON QRTZ_JOB_DETAILS (SCHED_NAME, REQUESTS_RECOVERY);\nCREATE INDEX IDX_QRTZ_J_GRP\n    ON QRTZ_JOB_DETAILS (SCHED_NAME, JOB_GROUP);\n\nCREATE INDEX IDX_QRTZ_T_J\n    ON QRTZ_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);\nCREATE INDEX IDX_QRTZ_T_JG\n    ON QRTZ_TRIGGERS (SCHED_NAME, JOB_GROUP);\nCREATE INDEX IDX_QRTZ_T_C\n    ON QRTZ_TRIGGERS (SCHED_NAME, CALENDAR_NAME);\nCREATE INDEX IDX_QRTZ_T_G\n    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);\nCREATE INDEX IDX_QRTZ_T_STATE\n    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_N_STATE\n    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_N_G_STATE\n    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME\n    ON QRTZ_TRIGGERS (SCHED_NAME, NEXT_FIRE_TIME);\nCREATE INDEX IDX_QRTZ_T_NFT_ST\n    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME);\nCREATE INDEX IDX_QRTZ_T_NFT_MISFIRE\n    ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME);\nCREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE\n    ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP\n    ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE);\n\nCREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME\n    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME);\nCREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY\n    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY);\nCREATE INDEX IDX_QRTZ_FT_J_G\n    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);\nCREATE INDEX IDX_QRTZ_FT_JG\n    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_GROUP);\nCREATE INDEX IDX_QRTZ_FT_T_G\n    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);\nCREATE INDEX IDX_QRTZ_FT_TG\n    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);\n\n\nCOMMIT;"
  },
  {
    "path": "sql/postgresql/ruoyi-vue-pro.sql",
    "content": "/*\n Yudao Database Transfer Tool\n\n Source Server Type    : MySQL\n\n Target Server Type    : PostgreSQL\n\n Date: 2025-12-27 19:04:22\n*/\n\n\n-- ----------------------------\n-- Table structure for dual\n-- ----------------------------\nDROP TABLE IF EXISTS dual;\nCREATE TABLE dual\n(\n    id int2\n);\n\nCOMMENT ON TABLE dual IS '数据库连接的表';\n\n-- ----------------------------\n-- Records of dual\n-- ----------------------------\n-- @formatter:off\nINSERT INTO dual VALUES (1);\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_api_access_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_api_access_log;\nCREATE TABLE infra_api_access_log (\n    id int8 NOT NULL,\n  trace_id varchar(64) NOT NULL DEFAULT '',\n  user_id int8 NOT NULL DEFAULT 0,\n  user_type int2 NOT NULL DEFAULT 0,\n  application_name varchar(50) NOT NULL,\n  request_method varchar(16) NOT NULL DEFAULT '',\n  request_url varchar(255) NOT NULL DEFAULT '',\n  request_params text NULL,\n  response_body text NULL,\n  user_ip varchar(50) NOT NULL,\n  user_agent varchar(512) NOT NULL,\n  operate_module varchar(50) NULL DEFAULT NULL,\n  operate_name varchar(50) NULL DEFAULT NULL,\n  operate_type int2 NULL DEFAULT 0,\n  begin_time timestamp NOT NULL,\n  end_time timestamp NOT NULL,\n  duration int4 NOT NULL,\n  result_code int4 NOT NULL DEFAULT 0,\n  result_msg varchar(512) NULL DEFAULT '',\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_api_access_log ADD CONSTRAINT pk_infra_api_access_log PRIMARY KEY (id);\n\nCREATE INDEX idx_infra_api_access_log_01 ON infra_api_access_log (create_time);\n\nCOMMENT ON COLUMN infra_api_access_log.id IS '日志主键';\nCOMMENT ON COLUMN infra_api_access_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_access_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_access_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_access_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_access_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_access_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_access_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_access_log.response_body IS '响应结果';\nCOMMENT ON COLUMN infra_api_access_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_access_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_access_log.operate_module IS '操作模块';\nCOMMENT ON COLUMN infra_api_access_log.operate_name IS '操作名';\nCOMMENT ON COLUMN infra_api_access_log.operate_type IS '操作分类';\nCOMMENT ON COLUMN infra_api_access_log.begin_time IS '开始请求时间';\nCOMMENT ON COLUMN infra_api_access_log.end_time IS '结束请求时间';\nCOMMENT ON COLUMN infra_api_access_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_api_access_log.result_code IS '结果码';\nCOMMENT ON COLUMN infra_api_access_log.result_msg IS '结果提示';\nCOMMENT ON COLUMN infra_api_access_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_access_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_access_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_access_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_access_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_access_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_access_log IS 'API 访问日志表';\n\nDROP SEQUENCE IF EXISTS infra_api_access_log_seq;\nCREATE SEQUENCE infra_api_access_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_api_error_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_api_error_log;\nCREATE TABLE infra_api_error_log (\n    id int8 NOT NULL,\n  trace_id varchar(64) NOT NULL,\n  user_id int8 NOT NULL DEFAULT 0,\n  user_type int2 NOT NULL DEFAULT 0,\n  application_name varchar(50) NOT NULL,\n  request_method varchar(16) NOT NULL,\n  request_url varchar(255) NOT NULL,\n  request_params varchar(8000) NOT NULL,\n  user_ip varchar(50) NOT NULL,\n  user_agent varchar(512) NOT NULL,\n  exception_time timestamp NOT NULL,\n  exception_name varchar(128) NOT NULL DEFAULT '',\n  exception_message text NOT NULL,\n  exception_root_cause_message text NOT NULL,\n  exception_stack_trace text NOT NULL,\n  exception_class_name varchar(512) NOT NULL,\n  exception_file_name varchar(512) NOT NULL,\n  exception_method_name varchar(512) NOT NULL,\n  exception_line_number int4 NOT NULL,\n  process_status int2 NOT NULL,\n  process_time timestamp NULL DEFAULT NULL,\n  process_user_id int4 NULL DEFAULT 0,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_api_error_log ADD CONSTRAINT pk_infra_api_error_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_api_error_log.id IS '编号';\nCOMMENT ON COLUMN infra_api_error_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN infra_api_error_log.user_id IS '用户编号';\nCOMMENT ON COLUMN infra_api_error_log.user_type IS '用户类型';\nCOMMENT ON COLUMN infra_api_error_log.application_name IS '应用名';\nCOMMENT ON COLUMN infra_api_error_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN infra_api_error_log.request_url IS '请求地址';\nCOMMENT ON COLUMN infra_api_error_log.request_params IS '请求参数';\nCOMMENT ON COLUMN infra_api_error_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN infra_api_error_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN infra_api_error_log.exception_time IS '异常发生时间';\nCOMMENT ON COLUMN infra_api_error_log.exception_name IS '异常名';\nCOMMENT ON COLUMN infra_api_error_log.exception_message IS '异常导致的消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_root_cause_message IS '异常导致的根消息';\nCOMMENT ON COLUMN infra_api_error_log.exception_stack_trace IS '异常的栈轨迹';\nCOMMENT ON COLUMN infra_api_error_log.exception_class_name IS '异常发生的类全名';\nCOMMENT ON COLUMN infra_api_error_log.exception_file_name IS '异常发生的类文件';\nCOMMENT ON COLUMN infra_api_error_log.exception_method_name IS '异常发生的方法名';\nCOMMENT ON COLUMN infra_api_error_log.exception_line_number IS '异常发生的方法所在行';\nCOMMENT ON COLUMN infra_api_error_log.process_status IS '处理状态';\nCOMMENT ON COLUMN infra_api_error_log.process_time IS '处理时间';\nCOMMENT ON COLUMN infra_api_error_log.process_user_id IS '处理用户编号';\nCOMMENT ON COLUMN infra_api_error_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_api_error_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_api_error_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_api_error_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_api_error_log.deleted IS '是否删除';\nCOMMENT ON COLUMN infra_api_error_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE infra_api_error_log IS '系统异常日志';\n\nDROP SEQUENCE IF EXISTS infra_api_error_log_seq;\nCREATE SEQUENCE infra_api_error_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_codegen_column\n-- ----------------------------\nDROP TABLE IF EXISTS infra_codegen_column;\nCREATE TABLE infra_codegen_column (\n    id int8 NOT NULL,\n  table_id int8 NOT NULL,\n  column_name varchar(200) NOT NULL,\n  data_type varchar(100) NOT NULL,\n  column_comment varchar(500) NOT NULL,\n  nullable bool NOT NULL,\n  primary_key bool NOT NULL,\n  ordinal_position int4 NOT NULL,\n  java_type varchar(32) NOT NULL,\n  java_field varchar(64) NOT NULL,\n  dict_type varchar(200) NULL DEFAULT '',\n  example varchar(64) NULL DEFAULT NULL,\n  create_operation bool NOT NULL,\n  update_operation bool NOT NULL,\n  list_operation bool NOT NULL,\n  list_operation_condition varchar(32) NOT NULL DEFAULT '=',\n  list_operation_result bool NOT NULL,\n  html_type varchar(32) NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_codegen_column ADD CONSTRAINT pk_infra_codegen_column PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_codegen_column.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_column.table_id IS '表编号';\nCOMMENT ON COLUMN infra_codegen_column.column_name IS '字段名';\nCOMMENT ON COLUMN infra_codegen_column.data_type IS '字段类型';\nCOMMENT ON COLUMN infra_codegen_column.column_comment IS '字段描述';\nCOMMENT ON COLUMN infra_codegen_column.nullable IS '是否允许为空';\nCOMMENT ON COLUMN infra_codegen_column.primary_key IS '是否主键';\nCOMMENT ON COLUMN infra_codegen_column.ordinal_position IS '排序';\nCOMMENT ON COLUMN infra_codegen_column.java_type IS 'Java 属性类型';\nCOMMENT ON COLUMN infra_codegen_column.java_field IS 'Java 属性名';\nCOMMENT ON COLUMN infra_codegen_column.dict_type IS '字典类型';\nCOMMENT ON COLUMN infra_codegen_column.example IS '数据示例';\nCOMMENT ON COLUMN infra_codegen_column.create_operation IS '是否为 Create 创建操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.update_operation IS '是否为 Update 更新操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation IS '是否为 List 查询操作的字段';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_condition IS 'List 查询操作的条件类型';\nCOMMENT ON COLUMN infra_codegen_column.list_operation_result IS '是否为 List 查询操作的返回字段';\nCOMMENT ON COLUMN infra_codegen_column.html_type IS '显示类型';\nCOMMENT ON COLUMN infra_codegen_column.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_column.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_column.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_column.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_column.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_column IS '代码生成表字段定义';\n\nDROP SEQUENCE IF EXISTS infra_codegen_column_seq;\nCREATE SEQUENCE infra_codegen_column_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_codegen_table\n-- ----------------------------\nDROP TABLE IF EXISTS infra_codegen_table;\nCREATE TABLE infra_codegen_table (\n    id int8 NOT NULL,\n  data_source_config_id int8 NOT NULL,\n  scene int2 NOT NULL DEFAULT 1,\n  table_name varchar(200) NOT NULL DEFAULT '',\n  table_comment varchar(500) NOT NULL DEFAULT '',\n  remark varchar(500) NULL DEFAULT NULL,\n  module_name varchar(30) NOT NULL,\n  business_name varchar(30) NOT NULL,\n  class_name varchar(100) NOT NULL DEFAULT '',\n  class_comment varchar(50) NOT NULL,\n  author varchar(50) NOT NULL,\n  template_type int2 NOT NULL DEFAULT 1,\n  front_type int2 NOT NULL,\n  parent_menu_id int8 NULL DEFAULT NULL,\n  master_table_id int8 NULL DEFAULT NULL,\n  sub_join_column_id int8 NULL DEFAULT NULL,\n  sub_join_many bool NULL DEFAULT NULL,\n  tree_parent_column_id int8 NULL DEFAULT NULL,\n  tree_name_column_id int8 NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_codegen_table ADD CONSTRAINT pk_infra_codegen_table PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_codegen_table.id IS '编号';\nCOMMENT ON COLUMN infra_codegen_table.data_source_config_id IS '数据源配置的编号';\nCOMMENT ON COLUMN infra_codegen_table.scene IS '生成场景';\nCOMMENT ON COLUMN infra_codegen_table.table_name IS '表名称';\nCOMMENT ON COLUMN infra_codegen_table.table_comment IS '表描述';\nCOMMENT ON COLUMN infra_codegen_table.remark IS '备注';\nCOMMENT ON COLUMN infra_codegen_table.module_name IS '模块名';\nCOMMENT ON COLUMN infra_codegen_table.business_name IS '业务名';\nCOMMENT ON COLUMN infra_codegen_table.class_name IS '类名称';\nCOMMENT ON COLUMN infra_codegen_table.class_comment IS '类描述';\nCOMMENT ON COLUMN infra_codegen_table.author IS '作者';\nCOMMENT ON COLUMN infra_codegen_table.template_type IS '模板类型';\nCOMMENT ON COLUMN infra_codegen_table.front_type IS '前端类型';\nCOMMENT ON COLUMN infra_codegen_table.parent_menu_id IS '父菜单编号';\nCOMMENT ON COLUMN infra_codegen_table.master_table_id IS '主表的编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_column_id IS '子表关联主表的字段编号';\nCOMMENT ON COLUMN infra_codegen_table.sub_join_many IS '主表与子表是否一对多';\nCOMMENT ON COLUMN infra_codegen_table.tree_parent_column_id IS '树表的父字段编号';\nCOMMENT ON COLUMN infra_codegen_table.tree_name_column_id IS '树表的名字字段编号';\nCOMMENT ON COLUMN infra_codegen_table.creator IS '创建者';\nCOMMENT ON COLUMN infra_codegen_table.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_codegen_table.updater IS '更新者';\nCOMMENT ON COLUMN infra_codegen_table.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_codegen_table.deleted IS '是否删除';\nCOMMENT ON TABLE infra_codegen_table IS '代码生成表定义';\n\nDROP SEQUENCE IF EXISTS infra_codegen_table_seq;\nCREATE SEQUENCE infra_codegen_table_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_config;\nCREATE TABLE infra_config (\n    id int8 NOT NULL,\n  category varchar(50) NOT NULL,\n  type int2 NOT NULL,\n  name varchar(100) NOT NULL DEFAULT '',\n  config_key varchar(100) NOT NULL DEFAULT '',\n  value varchar(500) NOT NULL DEFAULT '',\n  visible bool NOT NULL,\n  remark varchar(500) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_config ADD CONSTRAINT pk_infra_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_config.id IS '参数主键';\nCOMMENT ON COLUMN infra_config.category IS '参数分组';\nCOMMENT ON COLUMN infra_config.type IS '参数类型';\nCOMMENT ON COLUMN infra_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_config.config_key IS '参数键名';\nCOMMENT ON COLUMN infra_config.value IS '参数键值';\nCOMMENT ON COLUMN infra_config.visible IS '是否可见';\nCOMMENT ON COLUMN infra_config.remark IS '备注';\nCOMMENT ON COLUMN infra_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_config IS '参数配置表';\n\n-- ----------------------------\n-- Records of infra_config\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'system.user.init-password', '123456', '0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-07-20 17:22:47', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:33:38', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:57:03', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:52:07', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (10, 'url', 2, 'Swagger 接口文档的地址', 'url.swagger', '', '1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:59:00', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (11, 'ui', 2, '腾讯地图 key', 'tencent.lbs.key', 'TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E', '1', '腾讯地图 key', '1', '2023-06-03 19:16:27', '1', '2023-06-03 19:16:27', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 'test2', 2, 'test3', 'test4', 'test5', '1', 'test6', '1', '2023-12-03 09:55:16', '1', '2025-04-06 21:00:09', '0');\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '用户管理-账号初始密码', 2, '用户管理-注册开关', 'system.user.register-enabled', 'true', '0', '', '1', '2025-04-26 17:23:41', '1', '2025-04-26 17:23:41', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS infra_config_seq;\nCREATE SEQUENCE infra_config_seq\n    START 14;\n\n-- ----------------------------\n-- Table structure for infra_data_source_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_data_source_config;\nCREATE TABLE infra_data_source_config (\n    id int8 NOT NULL,\n  name varchar(100) NOT NULL DEFAULT '',\n  url varchar(1024) NOT NULL,\n  username varchar(255) NOT NULL,\n  password varchar(255) NOT NULL DEFAULT '',\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_data_source_config ADD CONSTRAINT pk_infra_data_source_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_data_source_config.id IS '主键编号';\nCOMMENT ON COLUMN infra_data_source_config.name IS '参数名称';\nCOMMENT ON COLUMN infra_data_source_config.url IS '数据源连接';\nCOMMENT ON COLUMN infra_data_source_config.username IS '用户名';\nCOMMENT ON COLUMN infra_data_source_config.password IS '密码';\nCOMMENT ON COLUMN infra_data_source_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_data_source_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_data_source_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_data_source_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_data_source_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_data_source_config IS '数据源配置表';\n\nDROP SEQUENCE IF EXISTS infra_data_source_config_seq;\nCREATE SEQUENCE infra_data_source_config_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_file\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file;\nCREATE TABLE infra_file (\n    id int8 NOT NULL,\n  config_id int8 NULL DEFAULT NULL,\n  name varchar(256) NULL DEFAULT NULL,\n  path varchar(512) NOT NULL,\n  url varchar(1024) NOT NULL,\n  type varchar(128) NULL DEFAULT NULL,\n  size int4 NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_file ADD CONSTRAINT pk_infra_file PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file.id IS '文件编号';\nCOMMENT ON COLUMN infra_file.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file.name IS '文件名';\nCOMMENT ON COLUMN infra_file.path IS '文件路径';\nCOMMENT ON COLUMN infra_file.url IS '文件 URL';\nCOMMENT ON COLUMN infra_file.type IS '文件类型';\nCOMMENT ON COLUMN infra_file.size IS '文件大小';\nCOMMENT ON COLUMN infra_file.creator IS '创建者';\nCOMMENT ON COLUMN infra_file.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file.updater IS '更新者';\nCOMMENT ON COLUMN infra_file.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file IS '文件表';\n\nDROP SEQUENCE IF EXISTS infra_file_seq;\nCREATE SEQUENCE infra_file_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_file_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file_config;\nCREATE TABLE infra_file_config (\n    id int8 NOT NULL,\n  name varchar(63) NOT NULL,\n  storage int2 NOT NULL,\n  remark varchar(255) NULL DEFAULT NULL,\n  master bool NOT NULL,\n  config varchar(4096) NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_file_config ADD CONSTRAINT pk_infra_file_config PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file_config.id IS '编号';\nCOMMENT ON COLUMN infra_file_config.name IS '配置名';\nCOMMENT ON COLUMN infra_file_config.storage IS '存储器';\nCOMMENT ON COLUMN infra_file_config.remark IS '备注';\nCOMMENT ON COLUMN infra_file_config.master IS '是否为主配置';\nCOMMENT ON COLUMN infra_file_config.config IS '存储配置';\nCOMMENT ON COLUMN infra_file_config.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_config.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_config.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_config.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_config.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_config IS '文件配置表';\n\n-- ----------------------------\n-- Records of infra_file_config\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (4, '数据库（示例）', 1, '我是数据库', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (22, '七牛存储器（示例）', 20, '请换成你自己的密钥！！！', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (24, '腾讯云存储（示例）', 20, '请换成你的密钥！！！', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (25, '阿里云存储（示例）', 20, '', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (26, '火山云存储（示例）', 20, '', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (27, '华为云存储（示例）', 20, '', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (28, 'MinIO 存储（示例）', 20, '', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (29, '本地存储（示例）', 10, 'mac/linux 使用 /，windows 使用 \\\\', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (30, 'SFTP 存储（示例）', 12, '', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (34, '七牛云存储【私有】（示例）', 20, '请换成你自己的密钥！！！', '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', '0');\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (35, '1', 20, '1', '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}', '1', '2025-10-02 14:32:12', '1', '2025-11-24 20:57:14', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS infra_file_config_seq;\nCREATE SEQUENCE infra_file_config_seq\n    START 36;\n\n-- ----------------------------\n-- Table structure for infra_file_content\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file_content;\nCREATE TABLE infra_file_content (\n    id int8 NOT NULL,\n  config_id int8 NOT NULL,\n  path varchar(512) NOT NULL,\n  content bytea NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_file_content ADD CONSTRAINT pk_infra_file_content PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_file_content.id IS '编号';\nCOMMENT ON COLUMN infra_file_content.config_id IS '配置编号';\nCOMMENT ON COLUMN infra_file_content.path IS '文件路径';\nCOMMENT ON COLUMN infra_file_content.content IS '文件内容';\nCOMMENT ON COLUMN infra_file_content.creator IS '创建者';\nCOMMENT ON COLUMN infra_file_content.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_file_content.updater IS '更新者';\nCOMMENT ON COLUMN infra_file_content.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_file_content.deleted IS '是否删除';\nCOMMENT ON TABLE infra_file_content IS '文件表';\n\nDROP SEQUENCE IF EXISTS infra_file_content_seq;\nCREATE SEQUENCE infra_file_content_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for infra_job\n-- ----------------------------\nDROP TABLE IF EXISTS infra_job;\nCREATE TABLE infra_job (\n    id int8 NOT NULL,\n  name varchar(32) NOT NULL,\n  status int2 NOT NULL,\n  handler_name varchar(64) NOT NULL,\n  handler_param varchar(255) NULL DEFAULT NULL,\n  cron_expression varchar(32) NOT NULL,\n  retry_count int4 NOT NULL DEFAULT 0,\n  retry_interval int4 NOT NULL DEFAULT 0,\n  monitor_timeout int4 NOT NULL DEFAULT 0,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_job ADD CONSTRAINT pk_infra_job PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_job.id IS '任务编号';\nCOMMENT ON COLUMN infra_job.name IS '任务名称';\nCOMMENT ON COLUMN infra_job.status IS '任务状态';\nCOMMENT ON COLUMN infra_job.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job.cron_expression IS 'CRON 表达式';\nCOMMENT ON COLUMN infra_job.retry_count IS '重试次数';\nCOMMENT ON COLUMN infra_job.retry_interval IS '重试间隔';\nCOMMENT ON COLUMN infra_job.monitor_timeout IS '监控超时时间';\nCOMMENT ON COLUMN infra_job.creator IS '创建者';\nCOMMENT ON COLUMN infra_job.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job.updater IS '更新者';\nCOMMENT ON COLUMN infra_job.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job IS '定时任务表';\n\n-- ----------------------------\n-- Records of infra_job\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (5, '支付通知 Job', 2, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', '2021-10-27 08:34:42', '1', '2024-09-12 13:32:48', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (17, '支付订单同步 Job', 2, 'payOrderSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 14:36:26', '1', '2023-07-22 15:39:08', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (18, '支付订单过期 Job', 2, 'payOrderExpireJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 15:36:23', '1', '2023-07-22 15:39:54', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (19, '退款订单的同步 Job', 2, 'payRefundSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-23 21:03:44', '1', '2023-07-23 21:09:00', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (21, 'Mall 交易订单的自动过期 Job', 2, 'tradeOrderAutoCancelJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-25 23:43:26', '1', '2025-10-02 11:08:34', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (22, 'Mall 交易订单的自动收货 Job', 2, 'tradeOrderAutoReceiveJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 19:23:53', '1', '2025-10-02 11:08:36', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (23, 'Mall 交易订单的自动评论 Job', 2, 'tradeOrderAutoCommentJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-26 23:38:29', '1', '2025-10-02 11:08:38', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (24, 'Mall 佣金解冻 Job', 2, 'brokerageRecordUnfreezeJob', '', '0 * * * * ?', 3, 0, 0, '1', '2023-09-28 22:01:46', '1', '2025-10-02 11:08:04', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (25, '访问日志清理 Job', 2, 'accessLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 10:59:41', '1', '2023-10-03 11:01:10', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (26, '错误日志清理 Job', 2, 'errorLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:00:43', '1', '2023-10-03 11:01:12', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (27, '任务日志清理 Job', 2, 'jobLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:01:33', '1', '2024-09-12 13:40:34', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (33, 'demoJob', 2, 'demoJob', '', '0 * * * * ?', 1, 1, 0, '1', '2024-10-27 19:38:46', '1', '2025-05-10 18:13:54', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (35, '转账订单的同步 Job', 2, 'payTransferSyncJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-05-10 17:35:54', '1', '2025-05-10 18:13:52', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (36, 'IoT 设备离线检查 Job', 2, 'iotDeviceOfflineCheckJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-07-03 23:48:44', '\"1\"', '2025-07-03 23:48:47', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (37, 'IoT OTA 升级推送 Job', 2, 'iotOtaUpgradeJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-07-03 23:49:07', '\"1\"', '2025-07-03 23:49:13', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (38, 'Mall 拼团过期 Job', 2, 'combinationRecordExpireJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-10-02 11:07:11', '1', '2025-10-02 11:07:14', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (39, 'Mall 优惠券过期 Job', 2, 'couponExpireJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-10-02 11:07:34', '1', '2025-10-02 11:07:37', '0');\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (40, 'Mall 商品统计 Job', 2, 'productStatisticsJob', '', '0 0 0 * * ?', 0, 0, 0, '1', '2025-11-22 18:51:25', '1', '2025-11-22 18:56:21', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS infra_job_seq;\nCREATE SEQUENCE infra_job_seq\n    START 41;\n\n-- ----------------------------\n-- Table structure for infra_job_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_job_log;\nCREATE TABLE infra_job_log (\n    id int8 NOT NULL,\n  job_id int8 NOT NULL,\n  handler_name varchar(64) NOT NULL,\n  handler_param varchar(255) NULL DEFAULT NULL,\n  execute_index int2 NOT NULL DEFAULT 1,\n  begin_time timestamp NOT NULL,\n  end_time timestamp NULL DEFAULT NULL,\n  duration int4 NULL DEFAULT NULL,\n  status int2 NOT NULL,\n  result varchar(4000) NULL DEFAULT '',\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE infra_job_log ADD CONSTRAINT pk_infra_job_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN infra_job_log.id IS '日志编号';\nCOMMENT ON COLUMN infra_job_log.job_id IS '任务编号';\nCOMMENT ON COLUMN infra_job_log.handler_name IS '处理器的名字';\nCOMMENT ON COLUMN infra_job_log.handler_param IS '处理器的参数';\nCOMMENT ON COLUMN infra_job_log.execute_index IS '第几次执行';\nCOMMENT ON COLUMN infra_job_log.begin_time IS '开始执行时间';\nCOMMENT ON COLUMN infra_job_log.end_time IS '结束执行时间';\nCOMMENT ON COLUMN infra_job_log.duration IS '执行时长';\nCOMMENT ON COLUMN infra_job_log.status IS '任务状态';\nCOMMENT ON COLUMN infra_job_log.result IS '结果数据';\nCOMMENT ON COLUMN infra_job_log.creator IS '创建者';\nCOMMENT ON COLUMN infra_job_log.create_time IS '创建时间';\nCOMMENT ON COLUMN infra_job_log.updater IS '更新者';\nCOMMENT ON COLUMN infra_job_log.update_time IS '更新时间';\nCOMMENT ON COLUMN infra_job_log.deleted IS '是否删除';\nCOMMENT ON TABLE infra_job_log IS '定时任务日志表';\n\nDROP SEQUENCE IF EXISTS infra_job_log_seq;\nCREATE SEQUENCE infra_job_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_dept\n-- ----------------------------\nDROP TABLE IF EXISTS system_dept;\nCREATE TABLE system_dept (\n    id int8 NOT NULL,\n  name varchar(30) NOT NULL DEFAULT '',\n  parent_id int8 NOT NULL DEFAULT 0,\n  sort int4 NOT NULL DEFAULT 0,\n  leader_user_id int8 NULL DEFAULT NULL,\n  phone varchar(11) NULL DEFAULT NULL,\n  email varchar(50) NULL DEFAULT NULL,\n  status int2 NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_dept ADD CONSTRAINT pk_system_dept PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dept.id IS '部门id';\nCOMMENT ON COLUMN system_dept.name IS '部门名称';\nCOMMENT ON COLUMN system_dept.parent_id IS '父部门id';\nCOMMENT ON COLUMN system_dept.sort IS '显示顺序';\nCOMMENT ON COLUMN system_dept.leader_user_id IS '负责人';\nCOMMENT ON COLUMN system_dept.phone IS '联系电话';\nCOMMENT ON COLUMN system_dept.email IS '邮箱';\nCOMMENT ON COLUMN system_dept.status IS '部门状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dept.creator IS '创建者';\nCOMMENT ON COLUMN system_dept.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dept.updater IS '更新者';\nCOMMENT ON COLUMN system_dept.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dept.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dept.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_dept IS '部门表';\n\n-- ----------------------------\n-- Records of system_dept\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, '芋道源码', 0, 0, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2025-03-29 15:47:53', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (101, '深圳总公司', 100, 1, 104, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2025-03-29 15:49:55', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (102, '长沙分公司', 100, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:40', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, '研发部门', 101, 1, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2024-10-02 10:22:03', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, '市场部门', 101, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:38', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (105, '测试部门', 101, 3, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-05-16 20:25:15', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (106, '财务部门', 101, 4, 103, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '103', '2022-01-15 21:32:22', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (107, '运维部门', 101, 5, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2023-12-02 09:28:22', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (108, '市场部门', 102, 1, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-02-16 08:35:45', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (109, '财务部门', 102, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:29', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (110, '新部门', 0, 1, NULL, NULL, NULL, 0, '110', '2022-02-23 20:46:30', '110', '2022-02-23 20:46:30', '0', 121);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (111, '顶级部门', 0, 1, NULL, NULL, NULL, 0, '113', '2022-03-07 21:44:50', '113', '2022-03-07 21:44:50', '0', 122);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, '产品部门', 101, 100, 1, NULL, NULL, 1, '1', '2023-12-02 09:45:13', '1', '2023-12-02 09:45:31', '0', 1);\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, '支持部门', 102, 3, 104, NULL, NULL, 1, '1', '2023-12-02 09:47:38', '1', '2025-03-29 15:00:56', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_dept_seq;\nCREATE SEQUENCE system_dept_seq\n    START 114;\n\n-- ----------------------------\n-- Table structure for system_dict_data\n-- ----------------------------\nDROP TABLE IF EXISTS system_dict_data;\nCREATE TABLE system_dict_data (\n    id int8 NOT NULL,\n  sort int4 NOT NULL DEFAULT 0,\n  label varchar(100) NOT NULL DEFAULT '',\n  value varchar(100) NOT NULL DEFAULT '',\n  dict_type varchar(100) NOT NULL DEFAULT '',\n  status int2 NOT NULL DEFAULT 0,\n  color_type varchar(100) NULL DEFAULT '',\n  css_class varchar(100) NULL DEFAULT '',\n  remark varchar(500) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_dict_data ADD CONSTRAINT pk_system_dict_data PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dict_data.id IS '字典编码';\nCOMMENT ON COLUMN system_dict_data.sort IS '字典排序';\nCOMMENT ON COLUMN system_dict_data.label IS '字典标签';\nCOMMENT ON COLUMN system_dict_data.value IS '字典键值';\nCOMMENT ON COLUMN system_dict_data.dict_type IS '字典类型';\nCOMMENT ON COLUMN system_dict_data.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_data.color_type IS '颜色类型';\nCOMMENT ON COLUMN system_dict_data.css_class IS 'css 样式';\nCOMMENT ON COLUMN system_dict_data.remark IS '备注';\nCOMMENT ON COLUMN system_dict_data.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_data.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_data.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_data.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_data.deleted IS '是否删除';\nCOMMENT ON TABLE system_dict_data IS '字典数据表';\n\n-- ----------------------------\n-- Records of system_dict_data\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1, 1, '男', '1', 'system_user_sex', 0, 'default', 'A', '性别男', 'admin', '2021-01-05 17:03:48', '1', '2022-03-29 00:14:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 2, '女', '2', 'system_user_sex', 0, 'success', '', '性别女', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 23:30:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 1, '正常', '1', 'infra_job_status', 0, 'success', '', '正常状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 2, '暂停', '2', 'infra_job_status', 0, 'danger', '', '停用状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 1, '系统内置', '1', 'infra_config_type', 0, 'danger', '', '参数类型 - 系统内置', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (13, 2, '自定义', '2', 'infra_config_type', 0, 'primary', '', '参数类型 - 自定义', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (14, 1, '通知', '1', 'system_notice_type', 0, 'success', '', '通知', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:05:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (15, 2, '公告', '2', 'system_notice_type', 0, 'info', '', '公告', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:06:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (16, 0, '其它', '0', 'infra_operate_type', 0, 'default', '', '其它操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (17, 1, '查询', '1', 'infra_operate_type', 0, 'info', '', '查询操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (18, 2, '新增', '2', 'infra_operate_type', 0, 'primary', '', '新增操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (19, 3, '修改', '3', 'infra_operate_type', 0, 'warning', '', '修改操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (20, 4, '删除', '4', 'infra_operate_type', 0, 'danger', '', '删除操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (22, 5, '导出', '5', 'infra_operate_type', 0, 'default', '', '导出操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (23, 6, '导入', '6', 'infra_operate_type', 0, 'default', '', '导入操作', 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (27, 1, '开启', '0', 'common_status', 0, 'primary', '', '开启状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (28, 2, '关闭', '1', 'common_status', 0, 'info', '', '关闭状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 08:00:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (29, 1, '目录', '1', 'system_menu_type', 0, '', '', '目录', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (30, 2, '菜单', '2', 'system_menu_type', 0, '', '', '菜单', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (31, 3, '按钮', '3', 'system_menu_type', 0, '', '', '按钮', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:43:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (32, 1, '内置', '1', 'system_role_type', 0, 'danger', '', '内置角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (33, 2, '自定义', '2', 'system_role_type', 0, 'primary', '', '自定义角色', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 13:02:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (34, 1, '全部数据权限', '1', 'system_data_scope', 0, '', '', '全部数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (35, 2, '指定部门数据权限', '2', 'system_data_scope', 0, '', '', '指定部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (36, 3, '本部门数据权限', '3', 'system_data_scope', 0, '', '', '本部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (37, 4, '本部门及以下数据权限', '4', 'system_data_scope', 0, '', '', '本部门及以下数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (38, 5, '仅本人数据权限', '5', 'system_data_scope', 0, '', '', '仅本人数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (39, 0, '成功', '0', 'system_login_result', 0, 'success', '', '登陆结果 - 成功', '', '2021-01-18 06:17:36', '1', '2022-02-16 13:23:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (40, 10, '账号或密码不正确', '10', 'system_login_result', 0, 'primary', '', '登陆结果 - 账号或密码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (41, 20, '用户被禁用', '20', 'system_login_result', 0, 'warning', '', '登陆结果 - 用户被禁用', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:23:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (42, 30, '验证码不存在', '30', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不存在', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (43, 31, '验证码不正确', '31', 'system_login_result', 0, 'info', '', '登陆结果 - 验证码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (44, 100, '未知异常', '100', 'system_login_result', 0, 'danger', '', '登陆结果 - 未知异常', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (45, 1, '是', 'true', 'infra_boolean_string', 0, 'danger', '', 'Boolean 是否类型 - 是', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:01:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (46, 1, '否', 'false', 'infra_boolean_string', 0, 'info', '', 'Boolean 是否类型 - 否', '', '2021-01-19 03:20:55', '1', '2022-03-15 23:09:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (50, 1, '单表（增删改查）', '1', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:09:06', '', '2022-03-10 16:33:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (51, 2, '树表（增删改查）', '2', 'infra_codegen_template_type', 0, '', '', NULL, '', '2021-02-05 07:14:46', '', '2022-03-10 16:33:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (53, 0, '初始化中', '0', 'infra_job_status', 0, 'primary', '', NULL, '', '2021-02-07 07:46:49', '1', '2022-02-16 19:33:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (57, 0, '运行中', '0', 'infra_job_log_status', 0, 'primary', '', 'RUNNING', '', '2021-02-08 10:04:24', '1', '2022-02-16 19:07:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (58, 1, '成功', '1', 'infra_job_log_status', 0, 'success', '', NULL, '', '2021-02-08 10:06:57', '1', '2022-02-16 19:07:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (59, 2, '失败', '2', 'infra_job_log_status', 0, 'warning', '', '失败', '', '2021-02-08 10:07:38', '1', '2022-02-16 19:07:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (60, 1, '会员', '1', 'user_type', 0, 'primary', '', NULL, '', '2021-02-26 00:16:27', '1', '2022-02-16 10:22:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (61, 2, '管理员', '2', 'user_type', 0, 'success', '', NULL, '', '2021-02-26 00:16:34', '1', '2025-04-06 18:37:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (62, 0, '未处理', '0', 'infra_api_error_log_process_status', 0, 'primary', '', NULL, '', '2021-02-26 07:07:19', '1', '2022-02-16 20:14:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (63, 1, '已处理', '1', 'infra_api_error_log_process_status', 0, 'success', '', NULL, '', '2021-02-26 07:07:26', '1', '2022-02-16 20:14:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (64, 2, '已忽略', '2', 'infra_api_error_log_process_status', 0, 'danger', '', NULL, '', '2021-02-26 07:07:34', '1', '2022-02-16 20:14:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (66, 1, '阿里云', 'ALIYUN', 'system_sms_channel_code', 0, 'primary', '', NULL, '1', '2021-04-05 01:05:26', '1', '2024-07-22 22:23:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (67, 1, '验证码', '1', 'system_sms_template_type', 0, 'warning', '', NULL, '1', '2021-04-05 21:50:57', '1', '2022-02-16 12:48:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (68, 2, '通知', '2', 'system_sms_template_type', 0, 'primary', '', NULL, '1', '2021-04-05 21:51:08', '1', '2022-02-16 12:48:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (69, 0, '营销', '3', 'system_sms_template_type', 0, 'danger', '', NULL, '1', '2021-04-05 21:51:15', '1', '2022-02-16 12:48:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (70, 0, '初始化', '0', 'system_sms_send_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:18:33', '1', '2022-02-16 10:26:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (71, 1, '发送成功', '10', 'system_sms_send_status', 0, 'success', '', NULL, '1', '2021-04-11 20:18:43', '1', '2022-02-16 10:25:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (72, 2, '发送失败', '20', 'system_sms_send_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:18:49', '1', '2022-02-16 10:26:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (73, 3, '不发送', '30', 'system_sms_send_status', 0, 'info', '', NULL, '1', '2021-04-11 20:19:44', '1', '2022-02-16 10:26:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (74, 0, '等待结果', '0', 'system_sms_receive_status', 0, 'primary', '', NULL, '1', '2021-04-11 20:27:43', '1', '2022-02-16 10:28:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (75, 1, '接收成功', '10', 'system_sms_receive_status', 0, 'success', '', NULL, '1', '2021-04-11 20:29:25', '1', '2022-02-16 10:28:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (76, 2, '接收失败', '20', 'system_sms_receive_status', 0, 'danger', '', NULL, '1', '2021-04-11 20:29:31', '1', '2022-02-16 10:28:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (77, 0, '调试(钉钉)', 'DEBUG_DING_TALK', 'system_sms_channel_code', 0, 'info', '', NULL, '1', '2021-04-13 00:20:37', '1', '2022-02-16 10:10:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (80, 100, '账号登录', '100', 'system_login_type', 0, 'primary', '', '账号登录', '1', '2021-10-06 00:52:02', '1', '2022-02-16 13:11:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (81, 101, '社交登录', '101', 'system_login_type', 0, 'info', '', '社交登录', '1', '2021-10-06 00:52:17', '1', '2022-02-16 13:11:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (83, 200, '主动登出', '200', 'system_login_type', 0, 'primary', '', '主动登出', '1', '2021-10-06 00:52:58', '1', '2022-02-16 13:11:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (85, 202, '强制登出', '202', 'system_login_type', 0, 'danger', '', '强制退出', '1', '2021-10-06 00:53:41', '1', '2022-02-16 13:11:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (86, 0, '病假', '1', 'bpm_oa_leave_type', 0, 'primary', '', NULL, '1', '2021-09-21 22:35:28', '1', '2022-02-16 10:00:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (87, 1, '事假', '2', 'bpm_oa_leave_type', 0, 'info', '', NULL, '1', '2021-09-21 22:36:11', '1', '2022-02-16 10:00:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (88, 2, '婚假', '3', 'bpm_oa_leave_type', 0, 'warning', '', NULL, '1', '2021-09-21 22:36:38', '1', '2022-02-16 10:00:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (112, 0, '微信 Wap 网站支付', 'wx_wap', 'pay_channel_code', 0, 'success', '', '微信 Wap 网站支付', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (113, 1, '微信公众号支付', 'wx_pub', 'pay_channel_code', 0, 'success', '', '微信公众号支付', '1', '2021-12-03 10:40:24', '1', '2023-07-19 20:08:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (114, 2, '微信小程序支付', 'wx_lite', 'pay_channel_code', 0, 'success', '', '微信小程序支付', '1', '2021-12-03 10:41:06', '1', '2023-07-19 20:08:50', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (115, 3, '微信 App 支付', 'wx_app', 'pay_channel_code', 0, 'success', '', '微信 App 支付', '1', '2021-12-03 10:41:20', '1', '2023-07-19 20:08:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (116, 10, '支付宝 PC 网站支付', 'alipay_pc', 'pay_channel_code', 0, 'primary', '', '支付宝 PC 网站支付', '1', '2021-12-03 10:42:09', '1', '2023-07-19 20:09:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (117, 11, '支付宝 Wap 网站支付', 'alipay_wap', 'pay_channel_code', 0, 'primary', '', '支付宝 Wap 网站支付', '1', '2021-12-03 10:42:26', '1', '2023-07-19 20:09:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (118, 12, '支付宝 App 支付', 'alipay_app', 'pay_channel_code', 0, 'primary', '', '支付宝 App 支付', '1', '2021-12-03 10:42:55', '1', '2023-07-19 20:09:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (119, 14, '支付宝扫码支付', 'alipay_qr', 'pay_channel_code', 0, 'primary', '', '支付宝扫码支付', '1', '2021-12-03 10:43:10', '1', '2023-07-19 20:09:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (120, 10, '通知成功', '10', 'pay_notify_status', 0, 'success', '', '通知成功', '1', '2021-12-03 11:02:41', '1', '2023-07-19 10:08:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (121, 20, '通知失败', '20', 'pay_notify_status', 0, 'danger', '', '通知失败', '1', '2021-12-03 11:02:59', '1', '2023-07-19 10:08:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (122, 0, '等待通知', '0', 'pay_notify_status', 0, 'info', '', '未通知', '1', '2021-12-03 11:03:10', '1', '2023-07-19 10:08:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (123, 10, '支付成功', '10', 'pay_order_status', 0, 'success', '', '支付成功', '1', '2021-12-03 11:18:29', '1', '2023-07-19 18:04:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (124, 30, '支付关闭', '30', 'pay_order_status', 0, 'info', '', '支付关闭', '1', '2021-12-03 11:18:42', '1', '2023-07-19 18:05:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (125, 0, '等待支付', '0', 'pay_order_status', 0, 'info', '', '未支付', '1', '2021-12-03 11:18:18', '1', '2023-07-19 18:04:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (600, 5, '首页', '1', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (601, 4, '秒杀活动页', '2', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (602, 3, '砍价活动页', '3', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (603, 2, '限时折扣页', '4', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (604, 1, '满减送页', '5', 'promotion_banner_position', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1118, 0, '等待退款', '0', 'pay_refund_status', 0, 'info', '', '等待退款', '1', '2021-12-10 16:44:59', '1', '2023-07-19 10:14:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1119, 20, '退款失败', '20', 'pay_refund_status', 0, 'danger', '', '退款失败', '1', '2021-12-10 16:45:10', '1', '2023-07-19 10:15:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1124, 10, '退款成功', '10', 'pay_refund_status', 0, 'success', '', '退款成功', '1', '2021-12-10 16:46:26', '1', '2023-07-19 10:15:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1127, 1, '审批中', '1', 'bpm_process_instance_status', 0, 'default', '', '流程实例的状态 - 进行中', '1', '2022-01-07 23:47:22', '1', '2024-03-16 16:11:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1128, 2, '审批通过', '2', 'bpm_process_instance_status', 0, 'success', '', '流程实例的状态 - 已完成', '1', '2022-01-07 23:47:49', '1', '2024-03-16 16:11:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1129, 1, '审批中', '1', 'bpm_task_status', 0, 'primary', '', '流程实例的结果 - 处理中', '1', '2022-01-07 23:48:32', '1', '2024-03-08 22:41:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1130, 2, '审批通过', '2', 'bpm_task_status', 0, 'success', '', '流程实例的结果 - 通过', '1', '2022-01-07 23:48:45', '1', '2024-03-08 22:41:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1131, 3, '审批不通过', '3', 'bpm_task_status', 0, 'danger', '', '流程实例的结果 - 不通过', '1', '2022-01-07 23:48:55', '1', '2024-03-08 22:41:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1132, 4, '已取消', '4', 'bpm_task_status', 0, 'info', '', '流程实例的结果 - 撤销', '1', '2022-01-07 23:49:06', '1', '2024-03-08 22:41:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1133, 10, '流程表单', '10', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 流程表单', '103', '2022-01-11 23:51:30', '103', '2022-01-11 23:51:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1134, 20, '业务表单', '20', 'bpm_model_form_type', 0, '', '', '流程的表单类型 - 业务表单', '103', '2022-01-11 23:51:47', '103', '2022-01-11 23:51:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1135, 10, '角色', '10', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 角色', '103', '2022-01-12 23:21:22', '1', '2024-03-06 02:53:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1136, 20, '部门的成员', '20', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的成员', '103', '2022-01-12 23:21:47', '1', '2024-03-06 02:53:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1137, 21, '部门的负责人', '21', 'bpm_task_candidate_strategy', 0, 'primary', '', '任务分配规则的类型 - 部门的负责人', '103', '2022-01-12 23:33:36', '1', '2024-03-06 02:53:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1138, 30, '用户', '30', 'bpm_task_candidate_strategy', 0, 'info', '', '任务分配规则的类型 - 用户', '103', '2022-01-12 23:34:02', '1', '2024-03-06 02:53:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1139, 40, '用户组', '40', 'bpm_task_candidate_strategy', 0, 'warning', '', '任务分配规则的类型 - 用户组', '103', '2022-01-12 23:34:21', '1', '2024-03-06 02:53:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1140, 60, '流程表达式', '60', 'bpm_task_candidate_strategy', 0, 'danger', '', '任务分配规则的类型 - 流程表达式', '103', '2022-01-12 23:34:43', '1', '2024-03-06 02:53:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1141, 22, '岗位', '22', 'bpm_task_candidate_strategy', 0, 'success', '', '任务分配规则的类型 - 岗位', '103', '2022-01-14 18:41:55', '1', '2024-03-06 02:53:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1145, 1, '管理后台', '1', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 管理后台', '1', '2022-02-02 13:15:06', '1', '2022-03-10 16:32:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1146, 2, '用户 APP', '2', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 用户 APP', '1', '2022-02-02 13:15:19', '1', '2022-03-10 16:33:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1150, 1, '数据库', '1', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:28', '1', '2022-03-15 00:25:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1151, 10, '本地磁盘', '10', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:41', '1', '2022-03-15 00:25:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1152, 11, 'FTP 服务器', '11', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:06', '1', '2022-03-15 00:26:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1153, 12, 'SFTP 服务器', '12', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:22', '1', '2022-03-15 00:26:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1154, 20, 'S3 对象存储', '20', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:31', '1', '2022-03-15 00:26:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1155, 103, '短信登录', '103', 'system_login_type', 0, 'default', '', NULL, '1', '2022-05-09 23:57:58', '1', '2022-05-09 23:58:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1156, 1, 'password', 'password', 'system_oauth2_grant_type', 0, 'default', '', '密码模式', '1', '2022-05-12 00:22:05', '1', '2022-05-11 16:26:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1157, 2, 'authorization_code', 'authorization_code', 'system_oauth2_grant_type', 0, 'primary', '', '授权码模式', '1', '2022-05-12 00:22:59', '1', '2022-05-11 16:26:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1158, 3, 'implicit', 'implicit', 'system_oauth2_grant_type', 0, 'success', '', '简化模式', '1', '2022-05-12 00:23:40', '1', '2022-05-11 16:26:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1159, 4, 'client_credentials', 'client_credentials', 'system_oauth2_grant_type', 0, 'default', '', '客户端模式', '1', '2022-05-12 00:23:51', '1', '2022-05-11 16:26:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1160, 5, 'refresh_token', 'refresh_token', 'system_oauth2_grant_type', 0, 'info', '', '刷新模式', '1', '2022-05-12 00:24:02', '1', '2022-05-11 16:26:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1162, 1, '销售中', '1', 'product_spu_status', 0, 'success', '', '商品 SPU 状态 - 销售中', '1', '2022-10-24 21:19:47', '1', '2022-10-24 21:20:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1163, 0, '仓库中', '0', 'product_spu_status', 0, 'info', '', '商品 SPU 状态 - 仓库中', '1', '2022-10-24 21:20:54', '1', '2022-10-24 21:21:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1164, 0, '回收站', '-1', 'product_spu_status', 0, 'default', '', '商品 SPU 状态 - 回收站', '1', '2022-10-24 21:21:11', '1', '2022-10-24 21:21:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1165, 1, '满减', '1', 'promotion_discount_type', 0, 'success', '', '优惠类型 - 满减', '1', '2022-11-01 12:46:41', '1', '2022-11-01 12:50:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1166, 2, '折扣', '2', 'promotion_discount_type', 0, 'primary', '', '优惠类型 - 折扣', '1', '2022-11-01 12:46:51', '1', '2022-11-01 12:50:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1167, 1, '固定日期', '1', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 固定日期', '1', '2022-11-02 00:07:34', '1', '2022-11-04 00:07:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1168, 2, '领取之后', '2', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 领取之后', '1', '2022-11-02 00:07:54', '1', '2022-11-04 00:07:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1169, 1, '通用劵', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', '2022-11-02 00:28:22', '1', '2023-09-28 00:27:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1170, 2, '商品劵', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', '2022-11-02 00:28:34', '1', '2023-09-28 00:27:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1171, 1, '未使用', '1', 'promotion_coupon_status', 0, 'primary', '', '优惠劵的状态 - 已领取', '1', '2022-11-04 00:15:08', '1', '2023-10-03 12:54:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1172, 2, '已使用', '2', 'promotion_coupon_status', 0, 'success', '', '优惠劵的状态 - 已使用', '1', '2022-11-04 00:15:21', '1', '2022-11-04 19:16:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1173, 3, '已过期', '3', 'promotion_coupon_status', 0, 'info', '', '优惠劵的状态 - 已过期', '1', '2022-11-04 00:15:43', '1', '2022-11-04 19:16:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1174, 1, '直接领取', '1', 'promotion_coupon_take_type', 0, 'primary', '', '优惠劵的领取方式 - 直接领取', '1', '2022-11-04 19:13:00', '1', '2022-11-04 19:13:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1175, 2, '指定发放', '2', 'promotion_coupon_take_type', 0, 'success', '', '优惠劵的领取方式 - 指定发放', '1', '2022-11-04 19:13:13', '1', '2022-11-04 19:14:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1176, 10, '未开始', '10', 'promotion_activity_status', 0, 'primary', '', '促销活动的状态枚举 - 未开始', '1', '2022-11-04 22:54:49', '1', '2022-11-04 22:55:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1177, 20, '进行中', '20', 'promotion_activity_status', 0, 'success', '', '促销活动的状态枚举 - 进行中', '1', '2022-11-04 22:55:06', '1', '2022-11-04 22:55:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1178, 30, '已结束', '30', 'promotion_activity_status', 0, 'info', '', '促销活动的状态枚举 - 已结束', '1', '2022-11-04 22:55:41', '1', '2022-11-04 22:55:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1179, 40, '已关闭', '40', 'promotion_activity_status', 0, 'warning', '', '促销活动的状态枚举 - 已关闭', '1', '2022-11-04 22:56:10', '1', '2022-11-04 22:56:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1180, 10, '满 N 元', '10', 'promotion_condition_type', 0, 'primary', '', '营销的条件类型 - 满 N 元', '1', '2022-11-04 22:59:45', '1', '2022-11-04 22:59:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1181, 20, '满 N 件', '20', 'promotion_condition_type', 0, 'success', '', '营销的条件类型 - 满 N 件', '1', '2022-11-04 23:00:02', '1', '2022-11-04 23:00:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1182, 10, '申请售后', '10', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 申请售后', '1', '2022-11-19 20:53:33', '1', '2022-11-19 20:54:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1183, 20, '商品待退货', '20', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商品待退货', '1', '2022-11-19 20:54:36', '1', '2022-11-19 20:58:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1184, 30, '商家待收货', '30', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 商家待收货', '1', '2022-11-19 20:56:56', '1', '2022-11-19 20:59:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1185, 40, '等待退款', '40', 'trade_after_sale_status', 0, 'primary', '', '交易售后状态 - 等待退款', '1', '2022-11-19 20:59:54', '1', '2022-11-19 21:00:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1186, 50, '退款成功', '50', 'trade_after_sale_status', 0, 'default', '', '交易售后状态 - 退款成功', '1', '2022-11-19 21:00:33', '1', '2022-11-19 21:00:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1187, 61, '买家取消', '61', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 买家取消', '1', '2022-11-19 21:01:29', '1', '2022-11-19 21:01:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1188, 62, '商家拒绝', '62', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒绝', '1', '2022-11-19 21:02:17', '1', '2022-11-19 21:02:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1189, 63, '商家拒收货', '63', 'trade_after_sale_status', 0, 'info', '', '交易售后状态 - 商家拒收货', '1', '2022-11-19 21:02:37', '1', '2022-11-19 21:03:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1190, 10, '售中退款', '10', 'trade_after_sale_type', 0, 'success', '', '交易售后的类型 - 售中退款', '1', '2022-11-19 21:05:05', '1', '2022-11-19 21:38:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1191, 20, '售后退款', '20', 'trade_after_sale_type', 0, 'primary', '', '交易售后的类型 - 售后退款', '1', '2022-11-19 21:05:32', '1', '2022-11-19 21:38:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1192, 10, '仅退款', '10', 'trade_after_sale_way', 0, 'primary', '', '交易售后的方式 - 仅退款', '1', '2022-11-19 21:39:19', '1', '2022-11-19 21:39:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1193, 20, '退货退款', '20', 'trade_after_sale_way', 0, 'success', '', '交易售后的方式 - 退货退款', '1', '2022-11-19 21:39:38', '1', '2022-11-19 21:39:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1194, 10, '微信小程序', '10', 'terminal', 0, 'default', '', '终端 - 微信小程序', '1', '2022-12-10 10:51:11', '1', '2022-12-10 10:51:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1195, 20, 'H5 网页', '20', 'terminal', 0, 'default', '', '终端 - H5 网页', '1', '2022-12-10 10:51:30', '1', '2022-12-10 10:51:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1196, 11, '微信公众号', '11', 'terminal', 0, 'default', '', '终端 - 微信公众号', '1', '2022-12-10 10:54:16', '1', '2022-12-10 10:52:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1197, 31, '苹果 App', '31', 'terminal', 0, 'default', '', '终端 - 苹果 App', '1', '2022-12-10 10:54:42', '1', '2022-12-10 10:52:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1198, 32, '安卓 App', '32', 'terminal', 0, 'default', '', '终端 - 安卓 App', '1', '2022-12-10 10:55:02', '1', '2022-12-10 10:59:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1199, 0, '普通订单', '0', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 普通订单', '1', '2022-12-10 16:34:14', '1', '2022-12-10 16:34:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1200, 1, '秒杀订单', '1', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 秒杀订单', '1', '2022-12-10 16:34:26', '1', '2022-12-10 16:34:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1201, 2, '砍价订单', '2', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 拼团订单', '1', '2022-12-10 16:34:36', '1', '2024-09-07 14:18:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1202, 3, '拼团订单', '3', 'trade_order_type', 0, 'default', '', '交易订单的类型 - 砍价订单', '1', '2022-12-10 16:34:48', '1', '2024-09-07 14:18:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1203, 0, '待支付', '0', 'trade_order_status', 0, 'default', '', '交易订单状态 - 待支付', '1', '2022-12-10 16:49:29', '1', '2022-12-10 16:49:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1204, 10, '待发货', '10', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 待发货', '1', '2022-12-10 16:49:53', '1', '2022-12-10 16:51:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1205, 20, '已发货', '20', 'trade_order_status', 0, 'primary', '', '交易订单状态 - 已发货', '1', '2022-12-10 16:50:13', '1', '2022-12-10 16:51:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1206, 30, '已完成', '30', 'trade_order_status', 0, 'success', '', '交易订单状态 - 已完成', '1', '2022-12-10 16:50:30', '1', '2022-12-10 16:51:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1207, 40, '已取消', '40', 'trade_order_status', 0, 'danger', '', '交易订单状态 - 已取消', '1', '2022-12-10 16:50:50', '1', '2022-12-10 16:51:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1208, 0, '未售后', '0', 'trade_order_item_after_sale_status', 0, 'info', '', '交易订单项的售后状态 - 未售后', '1', '2022-12-10 20:58:42', '1', '2022-12-10 20:59:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1209, 10, '售后中', '10', 'trade_order_item_after_sale_status', 0, 'primary', '', '交易订单项的售后状态 - 售后中', '1', '2022-12-10 20:59:21', '1', '2024-07-21 17:01:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1210, 20, '已退款', '20', 'trade_order_item_after_sale_status', 0, 'success', '', '交易订单项的售后状态 - 已退款', '1', '2022-12-10 20:59:46', '1', '2024-07-21 17:01:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1211, 1, '完全匹配', '1', 'mp_auto_reply_request_match', 0, 'primary', '', '公众号自动回复的请求关键字匹配模式 - 完全匹配', '1', '2023-01-16 23:30:39', '1', '2023-01-16 23:31:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1212, 2, '半匹配', '2', 'mp_auto_reply_request_match', 0, 'success', '', '公众号自动回复的请求关键字匹配模式 - 半匹配', '1', '2023-01-16 23:30:55', '1', '2023-01-16 23:31:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1213, 1, '文本', 'text', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 文本', '1', '2023-01-17 22:17:32', '1', '2023-01-17 22:17:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1214, 2, '图片', 'image', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图片', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1215, 3, '语音', 'voice', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 语音', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:20:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1216, 4, '视频', 'video', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:21:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1217, 5, '小视频', 'shortvideo', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 小视频', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:19:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1218, 6, '图文', 'news', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 图文', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1219, 7, '音乐', 'music', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 音乐', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:22:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1220, 8, '地理位置', 'location', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 地理位置', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:23:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1221, 9, '链接', 'link', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 链接', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1222, 10, '事件', 'event', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 事件', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1223, 0, '初始化', '0', 'system_mail_send_status', 0, 'primary', '', '邮件发送状态 - 初始化\\n', '1', '2023-01-26 09:53:49', '1', '2023-01-26 16:36:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1224, 10, '发送成功', '10', 'system_mail_send_status', 0, 'success', '', '邮件发送状态 - 发送成功', '1', '2023-01-26 09:54:28', '1', '2023-01-26 16:36:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1225, 20, '发送失败', '20', 'system_mail_send_status', 0, 'danger', '', '邮件发送状态 - 发送失败', '1', '2023-01-26 09:54:50', '1', '2023-01-26 16:36:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1226, 30, '不发送', '30', 'system_mail_send_status', 0, 'info', '', '邮件发送状态 -  不发送', '1', '2023-01-26 09:55:06', '1', '2023-01-26 16:36:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1227, 1, '通知公告', '1', 'system_notify_template_type', 0, 'primary', '', '站内信模版的类型 - 通知公告', '1', '2023-01-28 10:35:59', '1', '2023-01-28 10:35:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1228, 2, '系统消息', '2', 'system_notify_template_type', 0, 'success', '', '站内信模版的类型 - 系统消息', '1', '2023-01-28 10:36:20', '1', '2023-01-28 10:36:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1230, 13, '支付宝条码支付', 'alipay_bar', 'pay_channel_code', 0, 'primary', '', '支付宝条码支付', '1', '2023-02-18 23:32:24', '1', '2023-07-19 20:09:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1231, 10, 'Vue2 Element UI 标准模版', '10', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:03:55', '1', '2023-04-13 00:03:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1232, 20, 'Vue3 Element Plus 标准模版', '20', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:08', '1', '2023-04-13 00:04:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1234, 30, 'Vben2.0 Ant Design Schema 模版', '30', 'infra_codegen_front_type', 1, '', '', '', '1', '2023-04-13 00:04:26', '1', '2025-07-27 10:55:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1244, 0, '按件', '1', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:40', '1', '2023-05-21 22:46:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1245, 1, '按重量', '2', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:58', '1', '2023-05-21 22:46:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1246, 2, '按体积', '3', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:47:18', '1', '2023-05-21 22:47:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1335, 11, '订单积分抵扣', '11', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:27', '1', '2023-10-11 07:41:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1336, 1, '签到', '1', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:48', '1', '2023-08-20 11:59:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1341, 20, '已退款', '20', 'pay_order_status', 0, 'danger', '', '已退款', '1', '2023-07-19 18:05:37', '1', '2023-07-19 18:05:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1342, 21, '请求成功，但是结果失败', '21', 'pay_notify_status', 0, 'warning', '', '请求成功，但是结果失败', '1', '2023-07-19 18:10:47', '1', '2023-07-19 18:11:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1343, 22, '请求失败', '22', 'pay_notify_status', 0, 'warning', '', NULL, '1', '2023-07-19 18:11:05', '1', '2023-07-19 18:11:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1344, 4, '微信扫码支付', 'wx_native', 'pay_channel_code', 0, 'success', '', '微信扫码支付', '1', '2023-07-19 20:07:47', '1', '2023-07-19 20:09:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1345, 5, '微信条码支付', 'wx_bar', 'pay_channel_code', 0, 'success', '', '微信条码支付\\n', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1346, 1, '支付单', '1', 'pay_notify_type', 0, 'primary', '', '支付单', '1', '2023-07-20 12:23:17', '1', '2023-07-20 12:23:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1347, 2, '退款单', '2', 'pay_notify_type', 0, 'danger', '', NULL, '1', '2023-07-20 12:23:26', '1', '2023-07-20 12:23:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1348, 20, '模拟支付', 'mock', 'pay_channel_code', 0, 'default', '', '模拟支付', '1', '2023-07-29 11:10:51', '1', '2023-07-29 03:14:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1349, 12, '订单积分抵扣（整单取消）', '12', 'member_point_biz_type', 0, '', '', '', '1', '2023-08-20 12:00:03', '1', '2023-10-11 07:42:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1350, 0, '管理员调整', '0', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1351, 1, '邀新奖励', '1', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1352, 11, '下单奖励', '11', 'member_experience_biz_type', 0, 'success', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1353, 12, '下单奖励（整单取消）', '12', 'member_experience_biz_type', 0, 'warning', '', NULL, '', '2023-08-22 12:41:01', '1', '2023-10-11 07:45:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1354, 4, '签到奖励', '4', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1355, 5, '抽奖奖励', '5', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1356, 1, '快递发货', '1', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:04:55', '1', '2023-08-23 00:04:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1357, 2, '用户自提', '2', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:05:05', '1', '2023-08-23 00:05:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1358, 3, '品类劵', '3', 'promotion_product_scope', 0, 'default', '', '', '1', '2023-09-01 23:43:07', '1', '2023-09-28 00:27:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1359, 1, '人人分销', '1', 'brokerage_enabled_condition', 0, '', '', '所有用户都可以分销', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1360, 2, '指定分销', '2', 'brokerage_enabled_condition', 0, '', '', '仅可后台手动设置推广员', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1361, 1, '首次绑定', '1', 'brokerage_bind_mode', 0, '', '', '只要用户没有推广人，随时都可以绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1362, 2, '注册绑定', '2', 'brokerage_bind_mode', 0, '', '', '仅新用户注册时才能绑定推广关系', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1363, 3, '覆盖绑定', '3', 'brokerage_bind_mode', 0, '', '', '如果用户已经有推广人，推广人会被变更', '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1364, 1, '钱包', '1', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1365, 2, '银行卡', '2', 'brokerage_withdraw_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1366, 3, '微信收款码', '3', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1367, 4, '支付宝收款码', '4', 'brokerage_withdraw_type', 0, '', '', '手动打款', '', '2023-09-28 02:46:05', '1', '2025-05-10 08:24:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1368, 1, '订单返佣', '1', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1369, 2, '申请提现', '2', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1370, 3, '申请提现驳回', '3', 'brokerage_record_biz_type', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1371, 0, '待结算', '0', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1372, 1, '已结算', '1', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1373, 2, '已取消', '2', 'brokerage_record_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1374, 0, '审核中', '0', 'brokerage_withdraw_status', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1375, 10, '审核通过', '10', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1376, 11, '提现成功', '11', 'brokerage_withdraw_status', 0, 'success', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1377, 20, '审核不通过', '20', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1378, 21, '提现失败', '21', 'brokerage_withdraw_status', 0, 'danger', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1379, 0, '工商银行', '0', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1380, 1, '建设银行', '1', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1381, 2, '农业银行', '2', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1382, 3, '中国银行', '3', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1383, 4, '交通银行', '4', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1384, 5, '招商银行', '5', 'brokerage_bank_name', 0, '', '', NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1385, 21, '钱包', 'wallet', 'pay_channel_code', 0, 'primary', '', '', '1', '2023-10-01 21:46:19', '1', '2023-10-01 21:48:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1386, 1, '砍价中', '1', 'promotion_bargain_record_status', 0, 'default', '', '', '1', '2023-10-05 10:41:26', '1', '2023-10-05 10:41:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1387, 2, '砍价成功', '2', 'promotion_bargain_record_status', 0, 'success', '', '', '1', '2023-10-05 10:41:39', '1', '2023-10-05 10:41:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1388, 3, '砍价失败', '3', 'promotion_bargain_record_status', 0, 'warning', '', '', '1', '2023-10-05 10:41:57', '1', '2023-10-05 10:41:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1389, 0, '拼团中', '0', 'promotion_combination_record_status', 0, '', '', '', '1', '2023-10-08 07:24:44', '1', '2024-10-13 10:08:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1390, 1, '拼团成功', '1', 'promotion_combination_record_status', 0, 'success', '', '', '1', '2023-10-08 07:24:56', '1', '2024-10-13 10:08:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1391, 2, '拼团失败', '2', 'promotion_combination_record_status', 0, 'warning', '', '', '1', '2023-10-08 07:25:11', '1', '2024-10-13 10:08:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1392, 2, '管理员修改', '2', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:41:34', '1', '2023-10-11 07:41:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1393, 13, '订单积分抵扣（单个退款）', '13', 'member_point_biz_type', 0, '', '', '', '1', '2023-10-11 07:42:29', '1', '2023-10-11 07:42:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1394, 21, '订单积分奖励', '21', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:44', '1', '2023-10-11 07:42:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1395, 22, '订单积分奖励（整单取消）', '22', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:55', '1', '2023-10-11 07:43:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1396, 23, '订单积分奖励（单个退款）', '23', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:43:16', '1', '2023-10-11 07:43:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1397, 13, '下单奖励（单个退款）', '13', 'member_experience_biz_type', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1398, 5, '网上转账', '5', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:24', '1', '2023-10-18 21:55:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1399, 6, '支付宝', '6', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:38', '1', '2023-10-18 21:55:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1400, 7, '微信支付', '7', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:53', '1', '2023-10-18 21:55:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1401, 8, '其他', '8', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:56:06', '1', '2023-10-18 21:56:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1402, 1, 'IT', '1', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:15', '1', '2024-02-18 23:30:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1403, 2, '金融业', '2', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:29', '1', '2024-02-18 23:30:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1404, 3, '房地产', '3', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:41', '1', '2024-02-18 23:30:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1405, 4, '商业服务', '4', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:54', '1', '2024-02-18 23:30:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1406, 5, '运输/物流', '5', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:03', '1', '2024-02-18 23:31:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1407, 6, '生产', '6', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:13', '1', '2024-02-18 23:31:08', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1408, 7, '政府', '7', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:27', '1', '2024-02-18 23:31:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1409, 8, '文化传媒', '8', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:37', '1', '2024-02-18 23:31:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1422, 1, 'A （重点客户）', '1', 'crm_customer_level', 0, 'primary', '', '', '1', '2023-10-28 23:07:13', '1', '2023-10-28 23:07:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1423, 2, 'B （普通客户）', '2', 'crm_customer_level', 0, 'info', '', '', '1', '2023-10-28 23:07:35', '1', '2023-10-28 23:07:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1424, 3, 'C （非优先客户）', '3', 'crm_customer_level', 0, 'default', '', '', '1', '2023-10-28 23:07:53', '1', '2023-10-28 23:07:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1425, 1, '促销', '1', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:29', '1', '2023-10-28 23:08:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1426, 2, '搜索引擎', '2', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:39', '1', '2023-10-28 23:08:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1427, 3, '广告', '3', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:47', '1', '2023-10-28 23:08:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1428, 4, '转介绍', '4', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:58', '1', '2023-10-28 23:08:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1429, 5, '线上注册', '5', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:12', '1', '2023-10-28 23:09:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1430, 6, '线上咨询', '6', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:22', '1', '2023-10-28 23:09:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1431, 7, '预约上门', '7', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:39', '1', '2023-10-28 23:09:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1432, 8, '陌拜', '8', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:04', '1', '2023-10-28 23:10:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1433, 9, '电话咨询', '9', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:18', '1', '2023-10-28 23:10:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1434, 10, '邮件咨询', '10', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:33', '1', '2023-10-28 23:10:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1435, 10, 'Gitee', '10', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:42', '1', '2023-11-04 13:04:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1436, 20, '钉钉', '20', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:04:54', '1', '2023-11-04 13:04:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1437, 30, '企业微信', '30', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:09', '1', '2023-11-04 13:05:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1438, 31, '微信公众平台', '31', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:18', '1', '2023-11-04 13:05:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1439, 32, '微信开放平台', '32', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:30', '1', '2023-11-04 13:05:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1440, 34, '微信小程序', '34', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1441, 1, '上架', '1', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:34', '1', '2023-10-30 21:49:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1442, 0, '下架', '0', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:13', '1', '2023-10-30 21:49:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1443, 15, '子表', '15', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-13 23:06:16', '1', '2023-11-13 23:06:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1444, 10, '主表（标准模式）', '10', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:32:49', '1', '2023-11-14 12:32:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1445, 11, '主表（ERP 模式）', '11', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:33:05', '1', '2023-11-14 12:33:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1446, 12, '主表（内嵌模式）', '12', 'infra_codegen_template_type', 0, '', '', '', '1', '2023-11-14 12:33:31', '1', '2023-11-14 12:33:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1447, 1, '负责人', '1', 'crm_permission_level', 0, 'default', '', '', '1', '2023-11-30 09:53:12', '1', '2023-11-30 09:53:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1448, 2, '只读', '2', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:29', '1', '2023-11-30 09:53:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1449, 3, '读写', '3', 'crm_permission_level', 0, '', '', '', '1', '2023-11-30 09:53:36', '1', '2023-11-30 09:53:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1450, 0, '未提交', '0', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:56:59', '1', '2023-11-30 18:56:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1451, 10, '审批中', '10', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:10', '1', '2023-11-30 18:57:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1452, 20, '审核通过', '20', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:24', '1', '2023-11-30 18:57:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1453, 30, '审核不通过', '30', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:32', '1', '2023-11-30 18:57:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1454, 40, '已取消', '40', 'crm_audit_status', 0, '', '', '', '1', '2023-11-30 18:57:42', '1', '2023-11-30 18:57:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1456, 1, '支票', '1', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:29', '1', '2023-10-18 21:54:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1457, 2, '现金', '2', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:41', '1', '2023-10-18 21:54:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1458, 3, '邮政汇款', '3', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:53', '1', '2023-10-18 21:54:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1459, 4, '电汇', '4', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:07', '1', '2023-10-18 21:55:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1461, 1, '个', '1', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:26', '1', '2023-12-05 23:02:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1462, 2, '块', '2', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:34', '1', '2023-12-05 23:02:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1463, 3, '只', '3', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:57', '1', '2023-12-05 23:02:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1464, 4, '把', '4', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:05', '1', '2023-12-05 23:03:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1465, 5, '枚', '5', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:14', '1', '2023-12-05 23:03:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1466, 6, '瓶', '6', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:20', '1', '2023-12-05 23:03:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1467, 7, '盒', '7', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:30', '1', '2023-12-05 23:03:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1468, 8, '台', '8', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:41', '1', '2023-12-05 23:03:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1469, 9, '吨', '9', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:03:48', '1', '2023-12-05 23:03:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1470, 10, '千克', '10', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:03', '1', '2023-12-05 23:04:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1471, 11, '米', '11', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:12', '1', '2023-12-05 23:04:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1472, 12, '箱', '12', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:25', '1', '2023-12-05 23:04:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1473, 13, '套', '13', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:04:34', '1', '2023-12-05 23:04:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1474, 1, '打电话', '1', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:20', '1', '2024-01-15 20:48:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1475, 2, '发短信', '2', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:48:31', '1', '2024-01-15 20:48:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1476, 3, '上门拜访', '3', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:07', '1', '2024-01-15 20:49:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1477, 4, '微信沟通', '4', 'crm_follow_up_type', 0, '', '', '', '1', '2024-01-15 20:49:15', '1', '2024-01-15 20:49:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1482, 4, '转账失败', '20', 'pay_transfer_status', 0, 'warning', '', '', '1', '2023-10-28 16:24:16', '1', '2025-05-08 12:59:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1483, 3, '转账成功', '10', 'pay_transfer_status', 0, 'success', '', '', '1', '2023-10-28 16:23:50', '1', '2025-05-08 12:58:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1484, 2, '转账进行中', '5', 'pay_transfer_status', 0, 'info', '', '', '1', '2023-10-28 16:23:12', '1', '2025-05-08 12:58:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1485, 1, '等待转账', '0', 'pay_transfer_status', 0, 'default', '', '', '1', '2023-10-28 16:21:43', '1', '2023-10-28 16:23:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1486, 10, '其它入库', '10', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:07:25', '1', '2024-02-05 18:07:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1487, 11, '其它入库（作废）', '11', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:08:07', '1', '2024-02-05 19:20:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1488, 20, '其它出库', '20', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:08:51', '1', '2024-02-05 18:08:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1489, 21, '其它出库（作废）', '21', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:09:00', '1', '2024-02-05 19:20:10', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1490, 10, '未审核', '10', 'erp_audit_status', 0, 'default', '', '', '1', '2024-02-06 00:00:21', '1', '2024-02-06 00:00:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1491, 20, '已审核', '20', 'erp_audit_status', 0, 'success', '', '', '1', '2024-02-06 00:00:35', '1', '2024-02-06 00:00:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1492, 30, '调拨入库', '30', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:19', '1', '2024-02-07 12:36:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1493, 31, '调拨入库（作废）', '31', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:29', '1', '2024-02-07 20:37:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1494, 32, '调拨出库', '32', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:38', '1', '2024-02-07 12:36:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1495, 33, '调拨出库（作废）', '33', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:49', '1', '2024-02-07 20:37:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1496, 40, '盘盈入库', '40', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:53:00', '1', '2024-02-08 08:53:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1497, 41, '盘盈入库（作废）', '41', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:53:39', '1', '2024-02-16 19:40:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1498, 42, '盘亏出库', '42', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:54:16', '1', '2024-02-08 08:54:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1499, 43, '盘亏出库（作废）', '43', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:54:31', '1', '2024-02-16 19:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1500, 50, '销售出库', '50', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-11 21:47:25', '1', '2024-02-11 21:50:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1501, 51, '销售出库（作废）', '51', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-11 21:47:37', '1', '2024-02-11 21:51:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1502, 60, '销售退货入库', '60', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-12 06:51:05', '1', '2024-02-12 06:51:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1503, 61, '销售退货入库（作废）', '61', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-12 06:51:18', '1', '2024-02-12 06:51:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1504, 70, '采购入库', '70', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:02', '1', '2024-02-16 13:10:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1505, 71, '采购入库（作废）', '71', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:10', '1', '2024-02-16 19:40:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1506, 80, '采购退货出库', '80', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:17', '1', '2024-02-16 13:10:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1507, 81, '采购退货出库（作废）', '81', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:26', '1', '2024-02-16 19:40:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1509, 3, '审批不通过', '3', 'bpm_process_instance_status', 0, 'danger', '', '', '1', '2024-03-16 16:12:06', '1', '2024-03-16 16:12:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1510, 4, '已取消', '4', 'bpm_process_instance_status', 0, 'warning', '', '', '1', '2024-03-16 16:12:22', '1', '2024-03-16 16:12:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1511, 5, '已退回', '5', 'bpm_task_status', 0, 'warning', '', '', '1', '2024-03-16 19:10:46', '1', '2024-03-08 22:41:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1512, 6, '委派中', '6', 'bpm_task_status', 0, 'primary', '', '', '1', '2024-03-17 10:06:22', '1', '2024-03-08 22:41:40', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1513, 7, '审批通过中', '7', 'bpm_task_status', 0, 'success', '', '', '1', '2024-03-17 10:06:47', '1', '2024-03-08 22:41:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1514, 0, '待审批', '0', 'bpm_task_status', 0, 'info', '', '', '1', '2024-03-17 10:07:11', '1', '2024-03-08 22:41:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1515, 35, '发起人自选', '35', 'bpm_task_candidate_strategy', 0, '', '', '', '1', '2024-03-22 19:45:16', '1', '2024-03-22 19:45:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1516, 1, '执行监听器', 'execution', 'bpm_process_listener_type', 0, 'primary', '', '', '1', '2024-03-23 12:54:03', '1', '2024-03-23 19:14:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1517, 1, '任务监听器', 'task', 'bpm_process_listener_type', 0, 'success', '', '', '1', '2024-03-23 12:54:13', '1', '2024-03-23 19:14:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1526, 1, 'Java 类', 'class', 'bpm_process_listener_value_type', 0, 'primary', '', '', '1', '2024-03-23 15:08:45', '1', '2024-03-23 19:14:32', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1527, 2, '表达式', 'expression', 'bpm_process_listener_value_type', 0, 'success', '', '', '1', '2024-03-23 15:09:06', '1', '2024-03-23 19:14:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1528, 3, '代理表达式', 'delegateExpression', 'bpm_process_listener_value_type', 0, 'info', '', '', '1', '2024-03-23 15:11:23', '1', '2024-03-23 19:14:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1529, 1, '天', '1', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:26', '1', '2024-03-29 22:50:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1530, 2, '周', '2', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:36', '1', '2024-03-29 22:50:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1531, 3, '月', '3', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:50:46', '1', '2024-03-29 22:50:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1532, 4, '季度', '4', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:01', '1', '2024-03-29 22:51:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1533, 5, '年', '5', 'date_interval', 0, '', '', '', '1', '2024-03-29 22:51:07', '1', '2024-03-29 22:51:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1534, 1, '赢单', '1', 'crm_business_end_status_type', 0, 'success', '', '', '1', '2024-04-13 23:26:57', '1', '2024-04-13 23:26:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1535, 2, '输单', '2', 'crm_business_end_status_type', 0, 'primary', '', '', '1', '2024-04-13 23:27:31', '1', '2024-04-13 23:27:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1536, 3, '无效', '3', 'crm_business_end_status_type', 0, 'info', '', '', '1', '2024-04-13 23:27:59', '1', '2024-04-13 23:27:59', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1537, 1, 'OpenAI', 'OpenAI', 'ai_platform', 0, '', '', '', '1', '2024-05-09 22:33:47', '1', '2024-05-09 22:58:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1538, 2, 'Ollama', 'Ollama', 'ai_platform', 0, '', '', '', '1', '2024-05-17 23:02:55', '1', '2024-05-17 23:02:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1539, 3, '文心一言', 'YiYan', 'ai_platform', 0, '', '', '', '1', '2024-05-18 09:24:20', '1', '2024-05-18 09:29:01', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1540, 4, '讯飞星火', 'XingHuo', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:08:56', '1', '2024-05-18 10:08:56', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1541, 5, '通义千问', 'TongYi', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:32:29', '1', '2024-07-06 15:42:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1542, 6, 'StableDiffusion', 'StableDiffusion', 'ai_platform', 0, '', '', '', '1', '2024-06-01 15:09:31', '1', '2024-06-01 15:10:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1543, 10, '进行中', '10', 'ai_image_status', 0, 'primary', '', '', '1', '2024-06-26 20:51:41', '1', '2024-06-26 20:52:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1544, 20, '已完成', '20', 'ai_image_status', 0, 'success', '', '', '1', '2024-06-26 20:52:07', '1', '2024-06-26 20:52:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1545, 30, '已失败', '30', 'ai_image_status', 0, 'warning', '', '', '1', '2024-06-26 20:52:25', '1', '2024-06-26 20:52:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1546, 7, 'Midjourney', 'Midjourney', 'ai_platform', 0, '', '', '', '1', '2024-06-26 22:14:46', '1', '2024-06-26 22:14:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1547, 10, '进行中', '10', 'ai_music_status', 0, 'primary', '', '', '1', '2024-06-27 22:45:22', '1', '2024-06-28 00:56:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1548, 20, '已完成', '20', 'ai_music_status', 0, 'success', '', '', '1', '2024-06-27 22:45:33', '1', '2024-06-28 00:56:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1549, 30, '已失败', '30', 'ai_music_status', 0, 'danger', '', '', '1', '2024-06-27 22:45:44', '1', '2024-06-28 00:56:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1550, 1, '歌词模式', '1', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:31', '1', '2024-06-28 01:22:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1554, 13, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2025-02-24 20:18:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1558, 7, '博客文章', '7', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:23', '1', '2024-07-07 15:50:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1559, 8, '想法', '8', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:31', '1', '2024-07-07 15:50:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1560, 9, '大纲', '9', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:37', '1', '2024-07-07 15:50:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1561, 1, '自动', '1', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:06', '1', '2024-07-07 15:51:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1562, 2, '友善', '2', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:19', '1', '2024-07-07 15:51:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1563, 3, '随意', '3', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:27', '1', '2024-07-07 15:51:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1564, 4, '友好', '4', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:37', '1', '2024-07-07 15:51:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1565, 5, '专业', '5', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:49', '1', '2024-07-07 15:52:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1566, 6, '诙谐', '6', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:15', '1', '2024-07-07 15:52:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1567, 7, '有趣', '7', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:24', '1', '2024-07-07 15:52:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1568, 8, '正式', '8', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:54:33', '1', '2024-07-07 15:54:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1569, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1570, 1, '自动', '1', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:34', '1', '2024-07-07 15:19:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1571, 2, '电子邮件', '2', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:50', '1', '2024-07-07 15:49:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1572, 3, '消息', '3', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:01', '1', '2024-07-07 15:49:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1573, 4, '评论', '4', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:13', '1', '2024-07-07 15:49:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1574, 1, '自动', '1', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:18', '1', '2024-07-07 15:44:18', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1575, 2, '中文', '2', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:28', '1', '2024-07-07 15:44:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1576, 3, '英文', '3', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:37', '1', '2024-07-07 15:44:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1577, 4, '韩语', '4', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:28', '1', '2024-07-07 15:46:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1578, 5, '日语', '5', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:44', '1', '2024-07-07 15:46:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1579, 1, '自动', '1', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:34', '1', '2024-07-07 15:48:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1580, 2, '短', '2', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:44', '1', '2024-07-07 15:48:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1581, 3, '中等', '3', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:52', '1', '2024-07-07 15:48:52', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1582, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1584, 1, '撰写', '1', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:00', '1', '2024-07-10 21:26:00', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1585, 2, '回复', '2', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:06', '1', '2024-07-10 21:26:06', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1586, 2, '腾讯云', 'TENCENT', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:16', '1', '2024-07-22 22:23:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1587, 3, '华为云', 'HUAWEI', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:46', '1', '2024-07-22 22:23:53', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1588, 1, 'OpenAI 微软', 'AzureOpenAI', 'ai_platform', 0, '', '', '', '1', '2024-08-10 14:07:41', '1', '2024-08-10 14:07:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1589, 10, 'BPMN 设计器', '10', 'bpm_model_type', 0, 'primary', '', '', '1', '2024-08-26 15:22:17', '1', '2024-08-26 16:46:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1590, 20, 'SIMPLE 设计器', '20', 'bpm_model_type', 0, 'success', '', '', '1', '2024-08-26 15:22:27', '1', '2024-08-26 16:45:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1591, 4, '七牛云', 'QINIU', 'system_sms_channel_code', 0, '', '', '', '1', '2024-08-31 08:45:03', '1', '2024-08-31 08:45:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1592, 3, '新人券', '3', 'promotion_coupon_take_type', 0, 'info', '', '新人注册后，自动发放', '1', '2024-09-03 11:57:16', '1', '2024-09-03 11:57:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1593, 5, '微信零钱', '5', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2024-10-13 11:06:48', '1', '2025-05-10 08:24:55', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', '2025-02-23 19:51:40', '1', '2025-02-23 19:52:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', '2025-02-23 20:58:04', '1', '2025-02-23 20:58:04', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', '2025-02-24 20:19:09', '1', '2025-02-24 20:19:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1686, 1, '聊天', '1', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:26:34', '1', '2025-03-03 12:26:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1687, 2, '图像', '2', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:23', '1', '2025-03-03 12:27:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1688, 3, '音频', '3', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:51', '1', '2025-03-03 12:27:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1689, 4, '视频', '4', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:03', '1', '2025-03-03 12:28:03', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1690, 5, '向量', '5', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:15', '1', '2025-03-03 12:28:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1693, 15, '月之暗面', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-11-24 07:17:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2025-03-17 09:28:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2025-03-17 09:28:28', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2025-03-17 09:28:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2025-03-17 09:28:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2025-03-17 09:28:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2025-03-17 09:28:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2010, 1, '移动网络', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2025-06-12 23:27:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2025-03-17 09:28:51', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2025-03-17 09:28:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-03-17 09:29:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-03-17 09:29:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-03-17 09:29:14', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2025-03-17 09:29:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2025-03-17 09:29:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2025-03-17 09:29:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2033, 4, 'PH值', 'pH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2034, 5, '土壤EC值', 'dS/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2035, 6, '太阳总辐射', 'W/㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2036, 7, '降雨量', 'mm/hour', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2037, 8, '乏', 'var', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2038, 9, '厘泊', 'cP', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2039, 10, '饱和度', 'aw', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2040, 11, '个', 'pcs', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:19', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2041, 12, '厘斯', 'cst', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2042, 13, '巴', 'bar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2043, 14, '纳克每升', 'ppt', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2044, 15, '微克每升', 'ppb', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2045, 16, '微西每厘米', 'uS/cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:34', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2046, 17, '牛顿每库仑', 'N/C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2047, 18, '伏特每米', 'V/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2048, 19, '滴速', 'ml/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2049, 20, '毫米汞柱', 'mmHg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2050, 21, '血糖', 'mmol/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:54', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2051, 22, '毫米每秒', 'mm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2052, 23, '转每分钟', 'turn/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2053, 24, '次', 'count', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2054, 25, '档', 'gear', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:11', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2055, 26, '步', 'stepCount', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:13', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2056, 27, '标准立方米每小时', 'Nm3/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2057, 28, '千伏', 'kV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2058, 29, '千伏安', 'kVA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2060, 30, '千乏', 'kVar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2061, 31, '微瓦每平方厘米', 'uw/cm2', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2062, 32, '只', '只', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2063, 33, '相对湿度', '%RH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2064, 34, '立方米每秒', 'm³/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2065, 35, '公斤每秒', 'kg/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2066, 36, '转每分钟', 'r/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2067, 37, '吨每小时', 't/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2068, 38, '千卡每小时', 'KCL/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2069, 39, '升每秒', 'L/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2070, 40, '兆帕', 'Mpa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2071, 41, '立方米每小时', 'm³/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2072, 42, '千乏时', 'kvarh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2073, 43, '微克每升', 'μg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2074, 44, '千卡路里', 'kcal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2075, 45, '吉字节', 'GB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2076, 46, '兆字节', 'MB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2077, 47, '千字节', 'KB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2078, 48, '字节', 'B', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2079, 49, '微克每平方分米每天', 'μg/(d㎡·d)', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2080, 50, '无', '', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2081, 51, '百万分率', 'ppm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2082, 52, '像素', 'pixel', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2083, 53, '照度', 'Lux', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2084, 54, '重力加速度', 'grav', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2085, 55, '分贝', 'dB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2086, 56, '百分比', '%', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2087, 57, '流明', 'lm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2088, 58, '比特', 'bit', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2089, 59, '克每毫升', 'g/mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2090, 60, '克每升', 'g/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2091, 61, '毫克每升', 'mg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2092, 62, '微克每立方米', 'μg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2093, 63, '毫克每立方米', 'mg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2094, 64, '克每立方米', 'g/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2095, 65, '千克每立方米', 'kg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2096, 66, '纳法', 'nF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2097, 67, '皮法', 'pF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2098, 68, '微法', 'μF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2099, 69, '法拉', 'F', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2100, 70, '欧姆', 'Ω', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2101, 71, '微安', 'μA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2102, 72, '毫安', 'mA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2103, 73, '千安', 'kA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2104, 74, '安培', 'A', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2105, 75, '毫伏', 'mV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2106, 76, '伏特', 'V', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2107, 77, '毫秒', 'ms', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2108, 78, '秒', 's', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2109, 79, '分钟', 'min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2110, 80, '小时', 'h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2111, 81, '日', 'day', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2112, 82, '周', 'week', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2113, 83, '月', 'month', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2114, 84, '年', 'year', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2115, 85, '节', 'kn', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2116, 86, '千米每小时', 'km/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2117, 87, '米每秒', 'm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2118, 88, '秒', '″', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2119, 89, '分', '′', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2120, 90, '度', '°', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2121, 91, '弧度', 'rad', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2122, 92, '赫兹', 'Hz', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2123, 93, '微瓦', 'μW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2124, 94, '毫瓦', 'mW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2125, 95, '千瓦特', 'kW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2126, 96, '瓦特', 'W', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2127, 97, '卡路里', 'cal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2128, 98, '千瓦时', 'kW·h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2129, 99, '瓦时', 'Wh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2130, 100, '电子伏', 'eV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2131, 101, '千焦', 'kJ', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2132, 102, '焦耳', 'J', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2133, 103, '华氏度', '℉', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2134, 104, '开尔文', 'K', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2135, 105, '吨', 't', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2136, 106, '摄氏度', '°C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2137, 107, '毫帕', 'mPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2138, 108, '百帕', 'hPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2139, 109, '千帕', 'kPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2140, 110, '帕斯卡', 'Pa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2141, 111, '毫克', 'mg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2142, 112, '克', 'g', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2143, 113, '千克', 'kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2144, 114, '牛', 'N', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2145, 115, '毫升', 'mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2146, 116, '升', 'L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2147, 117, '立方毫米', 'mm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2148, 118, '立方厘米', 'cm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2149, 119, '立方千米', 'km³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2150, 120, '立方米', 'm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2151, 121, '公顷', 'h㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2152, 122, '平方厘米', 'c㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2153, 123, '平方毫米', 'm㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2154, 124, '平方千米', 'k㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2155, 125, '平方米', '㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2156, 126, '纳米', 'nm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2157, 127, '微米', 'μm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2158, 128, '毫米', 'mm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2159, 129, '厘米', 'cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2165, 1, 'HTTP', '1', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:39:54', '1', '2025-06-24 12:44:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2166, 2, 'TCP', '2', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:40:06', '1', '2025-06-24 12:44:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2167, 3, 'WebSocket', '3', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:40:24', '1', '2025-06-24 12:44:45', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2168, 10, 'MQTT', '10', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:40:37', '1', '2025-06-24 12:44:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2169, 20, 'Database', '20', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:05', '1', '2025-06-24 12:44:44', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2170, 21, 'Redis Stream', '21', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:18', '1', '2025-06-24 12:44:43', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2171, 30, 'RocketMQ', '30', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:30', '1', '2025-06-24 12:44:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2172, 31, 'RabbitMQ', '31', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:47', '1', '2025-06-24 12:44:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2173, 32, 'Kafka', '32', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:59', '1', '2025-06-24 12:44:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2174, 1, '设备上下线变更', '1', 'iot_rule_scene_trigger_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:00:01', '\"1\"', '2025-07-06 10:28:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2175, 2, '物模型属性上报', '2', 'iot_rule_scene_trigger_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:00:09', '\"1\"', '2025-07-06 10:28:22', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2176, 1, '设备状态', 'state', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:24:58', '1', '2025-03-20 15:24:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2177, 2, '设备属性', 'property', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:09', '1', '2025-03-20 15:25:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2178, 3, '设备事件', 'event', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:23', '1', '2025-03-20 15:25:23', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2179, 4, '设备服务', 'service', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:39', '1', '2025-03-20 15:25:39', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2180, 5, '设备配置', 'config', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:51', '1', '2025-03-20 15:25:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2181, 6, '设备 OTA', 'ota', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:26:17', '1', '2025-03-20 15:26:17', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2182, 7, '设备注册', 'register', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:26:35', '1', '2025-03-20 15:26:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2183, 8, '设备拓扑', 'topology', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:26:46', '1', '2025-03-20 15:26:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2184, 1, '设备属性设置', '1', 'iot_rule_scene_action_type_enum', 0, 'primary', '', '', '1', '2025-03-28 15:27:12', '\"1\"', '2025-07-06 10:37:33', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2185, 2, '设备服务调用', '2', 'iot_rule_scene_action_type_enum', 0, 'primary', '', '', '1', '2025-03-28 15:27:25', '\"1\"', '2025-07-06 10:37:41', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2186, 100, '告警触发', '100', 'iot_rule_scene_action_type_enum', 0, 'primary', '', '', '1', '2025-03-28 15:27:35', '\"1\"', '2025-07-06 10:37:50', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3000, 16, '百川智能', 'BaiChuan', 'ai_platform', 0, '', '', '', '1', '2025-03-23 12:15:46', '1', '2025-03-23 12:15:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3001, 40, 'Vben5.0 Ant Design Schema 模版', '40', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-04-23 21:47:47', '1', '2025-09-04 23:25:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3002, 6, '支付宝余额', '6', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2025-05-10 08:24:49', '1', '2025-05-10 08:24:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3003, 1, 'Alink', 'Alink', 'iot_codec_type', 0, '', '', '阿里云 Alink', '1', '2025-06-12 22:56:06', '1', '2025-06-12 23:22:24', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3004, 3, 'WARN', '3', 'iot_alert_level', 0, 'warning', '', '', '1', '2025-06-27 20:32:22', '1', '2025-06-27 20:34:31', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3005, 1, 'INFO', '1', 'iot_alert_level', 0, 'primary', '', '', '1', '2025-06-27 20:33:28', '1', '2025-06-27 20:34:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3006, 5, 'ERROR', '5', 'iot_alert_level', 0, 'danger', '', '', '1', '2025-06-27 20:33:50', '1', '2025-06-27 20:33:50', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3007, 1, '短信', '1', 'iot_alert_receive_type', 0, '', '', '', '1', '2025-06-27 22:49:30', '1', '2025-06-27 22:49:30', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3008, 2, '邮箱', '2', 'iot_alert_receive_type', 0, '', '', '', '1', '2025-06-27 22:49:39', '1', '2025-06-27 22:50:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3009, 3, '站内信', '3', 'iot_alert_receive_type', 0, '', '', '', '1', '2025-06-27 22:50:20', '1', '2025-06-27 22:50:20', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3010, 1, '全部设备', '1', 'iot_ota_task_device_scope', 0, '', '', '', '1', '2025-07-02 09:43:09', '1', '2025-07-02 09:43:09', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3011, 2, '指定设备', '2', 'iot_ota_task_device_scope', 0, '', '', '', '1', '2025-07-02 09:43:15', '1', '2025-07-02 09:43:15', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3012, 10, '进行中', '10', 'iot_ota_task_status', 0, 'primary', '', '', '1', '2025-07-02 09:44:01', '\"1\"', '2025-07-02 09:44:21', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3013, 20, '已结束', '20', 'iot_ota_task_status', 0, 'success', '', '', '1', '2025-07-02 09:44:14', '\"1\"', '2025-07-02 23:56:12', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3014, 30, '已取消', '30', 'iot_ota_task_status', 0, 'danger', '', '', '1', '2025-07-02 09:44:36', '1', '2025-07-02 09:44:36', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3015, 0, '待推送', '0', 'iot_ota_task_record_status', 0, '', '', '', '1', '2025-07-02 09:45:16', '1', '2025-07-02 09:45:16', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3016, 10, '已推送', '10', 'iot_ota_task_record_status', 0, '', '', '', '1', '2025-07-02 09:45:25', '1', '2025-07-02 09:45:25', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3017, 20, '升级中', '20', 'iot_ota_task_record_status', 0, 'primary', '', '', '1', '2025-07-02 09:45:37', '1', '2025-07-02 09:45:37', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3018, 30, '升级成功', '30', 'iot_ota_task_record_status', 0, 'success', '', '', '1', '2025-07-02 09:45:47', '1', '2025-07-02 09:45:47', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3019, 40, '升级失败', '40', 'iot_ota_task_record_status', 0, 'danger', '', '', '1', '2025-07-02 09:46:02', '1', '2025-07-02 09:46:02', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3020, 50, '升级取消', '50', 'iot_ota_task_record_status', 0, 'warning', '', '', '1', '2025-07-02 09:46:09', '\"1\"', '2025-07-02 09:46:27', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3021, 1, 'IP 定位', '1', 'iot_location_type', 0, '', '', '', '1', '2025-07-05 09:56:46', '1', '2025-07-05 09:56:46', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3022, 2, '设备上报', '2', 'iot_location_type', 0, '', '', '', '1', '2025-07-05 09:56:57', '1', '2025-07-05 09:56:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3023, 3, '手动定位', '3', 'iot_location_type', 0, '', '', '', '1', '2025-07-05 09:57:05', '1', '2025-07-05 09:57:05', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3024, 3, '设备事件上报', '3', 'iot_rule_scene_trigger_type_enum', 0, '', '', '', '1', '2025-07-06 10:28:29', '1', '2025-07-06 10:28:29', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3025, 4, '设备服务调用', '4', 'iot_rule_scene_trigger_type_enum', 0, '', '', '', '1', '2025-07-06 10:28:35', '1', '2025-07-06 10:28:35', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3026, 100, '定时触发', '100', 'iot_rule_scene_trigger_type_enum', 0, '', '', '', '1', '2025-07-06 10:28:48', '1', '2025-07-06 10:28:48', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3027, 101, '告警恢复', '101', 'iot_rule_scene_action_type_enum', 0, '', '', '', '1', '2025-07-06 10:37:57', '1', '2025-07-06 10:37:57', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3028, 2, 'Anthropic', 'Anthropic', 'ai_platform', 0, '', '', '', '1', '2025-08-21 22:54:24', '1', '2025-08-21 22:57:58', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3029, 2, '谷歌 Gemini', 'Gemini', 'ai_platform', 0, '', '', '', '1', '2025-08-22 22:39:35', '1', '2025-08-22 22:44:49', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3030, 1, '文件系统', 'filesystem', 'ai_mcp_client_name', 0, '', '', '', '1', '2025-08-28 13:58:43', '1', '2025-08-28 21:19:42', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3031, 41, 'Vben5.0 Ant Design 标准模版', '41', 'infra_codegen_front_type', 0, '', '', '', '1', '2025-09-04 23:26:07', '1', '2025-09-04 23:26:07', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3032, 50, 'Vben5.0 Element Plus Schema 模版', '50', 'infra_codegen_front_type', 0, '', '', '', '1', '2025-09-04 23:26:38', '1', '2025-09-04 23:26:38', '0');\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3033, 51, 'Vben5.0 Element Plus 标准模版', '51', 'infra_codegen_front_type', 0, '', '', '', '1', '2025-09-04 23:26:49', '1', '2025-09-04 23:26:49', '0');\nINSERT 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');\nINSERT 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');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_dict_data_seq;\nCREATE SEQUENCE system_dict_data_seq\n    START 3036;\n\n-- ----------------------------\n-- Table structure for system_dict_type\n-- ----------------------------\nDROP TABLE IF EXISTS system_dict_type;\nCREATE TABLE system_dict_type (\n    id int8 NOT NULL,\n  name varchar(100) NOT NULL DEFAULT '',\n  type varchar(100) NOT NULL DEFAULT '',\n  status int2 NOT NULL DEFAULT 0,\n  remark varchar(500) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  deleted_time timestamp NULL DEFAULT NULL\n);\n\nALTER TABLE system_dict_type ADD CONSTRAINT pk_system_dict_type PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_dict_type.id IS '字典主键';\nCOMMENT ON COLUMN system_dict_type.name IS '字典名称';\nCOMMENT ON COLUMN system_dict_type.type IS '字典类型';\nCOMMENT ON COLUMN system_dict_type.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_dict_type.remark IS '备注';\nCOMMENT ON COLUMN system_dict_type.creator IS '创建者';\nCOMMENT ON COLUMN system_dict_type.create_time IS '创建时间';\nCOMMENT ON COLUMN system_dict_type.updater IS '更新者';\nCOMMENT ON COLUMN system_dict_type.update_time IS '更新时间';\nCOMMENT ON COLUMN system_dict_type.deleted IS '是否删除';\nCOMMENT ON COLUMN system_dict_type.deleted_time IS '删除时间';\nCOMMENT ON TABLE system_dict_type IS '字典类型表';\n\n-- ----------------------------\n-- Records of system_dict_type\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1, '用户性别', 'system_user_sex', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2022-05-16 20:29:32', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (6, '参数类型', 'infra_config_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:36:54', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (7, '通知类型', 'system_notice_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:35:26', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (9, '操作类型', 'infra_operate_type', 0, NULL, 'admin', '2021-01-05 17:03:48', '1', '2024-03-14 12:44:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (10, '系统状态', 'common_status', 0, NULL, 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:21:28', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (11, 'Boolean 是否类型', 'infra_boolean_string', 0, 'boolean 转是否', '', '2021-01-19 03:20:08', '', '2022-02-01 16:37:10', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (104, '登陆结果', 'system_login_result', 0, '登陆结果', '', '2021-01-18 06:17:11', '', '2022-02-01 16:36:00', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (106, '代码生成模板类型', 'infra_codegen_template_type', 0, NULL, '', '2021-02-05 07:08:06', '1', '2022-05-16 20:26:50', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (107, '定时任务状态', 'infra_job_status', 0, NULL, '', '2021-02-07 07:44:16', '', '2022-02-01 16:51:11', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (108, '定时任务日志状态', 'infra_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2022-02-01 16:50:43', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (110, 'API 异常数据的处理状态', 'infra_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2022-02-01 16:50:53', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (111, '短信渠道编码', 'system_sms_channel_code', 0, NULL, '1', '2021-04-05 01:04:50', '1', '2022-02-16 02:09:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (112, '短信模板的类型', 'system_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:43', '1', '2022-02-01 16:35:06', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (113, '短信发送状态', 'system_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:03', '1', '2022-02-01 16:35:09', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (114, '短信接收状态', 'system_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:14', '1', '2022-02-01 16:35:14', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (116, '登陆日志的类型', 'system_login_type', 0, '登陆日志的类型', '1', '2021-10-06 00:50:46', '1', '2022-02-01 16:35:56', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (117, 'OA 请假类型', 'bpm_oa_leave_type', 0, NULL, '1', '2021-09-21 22:34:33', '1', '2022-01-22 10:41:37', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (130, '支付渠道编码类型', 'pay_channel_code', 0, '支付渠道的编码', '1', '2021-12-03 10:35:08', '1', '2023-07-10 10:11:39', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (131, '支付回调状态', 'pay_notify_status', 0, '支付回调状态（包括退款回调）', '1', '2021-12-03 10:53:29', '1', '2023-07-19 18:09:43', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (132, '支付订单状态', 'pay_order_status', 0, '支付订单状态', '1', '2021-12-03 11:17:50', '1', '2021-12-03 11:17:50', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (134, '退款订单状态', 'pay_refund_status', 0, '退款订单状态', '1', '2021-12-10 16:42:50', '1', '2023-07-19 10:13:17', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (139, '流程实例的状态', 'bpm_process_instance_status', 0, '流程实例的状态', '1', '2022-01-07 23:46:42', '1', '2022-01-07 23:46:42', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (140, '流程实例的结果', 'bpm_task_status', 0, '流程实例的结果', '1', '2022-01-07 23:48:10', '1', '2024-03-08 22:42:03', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (141, '流程的表单类型', 'bpm_model_form_type', 0, '流程的表单类型', '103', '2022-01-11 23:50:45', '103', '2022-01-11 23:50:45', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (142, '任务分配规则的类型', 'bpm_task_candidate_strategy', 0, 'BPM 任务的候选人的策略', '103', '2022-01-12 23:21:04', '103', '2024-03-06 02:53:59', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (144, '代码生成的场景枚举', 'infra_codegen_scene', 0, '代码生成的场景枚举', '1', '2022-02-02 13:14:45', '1', '2022-03-10 16:33:46', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (145, '角色类型', 'system_role_type', 0, '角色类型', '1', '2022-02-16 13:01:46', '1', '2022-02-16 13:01:46', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (146, '文件存储器', 'infra_file_storage', 0, '文件存储器', '1', '2022-03-15 00:24:38', '1', '2022-03-15 00:24:38', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (147, 'OAuth 2.0 授权类型', 'system_oauth2_grant_type', 0, 'OAuth 2.0 授权类型（模式）', '1', '2022-05-12 00:20:52', '1', '2022-05-11 16:25:49', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (149, '商品 SPU 状态', 'product_spu_status', 0, '商品 SPU 状态', '1', '2022-10-24 21:19:04', '1', '2022-10-24 21:19:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (150, '优惠类型', 'promotion_discount_type', 0, '优惠类型', '1', '2022-11-01 12:46:06', '1', '2022-11-01 12:46:06', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (151, '优惠劵模板的有限期类型', 'promotion_coupon_template_validity_type', 0, '优惠劵模板的有限期类型', '1', '2022-11-02 00:06:20', '1', '2022-11-04 00:08:26', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (152, '营销的商品范围', 'promotion_product_scope', 0, '营销的商品范围', '1', '2022-11-02 00:28:01', '1', '2022-11-02 00:28:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (153, '优惠劵的状态', 'promotion_coupon_status', 0, '优惠劵的状态', '1', '2022-11-04 00:14:49', '1', '2022-11-04 00:14:49', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (154, '优惠劵的领取方式', 'promotion_coupon_take_type', 0, '优惠劵的领取方式', '1', '2022-11-04 19:12:27', '1', '2022-11-04 19:12:27', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (155, '促销活动的状态', 'promotion_activity_status', 0, '促销活动的状态', '1', '2022-11-04 22:54:23', '1', '2022-11-04 22:54:23', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (156, '营销的条件类型', 'promotion_condition_type', 0, '营销的条件类型', '1', '2022-11-04 22:59:23', '1', '2022-11-04 22:59:23', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (157, '交易售后状态', 'trade_after_sale_status', 0, '交易售后状态', '1', '2022-11-19 20:52:56', '1', '2022-11-19 20:52:56', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (158, '交易售后的类型', 'trade_after_sale_type', 0, '交易售后的类型', '1', '2022-11-19 21:04:09', '1', '2022-11-19 21:04:09', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (159, '交易售后的方式', 'trade_after_sale_way', 0, '交易售后的方式', '1', '2022-11-19 21:39:04', '1', '2022-11-19 21:39:04', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (160, '终端', 'terminal', 0, '终端', '1', '2022-12-10 10:50:50', '1', '2022-12-10 10:53:11', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (161, '交易订单的类型', 'trade_order_type', 0, '交易订单的类型', '1', '2022-12-10 16:33:54', '1', '2022-12-10 16:33:54', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (162, '交易订单的状态', 'trade_order_status', 0, '交易订单的状态', '1', '2022-12-10 16:48:44', '1', '2022-12-10 16:48:44', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (163, '交易订单项的售后状态', 'trade_order_item_after_sale_status', 0, '交易订单项的售后状态', '1', '2022-12-10 20:58:08', '1', '2022-12-10 20:58:08', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (164, '公众号自动回复的请求关键字匹配模式', 'mp_auto_reply_request_match', 0, '公众号自动回复的请求关键字匹配模式', '1', '2023-01-16 23:29:56', '1', '2023-01-16 23:29:56', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (165, '公众号的消息类型', 'mp_message_type', 0, '公众号的消息类型', '1', '2023-01-17 22:17:09', '1', '2023-01-17 22:17:09', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (166, '邮件发送状态', 'system_mail_send_status', 0, '邮件发送状态', '1', '2023-01-26 09:53:13', '1', '2023-01-26 09:53:13', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (167, '站内信模版的类型', 'system_notify_template_type', 0, '站内信模版的类型', '1', '2023-01-28 10:35:10', '1', '2023-01-28 10:35:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (168, '代码生成的前端类型', 'infra_codegen_front_type', 0, '', '1', '2023-04-12 23:57:52', '1', '2023-04-12 23:57:52', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (170, '快递计费方式', 'trade_delivery_express_charge_mode', 0, '用于商城交易模块配送管理', '1', '2023-05-21 22:45:03', '1', '2023-05-21 22:45:03', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (171, '积分业务类型', 'member_point_biz_type', 0, '', '1', '2023-06-10 12:15:00', '1', '2023-06-28 13:48:20', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (173, '支付通知类型', 'pay_notify_type', 0, NULL, '1', '2023-07-20 12:23:03', '1', '2023-07-20 12:23:03', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (174, '会员经验业务类型', 'member_experience_biz_type', 0, NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (175, '交易配送类型', 'trade_delivery_type', 0, '', '1', '2023-08-23 00:03:14', '1', '2023-08-23 00:03:14', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (176, '分佣模式', 'brokerage_enabled_condition', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (177, '分销关系绑定模式', 'brokerage_bind_mode', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (178, '佣金提现类型', 'brokerage_withdraw_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (179, '佣金记录业务类型', 'brokerage_record_biz_type', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (180, '佣金记录状态', 'brokerage_record_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (181, '佣金提现状态', 'brokerage_withdraw_status', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (182, '佣金提现银行', 'brokerage_bank_name', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (183, '砍价记录的状态', 'promotion_bargain_record_status', 0, '', '1', '2023-10-05 10:41:08', '1', '2023-10-05 10:41:08', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (184, '拼团记录的状态', 'promotion_combination_record_status', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-10-08 07:24:25', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (185, '回款-回款方式', 'crm_receivable_return_type', 0, '回款-回款方式', '1', '2023-10-18 21:54:10', '1', '2023-10-18 21:54:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (186, 'CRM 客户行业', 'crm_customer_industry', 0, 'CRM 客户所属行业', '1', '2023-10-28 22:57:07', '1', '2024-02-18 23:30:22', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (187, '客户等级', 'crm_customer_level', 0, 'CRM 客户等级', '1', '2023-10-28 22:59:12', '1', '2023-10-28 15:11:16', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (188, '客户来源', 'crm_customer_source', 0, 'CRM 客户来源', '1', '2023-10-28 23:00:34', '1', '2023-10-28 15:11:16', '0', NULL);\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (600, 'Banner 位置', 'promotion_banner_position', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-11-04 13:04:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (601, '社交类型', 'system_social_type', 0, '', '1', '2023-11-04 13:03:54', '1', '2023-11-04 13:03:54', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (604, '产品状态', 'crm_product_status', 0, '', '1', '2023-10-30 21:47:59', '1', '2023-10-30 21:48:45', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (605, 'CRM 数据权限的级别', 'crm_permission_level', 0, '', '1', '2023-11-30 09:51:59', '1', '2023-11-30 09:51:59', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (606, 'CRM 审批状态', 'crm_audit_status', 0, '', '1', '2023-11-30 18:56:23', '1', '2023-11-30 18:56:23', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (607, 'CRM 产品单位', 'crm_product_unit', 0, '', '1', '2023-12-05 23:01:51', '1', '2023-12-05 23:01:51', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (608, 'CRM 跟进方式', 'crm_follow_up_type', 0, '', '1', '2024-01-15 20:48:05', '1', '2024-01-15 20:48:05', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (610, '转账订单状态', 'pay_transfer_status', 0, '', '1', '2023-10-28 16:18:32', '1', '2023-10-28 16:18:32', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (611, 'ERP 库存明细的业务类型', 'erp_stock_record_biz_type', 0, 'ERP 库存明细的业务类型', '1', '2024-02-05 18:07:02', '1', '2024-02-05 18:07:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (612, 'ERP 审批状态', 'erp_audit_status', 0, '', '1', '2024-02-06 00:00:07', '1', '2024-02-06 00:00:07', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (613, 'BPM 监听器类型', 'bpm_process_listener_type', 0, '', '1', '2024-03-23 12:52:24', '1', '2024-03-09 15:54:28', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (615, 'BPM 监听器值类型', 'bpm_process_listener_value_type', 0, '', '1', '2024-03-23 13:00:31', '1', '2024-03-23 13:00:31', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (616, '时间间隔', 'date_interval', 0, '', '1', '2024-03-29 22:50:09', '1', '2024-03-29 22:50:09', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (619, 'CRM 商机结束状态类型', 'crm_business_end_status_type', 0, '', '1', '2024-04-13 23:23:00', '1', '2024-04-13 23:23:00', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (620, 'AI 模型平台', 'ai_platform', 0, '', '1', '2024-05-09 22:27:38', '1', '2024-05-09 22:27:38', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (621, 'AI 绘画状态', 'ai_image_status', 0, '', '1', '2024-06-26 20:51:23', '1', '2024-06-26 20:51:23', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (622, 'AI 音乐状态', 'ai_music_status', 0, '', '1', '2024-06-27 22:45:07', '1', '2024-06-28 00:56:27', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (623, 'AI 音乐生成模式', 'ai_generate_mode', 0, '', '1', '2024-06-27 22:46:21', '1', '2024-06-28 01:22:29', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (624, '写作语气', 'ai_write_tone', 0, '', '1', '2024-07-07 15:19:02', '1', '2024-07-07 15:19:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (625, '写作语言', 'ai_write_language', 0, '', '1', '2024-07-07 15:18:52', '1', '2024-07-07 15:18:52', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (626, '写作长度', 'ai_write_length', 0, '', '1', '2024-07-07 15:18:41', '1', '2024-07-07 15:18:41', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (627, '写作格式', 'ai_write_format', 0, '', '1', '2024-07-07 15:14:34', '1', '2024-07-07 15:14:34', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1001, 'IoT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2025-03-17 09:25:08', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1002, 'IoT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2025-03-17 09:25:10', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1004, 'IoT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2025-03-17 09:25:14', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1006, 'IoT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-03-17 09:25:19', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1007, 'IoT 物模型功能类型', 'iot_thing_model_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2025-03-17 09:25:24', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1011, 'IoT 物模型单位', 'iot_thing_model_unit', 0, '', '1', '2024-12-25 17:36:46', '1', '2025-03-17 09:25:35', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1013, 'IoT 数据流转目的的类型枚举', 'iot_data_sink_type_enum', 0, '', '1', '2025-03-09 12:39:36', '1', '2025-06-24 12:45:24', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1014, 'IoT 场景流转的触发类型枚举', 'iot_rule_scene_trigger_type_enum', 0, '', '1', '2025-03-20 14:59:44', '1', '2025-03-20 14:59:44', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1015, 'IoT 设备消息类型枚举', 'iot_device_message_type_enum', 0, '', '1', '2025-03-20 15:01:15', '1', '2025-03-20 15:01:15', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1016, 'IoT 规则场景的触发类型枚举', 'iot_rule_scene_action_type_enum', 0, '', '1', '2025-03-28 15:26:54', '1', '2025-03-28 15:29:13', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (2000, 'IoT 数据格式', 'iot_codec_type', 0, 'IoT 编解码器类型', '1', '2025-06-12 22:55:46', '1', '2025-06-12 22:55:46', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (2001, 'IoT 告警级别', 'iot_alert_level', 0, '', '1', '2025-06-27 20:30:57', '1', '2025-06-27 20:30:57', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (2002, 'IoT 告警', 'iot_alert_receive_type', 0, '', '1', '2025-06-27 22:49:19', '1', '2025-06-27 22:49:19', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (2003, 'IoT 固件设备范围', 'iot_ota_task_device_scope', 0, '', '1', '2025-07-02 09:42:49', '1', '2025-07-02 09:42:49', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (2004, 'IoT 固件升级任务状态', 'iot_ota_task_status', 0, '', '1', '2025-07-02 09:43:43', '1', '2025-07-02 09:43:43', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (2005, 'IoT 固件升级记录状态', 'iot_ota_task_record_status', 0, '', '1', '2025-07-02 09:45:02', '1', '2025-07-02 09:45:02', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (2006, 'IoT 定位类型', 'iot_location_type', 0, '', '1', '2025-07-05 09:56:25', '1', '2025-07-05 09:56:25', '0', '1970-01-01 00:00:00');\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (2007, 'AI MCP 客户端名字', 'ai_mcp_client_name', 0, '', '1', '2025-08-28 13:57:40', '1', '2025-08-28 13:57:40', '0', '1970-01-01 00:00:00');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_dict_type_seq;\nCREATE SEQUENCE system_dict_type_seq\n    START 2008;\n\n-- ----------------------------\n-- Table structure for system_login_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_login_log;\nCREATE TABLE system_login_log (\n    id int8 NOT NULL,\n  log_type int8 NOT NULL,\n  trace_id varchar(64) NOT NULL DEFAULT '',\n  user_id int8 NOT NULL DEFAULT 0,\n  user_type int2 NOT NULL DEFAULT 0,\n  username varchar(50) NOT NULL DEFAULT '',\n  result int2 NOT NULL,\n  user_ip varchar(50) NOT NULL,\n  user_agent varchar(512) NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_login_log ADD CONSTRAINT pk_system_login_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_login_log.id IS '访问ID';\nCOMMENT ON COLUMN system_login_log.log_type IS '日志类型';\nCOMMENT ON COLUMN system_login_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_login_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_login_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_login_log.username IS '用户账号';\nCOMMENT ON COLUMN system_login_log.result IS '登陆结果';\nCOMMENT ON COLUMN system_login_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_login_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_login_log.creator IS '创建者';\nCOMMENT ON COLUMN system_login_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_login_log.updater IS '更新者';\nCOMMENT ON COLUMN system_login_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_login_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_login_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_login_log IS '系统访问记录';\n\nDROP SEQUENCE IF EXISTS system_login_log_seq;\nCREATE SEQUENCE system_login_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_mail_account\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_account;\nCREATE TABLE system_mail_account (\n    id int8 NOT NULL,\n  mail varchar(255) NOT NULL,\n  username varchar(255) NOT NULL,\n  password varchar(255) NOT NULL,\n  host varchar(255) NOT NULL,\n  port int4 NOT NULL,\n  ssl_enable bool NOT NULL DEFAULT '0',\n  starttls_enable bool NOT NULL DEFAULT '0',\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_mail_account ADD CONSTRAINT pk_system_mail_account PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_account.id IS '主键';\nCOMMENT ON COLUMN system_mail_account.mail IS '邮箱';\nCOMMENT ON COLUMN system_mail_account.username IS '用户名';\nCOMMENT ON COLUMN system_mail_account.password IS '密码';\nCOMMENT ON COLUMN system_mail_account.host IS 'SMTP 服务器域名';\nCOMMENT ON COLUMN system_mail_account.port IS 'SMTP 服务器端口';\nCOMMENT ON COLUMN system_mail_account.ssl_enable IS '是否开启 SSL';\nCOMMENT ON COLUMN system_mail_account.starttls_enable IS '是否开启 STARTTLS';\nCOMMENT ON COLUMN system_mail_account.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_account.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_account.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_account.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_account.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_account IS '邮箱账号表';\n\n-- ----------------------------\n-- Records of system_mail_account\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, '0', '0', '1', '2023-01-25 17:39:52', '1', '2025-04-04 16:34:40', '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, '1', '0', '1', '2023-01-26 01:26:03', '1', '2025-07-26 21:57:55', '0');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, '0', '0', '1', '2023-01-27 15:06:38', '1', '2023-01-27 07:08:36', '1');\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (4, '7685413x@qq.com', '2', '3', '4', 5, '1', '0', '1', '2023-04-12 23:05:06', '1', '2023-04-12 15:05:11', '1');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_mail_account_seq;\nCREATE SEQUENCE system_mail_account_seq\n    START 5;\n\n-- ----------------------------\n-- Table structure for system_mail_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_log;\nCREATE TABLE system_mail_log (\n    id int8 NOT NULL,\n  user_id int8 NULL DEFAULT NULL,\n  user_type int2 NULL DEFAULT NULL,\n  to_mails varchar(1024) NOT NULL,\n  cc_mails varchar(1024) NULL DEFAULT NULL,\n  bcc_mails varchar(1024) NULL DEFAULT NULL,\n  account_id int8 NOT NULL,\n  from_mail varchar(255) NOT NULL,\n  template_id int8 NOT NULL,\n  template_code varchar(63) NOT NULL,\n  template_nickname varchar(255) NULL DEFAULT NULL,\n  template_title varchar(255) NOT NULL,\n  template_content text NOT NULL,\n  template_params varchar(255) NOT NULL,\n  send_status int2 NOT NULL DEFAULT 0,\n  send_time timestamp NULL DEFAULT NULL,\n  send_message_id varchar(255) NULL DEFAULT NULL,\n  send_exception varchar(4096) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_mail_log ADD CONSTRAINT pk_system_mail_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_log.id IS '编号';\nCOMMENT ON COLUMN system_mail_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_mail_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_mail_log.to_mails IS '接收邮箱地址';\nCOMMENT ON COLUMN system_mail_log.cc_mails IS '抄送邮箱地址';\nCOMMENT ON COLUMN system_mail_log.bcc_mails IS '密送邮箱地址';\nCOMMENT ON COLUMN system_mail_log.account_id IS '邮箱账号编号';\nCOMMENT ON COLUMN system_mail_log.from_mail IS '发送邮箱地址';\nCOMMENT ON COLUMN system_mail_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_mail_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_mail_log.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_mail_log.template_title IS '邮件标题';\nCOMMENT ON COLUMN system_mail_log.template_content IS '邮件内容';\nCOMMENT ON COLUMN system_mail_log.template_params IS '邮件参数';\nCOMMENT ON COLUMN system_mail_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_mail_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_mail_log.send_message_id IS '发送返回的消息 ID';\nCOMMENT ON COLUMN system_mail_log.send_exception IS '发送异常';\nCOMMENT ON COLUMN system_mail_log.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_log.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_log IS '邮件日志表';\n\nDROP SEQUENCE IF EXISTS system_mail_log_seq;\nCREATE SEQUENCE system_mail_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_mail_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_template;\nCREATE TABLE system_mail_template (\n    id int8 NOT NULL,\n  name varchar(63) NOT NULL,\n  code varchar(63) NOT NULL,\n  account_id int8 NOT NULL,\n  nickname varchar(255) NULL DEFAULT NULL,\n  title varchar(255) NOT NULL,\n  content varchar(10240) NOT NULL,\n  params varchar(255) NOT NULL,\n  status int2 NOT NULL,\n  remark varchar(255) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_mail_template ADD CONSTRAINT pk_system_mail_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_mail_template.id IS '编号';\nCOMMENT ON COLUMN system_mail_template.name IS '模板名称';\nCOMMENT ON COLUMN system_mail_template.code IS '模板编码';\nCOMMENT ON COLUMN system_mail_template.account_id IS '发送的邮箱账号编号';\nCOMMENT ON COLUMN system_mail_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_mail_template.title IS '模板标题';\nCOMMENT ON COLUMN system_mail_template.content IS '模板内容';\nCOMMENT ON COLUMN system_mail_template.params IS '参数数组';\nCOMMENT ON COLUMN system_mail_template.status IS '开启状态';\nCOMMENT ON COLUMN system_mail_template.remark IS '备注';\nCOMMENT ON COLUMN system_mail_template.creator IS '创建者';\nCOMMENT ON COLUMN system_mail_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_mail_template.updater IS '更新者';\nCOMMENT ON COLUMN system_mail_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_mail_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_mail_template IS '邮件模版表';\n\n-- ----------------------------\n-- Records of system_mail_template\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (13, '后台用户短信登录', 'admin-sms-login', 1, '奥特曼', '你猜我猜', '<p>您的验证码是{code}，名字是{name}</p>', '[\"code\",\"name\"]', 0, '3', '1', '2021-10-11 08:10:00', '1', '2023-12-02 19:51:14', '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (14, '测试模版', 'test_01', 2, '芋艿', '一个标题', '<p>你是 {key01} 吗？</p><p><br></p><p>是的话，赶紧 {key02} 一下！</p>', '[\"key01\",\"key02\"]', 0, NULL, '1', '2023-01-26 01:27:40', '1', '2025-07-26 21:48:45', '0');\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (15, '3', '2', 2, '7', '4', '<p>45</p>', '[]', 1, '80', '1', '2023-01-27 15:50:35', '1', '2025-07-26 21:47:49', '1');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_mail_template_seq;\nCREATE SEQUENCE system_mail_template_seq\n    START 16;\n\n-- ----------------------------\n-- Table structure for system_menu\n-- ----------------------------\nDROP TABLE IF EXISTS system_menu;\nCREATE TABLE system_menu (\n    id int8 NOT NULL,\n  name varchar(50) NOT NULL,\n  permission varchar(100) NOT NULL DEFAULT '',\n  type int2 NOT NULL,\n  sort int4 NOT NULL DEFAULT 0,\n  parent_id int8 NOT NULL DEFAULT 0,\n  path varchar(200) NULL DEFAULT '',\n  icon varchar(100) NULL DEFAULT '#',\n  component varchar(255) NULL DEFAULT NULL,\n  component_name varchar(255) NULL DEFAULT NULL,\n  status int2 NOT NULL DEFAULT 0,\n  visible bool NOT NULL DEFAULT '1',\n  keep_alive bool NOT NULL DEFAULT '1',\n  always_show bool NOT NULL DEFAULT '1',\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_menu ADD CONSTRAINT pk_system_menu PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_menu.id IS '菜单ID';\nCOMMENT ON COLUMN system_menu.name IS '菜单名称';\nCOMMENT ON COLUMN system_menu.permission IS '权限标识';\nCOMMENT ON COLUMN system_menu.type IS '菜单类型';\nCOMMENT ON COLUMN system_menu.sort IS '显示顺序';\nCOMMENT ON COLUMN system_menu.parent_id IS '父菜单ID';\nCOMMENT ON COLUMN system_menu.path IS '路由地址';\nCOMMENT ON COLUMN system_menu.icon IS '菜单图标';\nCOMMENT ON COLUMN system_menu.component IS '组件路径';\nCOMMENT ON COLUMN system_menu.component_name IS '组件名';\nCOMMENT ON COLUMN system_menu.status IS '菜单状态';\nCOMMENT ON COLUMN system_menu.visible IS '是否可见';\nCOMMENT ON COLUMN system_menu.keep_alive IS '是否缓存';\nCOMMENT ON COLUMN system_menu.always_show IS '是否总是显示';\nCOMMENT ON COLUMN system_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_menu.deleted IS '是否删除';\nCOMMENT ON TABLE system_menu IS '菜单权限表';\n\n-- ----------------------------\n-- Records of system_menu\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-03-01 08:28:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5, 'OA 示例', '', 1, 40, 1185, 'oa', 'fa:road', NULL, NULL, 0, '1', '1', '1', 'admin', '2021-09-20 16:26:19', '1', '2024-02-29 12:38:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (101, '角色管理', '', 2, 2, 1, 'role', 'ep:user', 'system/role/index', 'SystemRole', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-05-01 18:35:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (102, '菜单管理', '', 2, 3, 1, 'menu', 'ep:menu', 'system/menu/index', 'SystemMenu', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:03:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (103, '部门管理', '', 2, 4, 1, 'dept', 'fa:address-card', 'system/dept/index', 'SystemDept', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (104, '岗位管理', '', 2, 5, 1, 'post', 'fa:address-book-o', 'system/post/index', 'SystemPost', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (105, '字典管理', '', 2, 6, 1, 'dict', 'ep:collection', 'system/dict/index', 'SystemDictType', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:07:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (106, '配置管理', '', 2, 8, 2, 'config', 'fa:connectdevelop', 'infra/config/index', 'InfraConfig', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:02:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (107, '通知公告', '', 2, 4, 2739, 'notice', 'ep:takeaway-box', 'system/notice/index', 'SystemNotice', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-22 23:56:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (108, '审计日志', '', 1, 9, 1, 'log', 'ep:document-copy', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:08:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (109, '令牌管理', '', 2, 2, 1261, 'token', 'fa:key', 'system/oauth2/token/index', 'SystemTokenClient', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:13:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (110, '定时任务', '', 2, 7, 2, 'job', 'fa-solid:tasks', 'infra/job/index', 'InfraJob', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:57:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (111, 'MySQL 监控', '', 2, 1, 2740, 'druid', 'fa-solid:box', 'infra/druid/index', 'InfraDruid', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:05:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (112, 'Java 监控', '', 2, 3, 2740, 'admin-server', 'ep:coffee-cup', 'infra/server/index', 'InfraAdminServer', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (113, 'Redis 监控', '', 2, 2, 2740, 'redis', 'fa:reddit-square', 'infra/redis/index', 'InfraRedis', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:06:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (114, '表单构建', 'infra:build:list', 2, 2, 2, 'build', 'fa:wpforms', 'infra/build/index', 'InfraBuild', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (115, '代码生成', 'infra:codegen:query', 2, 1, 2, 'codegen', 'ep:document-copy', 'infra/codegen/index', 'InfraCodegen', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 08:51:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (116, 'API 接口', 'infra:swagger:list', 2, 3, 2, 'swagger', 'fa:fighter-jet', 'infra/swagger/index', 'InfraSwagger', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-04-23 00:01:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (500, '操作日志', '', 2, 1, 108, 'operate-log', 'ep:position', 'system/operatelog/index', 'SystemOperateLog', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:09:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (501, '登录日志', '', 2, 2, 108, 'login-log', 'ep:promotion', 'system/loginlog/index', 'SystemLoginLog', 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:10:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1001, '用户查询', 'system:user:query', 3, 1, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1002, '用户新增', 'system:user:create', 3, 2, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1003, '用户修改', 'system:user:update', 3, 3, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1004, '用户删除', 'system:user:delete', 3, 4, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1005, '用户导出', 'system:user:export', 3, 5, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1006, '用户导入', 'system:user:import', 3, 6, 100, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1007, '重置密码', 'system:user:update-password', 3, 7, 100, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1008, '角色查询', 'system:role:query', 3, 1, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1009, '角色新增', 'system:role:create', 3, 2, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1010, '角色修改', 'system:role:update', 3, 3, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1011, '角色删除', 'system:role:delete', 3, 4, 101, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1012, '角色导出', 'system:role:export', 3, 5, 101, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1013, '菜单查询', 'system:menu:query', 3, 1, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1014, '菜单新增', 'system:menu:create', 3, 2, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1015, '菜单修改', 'system:menu:update', 3, 3, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1016, '菜单删除', 'system:menu:delete', 3, 4, 102, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1017, '部门查询', 'system:dept:query', 3, 1, 103, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1018, '部门新增', 'system:dept:create', 3, 2, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1019, '部门修改', 'system:dept:update', 3, 3, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1020, '部门删除', 'system:dept:delete', 3, 4, 103, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1021, '岗位查询', 'system:post:query', 3, 1, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1022, '岗位新增', 'system:post:create', 3, 2, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1023, '岗位修改', 'system:post:update', 3, 3, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1024, '岗位删除', 'system:post:delete', 3, 4, 104, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1025, '岗位导出', 'system:post:export', 3, 5, 104, '', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1026, '字典查询', 'system:dict:query', 3, 1, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1027, '字典新增', 'system:dict:create', 3, 2, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1028, '字典修改', 'system:dict:update', 3, 3, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1029, '字典删除', 'system:dict:delete', 3, 4, 105, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1030, '字典导出', 'system:dict:export', 3, 5, 105, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1031, '配置查询', 'infra:config:query', 3, 1, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1032, '配置新增', 'infra:config:create', 3, 2, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1033, '配置修改', 'infra:config:update', 3, 3, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1034, '配置删除', 'infra:config:delete', 3, 4, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1035, '配置导出', 'infra:config:export', 3, 5, 106, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1036, '公告查询', 'system:notice:query', 3, 1, 107, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1037, '公告新增', 'system:notice:create', 3, 2, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1038, '公告修改', 'system:notice:update', 3, 3, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1039, '公告删除', 'system:notice:delete', 3, 4, 107, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1040, '操作查询', 'system:operate-log:query', 3, 1, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1042, '日志导出', 'system:operate-log:export', 3, 2, 500, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1043, '登录查询', 'system:login-log:query', 3, 1, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1045, '日志导出', 'system:login-log:export', 3, 3, 501, '#', '#', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1046, '令牌列表', 'system:oauth2-token:page', 3, 1, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1048, '令牌删除', 'system:oauth2-token:delete', 3, 2, 109, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-05-09 23:54:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1050, '任务新增', 'infra:job:create', 3, 2, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1051, '任务修改', 'infra:job:update', 3, 3, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1052, '任务删除', 'infra:job:delete', 3, 4, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1053, '状态修改', 'infra:job:update', 3, 5, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1054, '任务导出', 'infra:job:export', 3, 7, 110, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1056, '生成修改', 'infra:codegen:update', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1057, '生成删除', 'infra:codegen:delete', 3, 3, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1058, '导入代码', 'infra:codegen:create', 3, 2, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1059, '预览代码', 'infra:codegen:preview', 3, 4, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1060, '生成代码', 'infra:codegen:download', 3, 5, 115, '', '', '', NULL, 0, '1', '1', '1', 'admin', '2021-01-05 17:03:48', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1063, '设置角色菜单权限', 'system:permission:assign-role-menu', 3, 6, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-06 17:53:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1064, '设置角色数据权限', 'system:permission:assign-role-data-scope', 3, 7, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-06 17:56:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1065, '设置用户角色', 'system:permission:assign-user-role', 3, 8, 101, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-07 10:23:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1066, '获得 Redis 监控信息', 'infra:redis:get-monitor-info', 3, 1, 113, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-26 01:02:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1067, '获得 Redis Key 列表', 'infra:redis:get-key-list', 3, 2, 113, '', '', '', NULL, 0, '1', '1', '1', '', '2021-01-26 01:02:52', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1070, '代码生成案例', '', 1, 1, 2, 'demo', 'ep:aim', 'infra/testDemo/index', NULL, 0, '1', '1', '1', '', '2021-02-06 12:42:49', '1', '2023-11-15 23:45:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1075, '任务触发', 'infra:job:trigger', 3, 8, 110, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-07 13:03:10', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1077, '链路追踪', '', 2, 4, 2740, 'skywalking', 'fa:eye', 'infra/skywalking/index', 'InfraSkyWalking', 0, '1', '1', '1', '', '2021-02-08 20:41:31', '1', '2024-04-23 00:07:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1078, '访问日志', '', 2, 1, 1083, 'api-access-log', 'ep:place', 'infra/apiAccessLog/index', 'InfraApiAccessLog', 0, '1', '1', '1', '', '2021-02-26 01:32:59', '1', '2024-02-29 08:54:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1082, '日志导出', 'infra:api-access-log:export', 3, 2, 1078, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 01:32:59', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1083, 'API 日志', '', 2, 4, 2, 'log', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '', '2021-02-26 02:18:24', '1', '2024-04-22 23:58:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1084, '错误日志', 'infra:api-error-log:query', 2, 2, 1083, 'api-error-log', 'ep:warning-filled', 'infra/apiErrorLog/index', 'InfraApiErrorLog', 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2024-02-29 08:55:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1085, '日志处理', 'infra:api-error-log:update-status', 3, 2, 1084, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1086, '日志导出', 'infra:api-error-log:export', 3, 3, 1084, '', '', '', NULL, 0, '1', '1', '1', '', '2021-02-26 07:53:20', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1087, '任务查询', 'infra:job:query', 3, 1, 110, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:26:19', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1088, '日志查询', 'infra:api-access-log:query', 3, 1, 1078, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:28:04', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1089, '日志查询', 'infra:api-error-log:query', 3, 1, 1084, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-03-10 01:29:09', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1090, '文件列表', '', 2, 5, 1243, 'file', 'ep:upload-filled', 'infra/file/index', 'InfraFile', 0, '1', '1', '1', '', '2021-03-12 20:16:20', '1', '2024-02-29 08:53:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', NULL, 0, '1', '1', '1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1092, '文件删除', 'infra:file:delete', 3, 4, 1090, '', '', '', NULL, 0, '1', '1', '1', '', '2021-03-12 20:16:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1093, '短信管理', '', 1, 1, 2739, 'sms', 'ep:message', NULL, NULL, 0, '1', '1', '1', '1', '2021-04-05 01:10:16', '1', '2024-04-22 23:56:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1094, '短信渠道', '', 2, 0, 1093, 'sms-channel', 'fa:stack-exchange', 'system/sms/channel/index', 'SystemSmsChannel', 0, '1', '1', '1', '', '2021-04-01 11:07:15', '1', '2024-02-29 01:15:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1095, '短信渠道查询', 'system:sms-channel:query', 3, 1, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1096, '短信渠道创建', 'system:sms-channel:create', 3, 2, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1097, '短信渠道更新', 'system:sms-channel:update', 3, 3, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1098, '短信渠道删除', 'system:sms-channel:delete', 3, 4, 1094, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 11:07:15', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1100, '短信模板', '', 2, 1, 1093, 'sms-template', 'ep:connection', 'system/sms/template/index', 'SystemSmsTemplate', 0, '1', '1', '1', '', '2021-04-01 17:35:17', '1', '2024-02-29 01:16:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1101, '短信模板查询', 'system:sms-template:query', 3, 1, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1102, '短信模板创建', 'system:sms-template:create', 3, 2, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1103, '短信模板更新', 'system:sms-template:update', 3, 3, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1104, '短信模板删除', 'system:sms-template:delete', 3, 4, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1105, '短信模板导出', 'system:sms-template:export', 3, 5, 1100, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-01 17:35:17', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1106, '发送测试短信', 'system:sms-template:send-sms', 3, 6, 1100, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-04-11 00:26:40', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1107, '短信日志', '', 2, 2, 1093, 'sms-log', 'fa:edit', 'system/sms/log/index', 'SystemSmsLog', 0, '1', '1', '1', '', '2021-04-11 08:37:05', '1', '2024-02-29 08:49:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1108, '短信日志查询', 'system:sms-log:query', 3, 1, 1107, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1109, '短信日志导出', 'system:sms-log:export', 3, 5, 1107, '', '', '', NULL, 0, '1', '1', '1', '', '2021-04-11 08:37:05', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1117, '支付管理', '', 1, 30, 0, '/pay', 'ep:money', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-25 16:43:41', '1', '2024-02-29 08:58:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1118, '请假查询', '', 2, 0, 5, 'leave', 'fa:leanpub', 'bpm/oa/leave/index', 'BpmOALeave', 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2024-02-29 12:38:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1119, '请假申请查询', 'bpm:oa-leave:query', 3, 1, 1118, '', '', '', NULL, 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1120, '请假申请创建', 'bpm:oa-leave:create', 3, 2, 1118, '', '', '', NULL, 0, '1', '1', '1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1126, '应用信息', '', 2, 1, 1117, 'app', 'fa:apple', 'pay/app/index', 'PayApp', 0, '1', '1', '1', '', '2021-11-10 01:13:30', '1', '2024-02-29 08:59:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1127, '支付应用信息查询', 'pay:app:query', 3, 1, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1128, '支付应用信息创建', 'pay:app:create', 3, 2, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1129, '支付应用信息更新', 'pay:app:update', 3, 3, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1130, '支付应用信息删除', 'pay:app:delete', 3, 4, 1126, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1132, '秘钥解析', 'pay:channel:parsing', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1133, '支付商户信息查询', 'pay:merchant:query', 3, 1, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1134, '支付商户信息创建', 'pay:merchant:create', 3, 2, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1135, '支付商户信息更新', 'pay:merchant:update', 3, 3, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1136, '支付商户信息删除', 'pay:merchant:delete', 3, 4, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1137, '支付商户信息导出', 'pay:merchant:export', 3, 5, 1132, '', '', '', NULL, 0, '1', '1', '1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1138, '租户列表', '', 2, 0, 1224, 'list', 'ep:house', 'system/tenant/index', 'SystemTenant', 0, '1', '1', '1', '', '2021-12-14 12:31:43', '1', '2024-02-29 01:01:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1139, '租户查询', 'system:tenant:query', 3, 1, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1140, '租户创建', 'system:tenant:create', 3, 2, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1141, '租户更新', 'system:tenant:update', 3, 3, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1142, '租户删除', 'system:tenant:delete', 3, 4, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1143, '租户导出', 'system:tenant:export', 3, 5, 1138, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-14 12:31:44', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1150, '秘钥解析', '', 3, 6, 1129, '', '', '', NULL, 0, '1', '1', '1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1161, '退款订单', '', 2, 3, 1117, 'refund', 'fa:registered', 'pay/refund/index', 'PayRefund', 0, '1', '1', '1', '', '2021-12-25 08:29:07', '1', '2024-02-29 08:59:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1162, '退款订单查询', 'pay:refund:query', 3, 1, 1161, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1166, '退款订单导出', 'pay:refund:export', 3, 5, 1161, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:29:07', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1173, '支付订单', '', 2, 2, 1117, 'order', 'fa:cc-paypal', 'pay/order/index', 'PayOrder', 0, '1', '1', '1', '', '2021-12-25 08:49:43', '1', '2024-02-29 08:59:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1174, '支付订单查询', 'pay:order:query', 3, 1, 1173, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1178, '支付订单导出', 'pay:order:export', 3, 5, 1173, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1185, '工作流程', '', 1, 50, 0, '/bpm', 'fa:medium', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-30 20:26:36', '1', '2024-02-29 12:43:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1186, '流程管理', '', 1, 10, 1185, 'manager', 'fa:dedent', NULL, NULL, 0, '1', '1', '1', '1', '2021-12-30 20:28:30', '1', '2024-02-29 12:36:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1187, '流程表单', '', 2, 2, 1186, 'form', 'fa:hdd-o', 'bpm/form/index', 'BpmForm', 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2024-03-19 12:25:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1188, '表单查询', 'bpm:form:query', 3, 1, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1189, '表单创建', 'bpm:form:create', 3, 2, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1190, '表单更新', 'bpm:form:update', 3, 3, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1191, '表单删除', 'bpm:form:delete', 3, 4, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1192, '表单导出', 'bpm:form:export', 3, 5, 1187, '', '', '', NULL, 0, '1', '1', '1', '', '2021-12-30 12:38:22', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1193, '流程模型', '', 2, 1, 1186, 'model', 'fa-solid:project-diagram', 'bpm/model/index', 'BpmModel', 0, '1', '1', '1', '1', '2021-12-31 23:24:58', '1', '2024-03-19 12:25:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1194, '模型查询', 'bpm:model:query', 3, 1, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:01:10', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1195, '模型创建', 'bpm:model:create', 3, 2, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:01:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1197, '模型更新', 'bpm:model:update', 3, 4, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:02:28', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1198, '模型删除', 'bpm:model:delete', 3, 5, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:02:43', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1199, '模型发布', 'bpm:model:deploy', 3, 6, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-03 19:03:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1200, '审批中心', '', 2, 20, 1185, 'task', 'fa:tasks', NULL, NULL, 0, '1', '1', '1', '1', '2022-01-07 23:51:48', '1', '2024-03-21 00:33:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1201, '我的流程', '', 2, 1, 1200, 'my', 'fa-solid:book', 'bpm/processInstance/index', 'BpmProcessInstanceMy', 0, '1', '1', '1', '', '2022-01-07 15:53:44', '1', '2024-03-21 23:52:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1202, '流程实例的查询', 'bpm:process-instance:query', 3, 1, 1201, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-07 15:53:44', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1207, '待办任务', '', 2, 10, 1200, 'todo', 'fa:slack', 'bpm/task/todo/index', 'BpmTodoTask', 0, '1', '1', '1', '1', '2022-01-08 10:33:37', '1', '2024-02-29 12:37:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1208, '已办任务', '', 2, 20, 1200, 'done', 'fa:delicious', 'bpm/task/done/index', 'BpmDoneTask', 0, '1', '1', '1', '1', '2022-01-08 10:34:13', '1', '2024-02-29 12:37:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1209, '用户分组', '', 2, 4, 1186, 'user-group', 'fa:user-secret', 'bpm/group/index', 'BpmUserGroup', 0, '1', '1', '1', '', '2022-01-14 02:14:20', '1', '2024-03-21 23:55:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1210, '用户组查询', 'bpm:user-group:query', 3, 1, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1211, '用户组创建', 'bpm:user-group:create', 3, 2, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1212, '用户组更新', 'bpm:user-group:update', 3, 3, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1213, '用户组删除', 'bpm:user-group:delete', 3, 4, 1209, '', '', '', NULL, 0, '1', '1', '1', '', '2022-01-14 02:14:20', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1215, '流程定义查询', 'bpm:process-definition:query', 3, 10, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:21:43', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1216, '流程任务分配规则查询', 'bpm:task-assign-rule:query', 3, 20, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:26:53', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1217, '流程任务分配规则创建', 'bpm:task-assign-rule:create', 3, 21, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:28:15', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1218, '流程任务分配规则更新', 'bpm:task-assign-rule:update', 3, 22, 1193, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:28:41', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1219, '流程实例的创建', 'bpm:process-instance:create', 3, 2, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:36:15', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1220, '流程实例的取消', 'bpm:process-instance:cancel', 3, 3, 1201, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:36:33', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1221, '流程任务的查询', 'bpm:task:query', 3, 1, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:38:52', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1222, '流程任务的更新', 'bpm:task:update', 3, 2, 1207, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-01-23 00:39:24', '1', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1224, '租户管理', '', 2, 0, 1, 'tenant', 'fa-solid:house-user', NULL, NULL, 0, '1', '1', '1', '1', '2022-02-20 01:41:13', '1', '2024-02-29 00:59:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1225, '租户套餐', '', 2, 0, 1224, 'package', 'fa:bars', 'system/tenantPackage/index', 'SystemTenantPackage', 0, '1', '1', '1', '', '2022-02-19 17:44:06', '1', '2024-02-29 01:01:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1226, '租户套餐查询', 'system:tenant-package:query', 3, 1, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1227, '租户套餐创建', 'system:tenant-package:create', 3, 2, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1228, '租户套餐更新', 'system:tenant-package:update', 3, 3, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1229, '租户套餐删除', 'system:tenant-package:delete', 3, 4, 1225, '', '', '', NULL, 0, '1', '1', '1', '', '2022-02-19 17:44:06', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1237, '文件配置', '', 2, 0, 1243, 'file-config', 'fa-solid:file-signature', 'infra/fileConfig/index', 'InfraFileConfig', 0, '1', '1', '1', '', '2022-03-15 14:35:28', '1', '2024-02-29 08:52:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1238, '文件配置查询', 'infra:file-config:query', 3, 1, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1239, '文件配置创建', 'infra:file-config:create', 3, 2, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1240, '文件配置更新', 'infra:file-config:update', 3, 3, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, '1', '1', '1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, '1', '1', '1', '1', '2022-03-16 23:47:40', '1', '2024-04-23 00:02:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, '1', '1', '1', '1', '2022-04-23 01:03:15', '1', '2025-04-29 17:45:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, '1', '1', '1', '', '2022-04-27 14:37:32', '1', '2024-02-29 08:51:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1258, '数据源配置更新', 'infra:data-source-config:update', 3, 3, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1259, '数据源配置删除', 'infra:data-source-config:delete', 3, 4, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1260, '数据源配置导出', 'infra:data-source-config:export', 3, 5, 1255, '', '', '', NULL, 0, '1', '1', '1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1261, 'OAuth 2.0', '', 2, 10, 1, 'oauth2', 'fa:dashcube', NULL, NULL, 0, '1', '1', '1', '1', '2022-05-09 23:38:17', '1', '2024-02-29 01:12:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1263, '应用管理', '', 2, 0, 1261, 'oauth2/application', 'fa:hdd-o', 'system/oauth2/client/index', 'SystemOAuth2Client', 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2024-02-29 01:13:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1264, '客户端查询', 'system:oauth2-client:query', 3, 1, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1265, '客户端创建', 'system:oauth2-client:create', 3, 2, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1266, '客户端更新', 'system:oauth2-client:update', 3, 3, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1267, '客户端删除', 'system:oauth2-client:delete', 3, 4, 1263, '', '', '', NULL, 0, '1', '1', '1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1281, '报表管理', '', 2, 40, 0, '/report', 'ep:pie-chart', NULL, NULL, 0, '1', '1', '1', '1', '2022-07-10 20:22:15', '1', '2024-02-29 12:33:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1282, '报表设计器', '', 2, 1, 1281, 'jimu-report', 'ep:trend-charts', 'report/jmreport/index', 'JimuReport', 0, '1', '1', '1', '1', '2022-07-10 20:26:36', '1', '2025-05-03 09:57:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2000, '商品中心', '', 1, 60, 2362, 'product', 'fa:product-hunt', NULL, NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '1', '2023-09-30 11:52:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2002, '商品分类', '', 2, 2, 2000, 'category', 'ep:cellphone', 'mall/product/category/index', 'ProductCategory', 0, '1', '1', '1', '', '2022-07-29 15:53:53', '1', '2023-08-21 10:27:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2003, '分类查询', 'product:category:query', 3, 1, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2004, '分类创建', 'product:category:create', 3, 2, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2005, '分类更新', 'product:category:update', 3, 3, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2006, '分类删除', 'product:category:delete', 3, 4, 2002, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2008, '商品品牌', '', 2, 3, 2000, 'brand', 'ep:chicken', 'mall/product/brand/index', 'ProductBrand', 0, '1', '1', '1', '', '2022-07-30 13:52:44', '1', '2023-08-21 10:27:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2009, '品牌查询', 'product:brand:query', 3, 1, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2010, '品牌创建', 'product:brand:create', 3, 2, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2011, '品牌更新', 'product:brand:update', 3, 3, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2012, '品牌删除', 'product:brand:delete', 3, 4, 2008, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2014, '商品列表', '', 2, 1, 2000, 'spu', 'ep:apple', 'mall/product/spu/index', 'ProductSpu', 0, '1', '1', '1', '', '2022-07-30 14:22:58', '1', '2025-10-08 10:36:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2015, '商品查询', 'product:spu:query', 3, 1, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2016, '商品创建', 'product:spu:create', 3, 2, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2017, '商品更新', 'product:spu:update', 3, 3, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2018, '商品删除', 'product:spu:delete', 3, 4, 2014, '', '', '', NULL, 0, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2019, '商品属性', '', 2, 4, 2000, 'property', 'ep:cold-drink', 'mall/product/property/index', 'ProductProperty', 0, '1', '1', '1', '', '2022-08-01 14:55:35', '1', '2023-08-26 11:01:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2020, '规格查询', 'product:property:query', 3, 1, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2021, '规格创建', 'product:property:create', 3, 2, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2022, '规格更新', 'product:property:update', 3, 3, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2023, '规格删除', 'product:property:delete', 3, 4, 2019, '', '', '', NULL, 0, '1', '1', '1', '', '2022-08-01 14:55:35', '', '2022-12-12 20:26:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2025, 'Banner', '', 2, 100, 2387, 'banner', 'fa:bandcamp', 'mall/promotion/banner/index', NULL, 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2026, 'Banner查询', 'promotion:banner:query', 3, 1, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2027, 'Banner创建', 'promotion:banner:create', 3, 2, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2028, 'Banner更新', 'promotion:banner:update', 3, 3, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2029, 'Banner删除', 'promotion:banner:delete', 3, 4, 2025, '', '', '', '', 0, '1', '1', '1', '', '2022-08-01 14:56:14', '1', '2023-10-24 20:20:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2030, '营销中心', '', 1, 70, 2362, 'promotion', 'ep:present', NULL, NULL, 0, '1', '1', '1', '1', '2022-10-31 21:25:09', '1', '2023-09-30 11:54:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2032, '优惠劵列表', '', 2, 1, 2365, 'template', 'ep:discount', 'mall/promotion/coupon/template/index', 'PromotionCouponTemplate', 0, '1', '1', '1', '', '2022-10-31 22:27:14', '1', '2023-10-03 12:40:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2033, '优惠劵模板查询', 'promotion:coupon-template:query', 3, 1, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2034, '优惠劵模板创建', 'promotion:coupon-template:create', 3, 2, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2035, '优惠劵模板更新', 'promotion:coupon-template:update', 3, 3, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2036, '优惠劵模板删除', 'promotion:coupon-template:delete', 3, 4, 2032, '', '', '', NULL, 0, '1', '1', '1', '', '2022-10-31 22:27:14', '', '2022-10-31 22:27:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2038, '领取记录', '', 2, 2, 2365, 'list', 'ep:collection-tag', 'mall/promotion/coupon/index', 'PromotionCoupon', 0, '1', '1', '1', '', '2022-11-03 23:21:31', '1', '2023-10-03 12:55:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2039, '优惠劵查询', 'promotion:coupon:query', 3, 1, 2038, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2040, '优惠劵删除', 'promotion:coupon:delete', 3, 4, 2038, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-03 23:21:31', '', '2022-11-03 23:21:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2041, '满减送', '', 2, 10, 2390, 'reward-activity', 'ep:goblet-square-full', 'mall/promotion/rewardActivity/index', 'PromotionRewardActivity', 0, '1', '1', '1', '', '2022-11-04 23:47:49', '1', '2023-10-21 19:24:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2042, '满减送活动查询', 'promotion:reward-activity:query', 3, 1, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2043, '满减送活动创建', 'promotion:reward-activity:create', 3, 2, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:49', '', '2022-11-04 23:47:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2044, '满减送活动更新', 'promotion:reward-activity:update', 3, 3, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2045, '满减送活动删除', 'promotion:reward-activity:delete', 3, 4, 2041, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-04 23:47:50', '', '2022-11-04 23:47:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2046, '满减送活动关闭', 'promotion:reward-activity:close', 3, 5, 2041, '', '', '', NULL, 0, '1', '1', '1', '1', '2022-11-05 10:42:53', '1', '2022-11-05 10:42:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2047, '限时折扣', '', 2, 7, 2390, 'discount-activity', 'ep:timer', 'mall/promotion/discountActivity/index', 'PromotionDiscountActivity', 0, '1', '1', '1', '', '2022-11-05 17:12:15', '1', '2023-10-21 19:24:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2048, '限时折扣活动查询', 'promotion:discount-activity:query', 3, 1, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2049, '限时折扣活动创建', 'promotion:discount-activity:create', 3, 2, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2050, '限时折扣活动更新', 'promotion:discount-activity:update', 3, 3, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2051, '限时折扣活动删除', 'promotion:discount-activity:delete', 3, 4, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2052, '限时折扣活动关闭', 'promotion:discount-activity:close', 3, 5, 2047, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2059, '秒杀商品', '', 2, 2, 2209, 'activity', 'ep:basketball', 'mall/promotion/seckill/activity/index', 'PromotionSeckillActivity', 0, '1', '1', '1', '', '2022-11-06 22:24:49', '1', '2023-06-24 18:57:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2060, '秒杀活动查询', 'promotion:seckill-activity:query', 3, 1, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2061, '秒杀活动创建', 'promotion:seckill-activity:create', 3, 2, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2062, '秒杀活动更新', 'promotion:seckill-activity:update', 3, 3, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2063, '秒杀活动删除', 'promotion:seckill-activity:delete', 3, 4, 2059, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2066, '秒杀时段', '', 2, 1, 2209, 'config', 'ep:baseball', 'mall/promotion/seckill/config/index', 'PromotionSeckillConfig', 0, '1', '1', '1', '', '2022-11-15 19:46:50', '1', '2023-06-24 18:57:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2067, '秒杀时段查询', 'promotion:seckill-config:query', 3, 1, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2068, '秒杀时段创建', 'promotion:seckill-config:create', 3, 2, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:48:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2069, '秒杀时段更新', 'promotion:seckill-config:update', 3, 3, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2070, '秒杀时段删除', 'promotion:seckill-config:delete', 3, 4, 2066, '', '', '', '', 0, '1', '1', '1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2072, '订单中心', '', 1, 65, 2362, 'trade', 'ep:eleme', NULL, NULL, 0, '1', '1', '1', '1', '2022-11-19 18:57:19', '1', '2023-09-30 11:54:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2073, '售后退款', '', 2, 2, 2072, 'after-sale', 'ep:refrigerator', 'mall/trade/afterSale/index', 'TradeAfterSale', 0, '1', '1', '1', '', '2022-11-19 20:15:32', '1', '2023-10-01 21:42:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2074, '售后查询', 'trade:after-sale:query', 3, 1, 2073, '', '', '', NULL, 0, '1', '1', '1', '', '2022-11-19 20:15:33', '1', '2022-12-10 21:04:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2075, '秒杀活动关闭', 'promotion:seckill-activity:close', 3, 5, 2059, '', '', '', '', 0, '1', '1', '1', '1', '2022-11-28 20:20:15', '1', '2023-10-03 18:34:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2076, '订单列表', '', 2, 1, 2072, 'order', 'ep:list', 'mall/trade/order/index', 'TradeOrder', 0, '1', '1', '1', '1', '2022-12-10 21:05:44', '1', '2023-10-01 21:42:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2083, '地区管理', '', 2, 14, 1, 'area', 'fa:map-marker', 'system/area/index', 'SystemArea', 0, '1', '1', '1', '1', '2022-12-23 17:35:05', '1', '2024-02-29 08:50:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2084, '公众号管理', '', 1, 100, 0, '/mp', 'ep:compass', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-01 20:11:04', '1', '2024-02-29 12:39:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2085, '账号管理', '', 2, 1, 2084, 'account', 'fa:user', 'mp/account/index', 'MpAccount', 0, '1', '1', '1', '1', '2023-01-01 20:13:31', '1', '2024-02-29 12:42:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2086, '新增账号', 'mp:account:create', 3, 1, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-01 20:21:40', '1', '2023-01-07 17:32:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2087, '修改账号', 'mp:account:update', 3, 2, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:32:46', '1', '2023-01-07 17:32:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2088, '查询账号', 'mp:account:query', 3, 0, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:07', '1', '2023-01-07 17:33:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2089, '删除账号', 'mp:account:delete', 3, 3, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:21', '1', '2023-01-07 17:33:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2090, '生成二维码', 'mp:account:qr-code', 3, 4, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 17:33:58', '1', '2023-01-07 17:33:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2091, '清空 API 配额', 'mp:account:clear-quota', 3, 5, 2085, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-07 18:20:32', '1', '2023-01-07 18:20:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2092, '数据统计', 'mp:statistics:query', 2, 2, 2084, 'statistics', 'ep:trend-charts', 'mp/statistics/index', 'MpStatistics', 0, '1', '1', '1', '1', '2023-01-07 20:17:36', '1', '2024-02-29 12:42:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2093, '标签管理', '', 2, 3, 2084, 'tag', 'ep:collection-tag', 'mp/tag/index', 'MpTag', 0, '1', '1', '1', '1', '2023-01-08 11:37:32', '1', '2024-02-29 12:42:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2094, '查询标签', 'mp:tag:query', 3, 0, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:03', '1', '2023-01-08 11:59:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2095, '新增标签', 'mp:tag:create', 3, 1, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:23', '1', '2023-01-08 11:59:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2096, '修改标签', 'mp:tag:update', 3, 2, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 11:59:41', '1', '2023-01-08 11:59:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2097, '删除标签', 'mp:tag:delete', 3, 3, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 12:00:04', '1', '2023-01-08 12:00:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2098, '同步标签', 'mp:tag:sync', 3, 4, 2093, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 12:00:29', '1', '2023-01-08 12:00:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2099, '粉丝管理', '', 2, 4, 2084, 'user', 'fa:user-secret', 'mp/user/index', 'MpUser', 0, '1', '1', '1', '1', '2023-01-08 16:51:20', '1', '2024-02-29 12:42:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2100, '查询粉丝', 'mp:user:query', 3, 0, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:16:59', '1', '2023-01-08 17:17:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2101, '修改粉丝', 'mp:user:update', 3, 1, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:17:11', '1', '2023-01-08 17:17:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2102, '同步粉丝', 'mp:user:sync', 3, 2, 2099, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-08 17:17:40', '1', '2023-01-08 17:17:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2103, '消息管理', '', 2, 5, 2084, 'message', 'ep:message', 'mp/message/index', 'MpMessage', 0, '1', '1', '1', '1', '2023-01-08 18:44:19', '1', '2024-02-29 12:42:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2104, '图文发表记录', '', 2, 10, 2084, 'free-publish', 'ep:edit-pen', 'mp/freePublish/index', 'MpFreePublish', 0, '1', '1', '1', '1', '2023-01-13 00:30:50', '1', '2024-02-29 12:43:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2105, '查询发布列表', 'mp:free-publish:query', 3, 1, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:19:17', '1', '2023-01-13 07:19:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2106, '发布草稿', 'mp:free-publish:submit', 3, 2, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:19:46', '1', '2023-01-13 07:19:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2107, '删除发布记录', 'mp:free-publish:delete', 3, 3, 2104, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 07:20:01', '1', '2023-01-13 07:20:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2108, '图文草稿箱', '', 2, 9, 2084, 'draft', 'ep:edit', 'mp/draft/index', 'MpDraft', 0, '1', '1', '1', '1', '2023-01-13 07:40:21', '1', '2024-02-29 12:43:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2109, '新建草稿', 'mp:draft:create', 3, 1, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-13 23:15:30', '1', '2023-01-13 23:15:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2110, '修改草稿', 'mp:draft:update', 3, 2, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:08:47', '1', '2023-01-14 10:08:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2111, '查询草稿', 'mp:draft:query', 3, 0, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:09:01', '1', '2023-01-14 10:09:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2112, '删除草稿', 'mp:draft:delete', 3, 3, 2108, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 10:09:19', '1', '2023-01-14 10:09:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2113, '素材管理', '', 2, 8, 2084, 'material', 'ep:basketball', 'mp/material/index', 'MpMaterial', 0, '1', '1', '1', '1', '2023-01-14 14:12:07', '1', '2024-02-29 12:43:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2114, '上传临时素材', 'mp:material:upload-temporary', 3, 1, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:33:55', '1', '2023-01-14 15:33:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2115, '上传永久素材', 'mp:material:upload-permanent', 3, 2, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:34:14', '1', '2023-01-14 15:34:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2116, '删除素材', 'mp:material:delete', 3, 3, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:35:37', '1', '2023-01-14 15:35:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2117, '上传图文图片', 'mp:material:upload-news-image', 3, 4, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:36:31', '1', '2023-01-14 15:36:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2118, '查询素材', 'mp:material:query', 3, 5, 2113, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-14 15:39:22', '1', '2023-01-14 15:39:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2119, '菜单管理', '', 2, 6, 2084, 'menu', 'ep:menu', 'mp/menu/index', 'MpMenu', 0, '1', '1', '1', '1', '2023-01-14 17:43:54', '1', '2025-04-01 20:21:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2120, '自动回复', '', 2, 7, 2084, 'auto-reply', 'fa-solid:republican', 'mp/autoReply/index', 'MpAutoReply', 0, '1', '1', '1', '1', '2023-01-15 22:13:09', '1', '2024-02-29 12:43:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2121, '查询回复', 'mp:auto-reply:query', 3, 0, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:28:41', '1', '2023-01-16 22:28:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2122, '新增回复', 'mp:auto-reply:create', 3, 1, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:28:54', '1', '2023-01-16 22:28:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2123, '修改回复', 'mp:auto-reply:update', 3, 2, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:29:05', '1', '2023-01-16 22:29:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2124, '删除回复', 'mp:auto-reply:delete', 3, 3, 2120, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-16 22:29:34', '1', '2023-01-16 22:29:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2125, '查询菜单', 'mp:menu:query', 3, 0, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:05:41', '1', '2023-01-17 23:05:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2126, '保存菜单', 'mp:menu:save', 3, 1, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:06:01', '1', '2023-01-17 23:06:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2127, '删除菜单', 'mp:menu:delete', 3, 2, 2119, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:06:16', '1', '2023-01-17 23:06:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2128, '查询消息', 'mp:message:query', 3, 0, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:07:14', '1', '2023-01-17 23:07:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2129, '发送消息', 'mp:message:send', 3, 1, 2103, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-17 23:07:26', '1', '2023-01-17 23:07:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2130, '邮箱管理', '', 2, 2, 2739, 'mail', 'fa-solid:mail-bulk', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-25 17:27:44', '1', '2024-04-22 23:56:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2131, '邮箱账号', '', 2, 0, 2130, 'mail-account', 'fa:universal-access', 'system/mail/account/index', 'SystemMailAccount', 0, '1', '1', '1', '', '2023-01-25 09:33:48', '1', '2024-02-29 08:48:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2132, '账号查询', 'system:mail-account:query', 3, 1, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2133, '账号创建', 'system:mail-account:create', 3, 2, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2134, '账号更新', 'system:mail-account:update', 3, 3, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2135, '账号删除', 'system:mail-account:delete', 3, 4, 2131, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2136, '邮件模版', '', 2, 0, 2130, 'mail-template', 'fa:tag', 'system/mail/template/index', 'SystemMailTemplate', 0, '1', '1', '1', '', '2023-01-25 12:05:31', '1', '2024-02-29 08:48:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2137, '模版查询', 'system:mail-template:query', 3, 1, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2138, '模版创建', 'system:mail-template:create', 3, 2, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2139, '模版更新', 'system:mail-template:update', 3, 3, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2140, '模版删除', 'system:mail-template:delete', 3, 4, 2136, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2141, '邮件记录', '', 2, 0, 2130, 'mail-log', 'fa:edit', 'system/mail/log/index', 'SystemMailLog', 0, '1', '1', '1', '', '2023-01-26 02:16:50', '1', '2024-02-29 08:48:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2142, '日志查询', 'system:mail-log:query', 3, 1, 2141, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-26 02:16:50', '', '2023-01-26 02:16:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2143, '发送测试邮件', 'system:mail-template:send-mail', 3, 5, 2136, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-26 23:29:15', '1', '2023-01-26 23:29:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2144, '站内信管理', '', 1, 3, 2739, 'notify', 'ep:message-box', NULL, NULL, 0, '1', '1', '1', '1', '2023-01-28 10:25:18', '1', '2024-04-22 23:56:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2145, '模板管理', '', 2, 0, 2144, 'notify-template', 'fa:archive', 'system/notify/template/index', 'SystemNotifyTemplate', 0, '1', '1', '1', '', '2023-01-28 02:26:42', '1', '2024-02-29 08:49:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2146, '站内信模板查询', 'system:notify-template:query', 3, 1, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2147, '站内信模板创建', 'system:notify-template:create', 3, 2, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2148, '站内信模板更新', 'system:notify-template:update', 3, 3, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2149, '站内信模板删除', 'system:notify-template:delete', 3, 4, 2145, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2150, '发送测试站内信', 'system:notify-template:send-notify', 3, 5, 2145, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-01-28 10:54:43', '1', '2023-01-28 10:54:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2151, '消息记录', '', 2, 0, 2144, 'notify-message', 'fa:edit', 'system/notify/message/index', 'SystemNotifyMessage', 0, '1', '1', '1', '', '2023-01-28 04:28:22', '1', '2024-02-29 08:49:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2152, '站内信消息查询', 'system:notify-message:query', 3, 1, 2151, '', '', '', NULL, 0, '1', '1', '1', '', '2023-01-28 04:28:22', '', '2023-01-28 04:28:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2153, '大屏设计器', '', 2, 2, 1281, 'go-view', 'fa:area-chart', 'report/goview/index', 'GoView', 0, '1', '1', '1', '1', '2023-02-07 00:03:19', '1', '2025-05-03 09:57:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2154, '创建项目', 'report:go-view-project:create', 3, 1, 2153, '', '', '', NULL, 0, '1', '1', '1', '1', '2023-02-07 19:25:14', '1', '2023-02-07 19:25:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2155, '更新项目', 'report:go-view-project:update', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', '2023-02-07 19:25:34', '1', '2024-04-24 20:01:18', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:25:53', '1', '2023-02-07 19:25:53', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:26:15', '1', '2023-02-07 19:26:15', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-07 19:26:35', '1', '2023-02-07 19:26:35', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-10 22:46:28', '1', '2024-07-28 11:36:48', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2023-02-10 22:47:07', '1', '2023-12-02 21:32:29', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2023-02-11 14:21:42', '1', '2024-01-18 23:50:00', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2164, '配送管理', '', 1, 3, 2072, 'delivery', 'ep:shopping-cart', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:18:02', '1', '2023-09-28 10:58:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2165, '快递发货', '', 1, 0, 2164, 'express', 'ep:bicycle', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:22:06', '1', '2023-08-30 21:02:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2166, '门店自提', '', 1, 1, 2164, 'pick-up-store', 'ep:add-location', '', '', 0, '1', '1', '1', '1', '2023-05-18 09:23:14', '1', '2023-08-30 21:03:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2167, '快递公司', '', 2, 0, 2165, 'express', 'ep:compass', 'mall/trade/delivery/express/index', 'Express', 0, '1', '1', '1', '1', '2023-05-18 09:27:21', '1', '2024-11-29 11:20:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2168, '快递公司查询', 'trade:delivery:express:query', 3, 1, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2169, '快递公司创建', 'trade:delivery:express:create', 3, 2, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2170, '快递公司更新', 'trade:delivery:express:update', 3, 3, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2171, '快递公司删除', 'trade:delivery:express:delete', 3, 4, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2172, '快递公司导出', 'trade:delivery:express:export', 3, 5, 2167, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2173, '运费模版', 'trade:delivery:express-template:query', 2, 1, 2165, 'express-template', 'ep:coordinate', 'mall/trade/delivery/expressTemplate/index', 'ExpressTemplate', 0, '1', '1', '1', '1', '2023-05-20 06:48:10', '1', '2023-08-30 21:03:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2174, '快递运费模板查询', 'trade:delivery:express-template:query', 3, 1, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2175, '快递运费模板创建', 'trade:delivery:express-template:create', 3, 2, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2176, '快递运费模板更新', 'trade:delivery:express-template:update', 3, 3, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2177, '快递运费模板删除', 'trade:delivery:express-template:delete', 3, 4, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2178, '快递运费模板导出', 'trade:delivery:express-template:export', 3, 5, 2173, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2179, '门店管理', '', 2, 1, 2166, 'pick-up-store', 'ep:basketball', 'mall/trade/delivery/pickUpStore/index', 'PickUpStore', 0, '1', '1', '1', '1', '2023-05-25 10:50:00', '1', '2023-08-30 21:03:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2180, '自提门店查询', 'trade:delivery:pick-up-store:query', 3, 1, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2181, '自提门店创建', 'trade:delivery:pick-up-store:create', 3, 2, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2182, '自提门店更新', 'trade:delivery:pick-up-store:update', 3, 3, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2183, '自提门店删除', 'trade:delivery:pick-up-store:delete', 3, 4, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2184, '自提门店导出', 'trade:delivery:pick-up-store:export', 3, 5, 2179, '', '', '', NULL, 0, '1', '1', '1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2209, '秒杀活动', '', 2, 3, 2030, 'seckill', 'ep:place', '', '', 0, '1', '1', '1', '1', '2023-06-24 17:39:13', '1', '2023-06-24 18:55:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2262, '会员中心', '', 1, 55, 0, '/member', 'ep:bicycle', NULL, NULL, 0, '1', '1', '1', '1', '2023-06-10 00:42:03', '1', '2023-08-20 09:23:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2275, '会员配置', '', 2, 0, 2262, 'config', 'fa:archive', 'member/config/index', 'MemberConfig', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2023-10-01 23:41:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2276, '会员配置查询', 'member:config:query', 3, 1, 2275, '', '', '', '', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:48:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2277, '会员配置保存', 'member:config:save', 3, 2, 2275, '', '', '', '', 0, '1', '1', '1', '', '2023-06-10 02:07:44', '1', '2024-04-24 19:49:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2281, '签到配置', '', 2, 2, 2300, 'config', 'ep:calendar', 'member/signin/config/index', 'SignInConfig', 0, '1', '1', '1', '', '2023-06-10 03:26:12', '1', '2023-08-20 19:25:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2282, '积分签到规则查询', 'point:sign-in-config:query', 3, 1, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2283, '积分签到规则创建', 'point:sign-in-config:create', 3, 2, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2284, '积分签到规则更新', 'point:sign-in-config:update', 3, 3, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2285, '积分签到规则删除', 'point:sign-in-config:delete', 3, 4, 2281, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2287, '会员积分', '', 2, 10, 2262, 'record', 'fa:asterisk', 'member/point/record/index', 'PointRecord', 0, '1', '1', '1', '', '2023-06-10 04:18:50', '1', '2023-10-01 23:42:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2288, '用户积分记录查询', 'point:record:query', 3, 1, 2287, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:18:50', '', '2023-06-10 04:18:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2293, '签到记录', '', 2, 3, 2300, 'record', 'ep:chicken', 'member/signin/record/index', 'SignInRecord', 0, '1', '1', '1', '', '2023-06-10 04:48:22', '1', '2023-08-20 19:26:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2294, '用户签到积分查询', 'point:sign-in-record:query', 3, 1, 2293, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2297, '用户签到积分删除', 'point:sign-in-record:delete', 3, 4, 2293, '', '', '', NULL, 0, '1', '1', '1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2300, '会员签到', '', 1, 11, 2262, 'signin', 'ep:alarm-clock', '', '', 0, '1', '1', '1', '1', '2023-06-27 22:49:53', '1', '2023-08-20 09:23:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2301, '回调通知', '', 2, 5, 1117, 'notify', 'ep:mute-notification', 'pay/notify/index', 'PayNotify', 0, '1', '1', '1', '', '2023-07-20 04:41:32', '1', '2024-01-18 23:56:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2302, '支付通知查询', 'pay:notify:query', 3, 1, 2301, '', '', '', NULL, 0, '1', '1', '1', '', '2023-07-20 04:41:32', '', '2023-07-20 04:41:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2303, '拼团活动', '', 2, 3, 2030, 'combination', 'fa:group', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:19:54', '1', '2023-08-12 17:20:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2304, '拼团商品', '', 2, 1, 2303, 'acitivity', 'ep:apple', 'mall/promotion/combination/activity/index', 'PromotionCombinationActivity', 0, '1', '1', '1', '1', '2023-08-12 17:22:03', '1', '2023-08-12 17:22:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2305, '拼团活动查询', 'promotion:combination-activity:query', 3, 1, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:54:32', '1', '2023-11-24 11:57:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2306, '拼团活动创建', 'promotion:combination-activity:create', 3, 2, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:54:49', '1', '2023-08-12 17:54:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2307, '拼团活动更新', 'promotion:combination-activity:update', 3, 3, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:04', '1', '2023-08-12 17:55:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2308, '拼团活动删除', 'promotion:combination-activity:delete', 3, 4, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:23', '1', '2023-08-12 17:55:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2309, '拼团活动关闭', 'promotion:combination-activity:close', 3, 5, 2304, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-12 17:55:37', '1', '2023-10-06 10:51:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2310, '砍价活动', '', 2, 4, 2030, 'bargain', 'ep:box', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:27:25', '1', '2023-08-13 00:27:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2311, '砍价商品', '', 2, 1, 2310, 'activity', 'ep:burger', 'mall/promotion/bargain/activity/index', 'PromotionBargainActivity', 0, '1', '1', '1', '1', '2023-08-13 00:28:49', '1', '2023-10-05 01:16:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2312, '砍价活动查询', 'promotion:bargain-activity:query', 3, 1, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:30', '1', '2023-08-13 00:32:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2313, '砍价活动创建', 'promotion:bargain-activity:create', 3, 2, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:44', '1', '2023-08-13 00:32:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2314, '砍价活动更新', 'promotion:bargain-activity:update', 3, 3, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:32:55', '1', '2023-08-13 00:32:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2315, '砍价活动删除', 'promotion:bargain-activity:delete', 3, 4, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:34:50', '1', '2023-08-13 00:34:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2316, '砍价活动关闭', 'promotion:bargain-activity:close', 3, 5, 2311, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-13 00:35:02', '1', '2023-08-13 00:35:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2317, '会员管理', '', 2, 0, 2262, 'user', 'ep:avatar', 'member/user/index', 'MemberUser', 0, '1', '1', '1', '', '2023-08-19 04:12:15', '1', '2023-08-24 00:50:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2318, '会员用户查询', 'member:user:query', 3, 1, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2319, '会员用户更新', 'member:user:update', 3, 3, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-19 04:12:15', '', '2023-08-19 04:12:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2320, '会员标签', '', 2, 1, 2262, 'tag', 'ep:collection-tag', 'member/tag/index', 'MemberTag', 0, '1', '1', '1', '', '2023-08-20 01:03:08', '1', '2023-08-20 09:23:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2321, '会员标签查询', 'member:tag:query', 3, 1, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2322, '会员标签创建', 'member:tag:create', 3, 2, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2323, '会员标签更新', 'member:tag:update', 3, 3, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2324, '会员标签删除', 'member:tag:delete', 3, 4, 2320, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-20 01:03:08', '', '2023-08-20 01:03:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2325, '会员等级', '', 2, 2, 2262, 'level', 'fa:level-up', 'member/level/index', 'MemberLevel', 0, '1', '1', '1', '', '2023-08-22 12:41:01', '1', '2023-08-22 21:47:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2326, '会员等级查询', 'member:level:query', 3, 1, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2327, '会员等级创建', 'member:level:create', 3, 2, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2328, '会员等级更新', 'member:level:update', 3, 3, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2329, '会员等级删除', 'member:level:delete', 3, 4, 2325, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 12:41:02', '', '2023-08-22 12:41:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2330, '会员分组', '', 2, 3, 2262, 'group', 'fa:group', 'member/group/index', 'MemberGroup', 0, '1', '1', '1', '', '2023-08-22 13:50:06', '1', '2023-10-01 23:42:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2331, '用户分组查询', 'member:group:query', 3, 1, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2332, '用户分组创建', 'member:group:create', 3, 2, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2333, '用户分组更新', 'member:group:update', 3, 3, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2334, '用户分组删除', 'member:group:delete', 3, 4, 2330, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-22 13:50:06', '', '2023-08-22 13:50:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2335, '用户等级修改', 'member:user:update-level', 3, 5, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-08-23 16:49:05', '', '2023-08-23 16:50:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2336, '商品评论', '', 2, 5, 2000, 'comment', 'ep:comment', 'mall/product/comment/index', 'ProductComment', 0, '1', '1', '1', '1', '2023-08-26 11:03:00', '1', '2023-08-26 11:03:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2337, '评论查询', 'product:comment:query', 3, 1, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:01', '1', '2023-08-26 11:04:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2338, '添加自评', 'product:comment:create', 3, 2, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:23', '1', '2023-08-26 11:08:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2339, '商家回复', 'product:comment:update', 3, 3, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:37', '1', '2023-08-26 11:04:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2340, '显隐评论', 'product:comment:update', 3, 4, 2336, '', '', '', '', 0, '1', '1', '1', '1', '2023-08-26 11:04:55', '1', '2023-08-26 11:04:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2341, '优惠劵发送', 'promotion:coupon:send', 3, 2, 2038, '', '', '', '', 0, '1', '1', '1', '1', '2023-09-02 00:03:14', '1', '2023-09-02 00:03:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2342, '交易配置', '', 2, 0, 2072, 'config', 'ep:setting', 'mall/trade/config/index', 'TradeConfig', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:30:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2343, '交易中心配置查询', 'trade:config:query', 3, 1, 2342, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2344, '交易中心配置保存', 'trade:config:save', 3, 2, 2342, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2345, '分销管理', '', 1, 4, 2072, 'brokerage', 'fa-solid:project-diagram', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2023-09-28 10:58:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2346, '分销用户', '', 2, 0, 2345, 'brokerage-user', 'fa-solid:user-tie', 'mall/trade/brokerage/user/index', 'TradeBrokerageUser', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2347, '分销用户查询', 'trade:brokerage-user:query', 3, 1, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2348, '分销用户推广人查询', 'trade:brokerage-user:user-query', 3, 2, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2349, '分销用户推广订单查询', 'trade:brokerage-user:order-query', 3, 3, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2350, '分销用户修改推广资格', 'trade:brokerage-user:update-brokerage-enable', 3, 4, 2346, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2351, '修改推广员', 'trade:brokerage-user:update-bind-user', 3, 5, 2346, '', '', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2352, '清除推广员', 'trade:brokerage-user:clear-bind-user', 3, 6, 2346, '', '', '', '', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-12-01 14:33:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2353, '佣金记录', '', 2, 1, 2345, 'brokerage-record', 'fa:money', 'mall/trade/brokerage/record/index', 'TradeBrokerageRecord', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2354, '佣金记录查询', 'trade:brokerage-record:query', 3, 1, 2353, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2355, '佣金提现', '', 2, 2, 2345, 'brokerage-withdraw', 'fa:credit-card', 'mall/trade/brokerage/withdraw/index', 'TradeBrokerageWithdraw', 0, '1', '1', '1', '', '2023-09-28 02:46:22', '1', '2024-02-26 20:33:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2356, '佣金提现查询', 'trade:brokerage-withdraw:query', 3, 1, 2355, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2357, '佣金提现审核', 'trade:brokerage-withdraw:audit', 3, 2, 2355, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-28 02:46:22', '', '2023-09-28 02:46:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2358, '统计中心', '', 1, 75, 2362, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '', '2023-09-30 03:22:40', '1', '2023-09-30 11:54:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2359, '交易统计', '', 2, 4, 2358, 'trade', 'fa-solid:credit-card', 'mall/statistics/trade/index', 'TradeStatistics', 0, '1', '1', '1', '', '2023-09-30 03:22:40', '1', '2024-02-26 20:42:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2360, '交易统计查询', 'statistics:trade:query', 3, 1, 2359, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2361, '交易统计导出', 'statistics:trade:export', 3, 2, 2359, '', '', '', NULL, 0, '1', '1', '1', '', '2023-09-30 03:22:40', '', '2023-09-30 03:22:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2362, '商城系统', '', 1, 59, 0, '/mall', 'ep:shop', '', '', 0, '1', '1', '1', '1', '2023-09-30 11:52:02', '1', '2023-09-30 11:52:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2363, '用户积分修改', 'member:user:update-point', 3, 6, 2317, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-01 14:39:43', '', '2023-10-01 14:39:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2364, '用户余额修改', 'pay:wallet:update-balance', 3, 7, 2317, '', '', '', '', 0, '1', '1', '1', '', '2023-10-01 14:39:43', '1', '2024-10-01 09:42:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2365, '优惠劵', '', 1, 2, 2030, 'coupon', 'fa-solid:disease', '', '', 0, '1', '1', '1', '1', '2023-10-03 12:39:15', '1', '2023-10-05 00:16:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2366, '砍价记录', '', 2, 2, 2310, 'record', 'ep:list', 'mall/promotion/bargain/record/index', 'PromotionBargainRecord', 0, '1', '1', '1', '', '2023-10-05 02:49:06', '1', '2023-10-05 10:50:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2367, '砍价记录查询', 'promotion:bargain-record:query', 3, 1, 2366, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-05 02:49:06', '', '2023-10-05 02:49:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2368, '助力记录查询', 'promotion:bargain-help:query', 3, 2, 2366, '', '', '', '', 0, '1', '1', '1', '1', '2023-10-05 12:27:49', '1', '2023-10-05 12:27:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2369, '拼团记录', 'promotion:combination-record:query', 2, 2, 2303, 'record', 'ep:avatar', 'mall/promotion/combination/record/index.vue', 'PromotionCombinationRecord', 0, '1', '1', '1', '1', '2023-10-08 07:10:22', '1', '2023-10-08 07:34:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2374, '会员统计', '', 2, 2, 2358, 'member', 'ep:avatar', 'mall/statistics/member/index', 'MemberStatistics', 0, '1', '1', '1', '', '2023-10-11 04:39:24', '1', '2024-02-26 20:41:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2375, '会员统计查询', 'statistics:member:query', 3, 1, 2374, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-11 04:39:24', '', '2023-10-11 04:39:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2376, '订单核销', 'trade:order:pick-up', 3, 10, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2023-10-14 17:11:58', '1', '2023-10-14 17:11:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2377, '文章分类', '', 2, 0, 2387, 'article/category', 'fa:certificate', 'mall/promotion/article/category/index', 'ArticleCategory', 0, '1', '1', '1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:38:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2378, '分类查询', 'promotion:article-category:query', 3, 1, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2379, '分类创建', 'promotion:article-category:create', 3, 2, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2380, '分类更新', 'promotion:article-category:update', 3, 3, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2381, '分类删除', 'promotion:article-category:delete', 3, 4, 2377, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2382, '文章列表', '', 2, 2, 2387, 'article', 'ep:connection', 'mall/promotion/article/index', 'Article', 0, '1', '1', '1', '', '2023-10-16 01:26:18', '1', '2023-10-16 09:41:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2383, '文章管理查询', 'promotion:article:query', 3, 1, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2384, '文章管理创建', 'promotion:article:create', 3, 2, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2385, '文章管理更新', 'promotion:article:update', 3, 3, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2386, '文章管理删除', 'promotion:article:delete', 3, 4, 2382, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-16 01:26:18', '', '2023-10-16 01:26:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2387, '内容管理', '', 1, 1, 2030, 'content', 'ep:collection', '', '', 0, '1', '1', '1', '1', '2023-10-16 09:37:31', '1', '2023-10-16 09:37:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2388, '商城首页', '', 2, 1, 2362, 'home', 'ep:home-filled', 'mall/home/index', 'MallHome', 0, '1', '1', '1', '', '2023-10-16 12:10:33', '', '2023-10-16 12:10:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2389, '核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder', 0, '1', '1', '1', '', '2023-10-19 16:09:51', '', '2023-10-19 16:09:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2390, '优惠活动', '', 1, 99, 2030, 'youhui', 'ep:aim', '', '', 0, '1', '1', '1', '1', '2023-10-21 19:23:49', '1', '2023-10-21 19:23:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2391, '客户管理', '', 2, 10, 2397, 'customer', 'fa:address-book-o', 'crm/customer/index', 'CrmCustomer', 0, '1', '1', '1', '', '2023-10-29 09:04:21', '1', '2024-02-17 17:13:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2392, '客户查询', 'crm:customer:query', 3, 1, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2393, '客户创建', 'crm:customer:create', 3, 2, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2394, '客户更新', 'crm:customer:update', 3, 3, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2395, '客户删除', 'crm:customer:delete', 3, 4, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2396, '客户导出', 'crm:customer:export', 3, 5, 2391, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2397, 'CRM 系统', '', 1, 200, 0, '/crm', 'simple-icons:civicrm', '', '', 0, '1', '1', '1', '1', '2023-10-29 17:08:30', '1', '2025-04-19 18:56:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2398, '合同管理', '', 2, 50, 2397, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, '1', '1', '1', '', '2023-10-29 10:50:41', '1', '2024-02-17 17:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2399, '合同查询', 'crm:contract:query', 3, 1, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2400, '合同创建', 'crm:contract:create', 3, 2, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2401, '合同更新', 'crm:contract:update', 3, 3, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2402, '合同删除', 'crm:contract:delete', 3, 4, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2403, '合同导出', 'crm:contract:export', 3, 5, 2398, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2404, '线索管理', '', 2, 8, 2397, 'clue', 'fa:pagelines', 'crm/clue/index', 'CrmClue', 0, '1', '1', '1', '', '2023-10-29 11:06:29', '1', '2024-02-17 17:15:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2405, '线索查询', 'crm:clue:query', 3, 1, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2406, '线索创建', 'crm:clue:create', 3, 2, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2407, '线索更新', 'crm:clue:update', 3, 3, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2408, '线索删除', 'crm:clue:delete', 3, 4, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2409, '线索导出', 'crm:clue:export', 3, 5, 2404, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2410, '商机管理', '', 2, 40, 2397, 'business', 'fa:bus', 'crm/business/index', 'CrmBusiness', 0, '1', '1', '1', '', '2023-10-29 11:12:35', '1', '2024-02-17 17:14:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2411, '商机查询', 'crm:business:query', 3, 1, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2412, '商机创建', 'crm:business:create', 3, 2, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2413, '商机更新', 'crm:business:update', 3, 3, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2414, '商机删除', 'crm:business:delete', 3, 4, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2415, '商机导出', 'crm:business:export', 3, 5, 2410, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2416, '联系人管理', '', 2, 20, 2397, 'contact', 'fa:address-book-o', 'crm/contact/index', 'CrmContact', 0, '1', '1', '1', '', '2023-10-29 11:14:56', '1', '2024-02-17 17:13:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2417, '联系人查询', 'crm:contact:query', 3, 1, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2418, '联系人创建', 'crm:contact:create', 3, 2, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2419, '联系人更新', 'crm:contact:update', 3, 3, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2420, '联系人删除', 'crm:contact:delete', 3, 4, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2421, '联系人导出', 'crm:contact:export', 3, 5, 2416, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2422, '回款管理', '', 2, 60, 2397, 'receivable', 'ep:money', 'crm/receivable/index', 'CrmReceivable', 0, '1', '1', '1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2423, '回款管理查询', 'crm:receivable:query', 3, 1, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2424, '回款管理创建', 'crm:receivable:create', 3, 2, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2425, '回款管理更新', 'crm:receivable:update', 3, 3, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2426, '回款管理删除', 'crm:receivable:delete', 3, 4, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2427, '回款管理导出', 'crm:receivable:export', 3, 5, 2422, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2428, '回款计划', '', 2, 61, 2397, 'receivable-plan', 'fa:money', 'crm/receivable/plan/index', 'CrmReceivablePlan', 0, '1', '1', '1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2429, '回款计划查询', 'crm:receivable-plan:query', 3, 1, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2430, '回款计划创建', 'crm:receivable-plan:create', 3, 2, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2432, '回款计划删除', 'crm:receivable-plan:delete', 3, 4, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2433, '回款计划导出', 'crm:receivable-plan:export', 3, 5, 2428, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', '', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '1', '2025-03-15 21:34:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2436, '装修模板', '', 2, 1, 2435, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2437, '装修模板查询', 'promotion:diy-template:query', 3, 1, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2438, '装修模板创建', 'promotion:diy-template:create', 3, 2, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2439, '装修模板更新', 'promotion:diy-template:update', 3, 3, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2440, '装修模板删除', 'promotion:diy-template:delete', 3, 4, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2441, '装修模板使用', 'promotion:diy-template:use', 3, 5, 2436, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2442, '装修页面', '', 2, 2, 2435, 'diy-page', 'foundation:page-edit', 'mall/promotion/diy/page/index', 'DiyPage', 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2443, '装修页面查询', 'promotion:diy-page:query', 3, 1, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2444, '装修页面创建', 'promotion:diy-page:create', 3, 2, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2445, '装修页面更新', 'promotion:diy-page:update', 3, 3, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2446, '装修页面删除', 'promotion:diy-page:delete', 3, 4, 2442, '', '', '', NULL, 0, '1', '1', '1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2447, '三方登录', '', 1, 10, 1, 'social', 'fa:rocket', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:12:01', '1', '2024-02-29 01:14:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2448, '三方应用', '', 2, 1, 2447, 'client', 'ep:set-up', 'system/social/client/index.vue', 'SocialClient', 0, '1', '1', '1', '1', '2023-11-04 12:17:19', '1', '2024-05-04 19:09:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2449, '三方应用查询', 'system:social-client:query', 3, 1, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:43:12', '1', '2023-11-04 12:43:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2450, '三方应用创建', 'system:social-client:create', 3, 2, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:43:58', '1', '2023-11-04 12:43:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2451, '三方应用更新', 'system:social-client:update', 3, 3, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:44:27', '1', '2023-11-04 12:44:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2452, '三方应用删除', 'system:social-client:delete', 3, 4, 2448, '', '', '', '', 0, '1', '1', '1', '1', '2023-11-04 12:44:43', '1', '2023-11-04 12:44:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2453, '三方用户', 'system:social-user:query', 2, 2, 2447, 'user', 'ep:avatar', 'system/social/user/index.vue', 'SocialUser', 0, '1', '1', '1', '1', '2023-11-04 14:01:05', '1', '2023-11-04 14:01:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2472, '主子表（内嵌）', '', 2, 12, 1070, 'demo03-inner', 'fa:power-off', 'infra/demo/demo03/inner/index', 'Demo03StudentInner', 0, '1', '1', '1', '', '2023-11-13 04:39:51', '1', '2023-11-16 23:53:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2478, '单表（增删改查）', '', 2, 1, 1070, 'demo01-contact', 'ep:bicycle', 'infra/demo/demo01/index', 'Demo01Contact', 0, '1', '1', '1', '', '2023-11-15 14:42:30', '1', '2023-11-16 20:34:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2479, '示例联系人查询', 'infra:demo01-contact:query', 3, 1, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2480, '示例联系人创建', 'infra:demo01-contact:create', 3, 2, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2481, '示例联系人更新', 'infra:demo01-contact:update', 3, 3, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2482, '示例联系人删除', 'infra:demo01-contact:delete', 3, 4, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2483, '示例联系人导出', 'infra:demo01-contact:export', 3, 5, 2478, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2484, '树表（增删改查）', '', 2, 2, 1070, 'demo02-category', 'fa:tree', 'infra/demo/demo02/index', 'Demo02Category', 0, '1', '1', '1', '', '2023-11-16 12:18:27', '1', '2023-11-16 20:35:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2485, '示例分类查询', 'infra:demo02-category:query', 3, 1, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2486, '示例分类创建', 'infra:demo02-category:create', 3, 2, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2487, '示例分类更新', 'infra:demo02-category:update', 3, 3, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2488, '示例分类删除', 'infra:demo02-category:delete', 3, 4, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2489, '示例分类导出', 'infra:demo02-category:export', 3, 5, 2484, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2490, '主子表（标准）', '', 2, 10, 1070, 'demo03-normal', 'fa:battery-3', 'infra/demo/demo03/normal/index', 'Demo03StudentNormal', 0, '1', '1', '1', '', '2023-11-16 12:53:37', '1', '2023-11-16 23:10:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2491, '学生查询', 'infra:demo03-student:query', 3, 1, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2492, '学生创建', 'infra:demo03-student:create', 3, 2, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2493, '学生更新', 'infra:demo03-student:update', 3, 3, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2494, '学生删除', 'infra:demo03-student:delete', 3, 4, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2495, '学生导出', 'infra:demo03-student:export', 3, 5, 2490, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2497, '主子表（ERP）', '', 2, 11, 1070, 'demo03-erp', 'ep:calendar', 'infra/demo/demo03/erp/index', 'Demo03StudentERP', 0, '1', '1', '1', '', '2023-11-16 15:50:59', '1', '2023-11-17 13:19:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2516, '客户公海配置', '', 2, 0, 2524, 'customer-pool-config', 'ep:data-analysis', 'crm/customer/poolConfig/index', 'CrmCustomerPoolConfig', 0, '1', '1', '1', '', '2023-11-18 13:33:31', '1', '2024-01-03 19:52:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2517, '客户公海配置保存', 'crm:customer-pool-config:update', 3, 1, 2516, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:31', '', '2023-11-18 13:33:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2518, '客户限制配置', '', 2, 1, 2524, 'customer-limit-config', 'ep:avatar', 'crm/customer/limitConfig/index', 'CrmCustomerLimitConfig', 0, '1', '1', '1', '', '2023-11-18 13:33:53', '1', '2024-02-24 16:43:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2519, '客户限制配置查询', 'crm:customer-limit-config:query', 3, 1, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2520, '客户限制配置创建', 'crm:customer-limit-config:create', 3, 2, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2521, '客户限制配置更新', 'crm:customer-limit-config:update', 3, 3, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2522, '客户限制配置删除', 'crm:customer-limit-config:delete', 3, 4, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2523, '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, 2518, '', '', '', NULL, 0, '1', '1', '1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2524, '系统配置', '', 1, 999, 2397, 'config', 'ep:connection', '', '', 0, '1', '1', '1', '1', '2023-11-18 21:58:00', '1', '2024-02-17 17:14:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2525, 'WebSocket', '', 2, 5, 2, 'websocket', 'ep:connection', 'infra/webSocket/index', 'InfraWebSocket', 0, '1', '1', '1', '1', '2023-11-23 19:41:55', '1', '2024-04-23 00:02:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2526, '产品管理', '', 2, 80, 2397, 'product', 'fa:product-hunt', 'crm/product/index', 'CrmProduct', 0, '1', '1', '1', '1', '2023-12-05 22:45:26', '1', '2024-02-20 20:36:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2527, '产品查询', 'crm:product:query', 3, 1, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:47:16', '1', '2023-12-05 22:47:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2528, '产品创建', 'crm:product:create', 3, 2, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:47:41', '1', '2023-12-05 22:47:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2529, '产品更新', 'crm:product:update', 3, 3, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:03', '1', '2023-12-05 22:48:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2530, '产品删除', 'crm:product:delete', 3, 4, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:17', '1', '2023-12-05 22:48:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2531, '产品导出', 'crm:product:export', 3, 5, 2526, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-05 22:48:29', '1', '2023-12-05 22:48:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2532, '产品分类配置', '', 2, 3, 2524, 'product/category', 'fa-solid:window-restore', 'crm/product/category/index', 'CrmProductCategory', 0, '1', '1', '1', '1', '2023-12-06 12:52:36', '1', '2023-12-06 12:52:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2533, '产品分类查询', 'crm:product-category:query', 3, 1, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:23', '1', '2023-12-06 12:53:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2534, '产品分类创建', 'crm:product-category:create', 3, 2, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:41', '1', '2023-12-06 12:53:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2535, '产品分类更新', 'crm:product-category:update', 3, 3, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:53:59', '1', '2023-12-06 12:53:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2536, '产品分类删除', 'crm:product-category:delete', 3, 4, 2532, '', '', '', '', 0, '1', '1', '1', '1', '2023-12-06 12:54:14', '1', '2023-12-06 12:54:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2543, '关联商机', 'crm:contact:create-business', 3, 10, 2416, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-02 17:28:25', '1', '2024-01-02 17:28:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2544, '取关商机', 'crm:contact:delete-business', 3, 11, 2416, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-02 17:28:43', '1', '2024-01-02 17:28:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2545, '商品统计', '', 2, 3, 2358, 'product', 'fa:product-hunt', 'mall/statistics/product/index', 'ProductStatistics', 0, '1', '1', '1', '', '2023-12-15 18:54:28', '1', '2024-02-26 20:41:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2546, '客户公海', '', 2, 30, 2397, 'customer/pool', 'fa-solid:swimming-pool', 'crm/customer/pool/index', 'CrmCustomerPool', 0, '1', '1', '1', '1', '2024-01-15 21:29:34', '1', '2024-02-17 17:14:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2547, '订单查询', 'trade:order:query', 3, 1, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-16 08:52:00', '1', '2024-01-16 08:52:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2548, '订单更新', 'trade:order:update', 3, 2, 2076, '', '', '', '', 0, '1', '1', '1', '1', '2024-01-16 08:52:21', '1', '2024-01-16 08:52:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2549, '支付&退款案例', '', 2, 1, 2161, 'order', 'fa:paypal', 'pay/demo/order/index', '', 0, '1', '1', '1', '1', '2024-01-18 23:45:00', '1', '2024-01-18 23:47:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2550, '提现转账案例', '', 2, 2, 2161, 'transfer', 'fa:transgender-alt', 'pay/demo/withdraw/index', '', 0, '1', '1', '1', '1', '2024-01-18 23:51:16', '1', '2025-05-08 13:04:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2551, '钱包管理', '', 1, 4, 1117, 'wallet', 'ep:wallet', '', '', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '1', '2024-02-29 08:58:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2552, '充值套餐', '', 2, 2, 2551, 'wallet-recharge-package', 'fa:leaf', 'pay/wallet/rechargePackage/index', 'WalletRechargePackage', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2553, '钱包充值套餐查询', 'pay:wallet-recharge-package:query', 3, 1, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2554, '钱包充值套餐创建', 'pay:wallet-recharge-package:create', 3, 2, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2555, '钱包充值套餐更新', 'pay:wallet-recharge-package:update', 3, 3, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2556, '钱包充值套餐删除', 'pay:wallet-recharge-package:delete', 3, 4, 2552, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2557, '钱包余额', '', 2, 1, 2551, 'wallet-balance', 'fa:leaf', 'pay/wallet/balance/index', 'WalletBalance', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2558, '钱包余额查询', 'pay:wallet:query', 3, 1, 2557, '', '', '', NULL, 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2559, '转账订单', '', 2, 3, 1117, 'transfer', 'ep:credit-card', 'pay/transfer/index', 'PayTransfer', 0, '1', '1', '1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2560, '数据统计', '', 1, 200, 2397, 'statistics', 'ep:data-line', '', '', 0, '1', '1', '1', '1', '2024-01-26 22:50:35', '1', '2024-02-24 20:10:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2561, '排行榜', 'crm:statistics-rank:query', 2, 1, 2560, 'ranking', 'fa:area-chart', 'crm/statistics/rank/index', 'CrmStatisticsRank', 0, '1', '1', '1', '1', '2024-01-26 22:52:09', '1', '2024-04-24 19:39:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2562, '客户导入', 'crm:customer:import', 3, 6, 2391, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-01 13:09:00', '1', '2024-02-01 13:09:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2563, 'ERP 系统', '', 1, 300, 0, '/erp', 'simple-icons:erpnext', '', '', 0, '1', '1', '1', '1', '2024-02-04 15:37:25', '1', '2025-04-19 18:56:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2564, '产品管理', '', 1, 40, 2563, 'product', 'fa:product-hunt', '', '', 0, '1', '1', '1', '1', '2024-02-04 15:38:43', '1', '2024-02-04 15:38:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2565, '产品信息', '', 2, 0, 2564, 'product', 'fa-solid:apple-alt', 'erp/product/product/index', 'ErpProduct', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-05 14:42:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2566, '产品查询', 'erp:product:query', 3, 1, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:21:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2567, '产品创建', 'erp:product:create', 3, 2, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2568, '产品更新', 'erp:product:update', 3, 3, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2569, '产品删除', 'erp:product:delete', 3, 4, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2570, '产品导出', 'erp:product:export', 3, 5, 2565, '', '', '', '', 0, '1', '1', '1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2571, '产品分类', '', 2, 1, 2564, 'product-category', 'fa:certificate', 'erp/product/category/index', 'ErpProductCategory', 0, '1', '1', '1', '', '2024-02-04 09:21:04', '1', '2024-02-04 17:24:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2572, '分类查询', 'erp:product-category:query', 3, 1, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2573, '分类创建', 'erp:product-category:create', 3, 2, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2574, '分类更新', 'erp:product-category:update', 3, 3, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2575, '分类删除', 'erp:product-category:delete', 3, 4, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2576, '分类导出', 'erp:product-category:export', 3, 5, 2571, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2577, '产品单位', '', 2, 2, 2564, 'unit', 'ep:opportunity', 'erp/product/unit/index', 'ErpProductUnit', 0, '1', '1', '1', '', '2024-02-04 11:54:08', '1', '2024-02-04 19:54:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2578, '单位查询', 'erp:product-unit:query', 3, 1, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2579, '单位创建', 'erp:product-unit:create', 3, 2, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2580, '单位更新', 'erp:product-unit:update', 3, 3, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2581, '单位删除', 'erp:product-unit:delete', 3, 4, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2582, '单位导出', 'erp:product-unit:export', 3, 5, 2577, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2583, '库存管理', '', 1, 30, 2563, 'stock', 'fa:window-restore', '', '', 0, '1', '1', '1', '1', '2024-02-05 00:29:37', '1', '2024-02-05 00:29:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2584, '仓库信息', '', 2, 0, 2583, 'warehouse', 'ep:house', 'erp/stock/warehouse/index', 'ErpWarehouse', 0, '1', '1', '1', '', '2024-02-04 17:12:09', '1', '2024-02-05 01:12:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2585, '仓库查询', 'erp:warehouse:query', 3, 1, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2586, '仓库创建', 'erp:warehouse:create', 3, 2, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2587, '仓库更新', 'erp:warehouse:update', 3, 3, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2588, '仓库删除', 'erp:warehouse:delete', 3, 4, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2589, '仓库导出', 'erp:warehouse:export', 3, 5, 2584, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2590, '产品库存', '', 2, 1, 2583, 'stock', 'ep:coffee', 'erp/stock/stock/index', 'ErpStock', 0, '1', '1', '1', '', '2024-02-05 06:40:50', '1', '2024-02-05 14:42:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2591, '库存查询', 'erp:stock:query', 3, 1, 2590, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2592, '库存导出', 'erp:stock:export', 3, 5, 2590, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2593, '出入库明细', '', 2, 2, 2583, 'record', 'fa-solid:blog', 'erp/stock/record/index', 'ErpStockRecord', 0, '1', '1', '1', '', '2024-02-05 10:27:21', '1', '2024-02-06 17:26:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2594, '库存明细查询', 'erp:stock-record:query', 3, 1, 2593, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2595, '库存明细导出', 'erp:stock-record:export', 3, 5, 2593, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2596, '其它入库', '', 2, 3, 2583, 'in', 'ep:zoom-in', 'erp/stock/in/index', 'ErpStockIn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2597, '其它入库单查询', 'erp:stock-in:query', 3, 1, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2598, '其它入库单创建', 'erp:stock-in:create', 3, 2, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2599, '其它入库单更新', 'erp:stock-in:update', 3, 3, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2600, '其它入库单删除', 'erp:stock-in:delete', 3, 4, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2601, '其它入库单导出', 'erp:stock-in:export', 3, 5, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2602, '采购管理', '', 1, 10, 2563, 'purchase', 'fa:buysellads', '', '', 0, '1', '1', '1', '1', '2024-02-06 16:01:01', '1', '2024-02-06 16:01:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2603, '供应商信息', '', 2, 4, 2602, 'supplier', 'fa:superpowers', 'erp/purchase/supplier/index', 'ErpSupplier', 0, '1', '1', '1', '', '2024-02-06 08:21:55', '1', '2024-02-06 16:22:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2604, '供应商查询', 'erp:supplier:query', 3, 1, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2605, '供应商创建', 'erp:supplier:create', 3, 2, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2606, '供应商更新', 'erp:supplier:update', 3, 3, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2607, '供应商删除', 'erp:supplier:delete', 3, 4, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2608, '供应商导出', 'erp:supplier:export', 3, 5, 2603, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2609, '其它入库单审批', 'erp:stock-in:update-status', 3, 6, 2596, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2610, '其它出库', '', 2, 4, 2583, 'out', 'ep:zoom-out', 'erp/stock/out/index', 'ErpStockOut', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2611, '其它出库单查询', 'erp:stock-out:query', 3, 1, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2612, '其它出库单创建', 'erp:stock-out:create', 3, 2, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2613, '其它出库单更新', 'erp:stock-out:update', 3, 3, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2614, '其它出库单删除', 'erp:stock-out:delete', 3, 4, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2615, '其它出库单导出', 'erp:stock-out:export', 3, 5, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2616, '其它出库单审批', 'erp:stock-out:update-status', 3, 6, 2610, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2617, '销售管理', '', 1, 20, 2563, 'sale', 'fa:sellsy', '', '', 0, '1', '1', '1', '1', '2024-02-07 15:12:32', '1', '2024-02-07 15:12:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2618, '客户信息', '', 2, 4, 2617, 'customer', 'ep:avatar', 'erp/sale/customer/index', 'ErpCustomer', 0, '1', '1', '1', '', '2024-02-07 07:21:45', '1', '2024-02-07 15:22:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2619, '客户查询', 'erp:customer:query', 3, 1, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2620, '客户创建', 'erp:customer:create', 3, 2, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2621, '客户更新', 'erp:customer:update', 3, 3, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2622, '客户删除', 'erp:customer:delete', 3, 4, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2623, '客户导出', 'erp:customer:export', 3, 5, 2618, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2624, '库存调拨', '', 2, 5, 2583, 'move', 'ep:folder-remove', 'erp/stock/move/index', 'ErpStockMove', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-16 18:53:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2625, '库存调度单查询', 'erp:stock-move:query', 3, 1, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2626, '库存调度单创建', 'erp:stock-move:create', 3, 2, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2627, '库存调度单更新', 'erp:stock-move:update', 3, 3, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2628, '库存调度单删除', 'erp:stock-move:delete', 3, 4, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2629, '库存调度单导出', 'erp:stock-move:export', 3, 5, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2630, '库存调度单审批', 'erp:stock-move:update-status', 3, 6, 2624, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2631, '库存盘点', '', 2, 6, 2583, 'check', 'ep:circle-check-filled', 'erp/stock/check/index', 'ErpStockCheck', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-08 08:31:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2632, '库存盘点单查询', 'erp:stock-check:query', 3, 1, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2633, '库存盘点单创建', 'erp:stock-check:create', 3, 2, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2634, '库存盘点单更新', 'erp:stock-check:update', 3, 3, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2635, '库存盘点单删除', 'erp:stock-check:delete', 3, 4, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2636, '库存盘点单导出', 'erp:stock-check:export', 3, 5, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2637, '库存盘点单审批', 'erp:stock-check:update-status', 3, 6, 2631, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2638, '销售订单', '', 2, 1, 2617, 'order', 'fa:first-order', 'erp/sale/order/index', 'ErpSaleOrder', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-10 21:59:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2639, '销售订单查询', 'erp:sale-order:query', 3, 1, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2640, '销售订单创建', 'erp:sale-order:create', 3, 2, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2641, '销售订单更新', 'erp:sale-order:update', 3, 3, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2642, '销售订单删除', 'erp:sale-order:delete', 3, 4, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2643, '销售订单导出', 'erp:sale-order:export', 3, 5, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2644, '销售订单审批', 'erp:sale-order:update-status', 3, 6, 2638, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2645, '财务管理', '', 1, 50, 2563, 'finance', 'ep:money', '', '', 0, '1', '1', '1', '1', '2024-02-10 08:05:58', '1', '2024-02-10 08:06:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2646, '结算账户', '', 2, 10, 2645, 'account', 'fa:universal-access', 'erp/finance/account/index', 'ErpAccount', 0, '1', '1', '1', '', '2024-02-10 00:15:07', '1', '2024-02-14 08:24:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2647, '结算账户查询', 'erp:account:query', 3, 1, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2648, '结算账户创建', 'erp:account:create', 3, 2, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2649, '结算账户更新', 'erp:account:update', 3, 3, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2650, '结算账户删除', 'erp:account:delete', 3, 4, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2651, '结算账户导出', 'erp:account:export', 3, 5, 2646, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2652, '销售出库', '', 2, 2, 2617, 'out', 'ep:sold-out', 'erp/sale/out/index', 'ErpSaleOut', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-10 22:02:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2653, '销售出库查询', 'erp:sale-out:query', 3, 1, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2654, '销售出库创建', 'erp:sale-out:create', 3, 2, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2655, '销售出库更新', 'erp:sale-out:update', 3, 3, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2656, '销售出库删除', 'erp:sale-out:delete', 3, 4, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2657, '销售出库导出', 'erp:sale-out:export', 3, 5, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2658, '销售出库审批', 'erp:sale-out:update-status', 3, 6, 2652, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2659, '销售退货', '', 2, 3, 2617, 'return', 'fa-solid:bone', 'erp/sale/return/index', 'ErpSaleReturn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 06:12:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2660, '销售退货查询', 'erp:sale-return:query', 3, 1, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2661, '销售退货创建', 'erp:sale-return:create', 3, 2, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2662, '销售退货更新', 'erp:sale-return:update', 3, 3, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2663, '销售退货删除', 'erp:sale-return:delete', 3, 4, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2664, '销售退货导出', 'erp:sale-return:export', 3, 5, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2665, '销售退货审批', 'erp:sale-return:update-status', 3, 6, 2659, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2666, '采购订单', '', 2, 1, 2602, 'order', 'fa-solid:border-all', 'erp/purchase/order/index', 'ErpPurchaseOrder', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 08:51:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2667, '采购订单查询', 'erp:purchase-order:query', 3, 1, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2668, '采购订单创建', 'erp:purchase-order:create', 3, 2, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2669, '采购订单更新', 'erp:purchase-order:update', 3, 3, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2670, '采购订单删除', 'erp:purchase-order:delete', 3, 4, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2671, '采购订单导出', 'erp:purchase-order:export', 3, 5, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2672, '采购订单审批', 'erp:purchase-order:update-status', 3, 6, 2666, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2673, '采购入库', '', 2, 2, 2602, 'in', 'fa-solid:gopuram', 'erp/purchase/in/index', 'ErpPurchaseIn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 11:19:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2674, '采购入库查询', 'erp:purchase-in:query', 3, 1, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2675, '采购入库创建', 'erp:purchase-in:create', 3, 2, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2676, '采购入库更新', 'erp:purchase-in:update', 3, 3, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2677, '采购入库删除', 'erp:purchase-in:delete', 3, 4, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2678, '采购入库导出', 'erp:purchase-in:export', 3, 5, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2679, '采购入库审批', 'erp:purchase-in:update-status', 3, 6, 2673, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2680, '采购退货', '', 2, 3, 2602, 'return', 'ep:minus', 'erp/purchase/return/index', 'ErpPurchaseReturn', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-12 20:51:02', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2681, '采购退货查询', 'erp:purchase-return:query', 3, 1, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2682, '采购退货创建', 'erp:purchase-return:create', 3, 2, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2683, '采购退货更新', 'erp:purchase-return:update', 3, 3, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2684, '采购退货删除', 'erp:purchase-return:delete', 3, 4, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2685, '采购退货导出', 'erp:purchase-return:export', 3, 5, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2686, '采购退货审批', 'erp:purchase-return:update-status', 3, 6, 2680, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2687, '付款单', '', 2, 1, 2645, 'payment', 'ep:caret-right', 'erp/finance/payment/index', 'ErpFinancePayment', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-14 08:24:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2688, '付款单查询', 'erp:finance-payment:query', 3, 1, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2689, '付款单创建', 'erp:finance-payment:create', 3, 2, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2690, '付款单更新', 'erp:finance-payment:update', 3, 3, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2691, '付款单删除', 'erp:finance-payment:delete', 3, 4, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2692, '付款单导出', 'erp:finance-payment:export', 3, 5, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2693, '付款单审批', 'erp:finance-payment:update-status', 3, 6, 2687, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2694, '收款单', '', 2, 2, 2645, 'receipt', 'ep:expand', 'erp/finance/receipt/index', 'ErpFinanceReceipt', 0, '1', '1', '1', '', '2024-02-05 16:08:56', '1', '2024-02-15 19:35:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2695, '收款单查询', 'erp:finance-receipt:query', 3, 1, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2696, '收款单创建', 'erp:finance-receipt:create', 3, 2, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2697, '收款单更新', 'erp:finance-receipt:update', 3, 3, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2698, '收款单删除', 'erp:finance-receipt:delete', 3, 4, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2699, '收款单导出', 'erp:finance-receipt:export', 3, 5, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2700, '收款单审批', 'erp:finance-receipt:update-status', 3, 6, 2694, '', '', '', NULL, 0, '1', '1', '1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2701, '待办事项', '', 2, 0, 2397, 'backlog', 'fa-solid:tasks', 'crm/backlog/index', 'CrmBacklog', 0, '1', '1', '1', '1', '2024-02-17 17:17:11', '1', '2024-02-17 17:17:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2702, 'ERP 首页', 'erp:statistics:query', 2, 0, 2563, 'home', 'ep:home-filled', 'erp/home/index.vue', 'ErpHome', 0, '1', '1', '1', '1', '2024-02-18 16:49:40', '1', '2024-02-26 21:12:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2703, '商机状态配置', '', 2, 4, 2524, 'business-status', 'fa-solid:charging-station', 'crm/business/status/index', 'CrmBusinessStatus', 0, '1', '1', '1', '1', '2024-02-21 20:15:17', '1', '2024-02-21 20:15:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2704, '商机状态查询', 'crm:business-status:query', 3, 1, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:35:36', '1', '2024-02-21 20:36:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2705, '商机状态创建', 'crm:business-status:create', 3, 2, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:35:57', '1', '2024-02-21 20:35:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2706, '商机状态更新', 'crm:business-status:update', 3, 3, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:36:21', '1', '2024-02-21 20:36:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2707, '商机状态删除', 'crm:business-status:delete', 3, 4, 2703, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-21 20:36:36', '1', '2024-02-21 20:36:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2708, '合同配置', '', 2, 5, 2524, 'contract-config', 'ep:connection', 'crm/contract/config/index', 'CrmContractConfig', 0, '1', '1', '1', '1', '2024-02-24 16:44:40', '1', '2024-02-24 16:44:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2709, '客户公海配置查询', 'crm:customer-pool-config:query', 3, 2, 2516, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:45:19', '1', '2024-02-24 16:45:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2710, '合同配置更新', 'crm:contract-config:update', 3, 1, 2708, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:45:56', '1', '2024-02-24 16:45:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2711, '合同配置查询', 'crm:contract-config:query', 3, 2, 2708, '', '', '', '', 0, '1', '1', '1', '1', '2024-02-24 16:46:16', '1', '2024-02-24 16:46:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2712, '客户分析', 'crm:statistics-customer:query', 2, 0, 2560, 'customer', 'ep:avatar', 'crm/statistics/customer/index.vue', 'CrmStatisticsCustomer', 0, '1', '1', '1', '1', '2024-03-09 16:43:56', '1', '2024-05-04 20:38:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2713, '抄送我的', 'bpm:process-instance-cc:query', 2, 30, 1200, 'copy', 'ep:copy-document', 'bpm/task/copy/index', 'BpmProcessInstanceCopy', 0, '1', '1', '1', '1', '2024-03-17 21:50:23', '1', '2024-04-24 19:55:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2714, '流程分类', '', 2, 3, 1186, 'category', 'fa:object-ungroup', 'bpm/category/index', 'BpmCategory', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-21 23:51:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2715, '分类查询', 'bpm:category:query', 3, 1, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2716, '分类创建', 'bpm:category:create', 3, 2, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2717, '分类更新', 'bpm:category:update', 3, 3, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2718, '分类删除', 'bpm:category:delete', 3, 4, 2714, '', '', '', '', 0, '1', '1', '1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2720, '发起流程', '', 2, 0, 1200, 'create', 'fa-solid:grin-stars', 'bpm/processInstance/create/index', 'BpmProcessInstanceCreate', 0, '1', '0', '1', '1', '2024-03-19 19:46:05', '1', '2024-03-23 19:03:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2721, '流程实例', '', 2, 10, 1186, 'process-instance/manager', 'fa:square', 'bpm/processInstance/manager/index', 'BpmProcessInstanceManager', 0, '1', '1', '1', '1', '2024-03-21 23:57:30', '1', '2024-03-21 23:57:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2722, '流程实例的查询（管理员）', 'bpm:process-instance:manager-query', 3, 1, 2721, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:18:27', '1', '2024-03-22 08:19:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2723, '流程实例的取消（管理员）', 'bpm:process-instance:cancel-by-admin', 3, 2, 2721, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:19:25', '1', '2024-03-22 08:19:25', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2724, '流程任务', '', 2, 11, 1186, 'process-tasnk', 'ep:collection-tag', 'bpm/task/manager/index', 'BpmManagerTask', 0, '1', '1', '1', '1', '2024-03-22 08:43:22', '1', '2024-03-22 08:43:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2725, '流程任务的查询（管理员）', 'bpm:task:mananger-query', 3, 1, 2724, '', '', '', '', 0, '1', '1', '1', '1', '2024-03-22 08:43:49', '1', '2024-03-22 08:43:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2726, '流程监听器', '', 2, 5, 1186, 'process-listener', 'fa:assistive-listening-systems', 'bpm/processListener/index', 'BpmProcessListener', 0, '1', '1', '1', '', '2024-03-09 16:05:34', '1', '2024-03-23 13:13:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2727, '流程监听器查询', 'bpm:process-listener:query', 3, 1, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2728, '流程监听器创建', 'bpm:process-listener:create', 3, 2, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2729, '流程监听器更新', 'bpm:process-listener:update', 3, 3, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2730, '流程监听器删除', 'bpm:process-listener:delete', 3, 4, 2726, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 16:05:34', '', '2024-03-09 16:05:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2731, '流程表达式', '', 2, 6, 1186, 'process-expression', 'fa:wpexplorer', 'bpm/processExpression/index', 'BpmProcessExpression', 0, '1', '1', '1', '', '2024-03-09 22:35:08', '1', '2024-03-23 19:43:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2732, '流程表达式查询', 'bpm:process-expression:query', 3, 1, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2733, '流程表达式创建', 'bpm:process-expression:create', 3, 2, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2734, '流程表达式更新', 'bpm:process-expression:update', 3, 3, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2735, '流程表达式删除', 'bpm:process-expression:delete', 3, 4, 2731, '', '', '', NULL, 0, '1', '1', '1', '', '2024-03-09 22:35:08', '', '2024-03-09 22:35:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2736, '员工业绩', 'crm:statistics-performance:query', 2, 3, 2560, 'performance', 'ep:dish-dot', 'crm/statistics/performance/index', 'CrmStatisticsPerformance', 0, '1', '1', '1', '1', '2024-04-05 13:49:20', '1', '2024-04-24 19:42:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2737, '客户画像', 'crm:statistics-portrait:query', 2, 4, 2560, 'portrait', 'ep:picture', 'crm/statistics/portrait/index', 'CrmStatisticsPortrait', 0, '1', '1', '1', '1', '2024-04-05 13:57:40', '1', '2024-04-24 19:42:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2738, '销售漏斗', 'crm:statistics-funnel:query', 2, 5, 2560, 'funnel', 'ep:grape', 'crm/statistics/funnel/index', 'CrmStatisticsFunnel', 0, '1', '1', '1', '1', '2024-04-13 10:53:26', '1', '2024-04-24 19:39:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2739, '消息中心', '', 1, 7, 1, 'messages', 'ep:chat-dot-round', '', '', 0, '1', '1', '1', '1', '2024-04-22 23:54:30', '1', '2024-04-23 09:36:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2740, '监控中心', '', 1, 10, 2, 'monitors', 'ep:monitor', '', '', 0, '1', '1', '1', '1', '2024-04-23 00:04:44', '1', '2024-04-23 00:04:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2741, '领取公海客户', 'crm:customer:receive', 3, 1, 2546, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:47:45', '1', '2024-04-24 19:47:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2742, '分配公海客户', 'crm:customer:distribute', 3, 2, 2546, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:48:05', '1', '2024-04-24 19:48:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2743, '商品统计查询', 'statistics:product:query', 3, 1, 2545, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:50:05', '1', '2024-04-24 19:50:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2744, '商品统计导出', 'statistics:product:export', 3, 2, 2545, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:50:26', '1', '2024-04-24 19:50:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2745, '支付渠道查询', 'pay:channel:query', 3, 10, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:01', '1', '2024-04-24 19:53:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2746, '支付渠道创建', 'pay:channel:create', 3, 11, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:18', '1', '2024-04-24 19:53:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2747, '支付渠道更新', 'pay:channel:update', 3, 12, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:53:32', '1', '2024-04-24 19:53:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2748, '支付渠道删除', 'pay:channel:delete', 3, 13, 1126, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:54:34', '1', '2024-04-24 19:54:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2749, '商品收藏查询', 'product:favorite:query', 3, 10, 2014, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:55:47', '1', '2024-04-24 19:55:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2750, '商品浏览查询', 'product:browse-history:query', 3, 20, 2014, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:57:43', '1', '2024-04-24 19:57:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2751, '售后同意', 'trade:after-sale:agree', 3, 2, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:58:40', '1', '2024-04-24 19:58:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2752, '售后不同意', 'trade:after-sale:disagree', 3, 3, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 19:59:03', '1', '2024-04-24 19:59:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2753, '售后确认退货', 'trade:after-sale:receive', 3, 4, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:00:07', '1', '2024-04-24 20:00:07', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2754, '售后确认退款', 'trade:after-sale:refund', 3, 5, 2073, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:00:24', '1', '2024-04-24 20:00:24', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2755, '删除项目', 'report:go-view-project:delete', 3, 2, 2153, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:01:37', '1', '2024-04-24 20:01:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2756, '会员等级记录查询', 'member:level-record:query', 3, 10, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:32', '1', '2024-04-24 20:02:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2757, '会员经验记录查询', 'member:experience-record:query', 3, 11, 2325, '', '', '', '', 0, '1', '1', '1', '1', '2024-04-24 20:02:51', '1', '2024-04-24 20:02:51', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2758, 'AI 大模型', '', 1, 400, 0, '/ai', 'tabler:ai', '', '', 0, '1', '1', '1', '1', '2024-05-07 15:07:56', '1', '2025-04-19 18:57:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2759, 'AI 对话', '', 2, 1, 2758, 'chat', 'ep:message', 'ai/chat/index/index.vue', 'AiChat', 0, '1', '1', '1', '1', '2024-05-07 15:09:14', '1', '2024-07-07 17:15:36', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2760, '控制台', '', 1, 100, 2758, 'console', 'ep:setting', '', '', 0, '1', '1', '1', '1', '2024-05-09 22:39:09', '1', '2024-05-24 23:34:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2761, 'API 密钥', '', 2, 0, 2760, 'api-key', 'ep:key', 'ai/model/apiKey/index.vue', 'AiApiKey', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-10 22:44:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2762, 'API 密钥查询', 'ai:api-key:query', 3, 1, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, '1', '1', '1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2767, '模型配置', '', 2, 0, 2760, 'model', 'fa-solid:abacus', 'ai/model/model/index.vue', 'AiModel', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:57:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2768, '聊天模型查询', 'ai:model:query', 3, 1, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:19:46', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2769, '聊天模型创建', 'ai:model:create', 3, 2, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2770, '聊天模型更新', 'ai:model:update', 3, 3, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:14', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2771, '聊天模型删除', 'ai:model:delete', 3, 4, 2767, '', '', '', '', 0, '1', '1', '1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, '1', '1', '1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2776, '聊天角色更新', 'ai:chat-role:update', 3, 3, 2773, '', '', '', NULL, 0, '1', '1', '1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2777, '聊天角色删除', 'ai:chat-role:delete', 3, 4, 2773, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-13 21:43:38', '1', '2024-05-13 21:43:38', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2778, '聊天管理', '', 2, 10, 2760, 'chat-conversation', 'ep:chat-square', 'ai/chat/manager/index.vue', 'AiChatManager', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-06-26 21:36:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2779, '会话查询', 'ai:chat-conversation:query', 3, 1, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2780, '会话删除', 'ai:chat-conversation:delete', 3, 2, 2778, '', '', '', '', 0, '1', '1', '1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2781, '消息查询', 'ai:chat-message:query', 3, 11, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:38:56', '1', '2024-05-25 08:38:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2782, '消息删除', 'ai:chat-message:delete', 3, 12, 2778, '', '', '', '', 0, '1', '1', '1', '1', '2024-05-25 08:39:10', '1', '2024-05-25 08:39:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2783, 'AI 绘画', '', 2, 2, 2758, 'image', 'ep:picture-rounded', 'ai/image/index/index.vue', 'AiImage', 0, '1', '1', '1', '1', '2024-05-26 11:45:17', '1', '2024-07-07 17:18:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2784, '绘画管理', '', 2, 11, 2760, 'image', 'fa:file-image-o', 'ai/image/manager/index.vue', 'AiImageManager', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 21:37:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2785, '绘画查询', 'ai:image:query', 3, 1, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:21:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2786, '绘画删除', 'ai:image:delete', 3, 4, 2784, '', '', '', '', 0, '1', '1', '1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:22:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2787, '绘图更新', 'ai:image:update', 3, 2, 2784, '', '', '', '', 0, '1', '1', '1', '1', '2024-06-26 22:47:56', '1', '2024-08-31 09:21:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2788, '音乐管理', '', 2, 12, 2760, 'music', 'fa:music', 'ai/music/manager/index.vue', 'AiMusicManager', 0, '1', '1', '1', '', '2024-06-27 15:03:33', '1', '2024-06-27 23:04:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2789, '音乐查询', 'ai:music:query', 3, 1, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2790, '音乐更新', 'ai:music:update', 3, 3, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2791, '音乐删除', 'ai:music:delete', 3, 4, 2788, '', '', '', NULL, 0, '1', '1', '1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2792, 'AI 写作', '', 2, 3, 2758, 'write', 'fa-solid:book-reader', 'ai/write/index/index.vue', 'AiWrite', 0, '1', '1', '1', '1', '2024-07-08 09:26:44', '1', '2024-07-16 13:03:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2793, '写作管理', '', 2, 13, 2760, 'write', 'fa:bookmark-o', 'ai/write/manager/index.vue', 'AiWriteManager', 0, '1', '1', '1', '', '2024-07-10 13:24:34', '1', '2024-07-10 21:31:59', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2794, 'AI 写作查询', 'ai:write:query', 3, 1, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, '1', '1', '1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, '1', '1', '1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, '1', '1', '1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2798, 'AI 思维导图', '', 2, 6, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, '1', '1', '1', '1', '2024-07-29 21:31:59', '1', '2025-03-02 18:57:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, '1', '1', '1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2802, '会话查询', 'promotion:kefu-conversation:query', 3, 1, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:17:52', '1', '2024-08-31 09:18:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2803, '会话更新', 'promotion:kefu-conversation:update', 3, 2, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:15', '1', '2024-08-31 09:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2804, '消息查询', 'promotion:kefu-message:query', 3, 10, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:18:42', '1', '2024-08-31 09:18:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2805, '会话删除', 'promotion:kefu-conversation:delete', 3, 3, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:19:51', '1', '2024-08-31 09:20:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2806, '消息发送', 'promotion:kefu-message:send', 3, 12, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:06', '1', '2024-08-31 09:20:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2807, '消息更新', 'promotion:kefu-message:update', 3, 11, 2797, '', '', '', '', 0, '1', '1', '1', '1', '2024-08-31 09:20:22', '1', '2024-08-31 09:20:22', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2808, '积分商城', '', 2, 5, 2030, 'point-activity', 'ep:bowl', 'mall/promotion/point/activity/index', 'PointActivity', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-23 09:14:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2809, '积分商城活动查询', 'promotion:point-activity:query', 3, 1, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2810, '积分商城活动创建', 'promotion:point-activity:create', 3, 2, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2811, '积分商城活动更新', 'promotion:point-activity:update', 3, 3, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2812, '积分商城活动删除', 'promotion:point-activity:delete', 3, 4, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2813, '积分商城活动导出', 'promotion:point-activity:export', 3, 5, 2808, '', '', '', '', 0, '1', '1', '1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, '1', '1', '1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-17 19:32:06', '1', '2025-01-17 19:32:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-23 20:23:34', '1', '2025-01-23 20:23:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2915, 'AI 知识库', '', 2, 5, 2758, 'knowledge', 'ep:notebook', 'ai/knowledge/knowledge/index', 'AiKnowledge', 0, '1', '1', '1', '', '2025-02-28 07:04:21', '1', '2025-03-02 18:58:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2916, 'AI 知识库查询', 'ai:knowledge:query', 3, 1, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2917, 'AI 知识库创建', 'ai:knowledge:create', 3, 2, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2918, 'AI 知识库更新', 'ai:knowledge:update', 3, 3, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2919, 'AI 知识库删除', 'ai:knowledge:delete', 3, 4, 2915, '', '', '', NULL, 0, '1', '1', '1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2920, '工具管理', '', 2, 0, 2760, 'tool', 'fa-solid:tools', 'ai/model/tool/index.vue', 'AiTool', 0, '1', '1', '1', '', '2025-03-14 11:19:29', '1', '2025-03-14 19:20:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2921, '工具查询', 'ai:tool:query', 3, 1, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, '1', '1', '1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4000, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, '1', '1', '1', '1', '2024-08-10 09:55:28', '1', '2024-12-07 15:58:34', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4001, '设备接入', '', 1, 2, 4000, 'device', 'ep:platform', '', '', 0, '1', '1', '1', '1', '2024-08-10 09:57:56', '1', '2025-02-27 08:39:49', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4002, '产品管理', '', 2, 1, 4001, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, '1', '1', '1', '', '2024-08-10 02:38:02', '1', '2025-06-15 20:56:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4003, '产品查询', 'iot:product:query', 3, 1, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4004, '产品创建', 'iot:product:create', 3, 2, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4005, '产品更新', 'iot:product:update', 3, 3, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4006, '产品删除', 'iot:product:delete', 3, 4, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4007, '产品导出', 'iot:product:export', 3, 5, 4002, '', '', '', NULL, 0, '1', '1', '1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:13', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4008, '设备管理', '', 2, 2, 4001, 'device', 'fa:mobile', 'iot/device/device/index', 'IoTDevice', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2025-06-15 20:56:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4009, '设备查询', 'iot:device:query', 3, 1, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4010, '设备创建', 'iot:device:create', 3, 2, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4011, '设备更新', 'iot:device:update', 3, 3, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4012, '设备删除', 'iot:device:delete', 3, 4, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4013, '设备导出', 'iot:device:export', 3, 5, 4008, '', '', '', '', 0, '1', '1', '1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:44', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4014, '产品分类', '', 2, 3, 4001, 'product-category', 'ep:notebook', 'iot/product/category/index', 'IotProductCategory', 0, '1', '1', '1', '', '2024-12-07 16:01:35', '1', '2025-06-15 20:56:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4015, '产品分类查询', 'iot:product-category:query', 3, 1, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4016, '产品分类创建', 'iot:product-category:create', 3, 2, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4017, '产品分类更新', 'iot:product-category:update', 3, 3, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4018, '产品分类删除', 'iot:product-category:delete', 3, 4, 4014, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4031, '设备分组', '', 2, 3, 4001, 'device-group', 'fa-solid:layer-group', 'iot/device/group/index', 'IotDeviceGroup', 0, '1', '1', '1', '', '2024-12-14 17:08:29', '1', '2024-12-14 17:09:17', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4032, '设备分组查询', 'iot:device-group:query', 3, 1, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4033, '设备分组创建', 'iot:device-group:create', 3, 2, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4034, '设备分组更新', 'iot:device-group:update', 3, 3, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4035, '设备分组删除', 'iot:device-group:delete', 3, 4, 4031, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4036, '设备导入', 'iot:device:import', 3, 6, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2024-12-15 10:35:47', '1', '2024-12-15 10:35:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4037, '产品物模型', '', 2, 99, 4001, 'thing-model', 'ep:mostly-cloudy', 'iot/thingmodel/index', 'IoTThingModel', 0, '0', '0', '0', '', '2024-12-16 17:17:50', '1', '2025-06-15 20:56:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4038, '产品物模型功能查询', 'iot:thing-model:query', 3, 1, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:51', '', '2025-03-17 09:14:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4039, '产品物模型功能创建', 'iot:thing-model:create', 3, 2, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:14:58', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4040, '产品物模型功能更新', 'iot:thing-model:update', 3, 3, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4041, '产品物模型功能删除', 'iot:thing-model:delete', 3, 4, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:06', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4042, '产品物模型功能导出', 'iot:thing-model:export', 3, 5, 4037, '', '', '', NULL, 0, '1', '1', '1', '', '2024-12-16 17:17:53', '', '2025-03-17 09:15:09', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4043, '设备消息发送', 'iot:device:message-send', 3, 12, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 04:40:16', '1', '2025-06-14 14:09:26', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4044, '设备属性查询', 'iot:device:property-query', 3, 10, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 11:52:54', '1', '2025-01-28 11:52:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4045, '设备消息查询', 'iot:device:message-query', 3, 11, 4008, '', '', '', '', 0, '1', '1', '1', '1', '2025-01-28 11:53:22', '1', '2025-06-14 11:11:20', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4047, '运维管理', '', 1, 4, 4000, 'operation', 'fa:align-center', '', '', 0, '1', '1', '1', '1', '2025-02-05 22:21:37', '\"1\"', '2025-06-30 20:12:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4048, '规则引擎', '', 1, 3, 4000, 'rule', 'fa-solid:cogs', '', '', 0, '1', '1', '1', '1', '2025-02-11 14:10:54', '1', '2025-02-11 14:10:54', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4049, '场景联动', '', 2, 1, 4048, 'scene', 'ep:link', 'iot/rule/scene/index', 'IoTSceneRule', 0, '1', '1', '1', '1', '2025-02-11 14:12:44', '\"1\"', '2025-08-09 15:38:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4050, 'IoT 首页', '', 2, 1, 4000, 'home', 'ep:home-filled', 'iot/home/index', 'IotHome', 0, '1', '1', '1', '1', '2025-02-27 08:39:35', '1', '2025-06-24 14:22:50', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4051, '数据流转', '', 2, 2, 4048, 'data-rule', 'ep:guide', 'iot/rule/data/index', 'IoTDataRule', 0, '1', '1', '1', '', '2025-03-09 13:47:11', '\"1\"', '2025-08-09 15:38:37', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4052, '数据流转规则查询', 'iot:data-rule:query', 3, 1, 4051, '', '', '', '', 0, '1', '1', '1', '', '2025-03-09 13:47:11', '1', '2025-06-24 20:48:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4053, '数据流转规则创建', 'iot:data-rule:create', 3, 2, 4051, '', '', '', '', 0, '1', '1', '1', '', '2025-03-09 13:47:11', '1', '2025-06-24 20:48:08', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4054, '数据流转规则更新', 'iot:data-rule:update', 3, 3, 4051, '', '', '', '', 0, '1', '1', '1', '', '2025-03-09 13:47:11', '1', '2025-06-24 20:48:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4055, '数据流转规则删除', 'iot:data-rule:delete', 3, 4, 4051, '', '', '', '', 0, '1', '1', '1', '', '2025-03-09 13:47:12', '1', '2025-06-24 20:48:15', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5000, 'AI 工作流', '', 2, 5, 2758, 'workflow', 'fa:hand-grab-o', 'ai/workflow/index.vue', 'AiWorkflow', 0, '1', '1', '1', '1', '2025-03-25 09:50:27', '1', '2025-05-03 18:55:12', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5001, 'AI 工作流查询', 'ai:workflow:query', 3, 1, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:11', '1', '2025-03-25 09:51:11', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5002, 'AI 工作流创建', 'ai:workflow:create', 3, 2, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:28', '1', '2025-03-25 09:51:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5003, 'AI 工作流更新', 'ai:workflow:update', 3, 3, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:42', '1', '2025-03-25 09:51:42', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5004, 'AI 工作流删除', 'ai:workflow:delete', 3, 4, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-25 09:51:55', '1', '2025-03-25 09:52:03', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5005, 'AI 工作流测试', 'ai:workflow:test', 3, 5, 5000, '', '', '', '', 0, '1', '1', '1', '1', '2025-03-30 10:29:41', '1', '2025-03-30 10:29:41', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5009, '仪表盘设计器', '', 2, 1, 1281, 'jimu-bi', 'fa:y-combinator', 'report/jmreport/bi', 'JimuBI', 0, '1', '1', '1', '1', '2025-05-03 09:57:15', '1', '2025-05-03 10:02:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5010, '租户切换', 'system:tenant:visit', 3, 999, 1138, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-05 15:25:32', '1', '2025-05-05 15:25:32', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5011, '转账订单查询', 'pay:transfer:query', 3, 1, 2559, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-08 12:46:53', '1', '2025-05-08 12:46:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5012, '转账订单导出', 'pay:transfer:export', 3, 2, 2559, '', '', '', '', 0, '1', '1', '1', '1', '2025-05-10 17:00:28', '1', '2025-05-10 17:00:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5013, '场景联动查询', 'iot:rule-scene:query', 3, 1, 4049, '', '', '', '', 0, '1', '1', '1', '1', '2025-06-20 16:53:01', '1', '2025-06-20 16:53:01', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5014, '场景联动创建', 'iot:rule-scene:create', 3, 2, 4049, '', '', '', '', 0, '1', '1', '1', '1', '2025-06-20 16:54:31', '1', '2025-06-20 16:54:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5015, '场景联动更新', 'iot:rule-scene:update', 3, 3, 4049, '', '', '', '', 0, '1', '1', '1', '1', '2025-06-20 16:54:47', '1', '2025-06-20 16:54:47', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5016, '场景联动删除', 'iot:rule-scene:delete', 3, 4, 4049, '', '', '', '', 0, '1', '1', '1', '1', '2025-06-20 16:55:04', '1', '2025-06-20 16:55:27', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5017, '场景联动导出', 'iot:rule-scene:export', 3, 5, 4049, '', '', '', '', 0, '1', '1', '1', '1', '2025-06-20 16:57:56', '1', '2025-06-20 16:57:56', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5018, '数据流转目的查询', 'iot:data-sink:query', 3, 11, 4051, '', '', '', '', 0, '1', '1', '1', '1', '2025-06-24 20:48:40', '1', '2025-06-24 20:48:40', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5019, '数据流转目的创建', 'iot:data-sink:create', 3, 12, 4051, '', '', '', '', 0, '1', '1', '1', '1', '2025-06-24 20:48:57', '1', '2025-06-24 20:48:57', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5020, '数据流转目的更新', 'iot:data-sink:update', 3, 13, 4051, '', '', '', '', 0, '1', '1', '1', '1', '2025-06-24 20:49:10', '1', '2025-06-24 20:49:10', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5021, '数据流转目的删除', 'iot:data-sink:delete', 3, 14, 4051, '', '', '', '', 0, '1', '1', '1', '1', '2025-06-24 20:49:23', '1', '2025-06-24 20:49:23', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5022, '告警配置', '', 2, 1, 5028, 'config', 'fa:connectdevelop', 'iot/alert/config/index', 'IotAlertConfig', 0, '1', '1', '1', '', '2025-06-27 14:28:59', '1', '2025-06-27 22:31:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5023, '告警配置查询', 'iot:alert-config:query', 3, 1, 5022, '', '', '', '', 0, '1', '1', '1', '', '2025-06-27 14:28:59', '1', '2025-06-28 16:00:31', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5024, '告警配置创建', 'iot:alert-config:create', 3, 2, 5022, '', '', '', '', 0, '1', '1', '1', '', '2025-06-27 14:28:59', '1', '2025-06-28 16:00:35', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5025, '告警配置更新', 'iot:alert-config:update', 3, 3, 5022, '', '', '', '', 0, '1', '1', '1', '', '2025-06-27 14:28:59', '1', '2025-06-28 16:00:43', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5026, '告警配置删除', 'iot:alert-config:delete', 3, 4, 5022, '', '', '', '', 0, '1', '1', '1', '', '2025-06-27 14:29:00', '1', '2025-06-28 16:00:39', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5028, '告警中心', '', 1, 3, 4000, 'alert', 'fa:soundcloud', '', '', 0, '1', '1', '1', '1', '2025-06-27 22:30:04', '1', '2025-06-27 22:30:19', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5029, '告警记录', '', 2, 2, 5028, 'record', 'fa-solid:record-vinyl', 'iot/alert/record/index', 'IotAlertRecord', 0, '1', '1', '1', '', '2025-06-28 07:59:32', '1', '2025-06-28 16:01:48', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5030, '告警记录查询', 'iot:alert-record:query', 3, 1, 5029, '', '', '', '', 0, '1', '1', '1', '', '2025-06-28 07:59:32', '1', '2025-06-28 16:00:53', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5031, '告警记录处理', 'iot:alert-record:process', 3, 2, 5029, '', '', '', '', 0, '1', '1', '1', '', '2025-06-28 07:59:32', '1', '2025-06-28 16:01:04', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5032, 'OTA 固件', '', 2, 1, 4047, 'ota/firmware', 'fa-solid:award', 'iot/ota/firmware/index', 'IoTOtaFirmware', 0, '1', '1', '1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 20:13:28', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5033, 'OTA 固件查询', 'iot:ota-firmware:query', 3, 1, 5032, '', '', '', '', 0, '1', '1', '1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:12', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:21', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:29', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:37', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2025-07-02 23:56:56', '1', '2025-07-02 23:56:56', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2025-07-02 23:57:26', '1', '2025-07-02 23:57:26', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2025-07-02 23:57:52', '1', '2025-07-02 23:57:52', '0');\nINSERT INTO system_menu (id, name, 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, '1', '1', '1', '1', '2025-07-02 23:58:30', '1', '2025-07-02 23:58:30', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5041, 'OTA 升级记录取消', 'iot:ota-task-record:cancel', 3, 23, 5032, '', '', '', '', 0, '1', '1', '1', '1', '2025-07-02 23:59:18', '1', '2025-07-02 23:59:18', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5042, '模版消息', '', 2, 5, 2084, 'message-template', 'ep:notebook', 'mp/messageTemplate/index', 'MpMessageTemplate', 0, '1', '1', '1', '1', '2025-11-26 16:45:35', '1', '2025-11-26 18:44:52', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5043, '查询模版消息', 'mp:message-template:query', 3, 1, 5042, '', '', '', '', 0, '1', '1', '1', '1', '2025-11-26 17:00:15', '1', '2025-11-26 18:45:00', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5044, '删除模版消息', 'mp:message-template:delete', 3, 2, 5042, '', '', '', '', 0, '1', '1', '1', '1', '2025-11-26 17:00:31', '1', '2025-11-26 18:45:05', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5045, '同步公众号模板', 'mp:message-template:sync', 3, 3, 5042, '', '', '', '', 0, '1', '1', '1', '1', '2025-11-26 17:00:55', '1', '2025-11-26 17:00:55', '0');\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5046, '给粉丝发送模版消息', 'mp:message-template:send', 3, 4, 5042, '', '', '', '', 0, '1', '1', '1', '1', '2025-11-26 17:01:11', '1', '2025-11-26 17:01:11', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_menu_seq;\nCREATE SEQUENCE system_menu_seq\n    START 5047;\n\n-- ----------------------------\n-- Table structure for system_notice\n-- ----------------------------\nDROP TABLE IF EXISTS system_notice;\nCREATE TABLE system_notice (\n    id int8 NOT NULL,\n  title varchar(50) NOT NULL,\n  content text NOT NULL,\n  type int2 NOT NULL,\n  status int2 NOT NULL DEFAULT 0,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_notice ADD CONSTRAINT pk_system_notice PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notice.id IS '公告ID';\nCOMMENT ON COLUMN system_notice.title IS '公告标题';\nCOMMENT ON COLUMN system_notice.content IS '公告内容';\nCOMMENT ON COLUMN system_notice.type IS '公告类型（1通知 2公告）';\nCOMMENT ON COLUMN system_notice.status IS '公告状态（0正常 1关闭）';\nCOMMENT ON COLUMN system_notice.creator IS '创建者';\nCOMMENT ON COLUMN system_notice.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notice.updater IS '更新者';\nCOMMENT ON COLUMN system_notice.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notice.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notice.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notice IS '通知公告表';\n\n-- ----------------------------\n-- Records of system_notice\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '芋道的公众', '<p>新版本内容133222</p>', 1, 0, 'admin', '2021-01-05 17:03:48', '\"1\"', '2025-08-31 09:38:22', '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '维护通知：2018-07-01 系统凌晨维护', '<p><img src=\"http://test.yudao.iocoder.cn/b7cb3cf49b4b3258bf7309a09dd2f4e5.jpg\" alt=\"\" data-href=\"\">11112222<img src=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\" alt=\"image\" data-href=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\">3333</p>', 2, 1, 'admin', '2021-01-05 17:03:48', '1', '2025-04-18 23:56:40', '0', 1);\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '我是测试标题', '<p>哈哈哈哈123</p>', 1, 0, '110', '2022-02-22 01:01:25', '110', '2022-02-22 01:01:46', '0', 121);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_notice_seq;\nCREATE SEQUENCE system_notice_seq\n    START 5;\n\n-- ----------------------------\n-- Table structure for system_notify_message\n-- ----------------------------\nDROP TABLE IF EXISTS system_notify_message;\nCREATE TABLE system_notify_message (\n    id int8 NOT NULL,\n  user_id int8 NOT NULL,\n  user_type int2 NOT NULL,\n  template_id int8 NOT NULL,\n  template_code varchar(64) NOT NULL,\n  template_nickname varchar(63) NOT NULL,\n  template_content varchar(1024) NOT NULL,\n  template_type int4 NOT NULL,\n  template_params varchar(255) NOT NULL,\n  read_status bool NOT NULL,\n  read_time timestamp NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_notify_message ADD CONSTRAINT pk_system_notify_message PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notify_message.id IS '用户ID';\nCOMMENT ON COLUMN system_notify_message.user_id IS '用户id';\nCOMMENT ON COLUMN system_notify_message.user_type IS '用户类型';\nCOMMENT ON COLUMN system_notify_message.template_id IS '模版编号';\nCOMMENT ON COLUMN system_notify_message.template_code IS '模板编码';\nCOMMENT ON COLUMN system_notify_message.template_nickname IS '模版发送人名称';\nCOMMENT ON COLUMN system_notify_message.template_content IS '模版内容';\nCOMMENT ON COLUMN system_notify_message.template_type IS '模版类型';\nCOMMENT ON COLUMN system_notify_message.template_params IS '模版参数';\nCOMMENT ON COLUMN system_notify_message.read_status IS '是否已读';\nCOMMENT ON COLUMN system_notify_message.read_time IS '阅读时间';\nCOMMENT ON COLUMN system_notify_message.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_message.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_message.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_message.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_message.deleted IS '是否删除';\nCOMMENT ON COLUMN system_notify_message.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_notify_message IS '站内信消息表';\n\n-- ----------------------------\n-- Records of system_notify_message\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 11:44:08', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 1, 2, 1, 'test', '123', '我是 1，我开始 2 了', 1, '{\"name\":\"1\",\"what\":\"2\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 11:45:04', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 103, 2, 2, 'register', '系统消息', '你好，欢迎 哈哈 加入大家庭！', 2, '{\"name\":\"哈哈\"}', '0', NULL, '1', '2023-01-28 21:02:20', '1', '2023-01-28 21:02:20', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', '2025-04-21 14:59:37', '1', '2023-01-28 22:21:42', '1', '2025-04-21 14:59:37', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 1, 2, 1, 'test', '123', '我是 芋艿，我开始 写代码 了', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', '1', '2025-04-21 14:59:36', '1', '2023-01-28 22:22:07', '1', '2025-04-21 14:59:36', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 1, 2, 1, 'test', '123', '我是 2，我开始 3 了', 1, '{\"name\":\"2\",\"what\":\"3\"}', '1', '2025-04-21 14:59:35', '1', '2023-01-28 23:45:21', '1', '2025-04-21 14:59:35', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 1, 2, 2, 'register', '系统消息', '你好，欢迎 123 加入大家庭！', 2, '{\"name\":\"123\"}', '1', '2025-04-21 14:59:35', '1', '2023-01-28 23:50:21', '1', '2025-04-21 14:59:35', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-28 08:35:46提现￥0.09元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-28 08:35:46\",\"price\":\"0.09\"}', '0', NULL, '1', '2023-09-28 16:36:22', '1', '2023-09-28 16:36:22', '0', 1);\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 247, 1, 4, 'brokerage_withdraw_audit_approve', 'system', '您在2023-09-30 20:59:40提现￥1.00元的申请已通过审核', 2, '{\"reason\":null,\"createTime\":\"2023-09-30 20:59:40\",\"price\":\"1.00\"}', '0', NULL, '1', '2023-10-03 12:11:34', '1', '2023-10-03 12:11:34', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_notify_message_seq;\nCREATE SEQUENCE system_notify_message_seq\n    START 11;\n\n-- ----------------------------\n-- Table structure for system_notify_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_notify_template;\nCREATE TABLE system_notify_template (\n    id int8 NOT NULL,\n  name varchar(63) NOT NULL,\n  code varchar(64) NOT NULL,\n  nickname varchar(255) NOT NULL,\n  content varchar(1024) NOT NULL,\n  type int2 NOT NULL,\n  params varchar(255) NULL DEFAULT NULL,\n  status int2 NOT NULL,\n  remark varchar(255) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_notify_template ADD CONSTRAINT pk_system_notify_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_notify_template.id IS '主键';\nCOMMENT ON COLUMN system_notify_template.name IS '模板名称';\nCOMMENT ON COLUMN system_notify_template.code IS '模版编码';\nCOMMENT ON COLUMN system_notify_template.nickname IS '发送人名称';\nCOMMENT ON COLUMN system_notify_template.content IS '模版内容';\nCOMMENT ON COLUMN system_notify_template.type IS '类型';\nCOMMENT ON COLUMN system_notify_template.params IS '参数数组';\nCOMMENT ON COLUMN system_notify_template.status IS '状态';\nCOMMENT ON COLUMN system_notify_template.remark IS '备注';\nCOMMENT ON COLUMN system_notify_template.creator IS '创建者';\nCOMMENT ON COLUMN system_notify_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_notify_template.updater IS '更新者';\nCOMMENT ON COLUMN system_notify_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_notify_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_notify_template IS '站内信模板表';\n\nDROP SEQUENCE IF EXISTS system_notify_template_seq;\nCREATE SEQUENCE system_notify_template_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_access_token\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_access_token;\nCREATE TABLE system_oauth2_access_token (\n    id int8 NOT NULL,\n  user_id int8 NOT NULL,\n  user_type int2 NOT NULL,\n  user_info varchar(512) NOT NULL,\n  access_token varchar(255) NOT NULL,\n  refresh_token varchar(32) NOT NULL,\n  client_id varchar(255) NOT NULL,\n  scopes varchar(255) NULL DEFAULT NULL,\n  expires_time timestamp NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_access_token ADD CONSTRAINT pk_system_oauth2_access_token PRIMARY KEY (id);\n\nCREATE INDEX idx_system_oauth2_access_token_01 ON system_oauth2_access_token (access_token);\nCREATE INDEX idx_system_oauth2_access_token_02 ON system_oauth2_access_token (refresh_token);\n\nCOMMENT ON COLUMN system_oauth2_access_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_access_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_access_token.user_info IS '用户信息';\nCOMMENT ON COLUMN system_oauth2_access_token.access_token IS '访问令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_access_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_access_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_access_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_access_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_access_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_access_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_access_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_access_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_access_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_access_token IS 'OAuth2 访问令牌';\n\nDROP SEQUENCE IF EXISTS system_oauth2_access_token_seq;\nCREATE SEQUENCE system_oauth2_access_token_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_approve\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_approve;\nCREATE TABLE system_oauth2_approve (\n    id int8 NOT NULL,\n  user_id int8 NOT NULL,\n  user_type int2 NOT NULL,\n  client_id varchar(255) NOT NULL,\n  scope varchar(255) NOT NULL DEFAULT '',\n  approved bool NOT NULL DEFAULT '0',\n  expires_time timestamp NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_approve ADD CONSTRAINT pk_system_oauth2_approve PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_approve.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_approve.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_approve.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_approve.scope IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_approve.approved IS '是否接受';\nCOMMENT ON COLUMN system_oauth2_approve.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_approve.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_approve.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_approve.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_approve.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_approve.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_approve.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_approve IS 'OAuth2 批准表';\n\nDROP SEQUENCE IF EXISTS system_oauth2_approve_seq;\nCREATE SEQUENCE system_oauth2_approve_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_client\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_client;\nCREATE TABLE system_oauth2_client (\n    id int8 NOT NULL,\n  client_id varchar(255) NOT NULL,\n  secret varchar(255) NOT NULL,\n  name varchar(255) NOT NULL,\n  logo varchar(255) NOT NULL,\n  description varchar(255) NULL DEFAULT NULL,\n  status int2 NOT NULL,\n  access_token_validity_seconds int4 NOT NULL,\n  refresh_token_validity_seconds int4 NOT NULL,\n  redirect_uris varchar(255) NOT NULL,\n  authorized_grant_types varchar(255) NOT NULL,\n  scopes varchar(255) NULL DEFAULT NULL,\n  auto_approve_scopes varchar(255) NULL DEFAULT NULL,\n  authorities varchar(255) NULL DEFAULT NULL,\n  resource_ids varchar(255) NULL DEFAULT NULL,\n  additional_information varchar(4096) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_client ADD CONSTRAINT pk_system_oauth2_client PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_client.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_client.secret IS '客户端密钥';\nCOMMENT ON COLUMN system_oauth2_client.name IS '应用名';\nCOMMENT ON COLUMN system_oauth2_client.logo IS '应用图标';\nCOMMENT ON COLUMN system_oauth2_client.description IS '应用描述';\nCOMMENT ON COLUMN system_oauth2_client.status IS '状态';\nCOMMENT ON COLUMN system_oauth2_client.access_token_validity_seconds IS '访问令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.refresh_token_validity_seconds IS '刷新令牌的有效期';\nCOMMENT ON COLUMN system_oauth2_client.redirect_uris IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_client.authorized_grant_types IS '授权类型';\nCOMMENT ON COLUMN system_oauth2_client.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_client.auto_approve_scopes IS '自动通过的授权范围';\nCOMMENT ON COLUMN system_oauth2_client.authorities IS '权限';\nCOMMENT ON COLUMN system_oauth2_client.resource_ids IS '资源';\nCOMMENT ON COLUMN system_oauth2_client.additional_information IS '附加信息';\nCOMMENT ON COLUMN system_oauth2_client.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_client.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_client.deleted IS '是否删除';\nCOMMENT ON TABLE system_oauth2_client IS 'OAuth2 客户端表';\n\n-- ----------------------------\n-- Records of system_oauth2_client\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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-08-21 10:04:50', '0');\nINSERT 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/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.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-05-02 19:58:08', '0');\nINSERT 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', '0');\nINSERT 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', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_oauth2_client_seq;\nCREATE SEQUENCE system_oauth2_client_seq\n    START 43;\n\n-- ----------------------------\n-- Table structure for system_oauth2_code\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_code;\nCREATE TABLE system_oauth2_code (\n    id int8 NOT NULL,\n  user_id int8 NOT NULL,\n  user_type int2 NOT NULL,\n  code varchar(32) NOT NULL,\n  client_id varchar(255) NOT NULL,\n  scopes varchar(255) NULL DEFAULT '',\n  expires_time timestamp NOT NULL,\n  redirect_uri varchar(255) NULL DEFAULT NULL,\n  state varchar(255) NOT NULL DEFAULT '',\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_code ADD CONSTRAINT pk_system_oauth2_code PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_code.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_code.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_code.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_code.code IS '授权码';\nCOMMENT ON COLUMN system_oauth2_code.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_code.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_code.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_code.redirect_uri IS '可重定向的 URI 地址';\nCOMMENT ON COLUMN system_oauth2_code.state IS '状态';\nCOMMENT ON COLUMN system_oauth2_code.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_code.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_code IS 'OAuth2 授权码表';\n\nDROP SEQUENCE IF EXISTS system_oauth2_code_seq;\nCREATE SEQUENCE system_oauth2_code_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_oauth2_refresh_token\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_refresh_token;\nCREATE TABLE system_oauth2_refresh_token (\n    id int8 NOT NULL,\n  user_id int8 NOT NULL,\n  refresh_token varchar(32) NOT NULL,\n  user_type int2 NOT NULL,\n  client_id varchar(255) NOT NULL,\n  scopes varchar(255) NULL DEFAULT NULL,\n  expires_time timestamp NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_oauth2_refresh_token ADD CONSTRAINT pk_system_oauth2_refresh_token PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_oauth2_refresh_token.id IS '编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_id IS '用户编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.refresh_token IS '刷新令牌';\nCOMMENT ON COLUMN system_oauth2_refresh_token.user_type IS '用户类型';\nCOMMENT ON COLUMN system_oauth2_refresh_token.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_oauth2_refresh_token.scopes IS '授权范围';\nCOMMENT ON COLUMN system_oauth2_refresh_token.expires_time IS '过期时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.creator IS '创建者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.create_time IS '创建时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.updater IS '更新者';\nCOMMENT ON COLUMN system_oauth2_refresh_token.update_time IS '更新时间';\nCOMMENT ON COLUMN system_oauth2_refresh_token.deleted IS '是否删除';\nCOMMENT ON COLUMN system_oauth2_refresh_token.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_oauth2_refresh_token IS 'OAuth2 刷新令牌';\n\nDROP SEQUENCE IF EXISTS system_oauth2_refresh_token_seq;\nCREATE SEQUENCE system_oauth2_refresh_token_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_operate_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_operate_log;\nCREATE TABLE system_operate_log (\n    id int8 NOT NULL,\n  trace_id varchar(64) NOT NULL DEFAULT '',\n  user_id int8 NOT NULL,\n  user_type int2 NOT NULL DEFAULT 0,\n  type varchar(50) NOT NULL,\n  sub_type varchar(50) NOT NULL,\n  biz_id int8 NOT NULL,\n  action varchar(2000) NOT NULL DEFAULT '',\n  success bool NOT NULL DEFAULT '1',\n  extra varchar(2000) NOT NULL DEFAULT '',\n  request_method varchar(16) NULL DEFAULT '',\n  request_url varchar(255) NULL DEFAULT '',\n  user_ip varchar(50) NULL DEFAULT NULL,\n  user_agent varchar(512) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_operate_log ADD CONSTRAINT pk_system_operate_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_operate_log.id IS '日志主键';\nCOMMENT ON COLUMN system_operate_log.trace_id IS '链路追踪编号';\nCOMMENT ON COLUMN system_operate_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_operate_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_operate_log.type IS '操作模块类型';\nCOMMENT ON COLUMN system_operate_log.sub_type IS '操作名';\nCOMMENT ON COLUMN system_operate_log.biz_id IS '操作数据模块编号';\nCOMMENT ON COLUMN system_operate_log.action IS '操作内容';\nCOMMENT ON COLUMN system_operate_log.success IS '操作结果';\nCOMMENT ON COLUMN system_operate_log.extra IS '拓展字段';\nCOMMENT ON COLUMN system_operate_log.request_method IS '请求方法名';\nCOMMENT ON COLUMN system_operate_log.request_url IS '请求地址';\nCOMMENT ON COLUMN system_operate_log.user_ip IS '用户 IP';\nCOMMENT ON COLUMN system_operate_log.user_agent IS '浏览器 UA';\nCOMMENT ON COLUMN system_operate_log.creator IS '创建者';\nCOMMENT ON COLUMN system_operate_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_operate_log.updater IS '更新者';\nCOMMENT ON COLUMN system_operate_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_operate_log.deleted IS '是否删除';\nCOMMENT ON COLUMN system_operate_log.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_operate_log IS '操作日志记录 V2 版本';\n\nDROP SEQUENCE IF EXISTS system_operate_log_seq;\nCREATE SEQUENCE system_operate_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_post\n-- ----------------------------\nDROP TABLE IF EXISTS system_post;\nCREATE TABLE system_post (\n    id int8 NOT NULL,\n  code varchar(64) NOT NULL,\n  name varchar(50) NOT NULL,\n  sort int4 NOT NULL,\n  status int2 NOT NULL,\n  remark varchar(500) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_post ADD CONSTRAINT pk_system_post PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_post.id IS '岗位ID';\nCOMMENT ON COLUMN system_post.code IS '岗位编码';\nCOMMENT ON COLUMN system_post.name IS '岗位名称';\nCOMMENT ON COLUMN system_post.sort IS '显示顺序';\nCOMMENT ON COLUMN system_post.status IS '状态（0正常 1停用）';\nCOMMENT ON COLUMN system_post.remark IS '备注';\nCOMMENT ON COLUMN system_post.creator IS '创建者';\nCOMMENT ON COLUMN system_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_post.updater IS '更新者';\nCOMMENT ON COLUMN system_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_post IS '岗位信息表';\n\n-- ----------------------------\n-- Records of system_post\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 'se', '项目经理', 2, 0, '', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 09:18:20', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 'user', '普通员工', 4, 0, '111222', 'admin', '2021-01-05 17:03:48', '1', '2025-03-24 21:32:40', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 'HR', '人力资源', 5, 0, '`', '1', '2024-03-24 20:45:40', '1', '2025-03-29 19:08:10', '0', 1);\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 'test', '测试', 10, 0, NULL, '1', '2025-09-02 08:45:57', '1', '2025-09-02 08:45:57', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_post_seq;\nCREATE SEQUENCE system_post_seq\n    START 8;\n\n-- ----------------------------\n-- Table structure for system_role\n-- ----------------------------\nDROP TABLE IF EXISTS system_role;\nCREATE TABLE system_role (\n    id int8 NOT NULL,\n  name varchar(30) NOT NULL,\n  code varchar(100) NOT NULL,\n  sort int4 NOT NULL,\n  data_scope int2 NOT NULL DEFAULT 1,\n  data_scope_dept_ids varchar(500) NOT NULL DEFAULT '',\n  status int2 NOT NULL,\n  type int2 NOT NULL,\n  remark varchar(500) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_role ADD CONSTRAINT pk_system_role PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_role.id IS '角色ID';\nCOMMENT ON COLUMN system_role.name IS '角色名称';\nCOMMENT ON COLUMN system_role.code IS '角色权限字符串';\nCOMMENT ON COLUMN system_role.sort IS '显示顺序';\nCOMMENT ON COLUMN system_role.data_scope IS '数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）';\nCOMMENT ON COLUMN system_role.data_scope_dept_ids IS '数据范围 ( 指定部门数组 ) ';\nCOMMENT ON COLUMN system_role.status IS '角色状态（0正常 1停用）';\nCOMMENT ON COLUMN system_role.type IS '角色类型';\nCOMMENT ON COLUMN system_role.remark IS '备注';\nCOMMENT ON COLUMN system_role.creator IS '创建者';\nCOMMENT ON COLUMN system_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role.updater IS '更新者';\nCOMMENT ON COLUMN system_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role IS '角色信息表';\n\n-- ----------------------------\n-- Records of system_role\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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 (1, '超级管理员', 'super_admin', 1, 1, '', 0, 1, '超级管理员', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:21', '0', 1);\nINSERT 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 (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:20', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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, '测试数据权限', 'test-dp', 3, 2, '[112,100,102,103,104,105,107,108]', 0, 2, '', '1', '2025-03-31 14:58:06', '1', '2025-09-06 20:15:13', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_role_seq;\nCREATE SEQUENCE system_role_seq\n    START 156;\n\n-- ----------------------------\n-- Table structure for system_role_menu\n-- ----------------------------\nDROP TABLE IF EXISTS system_role_menu;\nCREATE TABLE system_role_menu (\n    id int8 NOT NULL,\n  role_id int8 NOT NULL,\n  menu_id int8 NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_role_menu ADD CONSTRAINT pk_system_role_menu PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_role_menu.id IS '自增编号';\nCOMMENT ON COLUMN system_role_menu.role_id IS '角色ID';\nCOMMENT ON COLUMN system_role_menu.menu_id IS '菜单ID';\nCOMMENT ON COLUMN system_role_menu.creator IS '创建者';\nCOMMENT ON COLUMN system_role_menu.create_time IS '创建时间';\nCOMMENT ON COLUMN system_role_menu.updater IS '更新者';\nCOMMENT ON COLUMN system_role_menu.update_time IS '更新时间';\nCOMMENT ON COLUMN system_role_menu.deleted IS '是否删除';\nCOMMENT ON COLUMN system_role_menu.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_role_menu IS '角色和菜单关联表';\n\n-- ----------------------------\n-- Records of system_role_menu\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (263, 109, 1, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (434, 2, 1, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (454, 2, 1093, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (455, 2, 1094, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (460, 2, 1100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (467, 2, 1107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (476, 2, 1117, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (477, 2, 100, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (478, 2, 101, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (479, 2, 102, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (480, 2, 1126, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (481, 2, 103, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (483, 2, 104, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (485, 2, 105, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (488, 2, 107, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (490, 2, 108, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (492, 2, 109, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (498, 2, 1138, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (523, 2, 1224, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (524, 2, 1225, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (541, 2, 500, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (543, 2, 501, '1', '2022-02-22 13:09:12', '1', '2022-02-22 13:09:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (675, 2, 2, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (689, 2, 1077, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (690, 2, 1078, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (692, 2, 1083, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (693, 2, 1084, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (699, 2, 1090, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (703, 2, 106, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (704, 2, 110, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (705, 2, 111, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1732, 109, 1064, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1733, 109, 1001, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1734, 109, 1065, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1735, 109, 1002, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1736, 109, 1003, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1737, 109, 1004, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1738, 109, 1005, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1739, 109, 1006, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1740, 109, 1007, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1741, 109, 1008, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1742, 109, 1009, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1766, 109, 1064, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1767, 109, 1001, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1768, 109, 1065, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1769, 109, 1002, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1770, 109, 1003, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1771, 109, 1004, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1772, 109, 1005, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1773, 109, 1006, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1774, 109, 1007, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1775, 109, 1008, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1776, 109, 1009, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1800, 109, 1064, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1801, 109, 1001, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1802, 109, 1065, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1803, 109, 1002, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1804, 109, 1003, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1805, 109, 1004, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1806, 109, 1005, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1807, 109, 1006, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1808, 109, 1007, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1809, 109, 1008, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1810, 109, 1009, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1994, 2, 1027, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1995, 2, 1028, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1996, 2, 1029, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1997, 2, 1030, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1998, 2, 1031, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1999, 2, 1032, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2000, 2, 1033, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2001, 2, 1034, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2002, 2, 1035, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2003, 2, 1036, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2004, 2, 1037, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2005, 2, 1038, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2006, 2, 1039, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2007, 2, 1040, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2008, 2, 1042, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2009, 2, 1043, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2010, 2, 1045, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2011, 2, 1046, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2012, 2, 1048, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2013, 2, 1050, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2014, 2, 1051, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2015, 2, 1052, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2016, 2, 1053, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2017, 2, 1054, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2018, 2, 1056, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2019, 2, 1057, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2020, 2, 1058, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2021, 2, 2083, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2022, 2, 1059, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2023, 2, 1060, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2024, 2, 1063, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2025, 2, 1064, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2026, 2, 1065, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2027, 2, 1066, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2028, 2, 1067, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2029, 2, 1070, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2034, 2, 1075, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2036, 2, 1082, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2037, 2, 1085, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2038, 2, 1086, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2039, 2, 1087, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2040, 2, 1088, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2041, 2, 1089, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2042, 2, 1091, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2043, 2, 1092, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2044, 2, 1095, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2045, 2, 1096, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2046, 2, 1097, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2047, 2, 1098, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2048, 2, 1101, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2049, 2, 1102, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2050, 2, 1103, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2051, 2, 1104, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2052, 2, 1105, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2053, 2, 1106, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2054, 2, 1108, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2055, 2, 1109, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2061, 2, 1127, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2062, 2, 1128, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2063, 2, 1129, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2064, 2, 1130, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2066, 2, 1132, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2067, 2, 1133, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2068, 2, 1134, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2069, 2, 1135, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2070, 2, 1136, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2071, 2, 1137, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2072, 2, 114, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2073, 2, 1139, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2074, 2, 115, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2075, 2, 1140, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2076, 2, 116, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2077, 2, 1141, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2078, 2, 1142, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2079, 2, 1143, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2080, 2, 1150, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2081, 2, 1161, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2082, 2, 1162, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2086, 2, 1166, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2087, 2, 1173, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2088, 2, 1174, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2092, 2, 1178, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2099, 2, 1226, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2100, 2, 1227, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2101, 2, 1228, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2102, 2, 1229, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2103, 2, 1237, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2104, 2, 1238, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2105, 2, 1239, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2106, 2, 1240, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2107, 2, 1241, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2108, 2, 1242, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2109, 2, 1243, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2116, 2, 1254, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2117, 2, 1255, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2118, 2, 1256, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2119, 2, 1257, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2120, 2, 1258, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2121, 2, 1259, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2122, 2, 1260, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2123, 2, 1261, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2124, 2, 1263, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2125, 2, 1264, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2126, 2, 1265, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2127, 2, 1266, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2128, 2, 1267, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2129, 2, 1001, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2130, 2, 1002, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2131, 2, 1003, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2132, 2, 1004, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2133, 2, 1005, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2134, 2, 1006, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2135, 2, 1007, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2136, 2, 1008, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2137, 2, 1009, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2138, 2, 1010, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2148, 2, 1020, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2149, 2, 1021, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2150, 2, 1022, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2151, 2, 1023, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2152, 2, 1281, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2153, 2, 1282, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2154, 2, 2000, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2155, 2, 2002, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2156, 2, 2003, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2157, 2, 2004, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2158, 2, 2005, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2159, 2, 2006, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2160, 2, 2008, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2161, 2, 2009, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2162, 2, 2010, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2163, 2, 2011, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2164, 2, 2012, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2170, 2, 2019, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2171, 2, 2020, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2172, 2, 2021, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2173, 2, 2022, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2174, 2, 2023, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2175, 2, 2025, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2177, 2, 2027, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2178, 2, 2028, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2179, 2, 2029, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2180, 2, 2014, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2181, 2, 2015, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2182, 2, 2016, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2183, 2, 2017, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2184, 2, 2018, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2929, 109, 1224, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2930, 109, 1225, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2931, 109, 1226, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2932, 109, 1227, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2933, 109, 1228, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2934, 109, 1229, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2935, 109, 1138, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2936, 109, 1139, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2937, 109, 1140, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2996, 109, 1033, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2997, 109, 1034, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2998, 109, 1035, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2999, 109, 1050, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3000, 109, 1051, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3001, 109, 1052, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3002, 109, 1053, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3003, 109, 1054, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3004, 109, 1056, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3005, 109, 1057, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3006, 109, 1058, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3007, 109, 1059, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3008, 109, 1060, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3009, 109, 1066, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3010, 109, 1067, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3011, 109, 1070, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3012, 109, 1075, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3014, 109, 1077, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3015, 109, 1078, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3016, 109, 1082, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3017, 109, 1083, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3018, 109, 1084, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3019, 109, 1085, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3020, 109, 1086, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3021, 109, 1087, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3022, 109, 1088, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3023, 109, 1089, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3024, 109, 1090, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3025, 109, 1091, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3026, 109, 1092, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3027, 109, 106, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3028, 109, 110, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3029, 109, 111, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3030, 109, 112, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3031, 109, 113, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3032, 109, 114, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3033, 109, 115, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3034, 109, 116, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3035, 109, 2472, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3036, 109, 2478, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3037, 109, 2479, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3038, 109, 2480, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3039, 109, 2481, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3040, 109, 2482, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3041, 109, 2483, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3042, 109, 2484, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3043, 109, 2485, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3044, 109, 2486, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3045, 109, 2487, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3046, 109, 2488, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3047, 109, 2489, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3048, 109, 2490, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3049, 109, 2491, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3050, 109, 2492, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3051, 109, 2493, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3052, 109, 2494, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3053, 109, 2495, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3054, 109, 2497, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3055, 109, 1237, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3056, 109, 1238, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3057, 109, 1239, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3058, 109, 1240, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3059, 109, 1241, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3060, 109, 1242, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3061, 109, 1243, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3062, 109, 2525, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3063, 109, 1255, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3064, 109, 1256, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3065, 109, 1257, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4166, 109, 1120, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4167, 109, 2713, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4168, 109, 2714, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4169, 109, 2715, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4170, 109, 2716, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4171, 109, 2717, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4172, 109, 2718, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4173, 109, 2720, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4174, 109, 1185, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4175, 109, 2721, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4176, 109, 1186, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4177, 109, 2722, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4178, 109, 1187, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4179, 109, 2723, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4180, 109, 1188, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4181, 109, 2724, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4182, 109, 1189, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4183, 109, 2725, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4184, 109, 1190, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4185, 109, 2726, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4186, 109, 1191, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4187, 109, 2727, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4188, 109, 1192, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4189, 109, 2728, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4190, 109, 1193, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4191, 109, 2729, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4192, 109, 1194, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4193, 109, 2730, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4194, 109, 1195, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4195, 109, 2731, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4197, 109, 2732, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4198, 109, 1197, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4199, 109, 2733, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4200, 109, 1198, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4201, 109, 2734, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4202, 109, 1199, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4203, 109, 2735, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4204, 109, 1200, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4205, 109, 1201, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4206, 109, 1202, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4207, 109, 1207, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4208, 109, 1208, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4209, 109, 1209, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4210, 109, 1210, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4211, 109, 1211, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4212, 109, 1212, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4213, 109, 1213, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4214, 109, 1215, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4215, 109, 1216, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4216, 109, 1217, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4217, 109, 1218, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4218, 109, 1219, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5782, 2, 2759, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5783, 2, 2362, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5784, 2, 2387, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6105, 155, 4033, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6106, 155, 4034, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6107, 155, 4035, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6108, 155, 4036, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6109, 155, 4037, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6110, 155, 4038, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6111, 155, 4039, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6112, 155, 4040, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6113, 155, 4041, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6114, 155, 4042, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6115, 155, 4043, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6116, 155, 4044, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6117, 155, 4045, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6119, 155, 4001, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6120, 155, 4002, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6121, 155, 4003, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6122, 155, 4004, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6123, 155, 4005, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6124, 155, 4006, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6125, 155, 4007, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6126, 155, 4008, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6127, 155, 4009, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6128, 155, 4010, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6129, 155, 4011, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6130, 155, 4012, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6131, 155, 4013, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6132, 155, 4014, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6133, 155, 4015, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6134, 155, 4016, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6135, 155, 4017, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6136, 155, 4018, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6137, 155, 4031, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', '0', 1);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6139, 109, 1117, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6140, 109, 1126, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6141, 109, 1127, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6142, 109, 1128, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6143, 109, 1129, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6144, 109, 1130, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6145, 109, 1132, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6146, 109, 1133, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6147, 109, 1134, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6148, 109, 1135, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6149, 109, 1136, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6150, 109, 1137, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6151, 109, 2161, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6152, 109, 1150, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6153, 109, 1161, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6154, 109, 1162, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6155, 109, 1166, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6156, 109, 1173, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6157, 109, 1174, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6158, 109, 1178, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6159, 109, 2745, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6160, 109, 2746, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6161, 109, 2747, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6162, 109, 2748, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6163, 109, 2301, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6164, 109, 2302, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6165, 109, 5011, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6166, 109, 5012, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6167, 109, 2549, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6168, 109, 2550, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6169, 109, 2551, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6170, 109, 2552, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6171, 109, 2553, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6172, 109, 2554, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6173, 109, 2555, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6174, 109, 2556, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6220, 109, 2275, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6221, 109, 2276, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6222, 109, 2277, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6223, 109, 2281, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6224, 109, 2282, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6225, 109, 2283, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6226, 109, 2284, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6227, 109, 2285, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6228, 109, 2287, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6229, 109, 2288, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6230, 109, 2293, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6231, 109, 2294, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6232, 109, 2297, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6233, 109, 2300, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6234, 109, 2317, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6235, 109, 2318, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6236, 109, 2319, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6237, 109, 2320, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6238, 109, 2321, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6239, 109, 2322, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6240, 109, 2323, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6241, 109, 2324, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6242, 109, 2325, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6243, 109, 2326, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6244, 109, 2327, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6245, 109, 2328, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6246, 109, 2329, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6247, 109, 2330, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6248, 109, 2331, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6249, 109, 2332, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6250, 109, 2333, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6251, 109, 2334, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nINSERT 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', '0', 122);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_role_menu_seq;\nCREATE SEQUENCE system_role_menu_seq\n    START 6293;\n\n-- ----------------------------\n-- Table structure for system_sms_channel\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_channel;\nCREATE TABLE system_sms_channel (\n    id int8 NOT NULL,\n  signature varchar(12) NOT NULL,\n  code varchar(63) NOT NULL,\n  status int2 NOT NULL,\n  remark varchar(255) NULL DEFAULT NULL,\n  api_key varchar(128) NOT NULL,\n  api_secret varchar(128) NULL DEFAULT NULL,\n  callback_url varchar(255) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_channel ADD CONSTRAINT pk_system_sms_channel PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_channel.id IS '编号';\nCOMMENT ON COLUMN system_sms_channel.signature IS '短信签名';\nCOMMENT ON COLUMN system_sms_channel.code IS '渠道编码';\nCOMMENT ON COLUMN system_sms_channel.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_channel.remark IS '备注';\nCOMMENT ON COLUMN system_sms_channel.api_key IS '短信 API 的账号';\nCOMMENT ON COLUMN system_sms_channel.api_secret IS '短信 API 的秘钥';\nCOMMENT ON COLUMN system_sms_channel.callback_url IS '短信发送回调 URL';\nCOMMENT ON COLUMN system_sms_channel.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_channel.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_channel.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_channel.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_channel.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_channel IS '短信渠道';\n\n-- ----------------------------\n-- Records of system_sms_channel\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (2, 'Ballcat', 'ALIYUN', 0, '你要改哦，只有我可以用！！！！', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', '2021-03-31 11:53:10', '1', '2024-08-04 08:53:26', '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (4, '测试渠道', 'DEBUG_DING_TALK', 0, '123', '696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859', 'SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67', NULL, '1', '2021-04-13 00:23:14', '1', '2022-03-27 20:29:49', '0');\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (7, 'mock腾讯云', 'TENCENT', 0, '', '1 2', '2 3', '', '1', '2024-09-30 08:53:45', '1', '2024-09-30 08:55:01', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_sms_channel_seq;\nCREATE SEQUENCE system_sms_channel_seq\n    START 8;\n\n-- ----------------------------\n-- Table structure for system_sms_code\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_code;\nCREATE TABLE system_sms_code (\n    id int8 NOT NULL,\n  mobile varchar(11) NOT NULL,\n  code varchar(6) NOT NULL,\n  create_ip varchar(15) NOT NULL,\n  scene int2 NOT NULL,\n  today_index int2 NOT NULL,\n  used int2 NOT NULL,\n  used_time timestamp NULL DEFAULT NULL,\n  used_ip varchar(255) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_code ADD CONSTRAINT pk_system_sms_code PRIMARY KEY (id);\n\nCREATE INDEX idx_system_sms_code_01 ON system_sms_code (mobile);\n\nCOMMENT ON COLUMN system_sms_code.id IS '编号';\nCOMMENT ON COLUMN system_sms_code.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_code.code IS '验证码';\nCOMMENT ON COLUMN system_sms_code.create_ip IS '创建 IP';\nCOMMENT ON COLUMN system_sms_code.scene IS '发送场景';\nCOMMENT ON COLUMN system_sms_code.today_index IS '今日发送的第几条';\nCOMMENT ON COLUMN system_sms_code.used IS '是否使用';\nCOMMENT ON COLUMN system_sms_code.used_time IS '使用时间';\nCOMMENT ON COLUMN system_sms_code.used_ip IS '使用 IP';\nCOMMENT ON COLUMN system_sms_code.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_code.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_code.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_code.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_code.deleted IS '是否删除';\nCOMMENT ON COLUMN system_sms_code.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_sms_code IS '手机验证码';\n\nDROP SEQUENCE IF EXISTS system_sms_code_seq;\nCREATE SEQUENCE system_sms_code_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_sms_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_log;\nCREATE TABLE system_sms_log (\n    id int8 NOT NULL,\n  channel_id int8 NOT NULL,\n  channel_code varchar(63) NOT NULL,\n  template_id int8 NOT NULL,\n  template_code varchar(63) NOT NULL,\n  template_type int2 NOT NULL,\n  template_content varchar(255) NOT NULL,\n  template_params varchar(255) NOT NULL,\n  api_template_id varchar(63) NOT NULL,\n  mobile varchar(11) NOT NULL,\n  user_id int8 NULL DEFAULT NULL,\n  user_type int2 NULL DEFAULT NULL,\n  send_status int2 NOT NULL DEFAULT 0,\n  send_time timestamp NULL DEFAULT NULL,\n  api_send_code varchar(63) NULL DEFAULT NULL,\n  api_send_msg varchar(255) NULL DEFAULT NULL,\n  api_request_id varchar(255) NULL DEFAULT NULL,\n  api_serial_no varchar(255) NULL DEFAULT NULL,\n  receive_status int2 NOT NULL DEFAULT 0,\n  receive_time timestamp NULL DEFAULT NULL,\n  api_receive_code varchar(63) NULL DEFAULT NULL,\n  api_receive_msg varchar(255) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_log ADD CONSTRAINT pk_system_sms_log PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_log.id IS '编号';\nCOMMENT ON COLUMN system_sms_log.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_log.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_log.template_id IS '模板编号';\nCOMMENT ON COLUMN system_sms_log.template_code IS '模板编码';\nCOMMENT ON COLUMN system_sms_log.template_type IS '短信类型';\nCOMMENT ON COLUMN system_sms_log.template_content IS '短信内容';\nCOMMENT ON COLUMN system_sms_log.template_params IS '短信参数';\nCOMMENT ON COLUMN system_sms_log.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_log.mobile IS '手机号';\nCOMMENT ON COLUMN system_sms_log.user_id IS '用户编号';\nCOMMENT ON COLUMN system_sms_log.user_type IS '用户类型';\nCOMMENT ON COLUMN system_sms_log.send_status IS '发送状态';\nCOMMENT ON COLUMN system_sms_log.send_time IS '发送时间';\nCOMMENT ON COLUMN system_sms_log.api_send_code IS '短信 API 发送结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_send_msg IS '短信 API 发送失败的提示';\nCOMMENT ON COLUMN system_sms_log.api_request_id IS '短信 API 发送返回的唯一请求 ID';\nCOMMENT ON COLUMN system_sms_log.api_serial_no IS '短信 API 发送返回的序号';\nCOMMENT ON COLUMN system_sms_log.receive_status IS '接收状态';\nCOMMENT ON COLUMN system_sms_log.receive_time IS '接收时间';\nCOMMENT ON COLUMN system_sms_log.api_receive_code IS 'API 接收结果的编码';\nCOMMENT ON COLUMN system_sms_log.api_receive_msg IS 'API 接收结果的说明';\nCOMMENT ON COLUMN system_sms_log.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_log.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_log.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_log.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_log.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_log IS '短信日志';\n\nDROP SEQUENCE IF EXISTS system_sms_log_seq;\nCREATE SEQUENCE system_sms_log_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_sms_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_template;\nCREATE TABLE system_sms_template (\n    id int8 NOT NULL,\n  type int2 NOT NULL,\n  status int2 NOT NULL,\n  code varchar(63) NOT NULL,\n  name varchar(63) NOT NULL,\n  content varchar(255) NOT NULL,\n  params varchar(255) NOT NULL,\n  remark varchar(255) NULL DEFAULT NULL,\n  api_template_id varchar(63) NOT NULL,\n  channel_id int8 NOT NULL,\n  channel_code varchar(63) NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_sms_template ADD CONSTRAINT pk_system_sms_template PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_sms_template.id IS '编号';\nCOMMENT ON COLUMN system_sms_template.type IS '模板类型';\nCOMMENT ON COLUMN system_sms_template.status IS '开启状态';\nCOMMENT ON COLUMN system_sms_template.code IS '模板编码';\nCOMMENT ON COLUMN system_sms_template.name IS '模板名称';\nCOMMENT ON COLUMN system_sms_template.content IS '模板内容';\nCOMMENT ON COLUMN system_sms_template.params IS '参数数组';\nCOMMENT ON COLUMN system_sms_template.remark IS '备注';\nCOMMENT ON COLUMN system_sms_template.api_template_id IS '短信 API 的模板编号';\nCOMMENT ON COLUMN system_sms_template.channel_id IS '短信渠道编号';\nCOMMENT ON COLUMN system_sms_template.channel_code IS '短信渠道编码';\nCOMMENT ON COLUMN system_sms_template.creator IS '创建者';\nCOMMENT ON COLUMN system_sms_template.create_time IS '创建时间';\nCOMMENT ON COLUMN system_sms_template.updater IS '更新者';\nCOMMENT ON COLUMN system_sms_template.update_time IS '更新时间';\nCOMMENT ON COLUMN system_sms_template.deleted IS '是否删除';\nCOMMENT ON TABLE system_sms_template IS '短信模板';\n\n-- ----------------------------\n-- Records of system_sms_template\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (2, 1, 0, 'test_01', '测试验证码短信', '正在进行登录操作{operation}，您的验证码是{code}', '[\"operation\",\"code\"]', '测试备注', '4383920', 4, 'DEBUG_DING_TALK', '', '2021-03-31 10:49:38', '1', '2024-08-18 11:57:18', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (3, 1, 0, 'test_02', '公告通知', '您的验证码{code}，该验证码5分钟内有效，请勿泄漏于他人！', '[\"code\"]', NULL, 'SMS_207945135', 2, 'ALIYUN', '', '2021-03-31 11:56:30', '1', '2021-04-10 01:22:02', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (6, 3, 0, 'test-01', '测试模板', '哈哈哈 {name}', '[\"name\"]', 'f哈哈哈', '4383920', 4, 'DEBUG_DING_TALK', '1', '2021-04-10 01:07:21', '1', '2024-08-18 11:57:07', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (7, 3, 0, 'test-04', '测试下', '老鸡{name}，牛逼{code}', '[\"name\",\"code\"]', '哈哈哈哈', 'suibian', 7, 'DEBUG_DING_TALK', '1', '2021-04-13 00:29:53', '1', '2024-09-30 00:56:24', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (8, 1, 0, 'user-sms-login', '前台用户短信登录', '您的验证码是{code}', '[\"code\"]', NULL, '4372216', 4, 'DEBUG_DING_TALK', '1', '2021-10-11 08:10:00', '1', '2024-08-18 11:57:06', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (9, 2, 0, 'bpm_task_assigned', '【工作流】任务被分配', '您收到了一条新的待办任务：{processInstanceName}-{taskName}，申请人：{startUserNickname}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"startUserNickname\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-21 22:31:19', '1', '2022-01-22 00:03:36', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (10, 2, 0, 'bpm_process_instance_reject', '【工作流】流程被不通过', '您的流程被审批不通过：{processInstanceName}，原因：{reason}，查看链接：{detailUrl}', '[\"processInstanceName\",\"reason\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:03:31', '1', '2022-05-01 12:33:14', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (11, 2, 0, 'bpm_process_instance_approve', '【工作流】流程被通过', '您的流程被审批通过：{processInstanceName}，查看链接：{detailUrl}', '[\"processInstanceName\",\"detailUrl\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2022-01-22 00:04:31', '1', '2022-03-27 20:32:21', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (12, 2, 0, 'demo', '演示模板', '我就是测试一下下', '[]', NULL, 'biubiubiu', 4, 'DEBUG_DING_TALK', '1', '2022-04-10 23:22:49', '1', '2024-08-18 11:57:04', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (14, 1, 0, 'user-update-mobile', '会员用户 - 修改手机', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:04', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (15, 1, 0, 'user-update-password', '会员用户 - 修改密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:18', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (16, 1, 0, 'user-reset-password', '会员用户 - 重置密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-12-02 22:35:27', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (17, 2, 0, 'bpm_task_timeout', '【工作流】任务审批超时', '您收到了一条超时的待办任务：{processInstanceName}-{taskName}，处理链接：{detailUrl}', '[\"processInstanceName\",\"taskName\",\"detailUrl\"]', '', 'X', 4, 'DEBUG_DING_TALK', '1', '2024-08-16 21:59:15', '1', '2024-08-16 21:59:34', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (18, 1, 0, 'admin-reset-password', '后台用户 - 忘记密码', '您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2025-03-16 14:19:34', '1', '2025-03-16 14:19:45', '0');\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (19, 1, 0, 'admin-sms-login', '后台用户短信登录', '您的验证码是{code}', '[\"code\"]', '', '4372216', 4, 'DEBUG_DING_TALK', '1', '2025-04-08 09:36:03', '1', '2025-04-08 09:36:17', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_sms_template_seq;\nCREATE SEQUENCE system_sms_template_seq\n    START 20;\n\n-- ----------------------------\n-- Table structure for system_social_client\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_client;\nCREATE TABLE system_social_client (\n    id int8 NOT NULL,\n  name varchar(255) NOT NULL,\n  social_type int2 NOT NULL,\n  user_type int2 NOT NULL,\n  client_id varchar(255) NOT NULL,\n  client_secret varchar(2048) NOT NULL,\n  public_key varchar(2048) NULL,\n  agent_id varchar(255) NULL DEFAULT NULL,\n  status int2 NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_social_client ADD CONSTRAINT pk_system_social_client PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_client.id IS '编号';\nCOMMENT ON COLUMN system_social_client.name IS '应用名';\nCOMMENT ON COLUMN system_social_client.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_client.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_client.client_id IS '客户端编号';\nCOMMENT ON COLUMN system_social_client.client_secret IS '客户端密钥';\nCOMMENT ON COLUMN system_social_client.public_key IS 'publicKey公钥';\nCOMMENT ON COLUMN system_social_client.agent_id IS '代理编号';\nCOMMENT ON COLUMN system_social_client.status IS '状态';\nCOMMENT ON COLUMN system_social_client.creator IS '创建者';\nCOMMENT ON COLUMN system_social_client.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_client.updater IS '更新者';\nCOMMENT ON COLUMN system_social_client.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_client.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_client.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_client IS '社交客户端表';\n\n-- ----------------------------\n-- Records of system_social_client\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '钉钉', 20, 2, 'dingvrnreaje3yqvzhxg', 'i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI', NULL, 0, '', '2023-10-18 11:21:18', '1', '2023-12-20 21:28:26', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '钉钉（王土豆）', 20, 2, 'dingtsu9hpepjkbmthhw', 'FP_bnSq_HAHKCSncmJjw5hxhnzs6vaVDSZZn3egj6rdqTQ_hu5tQVJyLMpgCakdP', NULL, 0, '', '2023-10-18 11:21:18', '', '2023-12-20 21:28:26', '1', 121);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '微信公众号', 31, 1, 'wx5b23ba7a5589ecbb', '2a7b3b20c537e52e74afd395eb85f61f', NULL, 0, '', '2023-10-18 16:07:46', '1', '2023-12-20 21:28:23', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (43, '微信小程序', 34, 1, 'wx63c280fe3248a3e7', '6f270509224a7ae1296bbf1c8cb97aed', NULL, 0, '', '2023-10-19 13:37:41', '1', '2023-12-20 21:28:25', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (44, '1', 10, 1, '2', '3', NULL, 0, '1', '2025-04-06 20:36:28', '1', '2025-04-06 20:43:12', '1', 1);\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (45, '1', 10, 1, '2', '3', NULL, 1, '1', '2025-09-06 20:26:15', '1', '2025-09-06 20:27:55', '1', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_social_client_seq;\nCREATE SEQUENCE system_social_client_seq\n    START 46;\n\n-- ----------------------------\n-- Table structure for system_social_user\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_user;\nCREATE TABLE system_social_user (\n    id int8 NOT NULL,\n  type int2 NOT NULL,\n  openid varchar(32) NOT NULL,\n  token varchar(256) NULL DEFAULT NULL,\n  raw_token_info varchar(1024) NOT NULL,\n  nickname varchar(32) NOT NULL,\n  avatar varchar(255) NULL DEFAULT NULL,\n  raw_user_info varchar(1024) NOT NULL,\n  code varchar(256) NOT NULL,\n  state varchar(256) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_social_user ADD CONSTRAINT pk_system_social_user PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_user.id IS '主键 ( 自增策略 ) ';\nCOMMENT ON COLUMN system_social_user.type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user.openid IS '社交 openid';\nCOMMENT ON COLUMN system_social_user.token IS '社交 token';\nCOMMENT ON COLUMN system_social_user.raw_token_info IS '原始 Token 数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_social_user.avatar IS '用户头像';\nCOMMENT ON COLUMN system_social_user.raw_user_info IS '原始用户数据，一般是 JSON 格式';\nCOMMENT ON COLUMN system_social_user.code IS '最后一次的认证 code';\nCOMMENT ON COLUMN system_social_user.state IS '最后一次的认证 state';\nCOMMENT ON COLUMN system_social_user.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user IS '社交用户表';\n\nDROP SEQUENCE IF EXISTS system_social_user_seq;\nCREATE SEQUENCE system_social_user_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_social_user_bind\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_user_bind;\nCREATE TABLE system_social_user_bind (\n    id int8 NOT NULL,\n  user_id int8 NOT NULL,\n  user_type int2 NOT NULL,\n  social_type int2 NOT NULL,\n  social_user_id int8 NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_social_user_bind ADD CONSTRAINT pk_system_social_user_bind PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_social_user_bind.id IS '主键 ( 自增策略 ) ';\nCOMMENT ON COLUMN system_social_user_bind.user_id IS '用户编号';\nCOMMENT ON COLUMN system_social_user_bind.user_type IS '用户类型';\nCOMMENT ON COLUMN system_social_user_bind.social_type IS '社交平台的类型';\nCOMMENT ON COLUMN system_social_user_bind.social_user_id IS '社交用户的编号';\nCOMMENT ON COLUMN system_social_user_bind.creator IS '创建者';\nCOMMENT ON COLUMN system_social_user_bind.create_time IS '创建时间';\nCOMMENT ON COLUMN system_social_user_bind.updater IS '更新者';\nCOMMENT ON COLUMN system_social_user_bind.update_time IS '更新时间';\nCOMMENT ON COLUMN system_social_user_bind.deleted IS '是否删除';\nCOMMENT ON COLUMN system_social_user_bind.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_social_user_bind IS '社交绑定表';\n\nDROP SEQUENCE IF EXISTS system_social_user_bind_seq;\nCREATE SEQUENCE system_social_user_bind_seq\n    START 1;\n\n-- ----------------------------\n-- Table structure for system_tenant\n-- ----------------------------\nDROP TABLE IF EXISTS system_tenant;\nCREATE TABLE system_tenant (\n    id int8 NOT NULL,\n  name varchar(30) NOT NULL,\n  contact_user_id int8 NULL DEFAULT NULL,\n  contact_name varchar(30) NOT NULL,\n  contact_mobile varchar(500) NULL DEFAULT NULL,\n  status int2 NOT NULL DEFAULT 0,\n  websites varchar(1024) NULL DEFAULT '',\n  package_id int8 NOT NULL,\n  expire_time timestamp NOT NULL,\n  account_count int4 NOT NULL,\n  creator varchar(64) NOT NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_tenant ADD CONSTRAINT pk_system_tenant PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_tenant.id IS '租户编号';\nCOMMENT ON COLUMN system_tenant.name IS '租户名';\nCOMMENT ON COLUMN system_tenant.contact_user_id IS '联系人的用户编号';\nCOMMENT ON COLUMN system_tenant.contact_name IS '联系人';\nCOMMENT ON COLUMN system_tenant.contact_mobile IS '联系手机';\nCOMMENT ON COLUMN system_tenant.status IS '租户状态';\nCOMMENT ON COLUMN system_tenant.websites IS '绑定域名数组';\nCOMMENT ON COLUMN system_tenant.package_id IS '租户套餐编号';\nCOMMENT ON COLUMN system_tenant.expire_time IS '过期时间';\nCOMMENT ON COLUMN system_tenant.account_count IS '账号数量';\nCOMMENT ON COLUMN system_tenant.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant IS '租户表';\n\n-- ----------------------------\n-- Records of system_tenant\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', '0');\nINSERT 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', '0');\nINSERT 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, '2022-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2025-09-06 20:44:42', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_tenant_seq;\nCREATE SEQUENCE system_tenant_seq\n    START 123;\n\n-- ----------------------------\n-- Table structure for system_tenant_package\n-- ----------------------------\nDROP TABLE IF EXISTS system_tenant_package;\nCREATE TABLE system_tenant_package (\n    id int8 NOT NULL,\n  name varchar(30) NOT NULL,\n  status int2 NOT NULL DEFAULT 0,\n  remark varchar(256) NULL DEFAULT '',\n  menu_ids varchar(4096) NOT NULL,\n  creator varchar(64) NOT NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_tenant_package ADD CONSTRAINT pk_system_tenant_package PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_tenant_package.id IS '套餐编号';\nCOMMENT ON COLUMN system_tenant_package.name IS '套餐名';\nCOMMENT ON COLUMN system_tenant_package.status IS '租户状态（0正常 1停用）';\nCOMMENT ON COLUMN system_tenant_package.remark IS '备注';\nCOMMENT ON COLUMN system_tenant_package.menu_ids IS '关联的菜单编号';\nCOMMENT ON COLUMN system_tenant_package.creator IS '创建者';\nCOMMENT ON COLUMN system_tenant_package.create_time IS '创建时间';\nCOMMENT ON COLUMN system_tenant_package.updater IS '更新者';\nCOMMENT ON COLUMN system_tenant_package.update_time IS '更新时间';\nCOMMENT ON COLUMN system_tenant_package.deleted IS '是否删除';\nCOMMENT ON TABLE system_tenant_package IS '租户套餐表';\n\n-- ----------------------------\n-- Records of system_tenant_package\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', '0');\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_tenant_package_seq;\nCREATE SEQUENCE system_tenant_package_seq\n    START 112;\n\n-- ----------------------------\n-- Table structure for system_user_post\n-- ----------------------------\nDROP TABLE IF EXISTS system_user_post;\nCREATE TABLE system_user_post (\n    id int8 NOT NULL,\n  user_id int8 NOT NULL DEFAULT 0,\n  post_id int8 NOT NULL DEFAULT 0,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_user_post ADD CONSTRAINT pk_system_user_post PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_user_post.id IS 'id';\nCOMMENT ON COLUMN system_user_post.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_post.post_id IS '岗位ID';\nCOMMENT ON COLUMN system_user_post.creator IS '创建者';\nCOMMENT ON COLUMN system_user_post.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_post.updater IS '更新者';\nCOMMENT ON COLUMN system_user_post.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_post.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_post.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_post IS '用户岗位表';\n\n-- ----------------------------\n-- Records of system_user_post\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, 1, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, 100, 1, 'admin', '2022-05-02 07:25:24', 'admin', '2022-05-02 07:25:24', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (115, 104, 1, '1', '2022-05-16 19:36:28', '1', '2022-05-16 19:36:28', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (116, 117, 2, '1', '2022-07-09 17:40:26', '1', '2022-07-09 17:40:26', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (117, 118, 1, '1', '2022-07-09 17:44:44', '1', '2022-07-09 17:44:44', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (119, 114, 5, '1', '2024-03-24 20:45:51', '1', '2024-03-24 20:45:51', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (123, 115, 1, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (124, 115, 2, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', '0', 1);\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (125, 1, 2, '1', '2024-07-13 22:31:39', '1', '2024-07-13 22:31:39', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_user_post_seq;\nCREATE SEQUENCE system_user_post_seq\n    START 126;\n\n-- ----------------------------\n-- Table structure for system_user_role\n-- ----------------------------\nDROP TABLE IF EXISTS system_user_role;\nCREATE TABLE system_user_role (\n    id int8 NOT NULL,\n  user_id int8 NOT NULL,\n  role_id int8 NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_user_role ADD CONSTRAINT pk_system_user_role PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_user_role.id IS '自增编号';\nCOMMENT ON COLUMN system_user_role.user_id IS '用户ID';\nCOMMENT ON COLUMN system_user_role.role_id IS '角色ID';\nCOMMENT ON COLUMN system_user_role.creator IS '创建者';\nCOMMENT ON COLUMN system_user_role.create_time IS '创建时间';\nCOMMENT ON COLUMN system_user_role.updater IS '更新者';\nCOMMENT ON COLUMN system_user_role.update_time IS '更新时间';\nCOMMENT ON COLUMN system_user_role.deleted IS '是否删除';\nCOMMENT ON COLUMN system_user_role.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_user_role IS '用户和角色关联表';\n\n-- ----------------------------\n-- Records of system_user_role\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 1, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:17', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 100, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:12', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 100, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:11', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 103, 1, '1', '2022-01-11 13:19:45', '1', '2022-01-11 13:19:45', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 110, 109, '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 111, 110, '110', '2022-02-23 13:14:38', '110', '2022-02-23 13:14:38', '0', 121);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 113, 111, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', '0', 122);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (18, 1, 2, '1', '2022-05-12 20:39:29', '1', '2022-05-12 20:39:29', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (47, 104, 2, '1', '2025-01-04 10:40:33', '1', '2025-01-04 10:40:33', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (48, 100, 155, '1', '2025-04-04 10:41:14', '1', '2025-04-04 10:41:14', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (49, 142, 1, '1', '2025-07-23 09:11:42', '1', '2025-07-23 09:11:42', '0', 1);\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (50, 142, 2, '1', '2025-10-07 20:50:37', '1', '2025-10-07 20:50:37', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_user_role_seq;\nCREATE SEQUENCE system_user_role_seq\n    START 51;\n\n-- ----------------------------\n-- Table structure for system_users\n-- ----------------------------\nDROP TABLE IF EXISTS system_users;\nCREATE TABLE system_users (\n    id int8 NOT NULL,\n  username varchar(30) NOT NULL,\n  password varchar(100) NOT NULL DEFAULT '',\n  nickname varchar(30) NOT NULL,\n  remark varchar(500) NULL DEFAULT NULL,\n  dept_id int8 NULL DEFAULT NULL,\n  post_ids varchar(255) NULL DEFAULT NULL,\n  email varchar(50) NULL DEFAULT '',\n  mobile varchar(11) NULL DEFAULT '',\n  sex int2 NULL DEFAULT 0,\n  avatar varchar(512) NULL DEFAULT '',\n  status int2 NOT NULL DEFAULT 0,\n  login_ip varchar(50) NULL DEFAULT '',\n  login_date timestamp NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE system_users ADD CONSTRAINT pk_system_users PRIMARY KEY (id);\n\nCOMMENT ON COLUMN system_users.id IS '用户ID';\nCOMMENT ON COLUMN system_users.username IS '用户账号';\nCOMMENT ON COLUMN system_users.password IS '密码';\nCOMMENT ON COLUMN system_users.nickname IS '用户昵称';\nCOMMENT ON COLUMN system_users.remark IS '备注';\nCOMMENT ON COLUMN system_users.dept_id IS '部门ID';\nCOMMENT ON COLUMN system_users.post_ids IS '岗位编号数组';\nCOMMENT ON COLUMN system_users.email IS '用户邮箱';\nCOMMENT ON COLUMN system_users.mobile IS '手机号码';\nCOMMENT ON COLUMN system_users.sex IS '用户性别';\nCOMMENT ON COLUMN system_users.avatar IS '头像地址';\nCOMMENT ON COLUMN system_users.status IS '帐号状态（0正常 1停用）';\nCOMMENT ON COLUMN system_users.login_ip IS '最后登录IP';\nCOMMENT ON COLUMN system_users.login_date IS '最后登录时间';\nCOMMENT ON COLUMN system_users.creator IS '创建者';\nCOMMENT ON COLUMN system_users.create_time IS '创建时间';\nCOMMENT ON COLUMN system_users.updater IS '更新者';\nCOMMENT ON COLUMN system_users.update_time IS '更新时间';\nCOMMENT ON COLUMN system_users.deleted IS '是否删除';\nCOMMENT ON COLUMN system_users.tenant_id IS '租户编号';\nCOMMENT ON TABLE system_users IS '用户信息表';\n\n-- ----------------------------\n-- Records of system_users\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '芋道源码', '管理员', 103, '[1,2]', '11aoteman@126.com', '18818260272', 2, 'http://test.yudao.iocoder.cn/20250921/avatar_1758423875594.png', 0, '0:0:0:0:0:0:0:1', '2025-11-22 18:50:21', 'admin', '2021-01-05 17:03:47', NULL, '2025-11-22 18:50:21', '0', 1);\nINSERT 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$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', '1', '2025-07-09 23:41:58', '0', 1);\nINSERT 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', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '0', 118);\nINSERT 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', '0', 119);\nINSERT 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', '0', 120);\nINSERT 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', '0', 121);\nINSERT 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', '0', 121);\nINSERT 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', '0', 1);\nINSERT 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', '0', 122);\nINSERT 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', '2024-03-24 22:21:05', '1', '2022-03-19 21:50:58', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', '小秃头', NULL, NULL, NULL, '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-09-10 21:03:58', NULL, '2024-09-10 21:03:58', NULL, '2025-04-21 14:23:08', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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$IaR0fGYtalIDURMMdcaD2.4JDWZ15ueQZwap9oPUuxkwSbL66vIRG', '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', '1', '2025-11-25 19:49:08', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS system_users_seq;\nCREATE SEQUENCE system_users_seq\n    START 143;\n\n-- ----------------------------\n-- Table structure for yudao_demo01_contact\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo01_contact;\nCREATE TABLE yudao_demo01_contact (\n    id int8 NOT NULL,\n  name varchar(100) NOT NULL DEFAULT '',\n  sex int2 NOT NULL,\n  birthday timestamp NOT NULL,\n  description varchar(255) NOT NULL,\n  avatar varchar(512) NULL DEFAULT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo01_contact ADD CONSTRAINT pk_yudao_demo01_contact PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo01_contact.id IS '编号';\nCOMMENT ON COLUMN yudao_demo01_contact.name IS '名字';\nCOMMENT ON COLUMN yudao_demo01_contact.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo01_contact.birthday IS '出生年';\nCOMMENT ON COLUMN yudao_demo01_contact.description IS '简介';\nCOMMENT ON COLUMN yudao_demo01_contact.avatar IS '头像';\nCOMMENT ON COLUMN yudao_demo01_contact.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo01_contact.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo01_contact.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo01_contact.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo01_contact.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo01_contact.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo01_contact IS '示例联系人表';\n\n-- ----------------------------\n-- Records of yudao_demo01_contact\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO yudao_demo01_contact (id, name, sex, birthday, description, avatar, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 2, '2023-11-07 00:00:00', '<p>天蚕土豆！呀</p>', 'http://127.0.0.1:48080/admin-api/infra/file/4/get/46f8fa1a37db3f3960d8910ff2fe3962ab3b2db87cf2f8ccb4dc8145b8bdf237.jpeg', '1', '2023-11-15 23:34:30', '1', '2023-11-15 23:47:39', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo01_contact_seq;\nCREATE SEQUENCE yudao_demo01_contact_seq\n    START 2;\n\n-- ----------------------------\n-- Table structure for yudao_demo02_category\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo02_category;\nCREATE TABLE yudao_demo02_category (\n    id int8 NOT NULL,\n  name varchar(100) NOT NULL DEFAULT '',\n  parent_id int8 NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo02_category ADD CONSTRAINT pk_yudao_demo02_category PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo02_category.id IS '编号';\nCOMMENT ON COLUMN yudao_demo02_category.name IS '名字';\nCOMMENT ON COLUMN yudao_demo02_category.parent_id IS '父级编号';\nCOMMENT ON COLUMN yudao_demo02_category.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo02_category.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo02_category.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo02_category.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo02_category.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo02_category.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo02_category IS '示例分类表';\n\n-- ----------------------------\n-- Records of yudao_demo02_category\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, '土豆', 0, '1', '2023-11-15 23:34:30', '1', '2023-11-16 20:24:23', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, '番茄', 0, '1', '2023-11-16 20:24:00', '1', '2023-11-16 20:24:15', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, '怪怪', 0, '1', '2023-11-16 20:24:32', '1', '2023-11-16 20:24:32', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, '小番茄', 2, '1', '2023-11-16 20:24:39', '1', '2023-11-16 20:24:39', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, '大番茄', 2, '1', '2023-11-16 20:24:46', '1', '2023-11-16 20:24:46', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, '11', 3, '1', '2023-11-24 19:29:34', '1', '2023-11-24 19:29:34', '0', 1);\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, '1', 0, '1', '2025-10-01 09:19:20', '1', '2025-10-01 09:19:20', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo02_category_seq;\nCREATE SEQUENCE yudao_demo02_category_seq\n    START 8;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_course\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_course;\nCREATE TABLE yudao_demo03_course (\n    id int8 NOT NULL,\n  student_id int8 NOT NULL,\n  name varchar(100) NOT NULL DEFAULT '',\n  score int2 NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo03_course ADD CONSTRAINT pk_yudao_demo03_course PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_course.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_course.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_course.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_course.score IS '分数';\nCOMMENT ON COLUMN yudao_demo03_course.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_course.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_course.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_course.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_course.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_course.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_course IS '学生课程表';\n\n-- ----------------------------\n-- Records of yudao_demo03_course\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, '语文', 66, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 2, '数学', 22, '1', '2023-11-16 23:21:49', '1', '2024-09-17 10:55:30', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (11, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 10:55:28', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (12, 2, '电脑', 33, '1', '2023-11-17 00:20:42', '1', '2023-11-16 16:20:45', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (13, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:26', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2024-09-17 10:55:49', '1', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', '0', 1);\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '1', 1);\nINSERT 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', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo03_course_seq;\nCREATE SEQUENCE yudao_demo03_course_seq\n    START 21;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_grade\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_grade;\nCREATE TABLE yudao_demo03_grade (\n    id int8 NOT NULL,\n  student_id int8 NOT NULL,\n  name varchar(100) NOT NULL DEFAULT '',\n  teacher varchar(255) NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo03_grade ADD CONSTRAINT pk_yudao_demo03_grade PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_grade.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_grade.student_id IS '学生编号';\nCOMMENT ON COLUMN yudao_demo03_grade.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_grade.teacher IS '班主任';\nCOMMENT ON COLUMN yudao_demo03_grade.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_grade.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_grade.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_grade.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_grade.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_grade.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_grade IS '学生班级表';\n\n-- ----------------------------\n-- Records of yudao_demo03_grade\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo03_grade_seq;\nCREATE SEQUENCE yudao_demo03_grade_seq\n    START 10;\n\n-- ----------------------------\n-- Table structure for yudao_demo03_student\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_student;\nCREATE TABLE yudao_demo03_student (\n    id int8 NOT NULL,\n  name varchar(100) NOT NULL DEFAULT '',\n  sex int2 NOT NULL,\n  birthday timestamp NOT NULL,\n  description varchar(255) NOT NULL,\n  creator varchar(64) NULL DEFAULT '',\n  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updater varchar(64) NULL DEFAULT '',\n  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  deleted int2 NOT NULL DEFAULT 0,\n  tenant_id int8 NOT NULL DEFAULT 0\n);\n\nALTER TABLE yudao_demo03_student ADD CONSTRAINT pk_yudao_demo03_student PRIMARY KEY (id);\n\nCOMMENT ON COLUMN yudao_demo03_student.id IS '编号';\nCOMMENT ON COLUMN yudao_demo03_student.name IS '名字';\nCOMMENT ON COLUMN yudao_demo03_student.sex IS '性别';\nCOMMENT ON COLUMN yudao_demo03_student.birthday IS '出生日期';\nCOMMENT ON COLUMN yudao_demo03_student.description IS '简介';\nCOMMENT ON COLUMN yudao_demo03_student.creator IS '创建者';\nCOMMENT ON COLUMN yudao_demo03_student.create_time IS '创建时间';\nCOMMENT ON COLUMN yudao_demo03_student.updater IS '更新者';\nCOMMENT ON COLUMN yudao_demo03_student.update_time IS '更新时间';\nCOMMENT ON COLUMN yudao_demo03_student.deleted IS '是否删除';\nCOMMENT ON COLUMN yudao_demo03_student.tenant_id IS '租户编号';\nCOMMENT ON TABLE yudao_demo03_student IS '学生表';\n\n-- ----------------------------\n-- Records of yudao_demo03_student\n-- ----------------------------\n-- @formatter:off\nBEGIN;\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nINSERT 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', '0', 1);\nCOMMIT;\n-- @formatter:on\n\nDROP SEQUENCE IF EXISTS yudao_demo03_student_seq;\nCREATE SEQUENCE yudao_demo03_student_seq\n    START 10;\n\n"
  },
  {
    "path": "sql/sqlserver/quartz.sql",
    "content": "/*\n 注意：仅仅需要 Quartz 定时任务的场景，可选！！！\n\n Date: 30/04/2024 09:54:18\n*/\n\n-- ----------------------------\n-- Table structure for QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_BLOB_TRIGGERS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_BLOB_TRIGGERS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_BLOB_TRIGGERS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [BLOB_DATA] varbinary(max)  NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_BLOB_TRIGGERS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_CALENDARS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_CALENDARS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_CALENDARS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_CALENDARS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [CALENDAR_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [CALENDAR] varbinary(max)  NOT NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_CALENDARS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_CALENDARS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_CRON_TRIGGERS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_CRON_TRIGGERS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_CRON_TRIGGERS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_CRON_TRIGGERS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [CRON_EXPRESSION] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TIME_ZONE_ID] varchar(80) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_CRON_TRIGGERS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_FIRED_TRIGGERS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_FIRED_TRIGGERS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_FIRED_TRIGGERS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [ENTRY_ID] varchar(95) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [INSTANCE_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [FIRED_TIME] bigint  NOT NULL,\n    [SCHED_TIME] bigint  NOT NULL,\n    [PRIORITY] int  NOT NULL,\n    [STATE] varchar(16) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [JOB_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [JOB_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [IS_NONCONCURRENT] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [REQUESTS_RECOVERY] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_FIRED_TRIGGERS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_JOB_DETAILS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_JOB_DETAILS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_JOB_DETAILS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_JOB_DETAILS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [JOB_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [JOB_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [DESCRIPTION] varchar(250) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [JOB_CLASS_NAME] varchar(250) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [IS_DURABLE] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [IS_NONCONCURRENT] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [IS_UPDATE_DATA] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [REQUESTS_RECOVERY] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [JOB_DATA] varbinary(max)  NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_JOB_DETAILS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_JOB_DETAILS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_LOCKS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_LOCKS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_LOCKS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_LOCKS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [LOCK_NAME] varchar(40) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_LOCKS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_LOCKS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_PAUSED_TRIGGER_GRPS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_PAUSED_TRIGGER_GRPS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_PAUSED_TRIGGER_GRPS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_SCHEDULER_STATE\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_SCHEDULER_STATE]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_SCHEDULER_STATE]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_SCHEDULER_STATE] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [INSTANCE_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [LAST_CHECKIN_TIME] bigint  NOT NULL,\n    [CHECKIN_INTERVAL] bigint  NOT NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_SCHEDULER_STATE] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_SCHEDULER_STATE\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_SIMPLE_TRIGGERS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [REPEAT_COUNT] bigint  NOT NULL,\n    [REPEAT_INTERVAL] bigint  NOT NULL,\n    [TIMES_TRIGGERED] bigint  NOT NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_SIMPROP_TRIGGERS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [STR_PROP_1] varchar(512) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [STR_PROP_2] varchar(512) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [STR_PROP_3] varchar(512) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [INT_PROP_1] int  NULL,\n    [INT_PROP_2] int  NULL,\n    [LONG_PROP_1] bigint  NULL,\n    [LONG_PROP_2] bigint  NULL,\n    [DEC_PROP_1] numeric(13,4)  NULL,\n    [DEC_PROP_2] numeric(13,4)  NULL,\n    [BOOL_PROP_1] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [BOOL_PROP_2] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nCOMMIT\nGO\n\n\n-- ----------------------------\n-- Table structure for QRTZ_TRIGGERS\n-- ----------------------------\nIF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_TRIGGERS]') AND type IN ('U'))\nDROP TABLE [dbo].[QRTZ_TRIGGERS]\n    GO\n\nCREATE TABLE [dbo].[QRTZ_TRIGGERS] (\n    [SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [JOB_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [JOB_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [DESCRIPTION] varchar(250) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [NEXT_FIRE_TIME] bigint  NULL,\n    [PREV_FIRE_TIME] bigint  NULL,\n    [PRIORITY] int  NULL,\n    [TRIGGER_STATE] varchar(16) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [TRIGGER_TYPE] varchar(8) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,\n    [START_TIME] bigint  NOT NULL,\n    [END_TIME] bigint  NULL,\n    [CALENDAR_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,\n    [MISFIRE_INSTR] smallint  NULL,\n    [JOB_DATA] varbinary(max)  NULL\n    )\n    GO\n\nALTER TABLE [dbo].[QRTZ_TRIGGERS] SET (LOCK_ESCALATION = TABLE)\n    GO\n\n\n-- ----------------------------\n-- Records of QRTZ_TRIGGERS\n-- ----------------------------\nBEGIN TRANSACTION\nGO\n\nCOMMIT\nGO\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_CALENDARS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_CALENDARS] ADD CONSTRAINT [PK_QRTZ_CALENDARS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [CALENDAR_NAME])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n\n-- ----------------------------\n-- Indexes structure for table QRTZ_CRON_TRIGGERS\n-- ----------------------------\nCREATE NONCLUSTERED INDEX [IX_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS]\nON [dbo].[QRTZ_CRON_TRIGGERS] (\n  [SCHED_NAME] ASC,\n  [TRIGGER_NAME] ASC,\n  [TRIGGER_GROUP] ASC\n)\nGO\n\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_CRON_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_CRON_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_FIRED_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_FIRED_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_FIRED_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [ENTRY_ID])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_JOB_DETAILS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_JOB_DETAILS] ADD CONSTRAINT [PK_QRTZ_JOB_DETAILS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [JOB_NAME], [JOB_GROUP])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_LOCKS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_LOCKS] ADD CONSTRAINT [PK_QRTZ_LOCKS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [LOCK_NAME])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_PAUSED_TRIGGER_GRPS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] ADD CONSTRAINT [PK_QRTZ_PAUSED_TRIGGER_GRPS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_GROUP])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_SCHEDULER_STATE\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_SCHEDULER_STATE] ADD CONSTRAINT [PK_QRTZ_SCHEDULER_STATE] PRIMARY KEY CLUSTERED ([SCHED_NAME], [INSTANCE_NAME])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n\n-- ----------------------------\n-- Indexes structure for table QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nCREATE NONCLUSTERED INDEX [IX_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS]\nON [dbo].[QRTZ_SIMPLE_TRIGGERS] (\n  [SCHED_NAME] ASC,\n  [TRIGGER_NAME] ASC,\n  [TRIGGER_GROUP] ASC\n)\nGO\n\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_SIMPLE_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n\n-- ----------------------------\n-- Indexes structure for table QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nCREATE NONCLUSTERED INDEX [IX_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS]\nON [dbo].[QRTZ_SIMPROP_TRIGGERS] (\n  [SCHED_NAME] ASC,\n  [TRIGGER_NAME] ASC,\n  [TRIGGER_GROUP] ASC\n)\nGO\n\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_SIMPROP_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n\n-- ----------------------------\n-- Indexes structure for table QRTZ_TRIGGERS\n-- ----------------------------\nCREATE NONCLUSTERED INDEX [IX_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS]\nON [dbo].[QRTZ_TRIGGERS] (\n  [SCHED_NAME] ASC,\n  [TRIGGER_NAME] ASC,\n  [TRIGGER_GROUP] ASC\n)\nGO\n\n\n-- ----------------------------\n-- Primary Key structure for table QRTZ_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP])\n    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\n    ON [PRIMARY]\n    GO\n\n-- ----------------------------\n-- Foreign Keys structure for table QRTZ_BLOB_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_BLOB_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_BLOB_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) REFERENCES [dbo].[QRTZ_TRIGGERS] ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) ON DELETE CASCADE ON UPDATE NO ACTION\n    GO\n\n\n-- ----------------------------\n-- Foreign Keys structure for table QRTZ_CRON_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) REFERENCES [dbo].[QRTZ_TRIGGERS] ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) ON DELETE CASCADE ON UPDATE NO ACTION\n    GO\n\n\n-- ----------------------------\n-- Foreign Keys structure for table QRTZ_SIMPLE_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) REFERENCES [dbo].[QRTZ_TRIGGERS] ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) ON DELETE CASCADE ON UPDATE NO ACTION\n    GO\n\n\n-- ----------------------------\n-- Foreign Keys structure for table QRTZ_SIMPROP_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) REFERENCES [dbo].[QRTZ_TRIGGERS] ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) ON DELETE CASCADE ON UPDATE NO ACTION\n    GO\n\n\n-- ----------------------------\n-- Foreign Keys structure for table QRTZ_TRIGGERS\n-- ----------------------------\nALTER TABLE [dbo].[QRTZ_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS] FOREIGN KEY ([SCHED_NAME], [JOB_NAME], [JOB_GROUP]) REFERENCES [dbo].[QRTZ_JOB_DETAILS] ([SCHED_NAME], [JOB_NAME], [JOB_GROUP]) ON DELETE NO ACTION ON UPDATE NO ACTION\n    GO"
  },
  {
    "path": "sql/sqlserver/ruoyi-vue-pro.sql",
    "content": "/*\n Yudao Database Transfer Tool\n\n Source Server Type    : MySQL\n\n Target Server Type    : Microsoft SQL Server\n\n Date: 2025-05-22 21:03:59\n*/\n\n\n-- ----------------------------\n-- Table structure for dual\n-- ----------------------------\nDROP TABLE IF EXISTS dual\nGO\nCREATE TABLE dual\n(\n    id int\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'数据库连接的表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'dual'\nGO\n\n-- ----------------------------\n-- Records of dual\n-- ----------------------------\n-- @formatter:off\nINSERT INTO dual VALUES (1)\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_api_access_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_api_access_log\nGO\nCREATE TABLE infra_api_access_log\n(\n    id               bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    trace_id         nvarchar(64)  DEFAULT ''                NOT NULL,\n    user_id          bigint        DEFAULT 0                 NOT NULL,\n    user_type        tinyint       DEFAULT 0                 NOT NULL,\n    application_name nvarchar(50)                            NOT NULL,\n    request_method   nvarchar(16)  DEFAULT ''                NOT NULL,\n    request_url      nvarchar(255) DEFAULT ''                NOT NULL,\n    request_params   nvarchar(max)                           NULL,\n    response_body    nvarchar(max)                           NULL,\n    user_ip          nvarchar(50)                            NOT NULL,\n    user_agent       nvarchar(512)                           NOT NULL,\n    operate_module   nvarchar(50)  DEFAULT NULL              NULL,\n    operate_name     nvarchar(50)  DEFAULT NULL              NULL,\n    operate_type     tinyint       DEFAULT 0                 NULL,\n    begin_time       datetime2                               NOT NULL,\n    end_time         datetime2                               NOT NULL,\n    duration         int                                     NOT NULL,\n    result_code      int           DEFAULT 0                 NOT NULL,\n    result_msg       nvarchar(512) DEFAULT ''                NULL,\n    creator          nvarchar(64)  DEFAULT ''                NULL,\n    create_time      datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater          nvarchar(64)  DEFAULT ''                NULL,\n    update_time      datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted          bit           DEFAULT 0                 NOT NULL,\n    tenant_id        bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nCREATE INDEX idx_infra_api_access_log_01 ON infra_api_access_log (create_time)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'日志主键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'链路追踪编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'trace_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'应用名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'application_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'请求方法名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'request_method'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'请求地址',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'request_url'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'请求参数',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'request_params'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'响应结果',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'response_body'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户 IP',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'user_ip'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'浏览器 UA',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'user_agent'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'操作模块',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'operate_module'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'操作名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'operate_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'操作分类',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'operate_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'开始请求时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'begin_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'结束请求时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'end_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'执行时长',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'duration'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'结果码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'result_code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'结果提示',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'result_msg'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'API 访问日志表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_access_log'\nGO\n\n-- ----------------------------\n-- Table structure for infra_api_error_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_api_error_log\nGO\nCREATE TABLE infra_api_error_log\n(\n    id                           bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    trace_id                     nvarchar(64)                            NOT NULL,\n    user_id                      bigint        DEFAULT 0                 NOT NULL,\n    user_type                    tinyint       DEFAULT 0                 NOT NULL,\n    application_name             nvarchar(50)                            NOT NULL,\n    request_method               nvarchar(16)                            NOT NULL,\n    request_url                  nvarchar(255)                           NOT NULL,\n    request_params               nvarchar(4000)                          NOT NULL,\n    user_ip                      nvarchar(50)                            NOT NULL,\n    user_agent                   nvarchar(512)                           NOT NULL,\n    exception_time               datetime2                               NOT NULL,\n    exception_name               nvarchar(128) DEFAULT ''                NOT NULL,\n    exception_message            nvarchar(max)                           NOT NULL,\n    exception_root_cause_message nvarchar(max)                           NOT NULL,\n    exception_stack_trace        nvarchar(max)                           NOT NULL,\n    exception_class_name         nvarchar(512)                           NOT NULL,\n    exception_file_name          nvarchar(512)                           NOT NULL,\n    exception_method_name        nvarchar(512)                           NOT NULL,\n    exception_line_number        int                                     NOT NULL,\n    process_status               tinyint                                 NOT NULL,\n    process_time                 datetime2     DEFAULT NULL              NULL,\n    process_user_id              int           DEFAULT 0                 NULL,\n    creator                      nvarchar(64)  DEFAULT ''                NULL,\n    create_time                  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater                      nvarchar(64)  DEFAULT ''                NULL,\n    update_time                  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted                      bit           DEFAULT 0                 NOT NULL,\n    tenant_id                    bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'链路追踪编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'trace_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'应用名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'application_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'请求方法名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'request_method'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'请求地址',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'request_url'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'请求参数',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'request_params'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户 IP',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'user_ip'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'浏览器 UA',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'user_agent'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'异常发生时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'exception_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'异常名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'exception_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'异常导致的消息',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'exception_message'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'异常导致的根消息',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'exception_root_cause_message'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'异常的栈轨迹',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'exception_stack_trace'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'异常发生的类全名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'exception_class_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'异常发生的类文件',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'exception_file_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'异常发生的方法名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'exception_method_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'异常发生的方法所在行',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'exception_line_number'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'处理状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'process_status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'处理时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'process_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'处理用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'process_user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'系统异常日志',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_api_error_log'\nGO\n\n-- ----------------------------\n-- Table structure for infra_codegen_column\n-- ----------------------------\nDROP TABLE IF EXISTS infra_codegen_column\nGO\nCREATE TABLE infra_codegen_column\n(\n    id                       bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    table_id                 bigint                                  NOT NULL,\n    column_name              nvarchar(200)                           NOT NULL,\n    data_type                nvarchar(100)                           NOT NULL,\n    column_comment           nvarchar(500)                           NOT NULL,\n    nullable                 varchar(1)                              NOT NULL,\n    primary_key              varchar(1)                              NOT NULL,\n    ordinal_position         int                                     NOT NULL,\n    java_type                nvarchar(32)                            NOT NULL,\n    java_field               nvarchar(64)                            NOT NULL,\n    dict_type                nvarchar(200) DEFAULT ''                NULL,\n    example                  nvarchar(64)  DEFAULT NULL              NULL,\n    create_operation         varchar(1)                              NOT NULL,\n    update_operation         varchar(1)                              NOT NULL,\n    list_operation           varchar(1)                              NOT NULL,\n    list_operation_condition nvarchar(32)  DEFAULT '='               NOT NULL,\n    list_operation_result    varchar(1)                              NOT NULL,\n    html_type                nvarchar(32)                            NOT NULL,\n    creator                  nvarchar(64)  DEFAULT ''                NULL,\n    create_time              datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater                  nvarchar(64)  DEFAULT ''                NULL,\n    update_time              datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted                  bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'表编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'table_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字段名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'column_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字段类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'data_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字段描述',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'column_comment'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否允许为空',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'nullable'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否主键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'primary_key'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'排序',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'ordinal_position'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'Java 属性类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'java_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'Java 属性名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'java_field'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'dict_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'数据示例',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'example'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否为 Create 创建操作的字段',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'create_operation'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否为 Update 更新操作的字段',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'update_operation'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否为 List 查询操作的字段',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'list_operation'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'List 查询操作的条件类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'list_operation_condition'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否为 List 查询操作的返回字段',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'list_operation_result'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'显示类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'html_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'代码生成表字段定义',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_column'\nGO\n\n-- ----------------------------\n-- Table structure for infra_codegen_table\n-- ----------------------------\nDROP TABLE IF EXISTS infra_codegen_table\nGO\nCREATE TABLE infra_codegen_table\n(\n    id                    bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    data_source_config_id bigint                                  NOT NULL,\n    scene                 tinyint       DEFAULT 1                 NOT NULL,\n    table_name            nvarchar(200) DEFAULT ''                NOT NULL,\n    table_comment         nvarchar(500) DEFAULT ''                NOT NULL,\n    remark                nvarchar(500) DEFAULT NULL              NULL,\n    module_name           nvarchar(30)                            NOT NULL,\n    business_name         nvarchar(30)                            NOT NULL,\n    class_name            nvarchar(100) DEFAULT ''                NOT NULL,\n    class_comment         nvarchar(50)                            NOT NULL,\n    author                nvarchar(50)                            NOT NULL,\n    template_type         tinyint       DEFAULT 1                 NOT NULL,\n    front_type            tinyint                                 NOT NULL,\n    parent_menu_id        bigint        DEFAULT NULL              NULL,\n    master_table_id       bigint        DEFAULT NULL              NULL,\n    sub_join_column_id    bigint        DEFAULT NULL              NULL,\n    sub_join_many         varchar(1)    DEFAULT NULL              NULL,\n    tree_parent_column_id bigint        DEFAULT NULL              NULL,\n    tree_name_column_id   bigint        DEFAULT NULL              NULL,\n    creator               nvarchar(64)  DEFAULT ''                NULL,\n    create_time           datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater               nvarchar(64)  DEFAULT ''                NULL,\n    update_time           datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted               bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'数据源配置的编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'data_source_config_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'生成场景',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'scene'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'表名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'table_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'表描述',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'table_comment'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模块名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'module_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'业务名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'business_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'类名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'class_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'类描述',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'class_comment'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'作者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'author'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'template_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'前端类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'front_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'父菜单编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'parent_menu_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'主表的编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'master_table_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'子表关联主表的字段编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'sub_join_column_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'主表与子表是否一对多',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'sub_join_many'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'树表的父字段编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'tree_parent_column_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'树表的名字字段编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'tree_name_column_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'代码生成表定义',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_codegen_table'\nGO\n\n-- ----------------------------\n-- Table structure for infra_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_config\nGO\nCREATE TABLE infra_config\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    category    nvarchar(50)                            NOT NULL,\n    type        tinyint                                 NOT NULL,\n    name        nvarchar(100) DEFAULT ''                NOT NULL,\n    config_key  nvarchar(100) DEFAULT ''                NOT NULL,\n    value       nvarchar(500) DEFAULT ''                NOT NULL,\n    visible     varchar(1)                              NOT NULL,\n    remark      nvarchar(500) DEFAULT NULL              NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数主键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数分组',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'category'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数键名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'config_key'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数键值',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'value'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否可见',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'visible'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数配置表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_config'\nGO\n\n-- ----------------------------\n-- Records of infra_config\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT infra_config ON\nGO\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (2, N'biz', 1, N'用户管理-账号初始密码', N'system.user.init-password', N'123456', N'0', N'初始化密码 123456', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-07-20 17:22:47', N'0')\nGO\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (7, N'url', 2, N'MySQL 监控的地址', N'url.druid', N'', N'1', N'', N'1', N'2023-04-07 13:41:16', N'1', N'2023-04-07 14:33:38', N'0')\nGO\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (8, N'url', 2, N'SkyWalking 监控的地址', N'url.skywalking', N'', N'1', N'', N'1', N'2023-04-07 13:41:16', N'1', N'2023-04-07 14:57:03', N'0')\nGO\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (9, N'url', 2, N'Spring Boot Admin 监控的地址', N'url.spring-boot-admin', N'', N'1', N'', N'1', N'2023-04-07 13:41:16', N'1', N'2023-04-07 14:52:07', N'0')\nGO\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (10, N'url', 2, N'Swagger 接口文档的地址', N'url.swagger', N'', N'1', N'', N'1', N'2023-04-07 13:41:16', N'1', N'2023-04-07 14:59:00', N'0')\nGO\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (11, N'ui', 2, N'腾讯地图 key', N'tencent.lbs.key', N'TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E', N'1', N'腾讯地图 key', N'1', N'2023-06-03 19:16:27', N'1', N'2023-06-03 19:16:27', N'0')\nGO\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (12, N'test2', 2, N'test3', N'test4', N'test5', N'1', N'test6', N'1', N'2023-12-03 09:55:16', N'1', N'2025-04-06 21:00:09', N'0')\nGO\nINSERT INTO infra_config (id, category, type, name, config_key, value, visible, remark, creator, create_time, updater, update_time, deleted) VALUES (13, N'用户管理-账号初始密码', 2, N'用户管理-注册开关', N'system.user.register-enabled', N'true', N'0', N'', N'1', N'2025-04-26 17:23:41', N'1', N'2025-04-26 17:23:41', N'0')\nGO\nSET IDENTITY_INSERT infra_config OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_data_source_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_data_source_config\nGO\nCREATE TABLE infra_data_source_config\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name        nvarchar(100) DEFAULT ''                NOT NULL,\n    url         nvarchar(1024)                          NOT NULL,\n    username    nvarchar(255)                           NOT NULL,\n    password    nvarchar(255) DEFAULT ''                NOT NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'主键编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'数据源连接',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'url'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'username'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'密码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'password'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'数据源配置表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_data_source_config'\nGO\n\n-- ----------------------------\n-- Table structure for infra_file\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file\nGO\nCREATE TABLE infra_file\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    config_id   bigint        DEFAULT NULL              NULL,\n    name        nvarchar(256) DEFAULT NULL              NULL,\n    path        nvarchar(512)                           NOT NULL,\n    url         nvarchar(1024)                          NOT NULL,\n    type        nvarchar(128) DEFAULT NULL              NULL,\n    size        int                                     NOT NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'配置编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'config_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件路径',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'path'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件 URL',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'url'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件大小',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'size'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file'\nGO\n\n-- ----------------------------\n-- Table structure for infra_file_config\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file_config\nGO\nCREATE TABLE infra_file_config\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name        nvarchar(63)                            NOT NULL,\n    storage     tinyint                                 NOT NULL,\n    remark      nvarchar(255) DEFAULT NULL              NULL,\n    master      varchar(1)                              NOT NULL,\n    config      nvarchar(4000)                          NOT NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'配置名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'存储器',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'storage'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否为主配置',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'master'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'存储配置',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'config'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件配置表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_config'\nGO\n\n-- ----------------------------\n-- Records of infra_file_config\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT infra_file_config ON\nGO\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (4, N'数据库（示例）', 1, N'我是数据库', N'0', N'{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', N'1', N'2022-03-15 23:56:24', N'1', N'2025-05-02 18:30:28', N'0')\nGO\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (22, N'七牛存储器（示例）', 20, N'请换成你自己的密钥！！！', N'1', N'{\"@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}', N'1', N'2024-01-13 22:11:12', N'1', N'2025-05-02 18:30:28', N'0')\nGO\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (24, N'腾讯云存储（示例）', 20, N'请换成你的密钥！！！', N'0', N'{\"@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\"}', N'1', N'2024-11-09 16:03:22', N'1', N'2025-05-02 18:30:28', N'0')\nGO\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (25, N'阿里云存储（示例）', 20, N'', N'0', N'{\"@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}', N'1', N'2024-11-09 16:47:08', N'1', N'2025-05-02 18:30:28', N'0')\nGO\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (26, N'火山云存储（示例）', 20, N'', N'0', N'{\"@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}', N'1', N'2024-11-09 16:56:42', N'1', N'2025-05-02 18:30:28', N'0')\nGO\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (27, N'华为云存储（示例）', 20, N'', N'0', N'{\"@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}', N'1', N'2024-11-09 17:18:41', N'1', N'2025-05-02 18:30:28', N'0')\nGO\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (28, N'MinIO 存储（示例）', 20, N'', N'0', N'{\"@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}', N'1', N'2024-11-09 17:43:10', N'1', N'2025-05-02 18:30:28', N'0')\nGO\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (29, N'本地存储（示例）', 10, N'仅适合 mac 或 windows', N'0', N'{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig\",\"basePath\":\"/Users/yunai/tmp/file\",\"domain\":\"http://127.0.0.1:48080\"}', N'1', N'2025-05-02 11:25:45', N'1', N'2025-05-02 18:30:28', N'0')\nGO\nINSERT INTO infra_file_config (id, name, storage, remark, master, config, creator, create_time, updater, update_time, deleted) VALUES (30, N'SFTP 存储（示例）', 12, N'', N'0', N'{\"@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\"}', N'1', N'2025-05-02 16:34:10', N'1', N'2025-05-02 18:30:28', N'0')\nGO\nSET IDENTITY_INSERT infra_file_config OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_file_content\n-- ----------------------------\nDROP TABLE IF EXISTS infra_file_content\nGO\nCREATE TABLE infra_file_content\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    config_id   bigint                                 NOT NULL,\n    path        nvarchar(512)                          NOT NULL,\n    content     varbinary(max)                         NOT NULL,\n    creator     nvarchar(64) DEFAULT ''                NULL,\n    create_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64) DEFAULT ''                NULL,\n    update_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'配置编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content',\n     'COLUMN', N'config_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件路径',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content',\n     'COLUMN', N'path'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件内容',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content',\n     'COLUMN', N'content'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'文件表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_file_content'\nGO\n\n-- ----------------------------\n-- Table structure for infra_job\n-- ----------------------------\nDROP TABLE IF EXISTS infra_job\nGO\nCREATE TABLE infra_job\n(\n    id              bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name            nvarchar(32)                            NOT NULL,\n    status          tinyint                                 NOT NULL,\n    handler_name    nvarchar(64)                            NOT NULL,\n    handler_param   nvarchar(255) DEFAULT NULL              NULL,\n    cron_expression nvarchar(32)                            NOT NULL,\n    retry_count     int           DEFAULT 0                 NOT NULL,\n    retry_interval  int           DEFAULT 0                 NOT NULL,\n    monitor_timeout int           DEFAULT 0                 NOT NULL,\n    creator         nvarchar(64)  DEFAULT ''                NULL,\n    create_time     datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         nvarchar(64)  DEFAULT ''                NULL,\n    update_time     datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'任务编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'任务名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'任务状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'处理器的名字',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'handler_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'处理器的参数',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'handler_param'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'CRON 表达式',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'cron_expression'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'重试次数',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'retry_count'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'重试间隔',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'retry_interval'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'监控超时时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'monitor_timeout'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'定时任务表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job'\nGO\n\n-- ----------------------------\n-- Records of infra_job\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT infra_job ON\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (5, N'支付通知 Job', 2, N'payNotifyJob', NULL, N'* * * * * ?', 0, 0, 0, N'1', N'2021-10-27 08:34:42', N'1', N'2024-09-12 13:32:48', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (17, N'支付订单同步 Job', 2, N'payOrderSyncJob', NULL, N'0 0/1 * * * ?', 0, 0, 0, N'1', N'2023-07-22 14:36:26', N'1', N'2023-07-22 15:39:08', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (18, N'支付订单过期 Job', 2, N'payOrderExpireJob', NULL, N'0 0/1 * * * ?', 0, 0, 0, N'1', N'2023-07-22 15:36:23', N'1', N'2023-07-22 15:39:54', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (19, N'退款订单的同步 Job', 2, N'payRefundSyncJob', NULL, N'0 0/1 * * * ?', 0, 0, 0, N'1', N'2023-07-23 21:03:44', N'1', N'2023-07-23 21:09:00', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (21, N'交易订单的自动过期 Job', 2, N'tradeOrderAutoCancelJob', N'', N'0 * * * * ?', 3, 0, 0, N'1', N'2023-09-25 23:43:26', N'1', N'2023-09-26 19:23:30', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (22, N'交易订单的自动收货 Job', 2, N'tradeOrderAutoReceiveJob', N'', N'0 * * * * ?', 3, 0, 0, N'1', N'2023-09-26 19:23:53', N'1', N'2023-09-26 23:38:08', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (23, N'交易订单的自动评论 Job', 2, N'tradeOrderAutoCommentJob', N'', N'0 * * * * ?', 3, 0, 0, N'1', N'2023-09-26 23:38:29', N'1', N'2023-09-27 11:03:10', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (24, N'佣金解冻 Job', 2, N'brokerageRecordUnfreezeJob', N'', N'0 * * * * ?', 3, 0, 0, N'1', N'2023-09-28 22:01:46', N'1', N'2023-09-28 22:01:56', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (25, N'访问日志清理 Job', 2, N'accessLogCleanJob', N'', N'0 0 0 * * ?', 3, 0, 0, N'1', N'2023-10-03 10:59:41', N'1', N'2023-10-03 11:01:10', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (26, N'错误日志清理 Job', 2, N'errorLogCleanJob', N'', N'0 0 0 * * ?', 3, 0, 0, N'1', N'2023-10-03 11:00:43', N'1', N'2023-10-03 11:01:12', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (27, N'任务日志清理 Job', 2, N'jobLogCleanJob', N'', N'0 0 0 * * ?', 3, 0, 0, N'1', N'2023-10-03 11:01:33', N'1', N'2024-09-12 13:40:34', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (33, N'demoJob', 2, N'demoJob', N'', N'0 * * * * ?', 1, 1, 0, N'1', N'2024-10-27 19:38:46', N'1', N'2025-05-10 18:13:54', N'0')\nGO\nINSERT INTO infra_job (id, name, status, handler_name, handler_param, cron_expression, retry_count, retry_interval, monitor_timeout, creator, create_time, updater, update_time, deleted) VALUES (35, N'转账订单的同步 Job', 2, N'payTransferSyncJob', N'', N'0 * * * * ?', 0, 0, 0, N'1', N'2025-05-10 17:35:54', N'1', N'2025-05-10 18:13:52', N'0')\nGO\nSET IDENTITY_INSERT infra_job OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for infra_job_log\n-- ----------------------------\nDROP TABLE IF EXISTS infra_job_log\nGO\nCREATE TABLE infra_job_log\n(\n    id            bigint                                   NOT NULL PRIMARY KEY IDENTITY,\n    job_id        bigint                                   NOT NULL,\n    handler_name  nvarchar(64)                             NOT NULL,\n    handler_param nvarchar(255)  DEFAULT NULL              NULL,\n    execute_index tinyint        DEFAULT 1                 NOT NULL,\n    begin_time    datetime2                                NOT NULL,\n    end_time      datetime2      DEFAULT NULL              NULL,\n    duration      int            DEFAULT NULL              NULL,\n    status        tinyint                                  NOT NULL,\n    result        nvarchar(4000) DEFAULT ''                NULL,\n    creator       nvarchar(64)   DEFAULT ''                NULL,\n    create_time   datetime2      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       nvarchar(64)   DEFAULT ''                NULL,\n    update_time   datetime2      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       bit            DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'日志编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'任务编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'job_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'处理器的名字',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'handler_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'处理器的参数',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'handler_param'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'第几次执行',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'execute_index'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'开始执行时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'begin_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'结束执行时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'end_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'执行时长',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'duration'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'任务状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'结果数据',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'result'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'定时任务日志表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'infra_job_log'\nGO\n\n-- ----------------------------\n-- Table structure for system_dept\n-- ----------------------------\nDROP TABLE IF EXISTS system_dept\nGO\nCREATE TABLE system_dept\n(\n    id             bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    name           nvarchar(30) DEFAULT ''                NOT NULL,\n    parent_id      bigint       DEFAULT 0                 NOT NULL,\n    sort           int          DEFAULT 0                 NOT NULL,\n    leader_user_id bigint       DEFAULT NULL              NULL,\n    phone          nvarchar(11) DEFAULT NULL              NULL,\n    email          nvarchar(50) DEFAULT NULL              NULL,\n    status         tinyint                                NOT NULL,\n    creator        nvarchar(64) DEFAULT ''                NULL,\n    create_time    datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        nvarchar(64) DEFAULT ''                NULL,\n    update_time    datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit          DEFAULT 0                 NOT NULL,\n    tenant_id      bigint       DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'部门id',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'部门名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'父部门id',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'parent_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'显示顺序',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'sort'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'负责人',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'leader_user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'联系电话',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'phone'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'邮箱',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'email'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'部门状态（0正常 1停用）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'部门表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dept'\nGO\n\n-- ----------------------------\n-- Records of system_dept\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_dept ON\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, N'芋道源码', 0, 0, 1, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'1', N'2025-03-29 15:47:53', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (101, N'深圳总公司', 100, 1, 104, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'1', N'2025-03-29 15:49:55', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (102, N'长沙分公司', 100, 2, NULL, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'', N'2021-12-15 05:01:40', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, N'研发部门', 101, 1, 1, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'1', N'2024-10-02 10:22:03', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, N'市场部门', 101, 2, NULL, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'', N'2021-12-15 05:01:38', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (105, N'测试部门', 101, 3, NULL, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'1', N'2022-05-16 20:25:15', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (106, N'财务部门', 101, 4, 103, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'103', N'2022-01-15 21:32:22', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (107, N'运维部门', 101, 5, 1, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'1', N'2023-12-02 09:28:22', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (108, N'市场部门', 102, 1, NULL, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'1', N'2022-02-16 08:35:45', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (109, N'财务部门', 102, 2, NULL, N'15888888888', N'ry@qq.com', 0, N'admin', N'2021-01-05 17:03:47', N'', N'2021-12-15 05:01:29', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (110, N'新部门', 0, 1, NULL, NULL, NULL, 0, N'110', N'2022-02-23 20:46:30', N'110', N'2022-02-23 20:46:30', N'0', 121)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (111, N'顶级部门', 0, 1, NULL, NULL, NULL, 0, N'113', N'2022-03-07 21:44:50', N'113', N'2022-03-07 21:44:50', N'0', 122)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, N'产品部门', 101, 100, 1, NULL, NULL, 1, N'1', N'2023-12-02 09:45:13', N'1', N'2023-12-02 09:45:31', N'0', 1)\nGO\nINSERT INTO system_dept (id, name, parent_id, sort, leader_user_id, phone, email, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, N'支持部门', 102, 3, 104, NULL, NULL, 1, N'1', N'2023-12-02 09:47:38', N'1', N'2025-03-29 15:00:56', N'0', 1)\nGO\nSET IDENTITY_INSERT system_dept OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_dict_data\n-- ----------------------------\nDROP TABLE IF EXISTS system_dict_data\nGO\nCREATE TABLE system_dict_data\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    sort        int           DEFAULT 0                 NOT NULL,\n    label       nvarchar(100) DEFAULT ''                NOT NULL,\n    value       nvarchar(100) DEFAULT ''                NOT NULL,\n    dict_type   nvarchar(100) DEFAULT ''                NOT NULL,\n    status      tinyint       DEFAULT 0                 NOT NULL,\n    color_type  nvarchar(100) DEFAULT ''                NULL,\n    css_class   nvarchar(100) DEFAULT ''                NULL,\n    remark      nvarchar(500) DEFAULT NULL              NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典排序',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'sort'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典标签',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'label'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典键值',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'value'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'dict_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'状态（0正常 1停用）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'颜色类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'color_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'css 样式',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'css_class'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典数据表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_data'\nGO\n\n-- ----------------------------\n-- Records of system_dict_data\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_dict_data ON\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1, 1, N'男', N'1', N'system_user_sex', 0, N'default', N'A', N'性别男', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-03-29 00:14:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2, 2, N'女', N'2', N'system_user_sex', 0, N'success', N'', N'性别女', N'admin', N'2021-01-05 17:03:48', N'1', N'2023-11-15 23:30:37', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (8, 1, N'正常', N'1', N'infra_job_status', 0, N'success', N'', N'正常状态', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 19:33:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (9, 2, N'暂停', N'2', N'infra_job_status', 0, N'danger', N'', N'停用状态', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 19:33:45', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (12, 1, N'系统内置', N'1', N'infra_config_type', 0, N'danger', N'', N'参数类型 - 系统内置', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 19:06:02', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (13, 2, N'自定义', N'2', N'infra_config_type', 0, N'primary', N'', N'参数类型 - 自定义', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 19:06:07', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (14, 1, N'通知', N'1', N'system_notice_type', 0, N'success', N'', N'通知', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 13:05:57', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (15, 2, N'公告', N'2', N'system_notice_type', 0, N'info', N'', N'公告', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 13:06:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (16, 0, N'其它', N'0', N'infra_operate_type', 0, N'default', N'', N'其它操作', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-03-14 12:44:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (17, 1, N'查询', N'1', N'infra_operate_type', 0, N'info', N'', N'查询操作', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-03-14 12:44:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (18, 2, N'新增', N'2', N'infra_operate_type', 0, N'primary', N'', N'新增操作', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-03-14 12:44:21', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (19, 3, N'修改', N'3', N'infra_operate_type', 0, N'warning', N'', N'修改操作', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-03-14 12:44:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (20, 4, N'删除', N'4', N'infra_operate_type', 0, N'danger', N'', N'删除操作', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-03-14 12:44:23', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (22, 5, N'导出', N'5', N'infra_operate_type', 0, N'default', N'', N'导出操作', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-03-14 12:44:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (23, 6, N'导入', N'6', N'infra_operate_type', 0, N'default', N'', N'导入操作', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-03-14 12:44:25', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (27, 1, N'开启', N'0', N'common_status', 0, N'primary', N'', N'开启状态', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 08:00:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (28, 2, N'关闭', N'1', N'common_status', 0, N'info', N'', N'关闭状态', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 08:00:44', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (29, 1, N'目录', N'1', N'system_menu_type', 0, N'', N'', N'目录', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:43:45', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (30, 2, N'菜单', N'2', N'system_menu_type', 0, N'', N'', N'菜单', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:43:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (31, 3, N'按钮', N'3', N'system_menu_type', 0, N'', N'', N'按钮', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:43:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (32, 1, N'内置', N'1', N'system_role_type', 0, N'danger', N'', N'内置角色', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 13:02:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (33, 2, N'自定义', N'2', N'system_role_type', 0, N'primary', N'', N'自定义角色', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-02-16 13:02:12', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (34, 1, N'全部数据权限', N'1', N'system_data_scope', 0, N'', N'', N'全部数据权限', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:47:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (35, 2, N'指定部门数据权限', N'2', N'system_data_scope', 0, N'', N'', N'指定部门数据权限', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:47:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (36, 3, N'本部门数据权限', N'3', N'system_data_scope', 0, N'', N'', N'本部门数据权限', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:47:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (37, 4, N'本部门及以下数据权限', N'4', N'system_data_scope', 0, N'', N'', N'本部门及以下数据权限', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:47:21', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (38, 5, N'仅本人数据权限', N'5', N'system_data_scope', 0, N'', N'', N'仅本人数据权限', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:47:23', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (39, 0, N'成功', N'0', N'system_login_result', 0, N'success', N'', N'登陆结果 - 成功', N'', N'2021-01-18 06:17:36', N'1', N'2022-02-16 13:23:49', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (40, 10, N'账号或密码不正确', N'10', N'system_login_result', 0, N'primary', N'', N'登陆结果 - 账号或密码不正确', N'', N'2021-01-18 06:17:54', N'1', N'2022-02-16 13:24:27', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (41, 20, N'用户被禁用', N'20', N'system_login_result', 0, N'warning', N'', N'登陆结果 - 用户被禁用', N'', N'2021-01-18 06:17:54', N'1', N'2022-02-16 13:23:57', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (42, 30, N'验证码不存在', N'30', N'system_login_result', 0, N'info', N'', N'登陆结果 - 验证码不存在', N'', N'2021-01-18 06:17:54', N'1', N'2022-02-16 13:24:07', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (43, 31, N'验证码不正确', N'31', N'system_login_result', 0, N'info', N'', N'登陆结果 - 验证码不正确', N'', N'2021-01-18 06:17:54', N'1', N'2022-02-16 13:24:11', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (44, 100, N'未知异常', N'100', N'system_login_result', 0, N'danger', N'', N'登陆结果 - 未知异常', N'', N'2021-01-18 06:17:54', N'1', N'2022-02-16 13:24:23', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (45, 1, N'是', N'true', N'infra_boolean_string', 0, N'danger', N'', N'Boolean 是否类型 - 是', N'', N'2021-01-19 03:20:55', N'1', N'2022-03-15 23:01:45', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (46, 1, N'否', N'false', N'infra_boolean_string', 0, N'info', N'', N'Boolean 是否类型 - 否', N'', N'2021-01-19 03:20:55', N'1', N'2022-03-15 23:09:45', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (50, 1, N'单表（增删改查）', N'1', N'infra_codegen_template_type', 0, N'', N'', NULL, N'', N'2021-02-05 07:09:06', N'', N'2022-03-10 16:33:15', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (51, 2, N'树表（增删改查）', N'2', N'infra_codegen_template_type', 0, N'', N'', NULL, N'', N'2021-02-05 07:14:46', N'', N'2022-03-10 16:33:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (53, 0, N'初始化中', N'0', N'infra_job_status', 0, N'primary', N'', NULL, N'', N'2021-02-07 07:46:49', N'1', N'2022-02-16 19:33:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (57, 0, N'运行中', N'0', N'infra_job_log_status', 0, N'primary', N'', N'RUNNING', N'', N'2021-02-08 10:04:24', N'1', N'2022-02-16 19:07:48', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (58, 1, N'成功', N'1', N'infra_job_log_status', 0, N'success', N'', NULL, N'', N'2021-02-08 10:06:57', N'1', N'2022-02-16 19:07:52', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (59, 2, N'失败', N'2', N'infra_job_log_status', 0, N'warning', N'', N'失败', N'', N'2021-02-08 10:07:38', N'1', N'2022-02-16 19:07:56', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (60, 1, N'会员', N'1', N'user_type', 0, N'primary', N'', NULL, N'', N'2021-02-26 00:16:27', N'1', N'2022-02-16 10:22:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (61, 2, N'管理员', N'2', N'user_type', 0, N'success', N'', NULL, N'', N'2021-02-26 00:16:34', N'1', N'2025-04-06 18:37:43', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (62, 0, N'未处理', N'0', N'infra_api_error_log_process_status', 0, N'primary', N'', NULL, N'', N'2021-02-26 07:07:19', N'1', N'2022-02-16 20:14:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (63, 1, N'已处理', N'1', N'infra_api_error_log_process_status', 0, N'success', N'', NULL, N'', N'2021-02-26 07:07:26', N'1', N'2022-02-16 20:14:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (64, 2, N'已忽略', N'2', N'infra_api_error_log_process_status', 0, N'danger', N'', NULL, N'', N'2021-02-26 07:07:34', N'1', N'2022-02-16 20:14:14', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (66, 1, N'阿里云', N'ALIYUN', N'system_sms_channel_code', 0, N'primary', N'', NULL, N'1', N'2021-04-05 01:05:26', N'1', N'2024-07-22 22:23:25', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (67, 1, N'验证码', N'1', N'system_sms_template_type', 0, N'warning', N'', NULL, N'1', N'2021-04-05 21:50:57', N'1', N'2022-02-16 12:48:30', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (68, 2, N'通知', N'2', N'system_sms_template_type', 0, N'primary', N'', NULL, N'1', N'2021-04-05 21:51:08', N'1', N'2022-02-16 12:48:27', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (69, 0, N'营销', N'3', N'system_sms_template_type', 0, N'danger', N'', NULL, N'1', N'2021-04-05 21:51:15', N'1', N'2022-02-16 12:48:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (70, 0, N'初始化', N'0', N'system_sms_send_status', 0, N'primary', N'', NULL, N'1', N'2021-04-11 20:18:33', N'1', N'2022-02-16 10:26:07', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (71, 1, N'发送成功', N'10', N'system_sms_send_status', 0, N'success', N'', NULL, N'1', N'2021-04-11 20:18:43', N'1', N'2022-02-16 10:25:56', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (72, 2, N'发送失败', N'20', N'system_sms_send_status', 0, N'danger', N'', NULL, N'1', N'2021-04-11 20:18:49', N'1', N'2022-02-16 10:26:03', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (73, 3, N'不发送', N'30', N'system_sms_send_status', 0, N'info', N'', NULL, N'1', N'2021-04-11 20:19:44', N'1', N'2022-02-16 10:26:10', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (74, 0, N'等待结果', N'0', N'system_sms_receive_status', 0, N'primary', N'', NULL, N'1', N'2021-04-11 20:27:43', N'1', N'2022-02-16 10:28:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (75, 1, N'接收成功', N'10', N'system_sms_receive_status', 0, N'success', N'', NULL, N'1', N'2021-04-11 20:29:25', N'1', N'2022-02-16 10:28:28', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (76, 2, N'接收失败', N'20', N'system_sms_receive_status', 0, N'danger', N'', NULL, N'1', N'2021-04-11 20:29:31', N'1', N'2022-02-16 10:28:32', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (77, 0, N'调试(钉钉)', N'DEBUG_DING_TALK', N'system_sms_channel_code', 0, N'info', N'', NULL, N'1', N'2021-04-13 00:20:37', N'1', N'2022-02-16 10:10:00', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (80, 100, N'账号登录', N'100', N'system_login_type', 0, N'primary', N'', N'账号登录', N'1', N'2021-10-06 00:52:02', N'1', N'2022-02-16 13:11:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (81, 101, N'社交登录', N'101', N'system_login_type', 0, N'info', N'', N'社交登录', N'1', N'2021-10-06 00:52:17', N'1', N'2022-02-16 13:11:40', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (83, 200, N'主动登出', N'200', N'system_login_type', 0, N'primary', N'', N'主动登出', N'1', N'2021-10-06 00:52:58', N'1', N'2022-02-16 13:11:49', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (85, 202, N'强制登出', N'202', N'system_login_type', 0, N'danger', N'', N'强制退出', N'1', N'2021-10-06 00:53:41', N'1', N'2022-02-16 13:11:57', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (86, 0, N'病假', N'1', N'bpm_oa_leave_type', 0, N'primary', N'', NULL, N'1', N'2021-09-21 22:35:28', N'1', N'2022-02-16 10:00:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (87, 1, N'事假', N'2', N'bpm_oa_leave_type', 0, N'info', N'', NULL, N'1', N'2021-09-21 22:36:11', N'1', N'2022-02-16 10:00:49', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (88, 2, N'婚假', N'3', N'bpm_oa_leave_type', 0, N'warning', N'', NULL, N'1', N'2021-09-21 22:36:38', N'1', N'2022-02-16 10:00:53', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (112, 0, N'微信 Wap 网站支付', N'wx_wap', N'pay_channel_code', 0, N'success', N'', N'微信 Wap 网站支付', N'1', N'2023-07-19 20:08:06', N'1', N'2023-07-19 20:09:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (113, 1, N'微信公众号支付', N'wx_pub', N'pay_channel_code', 0, N'success', N'', N'微信公众号支付', N'1', N'2021-12-03 10:40:24', N'1', N'2023-07-19 20:08:47', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (114, 2, N'微信小程序支付', N'wx_lite', N'pay_channel_code', 0, N'success', N'', N'微信小程序支付', N'1', N'2021-12-03 10:41:06', N'1', N'2023-07-19 20:08:50', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (115, 3, N'微信 App 支付', N'wx_app', N'pay_channel_code', 0, N'success', N'', N'微信 App 支付', N'1', N'2021-12-03 10:41:20', N'1', N'2023-07-19 20:08:56', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (116, 10, N'支付宝 PC 网站支付', N'alipay_pc', N'pay_channel_code', 0, N'primary', N'', N'支付宝 PC 网站支付', N'1', N'2021-12-03 10:42:09', N'1', N'2023-07-19 20:09:12', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (117, 11, N'支付宝 Wap 网站支付', N'alipay_wap', N'pay_channel_code', 0, N'primary', N'', N'支付宝 Wap 网站支付', N'1', N'2021-12-03 10:42:26', N'1', N'2023-07-19 20:09:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (118, 12, N'支付宝 App 支付', N'alipay_app', N'pay_channel_code', 0, N'primary', N'', N'支付宝 App 支付', N'1', N'2021-12-03 10:42:55', N'1', N'2023-07-19 20:09:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (119, 14, N'支付宝扫码支付', N'alipay_qr', N'pay_channel_code', 0, N'primary', N'', N'支付宝扫码支付', N'1', N'2021-12-03 10:43:10', N'1', N'2023-07-19 20:09:28', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (120, 10, N'通知成功', N'10', N'pay_notify_status', 0, N'success', N'', N'通知成功', N'1', N'2021-12-03 11:02:41', N'1', N'2023-07-19 10:08:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (121, 20, N'通知失败', N'20', N'pay_notify_status', 0, N'danger', N'', N'通知失败', N'1', N'2021-12-03 11:02:59', N'1', N'2023-07-19 10:08:21', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (122, 0, N'等待通知', N'0', N'pay_notify_status', 0, N'info', N'', N'未通知', N'1', N'2021-12-03 11:03:10', N'1', N'2023-07-19 10:08:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (123, 10, N'支付成功', N'10', N'pay_order_status', 0, N'success', N'', N'支付成功', N'1', N'2021-12-03 11:18:29', N'1', N'2023-07-19 18:04:28', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (124, 30, N'支付关闭', N'30', N'pay_order_status', 0, N'info', N'', N'支付关闭', N'1', N'2021-12-03 11:18:42', N'1', N'2023-07-19 18:05:07', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (125, 0, N'等待支付', N'0', N'pay_order_status', 0, N'info', N'', N'未支付', N'1', N'2021-12-03 11:18:18', N'1', N'2023-07-19 18:04:15', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (600, 5, N'首页', N'1', N'promotion_banner_position', 0, N'warning', N'', N'', N'1', N'2023-10-11 07:45:24', N'1', N'2023-10-11 07:45:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (601, 4, N'秒杀活动页', N'2', N'promotion_banner_position', 0, N'warning', N'', N'', N'1', N'2023-10-11 07:45:24', N'1', N'2023-10-11 07:45:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (602, 3, N'砍价活动页', N'3', N'promotion_banner_position', 0, N'warning', N'', N'', N'1', N'2023-10-11 07:45:24', N'1', N'2023-10-11 07:45:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (603, 2, N'限时折扣页', N'4', N'promotion_banner_position', 0, N'warning', N'', N'', N'1', N'2023-10-11 07:45:24', N'1', N'2023-10-11 07:45:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (604, 1, N'满减送页', N'5', N'promotion_banner_position', 0, N'warning', N'', N'', N'1', N'2023-10-11 07:45:24', N'1', N'2023-10-11 07:45:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1118, 0, N'等待退款', N'0', N'pay_refund_status', 0, N'info', N'', N'等待退款', N'1', N'2021-12-10 16:44:59', N'1', N'2023-07-19 10:14:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1119, 20, N'退款失败', N'20', N'pay_refund_status', 0, N'danger', N'', N'退款失败', N'1', N'2021-12-10 16:45:10', N'1', N'2023-07-19 10:15:10', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1124, 10, N'退款成功', N'10', N'pay_refund_status', 0, N'success', N'', N'退款成功', N'1', N'2021-12-10 16:46:26', N'1', N'2023-07-19 10:15:00', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1127, 1, N'审批中', N'1', N'bpm_process_instance_status', 0, N'default', N'', N'流程实例的状态 - 进行中', N'1', N'2022-01-07 23:47:22', N'1', N'2024-03-16 16:11:45', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1128, 2, N'审批通过', N'2', N'bpm_process_instance_status', 0, N'success', N'', N'流程实例的状态 - 已完成', N'1', N'2022-01-07 23:47:49', N'1', N'2024-03-16 16:11:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1129, 1, N'审批中', N'1', N'bpm_task_status', 0, N'primary', N'', N'流程实例的结果 - 处理中', N'1', N'2022-01-07 23:48:32', N'1', N'2024-03-08 22:41:37', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1130, 2, N'审批通过', N'2', N'bpm_task_status', 0, N'success', N'', N'流程实例的结果 - 通过', N'1', N'2022-01-07 23:48:45', N'1', N'2024-03-08 22:41:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1131, 3, N'审批不通过', N'3', N'bpm_task_status', 0, N'danger', N'', N'流程实例的结果 - 不通过', N'1', N'2022-01-07 23:48:55', N'1', N'2024-03-08 22:41:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1132, 4, N'已取消', N'4', N'bpm_task_status', 0, N'info', N'', N'流程实例的结果 - 撤销', N'1', N'2022-01-07 23:49:06', N'1', N'2024-03-08 22:41:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1133, 10, N'流程表单', N'10', N'bpm_model_form_type', 0, N'', N'', N'流程的表单类型 - 流程表单', N'103', N'2022-01-11 23:51:30', N'103', N'2022-01-11 23:51:30', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1134, 20, N'业务表单', N'20', N'bpm_model_form_type', 0, N'', N'', N'流程的表单类型 - 业务表单', N'103', N'2022-01-11 23:51:47', N'103', N'2022-01-11 23:51:47', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1135, 10, N'角色', N'10', N'bpm_task_candidate_strategy', 0, N'info', N'', N'任务分配规则的类型 - 角色', N'103', N'2022-01-12 23:21:22', N'1', N'2024-03-06 02:53:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1136, 20, N'部门的成员', N'20', N'bpm_task_candidate_strategy', 0, N'primary', N'', N'任务分配规则的类型 - 部门的成员', N'103', N'2022-01-12 23:21:47', N'1', N'2024-03-06 02:53:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1137, 21, N'部门的负责人', N'21', N'bpm_task_candidate_strategy', 0, N'primary', N'', N'任务分配规则的类型 - 部门的负责人', N'103', N'2022-01-12 23:33:36', N'1', N'2024-03-06 02:53:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1138, 30, N'用户', N'30', N'bpm_task_candidate_strategy', 0, N'info', N'', N'任务分配规则的类型 - 用户', N'103', N'2022-01-12 23:34:02', N'1', N'2024-03-06 02:53:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1139, 40, N'用户组', N'40', N'bpm_task_candidate_strategy', 0, N'warning', N'', N'任务分配规则的类型 - 用户组', N'103', N'2022-01-12 23:34:21', N'1', N'2024-03-06 02:53:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1140, 60, N'流程表达式', N'60', N'bpm_task_candidate_strategy', 0, N'danger', N'', N'任务分配规则的类型 - 流程表达式', N'103', N'2022-01-12 23:34:43', N'1', N'2024-03-06 02:53:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1141, 22, N'岗位', N'22', N'bpm_task_candidate_strategy', 0, N'success', N'', N'任务分配规则的类型 - 岗位', N'103', N'2022-01-14 18:41:55', N'1', N'2024-03-06 02:53:21', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1145, 1, N'管理后台', N'1', N'infra_codegen_scene', 0, N'', N'', N'代码生成的场景枚举 - 管理后台', N'1', N'2022-02-02 13:15:06', N'1', N'2022-03-10 16:32:59', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1146, 2, N'用户 APP', N'2', N'infra_codegen_scene', 0, N'', N'', N'代码生成的场景枚举 - 用户 APP', N'1', N'2022-02-02 13:15:19', N'1', N'2022-03-10 16:33:03', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1150, 1, N'数据库', N'1', N'infra_file_storage', 0, N'default', N'', NULL, N'1', N'2022-03-15 00:25:28', N'1', N'2022-03-15 00:25:28', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1151, 10, N'本地磁盘', N'10', N'infra_file_storage', 0, N'default', N'', NULL, N'1', N'2022-03-15 00:25:41', N'1', N'2022-03-15 00:25:56', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1152, 11, N'FTP 服务器', N'11', N'infra_file_storage', 0, N'default', N'', NULL, N'1', N'2022-03-15 00:26:06', N'1', N'2022-03-15 00:26:10', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1153, 12, N'SFTP 服务器', N'12', N'infra_file_storage', 0, N'default', N'', NULL, N'1', N'2022-03-15 00:26:22', N'1', N'2022-03-15 00:26:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1154, 20, N'S3 对象存储', N'20', N'infra_file_storage', 0, N'default', N'', NULL, N'1', N'2022-03-15 00:26:31', N'1', N'2022-03-15 00:26:45', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1155, 103, N'短信登录', N'103', N'system_login_type', 0, N'default', N'', NULL, N'1', N'2022-05-09 23:57:58', N'1', N'2022-05-09 23:58:09', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1156, 1, N'password', N'password', N'system_oauth2_grant_type', 0, N'default', N'', N'密码模式', N'1', N'2022-05-12 00:22:05', N'1', N'2022-05-11 16:26:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1157, 2, N'authorization_code', N'authorization_code', N'system_oauth2_grant_type', 0, N'primary', N'', N'授权码模式', N'1', N'2022-05-12 00:22:59', N'1', N'2022-05-11 16:26:02', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1158, 3, N'implicit', N'implicit', N'system_oauth2_grant_type', 0, N'success', N'', N'简化模式', N'1', N'2022-05-12 00:23:40', N'1', N'2022-05-11 16:26:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1159, 4, N'client_credentials', N'client_credentials', N'system_oauth2_grant_type', 0, N'default', N'', N'客户端模式', N'1', N'2022-05-12 00:23:51', N'1', N'2022-05-11 16:26:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1160, 5, N'refresh_token', N'refresh_token', N'system_oauth2_grant_type', 0, N'info', N'', N'刷新模式', N'1', N'2022-05-12 00:24:02', N'1', N'2022-05-11 16:26:11', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1162, 1, N'销售中', N'1', N'product_spu_status', 0, N'success', N'', N'商品 SPU 状态 - 销售中', N'1', N'2022-10-24 21:19:47', N'1', N'2022-10-24 21:20:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1163, 0, N'仓库中', N'0', N'product_spu_status', 0, N'info', N'', N'商品 SPU 状态 - 仓库中', N'1', N'2022-10-24 21:20:54', N'1', N'2022-10-24 21:21:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1164, 0, N'回收站', N'-1', N'product_spu_status', 0, N'default', N'', N'商品 SPU 状态 - 回收站', N'1', N'2022-10-24 21:21:11', N'1', N'2022-10-24 21:21:11', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1165, 1, N'满减', N'1', N'promotion_discount_type', 0, N'success', N'', N'优惠类型 - 满减', N'1', N'2022-11-01 12:46:41', N'1', N'2022-11-01 12:50:11', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1166, 2, N'折扣', N'2', N'promotion_discount_type', 0, N'primary', N'', N'优惠类型 - 折扣', N'1', N'2022-11-01 12:46:51', N'1', N'2022-11-01 12:50:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1167, 1, N'固定日期', N'1', N'promotion_coupon_template_validity_type', 0, N'default', N'', N'优惠劵模板的有限期类型 - 固定日期', N'1', N'2022-11-02 00:07:34', N'1', N'2022-11-04 00:07:49', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1168, 2, N'领取之后', N'2', N'promotion_coupon_template_validity_type', 0, N'default', N'', N'优惠劵模板的有限期类型 - 领取之后', N'1', N'2022-11-02 00:07:54', N'1', N'2022-11-04 00:07:52', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1169, 1, N'通用劵', N'1', N'promotion_product_scope', 0, N'default', N'', N'营销的商品范围 - 全部商品参与', N'1', N'2022-11-02 00:28:22', N'1', N'2023-09-28 00:27:42', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1170, 2, N'商品劵', N'2', N'promotion_product_scope', 0, N'default', N'', N'营销的商品范围 - 指定商品参与', N'1', N'2022-11-02 00:28:34', N'1', N'2023-09-28 00:27:44', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1171, 1, N'未使用', N'1', N'promotion_coupon_status', 0, N'primary', N'', N'优惠劵的状态 - 已领取', N'1', N'2022-11-04 00:15:08', N'1', N'2023-10-03 12:54:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1172, 2, N'已使用', N'2', N'promotion_coupon_status', 0, N'success', N'', N'优惠劵的状态 - 已使用', N'1', N'2022-11-04 00:15:21', N'1', N'2022-11-04 19:16:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1173, 3, N'已过期', N'3', N'promotion_coupon_status', 0, N'info', N'', N'优惠劵的状态 - 已过期', N'1', N'2022-11-04 00:15:43', N'1', N'2022-11-04 19:16:12', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1174, 1, N'直接领取', N'1', N'promotion_coupon_take_type', 0, N'primary', N'', N'优惠劵的领取方式 - 直接领取', N'1', N'2022-11-04 19:13:00', N'1', N'2022-11-04 19:13:25', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1175, 2, N'指定发放', N'2', N'promotion_coupon_take_type', 0, N'success', N'', N'优惠劵的领取方式 - 指定发放', N'1', N'2022-11-04 19:13:13', N'1', N'2022-11-04 19:14:48', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1176, 10, N'未开始', N'10', N'promotion_activity_status', 0, N'primary', N'', N'促销活动的状态枚举 - 未开始', N'1', N'2022-11-04 22:54:49', N'1', N'2022-11-04 22:55:53', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1177, 20, N'进行中', N'20', N'promotion_activity_status', 0, N'success', N'', N'促销活动的状态枚举 - 进行中', N'1', N'2022-11-04 22:55:06', N'1', N'2022-11-04 22:55:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1178, 30, N'已结束', N'30', N'promotion_activity_status', 0, N'info', N'', N'促销活动的状态枚举 - 已结束', N'1', N'2022-11-04 22:55:41', N'1', N'2022-11-04 22:55:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1179, 40, N'已关闭', N'40', N'promotion_activity_status', 0, N'warning', N'', N'促销活动的状态枚举 - 已关闭', N'1', N'2022-11-04 22:56:10', N'1', N'2022-11-04 22:56:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1180, 10, N'满 N 元', N'10', N'promotion_condition_type', 0, N'primary', N'', N'营销的条件类型 - 满 N 元', N'1', N'2022-11-04 22:59:45', N'1', N'2022-11-04 22:59:45', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1181, 20, N'满 N 件', N'20', N'promotion_condition_type', 0, N'success', N'', N'营销的条件类型 - 满 N 件', N'1', N'2022-11-04 23:00:02', N'1', N'2022-11-04 23:00:02', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1182, 10, N'申请售后', N'10', N'trade_after_sale_status', 0, N'primary', N'', N'交易售后状态 - 申请售后', N'1', N'2022-11-19 20:53:33', N'1', N'2022-11-19 20:54:42', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1183, 20, N'商品待退货', N'20', N'trade_after_sale_status', 0, N'primary', N'', N'交易售后状态 - 商品待退货', N'1', N'2022-11-19 20:54:36', N'1', N'2022-11-19 20:58:58', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1184, 30, N'商家待收货', N'30', N'trade_after_sale_status', 0, N'primary', N'', N'交易售后状态 - 商家待收货', N'1', N'2022-11-19 20:56:56', N'1', N'2022-11-19 20:59:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1185, 40, N'等待退款', N'40', N'trade_after_sale_status', 0, N'primary', N'', N'交易售后状态 - 等待退款', N'1', N'2022-11-19 20:59:54', N'1', N'2022-11-19 21:00:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1186, 50, N'退款成功', N'50', N'trade_after_sale_status', 0, N'default', N'', N'交易售后状态 - 退款成功', N'1', N'2022-11-19 21:00:33', N'1', N'2022-11-19 21:00:33', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1187, 61, N'买家取消', N'61', N'trade_after_sale_status', 0, N'info', N'', N'交易售后状态 - 买家取消', N'1', N'2022-11-19 21:01:29', N'1', N'2022-11-19 21:01:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1188, 62, N'商家拒绝', N'62', N'trade_after_sale_status', 0, N'info', N'', N'交易售后状态 - 商家拒绝', N'1', N'2022-11-19 21:02:17', N'1', N'2022-11-19 21:02:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1189, 63, N'商家拒收货', N'63', N'trade_after_sale_status', 0, N'info', N'', N'交易售后状态 - 商家拒收货', N'1', N'2022-11-19 21:02:37', N'1', N'2022-11-19 21:03:07', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1190, 10, N'售中退款', N'10', N'trade_after_sale_type', 0, N'success', N'', N'交易售后的类型 - 售中退款', N'1', N'2022-11-19 21:05:05', N'1', N'2022-11-19 21:38:23', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1191, 20, N'售后退款', N'20', N'trade_after_sale_type', 0, N'primary', N'', N'交易售后的类型 - 售后退款', N'1', N'2022-11-19 21:05:32', N'1', N'2022-11-19 21:38:32', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1192, 10, N'仅退款', N'10', N'trade_after_sale_way', 0, N'primary', N'', N'交易售后的方式 - 仅退款', N'1', N'2022-11-19 21:39:19', N'1', N'2022-11-19 21:39:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1193, 20, N'退货退款', N'20', N'trade_after_sale_way', 0, N'success', N'', N'交易售后的方式 - 退货退款', N'1', N'2022-11-19 21:39:38', N'1', N'2022-11-19 21:39:49', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1194, 10, N'微信小程序', N'10', N'terminal', 0, N'default', N'', N'终端 - 微信小程序', N'1', N'2022-12-10 10:51:11', N'1', N'2022-12-10 10:51:57', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1195, 20, N'H5 网页', N'20', N'terminal', 0, N'default', N'', N'终端 - H5 网页', N'1', N'2022-12-10 10:51:30', N'1', N'2022-12-10 10:51:59', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1196, 11, N'微信公众号', N'11', N'terminal', 0, N'default', N'', N'终端 - 微信公众号', N'1', N'2022-12-10 10:54:16', N'1', N'2022-12-10 10:52:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1197, 31, N'苹果 App', N'31', N'terminal', 0, N'default', N'', N'终端 - 苹果 App', N'1', N'2022-12-10 10:54:42', N'1', N'2022-12-10 10:52:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1198, 32, N'安卓 App', N'32', N'terminal', 0, N'default', N'', N'终端 - 安卓 App', N'1', N'2022-12-10 10:55:02', N'1', N'2022-12-10 10:59:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1199, 0, N'普通订单', N'0', N'trade_order_type', 0, N'default', N'', N'交易订单的类型 - 普通订单', N'1', N'2022-12-10 16:34:14', N'1', N'2022-12-10 16:34:14', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1200, 1, N'秒杀订单', N'1', N'trade_order_type', 0, N'default', N'', N'交易订单的类型 - 秒杀订单', N'1', N'2022-12-10 16:34:26', N'1', N'2022-12-10 16:34:26', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1201, 2, N'砍价订单', N'2', N'trade_order_type', 0, N'default', N'', N'交易订单的类型 - 拼团订单', N'1', N'2022-12-10 16:34:36', N'1', N'2024-09-07 14:18:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1202, 3, N'拼团订单', N'3', N'trade_order_type', 0, N'default', N'', N'交易订单的类型 - 砍价订单', N'1', N'2022-12-10 16:34:48', N'1', N'2024-09-07 14:18:32', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1203, 0, N'待支付', N'0', N'trade_order_status', 0, N'default', N'', N'交易订单状态 - 待支付', N'1', N'2022-12-10 16:49:29', N'1', N'2022-12-10 16:49:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1204, 10, N'待发货', N'10', N'trade_order_status', 0, N'primary', N'', N'交易订单状态 - 待发货', N'1', N'2022-12-10 16:49:53', N'1', N'2022-12-10 16:51:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1205, 20, N'已发货', N'20', N'trade_order_status', 0, N'primary', N'', N'交易订单状态 - 已发货', N'1', N'2022-12-10 16:50:13', N'1', N'2022-12-10 16:51:31', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1206, 30, N'已完成', N'30', N'trade_order_status', 0, N'success', N'', N'交易订单状态 - 已完成', N'1', N'2022-12-10 16:50:30', N'1', N'2022-12-10 16:51:06', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1207, 40, N'已取消', N'40', N'trade_order_status', 0, N'danger', N'', N'交易订单状态 - 已取消', N'1', N'2022-12-10 16:50:50', N'1', N'2022-12-10 16:51:00', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1208, 0, N'未售后', N'0', N'trade_order_item_after_sale_status', 0, N'info', N'', N'交易订单项的售后状态 - 未售后', N'1', N'2022-12-10 20:58:42', N'1', N'2022-12-10 20:59:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1209, 10, N'售后中', N'10', N'trade_order_item_after_sale_status', 0, N'primary', N'', N'交易订单项的售后状态 - 售后中', N'1', N'2022-12-10 20:59:21', N'1', N'2024-07-21 17:01:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1210, 20, N'已退款', N'20', N'trade_order_item_after_sale_status', 0, N'success', N'', N'交易订单项的售后状态 - 已退款', N'1', N'2022-12-10 20:59:46', N'1', N'2024-07-21 17:01:35', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1211, 1, N'完全匹配', N'1', N'mp_auto_reply_request_match', 0, N'primary', N'', N'公众号自动回复的请求关键字匹配模式 - 完全匹配', N'1', N'2023-01-16 23:30:39', N'1', N'2023-01-16 23:31:00', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1212, 2, N'半匹配', N'2', N'mp_auto_reply_request_match', 0, N'success', N'', N'公众号自动回复的请求关键字匹配模式 - 半匹配', N'1', N'2023-01-16 23:30:55', N'1', N'2023-01-16 23:31:10', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1213, 1, N'文本', N'text', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 文本', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 22:17:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1214, 2, N'图片', N'image', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 图片', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 14:19:47', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1215, 3, N'语音', N'voice', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 语音', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 14:20:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1216, 4, N'视频', N'video', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 视频', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 14:21:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1217, 5, N'小视频', N'shortvideo', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 小视频', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 14:19:59', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1218, 6, N'图文', N'news', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 图文', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 14:22:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1219, 7, N'音乐', N'music', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 音乐', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 14:22:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1220, 8, N'地理位置', N'location', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 地理位置', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 14:23:51', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1221, 9, N'链接', N'link', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 链接', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 14:24:49', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1222, 10, N'事件', N'event', N'mp_message_type', 0, N'default', N'', N'公众号的消息类型 - 事件', N'1', N'2023-01-17 22:17:32', N'1', N'2023-01-17 14:24:49', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1223, 0, N'初始化', N'0', N'system_mail_send_status', 0, N'primary', N'', N'邮件发送状态 - 初始化\\n', N'1', N'2023-01-26 09:53:49', N'1', N'2023-01-26 16:36:14', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1224, 10, N'发送成功', N'10', N'system_mail_send_status', 0, N'success', N'', N'邮件发送状态 - 发送成功', N'1', N'2023-01-26 09:54:28', N'1', N'2023-01-26 16:36:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1225, 20, N'发送失败', N'20', N'system_mail_send_status', 0, N'danger', N'', N'邮件发送状态 - 发送失败', N'1', N'2023-01-26 09:54:50', N'1', N'2023-01-26 16:36:26', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1226, 30, N'不发送', N'30', N'system_mail_send_status', 0, N'info', N'', N'邮件发送状态 -  不发送', N'1', N'2023-01-26 09:55:06', N'1', N'2023-01-26 16:36:36', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1227, 1, N'通知公告', N'1', N'system_notify_template_type', 0, N'primary', N'', N'站内信模版的类型 - 通知公告', N'1', N'2023-01-28 10:35:59', N'1', N'2023-01-28 10:35:59', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1228, 2, N'系统消息', N'2', N'system_notify_template_type', 0, N'success', N'', N'站内信模版的类型 - 系统消息', N'1', N'2023-01-28 10:36:20', N'1', N'2023-01-28 10:36:25', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1230, 13, N'支付宝条码支付', N'alipay_bar', N'pay_channel_code', 0, N'primary', N'', N'支付宝条码支付', N'1', N'2023-02-18 23:32:24', N'1', N'2023-07-19 20:09:23', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1231, 10, N'Vue2 Element UI 标准模版', N'10', N'infra_codegen_front_type', 0, N'', N'', N'', N'1', N'2023-04-13 00:03:55', N'1', N'2023-04-13 00:03:55', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1232, 20, N'Vue3 Element Plus 标准模版', N'20', N'infra_codegen_front_type', 0, N'', N'', N'', N'1', N'2023-04-13 00:04:08', N'1', N'2023-04-13 00:04:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1234, 30, N'Vben2.0 Ant Design Schema 模版', N'30', N'infra_codegen_front_type', 0, N'', N'', N'', N'1', N'2023-04-13 00:04:26', N'1', N'2025-04-23 21:27:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1244, 0, N'按件', N'1', N'trade_delivery_express_charge_mode', 0, N'', N'', N'', N'1', N'2023-05-21 22:46:40', N'1', N'2023-05-21 22:46:40', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1245, 1, N'按重量', N'2', N'trade_delivery_express_charge_mode', 0, N'', N'', N'', N'1', N'2023-05-21 22:46:58', N'1', N'2023-05-21 22:46:58', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1246, 2, N'按体积', N'3', N'trade_delivery_express_charge_mode', 0, N'', N'', N'', N'1', N'2023-05-21 22:47:18', N'1', N'2023-05-21 22:47:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1335, 11, N'订单积分抵扣', N'11', N'member_point_biz_type', 0, N'', N'', N'', N'1', N'2023-06-10 12:15:27', N'1', N'2023-10-11 07:41:43', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1336, 1, N'签到', N'1', N'member_point_biz_type', 0, N'', N'', N'', N'1', N'2023-06-10 12:15:48', N'1', N'2023-08-20 11:59:53', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1341, 20, N'已退款', N'20', N'pay_order_status', 0, N'danger', N'', N'已退款', N'1', N'2023-07-19 18:05:37', N'1', N'2023-07-19 18:05:37', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1342, 21, N'请求成功，但是结果失败', N'21', N'pay_notify_status', 0, N'warning', N'', N'请求成功，但是结果失败', N'1', N'2023-07-19 18:10:47', N'1', N'2023-07-19 18:11:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1343, 22, N'请求失败', N'22', N'pay_notify_status', 0, N'warning', N'', NULL, N'1', N'2023-07-19 18:11:05', N'1', N'2023-07-19 18:11:27', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1344, 4, N'微信扫码支付', N'wx_native', N'pay_channel_code', 0, N'success', N'', N'微信扫码支付', N'1', N'2023-07-19 20:07:47', N'1', N'2023-07-19 20:09:03', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1345, 5, N'微信条码支付', N'wx_bar', N'pay_channel_code', 0, N'success', N'', N'微信条码支付\\n', N'1', N'2023-07-19 20:08:06', N'1', N'2023-07-19 20:09:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1346, 1, N'支付单', N'1', N'pay_notify_type', 0, N'primary', N'', N'支付单', N'1', N'2023-07-20 12:23:17', N'1', N'2023-07-20 12:23:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1347, 2, N'退款单', N'2', N'pay_notify_type', 0, N'danger', N'', NULL, N'1', N'2023-07-20 12:23:26', N'1', N'2023-07-20 12:23:26', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1348, 20, N'模拟支付', N'mock', N'pay_channel_code', 0, N'default', N'', N'模拟支付', N'1', N'2023-07-29 11:10:51', N'1', N'2023-07-29 03:14:10', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1349, 12, N'订单积分抵扣（整单取消）', N'12', N'member_point_biz_type', 0, N'', N'', N'', N'1', N'2023-08-20 12:00:03', N'1', N'2023-10-11 07:42:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1350, 0, N'管理员调整', N'0', N'member_experience_biz_type', 0, N'', N'', NULL, N'', N'2023-08-22 12:41:01', N'', N'2023-08-22 12:41:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1351, 1, N'邀新奖励', N'1', N'member_experience_biz_type', 0, N'', N'', NULL, N'', N'2023-08-22 12:41:01', N'', N'2023-08-22 12:41:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1352, 11, N'下单奖励', N'11', N'member_experience_biz_type', 0, N'success', N'', NULL, N'', N'2023-08-22 12:41:01', N'1', N'2023-10-11 07:45:09', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1353, 12, N'下单奖励（整单取消）', N'12', N'member_experience_biz_type', 0, N'warning', N'', NULL, N'', N'2023-08-22 12:41:01', N'1', N'2023-10-11 07:45:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1354, 4, N'签到奖励', N'4', N'member_experience_biz_type', 0, N'', N'', NULL, N'', N'2023-08-22 12:41:01', N'', N'2023-08-22 12:41:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1355, 5, N'抽奖奖励', N'5', N'member_experience_biz_type', 0, N'', N'', NULL, N'', N'2023-08-22 12:41:01', N'', N'2023-08-22 12:41:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1356, 1, N'快递发货', N'1', N'trade_delivery_type', 0, N'', N'', N'', N'1', N'2023-08-23 00:04:55', N'1', N'2023-08-23 00:04:55', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1357, 2, N'用户自提', N'2', N'trade_delivery_type', 0, N'', N'', N'', N'1', N'2023-08-23 00:05:05', N'1', N'2023-08-23 00:05:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1358, 3, N'品类劵', N'3', N'promotion_product_scope', 0, N'default', N'', N'', N'1', N'2023-09-01 23:43:07', N'1', N'2023-09-28 00:27:47', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1359, 1, N'人人分销', N'1', N'brokerage_enabled_condition', 0, N'', N'', N'所有用户都可以分销', N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1360, 2, N'指定分销', N'2', N'brokerage_enabled_condition', 0, N'', N'', N'仅可后台手动设置推广员', N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1361, 1, N'首次绑定', N'1', N'brokerage_bind_mode', 0, N'', N'', N'只要用户没有推广人，随时都可以绑定推广关系', N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1362, 2, N'注册绑定', N'2', N'brokerage_bind_mode', 0, N'', N'', N'仅新用户注册时才能绑定推广关系', N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1363, 3, N'覆盖绑定', N'3', N'brokerage_bind_mode', 0, N'', N'', N'如果用户已经有推广人，推广人会被变更', N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1364, 1, N'钱包', N'1', N'brokerage_withdraw_type', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1365, 2, N'银行卡', N'2', N'brokerage_withdraw_type', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1366, 3, N'微信收款码', N'3', N'brokerage_withdraw_type', 0, N'', N'', N'手动打款', N'', N'2023-09-28 02:46:05', N'1', N'2025-05-10 08:24:25', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1367, 4, N'支付宝收款码', N'4', N'brokerage_withdraw_type', 0, N'', N'', N'手动打款', N'', N'2023-09-28 02:46:05', N'1', N'2025-05-10 08:24:37', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1368, 1, N'订单返佣', N'1', N'brokerage_record_biz_type', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1369, 2, N'申请提现', N'2', N'brokerage_record_biz_type', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1370, 3, N'申请提现驳回', N'3', N'brokerage_record_biz_type', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1371, 0, N'待结算', N'0', N'brokerage_record_status', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1372, 1, N'已结算', N'1', N'brokerage_record_status', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1373, 2, N'已取消', N'2', N'brokerage_record_status', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1374, 0, N'审核中', N'0', N'brokerage_withdraw_status', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1375, 10, N'审核通过', N'10', N'brokerage_withdraw_status', 0, N'success', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1376, 11, N'提现成功', N'11', N'brokerage_withdraw_status', 0, N'success', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1377, 20, N'审核不通过', N'20', N'brokerage_withdraw_status', 0, N'danger', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1378, 21, N'提现失败', N'21', N'brokerage_withdraw_status', 0, N'danger', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1379, 0, N'工商银行', N'0', N'brokerage_bank_name', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1380, 1, N'建设银行', N'1', N'brokerage_bank_name', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1381, 2, N'农业银行', N'2', N'brokerage_bank_name', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1382, 3, N'中国银行', N'3', N'brokerage_bank_name', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1383, 4, N'交通银行', N'4', N'brokerage_bank_name', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1384, 5, N'招商银行', N'5', N'brokerage_bank_name', 0, N'', N'', NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1385, 21, N'钱包', N'wallet', N'pay_channel_code', 0, N'primary', N'', N'', N'1', N'2023-10-01 21:46:19', N'1', N'2023-10-01 21:48:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1386, 1, N'砍价中', N'1', N'promotion_bargain_record_status', 0, N'default', N'', N'', N'1', N'2023-10-05 10:41:26', N'1', N'2023-10-05 10:41:26', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1387, 2, N'砍价成功', N'2', N'promotion_bargain_record_status', 0, N'success', N'', N'', N'1', N'2023-10-05 10:41:39', N'1', N'2023-10-05 10:41:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1388, 3, N'砍价失败', N'3', N'promotion_bargain_record_status', 0, N'warning', N'', N'', N'1', N'2023-10-05 10:41:57', N'1', N'2023-10-05 10:41:57', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1389, 0, N'拼团中', N'0', N'promotion_combination_record_status', 0, N'', N'', N'', N'1', N'2023-10-08 07:24:44', N'1', N'2024-10-13 10:08:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1390, 1, N'拼团成功', N'1', N'promotion_combination_record_status', 0, N'success', N'', N'', N'1', N'2023-10-08 07:24:56', N'1', N'2024-10-13 10:08:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1391, 2, N'拼团失败', N'2', N'promotion_combination_record_status', 0, N'warning', N'', N'', N'1', N'2023-10-08 07:25:11', N'1', N'2024-10-13 10:08:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1392, 2, N'管理员修改', N'2', N'member_point_biz_type', 0, N'default', N'', N'', N'1', N'2023-10-11 07:41:34', N'1', N'2023-10-11 07:41:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1393, 13, N'订单积分抵扣（单个退款）', N'13', N'member_point_biz_type', 0, N'', N'', N'', N'1', N'2023-10-11 07:42:29', N'1', N'2023-10-11 07:42:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1394, 21, N'订单积分奖励', N'21', N'member_point_biz_type', 0, N'default', N'', N'', N'1', N'2023-10-11 07:42:44', N'1', N'2023-10-11 07:42:44', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1395, 22, N'订单积分奖励（整单取消）', N'22', N'member_point_biz_type', 0, N'default', N'', N'', N'1', N'2023-10-11 07:42:55', N'1', N'2023-10-11 07:43:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1396, 23, N'订单积分奖励（单个退款）', N'23', N'member_point_biz_type', 0, N'default', N'', N'', N'1', N'2023-10-11 07:43:16', N'1', N'2023-10-11 07:43:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1397, 13, N'下单奖励（单个退款）', N'13', N'member_experience_biz_type', 0, N'warning', N'', N'', N'1', N'2023-10-11 07:45:24', N'1', N'2023-10-11 07:45:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1398, 5, N'网上转账', N'5', N'crm_receivable_return_type', 0, N'default', N'', N'', N'1', N'2023-10-18 21:55:24', N'1', N'2023-10-18 21:55:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1399, 6, N'支付宝', N'6', N'crm_receivable_return_type', 0, N'default', N'', N'', N'1', N'2023-10-18 21:55:38', N'1', N'2023-10-18 21:55:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1400, 7, N'微信支付', N'7', N'crm_receivable_return_type', 0, N'default', N'', N'', N'1', N'2023-10-18 21:55:53', N'1', N'2023-10-18 21:55:53', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1401, 8, N'其他', N'8', N'crm_receivable_return_type', 0, N'default', N'', N'', N'1', N'2023-10-18 21:56:06', N'1', N'2023-10-18 21:56:06', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1402, 1, N'IT', N'1', N'crm_customer_industry', 0, N'default', N'', N'', N'1', N'2023-10-28 23:02:15', N'1', N'2024-02-18 23:30:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1403, 2, N'金融业', N'2', N'crm_customer_industry', 0, N'default', N'', N'', N'1', N'2023-10-28 23:02:29', N'1', N'2024-02-18 23:30:43', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1404, 3, N'房地产', N'3', N'crm_customer_industry', 0, N'default', N'', N'', N'1', N'2023-10-28 23:02:41', N'1', N'2024-02-18 23:30:48', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1405, 4, N'商业服务', N'4', N'crm_customer_industry', 0, N'default', N'', N'', N'1', N'2023-10-28 23:02:54', N'1', N'2024-02-18 23:30:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1406, 5, N'运输/物流', N'5', N'crm_customer_industry', 0, N'default', N'', N'', N'1', N'2023-10-28 23:03:03', N'1', N'2024-02-18 23:31:00', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1407, 6, N'生产', N'6', N'crm_customer_industry', 0, N'default', N'', N'', N'1', N'2023-10-28 23:03:13', N'1', N'2024-02-18 23:31:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1408, 7, N'政府', N'7', N'crm_customer_industry', 0, N'default', N'', N'', N'1', N'2023-10-28 23:03:27', N'1', N'2024-02-18 23:31:13', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1409, 8, N'文化传媒', N'8', N'crm_customer_industry', 0, N'default', N'', N'', N'1', N'2023-10-28 23:03:37', N'1', N'2024-02-18 23:31:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1422, 1, N'A （重点客户）', N'1', N'crm_customer_level', 0, N'primary', N'', N'', N'1', N'2023-10-28 23:07:13', N'1', N'2023-10-28 23:07:13', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1423, 2, N'B （普通客户）', N'2', N'crm_customer_level', 0, N'info', N'', N'', N'1', N'2023-10-28 23:07:35', N'1', N'2023-10-28 23:07:35', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1424, 3, N'C （非优先客户）', N'3', N'crm_customer_level', 0, N'default', N'', N'', N'1', N'2023-10-28 23:07:53', N'1', N'2023-10-28 23:07:53', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1425, 1, N'促销', N'1', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:08:29', N'1', N'2023-10-28 23:08:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1426, 2, N'搜索引擎', N'2', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:08:39', N'1', N'2023-10-28 23:08:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1427, 3, N'广告', N'3', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:08:47', N'1', N'2023-10-28 23:08:47', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1428, 4, N'转介绍', N'4', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:08:58', N'1', N'2023-10-28 23:08:58', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1429, 5, N'线上注册', N'5', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:09:12', N'1', N'2023-10-28 23:09:12', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1430, 6, N'线上咨询', N'6', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:09:22', N'1', N'2023-10-28 23:09:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1431, 7, N'预约上门', N'7', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:09:39', N'1', N'2023-10-28 23:09:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1432, 8, N'陌拜', N'8', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:10:04', N'1', N'2023-10-28 23:10:04', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1433, 9, N'电话咨询', N'9', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:10:18', N'1', N'2023-10-28 23:10:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1434, 10, N'邮件咨询', N'10', N'crm_customer_source', 0, N'default', N'', N'', N'1', N'2023-10-28 23:10:33', N'1', N'2023-10-28 23:10:33', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1435, 10, N'Gitee', N'10', N'system_social_type', 0, N'', N'', N'', N'1', N'2023-11-04 13:04:42', N'1', N'2023-11-04 13:04:42', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1436, 20, N'钉钉', N'20', N'system_social_type', 0, N'', N'', N'', N'1', N'2023-11-04 13:04:54', N'1', N'2023-11-04 13:04:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1437, 30, N'企业微信', N'30', N'system_social_type', 0, N'', N'', N'', N'1', N'2023-11-04 13:05:09', N'1', N'2023-11-04 13:05:09', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1438, 31, N'微信公众平台', N'31', N'system_social_type', 0, N'', N'', N'', N'1', N'2023-11-04 13:05:18', N'1', N'2023-11-04 13:05:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1439, 32, N'微信开放平台', N'32', N'system_social_type', 0, N'', N'', N'', N'1', N'2023-11-04 13:05:30', N'1', N'2023-11-04 13:05:30', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1440, 34, N'微信小程序', N'34', N'system_social_type', 0, N'', N'', N'', N'1', N'2023-11-04 13:05:38', N'1', N'2023-11-04 13:07:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1441, 1, N'上架', N'1', N'crm_product_status', 0, N'success', N'', N'', N'1', N'2023-10-30 21:49:34', N'1', N'2023-10-30 21:49:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1442, 0, N'下架', N'0', N'crm_product_status', 0, N'success', N'', N'', N'1', N'2023-10-30 21:49:13', N'1', N'2023-10-30 21:49:13', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1443, 15, N'子表', N'15', N'infra_codegen_template_type', 0, N'default', N'', N'', N'1', N'2023-11-13 23:06:16', N'1', N'2023-11-13 23:06:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1444, 10, N'主表（标准模式）', N'10', N'infra_codegen_template_type', 0, N'default', N'', N'', N'1', N'2023-11-14 12:32:49', N'1', N'2023-11-14 12:32:49', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1445, 11, N'主表（ERP 模式）', N'11', N'infra_codegen_template_type', 0, N'default', N'', N'', N'1', N'2023-11-14 12:33:05', N'1', N'2023-11-14 12:33:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1446, 12, N'主表（内嵌模式）', N'12', N'infra_codegen_template_type', 0, N'', N'', N'', N'1', N'2023-11-14 12:33:31', N'1', N'2023-11-14 12:33:31', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1447, 1, N'负责人', N'1', N'crm_permission_level', 0, N'default', N'', N'', N'1', N'2023-11-30 09:53:12', N'1', N'2023-11-30 09:53:12', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1448, 2, N'只读', N'2', N'crm_permission_level', 0, N'', N'', N'', N'1', N'2023-11-30 09:53:29', N'1', N'2023-11-30 09:53:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1449, 3, N'读写', N'3', N'crm_permission_level', 0, N'', N'', N'', N'1', N'2023-11-30 09:53:36', N'1', N'2023-11-30 09:53:36', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1450, 0, N'未提交', N'0', N'crm_audit_status', 0, N'', N'', N'', N'1', N'2023-11-30 18:56:59', N'1', N'2023-11-30 18:56:59', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1451, 10, N'审批中', N'10', N'crm_audit_status', 0, N'', N'', N'', N'1', N'2023-11-30 18:57:10', N'1', N'2023-11-30 18:57:10', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1452, 20, N'审核通过', N'20', N'crm_audit_status', 0, N'', N'', N'', N'1', N'2023-11-30 18:57:24', N'1', N'2023-11-30 18:57:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1453, 30, N'审核不通过', N'30', N'crm_audit_status', 0, N'', N'', N'', N'1', N'2023-11-30 18:57:32', N'1', N'2023-11-30 18:57:32', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1454, 40, N'已取消', N'40', N'crm_audit_status', 0, N'', N'', N'', N'1', N'2023-11-30 18:57:42', N'1', N'2023-11-30 18:57:42', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1456, 1, N'支票', N'1', N'crm_receivable_return_type', 0, N'default', N'', N'', N'1', N'2023-10-18 21:54:29', N'1', N'2023-10-18 21:54:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1457, 2, N'现金', N'2', N'crm_receivable_return_type', 0, N'default', N'', N'', N'1', N'2023-10-18 21:54:41', N'1', N'2023-10-18 21:54:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1458, 3, N'邮政汇款', N'3', N'crm_receivable_return_type', 0, N'default', N'', N'', N'1', N'2023-10-18 21:54:53', N'1', N'2023-10-18 21:54:53', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1459, 4, N'电汇', N'4', N'crm_receivable_return_type', 0, N'default', N'', N'', N'1', N'2023-10-18 21:55:07', N'1', N'2023-10-18 21:55:07', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1461, 1, N'个', N'1', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:02:26', N'1', N'2023-12-05 23:02:26', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1462, 2, N'块', N'2', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:02:34', N'1', N'2023-12-05 23:02:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1463, 3, N'只', N'3', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:02:57', N'1', N'2023-12-05 23:02:57', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1464, 4, N'把', N'4', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:03:05', N'1', N'2023-12-05 23:03:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1465, 5, N'枚', N'5', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:03:14', N'1', N'2023-12-05 23:03:14', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1466, 6, N'瓶', N'6', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:03:20', N'1', N'2023-12-05 23:03:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1467, 7, N'盒', N'7', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:03:30', N'1', N'2023-12-05 23:03:30', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1468, 8, N'台', N'8', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:03:41', N'1', N'2023-12-05 23:03:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1469, 9, N'吨', N'9', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:03:48', N'1', N'2023-12-05 23:03:48', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1470, 10, N'千克', N'10', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:04:03', N'1', N'2023-12-05 23:04:03', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1471, 11, N'米', N'11', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:04:12', N'1', N'2023-12-05 23:04:12', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1472, 12, N'箱', N'12', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:04:25', N'1', N'2023-12-05 23:04:25', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1473, 13, N'套', N'13', N'crm_product_unit', 0, N'', N'', N'', N'1', N'2023-12-05 23:04:34', N'1', N'2023-12-05 23:04:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1474, 1, N'打电话', N'1', N'crm_follow_up_type', 0, N'', N'', N'', N'1', N'2024-01-15 20:48:20', N'1', N'2024-01-15 20:48:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1475, 2, N'发短信', N'2', N'crm_follow_up_type', 0, N'', N'', N'', N'1', N'2024-01-15 20:48:31', N'1', N'2024-01-15 20:48:31', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1476, 3, N'上门拜访', N'3', N'crm_follow_up_type', 0, N'', N'', N'', N'1', N'2024-01-15 20:49:07', N'1', N'2024-01-15 20:49:07', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1477, 4, N'微信沟通', N'4', N'crm_follow_up_type', 0, N'', N'', N'', N'1', N'2024-01-15 20:49:15', N'1', N'2024-01-15 20:49:15', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1482, 4, N'转账失败', N'20', N'pay_transfer_status', 0, N'warning', N'', N'', N'1', N'2023-10-28 16:24:16', N'1', N'2025-05-08 12:59:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1483, 3, N'转账成功', N'10', N'pay_transfer_status', 0, N'success', N'', N'', N'1', N'2023-10-28 16:23:50', N'1', N'2025-05-08 12:58:58', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1484, 2, N'转账进行中', N'5', N'pay_transfer_status', 0, N'info', N'', N'', N'1', N'2023-10-28 16:23:12', N'1', N'2025-05-08 12:58:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1485, 1, N'等待转账', N'0', N'pay_transfer_status', 0, N'default', N'', N'', N'1', N'2023-10-28 16:21:43', N'1', N'2023-10-28 16:23:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1486, 10, N'其它入库', N'10', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-05 18:07:25', N'1', N'2024-02-05 18:07:43', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1487, 11, N'其它入库（作废）', N'11', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-05 18:08:07', N'1', N'2024-02-05 19:20:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1488, 20, N'其它出库', N'20', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-05 18:08:51', N'1', N'2024-02-05 18:08:51', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1489, 21, N'其它出库（作废）', N'21', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-05 18:09:00', N'1', N'2024-02-05 19:20:10', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1490, 10, N'未审核', N'10', N'erp_audit_status', 0, N'default', N'', N'', N'1', N'2024-02-06 00:00:21', N'1', N'2024-02-06 00:00:21', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1491, 20, N'已审核', N'20', N'erp_audit_status', 0, N'success', N'', N'', N'1', N'2024-02-06 00:00:35', N'1', N'2024-02-06 00:00:35', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1492, 30, N'调拨入库', N'30', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-07 20:34:19', N'1', N'2024-02-07 12:36:31', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1493, 31, N'调拨入库（作废）', N'31', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-07 20:34:29', N'1', N'2024-02-07 20:37:11', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1494, 32, N'调拨出库', N'32', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-07 20:34:38', N'1', N'2024-02-07 12:36:33', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1495, 33, N'调拨出库（作废）', N'33', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-07 20:34:49', N'1', N'2024-02-07 20:37:06', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1496, 40, N'盘盈入库', N'40', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-08 08:53:00', N'1', N'2024-02-08 08:53:09', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1497, 41, N'盘盈入库（作废）', N'41', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-08 08:53:39', N'1', N'2024-02-16 19:40:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1498, 42, N'盘亏出库', N'42', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-08 08:54:16', N'1', N'2024-02-08 08:54:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1499, 43, N'盘亏出库（作废）', N'43', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-08 08:54:31', N'1', N'2024-02-16 19:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1500, 50, N'销售出库', N'50', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-11 21:47:25', N'1', N'2024-02-11 21:50:40', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1501, 51, N'销售出库（作废）', N'51', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-11 21:47:37', N'1', N'2024-02-11 21:51:12', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1502, 60, N'销售退货入库', N'60', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-12 06:51:05', N'1', N'2024-02-12 06:51:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1503, 61, N'销售退货入库（作废）', N'61', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-12 06:51:18', N'1', N'2024-02-12 06:51:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1504, 70, N'采购入库', N'70', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-16 13:10:02', N'1', N'2024-02-16 13:10:02', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1505, 71, N'采购入库（作废）', N'71', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-16 13:10:10', N'1', N'2024-02-16 19:40:40', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1506, 80, N'采购退货出库', N'80', N'erp_stock_record_biz_type', 0, N'', N'', N'', N'1', N'2024-02-16 13:10:17', N'1', N'2024-02-16 13:10:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1507, 81, N'采购退货出库（作废）', N'81', N'erp_stock_record_biz_type', 0, N'danger', N'', N'', N'1', N'2024-02-16 13:10:26', N'1', N'2024-02-16 19:40:33', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1509, 3, N'审批不通过', N'3', N'bpm_process_instance_status', 0, N'danger', N'', N'', N'1', N'2024-03-16 16:12:06', N'1', N'2024-03-16 16:12:06', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1510, 4, N'已取消', N'4', N'bpm_process_instance_status', 0, N'warning', N'', N'', N'1', N'2024-03-16 16:12:22', N'1', N'2024-03-16 16:12:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1511, 5, N'已退回', N'5', N'bpm_task_status', 0, N'warning', N'', N'', N'1', N'2024-03-16 19:10:46', N'1', N'2024-03-08 22:41:40', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1512, 6, N'委派中', N'6', N'bpm_task_status', 0, N'primary', N'', N'', N'1', N'2024-03-17 10:06:22', N'1', N'2024-03-08 22:41:40', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1513, 7, N'审批通过中', N'7', N'bpm_task_status', 0, N'success', N'', N'', N'1', N'2024-03-17 10:06:47', N'1', N'2024-03-08 22:41:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1514, 0, N'待审批', N'0', N'bpm_task_status', 0, N'info', N'', N'', N'1', N'2024-03-17 10:07:11', N'1', N'2024-03-08 22:41:42', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1515, 35, N'发起人自选', N'35', N'bpm_task_candidate_strategy', 0, N'', N'', N'', N'1', N'2024-03-22 19:45:16', N'1', N'2024-03-22 19:45:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1516, 1, N'执行监听器', N'execution', N'bpm_process_listener_type', 0, N'primary', N'', N'', N'1', N'2024-03-23 12:54:03', N'1', N'2024-03-23 19:14:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1517, 1, N'任务监听器', N'task', N'bpm_process_listener_type', 0, N'success', N'', N'', N'1', N'2024-03-23 12:54:13', N'1', N'2024-03-23 19:14:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1526, 1, N'Java 类', N'class', N'bpm_process_listener_value_type', 0, N'primary', N'', N'', N'1', N'2024-03-23 15:08:45', N'1', N'2024-03-23 19:14:32', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1527, 2, N'表达式', N'expression', N'bpm_process_listener_value_type', 0, N'success', N'', N'', N'1', N'2024-03-23 15:09:06', N'1', N'2024-03-23 19:14:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1528, 3, N'代理表达式', N'delegateExpression', N'bpm_process_listener_value_type', 0, N'info', N'', N'', N'1', N'2024-03-23 15:11:23', N'1', N'2024-03-23 19:14:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1529, 1, N'天', N'1', N'date_interval', 0, N'', N'', N'', N'1', N'2024-03-29 22:50:26', N'1', N'2024-03-29 22:50:26', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1530, 2, N'周', N'2', N'date_interval', 0, N'', N'', N'', N'1', N'2024-03-29 22:50:36', N'1', N'2024-03-29 22:50:36', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1531, 3, N'月', N'3', N'date_interval', 0, N'', N'', N'', N'1', N'2024-03-29 22:50:46', N'1', N'2024-03-29 22:50:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1532, 4, N'季度', N'4', N'date_interval', 0, N'', N'', N'', N'1', N'2024-03-29 22:51:01', N'1', N'2024-03-29 22:51:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1533, 5, N'年', N'5', N'date_interval', 0, N'', N'', N'', N'1', N'2024-03-29 22:51:07', N'1', N'2024-03-29 22:51:07', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1534, 1, N'赢单', N'1', N'crm_business_end_status_type', 0, N'success', N'', N'', N'1', N'2024-04-13 23:26:57', N'1', N'2024-04-13 23:26:57', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1535, 2, N'输单', N'2', N'crm_business_end_status_type', 0, N'primary', N'', N'', N'1', N'2024-04-13 23:27:31', N'1', N'2024-04-13 23:27:31', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1536, 3, N'无效', N'3', N'crm_business_end_status_type', 0, N'info', N'', N'', N'1', N'2024-04-13 23:27:59', N'1', N'2024-04-13 23:27:59', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1537, 1, N'OpenAI', N'OpenAI', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-05-09 22:33:47', N'1', N'2024-05-09 22:58:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1538, 2, N'Ollama', N'Ollama', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-05-17 23:02:55', N'1', N'2024-05-17 23:02:55', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1539, 3, N'文心一言', N'YiYan', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-05-18 09:24:20', N'1', N'2024-05-18 09:29:01', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1540, 4, N'讯飞星火', N'XingHuo', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-05-18 10:08:56', N'1', N'2024-05-18 10:08:56', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1541, 5, N'通义千问', N'TongYi', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-05-18 10:32:29', N'1', N'2024-07-06 15:42:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1542, 6, N'StableDiffusion', N'StableDiffusion', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-06-01 15:09:31', N'1', N'2024-06-01 15:10:25', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1543, 10, N'进行中', N'10', N'ai_image_status', 0, N'primary', N'', N'', N'1', N'2024-06-26 20:51:41', N'1', N'2024-06-26 20:52:48', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1544, 20, N'已完成', N'20', N'ai_image_status', 0, N'success', N'', N'', N'1', N'2024-06-26 20:52:07', N'1', N'2024-06-26 20:52:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1545, 30, N'已失败', N'30', N'ai_image_status', 0, N'warning', N'', N'', N'1', N'2024-06-26 20:52:25', N'1', N'2024-06-26 20:52:35', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1546, 7, N'Midjourney', N'Midjourney', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-06-26 22:14:46', N'1', N'2024-06-26 22:14:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1547, 10, N'进行中', N'10', N'ai_music_status', 0, N'primary', N'', N'', N'1', N'2024-06-27 22:45:22', N'1', N'2024-06-28 00:56:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1548, 20, N'已完成', N'20', N'ai_music_status', 0, N'success', N'', N'', N'1', N'2024-06-27 22:45:33', N'1', N'2024-06-28 00:56:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1549, 30, N'已失败', N'30', N'ai_music_status', 0, N'danger', N'', N'', N'1', N'2024-06-27 22:45:44', N'1', N'2024-06-28 00:56:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1550, 1, N'歌词模式', N'1', N'ai_generate_mode', 0, N'', N'', N'', N'1', N'2024-06-27 22:46:31', N'1', N'2024-06-28 01:22:25', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1551, 2, N'描述模式', N'2', N'ai_generate_mode', 0, N'', N'', N'', N'1', N'2024-06-27 22:46:37', N'1', N'2024-06-28 01:22:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1552, 8, N'Suno', N'Suno', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-06-29 09:13:36', N'1', N'2024-06-29 09:13:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1553, 9, N'DeepSeek', N'DeepSeek', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-07-06 12:04:30', N'1', N'2024-07-06 12:05:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1554, 13, N'智谱', N'ZhiPu', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-07-06 18:00:35', N'1', N'2025-02-24 20:18:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1555, 4, N'长', N'4', N'ai_write_length', 0, N'', N'', N'', N'1', N'2024-07-07 15:49:03', N'1', N'2024-07-07 15:49:03', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1556, 5, N'段落', N'5', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:49:54', N'1', N'2024-07-07 15:49:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1557, 6, N'文章', N'6', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:50:05', N'1', N'2024-07-07 15:50:05', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1558, 7, N'博客文章', N'7', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:50:23', N'1', N'2024-07-07 15:50:23', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1559, 8, N'想法', N'8', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:50:31', N'1', N'2024-07-07 15:50:31', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1560, 9, N'大纲', N'9', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:50:37', N'1', N'2024-07-07 15:50:37', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1561, 1, N'自动', N'1', N'ai_write_tone', 0, N'', N'', N'', N'1', N'2024-07-07 15:51:06', N'1', N'2024-07-07 15:51:06', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1562, 2, N'友善', N'2', N'ai_write_tone', 0, N'', N'', N'', N'1', N'2024-07-07 15:51:19', N'1', N'2024-07-07 15:51:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1563, 3, N'随意', N'3', N'ai_write_tone', 0, N'', N'', N'', N'1', N'2024-07-07 15:51:27', N'1', N'2024-07-07 15:51:27', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1564, 4, N'友好', N'4', N'ai_write_tone', 0, N'', N'', N'', N'1', N'2024-07-07 15:51:37', N'1', N'2024-07-07 15:51:37', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1565, 5, N'专业', N'5', N'ai_write_tone', 0, N'', N'', N'', N'1', N'2024-07-07 15:51:49', N'1', N'2024-07-07 15:52:02', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1566, 6, N'诙谐', N'6', N'ai_write_tone', 0, N'', N'', N'', N'1', N'2024-07-07 15:52:15', N'1', N'2024-07-07 15:52:15', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1567, 7, N'有趣', N'7', N'ai_write_tone', 0, N'', N'', N'', N'1', N'2024-07-07 15:52:24', N'1', N'2024-07-07 15:52:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1568, 8, N'正式', N'8', N'ai_write_tone', 0, N'', N'', N'', N'1', N'2024-07-07 15:54:33', N'1', N'2024-07-07 15:54:33', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1569, 5, N'段落', N'5', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:49:54', N'1', N'2024-07-07 15:49:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1570, 1, N'自动', N'1', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:19:34', N'1', N'2024-07-07 15:19:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1571, 2, N'电子邮件', N'2', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:19:50', N'1', N'2024-07-07 15:49:30', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1572, 3, N'消息', N'3', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:20:01', N'1', N'2024-07-07 15:49:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1573, 4, N'评论', N'4', N'ai_write_format', 0, N'', N'', N'', N'1', N'2024-07-07 15:20:13', N'1', N'2024-07-07 15:49:45', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1574, 1, N'自动', N'1', N'ai_write_language', 0, N'', N'', N'', N'1', N'2024-07-07 15:44:18', N'1', N'2024-07-07 15:44:18', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1575, 2, N'中文', N'2', N'ai_write_language', 0, N'', N'', N'', N'1', N'2024-07-07 15:44:28', N'1', N'2024-07-07 15:44:28', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1576, 3, N'英文', N'3', N'ai_write_language', 0, N'', N'', N'', N'1', N'2024-07-07 15:44:37', N'1', N'2024-07-07 15:44:37', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1577, 4, N'韩语', N'4', N'ai_write_language', 0, N'', N'', N'', N'1', N'2024-07-07 15:46:28', N'1', N'2024-07-07 15:46:28', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1578, 5, N'日语', N'5', N'ai_write_language', 0, N'', N'', N'', N'1', N'2024-07-07 15:46:44', N'1', N'2024-07-07 15:46:44', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1579, 1, N'自动', N'1', N'ai_write_length', 0, N'', N'', N'', N'1', N'2024-07-07 15:48:34', N'1', N'2024-07-07 15:48:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1580, 2, N'短', N'2', N'ai_write_length', 0, N'', N'', N'', N'1', N'2024-07-07 15:48:44', N'1', N'2024-07-07 15:48:44', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1581, 3, N'中等', N'3', N'ai_write_length', 0, N'', N'', N'', N'1', N'2024-07-07 15:48:52', N'1', N'2024-07-07 15:48:52', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1582, 4, N'长', N'4', N'ai_write_length', 0, N'', N'', N'', N'1', N'2024-07-07 15:49:03', N'1', N'2024-07-07 15:49:03', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1584, 1, N'撰写', N'1', N'ai_write_type', 0, N'', N'', N'', N'1', N'2024-07-10 21:26:00', N'1', N'2024-07-10 21:26:00', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1585, 2, N'回复', N'2', N'ai_write_type', 0, N'', N'', N'', N'1', N'2024-07-10 21:26:06', N'1', N'2024-07-10 21:26:06', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1586, 2, N'腾讯云', N'TENCENT', N'system_sms_channel_code', 0, N'', N'', N'', N'1', N'2024-07-22 22:23:16', N'1', N'2024-07-22 22:23:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1587, 3, N'华为云', N'HUAWEI', N'system_sms_channel_code', 0, N'', N'', N'', N'1', N'2024-07-22 22:23:46', N'1', N'2024-07-22 22:23:53', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1588, 1, N'OpenAI 微软', N'AzureOpenAI', N'ai_platform', 0, N'', N'', N'', N'1', N'2024-08-10 14:07:41', N'1', N'2024-08-10 14:07:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1589, 10, N'BPMN 设计器', N'10', N'bpm_model_type', 0, N'primary', N'', N'', N'1', N'2024-08-26 15:22:17', N'1', N'2024-08-26 16:46:02', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1590, 20, N'SIMPLE 设计器', N'20', N'bpm_model_type', 0, N'success', N'', N'', N'1', N'2024-08-26 15:22:27', N'1', N'2024-08-26 16:45:58', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1591, 4, N'七牛云', N'QINIU', N'system_sms_channel_code', 0, N'', N'', N'', N'1', N'2024-08-31 08:45:03', N'1', N'2024-08-31 08:45:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1592, 3, N'新人券', N'3', N'promotion_coupon_take_type', 0, N'info', N'', N'新人注册后，自动发放', N'1', N'2024-09-03 11:57:16', N'1', N'2024-09-03 11:57:28', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1593, 5, N'微信零钱', N'5', N'brokerage_withdraw_type', 0, N'', N'', N'API 打款', N'1', N'2024-10-13 11:06:48', N'1', N'2025-05-10 08:24:55', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1683, 10, N'字节豆包', N'DouBao', N'ai_platform', 0, N'', N'', N'', N'1', N'2025-02-23 19:51:40', N'1', N'2025-02-23 19:52:02', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1684, 11, N'腾讯混元', N'HunYuan', N'ai_platform', 0, N'', N'', N'', N'1', N'2025-02-23 20:58:04', N'1', N'2025-02-23 20:58:04', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1685, 12, N'硅基流动', N'SiliconFlow', N'ai_platform', 0, N'', N'', N'', N'1', N'2025-02-24 20:19:09', N'1', N'2025-02-24 20:19:09', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1686, 1, N'聊天', N'1', N'ai_model_type', 0, N'', N'', N'', N'1', N'2025-03-03 12:26:34', N'1', N'2025-03-03 12:26:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1687, 2, N'图像', N'2', N'ai_model_type', 0, N'', N'', N'', N'1', N'2025-03-03 12:27:23', N'1', N'2025-03-03 12:27:23', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1688, 3, N'音频', N'3', N'ai_model_type', 0, N'', N'', N'', N'1', N'2025-03-03 12:27:51', N'1', N'2025-03-03 12:27:51', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1689, 4, N'视频', N'4', N'ai_model_type', 0, N'', N'', N'', N'1', N'2025-03-03 12:28:03', N'1', N'2025-03-03 12:28:03', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1690, 5, N'向量', N'5', N'ai_model_type', 0, N'', N'', N'', N'1', N'2025-03-03 12:28:15', N'1', N'2025-03-03 12:28:15', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1691, 6, N'重排', N'6', N'ai_model_type', 0, N'', N'', N'', N'1', N'2025-03-03 12:28:26', N'1', N'2025-03-03 12:28:26', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1692, 14, N'MiniMax', N'MiniMax', N'ai_platform', 0, N'', N'', N'', N'1', N'2025-03-11 20:04:51', N'1', N'2025-03-11 20:04:51', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (1693, 15, N'月之暗面', N'Moonshot', N'ai_platform', 0, N'', N'', N'', N'1', N'2025-03-11 20:05:08', N'1', N'2025-03-11 20:05:08', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2000, 0, N'标准数据格式（JSON）', N'0', N'iot_data_format', 0, N'default', N'', N'', N'1', N'2024-08-10 11:53:26', N'1', N'2025-03-17 09:28:16', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2001, 1, N'透传/自定义', N'1', N'iot_data_format', 0, N'default', N'', N'', N'1', N'2024-08-10 11:53:37', N'1', N'2025-03-17 09:28:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2002, 0, N'直连设备', N'0', N'iot_product_device_type', 0, N'default', N'', N'', N'1', N'2024-08-10 11:54:58', N'1', N'2025-03-17 09:28:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2003, 2, N'网关设备', N'2', N'iot_product_device_type', 0, N'default', N'', N'', N'1', N'2024-08-10 11:55:08', N'1', N'2025-03-17 09:28:28', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2004, 1, N'网关子设备', N'1', N'iot_product_device_type', 0, N'default', N'', N'', N'1', N'2024-08-10 11:55:20', N'1', N'2025-03-17 09:28:31', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2005, 1, N'已发布', N'1', N'iot_product_status', 0, N'success', N'', N'', N'1', N'2024-08-10 12:10:33', N'1', N'2025-03-17 09:28:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2006, 0, N'开发中', N'0', N'iot_product_status', 0, N'default', N'', N'', N'1', N'2024-08-10 14:19:18', N'1', N'2025-03-17 09:28:39', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2007, 0, N'弱校验', N'0', N'iot_validate_type', 0, N'', N'', N'', N'1', N'2024-09-06 20:05:48', N'1', N'2025-03-17 09:28:41', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2008, 1, N'免校验', N'1', N'iot_validate_type', 0, N'', N'', N'', N'1', N'2024-09-06 20:06:03', N'1', N'2025-03-17 09:28:44', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2009, 0, N'Wi-Fi', N'0', N'iot_net_type', 0, N'', N'', N'', N'1', N'2024-09-06 22:04:47', N'1', N'2025-03-17 09:28:47', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2010, 1, N'蜂窝（2G / 3G / 4G / 5G）', N'1', N'iot_net_type', 0, N'', N'', N'', N'1', N'2024-09-06 22:05:14', N'1', N'2025-03-17 09:28:49', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2011, 2, N'以太网', N'2', N'iot_net_type', 0, N'', N'', N'', N'1', N'2024-09-06 22:05:35', N'1', N'2025-03-17 09:28:51', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2012, 3, N'其他', N'3', N'iot_net_type', 0, N'', N'', N'', N'1', N'2024-09-06 22:05:52', N'1', N'2025-03-17 09:28:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2013, 0, N'自定义', N'0', N'iot_protocol_type', 0, N'', N'', N'', N'1', N'2024-09-06 22:26:10', N'1', N'2025-03-17 09:28:56', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2014, 1, N'Modbus', N'1', N'iot_protocol_type', 0, N'', N'', N'', N'1', N'2024-09-06 22:26:21', N'1', N'2025-03-17 09:28:58', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2015, 2, N'OPC UA', N'2', N'iot_protocol_type', 0, N'', N'', N'', N'1', N'2024-09-06 22:26:31', N'1', N'2025-03-17 09:29:00', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2016, 3, N'ZigBee', N'3', N'iot_protocol_type', 0, N'', N'', N'', N'1', N'2024-09-06 22:26:39', N'1', N'2025-03-17 09:29:04', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2017, 4, N'BLE', N'4', N'iot_protocol_type', 0, N'', N'', N'', N'1', N'2024-09-06 22:26:48', N'1', N'2025-03-17 09:29:06', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2018, 0, N'未激活', N'0', N'iot_device_state', 0, N'', N'', N'', N'1', N'2024-09-21 08:13:34', N'1', N'2025-03-17 09:29:09', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2019, 1, N'在线', N'1', N'iot_device_state', 0, N'', N'', N'', N'1', N'2024-09-21 08:13:48', N'1', N'2025-03-17 09:29:12', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2020, 2, N'离线', N'2', N'iot_device_state', 0, N'', N'', N'', N'1', N'2024-09-21 08:13:59', N'1', N'2025-03-17 09:29:14', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2021, 1, N'属性', N'1', N'iot_thing_model_type', 0, N'', N'', N'', N'1', N'2024-09-29 20:03:01', N'1', N'2025-03-17 09:29:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2022, 2, N'服务', N'2', N'iot_thing_model_type', 0, N'', N'', N'', N'1', N'2024-09-29 20:03:11', N'1', N'2025-03-17 09:29:27', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2023, 3, N'事件', N'3', N'iot_thing_model_type', 0, N'', N'', N'', N'1', N'2024-09-29 20:03:20', N'1', N'2025-03-17 09:29:29', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2024, 1, N'JAR 部署', N'0', N'iot_plugin_deploy_type', 0, N'', N'', N'', N'1', N'2024-12-13 10:55:32', N'1', N'2025-03-17 09:29:32', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2025, 2, N'独立部署', N'1', N'iot_plugin_deploy_type', 0, N'', N'', N'', N'1', N'2024-12-13 10:55:43', N'1', N'2025-03-17 09:29:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2026, 0, N'停止', N'0', N'iot_plugin_status', 0, N'danger', N'', N'', N'1', N'2024-12-13 11:07:37', N'1', N'2025-03-17 09:29:37', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2027, 1, N'运行', N'1', N'iot_plugin_status', 0, N'', N'', N'', N'1', N'2024-12-13 11:07:45', N'1', N'2025-03-17 09:34:17', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2028, 0, N'普通插件', N'0', N'iot_plugin_type', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:32', N'1', N'2025-03-17 09:34:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2029, 1, N'设备插件', N'1', N'iot_plugin_type', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:34:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2030, 1, N'升每分钟', N'L/min', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:34:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2031, 2, N'毫克每千克', N'mg/kg', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:34:27', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2032, 3, N'浊度', N'NTU', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:34:31', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2033, 4, N'PH值', N'pH', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:34:36', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2034, 5, N'土壤EC值', N'dS/m', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:34:43', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2035, 6, N'太阳总辐射', N'W/㎡', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:36:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2036, 7, N'降雨量', N'mm/hour', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:36:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2037, 8, N'乏', N'var', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:36:27', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2038, 9, N'厘泊', N'cP', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:36:33', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2039, 10, N'饱和度', N'aw', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:11', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2040, 11, N'个', N'pcs', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:19', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2041, 12, N'厘斯', N'cst', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:22', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2042, 13, N'巴', N'bar', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2043, 14, N'纳克每升', N'ppt', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:27', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2044, 15, N'微克每升', N'ppb', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:31', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2045, 16, N'微西每厘米', N'uS/cm', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:34', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2046, 17, N'牛顿每库仑', N'N/C', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:38', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2047, 18, N'伏特每米', N'V/m', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:43', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2048, 19, N'滴速', N'ml/min', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2049, 20, N'毫米汞柱', N'mmHg', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:48', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2050, 21, N'血糖', N'mmol/L', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:37:54', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2051, 22, N'毫米每秒', N'mm/s', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:38:02', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2052, 23, N'转每分钟', N'turn/m', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:38:07', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2053, 24, N'次', N'count', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:38:09', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2054, 25, N'档', N'gear', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:38:11', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2055, 26, N'步', N'stepCount', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:38:13', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2056, 27, N'标准立方米每小时', N'Nm3/h', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:38:15', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2057, 28, N'千伏', N'kV', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:38:20', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2058, 29, N'千伏安', N'kVA', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:38:24', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2060, 30, N'千乏', N'kVar', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2061, 31, N'微瓦每平方厘米', N'uw/cm2', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2062, 32, N'只', N'只', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2063, 33, N'相对湿度', N'%RH', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2064, 34, N'立方米每秒', N'm³/s', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2065, 35, N'公斤每秒', N'kg/s', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2066, 36, N'转每分钟', N'r/min', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2067, 37, N'吨每小时', N't/h', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2068, 38, N'千卡每小时', N'KCL/h', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2069, 39, N'升每秒', N'L/s', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2070, 40, N'兆帕', N'Mpa', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2071, 41, N'立方米每小时', N'm³/h', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2072, 42, N'千乏时', N'kvarh', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2073, 43, N'微克每升', N'μg/L', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2074, 44, N'千卡路里', N'kcal', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2075, 45, N'吉字节', N'GB', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2076, 46, N'兆字节', N'MB', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2077, 47, N'千字节', N'KB', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2078, 48, N'字节', N'B', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2079, 49, N'微克每平方分米每天', N'μg/(d㎡·d)', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2080, 50, N'无', N'', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2081, 51, N'百万分率', N'ppm', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2082, 52, N'像素', N'pixel', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2083, 53, N'照度', N'Lux', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2084, 54, N'重力加速度', N'grav', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2085, 55, N'分贝', N'dB', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2086, 56, N'百分比', N'%', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2087, 57, N'流明', N'lm', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2088, 58, N'比特', N'bit', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2089, 59, N'克每毫升', N'g/mL', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2090, 60, N'克每升', N'g/L', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2091, 61, N'毫克每升', N'mg/L', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2092, 62, N'微克每立方米', N'μg/m³', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2093, 63, N'毫克每立方米', N'mg/m³', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2094, 64, N'克每立方米', N'g/m³', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2095, 65, N'千克每立方米', N'kg/m³', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2096, 66, N'纳法', N'nF', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2097, 67, N'皮法', N'pF', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2098, 68, N'微法', N'μF', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2099, 69, N'法拉', N'F', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2100, 70, N'欧姆', N'Ω', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2101, 71, N'微安', N'μA', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2102, 72, N'毫安', N'mA', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2103, 73, N'千安', N'kA', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2104, 74, N'安培', N'A', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2105, 75, N'毫伏', N'mV', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2106, 76, N'伏特', N'V', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2107, 77, N'毫秒', N'ms', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2108, 78, N'秒', N's', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2109, 79, N'分钟', N'min', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2110, 80, N'小时', N'h', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2111, 81, N'日', N'day', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2112, 82, N'周', N'week', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2113, 83, N'月', N'month', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2114, 84, N'年', N'year', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2115, 85, N'节', N'kn', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2116, 86, N'千米每小时', N'km/h', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2117, 87, N'米每秒', N'm/s', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2118, 88, N'秒', N'″', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2119, 89, N'分', N'′', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2120, 90, N'度', N'°', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2121, 91, N'弧度', N'rad', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2122, 92, N'赫兹', N'Hz', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2123, 93, N'微瓦', N'μW', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2124, 94, N'毫瓦', N'mW', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2125, 95, N'千瓦特', N'kW', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2126, 96, N'瓦特', N'W', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2127, 97, N'卡路里', N'cal', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2128, 98, N'千瓦时', N'kW·h', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2129, 99, N'瓦时', N'Wh', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2130, 100, N'电子伏', N'eV', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2131, 101, N'千焦', N'kJ', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2132, 102, N'焦耳', N'J', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2133, 103, N'华氏度', N'℉', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2134, 104, N'开尔文', N'K', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2135, 105, N'吨', N't', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2136, 106, N'摄氏度', N'°C', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2137, 107, N'毫帕', N'mPa', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2138, 108, N'百帕', N'hPa', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2139, 109, N'千帕', N'kPa', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2140, 110, N'帕斯卡', N'Pa', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2141, 111, N'毫克', N'mg', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2142, 112, N'克', N'g', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2143, 113, N'千克', N'kg', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2144, 114, N'牛', N'N', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2145, 115, N'毫升', N'mL', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2146, 116, N'升', N'L', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2147, 117, N'立方毫米', N'mm³', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2148, 118, N'立方厘米', N'cm³', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2149, 119, N'立方千米', N'km³', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2150, 120, N'立方米', N'm³', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2151, 121, N'公顷', N'h㎡', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2152, 122, N'平方厘米', N'c㎡', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2153, 123, N'平方毫米', N'm㎡', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2154, 124, N'平方千米', N'k㎡', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2155, 125, N'平方米', N'㎡', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2156, 126, N'纳米', N'nm', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2157, 127, N'微米', N'μm', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2158, 128, N'毫米', N'mm', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2159, 129, N'厘米', N'cm', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2160, 130, N'分米', N'dm', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2161, 131, N'千米', N'km', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2162, 132, N'米', N'm', N'iot_thing_model_unit', 0, N'', N'', N'', N'1', N'2024-12-13 11:08:41', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2163, 1, N'输入', N'1', N'iot_data_bridge_direction_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:38:24', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2164, 2, N'输出', N'2', N'iot_data_bridge_direction_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:38:36', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2165, 1, N'HTTP', N'1', N'iot_data_bridge_type_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:39:54', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2166, 2, N'TCP', N'2', N'iot_data_bridge_type_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:40:06', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2167, 3, N'WEBSOCKET', N'3', N'iot_data_bridge_type_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:40:24', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2168, 10, N'MQTT', N'10', N'iot_data_bridge_type_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:40:37', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2169, 20, N'DATABASE', N'20', N'iot_data_bridge_type_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:41:05', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2170, 21, N'REDIS_STREAM', N'21', N'iot_data_bridge_type_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:41:18', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2171, 30, N'ROCKETMQ', N'30', N'iot_data_bridge_type_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:41:30', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2172, 31, N'RABBITMQ', N'31', N'iot_data_bridge_type_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:41:47', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (2173, 32, N'KAFKA', N'32', N'iot_data_bridge_type_enum', 0, N'primary', N'', N'', N'1', N'2025-03-09 12:41:59', N'1', N'2025-03-17 09:40:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3000, 16, N'百川智能', N'BaiChuan', N'ai_platform', 0, N'', N'', N'', N'1', N'2025-03-23 12:15:46', N'1', N'2025-03-23 12:15:46', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3001, 50, N'Vben5.0 Ant Design Schema 模版', N'40', N'infra_codegen_front_type', 0, N'', N'', NULL, N'1', N'2025-04-23 21:47:47', N'1', N'2025-05-02 12:01:15', N'0')\nGO\nINSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3002, 6, N'支付宝余额', N'6', N'brokerage_withdraw_type', 0, N'', N'', N'API 打款', N'1', N'2025-05-10 08:24:49', N'1', N'2025-05-10 08:24:49', N'0')\nGO\nINSERT 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, N'支付宝小程序', N'40', N'system_social_type', 0, N'', N'', N'', N'1', N'2023-11-04 13:05:38', N'1', N'2023-11-04 13:07:16', N'0')\nGO\nSET IDENTITY_INSERT system_dict_data OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_dict_type\n-- ----------------------------\nDROP TABLE IF EXISTS system_dict_type\nGO\nCREATE TABLE system_dict_type\n(\n    id           bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name         nvarchar(100) DEFAULT ''                NOT NULL,\n    type         nvarchar(100) DEFAULT ''                NOT NULL,\n    status       tinyint       DEFAULT 0                 NOT NULL,\n    remark       nvarchar(500) DEFAULT NULL              NULL,\n    creator      nvarchar(64)  DEFAULT ''                NULL,\n    create_time  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      nvarchar(64)  DEFAULT ''                NULL,\n    update_time  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      bit           DEFAULT 0                 NOT NULL,\n    deleted_time datetime2     DEFAULT NULL              NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典主键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'状态（0正常 1停用）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'删除时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type',\n     'COLUMN', N'deleted_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'字典类型表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_dict_type'\nGO\n\n-- ----------------------------\n-- Records of system_dict_type\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_dict_type ON\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1, N'用户性别', N'system_user_sex', 0, NULL, N'admin', N'2021-01-05 17:03:48', N'1', N'2022-05-16 20:29:32', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (6, N'参数类型', N'infra_config_type', 0, NULL, N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:36:54', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (7, N'通知类型', N'system_notice_type', 0, NULL, N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:35:26', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (9, N'操作类型', N'infra_operate_type', 0, NULL, N'admin', N'2021-01-05 17:03:48', N'1', N'2024-03-14 12:44:01', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (10, N'系统状态', N'common_status', 0, NULL, N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-01 16:21:28', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (11, N'Boolean 是否类型', N'infra_boolean_string', 0, N'boolean 转是否', N'', N'2021-01-19 03:20:08', N'', N'2022-02-01 16:37:10', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (104, N'登陆结果', N'system_login_result', 0, N'登陆结果', N'', N'2021-01-18 06:17:11', N'', N'2022-02-01 16:36:00', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (106, N'代码生成模板类型', N'infra_codegen_template_type', 0, NULL, N'', N'2021-02-05 07:08:06', N'1', N'2022-05-16 20:26:50', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (107, N'定时任务状态', N'infra_job_status', 0, NULL, N'', N'2021-02-07 07:44:16', N'', N'2022-02-01 16:51:11', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (108, N'定时任务日志状态', N'infra_job_log_status', 0, NULL, N'', N'2021-02-08 10:03:51', N'', N'2022-02-01 16:50:43', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (109, N'用户类型', N'user_type', 0, NULL, N'', N'2021-02-26 00:15:51', N'', N'2021-02-26 00:15:51', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (110, N'API 异常数据的处理状态', N'infra_api_error_log_process_status', 0, NULL, N'', N'2021-02-26 07:07:01', N'', N'2022-02-01 16:50:53', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (111, N'短信渠道编码', N'system_sms_channel_code', 0, NULL, N'1', N'2021-04-05 01:04:50', N'1', N'2022-02-16 02:09:08', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (112, N'短信模板的类型', N'system_sms_template_type', 0, NULL, N'1', N'2021-04-05 21:50:43', N'1', N'2022-02-01 16:35:06', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (113, N'短信发送状态', N'system_sms_send_status', 0, NULL, N'1', N'2021-04-11 20:18:03', N'1', N'2022-02-01 16:35:09', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (114, N'短信接收状态', N'system_sms_receive_status', 0, NULL, N'1', N'2021-04-11 20:27:14', N'1', N'2022-02-01 16:35:14', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (116, N'登陆日志的类型', N'system_login_type', 0, N'登陆日志的类型', N'1', N'2021-10-06 00:50:46', N'1', N'2022-02-01 16:35:56', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (117, N'OA 请假类型', N'bpm_oa_leave_type', 0, NULL, N'1', N'2021-09-21 22:34:33', N'1', N'2022-01-22 10:41:37', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (130, N'支付渠道编码类型', N'pay_channel_code', 0, N'支付渠道的编码', N'1', N'2021-12-03 10:35:08', N'1', N'2023-07-10 10:11:39', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (131, N'支付回调状态', N'pay_notify_status', 0, N'支付回调状态（包括退款回调）', N'1', N'2021-12-03 10:53:29', N'1', N'2023-07-19 18:09:43', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (132, N'支付订单状态', N'pay_order_status', 0, N'支付订单状态', N'1', N'2021-12-03 11:17:50', N'1', N'2021-12-03 11:17:50', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (134, N'退款订单状态', N'pay_refund_status', 0, N'退款订单状态', N'1', N'2021-12-10 16:42:50', N'1', N'2023-07-19 10:13:17', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (139, N'流程实例的状态', N'bpm_process_instance_status', 0, N'流程实例的状态', N'1', N'2022-01-07 23:46:42', N'1', N'2022-01-07 23:46:42', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (140, N'流程实例的结果', N'bpm_task_status', 0, N'流程实例的结果', N'1', N'2022-01-07 23:48:10', N'1', N'2024-03-08 22:42:03', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (141, N'流程的表单类型', N'bpm_model_form_type', 0, N'流程的表单类型', N'103', N'2022-01-11 23:50:45', N'103', N'2022-01-11 23:50:45', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (142, N'任务分配规则的类型', N'bpm_task_candidate_strategy', 0, N'BPM 任务的候选人的策略', N'103', N'2022-01-12 23:21:04', N'103', N'2024-03-06 02:53:59', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (144, N'代码生成的场景枚举', N'infra_codegen_scene', 0, N'代码生成的场景枚举', N'1', N'2022-02-02 13:14:45', N'1', N'2022-03-10 16:33:46', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (145, N'角色类型', N'system_role_type', 0, N'角色类型', N'1', N'2022-02-16 13:01:46', N'1', N'2022-02-16 13:01:46', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (146, N'文件存储器', N'infra_file_storage', 0, N'文件存储器', N'1', N'2022-03-15 00:24:38', N'1', N'2022-03-15 00:24:38', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (147, N'OAuth 2.0 授权类型', N'system_oauth2_grant_type', 0, N'OAuth 2.0 授权类型（模式）', N'1', N'2022-05-12 00:20:52', N'1', N'2022-05-11 16:25:49', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (149, N'商品 SPU 状态', N'product_spu_status', 0, N'商品 SPU 状态', N'1', N'2022-10-24 21:19:04', N'1', N'2022-10-24 21:19:08', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (150, N'优惠类型', N'promotion_discount_type', 0, N'优惠类型', N'1', N'2022-11-01 12:46:06', N'1', N'2022-11-01 12:46:06', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (151, N'优惠劵模板的有限期类型', N'promotion_coupon_template_validity_type', 0, N'优惠劵模板的有限期类型', N'1', N'2022-11-02 00:06:20', N'1', N'2022-11-04 00:08:26', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (152, N'营销的商品范围', N'promotion_product_scope', 0, N'营销的商品范围', N'1', N'2022-11-02 00:28:01', N'1', N'2022-11-02 00:28:01', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (153, N'优惠劵的状态', N'promotion_coupon_status', 0, N'优惠劵的状态', N'1', N'2022-11-04 00:14:49', N'1', N'2022-11-04 00:14:49', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (154, N'优惠劵的领取方式', N'promotion_coupon_take_type', 0, N'优惠劵的领取方式', N'1', N'2022-11-04 19:12:27', N'1', N'2022-11-04 19:12:27', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (155, N'促销活动的状态', N'promotion_activity_status', 0, N'促销活动的状态', N'1', N'2022-11-04 22:54:23', N'1', N'2022-11-04 22:54:23', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (156, N'营销的条件类型', N'promotion_condition_type', 0, N'营销的条件类型', N'1', N'2022-11-04 22:59:23', N'1', N'2022-11-04 22:59:23', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (157, N'交易售后状态', N'trade_after_sale_status', 0, N'交易售后状态', N'1', N'2022-11-19 20:52:56', N'1', N'2022-11-19 20:52:56', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (158, N'交易售后的类型', N'trade_after_sale_type', 0, N'交易售后的类型', N'1', N'2022-11-19 21:04:09', N'1', N'2022-11-19 21:04:09', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (159, N'交易售后的方式', N'trade_after_sale_way', 0, N'交易售后的方式', N'1', N'2022-11-19 21:39:04', N'1', N'2022-11-19 21:39:04', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (160, N'终端', N'terminal', 0, N'终端', N'1', N'2022-12-10 10:50:50', N'1', N'2022-12-10 10:53:11', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (161, N'交易订单的类型', N'trade_order_type', 0, N'交易订单的类型', N'1', N'2022-12-10 16:33:54', N'1', N'2022-12-10 16:33:54', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (162, N'交易订单的状态', N'trade_order_status', 0, N'交易订单的状态', N'1', N'2022-12-10 16:48:44', N'1', N'2022-12-10 16:48:44', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (163, N'交易订单项的售后状态', N'trade_order_item_after_sale_status', 0, N'交易订单项的售后状态', N'1', N'2022-12-10 20:58:08', N'1', N'2022-12-10 20:58:08', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (164, N'公众号自动回复的请求关键字匹配模式', N'mp_auto_reply_request_match', 0, N'公众号自动回复的请求关键字匹配模式', N'1', N'2023-01-16 23:29:56', N'1', N'2023-01-16 23:29:56', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (165, N'公众号的消息类型', N'mp_message_type', 0, N'公众号的消息类型', N'1', N'2023-01-17 22:17:09', N'1', N'2023-01-17 22:17:09', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (166, N'邮件发送状态', N'system_mail_send_status', 0, N'邮件发送状态', N'1', N'2023-01-26 09:53:13', N'1', N'2023-01-26 09:53:13', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (167, N'站内信模版的类型', N'system_notify_template_type', 0, N'站内信模版的类型', N'1', N'2023-01-28 10:35:10', N'1', N'2023-01-28 10:35:10', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (168, N'代码生成的前端类型', N'infra_codegen_front_type', 0, N'', N'1', N'2023-04-12 23:57:52', N'1', N'2023-04-12 23:57:52', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (170, N'快递计费方式', N'trade_delivery_express_charge_mode', 0, N'用于商城交易模块配送管理', N'1', N'2023-05-21 22:45:03', N'1', N'2023-05-21 22:45:03', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (171, N'积分业务类型', N'member_point_biz_type', 0, N'', N'1', N'2023-06-10 12:15:00', N'1', N'2023-06-28 13:48:20', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (173, N'支付通知类型', N'pay_notify_type', 0, NULL, N'1', N'2023-07-20 12:23:03', N'1', N'2023-07-20 12:23:03', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (174, N'会员经验业务类型', N'member_experience_biz_type', 0, NULL, N'', N'2023-08-22 12:41:01', N'', N'2023-08-22 12:41:01', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (175, N'交易配送类型', N'trade_delivery_type', 0, N'', N'1', N'2023-08-23 00:03:14', N'1', N'2023-08-23 00:03:14', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (176, N'分佣模式', N'brokerage_enabled_condition', 0, NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (177, N'分销关系绑定模式', N'brokerage_bind_mode', 0, NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (178, N'佣金提现类型', N'brokerage_withdraw_type', 0, NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (179, N'佣金记录业务类型', N'brokerage_record_biz_type', 0, NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (180, N'佣金记录状态', N'brokerage_record_status', 0, NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (181, N'佣金提现状态', N'brokerage_withdraw_status', 0, NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (182, N'佣金提现银行', N'brokerage_bank_name', 0, NULL, N'', N'2023-09-28 02:46:05', N'', N'2023-09-28 02:46:05', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (183, N'砍价记录的状态', N'promotion_bargain_record_status', 0, N'', N'1', N'2023-10-05 10:41:08', N'1', N'2023-10-05 10:41:08', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (184, N'拼团记录的状态', N'promotion_combination_record_status', 0, N'', N'1', N'2023-10-08 07:24:25', N'1', N'2023-10-08 07:24:25', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (185, N'回款-回款方式', N'crm_receivable_return_type', 0, N'回款-回款方式', N'1', N'2023-10-18 21:54:10', N'1', N'2023-10-18 21:54:10', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (186, N'CRM 客户行业', N'crm_customer_industry', 0, N'CRM 客户所属行业', N'1', N'2023-10-28 22:57:07', N'1', N'2024-02-18 23:30:22', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (187, N'客户等级', N'crm_customer_level', 0, N'CRM 客户等级', N'1', N'2023-10-28 22:59:12', N'1', N'2023-10-28 15:11:16', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (188, N'客户来源', N'crm_customer_source', 0, N'CRM 客户来源', N'1', N'2023-10-28 23:00:34', N'1', N'2023-10-28 15:11:16', N'0', NULL)\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (600, N'Banner 位置', N'promotion_banner_position', 0, N'', N'1', N'2023-10-08 07:24:25', N'1', N'2023-11-04 13:04:02', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (601, N'社交类型', N'system_social_type', 0, N'', N'1', N'2023-11-04 13:03:54', N'1', N'2023-11-04 13:03:54', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (604, N'产品状态', N'crm_product_status', 0, N'', N'1', N'2023-10-30 21:47:59', N'1', N'2023-10-30 21:48:45', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (605, N'CRM 数据权限的级别', N'crm_permission_level', 0, N'', N'1', N'2023-11-30 09:51:59', N'1', N'2023-11-30 09:51:59', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (606, N'CRM 审批状态', N'crm_audit_status', 0, N'', N'1', N'2023-11-30 18:56:23', N'1', N'2023-11-30 18:56:23', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (607, N'CRM 产品单位', N'crm_product_unit', 0, N'', N'1', N'2023-12-05 23:01:51', N'1', N'2023-12-05 23:01:51', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (608, N'CRM 跟进方式', N'crm_follow_up_type', 0, N'', N'1', N'2024-01-15 20:48:05', N'1', N'2024-01-15 20:48:05', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (610, N'转账订单状态', N'pay_transfer_status', 0, N'', N'1', N'2023-10-28 16:18:32', N'1', N'2023-10-28 16:18:32', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (611, N'ERP 库存明细的业务类型', N'erp_stock_record_biz_type', 0, N'ERP 库存明细的业务类型', N'1', N'2024-02-05 18:07:02', N'1', N'2024-02-05 18:07:02', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (612, N'ERP 审批状态', N'erp_audit_status', 0, N'', N'1', N'2024-02-06 00:00:07', N'1', N'2024-02-06 00:00:07', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (613, N'BPM 监听器类型', N'bpm_process_listener_type', 0, N'', N'1', N'2024-03-23 12:52:24', N'1', N'2024-03-09 15:54:28', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (615, N'BPM 监听器值类型', N'bpm_process_listener_value_type', 0, N'', N'1', N'2024-03-23 13:00:31', N'1', N'2024-03-23 13:00:31', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (616, N'时间间隔', N'date_interval', 0, N'', N'1', N'2024-03-29 22:50:09', N'1', N'2024-03-29 22:50:09', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (619, N'CRM 商机结束状态类型', N'crm_business_end_status_type', 0, N'', N'1', N'2024-04-13 23:23:00', N'1', N'2024-04-13 23:23:00', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (620, N'AI 模型平台', N'ai_platform', 0, N'', N'1', N'2024-05-09 22:27:38', N'1', N'2024-05-09 22:27:38', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (621, N'AI 绘画状态', N'ai_image_status', 0, N'', N'1', N'2024-06-26 20:51:23', N'1', N'2024-06-26 20:51:23', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (622, N'AI 音乐状态', N'ai_music_status', 0, N'', N'1', N'2024-06-27 22:45:07', N'1', N'2024-06-28 00:56:27', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (623, N'AI 音乐生成模式', N'ai_generate_mode', 0, N'', N'1', N'2024-06-27 22:46:21', N'1', N'2024-06-28 01:22:29', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (624, N'写作语气', N'ai_write_tone', 0, N'', N'1', N'2024-07-07 15:19:02', N'1', N'2024-07-07 15:19:02', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (625, N'写作语言', N'ai_write_language', 0, N'', N'1', N'2024-07-07 15:18:52', N'1', N'2024-07-07 15:18:52', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (626, N'写作长度', N'ai_write_length', 0, N'', N'1', N'2024-07-07 15:18:41', N'1', N'2024-07-07 15:18:41', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (627, N'写作格式', N'ai_write_format', 0, N'', N'1', N'2024-07-07 15:14:34', N'1', N'2024-07-07 15:14:34', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (628, N'AI 写作类型', N'ai_write_type', 0, N'', N'1', N'2024-07-10 21:25:29', N'1', N'2024-07-10 21:25:29', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (629, N'BPM 流程模型类型', N'bpm_model_type', 0, N'', N'1', N'2024-08-26 15:21:43', N'1', N'2024-08-26 15:21:43', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (640, N'AI 模型类型', N'ai_model_type', 0, N'', N'1', N'2025-03-03 12:24:07', N'1', N'2025-03-03 12:24:07', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1000, N'IoT 数据格式', N'iot_data_format', 0, N'', N'1', N'2024-08-10 11:52:58', N'1', N'2025-03-17 09:25:06', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1001, N'IoT 产品设备类型', N'iot_product_device_type', 0, N'', N'1', N'2024-08-10 11:54:30', N'1', N'2025-03-17 09:25:08', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1002, N'IoT 产品状态', N'iot_product_status', 0, N'', N'1', N'2024-08-10 12:06:09', N'1', N'2025-03-17 09:25:10', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1003, N'IoT 数据校验级别', N'iot_validate_type', 0, N'', N'1', N'2024-09-06 20:05:13', N'1', N'2025-03-17 09:25:12', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1004, N'IoT 联网方式', N'iot_net_type', 0, N'', N'1', N'2024-09-06 22:04:13', N'1', N'2025-03-17 09:25:14', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1005, N'IoT 接入网关协议', N'iot_protocol_type', 0, N'', N'1', N'2024-09-06 22:20:17', N'1', N'2025-03-17 09:25:16', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1006, N'IoT 设备状态', N'iot_device_state', 0, N'', N'1', N'2024-09-21 08:12:55', N'1', N'2025-03-17 09:25:19', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1007, N'IoT 物模型功能类型', N'iot_thing_model_type', 0, N'', N'1', N'2024-09-29 20:02:36', N'1', N'2025-03-17 09:25:24', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1008, N'IoT 插件部署方式', N'iot_plugin_deploy_type', 0, N'', N'1', N'2024-12-13 10:55:13', N'1', N'2025-03-17 09:25:27', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1009, N'IoT 插件状态', N'iot_plugin_status', 0, N'', N'1', N'2024-12-13 11:05:34', N'1', N'2025-03-17 09:25:30', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1010, N'IoT 插件类型', N'iot_plugin_type', 0, N'', N'1', N'2024-12-13 11:08:19', N'1', N'2025-03-17 09:25:32', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1011, N'IoT 物模型单位', N'iot_thing_model_unit', 0, N'', N'1', N'2024-12-25 17:36:46', N'1', N'2025-03-17 09:25:35', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1012, N'IoT 数据桥接的方向枚举', N'iot_data_bridge_direction_enum', 0, N'', N'1', N'2025-03-09 12:37:40', N'1', N'2025-03-17 09:25:39', N'0', N'1970-01-01 00:00:00')\nGO\nINSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted, deleted_time) VALUES (1013, N'IoT 数据桥梁的类型枚举', N'iot_data_bridge_type_enum', 0, N'', N'1', N'2025-03-09 12:39:36', N'1', N'2025-04-06 17:09:46', N'0', N'1970-01-01 00:00:00')\nGO\nSET IDENTITY_INSERT system_dict_type OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_login_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_login_log\nGO\nCREATE TABLE system_login_log\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    log_type    bigint                                 NOT NULL,\n    trace_id    nvarchar(64) DEFAULT ''                NOT NULL,\n    user_id     bigint       DEFAULT 0                 NOT NULL,\n    user_type   tinyint      DEFAULT 0                 NOT NULL,\n    username    nvarchar(50) DEFAULT ''                NOT NULL,\n    result      tinyint                                NOT NULL,\n    user_ip     nvarchar(50)                           NOT NULL,\n    user_agent  nvarchar(512)                          NOT NULL,\n    creator     nvarchar(64) DEFAULT ''                NULL,\n    create_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64) DEFAULT ''                NULL,\n    update_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT 0                 NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'访问ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'日志类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'log_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'链路追踪编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'trace_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户账号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'username'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'登陆结果',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'result'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户 IP',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'user_ip'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'浏览器 UA',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'user_agent'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'系统访问记录',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_login_log'\nGO\n\n-- ----------------------------\n-- Table structure for system_mail_account\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_account\nGO\nCREATE TABLE system_mail_account\n(\n    id              bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    mail            nvarchar(255)                          NOT NULL,\n    username        nvarchar(255)                          NOT NULL,\n    password        nvarchar(255)                          NOT NULL,\n    host            nvarchar(255)                          NOT NULL,\n    port            int                                    NOT NULL,\n    ssl_enable      varchar(1)   DEFAULT '0'               NOT NULL,\n    starttls_enable varchar(1)   DEFAULT '0'               NOT NULL,\n    creator         nvarchar(64) DEFAULT ''                NULL,\n    create_time     datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         nvarchar(64) DEFAULT ''                NULL,\n    update_time     datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         bit          DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'主键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'邮箱',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'mail'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'username'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'密码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'password'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'SMTP 服务器域名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'host'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'SMTP 服务器端口',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'port'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否开启 SSL',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'ssl_enable'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否开启 STARTTLS',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'starttls_enable'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'邮箱账号表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_account'\nGO\n\n-- ----------------------------\n-- Records of system_mail_account\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_mail_account ON\nGO\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (1, N'7684413@qq.com', N'7684413@qq.com', N'1234576', N'127.0.0.1', 8080, N'0', N'0', N'1', N'2023-01-25 17:39:52', N'1', N'2025-04-04 16:34:40', N'0')\nGO\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (2, N'ydym_test@163.com', N'ydym_test@163.com', N'WBZTEINMIFVRYSOE', N'smtp.163.com', 465, N'1', N'0', N'1', N'2023-01-26 01:26:03', N'1', N'2023-04-12 22:39:38', N'0')\nGO\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (3, N'76854114@qq.com', N'3335', N'11234', N'yunai1.cn', 466, N'0', N'0', N'1', N'2023-01-27 15:06:38', N'1', N'2023-01-27 07:08:36', N'1')\nGO\nINSERT INTO system_mail_account (id, mail, username, password, host, port, ssl_enable, starttls_enable, creator, create_time, updater, update_time, deleted) VALUES (4, N'7685413x@qq.com', N'2', N'3', N'4', 5, N'1', N'0', N'1', N'2023-04-12 23:05:06', N'1', N'2023-04-12 15:05:11', N'1')\nGO\nSET IDENTITY_INSERT system_mail_account OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_mail_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_log\nGO\nCREATE TABLE system_mail_log\n(\n    id                bigint                                   NOT NULL PRIMARY KEY IDENTITY,\n    user_id           bigint         DEFAULT NULL              NULL,\n    user_type         tinyint        DEFAULT NULL              NULL,\n    to_mail           nvarchar(255)                            NOT NULL,\n    account_id        bigint                                   NOT NULL,\n    from_mail         nvarchar(255)                            NOT NULL,\n    template_id       bigint                                   NOT NULL,\n    template_code     nvarchar(63)                             NOT NULL,\n    template_nickname nvarchar(255)  DEFAULT NULL              NULL,\n    template_title    nvarchar(255)                            NOT NULL,\n    template_content  nvarchar(4000)                           NOT NULL,\n    template_params   nvarchar(255)                            NOT NULL,\n    send_status       tinyint        DEFAULT 0                 NOT NULL,\n    send_time         datetime2      DEFAULT NULL              NULL,\n    send_message_id   nvarchar(255)  DEFAULT NULL              NULL,\n    send_exception    nvarchar(4000) DEFAULT NULL              NULL,\n    creator           nvarchar(64)   DEFAULT ''                NULL,\n    create_time       datetime2      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater           nvarchar(64)   DEFAULT ''                NULL,\n    update_time       datetime2      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted           bit            DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'接收邮箱地址',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'to_mail'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'邮箱账号编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'account_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送邮箱地址',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'from_mail'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'template_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'template_code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模版发送人名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'template_nickname'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'邮件标题',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'template_title'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'邮件内容',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'template_content'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'邮件参数',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'template_params'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'send_status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'send_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送返回的消息 ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'send_message_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送异常',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'send_exception'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'邮件日志表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_log'\nGO\n\n-- ----------------------------\n-- Table structure for system_mail_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_mail_template\nGO\nCREATE TABLE system_mail_template\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name        nvarchar(63)                            NOT NULL,\n    code        nvarchar(63)                            NOT NULL,\n    account_id  bigint                                  NOT NULL,\n    nickname    nvarchar(255) DEFAULT NULL              NULL,\n    title       nvarchar(255)                           NOT NULL,\n    content     nvarchar(4000)                          NOT NULL,\n    params      nvarchar(255)                           NOT NULL,\n    status      tinyint                                 NOT NULL,\n    remark      nvarchar(255) DEFAULT NULL              NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送的邮箱账号编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'account_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送人名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'nickname'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板标题',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'title'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板内容',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'content'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数数组',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'params'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'开启状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'邮件模版表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_mail_template'\nGO\n\n-- ----------------------------\n-- Records of system_mail_template\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_mail_template ON\nGO\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (13, N'后台用户短信登录', N'admin-sms-login', 1, N'奥特曼', N'你猜我猜', N'<p>您的验证码是{code}，名字是{name}</p>', N'[\"code\",\"name\"]', 0, N'3', N'1', N'2021-10-11 08:10:00', N'1', N'2023-12-02 19:51:14', N'0')\nGO\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (14, N'测试模版', N'test_01', 2, N'芋艿', N'一个标题', N'<p>你是 {key01} 吗？</p><p><br></p><p>是的话，赶紧 {key02} 一下！</p>', N'[\"key01\",\"key02\"]', 0, NULL, N'1', N'2023-01-26 01:27:40', N'1', N'2023-01-27 10:32:16', N'0')\nGO\nINSERT INTO system_mail_template (id, name, code, account_id, nickname, title, content, params, status, remark, creator, create_time, updater, update_time, deleted) VALUES (15, N'3', N'2', 2, N'7', N'4', N'<p>45</p>', N'[]', 1, N'80', N'1', N'2023-01-27 15:50:35', N'1', N'2023-01-27 16:34:49', N'0')\nGO\nSET IDENTITY_INSERT system_mail_template OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_menu\n-- ----------------------------\nDROP TABLE IF EXISTS system_menu\nGO\nCREATE TABLE system_menu\n(\n    id             bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name           nvarchar(50)                            NOT NULL,\n    permission     nvarchar(100) DEFAULT ''                NOT NULL,\n    type           tinyint                                 NOT NULL,\n    sort           int           DEFAULT 0                 NOT NULL,\n    parent_id      bigint        DEFAULT 0                 NOT NULL,\n    path           nvarchar(200) DEFAULT ''                NULL,\n    icon           nvarchar(100) DEFAULT '#'               NULL,\n    component      nvarchar(255) DEFAULT NULL              NULL,\n    component_name nvarchar(255) DEFAULT NULL              NULL,\n    status         tinyint       DEFAULT 0                 NOT NULL,\n    visible        varchar(1)    DEFAULT '1'               NOT NULL,\n    keep_alive     varchar(1)    DEFAULT '1'               NOT NULL,\n    always_show    varchar(1)    DEFAULT '1'               NOT NULL,\n    creator        nvarchar(64)  DEFAULT ''                NULL,\n    create_time    datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        nvarchar(64)  DEFAULT ''                NULL,\n    update_time    datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'菜单ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'菜单名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'权限标识',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'permission'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'菜单类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'显示顺序',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'sort'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'父菜单ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'parent_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'路由地址',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'path'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'菜单图标',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'icon'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'组件路径',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'component'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'组件名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'component_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'菜单状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否可见',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'visible'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否缓存',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'keep_alive'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否总是显示',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'always_show'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'菜单权限表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_menu'\nGO\n\n-- ----------------------------\n-- Records of system_menu\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_menu ON\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1, N'系统管理', N'', 1, 10, 0, N'/system', N'ep:tools', NULL, NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2025-03-15 21:30:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2, N'基础设施', N'', 1, 20, 0, N'/infra', N'ep:monitor', NULL, NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-03-01 08:28:40', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5, N'OA 示例', N'', 1, 40, 1185, N'oa', N'fa:road', NULL, NULL, 0, N'1', N'1', N'1', N'admin', N'2021-09-20 16:26:19', N'1', N'2024-02-29 12:38:13', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (100, N'用户管理', N'system:user:list', 2, 1, 1, N'user', N'ep:avatar', N'system/user/index', N'SystemUser', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2025-03-15 21:30:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (101, N'角色管理', N'', 2, 2, 1, N'role', N'ep:user', N'system/role/index', N'SystemRole', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-05-01 18:35:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (102, N'菜单管理', N'', 2, 3, 1, N'menu', N'ep:menu', N'system/menu/index', N'SystemMenu', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 01:03:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (103, N'部门管理', N'', 2, 4, 1, N'dept', N'fa:address-card', N'system/dept/index', N'SystemDept', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 01:06:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (104, N'岗位管理', N'', 2, 5, 1, N'post', N'fa:address-book-o', N'system/post/index', N'SystemPost', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 01:06:39', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (105, N'字典管理', N'', 2, 6, 1, N'dict', N'ep:collection', N'system/dict/index', N'SystemDictType', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 01:07:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (106, N'配置管理', N'', 2, 8, 2, N'config', N'fa:connectdevelop', N'infra/config/index', N'InfraConfig', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-04-23 00:02:45', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (107, N'通知公告', N'', 2, 4, 2739, N'notice', N'ep:takeaway-box', N'system/notice/index', N'SystemNotice', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-04-22 23:56:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (108, N'审计日志', N'', 1, 9, 1, N'log', N'ep:document-copy', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 01:08:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (109, N'令牌管理', N'', 2, 2, 1261, N'token', N'fa:key', N'system/oauth2/token/index', N'SystemTokenClient', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 01:13:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (110, N'定时任务', N'', 2, 7, 2, N'job', N'fa-solid:tasks', N'infra/job/index', N'InfraJob', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 08:57:36', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (111, N'MySQL 监控', N'', 2, 1, 2740, N'druid', N'fa-solid:box', N'infra/druid/index', N'InfraDruid', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-04-23 00:05:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (112, N'Java 监控', N'', 2, 3, 2740, N'admin-server', N'ep:coffee-cup', N'infra/server/index', N'InfraAdminServer', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-04-23 00:06:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (113, N'Redis 监控', N'', 2, 2, 2740, N'redis', N'fa:reddit-square', N'infra/redis/index', N'InfraRedis', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-04-23 00:06:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (114, N'表单构建', N'infra:build:list', 2, 2, 2, N'build', N'fa:wpforms', N'infra/build/index', N'InfraBuild', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 08:51:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (115, N'代码生成', N'infra:codegen:query', 2, 1, 2, N'codegen', N'ep:document-copy', N'infra/codegen/index', N'InfraCodegen', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 08:51:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (116, N'API 接口', N'infra:swagger:list', 2, 3, 2, N'swagger', N'fa:fighter-jet', N'infra/swagger/index', N'InfraSwagger', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-04-23 00:01:24', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (500, N'操作日志', N'', 2, 1, 108, N'operate-log', N'ep:position', N'system/operatelog/index', N'SystemOperateLog', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 01:09:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (501, N'登录日志', N'', 2, 2, 108, N'login-log', N'ep:promotion', N'system/loginlog/index', N'SystemLoginLog', 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2024-02-29 01:10:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1001, N'用户查询', N'system:user:query', 3, 1, 100, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1002, N'用户新增', N'system:user:create', 3, 2, 100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1003, N'用户修改', N'system:user:update', 3, 3, 100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1004, N'用户删除', N'system:user:delete', 3, 4, 100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1005, N'用户导出', N'system:user:export', 3, 5, 100, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1006, N'用户导入', N'system:user:import', 3, 6, 100, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1007, N'重置密码', N'system:user:update-password', 3, 7, 100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1008, N'角色查询', N'system:role:query', 3, 1, 101, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1009, N'角色新增', N'system:role:create', 3, 2, 101, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1010, N'角色修改', N'system:role:update', 3, 3, 101, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1011, N'角色删除', N'system:role:delete', 3, 4, 101, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1012, N'角色导出', N'system:role:export', 3, 5, 101, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1013, N'菜单查询', N'system:menu:query', 3, 1, 102, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1014, N'菜单新增', N'system:menu:create', 3, 2, 102, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1015, N'菜单修改', N'system:menu:update', 3, 3, 102, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1016, N'菜单删除', N'system:menu:delete', 3, 4, 102, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1017, N'部门查询', N'system:dept:query', 3, 1, 103, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1018, N'部门新增', N'system:dept:create', 3, 2, 103, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1019, N'部门修改', N'system:dept:update', 3, 3, 103, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1020, N'部门删除', N'system:dept:delete', 3, 4, 103, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1021, N'岗位查询', N'system:post:query', 3, 1, 104, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1022, N'岗位新增', N'system:post:create', 3, 2, 104, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1023, N'岗位修改', N'system:post:update', 3, 3, 104, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1024, N'岗位删除', N'system:post:delete', 3, 4, 104, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1025, N'岗位导出', N'system:post:export', 3, 5, 104, N'', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1026, N'字典查询', N'system:dict:query', 3, 1, 105, N'#', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1027, N'字典新增', N'system:dict:create', 3, 2, 105, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1028, N'字典修改', N'system:dict:update', 3, 3, 105, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1029, N'字典删除', N'system:dict:delete', 3, 4, 105, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1030, N'字典导出', N'system:dict:export', 3, 5, 105, N'#', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1031, N'配置查询', N'infra:config:query', 3, 1, 106, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1032, N'配置新增', N'infra:config:create', 3, 2, 106, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1033, N'配置修改', N'infra:config:update', 3, 3, 106, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1034, N'配置删除', N'infra:config:delete', 3, 4, 106, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1035, N'配置导出', N'infra:config:export', 3, 5, 106, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1036, N'公告查询', N'system:notice:query', 3, 1, 107, N'#', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1037, N'公告新增', N'system:notice:create', 3, 2, 107, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1038, N'公告修改', N'system:notice:update', 3, 3, 107, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1039, N'公告删除', N'system:notice:delete', 3, 4, 107, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1040, N'操作查询', N'system:operate-log:query', 3, 1, 500, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1042, N'日志导出', N'system:operate-log:export', 3, 2, 500, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1043, N'登录查询', N'system:login-log:query', 3, 1, 501, N'#', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1045, N'日志导出', N'system:login-log:export', 3, 3, 501, N'#', N'#', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1046, N'令牌列表', N'system:oauth2-token:page', 3, 1, 109, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-05-09 23:54:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1048, N'令牌删除', N'system:oauth2-token:delete', 3, 2, 109, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-05-09 23:54:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1050, N'任务新增', N'infra:job:create', 3, 2, 110, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1051, N'任务修改', N'infra:job:update', 3, 3, 110, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1052, N'任务删除', N'infra:job:delete', 3, 4, 110, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1053, N'状态修改', N'infra:job:update', 3, 5, 110, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1054, N'任务导出', N'infra:job:export', 3, 7, 110, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1056, N'生成修改', N'infra:codegen:update', 3, 2, 115, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1057, N'生成删除', N'infra:codegen:delete', 3, 3, 115, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1058, N'导入代码', N'infra:codegen:create', 3, 2, 115, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1059, N'预览代码', N'infra:codegen:preview', 3, 4, 115, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1060, N'生成代码', N'infra:codegen:download', 3, 5, 115, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'admin', N'2021-01-05 17:03:48', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1063, N'设置角色菜单权限', N'system:permission:assign-role-menu', 3, 6, 101, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-01-06 17:53:44', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1064, N'设置角色数据权限', N'system:permission:assign-role-data-scope', 3, 7, 101, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-01-06 17:56:31', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1065, N'设置用户角色', N'system:permission:assign-user-role', 3, 8, 101, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-01-07 10:23:28', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1066, N'获得 Redis 监控信息', N'infra:redis:get-monitor-info', 3, 1, 113, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-01-26 01:02:31', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1067, N'获得 Redis Key 列表', N'infra:redis:get-key-list', 3, 2, 113, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-01-26 01:02:52', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1070, N'代码生成案例', N'', 1, 1, 2, N'demo', N'ep:aim', N'infra/testDemo/index', NULL, 0, N'1', N'1', N'1', N'', N'2021-02-06 12:42:49', N'1', N'2023-11-15 23:45:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1075, N'任务触发', N'infra:job:trigger', 3, 8, 110, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-02-07 13:03:10', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1077, N'链路追踪', N'', 2, 4, 2740, N'skywalking', N'fa:eye', N'infra/skywalking/index', N'InfraSkyWalking', 0, N'1', N'1', N'1', N'', N'2021-02-08 20:41:31', N'1', N'2024-04-23 00:07:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1078, N'访问日志', N'', 2, 1, 1083, N'api-access-log', N'ep:place', N'infra/apiAccessLog/index', N'InfraApiAccessLog', 0, N'1', N'1', N'1', N'', N'2021-02-26 01:32:59', N'1', N'2024-02-29 08:54:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1082, N'日志导出', N'infra:api-access-log:export', 3, 2, 1078, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-02-26 01:32:59', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1083, N'API 日志', N'', 2, 4, 2, N'log', N'fa:tasks', NULL, NULL, 0, N'1', N'1', N'1', N'', N'2021-02-26 02:18:24', N'1', N'2024-04-22 23:58:36', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1084, N'错误日志', N'infra:api-error-log:query', 2, 2, 1083, N'api-error-log', N'ep:warning-filled', N'infra/apiErrorLog/index', N'InfraApiErrorLog', 0, N'1', N'1', N'1', N'', N'2021-02-26 07:53:20', N'1', N'2024-02-29 08:55:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1085, N'日志处理', N'infra:api-error-log:update-status', 3, 2, 1084, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-02-26 07:53:20', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1086, N'日志导出', N'infra:api-error-log:export', 3, 3, 1084, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-02-26 07:53:20', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1087, N'任务查询', N'infra:job:query', 3, 1, 110, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2021-03-10 01:26:19', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1088, N'日志查询', N'infra:api-access-log:query', 3, 1, 1078, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2021-03-10 01:28:04', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1089, N'日志查询', N'infra:api-error-log:query', 3, 1, 1084, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2021-03-10 01:29:09', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1090, N'文件列表', N'', 2, 5, 1243, N'file', N'ep:upload-filled', N'infra/file/index', N'InfraFile', 0, N'1', N'1', N'1', N'', N'2021-03-12 20:16:20', N'1', N'2024-02-29 08:53:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1091, N'文件查询', N'infra:file:query', 3, 1, 1090, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-03-12 20:16:20', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1092, N'文件删除', N'infra:file:delete', 3, 4, 1090, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-03-12 20:16:20', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1093, N'短信管理', N'', 1, 1, 2739, N'sms', N'ep:message', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2021-04-05 01:10:16', N'1', N'2024-04-22 23:56:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1094, N'短信渠道', N'', 2, 0, 1093, N'sms-channel', N'fa:stack-exchange', N'system/sms/channel/index', N'SystemSmsChannel', 0, N'1', N'1', N'1', N'', N'2021-04-01 11:07:15', N'1', N'2024-02-29 01:15:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1095, N'短信渠道查询', N'system:sms-channel:query', 3, 1, 1094, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-01 11:07:15', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1096, N'短信渠道创建', N'system:sms-channel:create', 3, 2, 1094, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-01 11:07:15', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1097, N'短信渠道更新', N'system:sms-channel:update', 3, 3, 1094, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-01 11:07:15', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1098, N'短信渠道删除', N'system:sms-channel:delete', 3, 4, 1094, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-01 11:07:15', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1100, N'短信模板', N'', 2, 1, 1093, N'sms-template', N'ep:connection', N'system/sms/template/index', N'SystemSmsTemplate', 0, N'1', N'1', N'1', N'', N'2021-04-01 17:35:17', N'1', N'2024-02-29 01:16:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1101, N'短信模板查询', N'system:sms-template:query', 3, 1, 1100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-01 17:35:17', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1102, N'短信模板创建', N'system:sms-template:create', 3, 2, 1100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-01 17:35:17', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1103, N'短信模板更新', N'system:sms-template:update', 3, 3, 1100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-01 17:35:17', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1104, N'短信模板删除', N'system:sms-template:delete', 3, 4, 1100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-01 17:35:17', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1105, N'短信模板导出', N'system:sms-template:export', 3, 5, 1100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-01 17:35:17', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1106, N'发送测试短信', N'system:sms-template:send-sms', 3, 6, 1100, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2021-04-11 00:26:40', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1107, N'短信日志', N'', 2, 2, 1093, N'sms-log', N'fa:edit', N'system/sms/log/index', N'SystemSmsLog', 0, N'1', N'1', N'1', N'', N'2021-04-11 08:37:05', N'1', N'2024-02-29 08:49:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1108, N'短信日志查询', N'system:sms-log:query', 3, 1, 1107, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-11 08:37:05', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1109, N'短信日志导出', N'system:sms-log:export', 3, 5, 1107, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-04-11 08:37:05', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1117, N'支付管理', N'', 1, 30, 0, N'/pay', N'ep:money', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2021-12-25 16:43:41', N'1', N'2024-02-29 08:58:38', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1118, N'请假查询', N'', 2, 0, 5, N'leave', N'fa:leanpub', N'bpm/oa/leave/index', N'BpmOALeave', 0, N'1', N'1', N'1', N'', N'2021-09-20 08:51:03', N'1', N'2024-02-29 12:38:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1119, N'请假申请查询', N'bpm:oa-leave:query', 3, 1, 1118, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-09-20 08:51:03', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1120, N'请假申请创建', N'bpm:oa-leave:create', 3, 2, 1118, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-09-20 08:51:03', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1126, N'应用信息', N'', 2, 1, 1117, N'app', N'fa:apple', N'pay/app/index', N'PayApp', 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:30', N'1', N'2024-02-29 08:59:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1127, N'支付应用信息查询', N'pay:app:query', 3, 1, 1126, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:31', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1128, N'支付应用信息创建', N'pay:app:create', 3, 2, 1126, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:31', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1129, N'支付应用信息更新', N'pay:app:update', 3, 3, 1126, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:31', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1130, N'支付应用信息删除', N'pay:app:delete', 3, 4, 1126, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:31', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1132, N'秘钥解析', N'pay:channel:parsing', 3, 6, 1129, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2021-11-08 15:15:47', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1133, N'支付商户信息查询', N'pay:merchant:query', 3, 1, 1132, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:41', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1134, N'支付商户信息创建', N'pay:merchant:create', 3, 2, 1132, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:41', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1135, N'支付商户信息更新', N'pay:merchant:update', 3, 3, 1132, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:41', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1136, N'支付商户信息删除', N'pay:merchant:delete', 3, 4, 1132, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:41', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1137, N'支付商户信息导出', N'pay:merchant:export', 3, 5, 1132, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-11-10 01:13:41', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1138, N'租户列表', N'', 2, 0, 1224, N'list', N'ep:house', N'system/tenant/index', N'SystemTenant', 0, N'1', N'1', N'1', N'', N'2021-12-14 12:31:43', N'1', N'2024-02-29 01:01:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1139, N'租户查询', N'system:tenant:query', 3, 1, 1138, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-14 12:31:44', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1140, N'租户创建', N'system:tenant:create', 3, 2, 1138, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-14 12:31:44', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1141, N'租户更新', N'system:tenant:update', 3, 3, 1138, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-14 12:31:44', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1142, N'租户删除', N'system:tenant:delete', 3, 4, 1138, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-14 12:31:44', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1143, N'租户导出', N'system:tenant:export', 3, 5, 1138, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-14 12:31:44', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1150, N'秘钥解析', N'', 3, 6, 1129, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2021-11-08 15:15:47', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1161, N'退款订单', N'', 2, 3, 1117, N'refund', N'fa:registered', N'pay/refund/index', N'PayRefund', 0, N'1', N'1', N'1', N'', N'2021-12-25 08:29:07', N'1', N'2024-02-29 08:59:20', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1162, N'退款订单查询', N'pay:refund:query', 3, 1, 1161, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-25 08:29:07', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1166, N'退款订单导出', N'pay:refund:export', 3, 5, 1161, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-25 08:29:07', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1173, N'支付订单', N'', 2, 2, 1117, N'order', N'fa:cc-paypal', N'pay/order/index', N'PayOrder', 0, N'1', N'1', N'1', N'', N'2021-12-25 08:49:43', N'1', N'2024-02-29 08:59:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1174, N'支付订单查询', N'pay:order:query', 3, 1, 1173, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-25 08:49:43', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1178, N'支付订单导出', N'pay:order:export', 3, 5, 1173, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-25 08:49:43', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1185, N'工作流程', N'', 1, 50, 0, N'/bpm', N'fa:medium', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2021-12-30 20:26:36', N'1', N'2024-02-29 12:43:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1186, N'流程管理', N'', 1, 10, 1185, N'manager', N'fa:dedent', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2021-12-30 20:28:30', N'1', N'2024-02-29 12:36:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1187, N'流程表单', N'', 2, 2, 1186, N'form', N'fa:hdd-o', N'bpm/form/index', N'BpmForm', 0, N'1', N'1', N'1', N'', N'2021-12-30 12:38:22', N'1', N'2024-03-19 12:25:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1188, N'表单查询', N'bpm:form:query', 3, 1, 1187, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-30 12:38:22', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1189, N'表单创建', N'bpm:form:create', 3, 2, 1187, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-30 12:38:22', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1190, N'表单更新', N'bpm:form:update', 3, 3, 1187, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-30 12:38:22', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1191, N'表单删除', N'bpm:form:delete', 3, 4, 1187, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-30 12:38:22', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1192, N'表单导出', N'bpm:form:export', 3, 5, 1187, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2021-12-30 12:38:22', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1193, N'流程模型', N'', 2, 1, 1186, N'model', N'fa-solid:project-diagram', N'bpm/model/index', N'BpmModel', 0, N'1', N'1', N'1', N'1', N'2021-12-31 23:24:58', N'1', N'2024-03-19 12:25:19', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1194, N'模型查询', N'bpm:model:query', 3, 1, 1193, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-03 19:01:10', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1195, N'模型创建', N'bpm:model:create', 3, 2, 1193, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-03 19:01:24', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1197, N'模型更新', N'bpm:model:update', 3, 4, 1193, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-03 19:02:28', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1198, N'模型删除', N'bpm:model:delete', 3, 5, 1193, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-03 19:02:43', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1199, N'模型发布', N'bpm:model:deploy', 3, 6, 1193, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-03 19:03:24', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1200, N'审批中心', N'', 2, 20, 1185, N'task', N'fa:tasks', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-07 23:51:48', N'1', N'2024-03-21 00:33:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1201, N'我的流程', N'', 2, 1, 1200, N'my', N'fa-solid:book', N'bpm/processInstance/index', N'BpmProcessInstanceMy', 0, N'1', N'1', N'1', N'', N'2022-01-07 15:53:44', N'1', N'2024-03-21 23:52:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1202, N'流程实例的查询', N'bpm:process-instance:query', 3, 1, 1201, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-01-07 15:53:44', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1207, N'待办任务', N'', 2, 10, 1200, N'todo', N'fa:slack', N'bpm/task/todo/index', N'BpmTodoTask', 0, N'1', N'1', N'1', N'1', N'2022-01-08 10:33:37', N'1', N'2024-02-29 12:37:39', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1208, N'已办任务', N'', 2, 20, 1200, N'done', N'fa:delicious', N'bpm/task/done/index', N'BpmDoneTask', 0, N'1', N'1', N'1', N'1', N'2022-01-08 10:34:13', N'1', N'2024-02-29 12:37:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1209, N'用户分组', N'', 2, 4, 1186, N'user-group', N'fa:user-secret', N'bpm/group/index', N'BpmUserGroup', 0, N'1', N'1', N'1', N'', N'2022-01-14 02:14:20', N'1', N'2024-03-21 23:55:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1210, N'用户组查询', N'bpm:user-group:query', 3, 1, 1209, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-01-14 02:14:20', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1211, N'用户组创建', N'bpm:user-group:create', 3, 2, 1209, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-01-14 02:14:20', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1212, N'用户组更新', N'bpm:user-group:update', 3, 3, 1209, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-01-14 02:14:20', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1213, N'用户组删除', N'bpm:user-group:delete', 3, 4, 1209, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-01-14 02:14:20', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1215, N'流程定义查询', N'bpm:process-definition:query', 3, 10, 1193, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-23 00:21:43', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1216, N'流程任务分配规则查询', N'bpm:task-assign-rule:query', 3, 20, 1193, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-23 00:26:53', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1217, N'流程任务分配规则创建', N'bpm:task-assign-rule:create', 3, 21, 1193, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-23 00:28:15', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1218, N'流程任务分配规则更新', N'bpm:task-assign-rule:update', 3, 22, 1193, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-23 00:28:41', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1219, N'流程实例的创建', N'bpm:process-instance:create', 3, 2, 1201, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-23 00:36:15', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1220, N'流程实例的取消', N'bpm:process-instance:cancel', 3, 3, 1201, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-23 00:36:33', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1221, N'流程任务的查询', N'bpm:task:query', 3, 1, 1207, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-23 00:38:52', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1222, N'流程任务的更新', N'bpm:task:update', 3, 2, 1207, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-01-23 00:39:24', N'1', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1224, N'租户管理', N'', 2, 0, 1, N'tenant', N'fa-solid:house-user', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2022-02-20 01:41:13', N'1', N'2024-02-29 00:59:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1225, N'租户套餐', N'', 2, 0, 1224, N'package', N'fa:bars', N'system/tenantPackage/index', N'SystemTenantPackage', 0, N'1', N'1', N'1', N'', N'2022-02-19 17:44:06', N'1', N'2024-02-29 01:01:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1226, N'租户套餐查询', N'system:tenant-package:query', 3, 1, 1225, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-02-19 17:44:06', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1227, N'租户套餐创建', N'system:tenant-package:create', 3, 2, 1225, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-02-19 17:44:06', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1228, N'租户套餐更新', N'system:tenant-package:update', 3, 3, 1225, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-02-19 17:44:06', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1229, N'租户套餐删除', N'system:tenant-package:delete', 3, 4, 1225, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-02-19 17:44:06', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1237, N'文件配置', N'', 2, 0, 1243, N'file-config', N'fa-solid:file-signature', N'infra/fileConfig/index', N'InfraFileConfig', 0, N'1', N'1', N'1', N'', N'2022-03-15 14:35:28', N'1', N'2024-02-29 08:52:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1238, N'文件配置查询', N'infra:file-config:query', 3, 1, 1237, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-03-15 14:35:28', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1239, N'文件配置创建', N'infra:file-config:create', 3, 2, 1237, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-03-15 14:35:28', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1240, N'文件配置更新', N'infra:file-config:update', 3, 3, 1237, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-03-15 14:35:28', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1241, N'文件配置删除', N'infra:file-config:delete', 3, 4, 1237, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-03-15 14:35:28', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1242, N'文件配置导出', N'infra:file-config:export', 3, 5, 1237, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-03-15 14:35:28', N'', N'2022-04-20 17:03:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1243, N'文件管理', N'', 2, 6, 2, N'file', N'ep:files', NULL, N'', 0, N'1', N'1', N'1', N'1', N'2022-03-16 23:47:40', N'1', N'2024-04-23 00:02:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1254, N'作者动态', N'', 1, 0, 0, N'https://www.iocoder.cn', N'ep:avatar', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2022-04-23 01:03:15', N'1', N'2025-04-29 17:45:38', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1255, N'数据源配置', N'', 2, 1, 2, N'data-source-config', N'ep:data-analysis', N'infra/dataSourceConfig/index', N'InfraDataSourceConfig', 0, N'1', N'1', N'1', N'', N'2022-04-27 14:37:32', N'1', N'2024-02-29 08:51:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1256, N'数据源配置查询', N'infra:data-source-config:query', 3, 1, 1255, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-04-27 14:37:32', N'', N'2022-04-27 14:37:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1257, N'数据源配置创建', N'infra:data-source-config:create', 3, 2, 1255, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-04-27 14:37:32', N'', N'2022-04-27 14:37:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1258, N'数据源配置更新', N'infra:data-source-config:update', 3, 3, 1255, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-04-27 14:37:32', N'', N'2022-04-27 14:37:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1259, N'数据源配置删除', N'infra:data-source-config:delete', 3, 4, 1255, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-04-27 14:37:32', N'', N'2022-04-27 14:37:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1260, N'数据源配置导出', N'infra:data-source-config:export', 3, 5, 1255, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-04-27 14:37:32', N'', N'2022-04-27 14:37:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1261, N'OAuth 2.0', N'', 2, 10, 1, N'oauth2', N'fa:dashcube', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2022-05-09 23:38:17', N'1', N'2024-02-29 01:12:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1263, N'应用管理', N'', 2, 0, 1261, N'oauth2/application', N'fa:hdd-o', N'system/oauth2/client/index', N'SystemOAuth2Client', 0, N'1', N'1', N'1', N'', N'2022-05-10 16:26:33', N'1', N'2024-02-29 01:13:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1264, N'客户端查询', N'system:oauth2-client:query', 3, 1, 1263, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-05-10 16:26:33', N'1', N'2022-05-11 00:31:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1265, N'客户端创建', N'system:oauth2-client:create', 3, 2, 1263, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-05-10 16:26:33', N'1', N'2022-05-11 00:31:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1266, N'客户端更新', N'system:oauth2-client:update', 3, 3, 1263, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-05-10 16:26:33', N'1', N'2022-05-11 00:31:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1267, N'客户端删除', N'system:oauth2-client:delete', 3, 4, 1263, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-05-10 16:26:33', N'1', N'2022-05-11 00:31:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1281, N'报表管理', N'', 2, 40, 0, N'/report', N'ep:pie-chart', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2022-07-10 20:22:15', N'1', N'2024-02-29 12:33:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (1282, N'报表设计器', N'', 2, 1, 1281, N'jimu-report', N'ep:trend-charts', N'report/jmreport/index', N'JimuReport', 0, N'1', N'1', N'1', N'1', N'2022-07-10 20:26:36', N'1', N'2025-05-03 09:57:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2000, N'商品中心', N'', 1, 60, 2362, N'product', N'fa:product-hunt', NULL, NULL, 0, N'1', N'1', N'1', N'', N'2022-07-29 15:53:53', N'1', N'2023-09-30 11:52:36', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2002, N'商品分类', N'', 2, 2, 2000, N'category', N'ep:cellphone', N'mall/product/category/index', N'ProductCategory', 0, N'1', N'1', N'1', N'', N'2022-07-29 15:53:53', N'1', N'2023-08-21 10:27:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2003, N'分类查询', N'product:category:query', 3, 1, 2002, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-29 15:53:53', N'', N'2022-07-29 15:53:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2004, N'分类创建', N'product:category:create', 3, 2, 2002, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-29 15:53:53', N'', N'2022-07-29 15:53:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2005, N'分类更新', N'product:category:update', 3, 3, 2002, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-29 15:53:53', N'', N'2022-07-29 15:53:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2006, N'分类删除', N'product:category:delete', 3, 4, 2002, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-29 15:53:53', N'', N'2022-07-29 15:53:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2008, N'商品品牌', N'', 2, 3, 2000, N'brand', N'ep:chicken', N'mall/product/brand/index', N'ProductBrand', 0, N'1', N'1', N'1', N'', N'2022-07-30 13:52:44', N'1', N'2023-08-21 10:27:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2009, N'品牌查询', N'product:brand:query', 3, 1, 2008, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-30 13:52:44', N'', N'2022-07-30 13:52:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2010, N'品牌创建', N'product:brand:create', 3, 2, 2008, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-30 13:52:44', N'', N'2022-07-30 13:52:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2011, N'品牌更新', N'product:brand:update', 3, 3, 2008, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-30 13:52:44', N'', N'2022-07-30 13:52:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2012, N'品牌删除', N'product:brand:delete', 3, 4, 2008, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-30 13:52:44', N'', N'2022-07-30 13:52:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2014, N'商品列表', N'', 2, 1, 2000, N'spu', N'ep:apple', N'mall/product/spu/index', N'ProductSpu', 0, N'1', N'1', N'1', N'', N'2022-07-30 14:22:58', N'1', N'2023-08-21 10:27:01', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2015, N'商品查询', N'product:spu:query', 3, 1, 2014, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-30 14:22:58', N'', N'2022-07-30 14:22:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2016, N'商品创建', N'product:spu:create', 3, 2, 2014, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-30 14:22:58', N'', N'2022-07-30 14:22:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2017, N'商品更新', N'product:spu:update', 3, 3, 2014, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-30 14:22:58', N'', N'2022-07-30 14:22:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2018, N'商品删除', N'product:spu:delete', 3, 4, 2014, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-30 14:22:58', N'', N'2022-07-30 14:22:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2019, N'商品属性', N'', 2, 4, 2000, N'property', N'ep:cold-drink', N'mall/product/property/index', N'ProductProperty', 0, N'1', N'1', N'1', N'', N'2022-08-01 14:55:35', N'1', N'2023-08-26 11:01:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2020, N'规格查询', N'product:property:query', 3, 1, 2019, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-08-01 14:55:35', N'', N'2022-12-12 20:26:24', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2021, N'规格创建', N'product:property:create', 3, 2, 2019, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-08-01 14:55:35', N'', N'2022-12-12 20:26:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2022, N'规格更新', N'product:property:update', 3, 3, 2019, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-08-01 14:55:35', N'', N'2022-12-12 20:26:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2023, N'规格删除', N'product:property:delete', 3, 4, 2019, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-08-01 14:55:35', N'', N'2022-12-12 20:26:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2025, N'Banner', N'', 2, 100, 2387, N'banner', N'fa:bandcamp', N'mall/promotion/banner/index', NULL, 0, N'1', N'1', N'1', N'', N'2022-08-01 14:56:14', N'1', N'2023-10-24 20:20:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2026, N'Banner查询', N'promotion:banner:query', 3, 1, 2025, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2022-08-01 14:56:14', N'1', N'2023-10-24 20:20:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2027, N'Banner创建', N'promotion:banner:create', 3, 2, 2025, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2022-08-01 14:56:14', N'1', N'2023-10-24 20:20:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2028, N'Banner更新', N'promotion:banner:update', 3, 3, 2025, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2022-08-01 14:56:14', N'1', N'2023-10-24 20:20:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2029, N'Banner删除', N'promotion:banner:delete', 3, 4, 2025, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2022-08-01 14:56:14', N'1', N'2023-10-24 20:20:36', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2030, N'营销中心', N'', 1, 70, 2362, N'promotion', N'ep:present', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2022-10-31 21:25:09', N'1', N'2023-09-30 11:54:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2032, N'优惠劵列表', N'', 2, 1, 2365, N'template', N'ep:discount', N'mall/promotion/coupon/template/index', N'PromotionCouponTemplate', 0, N'1', N'1', N'1', N'', N'2022-10-31 22:27:14', N'1', N'2023-10-03 12:40:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2033, N'优惠劵模板查询', N'promotion:coupon-template:query', 3, 1, 2032, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-10-31 22:27:14', N'', N'2022-10-31 22:27:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2034, N'优惠劵模板创建', N'promotion:coupon-template:create', 3, 2, 2032, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-10-31 22:27:14', N'', N'2022-10-31 22:27:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2035, N'优惠劵模板更新', N'promotion:coupon-template:update', 3, 3, 2032, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-10-31 22:27:14', N'', N'2022-10-31 22:27:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2036, N'优惠劵模板删除', N'promotion:coupon-template:delete', 3, 4, 2032, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-10-31 22:27:14', N'', N'2022-10-31 22:27:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2038, N'领取记录', N'', 2, 2, 2365, N'list', N'ep:collection-tag', N'mall/promotion/coupon/index', N'PromotionCoupon', 0, N'1', N'1', N'1', N'', N'2022-11-03 23:21:31', N'1', N'2023-10-03 12:55:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2039, N'优惠劵查询', N'promotion:coupon:query', 3, 1, 2038, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-03 23:21:31', N'', N'2022-11-03 23:21:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2040, N'优惠劵删除', N'promotion:coupon:delete', 3, 4, 2038, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-03 23:21:31', N'', N'2022-11-03 23:21:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2041, N'满减送', N'', 2, 10, 2390, N'reward-activity', N'ep:goblet-square-full', N'mall/promotion/rewardActivity/index', N'PromotionRewardActivity', 0, N'1', N'1', N'1', N'', N'2022-11-04 23:47:49', N'1', N'2023-10-21 19:24:46', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2042, N'满减送活动查询', N'promotion:reward-activity:query', 3, 1, 2041, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-04 23:47:49', N'', N'2022-11-04 23:47:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2043, N'满减送活动创建', N'promotion:reward-activity:create', 3, 2, 2041, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-04 23:47:49', N'', N'2022-11-04 23:47:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2044, N'满减送活动更新', N'promotion:reward-activity:update', 3, 3, 2041, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-04 23:47:50', N'', N'2022-11-04 23:47:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2045, N'满减送活动删除', N'promotion:reward-activity:delete', 3, 4, 2041, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-04 23:47:50', N'', N'2022-11-04 23:47:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2046, N'满减送活动关闭', N'promotion:reward-activity:close', 3, 5, 2041, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2022-11-05 10:42:53', N'1', N'2022-11-05 10:42:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2047, N'限时折扣', N'', 2, 7, 2390, N'discount-activity', N'ep:timer', N'mall/promotion/discountActivity/index', N'PromotionDiscountActivity', 0, N'1', N'1', N'1', N'', N'2022-11-05 17:12:15', N'1', N'2023-10-21 19:24:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2048, N'限时折扣活动查询', N'promotion:discount-activity:query', 3, 1, 2047, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-05 17:12:15', N'', N'2022-11-05 17:12:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2049, N'限时折扣活动创建', N'promotion:discount-activity:create', 3, 2, 2047, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-05 17:12:15', N'', N'2022-11-05 17:12:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2050, N'限时折扣活动更新', N'promotion:discount-activity:update', 3, 3, 2047, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-05 17:12:16', N'', N'2022-11-05 17:12:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2051, N'限时折扣活动删除', N'promotion:discount-activity:delete', 3, 4, 2047, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-05 17:12:16', N'', N'2022-11-05 17:12:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2052, N'限时折扣活动关闭', N'promotion:discount-activity:close', 3, 5, 2047, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-05 17:12:16', N'', N'2022-11-05 17:12:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2059, N'秒杀商品', N'', 2, 2, 2209, N'activity', N'ep:basketball', N'mall/promotion/seckill/activity/index', N'PromotionSeckillActivity', 0, N'1', N'1', N'1', N'', N'2022-11-06 22:24:49', N'1', N'2023-06-24 18:57:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2060, N'秒杀活动查询', N'promotion:seckill-activity:query', 3, 1, 2059, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-06 22:24:49', N'', N'2022-11-06 22:24:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2061, N'秒杀活动创建', N'promotion:seckill-activity:create', 3, 2, 2059, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-06 22:24:49', N'', N'2022-11-06 22:24:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2062, N'秒杀活动更新', N'promotion:seckill-activity:update', 3, 3, 2059, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-06 22:24:49', N'', N'2022-11-06 22:24:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2063, N'秒杀活动删除', N'promotion:seckill-activity:delete', 3, 4, 2059, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-06 22:24:49', N'', N'2022-11-06 22:24:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2066, N'秒杀时段', N'', 2, 1, 2209, N'config', N'ep:baseball', N'mall/promotion/seckill/config/index', N'PromotionSeckillConfig', 0, N'1', N'1', N'1', N'', N'2022-11-15 19:46:50', N'1', N'2023-06-24 18:57:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2067, N'秒杀时段查询', N'promotion:seckill-config:query', 3, 1, 2066, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2022-11-15 19:46:51', N'1', N'2023-06-24 17:50:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2068, N'秒杀时段创建', N'promotion:seckill-config:create', 3, 2, 2066, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2022-11-15 19:46:51', N'1', N'2023-06-24 17:48:39', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2069, N'秒杀时段更新', N'promotion:seckill-config:update', 3, 3, 2066, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2022-11-15 19:46:51', N'1', N'2023-06-24 17:50:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2070, N'秒杀时段删除', N'promotion:seckill-config:delete', 3, 4, 2066, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2022-11-15 19:46:51', N'1', N'2023-06-24 17:50:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2072, N'订单中心', N'', 1, 65, 2362, N'trade', N'ep:eleme', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2022-11-19 18:57:19', N'1', N'2023-09-30 11:54:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2073, N'售后退款', N'', 2, 2, 2072, N'after-sale', N'ep:refrigerator', N'mall/trade/afterSale/index', N'TradeAfterSale', 0, N'1', N'1', N'1', N'', N'2022-11-19 20:15:32', N'1', N'2023-10-01 21:42:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2074, N'售后查询', N'trade:after-sale:query', 3, 1, 2073, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-11-19 20:15:33', N'1', N'2022-12-10 21:04:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2075, N'秒杀活动关闭', N'promotion:seckill-activity:close', 3, 5, 2059, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2022-11-28 20:20:15', N'1', N'2023-10-03 18:34:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2076, N'订单列表', N'', 2, 1, 2072, N'order', N'ep:list', N'mall/trade/order/index', N'TradeOrder', 0, N'1', N'1', N'1', N'1', N'2022-12-10 21:05:44', N'1', N'2023-10-01 21:42:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2083, N'地区管理', N'', 2, 14, 1, N'area', N'fa:map-marker', N'system/area/index', N'SystemArea', 0, N'1', N'1', N'1', N'1', N'2022-12-23 17:35:05', N'1', N'2024-02-29 08:50:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2084, N'公众号管理', N'', 1, 100, 0, N'/mp', N'ep:compass', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-01 20:11:04', N'1', N'2024-02-29 12:39:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2085, N'账号管理', N'', 2, 1, 2084, N'account', N'fa:user', N'mp/account/index', N'MpAccount', 0, N'1', N'1', N'1', N'1', N'2023-01-01 20:13:31', N'1', N'2024-02-29 12:42:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2086, N'新增账号', N'mp:account:create', 3, 1, 2085, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-01 20:21:40', N'1', N'2023-01-07 17:32:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2087, N'修改账号', N'mp:account:update', 3, 2, 2085, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-07 17:32:46', N'1', N'2023-01-07 17:32:46', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2088, N'查询账号', N'mp:account:query', 3, 0, 2085, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-07 17:33:07', N'1', N'2023-01-07 17:33:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2089, N'删除账号', N'mp:account:delete', 3, 3, 2085, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-07 17:33:21', N'1', N'2023-01-07 17:33:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2090, N'生成二维码', N'mp:account:qr-code', 3, 4, 2085, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-07 17:33:58', N'1', N'2023-01-07 17:33:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2091, N'清空 API 配额', N'mp:account:clear-quota', 3, 5, 2085, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-07 18:20:32', N'1', N'2023-01-07 18:20:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2092, N'数据统计', N'mp:statistics:query', 2, 2, 2084, N'statistics', N'ep:trend-charts', N'mp/statistics/index', N'MpStatistics', 0, N'1', N'1', N'1', N'1', N'2023-01-07 20:17:36', N'1', N'2024-02-29 12:42:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2093, N'标签管理', N'', 2, 3, 2084, N'tag', N'ep:collection-tag', N'mp/tag/index', N'MpTag', 0, N'1', N'1', N'1', N'1', N'2023-01-08 11:37:32', N'1', N'2024-02-29 12:42:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2094, N'查询标签', N'mp:tag:query', 3, 0, 2093, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-08 11:59:03', N'1', N'2023-01-08 11:59:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2095, N'新增标签', N'mp:tag:create', 3, 1, 2093, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-08 11:59:23', N'1', N'2023-01-08 11:59:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2096, N'修改标签', N'mp:tag:update', 3, 2, 2093, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-08 11:59:41', N'1', N'2023-01-08 11:59:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2097, N'删除标签', N'mp:tag:delete', 3, 3, 2093, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-08 12:00:04', N'1', N'2023-01-08 12:00:13', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2098, N'同步标签', N'mp:tag:sync', 3, 4, 2093, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-08 12:00:29', N'1', N'2023-01-08 12:00:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2099, N'粉丝管理', N'', 2, 4, 2084, N'user', N'fa:user-secret', N'mp/user/index', N'MpUser', 0, N'1', N'1', N'1', N'1', N'2023-01-08 16:51:20', N'1', N'2024-02-29 12:42:39', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2100, N'查询粉丝', N'mp:user:query', 3, 0, 2099, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-08 17:16:59', N'1', N'2023-01-08 17:17:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2101, N'修改粉丝', N'mp:user:update', 3, 1, 2099, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-08 17:17:11', N'1', N'2023-01-08 17:17:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2102, N'同步粉丝', N'mp:user:sync', 3, 2, 2099, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-08 17:17:40', N'1', N'2023-01-08 17:17:40', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2103, N'消息管理', N'', 2, 5, 2084, N'message', N'ep:message', N'mp/message/index', N'MpMessage', 0, N'1', N'1', N'1', N'1', N'2023-01-08 18:44:19', N'1', N'2024-02-29 12:42:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2104, N'图文发表记录', N'', 2, 10, 2084, N'free-publish', N'ep:edit-pen', N'mp/freePublish/index', N'MpFreePublish', 0, N'1', N'1', N'1', N'1', N'2023-01-13 00:30:50', N'1', N'2024-02-29 12:43:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2105, N'查询发布列表', N'mp:free-publish:query', 3, 1, 2104, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-13 07:19:17', N'1', N'2023-01-13 07:19:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2106, N'发布草稿', N'mp:free-publish:submit', 3, 2, 2104, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-13 07:19:46', N'1', N'2023-01-13 07:19:46', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2107, N'删除发布记录', N'mp:free-publish:delete', 3, 3, 2104, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-13 07:20:01', N'1', N'2023-01-13 07:20:01', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2108, N'图文草稿箱', N'', 2, 9, 2084, N'draft', N'ep:edit', N'mp/draft/index', N'MpDraft', 0, N'1', N'1', N'1', N'1', N'2023-01-13 07:40:21', N'1', N'2024-02-29 12:43:26', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2109, N'新建草稿', N'mp:draft:create', 3, 1, 2108, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-13 23:15:30', N'1', N'2023-01-13 23:15:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2110, N'修改草稿', N'mp:draft:update', 3, 2, 2108, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-14 10:08:47', N'1', N'2023-01-14 10:08:47', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2111, N'查询草稿', N'mp:draft:query', 3, 0, 2108, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-14 10:09:01', N'1', N'2023-01-14 10:09:01', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2112, N'删除草稿', N'mp:draft:delete', 3, 3, 2108, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-14 10:09:19', N'1', N'2023-01-14 10:09:19', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2113, N'素材管理', N'', 2, 8, 2084, N'material', N'ep:basketball', N'mp/material/index', N'MpMaterial', 0, N'1', N'1', N'1', N'1', N'2023-01-14 14:12:07', N'1', N'2024-02-29 12:43:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2114, N'上传临时素材', N'mp:material:upload-temporary', 3, 1, 2113, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-14 15:33:55', N'1', N'2023-01-14 15:33:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2115, N'上传永久素材', N'mp:material:upload-permanent', 3, 2, 2113, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-14 15:34:14', N'1', N'2023-01-14 15:34:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2116, N'删除素材', N'mp:material:delete', 3, 3, 2113, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-14 15:35:37', N'1', N'2023-01-14 15:35:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2117, N'上传图文图片', N'mp:material:upload-news-image', 3, 4, 2113, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-14 15:36:31', N'1', N'2023-01-14 15:36:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2118, N'查询素材', N'mp:material:query', 3, 5, 2113, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-14 15:39:22', N'1', N'2023-01-14 15:39:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2119, N'菜单管理', N'', 2, 6, 2084, N'menu', N'ep:menu', N'mp/menu/index', N'MpMenu', 0, N'1', N'1', N'1', N'1', N'2023-01-14 17:43:54', N'1', N'2025-04-01 20:21:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2120, N'自动回复', N'', 2, 7, 2084, N'auto-reply', N'fa-solid:republican', N'mp/autoReply/index', N'MpAutoReply', 0, N'1', N'1', N'1', N'1', N'2023-01-15 22:13:09', N'1', N'2024-02-29 12:43:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2121, N'查询回复', N'mp:auto-reply:query', 3, 0, 2120, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-16 22:28:41', N'1', N'2023-01-16 22:28:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2122, N'新增回复', N'mp:auto-reply:create', 3, 1, 2120, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-16 22:28:54', N'1', N'2023-01-16 22:28:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2123, N'修改回复', N'mp:auto-reply:update', 3, 2, 2120, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-16 22:29:05', N'1', N'2023-01-16 22:29:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2124, N'删除回复', N'mp:auto-reply:delete', 3, 3, 2120, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-16 22:29:34', N'1', N'2023-01-16 22:29:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2125, N'查询菜单', N'mp:menu:query', 3, 0, 2119, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-17 23:05:41', N'1', N'2023-01-17 23:05:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2126, N'保存菜单', N'mp:menu:save', 3, 1, 2119, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-17 23:06:01', N'1', N'2023-01-17 23:06:01', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2127, N'删除菜单', N'mp:menu:delete', 3, 2, 2119, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-17 23:06:16', N'1', N'2023-01-17 23:06:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2128, N'查询消息', N'mp:message:query', 3, 0, 2103, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-17 23:07:14', N'1', N'2023-01-17 23:07:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2129, N'发送消息', N'mp:message:send', 3, 1, 2103, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-17 23:07:26', N'1', N'2023-01-17 23:07:26', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2130, N'邮箱管理', N'', 2, 2, 2739, N'mail', N'fa-solid:mail-bulk', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-25 17:27:44', N'1', N'2024-04-22 23:56:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2131, N'邮箱账号', N'', 2, 0, 2130, N'mail-account', N'fa:universal-access', N'system/mail/account/index', N'SystemMailAccount', 0, N'1', N'1', N'1', N'', N'2023-01-25 09:33:48', N'1', N'2024-02-29 08:48:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2132, N'账号查询', N'system:mail-account:query', 3, 1, 2131, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-25 09:33:48', N'', N'2023-01-25 09:33:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2133, N'账号创建', N'system:mail-account:create', 3, 2, 2131, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-25 09:33:48', N'', N'2023-01-25 09:33:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2134, N'账号更新', N'system:mail-account:update', 3, 3, 2131, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-25 09:33:48', N'', N'2023-01-25 09:33:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2135, N'账号删除', N'system:mail-account:delete', 3, 4, 2131, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-25 09:33:48', N'', N'2023-01-25 09:33:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2136, N'邮件模版', N'', 2, 0, 2130, N'mail-template', N'fa:tag', N'system/mail/template/index', N'SystemMailTemplate', 0, N'1', N'1', N'1', N'', N'2023-01-25 12:05:31', N'1', N'2024-02-29 08:48:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2137, N'模版查询', N'system:mail-template:query', 3, 1, 2136, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-25 12:05:31', N'', N'2023-01-25 12:05:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2138, N'模版创建', N'system:mail-template:create', 3, 2, 2136, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-25 12:05:31', N'', N'2023-01-25 12:05:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2139, N'模版更新', N'system:mail-template:update', 3, 3, 2136, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-25 12:05:31', N'', N'2023-01-25 12:05:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2140, N'模版删除', N'system:mail-template:delete', 3, 4, 2136, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-25 12:05:31', N'', N'2023-01-25 12:05:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2141, N'邮件记录', N'', 2, 0, 2130, N'mail-log', N'fa:edit', N'system/mail/log/index', N'SystemMailLog', 0, N'1', N'1', N'1', N'', N'2023-01-26 02:16:50', N'1', N'2024-02-29 08:48:51', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2142, N'日志查询', N'system:mail-log:query', 3, 1, 2141, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-26 02:16:50', N'', N'2023-01-26 02:16:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2143, N'发送测试邮件', N'system:mail-template:send-mail', 3, 5, 2136, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-26 23:29:15', N'1', N'2023-01-26 23:29:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2144, N'站内信管理', N'', 1, 3, 2739, N'notify', N'ep:message-box', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-28 10:25:18', N'1', N'2024-04-22 23:56:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2145, N'模板管理', N'', 2, 0, 2144, N'notify-template', N'fa:archive', N'system/notify/template/index', N'SystemNotifyTemplate', 0, N'1', N'1', N'1', N'', N'2023-01-28 02:26:42', N'1', N'2024-02-29 08:49:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2146, N'站内信模板查询', N'system:notify-template:query', 3, 1, 2145, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-28 02:26:42', N'', N'2023-01-28 02:26:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2147, N'站内信模板创建', N'system:notify-template:create', 3, 2, 2145, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-28 02:26:42', N'', N'2023-01-28 02:26:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2148, N'站内信模板更新', N'system:notify-template:update', 3, 3, 2145, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-28 02:26:42', N'', N'2023-01-28 02:26:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2149, N'站内信模板删除', N'system:notify-template:delete', 3, 4, 2145, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-28 02:26:42', N'', N'2023-01-28 02:26:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2150, N'发送测试站内信', N'system:notify-template:send-notify', 3, 5, 2145, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-01-28 10:54:43', N'1', N'2023-01-28 10:54:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2151, N'消息记录', N'', 2, 0, 2144, N'notify-message', N'fa:edit', N'system/notify/message/index', N'SystemNotifyMessage', 0, N'1', N'1', N'1', N'', N'2023-01-28 04:28:22', N'1', N'2024-02-29 08:49:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2152, N'站内信消息查询', N'system:notify-message:query', 3, 1, 2151, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-01-28 04:28:22', N'', N'2023-01-28 04:28:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2153, N'大屏设计器', N'', 2, 2, 1281, N'go-view', N'fa:area-chart', N'report/goview/index', N'GoView', 0, N'1', N'1', N'1', N'1', N'2023-02-07 00:03:19', N'1', N'2025-05-03 09:57:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2154, N'创建项目', N'report:go-view-project:create', 3, 1, 2153, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-02-07 19:25:14', N'1', N'2023-02-07 19:25:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2155, N'更新项目', N'report:go-view-project:update', 3, 2, 2153, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-02-07 19:25:34', N'1', N'2024-04-24 20:01:18', N'0')\nGO\nINSERT INTO system_menu (id, name, 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, N'查询项目', N'report:go-view-project:query', 3, 0, 2153, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-02-07 19:25:53', N'1', N'2023-02-07 19:25:53', N'0')\nGO\nINSERT INTO system_menu (id, name, 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, N'使用 SQL 查询数据', N'report:go-view-data:get-by-sql', 3, 3, 2153, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-02-07 19:26:15', N'1', N'2023-02-07 19:26:15', N'0')\nGO\nINSERT INTO system_menu (id, name, 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, N'使用 HTTP 查询数据', N'report:go-view-data:get-by-http', 3, 4, 2153, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'1', N'2023-02-07 19:26:35', N'1', N'2023-02-07 19:26:35', N'0')\nGO\nINSERT INTO system_menu (id, name, 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, N'Boot 开发文档', N'', 1, 1, 0, N'https://doc.iocoder.cn/', N'ep:document', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2023-02-10 22:46:28', N'1', N'2024-07-28 11:36:48', N'0')\nGO\nINSERT INTO system_menu (id, name, 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, N'Cloud 开发文档', N'', 1, 2, 0, N'https://cloud.iocoder.cn', N'ep:document-copy', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2023-02-10 22:47:07', N'1', N'2023-12-02 21:32:29', N'0')\nGO\nINSERT INTO system_menu (id, name, 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, N'接入示例', N'', 1, 99, 1117, N'demo', N'fa-solid:dragon', N'pay/demo/index', NULL, 0, N'1', N'1', N'1', N'', N'2023-02-11 14:21:42', N'1', N'2024-01-18 23:50:00', N'0')\nGO\nINSERT INTO system_menu (id, name, 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, N'商品导出', N'product:spu:export', 3, 5, 2014, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2022-07-30 14:22:58', N'', N'2022-07-30 14:22:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2164, N'配送管理', N'', 1, 3, 2072, N'delivery', N'ep:shopping-cart', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-05-18 09:18:02', N'1', N'2023-09-28 10:58:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2165, N'快递发货', N'', 1, 0, 2164, N'express', N'ep:bicycle', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-05-18 09:22:06', N'1', N'2023-08-30 21:02:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2166, N'门店自提', N'', 1, 1, 2164, N'pick-up-store', N'ep:add-location', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-05-18 09:23:14', N'1', N'2023-08-30 21:03:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2167, N'快递公司', N'', 2, 0, 2165, N'express', N'ep:compass', N'mall/trade/delivery/express/index', N'Express', 0, N'1', N'1', N'1', N'1', N'2023-05-18 09:27:21', N'1', N'2024-11-29 11:20:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2168, N'快递公司查询', N'trade:delivery:express:query', 3, 1, 2167, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-18 09:37:53', N'', N'2023-05-18 09:37:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2169, N'快递公司创建', N'trade:delivery:express:create', 3, 2, 2167, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-18 09:37:53', N'', N'2023-05-18 09:37:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2170, N'快递公司更新', N'trade:delivery:express:update', 3, 3, 2167, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-18 09:37:53', N'', N'2023-05-18 09:37:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2171, N'快递公司删除', N'trade:delivery:express:delete', 3, 4, 2167, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-18 09:37:53', N'', N'2023-05-18 09:37:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2172, N'快递公司导出', N'trade:delivery:express:export', 3, 5, 2167, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-18 09:37:53', N'', N'2023-05-18 09:37:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2173, N'运费模版', N'trade:delivery:express-template:query', 2, 1, 2165, N'express-template', N'ep:coordinate', N'mall/trade/delivery/expressTemplate/index', N'ExpressTemplate', 0, N'1', N'1', N'1', N'1', N'2023-05-20 06:48:10', N'1', N'2023-08-30 21:03:13', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2174, N'快递运费模板查询', N'trade:delivery:express-template:query', 3, 1, 2173, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-20 06:49:53', N'', N'2023-05-20 06:49:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2175, N'快递运费模板创建', N'trade:delivery:express-template:create', 3, 2, 2173, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-20 06:49:53', N'', N'2023-05-20 06:49:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2176, N'快递运费模板更新', N'trade:delivery:express-template:update', 3, 3, 2173, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-20 06:49:53', N'', N'2023-05-20 06:49:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2177, N'快递运费模板删除', N'trade:delivery:express-template:delete', 3, 4, 2173, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-20 06:49:53', N'', N'2023-05-20 06:49:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2178, N'快递运费模板导出', N'trade:delivery:express-template:export', 3, 5, 2173, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-20 06:49:53', N'', N'2023-05-20 06:49:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2179, N'门店管理', N'', 2, 1, 2166, N'pick-up-store', N'ep:basketball', N'mall/trade/delivery/pickUpStore/index', N'PickUpStore', 0, N'1', N'1', N'1', N'1', N'2023-05-25 10:50:00', N'1', N'2023-08-30 21:03:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2180, N'自提门店查询', N'trade:delivery:pick-up-store:query', 3, 1, 2179, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-25 10:53:29', N'', N'2023-05-25 10:53:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2181, N'自提门店创建', N'trade:delivery:pick-up-store:create', 3, 2, 2179, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-25 10:53:29', N'', N'2023-05-25 10:53:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2182, N'自提门店更新', N'trade:delivery:pick-up-store:update', 3, 3, 2179, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-25 10:53:29', N'', N'2023-05-25 10:53:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2183, N'自提门店删除', N'trade:delivery:pick-up-store:delete', 3, 4, 2179, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-25 10:53:29', N'', N'2023-05-25 10:53:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2184, N'自提门店导出', N'trade:delivery:pick-up-store:export', 3, 5, 2179, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-05-25 10:53:29', N'', N'2023-05-25 10:53:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2209, N'秒杀活动', N'', 2, 3, 2030, N'seckill', N'ep:place', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-06-24 17:39:13', N'1', N'2023-06-24 18:55:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2262, N'会员中心', N'', 1, 55, 0, N'/member', N'ep:bicycle', NULL, NULL, 0, N'1', N'1', N'1', N'1', N'2023-06-10 00:42:03', N'1', N'2023-08-20 09:23:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2275, N'会员配置', N'', 2, 0, 2262, N'config', N'fa:archive', N'member/config/index', N'MemberConfig', 0, N'1', N'1', N'1', N'', N'2023-06-10 02:07:44', N'1', N'2023-10-01 23:41:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2276, N'会员配置查询', N'member:config:query', 3, 1, 2275, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2023-06-10 02:07:44', N'1', N'2024-04-24 19:48:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2277, N'会员配置保存', N'member:config:save', 3, 2, 2275, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2023-06-10 02:07:44', N'1', N'2024-04-24 19:49:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2281, N'签到配置', N'', 2, 2, 2300, N'config', N'ep:calendar', N'member/signin/config/index', N'SignInConfig', 0, N'1', N'1', N'1', N'', N'2023-06-10 03:26:12', N'1', N'2023-08-20 19:25:51', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2282, N'积分签到规则查询', N'point:sign-in-config:query', 3, 1, 2281, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-06-10 03:26:12', N'', N'2023-06-10 03:26:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2283, N'积分签到规则创建', N'point:sign-in-config:create', 3, 2, 2281, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-06-10 03:26:12', N'', N'2023-06-10 03:26:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2284, N'积分签到规则更新', N'point:sign-in-config:update', 3, 3, 2281, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-06-10 03:26:12', N'', N'2023-06-10 03:26:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2285, N'积分签到规则删除', N'point:sign-in-config:delete', 3, 4, 2281, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-06-10 03:26:12', N'', N'2023-06-10 03:26:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2287, N'会员积分', N'', 2, 10, 2262, N'record', N'fa:asterisk', N'member/point/record/index', N'PointRecord', 0, N'1', N'1', N'1', N'', N'2023-06-10 04:18:50', N'1', N'2023-10-01 23:42:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2288, N'用户积分记录查询', N'point:record:query', 3, 1, 2287, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-06-10 04:18:50', N'', N'2023-06-10 04:18:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2293, N'签到记录', N'', 2, 3, 2300, N'record', N'ep:chicken', N'member/signin/record/index', N'SignInRecord', 0, N'1', N'1', N'1', N'', N'2023-06-10 04:48:22', N'1', N'2023-08-20 19:26:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2294, N'用户签到积分查询', N'point:sign-in-record:query', 3, 1, 2293, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-06-10 04:48:22', N'', N'2023-06-10 04:48:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2297, N'用户签到积分删除', N'point:sign-in-record:delete', 3, 4, 2293, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-06-10 04:48:22', N'', N'2023-06-10 04:48:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2300, N'会员签到', N'', 1, 11, 2262, N'signin', N'ep:alarm-clock', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-06-27 22:49:53', N'1', N'2023-08-20 09:23:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2301, N'回调通知', N'', 2, 5, 1117, N'notify', N'ep:mute-notification', N'pay/notify/index', N'PayNotify', 0, N'1', N'1', N'1', N'', N'2023-07-20 04:41:32', N'1', N'2024-01-18 23:56:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2302, N'支付通知查询', N'pay:notify:query', 3, 1, 2301, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-07-20 04:41:32', N'', N'2023-07-20 04:41:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2303, N'拼团活动', N'', 2, 3, 2030, N'combination', N'fa:group', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-12 17:19:54', N'1', N'2023-08-12 17:20:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2304, N'拼团商品', N'', 2, 1, 2303, N'acitivity', N'ep:apple', N'mall/promotion/combination/activity/index', N'PromotionCombinationActivity', 0, N'1', N'1', N'1', N'1', N'2023-08-12 17:22:03', N'1', N'2023-08-12 17:22:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2305, N'拼团活动查询', N'promotion:combination-activity:query', 3, 1, 2304, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-12 17:54:32', N'1', N'2023-11-24 11:57:40', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2306, N'拼团活动创建', N'promotion:combination-activity:create', 3, 2, 2304, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-12 17:54:49', N'1', N'2023-08-12 17:54:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2307, N'拼团活动更新', N'promotion:combination-activity:update', 3, 3, 2304, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-12 17:55:04', N'1', N'2023-08-12 17:55:04', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2308, N'拼团活动删除', N'promotion:combination-activity:delete', 3, 4, 2304, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-12 17:55:23', N'1', N'2023-08-12 17:55:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2309, N'拼团活动关闭', N'promotion:combination-activity:close', 3, 5, 2304, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-12 17:55:37', N'1', N'2023-10-06 10:51:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2310, N'砍价活动', N'', 2, 4, 2030, N'bargain', N'ep:box', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-13 00:27:25', N'1', N'2023-08-13 00:27:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2311, N'砍价商品', N'', 2, 1, 2310, N'activity', N'ep:burger', N'mall/promotion/bargain/activity/index', N'PromotionBargainActivity', 0, N'1', N'1', N'1', N'1', N'2023-08-13 00:28:49', N'1', N'2023-10-05 01:16:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2312, N'砍价活动查询', N'promotion:bargain-activity:query', 3, 1, 2311, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-13 00:32:30', N'1', N'2023-08-13 00:32:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2313, N'砍价活动创建', N'promotion:bargain-activity:create', 3, 2, 2311, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-13 00:32:44', N'1', N'2023-08-13 00:32:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2314, N'砍价活动更新', N'promotion:bargain-activity:update', 3, 3, 2311, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-13 00:32:55', N'1', N'2023-08-13 00:32:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2315, N'砍价活动删除', N'promotion:bargain-activity:delete', 3, 4, 2311, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-13 00:34:50', N'1', N'2023-08-13 00:34:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2316, N'砍价活动关闭', N'promotion:bargain-activity:close', 3, 5, 2311, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-13 00:35:02', N'1', N'2023-08-13 00:35:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2317, N'会员管理', N'', 2, 0, 2262, N'user', N'ep:avatar', N'member/user/index', N'MemberUser', 0, N'1', N'1', N'1', N'', N'2023-08-19 04:12:15', N'1', N'2023-08-24 00:50:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2318, N'会员用户查询', N'member:user:query', 3, 1, 2317, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-19 04:12:15', N'', N'2023-08-19 04:12:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2319, N'会员用户更新', N'member:user:update', 3, 3, 2317, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-19 04:12:15', N'', N'2023-08-19 04:12:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2320, N'会员标签', N'', 2, 1, 2262, N'tag', N'ep:collection-tag', N'member/tag/index', N'MemberTag', 0, N'1', N'1', N'1', N'', N'2023-08-20 01:03:08', N'1', N'2023-08-20 09:23:19', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2321, N'会员标签查询', N'member:tag:query', 3, 1, 2320, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-20 01:03:08', N'', N'2023-08-20 01:03:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2322, N'会员标签创建', N'member:tag:create', 3, 2, 2320, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-20 01:03:08', N'', N'2023-08-20 01:03:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2323, N'会员标签更新', N'member:tag:update', 3, 3, 2320, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-20 01:03:08', N'', N'2023-08-20 01:03:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2324, N'会员标签删除', N'member:tag:delete', 3, 4, 2320, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-20 01:03:08', N'', N'2023-08-20 01:03:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2325, N'会员等级', N'', 2, 2, 2262, N'level', N'fa:level-up', N'member/level/index', N'MemberLevel', 0, N'1', N'1', N'1', N'', N'2023-08-22 12:41:01', N'1', N'2023-08-22 21:47:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2326, N'会员等级查询', N'member:level:query', 3, 1, 2325, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-22 12:41:02', N'', N'2023-08-22 12:41:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2327, N'会员等级创建', N'member:level:create', 3, 2, 2325, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-22 12:41:02', N'', N'2023-08-22 12:41:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2328, N'会员等级更新', N'member:level:update', 3, 3, 2325, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-22 12:41:02', N'', N'2023-08-22 12:41:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2329, N'会员等级删除', N'member:level:delete', 3, 4, 2325, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-22 12:41:02', N'', N'2023-08-22 12:41:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2330, N'会员分组', N'', 2, 3, 2262, N'group', N'fa:group', N'member/group/index', N'MemberGroup', 0, N'1', N'1', N'1', N'', N'2023-08-22 13:50:06', N'1', N'2023-10-01 23:42:01', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2331, N'用户分组查询', N'member:group:query', 3, 1, 2330, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-22 13:50:06', N'', N'2023-08-22 13:50:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2332, N'用户分组创建', N'member:group:create', 3, 2, 2330, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-22 13:50:06', N'', N'2023-08-22 13:50:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2333, N'用户分组更新', N'member:group:update', 3, 3, 2330, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-22 13:50:06', N'', N'2023-08-22 13:50:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2334, N'用户分组删除', N'member:group:delete', 3, 4, 2330, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-22 13:50:06', N'', N'2023-08-22 13:50:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2335, N'用户等级修改', N'member:user:update-level', 3, 5, 2317, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-08-23 16:49:05', N'', N'2023-08-23 16:50:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2336, N'商品评论', N'', 2, 5, 2000, N'comment', N'ep:comment', N'mall/product/comment/index', N'ProductComment', 0, N'1', N'1', N'1', N'1', N'2023-08-26 11:03:00', N'1', N'2023-08-26 11:03:38', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2337, N'评论查询', N'product:comment:query', 3, 1, 2336, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-26 11:04:01', N'1', N'2023-08-26 11:04:01', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2338, N'添加自评', N'product:comment:create', 3, 2, 2336, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-26 11:04:23', N'1', N'2023-08-26 11:08:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2339, N'商家回复', N'product:comment:update', 3, 3, 2336, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-26 11:04:37', N'1', N'2023-08-26 11:04:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2340, N'显隐评论', N'product:comment:update', 3, 4, 2336, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-08-26 11:04:55', N'1', N'2023-08-26 11:04:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2341, N'优惠劵发送', N'promotion:coupon:send', 3, 2, 2038, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-09-02 00:03:14', N'1', N'2023-09-02 00:03:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2342, N'交易配置', N'', 2, 0, 2072, N'config', N'ep:setting', N'mall/trade/config/index', N'TradeConfig', 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'1', N'2024-02-26 20:30:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2343, N'交易中心配置查询', N'trade:config:query', 3, 1, 2342, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'', N'2023-09-28 02:46:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2344, N'交易中心配置保存', N'trade:config:save', 3, 2, 2342, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'', N'2023-09-28 02:46:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2345, N'分销管理', N'', 1, 4, 2072, N'brokerage', N'fa-solid:project-diagram', N'', N'', 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'1', N'2023-09-28 10:58:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2346, N'分销用户', N'', 2, 0, 2345, N'brokerage-user', N'fa-solid:user-tie', N'mall/trade/brokerage/user/index', N'TradeBrokerageUser', 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'1', N'2024-02-26 20:33:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2347, N'分销用户查询', N'trade:brokerage-user:query', 3, 1, 2346, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'', N'2023-09-28 02:46:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2348, N'分销用户推广人查询', N'trade:brokerage-user:user-query', 3, 2, 2346, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'', N'2023-09-28 02:46:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2349, N'分销用户推广订单查询', N'trade:brokerage-user:order-query', 3, 3, 2346, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'', N'2023-09-28 02:46:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2350, N'分销用户修改推广资格', N'trade:brokerage-user:update-brokerage-enable', 3, 4, 2346, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'', N'2023-09-28 02:46:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2351, N'修改推广员', N'trade:brokerage-user:update-bind-user', 3, 5, 2346, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'1', N'2024-12-01 14:33:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2352, N'清除推广员', N'trade:brokerage-user:clear-bind-user', 3, 6, 2346, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'1', N'2024-12-01 14:33:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2353, N'佣金记录', N'', 2, 1, 2345, N'brokerage-record', N'fa:money', N'mall/trade/brokerage/record/index', N'TradeBrokerageRecord', 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'1', N'2024-02-26 20:33:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2354, N'佣金记录查询', N'trade:brokerage-record:query', 3, 1, 2353, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'', N'2023-09-28 02:46:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2355, N'佣金提现', N'', 2, 2, 2345, N'brokerage-withdraw', N'fa:credit-card', N'mall/trade/brokerage/withdraw/index', N'TradeBrokerageWithdraw', 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'1', N'2024-02-26 20:33:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2356, N'佣金提现查询', N'trade:brokerage-withdraw:query', 3, 1, 2355, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'', N'2023-09-28 02:46:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2357, N'佣金提现审核', N'trade:brokerage-withdraw:audit', 3, 2, 2355, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-28 02:46:22', N'', N'2023-09-28 02:46:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2358, N'统计中心', N'', 1, 75, 2362, N'statistics', N'ep:data-line', N'', N'', 0, N'1', N'1', N'1', N'', N'2023-09-30 03:22:40', N'1', N'2023-09-30 11:54:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2359, N'交易统计', N'', 2, 4, 2358, N'trade', N'fa-solid:credit-card', N'mall/statistics/trade/index', N'TradeStatistics', 0, N'1', N'1', N'1', N'', N'2023-09-30 03:22:40', N'1', N'2024-02-26 20:42:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2360, N'交易统计查询', N'statistics:trade:query', 3, 1, 2359, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-30 03:22:40', N'', N'2023-09-30 03:22:40', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2361, N'交易统计导出', N'statistics:trade:export', 3, 2, 2359, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-09-30 03:22:40', N'', N'2023-09-30 03:22:40', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2362, N'商城系统', N'', 1, 59, 0, N'/mall', N'ep:shop', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-09-30 11:52:02', N'1', N'2023-09-30 11:52:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2363, N'用户积分修改', N'member:user:update-point', 3, 6, 2317, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-01 14:39:43', N'', N'2023-10-01 14:39:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2364, N'用户余额修改', N'pay:wallet:update-balance', 3, 7, 2317, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2023-10-01 14:39:43', N'1', N'2024-10-01 09:42:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2365, N'优惠劵', N'', 1, 2, 2030, N'coupon', N'fa-solid:disease', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-10-03 12:39:15', N'1', N'2023-10-05 00:16:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2366, N'砍价记录', N'', 2, 2, 2310, N'record', N'ep:list', N'mall/promotion/bargain/record/index', N'PromotionBargainRecord', 0, N'1', N'1', N'1', N'', N'2023-10-05 02:49:06', N'1', N'2023-10-05 10:50:38', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2367, N'砍价记录查询', N'promotion:bargain-record:query', 3, 1, 2366, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-05 02:49:06', N'', N'2023-10-05 02:49:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2368, N'助力记录查询', N'promotion:bargain-help:query', 3, 2, 2366, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-10-05 12:27:49', N'1', N'2023-10-05 12:27:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2369, N'拼团记录', N'promotion:combination-record:query', 2, 2, 2303, N'record', N'ep:avatar', N'mall/promotion/combination/record/index.vue', N'PromotionCombinationRecord', 0, N'1', N'1', N'1', N'1', N'2023-10-08 07:10:22', N'1', N'2023-10-08 07:34:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2374, N'会员统计', N'', 2, 2, 2358, N'member', N'ep:avatar', N'mall/statistics/member/index', N'MemberStatistics', 0, N'1', N'1', N'1', N'', N'2023-10-11 04:39:24', N'1', N'2024-02-26 20:41:46', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2375, N'会员统计查询', N'statistics:member:query', 3, 1, 2374, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-11 04:39:24', N'', N'2023-10-11 04:39:24', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2376, N'订单核销', N'trade:order:pick-up', 3, 10, 2076, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-10-14 17:11:58', N'1', N'2023-10-14 17:11:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2377, N'文章分类', N'', 2, 0, 2387, N'article/category', N'fa:certificate', N'mall/promotion/article/category/index', N'ArticleCategory', 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'1', N'2023-10-16 09:38:26', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2378, N'分类查询', N'promotion:article-category:query', 3, 1, 2377, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'', N'2023-10-16 01:26:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2379, N'分类创建', N'promotion:article-category:create', 3, 2, 2377, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'', N'2023-10-16 01:26:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2380, N'分类更新', N'promotion:article-category:update', 3, 3, 2377, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'', N'2023-10-16 01:26:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2381, N'分类删除', N'promotion:article-category:delete', 3, 4, 2377, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'', N'2023-10-16 01:26:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2382, N'文章列表', N'', 2, 2, 2387, N'article', N'ep:connection', N'mall/promotion/article/index', N'Article', 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'1', N'2023-10-16 09:41:19', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2383, N'文章管理查询', N'promotion:article:query', 3, 1, 2382, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'', N'2023-10-16 01:26:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2384, N'文章管理创建', N'promotion:article:create', 3, 2, 2382, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'', N'2023-10-16 01:26:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2385, N'文章管理更新', N'promotion:article:update', 3, 3, 2382, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'', N'2023-10-16 01:26:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2386, N'文章管理删除', N'promotion:article:delete', 3, 4, 2382, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-16 01:26:18', N'', N'2023-10-16 01:26:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2387, N'内容管理', N'', 1, 1, 2030, N'content', N'ep:collection', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-10-16 09:37:31', N'1', N'2023-10-16 09:37:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2388, N'商城首页', N'', 2, 1, 2362, N'home', N'ep:home-filled', N'mall/home/index', N'MallHome', 0, N'1', N'1', N'1', N'', N'2023-10-16 12:10:33', N'', N'2023-10-16 12:10:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2389, N'核销订单', N'', 2, 2, 2166, N'pick-up-order', N'ep:list', N'mall/trade/delivery/pickUpOrder/index', N'PickUpOrder', 0, N'1', N'1', N'1', N'', N'2023-10-19 16:09:51', N'', N'2023-10-19 16:09:51', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2390, N'优惠活动', N'', 1, 99, 2030, N'youhui', N'ep:aim', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-10-21 19:23:49', N'1', N'2023-10-21 19:23:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2391, N'客户管理', N'', 2, 10, 2397, N'customer', N'fa:address-book-o', N'crm/customer/index', N'CrmCustomer', 0, N'1', N'1', N'1', N'', N'2023-10-29 09:04:21', N'1', N'2024-02-17 17:13:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2392, N'客户查询', N'crm:customer:query', 3, 1, 2391, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 09:04:21', N'', N'2023-10-29 09:04:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2393, N'客户创建', N'crm:customer:create', 3, 2, 2391, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 09:04:21', N'', N'2023-10-29 09:04:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2394, N'客户更新', N'crm:customer:update', 3, 3, 2391, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 09:04:21', N'', N'2023-10-29 09:04:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2395, N'客户删除', N'crm:customer:delete', 3, 4, 2391, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 09:04:21', N'', N'2023-10-29 09:04:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2396, N'客户导出', N'crm:customer:export', 3, 5, 2391, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 09:04:21', N'', N'2023-10-29 09:04:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2397, N'CRM 系统', N'', 1, 200, 0, N'/crm', N'simple-icons:civicrm', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-10-29 17:08:30', N'1', N'2025-04-19 18:56:38', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2398, N'合同管理', N'', 2, 50, 2397, N'contract', N'ep:notebook', N'crm/contract/index', N'CrmContract', 0, N'1', N'1', N'1', N'', N'2023-10-29 10:50:41', N'1', N'2024-02-17 17:15:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2399, N'合同查询', N'crm:contract:query', 3, 1, 2398, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 10:50:41', N'', N'2023-10-29 10:50:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2400, N'合同创建', N'crm:contract:create', 3, 2, 2398, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 10:50:41', N'', N'2023-10-29 10:50:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2401, N'合同更新', N'crm:contract:update', 3, 3, 2398, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 10:50:41', N'', N'2023-10-29 10:50:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2402, N'合同删除', N'crm:contract:delete', 3, 4, 2398, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 10:50:41', N'', N'2023-10-29 10:50:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2403, N'合同导出', N'crm:contract:export', 3, 5, 2398, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 10:50:41', N'', N'2023-10-29 10:50:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2404, N'线索管理', N'', 2, 8, 2397, N'clue', N'fa:pagelines', N'crm/clue/index', N'CrmClue', 0, N'1', N'1', N'1', N'', N'2023-10-29 11:06:29', N'1', N'2024-02-17 17:15:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2405, N'线索查询', N'crm:clue:query', 3, 1, 2404, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:06:29', N'', N'2023-10-29 11:06:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2406, N'线索创建', N'crm:clue:create', 3, 2, 2404, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:06:29', N'', N'2023-10-29 11:06:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2407, N'线索更新', N'crm:clue:update', 3, 3, 2404, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:06:29', N'', N'2023-10-29 11:06:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2408, N'线索删除', N'crm:clue:delete', 3, 4, 2404, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:06:29', N'', N'2023-10-29 11:06:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2409, N'线索导出', N'crm:clue:export', 3, 5, 2404, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:06:29', N'', N'2023-10-29 11:06:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2410, N'商机管理', N'', 2, 40, 2397, N'business', N'fa:bus', N'crm/business/index', N'CrmBusiness', 0, N'1', N'1', N'1', N'', N'2023-10-29 11:12:35', N'1', N'2024-02-17 17:14:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2411, N'商机查询', N'crm:business:query', 3, 1, 2410, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:12:35', N'', N'2023-10-29 11:12:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2412, N'商机创建', N'crm:business:create', 3, 2, 2410, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:12:35', N'', N'2023-10-29 11:12:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2413, N'商机更新', N'crm:business:update', 3, 3, 2410, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:12:35', N'', N'2023-10-29 11:12:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2414, N'商机删除', N'crm:business:delete', 3, 4, 2410, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:12:35', N'', N'2023-10-29 11:12:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2415, N'商机导出', N'crm:business:export', 3, 5, 2410, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:12:35', N'', N'2023-10-29 11:12:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2416, N'联系人管理', N'', 2, 20, 2397, N'contact', N'fa:address-book-o', N'crm/contact/index', N'CrmContact', 0, N'1', N'1', N'1', N'', N'2023-10-29 11:14:56', N'1', N'2024-02-17 17:13:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2417, N'联系人查询', N'crm:contact:query', 3, 1, 2416, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:14:56', N'', N'2023-10-29 11:14:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2418, N'联系人创建', N'crm:contact:create', 3, 2, 2416, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:14:56', N'', N'2023-10-29 11:14:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2419, N'联系人更新', N'crm:contact:update', 3, 3, 2416, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:14:56', N'', N'2023-10-29 11:14:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2420, N'联系人删除', N'crm:contact:delete', 3, 4, 2416, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:14:56', N'', N'2023-10-29 11:14:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2421, N'联系人导出', N'crm:contact:export', 3, 5, 2416, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:14:56', N'', N'2023-10-29 11:14:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2422, N'回款管理', N'', 2, 60, 2397, N'receivable', N'ep:money', N'crm/receivable/index', N'CrmReceivable', 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'1', N'2024-02-17 17:16:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2423, N'回款管理查询', N'crm:receivable:query', 3, 1, 2422, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2424, N'回款管理创建', N'crm:receivable:create', 3, 2, 2422, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2425, N'回款管理更新', N'crm:receivable:update', 3, 3, 2422, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2426, N'回款管理删除', N'crm:receivable:delete', 3, 4, 2422, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2427, N'回款管理导出', N'crm:receivable:export', 3, 5, 2422, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2428, N'回款计划', N'', 2, 61, 2397, N'receivable-plan', N'fa:money', N'crm/receivable/plan/index', N'CrmReceivablePlan', 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'1', N'2024-02-17 17:16:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2429, N'回款计划查询', N'crm:receivable-plan:query', 3, 1, 2428, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2430, N'回款计划创建', N'crm:receivable-plan:create', 3, 2, 2428, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2431, N'回款计划更新', N'crm:receivable-plan:update', 3, 3, 2428, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2432, N'回款计划删除', N'crm:receivable-plan:delete', 3, 4, 2428, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2433, N'回款计划导出', N'crm:receivable-plan:export', 3, 5, 2428, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 11:18:09', N'', N'2023-10-29 11:18:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2435, N'商城装修', N'', 2, 20, 2030, N'diy-template', N'fa6-solid:brush', N'mall/promotion/diy/template/index', N'', 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:25', N'1', N'2025-03-15 21:34:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2436, N'装修模板', N'', 2, 1, 2435, N'diy-template', N'fa6-solid:brush', N'mall/promotion/diy/template/index', N'DiyTemplate', 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:25', N'', N'2023-10-29 14:19:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2437, N'装修模板查询', N'promotion:diy-template:query', 3, 1, 2436, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:25', N'', N'2023-10-29 14:19:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2438, N'装修模板创建', N'promotion:diy-template:create', 3, 2, 2436, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:25', N'', N'2023-10-29 14:19:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2439, N'装修模板更新', N'promotion:diy-template:update', 3, 3, 2436, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:25', N'', N'2023-10-29 14:19:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2440, N'装修模板删除', N'promotion:diy-template:delete', 3, 4, 2436, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:25', N'', N'2023-10-29 14:19:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2441, N'装修模板使用', N'promotion:diy-template:use', 3, 5, 2436, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:25', N'', N'2023-10-29 14:19:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2442, N'装修页面', N'', 2, 2, 2435, N'diy-page', N'foundation:page-edit', N'mall/promotion/diy/page/index', N'DiyPage', 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:25', N'', N'2023-10-29 14:19:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2443, N'装修页面查询', N'promotion:diy-page:query', 3, 1, 2442, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:25', N'', N'2023-10-29 14:19:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2444, N'装修页面创建', N'promotion:diy-page:create', 3, 2, 2442, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:26', N'', N'2023-10-29 14:19:26', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2445, N'装修页面更新', N'promotion:diy-page:update', 3, 3, 2442, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:26', N'', N'2023-10-29 14:19:26', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2446, N'装修页面删除', N'promotion:diy-page:delete', 3, 4, 2442, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-10-29 14:19:26', N'', N'2023-10-29 14:19:26', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2447, N'三方登录', N'', 1, 10, 1, N'social', N'fa:rocket', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-11-04 12:12:01', N'1', N'2024-02-29 01:14:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2448, N'三方应用', N'', 2, 1, 2447, N'client', N'ep:set-up', N'system/social/client/index.vue', N'SocialClient', 0, N'1', N'1', N'1', N'1', N'2023-11-04 12:17:19', N'1', N'2024-05-04 19:09:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2449, N'三方应用查询', N'system:social-client:query', 3, 1, 2448, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-11-04 12:43:12', N'1', N'2023-11-04 12:43:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2450, N'三方应用创建', N'system:social-client:create', 3, 2, 2448, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-11-04 12:43:58', N'1', N'2023-11-04 12:43:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2451, N'三方应用更新', N'system:social-client:update', 3, 3, 2448, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-11-04 12:44:27', N'1', N'2023-11-04 12:44:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2452, N'三方应用删除', N'system:social-client:delete', 3, 4, 2448, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-11-04 12:44:43', N'1', N'2023-11-04 12:44:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2453, N'三方用户', N'system:social-user:query', 2, 2, 2447, N'user', N'ep:avatar', N'system/social/user/index.vue', N'SocialUser', 0, N'1', N'1', N'1', N'1', N'2023-11-04 14:01:05', N'1', N'2023-11-04 14:01:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2472, N'主子表（内嵌）', N'', 2, 12, 1070, N'demo03-inner', N'fa:power-off', N'infra/demo/demo03/inner/index', N'Demo03StudentInner', 0, N'1', N'1', N'1', N'', N'2023-11-13 04:39:51', N'1', N'2023-11-16 23:53:46', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2478, N'单表（增删改查）', N'', 2, 1, 1070, N'demo01-contact', N'ep:bicycle', N'infra/demo/demo01/index', N'Demo01Contact', 0, N'1', N'1', N'1', N'', N'2023-11-15 14:42:30', N'1', N'2023-11-16 20:34:40', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2479, N'示例联系人查询', N'infra:demo01-contact:query', 3, 1, 2478, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-15 14:42:30', N'', N'2023-11-15 14:42:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2480, N'示例联系人创建', N'infra:demo01-contact:create', 3, 2, 2478, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-15 14:42:30', N'', N'2023-11-15 14:42:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2481, N'示例联系人更新', N'infra:demo01-contact:update', 3, 3, 2478, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-15 14:42:30', N'', N'2023-11-15 14:42:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2482, N'示例联系人删除', N'infra:demo01-contact:delete', 3, 4, 2478, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-15 14:42:30', N'', N'2023-11-15 14:42:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2483, N'示例联系人导出', N'infra:demo01-contact:export', 3, 5, 2478, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-15 14:42:30', N'', N'2023-11-15 14:42:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2484, N'树表（增删改查）', N'', 2, 2, 1070, N'demo02-category', N'fa:tree', N'infra/demo/demo02/index', N'Demo02Category', 0, N'1', N'1', N'1', N'', N'2023-11-16 12:18:27', N'1', N'2023-11-16 20:35:01', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2485, N'示例分类查询', N'infra:demo02-category:query', 3, 1, 2484, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:18:27', N'', N'2023-11-16 12:18:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2486, N'示例分类创建', N'infra:demo02-category:create', 3, 2, 2484, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:18:27', N'', N'2023-11-16 12:18:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2487, N'示例分类更新', N'infra:demo02-category:update', 3, 3, 2484, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:18:27', N'', N'2023-11-16 12:18:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2488, N'示例分类删除', N'infra:demo02-category:delete', 3, 4, 2484, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:18:27', N'', N'2023-11-16 12:18:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2489, N'示例分类导出', N'infra:demo02-category:export', 3, 5, 2484, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:18:27', N'', N'2023-11-16 12:18:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2490, N'主子表（标准）', N'', 2, 10, 1070, N'demo03-normal', N'fa:battery-3', N'infra/demo/demo03/normal/index', N'Demo03StudentNormal', 0, N'1', N'1', N'1', N'', N'2023-11-16 12:53:37', N'1', N'2023-11-16 23:10:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2491, N'学生查询', N'infra:demo03-student:query', 3, 1, 2490, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:53:37', N'', N'2023-11-16 12:53:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2492, N'学生创建', N'infra:demo03-student:create', 3, 2, 2490, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:53:37', N'', N'2023-11-16 12:53:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2493, N'学生更新', N'infra:demo03-student:update', 3, 3, 2490, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:53:37', N'', N'2023-11-16 12:53:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2494, N'学生删除', N'infra:demo03-student:delete', 3, 4, 2490, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:53:37', N'', N'2023-11-16 12:53:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2495, N'学生导出', N'infra:demo03-student:export', 3, 5, 2490, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-16 12:53:37', N'', N'2023-11-16 12:53:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2497, N'主子表（ERP）', N'', 2, 11, 1070, N'demo03-erp', N'ep:calendar', N'infra/demo/demo03/erp/index', N'Demo03StudentERP', 0, N'1', N'1', N'1', N'', N'2023-11-16 15:50:59', N'1', N'2023-11-17 13:19:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2516, N'客户公海配置', N'', 2, 0, 2524, N'customer-pool-config', N'ep:data-analysis', N'crm/customer/poolConfig/index', N'CrmCustomerPoolConfig', 0, N'1', N'1', N'1', N'', N'2023-11-18 13:33:31', N'1', N'2024-01-03 19:52:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2517, N'客户公海配置保存', N'crm:customer-pool-config:update', 3, 1, 2516, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-18 13:33:31', N'', N'2023-11-18 13:33:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2518, N'客户限制配置', N'', 2, 1, 2524, N'customer-limit-config', N'ep:avatar', N'crm/customer/limitConfig/index', N'CrmCustomerLimitConfig', 0, N'1', N'1', N'1', N'', N'2023-11-18 13:33:53', N'1', N'2024-02-24 16:43:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2519, N'客户限制配置查询', N'crm:customer-limit-config:query', 3, 1, 2518, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-18 13:33:53', N'', N'2023-11-18 13:33:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2520, N'客户限制配置创建', N'crm:customer-limit-config:create', 3, 2, 2518, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-18 13:33:53', N'', N'2023-11-18 13:33:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2521, N'客户限制配置更新', N'crm:customer-limit-config:update', 3, 3, 2518, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-18 13:33:53', N'', N'2023-11-18 13:33:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2522, N'客户限制配置删除', N'crm:customer-limit-config:delete', 3, 4, 2518, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-18 13:33:53', N'', N'2023-11-18 13:33:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2523, N'客户限制配置导出', N'crm:customer-limit-config:export', 3, 5, 2518, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-11-18 13:33:53', N'', N'2023-11-18 13:33:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2524, N'系统配置', N'', 1, 999, 2397, N'config', N'ep:connection', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-11-18 21:58:00', N'1', N'2024-02-17 17:14:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2525, N'WebSocket', N'', 2, 5, 2, N'websocket', N'ep:connection', N'infra/webSocket/index', N'InfraWebSocket', 0, N'1', N'1', N'1', N'1', N'2023-11-23 19:41:55', N'1', N'2024-04-23 00:02:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2526, N'产品管理', N'', 2, 80, 2397, N'product', N'fa:product-hunt', N'crm/product/index', N'CrmProduct', 0, N'1', N'1', N'1', N'1', N'2023-12-05 22:45:26', N'1', N'2024-02-20 20:36:20', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2527, N'产品查询', N'crm:product:query', 3, 1, 2526, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-12-05 22:47:16', N'1', N'2023-12-05 22:47:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2528, N'产品创建', N'crm:product:create', 3, 2, 2526, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-12-05 22:47:41', N'1', N'2023-12-05 22:47:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2529, N'产品更新', N'crm:product:update', 3, 3, 2526, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-12-05 22:48:03', N'1', N'2023-12-05 22:48:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2530, N'产品删除', N'crm:product:delete', 3, 4, 2526, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-12-05 22:48:17', N'1', N'2023-12-05 22:48:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2531, N'产品导出', N'crm:product:export', 3, 5, 2526, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-12-05 22:48:29', N'1', N'2023-12-05 22:48:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2532, N'产品分类配置', N'', 2, 3, 2524, N'product/category', N'fa-solid:window-restore', N'crm/product/category/index', N'CrmProductCategory', 0, N'1', N'1', N'1', N'1', N'2023-12-06 12:52:36', N'1', N'2023-12-06 12:52:51', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2533, N'产品分类查询', N'crm:product-category:query', 3, 1, 2532, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-12-06 12:53:23', N'1', N'2023-12-06 12:53:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2534, N'产品分类创建', N'crm:product-category:create', 3, 2, 2532, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-12-06 12:53:41', N'1', N'2023-12-06 12:53:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2535, N'产品分类更新', N'crm:product-category:update', 3, 3, 2532, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-12-06 12:53:59', N'1', N'2023-12-06 12:53:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2536, N'产品分类删除', N'crm:product-category:delete', 3, 4, 2532, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2023-12-06 12:54:14', N'1', N'2023-12-06 12:54:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2543, N'关联商机', N'crm:contact:create-business', 3, 10, 2416, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-01-02 17:28:25', N'1', N'2024-01-02 17:28:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2544, N'取关商机', N'crm:contact:delete-business', 3, 11, 2416, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-01-02 17:28:43', N'1', N'2024-01-02 17:28:51', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2545, N'商品统计', N'', 2, 3, 2358, N'product', N'fa:product-hunt', N'mall/statistics/product/index', N'ProductStatistics', 0, N'1', N'1', N'1', N'', N'2023-12-15 18:54:28', N'1', N'2024-02-26 20:41:52', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2546, N'客户公海', N'', 2, 30, 2397, N'customer/pool', N'fa-solid:swimming-pool', N'crm/customer/pool/index', N'CrmCustomerPool', 0, N'1', N'1', N'1', N'1', N'2024-01-15 21:29:34', N'1', N'2024-02-17 17:14:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2547, N'订单查询', N'trade:order:query', 3, 1, 2076, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-01-16 08:52:00', N'1', N'2024-01-16 08:52:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2548, N'订单更新', N'trade:order:update', 3, 2, 2076, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-01-16 08:52:21', N'1', N'2024-01-16 08:52:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2549, N'支付&退款案例', N'', 2, 1, 2161, N'order', N'fa:paypal', N'pay/demo/order/index', N'', 0, N'1', N'1', N'1', N'1', N'2024-01-18 23:45:00', N'1', N'2024-01-18 23:47:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2550, N'提现转账案例', N'', 2, 2, 2161, N'transfer', N'fa:transgender-alt', N'pay/demo/withdraw/index', N'', 0, N'1', N'1', N'1', N'1', N'2024-01-18 23:51:16', N'1', N'2025-05-08 13:04:36', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2551, N'钱包管理', N'', 1, 4, 1117, N'wallet', N'ep:wallet', N'', N'', 0, N'1', N'1', N'1', N'', N'2023-12-29 02:32:54', N'1', N'2024-02-29 08:58:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2552, N'充值套餐', N'', 2, 2, 2551, N'wallet-recharge-package', N'fa:leaf', N'pay/wallet/rechargePackage/index', N'WalletRechargePackage', 0, N'1', N'1', N'1', N'', N'2023-12-29 02:32:54', N'', N'2023-12-29 02:32:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2553, N'钱包充值套餐查询', N'pay:wallet-recharge-package:query', 3, 1, 2552, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-12-29 02:32:54', N'', N'2023-12-29 02:32:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2554, N'钱包充值套餐创建', N'pay:wallet-recharge-package:create', 3, 2, 2552, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-12-29 02:32:54', N'', N'2023-12-29 02:32:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2555, N'钱包充值套餐更新', N'pay:wallet-recharge-package:update', 3, 3, 2552, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-12-29 02:32:54', N'', N'2023-12-29 02:32:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2556, N'钱包充值套餐删除', N'pay:wallet-recharge-package:delete', 3, 4, 2552, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-12-29 02:32:54', N'', N'2023-12-29 02:32:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2557, N'钱包余额', N'', 2, 1, 2551, N'wallet-balance', N'fa:leaf', N'pay/wallet/balance/index', N'WalletBalance', 0, N'1', N'1', N'1', N'', N'2023-12-29 02:32:54', N'', N'2023-12-29 02:32:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2558, N'钱包余额查询', N'pay:wallet:query', 3, 1, 2557, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2023-12-29 02:32:54', N'', N'2023-12-29 02:32:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2559, N'转账订单', N'', 2, 3, 1117, N'transfer', N'ep:credit-card', N'pay/transfer/index', N'PayTransfer', 0, N'1', N'1', N'1', N'', N'2023-12-29 02:32:54', N'', N'2023-12-29 02:32:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2560, N'数据统计', N'', 1, 200, 2397, N'statistics', N'ep:data-line', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-01-26 22:50:35', N'1', N'2024-02-24 20:10:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2561, N'排行榜', N'crm:statistics-rank:query', 2, 1, 2560, N'ranking', N'fa:area-chart', N'crm/statistics/rank/index', N'CrmStatisticsRank', 0, N'1', N'1', N'1', N'1', N'2024-01-26 22:52:09', N'1', N'2024-04-24 19:39:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2562, N'客户导入', N'crm:customer:import', 3, 6, 2391, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-01 13:09:00', N'1', N'2024-02-01 13:09:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2563, N'ERP 系统', N'', 1, 300, 0, N'/erp', N'simple-icons:erpnext', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-04 15:37:25', N'1', N'2025-04-19 18:56:15', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2564, N'产品管理', N'', 1, 40, 2563, N'product', N'fa:product-hunt', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-04 15:38:43', N'1', N'2024-02-04 15:38:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2565, N'产品信息', N'', 2, 0, 2564, N'product', N'fa-solid:apple-alt', N'erp/product/product/index', N'ErpProduct', 0, N'1', N'1', N'1', N'', N'2024-02-04 07:52:15', N'1', N'2024-02-05 14:42:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2566, N'产品查询', N'erp:product:query', 3, 1, 2565, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-02-04 07:52:15', N'1', N'2024-02-04 17:21:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2567, N'产品创建', N'erp:product:create', 3, 2, 2565, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-02-04 07:52:15', N'1', N'2024-02-04 17:22:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2568, N'产品更新', N'erp:product:update', 3, 3, 2565, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-02-04 07:52:15', N'1', N'2024-02-04 17:22:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2569, N'产品删除', N'erp:product:delete', 3, 4, 2565, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-02-04 07:52:15', N'1', N'2024-02-04 17:22:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2570, N'产品导出', N'erp:product:export', 3, 5, 2565, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-02-04 07:52:15', N'1', N'2024-02-04 17:22:26', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2571, N'产品分类', N'', 2, 1, 2564, N'product-category', N'fa:certificate', N'erp/product/category/index', N'ErpProductCategory', 0, N'1', N'1', N'1', N'', N'2024-02-04 09:21:04', N'1', N'2024-02-04 17:24:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2572, N'分类查询', N'erp:product-category:query', 3, 1, 2571, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 09:21:04', N'', N'2024-02-04 09:21:04', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2573, N'分类创建', N'erp:product-category:create', 3, 2, 2571, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 09:21:04', N'', N'2024-02-04 09:21:04', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2574, N'分类更新', N'erp:product-category:update', 3, 3, 2571, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 09:21:04', N'', N'2024-02-04 09:21:04', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2575, N'分类删除', N'erp:product-category:delete', 3, 4, 2571, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 09:21:04', N'', N'2024-02-04 09:21:04', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2576, N'分类导出', N'erp:product-category:export', 3, 5, 2571, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 09:21:04', N'', N'2024-02-04 09:21:04', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2577, N'产品单位', N'', 2, 2, 2564, N'unit', N'ep:opportunity', N'erp/product/unit/index', N'ErpProductUnit', 0, N'1', N'1', N'1', N'', N'2024-02-04 11:54:08', N'1', N'2024-02-04 19:54:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2578, N'单位查询', N'erp:product-unit:query', 3, 1, 2577, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 11:54:08', N'', N'2024-02-04 11:54:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2579, N'单位创建', N'erp:product-unit:create', 3, 2, 2577, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 11:54:08', N'', N'2024-02-04 11:54:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2580, N'单位更新', N'erp:product-unit:update', 3, 3, 2577, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 11:54:08', N'', N'2024-02-04 11:54:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2581, N'单位删除', N'erp:product-unit:delete', 3, 4, 2577, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 11:54:08', N'', N'2024-02-04 11:54:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2582, N'单位导出', N'erp:product-unit:export', 3, 5, 2577, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 11:54:08', N'', N'2024-02-04 11:54:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2583, N'库存管理', N'', 1, 30, 2563, N'stock', N'fa:window-restore', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-05 00:29:37', N'1', N'2024-02-05 00:29:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2584, N'仓库信息', N'', 2, 0, 2583, N'warehouse', N'ep:house', N'erp/stock/warehouse/index', N'ErpWarehouse', 0, N'1', N'1', N'1', N'', N'2024-02-04 17:12:09', N'1', N'2024-02-05 01:12:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2585, N'仓库查询', N'erp:warehouse:query', 3, 1, 2584, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 17:12:09', N'', N'2024-02-04 17:12:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2586, N'仓库创建', N'erp:warehouse:create', 3, 2, 2584, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 17:12:09', N'', N'2024-02-04 17:12:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2587, N'仓库更新', N'erp:warehouse:update', 3, 3, 2584, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 17:12:09', N'', N'2024-02-04 17:12:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2588, N'仓库删除', N'erp:warehouse:delete', 3, 4, 2584, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 17:12:09', N'', N'2024-02-04 17:12:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2589, N'仓库导出', N'erp:warehouse:export', 3, 5, 2584, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-04 17:12:09', N'', N'2024-02-04 17:12:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2590, N'产品库存', N'', 2, 1, 2583, N'stock', N'ep:coffee', N'erp/stock/stock/index', N'ErpStock', 0, N'1', N'1', N'1', N'', N'2024-02-05 06:40:50', N'1', N'2024-02-05 14:42:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2591, N'库存查询', N'erp:stock:query', 3, 1, 2590, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 06:40:50', N'', N'2024-02-05 06:40:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2592, N'库存导出', N'erp:stock:export', 3, 5, 2590, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 06:40:50', N'', N'2024-02-05 06:40:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2593, N'出入库明细', N'', 2, 2, 2583, N'record', N'fa-solid:blog', N'erp/stock/record/index', N'ErpStockRecord', 0, N'1', N'1', N'1', N'', N'2024-02-05 10:27:21', N'1', N'2024-02-06 17:26:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2594, N'库存明细查询', N'erp:stock-record:query', 3, 1, 2593, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 10:27:21', N'', N'2024-02-05 10:27:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2595, N'库存明细导出', N'erp:stock-record:export', 3, 5, 2593, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 10:27:21', N'', N'2024-02-05 10:27:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2596, N'其它入库', N'', 2, 3, 2583, N'in', N'ep:zoom-in', N'erp/stock/in/index', N'ErpStockIn', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-07 19:06:51', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2597, N'其它入库单查询', N'erp:stock-in:query', 3, 1, 2596, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-05 16:08:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2598, N'其它入库单创建', N'erp:stock-in:create', 3, 2, 2596, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-05 16:08:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2599, N'其它入库单更新', N'erp:stock-in:update', 3, 3, 2596, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-05 16:08:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2600, N'其它入库单删除', N'erp:stock-in:delete', 3, 4, 2596, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-05 16:08:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2601, N'其它入库单导出', N'erp:stock-in:export', 3, 5, 2596, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-05 16:08:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2602, N'采购管理', N'', 1, 10, 2563, N'purchase', N'fa:buysellads', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-06 16:01:01', N'1', N'2024-02-06 16:01:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2603, N'供应商信息', N'', 2, 4, 2602, N'supplier', N'fa:superpowers', N'erp/purchase/supplier/index', N'ErpSupplier', 0, N'1', N'1', N'1', N'', N'2024-02-06 08:21:55', N'1', N'2024-02-06 16:22:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2604, N'供应商查询', N'erp:supplier:query', 3, 1, 2603, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-06 08:21:55', N'', N'2024-02-06 08:21:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2605, N'供应商创建', N'erp:supplier:create', 3, 2, 2603, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-06 08:21:55', N'', N'2024-02-06 08:21:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2606, N'供应商更新', N'erp:supplier:update', 3, 3, 2603, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-06 08:21:55', N'', N'2024-02-06 08:21:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2607, N'供应商删除', N'erp:supplier:delete', 3, 4, 2603, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-06 08:21:55', N'', N'2024-02-06 08:21:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2608, N'供应商导出', N'erp:supplier:export', 3, 5, 2603, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-06 08:21:55', N'', N'2024-02-06 08:21:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2609, N'其它入库单审批', N'erp:stock-in:update-status', 3, 6, 2596, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-05 16:08:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2610, N'其它出库', N'', 2, 4, 2583, N'out', N'ep:zoom-out', N'erp/stock/out/index', N'ErpStockOut', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-07 19:06:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2611, N'其它出库单查询', N'erp:stock-out:query', 3, 1, 2610, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 06:43:39', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2612, N'其它出库单创建', N'erp:stock-out:create', 3, 2, 2610, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 06:43:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2613, N'其它出库单更新', N'erp:stock-out:update', 3, 3, 2610, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 06:43:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2614, N'其它出库单删除', N'erp:stock-out:delete', 3, 4, 2610, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 06:43:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2615, N'其它出库单导出', N'erp:stock-out:export', 3, 5, 2610, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 06:43:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2616, N'其它出库单审批', N'erp:stock-out:update-status', 3, 6, 2610, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 06:43:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2617, N'销售管理', N'', 1, 20, 2563, N'sale', N'fa:sellsy', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-07 15:12:32', N'1', N'2024-02-07 15:12:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2618, N'客户信息', N'', 2, 4, 2617, N'customer', N'ep:avatar', N'erp/sale/customer/index', N'ErpCustomer', 0, N'1', N'1', N'1', N'', N'2024-02-07 07:21:45', N'1', N'2024-02-07 15:22:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2619, N'客户查询', N'erp:customer:query', 3, 1, 2618, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-07 07:21:45', N'', N'2024-02-07 07:21:45', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2620, N'客户创建', N'erp:customer:create', 3, 2, 2618, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-07 07:21:45', N'', N'2024-02-07 07:21:45', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2621, N'客户更新', N'erp:customer:update', 3, 3, 2618, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-07 07:21:45', N'', N'2024-02-07 07:21:45', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2622, N'客户删除', N'erp:customer:delete', 3, 4, 2618, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-07 07:21:45', N'', N'2024-02-07 07:21:45', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2623, N'客户导出', N'erp:customer:export', 3, 5, 2618, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-07 07:21:45', N'', N'2024-02-07 07:21:45', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2624, N'库存调拨', N'', 2, 5, 2583, N'move', N'ep:folder-remove', N'erp/stock/move/index', N'ErpStockMove', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-16 18:53:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2625, N'库存调度单查询', N'erp:stock-move:query', 3, 1, 2624, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2626, N'库存调度单创建', N'erp:stock-move:create', 3, 2, 2624, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:52', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2627, N'库存调度单更新', N'erp:stock-move:update', 3, 3, 2624, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2628, N'库存调度单删除', N'erp:stock-move:delete', 3, 4, 2624, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2629, N'库存调度单导出', N'erp:stock-move:export', 3, 5, 2624, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2630, N'库存调度单审批', N'erp:stock-move:update-status', 3, 6, 2624, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:13:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2631, N'库存盘点', N'', 2, 6, 2583, N'check', N'ep:circle-check-filled', N'erp/stock/check/index', N'ErpStockCheck', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-08 08:31:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2632, N'库存盘点单查询', N'erp:stock-check:query', 3, 1, 2631, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2633, N'库存盘点单创建', N'erp:stock-check:create', 3, 2, 2631, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:52', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2634, N'库存盘点单更新', N'erp:stock-check:update', 3, 3, 2631, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2635, N'库存盘点单删除', N'erp:stock-check:delete', 3, 4, 2631, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2636, N'库存盘点单导出', N'erp:stock-check:export', 3, 5, 2631, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2637, N'库存盘点单审批', N'erp:stock-check:update-status', 3, 6, 2631, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:13:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2638, N'销售订单', N'', 2, 1, 2617, N'order', N'fa:first-order', N'erp/sale/order/index', N'ErpSaleOrder', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-10 21:59:20', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2639, N'销售订单查询', N'erp:sale-order:query', 3, 1, 2638, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2640, N'销售订单创建', N'erp:sale-order:create', 3, 2, 2638, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:52', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2641, N'销售订单更新', N'erp:sale-order:update', 3, 3, 2638, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2642, N'销售订单删除', N'erp:sale-order:delete', 3, 4, 2638, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2643, N'销售订单导出', N'erp:sale-order:export', 3, 5, 2638, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2644, N'销售订单审批', N'erp:sale-order:update-status', 3, 6, 2638, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:13:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2645, N'财务管理', N'', 1, 50, 2563, N'finance', N'ep:money', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-10 08:05:58', N'1', N'2024-02-10 08:06:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2646, N'结算账户', N'', 2, 10, 2645, N'account', N'fa:universal-access', N'erp/finance/account/index', N'ErpAccount', 0, N'1', N'1', N'1', N'', N'2024-02-10 00:15:07', N'1', N'2024-02-14 08:24:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2647, N'结算账户查询', N'erp:account:query', 3, 1, 2646, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-10 00:15:07', N'', N'2024-02-10 00:15:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2648, N'结算账户创建', N'erp:account:create', 3, 2, 2646, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-10 00:15:07', N'', N'2024-02-10 00:15:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2649, N'结算账户更新', N'erp:account:update', 3, 3, 2646, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-10 00:15:07', N'', N'2024-02-10 00:15:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2650, N'结算账户删除', N'erp:account:delete', 3, 4, 2646, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-10 00:15:07', N'', N'2024-02-10 00:15:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2651, N'结算账户导出', N'erp:account:export', 3, 5, 2646, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-10 00:15:07', N'', N'2024-02-10 00:15:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2652, N'销售出库', N'', 2, 2, 2617, N'out', N'ep:sold-out', N'erp/sale/out/index', N'ErpSaleOut', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-10 22:02:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2653, N'销售出库查询', N'erp:sale-out:query', 3, 1, 2652, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2654, N'销售出库创建', N'erp:sale-out:create', 3, 2, 2652, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:52', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2655, N'销售出库更新', N'erp:sale-out:update', 3, 3, 2652, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2656, N'销售出库删除', N'erp:sale-out:delete', 3, 4, 2652, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2657, N'销售出库导出', N'erp:sale-out:export', 3, 5, 2652, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2658, N'销售出库审批', N'erp:sale-out:update-status', 3, 6, 2652, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:13:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2659, N'销售退货', N'', 2, 3, 2617, N'return', N'fa-solid:bone', N'erp/sale/return/index', N'ErpSaleReturn', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-12 06:12:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2660, N'销售退货查询', N'erp:sale-return:query', 3, 1, 2659, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2661, N'销售退货创建', N'erp:sale-return:create', 3, 2, 2659, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:52', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2662, N'销售退货更新', N'erp:sale-return:update', 3, 3, 2659, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:55', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2663, N'销售退货删除', N'erp:sale-return:delete', 3, 4, 2659, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2664, N'销售退货导出', N'erp:sale-return:export', 3, 5, 2659, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:12:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2665, N'销售退货审批', N'erp:sale-return:update-status', 3, 6, 2659, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-07 11:13:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2666, N'采购订单', N'', 2, 1, 2602, N'order', N'fa-solid:border-all', N'erp/purchase/order/index', N'ErpPurchaseOrder', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-12 08:51:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2667, N'采购订单查询', N'erp:purchase-order:query', 3, 1, 2666, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2668, N'采购订单创建', N'erp:purchase-order:create', 3, 2, 2666, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2669, N'采购订单更新', N'erp:purchase-order:update', 3, 3, 2666, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2670, N'采购订单删除', N'erp:purchase-order:delete', 3, 4, 2666, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2671, N'采购订单导出', N'erp:purchase-order:export', 3, 5, 2666, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2672, N'采购订单审批', N'erp:purchase-order:update-status', 3, 6, 2666, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2673, N'采购入库', N'', 2, 2, 2602, N'in', N'fa-solid:gopuram', N'erp/purchase/in/index', N'ErpPurchaseIn', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-12 11:19:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2674, N'采购入库查询', N'erp:purchase-in:query', 3, 1, 2673, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2675, N'采购入库创建', N'erp:purchase-in:create', 3, 2, 2673, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2676, N'采购入库更新', N'erp:purchase-in:update', 3, 3, 2673, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2677, N'采购入库删除', N'erp:purchase-in:delete', 3, 4, 2673, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2678, N'采购入库导出', N'erp:purchase-in:export', 3, 5, 2673, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2679, N'采购入库审批', N'erp:purchase-in:update-status', 3, 6, 2673, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2680, N'采购退货', N'', 2, 3, 2602, N'return', N'ep:minus', N'erp/purchase/return/index', N'ErpPurchaseReturn', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-12 20:51:02', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2681, N'采购退货查询', N'erp:purchase-return:query', 3, 1, 2680, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2682, N'采购退货创建', N'erp:purchase-return:create', 3, 2, 2680, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2683, N'采购退货更新', N'erp:purchase-return:update', 3, 3, 2680, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2684, N'采购退货删除', N'erp:purchase-return:delete', 3, 4, 2680, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2685, N'采购退货导出', N'erp:purchase-return:export', 3, 5, 2680, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2686, N'采购退货审批', N'erp:purchase-return:update-status', 3, 6, 2680, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2687, N'付款单', N'', 2, 1, 2645, N'payment', N'ep:caret-right', N'erp/finance/payment/index', N'ErpFinancePayment', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-14 08:24:23', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2688, N'付款单查询', N'erp:finance-payment:query', 3, 1, 2687, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2689, N'付款单创建', N'erp:finance-payment:create', 3, 2, 2687, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2690, N'付款单更新', N'erp:finance-payment:update', 3, 3, 2687, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2691, N'付款单删除', N'erp:finance-payment:delete', 3, 4, 2687, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2692, N'付款单导出', N'erp:finance-payment:export', 3, 5, 2687, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2693, N'付款单审批', N'erp:finance-payment:update-status', 3, 6, 2687, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2694, N'收款单', N'', 2, 2, 2645, N'receipt', N'ep:expand', N'erp/finance/receipt/index', N'ErpFinanceReceipt', 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'1', N'2024-02-15 19:35:45', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2695, N'收款单查询', N'erp:finance-receipt:query', 3, 1, 2694, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2696, N'收款单创建', N'erp:finance-receipt:create', 3, 2, 2694, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2697, N'收款单更新', N'erp:finance-receipt:update', 3, 3, 2694, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:44:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2698, N'收款单删除', N'erp:finance-receipt:delete', 3, 4, 2694, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2699, N'收款单导出', N'erp:finance-receipt:export', 3, 5, 2694, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2700, N'收款单审批', N'erp:finance-receipt:update-status', 3, 6, 2694, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-02-05 16:08:56', N'', N'2024-02-12 00:45:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2701, N'待办事项', N'', 2, 0, 2397, N'backlog', N'fa-solid:tasks', N'crm/backlog/index', N'CrmBacklog', 0, N'1', N'1', N'1', N'1', N'2024-02-17 17:17:11', N'1', N'2024-02-17 17:17:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2702, N'ERP 首页', N'erp:statistics:query', 2, 0, 2563, N'home', N'ep:home-filled', N'erp/home/index.vue', N'ErpHome', 0, N'1', N'1', N'1', N'1', N'2024-02-18 16:49:40', N'1', N'2024-02-26 21:12:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2703, N'商机状态配置', N'', 2, 4, 2524, N'business-status', N'fa-solid:charging-station', N'crm/business/status/index', N'CrmBusinessStatus', 0, N'1', N'1', N'1', N'1', N'2024-02-21 20:15:17', N'1', N'2024-02-21 20:15:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2704, N'商机状态查询', N'crm:business-status:query', 3, 1, 2703, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-21 20:35:36', N'1', N'2024-02-21 20:36:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2705, N'商机状态创建', N'crm:business-status:create', 3, 2, 2703, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-21 20:35:57', N'1', N'2024-02-21 20:35:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2706, N'商机状态更新', N'crm:business-status:update', 3, 3, 2703, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-21 20:36:21', N'1', N'2024-02-21 20:36:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2707, N'商机状态删除', N'crm:business-status:delete', 3, 4, 2703, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-21 20:36:36', N'1', N'2024-02-21 20:36:36', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2708, N'合同配置', N'', 2, 5, 2524, N'contract-config', N'ep:connection', N'crm/contract/config/index', N'CrmContractConfig', 0, N'1', N'1', N'1', N'1', N'2024-02-24 16:44:40', N'1', N'2024-02-24 16:44:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2709, N'客户公海配置查询', N'crm:customer-pool-config:query', 3, 2, 2516, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-24 16:45:19', N'1', N'2024-02-24 16:45:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2710, N'合同配置更新', N'crm:contract-config:update', 3, 1, 2708, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-24 16:45:56', N'1', N'2024-02-24 16:45:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2711, N'合同配置查询', N'crm:contract-config:query', 3, 2, 2708, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-02-24 16:46:16', N'1', N'2024-02-24 16:46:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2712, N'客户分析', N'crm:statistics-customer:query', 2, 0, 2560, N'customer', N'ep:avatar', N'crm/statistics/customer/index.vue', N'CrmStatisticsCustomer', 0, N'1', N'1', N'1', N'1', N'2024-03-09 16:43:56', N'1', N'2024-05-04 20:38:50', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2713, N'抄送我的', N'bpm:process-instance-cc:query', 2, 30, 1200, N'copy', N'ep:copy-document', N'bpm/task/copy/index', N'BpmProcessInstanceCopy', 0, N'1', N'1', N'1', N'1', N'2024-03-17 21:50:23', N'1', N'2024-04-24 19:55:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2714, N'流程分类', N'', 2, 3, 1186, N'category', N'fa:object-ungroup', N'bpm/category/index', N'BpmCategory', 0, N'1', N'1', N'1', N'', N'2024-03-08 02:00:51', N'1', N'2024-03-21 23:51:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2715, N'分类查询', N'bpm:category:query', 3, 1, 2714, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-03-08 02:00:51', N'1', N'2024-03-19 14:36:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2716, N'分类创建', N'bpm:category:create', 3, 2, 2714, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-03-08 02:00:51', N'1', N'2024-03-19 14:36:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2717, N'分类更新', N'bpm:category:update', 3, 3, 2714, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-03-08 02:00:51', N'1', N'2024-03-19 14:36:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2718, N'分类删除', N'bpm:category:delete', 3, 4, 2714, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-03-08 02:00:51', N'1', N'2024-03-19 14:36:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2720, N'发起流程', N'', 2, 0, 1200, N'create', N'fa-solid:grin-stars', N'bpm/processInstance/create/index', N'BpmProcessInstanceCreate', 0, N'1', N'0', N'1', N'1', N'2024-03-19 19:46:05', N'1', N'2024-03-23 19:03:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2721, N'流程实例', N'', 2, 10, 1186, N'process-instance/manager', N'fa:square', N'bpm/processInstance/manager/index', N'BpmProcessInstanceManager', 0, N'1', N'1', N'1', N'1', N'2024-03-21 23:57:30', N'1', N'2024-03-21 23:57:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2722, N'流程实例的查询（管理员）', N'bpm:process-instance:manager-query', 3, 1, 2721, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-03-22 08:18:27', N'1', N'2024-03-22 08:19:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2723, N'流程实例的取消（管理员）', N'bpm:process-instance:cancel-by-admin', 3, 2, 2721, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-03-22 08:19:25', N'1', N'2024-03-22 08:19:25', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2724, N'流程任务', N'', 2, 11, 1186, N'process-tasnk', N'ep:collection-tag', N'bpm/task/manager/index', N'BpmManagerTask', 0, N'1', N'1', N'1', N'1', N'2024-03-22 08:43:22', N'1', N'2024-03-22 08:43:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2725, N'流程任务的查询（管理员）', N'bpm:task:mananger-query', 3, 1, 2724, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-03-22 08:43:49', N'1', N'2024-03-22 08:43:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2726, N'流程监听器', N'', 2, 5, 1186, N'process-listener', N'fa:assistive-listening-systems', N'bpm/processListener/index', N'BpmProcessListener', 0, N'1', N'1', N'1', N'', N'2024-03-09 16:05:34', N'1', N'2024-03-23 13:13:38', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2727, N'流程监听器查询', N'bpm:process-listener:query', 3, 1, 2726, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-03-09 16:05:34', N'', N'2024-03-09 16:05:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2728, N'流程监听器创建', N'bpm:process-listener:create', 3, 2, 2726, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-03-09 16:05:34', N'', N'2024-03-09 16:05:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2729, N'流程监听器更新', N'bpm:process-listener:update', 3, 3, 2726, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-03-09 16:05:34', N'', N'2024-03-09 16:05:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2730, N'流程监听器删除', N'bpm:process-listener:delete', 3, 4, 2726, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-03-09 16:05:34', N'', N'2024-03-09 16:05:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2731, N'流程表达式', N'', 2, 6, 1186, N'process-expression', N'fa:wpexplorer', N'bpm/processExpression/index', N'BpmProcessExpression', 0, N'1', N'1', N'1', N'', N'2024-03-09 22:35:08', N'1', N'2024-03-23 19:43:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2732, N'流程表达式查询', N'bpm:process-expression:query', 3, 1, 2731, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-03-09 22:35:08', N'', N'2024-03-09 22:35:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2733, N'流程表达式创建', N'bpm:process-expression:create', 3, 2, 2731, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-03-09 22:35:08', N'', N'2024-03-09 22:35:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2734, N'流程表达式更新', N'bpm:process-expression:update', 3, 3, 2731, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-03-09 22:35:08', N'', N'2024-03-09 22:35:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2735, N'流程表达式删除', N'bpm:process-expression:delete', 3, 4, 2731, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-03-09 22:35:08', N'', N'2024-03-09 22:35:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2736, N'员工业绩', N'crm:statistics-performance:query', 2, 3, 2560, N'performance', N'ep:dish-dot', N'crm/statistics/performance/index', N'CrmStatisticsPerformance', 0, N'1', N'1', N'1', N'1', N'2024-04-05 13:49:20', N'1', N'2024-04-24 19:42:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2737, N'客户画像', N'crm:statistics-portrait:query', 2, 4, 2560, N'portrait', N'ep:picture', N'crm/statistics/portrait/index', N'CrmStatisticsPortrait', 0, N'1', N'1', N'1', N'1', N'2024-04-05 13:57:40', N'1', N'2024-04-24 19:42:24', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2738, N'销售漏斗', N'crm:statistics-funnel:query', 2, 5, 2560, N'funnel', N'ep:grape', N'crm/statistics/funnel/index', N'CrmStatisticsFunnel', 0, N'1', N'1', N'1', N'1', N'2024-04-13 10:53:26', N'1', N'2024-04-24 19:39:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2739, N'消息中心', N'', 1, 7, 1, N'messages', N'ep:chat-dot-round', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-22 23:54:30', N'1', N'2024-04-23 09:36:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2740, N'监控中心', N'', 1, 10, 2, N'monitors', N'ep:monitor', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-23 00:04:44', N'1', N'2024-04-23 00:04:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2741, N'领取公海客户', N'crm:customer:receive', 3, 1, 2546, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:47:45', N'1', N'2024-04-24 19:47:45', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2742, N'分配公海客户', N'crm:customer:distribute', 3, 2, 2546, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:48:05', N'1', N'2024-04-24 19:48:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2743, N'商品统计查询', N'statistics:product:query', 3, 1, 2545, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:50:05', N'1', N'2024-04-24 19:50:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2744, N'商品统计导出', N'statistics:product:export', 3, 2, 2545, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:50:26', N'1', N'2024-04-24 19:50:26', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2745, N'支付渠道查询', N'pay:channel:query', 3, 10, 1126, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:53:01', N'1', N'2024-04-24 19:53:01', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2746, N'支付渠道创建', N'pay:channel:create', 3, 11, 1126, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:53:18', N'1', N'2024-04-24 19:53:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2747, N'支付渠道更新', N'pay:channel:update', 3, 12, 1126, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:53:32', N'1', N'2024-04-24 19:53:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2748, N'支付渠道删除', N'pay:channel:delete', 3, 13, 1126, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:54:34', N'1', N'2024-04-24 19:54:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2749, N'商品收藏查询', N'product:favorite:query', 3, 10, 2014, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:55:47', N'1', N'2024-04-24 19:55:47', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2750, N'商品浏览查询', N'product:browse-history:query', 3, 20, 2014, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:57:43', N'1', N'2024-04-24 19:57:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2751, N'售后同意', N'trade:after-sale:agree', 3, 2, 2073, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:58:40', N'1', N'2024-04-24 19:58:40', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2752, N'售后不同意', N'trade:after-sale:disagree', 3, 3, 2073, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 19:59:03', N'1', N'2024-04-24 19:59:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2753, N'售后确认退货', N'trade:after-sale:receive', 3, 4, 2073, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 20:00:07', N'1', N'2024-04-24 20:00:07', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2754, N'售后确认退款', N'trade:after-sale:refund', 3, 5, 2073, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 20:00:24', N'1', N'2024-04-24 20:00:24', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2755, N'删除项目', N'report:go-view-project:delete', 3, 2, 2153, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 20:01:37', N'1', N'2024-04-24 20:01:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2756, N'会员等级记录查询', N'member:level-record:query', 3, 10, 2325, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 20:02:32', N'1', N'2024-04-24 20:02:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2757, N'会员经验记录查询', N'member:experience-record:query', 3, 11, 2325, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-04-24 20:02:51', N'1', N'2024-04-24 20:02:51', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2758, N'AI 大模型', N'', 1, 400, 0, N'/ai', N'tabler:ai', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-05-07 15:07:56', N'1', N'2025-04-19 18:57:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2759, N'AI 对话', N'', 2, 1, 2758, N'chat', N'ep:message', N'ai/chat/index/index.vue', N'AiChat', 0, N'1', N'1', N'1', N'1', N'2024-05-07 15:09:14', N'1', N'2024-07-07 17:15:36', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2760, N'控制台', N'', 1, 100, 2758, N'console', N'ep:setting', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-05-09 22:39:09', N'1', N'2024-05-24 23:34:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2761, N'API 密钥', N'', 2, 0, 2760, N'api-key', N'ep:key', N'ai/model/apiKey/index.vue', N'AiApiKey', 0, N'1', N'1', N'1', N'', N'2024-05-09 14:52:56', N'1', N'2024-05-10 22:44:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2762, N'API 密钥查询', N'ai:api-key:query', 3, 1, 2761, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-09 14:52:56', N'1', N'2024-05-13 20:36:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2763, N'API 密钥创建', N'ai:api-key:create', 3, 2, 2761, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-09 14:52:56', N'1', N'2024-05-13 20:36:26', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2764, N'API 密钥更新', N'ai:api-key:update', 3, 3, 2761, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-09 14:52:56', N'1', N'2024-05-13 20:36:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2765, N'API 密钥删除', N'ai:api-key:delete', 3, 4, 2761, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-09 14:52:56', N'1', N'2024-05-13 20:36:48', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2767, N'模型配置', N'', 2, 0, 2760, N'model', N'fa-solid:abacus', N'ai/model/model/index.vue', N'AiModel', 0, N'1', N'1', N'1', N'', N'2024-05-10 14:42:48', N'1', N'2025-03-03 09:57:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2768, N'聊天模型查询', N'ai:model:query', 3, 1, 2767, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-10 14:42:48', N'1', N'2025-03-03 09:19:46', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2769, N'聊天模型创建', N'ai:model:create', 3, 2, 2767, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-10 14:42:48', N'1', N'2025-03-03 09:20:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2770, N'聊天模型更新', N'ai:model:update', 3, 3, 2767, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-10 14:42:48', N'1', N'2025-03-03 09:20:14', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2771, N'聊天模型删除', N'ai:model:delete', 3, 4, 2767, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-10 14:42:48', N'1', N'2025-03-03 09:20:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2773, N'聊天角色', N'', 2, 0, 2760, N'chat-role', N'fa:user-secret', N'ai/model/chatRole/index.vue', N'AiChatRole', 0, N'1', N'1', N'1', N'', N'2024-05-13 12:39:28', N'1', N'2024-05-13 20:41:45', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2774, N'聊天角色查询', N'ai:chat-role:query', 3, 1, 2773, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-05-13 12:39:28', N'', N'2024-05-13 12:39:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2775, N'聊天角色创建', N'ai:chat-role:create', 3, 2, 2773, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-05-13 12:39:28', N'', N'2024-05-13 12:39:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2776, N'聊天角色更新', N'ai:chat-role:update', 3, 3, 2773, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-05-13 12:39:28', N'', N'2024-05-13 12:39:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2777, N'聊天角色删除', N'ai:chat-role:delete', 3, 4, 2773, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-05-13 21:43:38', N'1', N'2024-05-13 21:43:38', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2778, N'聊天管理', N'', 2, 10, 2760, N'chat-conversation', N'ep:chat-square', N'ai/chat/manager/index.vue', N'AiChatManager', 0, N'1', N'1', N'1', N'', N'2024-05-24 15:39:18', N'1', N'2024-06-26 21:36:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2779, N'会话查询', N'ai:chat-conversation:query', 3, 1, 2778, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-24 15:39:18', N'1', N'2024-05-25 08:38:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2780, N'会话删除', N'ai:chat-conversation:delete', 3, 2, 2778, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-05-24 15:39:18', N'1', N'2024-05-25 08:38:40', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2781, N'消息查询', N'ai:chat-message:query', 3, 11, 2778, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-05-25 08:38:56', N'1', N'2024-05-25 08:38:56', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2782, N'消息删除', N'ai:chat-message:delete', 3, 12, 2778, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-05-25 08:39:10', N'1', N'2024-05-25 08:39:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2783, N'AI 绘画', N'', 2, 2, 2758, N'image', N'ep:picture-rounded', N'ai/image/index/index.vue', N'AiImage', 0, N'1', N'1', N'1', N'1', N'2024-05-26 11:45:17', N'1', N'2024-07-07 17:18:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2784, N'绘画管理', N'', 2, 11, 2760, N'image', N'fa:file-image-o', N'ai/image/manager/index.vue', N'AiImageManager', 0, N'1', N'1', N'1', N'', N'2024-06-26 13:32:31', N'1', N'2024-06-26 21:37:13', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2785, N'绘画查询', N'ai:image:query', 3, 1, 2784, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-06-26 13:32:31', N'1', N'2024-06-26 22:21:57', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2786, N'绘画删除', N'ai:image:delete', 3, 4, 2784, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-06-26 13:32:31', N'1', N'2024-06-26 22:22:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2787, N'绘图更新', N'ai:image:update', 3, 2, 2784, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-06-26 22:47:56', N'1', N'2024-08-31 09:21:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2788, N'音乐管理', N'', 2, 12, 2760, N'music', N'fa:music', N'ai/music/manager/index.vue', N'AiMusicManager', 0, N'1', N'1', N'1', N'', N'2024-06-27 15:03:33', N'1', N'2024-06-27 23:04:19', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2789, N'音乐查询', N'ai:music:query', 3, 1, 2788, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-06-27 15:03:33', N'', N'2024-06-27 15:03:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2790, N'音乐更新', N'ai:music:update', 3, 3, 2788, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-06-27 15:03:33', N'', N'2024-06-27 15:03:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2791, N'音乐删除', N'ai:music:delete', 3, 4, 2788, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-06-27 15:03:33', N'', N'2024-06-27 15:03:33', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2792, N'AI 写作', N'', 2, 3, 2758, N'write', N'fa-solid:book-reader', N'ai/write/index/index.vue', N'AiWrite', 0, N'1', N'1', N'1', N'1', N'2024-07-08 09:26:44', N'1', N'2024-07-16 13:03:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2793, N'写作管理', N'', 2, 13, 2760, N'write', N'fa:bookmark-o', N'ai/write/manager/index.vue', N'AiWriteManager', 0, N'1', N'1', N'1', N'', N'2024-07-10 13:24:34', N'1', N'2024-07-10 21:31:59', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2794, N'AI 写作查询', N'ai:write:query', 3, 1, 2793, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-07-10 13:24:34', N'', N'2024-07-10 13:24:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2795, N'AI 写作删除', N'ai:write:delete', 3, 4, 2793, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-07-10 13:24:34', N'', N'2024-07-10 13:24:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2796, N'AI 音乐', N'', 2, 4, 2758, N'music', N'fa:music', N'ai/music/index/index.vue', N'AiMusic', 0, N'1', N'1', N'1', N'1', N'2024-07-17 09:21:12', N'1', N'2024-07-29 21:11:52', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2797, N'客服中心', N'', 2, 100, 2362, N'kefu', N'fa-solid:user-alt', N'mall/promotion/kefu/index', N'KeFu', 0, N'1', N'1', N'1', N'1', N'2024-07-17 23:49:05', N'1', N'2024-07-17 23:49:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2798, N'AI 思维导图', N'', 2, 6, 2758, N'mind-map', N'fa:sitemap', N'ai/mindmap/index/index.vue', N'AiMindMap', 0, N'1', N'1', N'1', N'1', N'2024-07-29 21:31:59', N'1', N'2025-03-02 18:57:31', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2799, N'导图管理', N'', 2, 14, 2760, N'mind-map', N'fa:map', N'ai/mindmap/manager/index', N'AiMindMapManager', 0, N'1', N'1', N'1', N'', N'2024-08-10 09:15:09', N'1', N'2024-08-10 17:24:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2800, N'思维导图查询', N'ai:mind-map:query', 3, 1, 2799, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-08-10 09:15:09', N'', N'2024-08-10 09:15:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2801, N'思维导图删除', N'ai:mind-map:delete', 3, 4, 2799, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-08-10 09:15:09', N'', N'2024-08-10 09:15:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2802, N'会话查询', N'promotion:kefu-conversation:query', 3, 1, 2797, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-08-31 09:17:52', N'1', N'2024-08-31 09:18:52', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2803, N'会话更新', N'promotion:kefu-conversation:update', 3, 2, 2797, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-08-31 09:18:15', N'1', N'2024-08-31 09:19:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2804, N'消息查询', N'promotion:kefu-message:query', 3, 10, 2797, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-08-31 09:18:42', N'1', N'2024-08-31 09:18:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2805, N'会话删除', N'promotion:kefu-conversation:delete', 3, 3, 2797, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-08-31 09:19:51', N'1', N'2024-08-31 09:20:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2806, N'消息发送', N'promotion:kefu-message:send', 3, 12, 2797, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-08-31 09:20:06', N'1', N'2024-08-31 09:20:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2807, N'消息更新', N'promotion:kefu-message:update', 3, 11, 2797, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-08-31 09:20:22', N'1', N'2024-08-31 09:20:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2808, N'积分商城', N'', 2, 5, 2030, N'point-activity', N'ep:bowl', N'mall/promotion/point/activity/index', N'PointActivity', 0, N'1', N'1', N'1', N'', N'2024-09-21 05:36:42', N'1', N'2024-09-23 09:14:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2809, N'积分商城活动查询', N'promotion:point-activity:query', 3, 1, 2808, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-21 05:36:42', N'1', N'2024-09-22 14:49:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2810, N'积分商城活动创建', N'promotion:point-activity:create', 3, 2, 2808, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-21 05:36:42', N'1', N'2024-09-22 14:49:08', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2811, N'积分商城活动更新', N'promotion:point-activity:update', 3, 3, 2808, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-21 05:36:42', N'1', N'2024-09-22 14:49:10', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2812, N'积分商城活动删除', N'promotion:point-activity:delete', 3, 4, 2808, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-21 05:36:42', N'1', N'2024-09-22 14:49:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2813, N'积分商城活动导出', N'promotion:point-activity:export', 3, 5, 2808, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-21 05:36:42', N'1', N'2024-09-22 14:49:27', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2912, N'创建推广员', N'trade:brokerage-user:create', 3, 7, 2346, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-12-01 14:32:39', N'1', N'2024-12-01 14:32:39', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2913, N'流程清理', N'bpm:model:clean', 3, 7, 1193, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-01-17 19:32:06', N'1', N'2025-01-17 19:32:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2914, N'积分商城活动关闭', N'promotion:point-activity:close', 3, 6, 2808, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-01-23 20:23:34', N'1', N'2025-01-23 20:23:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2915, N'AI 知识库', N'', 2, 5, 2758, N'knowledge', N'ep:notebook', N'ai/knowledge/knowledge/index', N'AiKnowledge', 0, N'1', N'1', N'1', N'', N'2025-02-28 07:04:21', N'1', N'2025-03-02 18:58:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2916, N'AI 知识库查询', N'ai:knowledge:query', 3, 1, 2915, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-02-28 07:04:21', N'', N'2025-02-28 07:04:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2917, N'AI 知识库创建', N'ai:knowledge:create', 3, 2, 2915, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-02-28 07:04:21', N'', N'2025-02-28 07:04:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2918, N'AI 知识库更新', N'ai:knowledge:update', 3, 3, 2915, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-02-28 07:04:21', N'', N'2025-02-28 07:04:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2919, N'AI 知识库删除', N'ai:knowledge:delete', 3, 4, 2915, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-02-28 07:04:21', N'', N'2025-02-28 07:04:21', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2920, N'工具管理', N'', 2, 0, 2760, N'tool', N'fa-solid:tools', N'ai/model/tool/index.vue', N'AiTool', 0, N'1', N'1', N'1', N'', N'2025-03-14 11:19:29', N'1', N'2025-03-14 19:20:18', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2921, N'工具查询', N'ai:tool:query', 3, 1, 2920, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-03-14 11:19:29', N'', N'2025-03-14 11:19:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2922, N'工具创建', N'ai:tool:create', 3, 2, 2920, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-03-14 11:19:29', N'', N'2025-03-14 11:19:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2923, N'工具更新', N'ai:tool:update', 3, 3, 2920, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-03-14 11:19:29', N'', N'2025-03-14 11:19:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (2924, N'工具删除', N'ai:tool:delete', 3, 4, 2920, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-03-14 11:19:29', N'', N'2025-03-14 11:19:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4000, N'IoT 物联网', N'', 1, 500, 0, N'/iot', N'fa-solid:hdd', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-08-10 09:55:28', N'1', N'2024-12-07 15:58:34', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4001, N'设备接入', N'', 1, 2, 4000, N'device', N'ep:platform', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-08-10 09:57:56', N'1', N'2025-02-27 08:39:49', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4002, N'产品管理', N'', 2, 2, 4001, N'product', N'fa-solid:tools', N'iot/product/product/index', N'IoTProduct', 0, N'1', N'1', N'1', N'', N'2024-08-10 02:38:02', N'1', N'2024-12-07 18:47:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4003, N'产品查询', N'iot:product:query', 3, 1, 4002, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-08-10 02:38:02', N'', N'2024-12-07 15:55:00', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4004, N'产品创建', N'iot:product:create', 3, 2, 4002, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-08-10 02:38:02', N'', N'2024-12-07 15:55:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4005, N'产品更新', N'iot:product:update', 3, 3, 4002, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-08-10 02:38:02', N'', N'2024-12-07 15:55:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4006, N'产品删除', N'iot:product:delete', 3, 4, 4002, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-08-10 02:38:02', N'', N'2024-12-07 15:55:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4007, N'产品导出', N'iot:product:export', 3, 5, 4002, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-08-10 02:38:02', N'', N'2024-12-07 15:55:13', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4008, N'设备管理', N'', 2, 4, 4001, N'device', N'fa:mobile', N'iot/device/device/index', N'IoTDevice', 0, N'1', N'1', N'1', N'', N'2024-09-16 18:48:19', N'1', N'2024-12-14 11:39:30', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4009, N'设备查询', N'iot:device:query', 3, 1, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-16 18:48:19', N'1', N'2024-12-07 15:55:40', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4010, N'设备创建', N'iot:device:create', 3, 2, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-16 18:48:19', N'1', N'2024-12-07 15:55:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4011, N'设备更新', N'iot:device:update', 3, 3, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-16 18:48:19', N'1', N'2024-12-07 15:55:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4012, N'设备删除', N'iot:device:delete', 3, 4, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-16 18:48:19', N'1', N'2024-12-07 15:55:43', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4013, N'设备导出', N'iot:device:export', 3, 5, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'', N'2024-09-16 18:48:19', N'1', N'2024-12-07 15:55:44', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4014, N'产品分类', N'', 2, 1, 4001, N'product-category', N'ep:notebook', N'iot/product/category/index', N'IotProductCategory', 0, N'1', N'1', N'1', N'', N'2024-12-07 16:01:35', N'1', N'2024-12-07 16:31:52', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4015, N'产品分类查询', N'iot:product-category:query', 3, 1, 4014, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-07 16:01:35', N'', N'2024-12-07 16:01:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4016, N'产品分类创建', N'iot:product-category:create', 3, 2, 4014, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-07 16:01:35', N'', N'2024-12-07 16:01:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4017, N'产品分类更新', N'iot:product-category:update', 3, 3, 4014, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-07 16:01:35', N'', N'2024-12-07 16:01:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4018, N'产品分类删除', N'iot:product-category:delete', 3, 4, 4014, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-07 16:01:35', N'', N'2024-12-07 16:01:35', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4025, N'插件管理', N'', 2, 5, 4047, N'plugin-config', N'ep:folder-opened', N'iot/plugin/index', N'IoTPlugin', 0, N'1', N'1', N'1', N'', N'2024-12-09 21:25:06', N'1', N'2025-02-05 22:23:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4026, N'插件查询', N'iot:plugin-config:query', 3, 1, 4025, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-09 21:25:06', N'', N'2025-02-05 21:23:20', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4027, N'插件创建', N'iot:plugin-config:create', 3, 2, 4025, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-09 21:25:06', N'', N'2025-02-05 21:23:16', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4028, N'插件更新', N'iot:plugin-config:update', 3, 3, 4025, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-09 21:25:06', N'', N'2025-02-05 21:23:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4029, N'插件删除', N'iot:plugin-config:delete', 3, 4, 4025, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-09 21:25:06', N'', N'2025-02-05 21:23:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4030, N'插件导出', N'iot:plugin-config:export', 3, 5, 4025, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-09 21:25:06', N'', N'2025-02-05 21:23:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4031, N'设备分组', N'', 2, 3, 4001, N'device-group', N'fa-solid:layer-group', N'iot/device/group/index', N'IotDeviceGroup', 0, N'1', N'1', N'1', N'', N'2024-12-14 17:08:29', N'1', N'2024-12-14 17:09:17', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4032, N'设备分组查询', N'iot:device-group:query', 3, 1, 4031, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-14 17:08:29', N'', N'2024-12-14 17:08:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4033, N'设备分组创建', N'iot:device-group:create', 3, 2, 4031, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-14 17:08:29', N'', N'2024-12-14 17:08:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4034, N'设备分组更新', N'iot:device-group:update', 3, 3, 4031, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-14 17:08:29', N'', N'2024-12-14 17:08:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4035, N'设备分组删除', N'iot:device-group:delete', 3, 4, 4031, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-14 17:08:29', N'', N'2024-12-14 17:08:29', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4036, N'设备导入', N'iot:device:import', 3, 6, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2024-12-15 10:35:47', N'1', N'2024-12-15 10:35:47', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4037, N'产品物模型', N'', 2, 2, 4001, N'thing-model', N'ep:mostly-cloudy', N'iot/thingmodel/index', N'IoTThingModel', 0, N'0', N'0', N'0', N'', N'2024-12-16 17:17:50', N'1', N'2024-12-27 11:03:37', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4038, N'产品物模型功能查询', N'iot:thing-model:query', 3, 1, 4037, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-16 17:17:51', N'', N'2025-03-17 09:14:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4039, N'产品物模型功能创建', N'iot:thing-model:create', 3, 2, 4037, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-16 17:17:52', N'', N'2025-03-17 09:14:58', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4040, N'产品物模型功能更新', N'iot:thing-model:update', 3, 3, 4037, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-16 17:17:52', N'', N'2025-03-17 09:15:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4041, N'产品物模型功能删除', N'iot:thing-model:delete', 3, 4, 4037, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-16 17:17:52', N'', N'2025-03-17 09:15:06', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4042, N'产品物模型功能导出', N'iot:thing-model:export', 3, 5, 4037, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2024-12-16 17:17:53', N'', N'2025-03-17 09:15:09', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4043, N'设备上行', N'iot:device:upstream', 3, 7, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-01-28 04:40:16', N'1', N'2025-01-31 22:45:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4044, N'设备属性查询', N'iot:device:property-query', 3, 10, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-01-28 11:52:54', N'1', N'2025-01-28 11:52:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4045, N'设备日志查询', N'iot:device:log-query', 3, 11, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-01-28 11:53:22', N'1', N'2025-01-28 11:53:22', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4046, N'设备下行', N'iot:device:downstream', 3, 8, 4008, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-01-31 22:46:11', N'1', N'2025-01-31 22:46:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4047, N'运维管理', N'', 1, 2, 4000, N'operations', N'fa:cog', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-02-05 22:21:37', N'1', N'2025-02-05 22:22:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4048, N'规则引擎', N'', 1, 3, 4000, N'rule', N'fa-solid:cogs', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-02-11 14:10:54', N'1', N'2025-02-11 14:10:54', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4049, N'场景联动', N'', 2, 1, 4048, N'scene', N'ep:link', N'iot/rule/scene/index', N'Scene', 0, N'1', N'1', N'1', N'1', N'2025-02-11 14:12:44', N'1', N'2025-02-12 10:15:36', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4050, N'IoT首页', N'', 2, 1, 4000, N'home', N'ep:home-filled', N'iot/home/index', N'IotHome', 0, N'1', N'1', N'1', N'1', N'2025-02-27 08:39:35', N'1', N'2025-02-27 08:40:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4051, N'数据桥梁', N'', 2, 0, 4048, N'data-bridge', N'ep:guide', N'iot/rule/databridge/index', N'IotDataBridge', 0, N'1', N'1', N'1', N'', N'2025-03-09 13:47:11', N'1', N'2025-03-09 13:47:51', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4052, N'IoT 数据桥梁查询', N'iot:data-bridge:query', 3, 1, 4051, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-03-09 13:47:11', N'', N'2025-03-09 13:47:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4053, N'IoT 数据桥梁创建', N'iot:data-bridge:create', 3, 2, 4051, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-03-09 13:47:11', N'', N'2025-03-09 13:47:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4054, N'IoT 数据桥梁更新', N'iot:data-bridge:update', 3, 3, 4051, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-03-09 13:47:11', N'', N'2025-03-09 13:47:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4055, N'IoT 数据桥梁删除', N'iot:data-bridge:delete', 3, 4, 4051, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-03-09 13:47:12', N'', N'2025-03-09 13:47:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (4056, N'IoT 数据桥梁导出', N'iot:data-bridge:export', 3, 5, 4051, N'', N'', N'', NULL, 0, N'1', N'1', N'1', N'', N'2025-03-09 13:47:12', N'', N'2025-03-09 13:47:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5000, N'AI 工作流', N'', 2, 5, 2758, N'workflow', N'fa:hand-grab-o', N'ai/workflow/index.vue', N'AiWorkflow', 0, N'1', N'1', N'1', N'1', N'2025-03-25 09:50:27', N'1', N'2025-05-03 18:55:12', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5001, N'AI 工作流查询', N'ai:workflow:query', 3, 1, 5000, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-03-25 09:51:11', N'1', N'2025-03-25 09:51:11', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5002, N'AI 工作流创建', N'ai:workflow:create', 3, 2, 5000, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-03-25 09:51:28', N'1', N'2025-03-25 09:51:28', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5003, N'AI 工作流更新', N'ai:workflow:update', 3, 3, 5000, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-03-25 09:51:42', N'1', N'2025-03-25 09:51:42', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5004, N'AI 工作流删除', N'ai:workflow:delete', 3, 4, 5000, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-03-25 09:51:55', N'1', N'2025-03-25 09:52:03', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5005, N'AI 工作流测试', N'ai:workflow:test', 3, 5, 5000, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-03-30 10:29:41', N'1', N'2025-03-30 10:29:41', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5009, N'仪表盘设计器', N'', 2, 1, 1281, N'jimu-bi', N'fa:y-combinator', N'report/jmreport/bi', N'JimuBI', 0, N'1', N'1', N'1', N'1', N'2025-05-03 09:57:15', N'1', N'2025-05-03 10:02:05', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5010, N'租户切换', N'system:tenant:visit', 3, 999, 1138, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-05-05 15:25:32', N'1', N'2025-05-05 15:25:32', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5011, N'转账订单查询', N'pay:transfer:query', 3, 1, 2559, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-05-08 12:46:53', N'1', N'2025-05-08 12:46:53', N'0')\nGO\nINSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES (5012, N'转账订单导出', N'pay:transfer:export', 3, 2, 2559, N'', N'', N'', N'', 0, N'1', N'1', N'1', N'1', N'2025-05-10 17:00:28', N'1', N'2025-05-10 17:00:28', N'0')\nGO\nSET IDENTITY_INSERT system_menu OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_notice\n-- ----------------------------\nDROP TABLE IF EXISTS system_notice\nGO\nCREATE TABLE system_notice\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    title       nvarchar(50)                           NOT NULL,\n    content     nvarchar(max)                          NOT NULL,\n    type        tinyint                                NOT NULL,\n    status      tinyint      DEFAULT 0                 NOT NULL,\n    creator     nvarchar(64) DEFAULT ''                NULL,\n    create_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64) DEFAULT ''                NULL,\n    update_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT 0                 NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'公告ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'公告标题',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'title'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'公告内容',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'content'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'公告类型（1通知 2公告）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'公告状态（0正常 1关闭）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'通知公告表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notice'\nGO\n\n-- ----------------------------\n-- Records of system_notice\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_notice ON\nGO\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, N'芋道的公众', N'<p>新版本内容133</p>', 1, 0, N'admin', N'2021-01-05 17:03:48', N'1', N'2022-05-04 21:00:20', N'0', 1)\nGO\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, N'维护通知：2018-07-01 系统凌晨维护', N'<p><img src=\"http://test.yudao.iocoder.cn/b7cb3cf49b4b3258bf7309a09dd2f4e5.jpg\" alt=\"\" data-href=\"\">11112222<img src=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\" alt=\"image\" data-href=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\">3333</p>', 2, 1, N'admin', N'2021-01-05 17:03:48', N'1', N'2025-04-18 23:56:40', N'0', 1)\nGO\nINSERT INTO system_notice (id, title, content, type, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, N'我是测试标题', N'<p>哈哈哈哈123</p>', 1, 0, N'110', N'2022-02-22 01:01:25', N'110', N'2022-02-22 01:01:46', N'0', 121)\nGO\nSET IDENTITY_INSERT system_notice OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_notify_message\n-- ----------------------------\nDROP TABLE IF EXISTS system_notify_message\nGO\nCREATE TABLE system_notify_message\n(\n    id                bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    user_id           bigint                                 NOT NULL,\n    user_type         tinyint                                NOT NULL,\n    template_id       bigint                                 NOT NULL,\n    template_code     nvarchar(64)                           NOT NULL,\n    template_nickname nvarchar(63)                           NOT NULL,\n    template_content  nvarchar(1024)                         NOT NULL,\n    template_type     int                                    NOT NULL,\n    template_params   nvarchar(255)                          NOT NULL,\n    read_status       varchar(1)                             NOT NULL,\n    read_time         datetime2    DEFAULT NULL              NULL,\n    creator           nvarchar(64) DEFAULT ''                NULL,\n    create_time       datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater           nvarchar(64) DEFAULT ''                NULL,\n    update_time       datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted           bit          DEFAULT 0                 NOT NULL,\n    tenant_id         bigint       DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户id',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模版编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'template_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'template_code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模版发送人名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'template_nickname'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模版内容',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'template_content'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模版类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'template_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模版参数',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'template_params'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否已读',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'read_status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'阅读时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'read_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'站内信消息表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_message'\nGO\n\n-- ----------------------------\n-- Records of system_notify_message\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_notify_message ON\nGO\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 1, 2, 1, N'test', N'123', N'我是 1，我开始 2 了', 1, N'{\"name\":\"1\",\"what\":\"2\"}', N'1', N'2025-04-21 14:59:37', N'1', N'2023-01-28 11:44:08', N'1', N'2025-04-21 14:59:37', N'0', 1)\nGO\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 1, 2, 1, N'test', N'123', N'我是 1，我开始 2 了', 1, N'{\"name\":\"1\",\"what\":\"2\"}', N'1', N'2025-04-21 14:59:37', N'1', N'2023-01-28 11:45:04', N'1', N'2025-04-21 14:59:37', N'0', 1)\nGO\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 103, 2, 2, N'register', N'系统消息', N'你好，欢迎 哈哈 加入大家庭！', 2, N'{\"name\":\"哈哈\"}', N'0', NULL, N'1', N'2023-01-28 21:02:20', N'1', N'2023-01-28 21:02:20', N'0', 1)\nGO\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 1, 2, 1, N'test', N'123', N'我是 芋艿，我开始 写代码 了', 1, N'{\"name\":\"芋艿\",\"what\":\"写代码\"}', N'1', N'2025-04-21 14:59:37', N'1', N'2023-01-28 22:21:42', N'1', N'2025-04-21 14:59:37', N'0', 1)\nGO\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 1, 2, 1, N'test', N'123', N'我是 芋艿，我开始 写代码 了', 1, N'{\"name\":\"芋艿\",\"what\":\"写代码\"}', N'1', N'2025-04-21 14:59:36', N'1', N'2023-01-28 22:22:07', N'1', N'2025-04-21 14:59:36', N'0', 1)\nGO\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 1, 2, 1, N'test', N'123', N'我是 2，我开始 3 了', 1, N'{\"name\":\"2\",\"what\":\"3\"}', N'1', N'2025-04-21 14:59:35', N'1', N'2023-01-28 23:45:21', N'1', N'2025-04-21 14:59:35', N'0', 1)\nGO\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 1, 2, 2, N'register', N'系统消息', N'你好，欢迎 123 加入大家庭！', 2, N'{\"name\":\"123\"}', N'1', N'2025-04-21 14:59:35', N'1', N'2023-01-28 23:50:21', N'1', N'2025-04-21 14:59:35', N'0', 1)\nGO\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 247, 1, 4, N'brokerage_withdraw_audit_approve', N'system', N'您在2023-09-28 08:35:46提现￥0.09元的申请已通过审核', 2, N'{\"reason\":null,\"createTime\":\"2023-09-28 08:35:46\",\"price\":\"0.09\"}', N'0', NULL, N'1', N'2023-09-28 16:36:22', N'1', N'2023-09-28 16:36:22', N'0', 1)\nGO\nINSERT INTO system_notify_message (id, user_id, user_type, template_id, template_code, template_nickname, template_content, template_type, template_params, read_status, read_time, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 247, 1, 4, N'brokerage_withdraw_audit_approve', N'system', N'您在2023-09-30 20:59:40提现￥1.00元的申请已通过审核', 2, N'{\"reason\":null,\"createTime\":\"2023-09-30 20:59:40\",\"price\":\"1.00\"}', N'0', NULL, N'1', N'2023-10-03 12:11:34', N'1', N'2023-10-03 12:11:34', N'0', 1)\nGO\nSET IDENTITY_INSERT system_notify_message OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_notify_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_notify_template\nGO\nCREATE TABLE system_notify_template\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name        nvarchar(63)                            NOT NULL,\n    code        nvarchar(64)                            NOT NULL,\n    nickname    nvarchar(255)                           NOT NULL,\n    content     nvarchar(1024)                          NOT NULL,\n    type        tinyint                                 NOT NULL,\n    params      nvarchar(255) DEFAULT NULL              NULL,\n    status      tinyint                                 NOT NULL,\n    remark      nvarchar(255) DEFAULT NULL              NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'主键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模版编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送人名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'nickname'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模版内容',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'content'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数数组',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'params'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'站内信模板表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_notify_template'\nGO\n\n-- ----------------------------\n-- Table structure for system_oauth2_access_token\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_access_token\nGO\nCREATE TABLE system_oauth2_access_token\n(\n    id            bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    user_id       bigint                                  NOT NULL,\n    user_type     tinyint                                 NOT NULL,\n    user_info     nvarchar(512)                           NOT NULL,\n    access_token  nvarchar(255)                           NOT NULL,\n    refresh_token nvarchar(32)                            NOT NULL,\n    client_id     nvarchar(255)                           NOT NULL,\n    scopes        nvarchar(255) DEFAULT NULL              NULL,\n    expires_time  datetime2                               NOT NULL,\n    creator       nvarchar(64)  DEFAULT ''                NULL,\n    create_time   datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       nvarchar(64)  DEFAULT ''                NULL,\n    update_time   datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       bit           DEFAULT 0                 NOT NULL,\n    tenant_id     bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nCREATE INDEX idx_system_oauth2_access_token_01 ON system_oauth2_access_token (access_token)\nGO\nCREATE INDEX idx_system_oauth2_access_token_02 ON system_oauth2_access_token (refresh_token)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户信息',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'user_info'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'访问令牌',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'access_token'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'刷新令牌',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'refresh_token'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'客户端编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'client_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'授权范围',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'scopes'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'过期时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'expires_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'OAuth2 访问令牌',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_access_token'\nGO\n\n-- ----------------------------\n-- Table structure for system_oauth2_approve\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_approve\nGO\nCREATE TABLE system_oauth2_approve\n(\n    id           bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    user_id      bigint                                  NOT NULL,\n    user_type    tinyint                                 NOT NULL,\n    client_id    nvarchar(255)                           NOT NULL,\n    scope        nvarchar(255) DEFAULT ''                NOT NULL,\n    approved     varchar(1)    DEFAULT '0'               NOT NULL,\n    expires_time datetime2                               NOT NULL,\n    creator      nvarchar(64)  DEFAULT ''                NULL,\n    create_time  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      nvarchar(64)  DEFAULT ''                NULL,\n    update_time  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      bit           DEFAULT 0                 NOT NULL,\n    tenant_id    bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'客户端编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'client_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'授权范围',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'scope'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否接受',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'approved'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'过期时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'expires_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'OAuth2 批准表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_approve'\nGO\n\n-- ----------------------------\n-- Table structure for system_oauth2_client\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_client\nGO\nCREATE TABLE system_oauth2_client\n(\n    id                             bigint                                   NOT NULL PRIMARY KEY IDENTITY,\n    client_id                      nvarchar(255)                            NOT NULL,\n    secret                         nvarchar(255)                            NOT NULL,\n    name                           nvarchar(255)                            NOT NULL,\n    logo                           nvarchar(255)                            NOT NULL,\n    description                    nvarchar(255)  DEFAULT NULL              NULL,\n    status                         tinyint                                  NOT NULL,\n    access_token_validity_seconds  int                                      NOT NULL,\n    refresh_token_validity_seconds int                                      NOT NULL,\n    redirect_uris                  nvarchar(255)                            NOT NULL,\n    authorized_grant_types         nvarchar(255)                            NOT NULL,\n    scopes                         nvarchar(255)  DEFAULT NULL              NULL,\n    auto_approve_scopes            nvarchar(255)  DEFAULT NULL              NULL,\n    authorities                    nvarchar(255)  DEFAULT NULL              NULL,\n    resource_ids                   nvarchar(255)  DEFAULT NULL              NULL,\n    additional_information         nvarchar(4000) DEFAULT NULL              NULL,\n    creator                        nvarchar(64)   DEFAULT ''                NULL,\n    create_time                    datetime2      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater                        nvarchar(64)   DEFAULT ''                NULL,\n    update_time                    datetime2      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted                        bit            DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'客户端编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'client_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'客户端密钥',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'secret'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'应用名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'应用图标',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'logo'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'应用描述',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'description'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'访问令牌的有效期',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'access_token_validity_seconds'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'刷新令牌的有效期',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'refresh_token_validity_seconds'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'可重定向的 URI 地址',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'redirect_uris'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'授权类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'authorized_grant_types'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'授权范围',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'scopes'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'自动通过的授权范围',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'auto_approve_scopes'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'权限',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'authorities'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'资源',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'resource_ids'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'附加信息',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'additional_information'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'OAuth2 客户端表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_client'\nGO\n\n-- ----------------------------\n-- Records of system_oauth2_client\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_oauth2_client ON\nGO\nINSERT 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, N'default', N'admin123', N'芋道源码', N'http://test.yudao.iocoder.cn/20250502/sort2_1746189740718.png', N'我是描述', 0, 1800, 2592000, N'[\"https://www.iocoder.cn\",\"https://doc.iocoder.cn\"]', N'[\"password\",\"authorization_code\",\"implicit\",\"refresh_token\"]', N'[\"user.read\",\"user.write\"]', N'[]', N'[\"user.read\",\"user.write\"]', N'[]', N'{}', N'1', N'2022-05-11 21:47:12', N'1', N'2025-05-02 20:42:22', N'0')\nGO\nINSERT 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, N'test', N'test2', N'biubiu', N'http://test.yudao.iocoder.cn/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.jpg', N'啦啦啦啦', 0, 1800, 43200, N'[\"https://www.iocoder.cn\"]', N'[\"password\",\"authorization_code\",\"implicit\"]', N'[\"user_info\",\"projects\"]', N'[\"user_info\"]', N'[]', N'[]', N'{}', N'1', N'2022-05-12 00:28:20', N'1', N'2025-05-02 19:58:08', N'0')\nGO\nINSERT 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, N'yudao-sso-demo-by-code', N'test', N'基于授权码模式，如何实现 SSO 单点登录？', N'http://test.yudao.iocoder.cn/it/20250502/sign_1746181948685.png', NULL, 0, 1800, 43200, N'[\"http://127.0.0.1:18080\"]', N'[\"authorization_code\",\"refresh_token\"]', N'[\"user.read\",\"user.write\"]', N'[]', N'[]', N'[]', NULL, N'1', N'2022-09-29 13:28:31', N'1', N'2025-05-02 18:32:30', N'0')\nGO\nINSERT 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, N'yudao-sso-demo-by-password', N'test', N'基于密码模式，如何实现 SSO 单点登录？', N'http://test.yudao.iocoder.cn/604bdc695e13b3b22745be704d1f2aa8ee05c5f26f9fead6d1ca49005afbc857.jpeg', NULL, 0, 1800, 43200, N'[\"http://127.0.0.1:18080\"]', N'[\"password\",\"refresh_token\"]', N'[\"user.read\",\"user.write\"]', N'[]', N'[]', N'[]', NULL, N'1', N'2022-10-04 17:40:16', N'1', N'2025-05-04 16:00:46', N'0')\nGO\nSET IDENTITY_INSERT system_oauth2_client OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_oauth2_code\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_code\nGO\nCREATE TABLE system_oauth2_code\n(\n    id           bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    user_id      bigint                                  NOT NULL,\n    user_type    tinyint                                 NOT NULL,\n    code         nvarchar(32)                            NOT NULL,\n    client_id    nvarchar(255)                           NOT NULL,\n    scopes       nvarchar(255) DEFAULT ''                NULL,\n    expires_time datetime2                               NOT NULL,\n    redirect_uri nvarchar(255) DEFAULT NULL              NULL,\n    state        nvarchar(255) DEFAULT ''                NOT NULL,\n    creator      nvarchar(64)  DEFAULT ''                NULL,\n    create_time  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      nvarchar(64)  DEFAULT ''                NULL,\n    update_time  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      bit           DEFAULT 0                 NOT NULL,\n    tenant_id    bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'授权码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'客户端编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'client_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'授权范围',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'scopes'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'过期时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'expires_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'可重定向的 URI 地址',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'redirect_uri'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'state'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'OAuth2 授权码表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_code'\nGO\n\n-- ----------------------------\n-- Table structure for system_oauth2_refresh_token\n-- ----------------------------\nDROP TABLE IF EXISTS system_oauth2_refresh_token\nGO\nCREATE TABLE system_oauth2_refresh_token\n(\n    id            bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    user_id       bigint                                  NOT NULL,\n    refresh_token nvarchar(32)                            NOT NULL,\n    user_type     tinyint                                 NOT NULL,\n    client_id     nvarchar(255)                           NOT NULL,\n    scopes        nvarchar(255) DEFAULT NULL              NULL,\n    expires_time  datetime2                               NOT NULL,\n    creator       nvarchar(64)  DEFAULT ''                NULL,\n    create_time   datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       nvarchar(64)  DEFAULT ''                NULL,\n    update_time   datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       bit           DEFAULT 0                 NOT NULL,\n    tenant_id     bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'刷新令牌',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'refresh_token'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'客户端编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'client_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'授权范围',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'scopes'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'过期时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'expires_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'OAuth2 刷新令牌',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_oauth2_refresh_token'\nGO\n\n-- ----------------------------\n-- Table structure for system_operate_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_operate_log\nGO\nCREATE TABLE system_operate_log\n(\n    id             bigint                                   NOT NULL PRIMARY KEY IDENTITY,\n    trace_id       nvarchar(64)   DEFAULT ''                NOT NULL,\n    user_id        bigint                                   NOT NULL,\n    user_type      tinyint        DEFAULT 0                 NOT NULL,\n    type           nvarchar(50)                             NOT NULL,\n    sub_type       nvarchar(50)                             NOT NULL,\n    biz_id         bigint                                   NOT NULL,\n    action         nvarchar(2000) DEFAULT ''                NOT NULL,\n    success        varchar(1)     DEFAULT '1'               NOT NULL,\n    extra          nvarchar(2000) DEFAULT ''                NOT NULL,\n    request_method nvarchar(16)   DEFAULT ''                NULL,\n    request_url    nvarchar(255)  DEFAULT ''                NULL,\n    user_ip        nvarchar(50)   DEFAULT NULL              NULL,\n    user_agent     nvarchar(512)  DEFAULT NULL              NULL,\n    creator        nvarchar(64)   DEFAULT ''                NULL,\n    create_time    datetime2      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        nvarchar(64)   DEFAULT ''                NULL,\n    update_time    datetime2      DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit            DEFAULT 0                 NOT NULL,\n    tenant_id      bigint         DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'日志主键',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'链路追踪编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'trace_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'操作模块类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'操作名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'sub_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'操作数据模块编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'biz_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'操作内容',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'action'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'操作结果',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'success'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'拓展字段',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'extra'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'请求方法名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'request_method'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'请求地址',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'request_url'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户 IP',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'user_ip'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'浏览器 UA',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'user_agent'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'操作日志记录 V2 版本',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_operate_log'\nGO\n\n-- ----------------------------\n-- Table structure for system_post\n-- ----------------------------\nDROP TABLE IF EXISTS system_post\nGO\nCREATE TABLE system_post\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    code        nvarchar(64)                            NOT NULL,\n    name        nvarchar(50)                            NOT NULL,\n    sort        int                                     NOT NULL,\n    status      tinyint                                 NOT NULL,\n    remark      nvarchar(500) DEFAULT NULL              NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL,\n    tenant_id   bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'岗位ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'岗位编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'岗位名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'显示顺序',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'sort'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'状态（0正常 1停用）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'岗位信息表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_post'\nGO\n\n-- ----------------------------\n-- Records of system_post\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_post ON\nGO\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, N'ceo', N'董事长', 1, 0, N'', N'admin', N'2021-01-06 17:03:48', N'1', N'2023-02-11 15:19:04', N'0', 1)\nGO\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, N'se', N'项目经理', 2, 0, N'', N'admin', N'2021-01-05 17:03:48', N'1', N'2023-11-15 09:18:20', N'0', 1)\nGO\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, N'user', N'普通员工', 4, 0, N'111222', N'admin', N'2021-01-05 17:03:48', N'1', N'2025-03-24 21:32:40', N'0', 1)\nGO\nINSERT INTO system_post (id, code, name, sort, status, remark, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, N'HR', N'人力资源', 5, 0, N'`', N'1', N'2024-03-24 20:45:40', N'1', N'2025-03-29 19:08:10', N'0', 1)\nGO\nSET IDENTITY_INSERT system_post OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_role\n-- ----------------------------\nDROP TABLE IF EXISTS system_role\nGO\nCREATE TABLE system_role\n(\n    id                  bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name                nvarchar(30)                            NOT NULL,\n    code                nvarchar(100)                           NOT NULL,\n    sort                int                                     NOT NULL,\n    data_scope          tinyint       DEFAULT 1                 NOT NULL,\n    data_scope_dept_ids nvarchar(500) DEFAULT ''                NOT NULL,\n    status              tinyint                                 NOT NULL,\n    type                tinyint                                 NOT NULL,\n    remark              nvarchar(500) DEFAULT NULL              NULL,\n    creator             nvarchar(64)  DEFAULT ''                NULL,\n    create_time         datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater             nvarchar(64)  DEFAULT ''                NULL,\n    update_time         datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted             bit           DEFAULT 0                 NOT NULL,\n    tenant_id           bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'角色ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'角色名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'角色权限字符串',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'显示顺序',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'sort'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'data_scope'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'数据范围 ( 指定部门数组)',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'data_scope_dept_ids'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'角色状态（0正常 1停用）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'角色类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'角色信息表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role'\nGO\n\n-- ----------------------------\n-- Records of system_role\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_role ON\nGO\nINSERT 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 (1, N'超级管理员', N'super_admin', 1, 1, N'', 0, 1, N'超级管理员', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-22 05:08:21', N'0', 1)\nGO\nINSERT 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 (2, N'普通角色', N'common', 2, 2, N'', 0, 1, N'普通角色', N'admin', N'2021-01-05 17:03:48', N'', N'2022-02-22 05:08:20', N'0', 1)\nGO\nINSERT 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, N'CRM 管理员', N'crm_admin', 2, 1, N'', 0, 1, N'CRM 专属角色', N'1', N'2024-02-24 10:51:13', N'1', N'2024-02-24 02:51:32', N'0', 1)\nGO\nINSERT 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 (101, N'测试账号', N'test', 0, 1, N'[]', 0, 2, N'123', N'', N'2021-01-06 13:49:35', N'1', N'2025-04-30 17:38:28', N'0', 1)\nGO\nINSERT 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, N'租户管理员', N'tenant_admin', 0, 1, N'', 0, 1, N'系统自动生成', N'1', N'2022-02-22 00:56:14', N'1', N'2022-02-22 00:56:14', N'0', 121)\nGO\nINSERT 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, N'租户管理员', N'tenant_admin', 0, 1, N'', 0, 1, N'系统自动生成', N'1', N'2022-03-07 21:37:58', N'1', N'2022-03-07 21:37:58', N'0', 122)\nGO\nINSERT 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, N'测试数据权限', N'test-dp', 3, 2, N'[100,102,103,104,105,108]', 0, 2, N'', N'1', N'2025-03-31 14:58:06', N'1', N'2025-04-17 23:07:44', N'0', 1)\nGO\nINSERT 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 (158, N'2', N'3', 4, 1, N'', 0, 2, NULL, N'1', N'2025-04-17 20:08:08', N'1', N'2025-04-17 23:05:31', N'0', 1)\nGO\nSET IDENTITY_INSERT system_role OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_role_menu\n-- ----------------------------\nDROP TABLE IF EXISTS system_role_menu\nGO\nCREATE TABLE system_role_menu\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    role_id     bigint                                 NOT NULL,\n    menu_id     bigint                                 NOT NULL,\n    creator     nvarchar(64) DEFAULT ''                NULL,\n    create_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64) DEFAULT ''                NULL,\n    update_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT 0                 NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'自增编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'角色ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu',\n     'COLUMN', N'role_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'菜单ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu',\n     'COLUMN', N'menu_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'角色和菜单关联表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_role_menu'\nGO\n\n-- ----------------------------\n-- Records of system_role_menu\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_role_menu ON\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (263, 109, 1, N'1', N'2022-02-22 00:56:14', N'1', N'2022-02-22 00:56:14', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (434, 2, 1, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (454, 2, 1093, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (455, 2, 1094, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (460, 2, 1100, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (467, 2, 1107, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (476, 2, 1117, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (477, 2, 100, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (478, 2, 101, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (479, 2, 102, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (480, 2, 1126, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (481, 2, 103, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (483, 2, 104, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (485, 2, 105, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (488, 2, 107, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (490, 2, 108, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (492, 2, 109, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (498, 2, 1138, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (523, 2, 1224, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (524, 2, 1225, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (541, 2, 500, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (543, 2, 501, N'1', N'2022-02-22 13:09:12', N'1', N'2022-02-22 13:09:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (675, 2, 2, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (689, 2, 1077, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (690, 2, 1078, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (692, 2, 1083, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (693, 2, 1084, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (699, 2, 1090, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (703, 2, 106, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (704, 2, 110, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (705, 2, 111, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (706, 2, 112, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (707, 2, 113, N'1', N'2022-02-22 13:16:57', N'1', N'2022-02-22 13:16:57', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1296, 110, 1, N'110', N'2022-02-23 00:23:55', N'110', N'2022-02-23 00:23:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1578, 111, 1, N'1', N'2022-03-07 21:37:58', N'1', N'2022-03-07 21:37:58', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1604, 101, 1216, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1605, 101, 1217, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1606, 101, 1218, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1607, 101, 1219, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1608, 101, 1220, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1609, 101, 1221, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1610, 101, 5, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1611, 101, 1222, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1612, 101, 1118, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1613, 101, 1119, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1614, 101, 1120, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1615, 101, 1185, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1616, 101, 1186, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1617, 101, 1187, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1618, 101, 1188, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1619, 101, 1189, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1620, 101, 1190, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1621, 101, 1191, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1622, 101, 1192, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1623, 101, 1193, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1624, 101, 1194, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1625, 101, 1195, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1627, 101, 1197, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1628, 101, 1198, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1629, 101, 1199, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1630, 101, 1200, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1631, 101, 1201, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1632, 101, 1202, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1633, 101, 1207, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1634, 101, 1208, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1635, 101, 1209, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1636, 101, 1210, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1637, 101, 1211, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1638, 101, 1212, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1639, 101, 1213, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1640, 101, 1215, N'1', N'2022-03-19 21:45:52', N'1', N'2022-03-19 21:45:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1641, 101, 2, N'1', N'2022-04-01 22:21:24', N'1', N'2022-04-01 22:21:24', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1642, 101, 1031, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1643, 101, 1032, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1644, 101, 1033, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1645, 101, 1034, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1646, 101, 1035, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1647, 101, 1050, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1648, 101, 1051, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1649, 101, 1052, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1650, 101, 1053, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1651, 101, 1054, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1652, 101, 1056, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1653, 101, 1057, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1654, 101, 1058, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1655, 101, 1059, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1656, 101, 1060, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1657, 101, 1066, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1658, 101, 1067, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1659, 101, 1070, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1664, 101, 1075, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1666, 101, 1077, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1667, 101, 1078, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1668, 101, 1082, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1669, 101, 1083, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1670, 101, 1084, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1671, 101, 1085, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1672, 101, 1086, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1673, 101, 1087, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1674, 101, 1088, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1675, 101, 1089, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1679, 101, 1237, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1680, 101, 1238, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1681, 101, 1239, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1682, 101, 1240, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1683, 101, 1241, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1684, 101, 1242, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1685, 101, 1243, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1687, 101, 106, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1688, 101, 110, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1689, 101, 111, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1690, 101, 112, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1691, 101, 113, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1692, 101, 114, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1693, 101, 115, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1694, 101, 116, N'1', N'2022-04-01 22:21:37', N'1', N'2022-04-01 22:21:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1729, 109, 100, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1730, 109, 101, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1731, 109, 1063, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1732, 109, 1064, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1733, 109, 1001, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1734, 109, 1065, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1735, 109, 1002, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1736, 109, 1003, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1737, 109, 1004, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1738, 109, 1005, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1739, 109, 1006, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1740, 109, 1007, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1741, 109, 1008, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1742, 109, 1009, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1743, 109, 1010, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1744, 109, 1011, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1745, 109, 1012, N'1', N'2022-09-21 22:08:51', N'1', N'2022-09-21 22:08:51', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1746, 111, 100, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1747, 111, 101, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1748, 111, 1063, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1749, 111, 1064, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1750, 111, 1001, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1751, 111, 1065, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1752, 111, 1002, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1753, 111, 1003, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1754, 111, 1004, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1755, 111, 1005, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1756, 111, 1006, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1757, 111, 1007, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1758, 111, 1008, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1759, 111, 1009, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1760, 111, 1010, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1761, 111, 1011, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1762, 111, 1012, N'1', N'2022-09-21 22:08:52', N'1', N'2022-09-21 22:08:52', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1763, 109, 100, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1764, 109, 101, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1765, 109, 1063, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1766, 109, 1064, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1767, 109, 1001, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1768, 109, 1065, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1769, 109, 1002, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1770, 109, 1003, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1771, 109, 1004, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1772, 109, 1005, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1773, 109, 1006, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1774, 109, 1007, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1775, 109, 1008, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1776, 109, 1009, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1777, 109, 1010, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1778, 109, 1011, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1779, 109, 1012, N'1', N'2022-09-21 22:08:53', N'1', N'2022-09-21 22:08:53', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1780, 111, 100, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1781, 111, 101, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1782, 111, 1063, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1783, 111, 1064, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1784, 111, 1001, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1785, 111, 1065, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1786, 111, 1002, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1787, 111, 1003, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1788, 111, 1004, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1789, 111, 1005, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1790, 111, 1006, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1791, 111, 1007, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1792, 111, 1008, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1793, 111, 1009, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1794, 111, 1010, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1795, 111, 1011, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1796, 111, 1012, N'1', N'2022-09-21 22:08:54', N'1', N'2022-09-21 22:08:54', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1797, 109, 100, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1798, 109, 101, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1799, 109, 1063, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1800, 109, 1064, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1801, 109, 1001, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1802, 109, 1065, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1803, 109, 1002, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1804, 109, 1003, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1805, 109, 1004, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1806, 109, 1005, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1807, 109, 1006, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1808, 109, 1007, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1809, 109, 1008, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1810, 109, 1009, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1811, 109, 1010, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1812, 109, 1011, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1813, 109, 1012, N'1', N'2022-09-21 22:08:55', N'1', N'2022-09-21 22:08:55', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1814, 111, 100, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1815, 111, 101, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1816, 111, 1063, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1817, 111, 1064, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1818, 111, 1001, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1819, 111, 1065, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1820, 111, 1002, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1821, 111, 1003, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1822, 111, 1004, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1823, 111, 1005, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1824, 111, 1006, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1825, 111, 1007, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1826, 111, 1008, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1827, 111, 1009, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1828, 111, 1010, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1829, 111, 1011, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1830, 111, 1012, N'1', N'2022-09-21 22:08:56', N'1', N'2022-09-21 22:08:56', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1831, 109, 103, N'1', N'2022-09-21 22:43:23', N'1', N'2022-09-21 22:43:23', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1832, 109, 1017, N'1', N'2022-09-21 22:43:23', N'1', N'2022-09-21 22:43:23', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1833, 109, 1018, N'1', N'2022-09-21 22:43:23', N'1', N'2022-09-21 22:43:23', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1834, 109, 1019, N'1', N'2022-09-21 22:43:23', N'1', N'2022-09-21 22:43:23', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1835, 109, 1020, N'1', N'2022-09-21 22:43:23', N'1', N'2022-09-21 22:43:23', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1836, 111, 103, N'1', N'2022-09-21 22:43:24', N'1', N'2022-09-21 22:43:24', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1837, 111, 1017, N'1', N'2022-09-21 22:43:24', N'1', N'2022-09-21 22:43:24', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1838, 111, 1018, N'1', N'2022-09-21 22:43:24', N'1', N'2022-09-21 22:43:24', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1839, 111, 1019, N'1', N'2022-09-21 22:43:24', N'1', N'2022-09-21 22:43:24', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1840, 111, 1020, N'1', N'2022-09-21 22:43:24', N'1', N'2022-09-21 22:43:24', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1841, 109, 1036, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1842, 109, 1037, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1843, 109, 1038, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1844, 109, 1039, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1845, 109, 107, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1846, 111, 1036, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1847, 111, 1037, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1848, 111, 1038, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1849, 111, 1039, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1850, 111, 107, N'1', N'2022-09-21 22:48:13', N'1', N'2022-09-21 22:48:13', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1991, 2, 1024, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1992, 2, 1025, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1993, 2, 1026, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1994, 2, 1027, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1995, 2, 1028, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1996, 2, 1029, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1997, 2, 1030, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1998, 2, 1031, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1999, 2, 1032, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2000, 2, 1033, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2001, 2, 1034, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2002, 2, 1035, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2003, 2, 1036, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2004, 2, 1037, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2005, 2, 1038, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2006, 2, 1039, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2007, 2, 1040, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2008, 2, 1042, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2009, 2, 1043, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2010, 2, 1045, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2011, 2, 1046, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2012, 2, 1048, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2013, 2, 1050, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2014, 2, 1051, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2015, 2, 1052, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2016, 2, 1053, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2017, 2, 1054, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2018, 2, 1056, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2019, 2, 1057, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2020, 2, 1058, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2021, 2, 2083, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2022, 2, 1059, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2023, 2, 1060, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2024, 2, 1063, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2025, 2, 1064, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2026, 2, 1065, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2027, 2, 1066, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2028, 2, 1067, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2029, 2, 1070, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2034, 2, 1075, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2036, 2, 1082, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2037, 2, 1085, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2038, 2, 1086, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2039, 2, 1087, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2040, 2, 1088, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2041, 2, 1089, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2042, 2, 1091, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2043, 2, 1092, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2044, 2, 1095, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2045, 2, 1096, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2046, 2, 1097, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2047, 2, 1098, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2048, 2, 1101, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2049, 2, 1102, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2050, 2, 1103, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2051, 2, 1104, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2052, 2, 1105, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2053, 2, 1106, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2054, 2, 1108, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2055, 2, 1109, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2061, 2, 1127, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2062, 2, 1128, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2063, 2, 1129, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2064, 2, 1130, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2066, 2, 1132, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2067, 2, 1133, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2068, 2, 1134, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2069, 2, 1135, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2070, 2, 1136, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2071, 2, 1137, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2072, 2, 114, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2073, 2, 1139, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2074, 2, 115, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2075, 2, 1140, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2076, 2, 116, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2077, 2, 1141, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2078, 2, 1142, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2079, 2, 1143, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2080, 2, 1150, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2081, 2, 1161, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2082, 2, 1162, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2086, 2, 1166, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2087, 2, 1173, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2088, 2, 1174, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2092, 2, 1178, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2099, 2, 1226, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2100, 2, 1227, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2101, 2, 1228, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2102, 2, 1229, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2103, 2, 1237, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2104, 2, 1238, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2105, 2, 1239, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2106, 2, 1240, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2107, 2, 1241, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2108, 2, 1242, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2109, 2, 1243, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2116, 2, 1254, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2117, 2, 1255, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2118, 2, 1256, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2119, 2, 1257, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2120, 2, 1258, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2121, 2, 1259, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2122, 2, 1260, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2123, 2, 1261, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2124, 2, 1263, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2125, 2, 1264, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2126, 2, 1265, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2127, 2, 1266, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2128, 2, 1267, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2129, 2, 1001, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2130, 2, 1002, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2131, 2, 1003, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2132, 2, 1004, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2133, 2, 1005, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2134, 2, 1006, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2135, 2, 1007, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2136, 2, 1008, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2137, 2, 1009, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2138, 2, 1010, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2139, 2, 1011, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2140, 2, 1012, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2141, 2, 1013, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2143, 2, 1015, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2145, 2, 1017, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2146, 2, 1018, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2147, 2, 1019, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2148, 2, 1020, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2149, 2, 1021, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2150, 2, 1022, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2151, 2, 1023, N'1', N'2023-01-25 08:42:52', N'1', N'2023-01-25 08:42:52', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2152, 2, 1281, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2153, 2, 1282, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2154, 2, 2000, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2155, 2, 2002, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2156, 2, 2003, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2157, 2, 2004, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2158, 2, 2005, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2159, 2, 2006, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2160, 2, 2008, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2161, 2, 2009, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2162, 2, 2010, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2163, 2, 2011, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2164, 2, 2012, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2170, 2, 2019, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2171, 2, 2020, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2172, 2, 2021, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2173, 2, 2022, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2174, 2, 2023, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2175, 2, 2025, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2177, 2, 2027, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2178, 2, 2028, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2179, 2, 2029, N'1', N'2023-01-25 08:42:58', N'1', N'2023-01-25 08:42:58', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2180, 2, 2014, N'1', N'2023-01-25 08:43:12', N'1', N'2023-01-25 08:43:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2181, 2, 2015, N'1', N'2023-01-25 08:43:12', N'1', N'2023-01-25 08:43:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2182, 2, 2016, N'1', N'2023-01-25 08:43:12', N'1', N'2023-01-25 08:43:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2183, 2, 2017, N'1', N'2023-01-25 08:43:12', N'1', N'2023-01-25 08:43:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2184, 2, 2018, N'1', N'2023-01-25 08:43:12', N'1', N'2023-01-25 08:43:12', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2188, 101, 1024, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2189, 101, 1, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2190, 101, 1025, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2191, 101, 1026, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2192, 101, 1027, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2193, 101, 1028, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2194, 101, 1029, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2195, 101, 1030, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2196, 101, 1036, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2197, 101, 1037, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2198, 101, 1038, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2199, 101, 1039, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2200, 101, 1040, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2201, 101, 1042, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2202, 101, 1043, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2203, 101, 1045, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2204, 101, 1046, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2205, 101, 1048, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2206, 101, 2083, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2207, 101, 1063, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2208, 101, 1064, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2209, 101, 1065, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2210, 101, 1093, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2211, 101, 1094, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2212, 101, 1095, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2213, 101, 1096, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2214, 101, 1097, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2215, 101, 1098, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2216, 101, 1100, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2217, 101, 1101, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2218, 101, 1102, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2219, 101, 1103, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2220, 101, 1104, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2221, 101, 1105, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2222, 101, 1106, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2223, 101, 2130, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2224, 101, 1107, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2225, 101, 2131, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2226, 101, 1108, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2227, 101, 2132, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2228, 101, 1109, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2229, 101, 2133, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2230, 101, 2134, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2232, 101, 2135, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2234, 101, 2136, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2236, 101, 2137, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2238, 101, 2138, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2240, 101, 2139, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2242, 101, 2140, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2243, 101, 2141, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2244, 101, 2142, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2245, 101, 2143, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2246, 101, 2144, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2247, 101, 2145, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2248, 101, 2146, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2249, 101, 2147, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2250, 101, 100, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2251, 101, 2148, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2252, 101, 101, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2253, 101, 2149, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2254, 101, 102, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2255, 101, 2150, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2256, 101, 103, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2257, 101, 2151, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2258, 101, 104, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2259, 101, 2152, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2260, 101, 105, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2261, 101, 107, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2262, 101, 108, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2263, 101, 109, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2264, 101, 1138, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2265, 101, 1139, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2266, 101, 1140, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2267, 101, 1141, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2268, 101, 1142, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2269, 101, 1143, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2270, 101, 1224, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2271, 101, 1225, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2272, 101, 1226, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2273, 101, 1227, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2274, 101, 1228, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2275, 101, 1229, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2282, 101, 1261, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2283, 101, 1263, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2284, 101, 1264, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2285, 101, 1265, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2286, 101, 1266, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2287, 101, 1267, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2288, 101, 1001, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2289, 101, 1002, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2290, 101, 1003, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2291, 101, 1004, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2292, 101, 1005, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2293, 101, 1006, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2294, 101, 1007, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2295, 101, 1008, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2296, 101, 1009, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2297, 101, 1010, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2298, 101, 1011, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2299, 101, 1012, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2300, 101, 500, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2301, 101, 1013, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2302, 101, 501, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2303, 101, 1014, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2304, 101, 1015, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2305, 101, 1016, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2306, 101, 1017, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2307, 101, 1018, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2308, 101, 1019, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2309, 101, 1020, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2310, 101, 1021, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2311, 101, 1022, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2312, 101, 1023, N'1', N'2023-02-09 23:49:46', N'1', N'2023-02-09 23:49:46', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2929, 109, 1224, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2930, 109, 1225, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2931, 109, 1226, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2932, 109, 1227, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2933, 109, 1228, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2934, 109, 1229, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2935, 109, 1138, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2936, 109, 1139, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2937, 109, 1140, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2938, 109, 1141, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2939, 109, 1142, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2940, 109, 1143, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2941, 111, 1224, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2942, 111, 1225, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2943, 111, 1226, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2944, 111, 1227, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2945, 111, 1228, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2946, 111, 1229, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2947, 111, 1138, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2948, 111, 1139, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2949, 111, 1140, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2950, 111, 1141, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2951, 111, 1142, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2952, 111, 1143, N'1', N'2023-12-02 23:19:40', N'1', N'2023-12-02 23:19:40', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2993, 109, 2, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2994, 109, 1031, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2995, 109, 1032, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2996, 109, 1033, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2997, 109, 1034, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2998, 109, 1035, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2999, 109, 1050, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3000, 109, 1051, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3001, 109, 1052, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3002, 109, 1053, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3003, 109, 1054, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3004, 109, 1056, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3005, 109, 1057, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3006, 109, 1058, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3007, 109, 1059, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3008, 109, 1060, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3009, 109, 1066, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3010, 109, 1067, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3011, 109, 1070, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3012, 109, 1075, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3014, 109, 1077, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3015, 109, 1078, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3016, 109, 1082, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3017, 109, 1083, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3018, 109, 1084, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3019, 109, 1085, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3020, 109, 1086, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3021, 109, 1087, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3022, 109, 1088, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3023, 109, 1089, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3024, 109, 1090, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3025, 109, 1091, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3026, 109, 1092, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3027, 109, 106, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3028, 109, 110, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3029, 109, 111, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3030, 109, 112, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3031, 109, 113, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3032, 109, 114, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3033, 109, 115, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3034, 109, 116, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3035, 109, 2472, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3036, 109, 2478, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3037, 109, 2479, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3038, 109, 2480, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3039, 109, 2481, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3040, 109, 2482, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3041, 109, 2483, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3042, 109, 2484, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3043, 109, 2485, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3044, 109, 2486, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3045, 109, 2487, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3046, 109, 2488, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3047, 109, 2489, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3048, 109, 2490, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3049, 109, 2491, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3050, 109, 2492, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3051, 109, 2493, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3052, 109, 2494, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3053, 109, 2495, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3054, 109, 2497, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3055, 109, 1237, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3056, 109, 1238, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3057, 109, 1239, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3058, 109, 1240, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3059, 109, 1241, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3060, 109, 1242, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3061, 109, 1243, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3062, 109, 2525, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3063, 109, 1255, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3064, 109, 1256, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3065, 109, 1257, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3066, 109, 1258, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3067, 109, 1259, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3068, 109, 1260, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3069, 111, 2, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3070, 111, 1031, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3071, 111, 1032, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3072, 111, 1033, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3073, 111, 1034, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3074, 111, 1035, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3075, 111, 1050, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3076, 111, 1051, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3077, 111, 1052, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3078, 111, 1053, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3079, 111, 1054, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3080, 111, 1056, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3081, 111, 1057, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3082, 111, 1058, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3083, 111, 1059, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3084, 111, 1060, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3085, 111, 1066, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3086, 111, 1067, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3087, 111, 1070, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3088, 111, 1075, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3090, 111, 1077, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3091, 111, 1078, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3092, 111, 1082, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3093, 111, 1083, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3094, 111, 1084, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3095, 111, 1085, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3096, 111, 1086, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3097, 111, 1087, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3098, 111, 1088, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3099, 111, 1089, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3100, 111, 1090, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3101, 111, 1091, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3102, 111, 1092, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3103, 111, 106, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3104, 111, 110, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3105, 111, 111, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3106, 111, 112, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3107, 111, 113, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3108, 111, 114, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3109, 111, 115, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3110, 111, 116, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3111, 111, 2472, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3112, 111, 2478, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3113, 111, 2479, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3114, 111, 2480, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3115, 111, 2481, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3116, 111, 2482, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3117, 111, 2483, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3118, 111, 2484, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3119, 111, 2485, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3120, 111, 2486, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3121, 111, 2487, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3122, 111, 2488, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3123, 111, 2489, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3124, 111, 2490, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3125, 111, 2491, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3126, 111, 2492, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3127, 111, 2493, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3128, 111, 2494, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3129, 111, 2495, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3130, 111, 2497, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3131, 111, 1237, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3132, 111, 1238, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3133, 111, 1239, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3134, 111, 1240, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3135, 111, 1241, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3136, 111, 1242, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3137, 111, 1243, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3138, 111, 2525, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3139, 111, 1255, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3140, 111, 1256, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3141, 111, 1257, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3142, 111, 1258, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3143, 111, 1259, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3144, 111, 1260, N'1', N'2023-12-02 23:41:02', N'1', N'2023-12-02 23:41:02', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3221, 109, 102, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3222, 109, 1013, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3223, 109, 1014, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3224, 109, 1015, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3225, 109, 1016, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3226, 111, 102, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3227, 111, 1013, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3228, 111, 1014, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3229, 111, 1015, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3230, 111, 1016, N'1', N'2023-12-30 11:42:36', N'1', N'2023-12-30 11:42:36', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4163, 109, 5, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4164, 109, 1118, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4165, 109, 1119, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4166, 109, 1120, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4167, 109, 2713, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4168, 109, 2714, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4169, 109, 2715, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4170, 109, 2716, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4171, 109, 2717, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4172, 109, 2718, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4173, 109, 2720, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4174, 109, 1185, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4175, 109, 2721, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4176, 109, 1186, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4177, 109, 2722, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4178, 109, 1187, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4179, 109, 2723, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4180, 109, 1188, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4181, 109, 2724, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4182, 109, 1189, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4183, 109, 2725, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4184, 109, 1190, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4185, 109, 2726, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4186, 109, 1191, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4187, 109, 2727, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4188, 109, 1192, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4189, 109, 2728, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4190, 109, 1193, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4191, 109, 2729, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4192, 109, 1194, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4193, 109, 2730, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4194, 109, 1195, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4195, 109, 2731, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4196, 109, 1196, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4197, 109, 2732, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4198, 109, 1197, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4199, 109, 2733, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4200, 109, 1198, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4201, 109, 2734, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4202, 109, 1199, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4203, 109, 2735, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4204, 109, 1200, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4205, 109, 1201, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4206, 109, 1202, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4207, 109, 1207, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4208, 109, 1208, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4209, 109, 1209, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4210, 109, 1210, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4211, 109, 1211, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4212, 109, 1212, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4213, 109, 1213, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4214, 109, 1215, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4215, 109, 1216, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4216, 109, 1217, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4217, 109, 1218, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4218, 109, 1219, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4219, 109, 1220, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4220, 109, 1221, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4221, 109, 1222, N'1', N'2024-03-30 17:53:17', N'1', N'2024-03-30 17:53:17', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4222, 111, 5, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4223, 111, 1118, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4224, 111, 1119, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4225, 111, 1120, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4226, 111, 2713, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4227, 111, 2714, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4228, 111, 2715, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4229, 111, 2716, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4230, 111, 2717, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4231, 111, 2718, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4232, 111, 2720, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4233, 111, 1185, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4234, 111, 2721, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4235, 111, 1186, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4236, 111, 2722, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4237, 111, 1187, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4238, 111, 2723, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4239, 111, 1188, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4240, 111, 2724, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4241, 111, 1189, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4242, 111, 2725, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4243, 111, 1190, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4244, 111, 2726, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4245, 111, 1191, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4246, 111, 2727, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4247, 111, 1192, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4248, 111, 2728, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4249, 111, 1193, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4250, 111, 2729, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4251, 111, 1194, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4252, 111, 2730, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4253, 111, 1195, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4254, 111, 2731, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4255, 111, 1196, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4256, 111, 2732, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4257, 111, 1197, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4258, 111, 2733, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4259, 111, 1198, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4260, 111, 2734, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4261, 111, 1199, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4262, 111, 2735, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4263, 111, 1200, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4264, 111, 1201, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4265, 111, 1202, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4266, 111, 1207, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4267, 111, 1208, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4268, 111, 1209, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4269, 111, 1210, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4270, 111, 1211, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4271, 111, 1212, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4272, 111, 1213, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4273, 111, 1215, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4274, 111, 1216, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4275, 111, 1217, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4276, 111, 1218, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4277, 111, 1219, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4278, 111, 1220, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4279, 111, 1221, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4280, 111, 1222, N'1', N'2024-03-30 17:53:18', N'1', N'2024-03-30 17:53:18', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5777, 101, 2739, N'1', N'2024-04-30 09:38:37', N'1', N'2024-04-30 09:38:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5778, 101, 2740, N'1', N'2024-04-30 09:38:37', N'1', N'2024-04-30 09:38:37', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5779, 2, 2739, N'1', N'2024-07-07 20:39:38', N'1', N'2024-07-07 20:39:38', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5780, 2, 2740, N'1', N'2024-07-07 20:39:38', N'1', N'2024-07-07 20:39:38', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5781, 2, 2758, N'1', N'2024-07-07 20:39:38', N'1', N'2024-07-07 20:39:38', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5782, 2, 2759, N'1', N'2024-07-07 20:39:38', N'1', N'2024-07-07 20:39:38', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5783, 2, 2362, N'1', N'2024-07-07 20:39:38', N'1', N'2024-07-07 20:39:38', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5784, 2, 2387, N'1', N'2024-07-07 20:39:38', N'1', N'2024-07-07 20:39:38', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5785, 2, 2030, N'1', N'2024-07-07 20:39:38', N'1', N'2024-07-07 20:39:38', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5786, 101, 2758, N'1', N'2024-07-07 20:39:55', N'1', N'2024-07-07 20:39:55', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5787, 101, 2759, N'1', N'2024-07-07 20:39:55', N'1', N'2024-07-07 20:39:55', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5788, 101, 2783, N'1', N'2024-07-07 20:39:55', N'1', N'2024-07-07 20:39:55', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5789, 109, 2739, N'1', N'2024-07-13 22:37:24', N'1', N'2024-07-13 22:37:24', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5790, 109, 2740, N'1', N'2024-07-13 22:37:24', N'1', N'2024-07-13 22:37:24', N'0', 121)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5791, 111, 2739, N'1', N'2024-07-13 22:37:24', N'1', N'2024-07-13 22:37:24', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5792, 111, 2740, N'1', N'2024-07-13 22:37:24', N'1', N'2024-07-13 22:37:24', N'0', 122)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6053, 155, 4000, N'1', N'2025-04-01 13:48:26', N'1', N'2025-04-01 13:48:26', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6097, 155, 4050, N'1', N'2025-04-01 13:48:26', N'1', N'2025-04-01 13:48:26', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6104, 155, 4032, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6105, 155, 4033, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6106, 155, 4034, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6107, 155, 4035, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6108, 155, 4036, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6109, 155, 4037, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6110, 155, 4038, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6111, 155, 4039, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6112, 155, 4040, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6113, 155, 4041, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6114, 155, 4042, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6115, 155, 4043, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6116, 155, 4044, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6117, 155, 4045, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6118, 155, 4046, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6119, 155, 4001, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6120, 155, 4002, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6121, 155, 4003, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6122, 155, 4004, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6123, 155, 4005, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6124, 155, 4006, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6125, 155, 4007, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6126, 155, 4008, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6127, 155, 4009, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6128, 155, 4010, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6129, 155, 4011, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6130, 155, 4012, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6131, 155, 4013, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6132, 155, 4014, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6133, 155, 4015, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6134, 155, 4016, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6135, 155, 4017, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6136, 155, 4018, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6137, 155, 4031, N'1', N'2025-04-01 13:49:30', N'1', N'2025-04-01 13:49:30', N'0', 1)\nGO\nINSERT INTO system_role_menu (id, role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6138, 101, 5010, N'1', N'2025-05-05 17:49:17', N'1', N'2025-05-05 17:49:17', N'0', 1)\nGO\nSET IDENTITY_INSERT system_role_menu OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_sms_channel\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_channel\nGO\nCREATE TABLE system_sms_channel\n(\n    id           bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    signature    nvarchar(12)                            NOT NULL,\n    code         nvarchar(63)                            NOT NULL,\n    status       tinyint                                 NOT NULL,\n    remark       nvarchar(255) DEFAULT NULL              NULL,\n    api_key      nvarchar(128)                           NOT NULL,\n    api_secret   nvarchar(128) DEFAULT NULL              NULL,\n    callback_url nvarchar(255) DEFAULT NULL              NULL,\n    creator      nvarchar(64)  DEFAULT ''                NULL,\n    create_time  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater      nvarchar(64)  DEFAULT ''                NULL,\n    update_time  datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted      bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信签名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'signature'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'渠道编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'开启状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信 API 的账号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'api_key'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信 API 的秘钥',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'api_secret'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信发送回调 URL',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'callback_url'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信渠道',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_channel'\nGO\n\n-- ----------------------------\n-- Records of system_sms_channel\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_sms_channel ON\nGO\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (2, N'Ballcat', N'ALIYUN', 0, N'你要改哦，只有我可以用！！！！', N'LTAI5tCnKso2uG3kJ5gRav88', N'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, N'', N'2021-03-31 11:53:10', N'1', N'2024-08-04 08:53:26', N'0')\nGO\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (4, N'测试渠道', N'DEBUG_DING_TALK', 0, N'123', N'696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859', N'SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67', NULL, N'1', N'2021-04-13 00:23:14', N'1', N'2022-03-27 20:29:49', N'0')\nGO\nINSERT INTO system_sms_channel (id, signature, code, status, remark, api_key, api_secret, callback_url, creator, create_time, updater, update_time, deleted) VALUES (7, N'mock腾讯云', N'TENCENT', 0, N'', N'1 2', N'2 3', N'', N'1', N'2024-09-30 08:53:45', N'1', N'2024-09-30 08:55:01', N'0')\nGO\nSET IDENTITY_INSERT system_sms_channel OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_sms_code\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_code\nGO\nCREATE TABLE system_sms_code\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    mobile      nvarchar(11)                            NOT NULL,\n    code        nvarchar(6)                             NOT NULL,\n    create_ip   nvarchar(15)                            NOT NULL,\n    scene       tinyint                                 NOT NULL,\n    today_index tinyint                                 NOT NULL,\n    used        tinyint                                 NOT NULL,\n    used_time   datetime2     DEFAULT NULL              NULL,\n    used_ip     nvarchar(255) DEFAULT NULL              NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL,\n    tenant_id   bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nCREATE INDEX idx_system_sms_code_01 ON system_sms_code (mobile)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'手机号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'mobile'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'验证码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建 IP',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'create_ip'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送场景',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'scene'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'今日发送的第几条',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'today_index'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否使用',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'used'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'使用时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'used_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'使用 IP',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'used_ip'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'手机验证码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_code'\nGO\n\n-- ----------------------------\n-- Table structure for system_sms_log\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_log\nGO\nCREATE TABLE system_sms_log\n(\n    id               bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    channel_id       bigint                                  NOT NULL,\n    channel_code     nvarchar(63)                            NOT NULL,\n    template_id      bigint                                  NOT NULL,\n    template_code    nvarchar(63)                            NOT NULL,\n    template_type    tinyint                                 NOT NULL,\n    template_content nvarchar(255)                           NOT NULL,\n    template_params  nvarchar(255)                           NOT NULL,\n    api_template_id  nvarchar(63)                            NOT NULL,\n    mobile           nvarchar(11)                            NOT NULL,\n    user_id          bigint        DEFAULT NULL              NULL,\n    user_type        tinyint       DEFAULT NULL              NULL,\n    send_status      tinyint       DEFAULT 0                 NOT NULL,\n    send_time        datetime2     DEFAULT NULL              NULL,\n    api_send_code    nvarchar(63)  DEFAULT NULL              NULL,\n    api_send_msg     nvarchar(255) DEFAULT NULL              NULL,\n    api_request_id   nvarchar(255) DEFAULT NULL              NULL,\n    api_serial_no    nvarchar(255) DEFAULT NULL              NULL,\n    receive_status   tinyint       DEFAULT 0                 NOT NULL,\n    receive_time     datetime2     DEFAULT NULL              NULL,\n    api_receive_code nvarchar(63)  DEFAULT NULL              NULL,\n    api_receive_msg  nvarchar(255) DEFAULT NULL              NULL,\n    creator          nvarchar(64)  DEFAULT ''                NULL,\n    create_time      datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater          nvarchar(64)  DEFAULT ''                NULL,\n    update_time      datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted          bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信渠道编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'channel_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信渠道编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'channel_code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'template_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'template_code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'template_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信内容',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'template_content'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信参数',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'template_params'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信 API 的模板编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'api_template_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'手机号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'mobile'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'send_status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'发送时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'send_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信 API 发送结果的编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'api_send_code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信 API 发送失败的提示',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'api_send_msg'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信 API 发送返回的唯一请求 ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'api_request_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信 API 发送返回的序号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'api_serial_no'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'接收状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'receive_status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'接收时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'receive_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'API 接收结果的编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'api_receive_code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'API 接收结果的说明',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'api_receive_msg'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信日志',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_log'\nGO\n\n-- ----------------------------\n-- Table structure for system_sms_template\n-- ----------------------------\nDROP TABLE IF EXISTS system_sms_template\nGO\nCREATE TABLE system_sms_template\n(\n    id              bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    type            tinyint                                 NOT NULL,\n    status          tinyint                                 NOT NULL,\n    code            nvarchar(63)                            NOT NULL,\n    name            nvarchar(63)                            NOT NULL,\n    content         nvarchar(255)                           NOT NULL,\n    params          nvarchar(255)                           NOT NULL,\n    remark          nvarchar(255) DEFAULT NULL              NULL,\n    api_template_id nvarchar(63)                            NOT NULL,\n    channel_id      bigint                                  NOT NULL,\n    channel_code    nvarchar(63)                            NOT NULL,\n    creator         nvarchar(64)  DEFAULT ''                NULL,\n    create_time     datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         nvarchar(64)  DEFAULT ''                NULL,\n    update_time     datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'开启状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板名称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'模板内容',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'content'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'参数数组',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'params'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信 API 的模板编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'api_template_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信渠道编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'channel_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信渠道编码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'channel_code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'短信模板',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_sms_template'\nGO\n\n-- ----------------------------\n-- Records of system_sms_template\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_sms_template ON\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (2, 1, 0, N'test_01', N'测试验证码短信', N'正在进行登录操作{operation}，您的验证码是{code}', N'[\"operation\",\"code\"]', N'测试备注', N'4383920', 4, N'DEBUG_DING_TALK', N'', N'2021-03-31 10:49:38', N'1', N'2024-08-18 11:57:18', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (3, 1, 0, N'test_02', N'公告通知', N'您的验证码{code}，该验证码5分钟内有效，请勿泄漏于他人！', N'[\"code\"]', NULL, N'SMS_207945135', 2, N'ALIYUN', N'', N'2021-03-31 11:56:30', N'1', N'2021-04-10 01:22:02', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (6, 3, 0, N'test-01', N'测试模板', N'哈哈哈 {name}', N'[\"name\"]', N'f哈哈哈', N'4383920', 4, N'DEBUG_DING_TALK', N'1', N'2021-04-10 01:07:21', N'1', N'2024-08-18 11:57:07', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (7, 3, 0, N'test-04', N'测试下', N'老鸡{name}，牛逼{code}', N'[\"name\",\"code\"]', N'哈哈哈哈', N'suibian', 7, N'DEBUG_DING_TALK', N'1', N'2021-04-13 00:29:53', N'1', N'2024-09-30 00:56:24', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (8, 1, 0, N'user-sms-login', N'前台用户短信登录', N'您的验证码是{code}', N'[\"code\"]', NULL, N'4372216', 4, N'DEBUG_DING_TALK', N'1', N'2021-10-11 08:10:00', N'1', N'2024-08-18 11:57:06', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (9, 2, 0, N'bpm_task_assigned', N'【工作流】任务被分配', N'您收到了一条新的待办任务：{processInstanceName}-{taskName}，申请人：{startUserNickname}，处理链接：{detailUrl}', N'[\"processInstanceName\",\"taskName\",\"startUserNickname\",\"detailUrl\"]', NULL, N'suibian', 4, N'DEBUG_DING_TALK', N'1', N'2022-01-21 22:31:19', N'1', N'2022-01-22 00:03:36', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (10, 2, 0, N'bpm_process_instance_reject', N'【工作流】流程被不通过', N'您的流程被审批不通过：{processInstanceName}，原因：{reason}，查看链接：{detailUrl}', N'[\"processInstanceName\",\"reason\",\"detailUrl\"]', NULL, N'suibian', 4, N'DEBUG_DING_TALK', N'1', N'2022-01-22 00:03:31', N'1', N'2022-05-01 12:33:14', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (11, 2, 0, N'bpm_process_instance_approve', N'【工作流】流程被通过', N'您的流程被审批通过：{processInstanceName}，查看链接：{detailUrl}', N'[\"processInstanceName\",\"detailUrl\"]', NULL, N'suibian', 4, N'DEBUG_DING_TALK', N'1', N'2022-01-22 00:04:31', N'1', N'2022-03-27 20:32:21', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (12, 2, 0, N'demo', N'演示模板', N'我就是测试一下下', N'[]', NULL, N'biubiubiu', 4, N'DEBUG_DING_TALK', N'1', N'2022-04-10 23:22:49', N'1', N'2024-08-18 11:57:04', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (14, 1, 0, N'user-update-mobile', N'会员用户 - 修改手机', N'您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', N'[\"code\"]', N'', N'null', 4, N'DEBUG_DING_TALK', N'1', N'2023-08-19 18:58:01', N'1', N'2023-08-19 11:34:04', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (15, 1, 0, N'user-update-password', N'会员用户 - 修改密码', N'您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', N'[\"code\"]', N'', N'null', 4, N'DEBUG_DING_TALK', N'1', N'2023-08-19 18:58:01', N'1', N'2023-08-19 11:34:18', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (16, 1, 0, N'user-reset-password', N'会员用户 - 重置密码', N'您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', N'[\"code\"]', N'', N'null', 4, N'DEBUG_DING_TALK', N'1', N'2023-08-19 18:58:01', N'1', N'2023-12-02 22:35:27', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (17, 2, 0, N'bpm_task_timeout', N'【工作流】任务审批超时', N'您收到了一条超时的待办任务：{processInstanceName}-{taskName}，处理链接：{detailUrl}', N'[\"processInstanceName\",\"taskName\",\"detailUrl\"]', N'', N'X', 4, N'DEBUG_DING_TALK', N'1', N'2024-08-16 21:59:15', N'1', N'2024-08-16 21:59:34', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (18, 1, 0, N'admin-reset-password', N'后台用户 - 忘记密码', N'您的验证码{code}，该验证码 5 分钟内有效，请勿泄漏于他人！', N'[\"code\"]', N'', N'null', 4, N'DEBUG_DING_TALK', N'1', N'2025-03-16 14:19:34', N'1', N'2025-03-16 14:19:45', N'0')\nGO\nINSERT INTO system_sms_template (id, type, status, code, name, content, params, remark, api_template_id, channel_id, channel_code, creator, create_time, updater, update_time, deleted) VALUES (19, 1, 0, N'admin-sms-login', N'后台用户短信登录', N'您的验证码是{code}', N'[\"code\"]', N'', N'4372216', 4, N'DEBUG_DING_TALK', N'1', N'2025-04-08 09:36:03', N'1', N'2025-04-08 09:36:17', N'0')\nGO\nSET IDENTITY_INSERT system_sms_template OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_social_client\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_client\nGO\nCREATE TABLE system_social_client\n(\n    id            bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name          nvarchar(255)                           NOT NULL,\n    social_type   tinyint                                 NOT NULL,\n    user_type     tinyint                                 NOT NULL,\n    client_id     nvarchar(255)                           NOT NULL,\n    client_secret nvarchar(2048)                           NOT NULL,\n    public_key    nvarchar(2048) DEFAULT NULL              NULL,\n    agent_id      nvarchar(255) DEFAULT NULL              NULL,\n    status        tinyint                                 NOT NULL,\n    creator       nvarchar(64)  DEFAULT ''                NULL,\n    create_time   datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater       nvarchar(64)  DEFAULT ''                NULL,\n    update_time   datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted       bit           DEFAULT 0                 NOT NULL,\n    tenant_id     bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'应用名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'社交平台的类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'social_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'客户端编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'client_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'客户端密钥',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'client_secret'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'publicKey公钥',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'public_key'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'代理编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'agent_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'状态',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'社交客户端表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_client'\nGO\n\n-- ----------------------------\n-- Records of system_social_client\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_social_client ON\nGO\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, N'钉钉', 20, 2, N'dingvrnreaje3yqvzhxg', N'i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI', NULL, 0, N'', N'2023-10-18 11:21:18', N'1', N'2023-12-20 21:28:26', N'1', 1)\nGO\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, N'钉钉（王土豆）', 20, 2, N'dingtsu9hpepjkbmthhw', N'FP_bnSq_HAHKCSncmJjw5hxhnzs6vaVDSZZn3egj6rdqTQ_hu5tQVJyLMpgCakdP', NULL, 0, N'', N'2023-10-18 11:21:18', N'', N'2023-12-20 21:28:26', N'1', 121)\nGO\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, N'微信公众号', 31, 1, N'wx5b23ba7a5589ecbb', N'2a7b3b20c537e52e74afd395eb85f61f', NULL, 0, N'', N'2023-10-18 16:07:46', N'1', N'2023-12-20 21:28:23', N'1', 1)\nGO\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (43, N'微信小程序', 34, 1, N'wx63c280fe3248a3e7', N'6f270509224a7ae1296bbf1c8cb97aed', NULL, 0, N'', N'2023-10-19 13:37:41', N'1', N'2023-12-20 21:28:25', N'1', 1)\nGO\nINSERT INTO system_social_client (id, name, social_type, user_type, client_id, client_secret, agent_id, status, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (44, N'1', 10, 1, N'2', N'3', NULL, 0, N'1', N'2025-04-06 20:36:28', N'1', N'2025-04-06 20:43:12', N'1', 1)\nGO\nSET IDENTITY_INSERT system_social_client OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_social_user\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_user\nGO\nCREATE TABLE system_social_user\n(\n    id             bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    type           tinyint                                 NOT NULL,\n    openid         nvarchar(32)                            NOT NULL,\n    token          nvarchar(256) DEFAULT NULL              NULL,\n    raw_token_info nvarchar(1024)                          NOT NULL,\n    nickname       nvarchar(32)                            NOT NULL,\n    avatar         nvarchar(255) DEFAULT NULL              NULL,\n    raw_user_info  nvarchar(1024)                          NOT NULL,\n    code           nvarchar(256)                           NOT NULL,\n    state          nvarchar(256) DEFAULT NULL              NULL,\n    creator        nvarchar(64)  DEFAULT ''                NULL,\n    create_time    datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        nvarchar(64)  DEFAULT ''                NULL,\n    update_time    datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit           DEFAULT 0                 NOT NULL,\n    tenant_id      bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'主键 ( 自增策略)',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'社交平台的类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'社交 openid',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'openid'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'社交 token',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'token'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'原始 Token 数据，一般是 JSON 格式',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'raw_token_info'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户昵称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'nickname'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户头像',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'avatar'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'原始用户数据，一般是 JSON 格式',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'raw_user_info'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'最后一次的认证 code',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'code'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'最后一次的认证 state',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'state'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'社交用户表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user'\nGO\n\n-- ----------------------------\n-- Table structure for system_social_user_bind\n-- ----------------------------\nDROP TABLE IF EXISTS system_social_user_bind\nGO\nCREATE TABLE system_social_user_bind\n(\n    id             bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    user_id        bigint                                 NOT NULL,\n    user_type      tinyint                                NOT NULL,\n    social_type    tinyint                                NOT NULL,\n    social_user_id bigint                                 NOT NULL,\n    creator        nvarchar(64) DEFAULT ''                NULL,\n    create_time    datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater        nvarchar(64) DEFAULT ''                NULL,\n    update_time    datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted        bit          DEFAULT 0                 NOT NULL,\n    tenant_id      bigint       DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'主键 ( 自增策略)',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'user_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'社交平台的类型',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'social_type'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'社交用户的编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'social_user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'社交绑定表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_social_user_bind'\nGO\n\n-- ----------------------------\n-- Table structure for system_tenant\n-- ----------------------------\nDROP TABLE IF EXISTS system_tenant\nGO\nCREATE TABLE system_tenant\n(\n    id              bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name            nvarchar(30)                            NOT NULL,\n    contact_user_id bigint        DEFAULT NULL              NULL,\n    contact_name    nvarchar(30)                            NOT NULL,\n    contact_mobile  nvarchar(500) DEFAULT NULL              NULL,\n    status          tinyint       DEFAULT 0                 NOT NULL,\n    websites        nvarchar(256) DEFAULT ''                NULL,\n    package_id      bigint                                  NOT NULL,\n    expire_time     datetime2                               NOT NULL,\n    account_count   int                                     NOT NULL,\n    creator         nvarchar(64)  DEFAULT ''                NOT NULL,\n    create_time     datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater         nvarchar(64)  DEFAULT ''                NULL,\n    update_time     datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted         bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'联系人的用户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'contact_user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'联系人',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'contact_name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'联系手机',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'contact_mobile'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户状态（0正常 1停用）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'绑定域名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'website'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户套餐编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'package_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'过期时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'expire_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'账号数量',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'account_count'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant'\nGO\n\n-- ----------------------------\n-- Records of system_tenant\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_tenant ON\nGO\nINSERT 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, N'芋道源码', NULL, N'芋艿', N'17321315478', 0, N'www.iocoder.cn', 0, N'2099-02-19 17:14:16', 9999, N'1', N'2021-01-05 17:03:47', N'1', N'2023-11-06 11:41:41', N'0')\nGO\nINSERT 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, N'小租户', 110, N'小王2', N'15601691300', 0, N'zsxq.iocoder.cn', 111, N'2026-07-10 00:00:00', 30, N'1', N'2022-02-22 00:56:14', N'1', N'2025-04-03 21:33:01', N'0')\nGO\nINSERT 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, N'测试租户', 113, N'芋道', N'15601691300', 0, N'test.iocoder.cn', 111, N'2022-04-29 00:00:00', 50, N'1', N'2022-03-07 21:37:58', N'1', N'2024-09-22 12:10:50', N'0')\nGO\nSET IDENTITY_INSERT system_tenant OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_tenant_package\n-- ----------------------------\nDROP TABLE IF EXISTS system_tenant_package\nGO\nCREATE TABLE system_tenant_package\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name        nvarchar(30)                            NOT NULL,\n    status      tinyint       DEFAULT 0                 NOT NULL,\n    remark      nvarchar(256) DEFAULT ''                NULL,\n    menu_ids    nvarchar(4000)                          NOT NULL,\n    creator     nvarchar(64)  DEFAULT ''                NOT NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'套餐编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'套餐名',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户状态（0正常 1停用）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'关联的菜单编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'menu_ids'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户套餐表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_tenant_package'\nGO\n\n-- ----------------------------\n-- Records of system_tenant_package\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_tenant_package ON\nGO\nINSERT INTO system_tenant_package (id, name, status, remark, menu_ids, creator, create_time, updater, update_time, deleted) VALUES (111, N'普通套餐', 0, N'小功能', N'[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,1118,1119,1120,100,101,102,103,106,107,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2713,2714,2715,2716,2717,2718,2720,1185,2721,1186,2722,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,2472,1192,2728,1193,2729,1194,2730,1195,2731,1196,2732,1197,2733,2478,1198,2734,2479,1199,2735,2480,1200,2481,1201,2482,1202,2483,2739,2484,2740,2485,2486,2487,1207,2488,1208,2489,1209,2490,1210,2491,1211,2492,1212,2493,1213,2494,2495,1215,1216,2497,1217,1218,1219,1220,1221,1222,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,2525,1255,1256,1001,1257,1002,1258,1003,1259,1004,1260,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020]', N'1', N'2022-02-22 00:54:00', N'1', N'2024-07-13 22:37:24', N'0')\nGO\nINSERT INTO system_tenant_package (id, name, status, remark, menu_ids, creator, create_time, updater, update_time, deleted) VALUES (112, N'再来一个套餐', 0, N'1234', N'[1024,1,1025,1026,2,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1042,1043,1045,1046,1048,1050,1051,1052,1053,1054,1056,1057,1058,2083,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1100,1101,1102,1103,1104,1105,1106,2130,1107,2131,1108,2132,1109,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,100,2148,101,2149,102,2150,103,2151,104,2152,105,106,107,108,109,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2739,2740,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,1255,1256,1257,1258,1259,1260,1261,1263,1264,1265,1266,1267,2447,2448,2449,2450,2451,2452,2453,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,500,1013,501,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023]', N'1', N'2025-04-04 08:15:02', N'1', N'2025-04-04 08:15:21', N'0')\nGO\nSET IDENTITY_INSERT system_tenant_package OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_user_post\n-- ----------------------------\nDROP TABLE IF EXISTS system_user_post\nGO\nCREATE TABLE system_user_post\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    user_id     bigint       DEFAULT 0                 NOT NULL,\n    post_id     bigint       DEFAULT 0                 NOT NULL,\n    creator     nvarchar(64) DEFAULT ''                NULL,\n    create_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64) DEFAULT ''                NULL,\n    update_time datetime2    DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit          DEFAULT 0                 NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'id',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'岗位ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post',\n     'COLUMN', N'post_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户岗位表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_post'\nGO\n\n-- ----------------------------\n-- Records of system_user_post\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_user_post ON\nGO\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, 1, 1, N'admin', N'2022-05-02 07:25:24', N'admin', N'2022-05-02 07:25:24', N'0', 1)\nGO\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, 100, 1, N'admin', N'2022-05-02 07:25:24', N'admin', N'2022-05-02 07:25:24', N'0', 1)\nGO\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (115, 104, 1, N'1', N'2022-05-16 19:36:28', N'1', N'2022-05-16 19:36:28', N'0', 1)\nGO\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (116, 117, 2, N'1', N'2022-07-09 17:40:26', N'1', N'2022-07-09 17:40:26', N'0', 1)\nGO\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (117, 118, 1, N'1', N'2022-07-09 17:44:44', N'1', N'2022-07-09 17:44:44', N'0', 1)\nGO\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (119, 114, 5, N'1', N'2024-03-24 20:45:51', N'1', N'2024-03-24 20:45:51', N'0', 1)\nGO\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (123, 115, 1, N'1', N'2024-04-04 09:37:14', N'1', N'2024-04-04 09:37:14', N'0', 1)\nGO\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (124, 115, 2, N'1', N'2024-04-04 09:37:14', N'1', N'2024-04-04 09:37:14', N'0', 1)\nGO\nINSERT INTO system_user_post (id, user_id, post_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (125, 1, 2, N'1', N'2024-07-13 22:31:39', N'1', N'2024-07-13 22:31:39', N'0', 1)\nGO\nSET IDENTITY_INSERT system_user_post OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_user_role\n-- ----------------------------\nDROP TABLE IF EXISTS system_user_role\nGO\nCREATE TABLE system_user_role\n(\n    id          bigint                                 NOT NULL PRIMARY KEY IDENTITY,\n    user_id     bigint                                 NOT NULL,\n    role_id     bigint                                 NOT NULL,\n    creator     nvarchar(64) DEFAULT ''                NULL,\n    create_time datetime2    DEFAULT CURRENT_TIMESTAMP NULL,\n    updater     nvarchar(64) DEFAULT ''                NULL,\n    update_time datetime2    DEFAULT CURRENT_TIMESTAMP NULL,\n    deleted     bit          DEFAULT 0                 NOT NULL,\n    tenant_id   bigint       DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'自增编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role',\n     'COLUMN', N'user_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'角色ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role',\n     'COLUMN', N'role_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户和角色关联表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_user_role'\nGO\n\n-- ----------------------------\n-- Records of system_user_role\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_user_role ON\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 1, 1, N'', N'2022-01-11 13:19:45', N'', N'2022-05-12 12:35:17', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, 2, N'', N'2022-01-11 13:19:45', N'', N'2022-05-12 12:35:13', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, 100, 101, N'', N'2022-01-11 13:19:45', N'', N'2022-05-12 12:35:13', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, 100, 1, N'', N'2022-01-11 13:19:45', N'', N'2022-05-12 12:35:12', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 100, 2, N'', N'2022-01-11 13:19:45', N'', N'2022-05-12 12:35:11', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 103, 1, N'1', N'2022-01-11 13:19:45', N'1', N'2022-01-11 13:19:45', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 110, 109, N'1', N'2022-02-22 00:56:14', N'1', N'2022-02-22 00:56:14', N'0', 121)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 111, 110, N'110', N'2022-02-23 13:14:38', N'110', N'2022-02-23 13:14:38', N'0', 121)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 113, 111, N'1', N'2022-03-07 21:37:58', N'1', N'2022-03-07 21:37:58', N'0', 122)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (18, 1, 2, N'1', N'2022-05-12 20:39:29', N'1', N'2022-05-12 20:39:29', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (22, 115, 2, N'1', N'2022-07-21 22:08:30', N'1', N'2022-07-21 22:08:30', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (35, 112, 1, N'1', N'2024-03-15 20:00:24', N'1', N'2024-03-15 20:00:24', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (36, 118, 1, N'1', N'2024-03-17 09:12:08', N'1', N'2024-03-17 09:12:08', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (38, 114, 101, N'1', N'2024-03-24 22:23:03', N'1', N'2024-03-24 22:23:03', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (46, 117, 1, N'1', N'2024-10-02 10:16:11', N'1', N'2024-10-02 10:16:11', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (47, 104, 2, N'1', N'2025-01-04 10:40:33', N'1', N'2025-01-04 10:40:33', N'0', 1)\nGO\nINSERT INTO system_user_role (id, user_id, role_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (48, 100, 155, N'1', N'2025-04-04 10:41:14', N'1', N'2025-04-04 10:41:14', N'0', 1)\nGO\nSET IDENTITY_INSERT system_user_role OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for system_users\n-- ----------------------------\nDROP TABLE IF EXISTS system_users\nGO\nCREATE TABLE system_users\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    username    nvarchar(30)                            NOT NULL,\n    password    nvarchar(100) DEFAULT ''                NOT NULL,\n    nickname    nvarchar(30)                            NOT NULL,\n    remark      nvarchar(500) DEFAULT NULL              NULL,\n    dept_id     bigint        DEFAULT NULL              NULL,\n    post_ids    nvarchar(255) DEFAULT NULL              NULL,\n    email       nvarchar(50)  DEFAULT ''                NULL,\n    mobile      nvarchar(11)  DEFAULT ''                NULL,\n    sex         tinyint       DEFAULT 0                 NULL,\n    avatar      nvarchar(512) DEFAULT ''                NULL,\n    status      tinyint       DEFAULT 0                 NOT NULL,\n    login_ip    nvarchar(50)  DEFAULT ''                NULL,\n    login_date  datetime2     DEFAULT NULL              NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL,\n    tenant_id   bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户账号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'username'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'密码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'password'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户昵称',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'nickname'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'备注',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'remark'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'部门ID',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'dept_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'岗位编号数组',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'post_ids'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户邮箱',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'email'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'手机号码',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'mobile'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户性别',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'sex'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'头像地址',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'avatar'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'帐号状态（0正常 1停用）',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'status'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'最后登录IP',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'login_ip'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'最后登录时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'login_date'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'用户信息表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'system_users'\nGO\n\n-- ----------------------------\n-- Records of system_users\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT system_users ON\nGO\nINSERT 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, N'admin', N'$2a$04$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', N'芋道源码', N'管理员', 103, N'[1,2]', N'11aoteman@126.com', N'18818260277', 2, N'http://test.yudao.iocoder.cn/test/20250502/avatar_1746154660449.png', 0, N'0:0:0:0:0:0:0:1', N'2025-05-10 18:03:15', N'admin', N'2021-01-05 17:03:47', NULL, N'2025-05-10 18:03:15', N'0', 1)\nGO\nINSERT 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, N'yudao', N'$2a$04$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', N'芋道', N'不要吓我', 104, N'[1]', N'yudao@iocoder.cn', N'15601691300', 1, NULL, 0, N'0:0:0:0:0:0:0:1', N'2025-04-08 09:36:40', N'', N'2021-01-07 09:07:17', NULL, N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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, N'yuanma', N'$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', N'源码', NULL, 106, NULL, N'yuanma@iocoder.cn', N'15601701300', 0, NULL, 0, N'0:0:0:0:0:0:0:1', N'2024-08-11 17:48:12', N'', N'2021-01-13 23:50:35', NULL, N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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, N'test', N'$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', N'测试号', NULL, 107, N'[1,2]', N'111@qq.com', N'15601691200', 1, NULL, 0, N'0:0:0:0:0:0:0:1', N'2025-03-28 20:01:16', N'', N'2021-01-21 02:13:53', NULL, N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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, N'admin107', N'$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', N'芋艿', NULL, NULL, NULL, N'', N'15601691300', 0, NULL, 0, N'', NULL, N'1', N'2022-02-20 22:59:33', N'1', N'2025-04-21 14:23:08', N'0', 118)\nGO\nINSERT 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, N'admin108', N'$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', N'芋艿', NULL, NULL, NULL, N'', N'15601691300', 0, NULL, 0, N'', NULL, N'1', N'2022-02-20 23:00:50', N'1', N'2025-04-21 14:23:08', N'0', 119)\nGO\nINSERT 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, N'admin109', N'$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', N'芋艿', NULL, NULL, NULL, N'', N'15601691300', 0, NULL, 0, N'', NULL, N'1', N'2022-02-20 23:11:50', N'1', N'2025-04-21 14:23:08', N'0', 120)\nGO\nINSERT 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, N'admin110', N'$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', N'小王', NULL, NULL, NULL, N'', N'15601691300', 0, NULL, 0, N'0:0:0:0:0:0:0:1', N'2024-07-20 22:23:17', N'1', N'2022-02-22 00:56:14', NULL, N'2025-04-21 14:23:08', N'0', 121)\nGO\nINSERT 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, N'test', N'$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', N'测试用户', NULL, NULL, N'[]', N'', N'', 0, NULL, 0, N'0:0:0:0:0:0:0:1', N'2023-12-30 11:42:17', N'110', N'2022-02-23 13:14:33', NULL, N'2025-04-21 14:23:08', N'0', 121)\nGO\nINSERT 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, N'newobject', N'$2a$04$dB0z8Q819fJWz0hbaLe6B.VfHCjYgWx6LFfET5lyz3JwcqlyCkQ4C', N'新对象', NULL, 100, N'[]', N'', N'15601691235', 1, NULL, 0, N'0:0:0:0:0:0:0:1', N'2024-03-16 23:11:38', N'1', N'2022-02-23 19:08:03', NULL, N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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, N'aoteman', N'$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', N'芋道1', NULL, NULL, NULL, N'', N'15601691300', 0, NULL, 0, N'127.0.0.1', N'2022-03-19 18:38:51', N'1', N'2022-03-07 21:37:58', N'1', N'2025-05-05 15:30:53', N'0', 122)\nGO\nINSERT 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, N'hrmgr', N'$2a$10$TR4eybBioGRhBmDBWkqWLO6NIh3mzYa8KBKDDB5woiGYFVlRAi.fu', N'hr 小姐姐', NULL, NULL, N'[5]', N'', N'15601691236', 1, NULL, 0, N'0:0:0:0:0:0:0:1', N'2024-03-24 22:21:05', N'1', N'2022-03-19 21:50:58', NULL, N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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, N'aotemane', N'$2a$04$GcyP0Vyzb2F2Yni5PuIK9ueGxM0tkZGMtDwVRwrNbtMvorzbpNsV2', N'阿呆', N'11222', 102, N'[1,2]', N'7648@qq.com', N'15601691229', 2, NULL, 0, N'', NULL, N'1', N'2022-04-30 02:55:43', N'1', N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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, N'admin123', N'$2a$04$sEtimsHu9YCkYY4/oqElHem2Ijc9ld20eYO6lN.g/21NfLUTDLB9W', N'测试号02', N'1111', 100, N'[2]', N'', N'15601691234', 1, NULL, 0, N'0:0:0:0:0:0:0:1', N'2024-10-02 10:16:20', N'1', N'2022-07-09 17:40:26', NULL, N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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, N'goudan', N'$2a$04$jth0yOj8cSJq84D6vrzusOHDwW/LpBfgBnQ6bfFlD8zNZfM632Ta2', N'狗蛋', NULL, 103, N'[1]', N'', N'15601691239', 1, NULL, 0, N'0:0:0:0:0:0:0:1', N'2024-03-17 09:10:27', N'1', N'2022-07-09 17:44:43', N'1', N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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 (131, N'hh', N'$2a$04$jyH9h6.gaw8mpOjPfHIpx.8as2Rzfcmdlj5rlJFwgCw4rsv/MTb2K', N'呵呵', NULL, 100, N'[]', N'777@qq.com', N'15601882312', 1, NULL, 0, N'', NULL, N'1', N'2024-04-27 08:45:56', N'1', N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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, N'wwbwwb', N'$2a$04$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', N'小秃头', NULL, NULL, NULL, N'', N'', 0, NULL, 0, N'0:0:0:0:0:0:0:1', N'2024-09-10 21:03:58', NULL, N'2024-09-10 21:03:58', NULL, N'2025-04-21 14:23:08', N'0', 1)\nGO\nINSERT 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, N'admin1', N'$2a$04$oj6F6d7HrZ70kYVD3TNzEu.m3TPUzajOVuC66zdKna8KRerK1FmVa', N'新用户', NULL, NULL, NULL, N'', N'', 0, N'', 0, N'0:0:0:0:0:0:0:1', N'2025-04-08 13:09:07', N'1', N'2025-04-08 13:09:07', N'1', N'2025-04-08 13:09:07', N'0', 1)\nGO\nSET IDENTITY_INSERT system_users OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo01_contact\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo01_contact\nGO\nCREATE TABLE yudao_demo01_contact\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name        nvarchar(100) DEFAULT ''                NOT NULL,\n    sex         tinyint                                 NOT NULL,\n    birthday    datetime2                               NOT NULL,\n    description nvarchar(255)                           NOT NULL,\n    avatar      nvarchar(512) DEFAULT NULL              NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL,\n    tenant_id   bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'名字',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'性别',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'sex'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'出生年',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'birthday'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'简介',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'description'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'头像',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'avatar'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'示例联系人表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo01_contact'\nGO\n\n-- ----------------------------\n-- Records of yudao_demo01_contact\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT yudao_demo01_contact ON\nGO\nINSERT INTO yudao_demo01_contact (id, name, sex, birthday, description, avatar, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, N'土豆', 2, N'2023-11-07 00:00:00', N'<p>天蚕土豆！呀</p>', N'http://127.0.0.1:48080/admin-api/infra/file/4/get/46f8fa1a37db3f3960d8910ff2fe3962ab3b2db87cf2f8ccb4dc8145b8bdf237.jpeg', N'1', N'2023-11-15 23:34:30', N'1', N'2023-11-15 23:47:39', N'0', 1)\nGO\nSET IDENTITY_INSERT yudao_demo01_contact OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo02_category\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo02_category\nGO\nCREATE TABLE yudao_demo02_category\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name        nvarchar(100) DEFAULT ''                NOT NULL,\n    parent_id   bigint                                  NOT NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL,\n    tenant_id   bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'名字',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'父级编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category',\n     'COLUMN', N'parent_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'示例分类表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo02_category'\nGO\n\n-- ----------------------------\n-- Records of yudao_demo02_category\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT yudao_demo02_category ON\nGO\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, N'土豆', 0, N'1', N'2023-11-15 23:34:30', N'1', N'2023-11-16 20:24:23', N'0', 1)\nGO\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, N'番茄', 0, N'1', N'2023-11-16 20:24:00', N'1', N'2023-11-16 20:24:15', N'0', 1)\nGO\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, N'怪怪', 0, N'1', N'2023-11-16 20:24:32', N'1', N'2023-11-16 20:24:32', N'0', 1)\nGO\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (4, N'小番茄', 2, N'1', N'2023-11-16 20:24:39', N'1', N'2023-11-16 20:24:39', N'0', 1)\nGO\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, N'大番茄', 2, N'1', N'2023-11-16 20:24:46', N'1', N'2023-11-16 20:24:46', N'0', 1)\nGO\nINSERT INTO yudao_demo02_category (id, name, parent_id, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, N'11', 3, N'1', N'2023-11-24 19:29:34', N'1', N'2023-11-24 19:29:34', N'0', 1)\nGO\nSET IDENTITY_INSERT yudao_demo02_category OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo03_course\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_course\nGO\nCREATE TABLE yudao_demo03_course\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    student_id  bigint                                  NOT NULL,\n    name        nvarchar(100) DEFAULT ''                NOT NULL,\n    score       tinyint                                 NOT NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL,\n    tenant_id   bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'学生编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'student_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'名字',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'分数',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'score'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'学生课程表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_course'\nGO\n\n-- ----------------------------\n-- Records of yudao_demo03_course\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT yudao_demo03_course ON\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, 2, N'语文', 66, N'1', N'2023-11-16 23:21:49', N'1', N'2024-09-17 10:55:30', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (3, 2, N'数学', 22, N'1', N'2023-11-16 23:21:49', N'1', N'2024-09-17 10:55:30', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (6, 5, N'体育', 23, N'1', N'2023-11-16 23:22:46', N'1', N'2023-11-16 15:44:40', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 5, N'计算机', 11, N'1', N'2023-11-16 23:22:46', N'1', N'2023-11-16 15:44:40', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 5, N'体育', 23, N'1', N'2023-11-16 23:22:46', N'1', N'2023-11-16 15:47:09', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 5, N'计算机', 11, N'1', N'2023-11-16 23:22:46', N'1', N'2023-11-16 15:47:09', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (10, 5, N'体育', 23, N'1', N'2023-11-16 23:22:46', N'1', N'2024-09-17 10:55:28', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (11, 5, N'计算机', 11, N'1', N'2023-11-16 23:22:46', N'1', N'2024-09-17 10:55:28', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (12, 2, N'电脑', 33, N'1', N'2023-11-17 00:20:42', N'1', N'2023-11-16 16:20:45', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (13, 9, N'滑雪', 12, N'1', N'2023-11-17 13:13:20', N'1', N'2024-09-17 10:55:26', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (14, 9, N'滑雪', 12, N'1', N'2023-11-17 13:13:20', N'1', N'2024-09-17 10:55:49', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (15, 5, N'体育', 23, N'1', N'2023-11-16 23:22:46', N'1', N'2024-09-17 18:55:29', N'0', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (16, 5, N'计算机', 11, N'1', N'2023-11-16 23:22:46', N'1', N'2024-09-17 18:55:29', N'0', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (17, 2, N'语文', 66, N'1', N'2023-11-16 23:21:49', N'1', N'2024-09-17 18:55:31', N'0', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (18, 2, N'数学', 22, N'1', N'2023-11-16 23:21:49', N'1', N'2024-09-17 18:55:31', N'0', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (19, 9, N'滑雪', 12, N'1', N'2023-11-17 13:13:20', N'1', N'2025-04-19 02:49:03', N'1', 1)\nGO\nINSERT INTO yudao_demo03_course (id, student_id, name, score, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (20, 9, N'滑雪', 12, N'1', N'2023-11-17 13:13:20', N'1', N'2025-04-19 10:49:04', N'0', 1)\nGO\nSET IDENTITY_INSERT yudao_demo03_course OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo03_grade\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_grade\nGO\nCREATE TABLE yudao_demo03_grade\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    student_id  bigint                                  NOT NULL,\n    name        nvarchar(100) DEFAULT ''                NOT NULL,\n    teacher     nvarchar(255)                           NOT NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL,\n    tenant_id   bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'学生编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'student_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'名字',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'班主任',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'teacher'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'学生班级表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_grade'\nGO\n\n-- ----------------------------\n-- Records of yudao_demo03_grade\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT yudao_demo03_grade ON\nGO\nINSERT INTO yudao_demo03_grade (id, student_id, name, teacher, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (7, 2, N'三年 2 班', N'周杰伦', N'1', N'2023-11-16 23:21:49', N'1', N'2024-09-17 18:55:31', N'0', 1)\nGO\nINSERT INTO yudao_demo03_grade (id, student_id, name, teacher, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (8, 5, N'华为', N'遥遥领先', N'1', N'2023-11-16 23:22:46', N'1', N'2024-09-17 18:55:29', N'0', 1)\nGO\nINSERT INTO yudao_demo03_grade (id, student_id, name, teacher, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, 9, N'小图', N'小娃111', N'1', N'2023-11-17 13:10:23', N'1', N'2025-04-19 10:49:04', N'0', 1)\nGO\nSET IDENTITY_INSERT yudao_demo03_grade OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n-- ----------------------------\n-- Table structure for yudao_demo03_student\n-- ----------------------------\nDROP TABLE IF EXISTS yudao_demo03_student\nGO\nCREATE TABLE yudao_demo03_student\n(\n    id          bigint                                  NOT NULL PRIMARY KEY IDENTITY,\n    name        nvarchar(100) DEFAULT ''                NOT NULL,\n    sex         tinyint                                 NOT NULL,\n    birthday    datetime2                               NOT NULL,\n    description nvarchar(255)                           NOT NULL,\n    creator     nvarchar(64)  DEFAULT ''                NULL,\n    create_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    updater     nvarchar(64)  DEFAULT ''                NULL,\n    update_time datetime2     DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    deleted     bit           DEFAULT 0                 NOT NULL,\n    tenant_id   bigint        DEFAULT 0                 NOT NULL\n)\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'名字',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'name'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'性别',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'sex'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'出生日期',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'birthday'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'简介',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'description'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'creator'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'创建时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'create_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新者',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'updater'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'更新时间',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'update_time'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'是否删除',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'deleted'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'租户编号',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student',\n     'COLUMN', N'tenant_id'\nGO\n\nEXEC sp_addextendedproperty\n     'MS_Description', N'学生表',\n     'SCHEMA', N'dbo',\n     'TABLE', N'yudao_demo03_student'\nGO\n\n-- ----------------------------\n-- Records of yudao_demo03_student\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT yudao_demo03_student ON\nGO\nINSERT INTO yudao_demo03_student (id, name, sex, birthday, description, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (2, N'小白', 1, N'2023-11-16 00:00:00', N'<p>厉害</p>', N'1', N'2023-11-16 23:21:49', N'1', N'2024-09-17 18:55:31', N'0', 1)\nGO\nINSERT INTO yudao_demo03_student (id, name, sex, birthday, description, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (5, N'大黑', 2, N'2023-11-13 00:00:00', N'<p>你在教我做事?</p>', N'1', N'2023-11-16 23:22:46', N'1', N'2024-09-17 18:55:29', N'0', 1)\nGO\nINSERT INTO yudao_demo03_student (id, name, sex, birthday, description, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (9, N'小花', 1, N'2023-11-07 00:00:00', N'<p>哈哈哈</p>', N'1', N'2023-11-17 00:04:47', N'1', N'2025-04-19 10:49:04', N'0', 1)\nGO\nSET IDENTITY_INSERT yudao_demo03_student OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\n\n"
  },
  {
    "path": "sql/tools/.gitignore",
    "content": "# 忽略python虚拟环境\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n"
  },
  {
    "path": "sql/tools/README.md",
    "content": "## 0. 友情提示\n\n在 `sql/tools` 目录下，我们提供一些数据库相关的工具，包括测试数据库的快速启动、MySQL 转换其它数据库等等。\n\n注意！所有的操作，必须在 `sql/tools` 目录下执行。\n\n## 1. 测试数据库的快速启动\n\n基于 Docker Compose，快速启动 MySQL、Oracle、PostgreSQL、SQL Server 等数据库。\n\n注意！使用 Docker Compose 启动完测试数据后，因为会自动导入项目的 SQL 脚本，所以可能需要等待 1-2 分钟。\n\n### 1.1 MySQL\n\n```Bash\ndocker compose up -d mysql\n```\n\n#### 1.2 Oracle\n\n```Bash\n## x86 版本\ndocker compose up -d oracle\n\n## MacBook Apple Silicon\ndocker compose up -d oracle_m1\n```\n\n> 注意：如果使用 MacBook Apple Silicon 版本，它的 ORACLE_SID 不是 XE，而是 FREE！！！\n\n### 1.3 PostgreSQL\n\n```Bash\ndocker compose up -d postgres\n```\n\n### 1.4 SQL Server\n\n```Bash\ndocker compose up -d sqlserver\n# 注意：启动完 sqlserver 后，需要手动再执行如下命令，因为 SQL Server 不支持初始化脚本\ndocker compose exec sqlserver bash /tmp/create_schema.sh\n```\n\n### 1.5 DM 达梦\n\n① 下载达梦 Docker 镜像：<https://eco.dameng.com/download/> 地址，点击“Docker 镜像”选项，进行下载。\n\n② 加载镜像文件，在镜像 tar 文件所在目录运行：\n\n```Bash\ndocker load -i dm8_20240715_x86_rh6_rq_single.tar\n```\n\n③ 在项目 `sql/tools` 目录下运行：\n\n```Bash\ndocker compose up -d dm8\n# 注意：启动完 dm 后，需要手动再执行如下命令，因为 dm 不支持初始化脚本\ndocker compose exec dm8 bash -c '/opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \\`/tmp/schema.sql'\nexit\n```\n\n### 1.6 KingbaseES 人大金仓\n\n① 下载人大金仓 Docker 镜像：\n\n* [x86_64 版本](https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/x86_64/kdb_x86_64_V009R001C001B0025.tar) 【Windows 选择这个】\n* [aarch64 版本](https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/aarch64/kdb_aarch64_V009R001C001B0025.tar) 【MacBook Apple Silicon 选择这个】\n\n② 加载镜像文件，在镜像 tar 文件所在目录运行：\n\n```Bash\ndocker load -i kdb_x86_64_V009R001C001B0025.tar\n```\n\n③ 在项目 `sql/tools` 目录下运行：\n\n```Bash\ndocker compose up -d kingbase\n# 注意：启动完 kingbase 后，需要手动再执行如下命令\ndocker compose exec kingbase bash -c 'ksql -U $DB_USER -d test -f /tmp/schema.sql'\n```\n\n### 1.7 华为 OpenGauss\n\n```Bash\ndocker compose up -d opengauss\n# 注意：启动完 opengauss 后，需要手动再执行如下命令\ndocker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql'\n```\n\n## 1.X 容器的销毁重建\n\n开发测试过程中，有时候需要创建全新干净的数据库。由于测试数据 Docker 容器采用数据卷 Volume 挂载数据库实例的数据目录，因此销毁数据需要停止容器后，删除数据卷，然后再重新创建容器。\n\n以 postgres 为例，操作如下：\n\n```Bash\ndocker compose down postgres\ndocker volume rm ruoyi-vue-pro_postgres\n```\n\n## 2. MySQL 转换其它数据库\n\n项目提供了 `sql/tools/convertor.py` 脚本，支持将 MySQL 转换为 Oracle、PostgreSQL、SQL Server、达梦、人大金仓、OpenGauss 等数据库的脚本。\n\n### 2.1 实现原理\n\n通过读取 MySQL 的 `sql/mysql/ruoyi-vue-pro.sql` 数据库文件，转换成对应的数据库脚本。\n\n### 2.2 使用方法\n\n① 安装依赖库 `simple-ddl-parser`\n\n```bash\npip install simple-ddl-parser\n# pip3 install simple-ddl-parser\n```\n\n② 在 `sql/tools/` 目录下，执行如下命令打印生成 postgres 的脚本内容，其他可选参数有：`oracle`、`sqlserver`、`dm8`、`kingbase`、`opengauss`：\n\n```Bash\npython3 convertor.py postgres\n# python3 convertor.py postgres > tmp.sql\n```\n\n程序将 SQL 脚本打印到终端，可以重定向到临时文件 `tmp.sql`。\n\n确认无误后，可以利用 IDEA 进行格式化。当然，也可以直接导入到数据库中。\n"
  },
  {
    "path": "sql/tools/convertor.py",
    "content": "# encoding=utf8\n\"\"\"芋道系统数据库迁移工具\n\nAuthor: dhb52 (https://gitee.com/dhb52)\n\npip install simple-ddl-parser\n\nor with uv\nuv run --with simple-ddl-parser convertor.py postgres ../mysql/ruoyi-vue-pro.sql > ../postgresql/ruoyi-vue-pro.sql\nuv run --with simple-ddl-parser convertor.py sqlserver ../mysql/ruoyi-vue-pro.sql > ../sqlserver/ruoyi-vue-pro.sql\nuv run --with simple-ddl-parser convertor.py kingbase ../mysql/ruoyi-vue-pro.sql > ../kingbase/ruoyi-vue-pro.sql\nuv run --with simple-ddl-parser convertor.py opengauss ../mysql/ruoyi-vue-pro.sql > ../opengauss/ruoyi-vue-pro.sql\nuv run --with simple-ddl-parser convertor.py oracle ../mysql/ruoyi-vue-pro.sql > ../oracle/ruoyi-vue-pro.sql\nuv run --with simple-ddl-parser convertor.py dm8 ../mysql/ruoyi-vue-pro.sql > ../dm/ruoyi-vue-pro-dm8.sql\n\"\"\"\n\nimport argparse\nimport pathlib\nimport re\nimport sys\nimport time\nfrom abc import ABC, abstractmethod\nfrom typing import Dict, Generator, Optional, Tuple, Union\n\nfrom simple_ddl_parser import DDLParser\n\n# 避免 Windows 系统使用默认的 GBK 编码\nsys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf-8', buffering=1)\n\nPREAMBLE = \"\"\"/*\n Yudao Database Transfer Tool\n\n Source Server Type    : MySQL\n\n Target Server Type    : {db_type}\n\n Date: {date}\n*/\n\n\"\"\"\n\n\ndef load_and_clean(sql_file: str) -> str:\n    \"\"\"加载源 SQL 文件，并清理内容方便下一步 ddl 解析\n\n    Args:\n        sql_file (str): sql文件路径\n\n    Returns:\n        str: 清理后的sql文件内容\n    \"\"\"\n    REPLACE_PAIR_LIST = (\n        (\")\\nVALUES \", \") VALUES \"),\n        (\" CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci \", \" \"),\n        (\" CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci \", \" \"),\n        (\" KEY `\", \" INDEX `\"),\n        (\"UNIQUE INDEX\", \"UNIQUE KEY\"),\n        (\"b'0'\", \"'0'\"),\n        (\"b'1'\", \"'1'\"),\n    )\n\n    content = open(sql_file, encoding=\"utf-8\").read()\n    for replace_pair in REPLACE_PAIR_LIST:\n        content = content.replace(*replace_pair)\n    # 移除索引字段的前缀长度定义，例如: `name`(32) -> `name`\n    # 移除索引定义上的 USING BTREE COMMENT 部分\n    # 相关 issue：https://t.zsxq.com/96IFc 、https://t.zsxq.com/rC3A3\n    content = re.sub(r'`([^`]+)`\\(\\d+\\)', r'`\\1`', content)\n    content = re.sub(r'\\s+USING\\s+BTREE\\s+COMMENT\\s+\\'[^\\']+\\'', '', content)\n    content = re.sub(r\"ENGINE.*COMMENT\", \"COMMENT\", content)\n    content = re.sub(r\"ENGINE.*;\", \";\", content)\n    return content\n\n\nclass Convertor(ABC):\n    def __init__(self, src: str, db_type) -> None:\n        self.src = src\n        self.db_type = db_type\n        self.content = load_and_clean(self.src)\n        self.table_script_list = re.findall(r\"CREATE TABLE [^;]*;\", self.content)\n\n    @abstractmethod\n    def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]) -> str:\n        \"\"\"字段类型转换\n\n        Args:\n            type (str): 字段类型\n            size (Optional[Union[int, Tuple[int]]]): 字段长度描述, 如varchar(255), decimal(10,2)\n\n        Returns:\n            str: 类型定义\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def gen_create(self, table_ddl: Dict) -> str:\n        \"\"\"生成 create 脚本\n\n        Args:\n            table_ddl (Dict): 表DDL\n\n        Returns:\n            str:  生成脚本\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def gen_pk(self, table_name: str) -> str:\n        \"\"\"生成主键定义\n\n        Args:\n            table_name (str): 表名\n\n        Returns:\n            str: 生成脚本\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def gen_index(self, ddl: Dict) -> str:\n        \"\"\"生成索引定义\n\n        Args:\n            table_ddl (Dict): 表DDL\n\n        Returns:\n            str: 生成脚本\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def gen_comment(self, table_ddl: Dict) -> str:\n        \"\"\"生成字段/表注释\n\n        Args:\n            table_ddl (Dict): 表DDL\n\n        Returns:\n            str: 生成脚本\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def gen_uk(self, table_ddl: Dict) -> str:\n        \"\"\"生成\n\n        Args:\n            table_ddl (Dict): 表DDL\n\n        Returns:\n            str: 生成脚本\n        \"\"\"\n\n    @abstractmethod\n    def gen_insert(self, table_name: str) -> str:\n        \"\"\"生成 insert 语句块\n\n        Args:\n            table_name (str): 表名\n\n        Returns:\n            str: 生成脚本\n        \"\"\"\n        pass\n\n    def gen_dual(self) -> str:\n        \"\"\"生成虚拟 dual 表\n\n        Returns:\n            str: 生成脚本, 默认返回空脚本, 表示当前数据库无需手工创建\n        \"\"\"\n        return \"\"\n\n    @staticmethod\n    def inserts(table_name: str, script_content: str) -> Generator:\n        PREFIX = f\"INSERT INTO `{table_name}`\"\n\n        # 收集 `table_name` 对应的 insert 语句\n        for line in script_content.split(\"\\n\"):\n            if line.startswith(PREFIX):\n                head, tail = line.replace(PREFIX, \"\").split(\" VALUES \", maxsplit=1)\n                head = head.strip().replace(\"`\", \"\").lower()\n                tail = tail.strip().replace(r\"\\\"\", '\"')\n                # tail = tail.replace(\"b'0'\", \"'0'\").replace(\"b'1'\", \"'1'\")\n                yield f\"INSERT INTO {table_name.lower()} {head} VALUES {tail}\"\n\n    @staticmethod\n    def index(ddl: Dict) -> Generator:\n        \"\"\"生成索引定义\n\n        Args:\n            ddl (Dict): 表DDL\n\n        Yields:\n            Generator[str]: create index 语句\n        \"\"\"\n\n        def generate_columns(columns):\n            keys = [\n                f\"{col['name'].lower()}{' ' + col['order'].lower() if col['order'] != 'ASC' else ''}\"\n                for col in columns[0]\n            ]\n            return \", \".join(keys)\n\n        for no, index in enumerate(ddl[\"index\"], 1):\n            columns = generate_columns(index[\"columns\"])\n            table_name = ddl[\"table_name\"].lower()\n            yield f\"CREATE INDEX idx_{table_name}_{no:02d} ON {table_name} ({columns})\"\n\n    @staticmethod\n    def unique_index(ddl: Dict) -> Generator:\n        if \"constraints\" in ddl and \"uniques\" in ddl[\"constraints\"]:\n            uk_list = ddl[\"constraints\"][\"uniques\"]\n            for uk in uk_list:\n                table_name = ddl[\"table_name\"]\n                uk_name = uk[\"constraint_name\"]\n                uk_columns = uk[\"columns\"]\n                yield table_name, uk_name, uk_columns\n\n    @staticmethod\n    def filed_comments(table_sql: str) -> Generator:\n        for line in table_sql.split(\"\\n\"):\n            match = re.match(r\"^`([^`]+)`.* COMMENT '([^']+)'\", line.strip())\n            if match:\n                field = match.group(1)\n                comment_string = match.group(2).replace(\"\\\\n\", \"\\n\")\n                yield field, comment_string\n\n    def table_comment(self, table_sql: str) -> str:\n        match = re.search(r\"COMMENT \\='([^']+)';\", table_sql)\n        return match.group(1) if match else None\n\n    def print(self):\n        \"\"\"打印转换后的sql脚本到终端\"\"\"\n        print(\n            PREAMBLE.format(\n                db_type=self.db_type,\n                date=time.strftime(\"%Y-%m-%d %H:%M:%S\"),\n            )\n        )\n\n        dual = self.gen_dual()\n        if dual:\n            print(\n                f\"\"\"-- ----------------------------\n-- Table structure for dual\n-- ----------------------------\n{dual}\n\"\"\"\n            )\n\n        error_scripts = []\n        for table_sql in self.table_script_list:\n            ddl = DDLParser(table_sql.replace(\"`\", \"\")).run()\n\n            # 如果parse失败, 需要跟进\n            if len(ddl) == 0:\n                error_scripts.append(table_sql)\n                continue\n\n            table_ddl = ddl[0]\n            table_name = table_ddl[\"table_name\"]\n\n            # 忽略 quartz 的内容\n            if table_name.lower().startswith(\"qrtz\"):\n                continue\n\n            # 解析注释\n            for column in table_ddl[\"columns\"]:\n                column[\"comment\"] = bytes(column[\"comment\"], \"utf-8\").decode(\n                    r\"unicode_escape\"\n                )[1:-1]\n            table_ddl[\"comment\"] = bytes(table_ddl[\"comment\"], \"utf-8\").decode(\n                r\"unicode_escape\"\n            )[1:-1]\n\n            # 为每个表生成个6个基本部分\n            create = self.gen_create(table_ddl)\n            pk = self.gen_pk(table_name)\n            uk = self.gen_uk(table_ddl)\n            index = self.gen_index(table_ddl)\n            comment = self.gen_comment(table_ddl)\n            inserts = self.gen_insert(table_name)\n\n            # 组合当前表的DDL脚本\n            script = f\"\"\"{create}\n\n{pk}\n\n{uk}\n\n{index}\n\n{comment}\n\n{inserts}\n\"\"\"\n\n            # 清理\n            script = re.sub(\"\\n{3,}\", \"\\n\\n\", script).strip() + \"\\n\"\n\n            print(script)\n\n        # 将parse失败的脚本打印出来\n        if error_scripts:\n            print(\"!!! 以下内容无法正常解析\", file=sys.stderr)\n            for script in error_scripts:\n                # print to stderr\n                print(script, file=sys.stderr)\n\n\nclass PostgreSQLConvertor(Convertor):\n    def __init__(self, src):\n        super().__init__(src, \"PostgreSQL\")\n\n    def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]):\n        \"\"\"类型转换\"\"\"\n\n        type = type.lower()\n\n        if type == \"varchar\":\n            return f\"varchar({size})\"\n        if type in (\"int\", \"int unsigned\"):\n            return \"int4\"\n        if type in (\"bigint\", \"bigint unsigned\"):\n            return \"int8\"\n        if type == \"datetime\":\n            return \"timestamp\"\n        if type == \"timestamp\":\n            return f\"timestamp({size})\"\n        if type == \"bit\":\n            return \"bool\"\n        if type in (\"tinyint\", \"smallint\"):\n            return \"int2\"\n        if type in (\"text\", \"longtext\"):\n            return \"text\"\n        if type in (\"blob\", \"mediumblob\"):\n            return \"bytea\"\n        if type == \"decimal\":\n            return (\n                f\"numeric({','.join(str(s) for s in size)})\" if len(size) else \"numeric\"\n            )\n\n    def gen_create(self, ddl: Dict) -> str:\n        \"\"\"生成 create\"\"\"\n\n        def _generate_column(col):\n            name = col[\"name\"].lower()\n            if name == \"deleted\":\n                return \"deleted int2 NOT NULL DEFAULT 0\"\n\n            type = col[\"type\"].lower()\n            full_type = self.translate_type(type, col[\"size\"])\n            nullable = \"NULL\" if col[\"nullable\"] else \"NOT NULL\"\n            default = f\"DEFAULT {col['default']}\" if col[\"default\"] is not None else \"\"\n            return f\"{name} {full_type} {nullable} {default}\"\n\n        table_name = ddl[\"table_name\"].lower()\n        columns = [f\"{_generate_column(col).strip()}\" for col in ddl[\"columns\"]]\n        filed_def_list = \",\\n  \".join(columns)\n        script = f\"\"\"-- ----------------------------\n-- Table structure for {table_name}\n-- ----------------------------\nDROP TABLE IF EXISTS {table_name};\nCREATE TABLE {table_name} (\n    {filed_def_list}\n);\"\"\"\n\n        return script\n\n    def gen_index(self, ddl: Dict) -> str:\n        return \"\\n\".join(f\"{script};\" for script in self.index(ddl))\n\n    def gen_comment(self, table_ddl: Dict) -> str:\n        \"\"\"生成字段及表的注释\"\"\"\n\n        script = \"\"\n        for column in table_ddl[\"columns\"]:\n            table_comment = column[\"comment\"]\n            script += (\n                f\"COMMENT ON COLUMN {table_ddl['table_name']}.{column['name']} IS '{table_comment}';\"\n                + \"\\n\"\n            )\n\n        table_comment = table_ddl[\"comment\"]\n        if table_comment:\n            script += (\n                f\"COMMENT ON TABLE {table_ddl['table_name']} IS '{table_comment}';\\n\"\n            )\n\n        return script\n\n    def gen_pk(self, table_name) -> str:\n        \"\"\"生成主键定义\"\"\"\n        return f\"ALTER TABLE {table_name} ADD CONSTRAINT pk_{table_name} PRIMARY KEY (id);\\n\"\n\n    def gen_uk(self, table_ddl: Dict) -> str:\n        script = \"\"\n        uk_list = list(Convertor.unique_index(table_ddl))\n        for idx, (table_name, _, uk_columns) in enumerate(uk_list, 1):\n            uk_name = f\"uk_{table_name}_{idx:02d}\"\n            script += f\"CREATE UNIQUE INDEX {uk_name} ON {table_name} ({', '.join(uk_columns)});\\n\"\n\n        return script\n\n    def gen_insert(self, table_name: str) -> str:\n        \"\"\"生成 insert 语句，以及根据最后的 insert id+1 生成 Sequence\"\"\"\n\n        inserts = list(Convertor.inserts(table_name, self.content))\n        ## 生成 insert 脚本\n        script = \"\"\n        last_id = 0\n        if inserts:\n            inserts_lines = \"\\n\".join(inserts)\n            script += f\"\"\"\\n\\n-- ----------------------------\n-- Records of {table_name.lower()}\n-- ----------------------------\n-- @formatter:off\nBEGIN;\n{inserts_lines}\nCOMMIT;\n-- @formatter:on\"\"\"\n            match = re.search(r\"VALUES \\((\\d+),\", inserts[-1])\n            if match:\n                last_id = int(match.group(1))\n\n        # 生成 Sequence\n        script += (\n            \"\\n\\n\"\n            + f\"\"\"DROP SEQUENCE IF EXISTS {table_name}_seq;\nCREATE SEQUENCE {table_name}_seq\n    START {last_id + 1};\"\"\"\n        )\n\n        return script\n\n    def gen_dual(self) -> str:\n        return \"\"\"DROP TABLE IF EXISTS dual;\nCREATE TABLE dual\n(\n    id int2\n);\n\nCOMMENT ON TABLE dual IS '数据库连接的表';\n\n-- ----------------------------\n-- Records of dual\n-- ----------------------------\n-- @formatter:off\nINSERT INTO dual VALUES (1);\n-- @formatter:on\"\"\"\n\n\nclass OracleConvertor(Convertor):\n    def __init__(self, src):\n        super().__init__(src, \"Oracle\")\n\n    def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]):\n        \"\"\"类型转换\"\"\"\n        type = type.lower()\n\n        if type == \"varchar\":\n            return f\"varchar2({size if size < 4000 else 4000})\"\n        if type in (\"int\", \"int unsigned\"):\n            return \"number\"\n        if type == \"bigint\" or type == \"bigint unsigned\":\n            return \"number\"\n        if type == \"datetime\":\n            return \"date\"\n        if type == \"timestamp\":\n            return f\"timestamp({size})\"\n        if type == \"bit\":\n            return \"number(1,0)\"\n        if type in (\"tinyint\", \"smallint\"):\n            return \"smallint\"\n        if type in (\"text\", \"longtext\"):\n            return \"clob\"\n        if type in (\"blob\", \"mediumblob\"):\n            return \"blob\"\n        if type == \"decimal\":\n            return (\n                f\"number({','.join(str(s) for s in size)})\" if len(size) else \"number\"\n            )\n\n    def gen_create(self, ddl) -> str:\n        \"\"\"生成 CREATE 语句\"\"\"\n\n        def generate_column(col):\n            name = col[\"name\"].lower()\n            if name == \"deleted\":\n                return \"deleted number(1,0) DEFAULT 0 NOT NULL\"\n\n            type = col[\"type\"].lower()\n            full_type = self.translate_type(type, col[\"size\"])\n            nullable = \"NULL\" if col[\"nullable\"] else \"NOT NULL\"\n            # Oracle的 INSERT '' 不能通过NOT NULL校验，因此对文字类型字段覆写为 NULL\n            nullable = \"NULL\" if type in (\"varchar\", \"text\", \"longtext\") else nullable\n            default = f\"DEFAULT {col['default']}\" if col[\"default\"] is not None else \"\"\n            # Oracle 中 size 不能作为字段名\n            field_name = '\"size\"' if name == \"size\" else name\n            # Oracle DEFAULT 定义在 NULLABLE 之前\n            return f\"{field_name} {full_type} {default} {nullable}\"\n\n        table_name = ddl[\"table_name\"].lower()\n        columns = [f\"{generate_column(col).strip()}\" for col in ddl[\"columns\"]]\n        field_def_list = \",\\n    \".join(columns)\n        script = f\"\"\"-- ----------------------------\n-- Table structure for {table_name}\n-- ----------------------------\nCREATE TABLE {table_name} (\n    {field_def_list}\n);\"\"\"\n\n        # oracle INSERT '' 不能通过 NOT NULL 校验\n        script = script.replace(\"DEFAULT '' NOT NULL\", \"DEFAULT '' NULL\")\n\n        return script\n\n    def gen_index(self, ddl: Dict) -> str:\n        return \"\\n\".join(f\"{script};\" for script in self.index(ddl))\n\n    def gen_comment(self, table_ddl: Dict) -> str:\n        script = \"\"\n        for column in table_ddl[\"columns\"]:\n            table_comment = column[\"comment\"]\n            script += (\n                f\"COMMENT ON COLUMN {table_ddl['table_name']}.{column['name']} IS '{table_comment}';\"\n                + \"\\n\"\n            )\n\n        table_comment = table_ddl[\"comment\"]\n        if table_comment:\n            script += (\n                f\"COMMENT ON TABLE {table_ddl['table_name']} IS '{table_comment}';\\n\"\n            )\n\n        return script\n\n    def gen_pk(self, table_name: str) -> str:\n        \"\"\"生成主键定义\"\"\"\n        return f\"ALTER TABLE {table_name} ADD CONSTRAINT pk_{table_name} PRIMARY KEY (id);\\n\"\n\n    def gen_uk(self, table_ddl: Dict) -> str:\n        script = \"\"\n        uk_list = list(Convertor.unique_index(table_ddl))\n        for idx, (table_name, _, uk_columns) in enumerate(uk_list, 1):\n            uk_name = f\"uk_{table_name}_{idx:02d}\"\n            script += f\"CREATE UNIQUE INDEX {uk_name} ON {table_name} ({', '.join(uk_columns)});\\n\"\n\n        return script\n\n    def gen_index(self, ddl: Dict) -> str:\n        return \"\\n\".join(f\"{script};\" for script in self.index(ddl))\n\n    def gen_insert(self, table_name: str) -> str:\n        \"\"\"拷贝 INSERT 语句\"\"\"\n        inserts = []\n        for insert_script in Convertor.inserts(table_name, self.content):\n            # 对日期数据添加 TO_DATE 转换\n            insert_script = re.sub(\n                r\"('\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}')\",\n                r\"to_date(\\g<1>, 'SYYYY-MM-DD HH24:MI:SS')\",\n                insert_script,\n            )\n            inserts.append(insert_script)\n\n        ## 生成 insert 脚本\n        script = \"\"\n        last_id = 0\n        if inserts:\n            inserts_lines = \"\\n\".join(inserts)\n            script += f\"\"\"\\n\\n-- ----------------------------\n-- Records of {table_name.lower()}\n-- ----------------------------\n-- @formatter:off\n{inserts_lines}\nCOMMIT;\n-- @formatter:on\"\"\"\n            match = re.search(r\"VALUES \\((\\d+),\", inserts[-1])\n            if match:\n                last_id = int(match.group(1))\n\n        # 生成 Sequence\n        script += f\"\"\"\n\nCREATE SEQUENCE {table_name}_seq\n    START WITH {last_id + 1};\"\"\"\n\n        return script\n\n\nclass SQLServerConvertor(Convertor):\n    \"\"\"_summary_\n\n    Args:\n        Convertor (_type_): _description_\n    \"\"\"\n\n    def __init__(self, src):\n        super().__init__(src, \"Microsoft SQL Server\")\n\n    def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]):\n        \"\"\"类型转换\"\"\"\n\n        type = type.lower()\n\n        if type == \"varchar\":\n            return f\"nvarchar({size if size < 4000 else 4000})\"\n        if type in (\"int\", \"int unsigned\"):\n            return \"int\"\n        if type in (\"bigint\", \"bigint unsigned\"):\n            return \"bigint\"\n        if type in (\"datetime\", \"timestamp\"):\n            return \"datetime2\"\n        if type == \"bit\":\n            return \"varchar(1)\"\n        if type in (\"tinyint\", \"smallint\"):\n            return \"tinyint\"\n        if type in (\"text\", \"longtext\"):\n            return \"nvarchar(max)\"\n        if type in (\"blob\", \"mediumblob\"):\n            return \"varbinary(max)\"\n        if type == \"decimal\":\n            return (\n                f\"numeric({','.join(str(s) for s in size)})\" if len(size) else \"numeric\"\n            )\n\n    def gen_create(self, ddl: Dict) -> str:\n        \"\"\"生成 create\"\"\"\n\n        def _generate_column(col):\n            name = col[\"name\"].lower()\n            if name == \"id\":\n                return \"id bigint NOT NULL PRIMARY KEY IDENTITY\"\n            if name == \"deleted\":\n                return \"deleted bit DEFAULT 0 NOT NULL\"\n\n            type = col[\"type\"].lower()\n            full_type = self.translate_type(type, col[\"size\"])\n            nullable = \"NULL\" if col[\"nullable\"] else \"NOT NULL\"\n            default = f\"DEFAULT {col['default']}\" if col[\"default\"] is not None else \"\"\n            return f\"{name} {full_type} {default} {nullable}\"\n\n        table_name = ddl[\"table_name\"].lower()\n        columns = [f\"{_generate_column(col).strip()}\" for col in ddl[\"columns\"]]\n        filed_def_list = \",\\n    \".join(columns)\n        script = f\"\"\"-- ----------------------------\n-- Table structure for {table_name}\n-- ----------------------------\nDROP TABLE IF EXISTS {table_name}\nGO\nCREATE TABLE {table_name} (\n    {filed_def_list}\n)\nGO\"\"\"\n\n        return script\n\n    def gen_comment(self, table_ddl: Dict) -> str:\n        \"\"\"生成字段及表的注释\"\"\"\n\n        script = \"\"\n        table_name = table_ddl[\"table_name\"]\n\n        for column in table_ddl[\"columns\"]:\n            column_comment = column[\"comment\"]\n            field = column[\"name\"]\n\n            script += f\"\"\"EXEC sp_addextendedproperty\n    'MS_Description', N'{column_comment}',\n    'SCHEMA', N'dbo',\n    'TABLE', N'{table_name}',\n    'COLUMN', N'{field}'\nGO\n\n\"\"\"\n\n        table_comment = table_ddl[\"comment\"]\n        if table_comment:\n            script += f\"\"\"EXEC sp_addextendedproperty\n    'MS_Description', N'{table_comment}',\n    'SCHEMA', N'dbo',\n    'TABLE', N'{table_name}'\nGO\n\n\"\"\"\n        return script\n\n    def gen_pk(self, table_name: str) -> str:\n        \"\"\"生成主键定义\"\"\"\n        return \"\"\n\n    def gen_uk(self, table_ddl: Dict) -> str:\n        script = \"\"\n        uk_list = list(Convertor.unique_index(table_ddl))\n        for idx, (table_name, _, uk_columns) in enumerate(uk_list, 1):\n            uk_name = f\"uk_{table_name}_{idx:02d}\"\n            script += f\"CREATE UNIQUE INDEX {uk_name} ON {table_name} ({', '.join(uk_columns)})\\nGO\"\n\n        return script\n\n    def gen_index(self, ddl: Dict) -> str:\n        \"\"\"生成 index\"\"\"\n        return \"\\n\".join(f\"{script}\\nGO\" for script in self.index(ddl))\n\n    def gen_insert(self, table_name: str) -> str:\n        \"\"\"生成 insert 语句\"\"\"\n\n        # 收集 `table_name` 对应的 insert 语句\n        inserts = []\n        for insert_script in Convertor.inserts(table_name, self.content):\n            # SQLServer: 字符串前加N，hack，是否存在替换字符串内容的风险\n            insert_script = insert_script.replace(\", '\", \", N'\").replace(\n                \"VALUES ('\", \"VALUES (N')\"\n            )\n            # 删除 insert 的结尾分号\n            insert_script = re.sub(\";$\", r\"\\nGO\", insert_script)\n            inserts.append(insert_script)\n\n        ## 生成 insert 脚本\n        script = \"\"\n        if inserts:\n            inserts_lines = \"\\n\".join(inserts)\n            script += f\"\"\"\\n\\n-- ----------------------------\n-- Records of {table_name.lower()}\n-- ----------------------------\n-- @formatter:off\nBEGIN TRANSACTION\nGO\nSET IDENTITY_INSERT {table_name.lower()} ON\nGO\n{inserts_lines}\nSET IDENTITY_INSERT {table_name.lower()} OFF\nGO\nCOMMIT\nGO\n-- @formatter:on\"\"\"\n\n        return script\n\n    def gen_dual(self) -> str:\n        return \"\"\"DROP TABLE IF EXISTS dual\nGO\nCREATE TABLE dual\n(\n  id int\n)\nGO\n\nEXEC sp_addextendedproperty\n    'MS_Description', N'数据库连接的表',\n    'SCHEMA', N'dbo',\n    'TABLE', N'dual'\nGO\n\n-- ----------------------------\n-- Records of dual\n-- ----------------------------\n-- @formatter:off\nINSERT INTO dual VALUES (1)\nGO\n-- @formatter:on\"\"\"\n\n\nclass DM8Convertor(Convertor):\n    def __init__(self, src):\n        super().__init__(src, \"DM8\")\n\n    def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]):\n        \"\"\"类型转换\"\"\"\n        type = type.lower()\n\n        if type == \"varchar\":\n            return f\"varchar({size})\"\n        if type in (\"int\", \"int unsigned\"):\n            return \"int\"\n        if type in (\"bigint\", \"bigint unsigned\"):\n            return \"bigint\"\n        if type == \"datetime\":\n            return \"datetime\"\n        if type == \"timestamp\":\n            return f\"timestamp({size})\"\n        if type == \"bit\":\n            return \"bit\"\n        if type in (\"tinyint\", \"smallint\"):\n            return \"smallint\"\n        if type in (\"text\", \"longtext\"):\n            return \"text\"\n        if type in (\"blob\", \"mediumblob\"):\n            return \"blob\"\n        if type == \"decimal\":\n            return (\n                f\"decimal({','.join(str(s) for s in size)})\" if len(size) else \"decimal\"\n            )\n\n    def gen_create(self, ddl) -> str:\n        \"\"\"生成 CREATE 语句\"\"\"\n\n        def generate_column(col):\n            name = col[\"name\"].lower()\n            if name == \"id\":\n                return \"id bigint NOT NULL PRIMARY KEY IDENTITY\"\n\n            type = col[\"type\"].lower()\n            full_type = self.translate_type(type, col[\"size\"])\n            nullable = \"NULL\" if col[\"nullable\"] else \"NOT NULL\"\n            default = f\"DEFAULT {col['default']}\" if col[\"default\"] is not None else \"\"\n            return f\"{name} {full_type} {default} {nullable}\"\n\n        table_name = ddl[\"table_name\"].lower()\n        columns = [f\"{generate_column(col).strip()}\" for col in ddl[\"columns\"]]\n        field_def_list = \",\\n    \".join(columns)\n        script = f\"\"\"-- ----------------------------\n-- Table structure for {table_name}\n-- ----------------------------\nCREATE TABLE {table_name} (\n    {field_def_list}\n);\"\"\"\n\n        # oracle INSERT '' 不能通过 NOT NULL 校验\n        script = script.replace(\"DEFAULT '' NOT NULL\", \"DEFAULT '' NULL\")\n\n        return script\n\n    def gen_comment(self, table_ddl: Dict) -> str:\n        script = \"\"\n        for column in table_ddl[\"columns\"]:\n            table_comment = column[\"comment\"]\n            script += (\n                f\"COMMENT ON COLUMN {table_ddl['table_name']}.{column['name']} IS '{table_comment}';\"\n                + \"\\n\"\n            )\n\n        table_comment = table_ddl[\"comment\"]\n        if table_comment:\n            script += (\n                f\"COMMENT ON TABLE {table_ddl['table_name']} IS '{table_comment}';\\n\"\n            )\n\n        return script\n\n    def gen_pk(self, table_name: str) -> str:\n        \"\"\"生成主键定义\"\"\"\n        return \"\"\n\n    def gen_uk(self, table_ddl: Dict) -> str:\n        script = \"\"\n        uk_list = list(Convertor.unique_index(table_ddl))\n        for idx, (table_name, _, uk_columns) in enumerate(uk_list, 1):\n            uk_name = f\"uk_{table_name}_{idx:02d}\"\n            script += f\"CREATE UNIQUE INDEX {uk_name} ON {table_name} ({', '.join(uk_columns)});\\n\"\n\n        return script\n\n    def gen_index(self, ddl: Dict) -> str:\n        return \"\\n\".join(f\"{script};\" for script in self.index(ddl))\n\n    def gen_insert(self, table_name: str) -> str:\n        \"\"\"拷贝 INSERT 语句\"\"\"\n        inserts = list(Convertor.inserts(table_name, self.content))\n\n        ## 生成 insert 脚本\n        script = \"\"\n        if inserts:\n            inserts_lines = \"\\n\".join(inserts)\n            script += f\"\"\"\\n\\n-- ----------------------------\n-- Records of {table_name.lower()}\n-- ----------------------------\n-- @formatter:off\nSET IDENTITY_INSERT {table_name.lower()} ON;\n{inserts_lines}\nCOMMIT;\nSET IDENTITY_INSERT {table_name.lower()} OFF;\n-- @formatter:on\"\"\"\n\n        return script\n\n\nclass KingbaseConvertor(PostgreSQLConvertor):\n    def __init__(self, src):\n        super().__init__(src)\n        self.db_type = \"Kingbase\"\n\n    def gen_create(self, ddl: Dict) -> str:\n        \"\"\"生成 create\"\"\"\n\n        def _generate_column(col):\n            name = col[\"name\"].lower()\n            if name == \"deleted\":\n                return \"deleted int2 NOT NULL DEFAULT 0\"\n\n            type = col[\"type\"].lower()\n            full_type = self.translate_type(type, col[\"size\"])\n            nullable = \"NULL\" if col[\"nullable\"] else \"NOT NULL\"\n            if full_type == \"text\":\n                nullable = \"NULL\"\n            default = f\"DEFAULT {col['default']}\" if col[\"default\"] is not None else \"\"\n            return f\"{name} {full_type} {nullable} {default}\"\n\n        table_name = ddl[\"table_name\"].lower()\n        columns = [f\"{_generate_column(col).strip()}\" for col in ddl[\"columns\"]]\n        filed_def_list = \",\\n  \".join(columns)\n        script = f\"\"\"-- ----------------------------\n-- Table structure for {table_name}\n-- ----------------------------\nDROP TABLE IF EXISTS {table_name};\nCREATE TABLE {table_name} (\n    {filed_def_list}\n);\"\"\"\n\n        # Kingbase INSERT '' 不能通过 NOT NULL 校验\n        script = script.replace(\"NOT NULL DEFAULT ''\", \"NULL DEFAULT ''\")\n\n        return script\n\n\nclass OpengaussConvertor(KingbaseConvertor):\n    def __init__(self, src):\n        super().__init__(src)\n        self.db_type = \"OpenGauss\"\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"芋道系统数据库转换工具\")\n    parser.add_argument(\n        \"type\",\n        type=str,\n        help=\"目标数据库类型\",\n        choices=[\"postgres\", \"oracle\", \"sqlserver\", \"dm8\", \"kingbase\", \"opengauss\"],\n    )\n    parser.add_argument(\n        \"path\",\n        type=str,\n        help=\"源数据库脚本路径\",\n        default=\"../mysql/ruoyi-vue-pro.sql\"\n    )\n    args = parser.parse_args()\n\n    sql_file = pathlib.Path(args.path).resolve().as_posix()\n    convertor = None\n    if args.type == \"postgres\":\n        convertor = PostgreSQLConvertor(sql_file)\n    elif args.type == \"oracle\":\n        convertor = OracleConvertor(sql_file)\n    elif args.type == \"sqlserver\":\n        convertor = SQLServerConvertor(sql_file)\n    elif args.type == \"dm8\":\n        convertor = DM8Convertor(sql_file)\n    elif args.type == \"kingbase\":\n        convertor = KingbaseConvertor(sql_file)\n    elif args.type == \"opengauss\":\n        convertor = OpengaussConvertor(sql_file)\n    else:\n        raise NotImplementedError(f\"不支持目标数据库类型: {args.type}\")\n\n    convertor.print()\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "sql/tools/docker-compose.yaml",
    "content": "name: ruoyi-vue-pro\n\nvolumes:\n    mysql: { }\n    postgres: { }\n    sqlserver: { }\n    dm8: { }\n    kingbase: { }\n    opengauss: { }\n\nservices:\n    mysql:\n        image: mysql:8.0.33\n        restart: unless-stopped\n        environment:\n            TZ: Asia/Shanghai\n            MYSQL_ROOT_PASSWORD: 123456\n            MYSQL_DATABASE: ruoyi-vue-pro\n        ports:\n            - \"3306:3306\"\n        volumes:\n            - mysql:/var/lib/mysql/\n            # 注入初始化脚本\n            - ./mysql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/init.sql:ro\n        command:\n            --default-authentication-plugin=mysql_native_password\n            --character-set-server=utf8mb4\n            --collation-server=utf8mb4_general_ci\n            --explicit_defaults_for_timestamp=true\n            --lower_case_table_names=1\n\n    postgres:\n        image: postgres:14.2\n        restart: unless-stopped\n        environment:\n            POSTGRES_USER: root\n            POSTGRES_PASSWORD: 123456\n            POSTGRES_DB: ruoyi-vue-pro\n        ports:\n            - \"5432:5432\"\n        volumes:\n            - postgres:/var/lib/postgresql/data\n            # 注入初始化脚本\n            - ../postgresql/quartz.sql:/docker-entrypoint-initdb.d/quartz.sql:ro\n            - ../postgresql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/ruoyi-vue-pro.sql:ro\n\n    oracle:\n        image: gvenzl/oracle-xe:18-slim-faststart\n        restart: unless-stopped\n        environment:\n            ## 登录信息 SID: XE user: system password: oracle\n            ORACLE_PASSWORD: oracle\n        ports:\n            - \"1521:1521\"\n        volumes:\n            - ../oracle/ruoyi-vue-pro.sql:/tmp/schema.sql:ro\n            # 创建app用户: ROOT/123456@//localhost/XEPDB1\n            - ./oracle/1_create_user.sql:/docker-entrypoint-initdb.d/1_create_user.sql:ro\n            - ./oracle/2_create_schema.sh:/docker-entrypoint-initdb.d/2_create_schema.sh:ro\n\n    oracle_m1:\n      image: einslib/oracle-19c:19.3.0-ee-slim-faststart\n      restart: unless-stopped\n      environment:\n        ## 登录信息 SID: FREE user: system password: oracle\n        ORACLE_PASSWORD: oracle\n      ports:\n        - \"1521:1521\"\n      volumes:\n        - ../oracle/ruoyi-vue-pro.sql:/tmp/schema.sql:ro\n        # 创建app用户: ROOT/123456@//localhost/XEPDB1\n        - ./oracle/1_create_user.sql:/docker-entrypoint-initdb.d/1_create_user.sql:ro\n        - ./oracle/2_create_schema.sh:/docker-entrypoint-initdb.d/2_create_schema.sh:ro\n\n    sqlserver:\n        image: mcr.microsoft.com/mssql/server:2017-latest\n        restart: unless-stopped\n        environment:\n            TZ: Asia/Shanghai\n            ACCEPT_EULA: \"Y\"\n            SA_PASSWORD: \"Yudao@2024\"\n        ports:\n            - \"1433:1433\"\n        volumes:\n            - sqlserver:/var/opt/mssql\n            - ../sqlserver/ruoyi-vue-pro.sql:/tmp/schema.sql:ro\n            # docker compose exec sqlserver bash /tmp/create_schema.sh\n            - ./sqlserver/create_schema.sh:/tmp/create_schema.sh:ro\n\n    dm8:\n        # docker load -i dm8_20240715_x86_rh6_rq_single.tar\n        image: dm8_single:dm8_20240715_rev232765_x86_rh6_64\n        restart: unless-stopped\n        environment:\n            PAGE_SIZE: 16\n            LD_LIBRARY_PATH: /opt/dmdbms/bin\n            EXTENT_SIZE: 32\n            BLANK_PAD_MODE: 1\n            LOG_SIZE: 1024\n            UNICODE_FLAG: 1\n            LENGTH_IN_CHAR: 1\n            INSTANCE_NAME: dm8_test\n        ports:\n            - \"5236:5236\"\n        volumes:\n            - dm8:/opt/dmdbms/data\n            - ../dm/ruoyi-vue-pro-dm8.sql:/tmp/schema.sql:ro\n\n    kingbase:\n        image: kingbase_v009r001c001b0025_single_x86:v1\n#        image: kingbase_v009r001c001b0025_single_arm:v1\n        restart: unless-stopped\n        environment:\n            DB_USER: root\n            DB_PASSWORD: 123456\n        ports:\n            - \"54321:54321\"\n        volumes:\n            - kingbase:/home/kingbase/userdata\n            - ../kingbase/ruoyi-vue-pro.sql:/tmp/schema.sql:ro\n\n    opengauss:\n        image: opengauss/opengauss:5.0.0\n        restart: unless-stopped\n        environment:\n            GS_USERNAME: root\n            GS_PASSWORD: Yudao@2024\n            LD_LIBRARY_PATH: /usr/local/opengauss/lib:/usr/lib\n        ports:\n            - \"5432:5432\"\n        volumes:\n            - opengauss:/var/lib/opengauss\n            - ../opengauss/ruoyi-vue-pro.sql:/tmp/schema.sql:ro\n            # docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql'"
  },
  {
    "path": "sql/tools/oracle/1_create_user.sql",
    "content": "ALTER SESSION SET CONTAINER=XEPDB1;\nCREATE USER ROOT IDENTIFIED BY 123456 QUOTA UNLIMITED ON USERS;\nGRANT CONNECT, RESOURCE TO ROOT;\n"
  },
  {
    "path": "sql/tools/oracle/2_create_schema.sh",
    "content": "sqlplus -s ROOT/123456@//localhost/XEPDB1 @/tmp/schema.sql\n"
  },
  {
    "path": "sql/tools/sqlserver/create_schema.sh",
    "content": "#!/usr/bin/env bash\n\n/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${SA_PASSWORD} -Q \"CREATE DATABASE [ruoyi-vue-pro];\nGO\"\n/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${SA_PASSWORD} -d 'ruoyi-vue-pro' -i /tmp/schema.sql\n"
  },
  {
    "path": "yudao-dependencies/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cn.iocoder.cloud</groupId>\n    <artifactId>yudao-dependencies</artifactId>\n    <version>${revision}</version>\n    <packaging>pom</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>基础 bom 文件，管理整个项目的依赖版本</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <properties>\n        <revision>2026.01-jdk8-SNAPSHOT</revision>\n        <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>\n        <!-- 统一依赖管理 -->\n        <spring.framework.version>5.3.39</spring.framework.version>\n        <spring.security.version>5.8.16</spring.security.version>\n        <spring.boot.version>2.7.18</spring.boot.version>\n        <spring.cloud.version>2021.0.9</spring.cloud.version>\n        <spring.cloud.alibaba.version>2021.0.6.2</spring.cloud.alibaba.version>\n        <!-- Web 相关 -->\n        <servlet.versoin>2.5</servlet.versoin>\n        <springdoc.version>1.8.0</springdoc.version>\n        <knife4j.version>4.5.0</knife4j.version>\n        <!-- DB 相关 -->\n        <druid.version>1.2.27</druid.version>\n        <mybatis.version>3.5.19</mybatis.version>\n        <mybatis-plus.version>3.5.15</mybatis-plus.version>\n        <mybatis-plus-join.version>1.5.5</mybatis-plus-join.version>\n        <dynamic-datasource.version>4.5.0</dynamic-datasource.version>\n        <easy-trans.version>3.0.6</easy-trans.version>\n        <redisson.version>3.52.0</redisson.version>\n        <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>\n        <kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>\n        <opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>\n        <taos.version>3.7.9</taos.version>\n        <!-- 消息队列 -->\n        <rocketmq-spring.version>2.3.5</rocketmq-spring.version>\n        <!-- RPC 相关 -->\n        <!-- Config 配置中心相关 -->\n        <!-- Job 定时任务相关 -->\n        <xxl-job.version>2.4.0</xxl-job.version>\n        <!-- 服务保障相关 -->\n        <lock4j.version>2.2.7</lock4j.version>\n        <!-- 监控相关 -->\n        <skywalking.version>8.12.0</skywalking.version>\n        <spring-boot-admin.version>2.7.15</spring-boot-admin.version>\n        <opentracing.version>0.33.0</opentracing.version>\n        <!-- Test 测试相关 -->\n        <podam.version>7.2.11.RELEASE</podam.version> <!-- Spring Boot 2.X 最多使用 7.2.11 版本 -->\n        <jedis-mock.version>1.1.12</jedis-mock.version>\n        <mockito-inline.version>4.11.0</mockito-inline.version>\n        <!-- Bpm 工作流相关 -->\n        <flowable.version>6.8.0</flowable.version>\n        <!-- 工具类相关 -->\n        <anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>\n        <jsoup.version>1.21.2</jsoup.version>\n        <lombok.version>1.18.42</lombok.version>\n        <mapstruct.version>1.6.3</mapstruct.version>\n        <hutool-5.version>5.8.42</hutool-5.version>\n        <fastexcel.version>1.3.0</fastexcel.version>\n        <velocity.version>2.4</velocity.version> <!-- JDK8 不能从 2.4 升级到 2.4.1，会报包不存在！！！！ -->\n        <fastjson.version>1.2.83</fastjson.version>\n        <guava.version>33.5.0-jre</guava.version>\n        <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>\n        <commons-net.version>3.12.0</commons-net.version>\n        <commons-lang3.version>3.20.0</commons-lang3.version>\n        <jsch.version>2.27.7</jsch.version>\n        <tika-core.version>2.9.3</tika-core.version> <!-- JDK8 不能从 2.9.3 升级到 3.X，会报 JDK8 不支持 -->\n        <ip2region.version>2.7.0</ip2region.version>\n        <bizlog-sdk.version>3.0.6</bizlog-sdk.version>\n        <reflections.version>0.10.2</reflections.version>\n        <netty.version>4.2.9.Final</netty.version>\n        <mqtt.version>1.2.5</mqtt.version>\n        <vertx.version>4.5.22</vertx.version>\n        <okhttp.version>4.12.0</okhttp.version>\n        <californium.version>3.12.0</californium.version>\n        <j2mod.version>3.2.1</j2mod.version>\n        <!-- 三方云服务相关 -->\n        <awssdk.version>2.40.15</awssdk.version>\n        <justauth.version>1.16.7</justauth.version>\n        <justauth-starter.version>1.4.0</justauth-starter.version>\n        <jimureport.version>2.1.3</jimureport.version>\n        <jimubi.version>2.3.0</jimubi.version>\n        <weixin-java.version>4.7.9-20251224.161447</weixin-java.version>\n        <alipay-sdk-java.version>4.40.607.ALL</alipay-sdk-java.version>\n        <!-- 专属于 JDK8 安全漏洞升级 -->\n        <logback.version>1.2.13</logback.version> <!-- 无法使用 1.3.X 版本，启动会报错 -->\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <!-- 统一依赖管理 -->\n            <dependency>\n                <groupId>io.netty</groupId>\n                <artifactId>netty-bom</artifactId>\n                <version>${netty.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-framework-bom</artifactId> <!-- JDK8 版本独有：保证 Spring Framework 尽量高 -->\n                <version>${spring.framework.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.security</groupId>\n                <artifactId>spring-security-bom</artifactId> <!-- JDK8 版本独有：保证 Spring Security 尽量高 -->\n                <version>${spring.security.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n\n            <!-- 业务组件 -->\n            <dependency>\n                <groupId>io.github.mouzt</groupId>\n                <artifactId>bizlog-sdk</artifactId>\n                <version>${bizlog-sdk.version}</version>\n                <exclusions>\n                    <exclusion> <!-- 排除掉springboot依赖使用项目的 -->\n                        <groupId>org.springframework.boot</groupId>\n                        <artifactId>spring-boot-starter</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <!-- Spring 核心 -->\n            <dependency>\n                <!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-configuration-processor</artifactId>\n                <version>${spring.boot.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-env</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <!-- Web 相关 -->\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-web</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-security</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-websocket</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.springdoc</groupId> <!-- 接口文档 UI：默认 -->\n                <artifactId>springdoc-openapi-ui</artifactId>\n                <version>${springdoc.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UI：knife4j -->\n                <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>\n                <version>${knife4j.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UI：knife4j【网关专属】 -->\n                <artifactId>knife4j-gateway-spring-boot-starter</artifactId>\n                <version>${knife4j.version}</version>\n            </dependency>\n\n            <!-- DB 相关 -->\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springdoc</groupId>\n                <artifactId>springdoc-openapi-webflux-ui</artifactId>\n                <version>${springdoc.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>druid-spring-boot-starter</artifactId>\n                <version>${druid.version}</version>\n            </dependency>\n            <dependency>\n                <!-- 注意：必须声明，避免 flowable 和 mybatis-plus 引入的 mybatis 版本不一致！！！ -->\n                <groupId>org.mybatis</groupId>\n                <artifactId>mybatis</artifactId>\n                <version>${mybatis.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>mybatis-plus-boot-starter</artifactId>\n                <version>${mybatis-plus.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>mybatis-plus-jsqlparser-4.9</artifactId>\n                <version>${mybatis-plus.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器，使用它解析表结构 -->\n                <version>${mybatis-plus.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->\n                <version>${dynamic-datasource.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.github.yulichang</groupId>\n                <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->\n                <version>${mybatis-plus-join.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-redis</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->\n                <artifactId>easy-trans-spring-boot-starter</artifactId>\n                <version>${easy-trans.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <groupId>org.springframework</groupId>\n                        <artifactId>spring-context</artifactId>\n                    </exclusion>\n                    <exclusion>\n                        <groupId>org.springframework.cloud</groupId>\n                        <artifactId>spring-cloud-commons</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n            <dependency>\n                <groupId>com.fhs-opensource</groupId>\n                <artifactId>easy-trans-mybatis-plus-extend</artifactId>\n                <version>${easy-trans.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.fhs-opensource</groupId>\n                <artifactId>easy-trans-anno</artifactId>\n                <version>${easy-trans.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.redisson</groupId>\n                <artifactId>redisson-spring-boot-starter</artifactId>\n                <version>${redisson.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <groupId>org.springframework.boot</groupId>\n                        <artifactId>spring-boot-starter-actuator</artifactId>\n                    </exclusion>\n                    <exclusion>\n                        <groupId>org.redisson</groupId>\n                        <!-- 使用 redisson-spring-data-27 替代，解决 Tuple NoClassDefFoundError 报错 -->\n                        <artifactId>redisson-spring-data-35</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n            <dependency>\n                <groupId>org.redisson</groupId>\n                <artifactId>redisson-spring-data-27</artifactId>\n                <version>${redisson.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.dameng</groupId>\n                <artifactId>DmJdbcDriver18</artifactId>\n                <version>${dm8.jdbc.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.opengauss</groupId>\n                <artifactId>opengauss-jdbc</artifactId>\n                <version>${opengauss.jdbc.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>cn.com.kingbase</groupId>\n                <artifactId>kingbase8</artifactId>\n                <version>${kingbase.jdbc.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.taosdata.jdbc</groupId>\n                <artifactId>taos-jdbcdriver</artifactId>\n                <version>${taos.version}</version>\n            </dependency>\n\n            <!-- RPC 远程调用相关 -->\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <!-- Registry 注册中心相关 -->\n\n            <!-- Config 配置中心相关 -->\n\n            <!-- Job 定时任务相关 -->\n            <dependency>\n                <groupId>com.xuxueli</groupId>\n                <artifactId>xxl-job-core</artifactId>\n                <version>${xxl-job.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-job</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <!-- 消息队列相关 -->\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-mq</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.rocketmq</groupId>\n                <artifactId>rocketmq-spring-boot-starter</artifactId>\n                <version>${rocketmq-spring.version}</version>\n            </dependency>\n\n            <!-- 服务保障相关 -->\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-protection</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>lock4j-redisson-spring-boot-starter</artifactId>\n                <version>${lock4j.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <artifactId>redisson-spring-boot-starter</artifactId>\n                        <groupId>org.redisson</groupId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n\n            <!-- 监控相关 -->\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.skywalking</groupId>\n                <artifactId>apm-toolkit-trace</artifactId>\n                <version>${skywalking.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.skywalking</groupId>\n                <artifactId>apm-toolkit-logback-1.x</artifactId>\n                <version>${skywalking.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.skywalking</groupId>\n                <artifactId>apm-toolkit-opentracing</artifactId>\n                <version>${skywalking.version}</version>\n                <!--                <exclusions>-->\n                <!--                    <exclusion>-->\n                <!--                        <artifactId>opentracing-api</artifactId>-->\n                <!--                        <groupId>io.opentracing</groupId>-->\n                <!--                    </exclusion>-->\n                <!--                    <exclusion>-->\n                <!--                        <artifactId>opentracing-util</artifactId>-->\n                <!--                        <groupId>io.opentracing</groupId>-->\n                <!--                    </exclusion>-->\n                <!--                </exclusions>-->\n            </dependency>\n            <dependency>\n                <groupId>io.opentracing</groupId>\n                <artifactId>opentracing-api</artifactId>\n                <version>${opentracing.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.opentracing</groupId>\n                <artifactId>opentracing-util</artifactId>\n                <version>${opentracing.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.opentracing</groupId>\n                <artifactId>opentracing-noop</artifactId>\n                <version>${opentracing.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>de.codecentric</groupId>\n                <artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->\n                <version>${spring-boot-admin.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>de.codecentric</groupId>\n                <artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->\n                <version>${spring-boot-admin.version}</version>\n            </dependency>\n\n            <!-- Test 测试相关 -->\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-test</artifactId>\n                <version>${revision}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.mockito</groupId>\n                <artifactId>mockito-inline</artifactId>\n                <version>${mockito-inline.version}</version> <!-- 支持 Mockito 的 final 类与 static 方法的 mock -->\n            </dependency>\n\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-test</artifactId>\n                <version>${spring.boot.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <artifactId>asm</artifactId>\n                        <groupId>org.ow2.asm</groupId>\n                    </exclusion>\n                    <exclusion>\n                        <groupId>org.mockito</groupId>\n                        <artifactId>mockito-core</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n\n            <dependency>\n                <groupId>com.github.fppt</groupId> <!-- 单元测试，我们采用内嵌的 Redis 数据库 -->\n                <artifactId>jedis-mock</artifactId>\n                <version>${jedis-mock.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>uk.co.jemos.podam</groupId> <!-- 单元测试，随机生成 POJO 类 -->\n                <artifactId>podam</artifactId>\n                <version>${podam.version}</version>\n            </dependency>\n\n            <!-- 工作流相关 -->\n            <dependency>\n                <groupId>org.flowable</groupId>\n                <artifactId>flowable-spring-boot-starter-process</artifactId>\n                <version>${flowable.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.flowable</groupId>\n                <artifactId>flowable-spring-boot-starter-actuator</artifactId>\n                <version>${flowable.version}</version>\n            </dependency>\n            <!-- 工作流相关结束 -->\n\n            <!-- 工具类相关 -->\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-common</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>cn.iocoder.cloud</groupId>\n                <artifactId>yudao-spring-boot-starter-excel</artifactId>\n                <version>${revision}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.projectlombok</groupId>\n                <artifactId>lombok</artifactId>\n                <version>${lombok.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.mapstruct</groupId>\n                <artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->\n                <version>${mapstruct.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.mapstruct</groupId>\n                <artifactId>mapstruct-jdk8</artifactId>\n                <version>${mapstruct.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.mapstruct</groupId>\n                <artifactId>mapstruct-processor</artifactId>\n                <version>${mapstruct.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>cn.hutool</groupId>\n                <artifactId>hutool-all</artifactId>\n                <version>${hutool-5.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>cn.idev.excel</groupId>\n                <artifactId>fastexcel</artifactId>\n                <version>${fastexcel.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.tika</groupId>\n                <artifactId>tika-core</artifactId> <!-- 文件类型的识别 -->\n                <version>${tika-core.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.velocity</groupId>\n                <artifactId>velocity-engine-core</artifactId>\n                <version>${velocity.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>fastjson</artifactId>\n                <version>${fastjson.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.guava</groupId>\n                <artifactId>guava</artifactId>\n                <version>${guava.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>transmittable-thread-local</artifactId> <!-- 解决 ThreadLocal 父子线程的传值问题 -->\n                <version>${transmittable-thread-local.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>commons-net</groupId>\n                <artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 -->\n                <version>${commons-net.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.github.mwiede</groupId>\n                <artifactId>jsch</artifactId> <!-- 解决 sftp 连接 -->\n                <version>${jsch.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-lang3</artifactId>\n                <version>${commons-lang3.version}</version> <!-- 解决 CVE-2025-48924 漏洞 -->\n            </dependency>\n\n            <dependency>\n                <groupId>com.anji-plus</groupId>\n                <artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码，一般用于登录使用 -->\n                <version>${anji-plus-captcha.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.lionsoul</groupId>\n                <artifactId>ip2region</artifactId>\n                <version>${ip2region.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.jsoup</groupId>\n                <artifactId>jsoup</artifactId>\n                <version>${jsoup.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.reflections</groupId>\n                <artifactId>reflections</artifactId>\n                <version>${reflections.version}</version>\n            </dependency>\n\n            <!-- Vert.x -->\n            <dependency>\n                <groupId>io.vertx</groupId>\n                <artifactId>vertx-core</artifactId>\n                <version>${vertx.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.vertx</groupId>\n                <artifactId>vertx-web</artifactId>\n                <version>${vertx.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.vertx</groupId>\n                <artifactId>vertx-mqtt</artifactId>\n                <version>${vertx.version}</version>\n            </dependency>\n\n            <!-- MQTT -->\n            <dependency>\n                <groupId>org.eclipse.paho</groupId>\n                <artifactId>org.eclipse.paho.client.mqttv3</artifactId>\n                <version>${mqtt.version}</version>\n            </dependency>\n\n            <!-- OkHttp -->\n            <dependency>\n                <groupId>com.squareup.okhttp3</groupId>\n                <artifactId>okhttp</artifactId>\n                <version>${okhttp.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.squareup.okhttp3</groupId>\n                <artifactId>mockwebserver</artifactId>\n                <version>${okhttp.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <!-- CoAP - Eclipse Californium -->\n            <dependency>\n                <groupId>org.eclipse.californium</groupId>\n                <artifactId>californium-core</artifactId>\n                <version>${californium.version}</version>\n            </dependency>\n\n            <!-- Modbus 相关 -->\n            <dependency>\n                <groupId>com.ghgande</groupId>\n                <artifactId>j2mod</artifactId>\n                <version>${j2mod.version}</version>\n            </dependency>\n\n            <!-- 三方云服务相关 -->\n            <dependency>\n                <groupId>software.amazon.awssdk</groupId>\n                <artifactId>s3</artifactId>\n                <version>${awssdk.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>me.zhyd.oauth</groupId>\n                <artifactId>JustAuth</artifactId> <!-- 社交登陆（例如说，个人微信、企业微信等等） -->\n                <version>${justauth.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.xkcoding.justauth</groupId>\n                <artifactId>justauth-spring-boot-starter</artifactId>\n                <version>${justauth-starter.version}</version>\n                <exclusions>\n                    <!-- 移除，避免和项目里的 hutool-all 冲突 -->\n                    <exclusion>\n                        <groupId>cn.hutool</groupId>\n                        <artifactId>hutool-core</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sdk</groupId>\n                <artifactId>alipay-sdk-java</artifactId>\n                <version>${alipay-sdk-java.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <groupId>org.bouncycastle</groupId>\n                        <artifactId>bcprov-jdk15on</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n\n            <dependency>\n                <groupId>com.github.binarywang</groupId>\n                <artifactId>weixin-java-pay</artifactId>\n                <version>${weixin-java.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.github.binarywang</groupId>\n                <artifactId>wx-java-mp-spring-boot-starter</artifactId>\n                <version>${weixin-java.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.github.binarywang</groupId>\n                <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>\n                <version>${weixin-java.version}</version>\n            </dependency>\n\n            <!-- 积木报表-->\n            <dependency>\n                <groupId>org.jeecgframework.jimureport</groupId>\n                <artifactId>jimureport-spring-boot-starter</artifactId>\n                <version>${jimureport.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.jeecgframework.jimureport</groupId>\n                <artifactId>jimubi-spring-boot-starter</artifactId>\n                <version>${jimubi.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <groupId>com.github.jsqlparser</groupId>\n                        <artifactId>jsqlparser</artifactId>\n                    </exclusion>\n                    <exclusion>\n                        <groupId>cn.hutool</groupId>\n                        <artifactId>hutool-core</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <!-- 统一 revision 版本 -->\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>flatten-maven-plugin</artifactId>\n                <version>${flatten-maven-plugin.version}</version>\n                <configuration>\n                    <flattenMode>bom</flattenMode>\n                    <updatePomFile>true</updatePomFile>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>flatten</goal>\n                        </goals>\n                        <id>flatten</id>\n                        <phase>process-resources</phase>\n                    </execution>\n                    <execution>\n                        <goals>\n                            <goal>clean</goal>\n                        </goals>\n                        <id>flatten.clean</id>\n                        <phase>clean</phase>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>yudao</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <packaging>pom</packaging>\n    <modules>\n        <module>yudao-common</module>\n        <module>yudao-spring-boot-starter-env</module>\n        <module>yudao-spring-boot-starter-mybatis</module>\n        <module>yudao-spring-boot-starter-redis</module>\n        <module>yudao-spring-boot-starter-web</module>\n        <module>yudao-spring-boot-starter-security</module>\n        <module>yudao-spring-boot-starter-websocket</module>\n\n        <module>yudao-spring-boot-starter-monitor</module>\n        <module>yudao-spring-boot-starter-protection</module>\n<!--        <module>yudao-spring-boot-starter-config</module>-->\n        <module>yudao-spring-boot-starter-job</module>\n        <module>yudao-spring-boot-starter-mq</module>\n        <module>yudao-spring-boot-starter-rpc</module>\n\n        <module>yudao-spring-boot-starter-excel</module>\n        <module>yudao-spring-boot-starter-test</module>\n\n        <module>yudao-spring-boot-starter-biz-tenant</module>\n        <module>yudao-spring-boot-starter-biz-data-permission</module>\n        <module>yudao-spring-boot-starter-biz-ip</module>\n    </modules>\n\n    <artifactId>yudao-framework</artifactId>\n    <description>\n        该包是技术组件，每个子包，代表一个组件。每个组件包括两部分：\n            1. core 包：是该组件的核心封装\n            2. config 包：是该组件基于 Spring 的配置\n\n        技术组件，也分成两类：\n            1. 框架组件：和我们熟悉的 MyBatis、Redis 等等的拓展\n            2. 业务组件：和业务相关的组件的封装，例如说数据字典、操作日志等等。\n        如果是业务组件，Maven 名字会包含 biz\n    </description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-common/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-common</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>定义基础 pojo 类、枚举、工具类等等</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <!-- Spring 核心 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-core</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-expression</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aop</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <dependency>\n            <!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <dependency>\n            <groupId>jakarta.servlet</groupId>\n            <artifactId>jakarta.servlet-api</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <dependency>\n            <groupId>org.springdoc</groupId>\n            <artifactId>springdoc-openapi-ui</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-openfeign-core</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，主要是 api 包使用到 -->\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>org.apache.skywalking</groupId>\n            <artifactId>apm-toolkit-trace</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->\n        </dependency>\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct-processor</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-core</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.datatype</groupId>\n            <artifactId>jackson-datatype-jsr310</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <dependency>\n            <groupId>jakarta.validation</groupId>\n            <artifactId>jakarta.validation-api</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，主要是 PageParam 使用到 -->\n        </dependency>\n\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-all</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>transmittable-thread-local</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.jsoup</groupId>\n            <artifactId>jsoup</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->\n            <artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因，方便 xxx-module-api 包使用 -->\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/ApiAccessLogCommonApi.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.infra.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\n\nimport javax.validation.Valid;\n\n@FeignClient(name = RpcConstants.INFRA_NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - API 访问日志\")\npublic interface ApiAccessLogCommonApi {\n\n    String PREFIX = RpcConstants.INFRA_PREFIX + \"/api-access-log\";\n\n    @PostMapping(PREFIX + \"/create\")\n    @Operation(summary = \"创建 API 访问日志\")\n    CommonResult<Boolean> createApiAccessLog(@Valid @RequestBody ApiAccessLogCreateReqDTO createDTO);\n\n    /**\n     * 【异步】创建 API 访问日志\n     *\n     * @param createDTO 访问日志 DTO\n     */\n    @Async\n    default void createApiAccessLogAsync(ApiAccessLogCreateReqDTO createDTO) {\n        createApiAccessLog(createDTO).checkError();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/ApiErrorLogCommonApi.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.infra.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\n\nimport javax.validation.Valid;\n\n@FeignClient(name = RpcConstants.INFRA_NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - API 异常日志\")\npublic interface ApiErrorLogCommonApi {\n\n    String PREFIX = RpcConstants.INFRA_PREFIX + \"/api-error-log\";\n\n    @PostMapping(PREFIX + \"/create\")\n    @Operation(summary = \"创建 API 异常日志\")\n    CommonResult<Boolean> createApiErrorLog(@Valid @RequestBody ApiErrorLogCreateReqDTO createDTO);\n\n    /**\n     * 【异步】创建 API 异常日志\n     *\n     * @param createDTO 异常日志 DTO\n     */\n    @Async\n    default void createApiErrorLogAsync(ApiErrorLogCreateReqDTO createDTO) {\n        createApiErrorLog(createDTO).checkError();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/dto/ApiAccessLogCreateReqDTO.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.infra.logger.dto;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\n/**\n * API 访问日志\n *\n * @author 芋道源码\n */\n@Data\npublic class ApiAccessLogCreateReqDTO {\n\n    /**\n     * 链路追踪编号\n     */\n    private String traceId;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n    /**\n     * 用户类型\n     */\n    private Integer userType;\n    /**\n     * 应用名\n     */\n    @NotNull(message = \"应用名不能为空\")\n    private String applicationName;\n\n    /**\n     * 请求方法名\n     */\n    @NotNull(message = \"http 请求方法不能为空\")\n    private String requestMethod;\n    /**\n     * 访问地址\n     */\n    @NotNull(message = \"访问地址不能为空\")\n    private String requestUrl;\n    /**\n     * 请求参数\n     */\n    private String requestParams;\n    /**\n     * 响应结果\n     */\n    private String responseBody;\n    /**\n     * 用户 IP\n     */\n    @NotNull(message = \"ip 不能为空\")\n    private String userIp;\n    /**\n     * 浏览器 UA\n     */\n    @NotNull(message = \"User-Agent 不能为空\")\n    private String userAgent;\n\n    /**\n     * 操作模块\n     */\n    private String operateModule;\n    /**\n     * 操作名\n     */\n    private String operateName;\n    /**\n     * 操作分类\n     *\n     * 枚举，参见 OperateTypeEnum 类\n     */\n    private Integer operateType;\n\n    /**\n     * 开始请求时间\n     */\n    @NotNull(message = \"开始请求时间不能为空\")\n    private LocalDateTime beginTime;\n    /**\n     * 结束请求时间\n     */\n    @NotNull(message = \"结束请求时间不能为空\")\n    private LocalDateTime endTime;\n    /**\n     * 执行时长，单位：毫秒\n     */\n    @NotNull(message = \"执行时长不能为空\")\n    private Integer duration;\n    /**\n     * 结果码\n     */\n    @NotNull(message = \"错误码不能为空\")\n    private Integer resultCode;\n    /**\n     * 结果提示\n     */\n    private String resultMsg;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/dto/ApiErrorLogCreateReqDTO.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.infra.logger.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"RPC 服务 - API 错误日志创建 Request DTO\")\n@Data\npublic class ApiErrorLogCreateReqDTO {\n\n    @Schema(description = \"链路追踪编号\", example = \"89aca178-a370-411c-ae02-3f0d672be4ab\")\n    private String traceId;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long userId;\n    @Schema(description = \"用户类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer userType;\n    @Schema(description = \"应用名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"system-server\")\n    @NotNull(message = \"应用名不能为空\")\n    private String applicationName;\n\n    @Schema(description = \"请求方法名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"GET\")\n    @NotNull(message = \"http 请求方法不能为空\")\n    private String requestMethod;\n    @Schema(description = \"请求地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"/xxx/yyy\")\n    @NotNull(message = \"访问地址不能为空\")\n    private String requestUrl;\n    @Schema(description = \"请求参数\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"请求参数不能为空\")\n    private String requestParams;\n    @Schema(description = \"用户 IP\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"127.0.0.1\")\n    @NotNull(message = \"ip 不能为空\")\n    private String userIp;\n    @Schema(description = \"浏览器 UserAgent\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Mozilla/5.0\")\n    @NotNull(message = \"User-Agent 不能为空\")\n    private String userAgent;\n\n    @Schema(description = \"异常时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"异常时间不能为空\")\n    private LocalDateTime exceptionTime;\n    @Schema(description = \"异常名\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"异常名不能为空\")\n    private String exceptionName;\n    @Schema(description = \"异常发生的类全名\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"异常发生的类全名不能为空\")\n    private String exceptionClassName;\n    @Schema(description = \"异常发生的类文件\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"异常发生的类文件不能为空\")\n    private String exceptionFileName;\n    @Schema(description = \"异常发生的方法名\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"异常发生的方法名不能为空\")\n    private String exceptionMethodName;\n    @Schema(description = \"异常发生的方法所在行\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"异常发生的方法所在行不能为空\")\n    private Integer exceptionLineNumber;\n    @Schema(description = \"异常的栈轨迹异常的栈轨迹\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"异常的栈轨迹不能为空\")\n    private String exceptionStackTrace;\n    @Schema(description = \"异常导致的根消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"异常导致的根消息不能为空\")\n    private String exceptionRootCauseMessage;\n    @Schema(description = \"异常导致的消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"异常导致的消息不能为空\")\n    private String exceptionMessage;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/package-info.java",
    "content": "/**\n * 针对 infra 模块的 api 包\n */\npackage cn.iocoder.yudao.framework.common.biz.infra;"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/package-info.java",
    "content": "/**\n * 特殊：用于 framework 下，starter 需要调用 biz 业务模块的接口定义！\n */\npackage cn.iocoder.yudao.framework.common.biz;"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/dict/DictDataCommonApi.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.dict;\n\nimport cn.iocoder.yudao.framework.common.biz.system.dict.dto.DictDataRespDTO;\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.List;\n\n@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 字典数据\")\npublic interface DictDataCommonApi {\n\n    String PREFIX = RpcConstants.SYSTEM_PREFIX + \"/dict-data\";\n\n    @GetMapping(PREFIX + \"/list\")\n    @Operation(summary = \"获得指定字典类型的字典数据列表\")\n    @Parameter(name = \"dictType\", description = \"字典类型\", example = \"SEX\", required = true)\n    CommonResult<List<DictDataRespDTO>> getDictDataList(@RequestParam(\"dictType\") String dictType);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/dict/dto/DictDataRespDTO.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.dict.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"RPC 服务 - 字典数据 Response DTO\")\n@Data\npublic class DictDataRespDTO {\n\n    @Schema(description = \"字典标签\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String label;\n\n    @Schema(description = \"字典值\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"iocoder\")\n    private String value;\n\n    @Schema(description = \"字典类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"sys_common_sex\")\n    private String dictType;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status; // 参见 CommonStatusEnum 枚举\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/logger/OperateLogCommonApi.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\n\nimport javax.validation.Valid;\n\n@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 操作日志\")\npublic interface OperateLogCommonApi {\n\n    String PREFIX = RpcConstants.SYSTEM_PREFIX + \"/operate-log\";\n\n    @PostMapping(PREFIX + \"/create\")\n    @Operation(summary = \"创建操作日志\")\n    CommonResult<Boolean> createOperateLog(@Valid @RequestBody OperateLogCreateReqDTO createReqDTO);\n\n    /**\n     * 【异步】创建操作日志\n     *\n     * @param createReqDTO 请求\n     */\n    @Async\n    default void createOperateLogAsync(OperateLogCreateReqDTO createReqDTO) {\n        createOperateLog(createReqDTO).checkError();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/logger/dto/OperateLogCreateReqDTO.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.logger.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(name = \"RPC 服务 - 系统操作日志 Create Request DTO\")\n@Data\npublic class OperateLogCreateReqDTO {\n\n    @Schema(description = \"链路追踪编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"89aca178-a370-411c-ae02-3f0d672be4ab\")\n    private String traceId;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"666\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n    @Schema(description = \"用户类型，参见 UserTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\" )\n    @NotNull(message = \"用户类型不能为空\")\n    private Integer userType;\n    @Schema(description = \"操作模块类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"订单\")\n    @NotEmpty(message = \"操作模块类型不能为空\")\n    private String type;\n    @Schema(description = \"操作名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"创建订单\")\n    @NotEmpty(message = \"操作名不能为空\")\n    private String subType;\n    @Schema(description = \"操作模块业务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"188\")\n    @NotNull(message = \"操作模块业务编号不能为空\")\n    private Long bizId;\n    @Schema(description = \"操作内容\", requiredMode = Schema.RequiredMode.REQUIRED,\n            example = \"修改编号为 1 的用户信息，将性别从男改成女，将姓名从芋道改成源码\")\n    @NotEmpty(message = \"操作内容不能为空\")\n    private String action;\n    @Schema(description = \"拓展字段\", example = \"{\\\"orderId\\\": \\\"1\\\"}\")\n    private String extra;\n\n    @Schema(description = \"请求方法名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"GET\")\n    @NotEmpty(message = \"请求方法名不能为空\")\n    private String requestMethod;\n    @Schema(description = \"请求地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"/order/get\")\n    @NotEmpty(message = \"请求地址不能为空\")\n    private String requestUrl;\n    @Schema(description = \"用户 IP\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"127.0.0.1\")\n    @NotEmpty(message = \"用户 IP 不能为空\")\n    private String userIp;\n    @Schema(description = \"浏览器 UserAgent\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Mozilla/5.0\")\n    @NotEmpty(message = \"浏览器 UA 不能为空\")\n    private String userAgent;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/OAuth2TokenCommonApi.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.oauth2;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;\nimport cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.Operation;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\n\n@FeignClient(name = RpcConstants.SYSTEM_NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - OAuth2.0 令牌\")\npublic interface OAuth2TokenCommonApi {\n\n    String PREFIX = RpcConstants.SYSTEM_PREFIX + \"/oauth2/token\";\n\n    /**\n     * 校验 Token 的 URL 地址，主要是提供给 Gateway 使用\n     */\n    @SuppressWarnings(\"HttpUrlsUsage\")\n    String URL_CHECK = \"http://\" + RpcConstants.SYSTEM_NAME + PREFIX + \"/check\";\n\n    @PostMapping(PREFIX + \"/create\")\n    @Operation(summary = \"创建访问令牌\")\n    CommonResult<OAuth2AccessTokenRespDTO> createAccessToken(@Valid @RequestBody OAuth2AccessTokenCreateReqDTO reqDTO);\n\n    @GetMapping(PREFIX + \"/check\")\n    @Operation(summary = \"校验访问令牌\")\n    @Parameter(name = \"accessToken\", description = \"访问令牌\", required = true, example = \"tudou\")\n    CommonResult<OAuth2AccessTokenCheckRespDTO> checkAccessToken(@RequestParam(\"accessToken\") String accessToken);\n\n    @DeleteMapping(PREFIX + \"/remove\")\n    @Operation(summary = \"移除访问令牌\")\n    @Parameter(name = \"accessToken\", description = \"访问令牌\", required = true, example = \"tudou\")\n    CommonResult<OAuth2AccessTokenRespDTO> removeAccessToken(@RequestParam(\"accessToken\") String accessToken);\n\n    @PutMapping(PREFIX + \"/refresh\")\n    @Operation(summary = \"刷新访问令牌\")\n    @Parameters({\n        @Parameter(name = \"refreshToken\", description = \"刷新令牌\", required = true, example = \"haha\"),\n        @Parameter(name = \"clientId\", description = \"客户端编号\", required = true, example = \"yudaoyuanma\")\n    })\n    CommonResult<OAuth2AccessTokenRespDTO> refreshAccessToken(@RequestParam(\"refreshToken\") String refreshToken,\n                                                              @RequestParam(\"clientId\") String clientId);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.oauth2.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"RPC 服务 - OAuth2 访问令牌的校验 Response DTO\")\n@Data\npublic class OAuth2AccessTokenCheckRespDTO implements Serializable {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long userId;\n\n    @Schema(description = \"用户类型，参见 UserTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer userType;\n\n    @Schema(description = \"用户信息\", example = \"{\\\"nickname\\\": \\\"芋道\\\"}\")\n    private Map<String, String> userInfo;\n\n    @Schema(description = \"租户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long tenantId;\n\n    @Schema(description = \"授权范围的数组\", example = \"user_info\")\n    private List<String> scopes;\n\n    @Schema(description = \"过期时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime expiresTime;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.oauth2.dto;\n\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\nimport java.util.List;\n\n@Schema(description = \"RPC 服务 - OAuth2 访问令牌创建 Request DTO\")\n@Data\npublic class OAuth2AccessTokenCreateReqDTO implements Serializable {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    @Schema(description = \"用户类型，参见 UserTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"用户类型不能为空\")\n    @InEnum(value = UserTypeEnum.class, message = \"用户类型必须是 {value}\")\n    private Integer userType;\n\n    @Schema(description = \"客户端编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yudaoyuanma\")\n    @NotNull(message = \"客户端编号不能为空\")\n    private String clientId;\n\n    @Schema(description = \"授权范围的数组\", example = \"user_info\")\n    private List<String> scopes;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenRespDTO.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.oauth2.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"RPC 服务 - OAuth2 访问令牌的信息 Response DTO\")\n@Data\npublic class OAuth2AccessTokenRespDTO implements Serializable {\n\n    @Schema(description = \"访问令牌\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"tudou\")\n    private String accessToken;\n\n    @Schema(description = \"刷新令牌\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"haha\")\n    private String refreshToken;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long userId;\n\n    @Schema(description = \"用户类型，参见 UserTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\" )\n    private Integer userType;\n\n    @Schema(description = \"过期时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime expiresTime;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/package-info.java",
    "content": "/**\n * 针对 system 模块的 api 包\n */\npackage cn.iocoder.yudao.framework.common.biz.system;"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/permission/PermissionCommonApi.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.permission;\n\nimport cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 权限\")\npublic interface PermissionCommonApi {\n\n    String PREFIX = RpcConstants.SYSTEM_PREFIX + \"/permission\";\n\n    @GetMapping(PREFIX + \"/has-any-permissions\")\n    @Operation(summary = \"判断是否有权限，任一一个即可\")\n    @Parameters({\n            @Parameter(name = \"userId\", description = \"用户编号\", example = \"1\", required = true),\n            @Parameter(name = \"permissions\", description = \"权限\", example = \"read,write\", required = true)\n    })\n    CommonResult<Boolean> hasAnyPermissions(@RequestParam(\"userId\") Long userId,\n                                            @RequestParam(\"permissions\") String... permissions);\n\n    @GetMapping(PREFIX + \"/has-any-roles\")\n    @Operation(summary = \"判断是否有角色，任一一个即可\")\n    @Parameters({\n            @Parameter(name = \"userId\", description = \"用户编号\", example = \"1\", required = true),\n            @Parameter(name = \"roles\", description = \"角色数组\", example = \"2\", required = true)\n    })\n    CommonResult<Boolean> hasAnyRoles(@RequestParam(\"userId\") Long userId,\n                                      @RequestParam(\"roles\") String... roles);\n\n    @GetMapping(PREFIX + \"/get-dept-data-permission\")\n    @Operation(summary = \"获得登陆用户的部门数据权限\")\n    @Parameter(name = \"userId\", description = \"用户编号\", example = \"2\", required = true)\n    CommonResult<DeptDataPermissionRespDTO> getDeptDataPermission(@RequestParam(\"userId\") Long userId);\n\n}"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/permission/dto/DeptDataPermissionRespDTO.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.permission.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n@Schema(description = \"RPC 服务 - 部门的数据权限 Response DTO\")\n@Data\npublic class DeptDataPermissionRespDTO {\n\n    @Schema(description = \"是否可查看全部数据\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean all;\n\n    @Schema(description = \"是否可查看自己的数据\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean self;\n\n    @Schema(description = \"可查看的部门编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1, 3]\")\n    private Set<Long> deptIds;\n\n    public DeptDataPermissionRespDTO() {\n        this.all = false;\n        this.self = false;\n        this.deptIds = new HashSet<>();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/tenant/TenantCommonApi.java",
    "content": "package cn.iocoder.yudao.framework.common.biz.system.tenant;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.List;\n\n@FeignClient(name = RpcConstants.SYSTEM_NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 多租户\")\npublic interface TenantCommonApi {\n\n    String PREFIX = RpcConstants.SYSTEM_PREFIX + \"/tenant\";\n\n    @GetMapping(PREFIX + \"/id-list\")\n    @Operation(summary = \"获得所有租户编号\")\n    CommonResult<List<Long>> getTenantIdList();\n\n    @GetMapping(PREFIX + \"/valid\")\n    @Operation(summary = \"校验租户是否合法\")\n    @Parameter(name = \"id\", description = \"租户编号\", required = true, example = \"1024\")\n    CommonResult<Boolean> validTenant(@RequestParam(\"id\") Long id);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/ArrayValuable.java",
    "content": "package cn.iocoder.yudao.framework.common.core;\n\n/**\n * 可生成 T 数组的接口\n *\n * @author HUIHUI\n */\npublic interface ArrayValuable<T> {\n\n    /**\n     * @return 数组\n     */\n    T[] array();\n\n} "
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java",
    "content": "package cn.iocoder.yudao.framework.common.core;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * Key Value 的键值对\n *\n * @author 芋道源码\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class KeyValue<K, V> implements Serializable {\n\n    private K key;\n    private V value;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java",
    "content": "package cn.iocoder.yudao.framework.common.enums;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 通用状态枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum CommonStatusEnum implements ArrayValuable<Integer> {\n\n    ENABLE(0, \"开启\"),\n    DISABLE(1, \"关闭\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态值\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isEnable(Integer status) {\n        return ObjUtil.equal(ENABLE.status, status);\n    }\n\n    public static boolean isDisable(Integer status) {\n        return ObjUtil.equal(DISABLE.status, status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java",
    "content": "package cn.iocoder.yudao.framework.common.enums;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 时间间隔的枚举\n *\n * @author dhb52\n */\n@Getter\n@AllArgsConstructor\npublic enum DateIntervalEnum implements ArrayValuable<Integer> {\n\n    HOUR(0, \"小时\"), // 特殊：字典里，暂时不会有这个枚举！！！因为大多数情况下，用不到这个间隔\n    DAY(1, \"天\"),\n    WEEK(2, \"周\"),\n    MONTH(3, \"月\"),\n    QUARTER(4, \"季度\"),\n    YEAR(5, \"年\")\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(DateIntervalEnum::getInterval).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer interval;\n    /**\n     * 名称\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static DateIntervalEnum valueOf(Integer interval) {\n        return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values());\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DocumentEnum.java",
    "content": "package cn.iocoder.yudao.framework.common.enums;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 文档地址\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum DocumentEnum {\n\n    REDIS_INSTALL(\"https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ\", \"Redis 安装文档\"),\n    TENANT(\"https://doc.iocoder.cn\", \"SaaS 多租户文档\");\n\n    private final String url;\n    private final String memo;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/RpcConstants.java",
    "content": "package cn.iocoder.yudao.framework.common.enums;\n\n/**\n * RPC 相关的枚举\n *\n * 虽然放在 yudao-spring-boot-starter-rpc 会相对合适，但是每个 API 模块需要使用到，所以暂时只好放在此处\n *\n * @author 芋道源码\n */\npublic interface RpcConstants {\n\n    /**\n     * RPC API 的前缀\n     */\n    String RPC_API_PREFIX = \"/rpc-api\";\n\n    /**\n     * system 服务名\n     *\n     * 注意，需要保证和 spring.application.name 保持一致\n     */\n    String SYSTEM_NAME = \"system-server\";\n\n    /**\n     * system 服务的前缀\n     */\n    String SYSTEM_PREFIX = RPC_API_PREFIX + \"/system\";\n\n    /**\n     * infra 服务名\n     *\n     * 注意，需要保证和 spring.application.name 保持一致\n     */\n    String INFRA_NAME = \"infra-server\";\n    /**\n     * infra 服务的前缀\n     */\n    String INFRA_PREFIX = RPC_API_PREFIX + \"/infra\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java",
    "content": "package cn.iocoder.yudao.framework.common.enums;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * 终端的枚举\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum TerminalEnum implements ArrayValuable<Integer> {\n\n    UNKNOWN(0, \"未知\"), // 目的：在无法解析到 terminal 时，使用它\n    WECHAT_MINI_PROGRAM(10, \"微信小程序\"),\n    WECHAT_WAP(11, \"微信公众号\"),\n    H5(20, \"H5 网页\"),\n    APP(31, \"手机 App\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);\n\n    /**\n     * 终端\n     */\n    private final Integer terminal;\n    /**\n     * 终端名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java",
    "content": "package cn.iocoder.yudao.framework.common.enums;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 全局用户类型枚举\n */\n@AllArgsConstructor\n@Getter\npublic enum UserTypeEnum implements ArrayValuable<Integer> {\n\n    MEMBER(1, \"会员\"), // 面向 c 端，普通用户\n    ADMIN(2, \"管理员\"); // 面向 b 端，管理后台\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer value;\n    /**\n     * 类型名\n     */\n    private final String name;\n\n    public static UserTypeEnum valueOf(Integer value) {\n        return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values());\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java",
    "content": "package cn.iocoder.yudao.framework.common.enums;\n\n/**\n * Web 过滤器顺序的枚举类，保证过滤器按照符合我们的预期\n *\n *  考虑到每个 starter 都需要用到该工具类，所以放到 common 模块下的 enum 包下\n *\n * @author 芋道源码\n */\npublic interface WebFilterOrderEnum {\n\n    int CORS_FILTER = Integer.MIN_VALUE;\n\n    int TRACE_FILTER = CORS_FILTER + 1;\n\n    int ENV_TAG_FILTER = TRACE_FILTER + 1;\n\n    int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;\n\n    int API_ENCRYPT_FILTER = REQUEST_BODY_CACHE_FILTER + 1;\n\n    // OrderedRequestContextFilter 默认为 -105，用于国际化上下文等等\n\n    int TENANT_CONTEXT_FILTER = - 104; // 需要保证在 ApiAccessLogFilter 前面\n\n    int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面\n\n    int XSS_FILTER = -102;  // 需要保证在 RequestBodyCacheFilter 后面\n\n    // Spring Security Filter 默认为 -100，可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类\n\n    int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面\n\n    int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面\n\n    int DEMO_FILTER = Integer.MAX_VALUE;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ErrorCode.java",
    "content": "package cn.iocoder.yudao.framework.common.exception;\n\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.exception.enums.ServiceErrorCodeRange;\nimport lombok.Data;\n\n/**\n * 错误码对象\n *\n * 全局错误码，占用 [0, 999], 参见 {@link GlobalErrorCodeConstants}\n * 业务异常错误码，占用 [1 000 000 000, +∞)，参见 {@link ServiceErrorCodeRange}\n *\n * TODO 错误码设计成对象的原因，为未来的 i18 国际化做准备\n */\n@Data\npublic class ErrorCode {\n\n    /**\n     * 错误码\n     */\n    private final Integer code;\n    /**\n     * 错误提示\n     */\n    private final String msg;\n\n    public ErrorCode(Integer code, String message) {\n        this.code = code;\n        this.msg = message;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServerException.java",
    "content": "package cn.iocoder.yudao.framework.common.exception;\n\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n * 服务器异常 Exception\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic final class ServerException extends RuntimeException {\n\n    /**\n     * 全局错误码\n     *\n     * @see GlobalErrorCodeConstants\n     */\n    private Integer code;\n    /**\n     * 错误提示\n     */\n    private String message;\n\n    /**\n     * 空构造方法，避免反序列化问题\n     */\n    public ServerException() {\n    }\n\n    public ServerException(ErrorCode errorCode) {\n        this.code = errorCode.getCode();\n        this.message = errorCode.getMsg();\n    }\n\n    public ServerException(Integer code, String message) {\n        this.code = code;\n        this.message = message;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public ServerException setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    @Override\n    public String getMessage() {\n        return message;\n    }\n\n    public ServerException setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServiceException.java",
    "content": "package cn.iocoder.yudao.framework.common.exception;\n\nimport cn.iocoder.yudao.framework.common.exception.enums.ServiceErrorCodeRange;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n * 业务逻辑异常 Exception\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic final class ServiceException extends RuntimeException {\n\n    /**\n     * 业务错误码\n     *\n     * @see ServiceErrorCodeRange\n     */\n    private Integer code;\n    /**\n     * 错误提示\n     */\n    private String message;\n\n    /**\n     * 空构造方法，避免反序列化问题\n     */\n    public ServiceException() {\n    }\n\n    public ServiceException(ErrorCode errorCode) {\n        this.code = errorCode.getCode();\n        this.message = errorCode.getMsg();\n    }\n\n    public ServiceException(Integer code, String message) {\n        this.code = code;\n        this.message = message;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public ServiceException setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    @Override\n    public String getMessage() {\n        return message;\n    }\n\n    public ServiceException setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.framework.common.exception.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * 全局错误码枚举\n * 0-999 系统异常编码保留\n *\n * 一般情况下，使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status\n * 虽然说，HTTP 响应状态码作为业务使用表达能力偏弱，但是使用在系统层面还是非常不错的\n * 比较特殊的是，因为之前一直使用 0 作为成功，就不使用 200 啦。\n *\n * @author 芋道源码\n */\npublic interface GlobalErrorCodeConstants {\n\n    ErrorCode SUCCESS = new ErrorCode(0, \"成功\");\n\n    // ========== 客户端错误段 ==========\n\n    ErrorCode BAD_REQUEST = new ErrorCode(400, \"请求参数不正确\");\n    ErrorCode UNAUTHORIZED = new ErrorCode(401, \"账号未登录\");\n    ErrorCode FORBIDDEN = new ErrorCode(403, \"没有该操作权限\");\n    ErrorCode NOT_FOUND = new ErrorCode(404, \"请求未找到\");\n    ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, \"请求方法不正确\");\n    ErrorCode LOCKED = new ErrorCode(423, \"请求失败，请稍后重试\"); // 并发请求，不允许\n    ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, \"请求过于频繁，请稍后重试\");\n\n    // ========== 服务端错误段 ==========\n\n    ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, \"系统异常\");\n    ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, \"功能未实现/未开启\");\n    ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, \"错误的配置项\");\n\n    // ========== 自定义错误段 ==========\n    ErrorCode REPEATED_REQUESTS = new ErrorCode(900, \"重复请求，请稍后重试\"); // 重复请求\n    ErrorCode DEMO_DENY = new ErrorCode(901, \"演示模式，禁止写操作\");\n\n    ErrorCode UNKNOWN = new ErrorCode(999, \"未知错误\");\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java",
    "content": "package cn.iocoder.yudao.framework.common.exception.enums;\n\n/**\n * 业务异常的错误码区间，解决：解决各模块错误码定义，避免重复，在此只声明不做实际使用\n *\n * 一共 10 位，分成四段\n *\n * 第一段，1 位，类型\n *      1 - 业务级别异常\n *      x - 预留\n * 第二段，3 位，系统类型\n *      001 - 用户系统\n *      002 - 商品系统\n *      003 - 订单系统\n *      004 - 支付系统\n *      005 - 优惠劵系统\n *      ... - ...\n * 第三段，3 位，模块\n *      不限制规则。\n *      一般建议，每个系统里面，可能有多个模块，可以再去做分段。以用户系统为例子：\n *          001 - OAuth2 模块\n *          002 - User 模块\n *          003 - MobileCode 模块\n * 第四段，3 位，错误码\n *       不限制规则。\n *       一般建议，每个模块自增。\n *\n * @author 芋道源码\n */\npublic class ServiceErrorCodeRange {\n\n    // 模块 infra 错误码区间 [1-001-000-000 ~ 1-002-000-000)\n    // 模块 system 错误码区间 [1-002-000-000 ~ 1-003-000-000)\n    // 模块 report 错误码区间 [1-003-000-000 ~ 1-004-000-000)\n    // 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)\n    // 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000)\n    // 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)\n    // 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)\n\n    // 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)\n    // 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000)\n    // 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000)\n\n    // 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000)\n\n    // 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000)\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/util/ServiceExceptionUtil.java",
    "content": "package cn.iocoder.yudao.framework.common.exception.util;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport com.google.common.annotations.VisibleForTesting;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * {@link ServiceException} 工具类\n *\n * 目的在于，格式化异常信息提示。\n * 考虑到 String.format 在参数不正确时会报错，因此使用 {} 作为占位符，并使用 {@link #doFormat(int, String, Object...)} 方法来格式化\n *\n */\n@Slf4j\npublic class ServiceExceptionUtil {\n\n    // ========== 和 ServiceException 的集成 ==========\n\n    public static ServiceException exception(ErrorCode errorCode) {\n        return exception0(errorCode.getCode(), errorCode.getMsg());\n    }\n\n    public static ServiceException exception(ErrorCode errorCode, Object... params) {\n        return exception0(errorCode.getCode(), errorCode.getMsg(), params);\n    }\n\n    public static ServiceException exception0(Integer code, String messagePattern, Object... params) {\n        String message = doFormat(code, messagePattern, params);\n        return new ServiceException(code, message);\n    }\n\n    public static ServiceException invalidParamException(String messagePattern, Object... params) {\n        return exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), messagePattern, params);\n    }\n\n    // ========== 格式化方法 ==========\n\n    /**\n     * 将错误编号对应的消息使用 params 进行格式化。\n     *\n     * @param code           错误编号\n     * @param messagePattern 消息模版\n     * @param params         参数\n     * @return 格式化后的提示\n     */\n    @VisibleForTesting\n    public static String doFormat(int code, String messagePattern, Object... params) {\n        StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);\n        int i = 0;\n        int j;\n        int l;\n        for (l = 0; l < params.length; l++) {\n            j = messagePattern.indexOf(\"{}\", i);\n            if (j == -1) {\n                log.error(\"[doFormat][参数过多：错误码({})|错误内容({})|参数({})\", code, messagePattern, params);\n                if (i == 0) {\n                    return messagePattern;\n                } else {\n                    sbuf.append(messagePattern.substring(i));\n                    return sbuf.toString();\n                }\n            } else {\n                sbuf.append(messagePattern, i, j);\n                sbuf.append(params[l]);\n                i = j + 2;\n            }\n        }\n        if (messagePattern.indexOf(\"{}\", i) != -1) {\n            log.error(\"[doFormat][参数过少：错误码({})|错误内容({})|参数({})\", code, messagePattern, params);\n        }\n        sbuf.append(messagePattern.substring(i));\n        return sbuf.toString();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/package-info.java",
    "content": "/**\n * 基础的通用类，和框架无关\n *\n * 例如说，CommonResult 为通用返回\n */\npackage cn.iocoder.yudao.framework.common;\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java",
    "content": "package cn.iocoder.yudao.framework.common.pojo;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\n/**\n * 通用返回\n *\n * @param <T> 数据泛型\n */\n@Data\npublic class CommonResult<T> implements Serializable {\n\n    /**\n     * 错误码\n     *\n     * @see ErrorCode#getCode()\n     */\n    private Integer code;\n    /**\n     * 错误提示，用户可阅读\n     *\n     * @see ErrorCode#getMsg() ()\n     */\n    private String msg;\n    /**\n     * 返回数据\n     */\n    private T data;\n\n    /**\n     * 将传入的 result 对象，转换成另外一个泛型结果的对象\n     *\n     * 因为 A 方法返回的 CommonResult 对象，不满足调用其的 B 方法的返回，所以需要进行转换。\n     *\n     * @param result 传入的 result 对象\n     * @param <T> 返回的泛型\n     * @return 新的 CommonResult 对象\n     */\n    public static <T> CommonResult<T> error(CommonResult<?> result) {\n        return error(result.getCode(), result.getMsg());\n    }\n\n    public static <T> CommonResult<T> error(Integer code, String message) {\n        Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), code, \"code 必须是错误的！\");\n        CommonResult<T> result = new CommonResult<>();\n        result.code = code;\n        result.msg = message;\n        return result;\n    }\n\n    public static <T> CommonResult<T> error(ErrorCode errorCode, Object... params) {\n        Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), errorCode.getCode(), \"code 必须是错误的！\");\n        CommonResult<T> result = new CommonResult<>();\n        result.code = errorCode.getCode();\n        result.msg = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), params);\n        return result;\n    }\n\n    public static <T> CommonResult<T> error(ErrorCode errorCode) {\n        return error(errorCode.getCode(), errorCode.getMsg());\n    }\n\n    public static <T> CommonResult<T> success(T data) {\n        CommonResult<T> result = new CommonResult<>();\n        result.code = GlobalErrorCodeConstants.SUCCESS.getCode();\n        result.data = data;\n        result.msg = \"\";\n        return result;\n    }\n\n    public static boolean isSuccess(Integer code) {\n        return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode());\n    }\n\n    @JsonIgnore // 避免 jackson 序列化\n    public boolean isSuccess() {\n        return isSuccess(code);\n    }\n\n    @JsonIgnore // 避免 jackson 序列化\n    public boolean isError() {\n        return !isSuccess();\n    }\n\n    // ========= 和 Exception 异常体系集成 =========\n\n    /**\n     * 判断是否有异常。如果有，则抛出 {@link ServiceException} 异常\n     */\n    public void checkError() throws ServiceException {\n        if (isSuccess()) {\n            return;\n        }\n        // 业务异常\n        throw new ServiceException(code, msg);\n    }\n\n    /**\n     * 判断是否有异常。如果有，则抛出 {@link ServiceException} 异常\n     * 如果没有，则返回 {@link #data} 数据\n     */\n    @JsonIgnore // 避免 jackson 序列化\n    public T getCheckedData() {\n        checkError();\n        return data;\n    }\n\n    public static <T> CommonResult<T> error(ServiceException serviceException) {\n        return error(serviceException.getCode(), serviceException.getMessage());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java",
    "content": "package cn.iocoder.yudao.framework.common.pojo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n@Schema(description=\"分页参数\")\n@Data\npublic class PageParam implements Serializable {\n\n    private static final Integer PAGE_NO = 1;\n    private static final Integer PAGE_SIZE = 10;\n\n    /**\n     * 每页条数 - 不分页\n     *\n     * 例如说，导出接口，可以设置 {@link #pageSize} 为 -1 不分页，查询所有数据。\n     */\n    public static final Integer PAGE_SIZE_NONE = -1;\n\n    @Schema(description = \"页码，从 1 开始\", requiredMode = Schema.RequiredMode.REQUIRED,example = \"1\")\n    @NotNull(message = \"页码不能为空\")\n    @Min(value = 1, message = \"页码最小值为 1\")\n    private Integer pageNo = PAGE_NO;\n\n    @Schema(description = \"每页条数，最大值为 200\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"每页条数不能为空\")\n    @Min(value = 1, message = \"每页条数最小值为 1\")\n    @Max(value = 200, message = \"每页条数最大值为 200\")\n    private Integer pageSize = PAGE_SIZE;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageResult.java",
    "content": "package cn.iocoder.yudao.framework.common.pojo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Schema(description = \"分页结果\")\n@Data\npublic final class PageResult<T> implements Serializable {\n\n    @Schema(description = \"总量\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Long total;\n\n    @Schema(description = \"数据\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<T> list;\n\n    public PageResult() {\n    }\n\n    public PageResult(List<T> list, Long total) {\n        this.list = list;\n        this.total = total;\n    }\n\n    public PageResult(Long total) {\n        this.list = new ArrayList<>();\n        this.total = total;\n    }\n\n    public static <T> PageResult<T> empty() {\n        return new PageResult<>(0L);\n    }\n\n    public static <T> PageResult<T> empty(Long total) {\n        return new PageResult<>(total);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortablePageParam.java",
    "content": "package cn.iocoder.yudao.framework.common.pojo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.util.List;\n\n@Schema(description = \"可排序的分页参数\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SortablePageParam extends PageParam {\n\n    @Schema(description = \"排序字段\")\n    private List<SortingField> sortingFields;\n\n}"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortingField.java",
    "content": "package cn.iocoder.yudao.framework.common.pojo;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * 排序字段 DTO\n *\n * 类名加了 ing 的原因是，避免和 ES SortField 重名。\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SortingField implements Serializable {\n\n    /**\n     * 顺序 - 升序\n     */\n    public static final String ORDER_ASC = \"asc\";\n    /**\n     * 顺序 - 降序\n     */\n    public static final String ORDER_DESC = \"desc\";\n\n    /**\n     * 字段\n     */\n    private String field;\n    /**\n     * 顺序\n     */\n    private String order;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.cache;\n\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\n\nimport java.time.Duration;\nimport java.util.concurrent.Executors;\n\n/**\n * Cache 工具类\n *\n * @author 芋道源码\n */\npublic class CacheUtils {\n\n    /**\n     * 异步刷新的 LoadingCache 最大缓存数量\n     *\n     * @see <a href=\"\">本地缓存 CacheUtils 工具类建议</a>\n     */\n    private static final Integer CACHE_MAX_SIZE = 10000;\n\n    /**\n     * 构建异步刷新的 LoadingCache 对象\n     *\n     * 注意：如果你的缓存和 ThreadLocal 有关系，要么自己处理 ThreadLocal 的传递，要么使用 {@link #buildCache(Duration, CacheLoader)} 方法\n     *\n     * 或者简单理解：\n     * 1、和“人”相关的，使用 {@link #buildCache(Duration, CacheLoader)} 方法\n     * 2、和“全局”、“系统”相关的，使用当前缓存方法\n     *\n     * @param duration 过期时间\n     * @param loader  CacheLoader 对象\n     * @return LoadingCache 对象\n     */\n    public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {\n        return CacheBuilder.newBuilder()\n                .maximumSize(CACHE_MAX_SIZE)\n                // 只阻塞当前数据加载线程，其他线程返回旧值\n                .refreshAfterWrite(duration)\n                // 通过 asyncReloading 实现全异步加载，包括 refreshAfterWrite 被阻塞的加载线程\n                .build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿：可能要思考下，未来要不要做成可配置\n    }\n\n    /**\n     * 构建同步刷新的 LoadingCache 对象\n     *\n     * @param duration 过期时间\n     * @param loader  CacheLoader 对象\n     * @return LoadingCache 对象\n     */\n    public static <K, V> LoadingCache<K, V> buildCache(Duration duration, CacheLoader<K, V> loader) {\n        return CacheBuilder.newBuilder()\n                .maximumSize(CACHE_MAX_SIZE)\n                // 只阻塞当前数据加载线程，其他线程返回旧值\n                .refreshAfterWrite(duration)\n                .build(loader);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/ArrayUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.collection;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.collection.IterUtil;\nimport cn.hutool.core.util.ArrayUtil;\n\nimport java.util.Collection;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * Array 工具类\n *\n * @author 芋道源码\n */\npublic class ArrayUtils {\n\n    /**\n     * 将 object 和 newElements 合并成一个数组\n     *\n     * @param object 对象\n     * @param newElements 数组\n     * @param <T> 泛型\n     * @return 结果数组\n     */\n    @SafeVarargs\n    public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) {\n        if (object == null) {\n            return newElements;\n        }\n        Consumer<T>[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length);\n        result[0] = object;\n        System.arraycopy(newElements, 0, result, 1, newElements.length);\n        return result;\n    }\n\n    public static <T, V> V[] toArray(Collection<T> from, Function<T, V> mapper) {\n        return toArray(convertList(from, mapper));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T[] toArray(Collection<T> from) {\n        if (CollectionUtil.isEmpty(from)) {\n            return (T[]) (new Object[0]);\n        }\n        return ArrayUtil.toArray(from, (Class<T>) IterUtil.getElementType(from.iterator()));\n    }\n\n    public static <T> T get(T[] array, int index) {\n        if (null == array || index >= array.length) {\n            return null;\n        }\n        return array[index];\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.collection;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport com.google.common.collect.ImmutableMap;\n\nimport java.util.*;\nimport java.util.function.*;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static cn.hutool.core.convert.Convert.toCollection;\nimport static java.util.Arrays.asList;\n\n/**\n * Collection 工具类\n *\n * @author 芋道源码\n */\npublic class CollectionUtils {\n\n    public static boolean containsAny(Object source, Object... targets) {\n        return asList(targets).contains(source);\n    }\n\n    public static boolean isAnyEmpty(Collection<?>... collections) {\n        return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);\n    }\n\n    public static <T> boolean anyMatch(Collection<T> from, Predicate<T> predicate) {\n        return from.stream().anyMatch(predicate);\n    }\n\n    public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {\n        if (CollUtil.isEmpty(from)) {\n            return new ArrayList<>();\n        }\n        return from.stream().filter(predicate).collect(Collectors.toList());\n    }\n\n    public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) {\n        if (CollUtil.isEmpty(from)) {\n            return new ArrayList<>();\n        }\n        return distinct(from, keyMapper, (t1, t2) -> t1);\n    }\n\n    public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) {\n        if (CollUtil.isEmpty(from)) {\n            return new ArrayList<>();\n        }\n        return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());\n    }\n\n    public static <T, U> List<U> convertList(T[] from, Function<T, U> func) {\n        if (ArrayUtil.isEmpty(from)) {\n            return new ArrayList<>();\n        }\n        return convertList(Arrays.asList(from), func);\n    }\n\n    public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {\n        if (CollUtil.isEmpty(from)) {\n            return new ArrayList<>();\n        }\n        return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());\n    }\n\n    public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) {\n        if (CollUtil.isEmpty(from)) {\n            return new ArrayList<>();\n        }\n        return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());\n    }\n\n    public static <T, U> PageResult<U> convertPage(PageResult<T> from, Function<T, U> func) {\n        if (ArrayUtil.isEmpty(from)) {\n            return new PageResult<>(from.getTotal());\n        }\n        return new PageResult<>(convertList(from.getList(), func), from.getTotal());\n    }\n\n    public static <T, U> List<U> convertListByFlatMap(Collection<T> from,\n                                                      Function<T, ? extends Stream<? extends U>> func) {\n        if (CollUtil.isEmpty(from)) {\n            return new ArrayList<>();\n        }\n        return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());\n    }\n\n    public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from,\n                                                         Function<? super T, ? extends U> mapper,\n                                                         Function<U, ? extends Stream<? extends R>> func) {\n        if (CollUtil.isEmpty(from)) {\n            return new ArrayList<>();\n        }\n        return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());\n    }\n\n    public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {\n        return map.values()\n                .stream()\n                .flatMap(List::stream)\n                .collect(Collectors.toList());\n    }\n\n    public static <T> Set<T> convertSet(Collection<T> from) {\n        return convertSet(from, v -> v);\n    }\n\n    public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashSet<>();\n        }\n        return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());\n    }\n\n    public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashSet<>();\n        }\n        return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());\n    }\n\n    public static <T, K> Map<K, T> convertMapByFilter(Collection<T> from, Predicate<T> filter, Function<T, K> keyFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashMap<>();\n        }\n        return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v));\n    }\n\n    public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from,\n                                                    Function<T, ? extends Stream<? extends U>> func) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashSet<>();\n        }\n        return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());\n    }\n\n    public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from,\n                                                       Function<? super T, ? extends U> mapper,\n                                                       Function<U, ? extends Stream<? extends R>> func) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashSet<>();\n        }\n        return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());\n    }\n\n    public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashMap<>();\n        }\n        return convertMap(from, keyFunc, Function.identity());\n    }\n\n    public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) {\n        if (CollUtil.isEmpty(from)) {\n            return supplier.get();\n        }\n        return convertMap(from, keyFunc, Function.identity(), supplier);\n    }\n\n    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashMap<>();\n        }\n        return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1);\n    }\n\n    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashMap<>();\n        }\n        return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new);\n    }\n\n    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) {\n        if (CollUtil.isEmpty(from)) {\n            return supplier.get();\n        }\n        return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier);\n    }\n\n    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashMap<>();\n        }\n        return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier));\n    }\n\n    public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashMap<>();\n        }\n        return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList())));\n    }\n\n    public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashMap<>();\n        }\n        return from.stream()\n                .collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList())));\n    }\n\n    // 暂时没想好名字，先以 2 结尾噶\n    public static <T, K, V> Map<K, Set<V>> convertMultiMap2(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return new HashMap<>();\n        }\n        return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));\n    }\n\n    public static <T, K> Map<K, T> convertImmutableMap(Collection<T> from, Function<T, K> keyFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return Collections.emptyMap();\n        }\n        ImmutableMap.Builder<K, T> builder = ImmutableMap.builder();\n        from.forEach(item -> builder.put(keyFunc.apply(item), item));\n        return builder.build();\n    }\n\n    /**\n     * 对比老、新两个列表，找出新增、修改、删除的数据\n     *\n     * @param oldList  老列表\n     * @param newList  新列表\n     * @param sameFunc 对比函数，返回 true 表示相同，返回 false 表示不同\n     *                 注意，same 是通过每个元素的“标识”，判断它们是不是同一个数据\n     * @return [新增列表、修改列表、删除列表]\n     */\n    public static <T> List<List<T>> diffList(Collection<T> oldList, Collection<T> newList,\n                                             BiFunction<T, T, Boolean> sameFunc) {\n        List<T> createList = new LinkedList<>(newList); // 默认都认为是新增的，后续会进行移除\n        List<T> updateList = new ArrayList<>();\n        List<T> deleteList = new ArrayList<>();\n\n        // 通过以 oldList 为主遍历，找出 updateList 和 deleteList\n        for (T oldObj : oldList) {\n            // 1. 寻找是否有匹配的\n            T foundObj = null;\n            for (Iterator<T> iterator = createList.iterator(); iterator.hasNext(); ) {\n                T newObj = iterator.next();\n                // 1.1 不匹配，则直接跳过\n                if (!sameFunc.apply(oldObj, newObj)) {\n                    continue;\n                }\n                // 1.2 匹配，则移除，并结束寻找\n                iterator.remove();\n                foundObj = newObj;\n                break;\n            }\n            // 2. 匹配添加到 updateList；不匹配则添加到 deleteList 中\n            if (foundObj != null) {\n                updateList.add(foundObj);\n            } else {\n                deleteList.add(oldObj);\n            }\n        }\n        return asList(createList, updateList, deleteList);\n    }\n\n    public static boolean containsAny(Collection<?> source, Collection<?> candidates) {\n        return org.springframework.util.CollectionUtils.containsAny(source, candidates);\n    }\n\n    public static <T> T getFirst(List<T> from) {\n        return !CollectionUtil.isEmpty(from) ? from.get(0) : null;\n    }\n\n    public static <T> T findFirst(Collection<T> from, Predicate<T> predicate) {\n        return findFirst(from, predicate, Function.identity());\n    }\n\n    public static <T, U> U findFirst(Collection<T> from, Predicate<T> predicate, Function<T, U> func) {\n        if (CollUtil.isEmpty(from)) {\n            return null;\n        }\n        return from.stream().filter(predicate).findFirst().map(func).orElse(null);\n    }\n\n    public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return null;\n        }\n        assert !from.isEmpty(); // 断言，避免告警\n        T t = from.stream().max(Comparator.comparing(valueFunc)).get();\n        return valueFunc.apply(t);\n    }\n\n    public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return null;\n        }\n        assert from.size() > 0; // 断言，避免告警\n        T t = from.stream().min(Comparator.comparing(valueFunc)).get();\n        return valueFunc.apply(t);\n    }\n\n    public static <T, V extends Comparable<? super V>> T getMinObject(List<T> from, Function<T, V> valueFunc) {\n        if (CollUtil.isEmpty(from)) {\n            return null;\n        }\n        assert from.size() > 0; // 断言，避免告警\n        return from.stream().min(Comparator.comparing(valueFunc)).get();\n    }\n\n    public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc,\n                                                                     BinaryOperator<V> accumulator) {\n        return getSumValue(from, valueFunc, accumulator, null);\n    }\n\n    public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc,\n                                                                     BinaryOperator<V> accumulator, V defaultValue) {\n        if (CollUtil.isEmpty(from)) {\n            return defaultValue;\n        }\n        assert !from.isEmpty(); // 断言，避免告警\n        return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue);\n    }\n\n    public static <T> void addIfNotNull(Collection<T> coll, T item) {\n        if (item == null) {\n            return;\n        }\n        coll.add(item);\n    }\n\n    public static <T> Collection<T> singleton(T obj) {\n        return obj == null ? Collections.emptyList() : Collections.singleton(obj);\n    }\n\n    public static <T> List<T> newArrayList(List<List<T>> list) {\n        return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());\n    }\n\n    /**\n     * 转换为 LinkedHashSet\n     *\n     * @param <T>         元素类型\n     * @param elementType 集合中元素类型\n     * @param value       被转换的值\n     * @return {@link LinkedHashSet}\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> LinkedHashSet<T> toLinkedHashSet(Class<T> elementType, Object value) {\n        return (LinkedHashSet<T>) toCollection(LinkedHashSet.class, elementType, value);\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.collection;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Multimap;\n\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\n/**\n * Map 工具类\n *\n * @author 芋道源码\n */\npublic class MapUtils {\n\n    /**\n     * 从哈希表表中，获得 keys 对应的所有 value 数组\n     *\n     * @param multimap 哈希表\n     * @param keys keys\n     * @return value 数组\n     */\n    public static <K, V> List<V> getList(Multimap<K, V> multimap, Collection<K> keys) {\n        List<V> result = new ArrayList<>();\n        keys.forEach(k -> {\n            Collection<V> values = multimap.get(k);\n            if (CollectionUtil.isEmpty(values)) {\n                return;\n            }\n            result.addAll(values);\n        });\n        return result;\n    }\n\n    /**\n     * 从哈希表查找到 key 对应的 value，然后进一步处理\n     * key 为 null 时, 不处理\n     * 注意，如果查找到的 value 为 null 时，不进行处理\n     *\n     * @param map 哈希表\n     * @param key key\n     * @param consumer 进一步处理的逻辑\n     */\n    public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {\n        if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) {\n            return;\n        }\n        V value = map.get(key);\n        if (value == null) {\n            return;\n        }\n        consumer.accept(value);\n    }\n\n    public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) {\n        Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size());\n        keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue()));\n        return map;\n    }\n\n    /**\n     * 从 Map 中获取 BigDecimal 值\n     *\n     * @param map Map 数据源\n     * @param key 键名\n     * @return BigDecimal 值，解析失败或值为 null 时返回 null\n     */\n    public static BigDecimal getBigDecimal(Map<String, ?> map, String key) {\n        return getBigDecimal(map, key, null);\n    }\n\n    /**\n     * 从 Map 中获取 BigDecimal 值\n     *\n     * @param map          Map 数据源\n     * @param key          键名\n     * @param defaultValue 默认值\n     * @return BigDecimal 值，解析失败或值为 null 时返回默认值\n     */\n    public static BigDecimal getBigDecimal(Map<String, ?> map, String key, BigDecimal defaultValue) {\n        if (map == null) {\n            return defaultValue;\n        }\n        Object value = map.get(key);\n        if (value == null) {\n            return defaultValue;\n        }\n        if (value instanceof BigDecimal) {\n            return (BigDecimal) value;\n        }\n        if (value instanceof Number) {\n            return BigDecimal.valueOf(((Number) value).doubleValue());\n        }\n        if (value instanceof String) {\n            try {\n                return new BigDecimal((String) value);\n            } catch (NumberFormatException e) {\n                return defaultValue;\n            }\n        }\n        return defaultValue;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/SetUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.collection;\n\nimport cn.hutool.core.collection.CollUtil;\n\nimport java.util.Set;\n\n/**\n * Set 工具类\n *\n * @author 芋道源码\n */\npublic class SetUtils {\n\n    @SafeVarargs\n    public static <T> Set<T> asSet(T... objs) {\n        return CollUtil.newHashSet(objs);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.date;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\n\nimport java.time.*;\nimport java.util.Calendar;\nimport java.util.Date;\n\n/**\n * 时间工具类\n *\n * @author 芋道源码\n */\npublic class DateUtils {\n\n    /**\n     * 时区 - 默认\n     */\n    public static final String TIME_ZONE_DEFAULT = \"GMT+8\";\n\n    /**\n     * 秒转换成毫秒\n     */\n    public static final long SECOND_MILLIS = 1000;\n\n    public static final String FORMAT_YEAR_MONTH_DAY = \"yyyy-MM-dd\";\n\n    public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = \"yyyy-MM-dd HH:mm:ss\";\n\n    /**\n     * 将 LocalDateTime 转换成 Date\n     *\n     * @param date LocalDateTime\n     * @return LocalDateTime\n     */\n    public static Date of(LocalDateTime date) {\n        if (date == null) {\n            return null;\n        }\n        // 将此日期时间与时区相结合以创建 ZonedDateTime\n        ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());\n        // 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳\n        Instant instant = zonedDateTime.toInstant();\n        // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间\n        return Date.from(instant);\n    }\n\n    /**\n     * 将 Date 转换成 LocalDateTime\n     *\n     * @param date Date\n     * @return LocalDateTime\n     */\n    public static LocalDateTime of(Date date) {\n        if (date == null) {\n            return null;\n        }\n        // 转为时间戳\n        Instant instant = date.toInstant();\n        // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间\n        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());\n    }\n\n    public static Date addTime(Duration duration) {\n        return new Date(System.currentTimeMillis() + duration.toMillis());\n    }\n\n    public static boolean isExpired(LocalDateTime time) {\n        LocalDateTime now = LocalDateTime.now();\n        return now.isAfter(time);\n    }\n\n    /**\n     * 创建指定时间\n     *\n     * @param year  年\n     * @param month 月\n     * @param day   日\n     * @return 指定时间\n     */\n    public static Date buildTime(int year, int month, int day) {\n        return buildTime(year, month, day, 0, 0, 0);\n    }\n\n    /**\n     * 创建指定时间\n     *\n     * @param year   年\n     * @param month  月\n     * @param day    日\n     * @param hour   小时\n     * @param minute 分钟\n     * @param second 秒\n     * @return 指定时间\n     */\n    public static Date buildTime(int year, int month, int day,\n                                 int hour, int minute, int second) {\n        Calendar calendar = Calendar.getInstance();\n        calendar.set(Calendar.YEAR, year);\n        calendar.set(Calendar.MONTH, month - 1);\n        calendar.set(Calendar.DAY_OF_MONTH, day);\n        calendar.set(Calendar.HOUR_OF_DAY, hour);\n        calendar.set(Calendar.MINUTE, minute);\n        calendar.set(Calendar.SECOND, second);\n        calendar.set(Calendar.MILLISECOND, 0); // 一般情况下，都是 0 毫秒\n        return calendar.getTime();\n    }\n\n    public static Date max(Date a, Date b) {\n        if (a == null) {\n            return b;\n        }\n        if (b == null) {\n            return a;\n        }\n        return a.compareTo(b) > 0 ? a : b;\n    }\n\n    public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {\n        if (a == null) {\n            return b;\n        }\n        if (b == null) {\n            return a;\n        }\n        return a.isAfter(b) ? a : b;\n    }\n\n    /**\n     * 是否今天\n     *\n     * @param date 日期\n     * @return 是否\n     */\n    public static boolean isToday(LocalDateTime date) {\n        return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());\n    }\n\n    /**\n     * 是否昨天\n     *\n     * @param date 日期\n     * @return 是否\n     */\n    public static boolean isYesterday(LocalDateTime date) {\n        return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.date;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.date.TemporalAccessorUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;\n\nimport java.sql.Timestamp;\nimport java.time.*;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\nimport java.time.temporal.ChronoUnit;\nimport java.time.temporal.TemporalAdjusters;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static cn.hutool.core.date.DatePattern.*;\n\n/**\n * 时间工具类，用于 {@link LocalDateTime}\n *\n * @author 芋道源码\n */\npublic class LocalDateTimeUtils {\n\n    /**\n     * 空的 LocalDateTime 对象，主要用于 DB 唯一索引的默认值\n     */\n    public static LocalDateTime EMPTY = buildTime(1970, 1, 1);\n\n    public static DateTimeFormatter UTC_MS_WITH_XXX_OFFSET_FORMATTER = createFormatter(UTC_MS_WITH_XXX_OFFSET_PATTERN);\n\n    /**\n     * 解析时间\n     *\n     * 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说，会尽量去解析，直到成功\n     *\n     * @param time 时间\n     * @return 时间字符串\n     */\n    public static LocalDateTime parse(String time) {\n        try {\n            return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN);\n        } catch (DateTimeParseException e) {\n            return LocalDateTimeUtil.parse(time);\n        }\n    }\n\n    public static LocalDateTime addTime(Duration duration) {\n        return LocalDateTime.now().plus(duration);\n    }\n\n    public static LocalDateTime minusTime(Duration duration) {\n        return LocalDateTime.now().minus(duration);\n    }\n\n    public static boolean beforeNow(LocalDateTime date) {\n        return date.isBefore(LocalDateTime.now());\n    }\n\n    public static boolean afterNow(LocalDateTime date) {\n        return date.isAfter(LocalDateTime.now());\n    }\n\n    /**\n     * 创建指定时间\n     *\n     * @param year  年\n     * @param month 月\n     * @param day   日\n     * @return 指定时间\n     */\n    public static LocalDateTime buildTime(int year, int month, int day) {\n        return LocalDateTime.of(year, month, day, 0, 0, 0);\n    }\n\n    public static LocalDateTime[] buildBetweenTime(int year1, int month1, int day1,\n                                                   int year2, int month2, int day2) {\n        return new LocalDateTime[]{buildTime(year1, month1, day1), buildTime(year2, month2, day2)};\n    }\n\n    /**\n     * 判指定断时间，是否在该时间范围内\n     *\n     * @param startTime 开始时间\n     * @param endTime 结束时间\n     * @param time 指定时间\n     * @return 是否\n     */\n    public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, Timestamp time) {\n        if (startTime == null || endTime == null || time == null) {\n            return false;\n        }\n        return LocalDateTimeUtil.isIn(LocalDateTimeUtil.of(time), startTime, endTime);\n    }\n\n    /**\n     * 判指定断时间，是否在该时间范围内\n     *\n     * @param startTime 开始时间\n     * @param endTime 结束时间\n     * @param time 指定时间\n     * @return 是否\n     */\n    public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) {\n        if (startTime == null || endTime == null || time == null) {\n            return false;\n        }\n        return LocalDateTimeUtil.isIn(parse(time), startTime, endTime);\n    }\n\n    /**\n     * 判断当前时间是否在该时间范围内\n     *\n     * @param startTime 开始时间\n     * @param endTime   结束时间\n     * @return 是否\n     */\n    public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) {\n        if (startTime == null || endTime == null) {\n            return false;\n        }\n        return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);\n    }\n\n    /**\n     * 判断当前时间是否在该时间范围内\n     *\n     * @param startTime 开始时间\n     * @param endTime   结束时间\n     * @return 是否\n     */\n    public static boolean isBetween(String startTime, String endTime) {\n        if (startTime == null || endTime == null) {\n            return false;\n        }\n        LocalDate nowDate = LocalDate.now();\n        return LocalDateTimeUtil.isIn(LocalDateTime.now(),\n                LocalDateTime.of(nowDate, LocalTime.parse(startTime)),\n                LocalDateTime.of(nowDate, LocalTime.parse(endTime)));\n    }\n\n    /**\n     * 判断时间段是否重叠\n     *\n     * @param startTime1 开始 time1\n     * @param endTime1   结束 time1\n     * @param startTime2 开始 time2\n     * @param endTime2   结束 time2\n     * @return 重叠：true 不重叠：false\n     */\n    public static boolean isOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {\n        LocalDate nowDate = LocalDate.now();\n        return LocalDateTimeUtil.isOverlap(LocalDateTime.of(nowDate, startTime1), LocalDateTime.of(nowDate, endTime1),\n                LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2));\n    }\n\n    /**\n     * 获取指定日期所在的月份的开始时间\n     * 例如：2023-09-30 00:00:00,000\n     *\n     * @param date 日期\n     * @return 月份的开始时间\n     */\n    public static LocalDateTime beginOfMonth(LocalDateTime date) {\n        return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);\n    }\n\n    /**\n     * 获取指定日期所在的月份的最后时间\n     * 例如：2023-09-30 23:59:59,999\n     *\n     * @param date 日期\n     * @return 月份的结束时间\n     */\n    public static LocalDateTime endOfMonth(LocalDateTime date) {\n        return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);\n    }\n\n    /**\n     * 获得指定日期所在季度\n     *\n     * @param date 日期\n     * @return 所在季度\n     */\n    public static int getQuarterOfYear(LocalDateTime date) {\n        return (date.getMonthValue() - 1) / 3 + 1;\n    }\n\n    /**\n     * 获取指定日期到现在过了几天，如果指定日期在当前日期之后，获取结果为负\n     *\n     * @param dateTime 日期\n     * @return 相差天数\n     */\n    public static Long between(LocalDateTime dateTime) {\n        return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS);\n    }\n\n    /**\n     * 获取今天的开始时间\n     *\n     * @return 今天\n     */\n    public static LocalDateTime getToday() {\n        return LocalDateTimeUtil.beginOfDay(LocalDateTime.now());\n    }\n\n    /**\n     * 获取昨天的开始时间\n     *\n     * @return 昨天\n     */\n    public static LocalDateTime getYesterday() {\n        return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1));\n    }\n\n    /**\n     * 获取本月的开始时间\n     *\n     * @return 本月\n     */\n    public static LocalDateTime getMonth() {\n        return beginOfMonth(LocalDateTime.now());\n    }\n\n    /**\n     * 获取本年的开始时间\n     *\n     * @return 本年\n     */\n    public static LocalDateTime getYear() {\n        return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);\n    }\n\n    public static List<LocalDateTime[]> getDateRangeList(LocalDateTime startTime,\n                                                         LocalDateTime endTime,\n                                                         Integer interval) {\n        // 1.1 找到枚举\n        DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);\n        Assert.notNull(intervalEnum, \"interval({}} 找不到对应的枚举\", interval);\n        // 1.2 将时间对齐\n        startTime = LocalDateTimeUtil.beginOfDay(startTime);\n        endTime = LocalDateTimeUtil.endOfDay(endTime);\n\n        // 2. 循环，生成时间范围\n        List<LocalDateTime[]> timeRanges = new ArrayList<>();\n        switch (intervalEnum) {\n            case HOUR:\n                while (startTime.isBefore(endTime)) {\n                    timeRanges.add(new LocalDateTime[]{startTime, startTime.plusHours(1).minusNanos(1)});\n                    startTime = startTime.plusHours(1);\n                }\n            case DAY:\n                while (startTime.isBefore(endTime)) {\n                    timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});\n                    startTime = startTime.plusDays(1);\n                }\n                break;\n            case WEEK:\n                while (startTime.isBefore(endTime)) {\n                    LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1);\n                    timeRanges.add(new LocalDateTime[]{startTime, endOfWeek});\n                    startTime = endOfWeek.plusNanos(1);\n                }\n                break;\n            case MONTH:\n                while (startTime.isBefore(endTime)) {\n                    LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1);\n                    timeRanges.add(new LocalDateTime[]{startTime, endOfMonth});\n                    startTime = endOfMonth.plusNanos(1);\n                }\n                break;\n            case QUARTER:\n                while (startTime.isBefore(endTime)) {\n                    int quarterOfYear = getQuarterOfYear(startTime);\n                    LocalDateTime quarterEnd = quarterOfYear == 4\n                            ? startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1)\n                            : startTime.withMonth(quarterOfYear * 3 + 1).withDayOfMonth(1).minusNanos(1);\n                    timeRanges.add(new LocalDateTime[]{startTime, quarterEnd});\n                    startTime = quarterEnd.plusNanos(1);\n                }\n                break;\n            case YEAR:\n                while (startTime.isBefore(endTime)) {\n                    LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1);\n                    timeRanges.add(new LocalDateTime[]{startTime, endOfYear});\n                    startTime = endOfYear.plusNanos(1);\n                }\n                break;\n            default:\n                throw new IllegalArgumentException(\"Invalid interval: \" + interval);\n        }\n        // 3. 兜底，最后一个时间，需要保持在 endTime 之前\n        LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges);\n        if (lastTimeRange != null) {\n            lastTimeRange[1] = endTime;\n        }\n        return timeRanges;\n    }\n\n    /**\n     * 格式化时间范围\n     *\n     * @param startTime 开始时间\n     * @param endTime   结束时间\n     * @param interval  时间间隔\n     * @return 时间范围\n     */\n    public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) {\n        // 1. 找到枚举\n        DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);\n        Assert.notNull(intervalEnum, \"interval({}} 找不到对应的枚举\", interval);\n\n        // 2. 循环，生成时间范围\n        switch (intervalEnum) {\n            case HOUR:\n                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATETIME_MINUTE_PATTERN);\n            case DAY:\n                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);\n            case WEEK:\n                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN)\n                        + StrUtil.format(\"(第 {} 周)\", LocalDateTimeUtil.weekOfYear(startTime));\n            case MONTH:\n                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN);\n            case QUARTER:\n                return StrUtil.format(\"{}-Q{}\", startTime.getYear(), getQuarterOfYear(startTime));\n            case YEAR:\n                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN);\n            default:\n                throw new IllegalArgumentException(\"Invalid interval: \" + interval);\n        }\n    }\n\n    /**\n     * 将给定的 {@link LocalDateTime} 转换为自 Unix 纪元时间（1970-01-01T00:00:00Z）以来的秒数。\n     *\n     * @param sourceDateTime 需要转换的本地日期时间，不能为空\n     * @return 自 1970-01-01T00:00:00Z 起的秒数（epoch second）\n     * @throws NullPointerException 如果 {@code sourceDateTime} 为 {@code null}\n     * @throws DateTimeException 如果转换过程中发生时间超出范围或其他时间处理异常\n     */\n    public static Long toEpochSecond(LocalDateTime sourceDateTime) {\n        return TemporalAccessorUtil.toInstant(sourceDateTime).getEpochSecond();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.http;\n\nimport cn.hutool.core.codec.Base64;\nimport cn.hutool.core.map.TableMap;\nimport cn.hutool.core.net.url.UrlBuilder;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.http.HttpRequest;\nimport cn.hutool.http.HttpResponse;\nimport lombok.SneakyThrows;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.net.URI;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Map;\n\n/**\n * HTTP 工具类\n *\n * @author 芋道源码\n */\npublic class HttpUtils {\n\n    /**\n     * 编码 URL 参数\n     *\n     * @param value 参数\n     * @return 编码后的参数\n     */\n    @SneakyThrows\n    public static String encodeUtf8(String value) {\n        return URLEncoder.encode(value, StandardCharsets.UTF_8.name());\n    }\n\n    /**\n     * 解码 URL 参数（query parameter）\n     * 注意：此方法会将 + 解码为空格，适用于 query parameter，不适用于 URL path\n     *\n     * @see #decodeUrlPath(String)\n     * @param value 参数\n     * @return 解码后的参数\n     */\n    @SneakyThrows\n    public static String decodeUtf8(String value) {\n        return URLDecoder.decode(value, StandardCharsets.UTF_8.name());\n    }\n\n    /**\n     * 解码 URL 路径\n     * 与 {@link #decodeUtf8(String)} 不同，此方法不会将 + 解码为空格，保持 + 为字面字符\n     * 适用于 URL path 部分的解码\n     *\n     * @param path URL 路径\n     * @return 解码后的路径\n     */\n    @SneakyThrows\n    public static String decodeUrlPath(String path) {\n        // 先将 + 替换为 %2B，避免被 URLDecoder 解码为空格\n        String encoded = path.replace(\"+\", \"%2B\");\n        return URLDecoder.decode(encoded, StandardCharsets.UTF_8.name());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static String replaceUrlQuery(String url, String key, String value) {\n        UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());\n        // 先移除\n        TableMap<CharSequence, CharSequence> query = (TableMap<CharSequence, CharSequence>)\n                ReflectUtil.getFieldValue(builder.getQuery(), \"query\");\n        query.remove(key);\n        // 后添加\n        builder.addQuery(key, value);\n        return builder.build();\n    }\n\n    public static String removeUrlQuery(String url) {\n        if (!StrUtil.contains(url, '?')) {\n            return url;\n        }\n        UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());\n        // 移除 query、fragment\n        builder.setQuery(null);\n        builder.setFragment(null);\n        return builder.build();\n    }\n\n    /**\n     * 拼接 URL\n     *\n     * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法\n     *\n     * @param base 基础 URL\n     * @param query 查询参数\n     * @param keys query 的 key，对应的原本的 key 的映射。例如说 query 里有个 key 是 xx，实际它的 key 是 extra_xx，则通过 keys 里添加这个映射\n     * @param fragment URL 的 fragment，即拼接到 # 中\n     * @return 拼接后的 URL\n     */\n    public static String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) {\n        UriComponentsBuilder template = UriComponentsBuilder.newInstance();\n        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base);\n        URI redirectUri;\n        try {\n            // assume it's encoded to start with (if it came in over the wire)\n            redirectUri = builder.build(true).toUri();\n        } catch (Exception e) {\n            // ... but allow client registrations to contain hard-coded non-encoded values\n            redirectUri = builder.build().toUri();\n            builder = UriComponentsBuilder.fromUri(redirectUri);\n        }\n        template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost())\n                .userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath());\n\n        if (fragment) {\n            StringBuilder values = new StringBuilder();\n            if (redirectUri.getFragment() != null) {\n                String append = redirectUri.getFragment();\n                values.append(append);\n            }\n            for (String key : query.keySet()) {\n                if (values.length() > 0) {\n                    values.append(\"&\");\n                }\n                String name = key;\n                if (keys != null && keys.containsKey(key)) {\n                    name = keys.get(key);\n                }\n                values.append(name).append(\"={\").append(key).append(\"}\");\n            }\n            if (values.length() > 0) {\n                template.fragment(values.toString());\n            }\n            UriComponents encoded = template.build().expand(query).encode();\n            builder.fragment(encoded.getFragment());\n        } else {\n            for (String key : query.keySet()) {\n                String name = key;\n                if (keys != null && keys.containsKey(key)) {\n                    name = keys.get(key);\n                }\n                template.queryParam(name, \"{\" + key + \"}\");\n            }\n            template.fragment(redirectUri.getFragment());\n            UriComponents encoded = template.build().expand(query).encode();\n            builder.query(encoded.getQuery());\n        }\n        return builder.build().toUriString();\n    }\n\n    public static String[] obtainBasicAuthorization(HttpServletRequest request) {\n        String clientId;\n        String clientSecret;\n        // 先从 Header 中获取\n        String authorization = request.getHeader(\"Authorization\");\n        authorization = StrUtil.subAfter(authorization, \"Basic \", true);\n        if (StringUtils.hasText(authorization)) {\n            authorization = Base64.decodeStr(authorization);\n            clientId = StrUtil.subBefore(authorization, \":\", false);\n            clientSecret = StrUtil.subAfter(authorization, \":\", false);\n            // 再从 Param 中获取\n        } else {\n            clientId = request.getParameter(\"client_id\");\n            clientSecret = request.getParameter(\"client_secret\");\n        }\n\n        // 如果两者非空，则返回\n        if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) {\n            return new String[]{clientId, clientSecret};\n        }\n        return null;\n    }\n\n    /**\n     * HTTP post 请求，基于 {@link cn.hutool.http.HttpUtil} 实现\n     *\n     * 为什么要封装该方法，因为 HttpUtil 默认封装的方法，没有允许传递 headers 参数\n     *\n     * @param url URL\n     * @param headers 请求头\n     * @param requestBody 请求体\n     * @return 请求结果\n     */\n    public static String post(String url, Map<String, String> headers, String requestBody) {\n        try (HttpResponse response = HttpRequest.post(url)\n                .addHeaders(headers)\n                .body(requestBody)\n                .execute()) {\n            return response.body();\n        }\n    }\n\n    /**\n     * HTTP get 请求，基于 {@link cn.hutool.http.HttpUtil} 实现\n     *\n     * 为什么要封装该方法，因为 HttpUtil 默认封装的方法，没有允许传递 headers 参数\n     *\n     * @param url URL\n     * @param headers 请求头\n     * @return 请求结果\n     */\n    public static String get(String url, Map<String, String> headers) {\n        try (HttpResponse response = HttpRequest.get(url)\n                .addHeaders(headers)\n                .execute()) {\n            return response.body();\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.io;\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.util.IdUtil;\nimport lombok.SneakyThrows;\n\nimport java.io.File;\n\n/**\n * 文件工具类\n *\n * @author 芋道源码\n */\npublic class FileUtils {\n\n    /**\n     * 创建临时文件\n     * 该文件会在 JVM 退出时，进行删除\n     *\n     * @param data 文件内容\n     * @return 文件\n     */\n    @SneakyThrows\n    public static File createTempFile(String data) {\n        File file = createTempFile();\n        // 写入内容\n        FileUtil.writeUtf8String(data, file);\n        return file;\n    }\n\n    /**\n     * 创建临时文件\n     * 该文件会在 JVM 退出时，进行删除\n     *\n     * @param data 文件内容\n     * @return 文件\n     */\n    @SneakyThrows\n    public static File createTempFile(byte[] data) {\n        File file = createTempFile();\n        // 写入内容\n        FileUtil.writeBytes(data, file);\n        return file;\n    }\n\n    /**\n     * 创建临时文件，无内容\n     * 该文件会在 JVM 退出时，进行删除\n     *\n     * @return 文件\n     */\n    @SneakyThrows\n    public static File createTempFile() {\n        // 创建文件，通过 UUID 保证唯一\n        File file = File.createTempFile(IdUtil.simpleUUID(), null);\n        // 标记 JVM 退出时，自动删除\n        file.deleteOnExit();\n        return file;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/IoUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.io;\n\nimport cn.hutool.core.io.IORuntimeException;\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.StrUtil;\n\nimport java.io.InputStream;\n\n/**\n * IO 工具类，用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法\n *\n * @author 芋道源码\n */\npublic class IoUtils {\n\n    /**\n     * 从流中读取 UTF8 编码的内容\n     *\n     * @param in 输入流\n     * @param isClose 是否关闭\n     * @return 内容\n     * @throws IORuntimeException IO 异常\n     */\n    public static String readUtf8(InputStream in, boolean isClose) throws IORuntimeException {\n        return StrUtil.utf8Str(IoUtil.read(in, isClose));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.json;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.json.JSONUtil;\nimport cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;\nimport cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.SerializationFeature;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport lombok.Getter;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * JSON 工具类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class JsonUtils {\n\n    @Getter\n    private static ObjectMapper objectMapper = new ObjectMapper();\n\n    static {\n        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);\n        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值\n        // 解决 LocalDateTime 的序列化\n        SimpleModule simpleModule = new JavaTimeModule()\n                .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)\n                .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);\n        objectMapper.registerModules(simpleModule);\n    }\n\n    /**\n     * 初始化 objectMapper 属性\n     * <p>\n     * 通过这样的方式，使用 Spring 创建的 ObjectMapper Bean\n     *\n     * @param objectMapper ObjectMapper 对象\n     */\n    public static void init(ObjectMapper objectMapper) {\n        JsonUtils.objectMapper = objectMapper;\n    }\n\n    @SneakyThrows\n    public static String toJsonString(Object object) {\n        return objectMapper.writeValueAsString(object);\n    }\n\n    @SneakyThrows\n    public static byte[] toJsonByte(Object object) {\n        return objectMapper.writeValueAsBytes(object);\n    }\n\n    @SneakyThrows\n    public static String toJsonPrettyString(Object object) {\n        return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);\n    }\n\n    public static <T> T parseObject(String text, Class<T> clazz) {\n        if (StrUtil.isEmpty(text)) {\n            return null;\n        }\n        try {\n            return objectMapper.readValue(text, clazz);\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", text, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> T parseObject(String text, String path, Class<T> clazz) {\n        if (StrUtil.isEmpty(text)) {\n            return null;\n        }\n        try {\n            JsonNode treeNode = objectMapper.readTree(text);\n            JsonNode pathNode = treeNode.path(path);\n            return objectMapper.readValue(pathNode.toString(), clazz);\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", text, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> T parseObject(String text, Type type) {\n        if (StrUtil.isEmpty(text)) {\n            return null;\n        }\n        try {\n            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", text, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> T parseObject(byte[] text, Type type) {\n        if (ArrayUtil.isEmpty(text)) {\n            return null;\n        }\n        try {\n            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", text, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * 将字符串解析成指定类型的对象\n     * 使用 {@link #parseObject(String, Class)} 时，在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下，\n     * 如果 text 没有 class 属性，则会报错。此时，使用这个方法，可以解决。\n     *\n     * @param text 字符串\n     * @param clazz 类型\n     * @return 对象\n     */\n    public static <T> T parseObject2(String text, Class<T> clazz) {\n        if (StrUtil.isEmpty(text)) {\n            return null;\n        }\n        return JSONUtil.toBean(text, clazz);\n    }\n\n    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {\n        if (ArrayUtil.isEmpty(bytes)) {\n            return null;\n        }\n        try {\n            return objectMapper.readValue(bytes, clazz);\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", bytes, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> T parseObject(String text, TypeReference<T> typeReference) {\n        try {\n            return objectMapper.readValue(text, typeReference);\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", text, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * 解析 JSON 字符串成指定类型的对象，如果解析失败，则返回 null\n     *\n     * @param text 字符串\n     * @param typeReference 类型引用\n     * @return 指定类型的对象\n     */\n    public static <T> T parseObjectQuietly(String text, TypeReference<T> typeReference) {\n        try {\n            return objectMapper.readValue(text, typeReference);\n        } catch (IOException e) {\n            return null;\n        }\n    }\n\n    public static <T> List<T> parseArray(String text, Class<T> clazz) {\n        if (StrUtil.isEmpty(text)) {\n            return new ArrayList<>();\n        }\n        try {\n            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", text, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> List<T> parseArray(String text, String path, Class<T> clazz) {\n        if (StrUtil.isEmpty(text)) {\n            return null;\n        }\n        try {\n            JsonNode treeNode = objectMapper.readTree(text);\n            JsonNode pathNode = treeNode.path(path);\n            return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", text, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static JsonNode parseTree(String text) {\n        try {\n            return objectMapper.readTree(text);\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", text, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static JsonNode parseTree(byte[] text) {\n        try {\n            return objectMapper.readTree(text);\n        } catch (IOException e) {\n            log.error(\"json parse err,json:{}\", text, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static boolean isJson(String text) {\n        return JSONUtil.isTypeJSON(text);\n    }\n\n    /**\n     * 判断字符串是否为 JSON 类型的字符串\n     * @param str 字符串\n     */\n    public static boolean isJsonObject(String str) {\n        return JSONUtil.isTypeJSONObject(str);\n    }\n\n    /**\n     * 将 Object 转换为目标类型\n     * <p>\n     * 避免先转 jsonString 再 parseObject 的性能损耗\n     *\n     * @param obj   源对象（可以是 Map、POJO 等）\n     * @param clazz 目标类型\n     * @return 转换后的对象\n     */\n    public static <T> T convertObject(Object obj, Class<T> clazz) {\n        if (obj == null) {\n            return null;\n        }\n        if (clazz.isInstance(obj)) {\n            return clazz.cast(obj);\n        }\n        return objectMapper.convertValue(obj, clazz);\n    }\n\n    /**\n     * 将 Object 转换为目标类型（支持泛型）\n     *\n     * @param obj           源对象\n     * @param typeReference 目标类型引用\n     * @return 转换后的对象\n     */\n    public static <T> T convertObject(Object obj, TypeReference<T> typeReference) {\n        if (obj == null) {\n            return null;\n        }\n        return objectMapper.convertValue(obj, typeReference);\n    }\n\n    /**\n     * 将 Object 转换为 List 类型\n     * <p>\n     * 避免先转 jsonString 再 parseArray 的性能损耗\n     *\n     * @param obj   源对象（可以是 List、数组等）\n     * @param clazz 目标元素类型\n     * @return 转换后的 List\n     */\n    public static <T> List<T> convertList(Object obj, Class<T> clazz) {\n        if (obj == null) {\n            return new ArrayList<>();\n        }\n        return objectMapper.convertValue(obj, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/NumberSerializer.java",
    "content": "package cn.iocoder.yudao.framework.common.util.json.databind;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.annotation.JacksonStdImpl;\n\nimport java.io.IOException;\n\n/**\n * Long 序列化规则\n *\n * 会将超长 long 值转换为 string，解决前端 JavaScript 最大安全整数是 2^53-1 的问题\n *\n * @author 星语\n */\n@JacksonStdImpl\npublic class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.NumberSerializer {\n\n    private static final long MAX_SAFE_INTEGER = 9007199254740991L;\n    private static final long MIN_SAFE_INTEGER = -9007199254740991L;\n\n    public static final NumberSerializer INSTANCE = new NumberSerializer(Number.class);\n\n    public NumberSerializer(Class<? extends Number> rawType) {\n        super(rawType);\n    }\n\n    @Override\n    public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException {\n        // 超出范围 序列化位字符串\n        if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {\n            super.serialize(value, gen, serializers);\n        } else {\n            gen.writeString(value.toString());\n        }\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java",
    "content": "package cn.iocoder.yudao.framework.common.util.json.databind;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\n\n/**\n * 基于时间戳的 LocalDateTime 反序列化器\n *\n * @author 老五\n */\npublic class TimestampLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {\n\n    public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer();\n\n    @Override\n    public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {\n        // 将 Long 时间戳，转换为 LocalDateTime 对象\n        return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java",
    "content": "package cn.iocoder.yudao.framework.common.util.json.databind;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 基于时间戳的 LocalDateTime 序列化器\n *\n * @author 老五\n */\n@Slf4j\npublic class TimestampLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {\n\n    public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer();\n\n    private static final Map<Class<?>, Map<String, Field>> FIELD_CACHE = new ConcurrentHashMap<>();\n\n    @Override\n    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {\n        // 情况一：有 JsonFormat 自定义注解，则使用它。https://github.com/YunaiV/ruoyi-vue-pro/pull/1019\n        String fieldName = gen.getOutputContext().getCurrentName();\n        if (fieldName != null) {\n            Object currentValue = gen.getOutputContext().getCurrentValue();\n            if (currentValue != null) {\n                Class<?> clazz = currentValue.getClass();\n                Map<String, Field> fieldMap = FIELD_CACHE.computeIfAbsent(clazz, this::buildFieldMap);\n                Field field = fieldMap.get(fieldName);\n                // 进一步修复：https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1480\n                if (field != null && field.isAnnotationPresent(JsonFormat.class)) {\n                    JsonFormat jsonFormat = field.getAnnotation(JsonFormat.class);\n                    try {\n                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(jsonFormat.pattern());\n                        gen.writeString(formatter.format(value));\n                        return;\n                    } catch (Exception ex) {\n                        log.warn(\"[serialize][({}#{}) 使用 JsonFormat pattern 失败，尝试使用默认的 Long 时间戳]\",\n                                clazz.getName(), fieldName, ex);\n                    }\n                }\n            }\n        }\n\n        // 情况二：默认将 LocalDateTime 对象，转换为 Long 时间戳\n        gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());\n    }\n\n    /**\n     * 构建字段映射（缓存）\n     *\n     * @param clazz 类\n     * @return 字段映射\n     */\n    private Map<String, Field> buildFieldMap(Class<?> clazz) {\n        Map<String, Field> fieldMap = new HashMap<>();\n        for (Field field : ReflectUtil.getFields(clazz)) {\n            String fieldName = field.getName();\n            JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);\n            if (jsonProperty != null) {\n                String value = jsonProperty.value();\n                if (StrUtil.isNotEmpty(value) && ObjUtil.notEqual(\"\\u0000\", value)) {\n                    fieldName = value;\n                }\n            }\n            fieldMap.put(fieldName, field);\n        }\n        return fieldMap;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/monitor/TracerUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.monitor;\n\nimport org.apache.skywalking.apm.toolkit.trace.TraceContext;\n\n/**\n * 链路追踪工具类\n *\n * 考虑到每个 starter 都需要用到该工具类，所以放到 common 模块下的 util 包下\n *\n * @author 芋道源码\n */\npublic class TracerUtils {\n\n    /**\n     * 私有化构造方法\n     */\n    private TracerUtils() {\n    }\n\n    /**\n     * 获得链路追踪编号，直接返回 SkyWalking 的 TraceId。\n     * 如果不存在的话为空字符串！！！\n     *\n     * @return 链路追踪编号\n     */\n    public static String getTraceId() {\n        return TraceContext.traceId();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.number;\n\nimport cn.hutool.core.math.Money;\nimport cn.hutool.core.util.NumberUtil;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\n/**\n * 金额工具类\n *\n * @author 芋道源码\n */\npublic class MoneyUtils {\n\n    /**\n     * 金额的小数位数\n     */\n    private static final int PRICE_SCALE = 2;\n\n    /**\n     * 百分比对应的 BigDecimal 对象\n     */\n    public static final BigDecimal PERCENT_100 = BigDecimal.valueOf(100);\n\n    /**\n     * 计算百分比金额，四舍五入\n     *\n     * @param price 金额\n     * @param rate  百分比，例如说 56.77% 则传入 56.77\n     * @return 百分比金额\n     */\n    public static Integer calculateRatePrice(Integer price, Double rate) {\n        return calculateRatePrice(price, rate, 0, RoundingMode.HALF_UP).intValue();\n    }\n\n    /**\n     * 计算百分比金额，向下传入\n     *\n     * @param price 金额\n     * @param rate  百分比，例如说 56.77% 则传入 56.77\n     * @return 百分比金额\n     */\n    public static Integer calculateRatePriceFloor(Integer price, Double rate) {\n        return calculateRatePrice(price, rate, 0, RoundingMode.FLOOR).intValue();\n    }\n\n    /**\n     * 计算百分比金额\n     *\n     * @param price   金额（单位分）\n     * @param count   数量\n     * @param percent 折扣（单位分），列如 60.2%，则传入 6020\n     * @return 商品总价\n     */\n    public static Integer calculator(Integer price, Integer count, Integer percent) {\n        price = price * count;\n        if (percent == null) {\n            return price;\n        }\n        return MoneyUtils.calculateRatePriceFloor(price, (double) (percent / 100));\n    }\n\n    /**\n     * 计算百分比金额\n     *\n     * @param price        金额\n     * @param rate         百分比，例如说 56.77% 则传入 56.77\n     * @param scale        保留小数位数\n     * @param roundingMode 舍入模式\n     */\n    public static BigDecimal calculateRatePrice(Number price, Number rate, int scale, RoundingMode roundingMode) {\n        return NumberUtil.toBigDecimal(price).multiply(NumberUtil.toBigDecimal(rate)) // 乘以\n                .divide(BigDecimal.valueOf(100), scale, roundingMode); // 除以 100\n    }\n\n    /**\n     * 分转元\n     *\n     * @param fen 分\n     * @return 元\n     */\n    public static BigDecimal fenToYuan(int fen) {\n        return new Money(0, fen).getAmount();\n    }\n\n    /**\n     * 分转元（字符串）\n     *\n     * 例如说 fen 为 1 时，则结果为 0.01\n     *\n     * @param fen 分\n     * @return 元\n     */\n    public static String fenToYuanStr(int fen) {\n        return new Money(0, fen).toString();\n    }\n\n    /**\n     * 金额相乘，默认进行四舍五入\n     *\n     * 位数：{@link #PRICE_SCALE}\n     *\n     * @param price 金额\n     * @param count 数量\n     * @return 金额相乘结果\n     */\n    public static BigDecimal priceMultiply(BigDecimal price, BigDecimal count) {\n        if (price == null || count == null) {\n            return null;\n        }\n        return price.multiply(count).setScale(PRICE_SCALE, RoundingMode.HALF_UP);\n    }\n\n    /**\n     * 金额相乘（百分比），默认进行四舍五入\n     *\n     * 位数：{@link #PRICE_SCALE}\n     *\n     * @param price  金额\n     * @param percent 百分比\n     * @return 金额相乘结果\n     */\n    public static BigDecimal priceMultiplyPercent(BigDecimal price, BigDecimal percent) {\n        if (price == null || percent == null) {\n            return null;\n        }\n        return price.multiply(percent).divide(PERCENT_100, PRICE_SCALE, RoundingMode.HALF_UP);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.number;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.NumberUtil;\nimport cn.hutool.core.util.StrUtil;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\n/**\n * 数字的工具类，补全 {@link cn.hutool.core.util.NumberUtil} 的功能\n *\n * @author 芋道源码\n */\npublic class NumberUtils {\n\n    public static Long parseLong(String str) {\n        return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null;\n    }\n\n    public static Integer parseInt(String str) {\n        return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null;\n    }\n\n    public static boolean isAllNumber(List<String> values) {\n        if (CollUtil.isEmpty(values)) {\n            return false;\n        }\n        for (String value : values) {\n            if (!NumberUtil.isNumber(value)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 通过经纬度获取地球上两点之间的距离\n     *\n     * 参考 <<a href=\"https://gitee.com/dromara/hutool/blob/1caabb586b1f95aec66a21d039c5695df5e0f4c1/hutool-core/src/main/java/cn/hutool/core/util/DistanceUtil.java\">DistanceUtil</a>> 实现，目前它已经被 hutool 删除\n     *\n     * @param lat1 经度1\n     * @param lng1 纬度1\n     * @param lat2 经度2\n     * @param lng2 纬度2\n     * @return 距离，单位：千米\n     */\n    public static double getDistance(double lat1, double lng1, double lat2, double lng2) {\n        double radLat1 = lat1 * Math.PI / 180.0;\n        double radLat2 = lat2 * Math.PI / 180.0;\n        double a = radLat1 - radLat2;\n        double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;\n        double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)\n                + Math.cos(radLat1) * Math.cos(radLat2)\n                * Math.pow(Math.sin(b / 2), 2)));\n        distance = distance * 6378.137;\n        distance = Math.round(distance * 10000d) / 10000d;\n        return distance;\n    }\n\n    /**\n     * 提供精确的乘法运算\n     *\n     * 和 hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是，如果存在 null，则返回 null\n     *\n     * @param values 多个被乘值\n     * @return 积\n     */\n    public static BigDecimal mul(BigDecimal... values) {\n        for (BigDecimal value : values) {\n            if (value == null) {\n                return null;\n            }\n        }\n        return NumberUtil.mul(values);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.object;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\n\nimport java.util.List;\nimport java.util.function.Consumer;\n\n/**\n * Bean 工具类\n *\n * 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类，虽然不同 bean 工具的性能有差别，但是对绝大多数同学的项目，不用在意这点性能\n * 2. 针对复杂的对象转换，可以搜参考 AuthConvert 实现，通过 mapstruct + default 配合实现\n *\n * @author 芋道源码\n */\npublic class BeanUtils {\n\n    public static <T> T toBean(Object source, Class<T> targetClass) {\n        return BeanUtil.toBean(source, targetClass);\n    }\n\n    public static <T> T toBean(Object source, Class<T> targetClass, Consumer<T> peek) {\n        T target = toBean(source, targetClass);\n        if (target != null) {\n            peek.accept(target);\n        }\n        return target;\n    }\n\n    public static <S, T> List<T> toBean(List<S> source, Class<T> targetType) {\n        if (source == null) {\n            return null;\n        }\n        return CollectionUtils.convertList(source, s -> toBean(s, targetType));\n    }\n\n    public static <S, T> List<T> toBean(List<S> source, Class<T> targetType, Consumer<T> peek) {\n        List<T> list = toBean(source, targetType);\n        if (list != null) {\n            list.forEach(peek);\n        }\n        return list;\n    }\n\n    public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType) {\n        return toBean(source, targetType, null);\n    }\n\n    public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType, Consumer<T> peek) {\n        if (source == null) {\n            return null;\n        }\n        List<T> list = toBean(source.getList(), targetType);\n        if (peek != null) {\n            list.forEach(peek);\n        }\n        return new PageResult<>(list, source.getTotal());\n    }\n\n    public static void copyProperties(Object source, Object target) {\n        if (source == null || target == null) {\n            return;\n        }\n        BeanUtil.copyProperties(source, target, false);\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.object;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.ReflectUtil;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.function.Consumer;\n\n/**\n * Object 工具类\n *\n * @author 芋道源码\n */\npublic class ObjectUtils {\n\n    /**\n     * 复制对象，并忽略 Id 编号\n     *\n     * @param object 被复制对象\n     * @param consumer 消费者，可以二次编辑被复制对象\n     * @return 复制后的对象\n     */\n    public static <T> T cloneIgnoreId(T object, Consumer<T> consumer) {\n        T result = ObjectUtil.clone(object);\n        // 忽略 id 编号\n        Field field = ReflectUtil.getField(object.getClass(), \"id\");\n        if (field != null) {\n            ReflectUtil.setFieldValue(result, field, null);\n        }\n        // 二次编辑\n        if (result != null) {\n            consumer.accept(result);\n        }\n        return result;\n    }\n\n    public static <T extends Comparable<T>> T max(T obj1, T obj2) {\n        if (obj1 == null) {\n            return obj2;\n        }\n        if (obj2 == null) {\n            return obj1;\n        }\n        return obj1.compareTo(obj2) > 0 ? obj1 : obj2;\n    }\n\n    @SafeVarargs\n    public static <T> T defaultIfNull(T... array) {\n        for (T item : array) {\n            if (item != null) {\n                return item;\n            }\n        }\n        return null;\n    }\n\n    @SafeVarargs\n    public static <T> boolean equalsAny(T obj, T... array) {\n        return Arrays.asList(array).contains(obj);\n    }\n\n    public static boolean isNotAllEmpty(Object... objs) {\n        return !ObjectUtil.isAllEmpty(objs);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/PageUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.object;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.func.Func1;\nimport cn.hutool.core.lang.func.LambdaUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.SortablePageParam;\nimport cn.iocoder.yudao.framework.common.pojo.SortingField;\nimport org.springframework.util.Assert;\n\nimport static java.util.Collections.singletonList;\n\n/**\n * {@link cn.iocoder.yudao.framework.common.pojo.PageParam} 工具类\n *\n * @author 芋道源码\n */\npublic class PageUtils {\n\n    private static final Object[] ORDER_TYPES = new String[]{SortingField.ORDER_ASC, SortingField.ORDER_DESC};\n\n    public static int getStart(PageParam pageParam) {\n        return (pageParam.getPageNo() - 1) * pageParam.getPageSize();\n    }\n\n    /**\n     * 构建排序字段（默认倒序）\n     *\n     * @param func 排序字段的 Lambda 表达式\n     * @param <T>  排序字段所属的类型\n     * @return 排序字段\n     */\n    public static <T> SortingField buildSortingField(Func1<T, ?> func) {\n        return buildSortingField(func, SortingField.ORDER_DESC);\n    }\n\n    /**\n     * 构建排序字段\n     *\n     * @param func  排序字段的 Lambda 表达式\n     * @param order 排序类型 {@link SortingField#ORDER_ASC} {@link SortingField#ORDER_DESC}\n     * @param <T>   排序字段所属的类型\n     * @return 排序字段\n     */\n    public static <T> SortingField buildSortingField(Func1<T, ?> func, String order) {\n        Assert.isTrue(ArrayUtil.contains(ORDER_TYPES, order), String.format(\"字段的排序类型只能是 %s/%s\", ORDER_TYPES));\n\n        String fieldName = LambdaUtil.getFieldName(func);\n        return new SortingField(fieldName, order);\n    }\n\n    /**\n     * 构建默认的排序字段\n     * 如果排序字段为空，则设置排序字段；否则忽略\n     *\n     * @param sortablePageParam 排序分页查询参数\n     * @param func              排序字段的 Lambda 表达式\n     * @param <T>               排序字段所属的类型\n     */\n    public static <T> void buildDefaultSortingField(SortablePageParam sortablePageParam, Func1<T, ?> func) {\n        if (sortablePageParam != null && CollUtil.isEmpty(sortablePageParam.getSortingFields())) {\n            sortablePageParam.setSortingFields(singletonList(buildSortingField(func)));\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/package-info.java",
    "content": "/**\n * 对于工具类的选择，优先查找 Hutool 中有没对应的方法\n * 如果没有，则自己封装对应的工具类，以 Utils 结尾，用于区分\n *\n * ps：如果担心 Hutool 存在坑的问题，可以阅读 Hutool 的实现源码，以确保可靠性。并且，可以补充相关的单元测试。\n */\npackage cn.iocoder.yudao.framework.common.util;\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.servlet;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.servlet.ServletUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport javax.servlet.ServletRequest;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.util.Map;\n\n/**\n * 客户端工具类\n *\n * @author 芋道源码\n */\npublic class ServletUtils {\n\n    /**\n     * 返回 JSON 字符串\n     *\n     * @param response 响应\n     * @param object   对象，会序列化成 JSON 字符串\n     */\n    @SuppressWarnings(\"deprecation\") // 必须使用 APPLICATION_JSON_UTF8_VALUE，否则会乱码\n    public static void writeJSON(HttpServletResponse response, Object object) {\n        String content = JsonUtils.toJsonString(object);\n        ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);\n    }\n\n    /**\n     * 返回附件\n     *\n     * @param response 响应\n     * @param filename 文件名\n     * @param content  附件内容\n     */\n    public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {\n        // 设置 header 和 contentType\n        response.setHeader(\"Content-Disposition\", \"attachment;filename=\" + URLEncoder.encode(filename, \"UTF-8\"));\n        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);\n        // 输出附件\n        IoUtil.write(response.getOutputStream(), false, content);\n    }\n\n    /**\n     * @param request 请求\n     * @return ua\n     */\n    public static String getUserAgent(HttpServletRequest request) {\n        String ua = request.getHeader(\"User-Agent\");\n        return ua != null ? ua : \"\";\n    }\n\n    /**\n     * 获得请求\n     *\n     * @return HttpServletRequest\n     */\n    public static HttpServletRequest getRequest() {\n        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n        if (!(requestAttributes instanceof ServletRequestAttributes)) {\n            return null;\n        }\n        return ((ServletRequestAttributes) requestAttributes).getRequest();\n    }\n\n    public static String getUserAgent() {\n        HttpServletRequest request = getRequest();\n        if (request == null) {\n            return null;\n        }\n        return getUserAgent(request);\n    }\n\n    public static String getClientIP() {\n        HttpServletRequest request = getRequest();\n        if (request == null) {\n            return null;\n        }\n        return ServletUtil.getClientIP(request);\n    }\n\n    public static boolean isJsonRequest(ServletRequest request) {\n        return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);\n    }\n\n    public static String getBody(HttpServletRequest request) {\n        // 只有在 json 请求在读取，因为只有 CacheRequestBodyFilter 才会进行缓存，支持重复读取\n        if (isJsonRequest(request)) {\n            return ServletUtil.getBody(request);\n        }\n        return null;\n    }\n\n    public static byte[] getBodyBytes(HttpServletRequest request) {\n        // 只有在 json 请求在读取，因为只有 CacheRequestBodyFilter 才会进行缓存，支持重复读取\n        if (isJsonRequest(request)) {\n            return ServletUtil.getBodyBytes(request);\n        }\n        return null;\n    }\n\n    public static String getClientIP(HttpServletRequest request) {\n        return ServletUtil.getClientIP(request);\n    }\n\n    public static Map<String, String> getParamMap(HttpServletRequest request) {\n        return ServletUtil.getParamMap(request);\n    }\n\n    public static Map<String, String> getHeaderMap(HttpServletRequest request) {\n        return ServletUtil.getHeaderMap(request);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringExpressionUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.spring;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.reflect.MethodSignature;\nimport org.springframework.context.expression.BeanFactoryResolver;\nimport org.springframework.core.DefaultParameterNameDiscoverer;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\n\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Spring EL 表达式的工具类\n *\n * @author mashu\n */\npublic class SpringExpressionUtils {\n\n    /**\n     * Spring EL 表达式解析器\n     */\n    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();\n    /**\n     * 参数名发现器\n     */\n    private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();\n\n    private SpringExpressionUtils() {\n    }\n\n    /**\n     * 从切面中，单个解析 EL 表达式的结果\n     *\n     * @param joinPoint        切面点\n     * @param expressionString EL 表达式数组\n     * @return 执行界面\n     */\n    public static Object parseExpression(JoinPoint joinPoint, String expressionString) {\n        Map<String, Object> result = parseExpressions(joinPoint, Collections.singletonList(expressionString));\n        return result.get(expressionString);\n    }\n\n    /**\n     * 从切面中，批量解析 EL 表达式的结果\n     *\n     * @param joinPoint         切面点\n     * @param expressionStrings EL 表达式数组\n     * @return 结果，key 为表达式，value 为对应值\n     */\n    public static Map<String, Object> parseExpressions(JoinPoint joinPoint, List<String> expressionStrings) {\n        // 如果为空，则不进行解析\n        if (CollUtil.isEmpty(expressionStrings)) {\n            return MapUtil.newHashMap();\n        }\n\n        // 第一步，构建解析的上下文 EvaluationContext\n        // 通过 joinPoint 获取被注解方法\n        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();\n        Method method = methodSignature.getMethod();\n        // 使用 spring 的 ParameterNameDiscoverer 获取方法形参名数组\n        String[] paramNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);\n        // Spring 的表达式上下文对象\n        EvaluationContext context = new StandardEvaluationContext();\n        // 给上下文赋值\n        if (ArrayUtil.isNotEmpty(paramNames)) {\n            Object[] args = joinPoint.getArgs();\n            for (int i = 0; i < paramNames.length; i++) {\n                context.setVariable(paramNames[i], args[i]);\n            }\n        }\n\n        // 第二步，逐个参数解析\n        Map<String, Object> result = MapUtil.newHashMap(expressionStrings.size(), true);\n        expressionStrings.forEach(key -> {\n            Object value = EXPRESSION_PARSER.parseExpression(key).getValue(context);\n            result.put(key, value);\n        });\n        return result;\n    }\n\n    /**\n     * 从 Bean 工厂，解析 EL 表达式的结果\n     *\n     * @param expressionString EL 表达式\n     * @return 执行界面\n     */\n    public static Object parseExpression(String expressionString) {\n        return parseExpression(expressionString, null);\n    }\n\n    /**\n     * 从 Bean 工厂，解析 EL 表达式的结果\n     *\n     * @param expressionString EL 表达式\n     * @param variables        变量\n     * @return 执行界面\n     */\n    public static Object parseExpression(String expressionString, Map<String, Object> variables) {\n        if (StrUtil.isBlank(expressionString)) {\n            return null;\n        }\n        Expression expression = EXPRESSION_PARSER.parseExpression(expressionString);\n        StandardEvaluationContext context = new StandardEvaluationContext();\n        context.setBeanResolver(new BeanFactoryResolver(SpringUtil.getApplicationContext()));\n        if (MapUtil.isNotEmpty(variables)) {\n            context.setVariables(variables);\n        }\n        return expression.getValue(context);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.spring;\n\nimport cn.hutool.extra.spring.SpringUtil;\n\nimport java.util.Objects;\n\n/**\n * Spring 工具类\n *\n * @author 芋道源码\n */\npublic class SpringUtils extends SpringUtil {\n\n    /**\n     * 是否为生产环境\n     *\n     * @return 是否生产环境\n     */\n    public static boolean isProd() {\n        String activeProfile = getActiveProfile();\n        return Objects.equals(\"prod\", activeProfile);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.string;\n\nimport cn.hutool.core.text.StrPool;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.StrUtil;\nimport org.aspectj.lang.JoinPoint;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * 字符串工具类\n *\n * @author 芋道源码\n */\npublic class StrUtils {\n\n    public static String maxLength(CharSequence str, int maxLength) {\n        return StrUtil.maxLength(str, maxLength - 3); // -3 的原因，是该方法会补充 ... 恰好\n    }\n\n    /**\n     * 给定字符串是否以任何一个字符串开始\n     * 给定字符串和数组为空都返回 false\n     *\n     * @param str      给定字符串\n     * @param prefixes 需要检测的开始字符串\n     * @since 3.0.6\n     */\n    public static boolean startWithAny(String str, Collection<String> prefixes) {\n        if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {\n            return false;\n        }\n\n        for (CharSequence suffix : prefixes) {\n            if (StrUtil.startWith(str, suffix, false)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public static List<Long> splitToLong(String value, CharSequence separator) {\n        long[] longs = StrUtil.splitToLong(value, separator);\n        return Arrays.stream(longs).boxed().collect(Collectors.toList());\n    }\n\n    public static Set<Long> splitToLongSet(String value) {\n        return splitToLongSet(value, StrPool.COMMA);\n    }\n\n    public static Set<Long> splitToLongSet(String value, CharSequence separator) {\n        long[] longs = StrUtil.splitToLong(value, separator);\n        return Arrays.stream(longs).boxed().collect(Collectors.toSet());\n    }\n\n    public static List<Integer> splitToInteger(String value, CharSequence separator) {\n        int[] integers = StrUtil.splitToInt(value, separator);\n        return Arrays.stream(integers).boxed().collect(Collectors.toList());\n    }\n\n    /**\n     * 移除字符串中，包含指定字符串的行\n     *\n     * @param content 字符串\n     * @param sequence 包含的字符串\n     * @return 移除后的字符串\n     */\n    public static String removeLineContains(String content, String sequence) {\n        if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) {\n            return content;\n        }\n        return Arrays.stream(content.split(\"\\n\"))\n                .filter(line -> !line.contains(sequence))\n                .collect(Collectors.joining(\"\\n\"));\n    }\n\n    /**\n     * 拼接方法的参数\n     *\n     * 特殊：排除一些无法序列化的参数，如 ServletRequest、ServletResponse、MultipartFile\n     *\n     * @param joinPoint 连接点\n     * @return 拼接后的参数\n     */\n    public static String joinMethodArgs(JoinPoint joinPoint) {\n        Object[] args = joinPoint.getArgs();\n        if (ArrayUtil.isEmpty(args)) {\n            return \"\";\n        }\n        return ArrayUtil.join(args, \",\", item -> {\n            if (item == null) {\n                return \"\";\n            }\n            // 讨论可见：https://t.zsxq.com/XUJVk、https://t.zsxq.com/MnKcL\n            String clazzName = item.getClass().getName();\n            if (StrUtil.startWithAny(clazzName, \"javax.servlet\", \"jakarta.servlet\", \"org.springframework.web\")) {\n                return \"\";\n            }\n            return item;\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java",
    "content": "package cn.iocoder.yudao.framework.common.util.validation;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport org.springframework.util.StringUtils;\n\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport javax.validation.Validation;\nimport javax.validation.Validator;\nimport java.util.Set;\nimport java.util.regex.Pattern;\n\n/**\n * 校验工具类\n *\n * @author 芋道源码\n */\npublic class ValidationUtils {\n\n    private static final Pattern PATTERN_MOBILE = Pattern.compile(\"^(?:(?:\\\\+|00)86)?1(?:(?:3[\\\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\\\d])|(?:9[0-3,5-9]))\\\\d{8}$\");\n\n    private static final Pattern PATTERN_URL = Pattern.compile(\"^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]\");\n\n    private static final Pattern PATTERN_XML_NCNAME = Pattern.compile(\"[a-zA-Z_][\\\\-_.0-9_a-zA-Z$]*\");\n\n    public static boolean isMobile(String mobile) {\n        return StringUtils.hasText(mobile)\n                && PATTERN_MOBILE.matcher(mobile).matches();\n    }\n\n    public static boolean isURL(String url) {\n        return StringUtils.hasText(url)\n                && PATTERN_URL.matcher(url).matches();\n    }\n\n    public static boolean isXmlNCName(String str) {\n        return StringUtils.hasText(str)\n                && PATTERN_XML_NCNAME.matcher(str).matches();\n    }\n\n    public static void validate(Object object, Class<?>... groups) {\n        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();\n        Assert.notNull(validator);\n        validate(validator, object, groups);\n    }\n\n    public static void validate(Validator validator, Object object, Class<?>... groups) {\n        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);\n        if (CollUtil.isNotEmpty(constraintViolations)) {\n            throw new ConstraintViolationException(constraintViolations);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java",
    "content": "package cn.iocoder.yudao.framework.common.validation;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\n\nimport javax.validation.Constraint;\nimport javax.validation.Payload;\nimport java.lang.annotation.*;\n\n@Target({\n        ElementType.METHOD,\n        ElementType.FIELD,\n        ElementType.ANNOTATION_TYPE,\n        ElementType.CONSTRUCTOR,\n        ElementType.PARAMETER,\n        ElementType.TYPE_USE\n})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Constraint(\n        validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class}\n)\npublic @interface InEnum {\n\n    /**\n     * @return 实现 ArrayValuable 接口的类\n     */\n    Class<? extends ArrayValuable<?>> value();\n\n    String message() default \"必须在指定范围 {value}\";\n\n    Class<?>[] groups() default {};\n\n    Class<? extends Payload>[] payload() default {};\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java",
    "content": "package cn.iocoder.yudao.framework.common.validation;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {\n\n    private List<?> values;\n\n    @Override\n    public void initialize(InEnum annotation) {\n        ArrayValuable<?>[] values = annotation.value().getEnumConstants();\n        if (values.length == 0) {\n            this.values = Collections.emptyList();\n        } else {\n            this.values = Arrays.asList(values[0].array());\n        }\n    }\n\n    @Override\n    public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {\n        if (list == null) {\n            return true;\n        }\n        // 校验通过\n        if (CollUtil.containsAll(values, list)) {\n            return true;\n        }\n        // 校验不通过，自定义提示语句\n        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值\n        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()\n                .replaceAll(\"\\\\{value}\", CollUtil.join(list, \",\"))).addConstraintViolation(); // 重新添加错误提示语句\n        return false;\n    }\n\n}\n\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumValidator.java",
    "content": "package cn.iocoder.yudao.framework.common.validation;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class InEnumValidator implements ConstraintValidator<InEnum, Object> {\n\n    private List<?> values;\n\n    @Override\n    public void initialize(InEnum annotation) {\n        ArrayValuable<?>[] values = annotation.value().getEnumConstants();\n        if (values.length == 0) {\n            this.values = Collections.emptyList();\n        } else {\n            this.values = Arrays.asList(values[0].array());\n        }\n    }\n\n    @Override\n    public boolean isValid(Object value, ConstraintValidatorContext context) {\n        // 为空时，默认不校验，即认为通过\n        if (value == null) {\n            return true;\n        }\n        // 校验通过\n        if (values.contains(value)) {\n            return true;\n        }\n        // 校验不通过，自定义提示语句\n        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值\n        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()\n                .replaceAll(\"\\\\{value}\", values.toString())).addConstraintViolation(); // 重新添加错误提示语句\n        return false;\n    }\n\n}\n\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/Mobile.java",
    "content": "package cn.iocoder.yudao.framework.common.validation;\n\nimport javax.validation.Constraint;\nimport javax.validation.Payload;\nimport java.lang.annotation.*;\n\n@Target({\n        ElementType.METHOD,\n        ElementType.FIELD,\n        ElementType.ANNOTATION_TYPE,\n        ElementType.CONSTRUCTOR,\n        ElementType.PARAMETER,\n        ElementType.TYPE_USE\n})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Constraint(\n        validatedBy = MobileValidator.class\n)\npublic @interface Mobile {\n\n    String message() default \"手机号格式不正确\";\n\n    Class<?>[] groups() default {};\n\n    Class<? extends Payload>[] payload() default {};\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/MobileValidator.java",
    "content": "package cn.iocoder.yudao.framework.common.validation;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\n\npublic class MobileValidator implements ConstraintValidator<Mobile, String> {\n\n    @Override\n    public void initialize(Mobile annotation) {\n    }\n\n    @Override\n    public boolean isValid(String value, ConstraintValidatorContext context) {\n        // 如果手机号为空，默认不校验，即校验通过\n        if (StrUtil.isEmpty(value)) {\n            return true;\n        }\n        // 校验手机\n        return ValidationUtils.isMobile(value);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/Telephone.java",
    "content": "package cn.iocoder.yudao.framework.common.validation;\n\nimport javax.validation.Constraint;\nimport javax.validation.Payload;\nimport java.lang.annotation.*;\n\n@Target({\n        ElementType.METHOD,\n        ElementType.FIELD,\n        ElementType.ANNOTATION_TYPE,\n        ElementType.CONSTRUCTOR,\n        ElementType.PARAMETER,\n        ElementType.TYPE_USE\n})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Constraint(\n        validatedBy = TelephoneValidator.class\n)\npublic @interface Telephone {\n\n    String message() default \"电话格式不正确\";\n\n    Class<?>[] groups() default {};\n\n    Class<? extends Payload>[] payload() default {};\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/TelephoneValidator.java",
    "content": "package cn.iocoder.yudao.framework.common.validation;\n\nimport cn.hutool.core.text.CharSequenceUtil;\nimport cn.hutool.core.util.PhoneUtil;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\n\npublic class TelephoneValidator implements ConstraintValidator<Telephone, String> {\n\n    @Override\n    public void initialize(Telephone annotation) {\n    }\n\n    @Override\n    public boolean isValid(String value, ConstraintValidatorContext context) {\n        // 如果手机号为空，默认不校验，即校验通过\n        if (CharSequenceUtil.isEmpty(value)) {\n            return true;\n        }\n        // 校验手机\n        return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/package-info.java",
    "content": "/**\n * 使用 Hibernate Validator 实现参数校验\n */\npackage cn.iocoder.yudao.framework.common.validation;\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/main/java/com/fhs/trans/service/AutoTransable.java",
    "content": "package com.fhs.trans.service;\n\nimport com.fhs.core.trans.vo.VO;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 只有实现了这个接口的才能自动翻译\n *\n * 为什么要赋值粘贴到 yudao-common 包下？\n * 因为 AutoTransable 属于 easy-trans-service 下，无法方便的在 yudao-module-xxx-api 模块下使用\n *\n * @author jackwang\n * @since  2020-05-19 10:26:15\n */\npublic interface AutoTransable<V extends VO> {\n\n    /**\n     * 根据 ids 查询数据列表\n     *\n     * 改方法已过期啦，请使用 selectByIds\n     *\n     * @param ids 编号数组\n     * @return 数据列表\n     */\n    @Deprecated\n    default List<V> findByIds(List<? extends Object> ids){\n        return new ArrayList<>();\n    }\n\n    /**\n     * 根据 ids 查询\n     *\n     * @param ids 编号数组\n     * @return 数据列表\n     */\n    default List<V> selectByIds(List<? extends Object> ids){\n        return this.findByIds(ids);\n    }\n\n    /**\n     * 获取 db 中所有的数据\n     *\n     * @return db 中所有的数据\n     */\n    default List<V> select(){\n        return new ArrayList<>();\n    }\n\n    /**\n     * 根据 id 获取 vo\n     *\n     * @param primaryValue id\n     * @return vo\n     */\n    V selectById(Object primaryValue);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtilsTest.java",
    "content": "package cn.iocoder.yudao.framework.common.util.collection;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.BiFunction;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * {@link CollectionUtils} 的单元测试\n */\npublic class CollectionUtilsTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Dog {\n\n        private Integer id;\n        private String name;\n        private String code;\n\n    }\n\n    @Test\n    public void testDiffList() {\n        // 准备参数\n        Collection<Dog> oldList = Arrays.asList(\n                new Dog(1, \"花花\", \"hh\"),\n                new Dog(2, \"旺财\", \"wc\")\n        );\n        Collection<Dog> newList = Arrays.asList(\n                new Dog(null, \"花花2\", \"hh\"),\n                new Dog(null, \"小白\", \"xb\")\n        );\n        BiFunction<Dog, Dog, Boolean> sameFunc = (oldObj, newObj) -> {\n            boolean same = oldObj.getCode().equals(newObj.getCode());\n            // 如果相等的情况下，需要设置下 id，后续好更新\n            if (same) {\n                newObj.setId(oldObj.getId());\n            }\n            return same;\n        };\n\n        // 调用\n        List<List<Dog>> result = CollectionUtils.diffList(oldList, newList, sameFunc);\n        // 断言\n        assertEquals(result.size(), 3);\n        // 断言 create\n        assertEquals(result.get(0).size(), 1);\n        assertEquals(result.get(0).get(0), new Dog(null, \"小白\", \"xb\"));\n        // 断言 update\n        assertEquals(result.get(1).size(), 1);\n        assertEquals(result.get(1).get(0), new Dog(1, \"花花2\", \"hh\"));\n        // 断言 delete\n        assertEquals(result.get(2).size(), 1);\n        assertEquals(result.get(2).get(0), new Dog(2, \"旺财\", \"wc\"));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-common/《芋道 Spring Boot 参数校验 Validation 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Validation/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao-framework</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>数据权限</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n            <optional>true</optional> <!-- 可选，如果使用 DeptDataPermissionRule 必须提供 -->\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDataPermissionAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.config;\n\nimport cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor;\nimport cn.iocoder.yudao.framework.datapermission.core.db.DataPermissionRuleHandler;\nimport cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;\nimport cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;\nimport cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactoryImpl;\nimport cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\n\nimport java.util.List;\n\n/**\n * 数据权限的自动配置类\n *\n * @author 芋道源码\n */\n@AutoConfiguration\npublic class YudaoDataPermissionAutoConfiguration {\n\n    @Bean\n    public DataPermissionRuleFactory dataPermissionRuleFactory(List<DataPermissionRule> rules) {\n        return new DataPermissionRuleFactoryImpl(rules);\n    }\n\n    @Bean\n    public DataPermissionRuleHandler dataPermissionRuleHandler(MybatisPlusInterceptor interceptor,\n                                                               DataPermissionRuleFactory ruleFactory) {\n        // 创建 DataPermissionInterceptor 拦截器\n        DataPermissionRuleHandler handler = new DataPermissionRuleHandler(ruleFactory);\n        DataPermissionInterceptor inner = new DataPermissionInterceptor(handler);\n        // 添加到 interceptor 中\n        // 需要加在首个，主要是为了在分页插件前面。这个是 MyBatis Plus 的规定\n        MyBatisUtils.addInterceptor(interceptor, inner, 0);\n        return handler;\n    }\n\n    @Bean\n    public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() {\n        return new DataPermissionAnnotationAdvisor();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDataPermissionRpcAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.config;\n\nimport cn.iocoder.yudao.framework.datapermission.core.rpc.DataPermissionRequestInterceptor;\nimport cn.iocoder.yudao.framework.datapermission.core.rpc.DataPermissionRpcWebFilter;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\n\nimport static cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum.TENANT_CONTEXT_FILTER;\n\n/**\n * 数据权限针对 RPC 的自动配置类\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@ConditionalOnClass(name = \"feign.RequestInterceptor\")\npublic class YudaoDataPermissionRpcAutoConfiguration {\n\n    @Bean\n    public DataPermissionRequestInterceptor dataPermissionRequestInterceptor() {\n        return new DataPermissionRequestInterceptor();\n    }\n\n    @Bean\n    public FilterRegistrationBean<DataPermissionRpcWebFilter> dataPermissionRpcFilter() {\n        FilterRegistrationBean<DataPermissionRpcWebFilter> registrationBean = new FilterRegistrationBean<>();\n        registrationBean.setFilter(new DataPermissionRpcWebFilter());\n        registrationBean.setOrder(TENANT_CONTEXT_FILTER - 1); // 顺序没有绝对的要求，在租户 Filter 前面稳妥点\n        return registrationBean;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDeptDataPermissionAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.config;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;\nimport cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRule;\nimport cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRuleCustomizer;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.context.annotation.Bean;\n\nimport java.util.List;\n\n/**\n * 基于部门的数据权限 AutoConfiguration\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@ConditionalOnClass(LoginUser.class)\n@ConditionalOnBean(value = {DeptDataPermissionRuleCustomizer.class})\npublic class YudaoDeptDataPermissionAutoConfiguration {\n\n    @Bean\n    public DeptDataPermissionRule deptDataPermissionRule(PermissionCommonApi permissionApi,\n                                                         List<DeptDataPermissionRuleCustomizer> customizers) {\n        // Cloud 专属逻辑：优先使用本地的 PermissionApi 实现类，而不是 Feign 调用\n        // 原因：在创建租户时，租户还没创建好，导致 Feign 调用获取数据权限时，报“租户不存在”的错误\n        try {\n            PermissionCommonApi permissionApiImpl = SpringUtil.getBean(\"permissionApiImpl\", PermissionCommonApi.class);\n            if (permissionApiImpl != null) {\n                permissionApi = permissionApiImpl;\n            }\n        } catch (Exception ignored) {}\n\n        // 创建 DeptDataPermissionRule 对象\n        DeptDataPermissionRule rule = new DeptDataPermissionRule(permissionApi);\n        // 补全表配置\n        customizers.forEach(customizer -> customizer.customize(rule));\n        return rule;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/annotation/DataPermission.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.annotation;\n\nimport cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;\n\nimport java.lang.annotation.*;\n\n/**\n * 数据权限注解\n * 可声明在类或者方法上，标识使用的数据权限规则\n *\n * @author 芋道源码\n */\n@Target({ElementType.TYPE, ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface DataPermission {\n\n    /**\n     * 当前类或方法是否开启数据权限\n     * 即使不添加 @DataPermission 注解，默认是开启状态\n     * 可通过设置 enable 为 false 禁用\n     */\n    boolean enable() default true;\n\n    /**\n     * 生效的数据权限规则数组，优先级高于 {@link #excludeRules()}\n     */\n    Class<? extends DataPermissionRule>[] includeRules() default {};\n\n    /**\n     * 排除的数据权限规则数组，优先级最低\n     */\n    Class<? extends DataPermissionRule>[] excludeRules() default {};\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationAdvisor.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.aop;\n\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport lombok.EqualsAndHashCode;\nimport lombok.Getter;\nimport org.aopalliance.aop.Advice;\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.support.AbstractPointcutAdvisor;\nimport org.springframework.aop.support.ComposablePointcut;\nimport org.springframework.aop.support.annotation.AnnotationMatchingPointcut;\n\n/**\n * {@link cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission} 注解的 Advisor 实现类\n *\n * @author 芋道源码\n */\n@Getter\n@EqualsAndHashCode(callSuper = true)\npublic class DataPermissionAnnotationAdvisor extends AbstractPointcutAdvisor {\n\n    private final Advice advice;\n\n    private final Pointcut pointcut;\n\n    public DataPermissionAnnotationAdvisor() {\n        this.advice = new DataPermissionAnnotationInterceptor();\n        this.pointcut = this.buildPointcut();\n    }\n\n    protected Pointcut buildPointcut() {\n        Pointcut classPointcut = new AnnotationMatchingPointcut(DataPermission.class, true);\n        Pointcut methodPointcut = new AnnotationMatchingPointcut(null, DataPermission.class, true);\n        return new ComposablePointcut(classPointcut).union(methodPointcut);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.aop;\n\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport lombok.Getter;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.springframework.core.MethodClassKey;\nimport org.springframework.core.annotation.AnnotationUtils;\n\nimport java.lang.reflect.Method;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * {@link DataPermission} 注解的拦截器\n * 1. 在执行方法前，将 @DataPermission 注解入栈\n * 2. 在执行方法后，将 @DataPermission 注解出栈\n *\n * @author 芋道源码\n */\n@DataPermission // 该注解，用于 {@link DATA_PERMISSION_NULL} 的空对象\npublic class DataPermissionAnnotationInterceptor implements MethodInterceptor {\n\n    /**\n     * DataPermission 空对象，用于方法无 {@link DataPermission} 注解时，使用 DATA_PERMISSION_NULL 进行占位\n     */\n    static final DataPermission DATA_PERMISSION_NULL = DataPermissionAnnotationInterceptor.class.getAnnotation(DataPermission.class);\n\n    @Getter\n    private final Map<MethodClassKey, DataPermission> dataPermissionCache = new ConcurrentHashMap<>();\n\n    @Override\n    public Object invoke(MethodInvocation methodInvocation) throws Throwable {\n        // 入栈\n        DataPermission dataPermission = this.findAnnotation(methodInvocation);\n        if (dataPermission != null) {\n            DataPermissionContextHolder.add(dataPermission);\n        }\n        try {\n            // 执行逻辑\n            return methodInvocation.proceed();\n        } finally {\n            // 出栈\n            if (dataPermission != null) {\n                DataPermissionContextHolder.remove();\n            }\n        }\n    }\n\n    private DataPermission findAnnotation(MethodInvocation methodInvocation) {\n        // 1. 从缓存中获取\n        Method method = methodInvocation.getMethod();\n        Object targetObject = methodInvocation.getThis();\n        Class<?> clazz = targetObject != null ? targetObject.getClass() : method.getDeclaringClass();\n        MethodClassKey methodClassKey = new MethodClassKey(method, clazz);\n        DataPermission dataPermission = dataPermissionCache.get(methodClassKey);\n        if (dataPermission != null) {\n            return dataPermission != DATA_PERMISSION_NULL ? dataPermission : null;\n        }\n\n        // 2.1 从方法中获取\n        dataPermission = AnnotationUtils.findAnnotation(method, DataPermission.class);\n        // 2.2 从类上获取\n        if (dataPermission == null) {\n            dataPermission = AnnotationUtils.findAnnotation(clazz, DataPermission.class);\n        }\n        // 2.3 添加到缓存中\n        dataPermissionCache.put(methodClassKey, dataPermission != null ? dataPermission : DATA_PERMISSION_NULL);\n        return dataPermission;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolder.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.aop;\n\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport com.alibaba.ttl.TransmittableThreadLocal;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * {@link DataPermission} 注解的 Context 上下文\n *\n * @author 芋道源码\n */\npublic class DataPermissionContextHolder {\n\n    /**\n     * 使用 List 的原因，可能存在方法的嵌套调用\n     */\n    private static final ThreadLocal<LinkedList<DataPermission>> DATA_PERMISSIONS =\n            TransmittableThreadLocal.withInitial(LinkedList::new);\n\n    /**\n     * 获得当前的 DataPermission 注解\n     *\n     * @return DataPermission 注解\n     */\n    public static DataPermission get() {\n        return DATA_PERMISSIONS.get().peekLast();\n    }\n\n    /**\n     * 入栈 DataPermission 注解\n     *\n     * @param dataPermission DataPermission 注解\n     */\n    public static void add(DataPermission dataPermission) {\n        DATA_PERMISSIONS.get().addLast(dataPermission);\n    }\n\n    /**\n     * 出栈 DataPermission 注解\n     *\n     * @return DataPermission 注解\n     */\n    public static DataPermission remove() {\n        DataPermission dataPermission = DATA_PERMISSIONS.get().removeLast();\n        // 无元素时，清空 ThreadLocal\n        if (DATA_PERMISSIONS.get().isEmpty()) {\n            DATA_PERMISSIONS.remove();\n        }\n        return dataPermission;\n    }\n\n    /**\n     * 获得所有 DataPermission\n     *\n     * @return DataPermission 队列\n     */\n    public static List<DataPermission> getAll() {\n        return DATA_PERMISSIONS.get();\n    }\n\n    /**\n     * 清空上下文\n     *\n     * 目前仅仅用于单测\n     */\n    public static void clear() {\n        DATA_PERMISSIONS.remove();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionRuleHandler.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.db;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;\nimport cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;\nimport cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;\nimport com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;\nimport lombok.RequiredArgsConstructor;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.schema.Table;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.skipPermissionCheck;\n\n/**\n * 基于 {@link DataPermissionRule} 的数据权限处理器\n *\n * 它的底层，是基于 MyBatis Plus 的 <a href=\"https://baomidou.com/plugins/data-permission/\">数据权限插件</a>\n * 核心原理：它会在 SQL 执行前拦截 SQL 语句，并根据用户权限动态添加权限相关的 SQL 片段。这样，只有用户有权限访问的数据才会被查询出来\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\npublic class DataPermissionRuleHandler implements MultiDataPermissionHandler {\n\n    private final DataPermissionRuleFactory ruleFactory;\n\n    @Override\n    public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {\n        // 特殊：跨租户访问\n        if (skipPermissionCheck()) {\n            return null;\n        }\n\n        // 获得 Mapper 对应的数据权限的规则\n        List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(mappedStatementId);\n        if (CollUtil.isEmpty(rules)) {\n            return null;\n        }\n\n        // 生成条件\n        Expression allExpression = null;\n        for (DataPermissionRule rule : rules) {\n            // 判断表名是否匹配\n            String tableName = MyBatisUtils.getTableName(table);\n            if (!rule.getTableNames().contains(tableName)) {\n                continue;\n            }\n\n            // 单条规则的条件\n            Expression oneExpress = rule.getExpression(tableName, table.getAlias());\n            if (oneExpress == null) {\n                continue;\n            }\n            // 拼接到 allExpression 中\n            allExpression = allExpression == null ? oneExpress\n                    : new AndExpression(allExpression, oneExpress);\n        }\n        return allExpression;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rpc/DataPermissionRequestInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.rpc;\n\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;\nimport feign.RequestInterceptor;\nimport feign.RequestTemplate;\n\n/**\n * DataPermission 的 RequestInterceptor 实现类：Feign 请求时，将 {@link DataPermission} 设置到 header 中，继续透传给被调用的服务\n *\n * 注意：由于 {@link DataPermission} 不支持序列化和反序列化，所以暂时只能传递它的 enable 属性\n *\n * @author 芋道源码\n */\npublic class DataPermissionRequestInterceptor implements RequestInterceptor {\n\n    public static final String ENABLE_HEADER_NAME = \"data-permission-enable\";\n\n    @Override\n    public void apply(RequestTemplate requestTemplate) {\n        DataPermission dataPermission = DataPermissionContextHolder.get();\n        if (dataPermission != null && Boolean.FALSE.equals(dataPermission.enable())) {\n            requestTemplate.header(ENABLE_HEADER_NAME, \"false\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rpc/DataPermissionRpcWebFilter.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.rpc;\n\nimport cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;\nimport cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.Objects;\n\n/**\n * 针对 {@link DataPermissionRequestInterceptor} 的 RPC 调用，设置 {@link DataPermissionContextHolder} 的上下文\n *\n * @author 芋道源码\n */\npublic class DataPermissionRpcWebFilter extends OncePerRequestFilter {\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        String enable = request.getHeader(DataPermissionRequestInterceptor.ENABLE_HEADER_NAME);\n        if (Objects.equals(enable, Boolean.FALSE.toString())) {\n            DataPermissionUtils.executeIgnore(() -> {\n                try {\n                    chain.doFilter(request, response);\n                } catch (IOException | ServletException e) {\n                    throw new RuntimeException(e);\n                }\n            });\n        } else {\n            chain.doFilter(request, response);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRule.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.rule;\n\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport net.sf.jsqlparser.expression.Alias;\nimport net.sf.jsqlparser.expression.Expression;\n\nimport java.util.Set;\n\n/**\n * 数据权限规则接口\n * 通过实现接口，自定义数据规则。例如说，\n *\n * @author 芋道源码\n */\npublic interface DataPermissionRule {\n\n    /**\n     * 返回需要生效的表名数组\n     * 为什么需要该方法？Data Permission 数组基于 SQL 重写，通过 Where 返回只有权限的数据\n     *\n     * 如果需要基于实体名获得表名，可调用 {@link TableInfoHelper#getTableInfo(Class)} 获得\n     *\n     * @return 表名数组\n     */\n    Set<String> getTableNames();\n\n    /**\n     * 根据表名和别名，生成对应的 WHERE / OR 过滤条件\n     *\n     * @param tableName 表名\n     * @param tableAlias 别名，可能为空\n     * @return 过滤条件 Expression 表达式\n     */\n    Expression getExpression(String tableName, Alias tableAlias);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactory.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.rule;\n\nimport java.util.List;\n\n/**\n * {@link DataPermissionRule} 工厂接口\n * 作为 {@link DataPermissionRule} 的容器，提供管理能力\n *\n * @author 芋道源码\n */\npublic interface DataPermissionRuleFactory {\n\n    /**\n     * 获得所有数据权限规则数组\n     *\n     * @return 数据权限规则数组\n     */\n    List<DataPermissionRule> getDataPermissionRules();\n\n    /**\n     * 获得指定 Mapper 的数据权限规则数组\n     *\n     * @param mappedStatementId 指定 Mapper 的编号\n     * @return 数据权限规则数组\n     */\n    List<DataPermissionRule> getDataPermissionRule(String mappedStatementId);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.rule;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;\nimport com.fhs.trans.service.impl.SimpleTransService;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * 默认的 DataPermissionRuleFactoryImpl 实现类\n * 支持通过 {@link DataPermissionContextHolder} 过滤数据权限\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\npublic class DataPermissionRuleFactoryImpl implements DataPermissionRuleFactory {\n\n    /**\n     * 数据权限规则数组\n     */\n    private final List<DataPermissionRule> rules;\n\n    @Override\n    public List<DataPermissionRule> getDataPermissionRules() {\n        return rules;\n    }\n\n    @Override // mappedStatementId 参数，暂时没有用。以后，可以基于 mappedStatementId + DataPermission 进行缓存\n    public List<DataPermissionRule> getDataPermissionRule(String mappedStatementId) {\n        // 1.1 无数据权限\n        if (CollUtil.isEmpty(rules)) {\n            return Collections.emptyList();\n        }\n        // 1.2 未配置，则默认开启\n        DataPermission dataPermission = DataPermissionContextHolder.get();\n        if (dataPermission == null) {\n            return rules;\n        }\n        // 1.3 已配置，但禁用\n        if (!dataPermission.enable()) {\n            return Collections.emptyList();\n        }\n        // 1.4 特殊：数据翻译时，强制忽略数据权限 https://github.com/YunaiV/ruoyi-vue-pro/issues/1007\n        if (isTranslateCall()) {\n            return Collections.emptyList();\n        }\n\n        // 2.1 情况一：已配置，只选择部分规则\n        if (ArrayUtil.isNotEmpty(dataPermission.includeRules())) {\n            return rules.stream().filter(rule -> ArrayUtil.contains(dataPermission.includeRules(), rule.getClass()))\n                    .collect(Collectors.toList()); // 一般规则不会太多，所以不采用 HashSet 查询\n        }\n        // 2.2 已配置，只排除部分规则\n        if (ArrayUtil.isNotEmpty(dataPermission.excludeRules())) {\n            return rules.stream().filter(rule -> !ArrayUtil.contains(dataPermission.excludeRules(), rule.getClass()))\n                    .collect(Collectors.toList()); // 一般规则不会太多，所以不采用 HashSet 查询\n        }\n        // 2.3 已配置，全部规则\n        return rules;\n    }\n\n    /**\n     * 判断是否为数据翻译 {@link com.fhs.core.trans.anno.Trans} 的调用\n     *\n     * 目前暂时只有这个办法，已经和 easy-trans 做过沟通\n     *\n     * @return 是否\n     */\n    private boolean isTranslateCall() {\n        StackTraceElement[] stack = Thread.currentThread().getStackTrace();\n        for (StackTraceElement e : stack) {\n            if (SimpleTransService.class.getName().equals(e.getClassName())) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.rule.dept;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport net.sf.jsqlparser.expression.*;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.expression.operators.relational.InExpression;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 基于部门的 {@link DataPermissionRule} 数据权限规则实现\n *\n * 注意，使用 DeptDataPermissionRule 时，需要保证表中有 dept_id 部门编号的字段，可自定义。\n *\n * 实际业务场景下，会存在一个经典的问题？当用户修改部门时，冗余的 dept_id 是否需要修改？\n * 1. 一般情况下，dept_id 不进行修改，则会导致用户看不到之前的数据。【yudao-server 采用该方案】\n * 2. 部分情况下，希望该用户还是能看到之前的数据，则有两种方式解决：【需要你改造该 DeptDataPermissionRule 的实现代码】\n *  1）编写洗数据的脚本，将 dept_id 修改成新部门的编号；【建议】\n *      最终过滤条件是 WHERE dept_id = ?\n *  2）洗数据的话，可能涉及的数据量较大，也可以采用 user_id 进行过滤的方式，此时需要获取到 dept_id 对应的所有 user_id 用户编号；\n *      最终过滤条件是 WHERE user_id IN (?, ?, ? ...)\n *  3）想要保证原 dept_id 和 user_id 都可以看的到，此时使用 dept_id 和 user_id 一起过滤；\n *      最终过滤条件是 WHERE dept_id = ? OR user_id IN (?, ?, ? ...)\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Slf4j\npublic class DeptDataPermissionRule implements DataPermissionRule {\n\n    /**\n     * LoginUser 的 Context 缓存 Key\n     */\n    protected static final String CONTEXT_KEY = DeptDataPermissionRule.class.getSimpleName();\n\n    private static final String DEPT_COLUMN_NAME = \"dept_id\";\n    private static final String USER_COLUMN_NAME = \"user_id\";\n\n    private final PermissionCommonApi permissionApi;\n\n    /**\n     * 基于部门的表字段配置\n     * 一般情况下，每个表的部门编号字段是 dept_id，通过该配置自定义。\n     *\n     * key：表名\n     * value：字段名\n     */\n    private final Map<String, String> deptColumns = new HashMap<>();\n    /**\n     * 基于用户的表字段配置\n     * 一般情况下，每个表的部门编号字段是 dept_id，通过该配置自定义。\n     *\n     * key：表名\n     * value：字段名\n     */\n    private final Map<String, String> userColumns = new HashMap<>();\n    /**\n     * 所有表名，是 {@link #deptColumns} 和 {@link #userColumns} 的合集\n     */\n    private final Set<String> TABLE_NAMES = new HashSet<>();\n\n    @Override\n    public Set<String> getTableNames() {\n        return TABLE_NAMES;\n    }\n\n    @Override\n    public Expression getExpression(String tableName, Alias tableAlias) {\n        // 只有有登陆用户的情况下，才进行数据权限的处理\n        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();\n        if (loginUser == null) {\n            return null;\n        }\n        // 只有管理员类型的用户，才进行数据权限的处理\n        if (ObjectUtil.notEqual(loginUser.getUserType(), UserTypeEnum.ADMIN.getValue())) {\n            return null;\n        }\n\n        // 获得数据权限\n        DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);\n        // 从上下文中拿不到，则调用逻辑进行获取\n        if (deptDataPermission == null) {\n            deptDataPermission = permissionApi.getDeptDataPermission(loginUser.getId()).getCheckedData();\n            if (deptDataPermission == null) {\n                log.error(\"[getExpression][LoginUser({}) 获取数据权限为 null]\", JsonUtils.toJsonString(loginUser));\n                throw new NullPointerException(String.format(\"LoginUser(%d) Table(%s/%s) 未返回数据权限\",\n                        loginUser.getId(), tableName, tableAlias.getName()));\n            }\n            // 添加到上下文中，避免重复计算\n            loginUser.setContext(CONTEXT_KEY, deptDataPermission);\n        }\n\n        // 情况一，如果是 ALL 可查看全部，则无需拼接条件\n        if (deptDataPermission.getAll()) {\n            return null;\n        }\n\n        // 情况二，即不能查看部门，又不能查看自己，则说明 100% 无权限\n        if (CollUtil.isEmpty(deptDataPermission.getDeptIds())\n            && Boolean.FALSE.equals(deptDataPermission.getSelf())) {\n            return new EqualsTo(null, null); // WHERE null = null，可以保证返回的数据为空\n        }\n\n        // 情况三，拼接 Dept 和 User 的条件，最后组合\n        Expression deptExpression = buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds());\n        Expression userExpression = buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId());\n        if (deptExpression == null && userExpression == null) {\n            // TODO 芋艿：获得不到条件的时候，暂时不抛出异常，而是不返回数据\n            log.warn(\"[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]\",\n                    JsonUtils.toJsonString(loginUser), tableName, tableAlias, JsonUtils.toJsonString(deptDataPermission));\n//            throw new NullPointerException(String.format(\"LoginUser(%d) Table(%s/%s) 构建的条件为空\",\n//                    loginUser.getId(), tableName, tableAlias.getName()));\n            return new EqualsTo(null, null); // WHERE null = null，可以保证返回的数据为空\n        }\n        if (deptExpression == null) {\n            return userExpression;\n        }\n        if (userExpression == null) {\n            return deptExpression;\n        }\n        // 目前，如果有指定部门 + 可查看自己，采用 OR 条件。即，WHERE (dept_id IN ? OR user_id = ?)\n        return new ParenthesedExpressionList(new OrExpression(deptExpression, userExpression));\n    }\n\n    private Expression buildDeptExpression(String tableName, Alias tableAlias, Set<Long> deptIds) {\n        // 如果不存在配置，则无需作为条件\n        String columnName = deptColumns.get(tableName);\n        if (StrUtil.isEmpty(columnName)) {\n            return null;\n        }\n        // 如果为空，则无条件\n        if (CollUtil.isEmpty(deptIds)) {\n            return null;\n        }\n        // 拼接条件\n        return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, columnName),\n                // Parenthesis 的目的，是提供 (1,2,3) 的 () 左右括号\n                new ParenthesedExpressionList(new ExpressionList<LongValue>(CollectionUtils.convertList(deptIds, LongValue::new))));\n    }\n\n    private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) {\n        // 如果不查看自己，则无需作为条件\n        if (Boolean.FALSE.equals(self)) {\n            return null;\n        }\n        String columnName = userColumns.get(tableName);\n        if (StrUtil.isEmpty(columnName)) {\n            return null;\n        }\n        // 拼接条件\n        return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId));\n    }\n\n    // ==================== 添加配置 ====================\n\n    public void addDeptColumn(Class<? extends BaseDO> entityClass) {\n        addDeptColumn(entityClass, DEPT_COLUMN_NAME);\n    }\n\n    public void addDeptColumn(Class<? extends BaseDO> entityClass, String columnName) {\n        String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();\n        addDeptColumn(tableName, columnName);\n    }\n\n    public void addDeptColumn(String tableName, String columnName) {\n        deptColumns.put(tableName, columnName);\n        TABLE_NAMES.add(tableName);\n    }\n\n    public void addUserColumn(Class<? extends BaseDO> entityClass) {\n        addUserColumn(entityClass, USER_COLUMN_NAME);\n    }\n\n    public void addUserColumn(Class<? extends BaseDO> entityClass, String columnName) {\n        String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();\n        addUserColumn(tableName, columnName);\n    }\n\n    public void addUserColumn(String tableName, String columnName) {\n        userColumns.put(tableName, columnName);\n        TABLE_NAMES.add(tableName);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRuleCustomizer.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.rule.dept;\n\n/**\n * {@link DeptDataPermissionRule} 的自定义配置接口\n *\n * @author 芋道源码\n */\n@FunctionalInterface\npublic interface DeptDataPermissionRuleCustomizer {\n\n    /**\n     * 自定义该权限规则\n     * 1. 调用 {@link DeptDataPermissionRule#addDeptColumn(Class, String)} 方法，配置基于 dept_id 的过滤规则\n     * 2. 调用 {@link DeptDataPermissionRule#addUserColumn(Class, String)} 方法，配置基于 user_id 的过滤规则\n     *\n     * @param rule 权限规则\n     */\n    void customize(DeptDataPermissionRule rule);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/package-info.java",
    "content": "/**\n * 基于部门的数据权限规则\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.framework.datapermission.core.rule.dept;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.util;\n\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;\nimport lombok.SneakyThrows;\n\nimport java.util.concurrent.Callable;\n\n/**\n * 数据权限 Util\n *\n * @author 芋道源码\n */\npublic class DataPermissionUtils {\n\n    private static DataPermission DATA_PERMISSION_DISABLE;\n\n    @DataPermission(enable = false)\n    @SneakyThrows\n    private static DataPermission getDisableDataPermissionDisable() {\n        if (DATA_PERMISSION_DISABLE == null) {\n            DATA_PERMISSION_DISABLE = DataPermissionUtils.class\n                    .getDeclaredMethod(\"getDisableDataPermissionDisable\")\n                    .getAnnotation(DataPermission.class);\n        }\n        return DATA_PERMISSION_DISABLE;\n    }\n\n    /**\n     * 忽略数据权限，执行对应的逻辑\n     *\n     * @param runnable 逻辑\n     */\n    public static void executeIgnore(Runnable runnable) {\n        addDisableDataPermission();\n        try {\n            // 执行 runnable\n            runnable.run();\n        } finally {\n            removeDataPermission();\n        }\n    }\n\n    /**\n     * 忽略数据权限，执行对应的逻辑\n     *\n     * @param callable 逻辑\n     * @return 执行结果\n     */\n    @SneakyThrows\n    public static <T> T executeIgnore(Callable<T> callable) {\n        addDisableDataPermission();\n        try {\n            // 执行 callable\n            return callable.call();\n        } finally {\n            removeDataPermission();\n        }\n    }\n\n    /**\n     * 添加忽略数据权限\n     */\n    public static void addDisableDataPermission(){\n        DataPermission dataPermission = getDisableDataPermissionDisable();\n        DataPermissionContextHolder.add(dataPermission);\n    }\n\n    public static void removeDataPermission(){\n        DataPermissionContextHolder.remove();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/package-info.java",
    "content": "/**\n * 基于 JSqlParser 解析 SQL，增加数据权限的 WHERE 条件\n */\npackage cn.iocoder.yudao.framework.datapermission;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.datapermission.config.YudaoDataPermissionAutoConfiguration\ncn.iocoder.yudao.framework.datapermission.config.YudaoDeptDataPermissionAutoConfiguration\ncn.iocoder.yudao.framework.datapermission.config.YudaoDataPermissionRpcAutoConfiguration\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptorTest.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.aop;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.lang.reflect.Method;\n\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.when;\n\n/**\n * {@link DataPermissionAnnotationInterceptor} 的单元测试\n *\n * @author 芋道源码\n */\npublic class DataPermissionAnnotationInterceptorTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private DataPermissionAnnotationInterceptor interceptor;\n\n    @Mock\n    private MethodInvocation methodInvocation;\n\n    @BeforeEach\n    public void setUp() {\n        interceptor.getDataPermissionCache().clear();\n    }\n\n    @Test // 无 @DataPermission 注解\n    public void testInvoke_none() throws Throwable {\n        // 参数\n        mockMethodInvocation(TestNone.class);\n\n        // 调用\n        Object result = interceptor.invoke(methodInvocation);\n        // 断言\n        assertEquals(\"none\", result);\n        assertEquals(1, interceptor.getDataPermissionCache().size());\n        assertTrue(CollUtil.getFirst(interceptor.getDataPermissionCache().values()).enable());\n    }\n\n    @Test // 在 Method 上有 @DataPermission 注解\n    public void testInvoke_method() throws Throwable {\n        // 参数\n        mockMethodInvocation(TestMethod.class);\n\n        // 调用\n        Object result = interceptor.invoke(methodInvocation);\n        // 断言\n        assertEquals(\"method\", result);\n        assertEquals(1, interceptor.getDataPermissionCache().size());\n        assertFalse(CollUtil.getFirst(interceptor.getDataPermissionCache().values()).enable());\n    }\n\n    @Test // 在 Class 上有 @DataPermission 注解\n    public void testInvoke_class() throws Throwable {\n        // 参数\n        mockMethodInvocation(TestClass.class);\n\n        // 调用\n        Object result = interceptor.invoke(methodInvocation);\n        // 断言\n        assertEquals(\"class\", result);\n        assertEquals(1, interceptor.getDataPermissionCache().size());\n        assertFalse(CollUtil.getFirst(interceptor.getDataPermissionCache().values()).enable());\n    }\n\n    private void mockMethodInvocation(Class<?> clazz) throws Throwable {\n        Object targetObject = clazz.newInstance();\n        Method method = targetObject.getClass().getMethod(\"echo\");\n        when(methodInvocation.getThis()).thenReturn(targetObject);\n        when(methodInvocation.getMethod()).thenReturn(method);\n        when(methodInvocation.proceed()).then(invocationOnMock -> method.invoke(targetObject));\n    }\n\n    static class TestMethod {\n\n        @DataPermission(enable = false)\n        public String echo() {\n            return \"method\";\n        }\n\n    }\n\n    @DataPermission(enable = false)\n    static class TestClass {\n\n        public String echo() {\n            return \"class\";\n        }\n\n    }\n\n    static class TestNone {\n\n        public String echo() {\n            return \"none\";\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolderTest.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.aop;\n\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.mockito.Mockito.mock;\n\n/**\n * {@link DataPermissionContextHolder} 的单元测试\n *\n * @author 芋道源码\n */\nclass DataPermissionContextHolderTest {\n\n    @BeforeEach\n    public void setUp() {\n        DataPermissionContextHolder.clear();\n    }\n\n    @Test\n    public void testGet() {\n        // mock 方法\n        DataPermission dataPermission01 = mock(DataPermission.class);\n        DataPermissionContextHolder.add(dataPermission01);\n        DataPermission dataPermission02 = mock(DataPermission.class);\n        DataPermissionContextHolder.add(dataPermission02);\n\n        // 调用\n        DataPermission result = DataPermissionContextHolder.get();\n        // 断言\n        assertSame(result, dataPermission02);\n    }\n\n    @Test\n    public void testPush() {\n        // 调用\n        DataPermission dataPermission01 = mock(DataPermission.class);\n        DataPermissionContextHolder.add(dataPermission01);\n        DataPermission dataPermission02 = mock(DataPermission.class);\n        DataPermissionContextHolder.add(dataPermission02);\n        // 断言\n        DataPermission first = DataPermissionContextHolder.getAll().get(0);\n        DataPermission second = DataPermissionContextHolder.getAll().get(1);\n        assertSame(dataPermission01, first);\n        assertSame(dataPermission02, second);\n    }\n\n    @Test\n    public void testRemove() {\n        // mock 方法\n        DataPermission dataPermission01 = mock(DataPermission.class);\n        DataPermissionContextHolder.add(dataPermission01);\n        DataPermission dataPermission02 = mock(DataPermission.class);\n        DataPermissionContextHolder.add(dataPermission02);\n\n        // 调用\n        DataPermission result = DataPermissionContextHolder.remove();\n        // 断言\n        assertSame(result, dataPermission02);\n        assertEquals(1, DataPermissionContextHolder.getAll().size());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionRuleHandlerTest.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.db;\n\nimport cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;\nimport cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;\nimport cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;\nimport net.sf.jsqlparser.expression.Alias;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.LongValue;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.expression.operators.relational.InExpression;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.Arrays;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.when;\n\n/**\n * {@link DataPermissionRuleHandler} 的单元测试\n * 主要复用了 MyBatis Plus 的 TenantLineInnerInterceptorTest 的单元测试\n * 不过它的单元测试不是很规范，考虑到是复用的，所以暂时不进行修改~\n *\n * @author 芋道源码\n */\npublic class DataPermissionRuleHandlerTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private DataPermissionRuleHandler handler;\n\n    @Mock\n    private DataPermissionRuleFactory ruleFactory;\n\n    private DataPermissionInterceptor interceptor;\n\n    @BeforeEach\n    public void setUp() {\n        interceptor = new DataPermissionInterceptor(handler);\n\n        // 租户的数据权限规则\n        DataPermissionRule tenantRule = new DataPermissionRule() {\n\n            private static final String COLUMN = \"tenant_id\";\n\n            @Override\n            public Set<String> getTableNames() {\n                return asSet(\"entity\", \"entity1\", \"entity2\", \"entity3\", \"t1\", \"t2\", \"sys_dict_item\", // 支持 MyBatis Plus 的单元测试\n                        \"t_user\", \"t_role\"); // 满足自己的单元测试\n            }\n\n            @Override\n            public Expression getExpression(String tableName, Alias tableAlias) {\n                Column column = MyBatisUtils.buildColumn(tableName, tableAlias, COLUMN);\n                LongValue value = new LongValue(1L);\n                return new EqualsTo(column, value);\n            }\n\n        };\n        // 部门的数据权限规则\n        DataPermissionRule deptRule = new DataPermissionRule() {\n\n            private static final String COLUMN = \"dept_id\";\n\n            @Override\n            public Set<String> getTableNames() {\n                return asSet(\"t_user\");  // 满足自己的单元测试\n            }\n\n            @Override\n            public Expression getExpression(String tableName, Alias tableAlias) {\n                Column column = MyBatisUtils.buildColumn(tableName, tableAlias, COLUMN);\n                ExpressionList<LongValue> values = new ExpressionList<>(new LongValue(10L),\n                        new LongValue(20L));\n                return new InExpression(column, new ParenthesedExpressionList((values)));\n            }\n\n        };\n        // 设置到上下文\n        when(ruleFactory.getDataPermissionRule(any())).thenReturn(Arrays.asList(tenantRule, deptRule));\n    }\n\n    @Test\n    void delete() {\n        assertSql(\"delete from entity where id = ?\",\n                \"DELETE FROM entity WHERE id = ? AND entity.tenant_id = 1\");\n    }\n\n    @Test\n    void update() {\n        assertSql(\"update entity set name = ? where id = ?\",\n                \"UPDATE entity SET name = ? WHERE id = ? AND entity.tenant_id = 1\");\n    }\n\n    @Test\n    void selectSingle() {\n        // 单表\n        assertSql(\"select * from entity where id = ?\",\n                \"SELECT * FROM entity WHERE id = ? AND entity.tenant_id = 1\");\n\n        assertSql(\"select * from entity where id = ? or name = ?\",\n                \"SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\",\n                \"SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1\");\n\n        /* not */\n        assertSql(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\",\n                \"SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND entity.tenant_id = 1\");\n    }\n\n    @Test\n    void selectSubSelectIn() {\n        /* in */\n        assertSql(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\",\n                \"SELECT * FROM entity e WHERE e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n        // 在最前\n        assertSql(\"SELECT * FROM entity e WHERE e.id IN \" +\n                        \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n                \"SELECT * FROM entity e WHERE e.id IN \" +\n                        \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1\");\n        // 在最后\n        assertSql(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                        \"(select e1.id from entity1 e1 where e1.id = ?)\",\n                \"SELECT * FROM entity e WHERE e.id = ? AND e.id IN \" +\n                        \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n        // 在中间\n        assertSql(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                        \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n                \"SELECT * FROM entity e WHERE e.id = ? AND e.id IN \" +\n                        \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1\");\n    }\n\n    @Test\n    void selectSubSelectEq() {\n        /* = */\n        assertSql(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\",\n                \"SELECT * FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n    }\n\n    @Test\n    void selectSubSelectInnerNotEq() {\n        /* inner not = */\n        assertSql(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n                \"SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1)) AND e.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\",\n                \"SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ?) AND e.tenant_id = 1\");\n    }\n\n    @Test\n    void selectSubSelectExists() {\n        /* EXISTS */\n        assertSql(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n                \"SELECT * FROM entity e WHERE EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n\n        /* NOT EXISTS */\n        assertSql(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n                \"SELECT * FROM entity e WHERE NOT EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n    }\n\n    @Test\n    void selectSubSelect() {\n        /* >= */\n        assertSql(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\",\n                \"SELECT * FROM entity e WHERE e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n\n        /* <= */\n        assertSql(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\",\n                \"SELECT * FROM entity e WHERE e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n\n        /* <> */\n        assertSql(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\",\n                \"SELECT * FROM entity e WHERE e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n    }\n\n    @Test\n    void selectFromSelect() {\n        assertSql(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n                \"SELECT * FROM (SELECT e.id FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1)\");\n    }\n\n    @Test\n    void selectBodySubSelect() {\n        assertSql(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\",\n                \"SELECT t1.col1, (SELECT t2.col2 FROM t2 t2 WHERE t1.col1 = t2.col1 AND t2.tenant_id = 1) FROM t1 t1 WHERE t1.tenant_id = 1\");\n    }\n\n    @Test\n    void selectLeftJoin() {\n        // left join\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"left join entity1 e1 on e1.id = e.id \" +\n                        \"WHERE e.id = ? OR e.name = ?\",\n                \"SELECT * FROM entity e \" +\n                        \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                        \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"left join entity1 e1 on e1.id = e.id \" +\n                        \"WHERE (e.id = ? OR e.name = ?)\",\n                \"SELECT * FROM entity e \" +\n                        \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                        \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"left join entity1 e1 on e1.id = e.id \" +\n                        \"left join entity2 e2 on e1.id = e2.id\",\n                \"SELECT * FROM entity e \" +\n                        \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                        \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 \" +\n                        \"WHERE e.tenant_id = 1\");\n    }\n\n    @Test\n    void selectRightJoin() {\n        // right join\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"right join entity1 e1 on e1.id = e.id\",\n                \"SELECT * FROM entity e \" +\n                        \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                        \"WHERE e1.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM with_as_1 e \" +\n                        \"right join entity1 e1 on e1.id = e.id\",\n                \"SELECT * FROM with_as_1 e \" +\n                        \"RIGHT JOIN entity1 e1 ON e1.id = e.id \" +\n                        \"WHERE e1.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"right join entity1 e1 on e1.id = e.id \" +\n                        \"WHERE e.id = ? OR e.name = ?\",\n                \"SELECT * FROM entity e \" +\n                        \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                        \"WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"right join entity1 e1 on e1.id = e.id \" +\n                        \"right join entity2 e2 on e1.id = e2.id \",\n                \"SELECT * FROM entity e \" +\n                        \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                        \"RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 \" +\n                        \"WHERE e2.tenant_id = 1\");\n    }\n\n    @Test\n    void selectMixJoin() {\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"right join entity1 e1 on e1.id = e.id \" +\n                        \"left join entity2 e2 on e1.id = e2.id\",\n                \"SELECT * FROM entity e \" +\n                        \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                        \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 \" +\n                        \"WHERE e1.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"left join entity1 e1 on e1.id = e.id \" +\n                        \"right join entity2 e2 on e1.id = e2.id\",\n                \"SELECT * FROM entity e \" +\n                        \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                        \"RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 \" +\n                        \"WHERE e2.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"left join entity1 e1 on e1.id = e.id \" +\n                        \"inner join entity2 e2 on e1.id = e2.id\",\n                \"SELECT * FROM entity e \" +\n                        \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                        \"INNER JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 AND e2.tenant_id = 1\");\n    }\n\n\n    @Test\n    void selectJoinSubSelect() {\n        assertSql(\"select * from (select * from entity) e1 \" +\n                        \"left join entity2 e2 on e1.id = e2.id\",\n                \"SELECT * FROM (SELECT * FROM entity WHERE entity.tenant_id = 1) e1 \" +\n                        \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1\");\n\n        assertSql(\"select * from entity1 e1 \" +\n                        \"left join (select * from entity2) e2 \" +\n                        \"on e1.id = e2.id\",\n                \"SELECT * FROM entity1 e1 \" +\n                        \"LEFT JOIN (SELECT * FROM entity2 WHERE entity2.tenant_id = 1) e2 \" +\n                        \"ON e1.id = e2.id \" +\n                        \"WHERE e1.tenant_id = 1\");\n    }\n\n    @Test\n    void selectSubJoin() {\n\n        assertSql(\"select * FROM \" +\n                        \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\",\n                \"SELECT * FROM \" +\n                        \"(entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) \" +\n                        \"WHERE e2.tenant_id = 1\");\n\n        assertSql(\"select * FROM \" +\n                        \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\",\n                \"SELECT * FROM \" +\n                        \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                        \"WHERE e1.tenant_id = 1\");\n\n\n        assertSql(\"select * FROM \" +\n                        \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n                        \"right join entity3 e3 on e1.id = e3.id\",\n                \"SELECT * FROM \" +\n                        \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                        \"RIGHT JOIN entity3 e3 ON e1.id = e3.id AND e1.tenant_id = 1 \" +\n                        \"WHERE e3.tenant_id = 1\");\n\n\n        assertSql(\"select * FROM entity e \" +\n                        \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n                        \"on e.id = e2.id\",\n                \"SELECT * FROM entity e \" +\n                        \"LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) \" +\n                        \"ON e.id = e2.id AND e2.tenant_id = 1 \" +\n                        \"WHERE e.tenant_id = 1\");\n\n        assertSql(\"select * FROM entity e \" +\n                        \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                        \"on e.id = e2.id\",\n                \"SELECT * FROM entity e \" +\n                        \"LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                        \"ON e.id = e2.id AND e1.tenant_id = 1 \" +\n                        \"WHERE e.tenant_id = 1\");\n\n        assertSql(\"select * FROM entity e \" +\n                        \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                        \"on e.id = e2.id\",\n                \"SELECT * FROM entity e \" +\n                        \"RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                        \"ON e.id = e2.id AND e.tenant_id = 1 \" +\n                        \"WHERE e1.tenant_id = 1\");\n    }\n\n\n    @Test\n    void selectLeftJoinMultipleTrailingOn() {\n        // 多个 on 尾缀的\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"LEFT JOIN entity1 e1 \" +\n                        \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n                        \"ON e1.id = e.id \" +\n                        \"WHERE (e.id = ? OR e.NAME = ?)\",\n                \"SELECT * FROM entity e \" +\n                        \"LEFT JOIN entity1 e1 \" +\n                        \"LEFT JOIN entity2 e2 ON e2.id = e1.id AND e2.tenant_id = 1 \" +\n                        \"ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                        \"WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"LEFT JOIN entity1 e1 \" +\n                        \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                        \"ON e1.id = e.id \" +\n                        \"WHERE (e.id = ? OR e.NAME = ?)\",\n                \"SELECT * FROM entity e \" +\n                        \"LEFT JOIN entity1 e1 \" +\n                        \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                        \"ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                        \"WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1\");\n    }\n\n    @Test\n    void selectInnerJoin() {\n        // inner join\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"inner join entity1 e1 on e1.id = e.id \" +\n                        \"WHERE e.id = ? OR e.name = ?\",\n                \"SELECT * FROM entity e \" +\n                        \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 \" +\n                        \"WHERE e.id = ? OR e.name = ?\");\n\n        assertSql(\"SELECT * FROM entity e \" +\n                        \"inner join entity1 e1 on e1.id = e.id \" +\n                        \"WHERE (e.id = ? OR e.name = ?)\",\n                \"SELECT * FROM entity e \" +\n                        \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 \" +\n                        \"WHERE (e.id = ? OR e.name = ?)\");\n\n        // 隐式内连接\n        assertSql(\"SELECT * FROM entity,entity1 \" +\n                        \"WHERE entity.id = entity1.id\",\n                \"SELECT * FROM entity, entity1 \" +\n                        \"WHERE entity.id = entity1.id AND entity.tenant_id = 1 AND entity1.tenant_id = 1\");\n\n        // 隐式内连接\n        assertSql(\"SELECT * FROM entity a, with_as_entity1 b \" +\n                        \"WHERE a.id = b.id\",\n                \"SELECT * FROM entity a, with_as_entity1 b \" +\n                        \"WHERE a.id = b.id AND a.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                        \"WHERE a.id = b.id\",\n                \"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                        \"WHERE a.id = b.id\");\n\n        // SubJoin with 隐式内连接\n        assertSql(\"SELECT * FROM (entity,entity1) \" +\n                        \"WHERE entity.id = entity1.id\",\n                \"SELECT * FROM (entity, entity1) \" +\n                        \"WHERE entity.id = entity1.id \" +\n                        \"AND entity.tenant_id = 1 AND entity1.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM ((entity,entity1),entity2) \" +\n                        \"WHERE entity.id = entity1.id and entity.id = entity2.id\",\n                \"SELECT * FROM ((entity, entity1), entity2) \" +\n                        \"WHERE entity.id = entity1.id AND entity.id = entity2.id \" +\n                        \"AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1\");\n\n        assertSql(\"SELECT * FROM (entity,(entity1,entity2)) \" +\n                        \"WHERE entity.id = entity1.id and entity.id = entity2.id\",\n                \"SELECT * FROM (entity, (entity1, entity2)) \" +\n                        \"WHERE entity.id = entity1.id AND entity.id = entity2.id \" +\n                        \"AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1\");\n\n        // 沙雕的括号写法\n        assertSql(\"SELECT * FROM (((entity,entity1))) \" +\n                        \"WHERE entity.id = entity1.id\",\n                \"SELECT * FROM (((entity, entity1))) \" +\n                        \"WHERE entity.id = entity1.id \" +\n                        \"AND entity.tenant_id = 1 AND entity1.tenant_id = 1\");\n\n    }\n\n\n    @Test\n    void selectWithAs() {\n        assertSql(\"with with_as_A as (select * from entity) select * from with_as_A\",\n                \"WITH with_as_A AS (SELECT * FROM entity WHERE entity.tenant_id = 1) SELECT * FROM with_as_A\");\n    }\n\n\n    @Test\n    void selectIgnoreTable() {\n        assertSql(\" SELECT dict.dict_code, item.item_text AS \\\"text\\\", item.item_value AS \\\"value\\\" FROM sys_dict_item item INNER JOIN sys_dict dict ON dict.id = item.dict_id WHERE dict.dict_code IN (1, 2, 3) AND item.item_value IN (1, 2, 3)\",\n                \"SELECT dict.dict_code, item.item_text AS \\\"text\\\", item.item_value AS \\\"value\\\" FROM sys_dict_item item INNER JOIN sys_dict dict ON dict.id = item.dict_id AND item.tenant_id = 1 WHERE dict.dict_code IN (1, 2, 3) AND item.item_value IN (1, 2, 3)\");\n    }\n\n    private void assertSql(String sql, String targetSql) {\n        assertEquals(targetSql, interceptor.parserSingle(sql, null));\n    }\n\n    // ========== 额外的测试 ==========\n\n    @Test\n    public void testSelectSingle() {\n        // 单表\n        assertSql(\"select * from t_user where id = ?\",\n                \"SELECT * FROM t_user WHERE id = ? AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)\");\n\n        assertSql(\"select * from t_user where id = ? or name = ?\",\n                \"SELECT * FROM t_user WHERE (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)\");\n\n        assertSql(\"SELECT * FROM t_user WHERE (id = ? OR name = ?)\",\n                \"SELECT * FROM t_user WHERE (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)\");\n\n        /* not */\n        assertSql(\"SELECT * FROM t_user WHERE not (id = ? OR name = ?)\",\n                \"SELECT * FROM t_user WHERE NOT (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)\");\n    }\n\n    @Test\n    public void testSelectLeftJoin() {\n        // left join\n        assertSql(\"SELECT * FROM t_user e \" +\n                        \"left join t_role e1 on e1.id = e.id \" +\n                        \"WHERE e.id = ? OR e.name = ?\",\n                \"SELECT * FROM t_user e \" +\n                        \"LEFT JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                        \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)\");\n\n        // 条件 e.id = ? OR e.name = ? 带括号\n        assertSql(\"SELECT * FROM t_user e \" +\n                        \"left join t_role e1 on e1.id = e.id \" +\n                        \"WHERE (e.id = ? OR e.name = ?)\",\n                \"SELECT * FROM t_user e \" +\n                        \"LEFT JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                        \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)\");\n    }\n\n    @Test\n    public void testSelectRightJoin() {\n        // right join\n        assertSql(\"SELECT * FROM t_user e \" +\n                        \"right join t_role e1 on e1.id = e.id \" +\n                        \"WHERE e.id = ? OR e.name = ?\",\n                \"SELECT * FROM t_user e \" +\n                        \"RIGHT JOIN t_role e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) \" +\n                        \"WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1\");\n\n        // 条件 e.id = ? OR e.name = ? 带括号\n        assertSql(\"SELECT * FROM t_user e \" +\n                        \"right join t_role e1 on e1.id = e.id \" +\n                        \"WHERE (e.id = ? OR e.name = ?)\",\n                \"SELECT * FROM t_user e \" +\n                        \"RIGHT JOIN t_role e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) \" +\n                        \"WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1\");\n    }\n\n    @Test\n    public void testSelectInnerJoin() {\n        // inner join\n        assertSql(\"SELECT * FROM t_user e \" +\n                        \"inner join entity1 e1 on e1.id = e.id \" +\n                        \"WHERE e.id = ? OR e.name = ?\",\n                \"SELECT * FROM t_user e \" +\n                        \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) AND e1.tenant_id = 1 \" +\n                        \"WHERE e.id = ? OR e.name = ?\");\n\n        // 条件 e.id = ? OR e.name = ? 带括号\n        assertSql(\"SELECT * FROM t_user e \" +\n                        \"inner join entity1 e1 on e1.id = e.id \" +\n                        \"WHERE (e.id = ? OR e.name = ?)\",\n                \"SELECT * FROM t_user e \" +\n                        \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) AND e1.tenant_id = 1 \" +\n                        \"WHERE (e.id = ? OR e.name = ?)\");\n\n        // 没有 On 的 inner join\n        assertSql(\"SELECT * FROM entity,entity1 \" +\n                \"WHERE entity.id = entity1.id\",\n            \"SELECT * FROM entity, entity1 \" +\n                    \"WHERE entity.id = entity1.id AND entity.tenant_id = 1 AND entity1.tenant_id = 1\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImplTest.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.rule;\n\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport net.sf.jsqlparser.expression.Alias;\nimport net.sf.jsqlparser.expression.Expression;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Spy;\nimport org.springframework.core.annotation.AnnotationUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link DataPermissionRuleFactoryImpl} 单元测试\n *\n * @author 芋道源码\n */\nclass DataPermissionRuleFactoryImplTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private DataPermissionRuleFactoryImpl dataPermissionRuleFactory;\n\n    @Spy\n    private List<DataPermissionRule> rules = Arrays.asList(new DataPermissionRule01(),\n            new DataPermissionRule02());\n\n    @BeforeEach\n    public void setUp() {\n        DataPermissionContextHolder.clear();\n    }\n\n    @Test\n    public void testGetDataPermissionRule_02() {\n        // 准备参数\n        String mappedStatementId = randomString();\n\n        // 调用\n        List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId);\n        // 断言\n        assertSame(rules, result);\n    }\n\n    @Test\n    public void testGetDataPermissionRule_03() {\n        // 准备参数\n        String mappedStatementId = randomString();\n        // mock 方法\n        DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass03.class, DataPermission.class));\n\n        // 调用\n        List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId);\n        // 断言\n        assertTrue(result.isEmpty());\n    }\n\n    @Test\n    public void testGetDataPermissionRule_04() {\n        // 准备参数\n        String mappedStatementId = randomString();\n        // mock 方法\n        DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass04.class, DataPermission.class));\n\n        // 调用\n        List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId);\n        // 断言\n        assertEquals(1, result.size());\n        assertEquals(DataPermissionRule01.class, result.get(0).getClass());\n    }\n\n    @Test\n    public void testGetDataPermissionRule_05() {\n        // 准备参数\n        String mappedStatementId = randomString();\n        // mock 方法\n        DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass05.class, DataPermission.class));\n\n        // 调用\n        List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId);\n        // 断言\n        assertEquals(1, result.size());\n        assertEquals(DataPermissionRule02.class, result.get(0).getClass());\n    }\n\n    @Test\n    public void testGetDataPermissionRule_06() {\n        // 准备参数\n        String mappedStatementId = randomString();\n        // mock 方法\n        DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass06.class, DataPermission.class));\n\n        // 调用\n        List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId);\n        // 断言\n        assertSame(rules, result);\n    }\n\n    @DataPermission(enable = false)\n    static class TestClass03 {}\n\n    @DataPermission(includeRules = DataPermissionRule01.class)\n    static class TestClass04 {}\n\n    @DataPermission(excludeRules = DataPermissionRule01.class)\n    static class TestClass05 {}\n\n    @DataPermission\n    static class TestClass06 {}\n\n    static class DataPermissionRule01 implements DataPermissionRule {\n\n        @Override\n        public Set<String> getTableNames() {\n            return null;\n        }\n\n        @Override\n        public Expression getExpression(String tableName, Alias tableAlias) {\n            return null;\n        }\n\n    }\n\n    static class DataPermissionRule02 implements DataPermissionRule {\n\n        @Override\n        public Set<String> getTableNames() {\n            return null;\n        }\n\n        @Override\n        public Expression getExpression(String tableName, Alias tableAlias) {\n            return null;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.rule.dept;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;\nimport cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport net.sf.jsqlparser.expression.Alias;\nimport net.sf.jsqlparser.expression.Expression;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\n\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.Mockito.mockStatic;\nimport static org.mockito.Mockito.when;\n\n/**\n * {@link DeptDataPermissionRule} 的单元测试\n *\n * @author 芋道源码\n */\nclass DeptDataPermissionRuleTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private DeptDataPermissionRule rule;\n\n    @Mock\n    private PermissionCommonApi permissionApi;\n\n    @BeforeEach\n    @SuppressWarnings(\"unchecked\")\n    public void setUp() {\n        // 清空 rule\n        rule.getTableNames().clear();\n        ((Map<String, String>) ReflectUtil.getFieldValue(rule, \"deptColumns\")).clear();\n        ((Map<String, String>) ReflectUtil.getFieldValue(rule, \"deptColumns\")).clear();\n    }\n\n    @Test // 无 LoginUser\n    public void testGetExpression_noLoginUser() {\n        // 准备参数\n        String tableName = randomString();\n        Alias tableAlias = new Alias(randomString());\n        // mock 方法\n\n        // 调用\n        Expression expression = rule.getExpression(tableName, tableAlias);\n        // 断言\n        assertNull(expression);\n    }\n\n    @Test // 无数据权限时\n    public void testGetExpression_noDeptDataPermission() {\n        try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock\n                     = mockStatic(SecurityFrameworkUtils.class)) {\n            // 准备参数\n            String tableName = \"t_user\";\n            Alias tableAlias = new Alias(\"u\");\n            // mock 方法\n            LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)\n                    .setUserType(UserTypeEnum.ADMIN.getValue()));\n            securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);\n            // mock 方法（permissionApi 返回 null）\n            when(permissionApi.getDeptDataPermission(eq(loginUser.getId()))).thenReturn(success(null));\n\n            // 调用\n            NullPointerException exception = assertThrows(NullPointerException.class,\n                    () -> rule.getExpression(tableName, tableAlias));\n            // 断言\n            assertEquals(\"LoginUser(1) Table(t_user/u) 未返回数据权限\", exception.getMessage());\n        }\n    }\n\n    @Test // 全部数据权限\n    public void testGetExpression_allDeptDataPermission() {\n        try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock\n                     = mockStatic(SecurityFrameworkUtils.class)) {\n            // 准备参数\n            String tableName = \"t_user\";\n            Alias tableAlias = new Alias(\"u\");\n            // mock 方法（LoginUser）\n            LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)\n                    .setUserType(UserTypeEnum.ADMIN.getValue()));\n            securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);\n            // mock 方法（DeptDataPermissionRespDTO）\n            DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO().setAll(true);\n            when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));\n\n            // 调用\n            Expression expression = rule.getExpression(tableName, tableAlias);\n            // 断言\n            assertNull(expression);\n            assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));\n        }\n    }\n\n    @Test // 即不能查看部门，又不能查看自己，则说明 100% 无权限\n    public void testGetExpression_noDept_noSelf() {\n        try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock\n                     = mockStatic(SecurityFrameworkUtils.class)) {\n            // 准备参数\n            String tableName = \"t_user\";\n            Alias tableAlias = new Alias(\"u\");\n            // mock 方法（LoginUser）\n            LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)\n                    .setUserType(UserTypeEnum.ADMIN.getValue()));\n            securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);\n            // mock 方法（DeptDataPermissionRespDTO）\n            DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO();\n            when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));\n\n            // 调用\n            Expression expression = rule.getExpression(tableName, tableAlias);\n            // 断言\n            assertEquals(\"null = null\", expression.toString());\n            assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));\n        }\n    }\n\n    @Test // 拼接 Dept 和 User 的条件（字段都不符合）\n    public void testGetExpression_noDeptColumn_noSelfColumn() {\n        try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock\n                     = mockStatic(SecurityFrameworkUtils.class)) {\n            // 准备参数\n            String tableName = \"t_user\";\n            Alias tableAlias = new Alias(\"u\");\n            // mock 方法（LoginUser）\n            LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)\n                    .setUserType(UserTypeEnum.ADMIN.getValue()));\n            securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);\n            // mock 方法（DeptDataPermissionRespDTO）\n            DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()\n                    .setDeptIds(SetUtils.asSet(10L, 20L)).setSelf(true);\n            when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));\n\n            // 调用\n            Expression expression = rule.getExpression(tableName, tableAlias);\n            // 断言\n            assertEquals(\"null = null\", expression.toString());\n            assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));\n        }\n    }\n\n    @Test // 拼接 Dept 和 User 的条件（self 符合）\n    public void testGetExpression_noDeptColumn_yesSelfColumn() {\n        try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock\n                     = mockStatic(SecurityFrameworkUtils.class)) {\n            // 准备参数\n            String tableName = \"t_user\";\n            Alias tableAlias = new Alias(\"u\");\n            // mock 方法（LoginUser）\n            LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)\n                    .setUserType(UserTypeEnum.ADMIN.getValue()));\n            securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);\n            // mock 方法（DeptDataPermissionRespDTO）\n            DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()\n                    .setSelf(true);\n            when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));\n            // 添加 user 字段配置\n            rule.addUserColumn(\"t_user\", \"id\");\n\n            // 调用\n            Expression expression = rule.getExpression(tableName, tableAlias);\n            // 断言\n            assertEquals(\"u.id = 1\", expression.toString());\n            assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));\n        }\n    }\n\n    @Test // 拼接 Dept 和 User 的条件（dept 符合）\n    public void testGetExpression_yesDeptColumn_noSelfColumn() {\n        try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock\n                     = mockStatic(SecurityFrameworkUtils.class)) {\n            // 准备参数\n            String tableName = \"t_user\";\n            Alias tableAlias = new Alias(\"u\");\n            // mock 方法（LoginUser）\n            LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)\n                    .setUserType(UserTypeEnum.ADMIN.getValue()));\n            securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);\n            // mock 方法（DeptDataPermissionRespDTO）\n            DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()\n                    .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L));\n            when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));\n            // 添加 dept 字段配置\n            rule.addDeptColumn(\"t_user\", \"dept_id\");\n\n            // 调用\n            Expression expression = rule.getExpression(tableName, tableAlias);\n            // 断言\n            assertEquals(\"u.dept_id IN (10, 20)\", expression.toString());\n            assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));\n        }\n    }\n\n    @Test // 拼接 Dept 和 User 的条件（dept + self 符合）\n    public void testGetExpression_yesDeptColumn_yesSelfColumn() {\n        try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock\n                     = mockStatic(SecurityFrameworkUtils.class)) {\n            // 准备参数\n            String tableName = \"t_user\";\n            Alias tableAlias = new Alias(\"u\");\n            // mock 方法（LoginUser）\n            LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)\n                    .setUserType(UserTypeEnum.ADMIN.getValue()));\n            securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);\n            // mock 方法（DeptDataPermissionRespDTO）\n            DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()\n                    .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)).setSelf(true);\n            when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));\n            // 添加 user 字段配置\n            rule.addUserColumn(\"t_user\", \"id\");\n            // 添加 dept 字段配置\n            rule.addDeptColumn(\"t_user\", \"dept_id\");\n\n            // 调用\n            Expression expression = rule.getExpression(tableName, tableAlias);\n            // 断言\n            assertEquals(\"(u.dept_id IN (10, 20) OR u.id = 1)\", expression.toString());\n            assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtilsTest.java",
    "content": "package cn.iocoder.yudao.framework.datapermission.core.util;\n\nimport cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\npublic class DataPermissionUtilsTest {\n\n    @Test\n    public void testExecuteIgnore() {\n        DataPermissionUtils.executeIgnore(() -> assertFalse(DataPermissionContextHolder.get().enable()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-ip/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>IP 拓展，支持如下功能：\n        1. IP 功能：查询 IP 对应的城市信息\n            基于 https://gitee.com/lionsoul/ip2region 实现\n        2. 城市功能：查询城市编码对应的城市信息\n            基于 https://github.com/modood/Administrative-divisions-of-China 实现\n    </description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- IP地址检索 -->\n        <dependency>\n            <groupId>org.lionsoul</groupId>\n            <artifactId>ip2region</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/Area.java",
    "content": "package cn.iocoder.yudao.framework.ip.core;\n\nimport cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;\nimport com.fasterxml.jackson.annotation.JsonBackReference;\nimport com.fasterxml.jackson.annotation.JsonManagedReference;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\n\nimport java.util.List;\n\n/**\n * 区域节点，包括国家、省份、城市、地区等信息\n *\n * 数据可见 resources/area.csv 文件\n *\n * @author 芋道源码\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@ToString(exclude = {\"parent\"}) // 参见 https://gitee.com/yudaocode/yudao-cloud-mini/pulls/2 原因\npublic class Area {\n\n    /**\n     * 编号 - 全球，即根目录\n     */\n    public static final Integer ID_GLOBAL = 0;\n    /**\n     * 编号 - 中国\n     */\n    public static final Integer ID_CHINA = 1;\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 类型\n     *\n     * 枚举 {@link AreaTypeEnum}\n     */\n    private Integer type;\n\n    /**\n     * 父节点\n     */\n    @JsonManagedReference\n    private Area parent;\n    /**\n     * 子节点\n     */\n    @JsonBackReference\n    private List<Area> children;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/enums/AreaTypeEnum.java",
    "content": "package cn.iocoder.yudao.framework.ip.core.enums;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 区域类型枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum AreaTypeEnum implements ArrayValuable<Integer> {\n\n    COUNTRY(1, \"国家\"),\n    PROVINCE(2, \"省份\"),\n    CITY(3, \"城市\"),\n    DISTRICT(4, \"地区\"), // 县、镇、区等\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AreaTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtils.java",
    "content": "package cn.iocoder.yudao.framework.ip.core.utils;\n\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.text.csv.CsvRow;\nimport cn.hutool.core.text.csv.CsvUtil;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;\nimport lombok.NonNull;\nimport lombok.experimental.UtilityClass;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;\n\n/**\n * 区域工具类\n *\n * @author 芋道源码\n */\n@Slf4j\n@UtilityClass\npublic class AreaUtils {\n\n    /**\n     * Area 内存缓存，提升访问速度\n     */\n    private static Map<Integer, Area> areas;\n\n    static {\n        init();\n    }\n\n    /**\n     * 初始化\n     */\n    private static void init() {\n        try {\n            long now = System.currentTimeMillis();\n            areas = new HashMap<>();\n            areas.put(Area.ID_GLOBAL, new Area(Area.ID_GLOBAL, \"全球\", 0, null, new ArrayList<>()));\n            // 从 csv 中加载数据\n            List<CsvRow> rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader(\"area.csv\")).getRows();\n            rows.remove(0); // 删除 header\n            for (CsvRow row : rows) {\n                Area area = new Area(Integer.valueOf(row.get(0)), row.get(1), Integer.valueOf(row.get(2)), null, new ArrayList<>());\n                areas.put(area.getId(), area);\n            }\n\n            // 构建父子关系：因为 Area 中没有 parentId 字段,所以需要重复读取\n            for (CsvRow row : rows) {\n                Area area = areas.get(Integer.valueOf(row.get(0))); // 自己\n                Area parent = areas.get(Integer.valueOf(row.get(3))); // 父\n                Assert.isTrue(area != parent, \"{}:父子节点相同\", area.getName());\n                area.setParent(parent);\n                parent.getChildren().add(area);\n            }\n            log.info(\"启动加载 AreaUtils 成功，耗时 ({}) 毫秒\", System.currentTimeMillis() - now);\n        } catch (Exception e) {\n            throw new RuntimeException(\"AreaUtils 初始化失败\", e);\n        }\n    }\n\n    /**\n     * 获得指定编号对应的区域\n     *\n     * @param id 区域编号\n     * @return 区域\n     */\n    public static Area getArea(Integer id) {\n        return areas.get(id);\n    }\n\n    /**\n     * 获得指定区域对应的编号\n     *\n     * @param pathStr 区域路径，例如说：河南省/石家庄市/新华区\n     * @return 区域\n     */\n    public static Area parseArea(String pathStr) {\n        String[] paths = pathStr.split(\"/\");\n        Area area = null;\n        for (String path : paths) {\n            if (area == null) {\n                area = findFirst(areas.values(), item -> item.getName().equals(path));\n            } else {\n                area = findFirst(area.getChildren(), item -> item.getName().equals(path));\n            }\n        }\n        return area;\n    }\n\n    /**\n     * 获取所有节点的全路径名称如：河南省/石家庄市/新华区\n     *\n     * @param areas 地区树\n     * @return 所有节点的全路径名称\n     */\n    public static List<String> getAreaNodePathList(List<Area> areas) {\n        List<String> paths = new ArrayList<>();\n        areas.forEach(area -> getAreaNodePathList(area, \"\", paths));\n        return paths;\n    }\n\n    /**\n     * 构建一棵树的所有节点的全路径名称，并将其存储为 \"祖先/父级/子级\" 的形式\n     *\n     * @param node  父节点\n     * @param path  全路径名称\n     * @param paths 全路径名称列表，省份/城市/地区\n     */\n    private static void getAreaNodePathList(Area node, String path, List<String> paths) {\n        if (node == null) {\n            return;\n        }\n        // 构建当前节点的路径\n        String currentPath = path.isEmpty() ? node.getName() : path + \"/\" + node.getName();\n        paths.add(currentPath);\n        // 递归遍历子节点\n        for (Area child : node.getChildren()) {\n            getAreaNodePathList(child, currentPath, paths);\n        }\n    }\n\n    /**\n     * 格式化区域\n     *\n     * @param id 区域编号\n     * @return 格式化后的区域\n     */\n    public static String format(Integer id) {\n        return format(id, \" \");\n    }\n\n    /**\n     * 格式化区域\n     *\n     * 例如说：\n     * 1. id = “静安区”时：上海 上海市 静安区\n     * 2. id = “上海市”时：上海 上海市\n     * 3. id = “上海”时：上海\n     * 4. id = “美国”时：美国\n     * 当区域在中国时，默认不显示中国\n     *\n     * @param id        区域编号\n     * @param separator 分隔符\n     * @return 格式化后的区域\n     */\n    public static String format(Integer id, String separator) {\n        // 获得区域\n        Area area = areas.get(id);\n        if (area == null) {\n            return null;\n        }\n\n        // 格式化\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < AreaTypeEnum.values().length; i++) { // 避免死循环\n            sb.insert(0, area.getName());\n            // “递归”父节点\n            area = area.getParent();\n            if (area == null\n                    || ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况\n                break;\n            }\n            sb.insert(0, separator);\n        }\n        return sb.toString();\n    }\n\n    /**\n     * 获取指定类型的区域列表\n     *\n     * @param type 区域类型\n     * @param func 转换函数\n     * @param <T>  结果类型\n     * @return 区域列表\n     */\n    public static <T> List<T> getByType(AreaTypeEnum type, Function<Area, T> func) {\n        return convertList(areas.values(), func, area -> type.getType().equals(area.getType()));\n    }\n\n    /**\n     * 根据区域编号、上级区域类型，获取上级区域编号\n     *\n     * @param id   区域编号\n     * @param type 区域类型\n     * @return 上级区域编号\n     */\n    public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {\n        for (int i = 0; i < Byte.MAX_VALUE; i++) {\n            Area area = AreaUtils.getArea(id);\n            if (area == null) {\n                return null;\n            }\n            // 情况一：匹配到，返回它\n            if (type.getType().equals(area.getType())) {\n                return area.getId();\n            }\n            // 情况二：找到根节点，返回空\n            if (area.getParent() == null || area.getParent().getId() == null) {\n                return null;\n            }\n            // 其它：继续向上查找\n            id = area.getParent().getId();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/utils/IPUtils.java",
    "content": "package cn.iocoder.yudao.framework.ip.core.utils;\n\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport lombok.SneakyThrows;\nimport lombok.experimental.UtilityClass;\nimport lombok.extern.slf4j.Slf4j;\nimport org.lionsoul.ip2region.xdb.Searcher;\n\n/**\n * IP 工具类\n *\n * IP 数据源来自 ip2region.xdb 精简版，基于 <a href=\"https://gitee.com/zhijiantianya/ip2region\"/> 项目\n *\n * @author wanglhup\n */\n@Slf4j\n@UtilityClass\npublic class IPUtils {\n\n    /**\n     * IP 查询器，启动加载到内存中\n     */\n    private static Searcher SEARCHER;\n\n    static {\n        init();\n    }\n\n    /**\n     * 初始化\n     */\n    private static void init() {\n        try {\n            long now = System.currentTimeMillis();\n            byte[] bytes = ResourceUtil.readBytes(\"ip2region.xdb\");\n            SEARCHER = Searcher.newWithBuffer(bytes);\n            log.info(\"启动加载 IPUtils 成功，耗时 ({}) 毫秒\", System.currentTimeMillis() - now);\n        } catch (Exception e) {\n            throw new RuntimeException(\"IPUtils 初始化失败\", e);\n        }\n    }\n\n    /**\n     * 查询 IP 对应的地区编号\n     *\n     * @param ip IP 地址，格式为 127.0.0.1\n     * @return 地区id\n     */\n    @SneakyThrows\n    public static Integer getAreaId(String ip) {\n        return Integer.parseInt(SEARCHER.search(ip.trim()));\n    }\n\n    /**\n     * 查询 IP 对应的地区编号\n     *\n     * @param ip IP 地址的时间戳，格式参考{@link Searcher#checkIP(String)} 的返回\n     * @return 地区编号\n     */\n    @SneakyThrows\n    public static Integer getAreaId(long ip) {\n        return Integer.parseInt(SEARCHER.search(ip));\n    }\n\n    /**\n     * 查询 IP 对应的地区\n     *\n     * @param ip IP 地址，格式为 127.0.0.1\n     * @return 地区\n     */\n    public static Area getArea(String ip) {\n        return AreaUtils.getArea(getAreaId(ip));\n    }\n\n    /**\n     * 查询 IP 对应的地区\n     *\n     * @param ip IP 地址的时间戳，格式参考{@link Searcher#checkIP(String)} 的返回\n     * @return 地区\n     */\n    public static Area getArea(long ip) {\n        return AreaUtils.getArea(getAreaId(ip));\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/package-info.java",
    "content": "/**\n * IP 拓展，支持如下功能：\n *\n * 1. IP 功能：查询 IP 对应的城市信息\n *      基于 https://gitee.com/lionsoul/ip2region 实现\n * 2. 城市功能：查询城市编码对应的城市信息\n *      基于 https://github.com/modood/Administrative-divisions-of-China 实现\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.framework.ip;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/resources/area.csv",
    "content": "id,name,type,parentId\n1,中国,1,0\n2,蒙古,1,0\n3,朝鲜,1,0\n4,韩国,1,0\n5,日本,1,0\n6,菲律宾,1,0\n7,越南,1,0\n8,老挝,1,0\n9,柬埔寨,1,0\n10,缅甸,1,0\n11,泰国,1,0\n12,马来西亚,1,0\n13,文莱,1,0\n14,新加坡,1,0\n15,印度尼西亚,1,0\n16,东帝汶,1,0\n17,尼泊尔,1,0\n18,不丹,1,0\n19,孟加拉国,1,0\n20,印度,1,0\n21,巴基斯坦,1,0\n22,斯里兰卡,1,0\n23,马尔代夫,1,0\n24,哈萨克斯坦,1,0\n25,吉尔吉斯斯坦,1,0\n26,塔吉克斯坦,1,0\n27,乌兹别克斯坦,1,0\n28,土库曼斯坦,1,0\n29,阿富汗,1,0\n30,伊拉克,1,0\n31,伊朗,1,0\n32,叙利亚,1,0\n33,约旦,1,0\n34,黎巴嫩,1,0\n35,以色列,1,0\n36,巴勒斯坦,1,0\n37,沙特阿拉伯,1,0\n38,巴林,1,0\n39,卡塔尔,1,0\n40,科威特,1,0\n41,阿拉伯联合酋长国,1,0\n42,阿曼,1,0\n43,也门,1,0\n44,格鲁吉亚,1,0\n45,亚美尼亚,1,0\n46,阿塞拜疆,1,0\n47,土耳其,1,0\n48,塞浦路斯,1,0\n49,芬兰,1,0\n50,瑞典,1,0\n51,挪威,1,0\n52,冰岛,1,0\n53,丹麦,1,0\n54,爱沙尼亚,1,0\n55,拉脱维亚,1,0\n56,立陶宛,1,0\n57,白俄罗斯,1,0\n58,俄罗斯,1,0\n59,乌克兰,1,0\n60,摩尔多瓦,1,0\n61,波兰,1,0\n62,捷克,1,0\n63,斯洛伐克,1,0\n64,匈牙利,1,0\n65,德国,1,0\n66,奥地利,1,0\n67,瑞士,1,0\n68,列支敦士登,1,0\n69,英国,1,0\n70,爱尔兰,1,0\n71,荷兰,1,0\n72,比利时,1,0\n73,卢森堡,1,0\n74,法国,1,0\n75,摩纳哥,1,0\n76,罗马尼亚,1,0\n77,保加利亚,1,0\n78,塞尔维亚,1,0\n79,马其顿,1,0\n80,阿尔巴尼亚,1,0\n81,希腊,1,0\n82,斯洛文尼亚,1,0\n83,克罗地亚,1,0\n84,波斯尼亚和墨塞哥维那,1,0\n85,意大利,1,0\n86,梵蒂冈,1,0\n87,圣马力诺,1,0\n88,马耳他,1,0\n89,西班牙,1,0\n90,葡萄牙,1,0\n91,安道尔共和国,1,0\n92,埃及,1,0\n93,利比亚,1,0\n94,苏丹,1,0\n95,突尼斯,1,0\n96,阿尔及利亚,1,0\n97,摩洛哥,1,0\n98,亚速尔群岛,1,0\n99,马德拉群岛,1,0\n100,埃塞俄比亚,1,0\n101,厄立特里亚,1,0\n102,索马里,1,0\n103,吉布提,1,0\n104,肯尼亚,1,0\n105,坦桑尼亚,1,0\n106,乌干达,1,0\n107,卢旺达,1,0\n108,布隆迪,1,0\n109,塞舌尔,1,0\n110,圣多美及普林西比,1,0\n111,塞内加尔,1,0\n112,冈比亚,1,0\n113,马里,1,0\n114,布基纳法索,1,0\n115,几内亚,1,0\n116,几内亚比绍,1,0\n117,佛得角,1,0\n118,塞拉利昂,1,0\n119,利比里亚,1,0\n120,科特迪瓦,1,0\n121,加纳,1,0\n122,多哥,1,0\n123,贝宁,1,0\n124,尼日尔,1,0\n125,加那利群岛,1,0\n126,赞比亚,1,0\n127,安哥拉,1,0\n128,津巴布韦,1,0\n129,马拉维,1,0\n130,莫桑比克,1,0\n131,博茨瓦纳,1,0\n132,纳米比亚,1,0\n133,南非,1,0\n134,斯威士兰,1,0\n135,莱索托,1,0\n136,马达加斯加,1,0\n137,科摩罗,1,0\n138,毛里求斯,1,0\n139,留尼旺,1,0\n140,圣赫勒拿,1,0\n141,澳大利亚,1,0\n142,新西兰,1,0\n143,巴布亚新几内亚,1,0\n144,所罗门群岛,1,0\n145,瓦努阿图共和国,1,0\n146,密克罗尼西亚,1,0\n147,马绍尔群岛,1,0\n148,帕劳,1,0\n149,瑙鲁,1,0\n150,基里巴斯,1,0\n151,图瓦卢,1,0\n152,萨摩亚,1,0\n153,斐济,1,0\n154,汤加,1,0\n155,库克群岛,1,0\n156,关岛,1,0\n157,新喀里多尼亚,1,0\n158,法属波利尼西亚,1,0\n159,皮特凯恩岛,1,0\n160,瓦利斯与富图纳,1,0\n161,纽埃,1,0\n162,托克劳,1,0\n163,美属萨摩亚,1,0\n164,北马里亚纳,1,0\n165,加拿大,1,0\n166,美国,1,0\n167,墨西哥,1,0\n168,格陵兰,1,0\n169,危地马拉,1,0\n170,伯利兹,1,0\n171,萨尔瓦多,1,0\n172,洪都拉斯,1,0\n173,尼加拉瓜,1,0\n174,哥斯达黎加,1,0\n175,巴拿马,1,0\n176,巴哈马,1,0\n177,古巴,1,0\n178,牙买加,1,0\n179,海地,1,0\n180,多米尼加共和国,1,0\n181,安提瓜和巴布达,1,0\n182,圣基茨和尼维斯,1,0\n183,多米尼克,1,0\n184,圣卢西亚,1,0\n185,圣文森特和格林纳丁斯,1,0\n186,格林纳达,1,0\n187,巴巴多斯,1,0\n188,特立尼达和多巴哥,1,0\n189,波多黎各,1,0\n190,英属维尔京群岛,1,0\n191,美属维尔京群岛,1,0\n192,安圭拉,1,0\n193,蒙特塞拉特岛,1,0\n194,瓜德罗普,1,0\n195,马提尼克,1,0\n196,荷属安的列斯,1,0\n197,阿鲁巴,1,0\n198,特克斯和凯科斯群岛,1,0\n199,开曼群岛,1,0\n200,百慕大,1,0\n201,哥伦比亚,1,0\n202,委内瑞拉,1,0\n203,圭亚那,1,0\n204,法属圭亚那,1,0\n205,苏里南,1,0\n206,厄瓜多尔,1,0\n207,秘鲁,1,0\n208,玻利维亚,1,0\n209,巴西,1,0\n210,智利,1,0\n211,阿根廷,1,0\n212,乌拉圭,1,0\n213,巴拉圭,1,0\n214,波黑,1,0\n215,直布罗陀,1,0\n216,新喀里多尼亚群岛,1,0\n217,瓦利斯和富图纳群岛,1,0\n218,泽西岛,1,0\n219,黑山,1,0\n220,英属马恩岛,1,0\n221,尼日利亚,1,0\n222,喀麦隆,1,0\n223,加蓬,1,0\n224,乍得,1,0\n225,刚果共和国,1,0\n226,中非共和国,1,0\n227,南苏丹,1,0\n228,赤道几内亚,1,0\n229,毛里塔尼亚,1,0\n230,刚果民主共和国,1,0\n231,留尼汪岛,1,0\n232,格陵兰岛,1,0\n233,法罗群岛,1,0\n234,根西岛,1,0\n235,百慕大群岛,1,0\n236,圣皮埃尔和密克隆群岛,1,0\n237,法属圣马丁,1,0\n238,奥兰群岛,1,0\n239,北马里亚纳群岛,1,0\n240,库拉索,1,0\n241,博内尔岛,1,0\n242,圣马丁岛,1,0\n243,圣巴泰勒米岛,1,0\n244,福克兰群岛,1,0\n245,圣多美和普林西比,1,0\n246,英属印度洋领地,1,0\n247,东萨摩亚,1,0\n248,诺福克岛,1,0\n110000,北京市,2,1\n120000,天津市,2,1\n130000,河北省,2,1\n140000,山西省,2,1\n150000,内蒙古自治区,2,1\n210000,辽宁省,2,1\n220000,吉林省,2,1\n230000,黑龙江省,2,1\n310000,上海市,2,1\n320000,江苏省,2,1\n330000,浙江省,2,1\n340000,安徽省,2,1\n350000,福建省,2,1\n360000,江西省,2,1\n370000,山东省,2,1\n410000,河南省,2,1\n420000,湖北省,2,1\n430000,湖南省,2,1\n440000,广东省,2,1\n450000,广西壮族自治区,2,1\n460000,海南省,2,1\n500000,重庆市,2,1\n510000,四川省,2,1\n520000,贵州省,2,1\n530000,云南省,2,1\n540000,西藏自治区,2,1\n610000,陕西省,2,1\n620000,甘肃省,2,1\n630000,青海省,2,1\n640000,宁夏回族自治区,2,1\n650000,新疆维吾尔自治区,2,1\n110100,北京市,3,110000\n120100,天津市,3,120000\n130100,石家庄市,3,130000\n130200,唐山市,3,130000\n130300,秦皇岛市,3,130000\n130400,邯郸市,3,130000\n130500,邢台市,3,130000\n130600,保定市,3,130000\n130700,张家口市,3,130000\n130800,承德市,3,130000\n130900,沧州市,3,130000\n131000,廊坊市,3,130000\n131100,衡水市,3,130000\n140100,太原市,3,140000\n140200,大同市,3,140000\n140300,阳泉市,3,140000\n140400,长治市,3,140000\n140500,晋城市,3,140000\n140600,朔州市,3,140000\n140700,晋中市,3,140000\n140800,运城市,3,140000\n140900,忻州市,3,140000\n141000,临汾市,3,140000\n141100,吕梁市,3,140000\n150100,呼和浩特市,3,150000\n150200,包头市,3,150000\n150300,乌海市,3,150000\n150400,赤峰市,3,150000\n150500,通辽市,3,150000\n150600,鄂尔多斯市,3,150000\n150700,呼伦贝尔市,3,150000\n150800,巴彦淖尔市,3,150000\n150900,乌兰察布市,3,150000\n152200,兴安盟,3,150000\n152500,锡林郭勒盟,3,150000\n152900,阿拉善盟,3,150000\n210100,沈阳市,3,210000\n210200,大连市,3,210000\n210300,鞍山市,3,210000\n210400,抚顺市,3,210000\n210500,本溪市,3,210000\n210600,丹东市,3,210000\n210700,锦州市,3,210000\n210800,营口市,3,210000\n210900,阜新市,3,210000\n211000,辽阳市,3,210000\n211100,盘锦市,3,210000\n211200,铁岭市,3,210000\n211300,朝阳市,3,210000\n211400,葫芦岛市,3,210000\n220100,长春市,3,220000\n220200,吉林市,3,220000\n220300,四平市,3,220000\n220400,辽源市,3,220000\n220500,通化市,3,220000\n220600,白山市,3,220000\n220700,松原市,3,220000\n220800,白城市,3,220000\n222400,延边朝鲜族自治州,3,220000\n230100,哈尔滨市,3,230000\n230200,齐齐哈尔市,3,230000\n230300,鸡西市,3,230000\n230400,鹤岗市,3,230000\n230500,双鸭山市,3,230000\n230600,大庆市,3,230000\n230700,伊春市,3,230000\n230800,佳木斯市,3,230000\n230900,七台河市,3,230000\n231000,牡丹江市,3,230000\n231100,黑河市,3,230000\n231200,绥化市,3,230000\n232700,大兴安岭地区,3,230000\n310100,上海市,3,310000\n320100,南京市,3,320000\n320200,无锡市,3,320000\n320300,徐州市,3,320000\n320400,常州市,3,320000\n320500,苏州市,3,320000\n320600,南通市,3,320000\n320700,连云港市,3,320000\n320800,淮安市,3,320000\n320900,盐城市,3,320000\n321000,扬州市,3,320000\n321100,镇江市,3,320000\n321200,泰州市,3,320000\n321300,宿迁市,3,320000\n330100,杭州市,3,330000\n330200,宁波市,3,330000\n330300,温州市,3,330000\n330400,嘉兴市,3,330000\n330500,湖州市,3,330000\n330600,绍兴市,3,330000\n330700,金华市,3,330000\n330800,衢州市,3,330000\n330900,舟山市,3,330000\n331000,台州市,3,330000\n331100,丽水市,3,330000\n340100,合肥市,3,340000\n340200,芜湖市,3,340000\n340300,蚌埠市,3,340000\n340400,淮南市,3,340000\n340500,马鞍山市,3,340000\n340600,淮北市,3,340000\n340700,铜陵市,3,340000\n340800,安庆市,3,340000\n341000,黄山市,3,340000\n341100,滁州市,3,340000\n341200,阜阳市,3,340000\n341300,宿州市,3,340000\n341500,六安市,3,340000\n341600,亳州市,3,340000\n341700,池州市,3,340000\n341800,宣城市,3,340000\n350100,福州市,3,350000\n350200,厦门市,3,350000\n350300,莆田市,3,350000\n350400,三明市,3,350000\n350500,泉州市,3,350000\n350600,漳州市,3,350000\n350700,南平市,3,350000\n350800,龙岩市,3,350000\n350900,宁德市,3,350000\n360100,南昌市,3,360000\n360200,景德镇市,3,360000\n360300,萍乡市,3,360000\n360400,九江市,3,360000\n360500,新余市,3,360000\n360600,鹰潭市,3,360000\n360700,赣州市,3,360000\n360800,吉安市,3,360000\n360900,宜春市,3,360000\n361000,抚州市,3,360000\n361100,上饶市,3,360000\n370100,济南市,3,370000\n370200,青岛市,3,370000\n370300,淄博市,3,370000\n370400,枣庄市,3,370000\n370500,东营市,3,370000\n370600,烟台市,3,370000\n370700,潍坊市,3,370000\n370800,济宁市,3,370000\n370900,泰安市,3,370000\n371000,威海市,3,370000\n371100,日照市,3,370000\n371300,临沂市,3,370000\n371400,德州市,3,370000\n371500,聊城市,3,370000\n371600,滨州市,3,370000\n371700,菏泽市,3,370000\n410100,郑州市,3,410000\n410200,开封市,3,410000\n410300,洛阳市,3,410000\n410400,平顶山市,3,410000\n410500,安阳市,3,410000\n410600,鹤壁市,3,410000\n410700,新乡市,3,410000\n410800,焦作市,3,410000\n410900,濮阳市,3,410000\n411000,许昌市,3,410000\n411100,漯河市,3,410000\n411200,三门峡市,3,410000\n411300,南阳市,3,410000\n411400,商丘市,3,410000\n411500,信阳市,3,410000\n411600,周口市,3,410000\n411700,驻马店市,3,410000\n419000,省直辖县级行政区划,3,410000\n420100,武汉市,3,420000\n420200,黄石市,3,420000\n420300,十堰市,3,420000\n420500,宜昌市,3,420000\n420600,襄阳市,3,420000\n420700,鄂州市,3,420000\n420800,荆门市,3,420000\n420900,孝感市,3,420000\n421000,荆州市,3,420000\n421100,黄冈市,3,420000\n421200,咸宁市,3,420000\n421300,随州市,3,420000\n422800,恩施土家族苗族自治州,3,420000\n429000,省直辖县级行政区划,3,420000\n430100,长沙市,3,430000\n430200,株洲市,3,430000\n430300,湘潭市,3,430000\n430400,衡阳市,3,430000\n430500,邵阳市,3,430000\n430600,岳阳市,3,430000\n430700,常德市,3,430000\n430800,张家界市,3,430000\n430900,益阳市,3,430000\n431000,郴州市,3,430000\n431100,永州市,3,430000\n431200,怀化市,3,430000\n431300,娄底市,3,430000\n433100,湘西土家族苗族自治州,3,430000\n440100,广州市,3,440000\n440200,韶关市,3,440000\n440300,深圳市,3,440000\n440400,珠海市,3,440000\n440500,汕头市,3,440000\n440600,佛山市,3,440000\n440700,江门市,3,440000\n440800,湛江市,3,440000\n440900,茂名市,3,440000\n441200,肇庆市,3,440000\n441300,惠州市,3,440000\n441400,梅州市,3,440000\n441500,汕尾市,3,440000\n441600,河源市,3,440000\n441700,阳江市,3,440000\n441800,清远市,3,440000\n441900,东莞市,3,440000\n441901,莞城区,4,441900\n441902,南城区,4,441900\n441904,万江区,4,441900\n441905,石碣镇,4,441900\n441906,石龙镇,4,441900\n441907,茶山镇,4,441900\n441908,石排镇,4,441900\n441909,企石镇,4,441900\n441910,横沥镇,4,441900\n441911,桥头镇,4,441900\n441912,谢岗镇,4,441900\n441913,东坑镇,4,441900\n441914,常平镇,4,441900\n441915,寮步镇,4,441900\n441916,大朗镇,4,441900\n441917,麻涌镇,4,441900\n441918,中堂镇,4,441900\n441919,高埗镇,4,441900\n441920,樟木头镇,4,441900\n441921,大岭山镇,4,441900\n441922,望牛墩镇,4,441900\n441923,黄江镇,4,441900\n441924,洪梅镇,4,441900\n441925,清溪镇,4,441900\n441926,沙田镇,4,441900\n441927,道滘镇,4,441900\n441928,塘厦镇,4,441900\n441929,虎门镇,4,441900\n441930,厚街镇,4,441900\n441931,凤岗镇,4,441900\n441932,长安镇,4,441900\n442000,中山市,3,440000\n442001,石岐街道,4,442000\n442002,东区街道,4,442000\n442003,中山港街道,4,442000\n442004,西区街道,4,442000\n442005,南区街道,4,442000\n442006,五桂山街道,4,442000\n442007,民众街道,4,442000\n442008,南朗街道,4,442000\n442009,黄圃镇,4,442000\n442010,东凤镇,4,442000\n442011,古镇镇,4,442000\n442012,沙溪镇,4,442000\n442013,坦洲镇,4,442000\n442014,港口镇,4,442000\n442015,三角镇,4,442000\n442016,横栏镇,4,442000\n442017,南头镇,4,442000\n442018,阜沙镇,4,442000\n442019,三乡镇,4,442000\n442020,板芙镇,4,442000\n442021,大涌镇,4,442000\n442022,神湾镇,4,442000\n442023,小榄镇,4,442000\n445100,潮州市,3,440000\n445200,揭阳市,3,440000\n445300,云浮市,3,440000\n450100,南宁市,3,450000\n450200,柳州市,3,450000\n450300,桂林市,3,450000\n450400,梧州市,3,450000\n450500,北海市,3,450000\n450600,防城港市,3,450000\n450700,钦州市,3,450000\n450800,贵港市,3,450000\n450900,玉林市,3,450000\n451000,百色市,3,450000\n451100,贺州市,3,450000\n451200,河池市,3,450000\n451300,来宾市,3,450000\n451400,崇左市,3,450000\n460100,海口市,3,460000\n460200,三亚市,3,460000\n460300,三沙市,3,460000\n460400,儋州市,3,460000\n469000,省直辖县级行政区划,3,460000\n500100,重庆市,3,500000\n510100,成都市,3,510000\n510300,自贡市,3,510000\n510400,攀枝花市,3,510000\n510500,泸州市,3,510000\n510600,德阳市,3,510000\n510700,绵阳市,3,510000\n510800,广元市,3,510000\n510900,遂宁市,3,510000\n511000,内江市,3,510000\n511100,乐山市,3,510000\n511300,南充市,3,510000\n511400,眉山市,3,510000\n511500,宜宾市,3,510000\n511600,广安市,3,510000\n511700,达州市,3,510000\n511800,雅安市,3,510000\n511900,巴中市,3,510000\n512000,资阳市,3,510000\n513200,阿坝藏族羌族自治州,3,510000\n513300,甘孜藏族自治州,3,510000\n513400,凉山彝族自治州,3,510000\n520100,贵阳市,3,520000\n520200,六盘水市,3,520000\n520300,遵义市,3,520000\n520400,安顺市,3,520000\n520500,毕节市,3,520000\n520600,铜仁市,3,520000\n522300,黔西南布依族苗族自治州,3,520000\n522600,黔东南苗族侗族自治州,3,520000\n522700,黔南布依族苗族自治州,3,520000\n530100,昆明市,3,530000\n530300,曲靖市,3,530000\n530400,玉溪市,3,530000\n530500,保山市,3,530000\n530600,昭通市,3,530000\n530700,丽江市,3,530000\n530800,普洱市,3,530000\n530900,临沧市,3,530000\n532300,楚雄彝族自治州,3,530000\n532500,红河哈尼族彝族自治州,3,530000\n532600,文山壮族苗族自治州,3,530000\n532800,西双版纳傣族自治州,3,530000\n532900,大理白族自治州,3,530000\n533100,德宏傣族景颇族自治州,3,530000\n533300,怒江傈僳族自治州,3,530000\n533400,迪庆藏族自治州,3,530000\n540100,拉萨市,3,540000\n540200,日喀则市,3,540000\n540300,昌都市,3,540000\n540400,林芝市,3,540000\n540500,山南市,3,540000\n540600,那曲市,3,540000\n542500,阿里地区,3,540000\n610100,西安市,3,610000\n610200,铜川市,3,610000\n610300,宝鸡市,3,610000\n610400,咸阳市,3,610000\n610500,渭南市,3,610000\n610600,延安市,3,610000\n610700,汉中市,3,610000\n610800,榆林市,3,610000\n610900,安康市,3,610000\n611000,商洛市,3,610000\n620100,兰州市,3,620000\n620200,嘉峪关市,3,620000\n620300,金昌市,3,620000\n620400,白银市,3,620000\n620500,天水市,3,620000\n620600,武威市,3,620000\n620700,张掖市,3,620000\n620800,平凉市,3,620000\n620900,酒泉市,3,620000\n621000,庆阳市,3,620000\n621100,定西市,3,620000\n621200,陇南市,3,620000\n622900,临夏回族自治州,3,620000\n623000,甘南藏族自治州,3,620000\n630100,西宁市,3,630000\n630200,海东市,3,630000\n632200,海北藏族自治州,3,630000\n632300,黄南藏族自治州,3,630000\n632500,海南藏族自治州,3,630000\n632600,果洛藏族自治州,3,630000\n632700,玉树藏族自治州,3,630000\n632800,海西蒙古族藏族自治州,3,630000\n640100,银川市,3,640000\n640200,石嘴山市,3,640000\n640300,吴忠市,3,640000\n640400,固原市,3,640000\n640500,中卫市,3,640000\n650100,乌鲁木齐市,3,650000\n650200,克拉玛依市,3,650000\n650400,吐鲁番市,3,650000\n650500,哈密市,3,650000\n652300,昌吉回族自治州,3,650000\n652700,博尔塔拉蒙古自治州,3,650000\n652800,巴音郭楞蒙古自治州,3,650000\n652900,阿克苏地区,3,650000\n653000,克孜勒苏柯尔克孜自治州,3,650000\n653100,喀什地区,3,650000\n653200,和田地区,3,650000\n654000,伊犁哈萨克自治州,3,650000\n654200,塔城地区,3,650000\n654300,阿勒泰地区,3,650000\n659000,自治区直辖县级行政区划,3,650000\n110101,东城区,4,110100\n110102,西城区,4,110100\n110105,朝阳区,4,110100\n110106,丰台区,4,110100\n110107,石景山区,4,110100\n110108,海淀区,4,110100\n110109,门头沟区,4,110100\n110111,房山区,4,110100\n110112,通州区,4,110100\n110113,顺义区,4,110100\n110114,昌平区,4,110100\n110115,大兴区,4,110100\n110116,怀柔区,4,110100\n110117,平谷区,4,110100\n110118,密云区,4,110100\n110119,延庆区,4,110100\n120101,和平区,4,120100\n120102,河东区,4,120100\n120103,河西区,4,120100\n120104,南开区,4,120100\n120105,河北区,4,120100\n120106,红桥区,4,120100\n120110,东丽区,4,120100\n120111,西青区,4,120100\n120112,津南区,4,120100\n120113,北辰区,4,120100\n120114,武清区,4,120100\n120115,宝坻区,4,120100\n120116,滨海新区,4,120100\n120117,宁河区,4,120100\n120118,静海区,4,120100\n120119,蓟州区,4,120100\n130102,长安区,4,130100\n130104,桥西区,4,130100\n130105,新华区,4,130100\n130107,井陉矿区,4,130100\n130108,裕华区,4,130100\n130109,藁城区,4,130100\n130110,鹿泉区,4,130100\n130111,栾城区,4,130100\n130121,井陉县,4,130100\n130123,正定县,4,130100\n130125,行唐县,4,130100\n130126,灵寿县,4,130100\n130127,高邑县,4,130100\n130128,深泽县,4,130100\n130129,赞皇县,4,130100\n130130,无极县,4,130100\n130131,平山县,4,130100\n130132,元氏县,4,130100\n130133,赵县,4,130100\n130171,石家庄高新技术产业开发区,4,130100\n130172,石家庄循环化工园区,4,130100\n130181,辛集市,4,130100\n130183,晋州市,4,130100\n130184,新乐市,4,130100\n130202,路南区,4,130200\n130203,路北区,4,130200\n130204,古冶区,4,130200\n130205,开平区,4,130200\n130207,丰南区,4,130200\n130208,丰润区,4,130200\n130209,曹妃甸区,4,130200\n130224,滦南县,4,130200\n130225,乐亭县,4,130200\n130227,迁西县,4,130200\n130229,玉田县,4,130200\n130271,河北唐山芦台经济开发区,4,130200\n130272,唐山市汉沽管理区,4,130200\n130273,唐山高新技术产业开发区,4,130200\n130274,河北唐山海港经济开发区,4,130200\n130281,遵化市,4,130200\n130283,迁安市,4,130200\n130284,滦州市,4,130200\n130302,海港区,4,130300\n130303,山海关区,4,130300\n130304,北戴河区,4,130300\n130306,抚宁区,4,130300\n130321,青龙满族自治县,4,130300\n130322,昌黎县,4,130300\n130324,卢龙县,4,130300\n130371,秦皇岛市经济技术开发区,4,130300\n130372,北戴河新区,4,130300\n130402,邯山区,4,130400\n130403,丛台区,4,130400\n130404,复兴区,4,130400\n130406,峰峰矿区,4,130400\n130407,肥乡区,4,130400\n130408,永年区,4,130400\n130423,临漳县,4,130400\n130424,成安县,4,130400\n130425,大名县,4,130400\n130426,涉县,4,130400\n130427,磁县,4,130400\n130430,邱县,4,130400\n130431,鸡泽县,4,130400\n130432,广平县,4,130400\n130433,馆陶县,4,130400\n130434,魏县,4,130400\n130435,曲周县,4,130400\n130471,邯郸经济技术开发区,4,130400\n130473,邯郸冀南新区,4,130400\n130481,武安市,4,130400\n130502,襄都区,4,130500\n130503,信都区,4,130500\n130505,任泽区,4,130500\n130506,南和区,4,130500\n130522,临城县,4,130500\n130523,内丘县,4,130500\n130524,柏乡县,4,130500\n130525,隆尧县,4,130500\n130528,宁晋县,4,130500\n130529,巨鹿县,4,130500\n130530,新河县,4,130500\n130531,广宗县,4,130500\n130532,平乡县,4,130500\n130533,威县,4,130500\n130534,清河县,4,130500\n130535,临西县,4,130500\n130571,河北邢台经济开发区,4,130500\n130581,南宫市,4,130500\n130582,沙河市,4,130500\n130602,竞秀区,4,130600\n130606,莲池区,4,130600\n130607,满城区,4,130600\n130608,清苑区,4,130600\n130609,徐水区,4,130600\n130623,涞水县,4,130600\n130624,阜平县,4,130600\n130626,定兴县,4,130600\n130627,唐县,4,130600\n130628,高阳县,4,130600\n130629,容城县,4,130600\n130630,涞源县,4,130600\n130631,望都县,4,130600\n130632,安新县,4,130600\n130633,易县,4,130600\n130634,曲阳县,4,130600\n130635,蠡县,4,130600\n130636,顺平县,4,130600\n130637,博野县,4,130600\n130638,雄县,4,130600\n130671,保定高新技术产业开发区,4,130600\n130672,保定白沟新城,4,130600\n130681,涿州市,4,130600\n130682,定州市,4,130600\n130683,安国市,4,130600\n130684,高碑店市,4,130600\n130702,桥东区,4,130700\n130703,桥西区,4,130700\n130705,宣化区,4,130700\n130706,下花园区,4,130700\n130708,万全区,4,130700\n130709,崇礼区,4,130700\n130722,张北县,4,130700\n130723,康保县,4,130700\n130724,沽源县,4,130700\n130725,尚义县,4,130700\n130726,蔚县,4,130700\n130727,阳原县,4,130700\n130728,怀安县,4,130700\n130730,怀来县,4,130700\n130731,涿鹿县,4,130700\n130732,赤城县,4,130700\n130771,张家口经济开发区,4,130700\n130772,张家口市察北管理区,4,130700\n130773,张家口市塞北管理区,4,130700\n130802,双桥区,4,130800\n130803,双滦区,4,130800\n130804,鹰手营子矿区,4,130800\n130821,承德县,4,130800\n130822,兴隆县,4,130800\n130824,滦平县,4,130800\n130825,隆化县,4,130800\n130826,丰宁满族自治县,4,130800\n130827,宽城满族自治县,4,130800\n130828,围场满族蒙古族自治县,4,130800\n130871,承德高新技术产业开发区,4,130800\n130881,平泉市,4,130800\n130902,新华区,4,130900\n130903,运河区,4,130900\n130921,沧县,4,130900\n130922,青县,4,130900\n130923,东光县,4,130900\n130924,海兴县,4,130900\n130925,盐山县,4,130900\n130926,肃宁县,4,130900\n130927,南皮县,4,130900\n130928,吴桥县,4,130900\n130929,献县,4,130900\n130930,孟村回族自治县,4,130900\n130971,河北沧州经济开发区,4,130900\n130972,沧州高新技术产业开发区,4,130900\n130973,沧州渤海新区,4,130900\n130981,泊头市,4,130900\n130982,任丘市,4,130900\n130983,黄骅市,4,130900\n130984,河间市,4,130900\n131002,安次区,4,131000\n131003,广阳区,4,131000\n131022,固安县,4,131000\n131023,永清县,4,131000\n131024,香河县,4,131000\n131025,大城县,4,131000\n131026,文安县,4,131000\n131028,大厂回族自治县,4,131000\n131071,廊坊经济技术开发区,4,131000\n131081,霸州市,4,131000\n131082,三河市,4,131000\n131102,桃城区,4,131100\n131103,冀州区,4,131100\n131121,枣强县,4,131100\n131122,武邑县,4,131100\n131123,武强县,4,131100\n131124,饶阳县,4,131100\n131125,安平县,4,131100\n131126,故城县,4,131100\n131127,景县,4,131100\n131128,阜城县,4,131100\n131171,河北衡水高新技术产业开发区,4,131100\n131172,衡水滨湖新区,4,131100\n131182,深州市,4,131100\n140105,小店区,4,140100\n140106,迎泽区,4,140100\n140107,杏花岭区,4,140100\n140108,尖草坪区,4,140100\n140109,万柏林区,4,140100\n140110,晋源区,4,140100\n140121,清徐县,4,140100\n140122,阳曲县,4,140100\n140123,娄烦县,4,140100\n140171,山西转型综合改革示范区,4,140100\n140181,古交市,4,140100\n140212,新荣区,4,140200\n140213,平城区,4,140200\n140214,云冈区,4,140200\n140215,云州区,4,140200\n140221,阳高县,4,140200\n140222,天镇县,4,140200\n140223,广灵县,4,140200\n140224,灵丘县,4,140200\n140225,浑源县,4,140200\n140226,左云县,4,140200\n140271,山西大同经济开发区,4,140200\n140302,城区,4,140300\n140303,矿区,4,140300\n140311,郊区,4,140300\n140321,平定县,4,140300\n140322,盂县,4,140300\n140403,潞州区,4,140400\n140404,上党区,4,140400\n140405,屯留区,4,140400\n140406,潞城区,4,140400\n140423,襄垣县,4,140400\n140425,平顺县,4,140400\n140426,黎城县,4,140400\n140427,壶关县,4,140400\n140428,长子县,4,140400\n140429,武乡县,4,140400\n140430,沁县,4,140400\n140431,沁源县,4,140400\n140471,山西长治高新技术产业园区,4,140400\n140502,城区,4,140500\n140521,沁水县,4,140500\n140522,阳城县,4,140500\n140524,陵川县,4,140500\n140525,泽州县,4,140500\n140581,高平市,4,140500\n140602,朔城区,4,140600\n140603,平鲁区,4,140600\n140621,山阴县,4,140600\n140622,应县,4,140600\n140623,右玉县,4,140600\n140671,山西朔州经济开发区,4,140600\n140681,怀仁市,4,140600\n140702,榆次区,4,140700\n140703,太谷区,4,140700\n140721,榆社县,4,140700\n140722,左权县,4,140700\n140723,和顺县,4,140700\n140724,昔阳县,4,140700\n140725,寿阳县,4,140700\n140727,祁县,4,140700\n140728,平遥县,4,140700\n140729,灵石县,4,140700\n140781,介休市,4,140700\n140802,盐湖区,4,140800\n140821,临猗县,4,140800\n140822,万荣县,4,140800\n140823,闻喜县,4,140800\n140824,稷山县,4,140800\n140825,新绛县,4,140800\n140826,绛县,4,140800\n140827,垣曲县,4,140800\n140828,夏县,4,140800\n140829,平陆县,4,140800\n140830,芮城县,4,140800\n140881,永济市,4,140800\n140882,河津市,4,140800\n140902,忻府区,4,140900\n140921,定襄县,4,140900\n140922,五台县,4,140900\n140923,代县,4,140900\n140924,繁峙县,4,140900\n140925,宁武县,4,140900\n140926,静乐县,4,140900\n140927,神池县,4,140900\n140928,五寨县,4,140900\n140929,岢岚县,4,140900\n140930,河曲县,4,140900\n140931,保德县,4,140900\n140932,偏关县,4,140900\n140971,五台山风景名胜区,4,140900\n140981,原平市,4,140900\n141002,尧都区,4,141000\n141021,曲沃县,4,141000\n141022,翼城县,4,141000\n141023,襄汾县,4,141000\n141024,洪洞县,4,141000\n141025,古县,4,141000\n141026,安泽县,4,141000\n141027,浮山县,4,141000\n141028,吉县,4,141000\n141029,乡宁县,4,141000\n141030,大宁县,4,141000\n141031,隰县,4,141000\n141032,永和县,4,141000\n141033,蒲县,4,141000\n141034,汾西县,4,141000\n141081,侯马市,4,141000\n141082,霍州市,4,141000\n141102,离石区,4,141100\n141121,文水县,4,141100\n141122,交城县,4,141100\n141123,兴县,4,141100\n141124,临县,4,141100\n141125,柳林县,4,141100\n141126,石楼县,4,141100\n141127,岚县,4,141100\n141128,方山县,4,141100\n141129,中阳县,4,141100\n141130,交口县,4,141100\n141181,孝义市,4,141100\n141182,汾阳市,4,141100\n150102,新城区,4,150100\n150103,回民区,4,150100\n150104,玉泉区,4,150100\n150105,赛罕区,4,150100\n150121,土默特左旗,4,150100\n150122,托克托县,4,150100\n150123,和林格尔县,4,150100\n150124,清水河县,4,150100\n150125,武川县,4,150100\n150172,呼和浩特经济技术开发区,4,150100\n150202,东河区,4,150200\n150203,昆都仑区,4,150200\n150204,青山区,4,150200\n150205,石拐区,4,150200\n150206,白云鄂博矿区,4,150200\n150207,九原区,4,150200\n150221,土默特右旗,4,150200\n150222,固阳县,4,150200\n150223,达尔罕茂明安联合旗,4,150200\n150271,包头稀土高新技术产业开发区,4,150200\n150302,海勃湾区,4,150300\n150303,海南区,4,150300\n150304,乌达区,4,150300\n150402,红山区,4,150400\n150403,元宝山区,4,150400\n150404,松山区,4,150400\n150421,阿鲁科尔沁旗,4,150400\n150422,巴林左旗,4,150400\n150423,巴林右旗,4,150400\n150424,林西县,4,150400\n150425,克什克腾旗,4,150400\n150426,翁牛特旗,4,150400\n150428,喀喇沁旗,4,150400\n150429,宁城县,4,150400\n150430,敖汉旗,4,150400\n150502,科尔沁区,4,150500\n150521,科尔沁左翼中旗,4,150500\n150522,科尔沁左翼后旗,4,150500\n150523,开鲁县,4,150500\n150524,库伦旗,4,150500\n150525,奈曼旗,4,150500\n150526,扎鲁特旗,4,150500\n150571,通辽经济技术开发区,4,150500\n150581,霍林郭勒市,4,150500\n150602,东胜区,4,150600\n150603,康巴什区,4,150600\n150621,达拉特旗,4,150600\n150622,准格尔旗,4,150600\n150623,鄂托克前旗,4,150600\n150624,鄂托克旗,4,150600\n150625,杭锦旗,4,150600\n150626,乌审旗,4,150600\n150627,伊金霍洛旗,4,150600\n150702,海拉尔区,4,150700\n150703,扎赉诺尔区,4,150700\n150721,阿荣旗,4,150700\n150722,莫力达瓦达斡尔族自治旗,4,150700\n150723,鄂伦春自治旗,4,150700\n150724,鄂温克族自治旗,4,150700\n150725,陈巴尔虎旗,4,150700\n150726,新巴尔虎左旗,4,150700\n150727,新巴尔虎右旗,4,150700\n150781,满洲里市,4,150700\n150782,牙克石市,4,150700\n150783,扎兰屯市,4,150700\n150784,额尔古纳市,4,150700\n150785,根河市,4,150700\n150802,临河区,4,150800\n150821,五原县,4,150800\n150822,磴口县,4,150800\n150823,乌拉特前旗,4,150800\n150824,乌拉特中旗,4,150800\n150825,乌拉特后旗,4,150800\n150826,杭锦后旗,4,150800\n150902,集宁区,4,150900\n150921,卓资县,4,150900\n150922,化德县,4,150900\n150923,商都县,4,150900\n150924,兴和县,4,150900\n150925,凉城县,4,150900\n150926,察哈尔右翼前旗,4,150900\n150927,察哈尔右翼中旗,4,150900\n150928,察哈尔右翼后旗,4,150900\n150929,四子王旗,4,150900\n150981,丰镇市,4,150900\n152201,乌兰浩特市,4,152200\n152202,阿尔山市,4,152200\n152221,科尔沁右翼前旗,4,152200\n152222,科尔沁右翼中旗,4,152200\n152223,扎赉特旗,4,152200\n152224,突泉县,4,152200\n152501,二连浩特市,4,152500\n152502,锡林浩特市,4,152500\n152522,阿巴嘎旗,4,152500\n152523,苏尼特左旗,4,152500\n152524,苏尼特右旗,4,152500\n152525,东乌珠穆沁旗,4,152500\n152526,西乌珠穆沁旗,4,152500\n152527,太仆寺旗,4,152500\n152528,镶黄旗,4,152500\n152529,正镶白旗,4,152500\n152530,正蓝旗,4,152500\n152531,多伦县,4,152500\n152571,乌拉盖管委会,4,152500\n152921,阿拉善左旗,4,152900\n152922,阿拉善右旗,4,152900\n152923,额济纳旗,4,152900\n152971,内蒙古阿拉善高新技术产业开发区,4,152900\n210102,和平区,4,210100\n210103,沈河区,4,210100\n210104,大东区,4,210100\n210105,皇姑区,4,210100\n210106,铁西区,4,210100\n210111,苏家屯区,4,210100\n210112,浑南区,4,210100\n210113,沈北新区,4,210100\n210114,于洪区,4,210100\n210115,辽中区,4,210100\n210123,康平县,4,210100\n210124,法库县,4,210100\n210181,新民市,4,210100\n210202,中山区,4,210200\n210203,西岗区,4,210200\n210204,沙河口区,4,210200\n210211,甘井子区,4,210200\n210212,旅顺口区,4,210200\n210213,金州区,4,210200\n210214,普兰店区,4,210200\n210224,长海县,4,210200\n210281,瓦房店市,4,210200\n210283,庄河市,4,210200\n210302,铁东区,4,210300\n210303,铁西区,4,210300\n210304,立山区,4,210300\n210311,千山区,4,210300\n210321,台安县,4,210300\n210323,岫岩满族自治县,4,210300\n210381,海城市,4,210300\n210402,新抚区,4,210400\n210403,东洲区,4,210400\n210404,望花区,4,210400\n210411,顺城区,4,210400\n210421,抚顺县,4,210400\n210422,新宾满族自治县,4,210400\n210423,清原满族自治县,4,210400\n210502,平山区,4,210500\n210503,溪湖区,4,210500\n210504,明山区,4,210500\n210505,南芬区,4,210500\n210521,本溪满族自治县,4,210500\n210522,桓仁满族自治县,4,210500\n210602,元宝区,4,210600\n210603,振兴区,4,210600\n210604,振安区,4,210600\n210624,宽甸满族自治县,4,210600\n210681,东港市,4,210600\n210682,凤城市,4,210600\n210702,古塔区,4,210700\n210703,凌河区,4,210700\n210711,太和区,4,210700\n210726,黑山县,4,210700\n210727,义县,4,210700\n210781,凌海市,4,210700\n210782,北镇市,4,210700\n210802,站前区,4,210800\n210803,西市区,4,210800\n210804,鲅鱼圈区,4,210800\n210811,老边区,4,210800\n210881,盖州市,4,210800\n210882,大石桥市,4,210800\n210902,海州区,4,210900\n210903,新邱区,4,210900\n210904,太平区,4,210900\n210905,清河门区,4,210900\n210911,细河区,4,210900\n210921,阜新蒙古族自治县,4,210900\n210922,彰武县,4,210900\n211002,白塔区,4,211000\n211003,文圣区,4,211000\n211004,宏伟区,4,211000\n211005,弓长岭区,4,211000\n211011,太子河区,4,211000\n211021,辽阳县,4,211000\n211081,灯塔市,4,211000\n211102,双台子区,4,211100\n211103,兴隆台区,4,211100\n211104,大洼区,4,211100\n211122,盘山县,4,211100\n211202,银州区,4,211200\n211204,清河区,4,211200\n211221,铁岭县,4,211200\n211223,西丰县,4,211200\n211224,昌图县,4,211200\n211281,调兵山市,4,211200\n211282,开原市,4,211200\n211302,双塔区,4,211300\n211303,龙城区,4,211300\n211321,朝阳县,4,211300\n211322,建平县,4,211300\n211324,喀喇沁左翼蒙古族自治县,4,211300\n211381,北票市,4,211300\n211382,凌源市,4,211300\n211402,连山区,4,211400\n211403,龙港区,4,211400\n211404,南票区,4,211400\n211421,绥中县,4,211400\n211422,建昌县,4,211400\n211481,兴城市,4,211400\n220102,南关区,4,220100\n220103,宽城区,4,220100\n220104,朝阳区,4,220100\n220105,二道区,4,220100\n220106,绿园区,4,220100\n220112,双阳区,4,220100\n220113,九台区,4,220100\n220122,农安县,4,220100\n220171,长春经济技术开发区,4,220100\n220172,长春净月高新技术产业开发区,4,220100\n220173,长春高新技术产业开发区,4,220100\n220174,长春汽车经济技术开发区,4,220100\n220182,榆树市,4,220100\n220183,德惠市,4,220100\n220184,公主岭市,4,220100\n220202,昌邑区,4,220200\n220203,龙潭区,4,220200\n220204,船营区,4,220200\n220211,丰满区,4,220200\n220221,永吉县,4,220200\n220271,吉林经济开发区,4,220200\n220272,吉林高新技术产业开发区,4,220200\n220273,吉林中国新加坡食品区,4,220200\n220281,蛟河市,4,220200\n220282,桦甸市,4,220200\n220283,舒兰市,4,220200\n220284,磐石市,4,220200\n220302,铁西区,4,220300\n220303,铁东区,4,220300\n220322,梨树县,4,220300\n220323,伊通满族自治县,4,220300\n220382,双辽市,4,220300\n220402,龙山区,4,220400\n220403,西安区,4,220400\n220421,东丰县,4,220400\n220422,东辽县,4,220400\n220502,东昌区,4,220500\n220503,二道江区,4,220500\n220521,通化县,4,220500\n220523,辉南县,4,220500\n220524,柳河县,4,220500\n220581,梅河口市,4,220500\n220582,集安市,4,220500\n220602,浑江区,4,220600\n220605,江源区,4,220600\n220621,抚松县,4,220600\n220622,靖宇县,4,220600\n220623,长白朝鲜族自治县,4,220600\n220681,临江市,4,220600\n220702,宁江区,4,220700\n220721,前郭尔罗斯蒙古族自治县,4,220700\n220722,长岭县,4,220700\n220723,乾安县,4,220700\n220771,吉林松原经济开发区,4,220700\n220781,扶余市,4,220700\n220802,洮北区,4,220800\n220821,镇赉县,4,220800\n220822,通榆县,4,220800\n220871,吉林白城经济开发区,4,220800\n220881,洮南市,4,220800\n220882,大安市,4,220800\n222401,延吉市,4,222400\n222402,图们市,4,222400\n222403,敦化市,4,222400\n222404,珲春市,4,222400\n222405,龙井市,4,222400\n222406,和龙市,4,222400\n222424,汪清县,4,222400\n222426,安图县,4,222400\n230102,道里区,4,230100\n230103,南岗区,4,230100\n230104,道外区,4,230100\n230108,平房区,4,230100\n230109,松北区,4,230100\n230110,香坊区,4,230100\n230111,呼兰区,4,230100\n230112,阿城区,4,230100\n230113,双城区,4,230100\n230123,依兰县,4,230100\n230124,方正县,4,230100\n230125,宾县,4,230100\n230126,巴彦县,4,230100\n230127,木兰县,4,230100\n230128,通河县,4,230100\n230129,延寿县,4,230100\n230183,尚志市,4,230100\n230184,五常市,4,230100\n230202,龙沙区,4,230200\n230203,建华区,4,230200\n230204,铁锋区,4,230200\n230205,昂昂溪区,4,230200\n230206,富拉尔基区,4,230200\n230207,碾子山区,4,230200\n230208,梅里斯达斡尔族区,4,230200\n230221,龙江县,4,230200\n230223,依安县,4,230200\n230224,泰来县,4,230200\n230225,甘南县,4,230200\n230227,富裕县,4,230200\n230229,克山县,4,230200\n230230,克东县,4,230200\n230231,拜泉县,4,230200\n230281,讷河市,4,230200\n230302,鸡冠区,4,230300\n230303,恒山区,4,230300\n230304,滴道区,4,230300\n230305,梨树区,4,230300\n230306,城子河区,4,230300\n230307,麻山区,4,230300\n230321,鸡东县,4,230300\n230381,虎林市,4,230300\n230382,密山市,4,230300\n230402,向阳区,4,230400\n230403,工农区,4,230400\n230404,南山区,4,230400\n230405,兴安区,4,230400\n230406,东山区,4,230400\n230407,兴山区,4,230400\n230421,萝北县,4,230400\n230422,绥滨县,4,230400\n230502,尖山区,4,230500\n230503,岭东区,4,230500\n230505,四方台区,4,230500\n230506,宝山区,4,230500\n230521,集贤县,4,230500\n230522,友谊县,4,230500\n230523,宝清县,4,230500\n230524,饶河县,4,230500\n230602,萨尔图区,4,230600\n230603,龙凤区,4,230600\n230604,让胡路区,4,230600\n230605,红岗区,4,230600\n230606,大同区,4,230600\n230621,肇州县,4,230600\n230622,肇源县,4,230600\n230623,林甸县,4,230600\n230624,杜尔伯特蒙古族自治县,4,230600\n230671,大庆高新技术产业开发区,4,230600\n230717,伊美区,4,230700\n230718,乌翠区,4,230700\n230719,友好区,4,230700\n230722,嘉荫县,4,230700\n230723,汤旺县,4,230700\n230724,丰林县,4,230700\n230725,大箐山县,4,230700\n230726,南岔县,4,230700\n230751,金林区,4,230700\n230781,铁力市,4,230700\n230803,向阳区,4,230800\n230804,前进区,4,230800\n230805,东风区,4,230800\n230811,郊区,4,230800\n230822,桦南县,4,230800\n230826,桦川县,4,230800\n230828,汤原县,4,230800\n230881,同江市,4,230800\n230882,富锦市,4,230800\n230883,抚远市,4,230800\n230902,新兴区,4,230900\n230903,桃山区,4,230900\n230904,茄子河区,4,230900\n230921,勃利县,4,230900\n231002,东安区,4,231000\n231003,阳明区,4,231000\n231004,爱民区,4,231000\n231005,西安区,4,231000\n231025,林口县,4,231000\n231071,牡丹江经济技术开发区,4,231000\n231081,绥芬河市,4,231000\n231083,海林市,4,231000\n231084,宁安市,4,231000\n231085,穆棱市,4,231000\n231086,东宁市,4,231000\n231102,爱辉区,4,231100\n231123,逊克县,4,231100\n231124,孙吴县,4,231100\n231181,北安市,4,231100\n231182,五大连池市,4,231100\n231183,嫩江市,4,231100\n231202,北林区,4,231200\n231221,望奎县,4,231200\n231222,兰西县,4,231200\n231223,青冈县,4,231200\n231224,庆安县,4,231200\n231225,明水县,4,231200\n231226,绥棱县,4,231200\n231281,安达市,4,231200\n231282,肇东市,4,231200\n231283,海伦市,4,231200\n232701,漠河市,4,232700\n232721,呼玛县,4,232700\n232722,塔河县,4,232700\n232761,加格达奇区,4,232700\n232762,松岭区,4,232700\n232763,新林区,4,232700\n232764,呼中区,4,232700\n310101,黄浦区,4,310100\n310104,徐汇区,4,310100\n310105,长宁区,4,310100\n310106,静安区,4,310100\n310107,普陀区,4,310100\n310109,虹口区,4,310100\n310110,杨浦区,4,310100\n310112,闵行区,4,310100\n310113,宝山区,4,310100\n310114,嘉定区,4,310100\n310115,浦东新区,4,310100\n310116,金山区,4,310100\n310117,松江区,4,310100\n310118,青浦区,4,310100\n310120,奉贤区,4,310100\n310151,崇明区,4,310100\n320102,玄武区,4,320100\n320104,秦淮区,4,320100\n320105,建邺区,4,320100\n320106,鼓楼区,4,320100\n320111,浦口区,4,320100\n320113,栖霞区,4,320100\n320114,雨花台区,4,320100\n320115,江宁区,4,320100\n320116,六合区,4,320100\n320117,溧水区,4,320100\n320118,高淳区,4,320100\n320205,锡山区,4,320200\n320206,惠山区,4,320200\n320211,滨湖区,4,320200\n320213,梁溪区,4,320200\n320214,新吴区,4,320200\n320281,江阴市,4,320200\n320282,宜兴市,4,320200\n320302,鼓楼区,4,320300\n320303,云龙区,4,320300\n320305,贾汪区,4,320300\n320311,泉山区,4,320300\n320312,铜山区,4,320300\n320321,丰县,4,320300\n320322,沛县,4,320300\n320324,睢宁县,4,320300\n320371,徐州经济技术开发区,4,320300\n320381,新沂市,4,320300\n320382,邳州市,4,320300\n320402,天宁区,4,320400\n320404,钟楼区,4,320400\n320411,新北区,4,320400\n320412,武进区,4,320400\n320413,金坛区,4,320400\n320481,溧阳市,4,320400\n320505,虎丘区,4,320500\n320506,吴中区,4,320500\n320507,相城区,4,320500\n320508,姑苏区,4,320500\n320509,吴江区,4,320500\n320571,苏州工业园区,4,320500\n320581,常熟市,4,320500\n320582,张家港市,4,320500\n320583,昆山市,4,320500\n320585,太仓市,4,320500\n320612,通州区,4,320600\n320613,崇川区,4,320600\n320614,海门区,4,320600\n320623,如东县,4,320600\n320671,南通经济技术开发区,4,320600\n320681,启东市,4,320600\n320682,如皋市,4,320600\n320685,海安市,4,320600\n320703,连云区,4,320700\n320706,海州区,4,320700\n320707,赣榆区,4,320700\n320722,东海县,4,320700\n320723,灌云县,4,320700\n320724,灌南县,4,320700\n320771,连云港经济技术开发区,4,320700\n320772,连云港高新技术产业开发区,4,320700\n320803,淮安区,4,320800\n320804,淮阴区,4,320800\n320812,清江浦区,4,320800\n320813,洪泽区,4,320800\n320826,涟水县,4,320800\n320830,盱眙县,4,320800\n320831,金湖县,4,320800\n320871,淮安经济技术开发区,4,320800\n320902,亭湖区,4,320900\n320903,盐都区,4,320900\n320904,大丰区,4,320900\n320921,响水县,4,320900\n320922,滨海县,4,320900\n320923,阜宁县,4,320900\n320924,射阳县,4,320900\n320925,建湖县,4,320900\n320971,盐城经济技术开发区,4,320900\n320981,东台市,4,320900\n321002,广陵区,4,321000\n321003,邗江区,4,321000\n321012,江都区,4,321000\n321023,宝应县,4,321000\n321071,扬州经济技术开发区,4,321000\n321081,仪征市,4,321000\n321084,高邮市,4,321000\n321102,京口区,4,321100\n321111,润州区,4,321100\n321112,丹徒区,4,321100\n321171,镇江新区,4,321100\n321181,丹阳市,4,321100\n321182,扬中市,4,321100\n321183,句容市,4,321100\n321202,海陵区,4,321200\n321203,高港区,4,321200\n321204,姜堰区,4,321200\n321271,泰州医药高新技术产业开发区,4,321200\n321281,兴化市,4,321200\n321282,靖江市,4,321200\n321283,泰兴市,4,321200\n321302,宿城区,4,321300\n321311,宿豫区,4,321300\n321322,沭阳县,4,321300\n321323,泗阳县,4,321300\n321324,泗洪县,4,321300\n321371,宿迁经济技术开发区,4,321300\n330102,上城区,4,330100\n330105,拱墅区,4,330100\n330106,西湖区,4,330100\n330108,滨江区,4,330100\n330109,萧山区,4,330100\n330110,余杭区,4,330100\n330111,富阳区,4,330100\n330112,临安区,4,330100\n330113,临平区,4,330100\n330114,钱塘区,4,330100\n330122,桐庐县,4,330100\n330127,淳安县,4,330100\n330182,建德市,4,330100\n330203,海曙区,4,330200\n330205,江北区,4,330200\n330206,北仑区,4,330200\n330211,镇海区,4,330200\n330212,鄞州区,4,330200\n330213,奉化区,4,330200\n330225,象山县,4,330200\n330226,宁海县,4,330200\n330281,余姚市,4,330200\n330282,慈溪市,4,330200\n330302,鹿城区,4,330300\n330303,龙湾区,4,330300\n330304,瓯海区,4,330300\n330305,洞头区,4,330300\n330324,永嘉县,4,330300\n330326,平阳县,4,330300\n330327,苍南县,4,330300\n330328,文成县,4,330300\n330329,泰顺县,4,330300\n330371,温州经济技术开发区,4,330300\n330381,瑞安市,4,330300\n330382,乐清市,4,330300\n330383,龙港市,4,330300\n330402,南湖区,4,330400\n330411,秀洲区,4,330400\n330421,嘉善县,4,330400\n330424,海盐县,4,330400\n330481,海宁市,4,330400\n330482,平湖市,4,330400\n330483,桐乡市,4,330400\n330502,吴兴区,4,330500\n330503,南浔区,4,330500\n330521,德清县,4,330500\n330522,长兴县,4,330500\n330523,安吉县,4,330500\n330602,越城区,4,330600\n330603,柯桥区,4,330600\n330604,上虞区,4,330600\n330624,新昌县,4,330600\n330681,诸暨市,4,330600\n330683,嵊州市,4,330600\n330702,婺城区,4,330700\n330703,金东区,4,330700\n330723,武义县,4,330700\n330726,浦江县,4,330700\n330727,磐安县,4,330700\n330781,兰溪市,4,330700\n330782,义乌市,4,330700\n330783,东阳市,4,330700\n330784,永康市,4,330700\n330802,柯城区,4,330800\n330803,衢江区,4,330800\n330822,常山县,4,330800\n330824,开化县,4,330800\n330825,龙游县,4,330800\n330881,江山市,4,330800\n330902,定海区,4,330900\n330903,普陀区,4,330900\n330921,岱山县,4,330900\n330922,嵊泗县,4,330900\n331002,椒江区,4,331000\n331003,黄岩区,4,331000\n331004,路桥区,4,331000\n331022,三门县,4,331000\n331023,天台县,4,331000\n331024,仙居县,4,331000\n331081,温岭市,4,331000\n331082,临海市,4,331000\n331083,玉环市,4,331000\n331102,莲都区,4,331100\n331121,青田县,4,331100\n331122,缙云县,4,331100\n331123,遂昌县,4,331100\n331124,松阳县,4,331100\n331125,云和县,4,331100\n331126,庆元县,4,331100\n331127,景宁畲族自治县,4,331100\n331181,龙泉市,4,331100\n340102,瑶海区,4,340100\n340103,庐阳区,4,340100\n340104,蜀山区,4,340100\n340111,包河区,4,340100\n340121,长丰县,4,340100\n340122,肥东县,4,340100\n340123,肥西县,4,340100\n340124,庐江县,4,340100\n340171,合肥高新技术产业开发区,4,340100\n340172,合肥经济技术开发区,4,340100\n340173,合肥新站高新技术产业开发区,4,340100\n340181,巢湖市,4,340100\n340202,镜湖区,4,340200\n340207,鸠江区,4,340200\n340209,弋江区,4,340200\n340210,湾沚区,4,340200\n340212,繁昌区,4,340200\n340223,南陵县,4,340200\n340271,芜湖经济技术开发区,4,340200\n340272,安徽芜湖三山经济开发区,4,340200\n340281,无为市,4,340200\n340302,龙子湖区,4,340300\n340303,蚌山区,4,340300\n340304,禹会区,4,340300\n340311,淮上区,4,340300\n340321,怀远县,4,340300\n340322,五河县,4,340300\n340323,固镇县,4,340300\n340371,蚌埠市高新技术开发区,4,340300\n340372,蚌埠市经济开发区,4,340300\n340402,大通区,4,340400\n340403,田家庵区,4,340400\n340404,谢家集区,4,340400\n340405,八公山区,4,340400\n340406,潘集区,4,340400\n340421,凤台县,4,340400\n340422,寿县,4,340400\n340503,花山区,4,340500\n340504,雨山区,4,340500\n340506,博望区,4,340500\n340521,当涂县,4,340500\n340522,含山县,4,340500\n340523,和县,4,340500\n340602,杜集区,4,340600\n340603,相山区,4,340600\n340604,烈山区,4,340600\n340621,濉溪县,4,340600\n340705,铜官区,4,340700\n340706,义安区,4,340700\n340711,郊区,4,340700\n340722,枞阳县,4,340700\n340802,迎江区,4,340800\n340803,大观区,4,340800\n340811,宜秀区,4,340800\n340822,怀宁县,4,340800\n340825,太湖县,4,340800\n340826,宿松县,4,340800\n340827,望江县,4,340800\n340828,岳西县,4,340800\n340871,安徽安庆经济开发区,4,340800\n340881,桐城市,4,340800\n340882,潜山市,4,340800\n341002,屯溪区,4,341000\n341003,黄山区,4,341000\n341004,徽州区,4,341000\n341021,歙县,4,341000\n341022,休宁县,4,341000\n341023,黟县,4,341000\n341024,祁门县,4,341000\n341102,琅琊区,4,341100\n341103,南谯区,4,341100\n341122,来安县,4,341100\n341124,全椒县,4,341100\n341125,定远县,4,341100\n341126,凤阳县,4,341100\n341171,中新苏滁高新技术产业开发区,4,341100\n341172,滁州经济技术开发区,4,341100\n341181,天长市,4,341100\n341182,明光市,4,341100\n341202,颍州区,4,341200\n341203,颍东区,4,341200\n341204,颍泉区,4,341200\n341221,临泉县,4,341200\n341222,太和县,4,341200\n341225,阜南县,4,341200\n341226,颍上县,4,341200\n341271,阜阳合肥现代产业园区,4,341200\n341272,阜阳经济技术开发区,4,341200\n341282,界首市,4,341200\n341302,埇桥区,4,341300\n341321,砀山县,4,341300\n341322,萧县,4,341300\n341323,灵璧县,4,341300\n341324,泗县,4,341300\n341371,宿州马鞍山现代产业园区,4,341300\n341372,宿州经济技术开发区,4,341300\n341502,金安区,4,341500\n341503,裕安区,4,341500\n341504,叶集区,4,341500\n341522,霍邱县,4,341500\n341523,舒城县,4,341500\n341524,金寨县,4,341500\n341525,霍山县,4,341500\n341602,谯城区,4,341600\n341621,涡阳县,4,341600\n341622,蒙城县,4,341600\n341623,利辛县,4,341600\n341702,贵池区,4,341700\n341721,东至县,4,341700\n341722,石台县,4,341700\n341723,青阳县,4,341700\n341802,宣州区,4,341800\n341821,郎溪县,4,341800\n341823,泾县,4,341800\n341824,绩溪县,4,341800\n341825,旌德县,4,341800\n341871,宣城市经济开发区,4,341800\n341881,宁国市,4,341800\n341882,广德市,4,341800\n350102,鼓楼区,4,350100\n350103,台江区,4,350100\n350104,仓山区,4,350100\n350105,马尾区,4,350100\n350111,晋安区,4,350100\n350112,长乐区,4,350100\n350121,闽侯县,4,350100\n350122,连江县,4,350100\n350123,罗源县,4,350100\n350124,闽清县,4,350100\n350125,永泰县,4,350100\n350128,平潭县,4,350100\n350181,福清市,4,350100\n350203,思明区,4,350200\n350205,海沧区,4,350200\n350206,湖里区,4,350200\n350211,集美区,4,350200\n350212,同安区,4,350200\n350213,翔安区,4,350200\n350302,城厢区,4,350300\n350303,涵江区,4,350300\n350304,荔城区,4,350300\n350305,秀屿区,4,350300\n350322,仙游县,4,350300\n350404,三元区,4,350400\n350405,沙县区,4,350400\n350421,明溪县,4,350400\n350423,清流县,4,350400\n350424,宁化县,4,350400\n350425,大田县,4,350400\n350426,尤溪县,4,350400\n350428,将乐县,4,350400\n350429,泰宁县,4,350400\n350430,建宁县,4,350400\n350481,永安市,4,350400\n350502,鲤城区,4,350500\n350503,丰泽区,4,350500\n350504,洛江区,4,350500\n350505,泉港区,4,350500\n350521,惠安县,4,350500\n350524,安溪县,4,350500\n350525,永春县,4,350500\n350526,德化县,4,350500\n350527,金门县,4,350500\n350581,石狮市,4,350500\n350582,晋江市,4,350500\n350583,南安市,4,350500\n350602,芗城区,4,350600\n350603,龙文区,4,350600\n350604,龙海区,4,350600\n350605,长泰区,4,350600\n350622,云霄县,4,350600\n350623,漳浦县,4,350600\n350624,诏安县,4,350600\n350626,东山县,4,350600\n350627,南靖县,4,350600\n350628,平和县,4,350600\n350629,华安县,4,350600\n350702,延平区,4,350700\n350703,建阳区,4,350700\n350721,顺昌县,4,350700\n350722,浦城县,4,350700\n350723,光泽县,4,350700\n350724,松溪县,4,350700\n350725,政和县,4,350700\n350781,邵武市,4,350700\n350782,武夷山市,4,350700\n350783,建瓯市,4,350700\n350802,新罗区,4,350800\n350803,永定区,4,350800\n350821,长汀县,4,350800\n350823,上杭县,4,350800\n350824,武平县,4,350800\n350825,连城县,4,350800\n350881,漳平市,4,350800\n350902,蕉城区,4,350900\n350921,霞浦县,4,350900\n350922,古田县,4,350900\n350923,屏南县,4,350900\n350924,寿宁县,4,350900\n350925,周宁县,4,350900\n350926,柘荣县,4,350900\n350981,福安市,4,350900\n350982,福鼎市,4,350900\n360102,东湖区,4,360100\n360103,西湖区,4,360100\n360104,青云谱区,4,360100\n360111,青山湖区,4,360100\n360112,新建区,4,360100\n360113,红谷滩区,4,360100\n360121,南昌县,4,360100\n360123,安义县,4,360100\n360124,进贤县,4,360100\n360202,昌江区,4,360200\n360203,珠山区,4,360200\n360222,浮梁县,4,360200\n360281,乐平市,4,360200\n360302,安源区,4,360300\n360313,湘东区,4,360300\n360321,莲花县,4,360300\n360322,上栗县,4,360300\n360323,芦溪县,4,360300\n360402,濂溪区,4,360400\n360403,浔阳区,4,360400\n360404,柴桑区,4,360400\n360423,武宁县,4,360400\n360424,修水县,4,360400\n360425,永修县,4,360400\n360426,德安县,4,360400\n360428,都昌县,4,360400\n360429,湖口县,4,360400\n360430,彭泽县,4,360400\n360481,瑞昌市,4,360400\n360482,共青城市,4,360400\n360483,庐山市,4,360400\n360502,渝水区,4,360500\n360521,分宜县,4,360500\n360602,月湖区,4,360600\n360603,余江区,4,360600\n360681,贵溪市,4,360600\n360702,章贡区,4,360700\n360703,南康区,4,360700\n360704,赣县区,4,360700\n360722,信丰县,4,360700\n360723,大余县,4,360700\n360724,上犹县,4,360700\n360725,崇义县,4,360700\n360726,安远县,4,360700\n360728,定南县,4,360700\n360729,全南县,4,360700\n360730,宁都县,4,360700\n360731,于都县,4,360700\n360732,兴国县,4,360700\n360733,会昌县,4,360700\n360734,寻乌县,4,360700\n360735,石城县,4,360700\n360781,瑞金市,4,360700\n360783,龙南市,4,360700\n360802,吉州区,4,360800\n360803,青原区,4,360800\n360821,吉安县,4,360800\n360822,吉水县,4,360800\n360823,峡江县,4,360800\n360824,新干县,4,360800\n360825,永丰县,4,360800\n360826,泰和县,4,360800\n360827,遂川县,4,360800\n360828,万安县,4,360800\n360829,安福县,4,360800\n360830,永新县,4,360800\n360881,井冈山市,4,360800\n360902,袁州区,4,360900\n360921,奉新县,4,360900\n360922,万载县,4,360900\n360923,上高县,4,360900\n360924,宜丰县,4,360900\n360925,靖安县,4,360900\n360926,铜鼓县,4,360900\n360981,丰城市,4,360900\n360982,樟树市,4,360900\n360983,高安市,4,360900\n361002,临川区,4,361000\n361003,东乡区,4,361000\n361021,南城县,4,361000\n361022,黎川县,4,361000\n361023,南丰县,4,361000\n361024,崇仁县,4,361000\n361025,乐安县,4,361000\n361026,宜黄县,4,361000\n361027,金溪县,4,361000\n361028,资溪县,4,361000\n361030,广昌县,4,361000\n361102,信州区,4,361100\n361103,广丰区,4,361100\n361104,广信区,4,361100\n361123,玉山县,4,361100\n361124,铅山县,4,361100\n361125,横峰县,4,361100\n361126,弋阳县,4,361100\n361127,余干县,4,361100\n361128,鄱阳县,4,361100\n361129,万年县,4,361100\n361130,婺源县,4,361100\n361181,德兴市,4,361100\n370102,历下区,4,370100\n370103,市中区,4,370100\n370104,槐荫区,4,370100\n370105,天桥区,4,370100\n370112,历城区,4,370100\n370113,长清区,4,370100\n370114,章丘区,4,370100\n370115,济阳区,4,370100\n370116,莱芜区,4,370100\n370117,钢城区,4,370100\n370124,平阴县,4,370100\n370126,商河县,4,370100\n370171,济南高新技术产业开发区,4,370100\n370202,市南区,4,370200\n370203,市北区,4,370200\n370211,黄岛区,4,370200\n370212,崂山区,4,370200\n370213,李沧区,4,370200\n370214,城阳区,4,370200\n370215,即墨区,4,370200\n370271,青岛高新技术产业开发区,4,370200\n370281,胶州市,4,370200\n370283,平度市,4,370200\n370285,莱西市,4,370200\n370302,淄川区,4,370300\n370303,张店区,4,370300\n370304,博山区,4,370300\n370305,临淄区,4,370300\n370306,周村区,4,370300\n370321,桓台县,4,370300\n370322,高青县,4,370300\n370323,沂源县,4,370300\n370402,市中区,4,370400\n370403,薛城区,4,370400\n370404,峄城区,4,370400\n370405,台儿庄区,4,370400\n370406,山亭区,4,370400\n370481,滕州市,4,370400\n370502,东营区,4,370500\n370503,河口区,4,370500\n370505,垦利区,4,370500\n370522,利津县,4,370500\n370523,广饶县,4,370500\n370571,东营经济技术开发区,4,370500\n370572,东营港经济开发区,4,370500\n370602,芝罘区,4,370600\n370611,福山区,4,370600\n370612,牟平区,4,370600\n370613,莱山区,4,370600\n370614,蓬莱区,4,370600\n370671,烟台高新技术产业开发区,4,370600\n370672,烟台经济技术开发区,4,370600\n370681,龙口市,4,370600\n370682,莱阳市,4,370600\n370683,莱州市,4,370600\n370685,招远市,4,370600\n370686,栖霞市,4,370600\n370687,海阳市,4,370600\n370702,潍城区,4,370700\n370703,寒亭区,4,370700\n370704,坊子区,4,370700\n370705,奎文区,4,370700\n370724,临朐县,4,370700\n370725,昌乐县,4,370700\n370772,潍坊滨海经济技术开发区,4,370700\n370781,青州市,4,370700\n370782,诸城市,4,370700\n370783,寿光市,4,370700\n370784,安丘市,4,370700\n370785,高密市,4,370700\n370786,昌邑市,4,370700\n370811,任城区,4,370800\n370812,兖州区,4,370800\n370826,微山县,4,370800\n370827,鱼台县,4,370800\n370828,金乡县,4,370800\n370829,嘉祥县,4,370800\n370830,汶上县,4,370800\n370831,泗水县,4,370800\n370832,梁山县,4,370800\n370871,济宁高新技术产业开发区,4,370800\n370881,曲阜市,4,370800\n370883,邹城市,4,370800\n370902,泰山区,4,370900\n370911,岱岳区,4,370900\n370921,宁阳县,4,370900\n370923,东平县,4,370900\n370982,新泰市,4,370900\n370983,肥城市,4,370900\n371002,环翠区,4,371000\n371003,文登区,4,371000\n371071,威海火炬高技术产业开发区,4,371000\n371072,威海经济技术开发区,4,371000\n371073,威海临港经济技术开发区,4,371000\n371082,荣成市,4,371000\n371083,乳山市,4,371000\n371102,东港区,4,371100\n371103,岚山区,4,371100\n371121,五莲县,4,371100\n371122,莒县,4,371100\n371171,日照经济技术开发区,4,371100\n371302,兰山区,4,371300\n371311,罗庄区,4,371300\n371312,河东区,4,371300\n371321,沂南县,4,371300\n371322,郯城县,4,371300\n371323,沂水县,4,371300\n371324,兰陵县,4,371300\n371325,费县,4,371300\n371326,平邑县,4,371300\n371327,莒南县,4,371300\n371328,蒙阴县,4,371300\n371329,临沭县,4,371300\n371371,临沂高新技术产业开发区,4,371300\n371402,德城区,4,371400\n371403,陵城区,4,371400\n371422,宁津县,4,371400\n371423,庆云县,4,371400\n371424,临邑县,4,371400\n371425,齐河县,4,371400\n371426,平原县,4,371400\n371427,夏津县,4,371400\n371428,武城县,4,371400\n371471,德州经济技术开发区,4,371400\n371472,德州运河经济开发区,4,371400\n371481,乐陵市,4,371400\n371482,禹城市,4,371400\n371502,东昌府区,4,371500\n371503,茌平区,4,371500\n371521,阳谷县,4,371500\n371522,莘县,4,371500\n371524,东阿县,4,371500\n371525,冠县,4,371500\n371526,高唐县,4,371500\n371581,临清市,4,371500\n371602,滨城区,4,371600\n371603,沾化区,4,371600\n371621,惠民县,4,371600\n371622,阳信县,4,371600\n371623,无棣县,4,371600\n371625,博兴县,4,371600\n371681,邹平市,4,371600\n371702,牡丹区,4,371700\n371703,定陶区,4,371700\n371721,曹县,4,371700\n371722,单县,4,371700\n371723,成武县,4,371700\n371724,巨野县,4,371700\n371725,郓城县,4,371700\n371726,鄄城县,4,371700\n371728,东明县,4,371700\n371771,菏泽经济技术开发区,4,371700\n371772,菏泽高新技术开发区,4,371700\n410102,中原区,4,410100\n410103,二七区,4,410100\n410104,管城回族区,4,410100\n410105,金水区,4,410100\n410106,上街区,4,410100\n410108,惠济区,4,410100\n410122,中牟县,4,410100\n410171,郑州经济技术开发区,4,410100\n410172,郑州高新技术产业开发区,4,410100\n410173,郑州航空港经济综合实验区,4,410100\n410181,巩义市,4,410100\n410182,荥阳市,4,410100\n410183,新密市,4,410100\n410184,新郑市,4,410100\n410185,登封市,4,410100\n410202,龙亭区,4,410200\n410203,顺河回族区,4,410200\n410204,鼓楼区,4,410200\n410205,禹王台区,4,410200\n410212,祥符区,4,410200\n410221,杞县,4,410200\n410222,通许县,4,410200\n410223,尉氏县,4,410200\n410225,兰考县,4,410200\n410302,老城区,4,410300\n410303,西工区,4,410300\n410304,瀍河回族区,4,410300\n410305,涧西区,4,410300\n410307,偃师区,4,410300\n410308,孟津区,4,410300\n410311,洛龙区,4,410300\n410323,新安县,4,410300\n410324,栾川县,4,410300\n410325,嵩县,4,410300\n410326,汝阳县,4,410300\n410327,宜阳县,4,410300\n410328,洛宁县,4,410300\n410329,伊川县,4,410300\n410371,洛阳高新技术产业开发区,4,410300\n410402,新华区,4,410400\n410403,卫东区,4,410400\n410404,石龙区,4,410400\n410411,湛河区,4,410400\n410421,宝丰县,4,410400\n410422,叶县,4,410400\n410423,鲁山县,4,410400\n410425,郏县,4,410400\n410471,平顶山高新技术产业开发区,4,410400\n410472,平顶山市城乡一体化示范区,4,410400\n410481,舞钢市,4,410400\n410482,汝州市,4,410400\n410502,文峰区,4,410500\n410503,北关区,4,410500\n410505,殷都区,4,410500\n410506,龙安区,4,410500\n410522,安阳县,4,410500\n410523,汤阴县,4,410500\n410526,滑县,4,410500\n410527,内黄县,4,410500\n410571,安阳高新技术产业开发区,4,410500\n410581,林州市,4,410500\n410602,鹤山区,4,410600\n410603,山城区,4,410600\n410611,淇滨区,4,410600\n410621,浚县,4,410600\n410622,淇县,4,410600\n410671,鹤壁经济技术开发区,4,410600\n410702,红旗区,4,410700\n410703,卫滨区,4,410700\n410704,凤泉区,4,410700\n410711,牧野区,4,410700\n410721,新乡县,4,410700\n410724,获嘉县,4,410700\n410725,原阳县,4,410700\n410726,延津县,4,410700\n410727,封丘县,4,410700\n410771,新乡高新技术产业开发区,4,410700\n410772,新乡经济技术开发区,4,410700\n410773,新乡市平原城乡一体化示范区,4,410700\n410781,卫辉市,4,410700\n410782,辉县市,4,410700\n410783,长垣市,4,410700\n410802,解放区,4,410800\n410803,中站区,4,410800\n410804,马村区,4,410800\n410811,山阳区,4,410800\n410821,修武县,4,410800\n410822,博爱县,4,410800\n410823,武陟县,4,410800\n410825,温县,4,410800\n410871,焦作城乡一体化示范区,4,410800\n410882,沁阳市,4,410800\n410883,孟州市,4,410800\n410902,华龙区,4,410900\n410922,清丰县,4,410900\n410923,南乐县,4,410900\n410926,范县,4,410900\n410927,台前县,4,410900\n410928,濮阳县,4,410900\n410971,河南濮阳工业园区,4,410900\n410972,濮阳经济技术开发区,4,410900\n411002,魏都区,4,411000\n411003,建安区,4,411000\n411024,鄢陵县,4,411000\n411025,襄城县,4,411000\n411071,许昌经济技术开发区,4,411000\n411081,禹州市,4,411000\n411082,长葛市,4,411000\n411102,源汇区,4,411100\n411103,郾城区,4,411100\n411104,召陵区,4,411100\n411121,舞阳县,4,411100\n411122,临颍县,4,411100\n411171,漯河经济技术开发区,4,411100\n411202,湖滨区,4,411200\n411203,陕州区,4,411200\n411221,渑池县,4,411200\n411224,卢氏县,4,411200\n411271,河南三门峡经济开发区,4,411200\n411281,义马市,4,411200\n411282,灵宝市,4,411200\n411302,宛城区,4,411300\n411303,卧龙区,4,411300\n411321,南召县,4,411300\n411322,方城县,4,411300\n411323,西峡县,4,411300\n411324,镇平县,4,411300\n411325,内乡县,4,411300\n411326,淅川县,4,411300\n411327,社旗县,4,411300\n411328,唐河县,4,411300\n411329,新野县,4,411300\n411330,桐柏县,4,411300\n411371,南阳高新技术产业开发区,4,411300\n411372,南阳市城乡一体化示范区,4,411300\n411381,邓州市,4,411300\n411402,梁园区,4,411400\n411403,睢阳区,4,411400\n411421,民权县,4,411400\n411422,睢县,4,411400\n411423,宁陵县,4,411400\n411424,柘城县,4,411400\n411425,虞城县,4,411400\n411426,夏邑县,4,411400\n411471,豫东综合物流产业聚集区,4,411400\n411472,河南商丘经济开发区,4,411400\n411481,永城市,4,411400\n411502,浉河区,4,411500\n411503,平桥区,4,411500\n411521,罗山县,4,411500\n411522,光山县,4,411500\n411523,新县,4,411500\n411524,商城县,4,411500\n411525,固始县,4,411500\n411526,潢川县,4,411500\n411527,淮滨县,4,411500\n411528,息县,4,411500\n411571,信阳高新技术产业开发区,4,411500\n411602,川汇区,4,411600\n411603,淮阳区,4,411600\n411621,扶沟县,4,411600\n411622,西华县,4,411600\n411623,商水县,4,411600\n411624,沈丘县,4,411600\n411625,郸城县,4,411600\n411627,太康县,4,411600\n411628,鹿邑县,4,411600\n411671,河南周口经济开发区,4,411600\n411681,项城市,4,411600\n411702,驿城区,4,411700\n411721,西平县,4,411700\n411722,上蔡县,4,411700\n411723,平舆县,4,411700\n411724,正阳县,4,411700\n411725,确山县,4,411700\n411726,泌阳县,4,411700\n411727,汝南县,4,411700\n411728,遂平县,4,411700\n411729,新蔡县,4,411700\n411771,河南驻马店经济开发区,4,411700\n419001,济源市,4,419000\n420102,江岸区,4,420100\n420103,江汉区,4,420100\n420104,硚口区,4,420100\n420105,汉阳区,4,420100\n420106,武昌区,4,420100\n420107,青山区,4,420100\n420111,洪山区,4,420100\n420112,东西湖区,4,420100\n420113,汉南区,4,420100\n420114,蔡甸区,4,420100\n420115,江夏区,4,420100\n420116,黄陂区,4,420100\n420117,新洲区,4,420100\n420202,黄石港区,4,420200\n420203,西塞山区,4,420200\n420204,下陆区,4,420200\n420205,铁山区,4,420200\n420222,阳新县,4,420200\n420281,大冶市,4,420200\n420302,茅箭区,4,420300\n420303,张湾区,4,420300\n420304,郧阳区,4,420300\n420322,郧西县,4,420300\n420323,竹山县,4,420300\n420324,竹溪县,4,420300\n420325,房县,4,420300\n420381,丹江口市,4,420300\n420502,西陵区,4,420500\n420503,伍家岗区,4,420500\n420504,点军区,4,420500\n420505,猇亭区,4,420500\n420506,夷陵区,4,420500\n420525,远安县,4,420500\n420526,兴山县,4,420500\n420527,秭归县,4,420500\n420528,长阳土家族自治县,4,420500\n420529,五峰土家族自治县,4,420500\n420581,宜都市,4,420500\n420582,当阳市,4,420500\n420583,枝江市,4,420500\n420602,襄城区,4,420600\n420606,樊城区,4,420600\n420607,襄州区,4,420600\n420624,南漳县,4,420600\n420625,谷城县,4,420600\n420626,保康县,4,420600\n420682,老河口市,4,420600\n420683,枣阳市,4,420600\n420684,宜城市,4,420600\n420702,梁子湖区,4,420700\n420703,华容区,4,420700\n420704,鄂城区,4,420700\n420802,东宝区,4,420800\n420804,掇刀区,4,420800\n420822,沙洋县,4,420800\n420881,钟祥市,4,420800\n420882,京山市,4,420800\n420902,孝南区,4,420900\n420921,孝昌县,4,420900\n420922,大悟县,4,420900\n420923,云梦县,4,420900\n420981,应城市,4,420900\n420982,安陆市,4,420900\n420984,汉川市,4,420900\n421002,沙市区,4,421000\n421003,荆州区,4,421000\n421022,公安县,4,421000\n421024,江陵县,4,421000\n421071,荆州经济技术开发区,4,421000\n421081,石首市,4,421000\n421083,洪湖市,4,421000\n421087,松滋市,4,421000\n421088,监利市,4,421000\n421102,黄州区,4,421100\n421121,团风县,4,421100\n421122,红安县,4,421100\n421123,罗田县,4,421100\n421124,英山县,4,421100\n421125,浠水县,4,421100\n421126,蕲春县,4,421100\n421127,黄梅县,4,421100\n421171,龙感湖管理区,4,421100\n421181,麻城市,4,421100\n421182,武穴市,4,421100\n421202,咸安区,4,421200\n421221,嘉鱼县,4,421200\n421222,通城县,4,421200\n421223,崇阳县,4,421200\n421224,通山县,4,421200\n421281,赤壁市,4,421200\n421303,曾都区,4,421300\n421321,随县,4,421300\n421381,广水市,4,421300\n422801,恩施市,4,422800\n422802,利川市,4,422800\n422822,建始县,4,422800\n422823,巴东县,4,422800\n422825,宣恩县,4,422800\n422826,咸丰县,4,422800\n422827,来凤县,4,422800\n422828,鹤峰县,4,422800\n429004,仙桃市,4,429000\n429005,潜江市,4,429000\n429006,天门市,4,429000\n429021,神农架林区,4,429000\n430102,芙蓉区,4,430100\n430103,天心区,4,430100\n430104,岳麓区,4,430100\n430105,开福区,4,430100\n430111,雨花区,4,430100\n430112,望城区,4,430100\n430121,长沙县,4,430100\n430181,浏阳市,4,430100\n430182,宁乡市,4,430100\n430202,荷塘区,4,430200\n430203,芦淞区,4,430200\n430204,石峰区,4,430200\n430211,天元区,4,430200\n430212,渌口区,4,430200\n430223,攸县,4,430200\n430224,茶陵县,4,430200\n430225,炎陵县,4,430200\n430271,云龙示范区,4,430200\n430281,醴陵市,4,430200\n430302,雨湖区,4,430300\n430304,岳塘区,4,430300\n430321,湘潭县,4,430300\n430371,湖南湘潭高新技术产业园区,4,430300\n430372,湘潭昭山示范区,4,430300\n430373,湘潭九华示范区,4,430300\n430381,湘乡市,4,430300\n430382,韶山市,4,430300\n430405,珠晖区,4,430400\n430406,雁峰区,4,430400\n430407,石鼓区,4,430400\n430408,蒸湘区,4,430400\n430412,南岳区,4,430400\n430421,衡阳县,4,430400\n430422,衡南县,4,430400\n430423,衡山县,4,430400\n430424,衡东县,4,430400\n430426,祁东县,4,430400\n430471,衡阳综合保税区,4,430400\n430472,湖南衡阳高新技术产业园区,4,430400\n430473,湖南衡阳松木经济开发区,4,430400\n430481,耒阳市,4,430400\n430482,常宁市,4,430400\n430502,双清区,4,430500\n430503,大祥区,4,430500\n430511,北塔区,4,430500\n430522,新邵县,4,430500\n430523,邵阳县,4,430500\n430524,隆回县,4,430500\n430525,洞口县,4,430500\n430527,绥宁县,4,430500\n430528,新宁县,4,430500\n430529,城步苗族自治县,4,430500\n430581,武冈市,4,430500\n430582,邵东市,4,430500\n430602,岳阳楼区,4,430600\n430603,云溪区,4,430600\n430611,君山区,4,430600\n430621,岳阳县,4,430600\n430623,华容县,4,430600\n430624,湘阴县,4,430600\n430626,平江县,4,430600\n430671,岳阳市屈原管理区,4,430600\n430681,汨罗市,4,430600\n430682,临湘市,4,430600\n430702,武陵区,4,430700\n430703,鼎城区,4,430700\n430721,安乡县,4,430700\n430722,汉寿县,4,430700\n430723,澧县,4,430700\n430724,临澧县,4,430700\n430725,桃源县,4,430700\n430726,石门县,4,430700\n430771,常德市西洞庭管理区,4,430700\n430781,津市市,4,430700\n430802,永定区,4,430800\n430811,武陵源区,4,430800\n430821,慈利县,4,430800\n430822,桑植县,4,430800\n430902,资阳区,4,430900\n430903,赫山区,4,430900\n430921,南县,4,430900\n430922,桃江县,4,430900\n430923,安化县,4,430900\n430971,益阳市大通湖管理区,4,430900\n430972,湖南益阳高新技术产业园区,4,430900\n430981,沅江市,4,430900\n431002,北湖区,4,431000\n431003,苏仙区,4,431000\n431021,桂阳县,4,431000\n431022,宜章县,4,431000\n431023,永兴县,4,431000\n431024,嘉禾县,4,431000\n431025,临武县,4,431000\n431026,汝城县,4,431000\n431027,桂东县,4,431000\n431028,安仁县,4,431000\n431081,资兴市,4,431000\n431102,零陵区,4,431100\n431103,冷水滩区,4,431100\n431122,东安县,4,431100\n431123,双牌县,4,431100\n431124,道县,4,431100\n431125,江永县,4,431100\n431126,宁远县,4,431100\n431127,蓝山县,4,431100\n431128,新田县,4,431100\n431129,江华瑶族自治县,4,431100\n431171,永州经济技术开发区,4,431100\n431173,永州市回龙圩管理区,4,431100\n431181,祁阳市,4,431100\n431202,鹤城区,4,431200\n431221,中方县,4,431200\n431222,沅陵县,4,431200\n431223,辰溪县,4,431200\n431224,溆浦县,4,431200\n431225,会同县,4,431200\n431226,麻阳苗族自治县,4,431200\n431227,新晃侗族自治县,4,431200\n431228,芷江侗族自治县,4,431200\n431229,靖州苗族侗族自治县,4,431200\n431230,通道侗族自治县,4,431200\n431271,怀化市洪江管理区,4,431200\n431281,洪江市,4,431200\n431302,娄星区,4,431300\n431321,双峰县,4,431300\n431322,新化县,4,431300\n431381,冷水江市,4,431300\n431382,涟源市,4,431300\n433101,吉首市,4,433100\n433122,泸溪县,4,433100\n433123,凤凰县,4,433100\n433124,花垣县,4,433100\n433125,保靖县,4,433100\n433126,古丈县,4,433100\n433127,永顺县,4,433100\n433130,龙山县,4,433100\n440103,荔湾区,4,440100\n440104,越秀区,4,440100\n440105,海珠区,4,440100\n440106,天河区,4,440100\n440111,白云区,4,440100\n440112,黄埔区,4,440100\n440113,番禺区,4,440100\n440114,花都区,4,440100\n440115,南沙区,4,440100\n440117,从化区,4,440100\n440118,增城区,4,440100\n440203,武江区,4,440200\n440204,浈江区,4,440200\n440205,曲江区,4,440200\n440222,始兴县,4,440200\n440224,仁化县,4,440200\n440229,翁源县,4,440200\n440232,乳源瑶族自治县,4,440200\n440233,新丰县,4,440200\n440281,乐昌市,4,440200\n440282,南雄市,4,440200\n440303,罗湖区,4,440300\n440304,福田区,4,440300\n440305,南山区,4,440300\n440306,宝安区,4,440300\n440307,龙岗区,4,440300\n440308,盐田区,4,440300\n440309,龙华区,4,440300\n440310,坪山区,4,440300\n440311,光明区,4,440300\n440402,香洲区,4,440400\n440403,斗门区,4,440400\n440404,金湾区,4,440400\n440507,龙湖区,4,440500\n440511,金平区,4,440500\n440512,濠江区,4,440500\n440513,潮阳区,4,440500\n440514,潮南区,4,440500\n440515,澄海区,4,440500\n440523,南澳县,4,440500\n440604,禅城区,4,440600\n440605,南海区,4,440600\n440606,顺德区,4,440600\n440607,三水区,4,440600\n440608,高明区,4,440600\n440703,蓬江区,4,440700\n440704,江海区,4,440700\n440705,新会区,4,440700\n440781,台山市,4,440700\n440783,开平市,4,440700\n440784,鹤山市,4,440700\n440785,恩平市,4,440700\n440802,赤坎区,4,440800\n440803,霞山区,4,440800\n440804,坡头区,4,440800\n440811,麻章区,4,440800\n440823,遂溪县,4,440800\n440825,徐闻县,4,440800\n440881,廉江市,4,440800\n440882,雷州市,4,440800\n440883,吴川市,4,440800\n440902,茂南区,4,440900\n440904,电白区,4,440900\n440981,高州市,4,440900\n440982,化州市,4,440900\n440983,信宜市,4,440900\n441202,端州区,4,441200\n441203,鼎湖区,4,441200\n441204,高要区,4,441200\n441223,广宁县,4,441200\n441224,怀集县,4,441200\n441225,封开县,4,441200\n441226,德庆县,4,441200\n441284,四会市,4,441200\n441302,惠城区,4,441300\n441303,惠阳区,4,441300\n441322,博罗县,4,441300\n441323,惠东县,4,441300\n441324,龙门县,4,441300\n441402,梅江区,4,441400\n441403,梅县区,4,441400\n441422,大埔县,4,441400\n441423,丰顺县,4,441400\n441424,五华县,4,441400\n441426,平远县,4,441400\n441427,蕉岭县,4,441400\n441481,兴宁市,4,441400\n441502,城区,4,441500\n441521,海丰县,4,441500\n441523,陆河县,4,441500\n441581,陆丰市,4,441500\n441602,源城区,4,441600\n441621,紫金县,4,441600\n441622,龙川县,4,441600\n441623,连平县,4,441600\n441624,和平县,4,441600\n441625,东源县,4,441600\n441702,江城区,4,441700\n441704,阳东区,4,441700\n441721,阳西县,4,441700\n441781,阳春市,4,441700\n441802,清城区,4,441800\n441803,清新区,4,441800\n441821,佛冈县,4,441800\n441823,阳山县,4,441800\n441825,连山壮族瑶族自治县,4,441800\n441826,连南瑶族自治县,4,441800\n441881,英德市,4,441800\n441882,连州市,4,441800\n445102,湘桥区,4,445100\n445103,潮安区,4,445100\n445122,饶平县,4,445100\n445202,榕城区,4,445200\n445203,揭东区,4,445200\n445222,揭西县,4,445200\n445224,惠来县,4,445200\n445281,普宁市,4,445200\n445302,云城区,4,445300\n445303,云安区,4,445300\n445321,新兴县,4,445300\n445322,郁南县,4,445300\n445381,罗定市,4,445300\n450102,兴宁区,4,450100\n450103,青秀区,4,450100\n450105,江南区,4,450100\n450107,西乡塘区,4,450100\n450108,良庆区,4,450100\n450109,邕宁区,4,450100\n450110,武鸣区,4,450100\n450123,隆安县,4,450100\n450124,马山县,4,450100\n450125,上林县,4,450100\n450126,宾阳县,4,450100\n450181,横州市,4,450100\n450202,城中区,4,450200\n450203,鱼峰区,4,450200\n450204,柳南区,4,450200\n450205,柳北区,4,450200\n450206,柳江区,4,450200\n450222,柳城县,4,450200\n450223,鹿寨县,4,450200\n450224,融安县,4,450200\n450225,融水苗族自治县,4,450200\n450226,三江侗族自治县,4,450200\n450302,秀峰区,4,450300\n450303,叠彩区,4,450300\n450304,象山区,4,450300\n450305,七星区,4,450300\n450311,雁山区,4,450300\n450312,临桂区,4,450300\n450321,阳朔县,4,450300\n450323,灵川县,4,450300\n450324,全州县,4,450300\n450325,兴安县,4,450300\n450326,永福县,4,450300\n450327,灌阳县,4,450300\n450328,龙胜各族自治县,4,450300\n450329,资源县,4,450300\n450330,平乐县,4,450300\n450332,恭城瑶族自治县,4,450300\n450381,荔浦市,4,450300\n450403,万秀区,4,450400\n450405,长洲区,4,450400\n450406,龙圩区,4,450400\n450421,苍梧县,4,450400\n450422,藤县,4,450400\n450423,蒙山县,4,450400\n450481,岑溪市,4,450400\n450502,海城区,4,450500\n450503,银海区,4,450500\n450512,铁山港区,4,450500\n450521,合浦县,4,450500\n450602,港口区,4,450600\n450603,防城区,4,450600\n450621,上思县,4,450600\n450681,东兴市,4,450600\n450702,钦南区,4,450700\n450703,钦北区,4,450700\n450721,灵山县,4,450700\n450722,浦北县,4,450700\n450802,港北区,4,450800\n450803,港南区,4,450800\n450804,覃塘区,4,450800\n450821,平南县,4,450800\n450881,桂平市,4,450800\n450902,玉州区,4,450900\n450903,福绵区,4,450900\n450921,容县,4,450900\n450922,陆川县,4,450900\n450923,博白县,4,450900\n450924,兴业县,4,450900\n450981,北流市,4,450900\n451002,右江区,4,451000\n451003,田阳区,4,451000\n451022,田东县,4,451000\n451024,德保县,4,451000\n451026,那坡县,4,451000\n451027,凌云县,4,451000\n451028,乐业县,4,451000\n451029,田林县,4,451000\n451030,西林县,4,451000\n451031,隆林各族自治县,4,451000\n451081,靖西市,4,451000\n451082,平果市,4,451000\n451102,八步区,4,451100\n451103,平桂区,4,451100\n451121,昭平县,4,451100\n451122,钟山县,4,451100\n451123,富川瑶族自治县,4,451100\n451202,金城江区,4,451200\n451203,宜州区,4,451200\n451221,南丹县,4,451200\n451222,天峨县,4,451200\n451223,凤山县,4,451200\n451224,东兰县,4,451200\n451225,罗城仫佬族自治县,4,451200\n451226,环江毛南族自治县,4,451200\n451227,巴马瑶族自治县,4,451200\n451228,都安瑶族自治县,4,451200\n451229,大化瑶族自治县,4,451200\n451302,兴宾区,4,451300\n451321,忻城县,4,451300\n451322,象州县,4,451300\n451323,武宣县,4,451300\n451324,金秀瑶族自治县,4,451300\n451381,合山市,4,451300\n451402,江州区,4,451400\n451421,扶绥县,4,451400\n451422,宁明县,4,451400\n451423,龙州县,4,451400\n451424,大新县,4,451400\n451425,天等县,4,451400\n451481,凭祥市,4,451400\n460105,秀英区,4,460100\n460106,龙华区,4,460100\n460107,琼山区,4,460100\n460108,美兰区,4,460100\n460202,海棠区,4,460200\n460203,吉阳区,4,460200\n460204,天涯区,4,460200\n460205,崖州区,4,460200\n460321,西沙群岛,4,460300\n460322,南沙群岛,4,460300\n460323,中沙群岛的岛礁及其海域,4,460300\n469001,五指山市,4,469000\n469002,琼海市,4,469000\n469005,文昌市,4,469000\n469006,万宁市,4,469000\n469007,东方市,4,469000\n469021,定安县,4,469000\n469022,屯昌县,4,469000\n469023,澄迈县,4,469000\n469024,临高县,4,469000\n469025,白沙黎族自治县,4,469000\n469026,昌江黎族自治县,4,469000\n469027,乐东黎族自治县,4,469000\n469028,陵水黎族自治县,4,469000\n469029,保亭黎族苗族自治县,4,469000\n469030,琼中黎族苗族自治县,4,469000\n500101,万州区,4,500100\n500102,涪陵区,4,500100\n500103,渝中区,4,500100\n500104,大渡口区,4,500100\n500105,江北区,4,500100\n500106,沙坪坝区,4,500100\n500107,九龙坡区,4,500100\n500108,南岸区,4,500100\n500109,北碚区,4,500100\n500110,綦江区,4,500100\n500111,大足区,4,500100\n500112,渝北区,4,500100\n500113,巴南区,4,500100\n500114,黔江区,4,500100\n500115,长寿区,4,500100\n500116,江津区,4,500100\n500117,合川区,4,500100\n500118,永川区,4,500100\n500119,南川区,4,500100\n500120,璧山区,4,500100\n500151,铜梁区,4,500100\n500152,潼南区,4,500100\n500153,荣昌区,4,500100\n500154,开州区,4,500100\n500155,梁平区,4,500100\n500156,武隆区,4,500100\n500229,城口县,4,500100\n500230,丰都县,4,500100\n500231,垫江县,4,500100\n500233,忠县,4,500100\n500235,云阳县,4,500100\n500236,奉节县,4,500100\n500237,巫山县,4,500100\n500238,巫溪县,4,500100\n500240,石柱土家族自治县,4,500100\n500241,秀山土家族苗族自治县,4,500100\n500242,酉阳土家族苗族自治县,4,500100\n500243,彭水苗族土家族自治县,4,500100\n510104,锦江区,4,510100\n510105,青羊区,4,510100\n510106,金牛区,4,510100\n510107,武侯区,4,510100\n510108,成华区,4,510100\n510112,龙泉驿区,4,510100\n510113,青白江区,4,510100\n510114,新都区,4,510100\n510115,温江区,4,510100\n510116,双流区,4,510100\n510117,郫都区,4,510100\n510118,新津区,4,510100\n510121,金堂县,4,510100\n510129,大邑县,4,510100\n510131,蒲江县,4,510100\n510181,都江堰市,4,510100\n510182,彭州市,4,510100\n510183,邛崃市,4,510100\n510184,崇州市,4,510100\n510185,简阳市,4,510100\n510302,自流井区,4,510300\n510303,贡井区,4,510300\n510304,大安区,4,510300\n510311,沿滩区,4,510300\n510321,荣县,4,510300\n510322,富顺县,4,510300\n510402,东区,4,510400\n510403,西区,4,510400\n510411,仁和区,4,510400\n510421,米易县,4,510400\n510422,盐边县,4,510400\n510502,江阳区,4,510500\n510503,纳溪区,4,510500\n510504,龙马潭区,4,510500\n510521,泸县,4,510500\n510522,合江县,4,510500\n510524,叙永县,4,510500\n510525,古蔺县,4,510500\n510603,旌阳区,4,510600\n510604,罗江区,4,510600\n510623,中江县,4,510600\n510681,广汉市,4,510600\n510682,什邡市,4,510600\n510683,绵竹市,4,510600\n510703,涪城区,4,510700\n510704,游仙区,4,510700\n510705,安州区,4,510700\n510722,三台县,4,510700\n510723,盐亭县,4,510700\n510725,梓潼县,4,510700\n510726,北川羌族自治县,4,510700\n510727,平武县,4,510700\n510781,江油市,4,510700\n510802,利州区,4,510800\n510811,昭化区,4,510800\n510812,朝天区,4,510800\n510821,旺苍县,4,510800\n510822,青川县,4,510800\n510823,剑阁县,4,510800\n510824,苍溪县,4,510800\n510903,船山区,4,510900\n510904,安居区,4,510900\n510921,蓬溪县,4,510900\n510923,大英县,4,510900\n510981,射洪市,4,510900\n511002,市中区,4,511000\n511011,东兴区,4,511000\n511024,威远县,4,511000\n511025,资中县,4,511000\n511071,内江经济开发区,4,511000\n511083,隆昌市,4,511000\n511102,市中区,4,511100\n511111,沙湾区,4,511100\n511112,五通桥区,4,511100\n511113,金口河区,4,511100\n511123,犍为县,4,511100\n511124,井研县,4,511100\n511126,夹江县,4,511100\n511129,沐川县,4,511100\n511132,峨边彝族自治县,4,511100\n511133,马边彝族自治县,4,511100\n511181,峨眉山市,4,511100\n511302,顺庆区,4,511300\n511303,高坪区,4,511300\n511304,嘉陵区,4,511300\n511321,南部县,4,511300\n511322,营山县,4,511300\n511323,蓬安县,4,511300\n511324,仪陇县,4,511300\n511325,西充县,4,511300\n511381,阆中市,4,511300\n511402,东坡区,4,511400\n511403,彭山区,4,511400\n511421,仁寿县,4,511400\n511423,洪雅县,4,511400\n511424,丹棱县,4,511400\n511425,青神县,4,511400\n511502,翠屏区,4,511500\n511503,南溪区,4,511500\n511504,叙州区,4,511500\n511523,江安县,4,511500\n511524,长宁县,4,511500\n511525,高县,4,511500\n511526,珙县,4,511500\n511527,筠连县,4,511500\n511528,兴文县,4,511500\n511529,屏山县,4,511500\n511602,广安区,4,511600\n511603,前锋区,4,511600\n511621,岳池县,4,511600\n511622,武胜县,4,511600\n511623,邻水县,4,511600\n511681,华蓥市,4,511600\n511702,通川区,4,511700\n511703,达川区,4,511700\n511722,宣汉县,4,511700\n511723,开江县,4,511700\n511724,大竹县,4,511700\n511725,渠县,4,511700\n511771,达州经济开发区,4,511700\n511781,万源市,4,511700\n511802,雨城区,4,511800\n511803,名山区,4,511800\n511822,荥经县,4,511800\n511823,汉源县,4,511800\n511824,石棉县,4,511800\n511825,天全县,4,511800\n511826,芦山县,4,511800\n511827,宝兴县,4,511800\n511902,巴州区,4,511900\n511903,恩阳区,4,511900\n511921,通江县,4,511900\n511922,南江县,4,511900\n511923,平昌县,4,511900\n511971,巴中经济开发区,4,511900\n512002,雁江区,4,512000\n512021,安岳县,4,512000\n512022,乐至县,4,512000\n513201,马尔康市,4,513200\n513221,汶川县,4,513200\n513222,理县,4,513200\n513223,茂县,4,513200\n513224,松潘县,4,513200\n513225,九寨沟县,4,513200\n513226,金川县,4,513200\n513227,小金县,4,513200\n513228,黑水县,4,513200\n513230,壤塘县,4,513200\n513231,阿坝县,4,513200\n513232,若尔盖县,4,513200\n513233,红原县,4,513200\n513301,康定市,4,513300\n513322,泸定县,4,513300\n513323,丹巴县,4,513300\n513324,九龙县,4,513300\n513325,雅江县,4,513300\n513326,道孚县,4,513300\n513327,炉霍县,4,513300\n513328,甘孜县,4,513300\n513329,新龙县,4,513300\n513330,德格县,4,513300\n513331,白玉县,4,513300\n513332,石渠县,4,513300\n513333,色达县,4,513300\n513334,理塘县,4,513300\n513335,巴塘县,4,513300\n513336,乡城县,4,513300\n513337,稻城县,4,513300\n513338,得荣县,4,513300\n513401,西昌市,4,513400\n513402,会理市,4,513400\n513422,木里藏族自治县,4,513400\n513423,盐源县,4,513400\n513424,德昌县,4,513400\n513426,会东县,4,513400\n513427,宁南县,4,513400\n513428,普格县,4,513400\n513429,布拖县,4,513400\n513430,金阳县,4,513400\n513431,昭觉县,4,513400\n513432,喜德县,4,513400\n513433,冕宁县,4,513400\n513434,越西县,4,513400\n513435,甘洛县,4,513400\n513436,美姑县,4,513400\n513437,雷波县,4,513400\n520102,南明区,4,520100\n520103,云岩区,4,520100\n520111,花溪区,4,520100\n520112,乌当区,4,520100\n520113,白云区,4,520100\n520115,观山湖区,4,520100\n520121,开阳县,4,520100\n520122,息烽县,4,520100\n520123,修文县,4,520100\n520181,清镇市,4,520100\n520201,钟山区,4,520200\n520203,六枝特区,4,520200\n520204,水城区,4,520200\n520281,盘州市,4,520200\n520302,红花岗区,4,520300\n520303,汇川区,4,520300\n520304,播州区,4,520300\n520322,桐梓县,4,520300\n520323,绥阳县,4,520300\n520324,正安县,4,520300\n520325,道真仡佬族苗族自治县,4,520300\n520326,务川仡佬族苗族自治县,4,520300\n520327,凤冈县,4,520300\n520328,湄潭县,4,520300\n520329,余庆县,4,520300\n520330,习水县,4,520300\n520381,赤水市,4,520300\n520382,仁怀市,4,520300\n520402,西秀区,4,520400\n520403,平坝区,4,520400\n520422,普定县,4,520400\n520423,镇宁布依族苗族自治县,4,520400\n520424,关岭布依族苗族自治县,4,520400\n520425,紫云苗族布依族自治县,4,520400\n520502,七星关区,4,520500\n520521,大方县,4,520500\n520523,金沙县,4,520500\n520524,织金县,4,520500\n520525,纳雍县,4,520500\n520526,威宁彝族回族苗族自治县,4,520500\n520527,赫章县,4,520500\n520581,黔西市,4,520500\n520602,碧江区,4,520600\n520603,万山区,4,520600\n520621,江口县,4,520600\n520622,玉屏侗族自治县,4,520600\n520623,石阡县,4,520600\n520624,思南县,4,520600\n520625,印江土家族苗族自治县,4,520600\n520626,德江县,4,520600\n520627,沿河土家族自治县,4,520600\n520628,松桃苗族自治县,4,520600\n522301,兴义市,4,522300\n522302,兴仁市,4,522300\n522323,普安县,4,522300\n522324,晴隆县,4,522300\n522325,贞丰县,4,522300\n522326,望谟县,4,522300\n522327,册亨县,4,522300\n522328,安龙县,4,522300\n522601,凯里市,4,522600\n522622,黄平县,4,522600\n522623,施秉县,4,522600\n522624,三穗县,4,522600\n522625,镇远县,4,522600\n522626,岑巩县,4,522600\n522627,天柱县,4,522600\n522628,锦屏县,4,522600\n522629,剑河县,4,522600\n522630,台江县,4,522600\n522631,黎平县,4,522600\n522632,榕江县,4,522600\n522633,从江县,4,522600\n522634,雷山县,4,522600\n522635,麻江县,4,522600\n522636,丹寨县,4,522600\n522701,都匀市,4,522700\n522702,福泉市,4,522700\n522722,荔波县,4,522700\n522723,贵定县,4,522700\n522725,瓮安县,4,522700\n522726,独山县,4,522700\n522727,平塘县,4,522700\n522728,罗甸县,4,522700\n522729,长顺县,4,522700\n522730,龙里县,4,522700\n522731,惠水县,4,522700\n522732,三都水族自治县,4,522700\n530102,五华区,4,530100\n530103,盘龙区,4,530100\n530111,官渡区,4,530100\n530112,西山区,4,530100\n530113,东川区,4,530100\n530114,呈贡区,4,530100\n530115,晋宁区,4,530100\n530124,富民县,4,530100\n530125,宜良县,4,530100\n530126,石林彝族自治县,4,530100\n530127,嵩明县,4,530100\n530128,禄劝彝族苗族自治县,4,530100\n530129,寻甸回族彝族自治县,4,530100\n530181,安宁市,4,530100\n530302,麒麟区,4,530300\n530303,沾益区,4,530300\n530304,马龙区,4,530300\n530322,陆良县,4,530300\n530323,师宗县,4,530300\n530324,罗平县,4,530300\n530325,富源县,4,530300\n530326,会泽县,4,530300\n530381,宣威市,4,530300\n530402,红塔区,4,530400\n530403,江川区,4,530400\n530423,通海县,4,530400\n530424,华宁县,4,530400\n530425,易门县,4,530400\n530426,峨山彝族自治县,4,530400\n530427,新平彝族傣族自治县,4,530400\n530428,元江哈尼族彝族傣族自治县,4,530400\n530481,澄江市,4,530400\n530502,隆阳区,4,530500\n530521,施甸县,4,530500\n530523,龙陵县,4,530500\n530524,昌宁县,4,530500\n530581,腾冲市,4,530500\n530602,昭阳区,4,530600\n530621,鲁甸县,4,530600\n530622,巧家县,4,530600\n530623,盐津县,4,530600\n530624,大关县,4,530600\n530625,永善县,4,530600\n530626,绥江县,4,530600\n530627,镇雄县,4,530600\n530628,彝良县,4,530600\n530629,威信县,4,530600\n530681,水富市,4,530600\n530702,古城区,4,530700\n530721,玉龙纳西族自治县,4,530700\n530722,永胜县,4,530700\n530723,华坪县,4,530700\n530724,宁蒗彝族自治县,4,530700\n530802,思茅区,4,530800\n530821,宁洱哈尼族彝族自治县,4,530800\n530822,墨江哈尼族自治县,4,530800\n530823,景东彝族自治县,4,530800\n530824,景谷傣族彝族自治县,4,530800\n530825,镇沅彝族哈尼族拉祜族自治县,4,530800\n530826,江城哈尼族彝族自治县,4,530800\n530827,孟连傣族拉祜族佤族自治县,4,530800\n530828,澜沧拉祜族自治县,4,530800\n530829,西盟佤族自治县,4,530800\n530902,临翔区,4,530900\n530921,凤庆县,4,530900\n530922,云县,4,530900\n530923,永德县,4,530900\n530924,镇康县,4,530900\n530925,双江拉祜族佤族布朗族傣族自治县,4,530900\n530926,耿马傣族佤族自治县,4,530900\n530927,沧源佤族自治县,4,530900\n532301,楚雄市,4,532300\n532302,禄丰市,4,532300\n532322,双柏县,4,532300\n532323,牟定县,4,532300\n532324,南华县,4,532300\n532325,姚安县,4,532300\n532326,大姚县,4,532300\n532327,永仁县,4,532300\n532328,元谋县,4,532300\n532329,武定县,4,532300\n532501,个旧市,4,532500\n532502,开远市,4,532500\n532503,蒙自市,4,532500\n532504,弥勒市,4,532500\n532523,屏边苗族自治县,4,532500\n532524,建水县,4,532500\n532525,石屏县,4,532500\n532527,泸西县,4,532500\n532528,元阳县,4,532500\n532529,红河县,4,532500\n532530,金平苗族瑶族傣族自治县,4,532500\n532531,绿春县,4,532500\n532532,河口瑶族自治县,4,532500\n532601,文山市,4,532600\n532622,砚山县,4,532600\n532623,西畴县,4,532600\n532624,麻栗坡县,4,532600\n532625,马关县,4,532600\n532626,丘北县,4,532600\n532627,广南县,4,532600\n532628,富宁县,4,532600\n532801,景洪市,4,532800\n532822,勐海县,4,532800\n532823,勐腊县,4,532800\n532901,大理市,4,532900\n532922,漾濞彝族自治县,4,532900\n532923,祥云县,4,532900\n532924,宾川县,4,532900\n532925,弥渡县,4,532900\n532926,南涧彝族自治县,4,532900\n532927,巍山彝族回族自治县,4,532900\n532928,永平县,4,532900\n532929,云龙县,4,532900\n532930,洱源县,4,532900\n532931,剑川县,4,532900\n532932,鹤庆县,4,532900\n533102,瑞丽市,4,533100\n533103,芒市,4,533100\n533122,梁河县,4,533100\n533123,盈江县,4,533100\n533124,陇川县,4,533100\n533301,泸水市,4,533300\n533323,福贡县,4,533300\n533324,贡山独龙族怒族自治县,4,533300\n533325,兰坪白族普米族自治县,4,533300\n533401,香格里拉市,4,533400\n533422,德钦县,4,533400\n533423,维西傈僳族自治县,4,533400\n540102,城关区,4,540100\n540103,堆龙德庆区,4,540100\n540104,达孜区,4,540100\n540121,林周县,4,540100\n540122,当雄县,4,540100\n540123,尼木县,4,540100\n540124,曲水县,4,540100\n540127,墨竹工卡县,4,540100\n540171,格尔木藏青工业园区,4,540100\n540172,拉萨经济技术开发区,4,540100\n540173,西藏文化旅游创意园区,4,540100\n540174,达孜工业园区,4,540100\n540202,桑珠孜区,4,540200\n540221,南木林县,4,540200\n540222,江孜县,4,540200\n540223,定日县,4,540200\n540224,萨迦县,4,540200\n540225,拉孜县,4,540200\n540226,昂仁县,4,540200\n540227,谢通门县,4,540200\n540228,白朗县,4,540200\n540229,仁布县,4,540200\n540230,康马县,4,540200\n540231,定结县,4,540200\n540232,仲巴县,4,540200\n540233,亚东县,4,540200\n540234,吉隆县,4,540200\n540235,聂拉木县,4,540200\n540236,萨嘎县,4,540200\n540237,岗巴县,4,540200\n540302,卡若区,4,540300\n540321,江达县,4,540300\n540322,贡觉县,4,540300\n540323,类乌齐县,4,540300\n540324,丁青县,4,540300\n540325,察雅县,4,540300\n540326,八宿县,4,540300\n540327,左贡县,4,540300\n540328,芒康县,4,540300\n540329,洛隆县,4,540300\n540330,边坝县,4,540300\n540402,巴宜区,4,540400\n540421,工布江达县,4,540400\n540422,米林县,4,540400\n540423,墨脱县,4,540400\n540424,波密县,4,540400\n540425,察隅县,4,540400\n540426,朗县,4,540400\n540502,乃东区,4,540500\n540521,扎囊县,4,540500\n540522,贡嘎县,4,540500\n540523,桑日县,4,540500\n540524,琼结县,4,540500\n540525,曲松县,4,540500\n540526,措美县,4,540500\n540527,洛扎县,4,540500\n540528,加查县,4,540500\n540529,隆子县,4,540500\n540530,错那县,4,540500\n540531,浪卡子县,4,540500\n540602,色尼区,4,540600\n540621,嘉黎县,4,540600\n540622,比如县,4,540600\n540623,聂荣县,4,540600\n540624,安多县,4,540600\n540625,申扎县,4,540600\n540626,索县,4,540600\n540627,班戈县,4,540600\n540628,巴青县,4,540600\n540629,尼玛县,4,540600\n540630,双湖县,4,540600\n542521,普兰县,4,542500\n542522,札达县,4,542500\n542523,噶尔县,4,542500\n542524,日土县,4,542500\n542525,革吉县,4,542500\n542526,改则县,4,542500\n542527,措勤县,4,542500\n610102,新城区,4,610100\n610103,碑林区,4,610100\n610104,莲湖区,4,610100\n610111,灞桥区,4,610100\n610112,未央区,4,610100\n610113,雁塔区,4,610100\n610114,阎良区,4,610100\n610115,临潼区,4,610100\n610116,长安区,4,610100\n610117,高陵区,4,610100\n610118,鄠邑区,4,610100\n610122,蓝田县,4,610100\n610124,周至县,4,610100\n610202,王益区,4,610200\n610203,印台区,4,610200\n610204,耀州区,4,610200\n610222,宜君县,4,610200\n610302,渭滨区,4,610300\n610303,金台区,4,610300\n610304,陈仓区,4,610300\n610305,凤翔区,4,610300\n610323,岐山县,4,610300\n610324,扶风县,4,610300\n610326,眉县,4,610300\n610327,陇县,4,610300\n610328,千阳县,4,610300\n610329,麟游县,4,610300\n610330,凤县,4,610300\n610331,太白县,4,610300\n610402,秦都区,4,610400\n610403,杨陵区,4,610400\n610404,渭城区,4,610400\n610422,三原县,4,610400\n610423,泾阳县,4,610400\n610424,乾县,4,610400\n610425,礼泉县,4,610400\n610426,永寿县,4,610400\n610428,长武县,4,610400\n610429,旬邑县,4,610400\n610430,淳化县,4,610400\n610431,武功县,4,610400\n610481,兴平市,4,610400\n610482,彬州市,4,610400\n610502,临渭区,4,610500\n610503,华州区,4,610500\n610522,潼关县,4,610500\n610523,大荔县,4,610500\n610524,合阳县,4,610500\n610525,澄城县,4,610500\n610526,蒲城县,4,610500\n610527,白水县,4,610500\n610528,富平县,4,610500\n610581,韩城市,4,610500\n610582,华阴市,4,610500\n610602,宝塔区,4,610600\n610603,安塞区,4,610600\n610621,延长县,4,610600\n610622,延川县,4,610600\n610625,志丹县,4,610600\n610626,吴起县,4,610600\n610627,甘泉县,4,610600\n610628,富县,4,610600\n610629,洛川县,4,610600\n610630,宜川县,4,610600\n610631,黄龙县,4,610600\n610632,黄陵县,4,610600\n610681,子长市,4,610600\n610702,汉台区,4,610700\n610703,南郑区,4,610700\n610722,城固县,4,610700\n610723,洋县,4,610700\n610724,西乡县,4,610700\n610725,勉县,4,610700\n610726,宁强县,4,610700\n610727,略阳县,4,610700\n610728,镇巴县,4,610700\n610729,留坝县,4,610700\n610730,佛坪县,4,610700\n610802,榆阳区,4,610800\n610803,横山区,4,610800\n610822,府谷县,4,610800\n610824,靖边县,4,610800\n610825,定边县,4,610800\n610826,绥德县,4,610800\n610827,米脂县,4,610800\n610828,佳县,4,610800\n610829,吴堡县,4,610800\n610830,清涧县,4,610800\n610831,子洲县,4,610800\n610881,神木市,4,610800\n610902,汉滨区,4,610900\n610921,汉阴县,4,610900\n610922,石泉县,4,610900\n610923,宁陕县,4,610900\n610924,紫阳县,4,610900\n610925,岚皋县,4,610900\n610926,平利县,4,610900\n610927,镇坪县,4,610900\n610929,白河县,4,610900\n610981,旬阳市,4,610900\n611002,商州区,4,611000\n611021,洛南县,4,611000\n611022,丹凤县,4,611000\n611023,商南县,4,611000\n611024,山阳县,4,611000\n611025,镇安县,4,611000\n611026,柞水县,4,611000\n620102,城关区,4,620100\n620103,七里河区,4,620100\n620104,西固区,4,620100\n620105,安宁区,4,620100\n620111,红古区,4,620100\n620121,永登县,4,620100\n620122,皋兰县,4,620100\n620123,榆中县,4,620100\n620171,兰州新区,4,620100\n620201,嘉峪关市,4,620200\n620302,金川区,4,620300\n620321,永昌县,4,620300\n620402,白银区,4,620400\n620403,平川区,4,620400\n620421,靖远县,4,620400\n620422,会宁县,4,620400\n620423,景泰县,4,620400\n620502,秦州区,4,620500\n620503,麦积区,4,620500\n620521,清水县,4,620500\n620522,秦安县,4,620500\n620523,甘谷县,4,620500\n620524,武山县,4,620500\n620525,张家川回族自治县,4,620500\n620602,凉州区,4,620600\n620621,民勤县,4,620600\n620622,古浪县,4,620600\n620623,天祝藏族自治县,4,620600\n620702,甘州区,4,620700\n620721,肃南裕固族自治县,4,620700\n620722,民乐县,4,620700\n620723,临泽县,4,620700\n620724,高台县,4,620700\n620725,山丹县,4,620700\n620802,崆峒区,4,620800\n620821,泾川县,4,620800\n620822,灵台县,4,620800\n620823,崇信县,4,620800\n620825,庄浪县,4,620800\n620826,静宁县,4,620800\n620881,华亭市,4,620800\n620902,肃州区,4,620900\n620921,金塔县,4,620900\n620922,瓜州县,4,620900\n620923,肃北蒙古族自治县,4,620900\n620924,阿克塞哈萨克族自治县,4,620900\n620981,玉门市,4,620900\n620982,敦煌市,4,620900\n621002,西峰区,4,621000\n621021,庆城县,4,621000\n621022,环县,4,621000\n621023,华池县,4,621000\n621024,合水县,4,621000\n621025,正宁县,4,621000\n621026,宁县,4,621000\n621027,镇原县,4,621000\n621102,安定区,4,621100\n621121,通渭县,4,621100\n621122,陇西县,4,621100\n621123,渭源县,4,621100\n621124,临洮县,4,621100\n621125,漳县,4,621100\n621126,岷县,4,621100\n621202,武都区,4,621200\n621221,成县,4,621200\n621222,文县,4,621200\n621223,宕昌县,4,621200\n621224,康县,4,621200\n621225,西和县,4,621200\n621226,礼县,4,621200\n621227,徽县,4,621200\n621228,两当县,4,621200\n622901,临夏市,4,622900\n622921,临夏县,4,622900\n622922,康乐县,4,622900\n622923,永靖县,4,622900\n622924,广河县,4,622900\n622925,和政县,4,622900\n622926,东乡族自治县,4,622900\n622927,积石山保安族东乡族撒拉族自治县,4,622900\n623001,合作市,4,623000\n623021,临潭县,4,623000\n623022,卓尼县,4,623000\n623023,舟曲县,4,623000\n623024,迭部县,4,623000\n623025,玛曲县,4,623000\n623026,碌曲县,4,623000\n623027,夏河县,4,623000\n630102,城东区,4,630100\n630103,城中区,4,630100\n630104,城西区,4,630100\n630105,城北区,4,630100\n630106,湟中区,4,630100\n630121,大通回族土族自治县,4,630100\n630123,湟源县,4,630100\n630202,乐都区,4,630200\n630203,平安区,4,630200\n630222,民和回族土族自治县,4,630200\n630223,互助土族自治县,4,630200\n630224,化隆回族自治县,4,630200\n630225,循化撒拉族自治县,4,630200\n632221,门源回族自治县,4,632200\n632222,祁连县,4,632200\n632223,海晏县,4,632200\n632224,刚察县,4,632200\n632301,同仁市,4,632300\n632322,尖扎县,4,632300\n632323,泽库县,4,632300\n632324,河南蒙古族自治县,4,632300\n632521,共和县,4,632500\n632522,同德县,4,632500\n632523,贵德县,4,632500\n632524,兴海县,4,632500\n632525,贵南县,4,632500\n632621,玛沁县,4,632600\n632622,班玛县,4,632600\n632623,甘德县,4,632600\n632624,达日县,4,632600\n632625,久治县,4,632600\n632626,玛多县,4,632600\n632701,玉树市,4,632700\n632722,杂多县,4,632700\n632723,称多县,4,632700\n632724,治多县,4,632700\n632725,囊谦县,4,632700\n632726,曲麻莱县,4,632700\n632801,格尔木市,4,632800\n632802,德令哈市,4,632800\n632803,茫崖市,4,632800\n632821,乌兰县,4,632800\n632822,都兰县,4,632800\n632823,天峻县,4,632800\n632857,大柴旦行政委员会,4,632800\n640104,兴庆区,4,640100\n640105,西夏区,4,640100\n640106,金凤区,4,640100\n640121,永宁县,4,640100\n640122,贺兰县,4,640100\n640181,灵武市,4,640100\n640202,大武口区,4,640200\n640205,惠农区,4,640200\n640221,平罗县,4,640200\n640302,利通区,4,640300\n640303,红寺堡区,4,640300\n640323,盐池县,4,640300\n640324,同心县,4,640300\n640381,青铜峡市,4,640300\n640402,原州区,4,640400\n640422,西吉县,4,640400\n640423,隆德县,4,640400\n640424,泾源县,4,640400\n640425,彭阳县,4,640400\n640502,沙坡头区,4,640500\n640521,中宁县,4,640500\n640522,海原县,4,640500\n650102,天山区,4,650100\n650103,沙依巴克区,4,650100\n650104,新市区,4,650100\n650105,水磨沟区,4,650100\n650106,头屯河区,4,650100\n650107,达坂城区,4,650100\n650109,米东区,4,650100\n650121,乌鲁木齐县,4,650100\n650202,独山子区,4,650200\n650203,克拉玛依区,4,650200\n650204,白碱滩区,4,650200\n650205,乌尔禾区,4,650200\n650402,高昌区,4,650400\n650421,鄯善县,4,650400\n650422,托克逊县,4,650400\n650502,伊州区,4,650500\n650521,巴里坤哈萨克自治县,4,650500\n650522,伊吾县,4,650500\n652301,昌吉市,4,652300\n652302,阜康市,4,652300\n652323,呼图壁县,4,652300\n652324,玛纳斯县,4,652300\n652325,奇台县,4,652300\n652327,吉木萨尔县,4,652300\n652328,木垒哈萨克自治县,4,652300\n652701,博乐市,4,652700\n652702,阿拉山口市,4,652700\n652722,精河县,4,652700\n652723,温泉县,4,652700\n652801,库尔勒市,4,652800\n652822,轮台县,4,652800\n652823,尉犁县,4,652800\n652824,若羌县,4,652800\n652825,且末县,4,652800\n652826,焉耆回族自治县,4,652800\n652827,和静县,4,652800\n652828,和硕县,4,652800\n652829,博湖县,4,652800\n652871,库尔勒经济技术开发区,4,652800\n652901,阿克苏市,4,652900\n652902,库车市,4,652900\n652922,温宿县,4,652900\n652924,沙雅县,4,652900\n652925,新和县,4,652900\n652926,拜城县,4,652900\n652927,乌什县,4,652900\n652928,阿瓦提县,4,652900\n652929,柯坪县,4,652900\n653001,阿图什市,4,653000\n653022,阿克陶县,4,653000\n653023,阿合奇县,4,653000\n653024,乌恰县,4,653000\n653101,喀什市,4,653100\n653121,疏附县,4,653100\n653122,疏勒县,4,653100\n653123,英吉沙县,4,653100\n653124,泽普县,4,653100\n653125,莎车县,4,653100\n653126,叶城县,4,653100\n653127,麦盖提县,4,653100\n653128,岳普湖县,4,653100\n653129,伽师县,4,653100\n653130,巴楚县,4,653100\n653131,塔什库尔干塔吉克自治县,4,653100\n653201,和田市,4,653200\n653221,和田县,4,653200\n653222,墨玉县,4,653200\n653223,皮山县,4,653200\n653224,洛浦县,4,653200\n653225,策勒县,4,653200\n653226,于田县,4,653200\n653227,民丰县,4,653200\n654002,伊宁市,4,654000\n654003,奎屯市,4,654000\n654004,霍尔果斯市,4,654000\n654021,伊宁县,4,654000\n654022,察布查尔锡伯自治县,4,654000\n654023,霍城县,4,654000\n654024,巩留县,4,654000\n654025,新源县,4,654000\n654026,昭苏县,4,654000\n654027,特克斯县,4,654000\n654028,尼勒克县,4,654000\n654201,塔城市,4,654200\n654202,乌苏市,4,654200\n654203,沙湾市,4,654200\n654221,额敏县,4,654200\n654224,托里县,4,654200\n654225,裕民县,4,654200\n654226,和布克赛尔蒙古自治县,4,654200\n654301,阿勒泰市,4,654300\n654321,布尔津县,4,654300\n654322,富蕴县,4,654300\n654323,福海县,4,654300\n654324,哈巴河县,4,654300\n654325,青河县,4,654300\n654326,吉木乃县,4,654300\n659001,石河子市,4,659000\n659002,阿拉尔市,4,659000\n659003,图木舒克市,4,659000\n659004,五家渠市,4,659000\n659005,北屯市,4,659000\n659006,铁门关市,4,659000\n659007,双河市,4,659000\n659008,可克达拉市,4,659000\n659009,昆玉市,4,659000\n659010,胡杨河市,4,659000\n659011,新星市,4,659000"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-ip/src/test/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtilsTest.java",
    "content": "package cn.iocoder.yudao.framework.ip.core.utils;\n\n\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * {@link AreaUtils} 的单元测试\n *\n * @author 芋道源码\n */\npublic class AreaUtilsTest {\n\n    @Test\n    public void testGetArea() {\n        // 调用：北京\n        Area area = AreaUtils.getArea(110100);\n        // 断言\n        assertEquals(area.getId(), 110100);\n        assertEquals(area.getName(), \"北京市\");\n        assertEquals(area.getType(), AreaTypeEnum.CITY.getType());\n        assertEquals(area.getParent().getId(), 110000);\n        assertEquals(area.getChildren().size(), 16);\n    }\n\n    @Test\n    public void testFormat() {\n        assertEquals(AreaUtils.format(110105), \"北京市 北京市 朝阳区\");\n        assertEquals(AreaUtils.format(1), \"中国\");\n        assertEquals(AreaUtils.format(2), \"蒙古\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-ip/src/test/java/cn/iocoder/yudao/framework/ip/core/utils/IPUtilsTest.java",
    "content": "package cn.iocoder.yudao.framework.ip.core.utils;\n\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport org.junit.jupiter.api.Test;\nimport org.lionsoul.ip2region.xdb.Searcher;\n\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * {@link IPUtils} 的单元测试\n *\n * @author wanglhup\n */\npublic class IPUtilsTest {\n\n    @Test\n    public void testGetAreaId_string() {\n        // 120.202.4.0|120.202.4.255|420600\n        Integer areaId = IPUtils.getAreaId(\"120.202.4.50\");\n        assertEquals(420600, areaId);\n    }\n\n    @Test\n    public void testGetAreaId_long() throws Exception {\n        // 120.203.123.0|120.203.133.255|360900\n        long ip = Searcher.checkIP(\"120.203.123.250\");\n        Integer areaId = IPUtils.getAreaId(ip);\n        assertEquals(360900, areaId);\n    }\n\n    @Test\n    public void testGetArea_string() {\n        // 120.202.4.0|120.202.4.255|420600\n        Area area = IPUtils.getArea(\"120.202.4.50\");\n        assertEquals(\"襄阳市\", area.getName());\n    }\n\n    @Test\n    public void testGetArea_long() throws Exception {\n        // 120.203.123.0|120.203.133.255|360900\n        long ip = Searcher.checkIP(\"120.203.123.252\");\n        Area area = IPUtils.getArea(ip);\n        assertEquals(\"宜春市\", area.getName());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao-framework</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>多租户</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- Job 定时任务相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-job</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- 消息队列相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mq</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.amqp</groupId>\n            <artifactId>spring-rabbit</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.rocketmq</groupId>\n            <artifactId>rocketmq-spring-boot-starter</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java",
    "content": "package cn.iocoder.yudao.framework.tenant.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * 多租户配置\n *\n * @author 芋道源码\n */\n@ConfigurationProperties(prefix = \"yudao.tenant\")\n@Data\npublic class TenantProperties {\n\n    /**\n     * 租户是否开启\n     */\n    private static final Boolean ENABLE_DEFAULT = true;\n\n    /**\n     * 是否开启\n     */\n    private Boolean enable = ENABLE_DEFAULT;\n\n    /**\n     * 需要忽略多租户的请求\n     *\n     * 默认情况下，每个请求需要带上 tenant-id 的请求头。但是，部分请求是无需带上的，例如说短信回调、支付回调等 Open API！\n     */\n    private Set<String> ignoreUrls = new HashSet<>();\n\n    /**\n     * 需要忽略跨（切换）租户访问的请求\n     *\n     * 原因是：某些接口，访问的是个人信息，在跨租户是获取不到的！\n     */\n    private Set<String> ignoreVisitUrls = Collections.emptySet();\n\n    /**\n     * 需要忽略多租户的表\n     *\n     * 即默认所有表都开启多租户的功能，所以记得添加对应的 tenant_id 字段哟\n     */\n    private Set<String> ignoreTables = Collections.emptySet();\n\n    /**\n     * 需要忽略多租户的 Spring Cache 缓存\n     *\n     * 即默认所有缓存都开启多租户的功能，所以记得添加对应的 tenant_id 字段哟\n     */\n    private Set<String> ignoreCaches = Collections.emptySet();\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.tenant.config;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.biz.system.tenant.TenantCommonApi;\nimport cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;\nimport cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties;\nimport cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;\nimport cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;\nimport cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect;\nimport cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq.TenantRabbitMQInitializer;\nimport cn.iocoder.yudao.framework.tenant.core.mq.redis.TenantRedisMessageInterceptor;\nimport cn.iocoder.yudao.framework.tenant.core.mq.rocketmq.TenantRocketMQInitializer;\nimport cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager;\nimport cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;\nimport cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;\nimport cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkServiceImpl;\nimport cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;\nimport cn.iocoder.yudao.framework.tenant.core.web.TenantVisitContextInterceptor;\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.data.redis.cache.BatchStrategies;\nimport org.springframework.data.redis.cache.RedisCacheConfiguration;\nimport org.springframework.data.redis.cache.RedisCacheManager;\nimport org.springframework.data.redis.cache.RedisCacheWriter;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\nimport org.springframework.web.servlet.mvc.method.RequestMappingInfo;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;\nimport org.springframework.web.util.pattern.PathPattern;\n\nimport javax.annotation.Resource;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@AutoConfiguration\n@ConditionalOnProperty(prefix = \"yudao.tenant\", value = \"enable\", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户\n@EnableConfigurationProperties(TenantProperties.class)\npublic class YudaoTenantAutoConfiguration {\n\n    @Resource\n    private ApplicationContext applicationContext;\n\n    @Bean\n    public TenantFrameworkService tenantFrameworkService(TenantCommonApi tenantApi) {\n        // 参见 https://gitee.com/zhijiantianya/yudao-cloud/issues/IC6YZF\n        try {\n            TenantCommonApi tenantApiImpl = SpringUtil.getBean(\"tenantApiImpl\", TenantCommonApi.class);\n            if (tenantApiImpl != null) {\n                tenantApi = tenantApiImpl;\n            }\n        } catch (Exception ignored) {}\n        return new TenantFrameworkServiceImpl(tenantApi);\n    }\n\n    // ========== AOP ==========\n\n    @Bean\n    public TenantIgnoreAspect tenantIgnoreAspect() {\n        return new TenantIgnoreAspect();\n    }\n\n    // ========== DB ==========\n\n    @Bean\n    public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties,\n                                                                 MybatisPlusInterceptor interceptor) {\n        TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties));\n        // 添加到 interceptor 中\n        // 需要加在首个，主要是为了在分页插件前面。这个是 MyBatis Plus 的规定\n        MyBatisUtils.addInterceptor(interceptor, inner, 0);\n        return inner;\n    }\n\n    // ========== WEB ==========\n\n    @Bean\n    public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() {\n        FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>();\n        registrationBean.setFilter(new TenantContextWebFilter());\n        registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER);\n        return registrationBean;\n    }\n\n    @Bean\n    public TenantVisitContextInterceptor tenantVisitContextInterceptor(TenantProperties tenantProperties,\n                                                                       SecurityFrameworkService securityFrameworkService) {\n        return new TenantVisitContextInterceptor(tenantProperties, securityFrameworkService);\n    }\n\n    @Bean\n    public WebMvcConfigurer tenantWebMvcConfigurer(TenantProperties tenantProperties,\n                                                   TenantVisitContextInterceptor tenantVisitContextInterceptor) {\n        return new WebMvcConfigurer() {\n\n            @Override\n            public void addInterceptors(InterceptorRegistry registry) {\n                registry.addInterceptor(tenantVisitContextInterceptor)\n                        .excludePathPatterns(tenantProperties.getIgnoreVisitUrls().toArray(new String[0]));\n            }\n        };\n    }\n\n    // ========== Security ==========\n\n    @Bean\n    public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter(TenantProperties tenantProperties,\n                                                                                   WebProperties webProperties,\n                                                                                   GlobalExceptionHandler globalExceptionHandler,\n                                                                                   TenantFrameworkService tenantFrameworkService) {\n        FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();\n        registrationBean.setFilter(new TenantSecurityWebFilter(webProperties, tenantProperties, getTenantIgnoreUrls(),\n                globalExceptionHandler, tenantFrameworkService));\n        registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER);\n        return registrationBean;\n    }\n\n    /**\n     * 如果 Controller 接口上，有 {@link TenantIgnore} 注解，则添加到忽略租户的 URL 集合中\n     *\n     * @return 忽略租户的 URL 集合\n     */\n    private Set<String> getTenantIgnoreUrls() {\n        Set<String> ignoreUrls = new HashSet<>();\n        // 获得接口对应的 HandlerMethod 集合\n        RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)\n                applicationContext.getBean(\"requestMappingHandlerMapping\");\n        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();\n        // 获得有 @TenantIgnore 注解的接口\n        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {\n            HandlerMethod handlerMethod = entry.getValue();\n            if (!handlerMethod.hasMethodAnnotation(TenantIgnore.class) // 方法级\n                && !handlerMethod.getBeanType().isAnnotationPresent(TenantIgnore.class)) { // 接口级\n                continue;\n            }\n            // 添加到忽略的 URL 中\n            if (entry.getKey().getPatternsCondition() != null) {\n                ignoreUrls.addAll(entry.getKey().getPatternsCondition().getPatterns());\n            }\n            if (entry.getKey().getPathPatternsCondition() != null) {\n                ignoreUrls.addAll(\n                        convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));\n            }\n        }\n        return ignoreUrls;\n    }\n\n    // ========== MQ ==========\n\n    /**\n     * 多租户 Redis 消息队列的配置类\n     *\n     * 为什么要单独一个配置类呢？如果直接把 TenantRedisMessageInterceptor Bean 的初始化放外面，会报 RedisMessageInterceptor 类不存在的错误\n     */\n    @Configuration\n    @ConditionalOnClass(name = \"cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate\")\n    public static class TenantRedisMQAutoConfiguration {\n\n        @Bean\n        public TenantRedisMessageInterceptor tenantRedisMessageInterceptor() {\n            return new TenantRedisMessageInterceptor();\n        }\n\n    }\n\n    @Bean\n    @ConditionalOnClass(name = \"org.springframework.amqp.rabbit.core.RabbitTemplate\")\n    public TenantRabbitMQInitializer tenantRabbitMQInitializer() {\n        return new TenantRabbitMQInitializer();\n    }\n\n    @Bean\n    @ConditionalOnClass(name = \"org.apache.rocketmq.spring.core.RocketMQTemplate\")\n    public TenantRocketMQInitializer tenantRocketMQInitializer() {\n        return new TenantRocketMQInitializer();\n    }\n\n    // ========== Job ==========\n\n    @Bean\n    @ConditionalOnClass(name = \"com.xxl.job.core.handler.annotation.XxlJob\")\n    public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) {\n        return new TenantJobAspect(tenantFrameworkService);\n    }\n\n    // ========== Redis ==========\n\n    @Bean\n    @Primary // 引入租户时，tenantRedisCacheManager 为主 Bean\n    public RedisCacheManager tenantRedisCacheManager(RedisTemplate<String, Object> redisTemplate,\n                                                     RedisCacheConfiguration redisCacheConfiguration,\n                                                     YudaoCacheProperties yudaoCacheProperties,\n                                                     TenantProperties tenantProperties) {\n        // 创建 RedisCacheWriter 对象\n        RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());\n        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,\n                BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));\n        // 创建 TenantRedisCacheManager 对象\n        return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration, tenantProperties.getIgnoreCaches());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantRpcAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.tenant.config;\n\nimport cn.iocoder.yudao.framework.tenant.core.rpc.TenantRequestInterceptor;\nimport cn.iocoder.yudao.framework.common.biz.system.tenant.TenantCommonApi;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Bean;\n\n@AutoConfiguration\n@ConditionalOnProperty(prefix = \"yudao.tenant\", value = \"enable\", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户\n@EnableFeignClients(clients = TenantCommonApi.class) // 主要是引入相关的 API 服务\npublic class YudaoTenantRpcAutoConfiguration {\n\n    @Bean\n    public TenantRequestInterceptor tenantRequestInterceptor() {\n        return new TenantRequestInterceptor();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/TenantIgnore.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.aop;\n\nimport cn.iocoder.yudao.framework.tenant.config.TenantProperties;\n\nimport java.lang.annotation.*;\n\n/**\n * 忽略租户，标记指定方法不进行租户的自动过滤\n *\n * 注意，只有 DB 的场景会过滤，其它场景暂时不过滤：\n * 1、Redis 场景：因为是基于 Key 实现多租户的能力，所以忽略没有意义，不像 DB 是一个 column 实现的\n * 2、MQ 场景：有点难以抉择，目前可以通过 Consumer 手动在消费的方法上，添加 @TenantIgnore 进行忽略\n *\n * 特殊：\n * 1、如果添加到 Controller 类上，则该 URL 自动添加到 {@link TenantProperties#getIgnoreUrls()} 中\n * 2、如果添加到 DO 实体类上，则它对应的表名“相当于”自动添加到 {@link TenantProperties#getIgnoreTables()} 中\n *\n * @author 芋道源码\n */\n@Target({ElementType.METHOD, ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\npublic @interface TenantIgnore {\n\n    /**\n     * 是否开启忽略租户，默认为 true 开启\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则满足条件，进行租户的忽略\n     */\n    String enable() default \"true\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/TenantIgnoreAspect.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.aop;\n\nimport cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\n\n/**\n * 忽略多租户的 Aspect，基于 {@link TenantIgnore} 注解实现，用于一些全局的逻辑。\n * 例如说，一个定时任务，读取所有数据，进行处理。\n * 又例如说，读取所有数据，进行缓存。\n *\n * 整体逻辑的实现，和 {@link TenantUtils#executeIgnore(Runnable)} 需要保持一致\n *\n * @author 芋道源码\n */\n@Aspect\n@Slf4j\npublic class TenantIgnoreAspect {\n\n    @Around(\"@annotation(tenantIgnore)\")\n    public Object around(ProceedingJoinPoint joinPoint, TenantIgnore tenantIgnore) throws Throwable {\n        Boolean oldIgnore = TenantContextHolder.isIgnore();\n        try {\n            // 计算条件，满足的情况下，才进行忽略\n            Object enable = SpringExpressionUtils.parseExpression(tenantIgnore.enable());\n            if (Boolean.TRUE.equals(enable)) {\n                TenantContextHolder.setIgnore(true);\n            }\n\n            // 执行逻辑\n            return joinPoint.proceed();\n        } finally {\n            TenantContextHolder.setIgnore(oldIgnore);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.context;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.enums.DocumentEnum;\nimport com.alibaba.ttl.TransmittableThreadLocal;\n\n/**\n * 多租户上下文 Holder\n *\n * @author 芋道源码\n */\npublic class TenantContextHolder {\n\n    /**\n     * 当前租户编号\n     */\n    private static final ThreadLocal<Long> TENANT_ID = new TransmittableThreadLocal<>();\n\n    /**\n     * 是否忽略租户\n     */\n    private static final ThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>();\n\n    /**\n     * 获得租户编号\n     *\n     * @return 租户编号\n     */\n    public static Long getTenantId() {\n        return TENANT_ID.get();\n    }\n\n    /**\n     * 获得租户编号。如果不存在，则抛出 NullPointerException 异常\n     *\n     * @return 租户编号\n     */\n    public static Long getRequiredTenantId() {\n        Long tenantId = getTenantId();\n        if (tenantId == null) {\n            throw new NullPointerException(\"TenantContextHolder 不存在租户编号！可参考文档：\"\n                + DocumentEnum.TENANT.getUrl());\n        }\n        return tenantId;\n    }\n\n    public static void setTenantId(Long tenantId) {\n        TENANT_ID.set(tenantId);\n    }\n\n    public static void setIgnore(Boolean ignore) {\n        IGNORE.set(ignore);\n    }\n\n    /**\n     * 当前是否忽略租户\n     *\n     * @return 是否忽略\n     */\n    public static boolean isIgnore() {\n        return Boolean.TRUE.equals(IGNORE.get());\n    }\n\n    public static void clear() {\n        TENANT_ID.remove();\n        IGNORE.remove();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantBaseDO.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.db;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n * 拓展多租户的 BaseDO 基类\n *\n * @author 芋道源码\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic abstract class TenantBaseDO extends BaseDO {\n\n    /**\n     * 多租户编号\n     */\n    private Long tenantId;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.db;\n\nimport cn.iocoder.yudao.framework.tenant.config.TenantProperties;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.LongValue;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 基于 MyBatis Plus 多租户的功能，实现 DB 层面的多租户的功能\n *\n * @author 芋道源码\n */\npublic class TenantDatabaseInterceptor implements TenantLineHandler {\n\n    /**\n     * 忽略的表\n     *\n     * KEY：表名\n     * VALUE：是否忽略\n     */\n    private final Map<String, Boolean> ignoreTables = new HashMap<>();\n\n    public TenantDatabaseInterceptor(TenantProperties properties) {\n        // 不同 DB 下，大小写的习惯不同，所以需要都添加进去\n        properties.getIgnoreTables().forEach(table -> {\n            addIgnoreTable(table, true);\n        });\n        // 在 OracleKeyGenerator 中，生成主键时，会查询这个表，查询这个表后，会自动拼接 TENANT_ID 导致报错\n        addIgnoreTable(\"DUAL\", true);\n    }\n\n    @Override\n    public Expression getTenantId() {\n        return new LongValue(TenantContextHolder.getRequiredTenantId());\n    }\n\n    @Override\n    public boolean ignoreTable(String tableName) {\n        // 情况一，全局忽略多租户\n        if (TenantContextHolder.isIgnore()) {\n            return true;\n        }\n        // 情况二，忽略多租户的表\n        tableName = SqlParserUtils.removeWrapperSymbol(tableName);\n        Boolean ignore = ignoreTables.get(tableName.toLowerCase());\n        if (ignore == null) {\n            ignore = computeIgnoreTable(tableName);\n            synchronized (ignoreTables) {\n                addIgnoreTable(tableName, ignore);\n            }\n        }\n        return ignore;\n    }\n\n    private void addIgnoreTable(String tableName, boolean ignore) {\n        ignoreTables.put(tableName.toLowerCase(), ignore);\n        ignoreTables.put(tableName.toUpperCase(), ignore);\n    }\n\n    private boolean computeIgnoreTable(String tableName) {\n        // 找不到的表，说明不是 yudao 项目里的，不进行拦截（忽略租户）\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);\n        if (tableInfo == null) {\n            return true;\n        }\n        // 如果继承了 TenantBaseDO 基类，显然不忽略租户\n        if (TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) {\n            return false;\n        }\n        // 如果添加了 @TenantIgnore 注解，则忽略租户\n        TenantIgnore tenantIgnore = tableInfo.getEntityType().getAnnotation(TenantIgnore.class);\n        return tenantIgnore != null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJob.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.job;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 多租户 Job 注解\n */\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface TenantJob {\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobAspect.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.job;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.exceptions.ExceptionUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport com.xxl.job.core.context.XxlJobContext;\nimport com.xxl.job.core.context.XxlJobHelper;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * 多租户 JobHandler AOP\n * 任务执行时，会按照租户逐个执行 Job 的逻辑\n *\n * 注意，需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时，之前执行成功的租户也会再次执行。\n *\n * @author 芋道源码\n */\n@Aspect\n@RequiredArgsConstructor\n@Slf4j\npublic class TenantJobAspect {\n\n    private final TenantFrameworkService tenantFrameworkService;\n\n    @Around(\"@annotation(tenantJob)\")\n    public void around(ProceedingJoinPoint joinPoint, TenantJob tenantJob) {\n        // 获得租户列表\n        List<Long> tenantIds = tenantFrameworkService.getTenantIds();\n        if (CollUtil.isEmpty(tenantIds)) {\n            return;\n        }\n\n        // 逐个租户，执行 Job\n        Map<Long, String> results = new ConcurrentHashMap<>();\n        AtomicBoolean success = new AtomicBoolean(true); // 标记，是否存在失败的情况\n        XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext(); // XXL-Job 上下文\n        tenantIds.parallelStream().forEach(tenantId -> {\n            // TODO 芋艿：先通过 parallel 实现并行；1）多个租户，是一条执行日志；2）异常的情况\n            TenantUtils.execute(tenantId, () -> {\n                try {\n                    XxlJobContext.setXxlJobContext(xxlJobContext);\n                    // 执行 Job\n                    Object result = joinPoint.proceed();\n                    results.put(tenantId, StrUtil.toStringOrEmpty(result));\n                } catch (Throwable e) {\n                    results.put(tenantId, ExceptionUtil.getRootCauseMessage(e));\n                    success.set(false);\n                    // 打印异常\n                    XxlJobHelper.log(StrUtil.format(\"[多租户({}) 执行任务({})，发生异常：{}]\",\n                            tenantId, joinPoint.getSignature(), ExceptionUtils.getStackTrace(e)));\n                }\n            });\n        });\n        // 记录执行结果\n        if (success.get()) {\n            XxlJobHelper.handleSuccess(JsonUtils.toJsonString(results));\n        } else {\n            XxlJobHelper.handleFail(JsonUtils.toJsonString(results));\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.mq.kafka;\n\nimport cn.hutool.core.util.StrUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.env.EnvironmentPostProcessor;\nimport org.springframework.core.env.ConfigurableEnvironment;\n\n/**\n * 多租户的 Kafka 的 {@link EnvironmentPostProcessor} 实现类\n *\n * Kafka Producer 发送消息时，增加 {@link TenantKafkaProducerInterceptor} 拦截器\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class TenantKafkaEnvironmentPostProcessor implements EnvironmentPostProcessor {\n\n    private static final String PROPERTY_KEY_INTERCEPTOR_CLASSES = \"spring.kafka.producer.properties.interceptor.classes\";\n\n    @Override\n    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {\n        // 添加 TenantKafkaProducerInterceptor 拦截器\n        try {\n            String value = environment.getProperty(PROPERTY_KEY_INTERCEPTOR_CLASSES);\n            if (StrUtil.isEmpty(value)) {\n                value = TenantKafkaProducerInterceptor.class.getName();\n            } else {\n                value += \",\" + TenantKafkaProducerInterceptor.class.getName();\n            }\n            environment.getSystemProperties().put(PROPERTY_KEY_INTERCEPTOR_CLASSES, value);\n        } catch (NoClassDefFoundError ignore) {\n            // 如果触发 NoClassDefFoundError 异常，说明 TenantKafkaProducerInterceptor 类不存在，即没引入 kafka-spring 依赖\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaProducerInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.mq.kafka;\n\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport org.apache.kafka.clients.producer.ProducerInterceptor;\nimport org.apache.kafka.clients.producer.ProducerRecord;\nimport org.apache.kafka.clients.producer.RecordMetadata;\nimport org.apache.kafka.common.header.Headers;\nimport org.springframework.messaging.handler.invocation.InvocableHandlerMethod;\n\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * Kafka 消息队列的多租户 {@link ProducerInterceptor} 实现类\n *\n * 1. Producer 发送消息时，将 {@link TenantContextHolder} 租户编号，添加到消息的 Header 中\n * 2. Consumer 消费消息时，将消息的 Header 的租户编号，添加到 {@link TenantContextHolder} 中，通过 {@link InvocableHandlerMethod} 实现\n *\n * @author 芋道源码\n */\npublic class TenantKafkaProducerInterceptor implements ProducerInterceptor<Object, Object> {\n\n    @Override\n    public ProducerRecord<Object, Object> onSend(ProducerRecord<Object, Object> record) {\n        Long tenantId = TenantContextHolder.getTenantId();\n        if (tenantId != null) {\n            Headers headers = (Headers) ReflectUtil.getFieldValue(record, \"headers\"); // private 属性，没有 get 方法，智能反射\n            headers.add(HEADER_TENANT_ID, tenantId.toString().getBytes());\n        }\n        return record;\n    }\n\n    @Override\n    public void onAcknowledgement(RecordMetadata metadata, Exception exception) {\n    }\n\n    @Override\n    public void close() {\n    }\n\n    @Override\n    public void configure(Map<String, ?> configs) {\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq;\n\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\n\n/**\n * 多租户的 RabbitMQ 初始化器\n *\n * @author 芋道源码\n */\npublic class TenantRabbitMQInitializer implements BeanPostProcessor {\n\n    @Override\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n        if (bean instanceof RabbitTemplate) {\n            RabbitTemplate rabbitTemplate = (RabbitTemplate) bean;\n            rabbitTemplate.addBeforePublishPostProcessors(new TenantRabbitMQMessagePostProcessor());\n        }\n        return bean;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQMessagePostProcessor.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq;\n\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport org.apache.kafka.clients.producer.ProducerInterceptor;\nimport org.springframework.amqp.AmqpException;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.core.MessagePostProcessor;\nimport org.springframework.messaging.handler.invocation.InvocableHandlerMethod;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * RabbitMQ 消息队列的多租户 {@link ProducerInterceptor} 实现类\n *\n * 1. Producer 发送消息时，将 {@link TenantContextHolder} 租户编号，添加到消息的 Header 中\n * 2. Consumer 消费消息时，将消息的 Header 的租户编号，添加到 {@link TenantContextHolder} 中，通过 {@link InvocableHandlerMethod} 实现\n *\n * @author 芋道源码\n */\npublic class TenantRabbitMQMessagePostProcessor implements MessagePostProcessor {\n\n    @Override\n    public Message postProcessMessage(Message message) throws AmqpException {\n        Long tenantId = TenantContextHolder.getTenantId();\n        if (tenantId != null) {\n            message.getMessageProperties().getHeaders().put(HEADER_TENANT_ID, tenantId);\n        }\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/redis/TenantRedisMessageInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.mq.redis;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor;\nimport cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * 多租户 {@link AbstractRedisMessage} 拦截器\n *\n * 1. Producer 发送消息时，将 {@link TenantContextHolder} 租户编号，添加到消息的 Header 中\n * 2. Consumer 消费消息时，将消息的 Header 的租户编号，添加到 {@link TenantContextHolder} 中\n *\n * @author 芋道源码\n */\npublic class TenantRedisMessageInterceptor implements RedisMessageInterceptor {\n\n    @Override\n    public void sendMessageBefore(AbstractRedisMessage message) {\n        Long tenantId = TenantContextHolder.getTenantId();\n        if (tenantId != null) {\n            message.addHeader(HEADER_TENANT_ID, tenantId.toString());\n        }\n    }\n\n    @Override\n    public void consumeMessageBefore(AbstractRedisMessage message) {\n        String tenantIdStr = message.getHeader(HEADER_TENANT_ID);\n        if (StrUtil.isNotEmpty(tenantIdStr)) {\n            TenantContextHolder.setTenantId(Long.valueOf(tenantIdStr));\n        }\n    }\n\n    @Override\n    public void consumeMessageAfter(AbstractRedisMessage message) {\n        // 注意，Consumer 是一个逻辑的入口，所以不考虑原本上下文就存在租户编号的情况\n        TenantContextHolder.clear();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQConsumeMessageHook.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport org.apache.rocketmq.client.hook.ConsumeMessageContext;\nimport org.apache.rocketmq.client.hook.ConsumeMessageHook;\nimport org.apache.rocketmq.common.message.MessageExt;\nimport org.springframework.messaging.handler.invocation.InvocableHandlerMethod;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * RocketMQ 消息队列的多租户 {@link ConsumeMessageHook} 实现类\n *\n * Consumer 消费消息时，将消息的 Header 的租户编号，添加到 {@link TenantContextHolder} 中，通过 {@link InvocableHandlerMethod} 实现\n *\n * @author 芋道源码\n */\npublic class TenantRocketMQConsumeMessageHook implements ConsumeMessageHook {\n\n    @Override\n    public String hookName() {\n        return getClass().getSimpleName();\n    }\n\n    @Override\n    public void consumeMessageBefore(ConsumeMessageContext context) {\n        // 校验，消息必须是单条，不然设置租户可能不正确\n        List<MessageExt> messages = context.getMsgList();\n        Assert.isTrue(messages.size() == 1, \"消息条数({})不正确\", messages.size());\n        // 设置租户编号\n        String tenantId = messages.get(0).getUserProperty(HEADER_TENANT_ID);\n        if (StrUtil.isNotEmpty(tenantId)) {\n            TenantContextHolder.setTenantId(Long.parseLong(tenantId));\n        }\n    }\n\n    @Override\n    public void consumeMessageAfter(ConsumeMessageContext context) {\n        TenantContextHolder.clear();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq;\n\nimport org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;\nimport org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl;\nimport org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl;\nimport org.apache.rocketmq.client.producer.DefaultMQProducer;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\n\n/**\n * 多租户的 RocketMQ 初始化器\n *\n * @author 芋道源码\n */\npublic class TenantRocketMQInitializer implements BeanPostProcessor {\n\n    @Override\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n        if (bean instanceof DefaultRocketMQListenerContainer) {\n            DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean;\n            initTenantConsumer(container.getConsumer());\n        } else if (bean instanceof RocketMQTemplate) {\n            RocketMQTemplate template = (RocketMQTemplate) bean;\n            initTenantProducer(template.getProducer());\n        }\n        return bean;\n    }\n\n    private void initTenantProducer(DefaultMQProducer producer) {\n        if (producer == null) {\n            return;\n        }\n        DefaultMQProducerImpl producerImpl = producer.getDefaultMQProducerImpl();\n        if (producerImpl == null) {\n            return;\n        }\n        producerImpl.registerSendMessageHook(new TenantRocketMQSendMessageHook());\n    }\n\n    private void initTenantConsumer(DefaultMQPushConsumer consumer) {\n        if (consumer == null) {\n            return;\n        }\n        DefaultMQPushConsumerImpl consumerImpl = consumer.getDefaultMQPushConsumerImpl();\n        if (consumerImpl == null) {\n            return;\n        }\n        consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQSendMessageHook.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq;\n\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport org.apache.rocketmq.client.hook.SendMessageContext;\nimport org.apache.rocketmq.client.hook.SendMessageHook;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * RocketMQ 消息队列的多租户 {@link SendMessageHook} 实现类\n *\n * Producer 发送消息时，将 {@link TenantContextHolder} 租户编号，添加到消息的 Header 中\n *\n * @author 芋道源码\n */\npublic class TenantRocketMQSendMessageHook implements SendMessageHook {\n\n    @Override\n    public String hookName() {\n        return getClass().getSimpleName();\n    }\n\n    @Override\n    public void sendMessageBefore(SendMessageContext sendMessageContext) {\n        Long tenantId = TenantContextHolder.getTenantId();\n        if (tenantId == null) {\n            return;\n        }\n        sendMessageContext.getMessage().putUserProperty(HEADER_TENANT_ID, tenantId.toString());\n    }\n\n    @Override\n    public void sendMessageAfter(SendMessageContext sendMessageContext) {\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.redis;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cache.Cache;\nimport org.springframework.data.redis.cache.RedisCacheConfiguration;\nimport org.springframework.data.redis.cache.RedisCacheManager;\nimport org.springframework.data.redis.cache.RedisCacheWriter;\n\nimport java.util.Set;\n\n/**\n * 多租户的 {@link RedisCacheManager} 实现类\n *\n * 操作指定 name 的 {@link Cache} 时，自动拼接租户后缀，格式为 name + \":\" + tenantId + 后缀\n *\n * @author airhead\n */\n@Slf4j\npublic class TenantRedisCacheManager extends TimeoutRedisCacheManager {\n\n    private static final String SPLIT = \"#\";\n\n    private final Set<String> ignoreCaches;\n\n    public TenantRedisCacheManager(RedisCacheWriter cacheWriter,\n                                   RedisCacheConfiguration defaultCacheConfiguration,\n                                   Set<String> ignoreCaches) {\n        super(cacheWriter, defaultCacheConfiguration);\n        this.ignoreCaches = ignoreCaches;\n    }\n\n    @Override\n    public Cache getCache(String name) {\n        String[] names = StrUtil.splitToArray(name, SPLIT);\n        // 如果开启多租户，则 name 拼接租户后缀\n        if (!TenantContextHolder.isIgnore()\n                && TenantContextHolder.getTenantId() != null\n                && !CollUtil.contains(ignoreCaches, names[0])) {\n            name = name + \":\" + TenantContextHolder.getTenantId();\n        }\n\n        // 继续基于父方法\n        return super.getCache(name);\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/rpc/TenantRequestInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.rpc;\n\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport feign.RequestInterceptor;\nimport feign.RequestTemplate;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * Tenant 的 RequestInterceptor 实现类：Feign 请求时，将 {@link TenantContextHolder} 设置到 header 中，继续透传给被调用的服务\n *\n * @author 芋道源码\n */\npublic class TenantRequestInterceptor implements RequestInterceptor {\n\n    @Override\n    public void apply(RequestTemplate requestTemplate) {\n        Long tenantId = TenantContextHolder.getTenantId();\n        if (tenantId != null) {\n            requestTemplate.header(HEADER_TENANT_ID, String.valueOf(tenantId));\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.security;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.framework.tenant.config.TenantProperties;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter;\nimport cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.util.AntPathMatcher;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.Objects;\nimport java.util.Set;\n\n/**\n * 多租户 Security Web 过滤器\n * 1. 如果是登陆的用户，校验是否有权限访问该租户，避免越权问题。\n * 2. 如果请求未带租户的编号，检查是否是忽略的 URL，否则也不允许访问。\n * 3. 校验租户是合法，例如说被禁用、到期\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class TenantSecurityWebFilter extends ApiRequestFilter {\n\n    private final TenantProperties tenantProperties;\n\n    /**\n     * 允许忽略租户的 URL 列表\n     *\n     * 目的：解决 <a href=\"https://gitee.com/zhijiantianya/yudao-cloud/issues/ICUQL9\">修改配置会导致 @TenantIgnore Controller 接口过滤失效</>\n     */\n    private final Set<String> ignoreUrls;\n\n    private final AntPathMatcher pathMatcher;\n\n    private final GlobalExceptionHandler globalExceptionHandler;\n    private final TenantFrameworkService tenantFrameworkService;\n\n    public TenantSecurityWebFilter(WebProperties webProperties,\n                                   TenantProperties tenantProperties,\n                                   Set<String> ignoreUrls,\n                                   GlobalExceptionHandler globalExceptionHandler,\n                                   TenantFrameworkService tenantFrameworkService) {\n        super(webProperties);\n        this.tenantProperties = tenantProperties;\n        this.ignoreUrls = ignoreUrls;\n        this.pathMatcher = new AntPathMatcher();\n        this.globalExceptionHandler = globalExceptionHandler;\n        this.tenantFrameworkService = tenantFrameworkService;\n    }\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        Long tenantId = TenantContextHolder.getTenantId();\n        // 1. 登陆的用户，校验是否有权限访问该租户，避免越权问题。\n        LoginUser user = SecurityFrameworkUtils.getLoginUser();\n        if (user != null) {\n            // 如果获取不到租户编号，则尝试使用登陆用户的租户编号\n            if (tenantId == null) {\n                tenantId = user.getTenantId();\n                TenantContextHolder.setTenantId(tenantId);\n                // 如果传递了租户编号，则进行比对租户编号，避免越权问题\n            } else if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId())) {\n                log.error(\"[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]\",\n                        user.getTenantId(), user.getId(), user.getUserType(),\n                        TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod());\n                ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.FORBIDDEN.getCode(),\n                        \"您无权访问该租户的数据\"));\n                return;\n            }\n        }\n\n        // 如果非允许忽略租户的 URL，则校验租户是否合法\n        if (!isIgnoreUrl(request)) {\n            // 2. 如果请求未带租户的编号，不允许访问。\n            if (tenantId == null) {\n                log.error(\"[doFilterInternal][URL({}/{}) 未传递租户编号]\", request.getRequestURI(), request.getMethod());\n                ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),\n                        \"请求的租户标识未传递，请进行排查\"));\n                return;\n            }\n            // 3. 校验租户是合法，例如说被禁用、到期\n            try {\n                tenantFrameworkService.validTenant(tenantId);\n            } catch (Throwable ex) {\n                CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);\n                ServletUtils.writeJSON(response, result);\n                return;\n            }\n        } else { // 如果是允许忽略租户的 URL，若未传递租户编号，则默认忽略租户编号，避免报错\n            if (tenantId == null) {\n                TenantContextHolder.setIgnore(true);\n            }\n        }\n\n        // 继续过滤\n        chain.doFilter(request, response);\n    }\n\n    private boolean isIgnoreUrl(HttpServletRequest request) {\n        String apiUri = request.getRequestURI().substring(request.getContextPath().length());\n        // 快速匹配，保证性能\n        if (CollUtil.contains(tenantProperties.getIgnoreUrls(), apiUri)\n                || CollUtil.contains(ignoreUrls, apiUri)) {\n            return true;\n        }\n        // 逐个 Ant 路径匹配\n        for (String url : tenantProperties.getIgnoreUrls()) {\n            if (pathMatcher.match(url, apiUri)) {\n                return true;\n            }\n        }\n        for (String url : ignoreUrls) {\n            if (pathMatcher.match(url, apiUri)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkService.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.service;\n\nimport java.util.List;\n\n/**\n * Tenant 框架 Service 接口，定义获取租户信息\n *\n * @author 芋道源码\n */\npublic interface TenantFrameworkService {\n\n    /**\n     * 获得所有租户\n     *\n     * @return 租户编号数组\n     */\n    List<Long> getTenantIds();\n\n    /**\n     * 校验租户是否合法\n     *\n     * @param id 租户编号\n     */\n    void validTenant(Long id);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkServiceImpl.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.service;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.biz.system.tenant.TenantCommonApi;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport lombok.RequiredArgsConstructor;\nimport lombok.SneakyThrows;\n\nimport java.time.Duration;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;\n\n/**\n * Tenant 框架 Service 实现类\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\npublic class TenantFrameworkServiceImpl implements TenantFrameworkService {\n\n    private final TenantCommonApi tenantApi;\n\n    /**\n     * 针对 {@link #getTenantIds()} 的缓存\n     */\n    private final LoadingCache<Object, List<Long>> getTenantIdsCache = buildAsyncReloadingCache(\n            Duration.ofMinutes(1L), // 过期时间 1 分钟\n            new CacheLoader<Object, List<Long>>() {\n\n                @Override\n                public List<Long> load(Object key) {\n                    return tenantApi.getTenantIdList().getCheckedData();\n                }\n\n            });\n\n    /**\n     * 针对 {@link #validTenant(Long)} 的缓存\n     */\n    private final LoadingCache<Long, CommonResult<Boolean>> validTenantCache = buildAsyncReloadingCache(\n            Duration.ofMinutes(1L), // 过期时间 1 分钟\n            new CacheLoader<Long, CommonResult<Boolean>>() {\n\n                @Override\n                public CommonResult<Boolean> load(Long id) {\n                    return tenantApi.validTenant(id);\n                }\n\n            });\n\n    @Override\n    @SneakyThrows\n    public List<Long> getTenantIds() {\n        return getTenantIdsCache.get(Boolean.TRUE);\n    }\n\n    @Override\n    @SneakyThrows\n    public void validTenant(Long id) {\n        validTenantCache.get(id).checkError();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.util;\n\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\n\nimport java.util.Map;\nimport java.util.concurrent.Callable;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * 多租户 Util\n *\n * @author 芋道源码\n */\npublic class TenantUtils {\n\n    /**\n     * 使用指定租户，执行对应的逻辑\n     *\n     * 注意，如果当前是忽略租户的情况下，会被强制设置成不忽略租户\n     * 当然，执行完成后，还是会恢复回去\n     *\n     * @param tenantId 租户编号\n     * @param runnable 逻辑\n     */\n    public static void execute(Long tenantId, Runnable runnable) {\n        Long oldTenantId = TenantContextHolder.getTenantId();\n        Boolean oldIgnore = TenantContextHolder.isIgnore();\n        try {\n            TenantContextHolder.setTenantId(tenantId);\n            TenantContextHolder.setIgnore(false);\n            // 执行逻辑\n            runnable.run();\n        } finally {\n            TenantContextHolder.setTenantId(oldTenantId);\n            TenantContextHolder.setIgnore(oldIgnore);\n        }\n    }\n\n    /**\n     * 使用指定租户，执行对应的逻辑\n     *\n     * 注意，如果当前是忽略租户的情况下，会被强制设置成不忽略租户\n     * 当然，执行完成后，还是会恢复回去\n     *\n     * @param tenantId 租户编号\n     * @param callable 逻辑\n     * @return 结果\n     */\n    public static <V> V execute(Long tenantId, Callable<V> callable) {\n        Long oldTenantId = TenantContextHolder.getTenantId();\n        Boolean oldIgnore = TenantContextHolder.isIgnore();\n        try {\n            TenantContextHolder.setTenantId(tenantId);\n            TenantContextHolder.setIgnore(false);\n            // 执行逻辑\n            return callable.call();\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        } finally {\n            TenantContextHolder.setTenantId(oldTenantId);\n            TenantContextHolder.setIgnore(oldIgnore);\n        }\n    }\n\n    /**\n     * 忽略租户，执行对应的逻辑\n     *\n     * @param runnable 逻辑\n     */\n    public static void executeIgnore(Runnable runnable) {\n        Boolean oldIgnore = TenantContextHolder.isIgnore();\n        try {\n            TenantContextHolder.setIgnore(true);\n            // 执行逻辑\n            runnable.run();\n        } finally {\n            TenantContextHolder.setIgnore(oldIgnore);\n        }\n    }\n\n    /**\n     * 忽略租户，执行对应的逻辑\n     *\n     * @param callable 逻辑\n     * @return 结果\n     */\n    public static <V> V executeIgnore(Callable<V> callable) {\n        Boolean oldIgnore = TenantContextHolder.isIgnore();\n        try {\n            TenantContextHolder.setIgnore(true);\n            // 执行逻辑\n            return callable.call();\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        } finally {\n            TenantContextHolder.setIgnore(oldIgnore);\n        }\n    }\n\n    /**\n     * 将多租户编号，添加到 header 中\n     *\n     * @param headers HTTP 请求 headers\n     * @param tenantId 租户编号\n     */\n    public static void addTenantHeader(Map<String, String> headers, Long tenantId) {\n        if (tenantId != null) {\n            headers.put(HEADER_TENANT_ID, tenantId.toString());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantContextWebFilter.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.web;\n\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * 多租户 Context Web 过滤器\n * 将请求 Header 中的 tenant-id 解析出来，添加到 {@link TenantContextHolder} 中，这样后续的 DB 等操作，可以获得到租户编号。\n *\n * @author 芋道源码\n */\npublic class TenantContextWebFilter extends OncePerRequestFilter {\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        // 设置\n        Long tenantId = WebFrameworkUtils.getTenantId(request);\n        if (tenantId != null) {\n            TenantContextHolder.setTenantId(tenantId);\n        }\n        try {\n            chain.doFilter(request, response);\n        } finally {\n            // 清理\n            TenantContextHolder.clear();\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantVisitContextInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.tenant.core.web;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.framework.tenant.config.TenantProperties;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.servlet.HandlerInterceptor;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;\n\n@RequiredArgsConstructor\n@Slf4j\npublic class TenantVisitContextInterceptor implements HandlerInterceptor {\n\n    private static final String PERMISSION = \"system:tenant:visit\";\n\n    private final TenantProperties tenantProperties;\n\n    private final SecurityFrameworkService securityFrameworkService;\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n        // 如果和当前租户编号一致，则直接跳过\n        Long visitTenantId = WebFrameworkUtils.getVisitTenantId(request);\n        if (visitTenantId == null) {\n            return true;\n        }\n        if (ObjUtil.equal(visitTenantId, TenantContextHolder.getTenantId())) {\n            return true;\n        }\n        // 必须是登录用户\n        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();\n        if (loginUser == null) {\n            return true;\n        }\n\n        // 校验用户是否可切换租户\n        if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) {\n            throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), \"您无权切换租户\");\n        }\n\n        // 【重点】切换租户编号\n        loginUser.setVisitTenantId(visitTenantId);\n        TenantContextHolder.setTenantId(visitTenantId);\n        return true;\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {\n        // 【重点】清理切换，换回原租户编号\n        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();\n        if (loginUser != null && loginUser.getTenantId() != null) {\n            TenantContextHolder.setTenantId(loginUser.getTenantId());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java",
    "content": "/**\n * 多租户，支持如下层面：\n * 1. DB：基于 MyBatis Plus 多租户的功能实现。\n * 2. Redis：通过在 Redis Key 上拼接租户编号的方式，进行隔离。\n * 3. Web：请求 HTTP API 时，解析 Header 的 tenant-id 租户编号，添加到租户上下文。\n * 4. Security：校验当前登陆的用户，是否越权访问其它租户的数据。\n * 5. Job：在 JobHandler 执行任务时，会按照每个租户，都独立并行执行一次。\n * 6. MQ：在 Producer 发送消息时，Header 带上 tenant-id 租户编号；在 Consumer 消费消息时，将 Header 的 tenant-id 租户编号，添加到租户上下文。\n * 7. Async：异步需要保证 ThreadLocal 的传递性，通过使用阿里开源的 TransmittableThreadLocal 实现。相关的改造点，可见：\n *      1）Spring Async：\n *          {@link cn.iocoder.yudao.framework.quartz.config.YudaoAsyncAutoConfiguration#threadPoolTaskExecutorBeanPostProcessor()}\n *      2）Spring Security：\n *          TransmittableThreadLocalSecurityContextHolderStrategy\n *          和 YudaoSecurityAutoConfiguration#securityContextHolderMethodInvokingFactoryBean() 方法\n *\n */\npackage cn.iocoder.yudao.framework.tenant;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java",
    "content": "/*\n * Copyright 2002-2021 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.messaging.handler.invocation;\n\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport org.springframework.core.DefaultParameterNameDiscoverer;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.lang.Nullable;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.handler.HandlerMethod;\nimport org.springframework.util.ObjectUtils;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Type;\nimport java.util.Arrays;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * Extension of {@link HandlerMethod} that invokes the underlying method with\n * argument values resolved from the current HTTP request through a list of\n * {@link HandlerMethodArgumentResolver}.\n *\n * 针对 rabbitmq-spring 和 kafka-spring，不存在合适的拓展点，可以实现 Consumer 消费前，读取 Header 中的 tenant-id 设置到 {@link TenantContextHolder} 中\n * TODO 芋艿：持续跟进，看看有没新的拓展点\n *\n * @author Rossen Stoyanchev\n * @author Juergen Hoeller\n * @since 4.0\n */\npublic class InvocableHandlerMethod extends HandlerMethod {\n\n    private static final Object[] EMPTY_ARGS = new Object[0];\n\n    private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();\n\n    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();\n\n    /**\n     * Create an instance from a {@code HandlerMethod}.\n     */\n    public InvocableHandlerMethod(HandlerMethod handlerMethod) {\n        super(handlerMethod);\n    }\n\n    /**\n     * Create an instance from a bean instance and a method.\n     */\n    public InvocableHandlerMethod(Object bean, Method method) {\n        super(bean, method);\n    }\n\n    /**\n     * Construct a new handler method with the given bean instance, method name and parameters.\n     * @param bean the object bean\n     * @param methodName the method name\n     * @param parameterTypes the method parameter types\n     * @throws NoSuchMethodException when the method cannot be found\n     */\n    public InvocableHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)\n            throws NoSuchMethodException {\n\n        super(bean, methodName, parameterTypes);\n    }\n\n    /**\n     * Set {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} to use for resolving method argument values.\n     */\n    public void setMessageMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) {\n        this.resolvers = argumentResolvers;\n    }\n\n    /**\n     * Set the ParameterNameDiscoverer for resolving parameter names when needed\n     * (e.g. default request attribute name).\n     * <p>Default is a {@link DefaultParameterNameDiscoverer}.\n     */\n    public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {\n        this.parameterNameDiscoverer = parameterNameDiscoverer;\n    }\n\n    /**\n     * Invoke the method after resolving its argument values in the context of the given message.\n     * <p>Argument values are commonly resolved through\n     * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.\n     * The {@code providedArgs} parameter however may supply argument values to be used directly,\n     * i.e. without argument resolution.\n     * <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the\n     * resolved arguments.\n     * @param message the current message being processed\n     * @param providedArgs \"given\" arguments matched by type, not resolved\n     * @return the raw value returned by the invoked method\n     * @throws Exception raised if no suitable argument resolver can be found,\n     * or if the method raised an exception\n     * @see #getMethodArgumentValues\n     * @see #doInvoke\n     */\n    @Nullable\n    public Object invoke(Message<?> message, Object... providedArgs) throws Exception {\n        Object[] args = getMethodArgumentValues(message, providedArgs);\n        if (logger.isTraceEnabled()) {\n            logger.trace(\"Arguments: \" + Arrays.toString(args));\n        }\n        // 注意：如下是本类的改动点！！！\n        // 情况一：无租户编号的情况\n        Long tenantId= parseTenantId(message);\n        if (tenantId == null) {\n            return doInvoke(args);\n        }\n        // 情况二：有租户的情况下\n        return TenantUtils.execute(tenantId, () -> doInvoke(args));\n    }\n\n    private Long parseTenantId(Message<?> message) {\n        Object tenantId = message.getHeaders().get(HEADER_TENANT_ID);\n        if (tenantId == null) {\n            return null;\n        }\n        if (tenantId instanceof Long) {\n            return (Long) tenantId;\n        }\n        if (tenantId instanceof Number) {\n            return ((Number) tenantId).longValue();\n        }\n        if (tenantId instanceof String) {\n            return Long.parseLong((String) tenantId);\n        }\n        if (tenantId instanceof byte[]) {\n            return Long.parseLong(new String((byte[]) tenantId));\n        }\n        throw new IllegalArgumentException(\"未知的数据类型：\" + tenantId);\n    }\n\n    /**\n     * Get the method argument values for the current message, checking the provided\n     * argument values and falling back to the configured argument resolvers.\n     * <p>The resulting array will be passed into {@link #doInvoke}.\n     * @since 5.1.2\n     */\n    protected Object[] getMethodArgumentValues(Message<?> message, Object... providedArgs) throws Exception {\n        MethodParameter[] parameters = getMethodParameters();\n        if (ObjectUtils.isEmpty(parameters)) {\n            return EMPTY_ARGS;\n        }\n\n        Object[] args = new Object[parameters.length];\n        for (int i = 0; i < parameters.length; i++) {\n            MethodParameter parameter = parameters[i];\n            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);\n            args[i] = findProvidedArgument(parameter, providedArgs);\n            if (args[i] != null) {\n                continue;\n            }\n            if (!this.resolvers.supportsParameter(parameter)) {\n                throw new MethodArgumentResolutionException(\n                        message, parameter, formatArgumentError(parameter, \"No suitable resolver\"));\n            }\n            try {\n                args[i] = this.resolvers.resolveArgument(parameter, message);\n            }\n            catch (Exception ex) {\n                // Leave stack trace for later, exception may actually be resolved and handled...\n                if (logger.isDebugEnabled()) {\n                    String exMsg = ex.getMessage();\n                    if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {\n                        logger.debug(formatArgumentError(parameter, exMsg));\n                    }\n                }\n                throw ex;\n            }\n        }\n        return args;\n    }\n\n    /**\n     * Invoke the handler method with the given argument values.\n     */\n    @Nullable\n    protected Object doInvoke(Object... args) throws Exception {\n        try {\n            return getBridgedMethod().invoke(getBean(), args);\n        }\n        catch (IllegalArgumentException ex) {\n            assertTargetBean(getBridgedMethod(), getBean(), args);\n            String text = (ex.getMessage() != null ? ex.getMessage() : \"Illegal argument\");\n            throw new IllegalStateException(formatInvokeError(text, args), ex);\n        }\n        catch (InvocationTargetException ex) {\n            // Unwrap for HandlerExceptionResolvers ...\n            Throwable targetException = ex.getTargetException();\n            if (targetException instanceof RuntimeException) {\n                throw (RuntimeException) targetException;\n            }\n            else if (targetException instanceof Error) {\n                throw (Error) targetException;\n            }\n            else if (targetException instanceof Exception) {\n                throw (Exception) targetException;\n            }\n            else {\n                throw new IllegalStateException(formatInvokeError(\"Invocation failure\", args), targetException);\n            }\n        }\n    }\n\n    MethodParameter getAsyncReturnValueType(@Nullable Object returnValue) {\n        return new AsyncResultMethodParameter(returnValue);\n    }\n\n    private class AsyncResultMethodParameter extends HandlerMethodParameter {\n\n        @Nullable\n        private final Object returnValue;\n\n        private final ResolvableType returnType;\n\n        public AsyncResultMethodParameter(@Nullable Object returnValue) {\n            super(-1);\n            this.returnValue = returnValue;\n            this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric();\n        }\n\n        protected AsyncResultMethodParameter(AsyncResultMethodParameter original) {\n            super(original);\n            this.returnValue = original.returnValue;\n            this.returnType = original.returnType;\n        }\n\n        @Override\n        public Class<?> getParameterType() {\n            if (this.returnValue != null) {\n                return this.returnValue.getClass();\n            }\n            if (!ResolvableType.NONE.equals(this.returnType)) {\n                return this.returnType.toClass();\n            }\n            return super.getParameterType();\n        }\n\n        @Override\n        public Type getGenericParameterType() {\n            return this.returnType.getType();\n        }\n\n        @Override\n        public AsyncResultMethodParameter clone() {\n            return new AsyncResultMethodParameter(this);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.tenant.config.YudaoTenantRpcAutoConfiguration\ncn.iocoder.yudao.framework.tenant.config.YudaoTenantAutoConfiguration\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.env.EnvironmentPostProcessor=\\\n  cn.iocoder.yudao.framework.tenant.core.mq.kafka.TenantKafkaEnvironmentPostProcessor\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-env</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        开发环境拓展，实现类似阿里的特性环境的能力\n        1. https://segmentfault.com/a/1190000018022987\n    </description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <properties>\n        <maven.compiler.source>8</maven.compiler.source>\n        <maven.compiler.target>8</maven.compiler.target>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Spring 核心 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>jakarta.servlet</groupId>\n            <artifactId>jakarta.servlet-api</artifactId>\n        </dependency>\n\n        <!-- RPC 相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-loadbalancer</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-core</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/config/EnvEnvironmentPostProcessor.java",
    "content": "package cn.iocoder.yudao.framework.env.config;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.framework.env.core.util.EnvUtils;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.env.EnvironmentPostProcessor;\nimport org.springframework.core.env.ConfigurableEnvironment;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.env.core.util.EnvUtils.HOST_NAME_VALUE;\n\n/**\n * 多环境的 {@link EnvEnvironmentPostProcessor} 实现类\n * 将 yudao.env.tag 设置到 nacos 等组件对应的 tag 配置项，当且仅当它们不存在时\n *\n * @author 芋道源码\n */\npublic class EnvEnvironmentPostProcessor implements EnvironmentPostProcessor {\n\n    private static final Set<String> TARGET_TAG_KEYS = SetUtils.asSet(\n            \"spring.cloud.nacos.discovery.metadata.tag\" // Nacos 注册中心\n            // MQ TODO\n    );\n\n    @Override\n    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {\n        // 0. 设置 ${HOST_NAME} 兜底的环境变量\n        String hostNameKey = StrUtil.subBetween(HOST_NAME_VALUE, \"{\", \"}\");\n        if (!environment.containsProperty(hostNameKey)) {\n            environment.getSystemProperties().put(hostNameKey, EnvUtils.getHostName());\n        }\n\n        // 1.1 如果没有 yudao.env.tag 配置项，则不进行配置项的修改\n        String tag = EnvUtils.getTag(environment);\n        if (StrUtil.isEmpty(tag)) {\n            return;\n        }\n        // 1.2 需要修改的配置项\n        for (String targetTagKey : TARGET_TAG_KEYS) {\n            String targetTagValue = environment.getProperty(targetTagKey);\n            if (StrUtil.isNotEmpty(targetTagValue)) {\n                continue;\n            }\n            environment.getSystemProperties().put(targetTagKey, tag);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/config/EnvProperties.java",
    "content": "package cn.iocoder.yudao.framework.env.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n\n/**\n * 环境配置\n *\n * @author 芋道源码\n */\n@ConfigurationProperties(prefix = \"yudao.env\")\n@Data\npublic class EnvProperties {\n\n    public static final String TAG_KEY = \"yudao.env.tag\";\n\n    /**\n     * 环境标签\n     */\n    private String tag;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/config/YudaoEnvRpcAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.env.config;\n\nimport cn.iocoder.yudao.framework.env.core.fegin.EnvLoadBalancerClientFactory;\nimport cn.iocoder.yudao.framework.env.core.fegin.EnvRequestInterceptor;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;\nimport org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification;\nimport org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;\nimport org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;\nimport org.springframework.context.annotation.Bean;\n\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * 多环境的 RPC 组件的自动配置\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@EnableConfigurationProperties(EnvProperties.class)\npublic class YudaoEnvRpcAutoConfiguration {\n\n    // ========== Feign 相关 ==========\n\n    /**\n     * 创建 {@link EnvLoadBalancerClientFactory} Bean\n     *\n     * 参考 {@link LoadBalancerAutoConfiguration#loadBalancerClientFactory(LoadBalancerClientsProperties)} 方法\n     */\n    @Bean\n    public LoadBalancerClientFactory loadBalancerClientFactory(LoadBalancerClientsProperties properties,\n                                                               ObjectProvider<List<LoadBalancerClientSpecification>> configurations) {\n        EnvLoadBalancerClientFactory clientFactory = new EnvLoadBalancerClientFactory(properties);\n        clientFactory.setConfigurations(configurations.getIfAvailable(Collections::emptyList));\n        return clientFactory;\n    }\n\n    @Bean\n    public EnvRequestInterceptor envRequestInterceptor() {\n        return new EnvRequestInterceptor();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/config/YudaoEnvWebAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.env.config;\n\nimport cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;\nimport cn.iocoder.yudao.framework.env.core.web.EnvWebFilter;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\n\n/**\n * 多环境的 Web 组件的自动配置\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)\n@EnableConfigurationProperties(EnvProperties.class)\npublic class YudaoEnvWebAutoConfiguration {\n\n    /**\n     * 创建 {@link EnvWebFilter} Bean\n     */\n    @Bean\n    public FilterRegistrationBean<EnvWebFilter> envWebFilterFilter() {\n        EnvWebFilter filter = new EnvWebFilter();\n        FilterRegistrationBean<EnvWebFilter> bean = new FilterRegistrationBean<>(filter);\n        bean.setOrder(WebFilterOrderEnum.ENV_TAG_FILTER);\n        return bean;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/core/context/EnvContextHolder.java",
    "content": "package cn.iocoder.yudao.framework.env.core.context;\n\nimport cn.hutool.core.collection.CollUtil;\nimport com.alibaba.ttl.TransmittableThreadLocal;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 开发环境上下文\n *\n * @author 芋道源码\n */\npublic class EnvContextHolder {\n\n    /**\n     * 标签的上下文\n     *\n     * 使用 {@link List} 的原因，可能存在多层设置或者清理\n     */\n    private static final ThreadLocal<List<String>> TAG_CONTEXT = TransmittableThreadLocal.withInitial(ArrayList::new);\n\n    public static void setTag(String tag) {\n        TAG_CONTEXT.get().add(tag);\n    }\n\n    public static String getTag() {\n        return CollUtil.getLast(TAG_CONTEXT.get());\n    }\n\n    public static void removeTag() {\n        List<String> tags = TAG_CONTEXT.get();\n        if (CollUtil.isEmpty(tags)) {\n            return;\n        }\n        tags.remove(tags.size() - 1);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/core/fegin/EnvLoadBalancerClient.java",
    "content": "package cn.iocoder.yudao.framework.env.core.fegin;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.env.core.context.EnvContextHolder;\nimport cn.iocoder.yudao.framework.env.core.util.EnvUtils;\nimport com.alibaba.cloud.nacos.balancer.NacosBalancer;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.DefaultResponse;\nimport org.springframework.cloud.client.loadbalancer.EmptyResponse;\nimport org.springframework.cloud.client.loadbalancer.Request;\nimport org.springframework.cloud.client.loadbalancer.Response;\nimport org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;\nimport org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;\nimport org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;\nimport org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\n\n/**\n * 多环境的 {@link org.springframework.cloud.client.loadbalancer.LoadBalancerClient} 实现类\n * 在从服务实例列表选择时，优先选择 tag 匹配的服务实例\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class EnvLoadBalancerClient implements ReactorServiceInstanceLoadBalancer {\n\n    /**\n     * 用于获取 serviceId 对应的服务实例的列表\n     */\n    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;\n    /**\n     * 需要获取的服务实例名\n     *\n     * 暂时用于打印 logger 日志\n     */\n    private final String serviceId;\n    /**\n     * 被代理的 ReactiveLoadBalancer 对象\n     */\n    private final ReactiveLoadBalancer<ServiceInstance> reactiveLoadBalancer;\n\n    @Override\n    public Mono<Response<ServiceInstance>> choose(Request request) {\n        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);\n        return supplier.get(request).next().map(list -> {\n            // 情况一，没有 tag 时，过滤掉有 tag 的节点。目的：避免 test 环境，打到本地有 tag 的实例\n            String tag = EnvContextHolder.getTag();\n            if (StrUtil.isEmpty(tag)) {\n                return getInstanceResponseWithoutTag(list);\n            }\n\n            // 情况二，有 tag 时，使用 tag 匹配服务实例\n            return getInstanceResponse(list, tag);\n        });\n    }\n\n    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, String tag) {\n        // 如果服务实例为空，则直接返回\n        if (CollUtil.isEmpty(instances)) {\n            log.warn(\"[getInstanceResponse][serviceId({}) 服务实例列表为空]\", serviceId);\n            return new EmptyResponse();\n        }\n\n        // 筛选满足条件的实例列表\n        List<ServiceInstance> chooseInstances = CollectionUtils.filterList(instances, instance -> tag.equals(EnvUtils.getTag(instance)));\n        if (CollUtil.isEmpty(chooseInstances)) {\n            log.warn(\"[getInstanceResponse][serviceId({}) 没有满足 tag({}) 的服务实例列表，直接使用所有服务实例列表]\", serviceId, tag);\n            chooseInstances = instances;\n        }\n\n        // TODO 芋艿：https://juejin.cn/post/7056770721858469896 相同网段\n\n        // 随机 + 权重获取实例列表 TODO 芋艿：目前直接使用 Nacos 提供的方法，如果替换注册中心，需要重新失败该方法\n        return new DefaultResponse(NacosBalancer.getHostByRandomWeight3(chooseInstances));\n    }\n\n    /**\n     * 当没有 tag 时，过滤掉有 tag 的实例列表\n     */\n    private Response<ServiceInstance> getInstanceResponseWithoutTag(List<ServiceInstance> instances) {\n        // 如果服务实例为空，则直接返回\n        if (CollUtil.isEmpty(instances)) {\n            log.warn(\"[getInstanceResponseWithoutTag][serviceId({}) 服务实例列表为空]\", serviceId);\n            return new EmptyResponse();\n        }\n\n        // 筛选没有 tag 的实例列表\n        List<ServiceInstance> chooseInstances = CollectionUtils.filterList(instances, instance -> StrUtil.isEmpty(EnvUtils.getTag(instance)));\n        // 【重要】补充说明：如果希望在 chooseInstances 为空时，不允许打到有 tag 的实例，可以取消注释下面的代码\n        if (CollUtil.isEmpty(chooseInstances)) {\n            chooseInstances = instances;\n        }\n\n        // 随机 + 权重获取实例列表\n        return new DefaultResponse(NacosBalancer.getHostByRandomWeight3(chooseInstances));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/core/fegin/EnvLoadBalancerClientFactory.java",
    "content": "package cn.iocoder.yudao.framework.env.core.fegin;\n\n\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;\nimport org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;\nimport org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;\nimport org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;\n\n/**\n * 多环境的 {@link LoadBalancerClientFactory} 实现类\n * 目的：在创建 {@link ReactiveLoadBalancer} 时，会额外增加 {@link EnvLoadBalancerClient} 代理，用于 tag 过滤服务实例\n *\n * @author 芋道源码\n */\npublic class EnvLoadBalancerClientFactory extends LoadBalancerClientFactory {\n\n    public EnvLoadBalancerClientFactory(LoadBalancerClientsProperties properties) {\n        super(properties);\n    }\n\n    @Override\n    public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {\n        ReactiveLoadBalancer<ServiceInstance> reactiveLoadBalancer = super.getInstance(serviceId);\n        // 参考 {@link com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancerClientConfiguration#nacosLoadBalancer(Environment, LoadBalancerClientFactory, NacosDiscoveryProperties)} 方法\n        return new EnvLoadBalancerClient(super.getLazyProvider(serviceId, ServiceInstanceListSupplier.class),\n                serviceId, reactiveLoadBalancer);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/core/fegin/EnvRequestInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.env.core.fegin;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.env.core.context.EnvContextHolder;\nimport cn.iocoder.yudao.framework.env.core.util.EnvUtils;\nimport feign.RequestInterceptor;\nimport feign.RequestTemplate;\n\n/**\n * 多环境的 {@link RequestInterceptor} 实现类：Feign 请求时，将 tag 设置到 header 中，继续透传给被调用的服务\n *\n * @author 芋道源码\n */\npublic class EnvRequestInterceptor implements RequestInterceptor {\n\n    @Override\n    public void apply(RequestTemplate requestTemplate) {\n        String tag = EnvContextHolder.getTag();\n        if (StrUtil.isNotEmpty(tag)) {\n            EnvUtils.setTag(requestTemplate, tag);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/core/package-info.java",
    "content": "package cn.iocoder.yudao.framework.env.core;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/core/util/EnvUtils.java",
    "content": "package cn.iocoder.yudao.framework.env.core.util;\n\nimport cn.iocoder.yudao.framework.env.config.EnvProperties;\nimport feign.RequestTemplate;\nimport lombok.SneakyThrows;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.core.env.Environment;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.net.InetAddress;\nimport java.util.Objects;\n\n/**\n * 环境 Utils\n *\n * @author 芋道源码\n */\npublic class EnvUtils {\n\n    private static final String HEADER_TAG = \"tag\";\n\n    public static final String HOST_NAME_VALUE = \"${HOSTNAME}\";\n\n    public static String getTag(HttpServletRequest request) {\n        String tag = request.getHeader(HEADER_TAG);\n        // 如果请求的是 \"${HOSTNAME}\"，则解析成对应的本地主机名\n        // 目的：特殊逻辑，解决 IDEA Rest Client 不支持环境变量的读取，所以就服务器来做\n        return Objects.equals(tag, HOST_NAME_VALUE) ? getHostName() : tag;\n    }\n\n    public static String getTag(ServiceInstance instance) {\n        return instance.getMetadata().get(HEADER_TAG);\n    }\n\n    public static String getTag(Environment environment) {\n        String tag = environment.getProperty(EnvProperties.TAG_KEY);\n        // 如果请求的是 \"${HOSTNAME}\"，则解析成对应的本地主机名\n        // 目的：特殊逻辑，解决 IDEA Rest Client 不支持环境变量的读取，所以就服务器来做\n        return Objects.equals(tag, HOST_NAME_VALUE) ? getHostName() : tag;\n    }\n\n    public static void setTag(RequestTemplate requestTemplate, String tag) {\n        requestTemplate.header(HEADER_TAG, tag);\n    }\n\n    /**\n     * 获得 hostname 主机名\n     *\n     * @return 主机名\n     */\n    @SneakyThrows\n    public static String getHostName() {\n        return InetAddress.getLocalHost().getHostName();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/core/web/EnvWebFilter.java",
    "content": "package cn.iocoder.yudao.framework.env.core.web;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.env.core.context.EnvContextHolder;\nimport cn.iocoder.yudao.framework.env.core.util.EnvUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * 环境的 {@link javax.servlet.Filter} 实现类\n * 当有 tag 请求头时，设置到 {@link EnvContextHolder} 的标签上下文\n *\n * @author 芋道源码\n */\npublic class EnvWebFilter extends OncePerRequestFilter {\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        // 如果没有 tag，则走默认的流程\n        String tag = EnvUtils.getTag(request);\n        if (StrUtil.isEmpty(tag)) {\n            chain.doFilter(request, response);\n            return;\n        }\n\n        // 如果有 tag，则设置到上下文\n        EnvContextHolder.setTag(tag);\n        try {\n            chain.doFilter(request, response);\n        } finally {\n            EnvContextHolder.removeTag();\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/java/cn/iocoder/yudao/framework/env/package-info.java",
    "content": "/**\n * 开发环境拓展，实现类似阿里的特性环境的能力\n * 1. https://segmentfault.com/a/1190000018022987\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.framework.env;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.env.config.YudaoEnvWebAutoConfiguration\ncn.iocoder.yudao.framework.env.config.YudaoEnvRpcAutoConfiguration\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-env/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.env.EnvironmentPostProcessor=\\\n    cn.iocoder.yudao.framework.env.config.EnvEnvironmentPostProcessor\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-excel</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>Excel 拓展</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Spring 核心 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有 ExcelUtils 使用 -->\n        </dependency>\n\n        <dependency>\n            <groupId>jakarta.servlet</groupId>\n            <artifactId>jakarta.servlet-api</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有 ExcelUtils 使用 -->\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.idev.excel</groupId>\n            <artifactId>fastexcel</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>\n            <optional>true</optional> <!-- 设置为 optional，只有在 AreaConvert 的时候使用 -->\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.dict.config;\n\nimport cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\n\n@AutoConfiguration\npublic class YudaoDictAutoConfiguration {\n\n    @Bean\n    @SuppressWarnings(\"InstantiationOfUtilityClass\")\n    public DictFrameworkUtils dictUtils(DictDataCommonApi dictDataApi) {\n        DictFrameworkUtils.init(dictDataApi);\n        return new DictFrameworkUtils();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictRpcAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.dict.config;\n\nimport cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n/**\n * 字典用到 Feign 的配置项\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@EnableFeignClients(clients = DictDataCommonApi.class) // 主要是引入相关的 API 服务\npublic class YudaoDictRpcAutoConfiguration {\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java",
    "content": "package cn.iocoder.yudao.framework.dict.core;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi;\nimport cn.iocoder.yudao.framework.common.util.cache.CacheUtils;\nimport cn.iocoder.yudao.framework.common.biz.system.dict.dto.DictDataRespDTO;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * 字典工具类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class DictFrameworkUtils {\n\n    private static DictDataCommonApi dictDataApi;\n\n    /**\n     * 针对 dictType 的字段数据缓存\n     */\n    private static final LoadingCache<String, List<DictDataRespDTO>> GET_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache(\n            Duration.ofMinutes(1L), // 过期时间 1 分钟\n            new CacheLoader<String, List<DictDataRespDTO>>() {\n\n                @Override\n                public List<DictDataRespDTO> load(String dictType) {\n                    return dictDataApi.getDictDataList(dictType).getCheckedData();\n                }\n\n            });\n\n    public static void init(DictDataCommonApi dictDataApi) {\n        DictFrameworkUtils.dictDataApi = dictDataApi;\n        log.info(\"[init][初始化 DictFrameworkUtils 成功]\");\n    }\n\n    public static void clearCache() {\n        GET_DICT_DATA_CACHE.invalidateAll();\n    }\n\n    @SneakyThrows\n    public static String parseDictDataLabel(String dictType, Integer value) {\n        if (value == null) {\n            return null;\n        }\n        return parseDictDataLabel(dictType, String.valueOf(value));\n    }\n\n    @SneakyThrows\n    public static String parseDictDataLabel(String dictType, String value) {\n        List<DictDataRespDTO> dictDatas = GET_DICT_DATA_CACHE.get(dictType);\n        DictDataRespDTO dictData = CollUtil.findOne(dictDatas, data -> Objects.equals(data.getValue(), value));\n        return dictData != null ? dictData.getLabel(): null;\n    }\n\n    @SneakyThrows\n    public static List<String> getDictDataLabelList(String dictType) {\n        List<DictDataRespDTO> dictDatas = GET_DICT_DATA_CACHE.get(dictType);\n        return convertList(dictDatas, DictDataRespDTO::getLabel);\n    }\n\n    @SneakyThrows\n    public static String parseDictDataValue(String dictType, String label) {\n        List<DictDataRespDTO> dictDatas = GET_DICT_DATA_CACHE.get(dictType);\n        DictDataRespDTO dictData = CollUtil.findOne(dictDatas, data -> Objects.equals(data.getLabel(), label));\n        return dictData!= null ? dictData.getValue(): null;\n    }\n\n    @SneakyThrows\n    public static List<String> getDictDataValueList(String dictType) {\n        List<DictDataRespDTO> dictDatas = GET_DICT_DATA_CACHE.get(dictType);\n        return convertList(dictDatas, DictDataRespDTO::getValue);\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/package-info.java",
    "content": "/**\n * 字典数据模块，提供 {@link cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils} 工具类\n *\n * 通过将字典缓存在内存中，保证性能\n */\npackage cn.iocoder.yudao.framework.dict;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDict.java",
    "content": "package cn.iocoder.yudao.framework.dict.validation;\n\nimport javax.validation.Constraint;\nimport javax.validation.Payload;\nimport java.lang.annotation.*;\n\n@Target({\n        ElementType.METHOD,\n        ElementType.FIELD,\n        ElementType.ANNOTATION_TYPE,\n        ElementType.CONSTRUCTOR,\n        ElementType.PARAMETER,\n        ElementType.TYPE_USE\n})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Constraint(\n        validatedBy = {InDictValidator.class, InDictCollectionValidator.class}\n)\npublic @interface InDict {\n\n    /**\n     * 数据字典 type\n     */\n    String type();\n\n    String message() default \"必须在指定范围 {value}\";\n\n    Class<?>[] groups() default {};\n\n    Class<? extends Payload>[] payload() default {};\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictCollectionValidator.java",
    "content": "package cn.iocoder.yudao.framework.dict.validation;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport java.util.Collection;\nimport java.util.List;\n\npublic class InDictCollectionValidator implements ConstraintValidator<InDict, Collection<?>> {\n\n    private String dictType;\n\n    @Override\n    public void initialize(InDict annotation) {\n        this.dictType = annotation.type();\n    }\n\n    @Override\n    public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {\n        // 为空时，默认不校验，即认为通过\n        if (CollUtil.isEmpty(list)) {\n            return true;\n        }\n        // 校验全部通过\n        List<String> dbValues = DictFrameworkUtils.getDictDataValueList(dictType);\n        boolean match = list.stream().allMatch(v -> dbValues.stream()\n                .anyMatch(dbValue -> dbValue.equalsIgnoreCase(v.toString())));\n        if (match) {\n            return true;\n        }\n\n        // 校验不通过，自定义提示语句\n        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值\n        context.buildConstraintViolationWithTemplate(\n                context.getDefaultConstraintMessageTemplate().replaceAll(\"\\\\{value}\", dbValues.toString())\n        ).addConstraintViolation(); // 重新添加错误提示语句\n        return false;\n    }\n\n}\n\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictValidator.java",
    "content": "package cn.iocoder.yudao.framework.dict.validation;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport java.util.List;\n\npublic class InDictValidator implements ConstraintValidator<InDict, Object> {\n\n    private String dictType;\n\n    @Override\n    public void initialize(InDict annotation) {\n        this.dictType = annotation.type();\n    }\n\n    @Override\n    public boolean isValid(Object value, ConstraintValidatorContext context) {\n        // 为空时，默认不校验，即认为通过\n        if (value == null) {\n            return true;\n        }\n        // 校验通过\n        final List<String> values = DictFrameworkUtils.getDictDataValueList(dictType);\n        boolean match = values.stream().anyMatch(v -> StrUtil.equalsIgnoreCase(v, value.toString()));\n        if (match) {\n            return true;\n        }\n\n        // 校验不通过，自定义提示语句\n        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值\n        context.buildConstraintViolationWithTemplate(\n                context.getDefaultConstraintMessageTemplate().replaceAll(\"\\\\{value}\", values.toString())\n        ).addConstraintViolation(); // 重新添加错误提示语句\n        return false;\n    }\n\n}\n\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/DictFormat.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.annotations;\n\nimport java.lang.annotation.*;\n\n/**\n * 字典格式化\n *\n * 实现将字典数据的值，格式化成字典数据的标签\n */\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\npublic @interface DictFormat {\n\n    /**\n     * 例如说，SysDictTypeConstants、InfDictTypeConstants\n     *\n     * @return 字典类型\n     */\n    String value();\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.annotations;\n\nimport java.lang.annotation.*;\n\n/**\n * 给 Excel 列添加下拉选择数据\n *\n * 其中 {@link #dictType()} 和 {@link #functionName()} 二选一\n *\n * @author HUIHUI\n */\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\npublic @interface ExcelColumnSelect {\n\n    /**\n     * @return 字典类型\n     */\n    String dictType() default \"\";\n\n    /**\n     * @return 获取下拉数据源的方法名称\n     */\n    String functionName() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.convert;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport cn.idev.excel.converters.Converter;\nimport cn.idev.excel.enums.CellDataTypeEnum;\nimport cn.idev.excel.metadata.GlobalConfiguration;\nimport cn.idev.excel.metadata.data.ReadCellData;\nimport cn.idev.excel.metadata.property.ExcelContentProperty;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * Excel 数据地区转换器\n *\n * @author HUIHUI\n */\n@Slf4j\npublic class AreaConvert implements Converter<Object> {\n\n    @Override\n    public Class<?> supportJavaTypeKey() {\n        throw new UnsupportedOperationException(\"暂不支持，也不需要\");\n    }\n\n    @Override\n    public CellDataTypeEnum supportExcelTypeKey() {\n        throw new UnsupportedOperationException(\"暂不支持，也不需要\");\n    }\n\n    @Override\n    public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,\n                                    GlobalConfiguration globalConfiguration) {\n        // 解析地区编号\n        String label = readCellData.getStringValue();\n        Area area = AreaUtils.parseArea(label);\n        if (area == null) {\n            log.error(\"[convertToJavaData][label({}) 解析不掉]\", label);\n            return null;\n        }\n        // 将 value 转换成对应的属性\n        Class<?> fieldClazz = contentProperty.getField().getType();\n        return Convert.convert(fieldClazz, area.getId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.convert;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.idev.excel.converters.Converter;\nimport cn.idev.excel.enums.CellDataTypeEnum;\nimport cn.idev.excel.metadata.GlobalConfiguration;\nimport cn.idev.excel.metadata.data.ReadCellData;\nimport cn.idev.excel.metadata.data.WriteCellData;\nimport cn.idev.excel.metadata.property.ExcelContentProperty;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * Excel 数据字典转换器\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class DictConvert implements Converter<Object> {\n\n    @Override\n    public Class<?> supportJavaTypeKey() {\n        throw new UnsupportedOperationException(\"暂不支持，也不需要\");\n    }\n\n    @Override\n    public CellDataTypeEnum supportExcelTypeKey() {\n        throw new UnsupportedOperationException(\"暂不支持，也不需要\");\n    }\n\n    @Override\n    public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,\n                                    GlobalConfiguration globalConfiguration) {\n        // 使用字典解析\n        String type = getType(contentProperty);\n        String label = readCellData.getStringValue();\n        String value = DictFrameworkUtils.parseDictDataValue(type, label);\n        if (value == null) {\n            log.error(\"[convertToJavaData][type({}) 解析不掉 label({})]\", type, label);\n            return null;\n        }\n        // 将 String 的 value 转换成对应的属性\n        Class<?> fieldClazz = contentProperty.getField().getType();\n        return Convert.convert(fieldClazz, value);\n    }\n\n    @Override\n    public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty,\n                                                    GlobalConfiguration globalConfiguration) {\n        // 空时，返回空\n        if (object == null) {\n            return new WriteCellData<>(\"\");\n        }\n\n        // 使用字典格式化\n        String type = getType(contentProperty);\n        String value = String.valueOf(object);\n        String label = DictFrameworkUtils.parseDictDataLabel(type, value);\n        if (label == null) {\n            log.error(\"[convertToExcelData][type({}) 转换不了 label({})]\", type, value);\n            return new WriteCellData<>(\"\");\n        }\n        // 生成 Excel 小表格\n        return new WriteCellData<>(label);\n    }\n\n    private static String getType(ExcelContentProperty contentProperty) {\n        return contentProperty.getField().getAnnotation(DictFormat.class).value();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.convert;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.idev.excel.converters.Converter;\nimport cn.idev.excel.enums.CellDataTypeEnum;\nimport cn.idev.excel.metadata.GlobalConfiguration;\nimport cn.idev.excel.metadata.data.WriteCellData;\nimport cn.idev.excel.metadata.property.ExcelContentProperty;\n\n/**\n * Excel Json 转换器\n *\n * @author 芋道源码\n */\npublic class JsonConvert implements Converter<Object> {\n\n    @Override\n    public Class<?> supportJavaTypeKey() {\n        throw new UnsupportedOperationException(\"暂不支持，也不需要\");\n    }\n\n    @Override\n    public CellDataTypeEnum supportExcelTypeKey() {\n        throw new UnsupportedOperationException(\"暂不支持，也不需要\");\n    }\n\n    @Override\n    public WriteCellData<String> convertToExcelData(Object value, ExcelContentProperty contentProperty,\n                                                    GlobalConfiguration globalConfiguration) {\n        // 生成 Excel 小表格\n        return new WriteCellData<>(JsonUtils.toJsonString(value));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.convert;\n\nimport cn.idev.excel.converters.Converter;\nimport cn.idev.excel.enums.CellDataTypeEnum;\nimport cn.idev.excel.metadata.GlobalConfiguration;\nimport cn.idev.excel.metadata.data.WriteCellData;\nimport cn.idev.excel.metadata.property.ExcelContentProperty;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\n/**\n * 金额转换器\n *\n * 金额单位：分\n *\n * @author 芋道源码\n */\npublic class MoneyConvert implements Converter<Integer> {\n\n    @Override\n    public Class<?> supportJavaTypeKey() {\n        throw new UnsupportedOperationException(\"暂不支持，也不需要\");\n    }\n\n    @Override\n    public CellDataTypeEnum supportExcelTypeKey() {\n        throw new UnsupportedOperationException(\"暂不支持，也不需要\");\n    }\n\n    @Override\n    public WriteCellData<String> convertToExcelData(Integer value, ExcelContentProperty contentProperty,\n                                                    GlobalConfiguration globalConfiguration) {\n        BigDecimal result = BigDecimal.valueOf(value)\n                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);\n        return new WriteCellData<>(result.toString());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/function/ExcelColumnSelectFunction.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.function;\n\nimport java.util.List;\n\n/**\n * Excel 列下拉数据源获取接口\n *\n * 为什么不直接解析字典还搞个接口？考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容\n\n * @author HUIHUI\n */\npublic interface ExcelColumnSelectFunction {\n\n    /**\n     * 获得方法名称\n     *\n     * @return 方法名称\n     */\n    String getName();\n\n    /**\n     * 获得列下拉数据源\n     *\n     * @return 下拉数据源\n     */\n    List<String> getOptions();\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.handler;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.idev.excel.enums.CellDataTypeEnum;\nimport cn.idev.excel.metadata.Head;\nimport cn.idev.excel.metadata.data.WriteCellData;\nimport cn.idev.excel.util.MapUtils;\nimport cn.idev.excel.write.metadata.holder.WriteSheetHolder;\nimport cn.idev.excel.write.style.column.AbstractColumnWidthStyleStrategy;\nimport cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;\nimport org.apache.poi.ss.usermodel.Cell;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Excel 自适应列宽处理器\n *\n * 相比 {@link LongestMatchColumnWidthStyleStrategy} 来说，额外处理了 DATE 类型！\n *\n * @see <a href=\"https://github.com/YunaiV/yudao-cloud/pull/196/\">添加自适应列宽处理器，并替换默认列宽策略</a>\n * @author hmb\n */\npublic class ColumnWidthMatchStyleStrategy extends AbstractColumnWidthStyleStrategy {\n\n    private static final int MAX_COLUMN_WIDTH = 255;\n\n    private final Map<Integer, Map<Integer, Integer>> cache = MapUtils.newHashMapWithExpectedSize(8);\n\n    @Override\n    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell,\n                                  Head head, Integer relativeRowIndex, Boolean isHead) {\n        boolean needSetWidth = isHead || CollUtil.isNotEmpty(cellDataList);\n        if (!needSetWidth) {\n            return;\n        }\n        Map<Integer, Integer> maxColumnWidthMap = cache.computeIfAbsent(writeSheetHolder.getSheetNo(),\n                key -> new HashMap<>(16));\n        Integer columnWidth = dataLength(cellDataList, cell, isHead);\n        if (columnWidth < 0) {\n            return;\n        }\n        if (columnWidth > MAX_COLUMN_WIDTH) {\n            columnWidth = MAX_COLUMN_WIDTH;\n        }\n        Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());\n        if (maxColumnWidth == null || columnWidth > maxColumnWidth) {\n            maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);\n            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);\n        }\n    }\n\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {\n        if (isHead) {\n            return cell.getStringCellValue().getBytes().length;\n        }\n        WriteCellData<?> cellData = cellDataList.get(0);\n        CellDataTypeEnum type = cellData.getType();\n        if (type == null) {\n            return -1;\n        }\n        switch (type) {\n            case STRING:\n                return cellData.getStringValue().getBytes().length;\n            case BOOLEAN:\n                return cellData.getBooleanValue().toString().getBytes().length;\n            case NUMBER:\n                return cellData.getNumberValue().toString().getBytes().length;\n            case DATE:\n                return cellData.getDateValue().toString().getBytes().length;\n            default:\n                return -1;\n        }\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.handler;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.hutool.poi.excel.ExcelUtil;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;\nimport cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction;\nimport cn.idev.excel.annotation.ExcelIgnore;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport cn.idev.excel.write.handler.SheetWriteHandler;\nimport cn.idev.excel.write.metadata.holder.WriteSheetHolder;\nimport cn.idev.excel.write.metadata.holder.WriteWorkbookHolder;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.poi.hssf.usermodel.HSSFDataValidation;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.ss.util.CellRangeAddressList;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * 基于固定 sheet 实现下拉框\n *\n * @author HUIHUI\n */\n@Slf4j\npublic class SelectSheetWriteHandler implements SheetWriteHandler {\n\n    /**\n     * 数据起始行从 0 开始\n     *\n     * 约定：本项目第一行有标题所以从 1 开始如果您的 Excel 有多行标题请自行更改\n     */\n    public static final int FIRST_ROW = 1;\n    /**\n     * 下拉列需要创建下拉框的行数，默认两千行如需更多请自行调整\n     */\n    public static final int LAST_ROW = 2000;\n\n    private static final String DICT_SHEET_NAME = \"字典sheet\";\n\n    /**\n     * key: 列 value: 下拉数据源\n     */\n    private final Map<Integer, List<String>> selectMap = new HashMap<>();\n\n    public SelectSheetWriteHandler(Class<?> head) {\n        // 解析下拉数据\n        int colIndex = 0;\n        boolean ignoreUnannotated = head.isAnnotationPresent(ExcelIgnoreUnannotated.class);\n        for (Field field : head.getDeclaredFields()) {\n            // 关联 https://github.com/YunaiV/ruoyi-vue-pro/pull/853\n            // 1.1 忽略 static final 或 transient 的字段\n            if (isStaticFinalOrTransient(field) ) {\n                continue;\n            }\n            // 1.2 忽略的字段跳过\n            if ((ignoreUnannotated && !field.isAnnotationPresent(ExcelProperty.class))\n                    || field.isAnnotationPresent(ExcelIgnore.class)) {\n                continue;\n            }\n\n            // 2. 核心：处理有 ExcelColumnSelect 注解的字段\n            if (field.isAnnotationPresent(ExcelColumnSelect.class)) {\n                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);\n                if (excelProperty != null && excelProperty.index() != -1) {\n                    colIndex = excelProperty.index();\n                }\n                getSelectDataList(colIndex, field);\n            }\n            colIndex++;\n        }\n    }\n\n    /**\n     * 判断字段是否是静态的、最终的、 transient 的\n     * 原因：FastExcel 默认是忽略 static final 或 transient 的字段，所以需要判断\n     *\n     * @param field 字段\n     * @return 是否是静态的、最终的、transient 的\n     */\n    private boolean isStaticFinalOrTransient(Field field) {\n        return (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()))\n                || Modifier.isTransient(field.getModifiers());\n    }\n\n\n    /**\n     * 获得下拉数据，并添加到 {@link #selectMap} 中\n     *\n     * @param colIndex 列索引\n     * @param field    字段\n     */\n    private void getSelectDataList(int colIndex, Field field) {\n        ExcelColumnSelect columnSelect = field.getAnnotation(ExcelColumnSelect.class);\n        String dictType = columnSelect.dictType();\n        String functionName = columnSelect.functionName();\n        Assert.isTrue(ObjectUtil.isNotEmpty(dictType) || ObjectUtil.isNotEmpty(functionName),\n                \"Field({}) 的 @ExcelColumnSelect 注解，dictType 和 functionName 不能同时为空\", field.getName());\n\n        // 情况一：使用 dictType 获得下拉数据\n        if (StrUtil.isNotEmpty(dictType)) { // 情况一： 字典数据 （默认）\n            selectMap.put(colIndex, DictFrameworkUtils.getDictDataLabelList(dictType));\n            return;\n        }\n\n        // 情况二：使用 functionName 获得下拉数据\n        Map<String, ExcelColumnSelectFunction> functionMap = SpringUtil.getApplicationContext().getBeansOfType(ExcelColumnSelectFunction.class);\n        ExcelColumnSelectFunction function = CollUtil.findOne(functionMap.values(), item -> item.getName().equals(functionName));\n        Assert.notNull(function, \"未找到对应的 function({})\", functionName);\n        selectMap.put(colIndex, function.getOptions());\n    }\n\n    @Override\n    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {\n        if (CollUtil.isEmpty(selectMap)) {\n            return;\n        }\n\n        // 1. 获取相应操作对象\n        DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手\n        Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿\n        List<KeyValue<Integer, List<String>>> keyValues = convertList(selectMap.entrySet(), entry -> new KeyValue<>(entry.getKey(), entry.getValue()));\n        keyValues.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错\n\n        // 2. 创建数据字典的 sheet 页\n        Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);\n        for (KeyValue<Integer, List<String>> keyValue : keyValues) {\n            int rowLength = keyValue.getValue().size();\n            // 2.1 设置字典 sheet 页的值，每一列一个字典项\n            for (int i = 0; i < rowLength; i++) {\n                Row row = dictSheet.getRow(i);\n                if (row == null) {\n                    row = dictSheet.createRow(i);\n                }\n                row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i));\n            }\n            // 2.2 设置单元格下拉选择\n            setColumnSelect(writeSheetHolder, workbook, helper, keyValue);\n        }\n    }\n\n    /**\n     * 设置单元格下拉选择\n     */\n    private static void setColumnSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper,\n                                        KeyValue<Integer, List<String>> keyValue) {\n        // 1.1 创建可被其他单元格引用的名称\n        Name name = workbook.createName();\n        String excelColumn = ExcelUtil.indexToColName(keyValue.getKey());\n        // 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2\n        String refers = DICT_SHEET_NAME + \"!$\" + excelColumn + \"$1:$\" + excelColumn + \"$\" + keyValue.getValue().size();\n        name.setNameName(\"dict\" + keyValue.getKey()); // 设置名称的名字\n        name.setRefersToFormula(refers); // 设置公式\n\n        // 2.1 设置约束\n        DataValidationConstraint constraint = helper.createFormulaListConstraint(\"dict\" + keyValue.getKey()); // 设置引用约束\n        // 设置下拉单元格的首行、末行、首列、末列\n        CellRangeAddressList rangeAddressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW,\n                keyValue.getKey(), keyValue.getKey());\n        DataValidation validation = helper.createValidation(constraint, rangeAddressList);\n        if (validation instanceof HSSFDataValidation) {\n            validation.setSuppressDropDownArrow(false);\n        } else {\n            validation.setSuppressDropDownArrow(true);\n            validation.setShowErrorBox(true);\n        }\n        // 2.2 阻止输入非下拉框的值\n        validation.setErrorStyle(DataValidation.ErrorStyle.STOP);\n        validation.createErrorBox(\"提示\", \"此值不存在于下拉选择中！\");\n        // 2.3 添加下拉框约束\n        writeSheetHolder.getSheet().addValidationData(validation);\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java",
    "content": "package cn.iocoder.yudao.framework.excel.core.util;\n\nimport cn.idev.excel.FastExcelFactory;\nimport cn.idev.excel.converters.longconverter.LongStringConverter;\nimport cn.iocoder.yudao.framework.common.util.http.HttpUtils;\nimport cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;\nimport cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\n\n/**\n * Excel 工具类\n *\n * @author 芋道源码\n */\npublic class ExcelUtils {\n\n    /**\n     * 将列表以 Excel 响应给前端\n     *\n     * @param response  响应\n     * @param filename  文件名\n     * @param sheetName Excel sheet 名\n     * @param head      Excel head 头\n     * @param data      数据列表哦\n     * @param <T>       泛型，保证 head 和 data 类型的一致性\n     * @throws IOException 写入失败的情况\n     */\n    public static <T> void write(HttpServletResponse response, String filename, String sheetName,\n                                 Class<T> head, List<T> data) throws IOException {\n        // 输出 Excel\n        FastExcelFactory.write(response.getOutputStream(), head)\n                .autoCloseStream(false) // 不要自动关闭，交给 Servlet 自己处理\n                .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度，自动适配。最大 255 宽度\n                .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框\n                .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度\n                .sheet(sheetName).doWrite(data);\n        // 设置 header 和 contentType。写在最后的原因是，避免报错时，响应 contentType 已经被修改了\n        response.addHeader(\"Content-Disposition\", \"attachment;filename=\" + HttpUtils.encodeUtf8(filename));\n        response.setContentType(\"application/vnd.ms-excel;charset=UTF-8\");\n    }\n\n    public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {\n        // 参考 https://t.zsxq.com/zM77F 帖子，增加 try 处理，兼容 windows 场景\n        try (InputStream inputStream = file.getInputStream()) {\n            return FastExcelFactory.read(inputStream, head, null)\n                    .autoCloseStream(false) // 不要自动关闭，交给 Servlet 自己处理\n                    .doReadAllSync();\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/package-info.java",
    "content": "/**\n * 基于 FastExcel 实现 Excel 相关的操作\n */\npackage cn.iocoder.yudao.framework.excel;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.dict.config.YudaoDictRpcAutoConfiguration\ncn.iocoder.yudao.framework.dict.config.YudaoDictAutoConfiguration\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-excel/src/test/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtilsTest.java",
    "content": "package cn.iocoder.yudao.framework.dict.core.util;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.framework.common.biz.system.dict.dto.DictDataRespDTO;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\n\n/**\n * {@link DictFrameworkUtils} 的单元测试\n */\npublic class DictFrameworkUtilsTest extends BaseMockitoUnitTest {\n\n    @Mock\n    private DictDataCommonApi dictDataApi;\n\n    @BeforeEach\n    public void setUp() {\n        DictFrameworkUtils.init(dictDataApi);\n        DictFrameworkUtils.clearCache();\n    }\n\n    @Test\n    public void testParseDictDataLabel() {\n        // mock 数据\n        List<DictDataRespDTO> dictDatas = ListUtil.of(\n                randomPojo(DictDataRespDTO.class, o -> o.setDictType(\"animal\").setValue(\"cat\").setLabel(\"猫\")),\n                randomPojo(DictDataRespDTO.class, o -> o.setDictType(\"animal\").setValue(\"dog\").setLabel(\"狗\"))\n        );\n        // mock 方法\n        when(dictDataApi.getDictDataList(eq(\"animal\"))).thenReturn(success(dictDatas));\n\n        // 断言返回值\n        assertEquals(\"狗\", DictFrameworkUtils.parseDictDataLabel(\"animal\", \"dog\"));\n    }\n\n    @Test\n    public void testParseDictDataValue() {\n        // mock 数据\n        List<DictDataRespDTO> dictDatas = ListUtil.of(\n                randomPojo(DictDataRespDTO.class, o -> o.setDictType(\"animal\").setValue(\"cat\").setLabel(\"猫\")),\n                randomPojo(DictDataRespDTO.class, o -> o.setDictType(\"animal\").setValue(\"dog\").setLabel(\"狗\"))\n        );\n        // mock 方法\n        when(dictDataApi.getDictDataList(eq(\"animal\"))).thenReturn(success(dictDatas));\n\n        // 断言返回值\n        assertEquals(\"dog\", DictFrameworkUtils.parseDictDataValue(\"animal\", \"狗\"));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-job/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-job</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>任务拓展，基于 XXL-Job 实现</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Spring 核心 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- Job 相关 -->\n        <dependency>\n            <groupId>com.xuxueli</groupId>\n            <artifactId>xxl-job-core</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>jakarta.validation</groupId>\n            <artifactId>jakarta.validation-api</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/XxlJobProperties.java",
    "content": "package cn.iocoder.yudao.framework.quartz.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * XXL-Job 配置类\n */\n@ConfigurationProperties(\"xxl.job\")\n@Validated\n@Data\npublic class XxlJobProperties {\n\n    /**\n     * 是否开启，默认为 true 关闭\n     */\n    private Boolean enabled = true;\n    /**\n     * 访问令牌\n     */\n    private String accessToken;\n    /**\n     * 控制器配置\n     */\n    @NotNull(message = \"控制器配置不能为空\")\n    private AdminProperties admin;\n    /**\n     * 执行器配置\n     */\n    @NotNull(message = \"执行器配置不能为空\")\n    private ExecutorProperties executor;\n\n    /**\n     * XXL-Job 调度器配置类\n     */\n    @Data\n    @Valid\n    public static class AdminProperties {\n\n        /**\n         * 调度器地址\n         */\n        @NotEmpty(message = \"调度器地址不能为空\")\n        private String addresses;\n\n    }\n\n    /**\n     * XXL-Job 执行器配置类\n     */\n    @Data\n    @Valid\n    public static class ExecutorProperties {\n\n        /**\n         * 默认端口\n         *\n         * 这里使用 -1 表示随机\n         */\n        private static final Integer PORT_DEFAULT = -1;\n\n        /**\n         * 默认日志保留天数\n         *\n         * 如果想永久保留，则设置为 -1\n         */\n        private static final Integer LOG_RETENTION_DAYS_DEFAULT = 30;\n\n        /**\n         * 应用名\n         */\n        @NotEmpty(message = \"应用名不能为空\")\n        private String appName;\n        /**\n         * 执行器的 IP\n         */\n        private String ip;\n        /**\n         * 执行器的 Port\n         */\n        private Integer port = PORT_DEFAULT;\n        /**\n         * 日志地址\n         */\n        @NotEmpty(message = \"日志地址不能为空\")\n        private String logPath;\n        /**\n         * 日志保留天数\n         */\n        private Integer logRetentionDays = LOG_RETENTION_DAYS_DEFAULT;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoAsyncAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.quartz.config;\n\nimport com.alibaba.ttl.TtlRunnable;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.scheduling.annotation.EnableAsync;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\nimport org.springframework.core.task.SimpleAsyncTaskExecutor;\n\n/**\n * 异步任务 Configuration\n */\n@AutoConfiguration\n@EnableAsync\npublic class YudaoAsyncAutoConfiguration {\n\n    @Bean\n    public BeanPostProcessor threadPoolTaskExecutorBeanPostProcessor() {\n        return new BeanPostProcessor() {\n\n            @Override\n            @SuppressWarnings(\"PatternVariableCanBeUsed\")\n            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n                // 处理 ThreadPoolTaskExecutor\n                if (bean instanceof ThreadPoolTaskExecutor) {\n                    ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean;\n                    executor.setTaskDecorator(TtlRunnable::get);\n                    return executor;\n                }\n                // 处理 SimpleAsyncTaskExecutor\n                // 参考 https://t.zsxq.com/CBoks 增加\n                if (bean instanceof SimpleAsyncTaskExecutor) {\n                    SimpleAsyncTaskExecutor executor = (SimpleAsyncTaskExecutor) bean;\n                    executor.setTaskDecorator(TtlRunnable::get);\n                    return executor;\n                }\n                return bean;\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoXxlJobAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.quartz.config;\n\nimport com.xxl.job.core.executor.XxlJobExecutor;\nimport com.xxl.job.core.executor.impl.XxlJobSpringExecutor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\n/**\n * XXL-Job 自动配置类\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@ConditionalOnClass(XxlJobSpringExecutor.class)\n@ConditionalOnProperty(prefix = \"xxl.job\", name = \"enabled\", havingValue = \"true\", matchIfMissing = true)\n@EnableConfigurationProperties({XxlJobProperties.class})\n@EnableScheduling // 开启 Spring 自带的定时任务\n@Slf4j\npublic class YudaoXxlJobAutoConfiguration {\n\n    @Bean\n    @ConditionalOnMissingBean\n    public XxlJobExecutor xxlJobExecutor(XxlJobProperties properties) {\n        log.info(\"[xxlJobExecutor][初始化 XXL-Job 执行器的配置]\");\n        XxlJobProperties.AdminProperties admin = properties.getAdmin();\n        XxlJobProperties.ExecutorProperties executor = properties.getExecutor();\n\n        // 初始化执行器\n        XxlJobExecutor xxlJobExecutor = new XxlJobSpringExecutor();\n        xxlJobExecutor.setIp(executor.getIp());\n        xxlJobExecutor.setPort(executor.getPort());\n        xxlJobExecutor.setAppname(executor.getAppName());\n        xxlJobExecutor.setLogPath(executor.getLogPath());\n        xxlJobExecutor.setLogRetentionDays(executor.getLogRetentionDays());\n        xxlJobExecutor.setAdminAddresses(admin.getAddresses());\n        xxlJobExecutor.setAccessToken(properties.getAccessToken());\n        return xxlJobExecutor;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/package-info.java",
    "content": "/**\n * 1. 定时任务，基于 XXL-Job 实现。\n * 2. 异步任务，采用 Spring Async 异步执行。\n */\npackage cn.iocoder.yudao.framework.quartz;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.quartz.config.YudaoXxlJobAutoConfiguration\ncn.iocoder.yudao.framework.quartz.config.YudaoAsyncAutoConfiguration\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-job/《芋道 Spring Boot 定时任务入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Job/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-job/《芋道 Spring Boot 异步任务入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Async-Job/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>服务监控，提供链路追踪、日志服务、指标收集等等功能</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Spring 核心 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有 TraceFilter 使用 -->\n        </dependency>\n\n        <dependency>\n            <groupId>jakarta.servlet</groupId>\n            <artifactId>jakarta.servlet-api</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有 TraceFilter 使用 -->\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>io.opentracing</groupId>\n            <artifactId>opentracing-util</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.skywalking</groupId>\n            <artifactId>apm-toolkit-trace</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.skywalking</groupId>\n            <artifactId>apm-toolkit-logback-1.x</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.skywalking</groupId>\n            <artifactId>apm-toolkit-opentracing</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- Micrometer 对 Prometheus 的支持 -->\n        <dependency>\n            <groupId>io.micrometer</groupId>\n            <artifactId>micrometer-registry-prometheus</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Client 客户端 -->\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/TracerProperties.java",
    "content": "package cn.iocoder.yudao.framework.tracer.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n\n/**\n * BizTracer配置类\n *\n * @author 麻薯\n */\n@ConfigurationProperties(\"yudao.tracer\")\n@Data\npublic class TracerProperties {\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoMetricsAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.tracer.config;\n\nimport io.micrometer.core.instrument.MeterRegistry;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * Metrics 配置类\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@ConditionalOnClass({MeterRegistryCustomizer.class})\n@ConditionalOnProperty(prefix = \"yudao.metrics\", value = \"enable\", matchIfMissing = true) // 允许使用 yudao.metrics.enable=false 禁用 Metrics\npublic class YudaoMetricsAutoConfiguration {\n\n    @Bean\n    public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags(\n            @Value(\"${spring.application.name}\") String applicationName) {\n        return registry -> registry.config().commonTags(\"application\", applicationName);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.tracer.config;\n\nimport cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;\nimport cn.iocoder.yudao.framework.tracer.core.filter.TraceFilter;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\n\n/**\n * Tracer 配置类\n *\n * @author mashu\n */\n@AutoConfiguration\n@ConditionalOnClass(name = {\n        \"org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer\", // 来自 apm-toolkit-opentracing.jar\n//        \"io.opentracing.Tracer\", // 来自 opentracing-api.jar\n        \"javax.servlet.Filter\"\n})\n@EnableConfigurationProperties(TracerProperties.class)\n@ConditionalOnProperty(prefix = \"yudao.tracer\", value = \"enable\", matchIfMissing = true)\npublic class YudaoTracerAutoConfiguration {\n\n    // TODO @芋艿：skywalking 不兼容最新的 opentracing 版本。同时，opentracing 也停止了维护，尬住了！后续换 opentelemetry 即可！\n//    @Bean\n//    public BizTraceAspect bizTracingAop() {\n//        return new BizTraceAspect(tracer());\n//    }\n//\n//    @Bean\n//    public Tracer tracer() {\n//        // 创建 SkywalkingTracer 对象\n//        SkywalkingTracer tracer = new SkywalkingTracer();\n//        // 设置为 GlobalTracer 的追踪器\n//        GlobalTracer.registerIfAbsent(tracer);\n//        return tracer;\n//    }\n\n    /**\n     * 创建 TraceFilter 过滤器，响应 header 设置 traceId\n     */\n    @Bean\n    public FilterRegistrationBean<TraceFilter> traceFilter() {\n        FilterRegistrationBean<TraceFilter> registrationBean = new FilterRegistrationBean<>();\n        registrationBean.setFilter(new TraceFilter());\n        registrationBean.setOrder(WebFilterOrderEnum.TRACE_FILTER);\n        return registrationBean;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/core/annotation/BizTrace.java",
    "content": "package cn.iocoder.yudao.framework.tracer.core.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 打印业务编号 / 业务类型注解\n *\n * 使用时，需要设置 SkyWalking OAP Server 的 application.yaml 配置文件，修改 SW_SEARCHABLE_TAG_KEYS 配置项，\n * 增加 biz.type 和 biz.id 两值，然后重启 SkyWalking OAP Server 服务器。\n *\n * @author 麻薯\n */\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\npublic @interface BizTrace {\n\n    /**\n     * 业务编号 tag 名\n     */\n    String ID_TAG = \"biz.id\";\n    /**\n     * 业务类型 tag 名\n     */\n    String TYPE_TAG = \"biz.type\";\n\n    /**\n     * @return 操作名\n     */\n    String operationName() default \"\";\n\n    /**\n     * @return 业务编号\n     */\n    String id();\n\n    /**\n     * @return 业务类型\n     */\n    String type();\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/core/aop/BizTraceAspect.java",
    "content": "package cn.iocoder.yudao.framework.tracer.core.aop;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.tracer.core.annotation.BizTrace;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;\nimport cn.iocoder.yudao.framework.tracer.core.util.TracerFrameworkUtils;\nimport io.opentracing.Span;\nimport io.opentracing.Tracer;\nimport io.opentracing.tag.Tags;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\n\nimport java.util.Map;\n\nimport static java.util.Arrays.asList;\n\n/**\n * {@link BizTrace} 切面，记录业务链路\n *\n * @author mashu\n */\n@Aspect\n@AllArgsConstructor\n@Slf4j\npublic class BizTraceAspect {\n\n    private static final String BIZ_OPERATION_NAME_PREFIX = \"Biz/\";\n\n    private final Tracer tracer;\n\n    @Around(value = \"@annotation(trace)\")\n    public Object around(ProceedingJoinPoint joinPoint, BizTrace trace) throws Throwable {\n        // 创建 span\n        String operationName = getOperationName(joinPoint, trace);\n        Span span = tracer.buildSpan(operationName)\n                .withTag(Tags.COMPONENT.getKey(), \"biz\")\n                .start();\n        try {\n            // 执行原有方法\n            return joinPoint.proceed();\n        } catch (Throwable throwable) {\n            TracerFrameworkUtils.onError(throwable, span);\n            throw throwable;\n        } finally {\n            // 设置 Span 的 biz 属性\n            setBizTag(span, joinPoint, trace);\n            // 完成 Span\n            span.finish();\n        }\n    }\n\n    private String getOperationName(ProceedingJoinPoint joinPoint, BizTrace trace) {\n        // 自定义操作名\n        if (StrUtil.isNotEmpty(trace.operationName())) {\n            return BIZ_OPERATION_NAME_PREFIX + trace.operationName();\n        }\n        // 默认操作名，使用方法名\n        return BIZ_OPERATION_NAME_PREFIX\n                + joinPoint.getSignature().getDeclaringType().getSimpleName()\n                + \"/\" + joinPoint.getSignature().getName();\n    }\n\n    private void setBizTag(Span span, ProceedingJoinPoint joinPoint, BizTrace trace) {\n        try {\n            Map<String, Object> result = SpringExpressionUtils.parseExpressions(joinPoint, asList(trace.type(), trace.id()));\n            span.setTag(BizTrace.TYPE_TAG, MapUtil.getStr(result, trace.type()));\n            span.setTag(BizTrace.ID_TAG, MapUtil.getStr(result, trace.id()));\n        } catch (Exception ex) {\n            log.error(\"[setBizTag][解析 bizType 与 bizId 发生异常]\", ex);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/core/filter/TraceFilter.java",
    "content": "package cn.iocoder.yudao.framework.tracer.core.filter;\n\nimport cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * Trace 过滤器，打印 traceId 到 header 中返回\n *\n * @author 芋道源码\n */\npublic class TraceFilter extends OncePerRequestFilter {\n\n    /**\n     * Header 名 - 链路追踪编号\n     */\n    private static final String HEADER_NAME_TRACE_ID = \"trace-id\";\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws IOException, ServletException {\n        // 设置响应 traceId\n        response.addHeader(HEADER_NAME_TRACE_ID, TracerUtils.getTraceId());\n        // 继续过滤\n        chain.doFilter(request, response);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/core/util/TracerFrameworkUtils.java",
    "content": "package cn.iocoder.yudao.framework.tracer.core.util;\n\nimport io.opentracing.Span;\nimport io.opentracing.tag.Tags;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 链路追踪 Util\n *\n * @author 芋道源码\n */\npublic class TracerFrameworkUtils {\n\n    /**\n     * 将异常记录到 Span 中，参考自 com.aliyuncs.utils.TraceUtils\n     *\n     * @param throwable 异常\n     * @param span Span\n     */\n    public static void onError(Throwable throwable, Span span) {\n        Tags.ERROR.set(span, Boolean.TRUE);\n        if (throwable != null) {\n            span.log(errorLogs(throwable));\n        }\n    }\n\n    private static Map<String, Object> errorLogs(Throwable throwable) {\n        Map<String, Object> errorLogs = new HashMap<String, Object>(10);\n        errorLogs.put(\"event\", Tags.ERROR.getKey());\n        errorLogs.put(\"error.object\", throwable);\n        errorLogs.put(\"error.kind\", throwable.getClass().getName());\n        String message = throwable.getCause() != null ? throwable.getCause().getMessage() : throwable.getMessage();\n        if (message != null) {\n            errorLogs.put(\"message\", message);\n        }\n        StringWriter sw = new StringWriter();\n        throwable.printStackTrace(new PrintWriter(sw));\n        errorLogs.put(\"stack\", sw.toString());\n        return errorLogs;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/package-info.java",
    "content": "/**\n * 使用 SkyWalking 组件，作为链路追踪、日志中心。\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.framework.tracer;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.tracer.config.YudaoTracerAutoConfiguration\ncn.iocoder.yudao.framework.tracer.config.YudaoMetricsAutoConfiguration\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/《芋道 Spring Boot 监控工具 Admin 入门》.md",
    "content": "<https://www.iocoder.cn/Spring-Boot/Admin/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/《芋道 Spring Boot 监控端点 Actuator 入门》.md",
    "content": "<https://www.iocoder.cn/Spring-Boot/Actuator/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-monitor/《芋道 Spring Boot 链路追踪 SkyWalking 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-mq</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>消息队列，支持 Redis、RocketMQ、RabbitMQ、Kafka 四种</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <!-- 消息队列相关 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.amqp</groupId>\n            <artifactId>spring-rabbit</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.rocketmq</groupId>\n            <artifactId>rocketmq-spring-boot-starter</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/package-info.java",
    "content": "/**\n * 消息队列，支持 Redis、RocketMQ、RabbitMQ、Kafka 四种\n */\npackage cn.iocoder.yudao.framework.mq;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.mq.rabbitmq.config;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;\nimport org.springframework.amqp.support.converter.MessageConverter;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.context.annotation.Bean;\n\n/**\n * RabbitMQ 消息队列配置类\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@Slf4j\n@ConditionalOnClass(name = \"org.springframework.amqp.rabbit.core.RabbitTemplate\")\npublic class YudaoRabbitMQAutoConfiguration {\n\n    /**\n     * Jackson2JsonMessageConverter Bean：使用 jackson 序列化消息\n     */\n    @Bean\n    public MessageConverter createMessageConverter() {\n        return new Jackson2JsonMessageConverter();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/core/package-info.java",
    "content": "/**\n * 占位符，无特殊逻辑\n */\npackage cn.iocoder.yudao.framework.mq.rabbitmq.core;"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/package-info.java",
    "content": "/**\n * 消息队列，基于 RabbitMQ 提供\n */\npackage cn.iocoder.yudao.framework.mq.rabbitmq;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQConsumerAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.config;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.system.SystemUtil;\nimport cn.iocoder.yudao.framework.common.enums.DocumentEnum;\nimport cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;\nimport cn.iocoder.yudao.framework.mq.redis.core.job.RedisPendingMessageResendJob;\nimport cn.iocoder.yudao.framework.mq.redis.core.job.RedisStreamMessageCleanupJob;\nimport cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener;\nimport cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;\nimport cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;\nimport lombok.extern.slf4j.Slf4j;\nimport org.redisson.api.RedissonClient;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.data.redis.connection.RedisServerCommands;\nimport org.springframework.data.redis.connection.stream.Consumer;\nimport org.springframework.data.redis.connection.stream.ObjectRecord;\nimport org.springframework.data.redis.connection.stream.ReadOffset;\nimport org.springframework.data.redis.connection.stream.StreamOffset;\nimport org.springframework.data.redis.core.RedisCallback;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.listener.ChannelTopic;\nimport org.springframework.data.redis.listener.RedisMessageListenerContainer;\nimport org.springframework.data.redis.stream.StreamMessageListenerContainer;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\nimport java.util.List;\nimport java.util.Properties;\n\n/**\n * Redis 消息队列 Consumer 配置类\n *\n * @author 芋道源码\n */\n@Slf4j\n@EnableScheduling // 启用定时任务，用于 RedisPendingMessageResendJob 重发消息\n@AutoConfiguration(after = YudaoRedisAutoConfiguration.class)\npublic class YudaoRedisMQConsumerAutoConfiguration {\n\n    /**\n     * 创建 Redis Pub/Sub 广播消费的容器\n     */\n    @Bean\n    @ConditionalOnBean(AbstractRedisChannelMessageListener.class) // 只有 AbstractChannelMessageListener 存在的时候，才需要注册 Redis pubsub 监听\n    public RedisMessageListenerContainer redisMessageListenerContainer(\n            RedisMQTemplate redisMQTemplate, List<AbstractRedisChannelMessageListener<?>> listeners) {\n        // 创建 RedisMessageListenerContainer 对象\n        RedisMessageListenerContainer container = new RedisMessageListenerContainer();\n        // 设置 RedisConnection 工厂。\n        container.setConnectionFactory(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory());\n        // 添加监听器\n        listeners.forEach(listener -> {\n            listener.setRedisMQTemplate(redisMQTemplate);\n            container.addMessageListener(listener, new ChannelTopic(listener.getChannel()));\n            log.info(\"[redisMessageListenerContainer][注册 Channel({}) 对应的监听器({})]\",\n                    listener.getChannel(), listener.getClass().getName());\n        });\n        return container;\n    }\n\n    /**\n     * 创建 Redis Stream 重新消费的任务\n     */\n    @Bean\n    @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候，才需要注册 Redis pubsub 监听\n    public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractRedisStreamMessageListener<?>> listeners,\n                                                                     RedisMQTemplate redisTemplate,\n                                                                     RedissonClient redissonClient) {\n        return new RedisPendingMessageResendJob(listeners, redisTemplate, redissonClient);\n    }\n\n    /**\n     * 创建 Redis Stream 消息清理任务\n     */\n    @Bean\n    @ConditionalOnBean(AbstractRedisStreamMessageListener.class)\n    public RedisStreamMessageCleanupJob redisStreamMessageCleanupJob(List<AbstractRedisStreamMessageListener<?>> listeners,\n                                                                     RedisMQTemplate redisTemplate,\n                                                                     RedissonClient redissonClient) {\n        return new RedisStreamMessageCleanupJob(listeners, redisTemplate, redissonClient);\n    }\n\n    /**\n     * 创建 Redis Stream 集群消费的容器\n     *\n     * 基础知识：<a href=\"https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html\">Redis Stream 的 xreadgroup 命令</a>\n     */\n    @Bean(initMethod = \"start\", destroyMethod = \"stop\")\n    @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候，才需要注册 Redis pubsub 监听\n    public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer(\n            RedisMQTemplate redisMQTemplate, List<AbstractRedisStreamMessageListener<?>> listeners) {\n        RedisTemplate<String, ?> redisTemplate = redisMQTemplate.getRedisTemplate();\n        checkRedisVersion(redisTemplate);\n        // 第一步，创建 StreamMessageListenerContainer 容器\n        // 创建 options 配置\n        StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions =\n                StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()\n                        .batchSize(10) // 一次性最多拉取多少条消息\n                        .targetType(String.class) // 目标类型。统一使用 String，通过自己封装的 AbstractStreamMessageListener 去反序列化\n                        .build();\n        // 创建 container 对象\n        StreamMessageListenerContainer<String, ObjectRecord<String, String>> container =\n                StreamMessageListenerContainer.create(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory(), containerOptions);\n\n        // 第二步，注册监听器，消费对应的 Stream 主题\n        String consumerName = buildConsumerName();\n        listeners.parallelStream().forEach(listener -> {\n            log.info(\"[redisStreamMessageListenerContainer][开始注册 StreamKey({}) 对应的监听器({})]\",\n                    listener.getStreamKey(), listener.getClass().getName());\n            // 创建 listener 对应的消费者分组\n            try {\n                redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup());\n            } catch (Exception ignore) {\n            }\n            // 设置 listener 对应的 redisTemplate\n            listener.setRedisMQTemplate(redisMQTemplate);\n            // 创建 Consumer 对象\n            Consumer consumer = Consumer.from(listener.getGroup(), consumerName);\n            // 设置 Consumer 消费进度，以最小消费进度为准\n            StreamOffset<String> streamOffset = StreamOffset.create(listener.getStreamKey(), ReadOffset.lastConsumed());\n            // 设置 Consumer 监听\n            StreamMessageListenerContainer.StreamReadRequestBuilder<String> builder = StreamMessageListenerContainer.StreamReadRequest\n                    .builder(streamOffset).consumer(consumer)\n                    .autoAcknowledge(false) // 不自动 ack\n                    .cancelOnError(throwable -> false); // 默认配置，发生异常就取消消费，显然不符合预期；因此，我们设置为 false\n            container.register(builder.build(), listener);\n            log.info(\"[redisStreamMessageListenerContainer][完成注册 StreamKey({}) 对应的监听器({})]\",\n                    listener.getStreamKey(), listener.getClass().getName());\n        });\n        return container;\n    }\n\n    /**\n     * 构建消费者名字，使用本地 IP + 进程编号的方式。\n     * 参考自 RocketMQ clientId 的实现\n     *\n     * @return 消费者名字\n     */\n    public static String buildConsumerName() {\n        return String.format(\"%s@%d\", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID());\n    }\n\n    /**\n     * 校验 Redis 版本号，是否满足最低的版本号要求！\n     */\n    public static void checkRedisVersion(RedisTemplate<String, ?> redisTemplate) {\n        // 获得 Redis 版本\n        Properties info = redisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info);\n        String version = MapUtil.getStr(info, \"redis_version\");\n        // 校验最低版本必须大于等于 5.0.0\n        int majorVersion = Integer.parseInt(StrUtil.subBefore(version, '.', false));\n        if (majorVersion < 5) {\n            throw new IllegalStateException(StrUtil.format(\"您当前的 Redis 版本为 {}，小于最低要求的 5.0.0 版本！\" +\n                    \"请参考 {} 文档进行安装。\", version, DocumentEnum.REDIS_INSTALL.getUrl()));\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQProducerAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.config;\n\nimport cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;\nimport cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor;\nimport cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.data.redis.core.StringRedisTemplate;\n\nimport java.util.List;\n\n/**\n * Redis 消息队列 Producer 配置类\n *\n * @author 芋道源码\n */\n@Slf4j\n@AutoConfiguration(after = YudaoRedisAutoConfiguration.class)\npublic class YudaoRedisMQProducerAutoConfiguration {\n\n    @Bean\n    public RedisMQTemplate redisMQTemplate(StringRedisTemplate redisTemplate,\n                                           List<RedisMessageInterceptor> interceptors) {\n        RedisMQTemplate redisMQTemplate = new RedisMQTemplate(redisTemplate);\n        // 添加拦截器\n        interceptors.forEach(redisMQTemplate::addInterceptor);\n        return redisMQTemplate;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/RedisMQTemplate.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.core;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor;\nimport cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage;\nimport cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessage;\nimport cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessage;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport org.springframework.data.redis.connection.stream.RecordId;\nimport org.springframework.data.redis.connection.stream.StreamRecords;\nimport org.springframework.data.redis.core.RedisTemplate;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Redis MQ 操作模板类\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\npublic class RedisMQTemplate {\n\n    @Getter\n    private final RedisTemplate<String, ?> redisTemplate;\n    /**\n     * 拦截器数组\n     */\n    @Getter\n    private final List<RedisMessageInterceptor> interceptors = new ArrayList<>();\n\n    /**\n     * 发送 Redis 消息，基于 Redis pub/sub 实现\n     *\n     * @param message 消息\n     */\n    public <T extends AbstractRedisChannelMessage> void send(T message) {\n        try {\n            sendMessageBefore(message);\n            // 发送消息\n            redisTemplate.convertAndSend(message.getChannel(), JsonUtils.toJsonString(message));\n        } finally {\n            sendMessageAfter(message);\n        }\n    }\n\n    /**\n     * 发送 Redis 消息，基于 Redis Stream 实现\n     *\n     * @param message 消息\n     * @return 消息记录的编号对象\n     */\n    public <T extends AbstractRedisStreamMessage> RecordId send(T message) {\n        try {\n            sendMessageBefore(message);\n            // 发送消息\n            return redisTemplate.opsForStream().add(StreamRecords.newRecord()\n                    .ofObject(JsonUtils.toJsonString(message)) // 设置内容\n                    .withStreamKey(message.getStreamKey())); // 设置 stream key\n        } finally {\n            sendMessageAfter(message);\n        }\n    }\n\n    /**\n     * 添加拦截器\n     *\n     * @param interceptor 拦截器\n     */\n    public void addInterceptor(RedisMessageInterceptor interceptor) {\n        interceptors.add(interceptor);\n    }\n\n    private void sendMessageBefore(AbstractRedisMessage message) {\n        // 正序\n        interceptors.forEach(interceptor -> interceptor.sendMessageBefore(message));\n    }\n\n    private void sendMessageAfter(AbstractRedisMessage message) {\n        // 倒序\n        for (int i = interceptors.size() - 1; i >= 0; i--) {\n            interceptors.get(i).sendMessageAfter(message);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/interceptor/RedisMessageInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.core.interceptor;\n\nimport cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage;\n\n/**\n * {@link AbstractRedisMessage} 消息拦截器\n * 通过拦截器，作为插件机制，实现拓展。\n * 例如说，多租户场景下的 MQ 消息处理\n *\n * @author 芋道源码\n */\npublic interface RedisMessageInterceptor {\n\n    default void sendMessageBefore(AbstractRedisMessage message) {\n    }\n\n    default void sendMessageAfter(AbstractRedisMessage message) {\n    }\n\n    default void consumeMessageBefore(AbstractRedisMessage message) {\n    }\n\n    default void consumeMessageAfter(AbstractRedisMessage message) {\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisPendingMessageResendJob.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.core.job;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;\nimport cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.redisson.api.RLock;\nimport org.redisson.api.RedissonClient;\nimport org.springframework.data.domain.Range;\nimport org.springframework.data.redis.connection.stream.*;\nimport org.springframework.data.redis.core.StreamOperations;\nimport org.springframework.scheduling.annotation.Scheduled;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * 这个任务用于处理，crash 之后的消费者未消费完的消息\n */\n@Slf4j\n@AllArgsConstructor\npublic class RedisPendingMessageResendJob {\n\n    private static final String LOCK_KEY = \"redis:stream:pending-message-resend:lock\";\n\n    /**\n     * 消息超时时间，默认 5 分钟\n     *\n     * 1. 超时的消息才会被重新投递\n     * 2. 由于定时任务 1 分钟一次，消息超时后不会被立即重投，极端情况下消息 5 分钟过期后，再等 1 分钟才会被扫瞄到\n     */\n    private static final int EXPIRE_TIME = 5 * 60;\n\n    private final List<AbstractRedisStreamMessageListener<?>> listeners;\n    private final RedisMQTemplate redisTemplate;\n    private final RedissonClient redissonClient;\n\n    /**\n     * 一分钟执行一次,这里选择每分钟的 35 秒执行，是为了避免整点任务过多的问题\n     */\n    @Scheduled(cron = \"35 * * * * ?\")\n    public void messageResend() {\n        RLock lock = redissonClient.getLock(LOCK_KEY);\n        // 尝试加锁\n        if (lock.tryLock()) {\n            try {\n                execute();\n            } catch (Exception ex) {\n                log.error(\"[messageResend][执行异常]\", ex);\n            } finally {\n                lock.unlock();\n            }\n        }\n    }\n\n    /**\n     * 执行清理逻辑\n     *\n     * @see <a href=\"https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/480/files\">讨论</a>\n     */\n    private void execute() {\n        StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream();\n        listeners.forEach(listener -> {\n            PendingMessagesSummary pendingMessagesSummary = Objects.requireNonNull(ops.pending(listener.getStreamKey(), listener.getGroup()));\n            // 每个消费者的 pending 队列消息数量\n            Map<String, Long> pendingMessagesPerConsumer = pendingMessagesSummary.getPendingMessagesPerConsumer();\n            pendingMessagesPerConsumer.forEach((consumerName, pendingMessageCount) -> {\n                log.info(\"[processPendingMessage][消费者({}) 消息数量({})]\", consumerName, pendingMessageCount);\n                // 每个消费者的 pending消息的详情信息\n                PendingMessages pendingMessages = ops.pending(listener.getStreamKey(), Consumer.from(listener.getGroup(), consumerName), Range.unbounded(), pendingMessageCount);\n                if (pendingMessages.isEmpty()) {\n                    return;\n                }\n                pendingMessages.forEach(pendingMessage -> {\n                    // 获取消息上一次传递到 consumer 的时间,\n                    long lastDelivery = pendingMessage.getElapsedTimeSinceLastDelivery().getSeconds();\n                    if (lastDelivery < EXPIRE_TIME){\n                        return;\n                    }\n                    // 获取指定 id 的消息体\n                    List<MapRecord<String, Object, Object>> records = ops.range(listener.getStreamKey(),\n                            Range.of(Range.Bound.inclusive(pendingMessage.getIdAsString()), Range.Bound.inclusive(pendingMessage.getIdAsString())));\n                    if (CollUtil.isEmpty(records)) {\n                        return;\n                    }\n                    // 重新投递消息\n                    redisTemplate.getRedisTemplate().opsForStream().add(StreamRecords.newRecord()\n                            .ofObject(records.get(0).getValue()) // 设置内容\n                            .withStreamKey(listener.getStreamKey()));\n                    // ack 消息消费完成\n                    redisTemplate.getRedisTemplate().opsForStream().acknowledge(listener.getGroup(), records.get(0));\n                    log.info(\"[processPendingMessage][消息({})重新投递成功]\", records.get(0).getId());\n                });\n            });\n        });\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisStreamMessageCleanupJob.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.core.job;\n\nimport cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;\nimport cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.redisson.api.RLock;\nimport org.redisson.api.RedissonClient;\nimport org.springframework.data.redis.core.StreamOperations;\nimport org.springframework.scheduling.annotation.Scheduled;\n\nimport java.util.List;\n\n/**\n * Redis Stream 消息清理任务\n * 用于定期清理已消费的消息，防止内存占用过大\n *\n * @see <a href=\"https://www.cnblogs.com/nanxiang/p/16179519.html\">记一次 redis stream 数据类型内存不释放问题</a>\n *\n * @author 芋道源码\n */\n@Slf4j\n@AllArgsConstructor\npublic class RedisStreamMessageCleanupJob {\n\n    private static final String LOCK_KEY = \"redis:stream:message-cleanup:lock\";\n\n    /**\n     * 保留的消息数量，默认保留最近 10000 条消息\n     */\n    private static final long MAX_COUNT = 10000;\n\n    private final List<AbstractRedisStreamMessageListener<?>> listeners;\n    private final RedisMQTemplate redisTemplate;\n    private final RedissonClient redissonClient;\n\n    /**\n     * 每小时执行一次清理任务\n     */\n    @Scheduled(cron = \"0 0 * * * ?\")\n    public void cleanup() {\n        RLock lock = redissonClient.getLock(LOCK_KEY);\n        // 尝试加锁\n        if (lock.tryLock()) {\n            try {\n                execute();\n            } catch (Exception ex) {\n                log.error(\"[cleanup][执行异常]\", ex);\n            } finally {\n                lock.unlock();\n            }\n        }\n    }\n\n    /**\n     * 执行清理逻辑\n     */\n    private void execute() {\n        StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream();\n        listeners.forEach(listener -> {\n            try {\n                // 使用 XTRIM 命令清理消息，只保留最近的 MAX_LEN 条消息\n                Long trimCount = ops.trim(listener.getStreamKey(), MAX_COUNT, true);\n                if (trimCount != null && trimCount > 0) {\n                    log.info(\"[execute][Stream({}) 清理消息数量({})]\", listener.getStreamKey(), trimCount);\n                }\n            } catch (Exception ex) {\n                log.error(\"[execute][Stream({}) 清理异常]\", listener.getStreamKey(), ex);\n            }\n        });\n    }\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/message/AbstractRedisMessage.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.core.message;\n\nimport lombok.Data;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Redis 消息抽象基类\n *\n * @author 芋道源码\n */\n@Data\npublic abstract class AbstractRedisMessage {\n\n    /**\n     * 头\n     */\n    private Map<String, String> headers = new HashMap<>();\n\n    public String getHeader(String key) {\n        return headers.get(key);\n    }\n\n    public void addHeader(String key, String value) {\n        headers.put(key, value);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessage.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.core.pubsub;\n\nimport cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\n\n/**\n * Redis Channel Message 抽象类\n *\n * @author 芋道源码\n */\npublic abstract class AbstractRedisChannelMessage extends AbstractRedisMessage {\n\n    /**\n     * 获得 Redis Channel，默认使用类名\n     *\n     * @return Channel\n     */\n    @JsonIgnore // 避免序列化。原因是，Redis 发布 Channel 消息的时候，已经会指定。\n    public String getChannel() {\n        return getClass().getSimpleName();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessageListener.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.core.pubsub;\n\nimport cn.hutool.core.util.TypeUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;\nimport cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor;\nimport cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage;\nimport lombok.Setter;\nimport lombok.SneakyThrows;\nimport org.springframework.data.redis.connection.Message;\nimport org.springframework.data.redis.connection.MessageListener;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\n\n/**\n * Redis Pub/Sub 监听器抽象类，用于实现广播消费\n *\n * @param <T> 消息类型。一定要填写噢，不然会报错\n *\n * @author 芋道源码\n */\npublic abstract class AbstractRedisChannelMessageListener<T extends AbstractRedisChannelMessage> implements MessageListener {\n\n    /**\n     * 消息类型\n     */\n    private final Class<T> messageType;\n    /**\n     * Redis Channel\n     */\n    private final String channel;\n    /**\n     * RedisMQTemplate\n     */\n    @Setter\n    private RedisMQTemplate redisMQTemplate;\n\n    @SneakyThrows\n    protected AbstractRedisChannelMessageListener() {\n        this.messageType = getMessageClass();\n        this.channel = messageType.getDeclaredConstructor().newInstance().getChannel();\n    }\n\n    /**\n     * 获得 Sub 订阅的 Redis Channel 通道\n     *\n     * @return channel\n     */\n    public final String getChannel() {\n        return channel;\n    }\n\n    @Override\n    public final void onMessage(Message message, byte[] bytes) {\n        T messageObj = JsonUtils.parseObject(message.getBody(), messageType);\n        try {\n            consumeMessageBefore(messageObj);\n            // 消费消息\n            this.onMessage(messageObj);\n        } finally {\n            consumeMessageAfter(messageObj);\n        }\n    }\n\n    /**\n     * 处理消息\n     *\n     * @param message 消息\n     */\n    public abstract void onMessage(T message);\n\n    /**\n     * 通过解析类上的泛型，获得消息类型\n     *\n     * @return 消息类型\n     */\n    @SuppressWarnings(\"unchecked\")\n    private Class<T> getMessageClass() {\n        Type type = TypeUtil.getTypeArgument(getClass(), 0);\n        if (type == null) {\n            throw new IllegalStateException(String.format(\"类型(%s) 需要设置消息类型\", getClass().getName()));\n        }\n        return (Class<T>) type;\n    }\n\n    private void consumeMessageBefore(AbstractRedisMessage message) {\n        assert redisMQTemplate != null;\n        List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors();\n        // 正序\n        interceptors.forEach(interceptor -> interceptor.consumeMessageBefore(message));\n    }\n\n    private void consumeMessageAfter(AbstractRedisMessage message) {\n        assert redisMQTemplate != null;\n        List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors();\n        // 倒序\n        for (int i = interceptors.size() - 1; i >= 0; i--) {\n            interceptors.get(i).consumeMessageAfter(message);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/stream/AbstractRedisStreamMessage.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.core.stream;\n\nimport cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\n\n/**\n * Redis Stream Message 抽象类\n *\n * @author 芋道源码\n */\npublic abstract class AbstractRedisStreamMessage extends AbstractRedisMessage {\n\n    /**\n     * 获得 Redis Stream Key，默认使用类名\n     *\n     * @return Channel\n     */\n    @JsonIgnore // 避免序列化\n    public String getStreamKey() {\n        return getClass().getSimpleName();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/stream/AbstractRedisStreamMessageListener.java",
    "content": "package cn.iocoder.yudao.framework.mq.redis.core.stream;\n\nimport cn.hutool.core.util.TypeUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;\nimport cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor;\nimport cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.SneakyThrows;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.data.redis.connection.stream.ObjectRecord;\nimport org.springframework.data.redis.stream.StreamListener;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\n\n/**\n * Redis Stream 监听器抽象类，用于实现集群消费\n *\n * @param <T> 消息类型。一定要填写噢，不然会报错\n *\n * @author 芋道源码\n */\npublic abstract class AbstractRedisStreamMessageListener<T extends AbstractRedisStreamMessage>\n        implements StreamListener<String, ObjectRecord<String, String>> {\n\n    /**\n     * 消息类型\n     */\n    private final Class<T> messageType;\n    /**\n     * Redis Channel\n     */\n    @Getter\n    private final String streamKey;\n\n    /**\n     * Redis 消费者分组，默认使用 spring.application.name 名字\n     */\n    @Value(\"${spring.application.name}\")\n    @Getter\n    private String group;\n    /**\n     * RedisMQTemplate\n     */\n    @Setter\n    private RedisMQTemplate redisMQTemplate;\n\n    @SneakyThrows\n    protected AbstractRedisStreamMessageListener() {\n        this.messageType = getMessageClass();\n        this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey();\n    }\n\n    protected AbstractRedisStreamMessageListener(String streamKey, String group) {\n        this.messageType = null;\n        this.streamKey = streamKey;\n        this.group = group;\n    }\n\n    @Override\n    public void onMessage(ObjectRecord<String, String> message) {\n        // 消费消息\n        T messageObj = JsonUtils.parseObject(message.getValue(), messageType);\n        try {\n            consumeMessageBefore(messageObj);\n            // 消费消息\n            this.onMessage(messageObj);\n            // ack 消息消费完成\n            redisMQTemplate.getRedisTemplate().opsForStream().acknowledge(group, message);\n            // TODO 芋艿：需要额外考虑以下几个点：\n            // 1. 处理异常的情况\n            // 2. 发送日志；以及事务的结合\n            // 3. 消费日志；以及通用的幂等性\n            // 4. 消费失败的重试，https://zhuanlan.zhihu.com/p/60501638\n        } finally {\n            consumeMessageAfter(messageObj);\n        }\n    }\n\n    /**\n     * 处理消息\n     *\n     * @param message 消息\n     */\n    public abstract void onMessage(T message);\n\n    /**\n     * 通过解析类上的泛型，获得消息类型\n     *\n     * @return 消息类型\n     */\n    @SuppressWarnings(\"unchecked\")\n    private Class<T> getMessageClass() {\n        Type type = TypeUtil.getTypeArgument(getClass(), 0);\n        if (type == null) {\n            throw new IllegalStateException(String.format(\"类型(%s) 需要设置消息类型\", getClass().getName()));\n        }\n        return (Class<T>) type;\n    }\n\n    private void consumeMessageBefore(AbstractRedisMessage message) {\n        assert redisMQTemplate != null;\n        List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors();\n        // 正序\n        interceptors.forEach(interceptor -> interceptor.consumeMessageBefore(message));\n    }\n\n    private void consumeMessageAfter(AbstractRedisMessage message) {\n        assert redisMQTemplate != null;\n        List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors();\n        // 倒序\n        for (int i = interceptors.size() - 1; i >= 0; i--) {\n            interceptors.get(i).consumeMessageAfter(message);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/package-info.java",
    "content": "/**\n * 消息队列，基于 Redis 提供：\n * 1. 基于 Pub/Sub 实现广播消费\n * 2. 基于 Stream 实现集群消费\n */\npackage cn.iocoder.yudao.framework.mq.redis;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQProducerAutoConfiguration\ncn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration\ncn.iocoder.yudao.framework.mq.rabbitmq.config.YudaoRabbitMQAutoConfiguration\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 事件机制 Event 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/RocketMQ/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 Kafka 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Kafka/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RabbitMQ 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/RabbitMQ/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RocketMQ 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/RocketMQ/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>数据库连接池、多数据源、事务、MyBatis 拓展</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有 DefaultDBFieldHandler 使用到 -->\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>com.mysql</groupId>\n            <artifactId>mysql-connector-j</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.oracle.database.jdbc</groupId>\n            <artifactId>ojdbc8</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.postgresql</groupId>\n            <artifactId>postgresql</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>com.microsoft.sqlserver</groupId>\n            <artifactId>mssql-jdbc</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>com.dameng</groupId>\n            <artifactId>DmJdbcDriver18</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>cn.com.kingbase</groupId>\n            <artifactId>kingbase8</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.opengauss</groupId>\n            <artifactId>opengauss-jdbc</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-jsqlparser-4.9</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.yulichang</groupId>\n            <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->\n        </dependency>\n\n        <dependency>\n            <groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->\n            <artifactId>easy-trans-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.fhs-opensource</groupId>\n            <artifactId>easy-trans-mybatis-plus-extend</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/datasource/config/YudaoDataSourceAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.datasource.config;\n\nimport cn.iocoder.yudao.framework.datasource.core.filter.DruidAdRemoveFilter;\nimport com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\n/**\n * 数据库配置类\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理\n@EnableConfigurationProperties(DruidStatProperties.class)\npublic class YudaoDataSourceAutoConfiguration {\n\n    /**\n     * 创建 DruidAdRemoveFilter 过滤器，过滤 common.js 的广告\n     */\n    @Bean\n    @ConditionalOnProperty(name = \"spring.datasource.druid.stat-view-servlet.enabled\", havingValue = \"true\")\n    public FilterRegistrationBean<DruidAdRemoveFilter> druidAdRemoveFilterFilter(DruidStatProperties properties) {\n        // 获取 druid web 监控页面的参数\n        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();\n        // 提取 common.js 的配置路径\n        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : \"/druid/*\";\n        String commonJsPattern = pattern.replaceAll(\"\\\\*\", \"js/common.js\");\n        // 创建 DruidAdRemoveFilter Bean\n        FilterRegistrationBean<DruidAdRemoveFilter> registrationBean = new FilterRegistrationBean<>();\n        registrationBean.setFilter(new DruidAdRemoveFilter());\n        registrationBean.addUrlPatterns(commonJsPattern);\n        return registrationBean;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/datasource/core/enums/DataSourceEnum.java",
    "content": "package cn.iocoder.yudao.framework.datasource.core.enums;\n\n/**\n * 对应于多数据源中不同数据源配置\n *\n * 通过在方法上，使用 {@link com.baomidou.dynamic.datasource.annotation.DS} 注解，设置使用的数据源。\n * 注意，默认是 {@link #MASTER} 数据源\n *\n * 对应官方文档为 http://dynamic-datasource.com/guide/customize/Annotation.html\n */\npublic interface DataSourceEnum {\n\n    /**\n     * 主库，推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Master} 注解\n     */\n    String MASTER = \"master\";\n    /**\n     * 从库，推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Slave} 注解\n     */\n    String SLAVE = \"slave\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/datasource/core/filter/DruidAdRemoveFilter.java",
    "content": "package cn.iocoder.yudao.framework.datasource.core.filter;\n\nimport com.alibaba.druid.util.Utils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * Druid 底部广告过滤器\n *\n * @author 芋道源码\n */\npublic class DruidAdRemoveFilter extends OncePerRequestFilter {\n\n    /**\n     * common.js 的路径\n     */\n    private static final String COMMON_JS_ILE_PATH = \"support/http/resources/js/common.js\";\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        chain.doFilter(request, response);\n        // 重置缓冲区，响应头不会被重置\n        response.resetBuffer();\n        // 获取 common.js\n        String text = Utils.readFromResource(COMMON_JS_ILE_PATH);\n        // 正则替换 banner, 除去底部的广告信息\n        text = text.replaceAll(\"<a.*?banner\\\"></a><br/>\", \"\");\n        text = text.replaceAll(\"powered.*?shrek.wang</a>\", \"\");\n        response.getWriter().write(text);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/datasource/package-info.java",
    "content": "/**\n * 数据库连接池，采用 Druid\n * 多数据源，采用爆米花\n */\npackage cn.iocoder.yudao.framework.datasource;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.config;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.env.EnvironmentPostProcessor;\nimport org.springframework.core.env.ConfigurableEnvironment;\nimport org.springframework.core.env.MapPropertySource;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 当 IdType 为 {@link IdType#NONE} 时，根据 PRIMARY 数据源所使用的数据库，自动设置\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IdTypeEnvironmentPostProcessor implements EnvironmentPostProcessor {\n\n    private static final String ID_TYPE_KEY = \"mybatis-plus.global-config.db-config.id-type\";\n\n    private static final String DATASOURCE_DYNAMIC_KEY = \"spring.datasource.dynamic\";\n\n    private static final String QUARTZ_JOB_STORE_DRIVER_KEY = \"spring.quartz.properties.org.quartz.jobStore.driverDelegateClass\";\n\n    private static final Set<DbType> INPUT_ID_TYPES = SetUtils.asSet(DbType.ORACLE, DbType.ORACLE_12C,\n            DbType.POSTGRE_SQL, DbType.KINGBASE_ES, DbType.DB2, DbType.H2);\n\n    @Override\n    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {\n        // 如果获取不到 DbType，则不进行处理\n        DbType dbType = getDbType(environment);\n        if (dbType == null) {\n            return;\n        }\n\n        // 设置 Quartz JobStore 对应的 Driver\n        // TODO 芋艿：暂时没有找到特别合适的地方，先放在这里\n        setJobStoreDriverIfPresent(environment, dbType);\n\n        // 如果非 NONE，则不进行处理\n        IdType idType = getIdType(environment);\n        if (idType != IdType.NONE) {\n            return;\n        }\n        // 情况一，用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n        if (INPUT_ID_TYPES.contains(dbType)) {\n            setIdType(environment, IdType.INPUT);\n            return;\n        }\n        // 情况二，自增 ID，适合 MySQL、DM 达梦等直接自增的数据库\n        setIdType(environment, IdType.AUTO);\n    }\n\n    public IdType getIdType(ConfigurableEnvironment environment) {\n        String value = environment.getProperty(ID_TYPE_KEY);\n        try {\n            return StrUtil.isNotBlank(value) ? IdType.valueOf(value) : IdType.NONE;\n        } catch (IllegalArgumentException ex) {\n            log.error(\"[getIdType][无法解析 id-type 配置值({})]\", value, ex);\n            return IdType.NONE;\n        }\n    }\n\n    public void setIdType(ConfigurableEnvironment environment, IdType idType) {\n        Map<String, Object> map = new HashMap<>();\n        map.put(ID_TYPE_KEY, idType);\n        environment.getPropertySources().addFirst(new MapPropertySource(\"mybatisPlusIdType\", map));\n        log.info(\"[setIdType][修改 MyBatis Plus 的 idType 为({})]\", idType);\n    }\n\n    public void setJobStoreDriverIfPresent(ConfigurableEnvironment environment, DbType dbType) {\n        String driverClass = environment.getProperty(QUARTZ_JOB_STORE_DRIVER_KEY);\n        if (StrUtil.isNotEmpty(driverClass)) {\n            return;\n        }\n        // 根据 dbType 类型，获取对应的 driverClass\n        switch (dbType) {\n            case POSTGRE_SQL:\n                driverClass = \"org.quartz.impl.jdbcjobstore.PostgreSQLDelegate\";\n                break;\n            case ORACLE:\n            case ORACLE_12C:\n                driverClass = \"org.quartz.impl.jdbcjobstore.oracle.OracleDelegate\";\n                break;\n            case SQL_SERVER:\n            case SQL_SERVER2005:\n                driverClass = \"org.quartz.impl.jdbcjobstore.MSSQLDelegate\";\n                break;\n            case DM:\n            case KINGBASE_ES:\n                driverClass = \"org.quartz.impl.jdbcjobstore.StdJDBCDelegate\";\n                break;\n        }\n        // 设置 driverClass 变量\n        if (StrUtil.isNotEmpty(driverClass)) {\n            environment.getSystemProperties().put(QUARTZ_JOB_STORE_DRIVER_KEY, driverClass);\n        }\n    }\n\n    public static DbType getDbType(ConfigurableEnvironment environment) {\n        String primary = environment.getProperty(DATASOURCE_DYNAMIC_KEY + \".\" + \"primary\");\n        if (StrUtil.isEmpty(primary)) {\n            return null;\n        }\n        String url = environment.getProperty(DATASOURCE_DYNAMIC_KEY + \".datasource.\" + primary + \".url\");\n        if (StrUtil.isEmpty(url)) {\n            return null;\n        }\n        return JdbcUtils.getDbType(url);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.config;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;\nimport com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport com.baomidou.mybatisplus.extension.incrementer.*;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\nimport com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.env.ConfigurableEnvironment;\n\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * MyBaits 配置类\n *\n * @author 芋道源码\n */\n@AutoConfiguration(before = MybatisPlusAutoConfiguration.class) // 目的：先于 MyBatis Plus 自动配置，避免 @MapperScan 可能扫描不到 Mapper 打印 warn 日志\n@MapperScan(value = \"${yudao.info.base-package}\", annotationClass = Mapper.class,\n        lazyInitialization = \"${mybatis.lazy-initialization:false}\") // Mapper 懒加载，目前仅用于单元测试\npublic class YudaoMybatisAutoConfiguration {\n\n    static {\n        // 动态 SQL 智能优化支持本地缓存加速解析，更完善的租户复杂 XML 动态 SQL 支持，静态注入缓存\n        JsqlParserGlobal.setJsqlParseCache(new JdkSerialCaffeineJsqlParseCache(\n                (cache) -> cache.maximumSize(1024)\n                        .expireAfterWrite(5, TimeUnit.SECONDS))\n        );\n    }\n\n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页插件\n        // ↓↓↓ 按需开启，可能会影响到 updateBatch 的地方：例如说文件配置管理 ↓↓↓\n        // mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 拦截没有指定条件的 update 和 delete 语句\n        return mybatisPlusInterceptor;\n    }\n\n    @Bean\n    public MetaObjectHandler defaultMetaObjectHandler() {\n        return new DefaultDBFieldHandler(); // 自动填充参数类\n    }\n\n    @Bean\n    @ConditionalOnProperty(prefix = \"mybatis-plus.global-config.db-config\", name = \"id-type\", havingValue = \"INPUT\")\n    public IKeyGenerator keyGenerator(ConfigurableEnvironment environment) {\n        DbType dbType = IdTypeEnvironmentPostProcessor.getDbType(environment);\n        if (dbType != null) {\n            switch (dbType) {\n                case POSTGRE_SQL:\n                    return new PostgreKeyGenerator();\n                case ORACLE:\n                case ORACLE_12C:\n                    return new OracleKeyGenerator();\n                case H2:\n                    return new H2KeyGenerator();\n                case KINGBASE_ES:\n                    return new KingbaseKeyGenerator();\n                case DM:\n                    return new DmKeyGenerator();\n            }\n        }\n        // 找不到合适的 IKeyGenerator 实现类\n        throw new IllegalArgumentException(StrUtil.format(\"DbType{} 找不到合适的 IKeyGenerator 实现类\", dbType));\n    }\n\n    @Bean // 特殊：返回结果使用 Object 而不用 JacksonTypeHandler 的原因，避免因为 JacksonTypeHandler 被 mybatis 全局使用！\n    public Object jacksonTypeHandler(List<ObjectMapper> objectMappers) {\n        // 特殊：设置 JacksonTypeHandler 的 ObjectMapper！\n        ObjectMapper objectMapper = CollUtil.getFirst(objectMappers);\n        if (objectMapper == null) {\n            objectMapper = JsonUtils.getObjectMapper();\n        }\n        JacksonTypeHandler.setObjectMapper(objectMapper);\n        return new JacksonTypeHandler(Object.class);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.dataobject;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fhs.core.trans.vo.TransPojo;\nimport lombok.Data;\nimport org.apache.ibatis.type.JdbcType;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\n/**\n * 基础实体对象\n *\n * 为什么实现 {@link TransPojo} 接口？\n * 因为使用 Easy-Trans TransType.SIMPLE 模式，集成 MyBatis Plus 查询\n *\n * @author 芋道源码\n */\n@Data\n@JsonIgnoreProperties(value = \"transMap\") // 由于 Easy-Trans 会添加 transMap 属性，避免 Jackson 在 Spring Cache 反序列化报错\npublic abstract class BaseDO implements Serializable, TransPojo {\n\n    /**\n     * 创建时间\n     */\n    @TableField(fill = FieldFill.INSERT)\n    private LocalDateTime createTime;\n    /**\n     * 最后更新时间\n     */\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private LocalDateTime updateTime;\n    /**\n     * 创建者，目前使用 SysUser 的 id 编号\n     *\n     * 使用 String 类型的原因是，未来可能会存在非数值的情况，留好拓展性。\n     */\n    @TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)\n    private String creator;\n    /**\n     * 更新者，目前使用 SysUser 的 id 编号\n     *\n     * 使用 String 类型的原因是，未来可能会存在非数值的情况，留好拓展性。\n     */\n    @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR)\n    private String updater;\n    /**\n     * 是否删除\n     */\n    @TableLogic\n    private Boolean deleted;\n\n    /**\n     * 把 creator、createTime、updateTime、updater 都清空，避免前端直接传递 creator 之类的字段，直接就被更新了\n     */\n    public void clean(){\n        this.creator = null;\n        this.createTime = null;\n        this.updater = null;\n        this.updateTime = null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/enums/DbTypeEnum.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.enums;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 针对 MyBatis Plus 的 {@link DbType} 增强，补充更多信息\n */\n@Getter\n@AllArgsConstructor\npublic enum DbTypeEnum {\n\n    /**\n     * H2\n     *\n     * 注意：H2 不支持 find_in_set 函数\n     */\n    H2(DbType.H2, \"H2\", \"\"),\n\n    /**\n     * MySQL\n     */\n    MY_SQL(DbType.MYSQL, \"MySQL\", \"FIND_IN_SET('#{value}', #{column}) <> 0\"),\n\n    /**\n     * Oracle\n     */\n    ORACLE(DbType.ORACLE, \"Oracle\", \"FIND_IN_SET('#{value}', #{column}) <> 0\"),\n\n    /**\n     * PostgreSQL\n     *\n     * 华为 openGauss 使用 ProductName 与 PostgreSQL 相同\n     */\n    POSTGRE_SQL(DbType.POSTGRE_SQL,\"PostgreSQL\", \"POSITION('#{value}' IN #{column}) <> 0\"),\n\n    /**\n     * SQL Server\n     */\n    SQL_SERVER(DbType.SQL_SERVER, \"Microsoft SQL Server\", \"CHARINDEX(',' + #{value} + ',', ',' + #{column} + ',') <> 0\"),\n    /**\n     * SQL Server 2005\n     */\n    SQL_SERVER2005(DbType.SQL_SERVER2005, \"Microsoft SQL Server 2005\", \"CHARINDEX(',' + #{value} + ',', ',' + #{column} + ',') <> 0\"),\n\n    /**\n     * 达梦\n     */\n    DM(DbType.DM, \"DM DBMS\", \"FIND_IN_SET('#{value}', #{column}) <> 0\"),\n\n    /**\n     * 人大金仓\n     */\n    KINGBASE_ES(DbType.KINGBASE_ES, \"KingbaseES\", \"POSITION('#{value}' IN #{column}) <> 0\"),\n\n    /**\n     * OceanBase\n     */\n    OCEAN_BASE(DbType.OCEAN_BASE, \"OceanBase\", \"FIND_IN_SET('#{value}', #{column}) <> 0\")\n\n    ;\n\n    public static final Map<String, DbTypeEnum> MAP_BY_NAME = Arrays.stream(values())\n            .collect(Collectors.toMap(DbTypeEnum::getProductName, Function.identity()));\n\n    public static final Map<DbType, DbTypeEnum> MAP_BY_MP = Arrays.stream(values())\n            .collect(Collectors.toMap(DbTypeEnum::getMpDbType, Function.identity()));\n\n    /**\n     * MyBatis Plus 类型\n     */\n    private final DbType mpDbType;\n    /**\n     * 数据库产品名\n     */\n    private final String productName;\n    /**\n     * SQL FIND_IN_SET 模板\n     */\n    private final String findInSetTemplate;\n\n    public static DbType find(String databaseProductName) {\n        if (StrUtil.isBlank(databaseProductName)) {\n            return null;\n        }\n        return MAP_BY_NAME.get(databaseProductName).getMpDbType();\n    }\n\n    public static String getFindInSetTemplate(DbType dbType) {\n        return Optional.of(MAP_BY_MP.get(dbType).getFindInSetTemplate())\n                .orElseThrow(() -> new IllegalArgumentException(\"FIND_IN_SET not supported\"));\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.handler;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport org.apache.ibatis.reflection.MetaObject;\n\nimport java.time.LocalDateTime;\nimport java.util.Objects;\n\n/**\n * 通用参数填充实现类\n *\n * 如果没有显式的对通用参数进行赋值，这里会对通用参数进行填充、赋值\n *\n * @author hexiaowu\n */\npublic class DefaultDBFieldHandler implements MetaObjectHandler {\n\n    @Override\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    public void insertFill(MetaObject metaObject) {\n        if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) {\n            BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();\n\n            LocalDateTime current = LocalDateTime.now();\n            // 创建时间为空，则以当前时间为插入时间\n            if (Objects.isNull(baseDO.getCreateTime())) {\n                baseDO.setCreateTime(current);\n            }\n            // 更新时间为空，则以当前时间为更新时间\n            if (Objects.isNull(baseDO.getUpdateTime())) {\n                baseDO.setUpdateTime(current);\n            }\n\n            Long userId = SecurityFrameworkUtils.getLoginUserId();\n            // 当前登录用户不为空，创建人为空，则当前登录用户为创建人\n            if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) {\n                baseDO.setCreator(userId.toString());\n            }\n            // 当前登录用户不为空，更新人为空，则当前登录用户为更新人\n            if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) {\n                baseDO.setUpdater(userId.toString());\n            }\n        }\n    }\n\n    @Override\n    public void updateFill(MetaObject metaObject) {\n        // 更新时间为空，则以当前时间为更新时间\n        Object modifyTime = getFieldValByName(\"updateTime\", metaObject);\n        if (Objects.isNull(modifyTime)) {\n            setFieldValByName(\"updateTime\", LocalDateTime.now(), metaObject);\n        }\n\n        // 当前登录用户不为空，更新人为空，则当前登录用户为更新人\n        Object modifier = getFieldValByName(\"updater\", metaObject);\n        Long userId = SecurityFrameworkUtils.getLoginUserId();\n        if (Objects.nonNull(userId) && Objects.isNull(modifier)) {\n            setFieldValByName(\"updater\", userId.toString(), metaObject);\n        }\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.mapper;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.SortablePageParam;\nimport cn.iocoder.yudao.framework.common.pojo.SortingField;\nimport cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.extension.toolkit.Db;\nimport com.github.yulichang.base.MPJBaseMapper;\nimport com.github.yulichang.interfaces.MPJBaseJoin;\nimport com.github.yulichang.wrapper.MPJLambdaWrapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 在 MyBatis Plus 的 BaseMapper 的基础上拓展，提供更多的能力\n *\n * 1. {@link BaseMapper} 为 MyBatis Plus 的基础接口，提供基础的 CRUD 能力\n * 2. {@link MPJBaseMapper} 为 MyBatis Plus Join 的基础接口，提供连表 Join 能力\n */\npublic interface BaseMapperX<T> extends MPJBaseMapper<T> {\n\n    default PageResult<T> selectPage(SortablePageParam pageParam, @Param(\"ew\") Wrapper<T> queryWrapper) {\n        return selectPage(pageParam, pageParam.getSortingFields(), queryWrapper);\n    }\n\n    default PageResult<T> selectPage(PageParam pageParam, @Param(\"ew\") Wrapper<T> queryWrapper) {\n        return selectPage(pageParam, null, queryWrapper);\n    }\n\n    default PageResult<T> selectPage(PageParam pageParam, Collection<SortingField> sortingFields, @Param(\"ew\") Wrapper<T> queryWrapper) {\n        // 特殊：不分页，直接查询全部\n        if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {\n            MyBatisUtils.addOrder(queryWrapper, sortingFields);\n            List<T> list = selectList(queryWrapper);\n            return new PageResult<>(list, (long) list.size());\n        }\n\n        // MyBatis Plus 查询\n        IPage<T> mpPage = MyBatisUtils.buildPage(pageParam, sortingFields);\n        selectPage(mpPage, queryWrapper);\n        // 转换返回\n        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());\n    }\n\n    default <D> PageResult<D> selectJoinPage(PageParam pageParam, Class<D> clazz, MPJLambdaWrapper<T> lambdaWrapper) {\n        // 特殊：不分页，直接查询全部\n        if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {\n            List<D> list = selectJoinList(clazz, lambdaWrapper);\n            return new PageResult<>(list, (long) list.size());\n        }\n\n        // MyBatis Plus Join 查询\n        IPage<D> mpPage = MyBatisUtils.buildPage(pageParam);\n        mpPage = selectJoinPage(mpPage, clazz, lambdaWrapper);\n        // 转换返回\n        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());\n    }\n\n    /**\n     * 执行分页查询并返回结果。\n     *\n     * @param pageParam 分页参数，包含页码、每页条数和排序字段信息。如果 pageSize 为 {@link PageParam#PAGE_SIZE_NONE}，则不分页，直接查询所有数据。\n     * @param clazz     结果集的类类型\n     * @param lambdaWrapper MyBatis Plus Join 查询条件包装器\n     * @param <D>       结果集的泛型类型\n     * @return 返回分页查询的结果，包括总记录数和当前页的数据列表\n     */\n    default <D> PageResult<D> selectJoinPage(SortablePageParam pageParam, Class<D> clazz, MPJLambdaWrapper<T> lambdaWrapper) {\n        // 特殊：不分页，直接查询全部\n        if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {\n            List<D> list = selectJoinList(clazz, lambdaWrapper);\n            return new PageResult<>(list, (long) list.size());\n        }\n\n        // MyBatis Plus Join 查询\n        IPage<D> mpPage = MyBatisUtils.buildPage(pageParam, pageParam.getSortingFields());\n        mpPage = selectJoinPage(mpPage, clazz, lambdaWrapper);\n        // 转换返回\n        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());\n    }\n\n    default <DTO> PageResult<DTO> selectJoinPage(PageParam pageParam, Class<DTO> resultTypeClass, MPJBaseJoin<T> joinQueryWrapper) {\n        IPage<DTO> mpPage = MyBatisUtils.buildPage(pageParam);\n        selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper);\n        // 转换返回\n        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());\n    }\n\n    default T selectOne(String field, Object value) {\n        return selectOne(new QueryWrapper<T>().eq(field, value));\n    }\n\n    default T selectOne(SFunction<T, ?> field, Object value) {\n        return selectOne(new LambdaQueryWrapper<T>().eq(field, value));\n    }\n\n    default T selectOne(String field1, Object value1, String field2, Object value2) {\n        return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2));\n    }\n\n    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {\n        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));\n    }\n\n    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,\n                        SFunction<T, ?> field3, Object value3) {\n        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));\n    }\n\n    /**\n     * 获取满足条件的第 1 条记录\n     *\n     * 目的：解决并发场景下，插入多条记录后，使用 selectOne 会报错的问题\n     *\n     * @param field 字段名\n     * @param value 字段值\n     * @return 实体\n     */\n    default T selectFirstOne(SFunction<T, ?> field, Object value) {\n        // 如果明确使用 MySQL 等场景，可以考虑使用 LIMIT 1 进行优化\n        List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field, value));\n        return CollUtil.getFirst(list);\n    }\n\n    default T selectFirstOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {\n        List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));\n        return CollUtil.getFirst(list);\n    }\n\n    default T selectFirstOne(SFunction<T,?> field1, Object value1, SFunction<T,?> field2, Object value2,\n                             SFunction<T,?> field3, Object value3) {\n        List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));\n        return CollUtil.getFirst(list);\n    }\n\n\n    default Long selectCount() {\n        return selectCount(new QueryWrapper<>());\n    }\n\n    default Long selectCount(String field, Object value) {\n        return selectCount(new QueryWrapper<T>().eq(field, value));\n    }\n\n    default Long selectCount(SFunction<T, ?> field, Object value) {\n        return selectCount(new LambdaQueryWrapper<T>().eq(field, value));\n    }\n\n    default List<T> selectList() {\n        return selectList(new QueryWrapper<>());\n    }\n\n    default List<T> selectList(String field, Object value) {\n        return selectList(new QueryWrapper<T>().eq(field, value));\n    }\n\n    default List<T> selectList(SFunction<T, ?> field, Object value) {\n        return selectList(new LambdaQueryWrapper<T>().eq(field, value));\n    }\n\n    default List<T> selectList(String field, Collection<?> values) {\n        if (CollUtil.isEmpty(values)) {\n            return CollUtil.newArrayList();\n        }\n        return selectList(new QueryWrapper<T>().in(field, values));\n    }\n\n    default List<T> selectList(SFunction<T, ?> field, Collection<?> values) {\n        if (CollUtil.isEmpty(values)) {\n            return CollUtil.newArrayList();\n        }\n        return selectList(new LambdaQueryWrapper<T>().in(field, values));\n    }\n\n    default List<T> selectList(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {\n        return selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));\n    }\n\n    /**\n     * 批量插入，适合大量数据插入\n     *\n     * @param entities 实体们\n     */\n    default Boolean insertBatch(Collection<T> entities) {\n        // 特殊：SQL Server 批量插入后，获取 id 会报错，因此通过循环处理\n        DbType dbType = JdbcUtils.getDbType();\n        if (JdbcUtils.isSQLServer(dbType)) {\n            entities.forEach(this::insert);\n            return CollUtil.isNotEmpty(entities);\n        }\n        return Db.saveBatch(entities);\n    }\n\n    /**\n     * 批量插入，适合大量数据插入\n     *\n     * @param entities 实体们\n     * @param size     插入数量 Db.saveBatch 默认为 1000\n     */\n    default Boolean insertBatch(Collection<T> entities, int size) {\n        // 特殊：SQL Server 批量插入后，获取 id 会报错，因此通过循环处理\n        DbType dbType = JdbcUtils.getDbType();\n        if (JdbcUtils.isSQLServer(dbType)) {\n            entities.forEach(this::insert);\n            return CollUtil.isNotEmpty(entities);\n        }\n        return Db.saveBatch(entities, size);\n    }\n\n    default int updateBatch(T update) {\n        return update(update, new QueryWrapper<>());\n    }\n\n    default Boolean updateBatch(Collection<T> entities) {\n        return Db.updateBatchById(entities);\n    }\n\n    default Boolean updateBatch(Collection<T> entities, int size) {\n        return Db.updateBatchById(entities, size);\n    }\n\n    default int delete(String field, String value) {\n        return delete(new QueryWrapper<T>().eq(field, value));\n    }\n\n    default int delete(SFunction<T, ?> field, Object value) {\n        return delete(new LambdaQueryWrapper<T>().eq(field, value));\n    }\n\n    default int deleteBatch(SFunction<T, ?> field, Collection<?> values) {\n        if (CollUtil.isEmpty(values)) {\n            return 0;\n        }\n        return delete(new LambdaQueryWrapper<T>().in(field, values));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.query;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport org.springframework.util.StringUtils;\n\nimport java.util.Collection;\n\n/**\n * 拓展 MyBatis Plus QueryWrapper 类，主要增加如下功能：\n * <p>\n * 1. 拼接条件的方法，增加 xxxIfPresent 方法，用于判断值不存在的时候，不要拼接到条件中。\n *\n * @param <T> 数据类型\n */\npublic class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {\n\n    public LambdaQueryWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {\n        if (StringUtils.hasText(val)) {\n            return (LambdaQueryWrapperX<T>) super.like(column, val);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {\n        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {\n            return (LambdaQueryWrapperX<T>) super.in(column, values);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {\n        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {\n            return (LambdaQueryWrapperX<T>) super.in(column, values);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {\n        if (ObjectUtil.isNotEmpty(val)) {\n            return (LambdaQueryWrapperX<T>) super.eq(column, val);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {\n        if (ObjectUtil.isNotEmpty(val)) {\n            return (LambdaQueryWrapperX<T>) super.ne(column, val);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {\n        if (val != null) {\n            return (LambdaQueryWrapperX<T>) super.gt(column, val);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {\n        if (val != null) {\n            return (LambdaQueryWrapperX<T>) super.ge(column, val);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {\n        if (val != null) {\n            return (LambdaQueryWrapperX<T>) super.lt(column, val);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {\n        if (val != null) {\n            return (LambdaQueryWrapperX<T>) super.le(column, val);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {\n        if (val1 != null && val2 != null) {\n            return (LambdaQueryWrapperX<T>) super.between(column, val1, val2);\n        }\n        if (val1 != null) {\n            return (LambdaQueryWrapperX<T>) ge(column, val1);\n        }\n        if (val2 != null) {\n            return (LambdaQueryWrapperX<T>) le(column, val2);\n        }\n        return this;\n    }\n\n    public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {\n        Object val1 = ArrayUtils.get(values, 0);\n        Object val2 = ArrayUtils.get(values, 1);\n        return betweenIfPresent(column, val1, val2);\n    }\n\n    // ========== 重写父类方法，方便链式调用 ==========\n\n    @Override\n    public LambdaQueryWrapperX<T> eq(boolean condition, SFunction<T, ?> column, Object val) {\n        super.eq(condition, column, val);\n        return this;\n    }\n\n    @Override\n    public LambdaQueryWrapperX<T> eq(SFunction<T, ?> column, Object val) {\n        super.eq(column, val);\n        return this;\n    }\n\n    @Override\n    public LambdaQueryWrapperX<T> orderByDesc(SFunction<T, ?> column) {\n        super.orderByDesc(true, column);\n        return this;\n    }\n\n    @Override\n    public LambdaQueryWrapperX<T> last(String lastSql) {\n        super.last(lastSql);\n        return this;\n    }\n\n    @Override\n    public LambdaQueryWrapperX<T> in(SFunction<T, ?> column, Collection<?> coll) {\n        super.in(column, coll);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.query;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.github.yulichang.wrapper.MPJLambdaWrapper;\nimport org.springframework.util.StringUtils;\n\nimport java.util.Collection;\nimport java.util.function.Consumer;\n\n/**\n * 拓展 MyBatis Plus Join QueryWrapper 类，主要增加如下功能：\n * <p>\n * 1. 拼接条件的方法，增加 xxxIfPresent 方法，用于判断值不存在的时候，不要拼接到条件中。\n * 2. SFunction<S, ?> column + <S> 泛型：支持任意类字段（主表、子表、三表），推荐写法, 让编译器自动推断 S 类型\n *\n * @param <T> 数据类型\n */\npublic class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {\n\n    public <S> MPJLambdaWrapperX<T> likeIfPresent(SFunction<S, ?> column, String val) {\n        if (StringUtils.hasText(val)) {\n            return (MPJLambdaWrapperX<T>) super.like(column, val);\n        }\n        return this;\n    }\n\n    public <S> MPJLambdaWrapperX<T> inIfPresent(SFunction<S, ?> column, Collection<?> values) {\n        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {\n            return (MPJLambdaWrapperX<T>) super.in(column, values);\n        }\n        return this;\n    }\n\n    public <S> MPJLambdaWrapperX<T> inIfPresent(SFunction<S, ?> column, Object... values) {\n        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {\n            return (MPJLambdaWrapperX<T>) super.in(column, values);\n        }\n        return this;\n    }\n\n    public <S> MPJLambdaWrapperX<T> eqIfPresent(SFunction<S, ?> column, Object val) {\n        if (ObjectUtil.isNotEmpty(val)) {\n            return (MPJLambdaWrapperX<T>) super.eq(column, val);\n        }\n        return this;\n    }\n\n    public <S> MPJLambdaWrapperX<T> neIfPresent(SFunction<S, ?> column, Object val) {\n        if (ObjectUtil.isNotEmpty(val)) {\n            return (MPJLambdaWrapperX<T>) super.ne(column, val);\n        }\n        return this;\n    }\n\n    public <S> MPJLambdaWrapperX<T> gtIfPresent(SFunction<S, ?> column, Object val) {\n        if (val != null) {\n            return (MPJLambdaWrapperX<T>) super.gt(column, val);\n        }\n        return this;\n    }\n\n    public <S> MPJLambdaWrapperX<T> geIfPresent(SFunction<S, ?> column, Object val) {\n        if (val != null) {\n            return (MPJLambdaWrapperX<T>) super.ge(column, val);\n        }\n        return this;\n    }\n\n    public <S> MPJLambdaWrapperX<T> ltIfPresent(SFunction<S, ?> column, Object val) {\n        if (val != null) {\n            return (MPJLambdaWrapperX<T>) super.lt(column, val);\n        }\n        return this;\n    }\n\n    public <S> MPJLambdaWrapperX<T> leIfPresent(SFunction<S, ?> column, Object val) {\n        if (val != null) {\n            return (MPJLambdaWrapperX<T>) super.le(column, val);\n        }\n        return this;\n    }\n\n    public <S> MPJLambdaWrapperX<T> betweenIfPresent(SFunction<S, ?> column, Object[] values) {\n        Object val1 = ArrayUtils.get(values, 0);\n        Object val2 = ArrayUtils.get(values, 1);\n        return betweenIfPresent(column, val1, val2);\n    }\n\n    public <S> MPJLambdaWrapperX<T> betweenIfPresent(SFunction<S, ?> column, Object val1, Object val2) {\n        if (val1 != null && val2 != null) {\n            return (MPJLambdaWrapperX<T>) super.between(column, val1, val2);\n        }\n        if (val1 != null) {\n            return (MPJLambdaWrapperX<T>) super.ge(column, val1);\n        }\n        if (val2 != null) {\n            return (MPJLambdaWrapperX<T>) super.le(column, val2);\n        }\n        return this;\n    }\n\n\n    // ========== 重写父类方法，方便链式调用 ==========\n\n    @Override\n    public <X> MPJLambdaWrapperX<T> eq(boolean condition, SFunction<X, ?> column, Object val) {\n        super.eq(condition, column, val);\n        return this;\n    }\n\n    @Override\n    public <X> MPJLambdaWrapperX<T> eq(SFunction<X, ?> column, Object val) {\n        super.eq(column, val);\n        return this;\n    }\n\n    @Override\n    public <X> MPJLambdaWrapperX<T> orderByDesc(SFunction<X, ?> column) {\n        super.orderByDesc(true, column);\n        return this;\n    }\n\n    @Override\n    public <X> MPJLambdaWrapperX<T> orderByAsc(SFunction<X, ?> column) {\n        super.orderByAsc(true, column);\n        return this;\n    }\n\n    @Override\n    public MPJLambdaWrapperX<T> last(String lastSql) {\n        super.last(lastSql);\n        return this;\n    }\n\n    @Override\n    public <X> MPJLambdaWrapperX<T> in(SFunction<X, ?> column, Collection<?> coll) {\n        super.in(column, coll);\n        return this;\n    }\n\n    @Override\n    public MPJLambdaWrapperX<T> selectAll(Class<?> clazz) {\n        super.selectAll(clazz);\n        return this;\n    }\n\n    @Override\n    public MPJLambdaWrapperX<T> selectAll(Class<?> clazz, String prefix) {\n        super.selectAll(clazz, prefix);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, String alias) {\n        super.selectAs(column, alias);\n        return this;\n    }\n\n    @Override\n    public <E> MPJLambdaWrapperX<T> selectAs(String column, SFunction<E, ?> alias) {\n        super.selectAs(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S, X> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, SFunction<X, ?> alias) {\n        super.selectAs(column, alias);\n        return this;\n    }\n\n    @Override\n    public <E, X> MPJLambdaWrapperX<T> selectAs(String index, SFunction<E, ?> column, SFunction<X, ?> alias) {\n        super.selectAs(index, column, alias);\n        return this;\n    }\n\n    @Override\n    public <E> MPJLambdaWrapperX<T> selectAsClass(Class<E> source, Class<?> tag) {\n        super.selectAsClass(source, tag);\n        return this;\n    }\n\n    @Override\n    public <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) {\n        super.selectSub(clazz, consumer, alias);\n        return this;\n    }\n\n    @Override\n    public <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, String st, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) {\n        super.selectSub(clazz, st, consumer, alias);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column) {\n        super.selectCount(column);\n        return this;\n    }\n\n    @Override\n    public MPJLambdaWrapperX<T> selectCount(Object column, String alias) {\n        super.selectCount(column, alias);\n        return this;\n    }\n\n    @Override\n    public <X> MPJLambdaWrapperX<T> selectCount(Object column, SFunction<X, ?> alias) {\n        super.selectCount(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, String alias) {\n        super.selectCount(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, SFunction<X, ?> alias) {\n        super.selectCount(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column) {\n        super.selectSum(column);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, String alias) {\n        super.selectSum(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, SFunction<X, ?> alias) {\n        super.selectSum(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column) {\n        super.selectMax(column);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, String alias) {\n        super.selectMax(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, SFunction<X, ?> alias) {\n        super.selectMax(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column) {\n        super.selectMin(column);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, String alias) {\n        super.selectMin(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, SFunction<X, ?> alias) {\n        super.selectMin(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column) {\n        super.selectAvg(column);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, String alias) {\n        super.selectAvg(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, SFunction<X, ?> alias) {\n        super.selectAvg(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column) {\n        super.selectLen(column);\n        return this;\n    }\n\n    @Override\n    public <S> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, String alias) {\n        super.selectLen(column, alias);\n        return this;\n    }\n\n    @Override\n    public <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, SFunction<X, ?> alias) {\n        super.selectLen(column, alias);\n        return this;\n    }\n\n    // ========== 关键重写：使 leftJoin 返回当前类型 this ==========\n    @Override\n    public <A, B> MPJLambdaWrapperX<T> leftJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right) {\n        super.leftJoin(clazz, left, right);\n        return this;\n    }\n\n    @Override\n    public <A, B> MPJLambdaWrapperX<T> rightJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right) {\n        super.rightJoin(clazz, left, right);\n        return this;\n    }\n\n    @Override\n    public <A, B> MPJLambdaWrapperX<T> innerJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right) {\n        super.innerJoin(clazz, left, right);\n        return this;\n    }\n\n    // ========== 添加扩展 Join 支持 ext 函数式参数 ==========\n    public <A, B> MPJLambdaWrapperX<T> leftJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right, Consumer<MPJLambdaWrapperX<T>> ext) {\n        super.leftJoin(clazz, left, right);\n        if (ext != null) ext.accept(this);\n        return this;\n    }\n\n    public <A, B> MPJLambdaWrapperX<T> rightJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right, Consumer<MPJLambdaWrapperX<T>> ext) {\n        super.rightJoin(clazz, left, right);\n        if (ext != null) ext.accept(this);\n        return this;\n    }\n\n    public <A, B> MPJLambdaWrapperX<T> innerJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right, Consumer<MPJLambdaWrapperX<T>> ext) {\n        super.innerJoin(clazz, left, right);\n        if (ext != null) ext.accept(this);\n        return this;\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.query;\n\nimport cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.ArrayUtils;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\nimport java.util.Collection;\n\n/**\n * 拓展 MyBatis Plus QueryWrapper 类，主要增加如下功能：\n *\n * 1. 拼接条件的方法，增加 xxxIfPresent 方法，用于判断值不存在的时候，不要拼接到条件中。\n *\n * @param <T> 数据类型\n */\npublic class QueryWrapperX<T> extends QueryWrapper<T> {\n\n    public QueryWrapperX<T> likeIfPresent(String column, String val) {\n        if (StringUtils.hasText(val)) {\n            return (QueryWrapperX<T>) super.like(column, val);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> inIfPresent(String column, Collection<?> values) {\n        if (!CollectionUtils.isEmpty(values)) {\n            return (QueryWrapperX<T>) super.in(column, values);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> inIfPresent(String column, Object... values) {\n        if (!ArrayUtils.isEmpty(values)) {\n            return (QueryWrapperX<T>) super.in(column, values);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> eqIfPresent(String column, Object val) {\n        if (val != null) {\n            return (QueryWrapperX<T>) super.eq(column, val);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> neIfPresent(String column, Object val) {\n        if (val != null) {\n            return (QueryWrapperX<T>) super.ne(column, val);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> gtIfPresent(String column, Object val) {\n        if (val != null) {\n            return (QueryWrapperX<T>) super.gt(column, val);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> geIfPresent(String column, Object val) {\n        if (val != null) {\n            return (QueryWrapperX<T>) super.ge(column, val);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> ltIfPresent(String column, Object val) {\n        if (val != null) {\n            return (QueryWrapperX<T>) super.lt(column, val);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> leIfPresent(String column, Object val) {\n        if (val != null) {\n            return (QueryWrapperX<T>) super.le(column, val);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> betweenIfPresent(String column, Object val1, Object val2) {\n        if (val1 != null && val2 != null) {\n            return (QueryWrapperX<T>) super.between(column, val1, val2);\n        }\n        if (val1 != null) {\n            return (QueryWrapperX<T>) ge(column, val1);\n        }\n        if (val2 != null) {\n            return (QueryWrapperX<T>) le(column, val2);\n        }\n        return this;\n    }\n\n    public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) {\n        if (values!= null && values.length != 0 && values[0] != null && values[1] != null) {\n            return (QueryWrapperX<T>) super.between(column, values[0], values[1]);\n        }\n        if (values!= null && values.length != 0 && values[0] != null) {\n            return (QueryWrapperX<T>) ge(column, values[0]);\n        }\n        if (values!= null && values.length != 0 && values[1] != null) {\n            return (QueryWrapperX<T>) le(column, values[1]);\n        }\n        return this;\n    }\n\n    // ========== 重写父类方法，方便链式调用 ==========\n\n    @Override\n    public QueryWrapperX<T> eq(boolean condition, String column, Object val) {\n        super.eq(condition, column, val);\n        return this;\n    }\n\n    @Override\n    public QueryWrapperX<T> eq(String column, Object val) {\n        super.eq(column, val);\n        return this;\n    }\n\n    @Override\n    public QueryWrapperX<T> orderByDesc(String column) {\n        super.orderByDesc(true, column);\n        return this;\n    }\n\n    @Override\n    public QueryWrapperX<T> last(String lastSql) {\n        super.last(lastSql);\n        return this;\n    }\n\n    @Override\n    public QueryWrapperX<T> in(String column, Collection<?> coll) {\n        super.in(column, coll);\n        return this;\n    }\n\n    /**\n     * 设置只返回最后一条\n     *\n     * TODO 芋艿：不是完美解，需要在思考下。如果使用多数据源，并且数据源是多种类型时，可能会存在问题：实现之返回一条的语法不同\n     *\n     * @return this\n     */\n    public QueryWrapperX<T> limitN(int n) {\n        DbType dbType = JdbcUtils.getDbType();\n        switch (dbType) {\n            case ORACLE:\n            case ORACLE_12C:\n                super.le(\"ROWNUM\", n);\n                break;\n            case SQL_SERVER:\n            case SQL_SERVER2005:\n                super.select(\"TOP \" + n + \" *\"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条，所以只好使用 * 查询剩余字段\n                break;\n            default: // MySQL、PostgreSQL、DM 达梦、KingbaseES 大金都是采用 LIMIT 实现\n                super.last(\"LIMIT \" + n);\n        }\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.type;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.crypto.symmetric.AES;\nimport cn.hutool.extra.spring.SpringUtil;\nimport org.apache.ibatis.type.BaseTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\n\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * 字段字段的 TypeHandler 实现类，基于 {@link AES} 实现\n * 可通过 jasypt.encryptor.password 配置项，设置密钥\n *\n * @author 芋道源码\n */\npublic class EncryptTypeHandler extends BaseTypeHandler<String> {\n\n    private static final String ENCRYPTOR_PROPERTY_NAME = \"mybatis-plus.encryptor.password\";\n\n    private static AES aes;\n\n    @Override\n    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {\n        ps.setString(i, encrypt(parameter));\n    }\n\n    @Override\n    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {\n        String value = rs.getString(columnName);\n        return decrypt(value);\n    }\n\n    @Override\n    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {\n        String value = rs.getString(columnIndex);\n        return decrypt(value);\n    }\n\n    @Override\n    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {\n        String value = cs.getString(columnIndex);\n        return decrypt(value);\n    }\n\n    private static String decrypt(String value) {\n        if (value == null) {\n            return null;\n        }\n        return getEncryptor().decryptStr(value);\n    }\n\n    public static String encrypt(String rawValue) {\n        if (rawValue == null) {\n            return null;\n        }\n        return getEncryptor().encryptBase64(rawValue);\n    }\n\n    private static AES getEncryptor() {\n        if (aes != null) {\n            return aes;\n        }\n        // 构建 AES\n        String password = SpringUtil.getProperty(ENCRYPTOR_PROPERTY_NAME);\n        Assert.notEmpty(password, \"配置项({}) 不能为空\", ENCRYPTOR_PROPERTY_NAME);\n        aes = SecureUtil.aes(password.getBytes());\n        return aes;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/IntegerListTypeHandler.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.type;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\nimport org.apache.ibatis.type.TypeHandler;\n\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * List<Integer> 的类型转换器实现类，对应数据库的 varchar 类型\n *\n * @author jason\n */\n@MappedJdbcTypes(JdbcType.VARCHAR)\n@MappedTypes(List.class)\npublic class IntegerListTypeHandler implements TypeHandler<List<Integer>> {\n\n    private static final String COMMA = \",\";\n\n    @Override\n    public void setParameter(PreparedStatement ps, int i, List<Integer> strings, JdbcType jdbcType) throws SQLException {\n        ps.setString(i, CollUtil.join(strings, COMMA));\n    }\n\n    @Override\n    public List<Integer> getResult(ResultSet rs, String columnName) throws SQLException {\n        String value = rs.getString(columnName);\n        return getResult(value);\n    }\n\n    @Override\n    public List<Integer> getResult(ResultSet rs, int columnIndex) throws SQLException {\n        String value = rs.getString(columnIndex);\n        return getResult(value);\n    }\n\n    @Override\n    public List<Integer> getResult(CallableStatement cs, int columnIndex) throws SQLException {\n        String value = cs.getString(columnIndex);\n        return getResult(value);\n    }\n\n    private List<Integer> getResult(String value) {\n        if (value == null) {\n            return null;\n        }\n        return StrUtils.splitToInteger(value, COMMA);\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongListTypeHandler.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.type;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\nimport org.apache.ibatis.type.TypeHandler;\n\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * List<Long> 的类型转换器实现类，对应数据库的 varchar 类型\n *\n * @author 芋道源码\n */\n@MappedJdbcTypes(JdbcType.VARCHAR)\n@MappedTypes(List.class)\npublic class LongListTypeHandler implements TypeHandler<List<Long>> {\n\n    private static final String COMMA = \",\";\n\n    @Override\n    public void setParameter(PreparedStatement ps, int i, List<Long> strings, JdbcType jdbcType) throws SQLException {\n        // 设置占位符\n        ps.setString(i, CollUtil.join(strings, COMMA));\n    }\n\n    @Override\n    public List<Long> getResult(ResultSet rs, String columnName) throws SQLException {\n        String value = rs.getString(columnName);\n        return getResult(value);\n    }\n\n    @Override\n    public List<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {\n        String value = rs.getString(columnIndex);\n        return getResult(value);\n    }\n\n    @Override\n    public List<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {\n        String value = cs.getString(columnIndex);\n        return getResult(value);\n    }\n\n    private List<Long> getResult(String value) {\n        if (value == null) {\n            return null;\n        }\n        return StrUtils.splitToLong(value, COMMA);\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongSetTypeHandler.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.type;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\nimport org.apache.ibatis.type.TypeHandler;\n\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Set<Long> 的类型转换器实现类，对应数据库的 varchar 类型\n *\n * @author 芋道源码\n */\n@MappedJdbcTypes(JdbcType.VARCHAR)\n@MappedTypes(List.class)\npublic class LongSetTypeHandler implements TypeHandler<Set<Long>> {\n\n    private static final String COMMA = \",\";\n\n    @Override\n    public void setParameter(PreparedStatement ps, int i, Set<Long> strings, JdbcType jdbcType) throws SQLException {\n        // 设置占位符\n        ps.setString(i, CollUtil.join(strings, COMMA));\n    }\n\n    @Override\n    public Set<Long> getResult(ResultSet rs, String columnName) throws SQLException {\n        String value = rs.getString(columnName);\n        return getResult(value);\n    }\n\n    @Override\n    public Set<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {\n        String value = rs.getString(columnIndex);\n        return getResult(value);\n    }\n\n    @Override\n    public Set<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {\n        String value = cs.getString(columnIndex);\n        return getResult(value);\n    }\n\n    private Set<Long> getResult(String value) {\n        if (value == null) {\n            return null;\n        }\n        return StrUtils.splitToLongSet(value, COMMA);\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/StringListTypeHandler.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.type;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\nimport org.apache.ibatis.type.TypeHandler;\n\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * List<String> 的类型转换器实现类，对应数据库的 varchar 类型\n *\n * @author 永不言败\n * @since 2022 3/23 12:50:15\n */\n@MappedJdbcTypes(JdbcType.VARCHAR)\n@MappedTypes(List.class)\npublic class StringListTypeHandler implements TypeHandler<List<String>> {\n\n    private static final String COMMA = \",\";\n\n    @Override\n    public void setParameter(PreparedStatement ps, int i, List<String> strings, JdbcType jdbcType) throws SQLException {\n        // 设置占位符\n        ps.setString(i, CollUtil.join(strings, COMMA));\n    }\n\n    @Override\n    public List<String> getResult(ResultSet rs, String columnName) throws SQLException {\n        String value = rs.getString(columnName);\n        return getResult(value);\n    }\n\n    @Override\n    public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {\n        String value = rs.getString(columnIndex);\n        return getResult(value);\n    }\n\n    @Override\n    public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {\n        String value = cs.getString(columnIndex);\n        return getResult(value);\n    }\n\n    private List<String> getResult(String value) {\n        if (value == null) {\n            return null;\n        }\n        return StrUtil.splitTrim(value, COMMA);\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/JdbcUtils.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.util;\n\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.enums.DbTypeEnum;\nimport com.baomidou.dynamic.datasource.DynamicRoutingDataSource;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\n\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\n/**\n * JDBC 工具类\n *\n * @author 芋道源码\n */\npublic class JdbcUtils {\n\n    /**\n     * 判断连接是否正确\n     *\n     * @param url      数据源连接\n     * @param username 账号\n     * @param password 密码\n     * @return 是否正确\n     */\n    public static boolean isConnectionOK(String url, String username, String password) {\n        try (Connection ignored = DriverManager.getConnection(url, username, password)) {\n            return true;\n        } catch (Exception ex) {\n            return false;\n        }\n    }\n\n    /**\n     * 获得 URL 对应的 DB 类型\n     *\n     * @param url URL\n     * @return DB 类型\n     */\n    public static DbType getDbType(String url) {\n        return com.baomidou.mybatisplus.extension.toolkit.JdbcUtils.getDbType(url);\n    }\n\n    /**\n     * 通过当前数据库连接获得对应的 DB 类型\n     *\n     * @return DB 类型\n     */\n    public static DbType getDbType() {\n        DataSource dataSource;\n        try {\n            DynamicRoutingDataSource dynamicRoutingDataSource = SpringUtils.getBean(DynamicRoutingDataSource.class);\n            dataSource = dynamicRoutingDataSource.determineDataSource();\n        } catch (NoSuchBeanDefinitionException e) {\n            dataSource = SpringUtils.getBean(DataSource.class);\n        }\n        try (Connection conn = dataSource.getConnection()) {\n            return DbTypeEnum.find(conn.getMetaData().getDatabaseProductName());\n        } catch (SQLException e) {\n            throw new IllegalArgumentException(e.getMessage());\n        }\n    }\n\n    /**\n     * 判断 JDBC 连接是否为 SQLServer 数据库\n     *\n     * @param url JDBC 连接\n     * @return 是否为 SQLServer 数据库\n     */\n    public static boolean isSQLServer(String url) {\n        DbType dbType = getDbType(url);\n        return isSQLServer(dbType);\n    }\n\n    /**\n     * 判断 JDBC 连接是否为 SQLServer 数据库\n     *\n     * @param dbType DB 类型\n     * @return 是否为 SQLServer 数据库\n     */\n    public static boolean isSQLServer(DbType dbType) {\n        return ObjectUtils.equalsAny(dbType, DbType.SQL_SERVER, DbType.SQL_SERVER2005);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java",
    "content": "package cn.iocoder.yudao.framework.mybatis.core.util;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.func.Func1;\nimport cn.hutool.core.lang.func.LambdaUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.SortingField;\nimport cn.iocoder.yudao.framework.mybatis.core.enums.DbTypeEnum;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport net.sf.jsqlparser.expression.Alias;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * MyBatis 工具类\n */\npublic class MyBatisUtils {\n\n    private static final String MYSQL_ESCAPE_CHARACTER = \"`\";\n\n    public static <T> Page<T> buildPage(PageParam pageParam) {\n        return buildPage(pageParam, null);\n    }\n\n    public static <T> Page<T> buildPage(PageParam pageParam, Collection<SortingField> sortingFields) {\n        // 页码 + 数量\n        Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());\n        page.setOptimizeJoinOfCountSql(false); // 关联 issue：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID2QLL\n        // 排序字段\n        if (CollUtil.isNotEmpty(sortingFields)) {\n            for (SortingField sortingField : sortingFields) {\n                page.addOrder(new OrderItem().setAsc(SortingField.ORDER_ASC.equals(sortingField.getOrder()))\n                        .setColumn(StrUtil.toUnderlineCase(sortingField.getField())));\n            }\n        }\n        return page;\n    }\n\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    public static <T> void addOrder(Wrapper<T> wrapper, Collection<SortingField> sortingFields) {\n        if (CollUtil.isEmpty(sortingFields)) {\n            return;\n        }\n        if (wrapper instanceof QueryWrapper) {\n            QueryWrapper<T> query = (QueryWrapper<T>) wrapper;\n            for (SortingField sortingField : sortingFields) {\n                query.orderBy(true,\n                        SortingField.ORDER_ASC.equals(sortingField.getOrder()),\n                        StrUtil.toUnderlineCase(sortingField.getField()));\n            }\n        } else if (wrapper instanceof LambdaQueryWrapper) {\n            // LambdaQueryWrapper 不直接支持字符串字段排序，使用 last 方法拼接 ORDER BY\n            LambdaQueryWrapper<T> lambdaQuery = (LambdaQueryWrapper<T>) wrapper;\n            StringBuilder orderBy = new StringBuilder();\n            for (SortingField sortingField : sortingFields) {\n                if (StrUtil.isNotEmpty(orderBy)) {\n                    orderBy.append(\", \");\n                }\n                orderBy.append(StrUtil.toUnderlineCase(sortingField.getField()))\n                        .append(\" \")\n                        .append(SortingField.ORDER_ASC.equals(sortingField.getOrder()) ? \"ASC\" : \"DESC\");\n            }\n            lambdaQuery.last(\"ORDER BY \" + orderBy);\n            // 另外个思路：https://blog.csdn.net/m0_59084856/article/details/138450913\n        } else {\n            throw new IllegalArgumentException(\"Unsupported wrapper type: \" + wrapper.getClass().getName());\n        }\n\n    }\n\n    /**\n     * 将拦截器添加到链中\n     * 由于 MybatisPlusInterceptor 不支持添加拦截器，所以只能全量设置\n     *\n     * @param interceptor 链\n     * @param inner       拦截器\n     * @param index       位置\n     */\n    public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) {\n        List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors());\n        inners.add(index, inner);\n        interceptor.setInterceptors(inners);\n    }\n\n    /**\n     * 获得 Table 对应的表名\n     * <p>\n     * 兼容 MySQL 转义表名 `t_xxx`\n     *\n     * @param table 表\n     * @return 去除转移字符后的表名\n     */\n    public static String getTableName(Table table) {\n        String tableName = table.getName();\n        if (tableName.startsWith(MYSQL_ESCAPE_CHARACTER) && tableName.endsWith(MYSQL_ESCAPE_CHARACTER)) {\n            tableName = tableName.substring(1, tableName.length() - 1);\n        }\n        return tableName;\n    }\n\n    /**\n     * 构建 Column 对象\n     *\n     * @param tableName  表名\n     * @param tableAlias 别名\n     * @param column     字段名\n     * @return Column 对象\n     */\n    public static Column buildColumn(String tableName, Alias tableAlias, String column) {\n        if (tableAlias != null) {\n            tableName = tableAlias.getName();\n        }\n        return new Column(tableName + StringPool.DOT + column);\n    }\n\n    /**\n     * 跨数据库的 find_in_set 实现\n     *\n     * @param column 字段名称\n     * @param value  查询值(不带单引号)\n     * @return sql\n     */\n    public static String findInSet(String column, Object value) {\n        DbType dbType = JdbcUtils.getDbType();\n        return DbTypeEnum.getFindInSetTemplate(dbType)\n                .replace(\"#{column}\", column)\n                .replace(\"#{value}\", StrUtil.toString(value));\n    }\n\n    /**\n     * 将驼峰命名转换为下划线命名\n     *\n     * 使用场景：\n     * 1. <a href=\"https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1357/files\">fix:修复\"商品统计聚合函数的别名与排序字段不符\"导致的 SQL 异常</a>\n     *\n     * @param func 字段名函数(驼峰命名)\n     * @return 字段名(下划线命名)\n     */\n    public static <T> String toUnderlineCase(Func1<T, ?> func) {\n        String fieldName = LambdaUtil.getFieldName(func);\n        return StrUtil.toUnderlineCase(fieldName);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/package-info.java",
    "content": "/**\n * 使用 MyBatis Plus 提升使用 MyBatis 的开发效率\n */\npackage cn.iocoder.yudao.framework.mybatis;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/translate/config/YudaoTranslateAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.translate.config;\n\nimport cn.iocoder.yudao.framework.translate.core.TranslateUtils;\nimport com.fhs.trans.service.impl.TransService;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\n\n@AutoConfiguration\npublic class YudaoTranslateAutoConfiguration {\n\n    @Bean\n    @SuppressWarnings({\"InstantiationOfUtilityClass\", \"SpringJavaInjectionPointsAutowiringInspection\"})\n    public TranslateUtils translateUtils(TransService transService) {\n        TranslateUtils.init(transService);\n        return new TranslateUtils();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/translate/core/TranslateUtils.java",
    "content": "package cn.iocoder.yudao.framework.translate.core;\n\nimport cn.hutool.core.collection.CollUtil;\nimport com.fhs.core.trans.vo.VO;\nimport com.fhs.trans.service.impl.TransService;\n\nimport java.util.List;\n\n/**\n * VO 数据翻译 Utils\n *\n * @author 芋道源码\n */\npublic class TranslateUtils {\n\n    private static TransService transService;\n\n    public static void init(TransService transService) {\n        TranslateUtils.transService = transService;\n    }\n\n    /**\n     * 数据翻译\n     *\n     * 使用场景：无法使用 @TransMethodResult 注解的场景，只能通过手动触发翻译\n     *\n     * @param data 数据\n     * @return 翻译结果\n     */\n    public static <T extends VO> List<T> translate(List<T> data) {\n        if (CollUtil.isNotEmpty((data))) {\n            transService.transBatch(data);\n        }\n        return data;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/translate/package-info.java",
    "content": "/**\n * 使用 Easy-Trans 提升使用 VO 数据翻译的开发效率\n */\npackage cn.iocoder.yudao.framework.translate;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration\ncn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration\ncn.iocoder.yudao.framework.translate.config.YudaoTranslateAutoConfiguration"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.env.EnvironmentPostProcessor=\\\n  cn.iocoder.yudao.framework.mybatis.config.IdTypeEnvironmentPostProcessor\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/《芋道 Spring Boot MyBatis 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/《芋道 Spring Boot 多数据源（读写分离）入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/dynamic-datasource/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-mybatis/《芋道 Spring Boot 数据库连接池入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-protection</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>服务保证，提供分布式锁、幂等、限流、熔断等等功能</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-web</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有限流、幂等使用到 -->\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <!-- 服务保障相关 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>lock4j-redisson-spring-boot-starter</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/config/YudaoIdempotentConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.idempotent.config;\n\nimport cn.iocoder.yudao.framework.idempotent.core.aop.IdempotentAspect;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.DefaultIdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.idempotent.core.redis.IdempotentRedisDAO;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.data.redis.core.StringRedisTemplate;\n\nimport java.util.List;\n\n@AutoConfiguration(after = YudaoRedisAutoConfiguration.class)\npublic class YudaoIdempotentConfiguration {\n\n    @Bean\n    public IdempotentAspect idempotentAspect(List<IdempotentKeyResolver> keyResolvers, IdempotentRedisDAO idempotentRedisDAO) {\n        return new IdempotentAspect(keyResolvers, idempotentRedisDAO);\n    }\n\n    @Bean\n    public IdempotentRedisDAO idempotentRedisDAO(StringRedisTemplate stringRedisTemplate) {\n        return new IdempotentRedisDAO(stringRedisTemplate);\n    }\n\n    // ========== 各种 IdempotentKeyResolver Bean ==========\n\n    @Bean\n    public DefaultIdempotentKeyResolver defaultIdempotentKeyResolver() {\n        return new DefaultIdempotentKeyResolver();\n    }\n\n    @Bean\n    public UserIdempotentKeyResolver userIdempotentKeyResolver() {\n        return new UserIdempotentKeyResolver();\n    }\n\n    @Bean\n    public ExpressionIdempotentKeyResolver expressionIdempotentKeyResolver() {\n        return new ExpressionIdempotentKeyResolver();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/annotation/Idempotent.java",
    "content": "package cn.iocoder.yudao.framework.idempotent.core.annotation;\n\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.DefaultIdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 幂等注解\n *\n * @author 芋道源码\n */\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Idempotent {\n\n    /**\n     * 幂等的超时时间，默认为 1 秒\n     *\n     * 注意，如果执行时间超过它，请求还是会进来\n     */\n    int timeout() default 1;\n    /**\n     * 时间单位，默认为 SECONDS 秒\n     */\n    TimeUnit timeUnit() default TimeUnit.SECONDS;\n\n    /**\n     * 提示信息，正在执行中的提示\n     */\n    String message() default \"重复请求，请稍后重试\";\n\n    /**\n     * 使用的 Key 解析器\n     *\n     * @see DefaultIdempotentKeyResolver 全局级别\n     * @see UserIdempotentKeyResolver 用户级别\n     * @see ExpressionIdempotentKeyResolver 自定义表达式，通过 {@link #keyArg()} 计算\n     */\n    Class<? extends IdempotentKeyResolver> keyResolver() default DefaultIdempotentKeyResolver.class;\n    /**\n     * 使用的 Key 参数\n     */\n    String keyArg() default \"\";\n\n    /**\n     * 删除 Key，当发生异常时候\n     *\n     * 问题：为什么发生异常时，需要删除 Key 呢？\n     * 回答：发生异常时，说明业务发生错误，此时需要删除 Key，避免下次请求无法正常执行。\n     *\n     * 问题：为什么不搞 deleteWhenSuccess 执行成功时，需要删除 Key 呢？\n     * 回答：这种情况下，本质上是分布式锁，推荐使用 @Lock4j 注解\n     */\n    boolean deleteKeyWhenException() default true;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/aop/IdempotentAspect.java",
    "content": "package cn.iocoder.yudao.framework.idempotent.core.aop;\n\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.idempotent.core.redis.IdempotentRedisDAO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.springframework.util.Assert;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 拦截声明了 {@link Idempotent} 注解的方法，实现幂等操作\n *\n * @author 芋道源码\n */\n@Aspect\n@Slf4j\npublic class IdempotentAspect {\n\n    /**\n     * IdempotentKeyResolver 集合\n     */\n    private final Map<Class<? extends IdempotentKeyResolver>, IdempotentKeyResolver> keyResolvers;\n\n    private final IdempotentRedisDAO idempotentRedisDAO;\n\n    public IdempotentAspect(List<IdempotentKeyResolver> keyResolvers, IdempotentRedisDAO idempotentRedisDAO) {\n        this.keyResolvers = CollectionUtils.convertMap(keyResolvers, IdempotentKeyResolver::getClass);\n        this.idempotentRedisDAO = idempotentRedisDAO;\n    }\n\n    @Around(value = \"@annotation(idempotent)\")\n    public Object aroundPointCut(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {\n        // 获得 IdempotentKeyResolver\n        IdempotentKeyResolver keyResolver = keyResolvers.get(idempotent.keyResolver());\n        Assert.notNull(keyResolver, \"找不到对应的 IdempotentKeyResolver\");\n        // 解析 Key\n        String key = keyResolver.resolver(joinPoint, idempotent);\n\n        // 1. 锁定 Key\n        boolean success = idempotentRedisDAO.setIfAbsent(key, idempotent.timeout(), idempotent.timeUnit());\n        // 锁定失败，抛出异常\n        if (!success) {\n            log.info(\"[aroundPointCut][方法({}) 参数({}) 存在重复请求]\", joinPoint.getSignature().toString(), joinPoint.getArgs());\n            throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), idempotent.message());\n        }\n\n        // 2. 执行逻辑\n        try {\n            return joinPoint.proceed();\n        } catch (Throwable throwable) {\n            // 3. 异常时，删除 Key\n            // 参考美团 GTIS 思路：https://tech.meituan.com/2016/09/29/distributed-system-mutually-exclusive-idempotence-cerberus-gtis.html\n            if (idempotent.deleteKeyWhenException()) {\n                idempotentRedisDAO.delete(key);\n            }\n            throw throwable;\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/IdempotentKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.idempotent.core.keyresolver;\n\nimport cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;\nimport org.aspectj.lang.JoinPoint;\n\n/**\n * 幂等 Key 解析器接口\n *\n * @author 芋道源码\n */\npublic interface IdempotentKeyResolver {\n\n    /**\n     * 解析一个 Key\n     *\n     * @param idempotent 幂等注解\n     * @param joinPoint  AOP 切面\n     * @return Key\n     */\n    String resolver(JoinPoint joinPoint, Idempotent idempotent);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl;\n\nimport cn.hutool.crypto.SecureUtil;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;\nimport org.aspectj.lang.JoinPoint;\n\n/**\n * 默认（全局级别）幂等 Key 解析器，使用方法名 + 方法参数，组装成一个 Key\n *\n * 为了避免 Key 过长，使用 MD5 进行“压缩”\n *\n * @author 芋道源码\n */\npublic class DefaultIdempotentKeyResolver implements IdempotentKeyResolver {\n\n    @Override\n    public String resolver(JoinPoint joinPoint, Idempotent idempotent) {\n        String methodName = joinPoint.getSignature().toString();\n        String argsStr = StrUtils.joinMethodArgs(joinPoint);\n        return SecureUtil.md5(methodName + argsStr);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/ExpressionIdempotentKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.reflect.MethodSignature;\nimport org.springframework.core.LocalVariableTableParameterNameDiscoverer;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\n\nimport java.lang.reflect.Method;\n\n/**\n * 基于 Spring EL 表达式，\n *\n * @author 芋道源码\n */\npublic class ExpressionIdempotentKeyResolver implements IdempotentKeyResolver {\n\n    private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();\n    private final ExpressionParser expressionParser = new SpelExpressionParser();\n\n    @Override\n    public String resolver(JoinPoint joinPoint, Idempotent idempotent) {\n        // 获得被拦截方法参数名列表\n        Method method = getMethod(joinPoint);\n        Object[] args = joinPoint.getArgs();\n        String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method);\n        // 准备 Spring EL 表达式解析的上下文\n        StandardEvaluationContext evaluationContext = new StandardEvaluationContext();\n        if (ArrayUtil.isNotEmpty(parameterNames)) {\n            for (int i = 0; i < parameterNames.length; i++) {\n                evaluationContext.setVariable(parameterNames[i], args[i]);\n            }\n        }\n\n        // 解析参数\n        Expression expression = expressionParser.parseExpression(idempotent.keyArg());\n        return expression.getValue(evaluationContext, String.class);\n    }\n\n    private static Method getMethod(JoinPoint point) {\n        // 处理，声明在类上的情况\n        MethodSignature signature = (MethodSignature) point.getSignature();\n        Method method = signature.getMethod();\n        if (!method.getDeclaringClass().isInterface()) {\n            return method;\n        }\n\n        // 处理，声明在接口上的情况\n        try {\n            return point.getTarget().getClass().getDeclaredMethod(\n                    point.getSignature().getName(), method.getParameterTypes());\n        } catch (NoSuchMethodException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl;\n\nimport cn.hutool.crypto.SecureUtil;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport org.aspectj.lang.JoinPoint;\n\n/**\n * 用户级别的幂等 Key 解析器，使用方法名 + 方法参数 + userId + userType，组装成一个 Key\n *\n * 为了避免 Key 过长，使用 MD5 进行“压缩”\n *\n * @author 芋道源码\n */\npublic class UserIdempotentKeyResolver implements IdempotentKeyResolver {\n\n    @Override\n    public String resolver(JoinPoint joinPoint, Idempotent idempotent) {\n        String methodName = joinPoint.getSignature().toString();\n        String argsStr = StrUtils.joinMethodArgs(joinPoint);\n        Long userId = WebFrameworkUtils.getLoginUserId();\n        Integer userType = WebFrameworkUtils.getLoginUserType();\n        return SecureUtil.md5(methodName + argsStr + userId + userType);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/redis/IdempotentRedisDAO.java",
    "content": "package cn.iocoder.yudao.framework.idempotent.core.redis;\n\nimport lombok.AllArgsConstructor;\nimport org.springframework.data.redis.core.StringRedisTemplate;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 幂等 Redis DAO\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\npublic class IdempotentRedisDAO {\n\n    /**\n     * 幂等操作\n     *\n     * KEY 格式：idempotent:%s // 参数为 uuid\n     * VALUE 格式：String\n     * 过期时间：不固定\n     */\n    private static final String IDEMPOTENT = \"idempotent:%s\";\n\n    private final StringRedisTemplate redisTemplate;\n\n    public Boolean setIfAbsent(String key, long timeout, TimeUnit timeUnit) {\n        String redisKey = formatKey(key);\n        return redisTemplate.opsForValue().setIfAbsent(redisKey, \"\", timeout, timeUnit);\n    }\n\n    public void delete(String key) {\n        String redisKey = formatKey(key);\n        redisTemplate.delete(redisKey);\n    }\n\n    private static String formatKey(String key) {\n        return String.format(IDEMPOTENT, key);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/package-info.java",
    "content": "/**\n * 幂等组件，参考 https://github.com/it4alla/idempotent 项目实现\n * 实现原理是，相同参数的方法，一段时间内，有且仅能执行一次。通过这样的方式，保证幂等性。\n *\n * 使用场景：例如说，用户快速的双击了某个按钮，前端没有禁用该按钮，导致发送了两次重复的请求。\n *\n * 和 it4alla/idempotent 组件的差异点，主要体现在两点：\n *  1. 我们去掉了 @Idempotent 注解的 delKey 属性。原因是，本质上 delKey 为 true 时，实现的是分布式锁的能力\n * 此时，我们偏向使用 Lock4j 组件。原则上，一个组件只提供一种单一的能力。\n *  2. 考虑到组件的通用性，我们并未像 it4alla/idempotent 组件一样使用 Redisson RMap 结构，而是直接使用 Redis 的 String 数据格式。\n */\npackage cn.iocoder.yudao.framework.idempotent;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/config/YudaoLock4jConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.lock4j.config;\n\nimport cn.iocoder.yudao.framework.lock4j.core.DefaultLockFailureStrategy;\nimport com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.context.annotation.Bean;\n\n@AutoConfiguration(before = LockAutoConfiguration.class)\n@ConditionalOnClass(name = \"com.baomidou.lock.annotation.Lock4j\")\npublic class YudaoLock4jConfiguration {\n\n    @Bean\n    public DefaultLockFailureStrategy lockFailureStrategy() {\n        return new DefaultLockFailureStrategy();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/DefaultLockFailureStrategy.java",
    "content": "package cn.iocoder.yudao.framework.lock4j.core;\n\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport com.baomidou.lock.LockFailureStrategy;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.lang.reflect.Method;\n\n/**\n * 自定义获取锁失败策略，抛出 {@link ServiceException} 异常\n */\n@Slf4j\npublic class DefaultLockFailureStrategy implements LockFailureStrategy {\n\n    @Override\n    public void onLockFailure(String key, Method method, Object[] arguments) {\n        log.debug(\"[onLockFailure][线程:{} 获取锁失败，key:{} 获取失败:{} ]\", Thread.currentThread().getName(), key, arguments);\n        throw new ServiceException(GlobalErrorCodeConstants.LOCKED);\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/Lock4jRedisKeyConstants.java",
    "content": "package cn.iocoder.yudao.framework.lock4j.core;\n\n/**\n * Lock4j Redis Key 枚举类\n *\n * @author 芋道源码\n */\npublic interface Lock4jRedisKeyConstants {\n\n    /**\n     * 分布式锁\n     *\n     * KEY 格式：lock4j:%s // 参数来自 DefaultLockKeyBuilder 类\n     * VALUE 数据格式：HASH // RLock.class：Redisson 的 Lock 锁，使用 Hash 数据结构\n     * 过期时间：不固定\n     */\n    String LOCK4J = \"lock4j:%s\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/package-info.java",
    "content": "/**\n * 分布式锁组件，使用 https://gitee.com/baomidou/lock4j 开源项目\n */\npackage cn.iocoder.yudao.framework.lock4j;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/config/YudaoRateLimiterConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.config;\n\nimport cn.iocoder.yudao.framework.ratelimiter.core.aop.RateLimiterAspect;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl.*;\nimport cn.iocoder.yudao.framework.ratelimiter.core.redis.RateLimiterRedisDAO;\nimport cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;\nimport org.redisson.api.RedissonClient;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\n\nimport java.util.List;\n\n@AutoConfiguration(after = YudaoRedisAutoConfiguration.class)\npublic class YudaoRateLimiterConfiguration {\n\n    @Bean\n    public RateLimiterAspect rateLimiterAspect(List<RateLimiterKeyResolver> keyResolvers, RateLimiterRedisDAO rateLimiterRedisDAO) {\n        return new RateLimiterAspect(keyResolvers, rateLimiterRedisDAO);\n    }\n\n    @Bean\n    @SuppressWarnings(\"SpringJavaInjectionPointsAutowiringInspection\")\n    public RateLimiterRedisDAO rateLimiterRedisDAO(RedissonClient redissonClient) {\n        return new RateLimiterRedisDAO(redissonClient);\n    }\n\n    // ========== 各种 RateLimiterRedisDAO Bean ==========\n\n    @Bean\n    public DefaultRateLimiterKeyResolver defaultRateLimiterKeyResolver() {\n        return new DefaultRateLimiterKeyResolver();\n    }\n\n    @Bean\n    public UserRateLimiterKeyResolver userRateLimiterKeyResolver() {\n        return new UserRateLimiterKeyResolver();\n    }\n\n    @Bean\n    public ClientIpRateLimiterKeyResolver clientIpRateLimiterKeyResolver() {\n        return new ClientIpRateLimiterKeyResolver();\n    }\n\n    @Bean\n    public ServerNodeRateLimiterKeyResolver serverNodeRateLimiterKeyResolver() {\n        return new ServerNodeRateLimiterKeyResolver();\n    }\n\n    @Bean\n    public ExpressionRateLimiterKeyResolver expressionRateLimiterKeyResolver() {\n        return new ExpressionRateLimiterKeyResolver();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/annotation/RateLimiter.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.core.annotation;\n\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl.ClientIpRateLimiterKeyResolver;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl.DefaultRateLimiterKeyResolver;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl.ServerNodeRateLimiterKeyResolver;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl.UserRateLimiterKeyResolver;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 限流注解\n *\n * @author 芋道源码\n */\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface RateLimiter {\n\n    /**\n     * 限流的时间，默认为 1 秒\n     */\n    int time() default 1;\n    /**\n     * 时间单位，默认为 SECONDS 秒\n     */\n    TimeUnit timeUnit() default TimeUnit.SECONDS;\n\n    /**\n     * 限流次数\n     */\n    int count() default 100;\n\n    /**\n     * 提示信息，请求过快的提示\n     *\n     * @see GlobalErrorCodeConstants#TOO_MANY_REQUESTS\n     */\n    String message() default \"\"; // 为空时，使用 TOO_MANY_REQUESTS 错误提示\n\n    /**\n     * 使用的 Key 解析器\n     *\n     * @see DefaultRateLimiterKeyResolver 全局级别\n     * @see UserRateLimiterKeyResolver 用户 ID 级别\n     * @see ClientIpRateLimiterKeyResolver 用户 IP 级别\n     * @see ServerNodeRateLimiterKeyResolver 服务器 Node 级别\n     * @see ExpressionIdempotentKeyResolver 自定义表达式，通过 {@link #keyArg()} 计算\n     */\n    Class<? extends RateLimiterKeyResolver> keyResolver() default DefaultRateLimiterKeyResolver.class;\n    /**\n     * 使用的 Key 参数\n     */\n    String keyArg() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/aop/RateLimiterAspect.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.core.aop;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;\nimport cn.iocoder.yudao.framework.ratelimiter.core.redis.RateLimiterRedisDAO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\nimport org.springframework.util.Assert;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 拦截声明了 {@link RateLimiter} 注解的方法，实现限流操作\n *\n * @author 芋道源码\n */\n@Aspect\n@Slf4j\npublic class RateLimiterAspect {\n\n    /**\n     * RateLimiterKeyResolver 集合\n     */\n    private final Map<Class<? extends RateLimiterKeyResolver>, RateLimiterKeyResolver> keyResolvers;\n\n    private final RateLimiterRedisDAO rateLimiterRedisDAO;\n\n    public RateLimiterAspect(List<RateLimiterKeyResolver> keyResolvers, RateLimiterRedisDAO rateLimiterRedisDAO) {\n        this.keyResolvers = CollectionUtils.convertMap(keyResolvers, RateLimiterKeyResolver::getClass);\n        this.rateLimiterRedisDAO = rateLimiterRedisDAO;\n    }\n\n    @Before(\"@annotation(rateLimiter)\")\n    public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) {\n        // 获得 RateLimiterKeyResolver 对象\n        RateLimiterKeyResolver keyResolver = keyResolvers.get(rateLimiter.keyResolver());\n        Assert.notNull(keyResolver, \"找不到对应的 RateLimiterKeyResolver\");\n        // 解析 Key\n        String key = keyResolver.resolver(joinPoint, rateLimiter);\n\n        // 获取 1 次限流\n        boolean success = rateLimiterRedisDAO.tryAcquire(key,\n                rateLimiter.count(), rateLimiter.time(), rateLimiter.timeUnit());\n        if (!success) {\n            log.info(\"[beforePointCut][方法({}) 参数({}) 请求过于频繁]\", joinPoint.getSignature().toString(), joinPoint.getArgs());\n            String message = StrUtil.blankToDefault(rateLimiter.message(),\n                    GlobalErrorCodeConstants.TOO_MANY_REQUESTS.getMsg());\n            throw new ServiceException(GlobalErrorCodeConstants.TOO_MANY_REQUESTS.getCode(), message);\n        }\n    }\n\n}\n\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/RateLimiterKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver;\n\nimport cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;\nimport org.aspectj.lang.JoinPoint;\n\n/**\n * 限流 Key 解析器接口\n *\n * @author 芋道源码\n */\npublic interface RateLimiterKeyResolver {\n\n    /**\n     * 解析一个 Key\n     *\n     * @param rateLimiter 限流注解\n     * @param joinPoint  AOP 切面\n     * @return Key\n     */\n    String resolver(JoinPoint joinPoint, RateLimiter rateLimiter);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;\n\nimport cn.hutool.crypto.SecureUtil;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;\nimport org.aspectj.lang.JoinPoint;\n\n/**\n * IP 级别的限流 Key 解析器，使用方法名 + 方法参数 + IP，组装成一个 Key\n *\n * 为了避免 Key 过长，使用 MD5 进行“压缩”\n *\n * @author 芋道源码\n */\npublic class ClientIpRateLimiterKeyResolver implements RateLimiterKeyResolver {\n\n    @Override\n    public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {\n        String methodName = joinPoint.getSignature().toString();\n        String argsStr = StrUtils.joinMethodArgs(joinPoint);\n        String clientIp = ServletUtils.getClientIP();\n        return SecureUtil.md5(methodName + argsStr + clientIp);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;\n\nimport cn.hutool.crypto.SecureUtil;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;\nimport org.aspectj.lang.JoinPoint;\n\n/**\n * 默认（全局级别）限流 Key 解析器，使用方法名 + 方法参数，组装成一个 Key\n *\n * 为了避免 Key 过长，使用 MD5 进行“压缩”\n *\n * @author 芋道源码\n */\npublic class DefaultRateLimiterKeyResolver implements RateLimiterKeyResolver {\n\n    @Override\n    public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {\n        String methodName = joinPoint.getSignature().toString();\n        String argsStr = StrUtils.joinMethodArgs(joinPoint);\n        return SecureUtil.md5(methodName + argsStr);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ExpressionRateLimiterKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.reflect.MethodSignature;\nimport org.springframework.core.DefaultParameterNameDiscoverer;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\n\nimport java.lang.reflect.Method;\n\n/**\n * 基于 Spring EL 表达式的 {@link RateLimiterKeyResolver} 实现类\n *\n * @author 芋道源码\n */\npublic class ExpressionRateLimiterKeyResolver implements RateLimiterKeyResolver {\n\n    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();\n\n    private final ExpressionParser expressionParser = new SpelExpressionParser();\n\n    @Override\n    public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {\n        // 获得被拦截方法参数名列表\n        Method method = getMethod(joinPoint);\n        Object[] args = joinPoint.getArgs();\n        String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method);\n        // 准备 Spring EL 表达式解析的上下文\n        StandardEvaluationContext evaluationContext = new StandardEvaluationContext();\n        if (ArrayUtil.isNotEmpty(parameterNames)) {\n            for (int i = 0; i < parameterNames.length; i++) {\n                evaluationContext.setVariable(parameterNames[i], args[i]);\n            }\n        }\n\n        // 解析参数\n        Expression expression = expressionParser.parseExpression(rateLimiter.keyArg());\n        return expression.getValue(evaluationContext, String.class);\n    }\n\n    private static Method getMethod(JoinPoint point) {\n        // 处理，声明在类上的情况\n        MethodSignature signature = (MethodSignature) point.getSignature();\n        Method method = signature.getMethod();\n        if (!method.getDeclaringClass().isInterface()) {\n            return method;\n        }\n\n        // 处理，声明在接口上的情况\n        try {\n            return point.getTarget().getClass().getDeclaredMethod(\n                    point.getSignature().getName(), method.getParameterTypes());\n        } catch (NoSuchMethodException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;\n\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.system.SystemUtil;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;\nimport org.aspectj.lang.JoinPoint;\n\n/**\n * Server 节点级别的限流 Key 解析器，使用方法名 + 方法参数 + IP，组装成一个 Key\n *\n * 为了避免 Key 过长，使用 MD5 进行“压缩”\n *\n * @author 芋道源码\n */\npublic class ServerNodeRateLimiterKeyResolver implements RateLimiterKeyResolver {\n\n    @Override\n    public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {\n        String methodName = joinPoint.getSignature().toString();\n        String argsStr = StrUtils.joinMethodArgs(joinPoint);\n        String serverNode = String.format(\"%s@%d\", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID());\n        return SecureUtil.md5(methodName + argsStr + serverNode);\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;\n\nimport cn.hutool.crypto.SecureUtil;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;\nimport cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport org.aspectj.lang.JoinPoint;\n\n/**\n * 用户级别的限流 Key 解析器，使用方法名 + 方法参数 + userId + userType，组装成一个 Key\n *\n * 为了避免 Key 过长，使用 MD5 进行“压缩”\n *\n * @author 芋道源码\n */\npublic class UserRateLimiterKeyResolver implements RateLimiterKeyResolver {\n\n    @Override\n    public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {\n        String methodName = joinPoint.getSignature().toString();\n        String argsStr = StrUtils.joinMethodArgs(joinPoint);\n        Long userId = WebFrameworkUtils.getLoginUserId();\n        Integer userType = WebFrameworkUtils.getLoginUserType();\n        return SecureUtil.md5(methodName + argsStr + userId + userType);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/redis/RateLimiterRedisDAO.java",
    "content": "package cn.iocoder.yudao.framework.ratelimiter.core.redis;\n\nimport lombok.AllArgsConstructor;\nimport org.redisson.api.*;\n\nimport java.time.Duration;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 限流 Redis DAO\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\npublic class RateLimiterRedisDAO {\n\n    /**\n     * 限流操作\n     *\n     * KEY 格式：rate_limiter:%s // 参数为 uuid\n     * VALUE 格式：String\n     * 过期时间：不固定\n     */\n    private static final String RATE_LIMITER = \"rate_limiter:%s\";\n\n    private final RedissonClient redissonClient;\n\n    public Boolean tryAcquire(String key, int count, int time, TimeUnit timeUnit) {\n        // 1. 获得 RRateLimiter，并设置 rate 速率\n        RRateLimiter rateLimiter = getRRateLimiter(key, count, time, timeUnit);\n        // 2. 尝试获取 1 个\n        return rateLimiter.tryAcquire();\n    }\n\n    private static String formatKey(String key) {\n        return String.format(RATE_LIMITER, key);\n    }\n\n    private RRateLimiter getRRateLimiter(String key, long count, int time, TimeUnit timeUnit) {\n        String redisKey = formatKey(key);\n        RRateLimiter rateLimiter = redissonClient.getRateLimiter(redisKey);\n        long rateInterval = timeUnit.toSeconds(time);\n        Duration duration = Duration.ofSeconds(rateInterval);\n        // 1. 如果不存在，设置 rate 速率\n        RateLimiterConfig config = rateLimiter.getConfig();\n        if (config == null) {\n            rateLimiter.trySetRate(RateType.OVERALL, count, duration);\n            // 原因参见 https://t.zsxq.com/lcR0W\n            rateLimiter.expire(duration);\n            return rateLimiter;\n        }\n        // 2. 如果存在，并且配置相同，则直接返回\n        if (config.getRateType() == RateType.OVERALL\n                && Objects.equals(config.getRate(), count)\n                && Objects.equals(config.getRateInterval(), TimeUnit.SECONDS.toMillis(rateInterval))) {\n            return rateLimiter;\n        }\n        // 3. 如果存在，并且配置不同，则进行新建\n        rateLimiter.setRate(RateType.OVERALL, count, duration);\n        // 原因参见 https://t.zsxq.com/lcR0W\n        rateLimiter.expire(duration);\n        return rateLimiter;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/package-info.java",
    "content": "/**\n * 限流组件，基于 Redisson {@link org.redisson.api.RRateLimiter} 限流实现\n */\npackage cn.iocoder.yudao.framework.ratelimiter;"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/config/YudaoApiSignatureAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.signature.config;\n\nimport cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;\nimport cn.iocoder.yudao.framework.signature.core.aop.ApiSignatureAspect;\nimport cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.data.redis.core.StringRedisTemplate;\n\n/**\n * HTTP API 签名的自动配置类\n *\n * @author Zhougang\n */\n@AutoConfiguration(after = YudaoRedisAutoConfiguration.class)\npublic class YudaoApiSignatureAutoConfiguration {\n\n    @Bean\n    public ApiSignatureAspect signatureAspect(ApiSignatureRedisDAO signatureRedisDAO) {\n        return new ApiSignatureAspect(signatureRedisDAO);\n    }\n\n    @Bean\n    public ApiSignatureRedisDAO signatureRedisDAO(StringRedisTemplate stringRedisTemplate) {\n        return new ApiSignatureRedisDAO(stringRedisTemplate);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/annotation/ApiSignature.java",
    "content": "package cn.iocoder.yudao.framework.signature.core.annotation;\n\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\n\nimport java.lang.annotation.*;\nimport java.util.concurrent.TimeUnit;\n\n\n/**\n * HTTP API 签名注解\n *\n * @author Zhougang\n */\n@Inherited\n@Documented\n@Target({ElementType.METHOD, ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface ApiSignature {\n\n    /**\n     * 同一个请求多长时间内有效 默认 60 秒\n     */\n    int timeout() default 60;\n\n    /**\n     * 时间单位，默认为 SECONDS 秒\n     */\n    TimeUnit timeUnit() default TimeUnit.SECONDS;\n\n    // ========================== 签名参数 ==========================\n\n    /**\n     * 提示信息，签名失败的提示\n     *\n     * @see GlobalErrorCodeConstants#BAD_REQUEST\n     */\n    String message() default \"签名不正确\"; // 为空时，使用 BAD_REQUEST 错误提示\n\n    /**\n     * 签名字段：appId 应用ID\n     */\n    String appId() default \"appId\";\n\n    /**\n     * 签名字段：timestamp 时间戳\n     */\n    String timestamp() default \"timestamp\";\n\n    /**\n     * 签名字段：nonce 随机数，10 位以上\n     */\n    String nonce() default \"nonce\";\n\n    /**\n     * sign 客户端签名\n     */\n    String sign() default \"sign\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java",
    "content": "package cn.iocoder.yudao.framework.signature.core.aop;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature;\nimport cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.SortedMap;\nimport java.util.TreeMap;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;\n\n/**\n * 拦截声明了 {@link ApiSignature} 注解的方法，实现签名\n *\n * @author Zhougang\n */\n@Aspect\n@Slf4j\n@AllArgsConstructor\npublic class ApiSignatureAspect {\n\n    private final ApiSignatureRedisDAO signatureRedisDAO;\n\n    @Before(\"@annotation(signature)\")\n    public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) {\n        // 1. 验证通过，直接结束\n        if (verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) {\n            return;\n        }\n\n        // 2. 验证不通过，抛出异常\n        log.error(\"[beforePointCut][方法{} 参数({}) 签名失败]\", joinPoint.getSignature().toString(),\n                joinPoint.getArgs());\n        throw new ServiceException(BAD_REQUEST.getCode(),\n                StrUtil.blankToDefault(signature.message(), BAD_REQUEST.getMsg()));\n    }\n\n    public boolean verifySignature(ApiSignature signature, HttpServletRequest request) {\n        // 1.1 校验 Header\n        if (!verifyHeaders(signature, request)) {\n            return false;\n        }\n        // 1.2 校验 appId 是否能获取到对应的 appSecret\n        String appId = request.getHeader(signature.appId());\n        String appSecret = signatureRedisDAO.getAppSecret(appId);\n        Assert.notNull(appSecret, \"[appId({})] 找不到对应的 appSecret\", appId);\n\n        // 2. 校验签名【重要！】\n        String clientSignature = request.getHeader(signature.sign()); // 客户端签名\n        String serverSignatureString = buildSignatureString(signature, request, appSecret); // 服务端签名字符串\n        String serverSignature = DigestUtil.sha256Hex(serverSignatureString); // 服务端签名\n        if (ObjUtil.notEqual(clientSignature, serverSignature)) {\n            return false;\n        }\n\n        // 3. 将 nonce 记入缓存，防止重复使用（重点二：此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 ）\n        String nonce = request.getHeader(signature.nonce());\n        if (BooleanUtil.isFalse(signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit()))) {\n            String timestamp = request.getHeader(signature.timestamp());\n            log.info(\"[verifySignature][appId({}) timestamp({}) nonce({}) sign({}) 存在重复请求]\", appId, timestamp, nonce, clientSignature);\n            throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), \"存在重复请求\");\n        }\n        return true;\n    }\n\n    /**\n     * 校验请求头加签参数\n     * <p>\n     * 1. appId 是否为空\n     * 2. timestamp 是否为空，请求是否已经超时，默认 10 分钟\n     * 3. nonce 是否为空，随机数是否 10 位以上，是否在规定时间内已经访问过了\n     * 4. sign 是否为空\n     *\n     * @param signature signature\n     * @param request   request\n     * @return 是否校验 Header 通过\n     */\n    private boolean verifyHeaders(ApiSignature signature, HttpServletRequest request) {\n        // 1. 非空校验\n        String appId = request.getHeader(signature.appId());\n        if (StrUtil.isBlank(appId)) {\n            return false;\n        }\n        String timestamp = request.getHeader(signature.timestamp());\n        if (StrUtil.isBlank(timestamp)) {\n            return false;\n        }\n        String nonce = request.getHeader(signature.nonce());\n        if (StrUtil.length(nonce) < 10) {\n            return false;\n        }\n        String sign = request.getHeader(signature.sign());\n        if (StrUtil.isBlank(sign)) {\n            return false;\n        }\n\n        // 2. 检查 timestamp 是否超出允许的范围 （重点一：此处需要取绝对值）\n        long expireTime = signature.timeUnit().toMillis(signature.timeout());\n        long requestTimestamp = Long.parseLong(timestamp);\n        long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp);\n        if (timestampDisparity > expireTime) {\n            return false;\n        }\n\n        // 3. 检查 nonce 是否存在，有且仅能使用一次\n        return signatureRedisDAO.getNonce(appId, nonce) == null;\n    }\n\n    /**\n     * 构建签名字符串\n     * <p>\n     * 格式为 = 请求参数 + 请求体 + 请求头 + 密钥\n     *\n     * @param signature signature\n     * @param request   request\n     * @param appSecret appSecret\n     * @return 签名字符串\n     */\n    private String buildSignatureString(ApiSignature signature, HttpServletRequest request, String appSecret) {\n        SortedMap<String, String> parameterMap = getRequestParameterMap(request); // 请求头\n        SortedMap<String, String> headerMap = getRequestHeaderMap(signature, request); // 请求参数\n        String requestBody = StrUtil.nullToDefault(ServletUtils.getBody(request), \"\"); // 请求体\n        return MapUtil.join(parameterMap, \"&\", \"=\")\n                + requestBody\n                + MapUtil.join(headerMap, \"&\", \"=\")\n                + appSecret;\n    }\n\n    /**\n     * 获取请求头加签参数 Map\n     *\n     * @param request   请求\n     * @param signature 签名注解\n     * @return signature params\n     */\n    private static SortedMap<String, String> getRequestHeaderMap(ApiSignature signature, HttpServletRequest request) {\n        SortedMap<String, String> sortedMap = new TreeMap<>();\n        sortedMap.put(signature.appId(), request.getHeader(signature.appId()));\n        sortedMap.put(signature.timestamp(), request.getHeader(signature.timestamp()));\n        sortedMap.put(signature.nonce(), request.getHeader(signature.nonce()));\n        return sortedMap;\n    }\n\n    /**\n     * 获取请求参数 Map\n     *\n     * @param request 请求\n     * @return queryParams\n     */\n    private static SortedMap<String, String> getRequestParameterMap(HttpServletRequest request) {\n        SortedMap<String, String> sortedMap = new TreeMap<>();\n        for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {\n            sortedMap.put(entry.getKey(), entry.getValue()[0]);\n        }\n        return sortedMap;\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java",
    "content": "package cn.iocoder.yudao.framework.signature.core.redis;\n\nimport lombok.AllArgsConstructor;\nimport org.springframework.data.redis.core.StringRedisTemplate;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * HTTP API 签名 Redis DAO\n *\n * @author Zhougang\n */\n@AllArgsConstructor\npublic class ApiSignatureRedisDAO {\n\n    private final StringRedisTemplate stringRedisTemplate;\n\n    /**\n     * 验签随机数\n     * <p>\n     * KEY 格式：signature_nonce:%s // 参数为 随机数\n     * VALUE 格式：String\n     * 过期时间：不固定\n     */\n    private static final String SIGNATURE_NONCE = \"api_signature_nonce:%s:%s\";\n\n    /**\n     * 签名密钥\n     * <p>\n     * HASH 结构\n     * KEY 格式：%s // 参数为 appid\n     * VALUE 格式：String\n     * 过期时间：永不过期（预加载到 Redis）\n     */\n    private static final String SIGNATURE_APPID = \"api_signature_app\";\n\n    // ========== 验签随机数 ==========\n\n    public String getNonce(String appId, String nonce) {\n        return stringRedisTemplate.opsForValue().get(formatNonceKey(appId, nonce));\n    }\n\n    public Boolean setNonce(String appId, String nonce, int time, TimeUnit timeUnit) {\n        return stringRedisTemplate.opsForValue().setIfAbsent(formatNonceKey(appId, nonce), \"\", time, timeUnit);\n    }\n\n    private static String formatNonceKey(String appId, String nonce) {\n        return String.format(SIGNATURE_NONCE, appId, nonce);\n    }\n\n    // ========== 签名密钥 ==========\n\n    public String getAppSecret(String appId) {\n        return (String) stringRedisTemplate.opsForHash().get(SIGNATURE_APPID, appId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/package-info.java",
    "content": "/**\n * HTTP API 签名，校验安全性\n *\n * @see <a href=\"https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3>微信支付 —— 安全规范</a>\n */\npackage cn.iocoder.yudao.framework.signature;"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.idempotent.config.YudaoIdempotentConfiguration\ncn.iocoder.yudao.framework.lock4j.config.YudaoLock4jConfiguration\ncn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration\ncn.iocoder.yudao.framework.signature.config.YudaoApiSignatureAutoConfiguration"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-protection/src/test/java/cn/iocoder/yudao/framework/signature/core/ApiSignatureTest.java",
    "content": "package cn.iocoder.yudao.framework.signature.core;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature;\nimport cn.iocoder.yudao.framework.signature.core.aop.ApiSignatureAspect;\nimport cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link ApiSignatureTest} 的单元测试\n */\n@ExtendWith(MockitoExtension.class)\npublic class ApiSignatureTest {\n\n    @InjectMocks\n    private ApiSignatureAspect apiSignatureAspect;\n\n    @Mock\n    private ApiSignatureRedisDAO signatureRedisDAO;\n\n    @Test\n    public void testSignatureGet() throws IOException {\n        // 搞一个签名\n        Long timestamp = System.currentTimeMillis();\n        String nonce = IdUtil.randomUUID();\n        String appId = \"xxxxxx\";\n        String appSecret = \"yyyyyy\";\n        String signString = \"k1=v1&v1=k1testappId=xxxxxx&nonce=\" + nonce + \"&timestamp=\" + timestamp + \"yyyyyy\";\n        String sign = DigestUtil.sha256Hex(signString);\n\n        // 准备参数\n        ApiSignature apiSignature = mock(ApiSignature.class);\n        when(apiSignature.appId()).thenReturn(\"appId\");\n        when(apiSignature.timestamp()).thenReturn(\"timestamp\");\n        when(apiSignature.nonce()).thenReturn(\"nonce\");\n        when(apiSignature.sign()).thenReturn(\"sign\");\n        when(apiSignature.timeout()).thenReturn(60);\n        when(apiSignature.timeUnit()).thenReturn(TimeUnit.SECONDS);\n        HttpServletRequest request = mock(HttpServletRequest.class);\n        when(request.getHeader(eq(\"appId\"))).thenReturn(appId);\n        when(request.getHeader(eq(\"timestamp\"))).thenReturn(String.valueOf(timestamp));\n        when(request.getHeader(eq(\"nonce\"))).thenReturn(nonce);\n        when(request.getHeader(eq(\"sign\"))).thenReturn(sign);\n        when(request.getParameterMap()).thenReturn(MapUtil.<String, String[]>builder()\n                .put(\"v1\", new String[]{\"k1\"}).put(\"k1\", new String[]{\"v1\"}).build());\n        when(request.getContentType()).thenReturn(\"application/json\");\n        when(request.getReader()).thenReturn(new BufferedReader(new StringReader(\"test\")));\n        // mock 方法\n        when(signatureRedisDAO.getAppSecret(eq(appId))).thenReturn(appSecret);\n        when(signatureRedisDAO.setNonce(eq(appId), eq(nonce), eq(120), eq(TimeUnit.SECONDS))).thenReturn(true);\n\n        // 调用\n        boolean result = apiSignatureAspect.verifySignature(apiSignature, request);\n        // 断言结果\n        assertTrue(result);\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-redis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-redis</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>Redis 封装拓展</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>org.redisson</groupId>\n            <artifactId>redisson-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.redisson</groupId>\n            <artifactId>redisson-spring-data-27</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-cache</artifactId> <!-- 实现对 Caches 的自动化配置 -->\n        </dependency>\n\n        <dependency>\n            <groupId>com.fasterxml.jackson.datatype</groupId>\n            <artifactId>jackson-datatype-jsr310</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.redis.config;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.cache.CacheProperties;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.cache.annotation.EnableCaching;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.data.redis.cache.BatchStrategies;\nimport org.springframework.data.redis.cache.RedisCacheConfiguration;\nimport org.springframework.data.redis.cache.RedisCacheManager;\nimport org.springframework.data.redis.cache.RedisCacheWriter;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.serializer.RedisSerializationContext;\nimport org.springframework.util.StringUtils;\n\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration.buildRedisSerializer;\n\n/**\n * Cache 配置类，基于 Redis 实现\n */\n@AutoConfiguration\n@EnableConfigurationProperties({CacheProperties.class, YudaoCacheProperties.class})\n@EnableCaching\npublic class YudaoCacheAutoConfiguration {\n\n    /**\n     * RedisCacheConfiguration Bean\n     * <p>\n     * 参考 org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration 的 createConfiguration 方法\n     */\n    @Bean\n    @Primary\n    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {\n        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();\n        // 设置使用 : 单冒号，而不是双 :: 冒号，避免 Redis Desktop Manager 多余空格\n        // 详细可见 https://blog.csdn.net/chuixue24/article/details/103928965 博客\n        // 再次修复单冒号，而不是双 :: 冒号问题，Issues 详情：https://gitee.com/zhijiantianya/yudao-cloud/issues/I86VY2\n        config = config.computePrefixWith(cacheName -> {\n            String keyPrefix = cacheProperties.getRedis().getKeyPrefix();\n            if (StringUtils.hasText(keyPrefix)) {\n                keyPrefix = keyPrefix.lastIndexOf(StrUtil.COLON) == -1 ? keyPrefix + StrUtil.COLON : keyPrefix;\n                return keyPrefix + cacheName + StrUtil.COLON;\n            }\n            return cacheName + StrUtil.COLON;\n        });\n        // 设置使用 JSON 序列化方式\n        config = config.serializeValuesWith(\n                RedisSerializationContext.SerializationPair.fromSerializer(buildRedisSerializer()));\n\n        // 设置 CacheProperties.Redis 的属性\n        CacheProperties.Redis redisProperties = cacheProperties.getRedis();\n        if (redisProperties.getTimeToLive() != null) {\n            config = config.entryTtl(redisProperties.getTimeToLive());\n        }\n        if (!redisProperties.isCacheNullValues()) {\n            config = config.disableCachingNullValues();\n        }\n        if (!redisProperties.isUseKeyPrefix()) {\n            config = config.disableKeyPrefix();\n        }\n        return config;\n    }\n\n    @Bean\n    public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate,\n                                               RedisCacheConfiguration redisCacheConfiguration,\n                                               YudaoCacheProperties yudaoCacheProperties) {\n        // 创建 RedisCacheWriter 对象\n        RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());\n        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,\n                BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));\n        // 创建 TenantRedisCacheManager 对象\n        return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheProperties.java",
    "content": "package cn.iocoder.yudao.framework.redis.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\n\n/**\n * Cache 配置项\n *\n * @author Wanwan\n */\n@ConfigurationProperties(\"yudao.cache\")\n@Data\n@Validated\npublic class YudaoCacheProperties {\n\n    /**\n     * {@link #redisScanBatchSize} 默认值\n     */\n    private static final Integer REDIS_SCAN_BATCH_SIZE_DEFAULT = 30;\n\n    /**\n     * redis scan 一次返回数量\n     */\n    private Integer redisScanBatchSize = REDIS_SCAN_BATCH_SIZE_DEFAULT;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.redis.config;\n\nimport cn.hutool.core.util.ReflectUtil;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.redisson.spring.starter.RedissonAutoConfiguration;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.serializer.RedisSerializer;\n\n/**\n * Redis 配置类\n */\n@AutoConfiguration(before = RedissonAutoConfiguration.class) // 目的：使用自己定义的 RedisTemplate Bean\npublic class YudaoRedisAutoConfiguration {\n\n    /**\n     * 创建 RedisTemplate Bean，使用 JSON 序列化方式\n     */\n    @Bean\n    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {\n        // 创建 RedisTemplate 对象\n        RedisTemplate<String, Object> template = new RedisTemplate<>();\n        // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友，可以自己去撸下。\n        template.setConnectionFactory(factory);\n        // 使用 String 序列化方式，序列化 KEY 。\n        template.setKeySerializer(RedisSerializer.string());\n        template.setHashKeySerializer(RedisSerializer.string());\n        // 使用 JSON 序列化方式（库是 Jackson ），序列化 VALUE 。\n        template.setValueSerializer(buildRedisSerializer());\n        template.setHashValueSerializer(buildRedisSerializer());\n        return template;\n    }\n\n    public static RedisSerializer<?> buildRedisSerializer() {\n        RedisSerializer<Object> json = RedisSerializer.json();\n        // 解决 LocalDateTime 的序列化\n        ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, \"mapper\");\n        objectMapper.registerModules(new JavaTimeModule());\n        return json;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java",
    "content": "package cn.iocoder.yudao.framework.redis.core;\n\nimport cn.hutool.core.util.NumberUtil;\nimport cn.hutool.core.util.StrUtil;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.data.redis.cache.RedisCache;\nimport org.springframework.data.redis.cache.RedisCacheConfiguration;\nimport org.springframework.data.redis.cache.RedisCacheManager;\nimport org.springframework.data.redis.cache.RedisCacheWriter;\n\nimport java.time.Duration;\n\n/**\n * 支持自定义过期时间的 {@link RedisCacheManager} 实现类\n *\n * 在 {@link Cacheable#cacheNames()} 格式为 \"key#ttl\" 时，# 后面的 ttl 为过期时间。\n * 单位为最后一个字母（支持的单位有：d 天，h 小时，m 分钟，s 秒），默认单位为 s 秒\n *\n * @author 芋道源码\n */\npublic class TimeoutRedisCacheManager extends RedisCacheManager {\n\n    private static final String SPLIT = \"#\";\n\n    public TimeoutRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {\n        super(cacheWriter, defaultCacheConfiguration);\n    }\n\n    @Override\n    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {\n        if (StrUtil.isEmpty(name)) {\n            return super.createRedisCache(name, cacheConfig);\n        }\n        // 如果使用 # 分隔，大小不为 2，则说明不使用自定义过期时间\n        String[] names = StrUtil.splitToArray(name, SPLIT);\n        if (names.length != 2) {\n            return super.createRedisCache(name, cacheConfig);\n        }\n\n        // 核心：通过修改 cacheConfig 的过期时间，实现自定义过期时间\n        if (cacheConfig != null) {\n            // 移除 # 后面的 : 以及后面的内容，避免影响解析\n            String ttlStr = StrUtil.subBefore(names[1], StrUtil.COLON, false); // 获得 ttlStr 时间部分\n            names[1] = StrUtil.subAfter(names[1], ttlStr, false); // 移除掉 ttlStr 时间部分\n            // 解析时间\n            Duration duration = parseDuration(ttlStr);\n            cacheConfig = cacheConfig.entryTtl(duration);\n        }\n\n        // 创建 RedisCache 对象，需要忽略掉 ttlStr\n        return super.createRedisCache(names[0] + names[1], cacheConfig);\n    }\n\n    /**\n     * 解析过期时间 Duration\n     *\n     * @param ttlStr 过期时间字符串\n     * @return 过期时间 Duration\n     */\n    private Duration parseDuration(String ttlStr) {\n        String timeUnit = StrUtil.subSuf(ttlStr, -1);\n        switch (timeUnit) {\n            case \"d\":\n                return Duration.ofDays(removeDurationSuffix(ttlStr));\n            case \"h\":\n                return Duration.ofHours(removeDurationSuffix(ttlStr));\n            case \"m\":\n                return Duration.ofMinutes(removeDurationSuffix(ttlStr));\n            case \"s\":\n                return Duration.ofSeconds(removeDurationSuffix(ttlStr));\n            default:\n                return Duration.ofSeconds(Long.parseLong(ttlStr));\n        }\n    }\n\n    /**\n     * 移除多余的后缀，返回具体的时间\n     *\n     * @param ttlStr 过期时间字符串\n     * @return 时间\n     */\n    private Long removeDurationSuffix(String ttlStr) {\n        return NumberUtil.parseLong(StrUtil.sub(ttlStr, 0, ttlStr.length() - 1));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/package-info.java",
    "content": "/**\n * 采用 Spring Data Redis 操作 Redis，底层使用 Redisson 作为客户端\n */\npackage cn.iocoder.yudao.framework.redis;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration\ncn.iocoder.yudao.framework.redis.config.YudaoCacheAutoConfiguration\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-redis/《芋道 Spring Boot Cache 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Cache/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-redis/《芋道 Spring Boot Redis 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Redis/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-rpc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao-framework</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        OpenFeign：提供 RESTful API 的调用\n    </description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-loadbalancer</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-okhttp</artifactId>\n        </dependency>\n\n        <!-- 工具相关 -->\n        <dependency>\n            <groupId>jakarta.validation</groupId>\n            <artifactId>jakarta.validation-api</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-rpc/src/main/java/cn/iocoder/yudao/framework/rpc/config/package-info.java",
    "content": "/**\n * 占坑 TODO\n */\npackage cn.iocoder.yudao.framework.rpc.config;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-rpc/src/main/java/cn/iocoder/yudao/framework/rpc/core/package-info.java",
    "content": "/**\n * 占坑 TODO\n */\npackage cn.iocoder.yudao.framework.rpc.core;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-rpc/src/main/java/cn/iocoder/yudao/framework/rpc/package-info.java",
    "content": "/**\n * OpenFeign：提供 RESTful API 的调用\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.framework.rpc;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-rpc/《芋道 Spring Boot 声明式调用 Feign 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Feign/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-rpc/《芋道 Spring Cloud 声明式调用 Feign 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Feign/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-security</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        1. security：用户的认证、权限的校验，实现「谁」可以做「什么事」\n        2. operatelog：操作日志，实现「谁」在「什么时间」对「什么」做了「什么事」\n    </description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Spring 核心 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-web</artifactId>\n        </dependency>\n        <!-- spring boot 配置所需依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n        </dependency>\n\n        <dependency>\n            <!-- Spring Boot 通用操作日志组件，基于注解实现 -->\n            <!-- 此组件解决的问题是：「谁」在「什么时间」对「什么」做了「什么事」 -->\n            <groupId>io.github.mouzt</groupId>\n            <artifactId>bizlog-sdk</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.operatelog.config;\n\nimport cn.iocoder.yudao.framework.operatelog.core.service.LogRecordServiceImpl;\nimport com.mzt.logapi.service.ILogRecordService;\nimport com.mzt.logapi.starter.annotation.EnableLogRecord;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Primary;\n\n/**\n * 操作日志配置类\n *\n * @author HUIHUI\n */\n@EnableLogRecord(tenant = \"\") // 貌似用不上 tenant 这玩意给个空好啦\n@AutoConfiguration\n@Slf4j\npublic class YudaoOperateLogConfiguration {\n\n    @Bean\n    @Primary\n    public ILogRecordService iLogRecordServiceImpl() {\n        return new LogRecordServiceImpl();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogRpcAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.operatelog.config;\n\nimport cn.iocoder.yudao.framework.common.biz.system.logger.OperateLogCommonApi;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n/**\n * OperateLog 使用到 Feign 的配置项\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@EnableFeignClients(clients = {OperateLogCommonApi.class}) // 主要是引入相关的 API 服务\npublic class YudaoOperateLogRpcAutoConfiguration {\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/package-info.java",
    "content": "/**\n * 占位，无特殊作用\n */\npackage cn.iocoder.yudao.framework.operatelog.core;"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/LogRecordServiceImpl.java",
    "content": "package cn.iocoder.yudao.framework.operatelog.core.service;\n\nimport cn.iocoder.yudao.framework.common.biz.system.logger.OperateLogCommonApi;\nimport cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO;\nimport com.mzt.logapi.beans.LogRecord;\nimport com.mzt.logapi.service.ILogRecordService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Qualifier;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.List;\n\n/**\n * 操作日志 ILogRecordService 实现类\n *\n * 基于 {@link OperateLogCommonApi} 实现，记录操作日志\n *\n * @author HUIHUI\n */\n@Slf4j\npublic class LogRecordServiceImpl implements ILogRecordService {\n\n    @Resource\n    private OperateLogCommonApi operateLogApi;\n\n    @Override\n    public void record(LogRecord logRecord) {\n        OperateLogCreateReqDTO reqDTO = new OperateLogCreateReqDTO();\n        try {\n            reqDTO.setTraceId(TracerUtils.getTraceId());\n            // 补充用户信息\n            fillUserFields(reqDTO);\n            // 补全模块信息\n            fillModuleFields(reqDTO, logRecord);\n            // 补全请求信息\n            fillRequestFields(reqDTO);\n\n            // 2. 异步记录日志\n            operateLogApi.createOperateLogAsync(reqDTO);\n        } catch (Throwable ex) {\n            // 由于 @Async 异步调用，这里打印下日志，更容易跟进\n            log.error(\"[record][url({}) log({}) 发生异常]\", reqDTO.getRequestUrl(), reqDTO, ex);\n        }\n    }\n\n    private static void fillUserFields(OperateLogCreateReqDTO reqDTO) {\n        // 使用 SecurityFrameworkUtils。因为要考虑，rpc、mq、job，它其实不是 web；\n        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();\n        if (loginUser == null) {\n            return;\n        }\n        reqDTO.setUserId(loginUser.getId());\n        reqDTO.setUserType(loginUser.getUserType());\n    }\n\n    public static void fillModuleFields(OperateLogCreateReqDTO reqDTO, LogRecord logRecord) {\n        reqDTO.setType(logRecord.getType()); // 大模块类型，例如：CRM 客户\n        reqDTO.setSubType(logRecord.getSubType());// 操作名称，例如：转移客户\n        reqDTO.setBizId(Long.parseLong(logRecord.getBizNo())); // 业务编号，例如：客户编号\n        reqDTO.setAction(logRecord.getAction());// 操作内容，例如：修改编号为 1 的用户信息，将性别从男改成女，将姓名从芋道改成源码。\n        reqDTO.setExtra(logRecord.getExtra()); // 拓展字段，有些复杂的业务，需要记录一些字段 ( JSON 格式 )，例如说，记录订单编号，{ orderId: \"1\"}\n    }\n\n    private static void fillRequestFields(OperateLogCreateReqDTO reqDTO) {\n        // 获得 Request 对象\n        HttpServletRequest request = ServletUtils.getRequest();\n        if (request == null) {\n            return;\n        }\n        // 补全请求信息\n        reqDTO.setRequestMethod(request.getMethod());\n        reqDTO.setRequestUrl(request.getRequestURI());\n        reqDTO.setUserIp(ServletUtils.getClientIP(request));\n        reqDTO.setUserAgent(ServletUtils.getUserAgent(request));\n    }\n\n    @Override\n    public List<LogRecord> queryLog(String bizNo, String type) {\n        throw new UnsupportedOperationException(\"使用 OperateLogApi 进行操作日志的查询\");\n    }\n\n    @Override\n    public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) {\n        throw new UnsupportedOperationException(\"使用 OperateLogApi 进行操作日志的查询\");\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/package-info.java",
    "content": "/**\n * 基于 mzt-log 框架\n * 实现操作日志功能\n *\n * @author HUIHUI\n */\npackage cn.iocoder.yudao.framework.operatelog;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/AuthorizeRequestsCustomizer.java",
    "content": "package cn.iocoder.yudao.framework.security.config;\n\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport org.springframework.core.Ordered;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\nimport javax.annotation.Resource;\n\n/**\n * 自定义的 URL 的安全配置\n * 目的：每个 Maven Module 可以自定义规则！\n *\n * @author 芋道源码\n */\npublic abstract class AuthorizeRequestsCustomizer\n        implements Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry>, Ordered {\n\n    @Resource\n    private WebProperties webProperties;\n\n    protected String buildAdminApi(String url) {\n        return webProperties.getAdminApi().getPrefix() + url;\n    }\n\n    protected String buildAppApi(String url) {\n        return webProperties.getAppApi().getPrefix() + url;\n    }\n\n    @Override\n    public int getOrder() {\n        return 0;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/SecurityProperties.java",
    "content": "package cn.iocoder.yudao.framework.security.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Collections;\nimport java.util.List;\n\n@ConfigurationProperties(prefix = \"yudao.security\")\n@Validated\n@Data\npublic class SecurityProperties {\n\n    /**\n     * HTTP 请求时，访问令牌的请求 Header\n     */\n    @NotEmpty(message = \"Token Header 不能为空\")\n    private String tokenHeader = \"Authorization\";\n    /**\n     * HTTP 请求时，访问令牌的请求参数\n     *\n     * 初始目的：解决 WebSocket 无法通过 header 传参，只能通过 token 参数拼接\n     */\n    @NotEmpty(message = \"Token Parameter 不能为空\")\n    private String tokenParameter = \"token\";\n\n    /**\n     * mock 模式的开关\n     */\n    @NotNull(message = \"mock 模式的开关不能为空\")\n    private Boolean mockEnable = false;\n    /**\n     * mock 模式的密钥\n     * 一定要配置密钥，保证安全性\n     */\n    @NotEmpty(message = \"mock 模式的密钥不能为空\") // 这里设置了一个默认值，因为实际上只有 mockEnable 为 true 时才需要配置。\n    private String mockSecret = \"test\";\n\n    /**\n     * 免登录的 URL 列表\n     */\n    private List<String> permitAllUrls = Collections.emptyList();\n\n    /**\n     * PasswordEncoder 加密复杂度，越高开销越大\n     */\n    private Integer passwordEncoderLength = 4;\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.security.config;\n\nimport cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;\nimport cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;\nimport cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy;\nimport cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter;\nimport cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl;\nimport cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl;\nimport cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;\nimport cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkServiceImpl;\nimport cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;\nimport org.springframework.beans.factory.config.MethodInvokingFactoryBean;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.AutoConfigureOrder;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.access.AccessDeniedHandler;\n\nimport javax.annotation.Resource;\n\n/**\n * Spring Security 自动配置类，主要用于相关组件的配置\n *\n * 注意，不能和 {@link YudaoWebSecurityConfigurerAdapter} 用一个，原因是会导致初始化报错。\n * 参见 https://stackoverflow.com/questions/53847050/spring-boot-delegatebuilder-cannot-be-null-on-autowiring-authenticationmanager 文档。\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@AutoConfigureOrder(-1) // 目的：先于 Spring Security 自动配置，避免一键改包后，org.* 基础包无法生效\n@EnableConfigurationProperties(SecurityProperties.class)\npublic class YudaoSecurityAutoConfiguration {\n\n    @Resource\n    private SecurityProperties securityProperties;\n\n    /**\n     * 认证失败处理类 Bean\n     */\n    @Bean\n    public AuthenticationEntryPoint authenticationEntryPoint() {\n        return new AuthenticationEntryPointImpl();\n    }\n\n    /**\n     * 权限不够处理器 Bean\n     */\n    @Bean\n    public AccessDeniedHandler accessDeniedHandler() {\n        return new AccessDeniedHandlerImpl();\n    }\n\n    /**\n     * Spring Security 加密器\n     * 考虑到安全性，这里采用 BCryptPasswordEncoder 加密器\n     *\n     * @see <a href=\"http://stackabuse.com/password-encoding-with-spring-security/\">Password Encoding with Spring Security</a>\n     */\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder(securityProperties.getPasswordEncoderLength());\n    }\n\n    /**\n     * Token 认证过滤器 Bean\n     */\n    @Bean\n    public TokenAuthenticationFilter authenticationTokenFilter(GlobalExceptionHandler globalExceptionHandler,\n                                                               OAuth2TokenCommonApi oauth2TokenApi) {\n        return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler, oauth2TokenApi);\n    }\n\n    @Bean(\"ss\") // 使用 Spring Security 的缩写，方便使用\n    public SecurityFrameworkService securityFrameworkService(PermissionCommonApi permissionApi) {\n        return new SecurityFrameworkServiceImpl(permissionApi);\n    }\n\n    /**\n     * 声明调用 {@link SecurityContextHolder#setStrategyName(String)} 方法，\n     * 设置使用 {@link TransmittableThreadLocalSecurityContextHolderStrategy} 作为 Security 的上下文策略\n     */\n    @Bean\n    public MethodInvokingFactoryBean securityContextHolderMethodInvokingFactoryBean() {\n        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();\n        methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);\n        methodInvokingFactoryBean.setTargetMethod(\"setStrategyName\");\n        methodInvokingFactoryBean.setArguments(TransmittableThreadLocalSecurityContextHolderStrategy.class.getName());\n        return methodInvokingFactoryBean;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityRpcAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.security.config;\n\nimport cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;\nimport cn.iocoder.yudao.framework.security.core.rpc.LoginUserRequestInterceptor;\nimport cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Bean;\n\n/**\n * Security 使用到 Feign 的配置项\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@EnableFeignClients(clients = {OAuth2TokenCommonApi.class, // 主要是引入相关的 API 服务\n        PermissionCommonApi.class})\npublic class YudaoSecurityRpcAutoConfiguration {\n\n    @Bean\n    public LoginUserRequestInterceptor loginUserRequestInterceptor() {\n        return new LoginUserRequestInterceptor();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java",
    "content": "package cn.iocoder.yudao.framework.security.config;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter;\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport com.google.common.collect.HashMultimap;\nimport com.google.common.collect.Multimap;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.AutoConfigureOrder;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.mvc.method.RequestMappingInfo;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;\nimport org.springframework.web.util.pattern.PathPattern;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.servlet.DispatcherType;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * 自定义的 Spring Security 配置适配器实现\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@AutoConfigureOrder(-1) // 目的：先于 Spring Security 自动配置，避免一键改包后，org.* 基础包无法生效\n@EnableMethodSecurity(securedEnabled = true)\npublic class YudaoWebSecurityConfigurerAdapter {\n\n    @Resource\n    private WebProperties webProperties;\n    @Resource\n    private SecurityProperties securityProperties;\n\n    /**\n     * 认证失败处理类 Bean\n     */\n    @Resource\n    private AuthenticationEntryPoint authenticationEntryPoint;\n    /**\n     * 权限不够处理器 Bean\n     */\n    @Resource\n    private AccessDeniedHandler accessDeniedHandler;\n    /**\n     * Token 认证过滤器 Bean\n     */\n    @Resource\n    private TokenAuthenticationFilter authenticationTokenFilter;\n\n    /**\n     * 自定义的权限映射 Bean 们\n     *\n     * @see #filterChain(HttpSecurity)\n     */\n    @Resource\n    private List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers;\n\n    @Resource\n    private ApplicationContext applicationContext;\n\n    /**\n     * 由于 Spring Security 创建 AuthenticationManager 对象时，没声明 @Bean 注解，导致无法被注入\n     * 通过覆写父类的该方法，添加 @Bean 注解，解决该问题\n     */\n    @Bean\n    public AuthenticationManager authenticationManagerBean(AuthenticationConfiguration authenticationConfiguration) throws Exception {\n        return authenticationConfiguration.getAuthenticationManager();\n    }\n\n    /**\n     * 配置 URL 的安全配置\n     *\n     * anyRequest          |   匹配所有请求路径\n     * access              |   SpringEl表达式结果为true时可以访问\n     * anonymous           |   匿名可以访问\n     * denyAll             |   用户不能访问\n     * fullyAuthenticated  |   用户完全认证可以访问（非remember-me下自动登录）\n     * hasAnyAuthority     |   如果有参数，参数表示权限，则其中任何一个权限可以访问\n     * hasAnyRole          |   如果有参数，参数表示角色，则其中任何一个角色可以访问\n     * hasAuthority        |   如果有参数，参数表示权限，则其权限可以访问\n     * hasIpAddress        |   如果有参数，参数表示IP地址，如果用户IP和参数匹配，则可以访问\n     * hasRole             |   如果有参数，参数表示角色，则其角色可以访问\n     * permitAll           |   用户可以任意访问\n     * rememberMe          |   允许通过remember-me登录的用户访问\n     * authenticated       |   用户登录后可访问\n     */\n    @Bean\n    protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {\n        // 登出\n        httpSecurity\n                // 开启跨域\n                .cors(Customizer.withDefaults())\n                // CSRF 禁用，因为不使用 Session\n                .csrf(AbstractHttpConfigurer::disable)\n                // 基于 token 机制，所以不需要 Session\n                .sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS))\n                .headers(c -> c.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))\n                // 一堆自定义的 Spring Security 处理器\n                .exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint)\n                        .accessDeniedHandler(accessDeniedHandler));\n        // 登录、登录暂时不使用 Spring Security 的拓展点，主要考虑一方面拓展多用户、多种登录方式相对复杂，一方面用户的学习成本较高\n\n        // 获得 @PermitAll 带来的 URL 列表，免登录\n        Multimap<HttpMethod, String> permitAllUrls = getPermitAllUrlsFromAnnotations();\n        // 设置每个请求的权限\n        httpSecurity\n                // ①：全局共享规则\n                .authorizeHttpRequests(c -> c\n                    // 1.1 静态资源，可匿名访问\n                    .requestMatchers(HttpMethod.GET, \"/*.html\", \"/*.css\", \"/*.js\").permitAll()\n                    // 1.2 设置 @PermitAll 无需认证\n                    .requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()\n                    .requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()\n                    .requestMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll()\n                    .requestMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll()\n                    .requestMatchers(HttpMethod.HEAD, permitAllUrls.get(HttpMethod.HEAD).toArray(new String[0])).permitAll()\n                    .requestMatchers(HttpMethod.PATCH, permitAllUrls.get(HttpMethod.PATCH).toArray(new String[0])).permitAll()\n                    // 1.3 基于 yudao.security.permit-all-urls 无需认证\n                    .requestMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll()\n                )\n                // ②：每个项目的自定义规则\n                .authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c)))\n                // ③：兜底规则，必须认证\n                .authorizeHttpRequests(c -> c\n                        .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll() // WebFlux 异步请求，无需认证，目的：SSE 场景\n                        .anyRequest().authenticated());\n\n        // 添加 Token Filter\n        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);\n        return httpSecurity.build();\n    }\n\n    private String buildAppApi(String url) {\n        return webProperties.getAppApi().getPrefix() + url;\n    }\n\n    private Multimap<HttpMethod, String> getPermitAllUrlsFromAnnotations() {\n        Multimap<HttpMethod, String> result = HashMultimap.create();\n        // 获得接口对应的 HandlerMethod 集合\n        RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)\n                applicationContext.getBean(\"requestMappingHandlerMapping\");\n        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();\n        // 获得有 @PermitAll 注解的接口\n        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {\n            HandlerMethod handlerMethod = entry.getValue();\n            if (!handlerMethod.hasMethodAnnotation(PermitAll.class) // 方法级\n                    && !handlerMethod.getBeanType().isAnnotationPresent(PermitAll.class)) { // 接口级\n                continue;\n            }\n            Set<String> urls = new HashSet<>();\n            if (entry.getKey().getPatternsCondition() != null) {\n                urls.addAll(entry.getKey().getPatternsCondition().getPatterns());\n            }\n            if (entry.getKey().getPathPatternsCondition() != null) {\n                urls.addAll(convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));\n            }\n            if (urls.isEmpty()) {\n                continue;\n            }\n\n            // 特殊：使用 @RequestMapping 注解，并且未写 method 属性，此时认为都需要免登录\n            Set<RequestMethod> methods = entry.getKey().getMethodsCondition().getMethods();\n            if (CollUtil.isEmpty(methods)) {\n                result.putAll(HttpMethod.GET, urls);\n                result.putAll(HttpMethod.POST, urls);\n                result.putAll(HttpMethod.PUT, urls);\n                result.putAll(HttpMethod.DELETE, urls);\n                result.putAll(HttpMethod.HEAD, urls);\n                result.putAll(HttpMethod.PATCH, urls);\n                continue;\n            }\n            // 根据请求方法，添加到 result 结果\n            entry.getKey().getMethodsCondition().getMethods().forEach(requestMethod -> {\n                switch (requestMethod) {\n                    case GET:\n                        result.putAll(HttpMethod.GET, urls);\n                        break;\n                    case POST:\n                        result.putAll(HttpMethod.POST, urls);\n                        break;\n                    case PUT:\n                        result.putAll(HttpMethod.PUT, urls);\n                        break;\n                    case DELETE:\n                        result.putAll(HttpMethod.DELETE, urls);\n                        break;\n                    case HEAD:\n                        result.putAll(HttpMethod.HEAD, urls);\n                        break;\n                    case PATCH:\n                        result.putAll(HttpMethod.PATCH, urls);\n                        break;\n                }\n            });\n        }\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java",
    "content": "package cn.iocoder.yudao.framework.security.core;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 登录用户信息\n *\n * @author 芋道源码\n */\n@Data\npublic class LoginUser {\n\n    public static final String INFO_KEY_NICKNAME = \"nickname\";\n    public static final String INFO_KEY_DEPT_ID = \"deptId\";\n\n    /**\n     * 用户编号\n     */\n    private Long id;\n    /**\n     * 用户类型\n     *\n     * 关联 {@link UserTypeEnum}\n     */\n    private Integer userType;\n    /**\n     * 额外的用户信息\n     */\n    private Map<String, String> info;\n    /**\n     * 租户编号\n     */\n    private Long tenantId;\n    /**\n     * 授权范围\n     */\n    private List<String> scopes;\n    /**\n     * 过期时间\n     */\n    private LocalDateTime expiresTime;\n\n    // ========== 上下文 ==========\n    /**\n     * 上下文字段，不进行持久化\n     *\n     * 1. 用于基于 LoginUser 维度的临时缓存\n     */\n    @JsonIgnore\n    private Map<String, Object> context;\n    /**\n     * 访问的租户编号\n     */\n    private Long visitTenantId;\n\n    public void setContext(String key, Object value) {\n        if (context == null) {\n            context = new HashMap<>();\n        }\n        context.put(key, value);\n    }\n\n    public <T> T getContext(String key, Class<T> type) {\n        return MapUtil.get(context, key, type);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java",
    "content": "package cn.iocoder.yudao.framework.security.core.context;\n\nimport com.alibaba.ttl.TransmittableThreadLocal;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.util.Assert;\n\n/**\n * 基于 TransmittableThreadLocal 实现的 Security Context 持有者策略\n * 目的是，避免 @Async 等异步执行时，原生 ThreadLocal 的丢失问题\n *\n * @author 芋道源码\n */\npublic class TransmittableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {\n\n    /**\n     * 使用 TransmittableThreadLocal 作为上下文\n     */\n    private static final ThreadLocal<SecurityContext> CONTEXT_HOLDER = new TransmittableThreadLocal<>();\n\n    @Override\n    public void clearContext() {\n        CONTEXT_HOLDER.remove();\n    }\n\n    @Override\n    public SecurityContext getContext() {\n        SecurityContext ctx = CONTEXT_HOLDER.get();\n        if (ctx == null) {\n            ctx = createEmptyContext();\n            CONTEXT_HOLDER.set(ctx);\n        }\n        return ctx;\n    }\n\n    @Override\n    public void setContext(SecurityContext context) {\n        Assert.notNull(context, \"Only non-null SecurityContext instances are permitted\");\n        CONTEXT_HOLDER.set(context);\n    }\n\n    @Override\n    public SecurityContext createEmptyContext() {\n        return new SecurityContextImpl();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java",
    "content": "package cn.iocoder.yudao.framework.security.core.filter;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;\nimport cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.security.config.SecurityProperties;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport lombok.RequiredArgsConstructor;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * Token 过滤器，验证 token 的有效性\n * 验证通过后，获得 {@link LoginUser} 信息，并加入到 Spring Security 上下文\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class TokenAuthenticationFilter extends OncePerRequestFilter {\n\n    private final SecurityProperties securityProperties;\n\n    private final GlobalExceptionHandler globalExceptionHandler;\n\n    private final OAuth2TokenCommonApi oauth2TokenApi;\n\n    @Override\n    @SuppressWarnings(\"NullableProblems\")\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        // 情况一，基于 header[login-user] 获得用户，例如说来自 Gateway 或者其它服务透传\n        LoginUser loginUser = buildLoginUserByHeader(request);\n\n        // 情况二，基于 Token 获得用户\n        // 注意，这里主要满足直接使用 Nginx 直接转发到 Spring Cloud 服务的场景。\n        if (loginUser == null) {\n            String token = SecurityFrameworkUtils.obtainAuthorization(request,\n                    securityProperties.getTokenHeader(), securityProperties.getTokenParameter());\n            if (StrUtil.isNotEmpty(token)) {\n                Integer userType = WebFrameworkUtils.getLoginUserType(request);\n                try {\n                    // 1.1 基于 token 构建登录用户\n                    loginUser = buildLoginUserByToken(token, userType);\n                    // 1.2 模拟 Login 功能，方便日常开发调试\n                    if (loginUser == null) {\n                        loginUser = mockLoginUser(request, token, userType);\n                    }\n                } catch (Throwable ex) {\n                    CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);\n                    ServletUtils.writeJSON(response, result);\n                    return;\n                }\n            }\n        }\n\n        // 设置当前用户\n        if (loginUser != null) {\n            SecurityFrameworkUtils.setLoginUser(loginUser, request);\n        }\n        // 继续过滤链\n        chain.doFilter(request, response);\n    }\n\n    private LoginUser buildLoginUserByToken(String token, Integer userType) {\n        try {\n            // 校验访问令牌\n            OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token).getCheckedData();\n            if (accessToken == null) {\n                return null;\n            }\n            // 用户类型不匹配，无权限\n            // 注意：只有 /admin-api/* 和 /app-api/* 有 userType，才需要比对用户类型\n            // 类似 WebSocket 的 /ws/* 连接地址，是不需要比对用户类型的\n            if (userType != null\n                    && ObjectUtil.notEqual(accessToken.getUserType(), userType)) {\n                throw new AccessDeniedException(\"错误的用户类型\");\n            }\n            // 构建登录用户\n            return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())\n                    .setInfo(accessToken.getUserInfo()) // 额外的用户信息\n                    .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())\n                    .setExpiresTime(accessToken.getExpiresTime());\n        } catch (ServiceException serviceException) {\n            // 校验 Token 不通过时，考虑到一些接口是无需登录的，所以直接返回 null 即可\n            return null;\n        }\n    }\n\n    /**\n     * 模拟登录用户，方便日常开发调试\n     *\n     * 注意，在线上环境下，一定要关闭该功能！！！\n     *\n     * @param request 请求\n     * @param token 模拟的 token，格式为 {@link SecurityProperties#getMockSecret()} + 用户编号\n     * @param userType 用户类型\n     * @return 模拟的 LoginUser\n     */\n    private LoginUser mockLoginUser(HttpServletRequest request, String token, Integer userType) {\n        if (!securityProperties.getMockEnable()) {\n            return null;\n        }\n        // 必须以 mockSecret 开头\n        if (!token.startsWith(securityProperties.getMockSecret())) {\n            return null;\n        }\n        // 构建模拟用户\n        Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length()));\n        return new LoginUser().setId(userId).setUserType(userType)\n                .setTenantId(WebFrameworkUtils.getTenantId(request));\n    }\n\n    @SneakyThrows\n    private LoginUser buildLoginUserByHeader(HttpServletRequest request) {\n        String loginUserStr = request.getHeader(SecurityFrameworkUtils.LOGIN_USER_HEADER);\n        if (StrUtil.isEmpty(loginUserStr)) {\n            return null;\n        }\n        try {\n            loginUserStr = URLDecoder.decode(loginUserStr, StandardCharsets.UTF_8.name()); // 解码，解决中文乱码问题\n            LoginUser loginUser = JsonUtils.parseObject(loginUserStr, LoginUser.class);\n            // 用户类型不匹配，无权限\n            // 注意：只有 /admin-api/* 和 /app-api/* 有 userType，才需要比对用户类型\n            // 类似 WebSocket 的 /ws/* 连接地址，是不需要比对用户类型的\n            Integer userType = WebFrameworkUtils.getLoginUserType(request);\n            if (userType != null\n                    && loginUser != null\n                    && ObjectUtil.notEqual(loginUser.getUserType(), userType)) {\n                throw new AccessDeniedException(\"错误的用户类型\");\n            }\n            return loginUser;\n        } catch (Exception ex) {\n            log.error(\"[buildLoginUserByHeader][解析 LoginUser({}) 发生异常]\", loginUserStr, ex);  ;\n            throw ex;\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/AccessDeniedHandlerImpl.java",
    "content": "package cn.iocoder.yudao.framework.security.core.handler;\n\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.FORBIDDEN;\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;\n\n/**\n * 访问一个需要认证的 URL 资源，已经认证（登录）但是没有权限的情况下，返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。\n *\n * 补充：Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法，调用当前类\n *\n * @author 芋道源码\n */\n@Slf4j\n@SuppressWarnings(\"JavadocReference\")\npublic class AccessDeniedHandlerImpl implements AccessDeniedHandler {\n\n    @Override\n    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)\n            throws IOException, ServletException {\n        // 打印 warn 的原因是，不定期合并 warn，看看有没恶意破坏\n        log.warn(\"[commence][访问 URL({}) 时，用户({}) 权限不够]\", request.getRequestURI(),\n                SecurityFrameworkUtils.getLoginUserId(), e);\n        // 返回 403\n        ServletUtils.writeJSON(response, CommonResult.error(FORBIDDEN));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/AuthenticationEntryPointImpl.java",
    "content": "package cn.iocoder.yudao.framework.security.core.handler;\n\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;\n\n/**\n * 访问一个需要认证的 URL 资源，但是此时自己尚未认证（登录）的情况下，返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码，从而使前端重定向到登录页\n *\n * 补充：Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法，调用当前类\n *\n * @author ruoyi\n */\n@Slf4j\n@SuppressWarnings(\"JavadocReference\") // 忽略文档引用报错\npublic class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {\n\n    @Override\n    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {\n        log.debug(\"[commence][访问 URL({}) 时，没有登录]\", request.getRequestURI(), e);\n        // 返回 401\n        ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/rpc/LoginUserRequestInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.security.core.rpc;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport feign.RequestInterceptor;\nimport feign.RequestTemplate;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * LoginUser 的 RequestInterceptor 实现类：Feign 请求时，将 {@link LoginUser} 设置到 header 中，继续透传给被调用的服务\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class LoginUserRequestInterceptor implements RequestInterceptor {\n\n    @Override\n    @SneakyThrows\n    public void apply(RequestTemplate requestTemplate) {\n        LoginUser user = SecurityFrameworkUtils.getLoginUser();\n        if (user == null) {\n            return;\n        }\n        try {\n            String userStr = JsonUtils.toJsonString(user);\n            userStr = URLEncoder.encode(userStr, StandardCharsets.UTF_8.name()); // 编码，避免中文乱码\n            requestTemplate.header(SecurityFrameworkUtils.LOGIN_USER_HEADER, userStr);\n        } catch (Exception ex) {\n            log.error(\"[apply][序列化 LoginUser({}) 发生异常]\", user, ex);\n            throw ex;\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkService.java",
    "content": "package cn.iocoder.yudao.framework.security.core.service;\n\n/**\n * Security 框架 Service 接口，定义权限相关的校验操作\n *\n * @author 芋道源码\n */\npublic interface SecurityFrameworkService {\n\n    /**\n     * 判断是否有权限\n     *\n     * @param permission 权限\n     * @return 是否\n     */\n    boolean hasPermission(String permission);\n\n    /**\n     * 判断是否有权限，任一一个即可\n     *\n     * @param permissions 权限\n     * @return 是否\n     */\n    boolean hasAnyPermissions(String... permissions);\n\n    /**\n     * 判断是否有角色\n     *\n     * 注意，角色使用的是 SysRoleDO 的 code 标识\n     *\n     * @param role 角色\n     * @return 是否\n     */\n    boolean hasRole(String role);\n\n    /**\n     * 判断是否有角色，任一一个即可\n     *\n     * @param roles 角色数组\n     * @return 是否\n     */\n    boolean hasAnyRoles(String... roles);\n\n    /**\n     * 判断是否有授权\n     *\n     * @param scope 授权\n     * @return 是否\n     */\n    boolean hasScope(String scope);\n\n    /**\n     * 判断是否有授权范围，任一一个即可\n     *\n     * @param scope 授权范围数组\n     * @return 是否\n     */\n    boolean hasAnyScopes(String... scope);\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkServiceImpl.java",
    "content": "package cn.iocoder.yudao.framework.security.core.service;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport lombok.AllArgsConstructor;\nimport lombok.SneakyThrows;\n\nimport java.time.Duration;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildCache;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.skipPermissionCheck;\n\n/**\n * 默认的 {@link SecurityFrameworkService} 实现类\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\npublic class SecurityFrameworkServiceImpl implements SecurityFrameworkService {\n\n    private final PermissionCommonApi permissionApi;\n\n    /**\n     * 针对 {@link #hasAnyRoles(String...)} 的缓存\n     */\n    private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyRolesCache = buildCache(\n            Duration.ofMinutes(1L), // 过期时间 1 分钟\n            new CacheLoader<KeyValue<Long, List<String>>, Boolean>() {\n\n                @Override\n                public Boolean load(KeyValue<Long, List<String>> key) {\n                    return permissionApi.hasAnyRoles(key.getKey(), key.getValue().toArray(new String[0])).getCheckedData();\n                }\n\n            });\n\n    /**\n     * 针对 {@link #hasAnyPermissions(String...)} 的缓存\n     */\n    private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyPermissionsCache = buildCache(\n            Duration.ofMinutes(1L), // 过期时间 1 分钟\n            new CacheLoader<KeyValue<Long, List<String>>, Boolean>() {\n\n                @Override\n                public Boolean load(KeyValue<Long, List<String>> key) {\n                    return permissionApi.hasAnyPermissions(key.getKey(), key.getValue().toArray(new String[0])).getCheckedData();\n                }\n\n            });\n\n    @Override\n    public boolean hasPermission(String permission) {\n        return hasAnyPermissions(permission);\n    }\n\n    @Override\n    @SneakyThrows\n    public boolean hasAnyPermissions(String... permissions) {\n        // 特殊：跨租户访问\n        if (skipPermissionCheck()) {\n            return true;\n        }\n\n        // 权限校验\n        Long userId = getLoginUserId();\n        if (userId == null) {\n            return false;\n        }\n        return hasAnyPermissionsCache.get(new KeyValue<>(userId, Arrays.asList(permissions)));\n    }\n\n    @Override\n    public boolean hasRole(String role) {\n        return hasAnyRoles(role);\n    }\n\n    @Override\n    @SneakyThrows\n    public boolean hasAnyRoles(String... roles) {\n        // 特殊：跨租户访问\n        if (skipPermissionCheck()) {\n            return true;\n        }\n\n        // 权限校验\n        Long userId = getLoginUserId();\n        if (userId == null) {\n            return false;\n        }\n        return hasAnyRolesCache.get(new KeyValue<>(userId, Arrays.asList(roles)));\n    }\n\n    @Override\n    public boolean hasScope(String scope) {\n        return hasAnyScopes(scope);\n    }\n\n    @Override\n    public boolean hasAnyScopes(String... scope) {\n        // 特殊：跨租户访问\n        if (skipPermissionCheck()) {\n            return true;\n        }\n\n        // 权限校验\n        LoginUser user = SecurityFrameworkUtils.getLoginUser();\n        if (user == null) {\n            return false;\n        }\n        return CollUtil.containsAny(user.getScopes(), Arrays.asList(scope));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java",
    "content": "package cn.iocoder.yudao.framework.security.core.util;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.util.StringUtils;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Collections;\n\n/**\n * 安全服务工具类\n *\n * @author 芋道源码\n */\npublic class SecurityFrameworkUtils {\n\n    /**\n     * HEADER 认证头 value 的前缀\n     */\n    public static final String AUTHORIZATION_BEARER = \"Bearer\";\n\n    public static final String LOGIN_USER_HEADER = \"login-user\";\n\n    private SecurityFrameworkUtils() {}\n\n    /**\n     * 从请求中，获得认证 Token\n     *\n     * @param request 请求\n     * @param headerName 认证 Token 对应的 Header 名字\n     * @param parameterName 认证 Token 对应的 Parameter 名字\n     * @return 认证 Token\n     */\n    public static String obtainAuthorization(HttpServletRequest request,\n                                             String headerName, String parameterName) {\n        // 1. 获得 Token。优先级：Header > Parameter\n        String token = request.getHeader(headerName);\n        if (StrUtil.isEmpty(token)) {\n            token = request.getParameter(parameterName);\n        }\n        if (!StringUtils.hasText(token)) {\n            return null;\n        }\n        // 2. 去除 Token 中带的 Bearer\n        int index = token.indexOf(AUTHORIZATION_BEARER + \" \");\n        return index >= 0 ? token.substring(index + 7).trim() : token;\n    }\n\n    /**\n     * 获得当前认证信息\n     *\n     * @return 认证信息\n     */\n    public static Authentication getAuthentication() {\n        SecurityContext context = SecurityContextHolder.getContext();\n        if (context == null) {\n            return null;\n        }\n        return context.getAuthentication();\n    }\n\n    /**\n     * 获取当前用户\n     *\n     * @return 当前用户\n     */\n    @Nullable\n    public static LoginUser getLoginUser() {\n        Authentication authentication = getAuthentication();\n        if (authentication == null) {\n            return null;\n        }\n        return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;\n    }\n\n    /**\n     * 获得当前用户的编号，从上下文中\n     *\n     * @return 用户编号\n     */\n    @Nullable\n    public static Long getLoginUserId() {\n        LoginUser loginUser = getLoginUser();\n        return loginUser != null ? loginUser.getId() : null;\n    }\n\n    /**\n     * 获得当前用户的昵称，从上下文中\n     *\n     * @return 昵称\n     */\n    @Nullable\n    public static String getLoginUserNickname() {\n        LoginUser loginUser = getLoginUser();\n        return loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_NICKNAME) : null;\n    }\n\n    /**\n     * 获得当前用户的部门编号，从上下文中\n     *\n     * @return 部门编号\n     */\n    @Nullable\n    public static Long getLoginUserDeptId() {\n        LoginUser loginUser = getLoginUser();\n        return loginUser != null ? MapUtil.getLong(loginUser.getInfo(), LoginUser.INFO_KEY_DEPT_ID) : null;\n    }\n\n    /**\n     * 设置当前用户\n     *\n     * @param loginUser 登录用户\n     * @param request 请求\n     */\n    public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {\n        // 创建 Authentication，并设置到上下文\n        Authentication authentication = buildAuthentication(loginUser, request);\n        SecurityContextHolder.getContext().setAuthentication(authentication);\n\n        // 额外设置到 request 中，用于 ApiAccessLogFilter 可以获取到用户编号；\n        // 原因是，Spring Security 的 Filter 在 ApiAccessLogFilter 后面，在它记录访问日志时，线上上下文已经没有用户编号等信息\n        if (request != null) {\n            WebFrameworkUtils.setLoginUserId(request, loginUser.getId());\n            WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType());\n        }\n    }\n\n    private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {\n        // 创建 UsernamePasswordAuthenticationToken 对象\n        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(\n                loginUser, null, Collections.emptyList());\n        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));\n        return authenticationToken;\n    }\n\n    /**\n     * 是否条件跳过权限校验，包括数据权限、功能权限\n     *\n     * @return 是否跳过\n     */\n    public static boolean skipPermissionCheck() {\n        LoginUser loginUser = getLoginUser();\n        if (loginUser == null) {\n            return false;\n        }\n        if (loginUser.getVisitTenantId() == null) {\n            return false;\n        }\n        // 重点：跨租户访问时，无法进行权限校验\n        return ObjUtil.notEqual(loginUser.getVisitTenantId(), loginUser.getTenantId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/package-info.java",
    "content": "/**\n * 基于 Spring Security 框架\n * 实现安全认证功能\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.framework.security;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.security.config.YudaoSecurityRpcAutoConfiguration\ncn.iocoder.yudao.framework.security.config.YudaoSecurityAutoConfiguration\ncn.iocoder.yudao.framework.security.config.YudaoWebSecurityConfigurerAdapter\ncn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogConfiguration\ncn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogRpcAutoConfiguration"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-security/《芋道 Spring Boot 安全框架 Spring Security 入门》.md",
    "content": "* 芋道 Spring Security 入门：<http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao>\n* Spring Security 基本概念：<http://www.iocoder.cn/Fight/Spring-Security-4-1-0-Basic-concept-description/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-test</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>测试组件，用于单元测试、集成测试</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.h2database</groupId> <!-- 单元测试，我们采用 H2 作为数据库 -->\n            <artifactId>h2</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.fppt</groupId> <!-- 单元测试，我们采用内嵌的 Redis 数据库 -->\n            <artifactId>jedis-mock</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>uk.co.jemos.podam</groupId> <!-- 单元测试，随机生成 POJO 类 -->\n            <artifactId>podam</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/RedisTestConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.test.config;\n\nimport com.github.fppt.jedismock.RedisServer;\nimport org.springframework.boot.autoconfigure.data.redis.RedisProperties;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Lazy;\n\nimport java.io.IOException;\n\n/**\n * Redis 测试 Configuration，主要实现内嵌 Redis 的启动\n *\n * @author 芋道源码\n */\n@Configuration(proxyBeanMethods = false)\n@Lazy(false) // 禁止延迟加载\n@EnableConfigurationProperties(RedisProperties.class)\npublic class RedisTestConfiguration {\n\n    /**\n     * 创建模拟的 Redis Server 服务器\n     */\n    @Bean\n    public RedisServer redisServer(RedisProperties properties) throws IOException {\n        RedisServer redisServer = new RedisServer(properties.getPort());\n        // 一次执行多个单元测试时，貌似创建多个 spring 容器，导致不进行 stop。这样，就导致端口被占用，无法启动。。。\n        try {\n            redisServer.start();\n        } catch (Exception ignore) {}\n        return redisServer;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/SqlInitializationTestConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.test.config;\n\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;\nimport org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;\nimport org.springframework.boot.sql.init.DatabaseInitializationSettings;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Lazy;\n\nimport javax.sql.DataSource;\n\n/**\n * SQL 初始化的测试 Configuration\n *\n * 为什么不使用 org.springframework.boot.autoconfigure.sql.init.DataSourceInitializationConfiguration 呢？\n * 因为我们在单元测试会使用 spring.main.lazy-initialization 为 true，开启延迟加载。此时，会导致 DataSourceInitializationConfiguration 初始化\n * 不过呢，当前类的实现代码，基本是复制 DataSourceInitializationConfiguration 的哈！\n *\n * @author 芋道源码\n */\n@Configuration(proxyBeanMethods = false)\n@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class)\n@ConditionalOnSingleCandidate(DataSource.class)\n@ConditionalOnClass(name = \"org.springframework.jdbc.datasource.init.DatabasePopulator\")\n@Lazy(value = false) // 禁止延迟加载\n@EnableConfigurationProperties(SqlInitializationProperties.class)\npublic class SqlInitializationTestConfiguration {\n\n\t@Bean\n\tpublic DataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   SqlInitializationProperties initializationProperties) {\n\t\tDatabaseInitializationSettings settings = createFrom(initializationProperties);\n\t\treturn new DataSourceScriptDatabaseInitializer(dataSource, settings);\n\t}\n\n\tstatic DatabaseInitializationSettings createFrom(SqlInitializationProperties properties) {\n\t\tDatabaseInitializationSettings settings = new DatabaseInitializationSettings();\n\t\tsettings.setSchemaLocations(properties.getSchemaLocations());\n\t\tsettings.setDataLocations(properties.getDataLocations());\n\t\tsettings.setContinueOnError(properties.isContinueOnError());\n\t\tsettings.setSeparator(properties.getSeparator());\n\t\tsettings.setEncoding(properties.getEncoding());\n\t\tsettings.setMode(properties.getMode());\n\t\treturn settings;\n\t}\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java",
    "content": "package cn.iocoder.yudao.framework.test.core.ut;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;\nimport cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;\nimport cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;\nimport cn.iocoder.yudao.framework.test.config.RedisTestConfiguration;\nimport cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration;\nimport com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;\nimport org.redisson.spring.starter.RedissonAutoConfiguration;\nimport org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.ActiveProfiles;\nimport org.springframework.test.context.jdbc.Sql;\n\n/**\n * 依赖内存 DB + Redis 的单元测试\n *\n * 相比 {@link BaseDbUnitTest} 来说，额外增加了内存 Redis\n *\n * @author 芋道源码\n */\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class)\n@ActiveProfiles(\"unit-test\") // 设置使用 application-unit-test 配置文件\n@Sql(scripts = \"/sql/clean.sql\", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后，清理 DB\npublic class BaseDbAndRedisUnitTest {\n\n    @Import({\n            // DB 配置类\n            YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类\n            DataSourceAutoConfiguration.class, // Spring DB 自动配置类\n            DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类\n            DruidDataSourceAutoConfigure.class, // Druid 自动配置类\n            SqlInitializationTestConfiguration.class, // SQL 初始化\n            // MyBatis 配置类\n            YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类\n            MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类\n\n            // Redis 配置类\n            RedisTestConfiguration.class, // Redis 测试配置类，用于启动 RedisServer\n            YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类\n            RedisAutoConfiguration.class, // Spring Redis 自动配置类\n            RedissonAutoConfiguration.class, // Redisson 自动配置类\n\n            // 其它配置类\n            SpringUtil.class\n    })\n    public static class Application {\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbUnitTest.java",
    "content": "package cn.iocoder.yudao.framework.test.core.ut;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;\nimport cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;\nimport cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration;\nimport com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;\nimport com.github.yulichang.autoconfigure.MybatisPlusJoinAutoConfiguration;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.ActiveProfiles;\nimport org.springframework.test.context.jdbc.Sql;\n\n/**\n * 依赖内存 DB 的单元测试\n *\n * 注意，Service 层同样适用。对于 Service 层的单元测试，我们针对自己模块的 Mapper 走的是 H2 内存数据库，针对别的模块的 Service 走的是 Mock 方法\n *\n * @author 芋道源码\n */\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class)\n@ActiveProfiles(\"unit-test\") // 设置使用 application-unit-test 配置文件\n@Sql(scripts = \"/sql/clean.sql\", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后，清理 DB\npublic class BaseDbUnitTest {\n\n    @Import({\n            // DB 配置类\n            YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类\n            DataSourceAutoConfiguration.class, // Spring DB 自动配置类\n            DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类\n            DruidDataSourceAutoConfigure.class, // Druid 自动配置类\n            SqlInitializationTestConfiguration.class, // SQL 初始化\n            // MyBatis 配置类\n            YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类\n            MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类\n            MybatisPlusJoinAutoConfiguration.class, // MyBatis 的Join配置类\n\n            // 其它配置类\n            SpringUtil.class\n    })\n    public static class Application {\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseMockitoUnitTest.java",
    "content": "package cn.iocoder.yudao.framework.test.core.ut;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n/**\n * 纯 Mockito 的单元测试\n *\n * @author 芋道源码\n */\n@ExtendWith(MockitoExtension.class)\npublic class BaseMockitoUnitTest {\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseRedisUnitTest.java",
    "content": "package cn.iocoder.yudao.framework.test.core.ut;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;\nimport cn.iocoder.yudao.framework.test.config.RedisTestConfiguration;\nimport org.redisson.spring.starter.RedissonAutoConfiguration;\nimport org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.ActiveProfiles;\n\n/**\n * 依赖内存 Redis 的单元测试\n *\n * 相比 {@link BaseDbUnitTest} 来说，从内存 DB 改成了内存 Redis\n *\n * @author 芋道源码\n */\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisUnitTest.Application.class)\n@ActiveProfiles(\"unit-test\") // 设置使用 application-unit-test 配置文件\npublic class BaseRedisUnitTest {\n\n    @Import({\n            // Redis 配置类\n            RedisTestConfiguration.class, // Redis 测试配置类，用于启动 RedisServer\n            RedisAutoConfiguration.class, // Spring Redis 自动配置类\n            YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类\n            RedissonAutoConfiguration.class, // Redisson 自动配置类\n\n            // 其它配置类\n            SpringUtil.class\n    })\n    public static class Application {\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/package-info.java",
    "content": "/**\n * 提供单元测试 Unit Test 的基类\n */\npackage cn.iocoder.yudao.framework.test.core.ut;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/AssertUtils.java",
    "content": "package cn.iocoder.yudao.framework.test.core.util;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.function.Executable;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.Objects;\n\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\n/**\n * 单元测试，assert 断言工具类\n *\n * @author 芋道源码\n */\npublic class AssertUtils {\n\n    /**\n     * 比对两个对象的属性是否一致\n     *\n     * 注意，如果 expected 存在的属性，actual 不存在的时候，会进行忽略\n     *\n     * @param expected 期望对象\n     * @param actual 实际对象\n     * @param ignoreFields 忽略的属性数组\n     */\n    public static void assertPojoEquals(Object expected, Object actual, String... ignoreFields) {\n        Field[] expectedFields = ReflectUtil.getFields(expected.getClass());\n        Arrays.stream(expectedFields).forEach(expectedField -> {\n            // 忽略 jacoco 自动生成的 $jacocoData 属性的情况\n            if (expectedField.isSynthetic()) {\n                return;\n            }\n            // 如果是忽略的属性，则不进行比对\n            if (ArrayUtil.contains(ignoreFields, expectedField.getName())) {\n                return;\n            }\n            // 忽略不存在的属性\n            Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName());\n            if (actualField == null) {\n                return;\n            }\n            // 比对\n            Assertions.assertEquals(\n                    ReflectUtil.getFieldValue(expected, expectedField),\n                    ReflectUtil.getFieldValue(actual, actualField),\n                    String.format(\"Field(%s) 不匹配\", expectedField.getName())\n            );\n        });\n    }\n\n    /**\n     * 比对两个对象的属性是否一致\n     *\n     * 注意，如果 expected 存在的属性，actual 不存在的时候，会进行忽略\n     *\n     * @param expected 期望对象\n     * @param actual 实际对象\n     * @param ignoreFields 忽略的属性数组\n     * @return 是否一致\n     */\n    public static boolean isPojoEquals(Object expected, Object actual, String... ignoreFields) {\n        Field[] expectedFields = ReflectUtil.getFields(expected.getClass());\n        return Arrays.stream(expectedFields).allMatch(expectedField -> {\n            // 如果是忽略的属性，则不进行比对\n            if (ArrayUtil.contains(ignoreFields, expectedField.getName())) {\n                return true;\n            }\n            // 忽略不存在的属性\n            Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName());\n            if (actualField == null) {\n                return true;\n            }\n            return Objects.equals(ReflectUtil.getFieldValue(expected, expectedField),\n                    ReflectUtil.getFieldValue(actual, actualField));\n        });\n    }\n\n    /**\n     * 执行方法，校验抛出的 Service 是否符合条件\n     *\n     * @param executable 业务异常\n     * @param errorCode 错误码对象\n     * @param messageParams 消息参数\n     */\n    public static void assertServiceException(Executable executable, ErrorCode errorCode, Object... messageParams) {\n        // 调用方法\n        ServiceException serviceException = assertThrows(ServiceException.class, executable);\n        // 校验错误码\n        Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), \"错误码不匹配\");\n        String message = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), messageParams);\n        Assertions.assertEquals(message, serviceException.getMessage(), \"错误提示不匹配\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java",
    "content": "package cn.iocoder.yudao.framework.test.core.util;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.RandomUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport uk.co.jemos.podam.api.PodamFactory;\nimport uk.co.jemos.podam.api.PodamFactoryImpl;\n\nimport java.lang.reflect.Type;\nimport java.time.LocalDateTime;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * 随机工具类\n *\n * @author 芋道源码\n */\npublic class RandomUtils {\n\n    private static final int RANDOM_STRING_LENGTH = 10;\n\n    private static final int TINYINT_MAX = 127;\n\n    private static final int RANDOM_DATE_MAX = 30;\n\n    private static final int RANDOM_COLLECTION_LENGTH = 5;\n\n    private static final PodamFactory PODAM_FACTORY = new PodamFactoryImpl();\n\n    static {\n        // 字符串\n        PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(String.class,\n                (dataProviderStrategy, attributeMetadata, map) -> randomString());\n        // Integer\n        PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Integer.class, (dataProviderStrategy, attributeMetadata, map) -> {\n            // 如果是 status 的字段，返回 0 或 1\n            if (\"status\".equals(attributeMetadata.getAttributeName())) {\n                return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus();\n            }\n            // 如果是 type、status 结尾的字段，返回 tinyint 范围\n            if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(),\n                    \"type\", \"status\", \"category\", \"scope\", \"result\")) {\n                return RandomUtil.randomInt(0, TINYINT_MAX + 1);\n            }\n            return RandomUtil.randomInt();\n        });\n        // LocalDateTime\n        PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(LocalDateTime.class,\n                (dataProviderStrategy, attributeMetadata, map) -> randomLocalDateTime());\n        // Boolean\n        PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Boolean.class, (dataProviderStrategy, attributeMetadata, map) -> {\n            // 如果是 deleted 的字段，返回非删除\n            if (\"deleted\".equals(attributeMetadata.getAttributeName())) {\n                return false;\n            }\n            return RandomUtil.randomBoolean();\n        });\n    }\n\n    public static String randomString() {\n        return RandomUtil.randomString(RANDOM_STRING_LENGTH);\n    }\n\n    public static Long randomLongId() {\n        return RandomUtil.randomLong(0, Long.MAX_VALUE);\n    }\n\n    public static Integer randomInteger() {\n        return RandomUtil.randomInt(0, Integer.MAX_VALUE);\n    }\n\n    public static Date randomDate() {\n        return RandomUtil.randomDay(0, RANDOM_DATE_MAX);\n    }\n\n    public static LocalDateTime randomLocalDateTime() {\n        // 设置 Nano 为零的原因，避免 MySQL、H2 存储不到时间戳\n        return LocalDateTimeUtil.of(randomDate()).withNano(0);\n    }\n\n    public static Short randomShort() {\n        return (short) RandomUtil.randomInt(0, Short.MAX_VALUE);\n    }\n\n    public static <T> Set<T> randomSet(Class<T> clazz) {\n        return Stream.iterate(0, i -> i).limit(RandomUtil.randomInt(1, RANDOM_COLLECTION_LENGTH))\n                .map(i -> randomPojo(clazz)).collect(Collectors.toSet());\n    }\n\n    public static Integer randomCommonStatus() {\n        return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus();\n    }\n\n    public static String randomEmail() {\n        return randomString() + \"@qq.com\";\n    }\n\n    public static String randomMobile() {\n        return \"13800138\" + RandomUtil.randomNumbers(3);\n    }\n\n    public static String randomURL() {\n        return \"https://www.iocoder.cn/\" + randomString();\n    }\n\n    @SafeVarargs\n    public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) {\n        T pojo = PODAM_FACTORY.manufacturePojo(clazz);\n        // 非空时，回调逻辑。通过它，可以实现 Pojo 的进一步处理\n        if (ArrayUtil.isNotEmpty(consumers)) {\n            Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo));\n        }\n        return pojo;\n    }\n\n    @SafeVarargs\n    public static <T> T randomPojo(Class<T> clazz, Type type, Consumer<T>... consumers) {\n        T pojo = PODAM_FACTORY.manufacturePojo(clazz, type);\n        // 非空时，回调逻辑。通过它，可以实现 Pojo 的进一步处理\n        if (ArrayUtil.isNotEmpty(consumers)) {\n            Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo));\n        }\n        return pojo;\n    }\n\n    @SafeVarargs\n    public static <T> List<T> randomPojoList(Class<T> clazz, Consumer<T>... consumers) {\n        int size = RandomUtil.randomInt(1, RANDOM_COLLECTION_LENGTH);\n        return randomPojoList(clazz, size, consumers);\n    }\n\n    @SafeVarargs\n    public static <T> List<T> randomPojoList(Class<T> clazz, int size, Consumer<T>... consumers) {\n        return Stream.iterate(0, i -> i).limit(size).map(o -> randomPojo(clazz, consumers))\n                .collect(Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/package-info.java",
    "content": "/**\n * 测试组件，用于单元测试、集成测试等等\n */\npackage cn.iocoder.yudao.framework.test;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-test/《芋道 Spring Boot 单元测试 Test 入门》.md",
    "content": "<https://www.iocoder.cn/Spring-Boot/Unit-Test/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-web</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>Web 框架，全局异常、API 日志、脱敏、错误码等</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Spring Boot 配置所需依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.security</groupId>\n            <artifactId>spring-security-core</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，主要是 GlobalExceptionHandler 使用 -->\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.xiaoymin</groupId> <!-- 接口文档 -->\n            <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springdoc</groupId>  <!-- 接口文档 -->\n            <artifactId>springdoc-openapi-ui</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <dependency>\n            <groupId>org.jsoup</groupId>\n            <artifactId>jsoup</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.apilog.config;\n\nimport cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter;\nimport cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor;\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.ApiAccessLogCommonApi;\nimport cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport javax.servlet.Filter;\n\n@AutoConfiguration(after = YudaoWebAutoConfiguration.class)\npublic class YudaoApiLogAutoConfiguration implements WebMvcConfigurer {\n\n    /**\n     * 创建 ApiAccessLogFilter Bean，记录 API 请求日志\n     */\n    @Bean\n    @ConditionalOnProperty(prefix = \"yudao.access-log\", value = \"enable\", matchIfMissing = true) // 允许使用 yudao.access-log.enable=false 禁用访问日志\n    public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties,\n                                                                         @Value(\"${spring.application.name}\") String applicationName,\n                                                                         ApiAccessLogCommonApi apiAccessLogApi) {\n        ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties, applicationName, apiAccessLogApi);\n        return createFilterBean(filter, WebFilterOrderEnum.API_ACCESS_LOG_FILTER);\n    }\n\n    private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {\n        FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);\n        bean.setOrder(order);\n        return bean;\n    }\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(new ApiAccessLogInterceptor());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogRpcAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.apilog.config;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.ApiAccessLogCommonApi;\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n/**\n * API 日志使用到 Feign 的配置项\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@EnableFeignClients(clients = {ApiAccessLogCommonApi.class, ApiErrorLogCommonApi.class}) // 主要是引入相关的 API 服务\npublic class YudaoApiLogRpcAutoConfiguration {\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/annotation/ApiAccessLog.java",
    "content": "package cn.iocoder.yudao.framework.apilog.core.annotation;\n\nimport cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 访问日志注解\n *\n * @author 芋道源码\n */\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface ApiAccessLog {\n\n    // ========== 开关字段 ==========\n\n    /**\n     * 是否记录访问日志\n     */\n    boolean enable() default true;\n    /**\n     * 是否记录请求参数\n     *\n     * 默认记录，主要考虑请求数据一般不大。可手动设置为 false 进行关闭\n     */\n    boolean requestEnable() default true;\n    /**\n     * 是否记录响应结果\n     *\n     * 默认不记录，主要考虑响应数据可能比较大。可手动设置为 true 进行打开\n     */\n    boolean responseEnable() default false;\n    /**\n     * 敏感参数数组\n     *\n     * 添加后，请求参数、响应结果不会记录该参数\n     */\n    String[] sanitizeKeys() default {};\n\n    // ========== 模块字段 ==========\n\n    /**\n     * 操作模块\n     *\n     * 为空时，会尝试读取 {@link io.swagger.v3.oas.annotations.tags.Tag#name()} 属性\n     */\n    String operateModule() default \"\";\n    /**\n     * 操作名\n     *\n     * 为空时，会尝试读取 {@link io.swagger.v3.oas.annotations.Operation#summary()} 属性\n     */\n    String operateName() default \"\";\n    /**\n     * 操作分类\n     *\n     * 实际并不是数组，因为枚举不能设置 null 作为默认值\n     */\n    OperateTypeEnum[] operateType() default {};\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/enums/OperateTypeEnum.java",
    "content": "package cn.iocoder.yudao.framework.apilog.core.enums;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 操作日志的操作类型\n *\n * @author ruoyi\n */\n@Getter\n@AllArgsConstructor\npublic enum OperateTypeEnum {\n\n    /**\n     * 查询\n     */\n    GET(1),\n    /**\n     * 新增\n     */\n    CREATE(2),\n    /**\n     * 修改\n     */\n    UPDATE(3),\n    /**\n     * 删除\n     */\n    DELETE(4),\n    /**\n     * 导出\n     */\n    EXPORT(5),\n    /**\n     * 导入\n     */\n    IMPORT(6),\n    /**\n     * 其它\n     *\n     * 在无法归类时，可以选择使用其它。因为还有操作名可以进一步标识\n     */\n    OTHER(0);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java",
    "content": "package cn.iocoder.yudao.framework.apilog.core.filter;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.exceptions.ExceptionUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.ApiAccessLogCommonApi;\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.method.HandlerMethod;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.time.LocalDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Iterator;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor.ATTRIBUTE_HANDLER_METHOD;\nimport static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;\n\n/**\n * API 访问日志 Filter\n *\n * 目的：记录 API 访问日志到数据库中\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class ApiAccessLogFilter extends ApiRequestFilter {\n\n    private static final String[] SANITIZE_KEYS = new String[]{\"password\", \"token\", \"accessToken\", \"refreshToken\"};\n\n    private final String applicationName;\n\n    private final ApiAccessLogCommonApi apiAccessLogApi;\n\n    public ApiAccessLogFilter(WebProperties webProperties, String applicationName, ApiAccessLogCommonApi apiAccessLogApi) {\n        super(webProperties);\n        this.applicationName = applicationName;\n        this.apiAccessLogApi = apiAccessLogApi;\n    }\n\n    @Override\n    @SuppressWarnings(\"NullableProblems\")\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n            throws ServletException, IOException {\n        // 获得开始时间\n        LocalDateTime beginTime = LocalDateTime.now();\n        // 提前获得参数，避免 XssFilter 过滤处理\n        Map<String, String> queryString = ServletUtils.getParamMap(request);\n        String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;\n\n        try {\n            // 继续过滤器\n            filterChain.doFilter(request, response);\n            // 正常执行，记录日志\n            createApiAccessLog(request, beginTime, queryString, requestBody, null);\n        } catch (Exception ex) {\n            // 异常执行，记录日志\n            createApiAccessLog(request, beginTime, queryString, requestBody, ex);\n            throw ex;\n        }\n    }\n\n    private void createApiAccessLog(HttpServletRequest request, LocalDateTime beginTime,\n                                    Map<String, String> queryString, String requestBody, Exception ex) {\n        ApiAccessLogCreateReqDTO accessLog = new ApiAccessLogCreateReqDTO();\n        try {\n            boolean enable = buildApiAccessLog(accessLog, request, beginTime, queryString, requestBody, ex);\n            if (!enable) {\n                return;\n            }\n            apiAccessLogApi.createApiAccessLogAsync(accessLog);\n        } catch (Throwable th) {\n            log.error(\"[createApiAccessLog][url({}) log({}) 发生异常]\", request.getRequestURI(), toJsonString(accessLog), th);\n        }\n    }\n\n    private boolean buildApiAccessLog(ApiAccessLogCreateReqDTO accessLog, HttpServletRequest request, LocalDateTime beginTime,\n                                      Map<String, String> queryString, String requestBody, Exception ex) {\n        // 判断：是否要记录操作日志\n        HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(ATTRIBUTE_HANDLER_METHOD);\n        ApiAccessLog accessLogAnnotation = null;\n        if (handlerMethod != null) {\n            accessLogAnnotation = handlerMethod.getMethodAnnotation(ApiAccessLog.class);\n            if (accessLogAnnotation != null && BooleanUtil.isFalse(accessLogAnnotation.enable())) {\n                return false;\n            }\n        }\n\n        // 处理用户信息\n        accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request))\n                .setUserType(WebFrameworkUtils.getLoginUserType(request));\n        // 设置访问结果\n        CommonResult<?> result = WebFrameworkUtils.getCommonResult(request);\n        if (result != null) {\n            accessLog.setResultCode(result.getCode()).setResultMsg(result.getMsg());\n        } else if (ex != null) {\n            accessLog.setResultCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode())\n                    .setResultMsg(ExceptionUtil.getRootCauseMessage(ex));\n        } else {\n            accessLog.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()).setResultMsg(\"\");\n        }\n        // 设置请求字段\n        accessLog.setTraceId(TracerUtils.getTraceId()).setApplicationName(applicationName)\n                .setRequestUrl(request.getRequestURI()).setRequestMethod(request.getMethod())\n                .setUserAgent(ServletUtils.getUserAgent(request)).setUserIp(ServletUtils.getClientIP(request));\n        String[] sanitizeKeys = accessLogAnnotation != null ? accessLogAnnotation.sanitizeKeys() : null;\n        Boolean requestEnable = accessLogAnnotation != null ? accessLogAnnotation.requestEnable() : Boolean.TRUE;\n        if (!BooleanUtil.isFalse(requestEnable)) { // 默认记录，所以判断 !false\n            Map<String, Object> requestParams = MapUtil.<String, Object>builder()\n                    .put(\"query\", sanitizeMap(queryString, sanitizeKeys))\n                    .put(\"body\", sanitizeJson(requestBody, sanitizeKeys)).build();\n            accessLog.setRequestParams(toJsonString(requestParams));\n        }\n        Boolean responseEnable = accessLogAnnotation != null ? accessLogAnnotation.responseEnable() : Boolean.FALSE;\n        if (BooleanUtil.isTrue(responseEnable)) { // 默认不记录，默认强制要求 true\n            accessLog.setResponseBody(sanitizeJson(result, sanitizeKeys));\n        }\n        // 持续时间\n        accessLog.setBeginTime(beginTime).setEndTime(LocalDateTime.now())\n                .setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.MILLIS));\n\n        // 操作模块\n        if (handlerMethod != null) {\n            Tag tagAnnotation = handlerMethod.getBeanType().getAnnotation(Tag.class);\n            Operation operationAnnotation = handlerMethod.getMethodAnnotation(Operation.class);\n            String operateModule = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateModule()) ?\n                    accessLogAnnotation.operateModule() :\n                    tagAnnotation != null ? StrUtil.nullToDefault(tagAnnotation.name(), tagAnnotation.description()) : null;\n            String operateName = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateName()) ?\n                    accessLogAnnotation.operateName() :\n                    operationAnnotation != null ? operationAnnotation.summary() : null;\n            OperateTypeEnum operateType = accessLogAnnotation != null && accessLogAnnotation.operateType().length > 0 ?\n                    accessLogAnnotation.operateType()[0] : parseOperateLogType(request);\n            accessLog.setOperateModule(operateModule).setOperateName(operateName).setOperateType(operateType.getType());\n        }\n        return true;\n    }\n\n    // ========== 解析 @ApiAccessLog、@Swagger 注解  ==========\n\n    private static OperateTypeEnum parseOperateLogType(HttpServletRequest request) {\n        RequestMethod requestMethod = ArrayUtil.firstMatch(method ->\n                StrUtil.equalsAnyIgnoreCase(method.name(), request.getMethod()), RequestMethod.values());\n        if (requestMethod == null) {\n            return OperateTypeEnum.OTHER;\n        }\n        switch (requestMethod) {\n            case GET:\n                return OperateTypeEnum.GET;\n            case POST:\n                return OperateTypeEnum.CREATE;\n            case PUT:\n                return OperateTypeEnum.UPDATE;\n            case DELETE:\n                return OperateTypeEnum.DELETE;\n            default:\n                return OperateTypeEnum.OTHER;\n        }\n    }\n\n    // ========== 请求和响应的脱敏逻辑，移除类似 password、token 等敏感字段 ==========\n\n    private static String sanitizeMap(Map<String, ?> map, String[] sanitizeKeys) {\n        if (CollUtil.isEmpty(map)) {\n            return null;\n        }\n        if (sanitizeKeys != null) {\n            MapUtil.removeAny(map, sanitizeKeys);\n        }\n        MapUtil.removeAny(map, SANITIZE_KEYS);\n        return JsonUtils.toJsonString(map);\n    }\n\n    private static String sanitizeJson(String jsonString, String[] sanitizeKeys) {\n        if (StrUtil.isEmpty(jsonString)) {\n            return null;\n        }\n        try {\n            JsonNode rootNode = JsonUtils.parseTree(jsonString);\n            sanitizeJson(rootNode, sanitizeKeys);\n            return JsonUtils.toJsonString(rootNode);\n        } catch (Exception e) {\n            // 脱敏失败的情况下，直接忽略异常，避免影响用户请求\n            log.error(\"[sanitizeJson][脱敏({}) 发生异常]\", jsonString, e);\n            return jsonString;\n        }\n    }\n\n    private static String sanitizeJson(CommonResult<?> commonResult, String[] sanitizeKeys) {\n        if (commonResult == null) {\n            return null;\n        }\n        String jsonString = toJsonString(commonResult);\n        try {\n            JsonNode rootNode = JsonUtils.parseTree(jsonString);\n            sanitizeJson(rootNode.get(\"data\"), sanitizeKeys); // 只处理 data 字段，不处理 code、msg 字段，避免错误被脱敏掉\n            return JsonUtils.toJsonString(rootNode);\n        } catch (Exception e) {\n            // 脱敏失败的情况下，直接忽略异常，避免影响用户请求\n            log.error(\"[sanitizeJson][脱敏({}) 发生异常]\", jsonString, e);\n            return jsonString;\n        }\n    }\n\n    private static void sanitizeJson(JsonNode node, String[] sanitizeKeys) {\n        // 情况一：数组，遍历处理\n        if (node.isArray()) {\n            for (JsonNode childNode : node) {\n                sanitizeJson(childNode, sanitizeKeys);\n            }\n            return;\n        }\n        // 情况二：非 Object，只是某个值，直接返回\n        if (!node.isObject()) {\n            return;\n        }\n        //  情况三：Object，遍历处理\n        Iterator<Map.Entry<String, JsonNode>> iterator = node.fields();\n        while (iterator.hasNext()) {\n            Map.Entry<String, JsonNode> entry = iterator.next();\n            if (ArrayUtil.contains(sanitizeKeys, entry.getKey())\n                    || ArrayUtil.contains(SANITIZE_KEYS, entry.getKey())) {\n                iterator.remove();\n                continue;\n            }\n            sanitizeJson(entry.getValue(), sanitizeKeys);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.apilog.core.interceptor;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.util.StopWatch;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.HandlerInterceptor;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.stream.IntStream;\n\n/**\n * API 访问日志 Interceptor\n *\n * 目的：在非 prod 环境时，打印 request 和 response 两条日志到日志文件（控制台）中。\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class ApiAccessLogInterceptor implements HandlerInterceptor {\n\n    public static final String ATTRIBUTE_HANDLER_METHOD = \"HANDLER_METHOD\";\n\n    private static final String ATTRIBUTE_STOP_WATCH = \"ApiAccessLogInterceptor.StopWatch\";\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n        // 记录 HandlerMethod，提供给 ApiAccessLogFilter 使用\n        HandlerMethod handlerMethod = handler instanceof HandlerMethod ? (HandlerMethod) handler : null;\n        if (handlerMethod != null) {\n            request.setAttribute(ATTRIBUTE_HANDLER_METHOD, handlerMethod);\n        }\n\n        // 打印 request 日志\n        if (!SpringUtils.isProd()) {\n            Map<String, String> queryString = ServletUtils.getParamMap(request);\n            String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;\n            if (CollUtil.isEmpty(queryString) && StrUtil.isEmpty(requestBody)) {\n                log.info(\"[preHandle][开始请求 URL({}) 无参数]\", request.getRequestURI());\n            } else {\n                log.info(\"[preHandle][开始请求 URL({}) 参数({})]\", request.getRequestURI(),\n                        StrUtil.blankToDefault(requestBody, queryString.toString()));\n            }\n            // 计时\n            StopWatch stopWatch = new StopWatch();\n            stopWatch.start();\n            request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch);\n            // 打印 Controller 路径\n            printHandlerMethodPosition(handlerMethod);\n        }\n        return true;\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {\n        // 打印 response 日志\n        if (!SpringUtils.isProd()) {\n            StopWatch stopWatch = (StopWatch) request.getAttribute(ATTRIBUTE_STOP_WATCH);\n            stopWatch.stop();\n            log.info(\"[afterCompletion][完成请求 URL({}) 耗时({} ms)]\",\n                    request.getRequestURI(), stopWatch.getTotalTimeMillis());\n        }\n    }\n\n    /**\n     * 打印 Controller 方法路径\n     */\n    private void printHandlerMethodPosition(HandlerMethod handlerMethod) {\n        if (handlerMethod == null) {\n            return;\n        }\n        Method method = handlerMethod.getMethod();\n        Class<?> clazz = method.getDeclaringClass();\n        try {\n            // 获取 method 的 lineNumber\n            List<String> clazzContents = FileUtil.readUtf8Lines(\n                    ResourceUtil.getResource(null, clazz).getPath().replace(\"/target/classes/\", \"/src/main/java/\")\n                            + clazz.getSimpleName() + \".java\");\n            Optional<Integer> lineNumber = IntStream.range(0, clazzContents.size())\n                    .filter(i -> clazzContents.get(i).contains(\" \" + method.getName() + \"(\")) // 简单匹配，不考虑方法重名\n                    .mapToObj(i -> i + 1) // 行号从 1 开始\n                    .findFirst();\n            if (!lineNumber.isPresent()) {\n                return;\n            }\n            // 打印结果\n            System.out.printf(\"\\tController 方法路径：%s(%s.java:%d)\\n\", clazz.getName(), clazz.getSimpleName(), lineNumber.get());\n        } catch (Exception ignore) {\n            // 忽略异常。原因：仅仅打印，非重要逻辑\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/package-info.java",
    "content": "/**\n * API 日志：包含两类\n * 1. API 访问日志：记录用户访问 API 的访问日志，定期归档历史日志。\n * 2. 异常日志：记录用户访问 API 的系统异常，方便日常排查问题与告警。\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.framework.apilog;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/config/YudaoBannerAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.banner.config;\n\nimport cn.iocoder.yudao.framework.banner.core.BannerApplicationRunner;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.context.annotation.Bean;\n\n/**\n * Banner 的自动配置类\n *\n * @author 芋道源码\n */\n@AutoConfiguration\npublic class YudaoBannerAutoConfiguration {\n\n    @Bean\n    public BannerApplicationRunner bannerApplicationRunner() {\n        return new BannerApplicationRunner();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java",
    "content": "package cn.iocoder.yudao.framework.banner.core;\n\nimport cn.hutool.core.thread.ThreadUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 项目启动成功后，提供文档相关的地址\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class BannerApplicationRunner implements ApplicationRunner {\n\n    @Override\n    public void run(ApplicationArguments args) {\n        ThreadUtil.execute(() -> {\n            ThreadUtil.sleep(1, TimeUnit.SECONDS); // 延迟 1 秒，保证输出到结尾\n            log.info(\"\\n----------------------------------------------------------\\n\\t\" +\n                            \"项目启动成功！\\n\\t\" +\n                            \"接口文档: \\t{} \\n\\t\" +\n                            \"开发文档: \\t{} \\n\\t\" +\n                            \"视频教程: \\t{} \\n\" +\n                            \"----------------------------------------------------------\",\n                    \"https://cloud.iocoder.cn/api-doc/\",\n                    \"https://cloud.iocoder.cn\",\n                    \"https://t.zsxq.com/02Yf6M7Qn\");\n\n            // 数据报表\n            System.out.println(\"[报表模块 yudao-module-report 教程][参考 https://cloud.iocoder.cn/report/ 开启]\");\n            // 工作流\n            System.out.println(\"[工作流模块 yudao-module-bpm 教程][参考 https://cloud.iocoder.cn/bpm/ 开启]\");\n            // 商城系统\n            System.out.println(\"[商城系统 yudao-module-mall 教程][参考 https://cloud.iocoder.cn/mall/build/ 开启]\");\n            // ERP 系统\n            System.out.println(\"[ERP 系统 yudao-module-erp - 教程][参考 https://cloud.iocoder.cn/erp/build/ 开启]\");\n            // CRM 系统\n            System.out.println(\"[CRM 系统 yudao-module-crm - 教程][参考 https://cloud.iocoder.cn/crm/build/ 开启]\");\n            // 微信公众号\n            System.out.println(\"[微信公众号 yudao-module-mp 教程][参考 https://cloud.iocoder.cn/mp/build/ 开启]\");\n            // 支付平台\n            System.out.println(\"[支付系统 yudao-module-pay - 教程][参考 https://cloud.iocoder.cn/pay/build/ 开启]\");\n            // AI 大模型\n            System.out.println(\"[AI 大模型 yudao-module-ai - 教程][参考 https://cloud.iocoder.cn/ai/build/ 开启]\");\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/package-info.java",
    "content": "/**\n * Banner 用于在 console 控制台，打印开发文档、接口文档等\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.framework.banner;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.base.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;\nimport cn.iocoder.yudao.framework.desensitize.core.base.serializer.StringDesensitizeSerializer;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport java.lang.annotation.*;\n\n/**\n * 顶级脱敏注解，自定义注解需要使用此注解\n *\n * @author gaibu\n */\n@Documented\n@Target(ElementType.ANNOTATION_TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside // 此注解是其他所有 jackson 注解的元注解，打上了此注解的注解表明是 jackson 注解的一部分\n@JsonSerialize(using = StringDesensitizeSerializer.class) // 指定序列化器\npublic @interface DesensitizeBy {\n\n    /**\n     * 脱敏处理器\n     */\n    @SuppressWarnings(\"rawtypes\")\n    Class<? extends DesensitizationHandler> handler();\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/handler/DesensitizationHandler.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.base.handler;\n\nimport cn.hutool.core.util.ReflectUtil;\n\nimport java.lang.annotation.Annotation;\n\n/**\n * 脱敏处理器接口\n *\n * @author gaibu\n */\npublic interface DesensitizationHandler<T extends Annotation> {\n\n    /**\n     * 脱敏\n     *\n     * @param origin     原始字符串\n     * @param annotation 注解信息\n     * @return 脱敏后的字符串\n     */\n    String desensitize(String origin, T annotation);\n\n    /**\n     * 是否禁用脱敏的 Spring EL 表达式\n     *\n     * 如果返回 true 则跳过脱敏\n     *\n     * @param annotation 注解信息\n     * @return 是否禁用脱敏的 Spring EL 表达式\n     */\n    default String getDisable(T annotation) {\n        // 约定：默认就是 enable() 属性。如果不符合，子类重写\n        try {\n            return (String) ReflectUtil.invoke(annotation, \"disable\");\n        } catch (Exception ex) {\n            return \"\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.base.serializer;\n\nimport cn.hutool.core.annotation.AnnotationUtil;\nimport cn.hutool.core.lang.Singleton;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.BeanProperty;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.ContextualSerializer;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.io.IOException;\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\n\n/**\n * 脱敏序列化器\n *\n * 实现 JSON 返回数据时，使用 {@link DesensitizationHandler} 对声明脱敏注解的字段，进行脱敏处理。\n *\n * @author gaibu\n */\n@SuppressWarnings(\"rawtypes\")\npublic class StringDesensitizeSerializer extends StdSerializer<String> implements ContextualSerializer {\n\n    @Getter\n    @Setter\n    private DesensitizationHandler desensitizationHandler;\n\n    protected StringDesensitizeSerializer() {\n        super(String.class);\n    }\n\n    @Override\n    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) {\n        DesensitizeBy annotation = beanProperty.getAnnotation(DesensitizeBy.class);\n        if (annotation == null) {\n            return this;\n        }\n        // 创建一个 StringDesensitizeSerializer 对象，使用 DesensitizeBy 对应的处理器\n        StringDesensitizeSerializer serializer = new StringDesensitizeSerializer();\n        serializer.setDesensitizationHandler(Singleton.get(annotation.handler()));\n        return serializer;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void serialize(String value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {\n        if (StrUtil.isBlank(value)) {\n            gen.writeNull();\n            return;\n        }\n        // 获取序列化字段\n        Field field = getField(gen);\n\n        // 自定义处理器\n        DesensitizeBy[] annotations = AnnotationUtil.getCombinationAnnotations(field, DesensitizeBy.class);\n        if (ArrayUtil.isEmpty(annotations)) {\n            gen.writeString(value);\n            return;\n        }\n        for (Annotation annotation : field.getAnnotations()) {\n            if (AnnotationUtil.hasAnnotation(annotation.annotationType(), DesensitizeBy.class)) {\n                value = this.desensitizationHandler.desensitize(value, annotation);\n                gen.writeString(value);\n                return;\n            }\n        }\n        gen.writeString(value);\n    }\n\n    /**\n     * 获取字段\n     *\n     * @param generator JsonGenerator\n     * @return 字段\n     */\n    private Field getField(JsonGenerator generator) {\n        String currentName = generator.getOutputContext().getCurrentName();\n        Object currentValue = generator.currentValue();\n        Class<?> currentValueClass = currentValue.getClass();\n        return ReflectUtil.getField(currentValueClass, currentName);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/EmailDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.regex.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.regex.handler.EmailDesensitizationHandler;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 邮箱脱敏注解\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = EmailDesensitizationHandler.class)\npublic @interface EmailDesensitize {\n\n    /**\n     * 匹配的正则表达式\n     */\n    String regex() default \"(^.)[^@]*(@.*$)\";\n\n    /**\n     * 替换规则，邮箱;\n     *\n     * 比如：example@gmail.com 脱敏之后为 e****@gmail.com\n     */\n    String replacer() default \"$1****$2\";\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/RegexDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.regex.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.regex.handler.DefaultRegexDesensitizationHandler;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 正则脱敏注解\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = DefaultRegexDesensitizationHandler.class)\npublic @interface RegexDesensitize {\n\n    /**\n     * 匹配的正则表达式（默认匹配所有）\n     */\n    String regex() default \"^[\\\\s\\\\S]*$\";\n\n    /**\n     * 替换规则，会将匹配到的字符串全部替换成 replacer\n     *\n     * 例如：regex=123; replacer=******\n     * 原始字符串 123456789\n     * 脱敏后字符串 ******456789\n     */\n    String replacer() default \"******\";\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.regex.handler;\n\nimport cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;\nimport cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;\n\nimport java.lang.annotation.Annotation;\n\n/**\n * 正则表达式脱敏处理器抽象类，已实现通用的方法\n *\n * @author gaibu\n */\npublic abstract class AbstractRegexDesensitizationHandler<T extends Annotation>\n        implements DesensitizationHandler<T> {\n\n    @Override\n    public String desensitize(String origin, T annotation) {\n        // 1. 判断是否禁用脱敏\n        Object disable = SpringExpressionUtils.parseExpression(getDisable(annotation));\n        if (Boolean.TRUE.equals(disable)) {\n            return origin;\n        }\n\n        // 2. 执行脱敏\n        String regex = getRegex(annotation);\n        String replacer = getReplacer(annotation);\n        return origin.replaceAll(regex, replacer);\n    }\n\n    /**\n     * 获取注解上的 regex 参数\n     *\n     * @param annotation 注解信息\n     * @return 正则表达式\n     */\n    abstract String getRegex(T annotation);\n\n    /**\n     * 获取注解上的 replacer 参数\n     *\n     * @param annotation 注解信息\n     * @return 待替换的字符串\n     */\n    abstract String getReplacer(T annotation);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.regex.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.regex.annotation.RegexDesensitize;\n\n/**\n * {@link RegexDesensitize} 的正则脱敏处理器\n *\n * @author gaibu\n */\npublic class DefaultRegexDesensitizationHandler extends AbstractRegexDesensitizationHandler<RegexDesensitize> {\n\n    @Override\n    String getRegex(RegexDesensitize annotation) {\n        return annotation.regex();\n    }\n\n    @Override\n    String getReplacer(RegexDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n    @Override\n    public String getDisable(RegexDesensitize annotation) {\n        return annotation.disable();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.regex.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.regex.annotation.EmailDesensitize;\n\n/**\n * {@link EmailDesensitize} 的脱敏处理器\n *\n * @author gaibu\n */\npublic class EmailDesensitizationHandler extends AbstractRegexDesensitizationHandler<EmailDesensitize> {\n\n    @Override\n    String getRegex(EmailDesensitize annotation) {\n        return annotation.regex();\n    }\n\n    @Override\n    String getReplacer(EmailDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/BankCardDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.handler.BankCardDesensitization;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 银行卡号\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = BankCardDesensitization.class)\npublic @interface BankCardDesensitize {\n\n    /**\n     * 前缀保留长度\n     */\n    int prefixKeep() default 6;\n\n    /**\n     * 后缀保留长度\n     */\n    int suffixKeep() default 2;\n\n    /**\n     * 替换规则，银行卡号; 比如：9988002866797031 脱敏之后为 998800********31\n     */\n    String replacer() default \"*\";\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.handler.CarLicenseDesensitization;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 车牌号\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = CarLicenseDesensitization.class)\npublic @interface CarLicenseDesensitize {\n\n    /**\n     * 前缀保留长度\n     */\n    int prefixKeep() default 3;\n\n    /**\n     * 后缀保留长度\n     */\n    int suffixKeep() default 1;\n\n    /**\n     * 替换规则，车牌号;比如：粤A66666 脱敏之后为粤A6***6\n     */\n    String replacer() default \"*\";\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.handler.ChineseNameDesensitization;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 中文名\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = ChineseNameDesensitization.class)\npublic @interface ChineseNameDesensitize {\n\n    /**\n     * 前缀保留长度\n     */\n    int prefixKeep() default 1;\n\n    /**\n     * 后缀保留长度\n     */\n    int suffixKeep() default 0;\n\n    /**\n     * 替换规则，中文名;比如：刘子豪脱敏之后为刘**\n     */\n    String replacer() default \"*\";\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.handler.FixedPhoneDesensitization;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 固定电话\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = FixedPhoneDesensitization.class)\npublic @interface FixedPhoneDesensitize {\n\n    /**\n     * 前缀保留长度\n     */\n    int prefixKeep() default 4;\n\n    /**\n     * 后缀保留长度\n     */\n    int suffixKeep() default 2;\n\n    /**\n     * 替换规则，固定电话;比如：01086551122 脱敏之后为 0108*****22\n     */\n    String replacer() default \"*\";\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/IdCardDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.handler.IdCardDesensitization;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 身份证\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = IdCardDesensitization.class)\npublic @interface IdCardDesensitize {\n\n    /**\n     * 前缀保留长度\n     */\n    int prefixKeep() default 6;\n\n    /**\n     * 后缀保留长度\n     */\n    int suffixKeep() default 2;\n\n    /**\n     * 替换规则，身份证号码;比如：530321199204074611 脱敏之后为 530321**********11\n     */\n    String replacer() default \"*\";\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/MobileDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.handler.MobileDesensitization;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 手机号\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = MobileDesensitization.class)\npublic @interface MobileDesensitize {\n\n    /**\n     * 前缀保留长度\n     */\n    int prefixKeep() default 3;\n\n    /**\n     * 后缀保留长度\n     */\n    int suffixKeep() default 4;\n\n    /**\n     * 替换规则，手机号;比如：13248765917 脱敏之后为 132****5917\n     */\n    String replacer() default \"*\";\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/PasswordDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.handler.PasswordDesensitization;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 密码\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = PasswordDesensitization.class)\npublic @interface PasswordDesensitize {\n\n    /**\n     * 前缀保留长度\n     */\n    int prefixKeep() default 0;\n\n    /**\n     * 后缀保留长度\n     */\n    int suffixKeep() default 0;\n\n    /**\n     * 替换规则，密码;\n     *\n     * 比如：123456 脱敏之后为 ******\n     */\n    String replacer() default \"*\";\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/SliderDesensitize.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.handler.DefaultDesensitizationHandler;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.*;\n\n/**\n * 滑动脱敏注解\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = DefaultDesensitizationHandler.class)\npublic @interface SliderDesensitize {\n\n    /**\n     * 后缀保留长度\n     */\n    int suffixKeep() default 0;\n\n    /**\n     * 替换规则，会将前缀后缀保留后，全部替换成 replacer\n     *\n     * 例如：prefixKeep = 1; suffixKeep = 2; replacer = \"*\";\n     * 原始字符串  123456\n     * 脱敏后     1***56\n     */\n    String replacer() default \"*\";\n\n    /**\n     * 前缀保留长度\n     */\n    int prefixKeep() default 0;\n\n    /**\n     * 是否禁用脱敏\n     *\n     * 支持 Spring EL 表达式，如果返回 true 则跳过脱敏\n     */\n    String disable() default \"\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.handler;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;\nimport cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;\n\nimport java.lang.annotation.Annotation;\n\n/**\n * 滑动脱敏处理器抽象类，已实现通用的方法\n *\n * @author gaibu\n */\npublic abstract class AbstractSliderDesensitizationHandler<T extends Annotation>\n        implements DesensitizationHandler<T> {\n\n    @Override\n    public String desensitize(String origin, T annotation) {\n        // 1. 判断是否禁用脱敏\n        Object disable = SpringExpressionUtils.parseExpression(getDisable(annotation));\n        if (Boolean.TRUE.equals(disable)) {\n            return origin;\n        }\n\n        // 2. 执行脱敏\n        int prefixKeep = getPrefixKeep(annotation);\n        int suffixKeep = getSuffixKeep(annotation);\n        String replacer = getReplacer(annotation);\n        int length = origin.length();\n        int interval = length - prefixKeep - suffixKeep;\n\n        // 情况一：原始字符串长度小于等于前后缀保留字符串长度，则原始字符串全部替换\n        if (interval <= 0) {\n            return buildReplacerByLength(replacer, length);\n        }\n\n        // 情况二：原始字符串长度大于前后缀保留字符串长度，则替换中间字符串\n        return origin.substring(0, prefixKeep) +\n                buildReplacerByLength(replacer, interval) +\n                origin.substring(prefixKeep + interval);\n    }\n\n    /**\n     * 根据长度循环构建替换符\n     *\n     * @param replacer 替换符\n     * @param length   长度\n     * @return 构建后的替换符\n     */\n    private String buildReplacerByLength(String replacer, int length) {\n        return StrUtil.repeat(replacer, length);\n    }\n\n    /**\n     * 前缀保留长度\n     *\n     * @param annotation 注解信息\n     * @return 前缀保留长度\n     */\n    abstract Integer getPrefixKeep(T annotation);\n\n    /**\n     * 后缀保留长度\n     *\n     * @param annotation 注解信息\n     * @return 后缀保留长度\n     */\n    abstract Integer getSuffixKeep(T annotation);\n\n    /**\n     * 替换符\n     *\n     * @param annotation 注解信息\n     * @return 替换符\n     */\n    abstract String getReplacer(T annotation);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/BankCardDesensitization.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.BankCardDesensitize;\n\n/**\n * {@link BankCardDesensitize} 的脱敏处理器\n *\n * @author gaibu\n */\npublic class BankCardDesensitization extends AbstractSliderDesensitizationHandler<BankCardDesensitize> {\n\n    @Override\n    Integer getPrefixKeep(BankCardDesensitize annotation) {\n        return annotation.prefixKeep();\n    }\n\n    @Override\n    Integer getSuffixKeep(BankCardDesensitize annotation) {\n        return annotation.suffixKeep();\n    }\n\n    @Override\n    String getReplacer(BankCardDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n    @Override\n    public String getDisable(BankCardDesensitize annotation) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.CarLicenseDesensitize;\n\n/**\n * {@link CarLicenseDesensitize} 的脱敏处理器\n *\n * @author gaibu\n */\npublic class CarLicenseDesensitization extends AbstractSliderDesensitizationHandler<CarLicenseDesensitize> {\n\n    @Override\n    Integer getPrefixKeep(CarLicenseDesensitize annotation) {\n        return annotation.prefixKeep();\n    }\n\n    @Override\n    Integer getSuffixKeep(CarLicenseDesensitize annotation) {\n        return annotation.suffixKeep();\n    }\n\n    @Override\n    String getReplacer(CarLicenseDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n    @Override\n    public String getDisable(CarLicenseDesensitize annotation) {\n        return annotation.disable();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.ChineseNameDesensitize;\n\n/**\n * {@link ChineseNameDesensitize} 的脱敏处理器\n *\n * @author gaibu\n */\npublic class ChineseNameDesensitization extends AbstractSliderDesensitizationHandler<ChineseNameDesensitize> {\n\n    @Override\n    Integer getPrefixKeep(ChineseNameDesensitize annotation) {\n        return annotation.prefixKeep();\n    }\n\n    @Override\n    Integer getSuffixKeep(ChineseNameDesensitize annotation) {\n        return annotation.suffixKeep();\n    }\n\n    @Override\n    String getReplacer(ChineseNameDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.SliderDesensitize;\n\n/**\n * {@link SliderDesensitize} 的脱敏处理器\n *\n * @author gaibu\n */\npublic class DefaultDesensitizationHandler extends AbstractSliderDesensitizationHandler<SliderDesensitize> {\n\n    @Override\n    Integer getPrefixKeep(SliderDesensitize annotation) {\n        return annotation.prefixKeep();\n    }\n\n    @Override\n    Integer getSuffixKeep(SliderDesensitize annotation) {\n        return annotation.suffixKeep();\n    }\n\n    @Override\n    String getReplacer(SliderDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.FixedPhoneDesensitize;\n\n/**\n * {@link FixedPhoneDesensitize} 的脱敏处理器\n *\n * @author gaibu\n */\npublic class FixedPhoneDesensitization extends AbstractSliderDesensitizationHandler<FixedPhoneDesensitize> {\n\n    @Override\n    Integer getPrefixKeep(FixedPhoneDesensitize annotation) {\n        return annotation.prefixKeep();\n    }\n\n    @Override\n    Integer getSuffixKeep(FixedPhoneDesensitize annotation) {\n        return annotation.suffixKeep();\n    }\n\n    @Override\n    String getReplacer(FixedPhoneDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/IdCardDesensitization.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.IdCardDesensitize;\n\n/**\n * {@link IdCardDesensitize} 的脱敏处理器\n *\n * @author gaibu\n */\npublic class IdCardDesensitization extends AbstractSliderDesensitizationHandler<IdCardDesensitize> {\n    @Override\n    Integer getPrefixKeep(IdCardDesensitize annotation) {\n        return annotation.prefixKeep();\n    }\n\n    @Override\n    Integer getSuffixKeep(IdCardDesensitize annotation) {\n        return annotation.suffixKeep();\n    }\n\n    @Override\n    String getReplacer(IdCardDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/MobileDesensitization.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.MobileDesensitize;\n\n/**\n * {@link MobileDesensitize} 的脱敏处理器\n *\n * @author gaibu\n */\npublic class MobileDesensitization extends AbstractSliderDesensitizationHandler<MobileDesensitize> {\n\n    @Override\n    Integer getPrefixKeep(MobileDesensitize annotation) {\n        return annotation.prefixKeep();\n    }\n\n    @Override\n    Integer getSuffixKeep(MobileDesensitize annotation) {\n        return annotation.suffixKeep();\n    }\n\n    @Override\n    String getReplacer(MobileDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/PasswordDesensitization.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.slider.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.PasswordDesensitize;\n\n/**\n * {@link PasswordDesensitize} 的码脱敏处理器\n *\n * @author gaibu\n */\npublic class PasswordDesensitization extends AbstractSliderDesensitizationHandler<PasswordDesensitize> {\n    @Override\n    Integer getPrefixKeep(PasswordDesensitize annotation) {\n        return annotation.prefixKeep();\n    }\n\n    @Override\n    Integer getSuffixKeep(PasswordDesensitize annotation) {\n        return annotation.suffixKeep();\n    }\n\n    @Override\n    String getReplacer(PasswordDesensitize annotation) {\n        return annotation.replacer();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/package-info.java",
    "content": "/**\n * 脱敏组件：支持 JSON 返回数据时，将邮箱、手机等字段进行脱敏\n */\npackage cn.iocoder.yudao.framework.desensitize;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/config/ApiEncryptProperties.java",
    "content": "package cn.iocoder.yudao.framework.encrypt.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * HTTP API 加解密配置\n *\n * @author 芋道源码\n */\n@ConfigurationProperties(prefix = \"yudao.api-encrypt\")\n@Validated\n@Data\npublic class ApiEncryptProperties {\n\n    /**\n     * 是否开启\n     */\n    @NotNull(message = \"是否开启不能为空\")\n    private Boolean enable;\n\n    /**\n     * 请求头（响应头）名称\n     *\n     * 1. 如果该请求头非空，则表示请求参数已被「前端」加密，「后端」需要解密\n     * 2. 如果该响应头非空，则表示响应结果已被「后端」加密，「前端」需要解密\n     */\n    @NotEmpty(message = \"请求头（响应头）名称不能为空\")\n    private String header = \"X-Api-Encrypt\";\n\n    /**\n     * 对称加密算法，用于请求/响应的加解密\n     *\n     * 目前支持\n     * 【对称加密】：\n     *      1. {@link cn.hutool.crypto.symmetric.SymmetricAlgorithm#AES}\n     *      2. {@link cn.hutool.crypto.symmetric.SM4#ALGORITHM_NAME} （需要自己二次开发，成本低）\n     * 【非对称加密】\n     *      1. {@link cn.hutool.crypto.asymmetric.AsymmetricAlgorithm#RSA}\n     *      2. {@link cn.hutool.crypto.asymmetric.SM2} （需要自己二次开发，成本低）\n     *\n     * @see <a href=\"https://help.aliyun.com/zh/ssl-certificate/what-are-a-public-key-and-a-private-key\">什么是公钥和私钥？</a>\n     */\n    @NotEmpty(message = \"对称加密算法不能为空\")\n    private String algorithm;\n\n    /**\n     * 请求的解密密钥\n     *\n     * 注意：\n     * 1. 如果是【对称加密】时，它「后端」对应的是“密钥”。对应的，「前端」也对应的也是“密钥”。\n     * 2. 如果是【非对称加密】时，它「后端」对应的是“私钥”。对应的，「前端」对应的是“公钥”。（重要！！！）\n     */\n    @NotEmpty(message = \"请求的解密密钥不能为空\")\n    private String requestKey;\n\n    /**\n     * 响应的加密密钥\n     *\n     * 注意：\n     * 1. 如果是【对称加密】时，它「后端」对应的是“密钥”。对应的，「前端」也对应的也是“密钥”。\n     * 2. 如果是【非对称加密】时，它「后端」对应的是“公钥”。对应的，「前端」对应的是“私钥”。（重要！！！）\n     */\n    @NotEmpty(message = \"响应的加密密钥不能为空\")\n    private String responseKey;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/config/YudaoApiEncryptAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.encrypt.config;\n\nimport cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;\nimport cn.iocoder.yudao.framework.encrypt.core.filter.ApiEncryptFilter;\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;\n\nimport static cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration.createFilterBean;\n\n@AutoConfiguration\n@Slf4j\n@EnableConfigurationProperties(ApiEncryptProperties.class)\n@ConditionalOnProperty(prefix = \"yudao.api-encrypt\", name = \"enable\", havingValue = \"true\")\npublic class YudaoApiEncryptAutoConfiguration {\n\n    @Bean\n    public FilterRegistrationBean<ApiEncryptFilter> apiEncryptFilter(WebProperties webProperties,\n                                                                     ApiEncryptProperties apiEncryptProperties,\n                                                                     RequestMappingHandlerMapping requestMappingHandlerMapping,\n                                                                     GlobalExceptionHandler globalExceptionHandler) {\n        ApiEncryptFilter filter = new ApiEncryptFilter(webProperties, apiEncryptProperties,\n                requestMappingHandlerMapping, globalExceptionHandler);\n        return createFilterBean(filter, WebFilterOrderEnum.API_ENCRYPT_FILTER);\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/core/annotation/ApiEncrypt.java",
    "content": "package cn.iocoder.yudao.framework.encrypt.core.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * HTTP API 加解密注解\n */\n@Documented\n@Target({ElementType.TYPE, ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface ApiEncrypt {\n\n    /**\n     * 是否对请求参数进行解密，默认 true\n     */\n    boolean request() default true;\n\n    /**\n     * 是否对响应结果进行加密，默认 true\n     */\n    boolean response() default true;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/core/filter/ApiDecryptRequestWrapper.java",
    "content": "package cn.iocoder.yudao.framework.encrypt.core.filter;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.asymmetric.AsymmetricDecryptor;\nimport cn.hutool.crypto.asymmetric.KeyType;\nimport cn.hutool.crypto.symmetric.SymmetricDecryptor;\n\nimport javax.servlet.ReadListener;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\n\n/**\n * 解密请求 {@link HttpServletRequestWrapper} 实现类\n *\n * @author 芋道源码\n */\npublic class ApiDecryptRequestWrapper extends HttpServletRequestWrapper {\n\n    private final byte[] body;\n\n    public ApiDecryptRequestWrapper(HttpServletRequest request,\n                                    SymmetricDecryptor symmetricDecryptor,\n                                    AsymmetricDecryptor asymmetricDecryptor) throws IOException {\n        super(request);\n        // 读取 body，允许 HEX、BASE64 传输\n        String requestBody = StrUtil.utf8Str(\n                IoUtil.readBytes(request.getInputStream(), false));\n\n        // 解密 body\n        body = symmetricDecryptor != null ? symmetricDecryptor.decrypt(requestBody)\n                : asymmetricDecryptor.decrypt(requestBody, KeyType.PrivateKey);\n    }\n\n    @Override\n    public BufferedReader getReader() {\n        return new BufferedReader(new InputStreamReader(this.getInputStream()));\n    }\n\n    @Override\n    public int getContentLength() {\n        return body.length;\n    }\n\n    @Override\n    public long getContentLengthLong() {\n        return body.length;\n    }\n\n    @Override\n    public ServletInputStream getInputStream() {\n        ByteArrayInputStream stream = new ByteArrayInputStream(body);\n        return new ServletInputStream() {\n\n            @Override\n            public int read() {\n                return stream.read();\n            }\n\n            @Override\n            public int available() {\n                return body.length;\n            }\n\n            @Override\n            public boolean isFinished() {\n                return false;\n            }\n\n            @Override\n            public boolean isReady() {\n                return false;\n            }\n\n            @Override\n            public void setReadListener(ReadListener readListener) {\n            }\n\n        };\n    }\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/core/filter/ApiEncryptFilter.java",
    "content": "package cn.iocoder.yudao.framework.encrypt.core.filter;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.crypto.asymmetric.AsymmetricDecryptor;\nimport cn.hutool.crypto.asymmetric.AsymmetricEncryptor;\nimport cn.hutool.crypto.symmetric.SymmetricDecryptor;\nimport cn.hutool.crypto.symmetric.SymmetricEncryptor;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.encrypt.config.ApiEncryptProperties;\nimport cn.iocoder.yudao.framework.encrypt.core.annotation.ApiEncrypt;\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter;\nimport cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.HandlerExecutionChain;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;\nimport org.springframework.web.util.ServletRequestPathUtils;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;\n\n/**\n * API 加密过滤器，处理 {@link ApiEncrypt} 注解。\n *\n * 1. 解密请求参数\n * 2. 加密响应结果\n *\n * 疑问：为什么不使用 SpringMVC 的 RequestBodyAdvice 或 ResponseBodyAdvice 机制呢？\n * 回答：考虑到项目中会记录访问日志、异常日志，以及 HTTP API 签名等场景，最好是全局级、且提前做解析！！！\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class ApiEncryptFilter extends ApiRequestFilter {\n\n    private final ApiEncryptProperties apiEncryptProperties;\n\n    private final RequestMappingHandlerMapping requestMappingHandlerMapping;\n\n    private final GlobalExceptionHandler globalExceptionHandler;\n\n    private final SymmetricDecryptor requestSymmetricDecryptor;\n    private final AsymmetricDecryptor requestAsymmetricDecryptor;\n\n    private final SymmetricEncryptor responseSymmetricEncryptor;\n    private final AsymmetricEncryptor responseAsymmetricEncryptor;\n\n    public ApiEncryptFilter(WebProperties webProperties,\n                            ApiEncryptProperties apiEncryptProperties,\n                            RequestMappingHandlerMapping requestMappingHandlerMapping,\n                            GlobalExceptionHandler globalExceptionHandler) {\n        super(webProperties);\n        this.apiEncryptProperties = apiEncryptProperties;\n        this.requestMappingHandlerMapping = requestMappingHandlerMapping;\n        this.globalExceptionHandler = globalExceptionHandler;\n        if (StrUtil.equalsIgnoreCase(apiEncryptProperties.getAlgorithm(), \"AES\")) {\n            this.requestSymmetricDecryptor = SecureUtil.aes(StrUtil.utf8Bytes(apiEncryptProperties.getRequestKey()));\n            this.requestAsymmetricDecryptor = null;\n            this.responseSymmetricEncryptor = SecureUtil.aes(StrUtil.utf8Bytes(apiEncryptProperties.getResponseKey()));\n            this.responseAsymmetricEncryptor = null;\n        } else if (StrUtil.equalsIgnoreCase(apiEncryptProperties.getAlgorithm(), \"RSA\")) {\n            this.requestSymmetricDecryptor = null;\n            this.requestAsymmetricDecryptor = SecureUtil.rsa(apiEncryptProperties.getRequestKey(), null);\n            this.responseSymmetricEncryptor = null;\n            this.responseAsymmetricEncryptor = SecureUtil.rsa(null, apiEncryptProperties.getResponseKey());\n        } else {\n            // 补充说明：如果要支持 SM2、SM4 等算法，可在此处增加对应实例的创建，并添加相应的 Maven 依赖即可。\n            throw new IllegalArgumentException(\"不支持的加密算法：\" + apiEncryptProperties.getAlgorithm());\n        }\n    }\n\n    @Override\n    @SuppressWarnings(\"NullableProblems\")\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        // 获取 @ApiEncrypt 注解\n        ApiEncrypt apiEncrypt = getApiEncrypt(request);\n        boolean requestEnable = apiEncrypt != null && apiEncrypt.request();\n        boolean responseEnable = apiEncrypt != null && apiEncrypt.response();\n        String encryptHeader = request.getHeader(apiEncryptProperties.getHeader());\n        if (!requestEnable && !responseEnable && StrUtil.isBlank(encryptHeader))  {\n            chain.doFilter(request, response);\n            return;\n        }\n\n        // 1. 解密请求\n        if (ObjectUtils.equalsAny(HttpMethod.valueOf(request.getMethod()),\n                HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE)) {\n            try {\n                if (StrUtil.isNotBlank(encryptHeader)) {\n                    request = new ApiDecryptRequestWrapper(request,\n                            requestSymmetricDecryptor, requestAsymmetricDecryptor);\n                } else if (requestEnable) {\n                    throw invalidParamException(\"请求未包含加密标头，请检查是否正确配置了加密标头\");\n                }\n            } catch (Exception ex) {\n                CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);\n                ServletUtils.writeJSON(response, result);\n                return;\n            }\n        }\n\n        // 2. 执行过滤器链\n        if (responseEnable) {\n            // 特殊：仅包装，最后执行。目的：Response 内容可以被重复读取！！！\n            response = new ApiEncryptResponseWrapper(response);\n        }\n        chain.doFilter(request, response);\n\n        // 3. 加密响应（真正执行）\n        if (responseEnable) {\n            ((ApiEncryptResponseWrapper) response).encrypt(apiEncryptProperties,\n                    responseSymmetricEncryptor, responseAsymmetricEncryptor);\n        }\n    }\n\n    /**\n     * 获取 @ApiEncrypt 注解\n     *\n     * @param request 请求\n     */\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    private ApiEncrypt getApiEncrypt(HttpServletRequest request) {\n        try {\n            // 特殊：兼容 SpringBoot 2.X 版本会报错的问题 https://t.zsxq.com/kqyiB\n            if (!ServletRequestPathUtils.hasParsedRequestPath(request)) {\n                ServletRequestPathUtils.parseAndCache(request);\n            }\n\n            // 解析 @ApiEncrypt 注解\n            HandlerExecutionChain mappingHandler = requestMappingHandlerMapping.getHandler(request);\n            if (mappingHandler == null) {\n                return null;\n            }\n            Object handler = mappingHandler.getHandler();\n            if (handler instanceof HandlerMethod) {\n                HandlerMethod handlerMethod = (HandlerMethod) handler;\n                ApiEncrypt annotation = handlerMethod.getMethodAnnotation(ApiEncrypt.class);\n                if (annotation == null) {\n                    annotation = handlerMethod.getBeanType().getAnnotation(ApiEncrypt.class);\n                }\n                return annotation;\n            }\n        } catch (Exception e) {\n            log.error(\"[getApiEncrypt][url({}/{}) 获取 @ApiEncrypt 注解失败]\",\n                    request.getRequestURI(), request.getMethod(), e);\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/core/filter/ApiEncryptResponseWrapper.java",
    "content": "package cn.iocoder.yudao.framework.encrypt.core.filter;\n\nimport cn.hutool.crypto.asymmetric.AsymmetricEncryptor;\nimport cn.hutool.crypto.asymmetric.KeyType;\nimport cn.hutool.crypto.symmetric.SymmetricEncryptor;\nimport cn.iocoder.yudao.framework.encrypt.config.ApiEncryptProperties;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.WriteListener;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpServletResponseWrapper;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStreamWriter;\nimport java.io.PrintWriter;\n\n/**\n * 加密响应 {@link HttpServletResponseWrapper} 实现类\n *\n * @author 芋道源码\n */\npublic class ApiEncryptResponseWrapper extends HttpServletResponseWrapper {\n\n    private final ByteArrayOutputStream byteArrayOutputStream;\n    private final ServletOutputStream servletOutputStream;\n    private final PrintWriter printWriter;\n\n    public ApiEncryptResponseWrapper(HttpServletResponse response) {\n        super(response);\n        this.byteArrayOutputStream = new ByteArrayOutputStream();\n        this.servletOutputStream = this.getOutputStream();\n        this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));\n    }\n\n    public void encrypt(ApiEncryptProperties properties,\n                        SymmetricEncryptor symmetricEncryptor,\n                        AsymmetricEncryptor asymmetricEncryptor) throws IOException {\n        // 1.1 清空 body\n        HttpServletResponse response = (HttpServletResponse) this.getResponse();\n        response.resetBuffer();\n        // 1.2 获取 body\n        this.flushBuffer();\n        byte[] body = byteArrayOutputStream.toByteArray();\n\n        // 2. 添加加密 header 标识\n        this.addHeader(properties.getHeader(), \"true\");\n        // 特殊：特殊：https://juejin.cn/post/6867327674675625992\n        this.addHeader(\"Access-Control-Expose-Headers\", properties.getHeader());\n\n        // 3.1 加密 body\n        String encryptedBody = symmetricEncryptor != null ? symmetricEncryptor.encryptBase64(body)\n                : asymmetricEncryptor.encryptBase64(body, KeyType.PublicKey);\n        // 3.2 输出加密后的 body：（设置 header 要放在 response 的 write 之前）\n        response.getWriter().write(encryptedBody);\n    }\n\n    @Override\n    public PrintWriter getWriter() {\n        return printWriter;\n    }\n\n    @Override\n    public void flushBuffer() throws IOException {\n        if (servletOutputStream != null) {\n            servletOutputStream.flush();\n        }\n        if (printWriter != null) {\n            printWriter.flush();\n        }\n    }\n\n    @Override\n    public void reset() {\n        byteArrayOutputStream.reset();\n    }\n\n    @Override\n    public ServletOutputStream getOutputStream() {\n        return new ServletOutputStream() {\n\n            @Override\n            public boolean isReady() {\n                return false;\n            }\n\n            @Override\n            public void setWriteListener(WriteListener writeListener) {\n            }\n\n            @Override\n            public void write(int b) {\n                byteArrayOutputStream.write(b);\n            }\n\n            @Override\n            @SuppressWarnings(\"NullableProblems\")\n            public void write(byte[] b) throws IOException {\n                byteArrayOutputStream.write(b);\n            }\n\n            @Override\n            @SuppressWarnings(\"NullableProblems\")\n            public void write(byte[] b, int off, int len) {\n                byteArrayOutputStream.write(b, off, len);\n            }\n\n        };\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/package-info.java",
    "content": "/**\n * HTTP API 加密组件：支持 Request 和 Response 的加密、解密\n */\npackage cn.iocoder.yudao.framework.encrypt;"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.jackson.config;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer;\nimport cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;\nimport cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;\nimport com.fasterxml.jackson.databind.Module;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;\nimport com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;\nimport com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;\nimport com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;\nimport org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;\nimport org.springframework.context.annotation.Bean;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\n\n@AutoConfiguration(after = JacksonAutoConfiguration.class)\n@Slf4j\npublic class YudaoJacksonAutoConfiguration {\n\n    /**\n     * 从 Builder 源头定制（关键：使用 *ByType，避免 handledType 要求）\n     */\n    @Bean\n    public Jackson2ObjectMapperBuilderCustomizer ldtEpochMillisCustomizer() {\n        return builder -> builder\n                // Long -> Number\n                .serializerByType(Long.class, NumberSerializer.INSTANCE)\n                .serializerByType(Long.TYPE, NumberSerializer.INSTANCE)\n                // LocalDate / LocalTime\n                .serializerByType(LocalDate.class, LocalDateSerializer.INSTANCE)\n                .deserializerByType(LocalDate.class, LocalDateDeserializer.INSTANCE)\n                .serializerByType(LocalTime.class, LocalTimeSerializer.INSTANCE)\n                .deserializerByType(LocalTime.class, LocalTimeDeserializer.INSTANCE)\n                // LocalDateTime < - > EpochMillis\n                .serializerByType(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)\n                .deserializerByType(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);\n    }\n\n    /**\n     * 以 Bean 形式暴露 Module（Boot 会自动注册到所有 ObjectMapper）\n     */\n    @Bean\n    public Module timestampSupportModuleBean() {\n        SimpleModule m = new SimpleModule(\"TimestampSupportModule\");\n        // Long -> Number，避免前端精度丢失\n        m.addSerializer(Long.class, NumberSerializer.INSTANCE);\n        m.addSerializer(Long.TYPE, NumberSerializer.INSTANCE);\n        // LocalDate / LocalTime\n        m.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE);\n        m.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);\n        m.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE);\n        m.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE);\n        // LocalDateTime < - > EpochMillis\n        m.addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE);\n        m.addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);\n        return m;\n    }\n\n    /**\n     * 初始化全局 JsonUtils，直接使用主 ObjectMapper\n     */\n    @Bean\n    @SuppressWarnings(\"InstantiationOfUtilityClass\")\n    public JsonUtils jsonUtils(ObjectMapper objectMapper) {\n        JsonUtils.init(objectMapper);\n        log.debug(\"[init][初始化 JsonUtils 成功]\");\n        return new JsonUtils();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/core/package-info.java",
    "content": "package cn.iocoder.yudao.framework.jackson.core;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/package-info.java",
    "content": "/**\n * Web 框架，全局异常、API 日志等\n */\npackage cn.iocoder.yudao.framework;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/Knife4jOpenApiCustomizer.java",
    "content": "package cn.iocoder.yudao.framework.swagger.config;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiSupport;\nimport com.github.xiaoymin.knife4j.core.conf.ExtensionsConstants;\nimport com.github.xiaoymin.knife4j.core.conf.GlobalConstants;\nimport com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties;\nimport com.github.xiaoymin.knife4j.spring.configuration.Knife4jSetting;\nimport com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.models.OpenAPI;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.springdoc.core.SpringDocConfigProperties;\nimport org.springdoc.core.customizers.GlobalOpenApiCustomizer;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.core.type.filter.AnnotationTypeFilter;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.lang.annotation.Annotation;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 增强扩展属性支持\n *\n * 参考 <a href=\"https://github.com/xiaoymin/knife4j/issues/913\">Spring Boot 3.4 以上版本 /v3/api-docs 解决接口报错，依赖修复</a>\n *\n * @since 4.1.0\n * @author <a href=\"xiaoymin@foxmail.com\">xiaoymin@foxmail.com</a>\n * 2022/12/11 22:40\n */\n@Primary\n@Configuration\n@Slf4j\npublic class Knife4jOpenApiCustomizer extends com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer\n        implements GlobalOpenApiCustomizer {\n\n    final Knife4jProperties knife4jProperties;\n    final SpringDocConfigProperties properties;\n\n    public Knife4jOpenApiCustomizer(Knife4jProperties knife4jProperties, SpringDocConfigProperties properties) {\n        super(knife4jProperties,properties);\n        this.knife4jProperties = knife4jProperties;\n        this.properties = properties;\n    }\n\n    @Override\n    public void customise(OpenAPI openApi) {\n        if (knife4jProperties.isEnable()) {\n            Knife4jSetting setting = knife4jProperties.getSetting();\n            OpenApiExtensionResolver openApiExtensionResolver = new OpenApiExtensionResolver(setting, knife4jProperties.getDocuments());\n            // 解析初始化\n            openApiExtensionResolver.start();\n            Map<String, Object> objectMap = new HashMap<>();\n            objectMap.put(GlobalConstants.EXTENSION_OPEN_SETTING_NAME, setting);\n            objectMap.put(GlobalConstants.EXTENSION_OPEN_MARKDOWN_NAME, openApiExtensionResolver.getMarkdownFiles());\n            openApi.addExtension(GlobalConstants.EXTENSION_OPEN_API_NAME, objectMap);\n            addOrderExtension(openApi);\n        }\n    }\n\n    /**\n     * 往 OpenAPI 内 tags 字段添加 x-order 属性\n     *\n     * @param openApi openApi\n     */\n    private void addOrderExtension(OpenAPI openApi) {\n        if (CollectionUtils.isEmpty(properties.getGroupConfigs())) {\n            return;\n        }\n        // 获取包扫描路径\n        Set<String> packagesToScan =\n                properties.getGroupConfigs().stream()\n                        .map(SpringDocConfigProperties.GroupConfig::getPackagesToScan)\n                        .filter(toScan -> !CollectionUtils.isEmpty(toScan))\n                        .flatMap(List::stream)\n                        .collect(Collectors.toSet());\n        if (CollectionUtils.isEmpty(packagesToScan)) {\n            return;\n        }\n        // 扫描包下被 ApiSupport 注解的 RestController Class\n        Set<Class<?>> classes = packagesToScan.stream()\n                .map(packageToScan -> scanPackageByAnnotation(packageToScan, RestController.class))\n                .flatMap(Set::stream)\n                .filter(clazz -> clazz.isAnnotationPresent(ApiSupport.class))\n                .collect(Collectors.toSet());\n        if (!CollectionUtils.isEmpty(classes)) {\n            // ApiSupport oder 值存入 tagSortMap<Tag.name,ApiSupport.order>\n            Map<String, Integer> tagOrderMap = new HashMap<>();\n            classes.forEach(clazz -> {\n                Tag tag = getTag(clazz);\n                if (Objects.nonNull(tag)) {\n                    ApiSupport apiSupport = clazz.getAnnotation(ApiSupport.class);\n                    tagOrderMap.putIfAbsent(tag.name(), apiSupport.order());\n                }\n            });\n            // 往 openApi tags 字段添加 x-order 增强属性\n            if (openApi.getTags() != null) {\n                openApi.getTags().forEach(tag -> {\n                    if (tagOrderMap.containsKey(tag.getName())) {\n                        tag.addExtension(ExtensionsConstants.EXTENSION_ORDER, tagOrderMap.get(tag.getName()));\n                    }\n                });\n            }\n        }\n    }\n\n    private Tag getTag(Class<?> clazz) {\n        // 从类上获取\n        Tag tag = clazz.getAnnotation(Tag.class);\n        if (Objects.isNull(tag)) {\n            // 从接口上获取\n            Class<?>[] interfaces = clazz.getInterfaces();\n            if (ArrayUtils.isNotEmpty(interfaces)) {\n                for (Class<?> interfaceClazz : interfaces) {\n                    Tag anno = interfaceClazz.getAnnotation(Tag.class);\n                    if (Objects.nonNull(anno)) {\n                        tag = anno;\n                        break;\n                    }\n                }\n            }\n        }\n        return tag;\n    }\n\n    private Set<Class<?>> scanPackageByAnnotation(String packageName, final Class<? extends Annotation> annotationClass) {\n        ClassPathScanningCandidateComponentProvider scanner =\n                new ClassPathScanningCandidateComponentProvider(false);\n        scanner.addIncludeFilter(new AnnotationTypeFilter(annotationClass));\n        Set<Class<?>> classes = new HashSet<>();\n        for (BeanDefinition beanDefinition : scanner.findCandidateComponents(packageName)) {\n            try {\n                Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());\n                classes.add(clazz);\n            } catch (ClassNotFoundException ignore) {\n            }\n        }\n        return classes;\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/SwaggerProperties.java",
    "content": "package cn.iocoder.yudao.framework.swagger.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * Swagger 配置属性\n *\n * @author 芋道源码\n */\n@ConfigurationProperties(\"yudao.swagger\")\n@Data\npublic class SwaggerProperties {\n\n    /**\n     * 标题\n     */\n    @NotEmpty(message = \"标题不能为空\")\n    private String title;\n    /**\n     * 描述\n     */\n    @NotEmpty(message = \"描述不能为空\")\n    private String description;\n    /**\n     * 作者\n     */\n    @NotEmpty(message = \"作者不能为空\")\n    private String author;\n    /**\n     * 版本\n     */\n    @NotEmpty(message = \"版本不能为空\")\n    private String version;\n    /**\n     * url\n     */\n    @NotEmpty(message = \"扫描的 package 不能为空\")\n    private String url;\n    /**\n     * email\n     */\n    @NotEmpty(message = \"扫描的 email 不能为空\")\n    private String email;\n\n    /**\n     * license\n     */\n    @NotEmpty(message = \"扫描的 license 不能为空\")\n    private String license;\n\n    /**\n     * license-url\n     */\n    @NotEmpty(message = \"扫描的 license-url 不能为空\")\n    private String licenseUrl;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.swagger.config;\n\nimport com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration;\nimport io.swagger.v3.oas.models.Components;\nimport io.swagger.v3.oas.models.OpenAPI;\nimport io.swagger.v3.oas.models.info.Contact;\nimport io.swagger.v3.oas.models.info.Info;\nimport io.swagger.v3.oas.models.info.License;\nimport io.swagger.v3.oas.models.media.IntegerSchema;\nimport io.swagger.v3.oas.models.media.StringSchema;\nimport io.swagger.v3.oas.models.parameters.Parameter;\nimport io.swagger.v3.oas.models.security.SecurityRequirement;\nimport io.swagger.v3.oas.models.security.SecurityScheme;\nimport org.springdoc.core.*;\nimport org.springdoc.core.customizers.OpenApiBuilderCustomizer;\nimport org.springdoc.core.customizers.OperationCustomizer;\nimport org.springdoc.core.customizers.ServerBaseUrlCustomizer;\nimport org.springdoc.core.providers.JavadocProvider;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.http.HttpHeaders;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * Swagger 自动配置类，基于 OpenAPI + Springdoc 实现。\n *\n * 友情提示：\n * 1. Springdoc 文档地址：<a href=\"https://github.com/springdoc/springdoc-openapi\">仓库</a>\n * 2. Swagger 规范，于 2015 更名为 OpenAPI 规范，本质是一个东西\n *\n * @author 芋道源码\n */\n@AutoConfiguration(before = Knife4jAutoConfiguration.class) // before 原因，保证覆写的 Knife4jOpenApiCustomizer 先生效！相关 https://github.com/YunaiV/ruoyi-vue-pro/issues/954 讨论\n@ConditionalOnClass({OpenAPI.class})\n@EnableConfigurationProperties(SwaggerProperties.class)\n@ConditionalOnProperty(prefix = \"springdoc.api-docs\", name = \"enabled\", havingValue = \"true\", matchIfMissing = true) // 设置为 false 时，禁用\n@Import(Knife4jOpenApiCustomizer.class)\npublic class YudaoSwaggerAutoConfiguration {\n\n    // ========== 全局 OpenAPI 配置 ==========\n\n    @Bean\n    public OpenAPI createApi(SwaggerProperties properties) {\n        Map<String, SecurityScheme> securitySchemas = buildSecuritySchemes();\n        OpenAPI openAPI = new OpenAPI()\n                // 接口信息\n                .info(buildInfo(properties))\n                // 接口安全配置\n                .components(new Components().securitySchemes(securitySchemas))\n                .addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION));\n        securitySchemas.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key)));\n        return openAPI;\n    }\n\n    /**\n     * API 摘要信息\n     */\n    private Info buildInfo(SwaggerProperties properties) {\n        return new Info()\n                .title(properties.getTitle())\n                .description(properties.getDescription())\n                .version(properties.getVersion())\n                .contact(new Contact().name(properties.getAuthor()).url(properties.getUrl()).email(properties.getEmail()))\n                .license(new License().name(properties.getLicense()).url(properties.getLicenseUrl()));\n    }\n\n    /**\n     * 安全模式，这里配置通过请求头 Authorization 传递 token 参数\n     */\n    private Map<String, SecurityScheme> buildSecuritySchemes() {\n        Map<String, SecurityScheme> securitySchemes = new HashMap<>();\n        SecurityScheme securityScheme = new SecurityScheme()\n                .type(SecurityScheme.Type.APIKEY) // 类型\n                .name(HttpHeaders.AUTHORIZATION) // 请求头的 name\n                .in(SecurityScheme.In.HEADER); // token 所在位置\n        securitySchemes.put(HttpHeaders.AUTHORIZATION, securityScheme);\n        return securitySchemes;\n    }\n\n    /**\n     * 自定义 OpenAPI 处理器\n     */\n    @Bean\n    @Primary // 目的：以我们创建的 OpenAPIService Bean 为主，避免一键改包后，启动报错！\n    public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,\n                                         SecurityService securityParser,\n                                         SpringDocConfigProperties springDocConfigProperties,\n                                         PropertyResolverUtils propertyResolverUtils,\n                                         Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,\n                                         Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,\n                                         Optional<JavadocProvider> javadocProvider) {\n        return new OpenAPIService(openAPI, securityParser, springDocConfigProperties,\n                propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);\n    }\n\n    // ========== 分组 OpenAPI 配置 ==========\n\n    /**\n     * 所有模块的 API 分组\n     */\n    @Bean\n    public GroupedOpenApi allGroupedOpenApi() {\n        return buildGroupedOpenApi(\"all\", \"\");\n    }\n\n    public static GroupedOpenApi buildGroupedOpenApi(String group) {\n        return buildGroupedOpenApi(group, group);\n    }\n\n    public static GroupedOpenApi buildGroupedOpenApi(String group, String path) {\n        return GroupedOpenApi.builder()\n                .group(group)\n                .pathsToMatch(\"/admin-api/\" + path + \"/**\", \"/app-api/\" + path + \"/**\")\n                .addOperationCustomizer((operation, handlerMethod) -> operation\n                        .addParametersItem(buildTenantHeaderParameter())\n                        .addParametersItem(buildSecurityHeaderParameter()))\n                .addOperationCustomizer(buildOperationIdCustomizer())\n                .build();\n    }\n\n    /**\n     * 构建 Tenant 租户编号请求头参数\n     *\n     * @return 多租户参数\n     */\n    private static Parameter buildTenantHeaderParameter() {\n        return new Parameter()\n                .name(HEADER_TENANT_ID) // header 名\n                .description(\"租户编号\") // 描述\n                .in(String.valueOf(SecurityScheme.In.HEADER)) // 请求 header\n                .schema(new IntegerSchema()._default(1L).name(HEADER_TENANT_ID).description(\"租户编号\")); // 默认：使用租户编号为 1\n    }\n\n    /**\n     * 构建 Authorization 认证请求头参数\n     *\n     * 解决 Knife4j <a href=\"https://gitee.com/xiaoym/knife4j/issues/I69QBU\">Authorize 未生效，请求header里未包含参数</a>\n     *\n     * @return 认证参数\n     */\n    private static Parameter buildSecurityHeaderParameter() {\n        return new Parameter()\n                .name(HttpHeaders.AUTHORIZATION) // header 名\n                .description(\"认证 Token\") // 描述\n                .in(String.valueOf(SecurityScheme.In.HEADER)) // 请求 header\n                .schema(new StringSchema()._default(\"Bearer test1\").name(HEADER_TENANT_ID).description(\"认证 Token\")); // 默认：使用用户编号为 1\n    }\n\n    /**\n     * 核心：自定义OperationId生成规则，组合「类名前缀 + 方法名」\n     *\n     * @see <a href=\"https://github.com/YunaiV/ruoyi-vue-pro/issues/957\">app-api 前缀不生效，都是使用 admin-api</a>\n     */\n    private static OperationCustomizer buildOperationIdCustomizer() {\n        return (operation, handlerMethod) -> {\n            // 1. 获取控制器类名（如 UserController）\n            String className = handlerMethod.getBeanType().getSimpleName();\n            // 2. 提取类名前缀（去除 Controller 后缀，如 UserController -> User）\n            String classPrefix = className.replaceAll(\"Controller$\", \"\");\n            // 3. 获取方法名（如 list）\n            String methodName = handlerMethod.getMethod().getName();\n            // 4. 组合生成 operationId（如 User_list）\n            String operationId = classPrefix + \"_\" + methodName;\n            // 5. 设置自定义 operationId\n            operation.setOperationId(operationId);\n            return operation;\n        };\n    }\n\n}\n\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/package-info.java",
    "content": "/**\n * 基于 Swagger + Knife4j 实现 API 接口文档\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.framework.swagger;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/WebProperties.java",
    "content": "package cn.iocoder.yudao.framework.web.config;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.servlet.config.annotation.PathMatchConfigurer;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@ConfigurationProperties(prefix = \"yudao.web\")\n@Validated\n@Data\npublic class WebProperties {\n\n    @NotNull(message = \"APP API 不能为空\")\n    private Api appApi = new Api(\"/app-api\", \"**.controller.app.**\");\n    @NotNull(message = \"Admin API 不能为空\")\n    private Api adminApi = new Api(\"/admin-api\", \"**.controller.admin.**\");\n\n    @NotNull(message = \"Admin UI 不能为空\")\n    private Ui adminUi;\n\n    @Data\n    @AllArgsConstructor\n    @NoArgsConstructor\n    @Valid\n    public static class Api {\n\n        /**\n         * API 前缀，实现所有 Controller 提供的 RESTFul API 的统一前缀\n         *\n         *\n         * 意义：通过该前缀，避免 Swagger、Actuator 意外通过 Nginx 暴露出来给外部，带来安全性问题\n         *      这样，Nginx 只需要配置转发到 /api/* 的所有接口即可。\n         *\n         * @see YudaoWebAutoConfiguration#configurePathMatch(PathMatchConfigurer)\n         */\n        @NotEmpty(message = \"API 前缀不能为空\")\n        private String prefix;\n\n        /**\n         * Controller 所在包的 Ant 路径规则\n         *\n         * 主要目的是，给该 Controller 设置指定的 {@link #prefix}\n         */\n        @NotEmpty(message = \"Controller 所在包不能为空\")\n        private String controller;\n\n    }\n\n    @Data\n    @Valid\n    public static class Ui {\n\n        /**\n         * 访问地址\n         */\n        private String url;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.web.config;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi;\nimport cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;\nimport cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter;\nimport cn.iocoder.yudao.framework.web.core.filter.DemoFilter;\nimport cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;\nimport cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport com.google.common.collect.Maps;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;\nimport org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.boot.web.client.RestTemplateBuilder;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.util.AntPathMatcher;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;\n\nimport javax.servlet.Filter;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\n@AutoConfiguration(beforeName = {\n        \"com.fhs.trans.config.TransServiceConfig\" // cloud 独有：避免一键改包后，RestTemplate 初始化的冲突。可见 https://t.zsxq.com/T4yj7 帖子\n})\n@EnableConfigurationProperties(WebProperties.class)\npublic class YudaoWebAutoConfiguration {\n\n    /**\n     * 应用名\n     */\n    @Value(\"${spring.application.name}\")\n    private String applicationName;\n\n    @Bean\n    public WebMvcRegistrations webMvcRegistrations(WebProperties webProperties) {\n        return new WebMvcRegistrations() {\n\n            @Override\n            public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {\n                RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();\n                // 实例化时就带上前缀\n                mapping.setPathPrefixes(buildPathPrefixes(webProperties));\n                return mapping;\n            }\n\n            /**\n             * 构建 prefix → 匹配条件的映射\n             */\n            private Map<String, Predicate<Class<?>>> buildPathPrefixes(WebProperties webProperties) {\n                AntPathMatcher antPathMatcher = new AntPathMatcher(\".\");\n                Map<String, Predicate<Class<?>>> pathPrefixes = Maps.newLinkedHashMapWithExpectedSize(2);\n                putPathPrefix(pathPrefixes, webProperties.getAdminApi(), antPathMatcher);\n                putPathPrefix(pathPrefixes, webProperties.getAppApi(), antPathMatcher);\n                return pathPrefixes;\n            }\n\n            /**\n             * 设置 API 前缀，仅仅匹配 controller 包下的\n             */\n            private void putPathPrefix(Map<String, Predicate<Class<?>>> pathPrefixes, WebProperties.Api api, AntPathMatcher matcher) {\n                if (api == null || StrUtil.isEmpty(api.getPrefix())) {\n                    return;\n                }\n                pathPrefixes.put(api.getPrefix(), // api 前缀\n                        clazz -> clazz.isAnnotationPresent(RestController.class)\n                                && matcher.match(api.getController(), clazz.getPackage().getName()));\n            }\n\n        };\n    }\n\n    @Bean\n    @SuppressWarnings(\"SpringJavaInjectionPointsAutowiringInspection\")\n    public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogCommonApi apiErrorLogApi) {\n        return new GlobalExceptionHandler(applicationName, apiErrorLogApi);\n    }\n\n    @Bean\n    public GlobalResponseBodyHandler globalResponseBodyHandler() {\n        return new GlobalResponseBodyHandler();\n    }\n\n    @Bean\n    @SuppressWarnings(\"InstantiationOfUtilityClass\")\n    public WebFrameworkUtils webFrameworkUtils(WebProperties webProperties) {\n        // 由于 WebFrameworkUtils 需要使用到 webProperties 属性，所以注册为一个 Bean\n        return new WebFrameworkUtils(webProperties);\n    }\n\n    // ========== Filter 相关 ==========\n\n    /**\n     * 创建 CorsFilter Bean，解决跨域问题\n     */\n    @Bean\n    @Order(value = WebFilterOrderEnum.CORS_FILTER) // 特殊：修复因执行顺序影响到跨域配置不生效问题\n    public FilterRegistrationBean<CorsFilter> corsFilterBean() {\n        // 创建 CorsConfiguration 对象\n        CorsConfiguration config = new CorsConfiguration();\n        config.setAllowCredentials(true);\n        config.addAllowedOriginPattern(\"*\"); // 设置访问源地址\n        config.addAllowedHeader(\"*\"); // 设置访问源请求头\n        config.addAllowedMethod(\"*\"); // 设置访问源请求方法\n        // 创建 UrlBasedCorsConfigurationSource 对象\n        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n        source.registerCorsConfiguration(\"/**\", config); // 对接口配置跨域设置\n        return createFilterBean(new CorsFilter(source), WebFilterOrderEnum.CORS_FILTER);\n    }\n\n    /**\n     * 创建 RequestBodyCacheFilter Bean，可重复读取请求内容\n     */\n    @Bean\n    public FilterRegistrationBean<CacheRequestBodyFilter> requestBodyCacheFilter() {\n        return createFilterBean(new CacheRequestBodyFilter(), WebFilterOrderEnum.REQUEST_BODY_CACHE_FILTER);\n    }\n\n    /**\n     * 创建 DemoFilter Bean，演示模式\n     */\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.demo\", havingValue = \"true\")\n    public FilterRegistrationBean<DemoFilter> demoFilter() {\n        return createFilterBean(new DemoFilter(), WebFilterOrderEnum.DEMO_FILTER);\n    }\n\n    public static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {\n        FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);\n        bean.setOrder(order);\n        return bean;\n    }\n\n    /**\n     * 创建 RestTemplate 实例\n     *\n     * @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder}\n     */\n    @Bean\n    @ConditionalOnMissingBean\n    @Primary\n    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {\n        return restTemplateBuilder.build();\n    }\n\n    /**\n     * 创建 RestTemplate 实例（支持负载均衡）\n     *\n     * @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder}\n     */\n    @Bean\n    @LoadBalanced\n    public RestTemplate loadBalancedRestTemplate(RestTemplateBuilder restTemplateBuilder) {\n        return restTemplateBuilder.build();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/ApiRequestFilter.java",
    "content": "package cn.iocoder.yudao.framework.web.core.filter;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.http.HttpServletRequest;\n\n/**\n * 过滤 /admin-api、/app-api 等 API 请求的过滤器\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\npublic abstract class ApiRequestFilter extends OncePerRequestFilter {\n\n    protected final WebProperties webProperties;\n\n    @Override\n    protected boolean shouldNotFilter(HttpServletRequest request) {\n        // 只过滤 API 请求的地址\n        String apiUri = request.getRequestURI().substring(request.getContextPath().length());\n        return !StrUtil.startWithAny(apiUri, webProperties.getAdminApi().getPrefix(), webProperties.getAppApi().getPrefix());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/CacheRequestBodyFilter.java",
    "content": "package cn.iocoder.yudao.framework.web.core.filter;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * Request Body 缓存 Filter，实现它的可重复读取\n *\n * @author 芋道源码\n */\npublic class CacheRequestBodyFilter extends OncePerRequestFilter {\n\n    /**\n     * 需要排除的 URI\n     *\n     * 1. 排除 Spring Boot Admin 相关请求，避免客户端连接中断导致的异常。\n     *    例如说：<a href=\"https://github.com/YunaiV/ruoyi-vue-pro/issues/795\">795 ISSUE</a>\n     */\n    private static final String[] IGNORE_URIS = {\"/admin/\", \"/actuator/\"};\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n            throws IOException, ServletException {\n        filterChain.doFilter(new CacheRequestBodyWrapper(request), response);\n    }\n\n    @Override\n    protected boolean shouldNotFilter(HttpServletRequest request) {\n        // 1. 校验是否为排除的 URL\n        String requestURI = request.getRequestURI();\n        if (StrUtil.startWithAny(requestURI, IGNORE_URIS)) {\n            return true;\n        }\n\n        // 2. 只处理 json 请求内容\n        return !ServletUtils.isJsonRequest(request);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/CacheRequestBodyWrapper.java",
    "content": "package cn.iocoder.yudao.framework.web.core.filter;\n\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\n\nimport javax.servlet.ReadListener;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStreamReader;\n\n/**\n *  Request Body 缓存 Wrapper\n *\n * @author 芋道源码\n */\npublic class CacheRequestBodyWrapper extends HttpServletRequestWrapper {\n\n    /**\n     * 缓存的内容\n     */\n    private final byte[] body;\n\n    public CacheRequestBodyWrapper(HttpServletRequest request) {\n        super(request);\n        body = ServletUtils.getBodyBytes(request);\n    }\n\n    @Override\n    public BufferedReader getReader() {\n        return new BufferedReader(new InputStreamReader(this.getInputStream()));\n    }\n\n    @Override\n    public int getContentLength() {\n        return body.length;\n    }\n\n    @Override\n    public long getContentLengthLong() {\n        return body.length;\n    }\n\n    @Override\n    public ServletInputStream getInputStream() {\n        final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);\n        // 返回 ServletInputStream\n        return new ServletInputStream() {\n\n            @Override\n            public int read() {\n                return inputStream.read();\n            }\n\n            @Override\n            public boolean isFinished() {\n                return false;\n            }\n\n            @Override\n            public boolean isReady() {\n                return false;\n            }\n\n            @Override\n            public void setReadListener(ReadListener readListener) {}\n\n            @Override\n            public int available() {\n                return body.length;\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/DemoFilter.java",
    "content": "package cn.iocoder.yudao.framework.web.core.filter;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.DEMO_DENY;\n\n/**\n * 演示 Filter，禁止用户发起写操作，避免影响测试数据\n *\n * @author 芋道源码\n */\npublic class DemoFilter extends OncePerRequestFilter {\n\n    @Override\n    protected boolean shouldNotFilter(HttpServletRequest request) {\n        String method = request.getMethod();\n        return !StrUtil.equalsAnyIgnoreCase(method, \"POST\", \"PUT\", \"DELETE\")  // 写操作时，不进行过滤率\n                || WebFrameworkUtils.getLoginUserId(request) == null; // 非登录用户时，不进行过滤\n    }\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {\n        // 直接返回 DEMO_DENY 的结果。即，请求不继续\n        ServletUtils.writeJSON(response, CommonResult.error(DEMO_DENY));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.yudao.framework.web.core.handler;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.exceptions.ExceptionUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi;\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport com.fasterxml.jackson.databind.exc.InvalidFormatException;\nimport com.google.common.util.concurrent.UncheckedExecutionException;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.util.Assert;\nimport org.springframework.validation.BindException;\nimport org.springframework.validation.FieldError;\nimport org.springframework.validation.ObjectError;\nimport org.springframework.web.HttpMediaTypeNotSupportedException;\nimport org.springframework.web.HttpRequestMethodNotSupportedException;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.MissingServletRequestParameterException;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\nimport org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;\nimport org.springframework.web.multipart.MaxUploadSizeExceededException;\nimport org.springframework.web.servlet.NoHandlerFoundException;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport javax.validation.ValidationException;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;\n\n/**\n * 全局异常处理器，将 Exception 翻译成 CommonResult + 对应的异常编号\n *\n * @author 芋道源码\n */\n@RestControllerAdvice\n@AllArgsConstructor\n@Slf4j\npublic class GlobalExceptionHandler {\n\n    /**\n     * 忽略的 ServiceException 错误提示，避免打印过多 logger\n     */\n    public static final Set<String> IGNORE_ERROR_MESSAGES = SetUtils.asSet(\"无效的刷新令牌\");\n\n    @SuppressWarnings(\"SpringJavaInjectionPointsAutowiringInspection\")\n    private final String applicationName;\n\n    private final ApiErrorLogCommonApi apiErrorLogApi;\n\n    /**\n     * 处理所有异常，主要是提供给 Filter 使用\n     * 因为 Filter 不走 SpringMVC 的流程，但是我们又需要兜底处理异常，所以这里提供一个全量的异常处理过程，保持逻辑统一。\n     *\n     * @param request 请求\n     * @param ex 异常\n     * @return 通用返回\n     */\n    public CommonResult<?> allExceptionHandler(HttpServletRequest request, Throwable ex) {\n        if (ex instanceof MissingServletRequestParameterException) {\n            return missingServletRequestParameterExceptionHandler((MissingServletRequestParameterException) ex);\n        }\n        if (ex instanceof MethodArgumentTypeMismatchException) {\n            return methodArgumentTypeMismatchExceptionHandler((MethodArgumentTypeMismatchException) ex);\n        }\n        if (ex instanceof MethodArgumentNotValidException) {\n            return methodArgumentNotValidExceptionExceptionHandler((MethodArgumentNotValidException) ex);\n        }\n        if (ex instanceof BindException) {\n            return bindExceptionHandler((BindException) ex);\n        }\n        if (ex instanceof ConstraintViolationException) {\n            return constraintViolationExceptionHandler((ConstraintViolationException) ex);\n        }\n        if (ex instanceof ValidationException) {\n            return validationException((ValidationException) ex);\n        }\n        if (ex instanceof MaxUploadSizeExceededException) {\n            return maxUploadSizeExceededExceptionHandler((MaxUploadSizeExceededException) ex);\n        }\n        if (ex instanceof NoHandlerFoundException) {\n            return noHandlerFoundExceptionHandler((NoHandlerFoundException) ex);\n        }\n        if (ex instanceof HttpRequestMethodNotSupportedException) {\n            return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex);\n        }\n        if (ex instanceof HttpMediaTypeNotSupportedException) {\n            return httpMediaTypeNotSupportedExceptionHandler((HttpMediaTypeNotSupportedException) ex);\n        }\n        if (ex instanceof ServiceException) {\n            return serviceExceptionHandler((ServiceException) ex);\n        }\n        if (ex instanceof AccessDeniedException) {\n            return accessDeniedExceptionHandler(request, (AccessDeniedException) ex);\n        }\n        return defaultExceptionHandler(request, ex);\n    }\n\n    /**\n     * 处理 SpringMVC 请求参数缺失\n     *\n     * 例如说，接口上设置了 @RequestParam(\"xx\") 参数，结果并未传递 xx 参数\n     */\n    @ExceptionHandler(value = MissingServletRequestParameterException.class)\n    public CommonResult<?> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {\n        log.warn(\"[missingServletRequestParameterExceptionHandler]\", ex);\n        return CommonResult.error(BAD_REQUEST.getCode(), String.format(\"请求参数缺失:%s\", ex.getParameterName()));\n    }\n\n    /**\n     * 处理 SpringMVC 请求参数类型错误\n     *\n     * 例如说，接口上设置了 @RequestParam(\"xx\") 参数为 Integer，结果传递 xx 参数类型为 String\n     */\n    @ExceptionHandler(MethodArgumentTypeMismatchException.class)\n    public CommonResult<?> methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException ex) {\n        log.warn(\"[methodArgumentTypeMismatchExceptionHandler]\", ex);\n        return CommonResult.error(BAD_REQUEST.getCode(), String.format(\"请求参数类型错误:%s\", ex.getMessage()));\n    }\n\n    /**\n     * 处理 SpringMVC 参数校验不正确\n     */\n    @ExceptionHandler(MethodArgumentNotValidException.class)\n    public CommonResult<?> methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) {\n        log.warn(\"[methodArgumentNotValidExceptionExceptionHandler]\", ex);\n        // 获取 errorMessage\n        String errorMessage = null;\n        FieldError fieldError = ex.getBindingResult().getFieldError();\n        if (fieldError == null) {\n            // 组合校验，参考自 https://t.zsxq.com/3HVTx\n            List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();\n            if (CollUtil.isNotEmpty(allErrors)) {\n                errorMessage = allErrors.get(0).getDefaultMessage();\n            }\n        } else {\n            errorMessage = fieldError.getDefaultMessage();\n        }\n        // 转换 CommonResult\n        if (StrUtil.isEmpty(errorMessage)) {\n            return CommonResult.error(BAD_REQUEST);\n        }\n        return CommonResult.error(BAD_REQUEST.getCode(), String.format(\"请求参数不正确:%s\", errorMessage));\n    }\n\n    /**\n     * 处理 SpringMVC 参数绑定不正确，本质上也是通过 Validator 校验\n     */\n    @ExceptionHandler(BindException.class)\n    public CommonResult<?> bindExceptionHandler(BindException ex) {\n        log.warn(\"[handleBindException]\", ex);\n        FieldError fieldError = ex.getFieldError();\n        assert fieldError != null; // 断言，避免告警\n        return CommonResult.error(BAD_REQUEST.getCode(), String.format(\"请求参数不正确:%s\", fieldError.getDefaultMessage()));\n    }\n\n    /**\n     * 处理 SpringMVC 请求参数类型错误\n     *\n     * 例如说，接口上设置了 @RequestBody 实体中 xx 属性类型为 Integer，结果传递 xx 参数类型为 String\n     */\n    @ExceptionHandler(HttpMessageNotReadableException.class)\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    public CommonResult<?> methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) {\n        log.warn(\"[methodArgumentTypeInvalidFormatExceptionHandler]\", ex);\n        if (ex.getCause() instanceof InvalidFormatException) {\n            InvalidFormatException invalidFormatException = (InvalidFormatException) ex.getCause();\n            return CommonResult.error(BAD_REQUEST.getCode(), String.format(\"请求参数类型错误:%s\", invalidFormatException.getValue()));\n        }\n        if (StrUtil.startWith(ex.getMessage(), \"Required request body is missing\")) {\n            return CommonResult.error(BAD_REQUEST.getCode(), \"请求参数类型错误: request body 缺失\");\n        }\n        return defaultExceptionHandler(ServletUtils.getRequest(), ex);\n    }\n\n    /**\n     * 处理 Validator 校验不通过产生的异常\n     */\n    @ExceptionHandler(value = ConstraintViolationException.class)\n    public CommonResult<?> constraintViolationExceptionHandler(ConstraintViolationException ex) {\n        log.warn(\"[constraintViolationExceptionHandler]\", ex);\n        ConstraintViolation<?> constraintViolation = ex.getConstraintViolations().iterator().next();\n        return CommonResult.error(BAD_REQUEST.getCode(), String.format(\"请求参数不正确:%s\", constraintViolation.getMessage()));\n    }\n\n    /**\n     * 处理 Dubbo Consumer 本地参数校验时，抛出的 ValidationException 异常\n     */\n    @ExceptionHandler(value = ValidationException.class)\n    public CommonResult<?> validationException(ValidationException ex) {\n        log.warn(\"[constraintViolationExceptionHandler]\", ex);\n        // 无法拼接明细的错误信息，因为 Dubbo Consumer 抛出 ValidationException 异常时，是直接的字符串信息，且人类不可读\n        return CommonResult.error(BAD_REQUEST);\n    }\n\n    /**\n     * 处理上传文件过大异常\n     */\n    @ExceptionHandler(MaxUploadSizeExceededException.class)\n    public CommonResult<?> maxUploadSizeExceededExceptionHandler(MaxUploadSizeExceededException ex) {\n        return CommonResult.error(BAD_REQUEST.getCode(), \"上传文件过大，请调整后重试\");\n    }\n\n    /**\n     * 处理 SpringMVC 请求地址不存在\n     *\n     * 注意，它需要设置如下两个配置项：\n     * 1. spring.mvc.throw-exception-if-no-handler-found 为 true\n     * 2. spring.mvc.static-path-pattern 为 /statics/**\n     */\n    @ExceptionHandler(NoHandlerFoundException.class)\n    public CommonResult<?> noHandlerFoundExceptionHandler(NoHandlerFoundException ex) {\n        log.warn(\"[noHandlerFoundExceptionHandler]\", ex);\n        return CommonResult.error(NOT_FOUND.getCode(), String.format(\"请求地址不存在:%s\", ex.getRequestURL()));\n    }\n\n    /**\n     * 处理 SpringMVC 请求方法不正确\n     *\n     * 例如说，A 接口的方法为 GET 方式，结果请求方法为 POST 方式，导致不匹配\n     */\n    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)\n    public CommonResult<?> httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException ex) {\n        log.warn(\"[httpRequestMethodNotSupportedExceptionHandler]\", ex);\n        return CommonResult.error(METHOD_NOT_ALLOWED.getCode(), String.format(\"请求方法不正确:%s\", ex.getMessage()));\n    }\n\n    /**\n     * 处理 SpringMVC 请求的 Content-Type 不正确\n     *\n     * 例如说，A 接口的 Content-Type 为 application/json，结果请求的 Content-Type 为 application/octet-stream，导致不匹配\n     */\n    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)\n    public CommonResult<?> httpMediaTypeNotSupportedExceptionHandler(HttpMediaTypeNotSupportedException ex) {\n        log.warn(\"[httpMediaTypeNotSupportedExceptionHandler]\", ex);\n        return CommonResult.error(BAD_REQUEST.getCode(), String.format(\"请求类型不正确:%s\", ex.getMessage()));\n    }\n\n    /**\n     * 处理 Spring Security 权限不足的异常\n     *\n     * 来源是，使用 @PreAuthorize 注解，AOP 进行权限拦截\n     */\n    @ExceptionHandler(value = AccessDeniedException.class)\n    public CommonResult<?> accessDeniedExceptionHandler(HttpServletRequest req, AccessDeniedException ex) {\n        log.warn(\"[accessDeniedExceptionHandler][userId({}) 无法访问 url({})]\", WebFrameworkUtils.getLoginUserId(req),\n                req.getRequestURL(), ex);\n        return CommonResult.error(FORBIDDEN);\n    }\n\n    /**\n     * 处理 Guava UncheckedExecutionException\n     *\n     * 例如说，缓存加载报错，可见 <a href=\"https://t.zsxq.com/UszdH\">https://t.zsxq.com/UszdH</a>\n     */\n    @ExceptionHandler(value = UncheckedExecutionException.class)\n    public CommonResult<?> uncheckedExecutionExceptionHandler(HttpServletRequest req, UncheckedExecutionException ex) {\n        return allExceptionHandler(req, ex.getCause());\n    }\n\n    /**\n     * 处理业务异常 ServiceException\n     *\n     * 例如说，商品库存不足，用户手机号已存在。\n     */\n    @ExceptionHandler(value = ServiceException.class)\n    public CommonResult<?> serviceExceptionHandler(ServiceException ex) {\n        // 不包含的时候，才进行打印，避免 ex 堆栈过多\n        if (!IGNORE_ERROR_MESSAGES.contains(ex.getMessage())) {\n            // 即使打印，也只打印第一层 StackTraceElement，并且使用 warn 在控制台输出，更容易看到\n            try {\n                StackTraceElement[] stackTraces = ex.getStackTrace();\n                for (StackTraceElement stackTrace : stackTraces) {\n                    if (ObjUtil.notEqual(stackTrace.getClassName(), ServiceExceptionUtil.class.getName())) {\n                        log.warn(\"[serviceExceptionHandler]\\n\\t{}\", stackTrace);\n                        break;\n                    }\n                }\n            } catch (Exception ignored) {\n                // 忽略日志，避免影响主流程\n            }\n        }\n        return CommonResult.error(ex.getCode(), ex.getMessage());\n    }\n\n    /**\n     * 处理系统异常，兜底处理所有的一切\n     */\n    @ExceptionHandler(value = Exception.class)\n    public CommonResult<?> defaultExceptionHandler(HttpServletRequest req, Throwable ex) {\n        // 特殊：如果是 ServiceException 的异常，则直接返回\n        // 例如说：https://gitee.com/zhijiantianya/yudao-cloud/issues/ICSSRM、https://gitee.com/zhijiantianya/yudao-cloud/issues/ICT6FM\n        if (ex.getCause() != null && ex.getCause() instanceof ServiceException) {\n            return serviceExceptionHandler((ServiceException) ex.getCause());\n        }\n\n        // 情况一：处理表不存在的异常\n        CommonResult<?> tableNotExistsResult = handleTableNotExists(ex);\n        if (tableNotExistsResult != null) {\n            return tableNotExistsResult;\n        }\n\n        // 情况二：处理异常\n        log.error(\"[defaultExceptionHandler]\", ex);\n        // 插入异常日志\n        createExceptionLog(req, ex);\n        // 返回 ERROR CommonResult\n        return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());\n    }\n\n    private void createExceptionLog(HttpServletRequest req, Throwable e) {\n        // 插入错误日志\n        ApiErrorLogCreateReqDTO errorLog = new ApiErrorLogCreateReqDTO();\n        try {\n            // 初始化 errorLog\n            buildExceptionLog(errorLog, req, e);\n            // 执行插入 errorLog\n            apiErrorLogApi.createApiErrorLogAsync(errorLog);\n        } catch (Throwable th) {\n            log.error(\"[createExceptionLog][url({}) log({}) 发生异常]\", req.getRequestURI(),  JsonUtils.toJsonString(errorLog), th);\n        }\n    }\n\n    private void buildExceptionLog(ApiErrorLogCreateReqDTO errorLog, HttpServletRequest request, Throwable e) {\n        // 处理用户信息\n        errorLog.setUserId(WebFrameworkUtils.getLoginUserId(request));\n        errorLog.setUserType(WebFrameworkUtils.getLoginUserType(request));\n        // 设置异常字段\n        errorLog.setExceptionName(e.getClass().getName());\n        errorLog.setExceptionMessage(ExceptionUtil.getMessage(e));\n        errorLog.setExceptionRootCauseMessage(ExceptionUtil.getRootCauseMessage(e));\n        errorLog.setExceptionStackTrace(ExceptionUtil.stacktraceToString(e));\n        StackTraceElement[] stackTraceElements = e.getStackTrace();\n        Assert.notEmpty(stackTraceElements, \"异常 stackTraceElements 不能为空\");\n        StackTraceElement stackTraceElement = stackTraceElements[0];\n        errorLog.setExceptionClassName(stackTraceElement.getClassName());\n        errorLog.setExceptionFileName(stackTraceElement.getFileName());\n        errorLog.setExceptionMethodName(stackTraceElement.getMethodName());\n        errorLog.setExceptionLineNumber(stackTraceElement.getLineNumber());\n        // 设置其它字段\n        errorLog.setTraceId(TracerUtils.getTraceId());\n        errorLog.setApplicationName(applicationName);\n        errorLog.setRequestUrl(request.getRequestURI());\n        Map<String, Object> requestParams = MapUtil.<String, Object>builder()\n                .put(\"query\", ServletUtils.getParamMap(request))\n                .put(\"body\", ServletUtils.getBody(request)).build();\n        errorLog.setRequestParams(JsonUtils.toJsonString(requestParams));\n        errorLog.setRequestMethod(request.getMethod());\n        errorLog.setUserAgent(ServletUtils.getUserAgent(request));\n        errorLog.setUserIp(ServletUtils.getClientIP(request));\n        errorLog.setExceptionTime(LocalDateTime.now());\n    }\n\n    /**\n     * 处理 Table 不存在的异常情况\n     *\n     * @param ex 异常\n     * @return 如果是 Table 不存在的异常，则返回对应的 CommonResult\n     */\n    private CommonResult<?> handleTableNotExists(Throwable ex) {\n        String message = ExceptionUtil.getRootCauseMessage(ex);\n        if (!message.contains(\"doesn't exist\")) {\n            return null;\n        }\n        // 1. 数据报表\n        if (message.contains(\"report_\")) {\n            log.error(\"[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]\");\n            return CommonResult.error(NOT_IMPLEMENTED.getCode(),\n                    \"[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]\");\n        }\n        // 2. 工作流\n        if (message.contains(\"bpm_\")) {\n            log.error(\"[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]\");\n            return CommonResult.error(NOT_IMPLEMENTED.getCode(),\n                    \"[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]\");\n        }\n        // 3. 微信公众号\n        if (message.contains(\"mp_\")) {\n            log.error(\"[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]\");\n            return CommonResult.error(NOT_IMPLEMENTED.getCode(),\n                    \"[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]\");\n        }\n        // 4. 商城系统\n        if (StrUtil.containsAny(message, \"product_\", \"promotion_\", \"trade_\")) {\n            log.error(\"[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]\");\n            return CommonResult.error(NOT_IMPLEMENTED.getCode(),\n                    \"[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]\");\n        }\n        // 5. ERP 系统\n        if (message.contains(\"erp_\")) {\n            log.error(\"[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]\");\n            return CommonResult.error(NOT_IMPLEMENTED.getCode(),\n                    \"[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]\");\n        }\n        // 6. CRM 系统\n        if (message.contains(\"crm_\")) {\n            log.error(\"[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]\");\n            return CommonResult.error(NOT_IMPLEMENTED.getCode(),\n                    \"[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]\");\n        }\n        // 7. 支付平台\n        if (message.contains(\"pay_\")) {\n            log.error(\"[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]\");\n            return CommonResult.error(NOT_IMPLEMENTED.getCode(),\n                    \"[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]\");\n        }\n        // 8. AI 大模型\n        if (message.contains(\"ai_\")) {\n            log.error(\"[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]\");\n            return CommonResult.error(NOT_IMPLEMENTED.getCode(),\n                    \"[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]\");\n        }\n        // 9. IoT 物联网\n        if (message.contains(\"iot_\")) {\n            log.error(\"[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]\");\n            return CommonResult.error(NOT_IMPLEMENTED.getCode(),\n                    \"[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]\");\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalResponseBodyHandler.java",
    "content": "package cn.iocoder.yudao.framework.web.core.handler;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;\n\n/**\n * 全局响应结果（ResponseBody）处理器\n *\n * 不同于在网上看到的很多文章，会选择自动将 Controller 返回结果包上 {@link CommonResult}，\n * 在 onemall 中，是 Controller 在返回时，主动自己包上 {@link CommonResult}。\n * 原因是，GlobalResponseBodyHandler 本质上是 AOP，它不应该改变 Controller 返回的数据结构\n *\n * 目前，GlobalResponseBodyHandler 的主要作用是，记录 Controller 的返回结果，\n * 方便 {@link cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter} 记录访问日志\n */\n@ControllerAdvice\npublic class GlobalResponseBodyHandler implements ResponseBodyAdvice {\n\n    @Override\n    @SuppressWarnings(\"NullableProblems\") // 避免 IDEA 警告\n    public boolean supports(MethodParameter returnType, Class converterType) {\n        if (returnType.getMethod() == null) {\n            return false;\n        }\n        // 只拦截返回结果为 CommonResult 类型\n        return returnType.getMethod().getReturnType() == CommonResult.class;\n    }\n\n    @Override\n    @SuppressWarnings(\"NullableProblems\") // 避免 IDEA 警告\n    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,\n                                  ServerHttpRequest request, ServerHttpResponse response) {\n        // 记录 Controller 结果\n        WebFrameworkUtils.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult<?>) body);\n        return body;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java",
    "content": "package cn.iocoder.yudao.framework.web.core.util;\n\nimport cn.hutool.core.util.NumberUtil;\nimport cn.hutool.extra.servlet.ServletUtil;\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\nimport cn.iocoder.yudao.framework.common.enums.TerminalEnum;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport javax.servlet.ServletRequest;\nimport javax.servlet.http.HttpServletRequest;\n\n/**\n * 专属于 web 包的工具类\n *\n * @author 芋道源码\n */\npublic class WebFrameworkUtils {\n\n    private static final String REQUEST_ATTRIBUTE_LOGIN_USER_ID = \"login_user_id\";\n    private static final String REQUEST_ATTRIBUTE_LOGIN_USER_TYPE = \"login_user_type\";\n\n    private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = \"common_result\";\n\n    public static final String HEADER_TENANT_ID = \"tenant-id\";\n    public static final String HEADER_VISIT_TENANT_ID = \"visit-tenant-id\";\n\n    /**\n     * 终端的 Header\n     *\n     * @see cn.iocoder.yudao.framework.common.enums.TerminalEnum\n     */\n    public static final String HEADER_TERMINAL = \"terminal\";\n\n    private static WebProperties properties;\n\n    public WebFrameworkUtils(WebProperties webProperties) {\n        WebFrameworkUtils.properties = webProperties;\n    }\n\n    /**\n     * 获得租户编号，从 header 中\n     * 考虑到其它 framework 组件也会使用到租户编号，所以不得不放在 WebFrameworkUtils 统一提供\n     *\n     * @param request 请求\n     * @return 租户编号\n     */\n    public static Long getTenantId(HttpServletRequest request) {\n        String tenantId = request.getHeader(HEADER_TENANT_ID);\n        return NumberUtil.isNumber(tenantId) ? Long.valueOf(tenantId) : null;\n    }\n\n    /**\n     * 获得访问的租户编号，从 header 中\n     * 考虑到其它 framework 组件也会使用到租户编号，所以不得不放在 WebFrameworkUtils 统一提供\n     *\n     * @param request 请求\n     * @return 租户编号\n     */\n    public static Long getVisitTenantId(HttpServletRequest request) {\n        String tenantId = request.getHeader(HEADER_VISIT_TENANT_ID);\n        return NumberUtil.isNumber(tenantId)? Long.valueOf(tenantId) : null;\n    }\n\n    public static void setLoginUserId(ServletRequest request, Long userId) {\n        request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId);\n    }\n\n    /**\n     * 设置用户类型\n     *\n     * @param request 请求\n     * @param userType 用户类型\n     */\n    public static void setLoginUserType(ServletRequest request, Integer userType) {\n        request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE, userType);\n    }\n\n    /**\n     * 获得当前用户的编号，从请求中\n     * 注意：该方法仅限于 framework 框架使用！！！\n     *\n     * @param request 请求\n     * @return 用户编号\n     */\n    public static Long getLoginUserId(HttpServletRequest request) {\n        if (request == null) {\n            return null;\n        }\n        return (Long) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID);\n    }\n\n    /**\n     * 获得当前用户的类型\n     * 注意：该方法仅限于 web 相关的 framework 组件使用！！！\n     *\n     * @param request 请求\n     * @return 用户编号\n     */\n    public static Integer getLoginUserType(HttpServletRequest request) {\n        if (request == null) {\n            return null;\n        }\n        // 1. 优先，从 Attribute 中获取\n        Integer userType = (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE);\n        if (userType != null) {\n            return userType;\n        }\n        // 2. 其次，基于 URL 前缀的约定\n        if (request.getServletPath().startsWith(properties.getAdminApi().getPrefix())) {\n            return UserTypeEnum.ADMIN.getValue();\n        }\n        if (request.getServletPath().startsWith(properties.getAppApi().getPrefix())) {\n            return UserTypeEnum.MEMBER.getValue();\n        }\n        return null;\n    }\n\n    public static Integer getLoginUserType() {\n        HttpServletRequest request = getRequest();\n        return getLoginUserType(request);\n    }\n\n    public static Long getLoginUserId() {\n        HttpServletRequest request = getRequest();\n        return getLoginUserId(request);\n    }\n\n    public static Integer getTerminal() {\n        HttpServletRequest request = getRequest();\n        if (request == null) {\n            return TerminalEnum.UNKNOWN.getTerminal();\n        }\n        String terminalValue = request.getHeader(HEADER_TERMINAL);\n        return NumberUtil.parseInt(terminalValue, TerminalEnum.UNKNOWN.getTerminal());\n    }\n\n    public static void setCommonResult(ServletRequest request, CommonResult<?> result) {\n        request.setAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT, result);\n    }\n\n    public static CommonResult<?> getCommonResult(ServletRequest request) {\n        return (CommonResult<?>) request.getAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT);\n    }\n\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    public static HttpServletRequest getRequest() {\n        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n        if (!(requestAttributes instanceof ServletRequestAttributes)) {\n            return null;\n        }\n        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;\n        return servletRequestAttributes.getRequest();\n    }\n\n    /**\n     * 判断是否为 RPC 请求\n     *\n     * @param request 请求\n     * @return 是否为 RPC 请求\n     */\n    public static boolean isRpcRequest(HttpServletRequest request) {\n        return request.getRequestURI().startsWith(RpcConstants.RPC_API_PREFIX);\n    }\n\n    /**\n     * 判断是否为 RPC 请求\n     *\n     * 约定大于配置，只要以 Api 结尾，都认为是 RPC 接口\n     *\n     * @param className 类名\n     * @return 是否为 RPC 请求\n     */\n    public static boolean isRpcRequest(String className) {\n        return className.endsWith(\"Api\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/package-info.java",
    "content": "/**\n * 针对 SpringMVC 的基础封装\n */\npackage cn.iocoder.yudao.framework.web;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/XssProperties.java",
    "content": "package cn.iocoder.yudao.framework.xss.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Xss 配置属性\n *\n * @author 芋道源码\n */\n@ConfigurationProperties(prefix = \"yudao.xss\")\n@Validated\n@Data\npublic class XssProperties {\n\n    /**\n     * 是否开启，默认为 true\n     */\n    private boolean enable = true;\n    /**\n     * 需要排除的 URL，默认为空\n     */\n    private List<String> excludeUrls = Collections.emptyList();\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.xss.config;\n\nimport cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;\nimport cn.iocoder.yudao.framework.xss.core.clean.JsoupXssCleaner;\nimport cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;\nimport cn.iocoder.yudao.framework.xss.core.filter.XssFilter;\nimport cn.iocoder.yudao.framework.xss.core.json.XssStringJsonDeserializer;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.util.PathMatcher;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport static cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration.createFilterBean;\n\n@AutoConfiguration\n@EnableConfigurationProperties(XssProperties.class)\n@ConditionalOnProperty(prefix = \"yudao.xss\", name = \"enable\", havingValue = \"true\", matchIfMissing = true) // 设置为 false 时，禁用\npublic class YudaoXssAutoConfiguration implements WebMvcConfigurer {\n\n    /**\n     * Xss 清理者\n     *\n     * @return XssCleaner\n     */\n    @Bean\n    @ConditionalOnMissingBean(XssCleaner.class)\n    public XssCleaner xssCleaner() {\n        return new JsoupXssCleaner();\n    }\n\n    /**\n     * 注册 Jackson 的序列化器，用于处理 json 类型参数的 xss 过滤\n     *\n     * @return Jackson2ObjectMapperBuilderCustomizer\n     */\n    @Bean\n    @ConditionalOnMissingBean(name = \"xssJacksonCustomizer\")\n    @ConditionalOnProperty(value = \"yudao.xss.enable\", havingValue = \"true\")\n    public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssProperties properties,\n                                                                      PathMatcher pathMatcher,\n                                                                      XssCleaner xssCleaner) {\n        // 在反序列化时进行 xss 过滤，可以替换使用 XssStringJsonSerializer，在序列化时进行处理\n        return builder ->\n                builder.deserializerByType(String.class, new XssStringJsonDeserializer(properties, pathMatcher, xssCleaner));\n    }\n\n    /**\n     * 创建 XssFilter Bean，解决 Xss 安全问题\n     */\n    @Bean\n    @ConditionalOnBean(XssCleaner.class)\n    public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher, XssCleaner xssCleaner) {\n        return createFilterBean(new XssFilter(properties, pathMatcher, xssCleaner), WebFilterOrderEnum.XSS_FILTER);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/clean/JsoupXssCleaner.java",
    "content": "package cn.iocoder.yudao.framework.xss.core.clean;\n\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.Document;\nimport org.jsoup.safety.Safelist;\n\n/**\n * 基于 JSONP 实现 XSS 过滤字符串\n */\npublic class JsoupXssCleaner implements XssCleaner {\n\n    private final Safelist safelist;\n\n    /**\n     * 用于在 src 属性使用相对路径时，强制转换为绝对路径。 为空时不处理，值应为绝对路径的前缀（包含协议部分）\n     */\n    private final String baseUri;\n\n    /**\n     * 无参构造，默认使用 {@link JsoupXssCleaner#buildSafelist} 方法构建一个安全列表\n     */\n    public JsoupXssCleaner() {\n        this.safelist = buildSafelist();\n        this.baseUri = \"\";\n    }\n\n    /**\n     * 构建一个 Xss 清理的 Safelist 规则。\n     * 基于 Safelist#relaxed() 的基础上:\n     * 1. 扩展支持了 style 和 class 属性\n     * 2. a 标签额外支持了 target 属性\n     * 3. img 标签额外支持了 data 协议，便于支持 base64\n     *\n     * @return Safelist\n     */\n    private Safelist buildSafelist() {\n        // 使用 jsoup 提供的默认的\n        Safelist relaxedSafelist = Safelist.relaxed();\n        // 富文本编辑时一些样式是使用 style 来进行实现的\n        // 比如红色字体 style=\"color:red;\", 所以需要给所有标签添加 style 属性\n        // 注意：style 属性会有注入风险 <img STYLE=\"background-image:url(javascript:alert('XSS'))\">\n        relaxedSafelist.addAttributes(\":all\", \"style\", \"class\");\n        // 保留 a 标签的 target 属性\n        relaxedSafelist.addAttributes(\"a\", \"target\");\n        // 支持img 为base64\n        relaxedSafelist.addProtocols(\"img\", \"src\", \"data\");\n\n        // 保留相对路径, 保留相对路径时，必须提供对应的 baseUri 属性，否则依然会被删除\n        // WHITELIST.preserveRelativeLinks(false);\n\n        // 移除 a 标签和 img 标签的一些协议限制，这会导致 xss 防注入失效，如 <img src=javascript:alert(\"xss\")>\n        // 虽然可以重写 WhiteList#isSafeAttribute 来处理，但是有隐患，所以暂时不支持相对路径\n        // WHITELIST.removeProtocols(\"a\", \"href\", \"ftp\", \"http\", \"https\", \"mailto\");\n        // WHITELIST.removeProtocols(\"img\", \"src\", \"http\", \"https\");\n        return relaxedSafelist;\n    }\n\n    @Override\n    public String clean(String html) {\n        return Jsoup.clean(html, baseUri, safelist, new Document.OutputSettings().prettyPrint(false));\n    }\n\n}\n\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/clean/XssCleaner.java",
    "content": "package cn.iocoder.yudao.framework.xss.core.clean;\n\n/**\n * 对 html 文本中的有 Xss 风险的数据进行清理\n */\npublic interface XssCleaner {\n\n    /**\n     * 清理有 Xss 风险的文本\n     *\n     * @param html 原 html\n     * @return 清理后的 html\n     */\n    String clean(String html);\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/filter/XssFilter.java",
    "content": "package cn.iocoder.yudao.framework.xss.core.filter;\n\nimport cn.iocoder.yudao.framework.xss.config.XssProperties;\nimport cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;\nimport lombok.AllArgsConstructor;\nimport org.springframework.util.PathMatcher;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * Xss 过滤器\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\npublic class XssFilter extends OncePerRequestFilter {\n\n    /**\n     * 属性\n     */\n    private final XssProperties properties;\n    /**\n     * 路径匹配器\n     */\n    private final PathMatcher pathMatcher;\n\n    private final XssCleaner xssCleaner;\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n            throws IOException, ServletException {\n        filterChain.doFilter(new XssRequestWrapper(request, xssCleaner), response);\n    }\n\n    @Override\n    protected boolean shouldNotFilter(HttpServletRequest request) {\n        // 如果关闭，则不过滤\n        if (!properties.isEnable()) {\n            return true;\n        }\n\n        // 如果匹配到无需过滤，则不过滤\n        String uri = request.getRequestURI();\n        return properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, uri));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/filter/XssRequestWrapper.java",
    "content": "package cn.iocoder.yudao.framework.xss.core.filter;\n\nimport cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n/**\n * Xss 请求 Wrapper\n *\n * @author 芋道源码\n */\npublic class XssRequestWrapper extends HttpServletRequestWrapper {\n\n    private final XssCleaner xssCleaner;\n\n    public XssRequestWrapper(HttpServletRequest request, XssCleaner xssCleaner) {\n        super(request);\n        this.xssCleaner = xssCleaner;\n    }\n\n    // ============================ parameter ============================\n    @Override\n    public Map<String, String[]> getParameterMap() {\n        Map<String, String[]> map = new LinkedHashMap<>();\n        Map<String, String[]> parameters = super.getParameterMap();\n        for (Map.Entry<String, String[]> entry : parameters.entrySet()) {\n            String[] values = entry.getValue();\n            for (int i = 0; i < values.length; i++) {\n                values[i] = xssCleaner.clean(values[i]);\n            }\n            map.put(entry.getKey(), values);\n        }\n        return map;\n    }\n\n    @Override\n    public String[] getParameterValues(String name) {\n        String[] values = super.getParameterValues(name);\n        if (values == null) {\n            return null;\n        }\n        int count = values.length;\n        String[] encodedValues = new String[count];\n        for (int i = 0; i < count; i++) {\n            encodedValues[i] = xssCleaner.clean(values[i]);\n        }\n        return encodedValues;\n    }\n\n    @Override\n    public String getParameter(String name) {\n        String value = super.getParameter(name);\n        if (value == null) {\n            return null;\n        }\n        return xssCleaner.clean(value);\n    }\n\n    // ============================ attribute ============================\n    @Override\n    public Object getAttribute(String name) {\n        Object value = super.getAttribute(name);\n        if (value instanceof String) {\n            return xssCleaner.clean((String) value);\n        }\n        return value;\n    }\n\n    // ============================ header ============================\n    @Override\n    public String getHeader(String name) {\n        String value = super.getHeader(name);\n        if (value == null) {\n            return null;\n        }\n        return xssCleaner.clean(value);\n    }\n\n    // ============================ queryString ============================\n    @Override\n    public String getQueryString() {\n        String value = super.getQueryString();\n        if (value == null) {\n            return null;\n        }\n        return xssCleaner.clean(value);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/json/XssStringJsonDeserializer.java",
    "content": "package cn.iocoder.yudao.framework.xss.core.json;\n\nimport cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;\nimport cn.iocoder.yudao.framework.xss.config.XssProperties;\nimport cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.core.JsonToken;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.deser.std.StringDeserializer;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.util.PathMatcher;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.io.IOException;\n\n/**\n * XSS 过滤 jackson 反序列化器。\n * 在反序列化的过程中，会对字符串进行 XSS 过滤。\n *\n * @author Hccake\n */\n@Slf4j\n@AllArgsConstructor\npublic class XssStringJsonDeserializer extends StringDeserializer {\n\n    /**\n     * 属性\n     */\n    private final XssProperties properties;\n    /**\n     * 路径匹配器\n     */\n    private final PathMatcher pathMatcher;\n\n    private final XssCleaner xssCleaner;\n\n    @Override\n    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {\n        // 1. 白名单 URL 的处理\n        HttpServletRequest request = ServletUtils.getRequest();\n        if (request != null) {\n            String uri = ServletUtils.getRequest().getRequestURI();\n            if (properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, uri))) {\n                return p.getText();\n            }\n        }\n\n        // 2. 真正使用 xssCleaner 进行过滤\n        if (p.hasToken(JsonToken.VALUE_STRING)) {\n            return xssCleaner.clean(p.getText());\n        }\n        JsonToken t = p.currentToken();\n        // [databind#381]\n        if (t == JsonToken.START_ARRAY) {\n            return _deserializeFromArray(p, ctxt);\n        }\n        // need to gracefully handle byte[] data, as base64\n        if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {\n            Object ob = p.getEmbeddedObject();\n            if (ob == null) {\n                return null;\n            }\n            if (ob instanceof byte[]) {\n                return ctxt.getBase64Variant().encode((byte[]) ob, false);\n            }\n            // otherwise, try conversion using toString()...\n            return ob.toString();\n        }\n        // 29-Jun-2020, tatu: New! \"Scalar from Object\" (mostly for XML)\n        if (t == JsonToken.START_OBJECT) {\n            return ctxt.extractScalarFromObject(p, this, _valueClass);\n        }\n\n        if (t.isScalarValue()) {\n            String text = p.getValueAsString();\n            return xssCleaner.clean(text);\n        }\n        return (String) ctxt.handleUnexpectedToken(_valueClass, p);\n    }\n}\n\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/package-info.java",
    "content": "/**\n * 针对 XSS 的基础封装\n *\n * XSS 说明：https://tech.meituan.com/2018/09/27/fe-security.html\n */\npackage cn.iocoder.yudao.framework.xss;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.apilog.config.YudaoApiLogAutoConfiguration\ncn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration\ncn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration\ncn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration\ncn.iocoder.yudao.framework.apilog.config.YudaoApiLogRpcAutoConfiguration\ncn.iocoder.yudao.framework.xss.config.YudaoXssAutoConfiguration\ncn.iocoder.yudao.framework.banner.config.YudaoBannerAutoConfiguration\ncn.iocoder.yudao.framework.encrypt.config.YudaoApiEncryptAutoConfiguration"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/main/resources/banner.txt",
    "content": "芋道源码 http://www.iocoder.cn\nApplication Version: ${yudao.info.version}\nSpring Boot Version: ${spring-boot.version}\n\n.__   __.   ______      .______    __    __    _______\n|  \\ |  |  /  __  \\     |   _  \\  |  |  |  |  /  _____|\n|   \\|  | |  |  |  |    |  |_)  | |  |  |  | |  |  __\n|  . `  | |  |  |  |    |   _  <  |  |  |  | |  | |_ |\n|  |\\   | |  `--'  |    |  |_)  | |  `--'  | |  |__| |\n|__| \\__|  \\______/     |______/   \\______/   \\______|\n\n███╗   ██╗ ██████╗     ██████╗ ██╗   ██╗ ██████╗\n████╗  ██║██╔═══██╗    ██╔══██╗██║   ██║██╔════╝\n██╔██╗ ██║██║   ██║    ██████╔╝██║   ██║██║  ███╗\n██║╚██╗██║██║   ██║    ██╔══██╗██║   ██║██║   ██║\n██║ ╚████║╚██████╔╝    ██████╔╝╚██████╔╝╚██████╔╝\n╚═╝  ╚═══╝ ╚═════╝     ╚═════╝  ╚═════╝  ╚═════╝\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/DesensitizeTest.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.desensitize.core.regex.annotation.EmailDesensitize;\nimport cn.iocoder.yudao.framework.desensitize.core.regex.annotation.RegexDesensitize;\nimport cn.iocoder.yudao.framework.desensitize.core.annotation.Address;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.BankCardDesensitize;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.CarLicenseDesensitize;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.ChineseNameDesensitize;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.FixedPhoneDesensitize;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.IdCardDesensitize;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.PasswordDesensitize;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.MobileDesensitize;\nimport cn.iocoder.yudao.framework.desensitize.core.slider.annotation.SliderDesensitize;\nimport lombok.Data;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link DesensitizeTest} 的单元测试\n */\n@ExtendWith(MockitoExtension.class)\npublic class DesensitizeTest {\n\n    @Test\n    public void test() {\n        // 准备参数\n        DesensitizeDemo desensitizeDemo = new DesensitizeDemo();\n        desensitizeDemo.setNickname(\"芋道源码\");\n        desensitizeDemo.setBankCard(\"9988002866797031\");\n        desensitizeDemo.setCarLicense(\"粤A66666\");\n        desensitizeDemo.setFixedPhone(\"01086551122\");\n        desensitizeDemo.setIdCard(\"530321199204074611\");\n        desensitizeDemo.setPassword(\"123456\");\n        desensitizeDemo.setPhoneNumber(\"13248765917\");\n        desensitizeDemo.setSlider1(\"ABCDEFG\");\n        desensitizeDemo.setSlider2(\"ABCDEFG\");\n        desensitizeDemo.setSlider3(\"ABCDEFG\");\n        desensitizeDemo.setEmail(\"1@email.com\");\n        desensitizeDemo.setRegex(\"你好，我是芋道源码\");\n        desensitizeDemo.setAddress(\"北京市海淀区上地十街10号\");\n        desensitizeDemo.setOrigin(\"芋道源码\");\n\n        // 调用\n        DesensitizeDemo d = JsonUtils.parseObject(JsonUtils.toJsonString(desensitizeDemo), DesensitizeDemo.class);\n        // 断言\n        assertNotNull(d);\n        assertEquals(\"芋***\", d.getNickname());\n        assertEquals(\"998800********31\", d.getBankCard());\n        assertEquals(\"粤A6***6\", d.getCarLicense());\n        assertEquals(\"0108*****22\", d.getFixedPhone());\n        assertEquals(\"530321**********11\", d.getIdCard());\n        assertEquals(\"******\", d.getPassword());\n        assertEquals(\"132****5917\", d.getPhoneNumber());\n        assertEquals(\"#######\", d.getSlider1());\n        assertEquals(\"ABC*EFG\", d.getSlider2());\n        assertEquals(\"*******\", d.getSlider3());\n        assertEquals(\"1****@email.com\", d.getEmail());\n        assertEquals(\"你好，我是*\", d.getRegex());\n        assertEquals(\"北京市海淀区上地十街10号*\", d.getAddress());\n        assertEquals(\"芋道源码\", d.getOrigin());\n    }\n\n    @Data\n    public static class DesensitizeDemo {\n\n        @ChineseNameDesensitize\n        private String nickname;\n        @BankCardDesensitize\n        private String bankCard;\n        @CarLicenseDesensitize\n        private String carLicense;\n        @FixedPhoneDesensitize\n        private String fixedPhone;\n        @IdCardDesensitize\n        private String idCard;\n        @PasswordDesensitize\n        private String password;\n        @MobileDesensitize\n        private String phoneNumber;\n        @SliderDesensitize(prefixKeep = 6, suffixKeep = 1, replacer = \"#\")\n        private String slider1;\n        @SliderDesensitize(prefixKeep = 3, suffixKeep = 3)\n        private String slider2;\n        @SliderDesensitize(prefixKeep = 10)\n        private String slider3;\n        @EmailDesensitize\n        private String email;\n        @RegexDesensitize(regex = \"芋道源码\", replacer = \"*\")\n        private String regex;\n        @Address\n        private String address;\n        private String origin;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/annotation/Address.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.annotation;\n\nimport cn.iocoder.yudao.framework.desensitize.core.DesensitizeTest;\nimport cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;\nimport cn.iocoder.yudao.framework.desensitize.core.handler.AddressHandler;\nimport com.fasterxml.jackson.annotation.JacksonAnnotationsInside;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 地址\n *\n * 用于 {@link DesensitizeTest} 测试使用\n *\n * @author gaibu\n */\n@Documented\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@JacksonAnnotationsInside\n@DesensitizeBy(handler = AddressHandler.class)\npublic @interface Address {\n\n    String replacer() default \"*\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/handler/AddressHandler.java",
    "content": "package cn.iocoder.yudao.framework.desensitize.core.handler;\n\nimport cn.iocoder.yudao.framework.desensitize.core.DesensitizeTest;\nimport cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;\nimport cn.iocoder.yudao.framework.desensitize.core.annotation.Address;\n\n/**\n * {@link Address} 的脱敏处理器\n *\n * 用于 {@link DesensitizeTest} 测试使用\n */\npublic class AddressHandler implements DesensitizationHandler<Address> {\n\n    @Override\n    public String desensitize(String origin, Address annotation) {\n        return origin + annotation.replacer();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/encrypt/ApiEncryptTest.java",
    "content": "package cn.iocoder.yudao.framework.encrypt;\n\nimport cn.hutool.core.util.RandomUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;\nimport cn.hutool.crypto.asymmetric.KeyType;\nimport cn.hutool.crypto.asymmetric.RSA;\nimport cn.hutool.crypto.symmetric.SymmetricAlgorithm;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Objects;\n\n/**\n * 各种 API 加解密的测试类：不是单测，而是方便大家生成密钥、加密、解密等操作。\n *\n * @author 芋道源码\n */\n@SuppressWarnings(\"ConstantValue\")\npublic class ApiEncryptTest {\n\n    @Test\n    public void testGenerateAsymmetric() {\n        String asymmetricAlgorithm = AsymmetricAlgorithm.RSA.getValue();\n//        String asymmetricAlgorithm = \"SM2\";\n//        String asymmetricAlgorithm = SM4.ALGORITHM_NAME;\n//        String asymmetricAlgorithm = SymmetricAlgorithm.AES.getValue();\n        String requestClientKey = null;\n        String requestServerKey = null;\n        String responseClientKey = null;\n        String responseServerKey = null;\n        if (Objects.equals(asymmetricAlgorithm, AsymmetricAlgorithm.RSA.getValue())) {\n            // 请求的密钥\n            RSA requestRsa = SecureUtil.rsa();\n            requestClientKey = requestRsa.getPublicKeyBase64();\n            requestServerKey = requestRsa.getPrivateKeyBase64();\n            // 响应的密钥\n            RSA responseRsa = new RSA();\n            responseClientKey = responseRsa.getPrivateKeyBase64();\n            responseServerKey = responseRsa.getPublicKeyBase64();\n        } else if (Objects.equals(asymmetricAlgorithm, SymmetricAlgorithm.AES.getValue())) {\n            // AES 密钥可选 32、24、16 位\n            // 请求的密钥（前后端密钥一致）\n            requestClientKey = RandomUtil.randomNumbers(32);\n            requestServerKey = requestClientKey;\n            // 响应的密钥（前后端密钥一致）\n            responseClientKey = RandomUtil.randomNumbers(32);\n            responseServerKey = responseClientKey;\n        }\n\n        // 打印结果\n        System.out.println(\"requestClientKey = \" + requestClientKey);\n        System.out.println(\"requestServerKey = \" + requestServerKey);\n        System.out.println(\"responseClientKey = \" + responseClientKey);\n        System.out.println(\"responseServerKey = \" + responseServerKey);\n    }\n\n    @Test\n    public void testEncrypt_aes() {\n        String key = \"52549111389893486934626385991395\";\n        String body = \"{\\n\" +\n                \"  \\\"username\\\": \\\"admin\\\",\\n\" +\n                \"  \\\"password\\\": \\\"admin123\\\",\\n\" +\n                \"  \\\"uuid\\\": \\\"3acd87a09a4f48fb9118333780e94883\\\",\\n\" +\n                \"  \\\"code\\\": \\\"1024\\\"\\n\" +\n                \"}\";\n        String encrypt = SecureUtil.aes(StrUtil.utf8Bytes(key))\n                .encryptBase64(body);\n        System.out.println(\"encrypt = \" + encrypt);\n    }\n\n    @Test\n    public void testEncrypt_rsa() {\n        String key = \"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCls2rIpnGdYnLFgz1XU13GbNQ5DloyPpvW00FPGjqn5Z6JpK+kDtVlnkhwR87iRrE5Vf2WNqRX6vzbLSgveIQY8e8oqGCb829myjf1MuI+ZzN4ghf/7tEYhZJGPI9AbfxFqBUzm+kR3/HByAI22GLT96WM26QiMK8n3tIP/yiLswIDAQAB\";\n        String body = \"{\\n\" +\n                \"  \\\"username\\\": \\\"admin\\\",\\n\" +\n                \"  \\\"password\\\": \\\"admin123\\\",\\n\" +\n                \"  \\\"uuid\\\": \\\"3acd87a09a4f48fb9118333780e94883\\\",\\n\" +\n                \"  \\\"code\\\": \\\"1024\\\"\\n\" +\n                \"}\";\n        String encrypt = SecureUtil.rsa(null, key)\n                .encryptBase64(body, KeyType.PublicKey);\n        System.out.println(\"encrypt = \" + encrypt);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/《芋道 Spring Boot API 接口文档 Swagger 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Swagger/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-web/《芋道 Spring Boot SpringMVC 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/SpringMVC/?yudao>\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-framework</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-spring-boot-starter-websocket</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>WebSocket 框架，支持多节点的广播</description>\n    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <!-- 为什么是 websocket 依赖 security 呢？而不是 security 拓展 websocket 呢？\n                 因为 websocket 和 LoginUser 当前登录的用户有一定的相关性，具体可见 WebSocketSessionManagerImpl 逻辑。\n                 如果让 security 拓展 websocket 的话，会导致 websocket 组件的封装很散，进而增大理解成本。\n            -->\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-websocket</artifactId>\n        </dependency>\n\n        <!-- 消息队列相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mq</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.amqp</groupId>\n            <artifactId>spring-rabbit</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.rocketmq</groupId>\n            <artifactId>rocketmq-spring-boot-starter</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <!-- 为什么要依赖 tenant 组件？\n                因为广播某个类型的用户时候，需要根据租户过滤下，避免广播到别的租户！\n            -->\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n            <scope>provided</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketProperties.java",
    "content": "package cn.iocoder.yudao.framework.websocket.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * WebSocket 配置项\n *\n * @author xingyu4j\n */\n@ConfigurationProperties(\"yudao.websocket\")\n@Data\n@Validated\npublic class WebSocketProperties {\n\n    /**\n     * WebSocket 的连接路径\n     */\n    @NotEmpty(message = \"WebSocket 的连接路径不能为空\")\n    private String path = \"/ws\";\n\n    /**\n     * 消息发送器的类型\n     *\n     * 可选值：local、redis、rocketmq、kafka、rabbitmq\n     */\n    @NotNull(message = \"WebSocket 的消息发送者不能为空\")\n    private String senderType = \"local\";\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/YudaoWebSocketAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.framework.websocket.config;\n\nimport cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration;\nimport cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;\nimport cn.iocoder.yudao.framework.websocket.core.handler.JsonWebSocketMessageHandler;\nimport cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;\nimport cn.iocoder.yudao.framework.websocket.core.security.LoginUserHandshakeInterceptor;\nimport cn.iocoder.yudao.framework.websocket.core.security.WebSocketAuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageConsumer;\nimport cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.sender.local.LocalWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.sender.rabbitmq.RabbitMQWebSocketMessageConsumer;\nimport cn.iocoder.yudao.framework.websocket.core.sender.rabbitmq.RabbitMQWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.sender.redis.RedisWebSocketMessageConsumer;\nimport cn.iocoder.yudao.framework.websocket.core.sender.redis.RedisWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.sender.rocketmq.RocketMQWebSocketMessageConsumer;\nimport cn.iocoder.yudao.framework.websocket.core.sender.rocketmq.RocketMQWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionHandlerDecorator;\nimport cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;\nimport cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManagerImpl;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.springframework.amqp.core.TopicExchange;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.config.annotation.EnableWebSocket;\nimport org.springframework.web.socket.config.annotation.WebSocketConfigurer;\nimport org.springframework.web.socket.server.HandshakeInterceptor;\n\nimport java.util.List;\n\n/**\n * WebSocket 自动配置\n *\n * @author xingyu4j\n */\n@AutoConfiguration(before = YudaoRedisMQConsumerAutoConfiguration.class) // before YudaoRedisMQConsumerAutoConfiguration 的原因是，需要保证 RedisWebSocketMessageConsumer 先创建，才能创建 RedisMessageListenerContainer\n@EnableWebSocket // 开启 websocket\n@ConditionalOnProperty(prefix = \"yudao.websocket\", value = \"enable\", matchIfMissing = true) // 允许使用 yudao.websocket.enable=false 禁用 websocket\n@EnableConfigurationProperties(WebSocketProperties.class)\npublic class YudaoWebSocketAutoConfiguration {\n\n    @Bean\n    public WebSocketConfigurer webSocketConfigurer(HandshakeInterceptor[] handshakeInterceptors,\n                                                   WebSocketHandler webSocketHandler,\n                                                   WebSocketProperties webSocketProperties) {\n        return registry -> registry\n                // 添加 WebSocketHandler\n                .addHandler(webSocketHandler, webSocketProperties.getPath())\n                .addInterceptors(handshakeInterceptors)\n                // 允许跨域，否则前端连接会直接断开\n                .setAllowedOriginPatterns(\"*\");\n    }\n\n    @Bean\n    public HandshakeInterceptor handshakeInterceptor() {\n        return new LoginUserHandshakeInterceptor();\n    }\n\n    @Bean\n    public WebSocketHandler webSocketHandler(WebSocketSessionManager sessionManager,\n                                             List<? extends WebSocketMessageListener<?>> messageListeners) {\n        // 1. 创建 JsonWebSocketMessageHandler 对象，处理消息\n        JsonWebSocketMessageHandler messageHandler = new JsonWebSocketMessageHandler(messageListeners);\n        // 2. 创建 WebSocketSessionHandlerDecorator 对象，处理连接\n        return new WebSocketSessionHandlerDecorator(messageHandler, sessionManager);\n    }\n\n    @Bean\n    public WebSocketSessionManager webSocketSessionManager() {\n        return new WebSocketSessionManagerImpl();\n    }\n\n    @Bean\n    public WebSocketAuthorizeRequestsCustomizer webSocketAuthorizeRequestsCustomizer(WebSocketProperties webSocketProperties) {\n        return new WebSocketAuthorizeRequestsCustomizer(webSocketProperties);\n    }\n\n    // ==================== Sender 相关 ====================\n\n    @Configuration\n    @ConditionalOnProperty(prefix = \"yudao.websocket\", name = \"sender-type\", havingValue = \"local\")\n    public class LocalWebSocketMessageSenderConfiguration {\n\n        @Bean\n        public LocalWebSocketMessageSender localWebSocketMessageSender(WebSocketSessionManager sessionManager) {\n            return new LocalWebSocketMessageSender(sessionManager);\n        }\n\n    }\n\n    @Configuration\n    @ConditionalOnProperty(prefix = \"yudao.websocket\", name = \"sender-type\", havingValue = \"redis\")\n    public class RedisWebSocketMessageSenderConfiguration {\n\n        @Bean\n        public RedisWebSocketMessageSender redisWebSocketMessageSender(WebSocketSessionManager sessionManager,\n                                                                       RedisMQTemplate redisMQTemplate) {\n            return new RedisWebSocketMessageSender(sessionManager, redisMQTemplate);\n        }\n\n        @Bean\n        public RedisWebSocketMessageConsumer redisWebSocketMessageConsumer(\n                RedisWebSocketMessageSender redisWebSocketMessageSender) {\n            return new RedisWebSocketMessageConsumer(redisWebSocketMessageSender);\n        }\n\n    }\n\n    @Configuration\n    @ConditionalOnProperty(prefix = \"yudao.websocket\", name = \"sender-type\", havingValue = \"rocketmq\")\n    public class RocketMQWebSocketMessageSenderConfiguration {\n\n        @Bean\n        public RocketMQWebSocketMessageSender rocketMQWebSocketMessageSender(\n                WebSocketSessionManager sessionManager, RocketMQTemplate rocketMQTemplate,\n                @Value(\"${yudao.websocket.sender-rocketmq.topic}\") String topic) {\n            return new RocketMQWebSocketMessageSender(sessionManager, rocketMQTemplate, topic);\n        }\n\n        @Bean\n        public RocketMQWebSocketMessageConsumer rocketMQWebSocketMessageConsumer(\n                RocketMQWebSocketMessageSender rocketMQWebSocketMessageSender) {\n            return new RocketMQWebSocketMessageConsumer(rocketMQWebSocketMessageSender);\n        }\n\n    }\n\n    @Configuration\n    @ConditionalOnProperty(prefix = \"yudao.websocket\", name = \"sender-type\", havingValue = \"rabbitmq\")\n    public class RabbitMQWebSocketMessageSenderConfiguration {\n\n        @Bean\n        public RabbitMQWebSocketMessageSender rabbitMQWebSocketMessageSender(\n                WebSocketSessionManager sessionManager, RabbitTemplate rabbitTemplate,\n                TopicExchange websocketTopicExchange) {\n            return new RabbitMQWebSocketMessageSender(sessionManager, rabbitTemplate, websocketTopicExchange);\n        }\n\n        @Bean\n        public RabbitMQWebSocketMessageConsumer rabbitMQWebSocketMessageConsumer(\n                RabbitMQWebSocketMessageSender rabbitMQWebSocketMessageSender) {\n            return new RabbitMQWebSocketMessageConsumer(rabbitMQWebSocketMessageSender);\n        }\n\n        /**\n         * 创建 Topic Exchange\n         */\n        @Bean\n        public TopicExchange websocketTopicExchange(@Value(\"${yudao.websocket.sender-rabbitmq.exchange}\") String exchange) {\n            return new TopicExchange(exchange,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n    }\n\n    @Configuration\n    @ConditionalOnProperty(prefix = \"yudao.websocket\", name = \"sender-type\", havingValue = \"kafka\")\n    public class KafkaWebSocketMessageSenderConfiguration {\n\n        @Bean\n        public KafkaWebSocketMessageSender kafkaWebSocketMessageSender(\n                WebSocketSessionManager sessionManager, KafkaTemplate<Object, Object> kafkaTemplate,\n                @Value(\"${yudao.websocket.sender-kafka.topic}\") String topic) {\n            return new KafkaWebSocketMessageSender(sessionManager, kafkaTemplate, topic);\n        }\n\n        @Bean\n        public KafkaWebSocketMessageConsumer kafkaWebSocketMessageConsumer(\n                KafkaWebSocketMessageSender kafkaWebSocketMessageSender) {\n            return new KafkaWebSocketMessageConsumer(kafkaWebSocketMessageSender);\n        }\n\n    }\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/handler/JsonWebSocketMessageHandler.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.handler;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.core.util.TypeUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;\nimport cn.iocoder.yudao.framework.websocket.core.message.JsonWebSocketMessage;\nimport cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.WebSocketSession;\nimport org.springframework.web.socket.handler.TextWebSocketHandler;\n\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * JSON 格式 {@link WebSocketHandler} 实现类\n *\n * 基于 {@link JsonWebSocketMessage#getType()} 消息类型，调度到对应的 {@link WebSocketMessageListener} 监听器。\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class JsonWebSocketMessageHandler extends TextWebSocketHandler {\n\n    /**\n     * type 与 WebSocketMessageListener 的映射\n     */\n    private final Map<String, WebSocketMessageListener<Object>> listeners = new HashMap<>();\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    public JsonWebSocketMessageHandler(List<? extends WebSocketMessageListener> listenersList) {\n        listenersList.forEach((Consumer<WebSocketMessageListener>)\n                listener -> listeners.put(listener.getType(), listener));\n    }\n\n    @Override\n    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {\n        // 1.1 空消息，跳过\n        if (message.getPayloadLength() == 0) {\n            return;\n        }\n        // 1.2 ping 心跳消息，直接返回 pong 消息。\n        if (message.getPayloadLength() == 4 && Objects.equals(message.getPayload(), \"ping\")) {\n            session.sendMessage(new TextMessage(\"pong\"));\n            return;\n        }\n\n        // 2.1 解析消息\n        try {\n            JsonWebSocketMessage jsonMessage = JsonUtils.parseObject(message.getPayload(), JsonWebSocketMessage.class);\n            if (jsonMessage == null) {\n                log.error(\"[handleTextMessage][session({}) message({}) 解析为空]\", session.getId(), message.getPayload());\n                return;\n            }\n            if (StrUtil.isEmpty(jsonMessage.getType())) {\n                log.error(\"[handleTextMessage][session({}) message({}) 类型为空]\", session.getId(), message.getPayload());\n                return;\n            }\n            // 2.2 获得对应的 WebSocketMessageListener\n            WebSocketMessageListener<Object> messageListener = listeners.get(jsonMessage.getType());\n            if (messageListener == null) {\n                log.error(\"[handleTextMessage][session({}) message({}) 监听器为空]\", session.getId(), message.getPayload());\n                return;\n            }\n            // 2.3 处理消息\n            Type type = TypeUtil.getTypeArgument(messageListener.getClass(), 0);\n            Object messageObj = JsonUtils.parseObject(jsonMessage.getContent(), type);\n            Long tenantId = WebSocketFrameworkUtils.getTenantId(session);\n            TenantUtils.execute(tenantId, () -> messageListener.onMessage(session, messageObj));\n        } catch (Throwable ex) {\n            log.error(\"[handleTextMessage][session({}) message({}) 处理异常]\", session.getId(), message.getPayload());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/listener/WebSocketMessageListener.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.listener;\n\nimport cn.iocoder.yudao.framework.websocket.core.message.JsonWebSocketMessage;\nimport org.springframework.web.socket.WebSocketSession;\n\n/**\n * WebSocket 消息监听器接口\n *\n * 目的：前端发送消息给后端后，处理对应 {@link #getType()} 类型的消息\n *\n * @param <T> 泛型，消息类型\n */\npublic interface WebSocketMessageListener<T> {\n\n    /**\n     * 处理消息\n     *\n     * @param session Session\n     * @param message 消息\n     */\n    void onMessage(WebSocketSession session, T message);\n\n    /**\n     * 获得消息类型\n     *\n     * @see JsonWebSocketMessage#getType()\n     * @return 消息类型\n     */\n    String getType();\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/message/JsonWebSocketMessage.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.message;\n\nimport cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * JSON 格式的 WebSocket 消息帧\n *\n * @author 芋道源码\n */\n@Data\npublic class JsonWebSocketMessage implements Serializable {\n\n    /**\n     * 消息类型\n     *\n     * 目的：用于分发到对应的 {@link WebSocketMessageListener} 实现类\n     */\n    private String type;\n    /**\n     * 消息内容\n     *\n     * 要求 JSON 对象\n     */\n    private String content;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/security/LoginUserHandshakeInterceptor.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.security;\n\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.WebSocketSession;\nimport org.springframework.web.socket.server.HandshakeInterceptor;\n\nimport java.util.Map;\n\n/**\n * 登录用户的 {@link HandshakeInterceptor} 实现类\n *\n * 流程如下：\n * 1. 前端连接 websocket 时，会通过拼接 ?token={token} 到 ws:// 连接后，这样它可以被 {@link TokenAuthenticationFilter} 所认证通过\n * 2. {@link LoginUserHandshakeInterceptor} 负责把 {@link LoginUser} 添加到 {@link WebSocketSession} 中\n *\n * @author 芋道源码\n */\npublic class LoginUserHandshakeInterceptor implements HandshakeInterceptor {\n\n    @Override\n    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,\n                                   WebSocketHandler wsHandler, Map<String, Object> attributes) {\n        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();\n        if (loginUser != null) {\n            WebSocketFrameworkUtils.setLoginUser(loginUser, attributes);\n        }\n        return true;\n    }\n\n    @Override\n    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,\n                               WebSocketHandler wsHandler, Exception exception) {\n        // do nothing\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/security/WebSocketAuthorizeRequestsCustomizer.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.security;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.framework.websocket.config.WebSocketProperties;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\n/**\n * WebSocket 的权限自定义\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\npublic class WebSocketAuthorizeRequestsCustomizer extends AuthorizeRequestsCustomizer {\n\n    private final WebSocketProperties webSocketProperties;\n\n    @Override\n    public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n        registry.requestMatchers(webSocketProperties.getPath()).permitAll();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/AbstractWebSocketMessageSender.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.websocket.core.message.JsonWebSocketMessage;\nimport cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketSession;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * WebSocketMessageSender 实现类\n *\n * @author 芋道源码\n */\n@Slf4j\n@RequiredArgsConstructor\npublic abstract class AbstractWebSocketMessageSender implements WebSocketMessageSender {\n\n    private final WebSocketSessionManager sessionManager;\n\n    @Override\n    public void send(Integer userType, Long userId, String messageType, String messageContent) {\n        send(null, userType, userId, messageType, messageContent);\n    }\n\n    @Override\n    public void send(Integer userType, String messageType, String messageContent) {\n        send(null, userType, null, messageType, messageContent);\n    }\n\n    @Override\n    public void send(String sessionId, String messageType, String messageContent) {\n        send(sessionId, null, null, messageType, messageContent);\n    }\n\n    /**\n     * 发送消息\n     *\n     * @param sessionId Session 编号\n     * @param userType 用户类型\n     * @param userId 用户编号\n     * @param messageType 消息类型\n     * @param messageContent 消息内容\n     */\n    public void send(String sessionId, Integer userType, Long userId, String messageType, String messageContent) {\n        // 1. 获得 Session 列表\n        List<WebSocketSession> sessions = Collections.emptyList();\n        if (StrUtil.isNotEmpty(sessionId)) {\n            WebSocketSession session = sessionManager.getSession(sessionId);\n            if (session != null) {\n                sessions = Collections.singletonList(session);\n            }\n        } else if (userType != null && userId != null) {\n            sessions = (List<WebSocketSession>) sessionManager.getSessionList(userType, userId);\n        } else if (userType != null) {\n            sessions = (List<WebSocketSession>) sessionManager.getSessionList(userType);\n        }\n        if (CollUtil.isEmpty(sessions)) {\n            if (log.isDebugEnabled()) {\n                log.debug(\"[send][sessionId({}) userType({}) userId({}) messageType({}) messageContent({}) 未匹配到会话]\",\n                        sessionId, userType, userId, messageType, messageContent);\n            }\n        }\n        // 2. 执行发送\n        doSend(sessions, messageType, messageContent);\n    }\n\n    /**\n     * 发送消息的具体实现\n     *\n     * @param sessions Session 列表\n     * @param messageType 消息类型\n     * @param messageContent 消息内容\n     */\n    public void doSend(Collection<WebSocketSession> sessions, String messageType, String messageContent) {\n        JsonWebSocketMessage message = new JsonWebSocketMessage().setType(messageType).setContent(messageContent);\n        String payload = JsonUtils.toJsonString(message); // 关键，使用 JSON 序列化\n        sessions.forEach(session -> {\n            // 1. 各种校验，保证 Session 可以被发送\n            if (session == null) {\n                log.error(\"[doSend][session 为空, message({})]\", message);\n                return;\n            }\n            if (!session.isOpen()) {\n                log.error(\"[doSend][session({}) 已关闭, message({})]\", session.getId(), message);\n                return;\n            }\n            // 2. 执行发送\n            try {\n                session.sendMessage(new TextMessage(payload));\n                log.info(\"[doSend][session({}) 发送消息成功，message({})]\", session.getId(), message);\n            } catch (IOException ex) {\n                log.error(\"[doSend][session({}) 发送消息失败，message({})]\", session.getId(), message, ex);\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/WebSocketMessageSender.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\n\n/**\n * WebSocket 消息的发送器接口\n *\n * @author 芋道源码\n */\npublic interface WebSocketMessageSender {\n\n    /**\n     * 发送消息给指定用户\n     *\n     * @param userType 用户类型\n     * @param userId 用户编号\n     * @param messageType 消息类型\n     * @param messageContent 消息内容，JSON 格式\n     */\n    void send(Integer userType, Long userId, String messageType, String messageContent);\n\n    /**\n     * 发送消息给指定用户类型\n     *\n     * @param userType 用户类型\n     * @param messageType 消息类型\n     * @param messageContent 消息内容，JSON 格式\n     */\n    void send(Integer userType, String messageType, String messageContent);\n\n    /**\n     * 发送消息给指定 Session\n     *\n     * @param sessionId Session 编号\n     * @param messageType 消息类型\n     * @param messageContent 消息内容，JSON 格式\n     */\n    void send(String sessionId, String messageType, String messageContent);\n\n    default void sendObject(Integer userType, Long userId, String messageType, Object messageContent) {\n        send(userType, userId, messageType, JsonUtils.toJsonString(messageContent));\n    }\n\n    default void sendObject(Integer userType, String messageType, Object messageContent) {\n        send(userType, messageType, JsonUtils.toJsonString(messageContent));\n    }\n\n    default void sendObject(String sessionId, String messageType, Object messageContent) {\n        send(sessionId, messageType, JsonUtils.toJsonString(messageContent));\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/kafka/KafkaWebSocketMessage.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.kafka;\n\nimport lombok.Data;\n\n/**\n * Kafka 广播 WebSocket 的消息\n *\n * @author 芋道源码\n */\n@Data\npublic class KafkaWebSocketMessage {\n\n    /**\n     * Session 编号\n     */\n    private String sessionId;\n    /**\n     * 用户类型\n     */\n    private Integer userType;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 消息类型\n     */\n    private String messageType;\n    /**\n     * 消息内容\n     */\n    private String messageContent;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/kafka/KafkaWebSocketMessageConsumer.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.kafka;\n\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.kafka.annotation.KafkaListener;\n\n/**\n * {@link KafkaWebSocketMessage} 广播消息的消费者，真正把消息发送出去\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\npublic class KafkaWebSocketMessageConsumer {\n\n    private final KafkaWebSocketMessageSender kafkaWebSocketMessageSender;\n\n    @RabbitHandler\n    @KafkaListener(\n            topics = \"${yudao.websocket.sender-kafka.topic}\",\n            // 在 Group 上，使用 UUID 生成其后缀。这样，启动的 Consumer 的 Group 不同，以达到广播消费的目的\n            groupId = \"${yudao.websocket.sender-kafka.consumer-group}\" + \"-\" + \"#{T(java.util.UUID).randomUUID()}\")\n    public void onMessage(KafkaWebSocketMessage message) {\n        kafkaWebSocketMessageSender.send(message.getSessionId(),\n                message.getUserType(), message.getUserId(),\n                message.getMessageType(), message.getMessageContent());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/kafka/KafkaWebSocketMessageSender.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.kafka;\n\nimport cn.iocoder.yudao.framework.websocket.core.sender.AbstractWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.kafka.core.KafkaTemplate;\n\nimport java.util.concurrent.ExecutionException;\n\n/**\n * 基于 Kafka 的 {@link WebSocketMessageSender} 实现类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class KafkaWebSocketMessageSender extends AbstractWebSocketMessageSender {\n\n    private final KafkaTemplate<Object, Object> kafkaTemplate;\n\n    private final String topic;\n\n    public KafkaWebSocketMessageSender(WebSocketSessionManager sessionManager,\n                                       KafkaTemplate<Object, Object> kafkaTemplate,\n                                       String topic) {\n        super(sessionManager);\n        this.kafkaTemplate = kafkaTemplate;\n        this.topic = topic;\n    }\n\n    @Override\n    public void send(Integer userType, Long userId, String messageType, String messageContent) {\n        sendKafkaMessage(null, userId, userType, messageType, messageContent);\n    }\n\n    @Override\n    public void send(Integer userType, String messageType, String messageContent) {\n        sendKafkaMessage(null, null, userType, messageType, messageContent);\n    }\n\n    @Override\n    public void send(String sessionId, String messageType, String messageContent) {\n        sendKafkaMessage(sessionId, null, null, messageType, messageContent);\n    }\n\n    /**\n     * 通过 Kafka 广播消息\n     *\n     * @param sessionId Session 编号\n     * @param userId 用户编号\n     * @param userType 用户类型\n     * @param messageType 消息类型\n     * @param messageContent 消息内容\n     */\n    private void sendKafkaMessage(String sessionId, Long userId, Integer userType,\n                                  String messageType, String messageContent) {\n        KafkaWebSocketMessage mqMessage = new KafkaWebSocketMessage()\n                .setSessionId(sessionId).setUserId(userId).setUserType(userType)\n                .setMessageType(messageType).setMessageContent(messageContent);\n        try {\n            kafkaTemplate.send(topic, mqMessage).get();\n        } catch (InterruptedException | ExecutionException e) {\n            log.error(\"[sendKafkaMessage][发送消息({}) 到 Kafka 失败]\", mqMessage, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/local/LocalWebSocketMessageSender.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.local;\n\nimport cn.iocoder.yudao.framework.websocket.core.sender.AbstractWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;\n\n/**\n * 本地的 {@link WebSocketMessageSender} 实现类\n *\n * 注意：仅仅适合单机场景！！！\n *\n * @author 芋道源码\n */\npublic class LocalWebSocketMessageSender extends AbstractWebSocketMessageSender {\n\n    public LocalWebSocketMessageSender(WebSocketSessionManager sessionManager) {\n        super(sessionManager);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessage.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.rabbitmq;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * RabbitMQ 广播 WebSocket 的消息\n *\n * @author 芋道源码\n */\n@Data\npublic class RabbitMQWebSocketMessage implements Serializable {\n\n    /**\n     * Session 编号\n     */\n    private String sessionId;\n    /**\n     * 用户类型\n     */\n    private Integer userType;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 消息类型\n     */\n    private String messageType;\n    /**\n     * 消息内容\n     */\n    private String messageContent;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessageConsumer.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.rabbitmq;\n\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.amqp.core.ExchangeTypes;\nimport org.springframework.amqp.rabbit.annotation.*;\n\n/**\n * {@link RabbitMQWebSocketMessage} 广播消息的消费者，真正把消息发送出去\n *\n * @author 芋道源码\n */\n@RabbitListener(\n        bindings = @QueueBinding(\n                value = @Queue(\n                        // 在 Queue 的名字上，使用 UUID 生成其后缀。这样，启动的 Consumer 的 Queue 不同，以达到广播消费的目的\n                        name = \"${yudao.websocket.sender-rabbitmq.queue}\" + \"-\" + \"#{T(java.util.UUID).randomUUID()}\",\n                        // Consumer 关闭时，该队列就可以被自动删除了\n                        autoDelete = \"true\"\n                ),\n                exchange = @Exchange(\n                        name = \"${yudao.websocket.sender-rabbitmq.exchange}\",\n                        type = ExchangeTypes.TOPIC,\n                        declare = \"false\"\n                )\n        )\n)\n@RequiredArgsConstructor\npublic class RabbitMQWebSocketMessageConsumer {\n\n    private final RabbitMQWebSocketMessageSender rabbitMQWebSocketMessageSender;\n\n    @RabbitHandler\n    public void onMessage(RabbitMQWebSocketMessage message) {\n        rabbitMQWebSocketMessageSender.send(message.getSessionId(),\n                message.getUserType(), message.getUserId(),\n                message.getMessageType(), message.getMessageContent());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessageSender.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.rabbitmq;\n\nimport cn.iocoder.yudao.framework.websocket.core.sender.AbstractWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.amqp.core.TopicExchange;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\n\n/**\n * 基于 RabbitMQ 的 {@link WebSocketMessageSender} 实现类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class RabbitMQWebSocketMessageSender extends AbstractWebSocketMessageSender {\n\n    private final RabbitTemplate rabbitTemplate;\n\n    private final TopicExchange topicExchange;\n\n    public RabbitMQWebSocketMessageSender(WebSocketSessionManager sessionManager,\n                                          RabbitTemplate rabbitTemplate,\n                                          TopicExchange topicExchange) {\n        super(sessionManager);\n        this.rabbitTemplate = rabbitTemplate;\n        this.topicExchange = topicExchange;\n    }\n\n    @Override\n    public void send(Integer userType, Long userId, String messageType, String messageContent) {\n        sendRabbitMQMessage(null, userId, userType, messageType, messageContent);\n    }\n\n    @Override\n    public void send(Integer userType, String messageType, String messageContent) {\n        sendRabbitMQMessage(null, null, userType, messageType, messageContent);\n    }\n\n    @Override\n    public void send(String sessionId, String messageType, String messageContent) {\n        sendRabbitMQMessage(sessionId, null, null, messageType, messageContent);\n    }\n\n    /**\n     * 通过 RabbitMQ 广播消息\n     *\n     * @param sessionId Session 编号\n     * @param userId 用户编号\n     * @param userType 用户类型\n     * @param messageType 消息类型\n     * @param messageContent 消息内容\n     */\n    private void sendRabbitMQMessage(String sessionId, Long userId, Integer userType,\n                                     String messageType, String messageContent) {\n        RabbitMQWebSocketMessage mqMessage = new RabbitMQWebSocketMessage()\n                .setSessionId(sessionId).setUserId(userId).setUserType(userType)\n                .setMessageType(messageType).setMessageContent(messageContent);\n        rabbitTemplate.convertAndSend(topicExchange.getName(), null, mqMessage);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/redis/RedisWebSocketMessage.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.redis;\n\nimport cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessage;\nimport lombok.Data;\n\n/**\n * Redis 广播 WebSocket 的消息\n */\n@Data\npublic class RedisWebSocketMessage extends AbstractRedisChannelMessage {\n\n    /**\n     * Session 编号\n     */\n    private String sessionId;\n    /**\n     * 用户类型\n     */\n    private Integer userType;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 消息类型\n     */\n    private String messageType;\n    /**\n     * 消息内容\n     */\n    private String messageContent;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/redis/RedisWebSocketMessageConsumer.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.redis;\n\nimport cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener;\nimport lombok.RequiredArgsConstructor;\n\n/**\n * {@link RedisWebSocketMessage} 广播消息的消费者，真正把消息发送出去\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\npublic class RedisWebSocketMessageConsumer extends AbstractRedisChannelMessageListener<RedisWebSocketMessage> {\n\n    private final RedisWebSocketMessageSender redisWebSocketMessageSender;\n\n    @Override\n    public void onMessage(RedisWebSocketMessage message) {\n        redisWebSocketMessageSender.send(message.getSessionId(),\n                message.getUserType(), message.getUserId(),\n                message.getMessageType(), message.getMessageContent());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/redis/RedisWebSocketMessageSender.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.redis;\n\nimport cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;\nimport cn.iocoder.yudao.framework.websocket.core.sender.AbstractWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * 基于 Redis 的 {@link WebSocketMessageSender} 实现类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class RedisWebSocketMessageSender extends AbstractWebSocketMessageSender {\n\n    private final RedisMQTemplate redisMQTemplate;\n\n    public RedisWebSocketMessageSender(WebSocketSessionManager sessionManager,\n                                       RedisMQTemplate redisMQTemplate) {\n        super(sessionManager);\n        this.redisMQTemplate = redisMQTemplate;\n    }\n\n    @Override\n    public void send(Integer userType, Long userId, String messageType, String messageContent) {\n        sendRedisMessage(null, userId, userType, messageType, messageContent);\n    }\n\n    @Override\n    public void send(Integer userType, String messageType, String messageContent) {\n        sendRedisMessage(null, null, userType, messageType, messageContent);\n    }\n\n    @Override\n    public void send(String sessionId, String messageType, String messageContent) {\n        sendRedisMessage(sessionId, null, null, messageType, messageContent);\n    }\n\n    /**\n     * 通过 Redis 广播消息\n     *\n     * @param sessionId Session 编号\n     * @param userId 用户编号\n     * @param userType 用户类型\n     * @param messageType 消息类型\n     * @param messageContent 消息内容\n     */\n    private void sendRedisMessage(String sessionId, Long userId, Integer userType,\n                                  String messageType, String messageContent) {\n        RedisWebSocketMessage mqMessage = new RedisWebSocketMessage()\n                .setSessionId(sessionId).setUserId(userId).setUserType(userType)\n                .setMessageType(messageType).setMessageContent(messageContent);\n        redisMQTemplate.send(mqMessage);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessage.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.rocketmq;\n\nimport lombok.Data;\n\n/**\n * RocketMQ 广播 WebSocket 的消息\n *\n * @author 芋道源码\n */\n@Data\npublic class RocketMQWebSocketMessage {\n\n    /**\n     * Session 编号\n     */\n    private String sessionId;\n    /**\n     * 用户类型\n     */\n    private Integer userType;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 消息类型\n     */\n    private String messageType;\n    /**\n     * 消息内容\n     */\n    private String messageContent;\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessageConsumer.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.rocketmq;\n\nimport lombok.RequiredArgsConstructor;\nimport org.apache.rocketmq.spring.annotation.MessageModel;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\n\n/**\n * {@link RocketMQWebSocketMessage} 广播消息的消费者，真正把消息发送出去\n *\n * @author 芋道源码\n */\n@RocketMQMessageListener( // 重点：添加 @RocketMQMessageListener 注解，声明消费的 topic\n        topic = \"${yudao.websocket.sender-rocketmq.topic}\",\n        consumerGroup = \"${yudao.websocket.sender-rocketmq.consumer-group}\",\n        messageModel = MessageModel.BROADCASTING // 设置为广播模式，保证每个实例都能收到消息\n)\n@RequiredArgsConstructor\npublic class RocketMQWebSocketMessageConsumer implements RocketMQListener<RocketMQWebSocketMessage> {\n\n    private final RocketMQWebSocketMessageSender rocketMQWebSocketMessageSender;\n\n    @Override\n    public void onMessage(RocketMQWebSocketMessage message) {\n        rocketMQWebSocketMessageSender.send(message.getSessionId(),\n                message.getUserType(), message.getUserId(),\n                message.getMessageType(), message.getMessageContent());\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessageSender.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.sender.rocketmq;\n\nimport cn.iocoder.yudao.framework.websocket.core.sender.AbstractWebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\n\n/**\n * 基于 RocketMQ 的 {@link WebSocketMessageSender} 实现类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class RocketMQWebSocketMessageSender extends AbstractWebSocketMessageSender {\n\n    private final RocketMQTemplate rocketMQTemplate;\n\n    private final String topic;\n\n    public RocketMQWebSocketMessageSender(WebSocketSessionManager sessionManager,\n                                          RocketMQTemplate rocketMQTemplate,\n                                          String topic) {\n        super(sessionManager);\n        this.rocketMQTemplate = rocketMQTemplate;\n        this.topic = topic;\n    }\n\n    @Override\n    public void send(Integer userType, Long userId, String messageType, String messageContent) {\n        sendRocketMQMessage(null, userId, userType, messageType, messageContent);\n    }\n\n    @Override\n    public void send(Integer userType, String messageType, String messageContent) {\n        sendRocketMQMessage(null, null, userType, messageType, messageContent);\n    }\n\n    @Override\n    public void send(String sessionId, String messageType, String messageContent) {\n        sendRocketMQMessage(sessionId, null, null, messageType, messageContent);\n    }\n\n    /**\n     * 通过 RocketMQ 广播消息\n     *\n     * @param sessionId Session 编号\n     * @param userId 用户编号\n     * @param userType 用户类型\n     * @param messageType 消息类型\n     * @param messageContent 消息内容\n     */\n    private void sendRocketMQMessage(String sessionId, Long userId, Integer userType,\n                                     String messageType, String messageContent) {\n        RocketMQWebSocketMessage mqMessage = new RocketMQWebSocketMessage()\n                .setSessionId(sessionId).setUserId(userId).setUserType(userType)\n                .setMessageType(messageType).setMessageContent(messageContent);\n        rocketMQTemplate.syncSend(topic, mqMessage);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/session/WebSocketSessionHandlerDecorator.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.session;\n\nimport org.springframework.web.socket.CloseStatus;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.WebSocketSession;\nimport org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator;\nimport org.springframework.web.socket.handler.WebSocketHandlerDecorator;\n\n/**\n * {@link WebSocketHandler} 的装饰类，实现了以下功能：\n *\n * 1. {@link WebSocketSession} 连接或关闭时，使用 {@link #sessionManager} 进行管理\n * 2. 封装 {@link WebSocketSession} 支持并发操作\n *\n * @author 芋道源码\n */\npublic class WebSocketSessionHandlerDecorator extends WebSocketHandlerDecorator {\n\n    /**\n     * 发送时间的限制，单位：毫秒\n     */\n    private static final Integer SEND_TIME_LIMIT = 1000 * 5;\n    /**\n     * 发送消息缓冲上线，单位：bytes\n     */\n    private static final Integer BUFFER_SIZE_LIMIT = 1024 * 100;\n\n    private final WebSocketSessionManager sessionManager;\n\n    public WebSocketSessionHandlerDecorator(WebSocketHandler delegate,\n                                            WebSocketSessionManager sessionManager) {\n        super(delegate);\n        this.sessionManager = sessionManager;\n    }\n\n    @Override\n    public void afterConnectionEstablished(WebSocketSession session) {\n        // 实现 session 支持并发，可参考 https://blog.csdn.net/abu935009066/article/details/131218149\n        session = new ConcurrentWebSocketSessionDecorator(session, SEND_TIME_LIMIT, BUFFER_SIZE_LIMIT);\n        // 添加到 WebSocketSessionManager 中\n        sessionManager.addSession(session);\n    }\n\n    @Override\n    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) {\n        sessionManager.removeSession(session);\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/session/WebSocketSessionManager.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.session;\n\nimport org.springframework.web.socket.WebSocketSession;\n\nimport java.util.Collection;\n\n/**\n * {@link WebSocketSession} 管理器的接口\n *\n * @author 芋道源码\n */\npublic interface WebSocketSessionManager {\n\n    /**\n     * 添加 Session\n     *\n     * @param session Session\n     */\n    void addSession(WebSocketSession session);\n\n    /**\n     * 移除 Session\n     *\n     * @param session Session\n     */\n    void removeSession(WebSocketSession session);\n\n    /**\n     * 获得指定编号的 Session\n     *\n     * @param id Session 编号\n     * @return Session\n     */\n    WebSocketSession getSession(String id);\n\n    /**\n     * 获得指定用户类型的 Session 列表\n     *\n     * @param userType 用户类型\n     * @return Session 列表\n     */\n    Collection<WebSocketSession> getSessionList(Integer userType);\n\n    /**\n     * 获得指定用户编号的 Session 列表\n     *\n     * @param userType 用户类型\n     * @param userId 用户编号\n     * @return Session 列表\n     */\n    Collection<WebSocketSession> getSessionList(Integer userType, Long userId);\n\n}"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/session/WebSocketSessionManagerImpl.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.session;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;\nimport org.springframework.web.socket.WebSocketSession;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\n/**\n * 默认的 {@link WebSocketSessionManager} 实现类\n *\n * @author 芋道源码\n */\npublic class WebSocketSessionManagerImpl implements WebSocketSessionManager {\n\n    /**\n     * id 与 WebSocketSession 映射\n     *\n     * key：Session 编号\n     */\n    private final ConcurrentMap<String, WebSocketSession> idSessions = new ConcurrentHashMap<>();\n\n    /**\n     * user 与 WebSocketSession 映射\n     *\n     * key1：用户类型\n     * key2：用户编号\n     */\n    private final ConcurrentMap<Integer, ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>>> userSessions\n            = new ConcurrentHashMap<>();\n\n    @Override\n    public void addSession(WebSocketSession session) {\n        // 添加到 idSessions 中\n        idSessions.put(session.getId(), session);\n        // 添加到 userSessions 中\n        LoginUser user = WebSocketFrameworkUtils.getLoginUser(session);\n        if (user == null) {\n            return;\n        }\n        ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>> userSessionsMap = userSessions.get(user.getUserType());\n        if (userSessionsMap == null) {\n            userSessionsMap = new ConcurrentHashMap<>();\n            if (userSessions.putIfAbsent(user.getUserType(), userSessionsMap) != null) {\n                userSessionsMap = userSessions.get(user.getUserType());\n            }\n        }\n        CopyOnWriteArrayList<WebSocketSession> sessions = userSessionsMap.get(user.getId());\n        if (sessions == null) {\n            sessions = new CopyOnWriteArrayList<>();\n            if (userSessionsMap.putIfAbsent(user.getId(), sessions) != null) {\n                sessions = userSessionsMap.get(user.getId());\n            }\n        }\n        sessions.add(session);\n    }\n\n    @Override\n    public void removeSession(WebSocketSession session) {\n        // 移除从 idSessions 中\n        idSessions.remove(session.getId());\n        // 移除从 idSessions 中\n        LoginUser user = WebSocketFrameworkUtils.getLoginUser(session);\n        if (user == null) {\n            return;\n        }\n        ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>> userSessionsMap = userSessions.get(user.getUserType());\n        if (userSessionsMap == null) {\n            return;\n        }\n        CopyOnWriteArrayList<WebSocketSession> sessions = userSessionsMap.get(user.getId());\n        sessions.removeIf(session0 -> session0.getId().equals(session.getId()));\n        if (CollUtil.isEmpty(sessions)) {\n            userSessionsMap.remove(user.getId(), sessions);\n        }\n    }\n\n    @Override\n    public WebSocketSession getSession(String id) {\n        return idSessions.get(id);\n    }\n\n    @Override\n    public Collection<WebSocketSession> getSessionList(Integer userType) {\n        ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>> userSessionsMap = userSessions.get(userType);\n        if (CollUtil.isEmpty(userSessionsMap)) {\n            return new ArrayList<>();\n        }\n        LinkedList<WebSocketSession> result = new LinkedList<>(); // 避免扩容\n        Long contextTenantId = TenantContextHolder.getTenantId();\n        for (List<WebSocketSession> sessions : userSessionsMap.values()) {\n            if (CollUtil.isEmpty(sessions)) {\n                continue;\n            }\n            // 特殊：如果租户不匹配，则直接排除\n            if (contextTenantId != null) {\n                Long userTenantId = WebSocketFrameworkUtils.getTenantId(sessions.get(0));\n                if (!contextTenantId.equals(userTenantId)) {\n                    continue;\n                }\n            }\n            result.addAll(sessions);\n        }\n        return result;\n    }\n\n    @Override\n    public Collection<WebSocketSession> getSessionList(Integer userType, Long userId) {\n        ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>> userSessionsMap = userSessions.get(userType);\n        if (CollUtil.isEmpty(userSessionsMap)) {\n            return new ArrayList<>();\n        }\n        CopyOnWriteArrayList<WebSocketSession> sessions = userSessionsMap.get(userId);\n        return CollUtil.isNotEmpty(sessions) ? new ArrayList<>(sessions) : new ArrayList<>();\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/util/WebSocketFrameworkUtils.java",
    "content": "package cn.iocoder.yudao.framework.websocket.core.util;\n\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport org.springframework.web.socket.WebSocketSession;\n\nimport java.util.Map;\n\n/**\n * 专属于 web 包的工具类\n *\n * @author 芋道源码\n */\npublic class WebSocketFrameworkUtils {\n\n    public static final String ATTRIBUTE_LOGIN_USER = \"LOGIN_USER\";\n\n    /**\n     * 设置当前用户\n     *\n     * @param loginUser 登录用户\n     * @param attributes Session\n     */\n    public static void setLoginUser(LoginUser loginUser, Map<String, Object> attributes) {\n        attributes.put(ATTRIBUTE_LOGIN_USER, loginUser);\n    }\n\n    /**\n     * 获取当前用户\n     *\n     * @return 当前用户\n     */\n    public static LoginUser getLoginUser(WebSocketSession session) {\n        return (LoginUser) session.getAttributes().get(ATTRIBUTE_LOGIN_USER);\n    }\n\n    /**\n     * 获得当前用户的编号\n     *\n     * @return 用户编号\n     */\n    public static Long getLoginUserId(WebSocketSession session) {\n        LoginUser loginUser = getLoginUser(session);\n        return loginUser != null ? loginUser.getId() : null;\n    }\n\n    /**\n     * 获得当前用户的类型\n     *\n     * @return 用户编号\n     */\n    public static Integer getLoginUserType(WebSocketSession session) {\n        LoginUser loginUser = getLoginUser(session);\n        return loginUser != null ? loginUser.getUserType() : null;\n    }\n\n    /**\n     * 获得当前用户的租户编号\n     *\n     * @param session Session\n     * @return 租户编号\n     */\n    public static Long getTenantId(WebSocketSession session) {\n        LoginUser loginUser = getLoginUser(session);\n        return loginUser != null ? loginUser.getTenantId() : null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/package-info.java",
    "content": "/**\n * WebSocket 框架，支持多节点的广播\n */\npackage cn.iocoder.yudao.framework.websocket;\n"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.framework.websocket.config.YudaoWebSocketAutoConfiguration"
  },
  {
    "path": "yudao-framework/yudao-spring-boot-starter-websocket/《芋道 Spring Boot WebSocket 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/WebSocket/?yudao>\n"
  },
  {
    "path": "yudao-gateway/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:8-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-gateway\nWORKDIR /yudao-gateway\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-gateway.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48080 端口\nEXPOSE 48080\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-gateway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-gateway</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>API 服务网关，基于 Spring Cloud Gateway 实现</description>\n    <url>https://github.com/YunaiV/yudao-cloud</url>\n\n    <dependencies>\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-system-api</artifactId>\n            <version>${revision}</version>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springdoc</groupId>\n                    <artifactId>springdoc-openapi-webmvc-core</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!-- Gateway 网关相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>javax.servlet</groupId>\n            <artifactId>javax.servlet-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>javax.servlet</groupId>\n            <artifactId>javax.servlet-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.xiaoymin</groupId> <!-- 接口文档 -->\n            <artifactId>knife4j-gateway-spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-loadbalancer</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n        </dependency>\n\n        <dependency>\n            <!-- 必须引入，否则会有 https://gitee.com/yudaocode/yudao-cloud-mini/issues/IB5PXW 告警 -->\n            <groupId>com.github.ben-manes.caffeine</groupId>\n            <artifactId>caffeine</artifactId>\n        </dependency>\n    </dependencies>\n\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/GatewayServerApplication.java",
    "content": "package cn.iocoder.yudao.gateway;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayServerApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(GatewayServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/cors/CorsFilter.java",
    "content": "package cn.iocoder.yudao.gateway.filter.cors;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.cors.reactive.CorsUtils;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport reactor.core.publisher.Mono;\n\n/**\n * 跨域 Filter\n *\n * @author 芋道源码\n */\n@Component\npublic class CorsFilter implements WebFilter {\n\n    private static final String ALL = \"*\";\n    private static final String MAX_AGE = \"3600L\";\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n        // 非跨域请求，直接放行\n        ServerHttpRequest request = exchange.getRequest();\n        if (!CorsUtils.isCorsRequest(request)) {\n            return chain.filter(exchange);\n        }\n\n        // 设置跨域响应头\n        ServerHttpResponse response = exchange.getResponse();\n        HttpHeaders headers = response.getHeaders();\n        headers.add(\"Access-Control-Allow-Origin\", ALL);\n        headers.add(\"Access-Control-Allow-Methods\", ALL);\n        headers.add(\"Access-Control-Allow-Headers\", ALL);\n        headers.add(\"Access-Control-Max-Age\", MAX_AGE);\n        if (request.getMethod() == HttpMethod.OPTIONS) {\n            response.setStatusCode(HttpStatus.OK);\n            return Mono.empty();\n        }\n        return chain.filter(exchange);\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/grey/GrayLoadBalancer.java",
    "content": "package cn.iocoder.yudao.gateway.filter.grey;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.gateway.util.EnvUtils;\nimport com.alibaba.cloud.nacos.balancer.NacosBalancer;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.*;\nimport org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;\nimport org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;\nimport org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;\nimport org.springframework.http.HttpHeaders;\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\n\n/**\n * 灰度 {@link GrayLoadBalancer} 实现类\n *\n * 根据请求的 header[version] 匹配，筛选满足 metadata[version] 相等的服务实例列表，然后随机 + 权重进行选择一个\n * 1. 假如请求的 header[version] 为空，则不进行筛选，所有服务实例都进行选择\n * 2. 如果 metadata[version] 都不相等，则不进行筛选，所有服务实例都进行选择\n *\n * 注意，考虑到实现的简易，它的权重是使用 Nacos 的 nacos.weight，所以随机 + 权重也是基于 {@link NacosBalancer} 筛选。\n * 也就是说，如果你不使用 Nacos 作为注册中心，需要微调一下筛选的实现逻辑\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {\n\n    private static final String VERSION = \"version\";\n\n    /**\n     * 用于获取 serviceId 对应的服务实例的列表\n     */\n    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;\n    /**\n     * 需要获取的服务实例名\n     *\n     * 暂时用于打印 logger 日志\n     */\n    private final String serviceId;\n\n    @Override\n    public Mono<Response<ServiceInstance>> choose(Request request) {\n        // 获得 HttpHeaders 属性，实现从 header 中获取 version\n        HttpHeaders headers = ((RequestDataContext) request.getContext()).getClientRequest().getHeaders();\n        // 选择实例\n        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);\n        return supplier.get(request).next().map(list -> getInstanceResponse(list, headers));\n    }\n\n    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, HttpHeaders headers) {\n        // 如果服务实例为空，则直接返回\n        if (CollUtil.isEmpty(instances)) {\n            log.warn(\"[getInstanceResponse][serviceId({}) 服务实例列表为空]\", serviceId);\n            return new EmptyResponse();\n        }\n\n        // 筛选满足 version 条件的实例列表\n        String version = headers.getFirst(VERSION);\n        List<ServiceInstance> chooseInstances;\n        if (StrUtil.isEmpty(version)) {\n            chooseInstances = instances;\n        } else {\n            chooseInstances = CollectionUtils.filterList(instances, instance -> version.equals(instance.getMetadata().get(\"version\")));\n            if (CollUtil.isEmpty(chooseInstances)) {\n                log.warn(\"[getInstanceResponse][serviceId({}) 没有满足版本({})的服务实例列表，直接使用所有服务实例列表]\", serviceId, version);\n                chooseInstances = instances;\n            }\n        }\n\n        // 基于 tag 过滤实例列表\n        chooseInstances = filterTagServiceInstances(chooseInstances, headers);\n\n        // 随机 + 权重获取实例列表 TODO 芋艿：目前直接使用 Nacos 提供的方法，如果替换注册中心，需要重新失败该方法\n        return new DefaultResponse(NacosBalancer.getHostByRandomWeight3(chooseInstances));\n    }\n\n    /**\n     * 基于 tag 请求头，过滤匹配 tag 的服务实例列表\n     *\n     * copy from EnvLoadBalancerClient\n     *\n     * @param instances 服务实例列表\n     * @param headers 请求头\n     * @return 服务实例列表\n     */\n    private List<ServiceInstance> filterTagServiceInstances(List<ServiceInstance> instances, HttpHeaders headers) {\n        // 情况一，没有 tag 时，过滤掉有 tag 的节点。目的：避免 test 环境，打到本地有 tag 的实例\n        String tag = EnvUtils.getTag(headers);\n        if (StrUtil.isEmpty(tag)) {\n            List<ServiceInstance> chooseInstances = CollectionUtils.filterList(instances, instance -> StrUtil.isEmpty(EnvUtils.getTag(instance)));\n            // 【重要】补充说明：如果希望在 chooseInstances 为空时，不允许打到有 tag 的实例，可以取消注释下面的代码\n            if (CollUtil.isEmpty(chooseInstances)) {\n                log.warn(\"[filterTagServiceInstances][serviceId({}) 没有不带 tag 的服务实例列表，直接使用所有服务实例列表]\", serviceId);\n                chooseInstances = instances;\n            }\n            return chooseInstances;\n        }\n\n        // 情况二，有 tag 时，使用 tag 匹配服务实例\n        List<ServiceInstance> chooseInstances = CollectionUtils.filterList(instances, instance -> tag.equals(EnvUtils.getTag(instance)));\n        if (CollUtil.isEmpty(chooseInstances)) {\n            log.warn(\"[filterTagServiceInstances][serviceId({}) 没有满足 tag({}) 的服务实例列表，直接使用所有服务实例列表]\", serviceId, tag);\n            chooseInstances = instances;\n        }\n        return chooseInstances;\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/grey/GrayReactiveLoadBalancerClientFilter.java",
    "content": "package cn.iocoder.yudao.gateway.filter.grey;\n\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.*;\nimport org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;\nimport org.springframework.cloud.gateway.support.DelegatingServiceInstance;\nimport org.springframework.cloud.gateway.support.NotFoundException;\nimport org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;\nimport org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;\nimport org.springframework.core.Ordered;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\nimport java.net.URI;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.*;\n\n/**\n * 支持灰度功能的 {@link ReactiveLoadBalancerClientFilter} 实现类\n *\n * 由于 {@link ReactiveLoadBalancerClientFilter#choose(Request, String, Set)} 是 private 方法，无法进行重写。\n * 因此，这里只好 copy 它所有的代码，手动重写 choose 方法\n *\n * 具体的使用与实现原理，可阅读如下两个文章：\n * 1. https://www.jianshu.com/p/6db15bc0be8f\n * 2. https://cloud.tencent.com/developer/article/1620795\n *\n * @author 芋道源码\n */\n@Component\n@AllArgsConstructor\n@Slf4j\n@SuppressWarnings({\"JavadocReference\", \"rawtypes\", \"unchecked\", \"ConstantConditions\"})\npublic class GrayReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {\n\n    private final LoadBalancerClientFactory clientFactory;\n\n    private final GatewayLoadBalancerProperties properties;\n\n    @Override\n    public int getOrder() {\n        // https://github.com/YunaiV/yudao-cloud/pull/213\n        return ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER - 50;\n    }\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n        URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);\n        String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);\n        // 修改 by 芋道源码：将 lb 替换成 grayLb，表示灰度负载均衡\n        if (url == null || (!\"grayLb\".equals(url.getScheme()) && !\"grayLb\".equals(schemePrefix))) {\n            return chain.filter(exchange);\n        }\n        // preserve the original url\n        addOriginalRequestUrl(exchange, url);\n\n        if (log.isTraceEnabled()) {\n            log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + \" url before: \" + url);\n        }\n\n        URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);\n        String serviceId = requestUri.getHost();\n        Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator\n                .getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),\n                        RequestDataContext.class, ResponseData.class, ServiceInstance.class);\n        DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(\n                new RequestDataContext(new RequestData(exchange.getRequest()), getHint(serviceId)));\n        return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {\n\n                    if (!response.hasServer()) {\n                        supportedLifecycleProcessors.forEach(lifecycle -> lifecycle\n                                .onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, response)));\n                        throw NotFoundException.create(properties.isUse404(), \"Unable to find instance for \" + url.getHost());\n                    }\n\n                    ServiceInstance retrievedInstance = response.getServer();\n\n                    URI uri = exchange.getRequest().getURI();\n\n                    // if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,\n                    // if the loadbalancer doesn't provide one.\n                    String overrideScheme = retrievedInstance.isSecure() ? \"https\" : \"http\";\n                    if (schemePrefix != null) {\n                        overrideScheme = url.getScheme();\n                    }\n\n                    DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(retrievedInstance,\n                            overrideScheme);\n\n                    URI requestUrl = reconstructURI(serviceInstance, uri);\n\n                    if (log.isTraceEnabled()) {\n                        log.trace(\"LoadBalancerClientFilter url chosen: \" + requestUrl);\n                    }\n                    exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);\n                    exchange.getAttributes().put(GATEWAY_LOADBALANCER_RESPONSE_ATTR, response);\n                    supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, response));\n                }).then(chain.filter(exchange))\n                .doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle\n                        .onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(\n                                CompletionContext.Status.FAILED, throwable, lbRequest,\n                                exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR)))))\n                .doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle\n                        .onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(\n                                CompletionContext.Status.SUCCESS, lbRequest,\n                                exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR),\n                                new ResponseData(exchange.getResponse(), new RequestData(exchange.getRequest()))))));\n    }\n\n    protected URI reconstructURI(ServiceInstance serviceInstance, URI original) {\n        return LoadBalancerUriTools.reconstructURI(serviceInstance, original);\n    }\n\n    private Mono<Response<ServiceInstance>> choose(Request<RequestDataContext> lbRequest, String serviceId,\n                                                   Set<LoadBalancerLifecycle> supportedLifecycleProcessors) {\n        // 修改 by 芋道源码：直接创建 GrayLoadBalancer 对象\n        GrayLoadBalancer loadBalancer = new GrayLoadBalancer(\n                clientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class), serviceId);\n        supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));\n        return loadBalancer.choose(lbRequest);\n    }\n\n    private String getHint(String serviceId) {\n        LoadBalancerProperties loadBalancerProperties = clientFactory.getProperties(serviceId);\n        Map<String, String> hints = loadBalancerProperties.getHint();\n        String defaultHint = hints.getOrDefault(\"default\", \"default\");\n        String hintPropertyValue = hints.get(serviceId);\n        return hintPropertyValue != null ? hintPropertyValue : defaultHint;\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLog.java",
    "content": "package cn.iocoder.yudao.gateway.filter.logging;\n\nimport lombok.Data;\nimport org.springframework.cloud.gateway.route.Route;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.util.MultiValueMap;\n\nimport java.time.LocalDateTime;\n\n/**\n * 网关的访问日志\n */\n@Data\npublic class AccessLog {\n\n    /**\n     * 链路追踪编号\n     */\n    private String traceId;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n    /**\n     * 用户类型\n     */\n    private Integer userType;\n    /**\n     * 路由\n     *\n     * 类似 ApiAccessLogCreateReqDTO 的 applicationName\n     */\n    private Route route;\n\n    /**\n     * 协议\n     */\n    private String schema;\n    /**\n     * 请求方法名\n     */\n    private String requestMethod;\n    /**\n     * 访问地址\n     */\n    private String requestUrl;\n    /**\n     * 查询参数\n     */\n    private MultiValueMap<String, String> queryParams;\n    /**\n     * 请求体\n     */\n    private String requestBody;\n    /**\n     * 请求头\n     */\n    private MultiValueMap<String, String> requestHeaders;\n    /**\n     * 用户 IP\n     */\n    private String userIp;\n\n    /**\n     * 响应体\n     *\n     * 类似 ApiAccessLogCreateReqDTO 的 resultCode + resultMsg\n     */\n    private String responseBody;\n    /**\n     * 响应头\n     */\n    private MultiValueMap<String, String> responseHeaders;\n    /**\n     * 响应结果\n     */\n    private HttpStatus httpStatus;\n\n    /**\n     * 开始请求时间\n     */\n    private LocalDateTime startTime;\n    /**\n     * 结束请求时间\n     */\n    private LocalDateTime endTime;\n    /**\n     * 执行时长，单位：毫秒\n     */\n    private Integer duration;\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java",
    "content": "package cn.iocoder.yudao.gateway.filter.logging;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.json.JSONUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.gateway.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.gateway.util.WebFrameworkUtils;\nimport com.alibaba.nacos.common.utils.StringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.reactivestreams.Publisher;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;\nimport org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory;\nimport org.springframework.cloud.gateway.support.BodyInserterContext;\nimport org.springframework.cloud.gateway.support.ServerWebExchangeUtils;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.core.io.buffer.DataBufferUtils;\nimport org.springframework.core.io.buffer.DefaultDataBufferFactory;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ReactiveHttpOutputMessage;\nimport org.springframework.http.codec.CodecConfigurer;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.http.server.reactive.ServerHttpRequestDecorator;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.http.server.reactive.ServerHttpResponseDecorator;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.reactive.function.BodyInserter;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.function.server.ServerRequest;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport javax.annotation.Resource;\nimport java.nio.charset.StandardCharsets;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.hutool.core.date.DatePattern.NORM_DATETIME_MS_FORMATTER;\n\n/**\n * 网关的访问日志过滤器\n *\n * 从功能上，它类似 yudao-spring-boot-starter-web 的 ApiAccessLogFilter 过滤器\n *\n * TODO 芋艿：如果网关执行异常，不会记录访问日志，后续研究下 https://github.com/Silvmike/webflux-demo/blob/master/tests/src/test/java/ru/hardcoders/demo/webflux/web_handler/filters/logging\n *\n * @author 芋道源码\n */\n@Slf4j\n@Component\npublic class AccessLogFilter implements GlobalFilter, Ordered {\n\n    @Resource\n    private CodecConfigurer codecConfigurer;\n\n    /**\n     * 打印日志\n     *\n     * @param gatewayLog 网关日志\n     */\n    private void writeAccessLog(AccessLog gatewayLog) {\n        // 方式一：打印 Logger 后，通过 ELK 进行收集\n        // log.info(\"[writeAccessLog][日志内容：{}]\", JsonUtils.toJsonString(gatewayLog));\n\n        // 方式二：调用远程服务，记录到数据库中\n        // TODO 芋艿：暂未实现\n\n        // 方式三：打印到控制台，方便排查错误\n        try {\n            Map<String, Object> values = MapUtil.newHashMap(15, true); // 手工拼接，保证排序；15 保证不用扩容\n            values.put(\"userId\", gatewayLog.getUserId());\n            values.put(\"userType\", gatewayLog.getUserType());\n            values.put(\"routeId\", gatewayLog.getRoute() != null ? gatewayLog.getRoute().getId() : null);\n            values.put(\"schema\", gatewayLog.getSchema());\n            values.put(\"requestUrl\", gatewayLog.getRequestUrl());\n            values.put(\"queryParams\", gatewayLog.getQueryParams().toSingleValueMap());\n            values.put(\"requestBody\", JsonUtils.isJson(gatewayLog.getRequestBody()) ? // 保证 body 的展示好看\n                    JSONUtil.parse(gatewayLog.getRequestBody()) : gatewayLog.getRequestBody());\n            values.put(\"requestHeaders\", JsonUtils.toJsonString(gatewayLog.getRequestHeaders().toSingleValueMap()));\n            values.put(\"userIp\", gatewayLog.getUserIp());\n            values.put(\"responseBody\", JsonUtils.isJson(gatewayLog.getResponseBody()) ? // 保证 body 的展示好看\n                    JSONUtil.parse(gatewayLog.getResponseBody()) : gatewayLog.getResponseBody());\n            values.put(\"responseHeaders\", gatewayLog.getResponseHeaders() != null ?\n                    JsonUtils.toJsonString(gatewayLog.getResponseHeaders().toSingleValueMap()) : null);\n            values.put(\"httpStatus\", gatewayLog.getHttpStatus());\n            values.put(\"startTime\", LocalDateTimeUtil.format(gatewayLog.getStartTime(), NORM_DATETIME_MS_FORMATTER));\n            values.put(\"endTime\", LocalDateTimeUtil.format(gatewayLog.getEndTime(), NORM_DATETIME_MS_FORMATTER));\n            values.put(\"duration\", gatewayLog.getDuration() != null ? gatewayLog.getDuration() + \" ms\" : null);\n            log.info(\"[writeAccessLog][网关日志：{}]\", JsonUtils.toJsonPrettyString(values));\n        } catch (Exception e) {\n            // 兜底处理，参见 https://gitee.com/zhijiantianya/yudao-cloud/issues/IC9A70\n            log.error(\"[writeAccessLog][打印网关日志时，发生异常]\", e);\n        }\n    }\n\n    @Override\n    public int getOrder() {\n        return Ordered.HIGHEST_PRECEDENCE;\n    }\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n        // 将 Request 中可以直接获取到的参数，设置到网关日志\n        ServerHttpRequest request = exchange.getRequest();\n        // TODO traceId\n        AccessLog gatewayLog = new AccessLog();\n        gatewayLog.setRoute(WebFrameworkUtils.getGatewayRoute(exchange));\n        gatewayLog.setSchema(request.getURI().getScheme());\n        gatewayLog.setRequestMethod(request.getMethodValue());\n        gatewayLog.setRequestUrl(request.getURI().getRawPath());\n        gatewayLog.setQueryParams(request.getQueryParams());\n        gatewayLog.setRequestHeaders(request.getHeaders());\n        gatewayLog.setStartTime(LocalDateTime.now());\n        gatewayLog.setUserIp(WebFrameworkUtils.getClientIP(exchange));\n\n        // 继续 filter 过滤\n        MediaType mediaType = request.getHeaders().getContentType();\n        if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)\n                || MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) { // 适合 JSON 和 Form 提交的请求\n            return filterWithRequestBody(exchange, chain, gatewayLog);\n        }\n        return filterWithoutRequestBody(exchange, chain, gatewayLog);\n    }\n\n    private Mono<Void> filterWithoutRequestBody(ServerWebExchange exchange, GatewayFilterChain chain, AccessLog accessLog) {\n        // 包装 Response，用于记录 Response Body\n        ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, accessLog);\n        return chain.filter(exchange.mutate().response(decoratedResponse).build())\n                .then(Mono.fromRunnable(() -> writeAccessLog(accessLog))); // 打印日志\n    }\n\n    /**\n     * 参考 {@link ModifyRequestBodyGatewayFilterFactory} 实现\n     *\n     * 差别主要在于使用 modifiedBody 来读取 Request Body 数据\n     */\n    private Mono<Void> filterWithRequestBody(ServerWebExchange exchange, GatewayFilterChain chain, AccessLog gatewayLog) {\n        // 设置 Request Body 读取时，设置到网关日志\n        // 此处 codecConfigurer.getReaders() 的目的，是解决 spring.codec.max-in-memory-size 不生效\n        ServerRequest serverRequest = ServerRequest.create(exchange, codecConfigurer.getReaders());\n        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {\n            gatewayLog.setRequestBody(body);\n            return Mono.just(body);\n        });\n\n        // 创建 BodyInserter 对象\n        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);\n        // 创建 CachedBodyOutputMessage 对象\n        HttpHeaders headers = new HttpHeaders();\n        headers.putAll(exchange.getRequest().getHeaders());\n        // the new content type will be computed by bodyInserter\n        // and then set in the request decorator\n        headers.remove(HttpHeaders.CONTENT_LENGTH); // 移除\n        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);\n        // 通过 BodyInserter 将 Request Body 写入到 CachedBodyOutputMessage 中\n        return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {\n            // 包装 Request，用于缓存 Request Body\n            ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage);\n            // 包装 Response，用于记录 Response Body\n            ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, gatewayLog);\n            // 记录普通的\n            return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build())\n                    .then(Mono.fromRunnable(() -> writeAccessLog(gatewayLog))); // 打印日志\n\n        }));\n    }\n\n    /**\n     * 记录响应日志\n     * 通过 DataBufferFactory 解决响应体分段传输问题。\n     */\n    private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, AccessLog gatewayLog) {\n        ServerHttpResponse response = exchange.getResponse();\n        return new ServerHttpResponseDecorator(response) {\n\n            @Override\n            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {\n                if (body instanceof Flux) {\n                    DataBufferFactory bufferFactory = response.bufferFactory();\n                    // 计算执行时间\n                    gatewayLog.setEndTime(LocalDateTime.now());\n                    gatewayLog.setDuration((int) (LocalDateTimeUtil.between(gatewayLog.getStartTime(),\n                            gatewayLog.getEndTime()).toMillis()));\n                    // 设置其它字段\n                    gatewayLog.setUserId(SecurityFrameworkUtils.getLoginUserId(exchange));\n                    gatewayLog.setUserType(SecurityFrameworkUtils.getLoginUserType(exchange));\n                    gatewayLog.setResponseHeaders(response.getHeaders());\n                    gatewayLog.setHttpStatus(response.getStatusCode());\n\n                    // 获取响应类型，如果是 json 就打印\n                    String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);\n                    if (StringUtils.isNotBlank(originalResponseContentType)\n                            && originalResponseContentType.contains(\"application/json\")) {\n                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);\n                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {\n                            // 设置 response body 到网关日志\n                            byte[] content = readContent(dataBuffers);\n                            String responseResult = new String(content, StandardCharsets.UTF_8);\n                            gatewayLog.setResponseBody(responseResult);\n\n                            // 响应\n                            return bufferFactory.wrap(content);\n                        }));\n                    }\n                }\n                // if body is not a flux. never got there.\n                return super.writeWith(body);\n            }\n        };\n    }\n\n    // ========== 参考 ModifyRequestBodyGatewayFilterFactory 中的方法 ==========\n\n    /**\n     * 请求装饰器，支持重新计算 headers、body 缓存\n     *\n     * @param exchange 请求\n     * @param headers 请求头\n     * @param outputMessage body 缓存\n     * @return 请求装饰器\n     */\n    private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {\n        return new ServerHttpRequestDecorator(exchange.getRequest()) {\n\n            @Override\n            public HttpHeaders getHeaders() {\n                long contentLength = headers.getContentLength();\n                HttpHeaders httpHeaders = new HttpHeaders();\n                httpHeaders.putAll(super.getHeaders());\n                if (contentLength > 0) {\n                    httpHeaders.setContentLength(contentLength);\n                } else {\n                    // TODO: this causes a 'HTTP/1.1 411 Length Required' // on\n                    // httpbin.org\n                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, \"chunked\");\n                }\n                return httpHeaders;\n            }\n\n            @Override\n            public Flux<DataBuffer> getBody() {\n                return outputMessage.getBody();\n            }\n        };\n    }\n\n    // ========== 参考 ModifyResponseBodyGatewayFilterFactory 中的方法 ==========\n\n    private byte[] readContent(List<? extends DataBuffer> dataBuffers) {\n        // 合并多个流集合，解决返回体分段传输\n        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();\n        DataBuffer join = dataBufferFactory.join(dataBuffers);\n        byte[] content = new byte[join.readableByteCount()];\n        join.read(content);\n        // 释放掉内存\n        DataBufferUtils.release(join);\n        return content;\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/security/LoginUser.java",
    "content": "package cn.iocoder.yudao.gateway.filter.security;\n\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 登录用户信息\n *\n * copy from yudao-spring-boot-starter-security 的 LoginUser 类\n *\n * @author 芋道源码\n */\n@Data\npublic class LoginUser {\n\n    /**\n     * 用户编号\n     */\n    private Long id;\n    /**\n     * 用户类型\n     */\n    private Integer userType;\n    /**\n     * 额外的用户信息\n     */\n    private Map<String, String> info;\n    /**\n     * 租户编号\n     */\n    private Long tenantId;\n    /**\n     * 授权范围\n     */\n    private List<String> scopes;\n    /**\n     * 过期时间\n     */\n    private LocalDateTime expiresTime;\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/security/TokenAuthenticationFilter.java",
    "content": "package cn.iocoder.yudao.gateway.filter.security;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.gateway.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.gateway.util.WebFrameworkUtils;\nimport cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;\nimport cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.core.Ordered;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\nimport java.time.Duration;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;\n\n/**\n * Token 过滤器，验证 token 的有效性\n * 1. 验证通过时，将 userId、userType、tenantId 通过 Header 转发给服务\n * 2. 验证不通过，还是会转发给服务。因为，接口是否需要登录的校验，还是交给服务自身处理\n *\n * @author 芋道源码\n */\n@Component\npublic class TokenAuthenticationFilter implements GlobalFilter, Ordered {\n\n    /**\n     * CommonResult<OAuth2AccessTokenCheckRespDTO> 对应的 TypeReference 结果，用于解析 checkToken 的结果\n     */\n    private static final TypeReference<CommonResult<OAuth2AccessTokenCheckRespDTO>> CHECK_RESULT_TYPE_REFERENCE\n            = new TypeReference<CommonResult<OAuth2AccessTokenCheckRespDTO>>() {};\n\n    /**\n     * 空的 LoginUser 的结果\n     *\n     * 用于解决如下问题：\n     * 1. {@link #getLoginUser(ServerWebExchange, String)} 返回 Mono.empty() 时，会导致后续的 flatMap 无法进行处理的问题。\n     * 2. {@link #buildUser(String)} 时，如果 Token 已经过期，返回 LOGIN_USER_EMPTY 对象，避免缓存无法刷新\n     */\n    private static final LoginUser LOGIN_USER_EMPTY = new LoginUser();\n\n    private final WebClient webClient;\n\n    /**\n     * 登录用户的本地缓存\n     *\n     * key1：多租户的编号\n     * key2：访问令牌\n     */\n    private final LoadingCache<KeyValue<Long, String>, LoginUser> loginUserCache = buildAsyncReloadingCache(Duration.ofMinutes(1),\n            new CacheLoader<KeyValue<Long, String>, LoginUser>() {\n\n                @Override\n                public LoginUser load(KeyValue<Long, String> token) {\n                    String body = checkAccessToken(token.getKey(), token.getValue()).block();\n                    return buildUser(body);\n                }\n\n            });\n\n    public TokenAuthenticationFilter(ReactorLoadBalancerExchangeFilterFunction lbFunction) {\n        // Q：为什么不使用 OAuth2TokenApi 进行调用？\n        // A1：Spring Cloud OpenFeign 官方未内置 Reactive 的支持 https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#reactive-support\n        // A2：校验 Token 的 API 需要使用到 header[tenant-id] 传递租户编号，暂时不想编写 RequestInterceptor 实现\n        // 因此，这里采用 WebClient，通过 lbFunction 实现负载均衡\n        this.webClient = WebClient.builder().filter(lbFunction).build();\n    }\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n        // 移除 login-user 的请求头，避免伪造模拟\n        exchange = SecurityFrameworkUtils.removeLoginUser(exchange);\n\n        // 情况一，如果没有 Token 令牌，则直接继续 filter\n        String token = SecurityFrameworkUtils.obtainAuthorization(exchange);\n        if (StrUtil.isEmpty(token)) {\n            return chain.filter(exchange);\n        }\n\n        // 情况二，如果有 Token 令牌，则解析对应 userId、userType、tenantId 等字段，并通过 通过 Header 转发给服务\n        // 重要说明：defaultIfEmpty 作用，保证 Mono.empty() 情况，可以继续执行 `flatMap 的 chain.filter(exchange)` 逻辑，避免返回给前端空的 Response！！\n        ServerWebExchange finalExchange = exchange;\n        return getLoginUser(exchange, token).defaultIfEmpty(LOGIN_USER_EMPTY).flatMap(user -> {\n            // 1. 无用户，直接 filter 继续请求\n            if (user == LOGIN_USER_EMPTY || // 下面 expiresTime 的判断，为了解决 token 实际已经过期的情况\n                    user.getExpiresTime() == null || LocalDateTimeUtils.beforeNow(user.getExpiresTime())) {\n                return chain.filter(finalExchange);\n            }\n\n            // 2.1 有用户，则设置登录用户\n            SecurityFrameworkUtils.setLoginUser(finalExchange, user);\n            // 2.2 将 user 并设置到 login-user 的请求头，使用 json 存储值\n            ServerWebExchange newExchange = finalExchange.mutate()\n                    .request(builder -> SecurityFrameworkUtils.setLoginUserHeader(builder, user)).build();\n            return chain.filter(newExchange);\n        });\n    }\n\n    private Mono<LoginUser> getLoginUser(ServerWebExchange exchange, String token) {\n        // 从缓存中，获取 LoginUser\n        Long tenantId = WebFrameworkUtils.getTenantId(exchange);\n        KeyValue<Long, String> cacheKey = new KeyValue<Long, String>().setKey(tenantId).setValue(token);\n        LoginUser localUser = loginUserCache.getIfPresent(cacheKey);\n        if (localUser != null) {\n            return Mono.just(localUser);\n        }\n\n        // 缓存不存在，则请求远程服务\n        return checkAccessToken(tenantId, token).flatMap((Function<String, Mono<LoginUser>>) body -> {\n            LoginUser remoteUser = buildUser(body);\n            if (remoteUser != null) {\n                // 非空，则进行缓存\n                loginUserCache.put(cacheKey, remoteUser);\n                return Mono.just(remoteUser);\n            }\n            return Mono.empty();\n        });\n    }\n\n    private Mono<String> checkAccessToken(Long tenantId, String token) {\n        return webClient.get()\n                .uri(OAuth2TokenCommonApi.URL_CHECK, uriBuilder -> uriBuilder.queryParam(\"accessToken\", token).build())\n                .headers(httpHeaders -> WebFrameworkUtils.setTenantIdHeader(tenantId, httpHeaders)) // 设置租户的 Header\n                .retrieve().bodyToMono(String.class);\n    }\n\n    private LoginUser buildUser(String body) {\n        // 处理结果，结果不正确\n        CommonResult<OAuth2AccessTokenCheckRespDTO> result = JsonUtils.parseObject(body, CHECK_RESULT_TYPE_REFERENCE);\n        if (result == null) {\n            return null;\n        }\n        if (result.isError()) {\n            // 特殊情况：令牌已经过期（code = 401），需要返回 LOGIN_USER_EMPTY，避免 Token 一直因为缓存，被误判为有效\n            if (Objects.equals(result.getCode(), HttpStatus.UNAUTHORIZED.value())) {\n                return LOGIN_USER_EMPTY;\n            }\n            return null;\n        }\n\n        // 创建登录用户\n        OAuth2AccessTokenCheckRespDTO tokenInfo = result.getData();\n        return new LoginUser().setId(tokenInfo.getUserId()).setUserType(tokenInfo.getUserType())\n                .setInfo(tokenInfo.getUserInfo()) // 额外的用户信息\n                .setTenantId(tokenInfo.getTenantId()).setScopes(tokenInfo.getScopes())\n                .setExpiresTime(tokenInfo.getExpiresTime());\n    }\n\n    @Override\n    public int getOrder() {\n        return -100; // 和 Spring Security Filter 的顺序对齐\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/handler/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.yudao.gateway.handler;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.gateway.util.WebFrameworkUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.server.ResponseStatusException;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;\n\n/**\n * Gateway 的全局异常处理器，将 Exception 翻译成 CommonResult + 对应的异常编号\n *\n * 在功能上，和 yudao-spring-boot-starter-web 的 GlobalExceptionHandler 类是一致的\n *\n * @author 芋道源码\n */\n@Component\n@Order(-1) // 保证优先级高于默认的 Spring Cloud Gateway 的 ErrorWebExceptionHandler 实现\n@Slf4j\npublic class GlobalExceptionHandler implements ErrorWebExceptionHandler {\n\n    @Override\n    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {\n        // 已经 commit，则直接返回异常\n        ServerHttpResponse response = exchange.getResponse();\n        if (response.isCommitted()) {\n            return Mono.error(ex);\n        }\n\n        // 转换成 CommonResult\n        CommonResult<?> result;\n        if (ex instanceof ResponseStatusException) {\n            result = responseStatusExceptionHandler(exchange, (ResponseStatusException) ex);\n        } else {\n            result = defaultExceptionHandler(exchange, ex);\n        }\n\n        // 返回给前端\n        return WebFrameworkUtils.writeJSON(exchange, result);\n    }\n\n    /**\n     * 处理 Spring Cloud Gateway 默认抛出的 ResponseStatusException 异常\n     */\n    private CommonResult<?> responseStatusExceptionHandler(ServerWebExchange exchange,\n                                                           ResponseStatusException ex) {\n        // TODO 芋艿：这里要精细化翻译，默认返回用户是看不懂的\n        ServerHttpRequest request = exchange.getRequest();\n        log.error(\"[responseStatusExceptionHandler][uri({}/{}) 发生异常]\", request.getURI(), request.getMethod(), ex);\n        return CommonResult.error(ex.getRawStatusCode(), ex.getReason());\n    }\n\n    /**\n     * 处理系统异常，兜底处理所有的一切\n     */\n    @ExceptionHandler(value = Exception.class)\n    public CommonResult<?> defaultExceptionHandler(ServerWebExchange exchange,\n                                                   Throwable ex) {\n        ServerHttpRequest request = exchange.getRequest();\n        log.error(\"[defaultExceptionHandler][uri({}/{}) 发生异常]\", request.getURI(), request.getMethod(), ex);\n        // TODO 芋艿：是否要插入异常日志呢？\n        // 返回 ERROR CommonResult\n        return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/jackson/GatewayJacksonAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.gateway.jackson;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer;\nimport cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;\nimport cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;\nimport com.fasterxml.jackson.databind.Module;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;\nimport com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;\nimport com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;\nimport com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;\nimport org.springframework.boot.web.codec.CodecCustomizer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.http.codec.json.Jackson2JsonDecoder;\nimport org.springframework.http.codec.json.Jackson2JsonEncoder;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\n\n@Configuration\n@Slf4j\npublic class GatewayJacksonAutoConfiguration {\n\n    /**\n     * 从 Builder 源头定制（关键：使用 *ByType，避免 handledType 要求）\n     */\n    @Bean\n    public Jackson2ObjectMapperBuilderCustomizer ldtEpochMillisCustomizer() {\n        return builder -> builder\n                // Long -> Number\n                .serializerByType(Long.class, NumberSerializer.INSTANCE)\n                .serializerByType(Long.TYPE, NumberSerializer.INSTANCE)\n                // LocalDate / LocalTime\n                .serializerByType(LocalDate.class, LocalDateSerializer.INSTANCE)\n                .deserializerByType(LocalDate.class, LocalDateDeserializer.INSTANCE)\n                .serializerByType(LocalTime.class, LocalTimeSerializer.INSTANCE)\n                .deserializerByType(LocalTime.class, LocalTimeDeserializer.INSTANCE)\n                // LocalDateTime < - > EpochMillis\n                .serializerByType(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)\n                .deserializerByType(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);\n    }\n\n    /**\n     * 以 Bean 形式暴露 Module（Boot 会自动注册到所有 ObjectMapper）\n     */\n    @Bean\n    public Module timestampSupportModuleBean() {\n        SimpleModule m = new SimpleModule(\"TimestampSupportModule\");\n        // Long -> Number\n        m.addSerializer(Long.class, NumberSerializer.INSTANCE);\n        m.addSerializer(Long.TYPE, NumberSerializer.INSTANCE);\n        // LocalDate / LocalTime\n        m.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE);\n        m.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);\n        m.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE);\n        m.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE);\n        // LocalDateTime < - > EpochMillis\n        m.addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE);\n        m.addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);\n        return m;\n    }\n\n    /**\n     * 初始化全局 JsonUtils，直接使用主 ObjectMapper\n     */\n    @Bean\n    @SuppressWarnings(\"InstantiationOfUtilityClass\")\n    public JsonUtils jsonUtils(ObjectMapper objectMapper) {\n        JsonUtils.init(objectMapper);\n        log.debug(\"[init][初始化 JsonUtils 成功]\");\n        return new JsonUtils();\n    }\n\n    /**\n     * WebFlux 场景：强制默认编解码器使用同一个 ObjectMapper\n     */\n    @Bean\n    public CodecCustomizer unifyJackson(ObjectMapper om) {\n        return configurer -> {\n            Jackson2JsonDecoder decoder = new Jackson2JsonDecoder(om);\n            Jackson2JsonEncoder encoder = new Jackson2JsonEncoder(om);\n            configurer.defaultCodecs().jackson2JsonDecoder(decoder);\n            configurer.defaultCodecs().jackson2JsonEncoder(encoder);\n        };\n    }\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/route/dynamic/package-info.java",
    "content": "/**\n * 在 Nacos 配置发生变化时，Spring Cloud Alibaba Nacos Config 内置的监听器，会监听到配置刷新，最终触发 Gateway 的路由信息刷新。\n *\n * 参见 https://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Gateway/?yudao 博客的「6. 基于配置中心 Nacos 实现动态路由」小节\n *\n * 使用方式：在 Nacos 修改 DataId 为 gateway-server.yaml 的配置，修改 spring.cloud.gateway.routes 配置项\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.gateway.route.dynamic;\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/route/package-info.java",
    "content": "/**\n * 占位符\n */\npackage cn.iocoder.yudao.gateway.route;\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/BannerApplicationRunner.java",
    "content": "package cn.iocoder.yudao.gateway.util;\n\nimport cn.hutool.core.thread.ThreadUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 项目启动成功后，提供文档相关的地址\n *\n * @author 芋道源码\n */\n@Component\n@Slf4j\npublic class BannerApplicationRunner implements ApplicationRunner {\n\n    @Override\n    public void run(ApplicationArguments args) {\n        ThreadUtil.execute(() -> {\n            ThreadUtil.sleep(1, TimeUnit.SECONDS); // 延迟 1 秒，保证输出到结尾\n            log.info(\"\\n----------------------------------------------------------\\n\\t\" +\n                            \"项目启动成功！\\n\\t\" +\n                            \"接口文档: \\t{} \\n\\t\" +\n                            \"开发文档: \\t{} \\n\\t\" +\n                            \"视频教程: \\t{} \\n\" +\n                            \"----------------------------------------------------------\",\n                    \"https://cloud.iocoder.cn/api-doc/\",\n                    \"https://cloud.iocoder.cn\",\n                    \"https://t.zsxq.com/02Yf6M7Qn\");\n\n            // 数据报表\n            System.out.println(\"[报表模块 yudao-module-report 教程][参考 https://cloud.iocoder.cn/report/ 开启]\");\n            // 工作流\n            System.out.println(\"[工作流模块 yudao-module-bpm 教程][参考 https://cloud.iocoder.cn/bpm/ 开启]\");\n            // 商城系统\n            System.out.println(\"[商城系统 yudao-module-mall 教程][参考 https://cloud.iocoder.cn/mall/build/ 开启]\");\n            // ERP 系统\n            System.out.println(\"[ERP 系统 yudao-module-erp - 教程][参考 https://cloud.iocoder.cn/erp/build/ 开启]\");\n            // CRM 系统\n            System.out.println(\"[CRM 系统 yudao-module-crm - 教程][参考 https://cloud.iocoder.cn/crm/build/ 开启]\");\n            // 微信公众号\n            System.out.println(\"[微信公众号 yudao-module-mp 教程][参考 https://cloud.iocoder.cn/mp/build/ 开启]\");\n            // 支付平台\n            System.out.println(\"[支付系统 yudao-module-pay - 教程][参考 https://doc.iocoder.cn/pay/build/ 开启]\");\n            // AI 大模型\n            System.out.println(\"[AI 大模型 yudao-module-ai - 教程][参考 https://cloud.iocoder.cn/ai/build/ 开启]\");\n            // IOT 物联网\n            System.out.println(\"[IoT 物联网 yudao-module-iot - 教程][参考 https://doc.iocoder.cn/iot/build/ 开启]\");\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/EnvUtils.java",
    "content": "package cn.iocoder.yudao.gateway.util;\n\nimport cn.hutool.core.net.NetUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.StrUtil;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.http.HttpHeaders;\n\nimport java.util.Objects;\n\n/**\n * 环境 Utils\n *\n * copy from yudao-spring-boot-starter-env 的 EnvUtils 类\n *\n * @author 芋道源码\n */\npublic class EnvUtils {\n\n    private static final String HEADER_TAG = \"tag\";\n\n    public static final String HOST_NAME_VALUE = \"${HOSTNAME}\";\n\n    public static String getTag(HttpHeaders headers) {\n        String tag = headers.getFirst(HEADER_TAG);\n        // 如果请求的是 \"${HOSTNAME}\"，则解析成对应的本地主机名\n        // 目的：特殊逻辑，解决 IDEA Rest Client 不支持环境变量的读取，所以就服务器来做\n        return Objects.equals(tag, HOST_NAME_VALUE) ? getHostName() : tag;\n    }\n\n    public static String getTag(ServiceInstance instance) {\n        return instance.getMetadata().get(HEADER_TAG);\n    }\n\n    public static String getHostName() {\n        return StrUtil.blankToDefault(NetUtil.getLocalHostName(), IdUtil.fastSimpleUUID());\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/SecurityFrameworkUtils.java",
    "content": "package cn.iocoder.yudao.gateway.util;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.gateway.filter.security.LoginUser;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * 安全服务工具类\n *\n * copy from yudao-spring-boot-starter-security 的 SecurityFrameworkUtils 类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class SecurityFrameworkUtils {\n\n    private static final String AUTHORIZATION_HEADER = \"Authorization\";\n\n    private static final String AUTHORIZATION_BEARER = \"Bearer\";\n\n    private static final String LOGIN_USER_HEADER = \"login-user\";\n\n    private static final String LOGIN_USER_ID_ATTR = \"login-user-id\";\n    private static final String LOGIN_USER_TYPE_ATTR = \"login-user-type\";\n\n    private SecurityFrameworkUtils() {}\n\n    /**\n     * 从请求中，获得认证 Token\n     *\n     * @param exchange 请求\n     * @return 认证 Token\n     */\n    public static String obtainAuthorization(ServerWebExchange exchange) {\n        String authorization = exchange.getRequest().getHeaders().getFirst(AUTHORIZATION_HEADER);\n        if (!StringUtils.hasText(authorization)) {\n            return null;\n        }\n        int index = authorization.indexOf(AUTHORIZATION_BEARER + \" \");\n        if (index == -1) { // 未找到\n            return null;\n        }\n        return authorization.substring(index + 7).trim();\n    }\n\n    /**\n     * 设置登录用户\n     *\n     * @param exchange 请求\n     * @param user 用户\n     */\n    public static void setLoginUser(ServerWebExchange exchange, LoginUser user) {\n        exchange.getAttributes().put(LOGIN_USER_ID_ATTR, user.getId());\n        exchange.getAttributes().put(LOGIN_USER_TYPE_ATTR, user.getUserType());\n    }\n\n    /**\n     * 移除请求头的用户\n     *\n     * @param exchange 请求\n     * @return 请求\n     */\n    public static ServerWebExchange removeLoginUser(ServerWebExchange exchange) {\n        // 如果不包含，直接返回\n        if (!exchange.getRequest().getHeaders().containsKey(LOGIN_USER_HEADER)) {\n            return exchange;\n        }\n        // 如果包含，则移除。参考 RemoveRequestHeaderGatewayFilterFactory 实现\n        ServerHttpRequest request = exchange.getRequest().mutate()\n                .headers(httpHeaders -> httpHeaders.remove(LOGIN_USER_HEADER)).build();\n        return exchange.mutate().request(request).build();\n    }\n\n    /**\n     * 获得登录用户的编号\n     *\n     * @param exchange 请求\n     * @return 用户编号\n     */\n    public static Long getLoginUserId(ServerWebExchange exchange) {\n        return MapUtil.getLong(exchange.getAttributes(), LOGIN_USER_ID_ATTR);\n    }\n\n    /**\n     * 获得登录用户的类型\n     *\n     * @param exchange 请求\n     * @return 用户类型\n     */\n    public static Integer getLoginUserType(ServerWebExchange exchange) {\n        return MapUtil.getInt(exchange.getAttributes(), LOGIN_USER_TYPE_ATTR);\n    }\n\n    /**\n     * 将 user 并设置到 login-user 的请求头，使用 json 存储值\n     *\n     * @param builder 请求\n     * @param user 用户\n     */\n    @SneakyThrows\n    public static void setLoginUserHeader(ServerHttpRequest.Builder builder, LoginUser user) {\n        try {\n            String userStr = JsonUtils.toJsonString(user);\n            userStr = URLEncoder.encode(userStr, StandardCharsets.UTF_8.name()); // 编码，避免中文乱码\n            builder.header(LOGIN_USER_HEADER, userStr);\n        } catch (Exception ex) {\n            log.error(\"[setLoginUserHeader][序列化 user({}) 发生异常]\", user, ex);\n            throw ex;\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java",
    "content": "package cn.iocoder.yudao.gateway.util;\n\nimport cn.hutool.core.net.NetUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.NumberUtil;\nimport cn.hutool.extra.servlet.ServletUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.gateway.route.Route;\nimport org.springframework.cloud.gateway.support.ServerWebExchangeUtils;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\n/**\n * Web 工具类\n *\n * copy from yudao-spring-boot-starter-web 的 WebFrameworkUtils 类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class WebFrameworkUtils {\n\n    private static final String HEADER_TENANT_ID = \"tenant-id\";\n\n    private WebFrameworkUtils() {}\n\n    /**\n     * 将 Gateway 请求中的 header，设置到 HttpHeaders 中\n     *\n     * @param tenantId 租户编号\n     * @param httpHeaders WebClient 的请求\n     */\n    public static void setTenantIdHeader(Long tenantId, HttpHeaders httpHeaders) {\n        if (tenantId == null) {\n            return;\n        }\n        httpHeaders.set(HEADER_TENANT_ID, String.valueOf(tenantId));\n    }\n\n    public static Long getTenantId(ServerWebExchange exchange) {\n        String tenantId = exchange.getRequest().getHeaders().getFirst(HEADER_TENANT_ID);\n        return NumberUtil.isNumber(tenantId) ? Long.valueOf(tenantId) : null;\n    }\n\n    /**\n     * 返回 JSON 字符串\n     *\n     * @param exchange 响应\n     * @param object 对象，会序列化成 JSON 字符串\n     */\n    @SuppressWarnings(\"deprecation\") // 必须使用 APPLICATION_JSON_UTF8_VALUE，否则会乱码\n    public static Mono<Void> writeJSON(ServerWebExchange exchange, Object object) {\n        // 设置 header\n        ServerHttpResponse response = exchange.getResponse();\n        response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);\n        // 设置 body\n        return response.writeWith(Mono.fromSupplier(() -> {\n            DataBufferFactory bufferFactory = response.bufferFactory();\n            try {\n                return bufferFactory.wrap(JsonUtils.toJsonByte(object));\n            } catch (Exception ex) {\n                ServerHttpRequest request = exchange.getRequest();\n                log.error(\"[writeJSON][uri({}/{}) 发生异常]\", request.getURI(), request.getMethod(), ex);\n                return bufferFactory.wrap(new byte[0]);\n            }\n        }));\n    }\n\n    /**\n     * 获得客户端 IP\n     *\n     * 参考 {@link ServletUtil} 的 getClientIP 方法\n     *\n     * @param exchange 请求\n     * @param otherHeaderNames 其它 header 名字的数组\n     * @return 客户端 IP\n     */\n    public static String getClientIP(ServerWebExchange exchange, String... otherHeaderNames) {\n        String[] headers = { \"X-Forwarded-For\", \"X-Real-IP\", \"Proxy-Client-IP\", \"WL-Proxy-Client-IP\", \"HTTP_CLIENT_IP\", \"HTTP_X_FORWARDED_FOR\" };\n        if (ArrayUtil.isNotEmpty(otherHeaderNames)) {\n            headers = ArrayUtil.addAll(headers, otherHeaderNames);\n        }\n        // 方式一，通过 header 获取\n        String ip;\n        for (String header : headers) {\n            ip = exchange.getRequest().getHeaders().getFirst(header);\n            if (!NetUtil.isUnknown(ip)) {\n                return NetUtil.getMultistageReverseProxyIp(ip);\n            }\n        }\n\n        // 方式二，通过 remoteAddress 获取\n        if (exchange.getRequest().getRemoteAddress() == null) {\n            return null;\n        }\n        ip = exchange.getRequest().getRemoteAddress().getHostString();\n        return NetUtil.getMultistageReverseProxyIp(ip);\n    }\n\n    /**\n     * 获得请求匹配的 Route 路由\n     *\n     * @param exchange 请求\n     * @return 路由\n     */\n    public static Route getGatewayRoute(ServerWebExchange exchange) {\n        return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);\n    }\n\n}\n"
  },
  {
    "path": "yudao-gateway/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin"
  },
  {
    "path": "yudao-gateway/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: nacos\n      password: nacos-admin\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUPn\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n# 日志文件配置\nlogging:\n  level:\n    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿：先禁用，Spring Boot 3.X 存在部分错误的 WARN 提示\n\n"
  },
  {
    "path": "yudao-gateway/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: gateway-server\n\n  profiles:\n    active: local\n\n  codec:\n    max-in-memory-size: 10MB # 调整缓冲区大小https://gitee.com/zhijiantianya/yudao-cloud/pulls/176\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        ## system-server 服务\n        - id: system-admin-api # 路由的编号\n          uri: grayLb://system-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/system/**\n          filters:\n              - RewritePath=/admin-api/system/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n        - id: system-app-api # 路由的编号\n          uri: grayLb://system-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/app-api/system/**\n          filters:\n              - RewritePath=/app-api/system/v3/api-docs, /v3/api-docs\n        ## infra-server 服务\n        - id: infra-admin-api # 路由的编号\n          uri: grayLb://infra-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/infra/**\n          filters:\n              - RewritePath=/admin-api/infra/v3/api-docs, /v3/api-docs\n        - id: infra-app-api # 路由的编号\n          uri: grayLb://infra-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/app-api/infra/**\n          filters:\n              - RewritePath=/app-api/infra/v3/api-docs, /v3/api-docs\n        - id: infra-spring-boot-admin # 路由的编号（Spring Boot Admin）\n          uri: grayLb://infra-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin/**\n        - id: infra-websocket # 路由的编号（WebSocket）\n          uri: grayLb://infra-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/infra/ws/**\n        ## member-server 服务\n        - id: member-admin-api # 路由的编号\n          uri: grayLb://member-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/member/**\n          filters:\n            - RewritePath=/admin-api/member/v3/api-docs, /v3/api-docs\n        - id: member-app-api # 路由的编号\n          uri: grayLb://member-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/app-api/member/**\n          filters:\n            - RewritePath=/app-api/member/v3/api-docs, /v3/api-docs\n        ## bpm-server 服务\n        - id: bpm-admin-api # 路由的编号\n          uri: grayLb://bpm-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/bpm/**\n          filters:\n              - RewritePath=/admin-api/bpm/v3/api-docs, /v3/api-docs\n        ## report-server 服务\n        - id: report-admin-api # 路由的编号\n          uri: grayLb://report-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/report/**\n          filters:\n            - RewritePath=/admin-api/report/v3/api-docs, /v3/api-docs\n        - id: report-jimu # 路由的编号（积木报表）\n          uri: grayLb://report-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/jmreport/**, /drag/**, /jimubi/**\n        ## pay-server 服务\n        - id: pay-admin-api # 路由的编号\n          uri: grayLb://pay-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/pay/**\n          filters:\n            - RewritePath=/admin-api/pay/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n        - id: pay-app-api # 路由的编号\n          uri: grayLb://pay-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/app-api/pay/**\n          filters:\n            - RewritePath=/app-api/pay/v3/api-docs, /v3/api-docs\n        ## mp-server 服务\n        - id: mp-admin-api # 路由的编号\n          uri: grayLb://mp-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/mp/**\n          filters:\n            - RewritePath=/admin-api/mp/v3/api-docs, /v3/api-docs\n        ## product-server 服务\n        - id: product-admin-api # 路由的编号\n          uri: grayLb://product-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/product/**\n          filters:\n            - RewritePath=/admin-api/product/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n        - id: product-app-api # 路由的编号\n          uri: grayLb://product-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/app-api/product/**\n          filters:\n            - RewritePath=/app-api/product/v3/api-docs, /v3/api-docs\n        ## promotion-server 服务\n        - id: promotion-admin-api # 路由的编号\n          uri: grayLb://promotion-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/promotion/**\n          filters:\n            - RewritePath=/admin-api/promotion/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n        - id: promotion-app-api # 路由的编号\n          uri: grayLb://promotion-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/app-api/promotion/**\n          filters:\n            - RewritePath=/app-api/promotion/v3/api-docs, /v3/api-docs\n        ## trade-server 服务\n        - id: trade-admin-api # 路由的编号\n          uri: grayLb://trade-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/trade/**\n          filters:\n            - RewritePath=/admin-api/trade/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n        - id: trade-app-api # 路由的编号\n          uri: grayLb://trade-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/app-api/trade/**\n          filters:\n            - RewritePath=/app-api/trade/v3/api-docs, /v3/api-docs\n        ## statistics-server 服务\n        - id: statistics-admin-api # 路由的编号\n          uri: grayLb://statistics-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/statistics/**\n          filters:\n            - RewritePath=/admin-api/statistics/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n        ## erp-server 服务\n        - id: erp-admin-api # 路由的编号\n          uri: grayLb://erp-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/erp/**\n          filters:\n            - RewritePath=/admin-api/erp/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n        ## crm-server 服务\n        - id: crm-admin-api # 路由的编号\n          uri: grayLb://crm-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/crm/**\n          filters:\n            - RewritePath=/admin-api/crm/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n        ## ai-server 服务\n        - id: ai-admin-api # 路由的编号\n          uri: grayLb://ai-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/ai/**\n          filters:\n            - RewritePath=/admin-api/ai/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n        - id: ai-mcp-server # 路由的编号（MCP Server）\n          uri: grayLb://ai-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/sse, /mcp/message\n        ## iot-server 服务\n        - id: iot-admin-api # 路由的编号\n          uri: grayLb://iot-server\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/admin-api/iot/**\n          filters:\n            - RewritePath=/admin-api/iot/v3/api-docs, /v3/api-docs # 配置，保证转发到 /v3/api-docs\n      x-forwarded:\n        prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀\n      default-filters: # 全局过滤器，对应 GatewayFilterDefinition 数组\n        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin\n\nserver:\n  port: 48080\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\nknife4j:\n  # 聚合 Swagger 文档，参考 https://doc.xiaominfo.com/docs/action/springcloud-gateway 文档\n  gateway:\n    enabled: true\n    routes:\n      - name: system-server\n        service-name: system-server\n        url: /admin-api/system/v3/api-docs\n      - name: infra-server\n        service-name: infra-server\n        url: /admin-api/infra/v3/api-docs\n      - name: member-server\n        service-name: member-server\n        url: /admin-api/member/v3/api-docs\n      - name: bpm-server\n        service-name: bpm-server\n        url: /admin-api/bpm/v3/api-docs\n      - name: pay-server\n        service-name: pay-server\n        url: /admin-api/pay/v3/api-docs\n      - name: mp-server\n        service-name: mp-server\n        url: /admin-api/mp/v3/api-docs\n      - name: product-server\n        service-name: product-server\n        url: /admin-api/product/v3/api-docs\n      - name: promotion-server\n        service-name: promotion-server\n        url: /admin-api/promotion/v3/api-docs\n      - name: trade-server\n        service-name: trade-server\n        url: /admin-api/trade/v3/api-docs\n      - name: statistics-server\n        service-name: statistics-server\n        url: /admin-api/statistics/v3/api-docs\n      - name: erp-server\n        service-name: erp-server\n        url: /admin-api/erp/v3/api-docs\n      - name: crm-server\n        service-name: crm-server\n        url: /admin-api/crm/v3/api-docs\n      - name: ai-server\n        service-name: ai-server\n        url: /admin-api/ai/v3/api-docs\n      - name: iot-server\n        service-name: iot-server\n        url: /admin-api/iot/v3/api-docs\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0"
  },
  {
    "path": "yudao-gateway/src/main/resources/banner.txt",
    "content": "芋道源码 http://www.iocoder.cn\nApplication Version: ${yudao.info.version}\nSpring Boot Version: ${spring-boot.version}\n\n.__   __.   ______      .______    __    __    _______\n|  \\ |  |  /  __  \\     |   _  \\  |  |  |  |  /  _____|\n|   \\|  | |  |  |  |    |  |_)  | |  |  |  | |  |  __\n|  . `  | |  |  |  |    |   _  <  |  |  |  | |  | |_ |\n|  |\\   | |  `--'  |    |  |_)  | |  `--'  | |  |__| |\n|__| \\__|  \\______/     |______/   \\______/   \\______|\n\n███╗   ██╗ ██████╗     ██████╗ ██╗   ██╗ ██████╗\n████╗  ██║██╔═══██╗    ██╔══██╗██║   ██║██╔════╝\n██╔██╗ ██║██║   ██║    ██████╔╝██║   ██║██║  ███╗\n██║╚██╗██║██║   ██║    ██╔══██╗██║   ██║██║   ██║\n██║ ╚████║╚██████╔╝    ██████╔╝╚██████╔╝╚██████╔╝\n╚═╝  ╚═══╝ ╚═════╝     ╚═════╝  ╚═════╝  ╚═════╝\n"
  },
  {
    "path": "yudao-gateway/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-gateway/src/test/java/cn/iocoder/yudao/ProjectReactor.java",
    "content": "package cn.iocoder.yudao;\n\nimport cn.hutool.core.io.FileTypeUtil;\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.io.File;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.stream.Collectors;\n\nimport static java.io.File.separator;\n\n/**\n * 项目修改器，一键替换 Maven 的 groupId、artifactId，项目的 package 等\n * <p>\n * 通过修改 groupIdNew、artifactIdNew、projectBaseDirNew 三个变量\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class ProjectReactor {\n\n    private static final String GROUP_ID = \"cn.iocoder.cloud\";\n    private static final String ARTIFACT_ID = \"yudao\";\n    private static final String PACKAGE_NAME = \"cn.iocoder.yudao\";\n    private static final String TITLE = \"芋道管理系统\";\n\n    /**\n     * 白名单文件，不进行重写，避免出问题\n     */\n    private static final Set<String> WHITE_FILE_TYPES = SetUtils.asSet(\"gif\", \"jpg\", \"svg\", \"png\", // 图片\n            \"eot\", \"woff2\", \"ttf\", \"woff\",  // 字体\n            \"xdb\"); // IP 库\n\n    public static void main(String[] args) {\n        long start = System.currentTimeMillis();\n        String projectBaseDir = getProjectBaseDir();\n        log.info(\"[main][原项目路劲改地址 ({})]\", projectBaseDir);\n\n        // ========== 配置，需要你手动修改 ==========\n        String groupIdNew = \"cn.star.gg\";\n        String artifactIdNew = \"star\";\n        String packageNameNew = \"cn.start.pp\";\n        String titleNew = \"土豆管理系统\";\n        String projectBaseDirNew = projectBaseDir + \"-new\"; // 一键改名后，“新”项目所在的目录\n        log.info(\"[main][检测新项目目录 ({})是否存在]\", projectBaseDirNew);\n        if (FileUtil.exist(projectBaseDirNew)) {\n            log.error(\"[main][新项目目录检测 ({})已存在，请更改新的目录！程序退出]\", projectBaseDirNew);\n            return;\n        }\n        // 如果新目录中存在 PACKAGE_NAME，ARTIFACT_ID 等关键字，路径会被替换，导致生成的文件不在预期目录\n        if (StrUtil.containsAny(projectBaseDirNew, PACKAGE_NAME, ARTIFACT_ID, StrUtil.upperFirst(ARTIFACT_ID))) {\n            log.error(\"[main][新项目目录 `projectBaseDirNew` 检测 ({}) 存在冲突名称「{}」或者「{}」，请更改新的目录！程序退出]\",\n                    projectBaseDirNew, PACKAGE_NAME, ARTIFACT_ID);\n            return;\n        }\n        log.info(\"[main][完成新项目目录检测，新项目路径地址 ({})]\", projectBaseDirNew);\n        // 获得需要复制的文件\n        log.info(\"[main][开始获得需要重写的文件，预计需要 10-20 秒]\");\n        Collection<File> files = listFiles(projectBaseDir);\n        log.info(\"[main][需要重写的文件数量：{}，预计需要 15-30 秒]\", files.size());\n        // 写入文件\n        files.forEach(file -> {\n            // 如果是白名单的文件类型，不进行重写，直接拷贝\n            String fileType = getFileType(file);\n            if (WHITE_FILE_TYPES.contains(fileType)) {\n                copyFile(file, projectBaseDir, projectBaseDirNew, packageNameNew, artifactIdNew);\n                return;\n            }\n            // 如果非白名单的文件类型，重写内容，在生成文件\n            String content = replaceFileContent(file, groupIdNew, artifactIdNew, packageNameNew, titleNew);\n            writeFile(file, content, projectBaseDir, projectBaseDirNew, packageNameNew, artifactIdNew);\n        });\n        log.info(\"[main][重写完成]共耗时：{} 秒\", (System.currentTimeMillis() - start) / 1000);\n    }\n\n    private static String getProjectBaseDir() {\n        String baseDir = System.getProperty(\"user.dir\");\n        if (StrUtil.isEmpty(baseDir)) {\n            throw new NullPointerException(\"项目基础路径不存在\");\n        }\n        return baseDir;\n    }\n\n    private static Collection<File> listFiles(String projectBaseDir) {\n        Collection<File> files = FileUtil.loopFiles(projectBaseDir);\n        // 移除 IDEA、Git 自身的文件、Node 编译出来的文件\n        files = files.stream()\n                .filter(file -> !file.getPath().contains(separator + \"target\" + separator)\n                        && !file.getPath().contains(separator + \"node_modules\" + separator)\n                        && !file.getPath().contains(separator + \".idea\" + separator)\n                        && !file.getPath().contains(separator + \".git\" + separator)\n                        && !file.getPath().contains(separator + \"dist\" + separator)\n                        && !file.getPath().contains(\".iml\")\n                        && !file.getPath().contains(\".html.gz\"))\n                .collect(Collectors.toList());\n        return files;\n    }\n\n    private static String replaceFileContent(File file, String groupIdNew,\n                                             String artifactIdNew, String packageNameNew,\n                                             String titleNew) {\n        String content = FileUtil.readString(file, StandardCharsets.UTF_8);\n        // 如果是白名单的文件类型，不进行重写\n        String fileType = getFileType(file);\n        if (WHITE_FILE_TYPES.contains(fileType)) {\n            return content;\n        }\n        // 执行文件内容都重写\n        return content.replaceAll(GROUP_ID, groupIdNew)\n                .replaceAll(PACKAGE_NAME, packageNameNew)\n                .replaceAll(ARTIFACT_ID, artifactIdNew) // 必须放在最后替换，因为 ARTIFACT_ID 太短！\n                .replaceAll(StrUtil.upperFirst(ARTIFACT_ID), StrUtil.upperFirst(artifactIdNew))\n                .replaceAll(TITLE, titleNew);\n    }\n\n    private static void writeFile(File file, String fileContent, String projectBaseDir,\n                                  String projectBaseDirNew, String packageNameNew, String artifactIdNew) {\n        String newPath = buildNewFilePath(file, projectBaseDir, projectBaseDirNew, packageNameNew, artifactIdNew);\n        FileUtil.writeUtf8String(fileContent, newPath);\n    }\n\n    private static void copyFile(File file, String projectBaseDir,\n                                 String projectBaseDirNew, String packageNameNew, String artifactIdNew) {\n        String newPath = buildNewFilePath(file, projectBaseDir, projectBaseDirNew, packageNameNew, artifactIdNew);\n        FileUtil.copyFile(file, new File(newPath));\n    }\n\n    private static String buildNewFilePath(File file, String projectBaseDir,\n                                           String projectBaseDirNew, String packageNameNew, String artifactIdNew) {\n        return file.getPath().replace(projectBaseDir, projectBaseDirNew) // 新目录\n                .replace(PACKAGE_NAME.replaceAll(\"\\\\.\", Matcher.quoteReplacement(separator)),\n                        packageNameNew.replaceAll(\"\\\\.\", Matcher.quoteReplacement(separator)))\n                .replace(ARTIFACT_ID, artifactIdNew) //\n                .replaceAll(StrUtil.upperFirst(ARTIFACT_ID), StrUtil.upperFirst(artifactIdNew));\n    }\n\n    private static String getFileType(File file) {\n        return file.length() > 0 ? FileTypeUtil.getType(file) : \"\";\n    }\n}\n"
  },
  {
    "path": "yudao-module-ai/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <modules>\n        <module>yudao-module-ai-api</module>\n        <module>yudao-module-ai-server</module>\n    </modules>\n    <packaging>pom</packaging>\n    <artifactId>yudao-module-ai</artifactId>\n\n    <name>${project.artifactId}</name>\n    <description>\n        ai 模块下，接入 LLM 大模型，支持聊天、绘图、音乐、写作、思维导图等功能。\n        目前已接入各种模型，不限于：\n          国内：通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek\n          国外：OpenAI、Ollama、Midjourney、StableDiffusion、Suno\n    </description>\n\n</project>"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-ai</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-ai-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        ai 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/package-info.java",
    "content": "/**\n * 占位，没有特别的作用\n */\npackage cn.iocoder.yudao.module.ai.api;"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java",
    "content": "package cn.iocoder.yudao.module.ai.enums;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * AI 内置聊天角色的枚举\n *\n * @author xiaoxin\n */\n@AllArgsConstructor\n@Getter\npublic enum AiChatRoleEnum {\n\n    AI_WRITE_ROLE(\"写作助手\", \"\"\"\n            你是一位出色的写作助手，能够帮助用户生成创意和灵感，并在用户提供场景和提示词时生成对应的回复。你的任务包括：\n            1.\t撰写建议：根据用户提供的主题或问题，提供详细的写作建议、情节发展方向、角色设定以及背景描写，确保内容结构清晰、有逻辑。\n            2.\t回复生成：根据用户提供的场景和提示词，生成合适的对话或文字回复，确保语气和风格符合场景需求。\n            除此之外不需要除了正文内容外的其他回复，如标题、开头、任何解释性语句或道歉。\n            \"\"\"),\n\n    AI_MIND_MAP_ROLE(\"导图助手\", \"\"\"\n             你是一位非常优秀的思维导图助手，你会把用户的所有提问都总结成思维导图，然后以 Markdown 格式输出。markdown 只需要输出一级标题，二级标题，三级标题，四级标题，最多输出四级，除此之外不要输出任何其他 markdown 标记。下面是一个合格的例子：\n             # Geek-AI 助手\n             ## 完整的开源系统\n             ### 前端开源\n             ### 后端开源\n             ## 支持各种大模型\n             ### OpenAI\n             ### Azure\n             ### 文心一言\n             ### 通义千问\n             ## 集成多种收费方式\n             ### 支付宝\n             ### 微信\n            除此之外不要任何解释性语句。\n            \"\"\"),\n    ;\n\n    /**\n     * 角色名\n     */\n    private final String name;\n\n    /**\n     * 角色设定\n     */\n    private final String systemMessage;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiDocumentSplitStrategyEnum.java",
    "content": "package cn.iocoder.yudao.module.ai.enums;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * AI 知识库文档切片策略枚举\n *\n * @author runzhen\n */\n@AllArgsConstructor\n@Getter\npublic enum AiDocumentSplitStrategyEnum {\n\n    /**\n     * 自动识别文档类型并选择最佳切片策略\n     */\n    AUTO(\"auto\", \"自动识别\"),\n\n    /**\n     * 基于 Token 数量机械切分（默认策略）\n     */\n    TOKEN(\"token\", \"Token 切分\"),\n\n    /**\n     * 按段落切分（以双换行符为分隔）\n     */\n    PARAGRAPH(\"paragraph\", \"段落切分\"),\n\n    /**\n     * Markdown QA 格式专用切片器\n     * 识别二级标题作为问题，保持问答对完整性\n     * 长答案智能切分但保留问题作为上下文\n     */\n    MARKDOWN_QA(\"markdown_qa\", \"Markdown QA 切分\"),\n\n    /**\n     * 语义化切分，保留句子完整性\n     * 在段落和句子边界处切分，避免截断\n     */\n    SEMANTIC(\"semantic\", \"语义切分\");\n\n    /**\n     * 策略代码\n     */\n    private final String code;\n\n    /**\n     * 策略名称\n     */\n    private final String name;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/DictTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.ai.enums;\n\n/**\n * AI 字典类型的枚举类\n *\n * @author xiaoxin\n */\npublic interface DictTypeConstants {\n\n    // ========== AI Write ==========\n    String AI_WRITE_FORMAT = \"ai_write_format\"; // 写作格式\n    String AI_WRITE_LENGTH = \"ai_write_length\"; // 写作长度\n    String AI_WRITE_LANGUAGE = \"ai_write_language\"; // 写作语言\n    String AI_WRITE_TONE = \"ai_write_tone\"; // 写作语气\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.ai.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * AI 错误码枚举类\n * <p>\n * ai 系统，使用 1-040-000-000 段\n */\npublic interface ErrorCodeConstants {\n\n    // ========== API 密钥 1-040-000-000 ==========\n    ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, \"API 密钥不存在\");\n    ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, \"API 密钥已禁用！\");\n\n    // ========== API 模型 1-040-001-000 ==========\n    ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, \"模型不存在!\");\n    ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, \"模型({})已禁用!\");\n    ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, \"操作失败，找不到默认模型\");\n    ErrorCode MODEL_USE_TYPE_ERROR = new ErrorCode(1_040_001_003, \"操作失败，该模型的模型类型不正确\");\n\n    // ========== API 聊天角色 1-040-002-000 ==========\n    ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, \"聊天角色不存在\");\n    ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, \"聊天角色({})已禁用!\");\n\n    // ========== API 聊天会话 1-040-003-000 ==========\n    ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, \"对话不存在!\");\n    ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, \"操作失败，该聊天模型的配置不完整\");\n\n    // ========== API 聊天消息 1-040-004-000 ==========\n    ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, \"消息不存在!\");\n    ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, \"对话生成异常!\");\n\n    // ========== API 绘画 1-040-005-000 ==========\n    ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_040_005_000, \"图片不存在!\");\n    ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_040_005_001, \"Midjourney 提交失败!原因：{}\");\n    ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_040_005_002, \"Midjourney 按钮 customId 不存在! {}\");\n\n    // ========== API 音乐 1-040-006-000 ==========\n    ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_040_006_000, \"音乐不存在!\");\n\n    // ========== API 写作 1-040-007-000 ==========\n    ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_040_007_000, \"作文不存在!\");\n    ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_040_07_001, \"写作生成异常!\");\n\n    // ========== API 思维导图 1-040-008-000 ==========\n    ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, \"思维导图不存在!\");\n\n    // ========== API 知识库 1-040-009-000 ==========\n    ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_040_009_000, \"知识库不存在!\");\n\n    ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_040_009_101, \"文档不存在!\");\n    ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_040_009_102, \"文档内容为空!\");\n    ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_040_009_102, \"文件下载失败!\");\n    ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_040_009_102, \"文档加载失败!\");\n\n    ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_040_009_202, \"段落不存在!\");\n    ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_040_009_203, \"内容 Token 数为 {}，超过最大限制 {}\");\n\n    // ========== AI 工具 1-040-010-000 ==========\n    ErrorCode TOOL_NOT_EXISTS = new ErrorCode(1_040_010_000, \"工具不存在\");\n    ErrorCode TOOL_NAME_NOT_EXISTS = new ErrorCode(1_040_010_001, \"工具({})找不到 Bean\");\n\n    // ========== AI 工作流 1-040-011-000 ==========\n    ErrorCode WORKFLOW_NOT_EXISTS = new ErrorCode(1_040_011_000, \"工作流不存在\");\n    ErrorCode WORKFLOW_CODE_EXISTS = new ErrorCode(1_040_011_001, \"工作流标识已存在\");\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.ai.enums.image;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * AI 绘画状态的枚举\n *\n * @author fansili\n */\n@AllArgsConstructor\n@Getter\npublic enum AiImageStatusEnum {\n\n    IN_PROGRESS(10, \"进行中\"),\n    SUCCESS(20, \"已完成\"),\n    FAIL(30, \"已失败\");\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    public static AiImageStatusEnum valueOfStatus(Integer status) {\n        for (AiImageStatusEnum statusEnum : AiImageStatusEnum.values()) {\n            if (statusEnum.getStatus().equals(status)) {\n                return statusEnum;\n            }\n        }\n        throw new IllegalArgumentException(\"未知会话状态： \" + status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.ai.enums.model;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * AI 模型类型的枚举\n *\n * @author 芋道源码\n */\n@Getter\n@RequiredArgsConstructor\npublic enum AiModelTypeEnum implements ArrayValuable<Integer> {\n\n    CHAT(1, \"对话\"),\n    IMAGE(2, \"图片\"),\n    VOICE(3, \"语音\"),\n    VIDEO(4, \"视频\"),\n    EMBEDDING(5, \"向量\"),\n    RERANK(6, \"重排序\");\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 类型名\n     */\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiModelTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java",
    "content": "package cn.iocoder.yudao.module.ai.enums.model;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * AI 模型平台\n *\n * @author fansili\n */\n@Getter\n@AllArgsConstructor\npublic enum AiPlatformEnum implements ArrayValuable<String> {\n\n    // ========== 国内平台 ==========\n\n    TONG_YI(\"TongYi\", \"通义千问\"), // 阿里\n    YI_YAN(\"YiYan\", \"文心一言\"), // 百度\n    DEEP_SEEK(\"DeepSeek\", \"DeepSeek\"), // DeepSeek\n    ZHI_PU(\"ZhiPu\", \"智谱\"), // 智谱 AI\n    XING_HUO(\"XingHuo\", \"星火\"), // 讯飞\n    DOU_BAO(\"DouBao\", \"豆包\"), // 字节\n    HUN_YUAN(\"HunYuan\", \"混元\"), // 腾讯\n    SILICON_FLOW(\"SiliconFlow\", \"硅基流动\"), // 硅基流动\n    MINI_MAX(\"MiniMax\", \"MiniMax\"), // 稀宇科技\n    MOONSHOT(\"Moonshot\", \"月之暗面\"), // KIMI\n    BAI_CHUAN(\"BaiChuan\", \"百川智能\"), // 百川智能\n\n    // ========== 国外平台 ==========\n\n    OPENAI(\"OpenAI\", \"OpenAI\"), // OpenAI 官方\n    AZURE_OPENAI(\"AzureOpenAI\", \"AzureOpenAI\"), // OpenAI 微软\n    ANTHROPIC(\"Anthropic\", \"Anthropic\"), // Anthropic Claude\n    GEMINI(\"Gemini\", \"Gemini\"), // 谷歌 Gemini\n    OLLAMA(\"Ollama\", \"Ollama\"),\n\n    STABLE_DIFFUSION(\"StableDiffusion\", \"StableDiffusion\"), // Stability AI\n    MIDJOURNEY(\"Midjourney\", \"Midjourney\"), // Midjourney\n    SUNO(\"Suno\", \"Suno\"), // Suno AI\n    GROK(\"Grok\",\"Grok\"), // Grok\n\n    ;\n\n    /**\n     * 平台\n     */\n    private final String platform;\n    /**\n     * 平台名\n     */\n    private final String name;\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(AiPlatformEnum::getPlatform).toArray(String[]::new);\n\n    public static AiPlatformEnum validatePlatform(String platform) {\n        for (AiPlatformEnum platformEnum : AiPlatformEnum.values()) {\n            if (platformEnum.getPlatform().equals(platform)) {\n                return platformEnum;\n            }\n        }\n        throw new IllegalArgumentException(\"非法平台： \" + platform);\n    }\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java",
    "content": "package cn.iocoder.yudao.module.ai.enums.music;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * AI 音乐生成模式的枚举\n *\n * @author xiaoxin\n */\n@AllArgsConstructor\n@Getter\npublic enum AiMusicGenerateModeEnum implements ArrayValuable<Integer> {\n\n    DESCRIPTION(1, \"描述模式\"),\n    LYRIC(2, \"歌词模式\");\n\n    /**\n     * 模式\n     */\n    private final Integer mode;\n    /**\n     * 模式名\n     */\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicGenerateModeEnum::getMode).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.ai.enums.music;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * AI 音乐状态的枚举\n *\n * @author xiaoxin\n */\n@AllArgsConstructor\n@Getter\npublic enum AiMusicStatusEnum implements ArrayValuable<Integer> {\n\n    IN_PROGRESS(10, \"进行中\"),\n    SUCCESS(20, \"已完成\"),\n    FAIL(30, \"已失败\");\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicStatusEnum::getStatus).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.ai.enums.write;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * AI 写作类型的枚举\n *\n * @author xiaoxin\n */\n@AllArgsConstructor\n@Getter\npublic enum AiWriteTypeEnum implements ArrayValuable<Integer> {\n\n    WRITING(1, \"撰写\", \"请撰写一篇关于 [{}] 的文章。文章的内容格式：{}，语气：{}，语言：{}，长度：{}。请确保涵盖主要内容，不需要除了正文内容外的其他回复，如标题、额外的解释或道歉。\"),\n    REPLY(2, \"回复\", \"请针对如下内容：[{}] 做个回复。回复内容参考：[{}], 回复格式：{}，语气：{}，语言：{}，长度：{}。不需要除了正文内容外的其他回复，如标题、开头、额外的解释或道歉。\");\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 类型名\n     */\n    private final String name;\n\n    /**\n     * 模版\n     */\n    private final String prompt;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiWriteTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:8-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-module-ai-server\nWORKDIR /yudao-module-ai-server\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-module-ai-server.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48080 端口\nEXPOSE 48090\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-ai</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-ai-server</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        ai 模块下，接入 LLM 大模型，支持聊天、绘图、音乐、写作、思维导图等功能。\n        目前已接入各种模型，不限于：\n          国内：通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek\n          国外：OpenAI、Ollama、Midjourney、StableDiffusion、Suno\n    </description>\n    <properties>\n        <spring-ai.version>1.1.2</spring-ai.version>\n        <!-- https://mvnrepository.com/artifact/com.alibaba.cloud.ai/spring-ai-alibaba -->\n        <alibaba-ai.version>1.1.0.0-RC2</alibaba-ai.version>\n        <tinyflow.version>1.2.6</tinyflow.version>\n    </properties>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-system-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-infra-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-ai-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- Job 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-job</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n\n        <!-- Spring AI Model 模型接入 -->\n        <dependency>\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-model-openai</artifactId>\n            <version>${spring-ai.version}</version>\n            <exclusions>\n                <exclusion>\n                    <groupId>io.swagger.core.v3</groupId>\n                    <artifactId>swagger-annotations-jakarta</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-model-azure-openai</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-model-anthropic</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-model-deepseek</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-model-ollama</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-model-stability-ai</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n        <dependency>\n            <!-- 智谱 GLM -->\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-model-zhipuai</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-model-minimax</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n\n        <dependency>\n            <!-- 通义千问 -->\n            <groupId>com.alibaba.cloud.ai</groupId>\n            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>\n            <version>${alibaba-ai.version}</version>\n        </dependency>\n\n        <dependency>\n            <!-- 文心一言 -->\n            <groupId>org.springaicommunity</groupId>\n            <artifactId>qianfan-spring-boot-starter</artifactId>\n            <version>1.0.0</version>\n        </dependency>\n        <dependency>\n            <!-- 月之暗面 -->\n            <groupId>org.springaicommunity</groupId>\n            <artifactId>moonshot-spring-boot-starter</artifactId>\n            <version>1.0.0</version>\n        </dependency>\n\n        <!-- 向量存储：https://db-engines.com/en/ranking/vector+dbms -->\n        <dependency>\n            <!-- Qdrant：https://qdrant.tech/ -->\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-vector-store-qdrant</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n\n        <dependency>\n            <!-- Redis：https://redis.io/docs/latest/develop/get-started/vector-database/ -->\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-vector-store-redis</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <dependency>\n            <!-- Milvus：https://milvus.io/ -->\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-vector-store-milvus</artifactId>\n            <version>${spring-ai.version}</version>\n            <exclusions>\n                <!-- 解决和 logback 的日志冲突 -->\n                <exclusion>\n                    <groupId>org.slf4j</groupId>\n                    <artifactId>slf4j-reload4j</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <!-- Tika：负责内容的解析 -->\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-tika-document-reader</artifactId>\n            <version>${spring-ai.version}</version>\n            <!-- TODO 芋艿：boot 项目里，不引入 cloud 依赖！！！另外，这样也是为了解决启动报错的问题！ -->\n            <exclusions>\n                <exclusion>\n                    <artifactId>spring-cloud-function-context</artifactId>\n                    <groupId>org.springframework.cloud</groupId>\n                </exclusion>\n                <exclusion>\n                    <artifactId>spring-cloud-function-core</artifactId>\n                    <groupId>org.springframework.cloud</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!-- MCP 相关 -->\n        <!--\n            特殊说明：不能使用 spring-ai-starter-mcp-server-webflux 或 spring-ai-starter-mcp-client-webflux ！！！\n            原因：项目使用了 SpringMVC，而不是 WebFlux。引入上述 2 个，会导致 SSE Server 失效。\n        -->\n        <dependency>\n            <!-- 服务端 -->\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>\n            <version>${spring-ai.version}</version>\n            <exclusions>\n                <exclusion>\n                    <groupId>io.swagger.core.v3</groupId>\n                    <artifactId>swagger-annotations-jakarta</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <!-- 客户端 -->\n            <groupId>org.springframework.ai</groupId>\n            <artifactId>spring-ai-starter-mcp-client</artifactId>\n            <version>${spring-ai.version}</version>\n        </dependency>\n\n        <!-- TinyFlow：AI 工作流 -->\n        <dependency>\n            <groupId>dev.tinyflow</groupId>\n            <artifactId>tinyflow-java-core</artifactId>\n            <version>${tinyflow.version}</version>\n            <exclusions>\n                <exclusion>\n                    <groupId>com.jfinal</groupId>\n                    <artifactId>enjoy</artifactId>\n                </exclusion>\n                <exclusion>\n                    <!-- 解决 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1318/ 问题 -->\n                    <groupId>com.agentsflex</groupId>\n                    <artifactId>agents-flex-store-elasticsearch</artifactId>\n                </exclusion>\n                <exclusion>\n                    <!-- 解决 https://t.zsxq.com/pCBZC 问题 -->\n                    <groupId>com.agentsflex</groupId>\n                    <artifactId>agents-flex-search-engine-es</artifactId>\n                </exclusion>\n                <exclusion>\n                    <!-- TODO @芋艿：暂时移除 groovy，和 iot 冲突 -->\n                    <groupId>org.codehaus.groovy</groupId>\n                    <artifactId>groovy-all</artifactId>\n                </exclusion>\n                <!-- 解决和 logback 的日志冲突 -->\n                <exclusion>\n                    <groupId>org.slf4j</groupId>\n                    <artifactId>slf4j-simple</artifactId>\n                </exclusion>\n                <exclusion>\n                    <groupId>org.apache.logging.log4j</groupId>\n                    <artifactId>log4j-slf4j-impl</artifactId>\n                </exclusion>\n                <exclusion>\n                    <groupId>org.slf4j</groupId>\n                    <artifactId>slf4j-reload4j</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/AiServerApplication.java",
    "content": "package cn.iocoder.yudao.module.ai;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n * <p>\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication(exclude = {\n        org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration.class,\n        org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration.class,\n}) // 解决 application-${profile}.yaml 配置文件下，通过 spring.autoconfigure.exclude 无法排除的问题\npublic class AiServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(AiServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;\nimport cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;\nimport cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;\nimport com.fhs.core.trans.anno.TransMethodResult;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - AI 聊天对话\")\n@RestController\n@RequestMapping(\"/ai/chat/conversation\")\n@Validated\npublic class AiChatConversationController {\n\n    @Resource\n    private AiChatConversationService chatConversationService;\n    @Resource\n    private AiChatMessageService chatMessageService;\n\n    @PostMapping(\"/create-my\")\n    @Operation(summary = \"创建【我的】聊天对话\")\n    public CommonResult<Long> createChatConversationMy(@RequestBody @Valid AiChatConversationCreateMyReqVO createReqVO) {\n        return success(chatConversationService.createChatConversationMy(createReqVO, getLoginUserId()));\n    }\n\n    @PutMapping(\"/update-my\")\n    @Operation(summary = \"更新【我的】聊天对话\")\n    public CommonResult<Boolean> updateChatConversationMy(@RequestBody @Valid AiChatConversationUpdateMyReqVO updateReqVO) {\n        chatConversationService.updateChatConversationMy(updateReqVO, getLoginUserId());\n        return success(true);\n    }\n\n    @GetMapping(\"/my-list\")\n    @Operation(summary = \"获得【我的】聊天对话列表\")\n    @TransMethodResult\n    public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() {\n        List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId());\n        return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));\n    }\n\n    @GetMapping(\"/get-my\")\n    @Operation(summary = \"获得【我的】聊天对话\")\n    @Parameter(name = \"id\", required = true, description = \"对话编号\", example = \"1024\")\n    @TransMethodResult\n    public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam(\"id\") Long id) {\n        AiChatConversationDO conversation = chatConversationService.getChatConversation(id);\n        if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {\n            conversation = null;\n        }\n        return success(BeanUtils.toBean(conversation, AiChatConversationRespVO.class));\n    }\n\n    @DeleteMapping(\"/delete-my\")\n    @Operation(summary = \"删除聊天对话\")\n    @Parameter(name = \"id\", required = true, description = \"对话编号\", example = \"1024\")\n    public CommonResult<Boolean> deleteChatConversationMy(@RequestParam(\"id\") Long id) {\n        chatConversationService.deleteChatConversationMy(id, getLoginUserId());\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-by-unpinned\")\n    @Operation(summary = \"删除未置顶的聊天对话\")\n    public CommonResult<Boolean> deleteChatConversationMyByUnpinned() {\n        chatConversationService.deleteChatConversationMyByUnpinned(getLoginUserId());\n        return success(true);\n    }\n\n    // ========== 对话管理 ==========\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得对话分页\", description = \"用于【对话管理】菜单\")\n    @PreAuthorize(\"@ss.hasPermission('ai:chat-conversation:query')\")\n    @TransMethodResult\n    public CommonResult<PageResult<AiChatConversationRespVO>> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {\n        PageResult<AiChatConversationDO> pageResult = chatConversationService.getChatConversationPage(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n        // 拼接关联数据\n        Map<Long, Integer> messageCountMap = chatMessageService.getChatMessageCountMap(\n                convertList(pageResult.getList(), AiChatConversationDO::getId));\n        return success(BeanUtils.toBean(pageResult, AiChatConversationRespVO.class,\n                conversation -> conversation.setMessageCount(messageCountMap.getOrDefault(conversation.getId(), 0))));\n    }\n\n    @Operation(summary = \"管理员删除对话\")\n    @DeleteMapping(\"/delete-by-admin\")\n    @Parameter(name = \"id\", required = true, description = \"对话编号\", example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:chat-conversation:delete')\")\n    public CommonResult<Boolean> deleteChatConversationByAdmin(@RequestParam(\"id\") Long id) {\n        chatConversationService.deleteChatConversationByAdmin(id);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http",
    "content": "### 发送消息（段式）\nPOST {{baseUrl}}/ai/chat/message/send\nContent-Type: application/json\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"conversationId\": \"1781604279872581724\",\n  \"content\": \"你是 OpenAI 么？\"\n}\n\n### 发送消息（流式）\nPOST {{baseUrl}}/ai/chat/message/send-stream\nContent-Type: application/json\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"conversationId\": \"1781604279872581724\",\n  \"content\": \"1+1=?\"\n}\n\n### 发送消息（流式）【带文件】\nPOST {{baseUrl}}/ai/chat/message/send-stream\nContent-Type: application/json\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"conversationId\": \"1781604279872581797\",\n  \"content\": \"图片里有什么？\",\n  \"attachmentUrls\": [\"http://test.yudao.iocoder.cn/1755531278.jpeg\"]\n}\n\n### 发送消息（流式）【追问带文件】\nPOST {{baseUrl}}/ai/chat/message/send-stream\nContent-Type: application/json\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"conversationId\": \"1781604279872581799\",\n  \"content\": \"说下图片里，有哪些字？\",\n  \"useContext\": true\n}\n\n### 发送消息（流式）【联网搜索】\nPOST {{baseUrl}}/ai/chat/message/send-stream\nContent-Type: application/json\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"conversationId\": \"1781604279872581799\",\n  \"content\": \"今天是周几？\",\n  \"useSearch\": true\n}\n\n### 获得指定对话的消息列表\nGET {{baseUrl}}/ai/chat/message/list-by-conversation-id?conversationId=1781604279872581799\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}\n\n### 删除消息\nDELETE {{baseUrl}}/ai/chat/message/delete?id=50\nAuthorization: {{token}}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;\nimport cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;\nimport cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - 聊天消息\")\n@RestController\n@RequestMapping(\"/ai/chat/message\")\n@Slf4j\npublic class AiChatMessageController {\n\n    @Resource\n    private AiChatMessageService chatMessageService;\n    @Resource\n    private AiChatConversationService chatConversationService;\n    @Resource\n    private AiChatRoleService chatRoleService;\n    @Resource\n    private AiKnowledgeSegmentService knowledgeSegmentService;\n    @Resource\n    private AiKnowledgeDocumentService knowledgeDocumentService;\n\n    @Operation(summary = \"发送消息（段式）\", description = \"一次性返回，响应较慢\")\n    @PostMapping(\"/send\")\n    public CommonResult<AiChatMessageSendRespVO> sendMessage(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {\n        return success(chatMessageService.sendMessage(sendReqVO, getLoginUserId()));\n    }\n\n    @Operation(summary = \"发送消息（流式）\", description = \"流式返回，响应较快\")\n    @PostMapping(value = \"/send-stream\", produces = MediaType.TEXT_EVENT_STREAM_VALUE)\n    public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {\n        return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());\n    }\n\n    @Operation(summary = \"获得指定对话的消息列表\")\n    @GetMapping(\"/list-by-conversation-id\")\n    @Parameter(name = \"conversationId\", required = true, description = \"对话编号\", example = \"1024\")\n    public CommonResult<List<AiChatMessageRespVO>> getChatMessageListByConversationId(\n            @RequestParam(\"conversationId\") Long conversationId) {\n        AiChatConversationDO conversation = chatConversationService.getChatConversation(conversationId);\n        if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {\n            return success(Collections.emptyList());\n        }\n        // 1. 获取消息列表\n        List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);\n        if (CollUtil.isEmpty(messageList)) {\n            return success(Collections.emptyList());\n        }\n\n        // 2. 拼接数据，主要是知识库段落信息\n        Map<Long, AiKnowledgeSegmentDO> segmentMap = knowledgeSegmentService.getKnowledgeSegmentMap(convertListByFlatMap(messageList,\n                message -> CollUtil.isEmpty(message.getSegmentIds()) ? null : message.getSegmentIds().stream()));\n        Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(\n                convertList(segmentMap.values(), AiKnowledgeSegmentDO::getDocumentId));\n        List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class);\n        for (int i = 0; i < messageList.size(); i++) {\n            AiChatMessageDO message = messageList.get(i);\n            if (CollUtil.isEmpty(message.getSegmentIds())) {\n                continue;\n            }\n            // 设置知识库段落信息\n            messageVOList.get(i).setSegments(convertList(message.getSegmentIds(), segmentId -> {\n                AiKnowledgeSegmentDO segment = segmentMap.get(segmentId);\n                if (segment == null) {\n                    return null;\n                }\n                AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());\n                if (document == null) {\n                    return null;\n                }\n                return new AiChatMessageRespVO.KnowledgeSegment().setId(segment.getId()).setContent(segment.getContent())\n                        .setDocumentId(segment.getDocumentId()).setDocumentName(document.getName());\n            }));\n        }\n        return success(messageVOList);\n    }\n\n    @Operation(summary = \"删除消息\")\n    @DeleteMapping(\"/delete\")\n    @Parameter(name = \"id\", required = true, description = \"消息编号\", example = \"1024\")\n    public CommonResult<Boolean> deleteChatMessage(@RequestParam(\"id\") Long id) {\n        chatMessageService.deleteChatMessage(id, getLoginUserId());\n        return success(true);\n    }\n\n    @Operation(summary = \"删除指定对话的消息\")\n    @DeleteMapping(\"/delete-by-conversation-id\")\n    @Parameter(name = \"conversationId\", required = true, description = \"对话编号\", example = \"1024\")\n    public CommonResult<Boolean> deleteChatMessageByConversationId(@RequestParam(\"conversationId\") Long conversationId) {\n        chatMessageService.deleteChatMessageByConversationId(conversationId, getLoginUserId());\n        return success(true);\n    }\n\n    // ========== 对话管理 ==========\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得消息分页\", description = \"用于【对话管理】菜单\")\n    @PreAuthorize(\"@ss.hasPermission('ai:chat-conversation:query')\")\n    public CommonResult<PageResult<AiChatMessageRespVO>> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {\n        PageResult<AiChatMessageDO> pageResult = chatMessageService.getChatMessagePage(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n        // 拼接数据\n        Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap(\n                convertSet(pageResult.getList(), AiChatMessageDO::getRoleId));\n        return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class,\n                respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(),\n                        role -> respVO.setRoleName(role.getName()))));\n    }\n\n    @Operation(summary = \"管理员删除消息\")\n    @DeleteMapping(\"/delete-by-admin\")\n    @Parameter(name = \"id\", required = true, description = \"消息编号\", example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:chat-message:delete')\")\n    public CommonResult<Boolean> deleteChatMessageByAdmin(@RequestParam(\"id\") Long id) {\n        chatMessageService.deleteChatMessageByAdmin(id);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 聊天对话创建【我的】 Request VO\")\n@Data\npublic class AiChatConversationCreateMyReqVO {\n\n    @Schema(description = \"聊天角色编号\", example = \"666\")\n    private Long roleId;\n\n    @Schema(description = \"知识库编号\", example = \"1204\")\n    private Long knowledgeId;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI 聊天对话的分页 Request VO\")\n@Data\npublic class AiChatConversationPageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"1024\")\n    private Long userId;\n\n    @Schema(description = \"对话标题\", example = \"你好\")\n    private String title;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;\n\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport com.fhs.core.trans.anno.Trans;\nimport com.fhs.core.trans.constant.TransType;\nimport com.fhs.core.trans.vo.VO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - AI 聊天对话 Response VO\")\n@Data\npublic class AiChatConversationRespVO implements VO {\n\n    @Schema(description = \"对话编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long userId;\n\n    @Schema(description = \"对话标题\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是一个标题\")\n    private String title;\n\n    @Schema(description = \"是否置顶\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean pinned;\n\n    @Schema(description = \"角色编号\", example = \"1\")\n    @Trans(type = TransType.SIMPLE, target = AiChatRoleDO.class, fields = {\"name\", \"avatar\"}, refs = {\"roleName\", \"roleAvatar\"})\n    private Long roleId;\n\n    @Schema(description = \"模型编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = \"name\", ref = \"modelName\")\n    private Long modelId;\n\n    @Schema(description = \"模型标志\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"ERNIE-Bot-turbo-0922\")\n    private String model;\n\n    @Schema(description = \"模型名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    private String modelName;\n\n    @Schema(description = \"角色设定\", example = \"一个快乐的程序员\")\n    private String systemMessage;\n\n    @Schema(description = \"温度参数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0.8\")\n    private Double temperature;\n\n    @Schema(description = \"单条回复的最大 Token 数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4096\")\n    private Integer maxTokens;\n\n    @Schema(description = \"上下文的最大 Message 数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer maxContexts;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    // ========== 关联 role 信息 ==========\n\n    @Schema(description = \"角色头像\", example = \"https://www.iocoder.cn/1.png\")\n    private String roleAvatar;\n\n    @Schema(description = \"角色名字\", example = \"小黄\")\n    private String roleName;\n\n    // ========== 仅在【对话管理】时加载 ==========\n\n    @Schema(description = \"消息数量\", example = \"20\")\n    private Integer messageCount;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - AI 聊天对话更新【我的】 Request VO\")\n@Data\npublic class AiChatConversationUpdateMyReqVO {\n\n    @Schema(description = \"对话编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"对话编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"对话标题\", example = \"我是一个标题\")\n    private String title;\n\n    @Schema(description = \"是否置顶\", example = \"true\")\n    private Boolean pinned;\n\n    @Schema(description = \"模型编号\", example = \"1\")\n    private Long modelId;\n\n    @Schema(description = \"知识库编号\", example = \"1\")\n    private Long knowledgeId;\n\n    @Schema(description = \"角色设定\", example = \"一个快乐的程序员\")\n    private String systemMessage;\n\n    @Schema(description = \"温度参数\", example = \"0.8\")\n    private Double temperature;\n\n    @Schema(description = \"单条回复的最大 Token 数量\", example = \"4096\")\n    private Integer maxTokens;\n\n    @Schema(description = \"上下文的最大 Message 数量\", example = \"10\")\n    private Integer maxContexts;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI 聊天消息的分页 Request VO\")\n@Data\npublic class AiChatMessagePageReqVO extends PageParam {\n\n    @Schema(description = \"对话编号\", example = \"2048\")\n    private Long conversationId;\n\n    @Schema(description = \"用户编号\", example = \"1024\")\n    private Long userId;\n\n    @Schema(description = \"消息内容\", example = \"你好\")\n    private String content;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - AI 聊天消息 Response VO\")\n@Data\npublic class AiChatMessageRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"对话编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long conversationId;\n\n    @Schema(description = \"回复消息编号\", example = \"1024\")\n    private Long replyId;\n\n    @Schema(description = \"消息类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"role\")\n    private String type; // 参见 MessageType 枚举类\n\n    @Schema(description = \"用户编号\", example = \"4096\")\n    private Long userId;\n\n    @Schema(description = \"角色编号\", example = \"888\")\n    private Long roleId;\n\n    @Schema(description = \"模型标志\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"gpt-3.5-turbo\")\n    private String model;\n\n    @Schema(description = \"模型编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123\")\n    private Long modelId;\n\n    @Schema(description = \"聊天内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你好，你好啊\")\n    private String content;\n\n    @Schema(description = \"推理内容\", example = \"要达到这个目标，你需要...\")\n    private String reasoningContent;\n\n    @Schema(description = \"是否携带上下文\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean useContext;\n\n    @Schema(description = \"知识库段落编号数组\", example = \"[1,2,3]\")\n    private List<Long> segmentIds;\n\n    @Schema(description = \"知识库段落数组\")\n    private List<KnowledgeSegment> segments;\n\n    @Schema(description = \"联网搜索的网页内容数组\")\n    private List<AiWebSearchResponse.WebPage> webSearchPages;\n\n    @Schema(description = \"附件 URL 数组\", example = \"https://www.iocoder.cn/1.png\")\n    private List<String> attachmentUrls;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2024-05-12 12:51\")\n    private LocalDateTime createTime;\n\n    // ========== 仅在【对话管理】时加载 ==========\n\n    @Schema(description = \"角色名字\", example = \"小黄\")\n    private String roleName;\n\n    @Schema(description = \"知识库段落\", example = \"Java 开发手册\")\n    @Data\n    public static class KnowledgeSegment {\n\n        @Schema(description = \"段落编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private Long id;\n\n        @Schema(description = \"切片内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Java 开发手册\")\n        private String content;\n\n        @Schema(description = \"文档编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24790\")\n        private Long documentId;\n\n        @Schema(description = \"文档名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"产品使用手册\")\n        private String documentName;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - AI 聊天消息发送 Request VO\")\n@Data\npublic class AiChatMessageSendReqVO {\n\n    @Schema(description = \"聊天对话编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"聊天对话编号不能为空\")\n    private Long conversationId;\n\n    @Schema(description = \"聊天内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"帮我写个 Java 算法\")\n    @NotEmpty(message = \"聊天内容不能为空\")\n    private String content;\n\n    @Schema(description = \"是否携带上下文\", example = \"true\")\n    private Boolean useContext;\n\n    @Schema(description = \"是否联网搜索\", example = \"true\")\n    private Boolean useSearch;\n\n    @Schema(description = \"附件 URL 数组\", example = \"https://www.iocoder.cn/1.png\")\n    private List<String> attachmentUrls;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - AI 聊天消息发送 Response VO\")\n@Data\npublic class AiChatMessageSendRespVO {\n\n    @Schema(description = \"发送消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Message send;\n\n    @Schema(description = \"接收消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Message receive;\n\n    @Schema(description = \"消息\")\n    @Data\n    public static class Message {\n\n        @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private Long id;\n\n        @Schema(description = \"消息类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"role\")\n        private String type; // 参见 MessageType 枚举类\n\n        @Schema(description = \"聊天内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你好，你好啊\")\n        private String content;\n\n        @Schema(description = \"推理内容\", example = \"要达到这个目标，你需要...\")\n        private String reasoningContent;\n\n        @Schema(description = \"知识库段落编号数组\", example = \"[1,2,3]\")\n        private List<Long> segmentIds;\n\n        @Schema(description = \"知识库段落数组\")\n        private List<AiChatMessageRespVO.KnowledgeSegment> segments;\n\n        @Schema(description = \"联网搜索的网页内容数组\")\n        private List<AiWebSearchResponse.WebPage> webSearchPages;\n\n        @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n        private LocalDateTime createTime;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http",
    "content": "### 生成图片：OpenAI（DALL）\nPOST {{baseUrl}}/ai/image/draw\nContent-Type: application/json\nAuthorization: {{token}}\n\n{\n  \"platform\": \"OpenAI\",\n  \"prompt\": \"可爱的小喵星人\",\n  \"model\": \"dall-e-3\",\n  \"height\": \"1024\",\n  \"width\": \"1024\",\n  \"options\": {\n    \"style\": \"vivid\"\n  }\n}\n\n### 生成图片：StableDiffusion\nPOST {{baseUrl}}/ai/image/draw\nContent-Type: application/json\nAuthorization: {{token}}\n\n{\n  \"platform\": \"StableDiffusion\",\n  \"prompt\": \"中国长城\",\n  \"model\": \"stable-diffusion-v1-6\",\n  \"height\": \"1024\",\n  \"width\": \"1024\",\n  \"style\": \"vivid\"\n}\n\n### 生成图片：生成图片（Midjourney）\nPOST {{baseUrl}}/ai/image/midjourney/imagine\nContent-Type: application/json\nAuthorization: {{token}}\n\n{\n  \"prompt\": \"中国旗袍\",\n  \"model\": \"midjourney\",\n  \"width\": \"1\",\n  \"height\": \"1\",\n  \"version\": \"6.0\"\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.image;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.*;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;\nimport cn.iocoder.yudao.module.ai.service.image.AiImageService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - AI 绘画\")\n@RestController\n@RequestMapping(\"/ai/image\")\n@Slf4j\npublic class AiImageController {\n\n    @Resource\n    private AiImageService imageService;\n\n    @GetMapping(\"/my-page\")\n    @Operation(summary = \"获取【我的】绘图分页\")\n    public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated AiImagePageReqVO pageReqVO) {\n        PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));\n    }\n\n    @GetMapping(\"/public-page\")\n    @Operation(summary = \"获取公开的绘图分页\")\n    public CommonResult<PageResult<AiImageRespVO>> getImagePagePublic(AiImagePublicPageReqVO pageReqVO) {\n        PageResult<AiImageDO> pageResult = imageService.getImagePagePublic(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));\n    }\n\n    @GetMapping(\"/get-my\")\n    @Operation(summary = \"获取【我的】绘图记录\")\n    @Parameter(name = \"id\", required = true, description = \"绘画编号\", example = \"1024\")\n    public CommonResult<AiImageRespVO> getImageMy(@RequestParam(\"id\") Long id) {\n        AiImageDO image = imageService.getImage(id);\n        if (image == null || ObjUtil.notEqual(getLoginUserId(), image.getUserId())) {\n            return success(null);\n        }\n        return success(BeanUtils.toBean(image, AiImageRespVO.class));\n    }\n\n    @GetMapping(\"/my-list-by-ids\")\n    @Operation(summary = \"获取【我的】绘图记录列表\")\n    @Parameter(name = \"ids\", required = true, description = \"绘画编号数组\", example = \"1024,2048\")\n    public CommonResult<List<AiImageRespVO>> getImageListMyByIds(@RequestParam(\"ids\") List<Long> ids) {\n        List<AiImageDO> imageList = imageService.getImageList(ids);\n        imageList.removeIf(item -> !ObjUtil.equal(getLoginUserId(), item.getUserId()));\n        return success(BeanUtils.toBean(imageList, AiImageRespVO.class));\n    }\n\n    @Operation(summary = \"生成图片\")\n    @PostMapping(\"/draw\")\n    public CommonResult<Long> drawImage(@Valid @RequestBody AiImageDrawReqVO drawReqVO) {\n        return success(imageService.drawImage(getLoginUserId(), drawReqVO));\n    }\n\n    @Operation(summary = \"删除【我的】绘画记录\")\n    @DeleteMapping(\"/delete-my\")\n    @Parameter(name = \"id\", required = true, description = \"绘画编号\", example = \"1024\")\n    public CommonResult<Boolean> deleteImageMy(@RequestParam(\"id\") Long id) {\n        imageService.deleteImageMy(id, getLoginUserId());\n        return success(true);\n    }\n\n    // ================ midjourney 专属 ================\n\n    @Operation(summary = \"【Midjourney】生成图片\")\n    @PostMapping(\"/midjourney/imagine\")\n    public CommonResult<Long> midjourneyImagine(@Valid @RequestBody AiMidjourneyImagineReqVO reqVO) {\n        Long imageId = imageService.midjourneyImagine(getLoginUserId(), reqVO);\n        return success(imageId);\n    }\n\n    @Operation(summary = \"【Midjourney】通知图片进展\", description = \"由 Midjourney Proxy 回调\")\n    @PostMapping(\"/midjourney/notify\") // 必须是 POST 方法，否则会报错\n    @PermitAll\n    @TenantIgnore\n    public CommonResult<Boolean> midjourneyNotify(@Valid @RequestBody MidjourneyApi.Notify notify) {\n        imageService.midjourneyNotify(notify);\n        return success(true);\n    }\n\n    @Operation(summary = \"【Midjourney】Action 操作（二次生成图片）\", description = \"例如说：放大、缩小、U1、U2 等\")\n    @PostMapping(\"/midjourney/action\")\n    public CommonResult<Long> midjourneyAction(@Valid @RequestBody AiMidjourneyActionReqVO reqVO) {\n        Long imageId = imageService.midjourneyAction(getLoginUserId(), reqVO);\n        return success(imageId);\n    }\n\n    // ================ 绘图管理 ================\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得绘画分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:image:query')\")\n    public CommonResult<PageResult<AiImageRespVO>> getImagePage(@Valid AiImagePageReqVO pageReqVO) {\n        PageResult<AiImageDO> pageResult = imageService.getImagePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新绘画\")\n    @PreAuthorize(\"@ss.hasPermission('ai:image:update')\")\n    public CommonResult<Boolean> updateImage(@Valid @RequestBody AiImageUpdateReqVO updateReqVO) {\n        imageService.updateImage(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除绘画\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('ai:image:delete')\")\n    public CommonResult<Boolean> deleteImage(@RequestParam(\"id\") Long id) {\n        imageService.deleteImage(id);\n        return success(true);\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.image.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.ai.openai.OpenAiImageOptions;\nimport org.springframework.ai.stabilityai.api.StabilityAiImageOptions;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - AI 绘画 Request VO\")\n@Data\npublic class AiImageDrawReqVO {\n\n    @Schema(description = \"模型编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"模型编号不能为空\")\n    private Long modelId;\n\n    @Schema(description = \"提示词\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"画一个长城\")\n    @NotEmpty(message = \"提示词不能为空\")\n    @Size(max = 1200, message = \"提示词最大 1200\")\n    private String prompt;\n\n    /**\n     * 1. dall-e-2 模型：256x256、512x512、1024x1024\n     * 2. dall-e-3 模型：1024x1024, 1792x1024, 或 1024x1792\n     */\n    @Schema(description = \"图片高度\")\n    @NotNull(message = \"图片高度不能为空\")\n    private Integer height;\n\n    @Schema(description = \"图片宽度\")\n    @NotNull(message = \"图片宽度不能为空\")\n    private Integer width;\n\n    // ========== 各平台绘画的拓展参数 ==========\n\n    /**\n     * 绘制参数，不同 platform 的不同参数\n     *\n     * 1. {@link OpenAiImageOptions}\n     * 2. {@link StabilityAiImageOptions}\n     */\n    @Schema(description = \"绘制参数\")\n    private Map<String, String> options;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.image.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI 绘画分页 Request VO\")\n@Data\npublic class AiImagePageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"28987\")\n    private Long userId;\n\n    @Schema(description = \"平台\", example = \"OpenAI\")\n    private String platform;\n\n    @Schema(description = \"提示词\", example = \"1\")\n    private String prompt;\n\n    @Schema(description = \"绘画状态\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"是否发布\", example = \"1\")\n    private Boolean publicStatus;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.image.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 绘画公开的分页 Request VO\")\n@Data\npublic class AiImagePublicPageReqVO extends PageParam {\n\n    @Schema(description = \"提示词\")\n    private String prompt;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.image.vo;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - AI 绘画 Response VO\")\n@Data\npublic class AiImageRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long userId;\n\n    @Schema(description = \"平台\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"OpenAI\")\n    private String platform;  // 参见 AiPlatformEnum 枚举\n\n    @Schema(description = \"模型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"stable-diffusion-v1-6\")\n    private String model;\n\n    @Schema(description = \"提示词\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"南极的小企鹅\")\n    private String prompt;\n\n    @Schema(description = \"图片宽度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer width;\n\n    @Schema(description = \"图片高度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer height;\n\n    @Schema(description = \"绘画状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer status;\n\n    @Schema(description = \"是否发布\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"public\")\n    private Boolean publicStatus;\n\n    @Schema(description = \"图片地址\", example = \"https://www.iocoder.cn/1.png\")\n    private String picUrl;\n\n    @Schema(description = \"绘画错误信息\", example = \"图片错误信息\")\n    private String errorMessage;\n\n    @Schema(description = \"绘制参数\")\n    private Map<String, String> options;\n\n    @Schema(description = \"mj buttons 按钮\")\n    private List<MidjourneyApi.Button> buttons;\n\n    @Schema(description = \"完成时间\")\n    private LocalDateTime finishTime;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.image.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - AI 绘画修改 Request VO\")\n@Data\npublic class AiImageUpdateReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15583\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"是否发布\", example = \"true\")\n    private Boolean publicStatus;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyActionReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - AI 绘图操作（Midjourney） Request VO\")\n@Data\npublic class AiMidjourneyActionReqVO {\n\n    @Schema(description = \"图片编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"图片编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"操作按钮编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"MJ::JOB::variation::4::06aa3e66-0e97-49cc-8201-e0295d883de4\")\n    @NotEmpty(message = \"操作按钮编号不能为空\")\n    private String customId;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - AI 绘画生成（Midjourney） Request VO\")\n@Data\npublic class AiMidjourneyImagineReqVO {\n\n    @Schema(description = \"提示词\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"中国神龙\")\n    @NotEmpty(message = \"提示词不能为空!\")\n    private String prompt;\n\n    @Schema(description = \"模型编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"模型编号不能为空\")\n    private Long modelId;\n\n    @Schema(description = \"图片宽度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"图片宽度不能为空\")\n    private Integer width;\n\n    @Schema(description = \"图片高度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"图片高度不能为空\")\n    private Integer height;\n\n    @Schema(description = \"版本号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6.0\")\n    @NotEmpty(message = \"版本号不能为空\")\n    private String version;\n\n    @Schema(description = \"参考图\", example = \"https://www.iocoder.cn/x.png\")\n    private String referImageUrl;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http",
    "content": "### 创建知识库\nPOST {{baseUrl}}/ai/knowledge/create\nContent-Type: application/json\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"name\": \"测试标题\",\n  \"description\": \"测试描述\",\n  \"embeddingModelId\": 30,\n  \"topK\": 3,\n  \"similarityThreshold\": 0.5,\n  \"status\": 0\n}\n\n### 更新知识库\nPUT {{baseUrl}}/ai/knowledge/update\nContent-Type: application/json\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"id\": 1,\n  \"name\": \"测试标题（更新）\",\n  \"description\": \"测试描述\",\n  \"embeddingModelId\": 30,\n  \"topK\": 5,\n  \"similarityThreshold\": 0.6,\n  \"status\": 0\n}\n\n### 获取知识库分页\nGET {{baseUrl}}/ai/knowledge/page?pageNo=1&pageSize=10\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - AI 知识库\")\n@RestController\n@RequestMapping(\"/ai/knowledge\")\n@Validated\npublic class AiKnowledgeController {\n\n    @Resource\n    private AiKnowledgeService knowledgeService;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获取知识库分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:query')\")\n    public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) {\n        PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得知识库\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:query')\")\n    public CommonResult<AiKnowledgeRespVO> getKnowledge(@RequestParam(\"id\") Long id) {\n        AiKnowledgeDO knowledge = knowledgeService.getKnowledge(id);\n        return success(BeanUtils.toBean(knowledge, AiKnowledgeRespVO.class));\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建知识库\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:create')\")\n    public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) {\n        return success(knowledgeService.createKnowledge(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新知识库\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:update')\")\n    public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO updateReqVO) {\n        knowledgeService.updateKnowledge(updateReqVO);\n        return success(true);\n    }\n    \n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除知识库\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:delete')\")\n    public CommonResult<Boolean> deleteKnowledge(@RequestParam(\"id\") Long id) {\n        knowledgeService.deleteKnowledge(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得知识库的精简列表\")\n    public CommonResult<List<AiKnowledgeRespVO>> getKnowledgeSimpleList() {\n        List<AiKnowledgeDO> list = knowledgeService.getKnowledgeSimpleListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, knowledge -> new AiKnowledgeRespVO()\n                .setId(knowledge.getId()).setName(knowledge.getName())));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http",
    "content": "### 创建知识文档\nPOST {{baseUrl}}/ai/knowledge/document/create\nContent-Type: application/json\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"knowledgeId\": 2,\n  \"name\": \"测试文档\",\n  \"url\": \"https://static.iocoder.cn/README.md\",\n  \"segmentMaxTokens\": 800\n}\n\n### 批量创建知识文档\nPOST {{baseUrl}}/ai/knowledge/document/create-list\nContent-Type: application/json\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"knowledgeId\": 1,\n  \"list\": [\n    {\n      \"name\": \"测试文档1\",\n      \"url\": \"https://static.iocoder.cn/README.md\",\n      \"segmentMaxTokens\": 800\n    },\n    {\n      \"name\": \"测试文档2\",\n      \"url\": \"https://static.iocoder.cn/README_yudao.md\",\n      \"segmentMaxTokens\": 400\n    }\n  ]\n}\n\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.*;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - AI 知识库文档\")\n@RestController\n@RequestMapping(\"/ai/knowledge/document\")\n@Validated\npublic class AiKnowledgeDocumentController {\n\n    @Resource\n    private AiKnowledgeDocumentService documentService;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获取文档分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:query')\")\n    public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(\n            @Valid AiKnowledgeDocumentPageReqVO pageReqVO) {\n        PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获取文档详情\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:query')\")\n    public CommonResult<AiKnowledgeDocumentRespVO> getKnowledgeDocument(@RequestParam(\"id\") Long id) {\n        AiKnowledgeDocumentDO document = documentService.getKnowledgeDocument(id);\n        return success(BeanUtils.toBean(document, AiKnowledgeDocumentRespVO.class));\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"新建文档（单个）\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:create')\")\n    public CommonResult<Long> createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) {\n        Long id = documentService.createKnowledgeDocument(reqVO);\n        return success(id);\n    }\n\n    @PostMapping(\"/create-list\")\n    @Operation(summary = \"新建文档（多个）\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:create')\")\n    public CommonResult<List<Long>> createKnowledgeDocumentList(\n            @RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) {\n        List<Long> ids = documentService.createKnowledgeDocumentList(reqVO);\n        return success(ids);\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新文档\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:update')\")\n    public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {\n        documentService.updateKnowledgeDocument(reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新文档状态\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:update')\")\n    public CommonResult<Boolean> updateKnowledgeDocumentStatus(\n            @Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) {\n        documentService.updateKnowledgeDocumentStatus(reqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除文档\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:delete')\")\n    public CommonResult<Boolean> deleteKnowledgeDocument(@RequestParam(\"id\") Long id) {\n        documentService.deleteKnowledgeDocument(id);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http",
    "content": "### 切片内容\nGET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.iocoder.cn/README_yudao.md&segmentMaxTokens=800\nContent-Type: application/json\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 搜索段落内容\nGET {{baseUrl}}/ai/knowledge/segment/search?knowledgeId=2&content=如何使用这个产品&topK=5&similarityThreshold=0.1\nContent-Type: application/json\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 获取文档处理列表\nGET {{baseUrl}}/ai/knowledge/segment/get-process-list?documentIds=1,2,3\nContent-Type: application/json\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;\nimport cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;\nimport cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.hibernate.validator.constraints.URL;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - AI 知识库段落\")\n@RestController\n@RequestMapping(\"/ai/knowledge/segment\")\n@Validated\npublic class AiKnowledgeSegmentController {\n\n    @Resource\n    private AiKnowledgeSegmentService segmentService;\n    @Resource\n    private AiKnowledgeDocumentService documentService;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获取段落详情\")\n    @Parameter(name = \"id\", description = \"段落编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:query')\")\n    public CommonResult<AiKnowledgeSegmentRespVO> getKnowledgeSegment(@RequestParam(\"id\") Long id) {\n        AiKnowledgeSegmentDO segment = segmentService.getKnowledgeSegment(id);\n        return success(BeanUtils.toBean(segment, AiKnowledgeSegmentRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获取段落分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:query')\")\n    public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(\n            @Valid AiKnowledgeSegmentPageReqVO pageReqVO) {\n        PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建段落\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:create')\")\n    public CommonResult<Long> createKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO createReqVO) {\n        return success(segmentService.createKnowledgeSegment(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新段落内容\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:update')\")\n    public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO reqVO) {\n        segmentService.updateKnowledgeSegment(reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"启禁用段落内容\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:update')\")\n    public CommonResult<Boolean> updateKnowledgeSegmentStatus(\n            @Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {\n        segmentService.updateKnowledgeSegmentStatus(reqVO);\n        return success(true);\n    }\n\n    @GetMapping(\"/split\")\n    @Operation(summary = \"切片内容\")\n    @Parameters({\n            @Parameter(name = \"url\", description = \"文档 URL\", required = true),\n            @Parameter(name = \"segmentMaxTokens\", description = \"分段的最大 Token 数\", required = true)\n    })\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:query')\")\n    public CommonResult<List<AiKnowledgeSegmentRespVO>> splitContent(\n            @RequestParam(\"url\") @URL String url,\n            @RequestParam(value = \"segmentMaxTokens\") Integer segmentMaxTokens) {\n        List<AiKnowledgeSegmentDO> segments = segmentService.splitContent(url, segmentMaxTokens);\n        return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class));\n    }\n\n    @GetMapping(\"/get-process-list\")\n    @Operation(summary = \"获取文档处理列表\")\n    @Parameter(name = \"documentIds\", description = \"文档编号列表\", required = true, example = \"1,2,3\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:query')\")\n    public CommonResult<List<AiKnowledgeSegmentProcessRespVO>> getKnowledgeSegmentProcessList(\n            @RequestParam(\"documentIds\") List<Long> documentIds) {\n        List<AiKnowledgeSegmentProcessRespVO> list = segmentService.getKnowledgeSegmentProcessList(documentIds);\n        return success(list);\n    }\n\n    @GetMapping(\"/search\")\n    @Operation(summary = \"搜索段落内容\")\n    @PreAuthorize(\"@ss.hasPermission('ai:knowledge:query')\")\n    public CommonResult<List<AiKnowledgeSegmentSearchRespVO>> searchKnowledgeSegment(\n            @Valid AiKnowledgeSegmentSearchReqVO reqVO) {\n        // 1. 搜索段落\n        List<AiKnowledgeSegmentSearchRespBO> segments = segmentService\n                .searchKnowledgeSegment(BeanUtils.toBean(reqVO, AiKnowledgeSegmentSearchReqBO.class));\n        if (CollUtil.isEmpty(segments)) {\n            return success(Collections.emptyList());\n        }\n\n        // 2. 拼接 VO\n        Map<Long, AiKnowledgeDocumentDO> documentMap = documentService.getKnowledgeDocumentMap(convertSet(\n                segments, AiKnowledgeSegmentSearchRespBO::getDocumentId));\n        return success(BeanUtils.toBean(segments, AiKnowledgeSegmentSearchRespVO.class,\n                segment -> MapUtils.findAndThen(documentMap, segment.getDocumentId(),\n                        document -> segment.setDocumentName(document.getName()))));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotBlank;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - AI 知识库文档批量创建 Request VO\")\n@Data\npublic class AiKnowledgeDocumentCreateListReqVO {\n\n    @Schema(description = \"知识库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1204\")\n    @NotNull(message = \"知识库编号不能为空\")\n    private Long knowledgeId;\n\n    @Schema(description = \"分段的最大 Token 数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"800\")\n    @NotNull(message = \"分段的最大 Token 数不能为空\")\n    private Integer segmentMaxTokens;\n\n    @Schema(description = \"文档列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"文档列表不能为空\")\n    private List<Document> list;\n\n    @Schema(description = \"文档\")\n    @Data\n    public static class Document {\n\n        @Schema(description = \"文档名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"三方登陆\")\n        @NotBlank(message = \"文档名称不能为空\")\n        private String name;\n\n        @Schema(description = \"文档 URL\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://doc.iocoder.cn\")\n        @URL(message = \"文档 URL 格式不正确\")\n        private String url;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 知识库文档的分页 Request VO\")\n@Data\npublic class AiKnowledgeDocumentPageReqVO extends PageParam {\n\n    @Schema(description = \"知识库编号\", example = \"1\")\n    private Long knowledgeId;\n\n    @Schema(description = \"文档名称\", example = \"Java 开发手册\")\n    private String name;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - AI 知识库文档 Response VO\")\n@Data\npublic class AiKnowledgeDocumentRespVO {\n\n    @Schema(description = \"文档编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24790\")\n    private Long id;\n\n    @Schema(description = \"知识库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24790\")\n    private Long knowledgeId;\n\n    @Schema(description = \"文档名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Java 开发手册\")\n    private String name;\n\n    @Schema(description = \"文档 URL\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://doc.iocoder.cn\")\n    private String url;\n\n    @Schema(description = \"文档内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Java 是一门面向对象的语言.....\")\n    private String content;\n\n    @Schema(description = \"文档内容长度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Integer contentLength;\n\n    @Schema(description = \"文档 Token 数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer tokens;\n\n    @Schema(description = \"分片最大 Token 数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"512\")\n    private Integer segmentMaxTokens;\n\n    @Schema(description = \"召回次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer retrievalCount;\n\n    @Schema(description = \"文档状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 知识库文档更新 Request VO\")\n@Data\npublic class AiKnowledgeDocumentUpdateReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15583\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"名称\", example = \"Java 开发手册\")\n    private String name;\n\n    @Schema(description = \"分片最大 Token 数\", example = \"1000\")\n    private Integer segmentMaxTokens;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 知识库文档更新状态 Request VO\")\n@Data\npublic class AiKnowledgeDocumentUpdateStatusReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15583\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n\n@Schema(description = \"管理后台 - AI 知识库文档的创建 Request VO\")\n@Data\npublic class AiKnowledgeDocumentCreateReqVO {\n\n    @Schema(description = \"知识库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1204\")\n    @NotNull(message = \"知识库编号不能为空\")\n    private Long knowledgeId;\n\n    @Schema(description = \"文档名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"三方登陆\")\n    @NotBlank(message = \"文档名称不能为空\")\n    private String name;\n\n    @Schema(description = \"文档 URL\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://doc.iocoder.cn\")\n    @URL(message = \"文档 URL 格式不正确\")\n    private String url;\n\n    @Schema(description = \"分段的最大 Token 数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"800\")\n    @NotNull(message = \"分段的最大 Token 数不能为空\")\n    private Integer segmentMaxTokens;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI 知识库的分页 Request VO\")\n@Data\npublic class AiKnowledgePageReqVO extends PageParam {\n\n    @Schema(description = \"知识库名称\", example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"是否启用\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - AI 知识库 Response VO\")\n@Data\npublic class AiKnowledgeRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24790\")\n    private Long id;\n\n    @Schema(description = \"知识库名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"ruoyi-vue-pro 用户指南\")\n    private String name;\n\n    @Schema(description = \"知识库描述\", example = \"帮助你快速构建系统\")\n    private String description;\n\n    @Schema(description = \"向量模型编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"14\")\n    private Long embeddingModelId;\n\n    @Schema(description = \"向量模型标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"qwen-72b-chat\")\n    private String embeddingModel;\n\n    @Schema(description = \"topK\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3\")\n    private Integer topK;\n\n    @Schema(description = \"相似度阈值\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0.7\")\n    private Double similarityThreshold;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotBlank;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 知识库新增/修改 Request VO\")\n@Data\npublic class AiKnowledgeSaveReqVO {\n\n    @Schema(description = \"对话编号\", example = \"1204\")\n    private Long id;\n\n    @Schema(description = \"知识库名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"ruoyi-vue-pro 用户指南\")\n    @NotBlank(message = \"知识库名称不能为空\")\n    private String name;\n\n    @Schema(description = \"知识库描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"存储 ruoyi-vue-pro 操作文档\")\n    private String description;\n\n    @Schema(description = \"向量模型编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"向量模型不能为空\")\n    private Long embeddingModelId;\n\n    @Schema(description = \"topK\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3\")\n    @NotNull(message = \"topK 不能为空\")\n    private Integer topK;\n\n    @Schema(description = \"相似性阈值\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0.5\")\n    @NotNull(message = \"相似性阈值不能为空\")\n    private Double similarityThreshold;\n\n    @Schema(description = \"是否启用\",  requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"是否启用不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 知识库分段的分页 Request VO\")\n@Data\npublic class AiKnowledgeSegmentPageReqVO extends PageParam {\n\n    @Schema(description = \"文档编号\", example = \"1\")\n    private Long documentId;\n\n    @Schema(description = \"分段内容关键字\", example = \"Java 开发\")\n    private String content;\n\n    @Schema(description = \"分段状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 知识库段落向量进度 Response VO\")\n@Data\npublic class AiKnowledgeSegmentProcessRespVO {\n\n    @Schema(description = \"文档编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long documentId;\n\n    @Schema(description = \"总段落数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long count;\n\n    @Schema(description = \"已向量化段落数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    private Long embeddingCount;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 知识库文档分片 Response VO\")\n@Data\npublic class AiKnowledgeSegmentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24790\")\n    private Long id;\n\n    @Schema(description = \"文档编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24790\")\n    private Long documentId;\n\n    @Schema(description = \"知识库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24790\")\n    private Long knowledgeId;\n\n    @Schema(description = \"向量库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1858496a-1dde-4edf-a43e-0aed08f37f8c\")\n    private String vectorId;\n\n    @Schema(description = \"切片内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Java 开发手册\")\n    private String content;\n\n    @Schema(description = \"切片内容长度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer contentLength;\n\n    @Schema(description = \"token 数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer tokens;\n\n    @Schema(description = \"召回次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer retrievalCount;\n\n    @Schema(description = \"文档状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Long createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotEmpty;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 新增/修改知识库段落 request VO\")\n@Data\npublic class AiKnowledgeSegmentSaveReqVO {\n\n    @Schema(description = \"编号\", example = \"24790\")\n    private Long id;\n\n    @Schema(description = \"知识库文档编号\", example = \"1024\")\n    private Long documentId;\n\n    @Schema(description = \"切片内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Java 开发手册\")\n    @NotEmpty(message = \"切片内容不能为空\")\n    private String content;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - AI 知识库段落搜索 Request VO\")\n@Data\npublic class AiKnowledgeSegmentSearchReqVO {\n\n    @Schema(description = \"知识库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"知识库编号不能为空\")\n    private Long knowledgeId;\n\n    @Schema(description = \"内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"如何使用这个产品\")\n    @NotEmpty(message = \"内容不能为空\")\n    private String content;\n\n    @Schema(description = \"最大返回数量\", example = \"5\")\n    private Integer topK;\n\n    @Schema(description = \"相似度阈值\", example = \"0.7\")\n    private Double similarityThreshold;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 知识库段落搜索 Response VO\")\n@Data\npublic class AiKnowledgeSegmentSearchRespVO extends AiKnowledgeSegmentRespVO {\n\n    @Schema(description = \"文档名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"产品使用手册\")\n    private String documentName;\n\n    @Schema(description = \"相似度分数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0.95\")\n    private Double score;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n\n@Schema(description = \"管理后台 - AI 知识库段落的更新状态 Request VO\")\n@Data\npublic class AiKnowledgeSegmentUpdateStatusReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24790\")\n    private Long id;\n\n    @Schema(description = \"是否启用\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"是否启用不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/AiMindMapController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.mindmap;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapRespVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;\nimport cn.iocoder.yudao.module.ai.service.mindmap.AiMindMapService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - AI 思维导图\")\n@RestController\n@RequestMapping(\"/ai/mind-map\")\npublic class AiMindMapController {\n\n    @Resource\n    private AiMindMapService mindMapService;\n\n    @PostMapping(value = \"/generate-stream\", produces = MediaType.TEXT_EVENT_STREAM_VALUE)\n    @Operation(summary = \"导图生成（流式）\", description = \"流式返回，响应较快\")\n    public Flux<CommonResult<String>> generateMindMap(@RequestBody @Valid AiMindMapGenerateReqVO generateReqVO) {\n        return mindMapService.generateMindMap(generateReqVO, getLoginUserId());\n    }\n\n    // ================ 导图管理 ================\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除思维导图\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('ai:mind-map:delete')\")\n    public CommonResult<Boolean> deleteMindMap(@RequestParam(\"id\") Long id) {\n        mindMapService.deleteMindMap(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得思维导图分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:mind-map:query')\")\n    public CommonResult<PageResult<AiMindMapRespVO>> getMindMapPage(@Valid AiMindMapPageReqVO pageReqVO) {\n        PageResult<AiMindMapDO> pageResult = mindMapService.getMindMapPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiMindMapRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\n\n@Schema(description = \"管理后台 - AI 思维导图生成 Request VO\")\n@Data\npublic class AiMindMapGenerateReqVO {\n\n    @Schema(description = \"思维导图内容提示\", example = \"Java 学习路线\")\n    @NotBlank(message = \"思维导图内容提示不能为空\")\n    private String prompt;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI 思维导图分页 Request VO\")\n@Data\npublic class AiMindMapPageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"4325\")\n    private Long userId;\n\n    @Schema(description = \"生成内容提示\", example = \"Java 学习路线\")\n    private String prompt;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - AI 思维导图 Response VO\")\n@Data\npublic class AiMindMapRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3373\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4325\")\n    private Long userId;\n\n    @Schema(description = \"生成内容提示\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Java 学习路线\")\n    private String prompt;\n\n    @Schema(description = \"生成的思维导图内容\")\n    private String generatedContent;\n\n    @Schema(description = \"平台\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"OpenAI\")\n    private String platform;\n\n    @Schema(description = \"模型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"gpt-3.5-turbo-0125\")\n    private String model;\n\n    @Schema(description = \"错误信息\")\n    private String errorMessage;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;\nimport cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - AI API 密钥\")\n@RestController\n@RequestMapping(\"/ai/api-key\")\n@Validated\npublic class AiApiKeyController {\n\n    @Resource\n    private AiApiKeyService apiKeyService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建 API 密钥\")\n    @PreAuthorize(\"@ss.hasPermission('ai:api-key:create')\")\n    public CommonResult<Long> createApiKey(@Valid @RequestBody AiApiKeySaveReqVO createReqVO) {\n        return success(apiKeyService.createApiKey(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新 API 密钥\")\n    @PreAuthorize(\"@ss.hasPermission('ai:api-key:update')\")\n    public CommonResult<Boolean> updateApiKey(@Valid @RequestBody AiApiKeySaveReqVO updateReqVO) {\n        apiKeyService.updateApiKey(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除 API 密钥\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('ai:api-key:delete')\")\n    public CommonResult<Boolean> deleteApiKey(@RequestParam(\"id\") Long id) {\n        apiKeyService.deleteApiKey(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得 API 密钥\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:api-key:query')\")\n    public CommonResult<AiApiKeyRespVO> getApiKey(@RequestParam(\"id\") Long id) {\n        AiApiKeyDO apiKey = apiKeyService.getApiKey(id);\n        return success(BeanUtils.toBean(apiKey, AiApiKeyRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得 API 密钥分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:api-key:query')\")\n    public CommonResult<PageResult<AiApiKeyRespVO>> getApiKeyPage(@Valid AiApiKeyPageReqVO pageReqVO) {\n        PageResult<AiApiKeyDO> pageResult = apiKeyService.getApiKeyPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiApiKeyRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得 API 密钥分页列表\")\n    public CommonResult<List<AiModelRespVO>> getApiKeySimpleList() {\n        List<AiApiKeyDO> list = apiKeyService.getApiKeyList();\n        return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName())));\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;\nimport com.fhs.core.trans.anno.TransMethodResult;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - AI 聊天角色\")\n@RestController\n@RequestMapping(\"/ai/chat-role\")\n@Validated\npublic class AiChatRoleController {\n\n    @Resource\n    private AiChatRoleService chatRoleService;\n\n    @GetMapping(\"/my-page\")\n    @Operation(summary = \"获得【我的】聊天角色分页\")\n    @TransMethodResult\n    public CommonResult<PageResult<AiChatRoleRespVO>> getChatRoleMyPage(@Valid AiChatRolePageReqVO pageReqVO) {\n        PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRoleMyPage(pageReqVO, getLoginUserId());\n        return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class));\n    }\n\n    @GetMapping(\"/get-my\")\n    @Operation(summary = \"获得【我的】聊天角色\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @TransMethodResult\n    public CommonResult<AiChatRoleRespVO> getChatRoleMy(@RequestParam(\"id\") Long id) {\n        AiChatRoleDO chatRole = chatRoleService.getChatRole(id);\n        if (ObjUtil.notEqual(chatRole.getUserId(), getLoginUserId())) {\n            return success(null);\n        }\n        return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class));\n    }\n\n    @PostMapping(\"/create-my\")\n    @Operation(summary = \"创建【我的】聊天角色\")\n    public CommonResult<Long> createChatRoleMy(@Valid @RequestBody AiChatRoleSaveMyReqVO createReqVO) {\n        return success(chatRoleService.createChatRoleMy(createReqVO, getLoginUserId()));\n    }\n\n    @PutMapping(\"/update-my\")\n    @Operation(summary = \"更新【我的】聊天角色\")\n    public CommonResult<Boolean> updateChatRoleMy(@Valid @RequestBody AiChatRoleSaveMyReqVO updateReqVO) {\n        chatRoleService.updateChatRoleMy(updateReqVO, getLoginUserId());\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-my\")\n    @Operation(summary = \"删除【我的】聊天角色\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    public CommonResult<Boolean> deleteChatRoleMy(@RequestParam(\"id\") Long id) {\n        chatRoleService.deleteChatRoleMy(id, getLoginUserId());\n        return success(true);\n    }\n\n    @GetMapping(\"/category-list\")\n    @Operation(summary = \"获得聊天角色的分类列表\")\n    public CommonResult<List<String>> getChatRoleCategoryList() {\n        return success(chatRoleService.getChatRoleCategoryList());\n    }\n\n    // ========== 角色管理 ==========\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建聊天角色\")\n    @PreAuthorize(\"@ss.hasPermission('ai:chat-role:create')\")\n    public CommonResult<Long> createChatRole(@Valid @RequestBody AiChatRoleSaveReqVO createReqVO) {\n        return success(chatRoleService.createChatRole(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新聊天角色\")\n    @PreAuthorize(\"@ss.hasPermission('ai:chat-role:update')\")\n    public CommonResult<Boolean> updateChatRole(@Valid @RequestBody AiChatRoleSaveReqVO updateReqVO) {\n        chatRoleService.updateChatRole(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除聊天角色\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('ai:chat-role:delete')\")\n    public CommonResult<Boolean> deleteChatRole(@RequestParam(\"id\") Long id) {\n        chatRoleService.deleteChatRole(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得聊天角色\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:chat-role:query')\")\n    @TransMethodResult\n    public CommonResult<AiChatRoleRespVO> getChatRole(@RequestParam(\"id\") Long id) {\n        AiChatRoleDO chatRole = chatRoleService.getChatRole(id);\n        return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得聊天角色分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:chat-role:query')\")\n    public CommonResult<PageResult<AiChatRoleRespVO>> getChatRolePage(@Valid AiChatRolePageReqVO pageReqVO) {\n        PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRolePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - AI 模型\")\n@RestController\n@RequestMapping(\"/ai/model\")\n@Validated\npublic class AiModelController {\n\n    @Resource\n    private AiModelService modelService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建模型\")\n    @PreAuthorize(\"@ss.hasPermission('ai:model:create')\")\n    public CommonResult<Long> createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) {\n        return success(modelService.createModel(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新模型\")\n    @PreAuthorize(\"@ss.hasPermission('ai:model:update')\")\n    public CommonResult<Boolean> updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) {\n        modelService.updateModel(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除模型\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('ai:model:delete')\")\n    public CommonResult<Boolean> deleteModel(@RequestParam(\"id\") Long id) {\n        modelService.deleteModel(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得模型\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:model:query')\")\n    public CommonResult<AiModelRespVO> getModel(@RequestParam(\"id\") Long id) {\n        AiModelDO model = modelService.getModel(id);\n        return success(BeanUtils.toBean(model, AiModelRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得模型分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:model:query')\")\n    public CommonResult<PageResult<AiModelRespVO>> getModelPage(@Valid AiModelPageReqVO pageReqVO) {\n        PageResult<AiModelDO> pageResult = modelService.getModelPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiModelRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得模型列表\")\n    @Parameter(name = \"type\", description = \"类型\", required = true, example = \"1\")\n    @Parameter(name = \"platform\", description = \"平台\", example = \"midjourney\")\n    public CommonResult<List<AiModelRespVO>> getModelSimpleList(\n            @RequestParam(\"type\") Integer type,\n            @RequestParam(value = \"platform\", required = false) String platform) {\n        List<AiModelDO> list = modelService.getModelListByStatusAndType(\n                CommonStatusEnum.ENABLE.getStatus(), type, platform);\n        return success(convertList(list, model -> new AiModelRespVO().setId(model.getId())\n                .setName(model.getName()).setModel(model.getModel()).setPlatform(model.getPlatform())));\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;\nimport cn.iocoder.yudao.module.ai.service.model.AiToolService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - AI 工具\")\n@RestController\n@RequestMapping(\"/ai/tool\")\n@Validated\npublic class AiToolController {\n\n    @Resource\n    private AiToolService toolService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建工具\")\n    @PreAuthorize(\"@ss.hasPermission('ai:tool:create')\")\n    public CommonResult<Long> createTool(@Valid @RequestBody AiToolSaveReqVO createReqVO) {\n        return success(toolService.createTool(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新工具\")\n    @PreAuthorize(\"@ss.hasPermission('ai:tool:update')\")\n    public CommonResult<Boolean> updateTool(@Valid @RequestBody AiToolSaveReqVO updateReqVO) {\n        toolService.updateTool(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除工具\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('ai:tool:delete')\")\n    public CommonResult<Boolean> deleteTool(@RequestParam(\"id\") Long id) {\n        toolService.deleteTool(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得工具\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:tool:query')\")\n    public CommonResult<AiToolRespVO> getTool(@RequestParam(\"id\") Long id) {\n        AiToolDO tool = toolService.getTool(id);\n        return success(BeanUtils.toBean(tool, AiToolRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得工具分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:tool:query')\")\n    public CommonResult<PageResult<AiToolRespVO>> getToolPage(@Valid AiToolPageReqVO pageReqVO) {\n        PageResult<AiToolDO> pageResult = toolService.getToolPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiToolRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得工具列表\")\n    public CommonResult<List<AiToolRespVO>> getToolSimpleList() {\n        List<AiToolDO> list = toolService.getToolListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, tool -> new AiToolRespVO()\n                .setId(tool.getId()).setName(tool.getName())));\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI API 密钥分页 Request VO\")\n@Data\npublic class AiApiKeyPageReqVO extends PageParam {\n\n    @Schema(description = \"名称\", example = \"文心一言\")\n    private String name;\n\n    @Schema(description = \"平台\", example = \"OpenAI\")\n    private String platform;\n\n    @Schema(description = \"状态\", example = \"1\")\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\n\n@Schema(description = \"管理后台 - AI API 密钥 Response VO\")\n@Data\npublic class AiApiKeyRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23538\")\n    private Long id;\n\n    @Schema(description = \"名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"文心一言\")\n    private String name;\n\n    @Schema(description = \"密钥\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"ABC\")\n    private String apiKey;\n\n    @Schema(description = \"平台\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"OpenAI\")\n    private String platform;\n\n    @Schema(description = \"自定义 API 地址\", example = \"https://aip.baidubce.com\")\n    private String url;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport jakarta.validation.constraints.*;\n\n@Schema(description = \"管理后台 - AI API 密钥新增/修改 Request VO\")\n@Data\npublic class AiApiKeySaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23538\")\n    private Long id;\n\n    @Schema(description = \"名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"文心一言\")\n    @NotEmpty(message = \"名称不能为空\")\n    private String name;\n\n    @Schema(description = \"密钥\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"ABC\")\n    @NotEmpty(message = \"密钥不能为空\")\n    private String apiKey;\n\n    @Schema(description = \"平台\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"OpenAI\")\n    @NotEmpty(message = \"平台不能为空\")\n    private String platform;\n\n    @Schema(description = \"自定义 API 地址\", example = \"https://aip.baidubce.com\")\n    private String url;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRolePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;\n\nimport lombok.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n@Schema(description = \"管理后台 - AI 聊天角色分页 Request VO\")\n@Data\npublic class AiChatRolePageReqVO extends PageParam {\n\n    @Schema(description = \"角色名称\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"角色类别\", example = \"创作\")\n    private String category;\n\n    @Schema(description = \"是否公开\", example = \"1\")\n    private Boolean publicStatus;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;\n\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport com.fhs.core.trans.anno.Trans;\nimport com.fhs.core.trans.constant.TransType;\nimport com.fhs.core.trans.vo.VO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - AI 聊天角色 Response VO\")\n@Data\npublic class AiChatRoleRespVO implements VO {\n\n    @Schema(description = \"角色编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"32746\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"9442\")\n    private Long userId;\n\n    @Schema(description = \"模型编号\", example = \"17640\")\n    @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = { \"name\", \"model\" }, refs = { \"modelName\", \"model\" })\n    private Long modelId;\n    @Schema(description = \"模型名字\", example = \"张三\")\n    private String modelName;\n    @Schema(description = \"模型标识\", example = \"gpt-3.5-turbo-0125\")\n    private String model;\n\n    @Schema(description = \"角色名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    private String name;\n\n    @Schema(description = \"角色头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    private String avatar;\n\n    @Schema(description = \"角色类别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"创作\")\n    private String category;\n\n    @Schema(description = \"角色排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer sort;\n\n    @Schema(description = \"角色描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你说的对\")\n    private String description;\n\n    @Schema(description = \"角色设定\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String systemMessage;\n\n    @Schema(description = \"引用的知识库编号列表\", example = \"1,2,3\")\n    private List<Long> knowledgeIds;\n\n    @Schema(description = \"引用的工具编号列表\", example = \"1,2,3\")\n    private List<Long> toolIds;\n\n    @Schema(description = \"引用的 MCP Client 名字列表\", example = \"filesystem\")\n    private List<String> mcpClientNames;\n\n    @Schema(description = \"是否公开\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Boolean publicStatus;\n\n    @Schema(description = \"状态\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotEmpty;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - AI 聊天角色新增/修改【我的】 Request VO\")\n@Data\npublic class AiChatRoleSaveMyReqVO {\n\n    @Schema(description = \"角色编号\", example = \"32746\")\n    private Long id;\n\n    @Schema(description = \"角色名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotEmpty(message = \"角色名称不能为空\")\n    private String name;\n\n    @Schema(description = \"角色头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"角色头像不能为空\")\n    @URL(message = \"角色头像必须是 URL 格式\")\n    private String avatar;\n\n    @Schema(description = \"角色描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你说的对\")\n    @NotEmpty(message = \"角色描述不能为空\")\n    private String description;\n\n    @Schema(description = \"角色设定\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"现在开始你扮演一位程序员，你是一名优秀的程序员，具有很强的逻辑思维能力，总能高效的解决问题\")\n    @NotEmpty(message = \"角色设定不能为空\")\n    private String systemMessage;\n\n    @Schema(description = \"引用的知识库编号列表\", example = \"1,2,3\")\n    private List<Long> knowledgeIds;\n\n    @Schema(description = \"引用的工具编号列表\", example = \"1,2,3\")\n    private List<Long> toolIds;\n\n    @Schema(description = \"引用的 MCP Client 名字列表\", example = \"filesystem\")\n    private List<String> mcpClientNames;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport jakarta.validation.constraints.*;\nimport org.hibernate.validator.constraints.URL;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - AI 聊天角色新增/修改 Request VO\")\n@Data\npublic class AiChatRoleSaveReqVO {\n\n    @Schema(description = \"角色编号\", example = \"32746\")\n    private Long id;\n\n    @Schema(description = \"模型编号\", example = \"17640\")\n    private Long modelId;\n\n    @Schema(description = \"角色名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotEmpty(message = \"角色名称不能为空\")\n    private String name;\n\n    @Schema(description = \"角色头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"角色头像不能为空\")\n    @URL(message = \"角色头像必须是 URL 格式\")\n    private String avatar;\n\n    @Schema(description = \"角色类别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"创作\")\n    @NotEmpty(message = \"角色类别不能为空\")\n    private String category;\n\n    @Schema(description = \"角色排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"角色排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"角色描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你说的对\")\n    @NotEmpty(message = \"角色描述不能为空\")\n    private String description;\n\n    @Schema(description = \"角色设定\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"现在开始你扮演一位程序员，你是一名优秀的程序员，具有很强的逻辑思维能力，总能高效的解决问题\")\n    @NotEmpty(message = \"角色设定不能为空\")\n    private String systemMessage;\n\n    @Schema(description = \"引用的知识库编号列表\", example = \"1,2,3\")\n    private List<Long> knowledgeIds;\n\n    @Schema(description = \"引用的工具编号列表\", example = \"1,2,3\")\n    private List<Long> toolIds;\n\n    @Schema(description = \"引用的 MCP Client 名字列表\", example = \"filesystem\")\n    private List<String> mcpClientNames;\n\n    @Schema(description = \"是否公开\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"是否公开不能为空\")\n    private Boolean publicStatus;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;\n\nimport lombok.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n@Schema(description = \"管理后台 - API 模型分页 Request VO\")\n@Data\npublic class AiModelPageReqVO extends PageParam {\n\n    @Schema(description = \"模型名字\", example = \"张三\")\n    private String name;\n\n    @Schema(description = \"模型标识\", example = \"gpt-3.5-turbo-0125\")\n    private String model;\n\n    @Schema(description = \"模型平台\", example = \"OpenAI\")\n    private String platform;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - AI 模型 Response VO\")\n@Data\npublic class AiModelRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2630\")\n    private Long id;\n\n    @Schema(description = \"API 秘钥编号\", example = \"22042\")\n    private Long keyId;\n\n    @Schema(description = \"模型名字\", example = \"张三\")\n    private String name;\n\n    @Schema(description = \"模型标识\", example = \"gpt-3.5-turbo-0125\")\n    private String model;\n\n    @Schema(description = \"模型平台\", example = \"OpenAI\")\n    private String platform;\n\n    @Schema(description = \"模型类型\", example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"排序\", example = \"1\")\n    private Integer sort;\n\n    @Schema(description = \"状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"温度参数\", example = \"1\")\n    private Double temperature;\n\n    @Schema(description = \"单条回复的最大 Token 数量\", example = \"4096\")\n    private Integer maxTokens;\n\n    @Schema(description = \"上下文的最大 Message 数量\", example = \"8192\")\n    private Integer maxContexts;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;\n\nimport cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - API 模型新增/修改 Request VO\")\n@Data\npublic class AiModelSaveReqVO {\n\n    @Schema(description = \"编号\", example = \"2630\")\n    private Long id;\n\n    @Schema(description = \"API 秘钥编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22042\")\n    @NotNull(message = \"API 秘钥编号不能为空\")\n    private Long keyId;\n\n    @Schema(description = \"模型名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    @NotEmpty(message = \"模型名字不能为空\")\n    private String name;\n\n    @Schema(description = \"模型标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"gpt-3.5-turbo-0125\")\n    @NotEmpty(message = \"模型标识不能为空\")\n    private String model;\n\n    @Schema(description = \"模型平台\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"OpenAI\")\n    @NotEmpty(message = \"模型平台不能为空\")\n    @InEnum(AiPlatformEnum.class)\n    private String platform;\n\n    @Schema(description = \"模型类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"模型类型不能为空\")\n    @InEnum(AiModelTypeEnum.class)\n    private Integer type;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"温度参数\", example = \"1\")\n    private Double temperature;\n\n    @Schema(description = \"单条回复的最大 Token 数量\", example = \"4096\")\n    private Integer maxTokens;\n\n    @Schema(description = \"上下文的最大 Message 数量\", example = \"8192\")\n    private Integer maxContexts;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI 工具分页 Request VO\")\n@Data\npublic class AiToolPageReqVO extends PageParam {\n\n    @Schema(description = \"工具名称\", example = \"王五\")\n    private String name;\n\n    @Schema(description = \"工具描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - AI 工具 Response VO\")\n@Data\npublic class AiToolRespVO {\n\n    @Schema(description = \"工具编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19661\")\n    private Long id;\n\n    @Schema(description = \"工具名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    private String name;\n\n    @Schema(description = \"工具描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotEmpty;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 工具新增/修改 Request VO\")\n@Data\npublic class AiToolSaveReqVO {\n\n    @Schema(description = \"工具编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19661\")\n    private Long id;\n\n    @Schema(description = \"工具名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    @NotEmpty(message = \"工具名称不能为空\")\n    private String name;\n\n    @Schema(description = \"工具描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.http",
    "content": "### 生成音乐：Suno + 歌词模式\nPOST {{baseUrl}}/ai/music/generate\nContent-Type: application/json\nAuthorization: {{token}}\n\n{\n  \"platform\": \"Suno\",\n  \"generateMode\": 2,\n  \"prompt\": \"创作一首带有轻松吉他旋律的流行歌曲，[verse] 描述夏日海滩的宁静，[chorus] 节奏加快，表达对自由的向往。\",\n  \"model\": \"chirp-v3.5\",\n  \"tags\": [\"Happy\"],\n  \"title\": \"Happy Song\"\n}\n\n### 生成音乐：Suno + 描述模式\nPOST {{baseUrl}}/ai/music/generate\nContent-Type: application/json\nAuthorization: {{token}}\n\n{\n  \"platform\": \"Suno\",\n  \"generateMode\": 1,\n  \"model\": \"chirp-v3.5\",\n  \"prompt\": \"happy music\",\n  \"makeInstrumental\": false\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.music;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;\nimport cn.iocoder.yudao.module.ai.service.music.AiMusicService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - AI 音乐\")\n@RestController\n@RequestMapping(\"/ai/music\")\npublic class AiMusicController {\n\n    @Resource\n    private AiMusicService musicService;\n\n    @GetMapping(\"/my-page\")\n    @Operation(summary = \"获得【我的】音乐分页\")\n    public CommonResult<PageResult<AiMusicRespVO>> getMusicMyPage(@Valid AiMusicPageReqVO pageReqVO) {\n        PageResult<AiMusicDO> pageResult = musicService.getMusicMyPage(pageReqVO, getLoginUserId());\n        return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));\n    }\n\n    @PostMapping(\"/generate\")\n    @Operation(summary = \"音乐生成\")\n    public CommonResult<List<Long>> generateMusic(@RequestBody @Valid AiSunoGenerateReqVO reqVO) {\n        return success(musicService.generateMusic(getLoginUserId(), reqVO));\n    }\n\n    @Operation(summary = \"删除【我的】音乐记录\")\n    @DeleteMapping(\"/delete-my\")\n    @Parameter(name = \"id\", required = true, description = \"音乐编号\", example = \"1024\")\n    public CommonResult<Boolean> deleteMusicMy(@RequestParam(\"id\") Long id) {\n        musicService.deleteMusicMy(id, getLoginUserId());\n        return success(true);\n    }\n\n    @GetMapping(\"/get-my\")\n    @Operation(summary = \"获取【我的】音乐\")\n    @Parameter(name = \"id\", required = true, description = \"音乐编号\", example = \"1024\")\n    public CommonResult<AiMusicRespVO> getMusicMy(@RequestParam(\"id\") Long id) {\n        AiMusicDO music = musicService.getMusic(id);\n        if (music == null || ObjUtil.notEqual(getLoginUserId(), music.getUserId())) {\n            return success(null);\n        }\n        return success(BeanUtils.toBean(music, AiMusicRespVO.class));\n    }\n\n    @PostMapping(\"/update-my\")\n    @Operation(summary = \"修改【我的】音乐 目前只支持修改标题\")\n    @Parameter(name = \"title\", required = true, description = \"音乐名称\", example = \"夜空中最亮的星\")\n    public CommonResult<Boolean> updateMy(AiMusicUpdateMyReqVO updateReqVO) {\n        musicService.updateMyMusic(updateReqVO, getLoginUserId());\n        return success(true);\n    }\n\n    // ================ 音乐管理 ================\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得音乐分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:music:query')\")\n    public CommonResult<PageResult<AiMusicRespVO>> getMusicPage(@Valid AiMusicPageReqVO pageReqVO) {\n        PageResult<AiMusicDO> pageResult = musicService.getMusicPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除音乐\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('ai:music:delete')\")\n    public CommonResult<Boolean> deleteMusic(@RequestParam(\"id\") Long id) {\n        musicService.deleteMusic(id);\n        return success(true);\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新音乐\")\n    @PreAuthorize(\"@ss.hasPermission('ai:music:update')\")\n    public CommonResult<Boolean> updateMusic(@Valid @RequestBody AiMusicUpdateReqVO updateReqVO) {\n        musicService.updateMusic(updateReqVO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.music.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;\nimport cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI 音乐分页 Request VO\")\n@Data\npublic class AiMusicPageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"12212\")\n    private Long userId;\n\n    @Schema(description = \"音乐名称\", example = \"夜空中最亮的星\")\n    private String title;\n\n    @Schema(description = \"音乐状态\", example = \"20\")\n    @InEnum(AiMusicStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"生成模式\", example = \"1\")\n    @InEnum(AiMusicGenerateModeEnum.class)\n    private Integer generateMode;\n\n    @Schema(description = \"是否发布\", example = \"true\")\n    private Boolean publicStatus;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.music.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - AI 音乐 Response VO\")\n@Data\npublic class AiMusicRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24790\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"12212\")\n    private Long userId;\n\n    @Schema(description = \"音乐名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"夜空中最亮的星\")\n    private String title;\n\n    @Schema(description = \"歌词\", example = \"oh~卖糕的\")\n    private String lyric;\n\n    @Schema(description = \"图片地址\", example = \"https://www.iocoder.cn\")\n    private String imageUrl;\n\n    @Schema(description = \"音频地址\", example = \"https://www.iocoder.cn\")\n    private String audioUrl;\n\n    @Schema(description = \"视频地址\", example = \"https://www.iocoder.cn\")\n    private String videoUrl;\n\n    @Schema(description = \"音乐状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Integer status;\n\n    @Schema(description = \"描述词\", example = \"一首轻快的歌曲\")\n    private String gptDescriptionPrompt;\n\n    @Schema(description = \"提示词\", example = \"创作一首带有轻松吉他旋律的流行歌曲，[verse] 描述夏日海滩的宁静，[chorus] 节奏加快，表达对自由的向往。\")\n    private String prompt;\n\n    @Schema(description = \"模型平台\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Suno\")\n    private String platform;\n\n    @Schema(description = \"模型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"chirp-v3.5\")\n    private String model;\n\n    @Schema(description = \"生成模式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer generateMode;\n\n    @Schema(description = \"音乐风格标签\")\n    private List<String> tags;\n\n    @Schema(description = \"音乐时长\", example = \"[\\\"pop\\\",\\\"jazz\\\",\\\"punk\\\"]\")\n    private Double duration;\n\n    @Schema(description = \"是否发布\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean publicStatus;\n\n    @Schema(description = \"任务编号\", example = \"11369\")\n    private String taskId;\n\n    @Schema(description = \"错误信息\")\n    private String errorMessage;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.music.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - AI 修改我的音乐 Request VO\")\n@Data\npublic class AiMusicUpdateMyReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15583\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"音乐名称\", example = \"夜空中最亮的星\")\n    private String title;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.music.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - AI 音乐修改 Request VO\")\n@Data\npublic class AiMusicUpdateReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15583\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"是否发布\", example = \"true\")\n    private Boolean publicStatus;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.music.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - AI 音乐生成 Request VO\")\n@Data\npublic class AiSunoGenerateReqVO {\n\n    @Schema(description = \"平台\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Suno\")\n    @NotBlank(message = \"平台不能为空\")\n    private String platform; // 参见 AiPlatformEnum 枚举\n\n    /**\n     * 1. 描述模式：描述词 + 是否纯音乐 + 模型\n     * 2. 歌词模式：歌词 + 音乐风格 + 标题 + 模型\n     */\n    @Schema(description = \"生成模式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"生成模式不能为空\")\n    private Integer generateMode; // 参见 AiMusicGenerateModeEnum 枚举\n\n    @Schema(description = \"用于生成音乐音频的歌词提示\",\n            example = \"\"\"\n                    1.描述模式：创作一首带有轻松吉他旋律的流行歌曲，[verse] 描述夏日海滩的宁静，[chorus] 节奏加快，表达对自由的向往。\n                    2.歌词模式：\n                    [Verse]\n                    阳光下奔跑 多么欢快\n                    假期就要来 心都飞起来\n                    朋友在一旁 笑声又灿烂\n                    无忧无虑的 每一天甜蜜\n                    [Chorus]\n                    马上放假了 快来庆祝\n                    一起去旅行 快去冒险\n                    日子太短暂 别再等待\n                    马上放假了 梦想起飞\n                    \"\"\")\n    private String prompt;\n\n    @Schema(description = \"是否纯音乐\", example = \"true\")\n    private Boolean makeInstrumental;\n\n    @Schema(description = \"模型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"chirp-v3.5\")\n    @NotEmpty(message = \"模型不能为空\")\n    private String model;\n\n    @Schema(description = \"音乐风格\", example = \"[\\\"pop\\\",\\\"jazz\\\",\\\"punk\\\"]\")\n    private List<String> tags;\n\n    @Schema(description = \"音乐/歌曲名称\", example = \"夜空中最亮的星\")\n    private String title;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http",
    "content": "### 测试 AI 工作流\nPOST {{baseUrl}}/ai/workflow/test\nContent-Type: application/json\nAuthorization: {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"id\": 4,\n  \"params\": {\n    \"message\": \"1 + 1 = ?\"\n  }\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.workflow;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.*;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO;\nimport cn.iocoder.yudao.module.ai.service.workflow.AiWorkflowService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - AI 工作流\")\n@RestController\n@RequestMapping(\"/ai/workflow\")\n@Slf4j\npublic class AiWorkflowController {\n\n    @Resource\n    private AiWorkflowService workflowService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建 AI 工作流\")\n    @PreAuthorize(\"@ss.hasPermission('ai:workflow:create')\")\n    public CommonResult<Long> createWorkflow(@Valid @RequestBody AiWorkflowSaveReqVO createReqVO) {\n        return success(workflowService.createWorkflow(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新 AI 工作流\")\n    @PreAuthorize(\"@ss.hasPermission('ai:workflow:update')\")\n    public CommonResult<Boolean> updateWorkflow(@Valid @RequestBody AiWorkflowSaveReqVO updateReqVO) {\n        workflowService.updateWorkflow(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除 AI 工作流\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('ai:workflow:delete')\")\n    public CommonResult<Boolean> deleteWorkflow(@RequestParam(\"id\") Long id) {\n        workflowService.deleteWorkflow(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得 AI 工作流\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('ai:workflow:query')\")\n    public CommonResult<AiWorkflowRespVO> getWorkflow(@RequestParam(\"id\") Long id) {\n        AiWorkflowDO workflow = workflowService.getWorkflow(id);\n        return success(BeanUtils.toBean(workflow, AiWorkflowRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得 AI 工作流分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:workflow:query')\")\n    public CommonResult<PageResult<AiWorkflowRespVO>> getWorkflowPage(@Valid AiWorkflowPageReqVO pageReqVO) {\n        PageResult<AiWorkflowDO> pageResult = workflowService.getWorkflowPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiWorkflowRespVO.class));\n    }\n\n    @PostMapping(\"/test\")\n    @Operation(summary = \"测试 AI 工作流\")\n    @PreAuthorize(\"@ss.hasPermission('ai:workflow:test')\")\n    public CommonResult<Object> testWorkflow(@Valid @RequestBody AiWorkflowTestReqVO testReqVO) {\n        return success(workflowService.testWorkflow(testReqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI 工作流分页 Request VO\")\n@Data\npublic class AiWorkflowPageReqVO extends PageParam {\n\n    @Schema(description = \"名称\", example = \"工作流\")\n    private String name;\n\n    @Schema(description = \"标识\", example = \"FLOW\")\n    private String code;\n\n    @Schema(description = \"状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - AI 工作流 Response VO\")\n@Data\npublic class AiWorkflowRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"工作流标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"FLOW\")\n    private String code;\n\n    @Schema(description = \"工作流名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"工作流\")\n    private String name;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"工作流\")\n    private String remark;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"工作流模型 JSON\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n    private String graph;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"时间戳格式\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - AI 工作流新增/修改 Request VO\")\n@Data\npublic class AiWorkflowSaveReqVO {\n\n    @Schema(description = \"编号\", example = \"1\")\n    private Long id;\n\n    @Schema(description = \"工作流标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"FLOW\")\n    @NotEmpty(message = \"工作流标识不能为空\")\n    private String code;\n\n    @Schema(description = \"工作流名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"工作流\")\n    @NotEmpty(message = \"工作流名称不能为空\")\n    private String name;\n\n    @Schema(description = \"备注\", example = \"FLOW\")\n    private String remark;\n\n    @Schema(description = \"工作流模型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n    @NotEmpty(message = \"工作流模型不能为空\")\n    private String graph;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"FLOW\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo;\n\nimport cn.hutool.core.util.StrUtil;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport jakarta.validation.constraints.AssertTrue;\nimport lombok.Data;\n\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - AI 工作流测试 Request VO\")\n@Data\npublic class AiWorkflowTestReqVO {\n\n    @Schema(description = \"工作流编号\", example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"工作流模型\", example = \"{}\")\n    private String graph;\n\n    @Schema(description = \"参数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n    private Map<String, Object> params;\n\n    @AssertTrue(message = \"工作流或模型，必须传递一个\")\n    public boolean isGraphValid() {\n        return id != null || StrUtil.isNotEmpty(graph);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.write;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteRespVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;\nimport cn.iocoder.yudao.module.ai.service.write.AiWriteService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - AI 写作\")\n@RestController\n@RequestMapping(\"/ai/write\")\npublic class AiWriteController {\n\n    @Resource\n    private AiWriteService writeService;\n\n    @PostMapping(value = \"/generate-stream\", produces = MediaType.TEXT_EVENT_STREAM_VALUE)\n    @Operation(summary = \"写作生成（流式）\", description = \"流式返回，响应较快\")\n    public Flux<CommonResult<String>> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) {\n        return writeService.generateWriteContent(generateReqVO, getLoginUserId());\n    }\n\n    // ================ 写作管理 ================\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除写作\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('ai:write:delete')\")\n    public CommonResult<Boolean> deleteWrite(@RequestParam(\"id\") Long id) {\n        writeService.deleteWrite(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得写作分页\")\n    @PreAuthorize(\"@ss.hasPermission('ai:write:query')\")\n    public CommonResult<PageResult<AiWriteRespVO>> getWritePage(@Valid AiWritePageReqVO pageReqVO) {\n        PageResult<AiWriteDO> pageResult = writeService.getWritePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AiWriteRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.write.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - AI 写作生成 Request VO\")\n@Data\npublic class AiWriteGenerateReqVO {\n\n    @Schema(description = \"写作类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @InEnum(value = AiWriteTypeEnum.class, message = \"写作类型必须是 {value}\")\n    private Integer type;\n\n    @Schema(description = \"写作内容提示\", example = \"1.撰写：田忌赛马；2.回复：不批\")\n    private String prompt;\n\n    @Schema(description = \"原文\", example = \"领导我要辞职\")\n    private String originalContent;\n\n    @Schema(description = \"长度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"长度不能为空\")\n    private Integer length;\n\n    @Schema(description = \"格式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"格式不能为空\")\n    private Integer format;\n\n    @Schema(description = \"语气\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"语气不能为空\")\n    private Integer tone;\n\n    @Schema(description = \"语言\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"语言不能为空\")\n    private Integer language;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.write.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - AI 写作分页 Request VO\")\n@Data\npublic class AiWritePageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"28404\")\n    private Long userId;\n\n    @Schema(description = \"写作类型\", example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"平台\", example = \"TongYi\")\n    private String platform;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteRespVO.java",
    "content": "package cn.iocoder.yudao.module.ai.controller.admin.write.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - AI 写作 Response VO\")\n@Data\npublic class AiWriteRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5311\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28404\")\n    private Long userId;\n\n    @Schema(description = \"写作类型\", example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"平台\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"TongYi\")\n    private String platform;\n\n    @Schema(description = \"模型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"qwen\")\n    private String model;\n\n    @Schema(description = \"生成内容提示\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"撰写：田忌赛马\")\n    private String prompt;\n\n    @Schema(description = \"生成的内容\", example = \"你非常不错\")\n    private String generatedContent;\n\n    @Schema(description = \"原文\", example = \"真的么？\")\n    private String originalContent;\n\n    @Schema(description = \"长度提示词\", example = \"1\")\n    private Integer length;\n\n    @Schema(description = \"格式提示词\", example = \"2\")\n    private Integer format;\n\n    @Schema(description = \"语气提示词\", example = \"3\")\n    private Integer tone;\n\n    @Schema(description = \"语言提示词\", example = \"4\")\n    private Integer language;\n\n    @Schema(description = \"错误信息\")\n    private String errorMessage;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/app/package-info.java",
    "content": "/**\n * TODO 芋艿：站位，无特殊作用\n */\npackage cn.iocoder.yudao.module.ai.controller.app;"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/controller/package-info.java",
    "content": "/**\n * 提供 RESTful API 给前端：\n * 1. admin 包：提供给管理后台 yudao-ui-admin 前端项目\n * 2. app 包：提供给用户 APP yudao-ui-app 前端项目，它的 Controller 和 VO 都要添加 App 前缀，用于和管理后台进行区分\n */\npackage cn.iocoder.yudao.module.ai.controller;\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.chat;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * AI Chat 对话 DO\n *\n * 用户每次发起 Chat 聊天时，会创建一个 {@link AiChatConversationDO} 对象，将它的消息关联在一起\n *\n * @author fansili\n * @since 2024/4/14 17:35\n */\n@TableName(\"ai_chat_conversation\")\n@KeySequence(\"ai_chat_conversation_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AiChatConversationDO extends BaseDO {\n\n    public static final String TITLE_DEFAULT = \"新对话\";\n\n    /**\n     * ID 编号，自增\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 用户编号\n     *\n     * 关联 AdminUserDO 的 userId 字段\n     */\n    private Long userId;\n\n    /**\n     * 对话标题\n     *\n     * 默认由系统自动生成，可用户手动修改\n     */\n    private String title;\n    /**\n     * 是否置顶\n     */\n    private Boolean pinned;\n    /**\n     * 置顶时间\n     */\n    private LocalDateTime pinnedTime;\n\n    /**\n     * 角色编号\n     *\n     * 关联 {@link AiChatRoleDO#getId()}\n     */\n    private Long roleId;\n\n    /**\n     * 模型编号\n     *\n     * 关联 {@link AiModelDO#getId()} 字段\n     */\n    private Long modelId;\n    /**\n     * 模型标志\n     *\n     * 冗余 {@link AiModelDO#getModel()} 字段\n     */\n    private String model;\n\n    // ========== 对话配置 ==========\n\n    /**\n     * 角色设定\n     */\n    private String systemMessage;\n    /**\n     * 温度参数\n     *\n     * 用于调整生成回复的随机性和多样性程度：较低的温度值会使输出更收敛于高频词汇，较高的则增加多样性\n     */\n    private Double temperature;\n    /**\n     * 单条回复的最大 Token 数量\n     */\n    private Integer maxTokens;\n    /**\n     * 上下文的最大 Message 数量\n     */\n    private Integer maxContexts;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.chat;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.ai.chat.messages.MessageType;\n\nimport java.util.List;\n\n/**\n * AI Chat 消息 DO\n *\n * @since 2024/4/14 17:35\n * @since 2024/4/14 17:35\n */\n@TableName(value = \"ai_chat_message\", autoResultMap = true)\n@KeySequence(\"ai_chat_message_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AiChatMessageDO extends BaseDO {\n\n    /**\n     * 编号，作为每条聊天记录的唯一标识符\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 对话编号\n     *\n     * 关联 {@link AiChatConversationDO#getId()} 字段\n     */\n    private Long conversationId;\n    /**\n     * 回复消息编号\n     *\n     * 关联 {@link #id} 字段\n     *\n     * 大模型回复的消息编号，用于“问答”的关联\n     */\n    private Long replyId;\n\n    /**\n     * 消息类型\n     *\n     * 也等价于 OpenAPI 的 role 字段\n     *\n     * 枚举 {@link MessageType}\n     */\n    private String type;\n    /**\n     * 用户编号\n     *\n     * 关联 AdminUserDO 的 userId 字段\n     */\n    private Long userId;\n    /**\n     * 角色编号\n     *\n     * 关联 {@link AiChatRoleDO#getId()} 字段\n     */\n    private Long roleId;\n\n    /**\n     * 模型标志\n     *\n     * 冗余 {@link AiModelDO#getModel()}\n     */\n    private String model;\n    /**\n     * 模型编号\n     *\n     * 关联 {@link AiModelDO#getId()} 字段\n     */\n    private Long modelId;\n\n    /**\n     * 聊天内容\n     */\n    private String content;\n    /**\n     * 推理内容\n     */\n    private String reasoningContent;\n\n    /**\n     * 是否携带上下文\n     */\n    private Boolean useContext;\n\n    /**\n     * 知识库段落编号数组\n     *\n     * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> segmentIds;\n\n    /**\n     * 联网搜索的网页内容数组\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<AiWebSearchResponse.WebPage> webSearchPages;\n\n    /**\n     * 附件 URL 数组\n     */\n    @TableField(typeHandler = StringListTypeHandler.class)\n    private List<String> attachmentUrls;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.image;\n\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.Data;\nimport org.springframework.ai.openai.OpenAiImageOptions;\nimport org.springframework.ai.stabilityai.api.StabilityAiImageOptions;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * AI 绘画 DO\n *\n * @author fansili\n */\n@TableName(value = \"ai_image\", autoResultMap = true)\n@KeySequence(\"ai_image_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\npublic class AiImageDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 用户编号\n     *\n     * 关联 {@link AdminUserRespDTO#getId()}\n     */\n    private Long userId;\n\n    /**\n     * 提示词\n     */\n    private String prompt;\n\n    /**\n     * 平台\n     *\n     * 枚举 {@link AiPlatformEnum}\n     */\n    private String platform;\n    /**\n     * 模型编号\n     *\n     * 关联 {@link AiModelDO#getId()}\n     */\n    private Long modelId;\n    /**\n     * 模型标识\n     *\n     * 冗余 {@link AiModelDO#getModel()}\n     */\n    private String model;\n\n    /**\n     * 图片宽度\n     */\n    private Integer width;\n    /**\n     * 图片高度\n     */\n    private Integer height;\n\n    /**\n     * 生成状态\n     *\n     * 枚举 {@link AiImageStatusEnum}\n     */\n    private Integer status;\n\n    /**\n     * 完成时间\n     */\n    private LocalDateTime finishTime;\n\n    /**\n     * 绘画错误信息\n     */\n    private String errorMessage;\n\n    /**\n     * 图片地址\n     */\n    private String picUrl;\n    /**\n     * 是否公开\n     */\n    private Boolean publicStatus;\n\n    /**\n     * 绘制参数，不同 platform 的不同参数\n     *\n     * 1. {@link OpenAiImageOptions}\n     * 2. {@link StabilityAiImageOptions}\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private Map<String, Object> options;\n\n    /**\n     * mj buttons 按钮\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<MidjourneyApi.Button> buttons;\n\n    /**\n     * 任务编号\n     *\n     * 1. midjourney proxy：关联的 task id\n     */\n    private String taskId;\n\n}\n\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * AI 知识库 DO\n *\n * @author xiaoxin\n */\n@TableName(value = \"ai_knowledge\", autoResultMap = true)\n@KeySequence(\"ai_knowledge_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\npublic class AiKnowledgeDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 知识库名称\n     */\n    private String name;\n    /**\n     * 知识库描述\n     */\n    private String description;\n\n    /**\n     * 向量模型编号\n     *\n     * 关联 {@link AiModelDO#getId()}\n     */\n    private Long embeddingModelId;\n    /**\n     * 模型标识\n     *\n     * 冗余 {@link AiModelDO#getModel()}\n     */\n    private String embeddingModel;\n\n    /**\n     * topK\n     */\n    private Integer topK;\n    /**\n     * 相似度阈值\n     */\n    private Double similarityThreshold;\n\n    /**\n     * 状态\n     * <p>\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * AI 知识库-文档 DO\n *\n * @author xiaoxin\n */\n@TableName(value = \"ai_knowledge_document\")\n@KeySequence(\"ai_knowledge_document_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\npublic class AiKnowledgeDocumentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 知识库编号\n     * <p>\n     * 关联 {@link AiKnowledgeDO#getId()}\n     */\n    private Long knowledgeId;\n    /**\n     * 文档名称\n     */\n    private String name;\n    /**\n     * 文件 URL\n     */\n    private String url;\n    /**\n     * 内容\n     */\n    private String content;\n    /**\n     * 文档长度\n     */\n    private Integer contentLength;\n\n    /**\n     * 文档 token 数量\n     */\n    private Integer tokens;\n    /**\n     * 分片最大 Token 数\n     */\n    private Integer segmentMaxTokens;\n\n    /**\n     * 召回次数\n     */\n    private Integer retrievalCount;\n\n    /**\n     * 状态\n     * <p>\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * AI 知识库-文档分段 DO\n *\n * @author xiaoxin\n */\n@TableName(value = \"ai_knowledge_segment\")\n@KeySequence(\"ai_knowledge_segment_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\npublic class AiKnowledgeSegmentDO extends BaseDO {\n\n    /**\n     * 向量库的编号 - 空值\n     */\n    public static final String VECTOR_ID_EMPTY = \"\";\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 知识库编号\n     * <p>\n     * 关联 {@link AiKnowledgeDO#getId()}\n     */\n    private Long knowledgeId;\n    /**\n     * 文档编号\n     * <p>\n     * 关联 {@link AiKnowledgeDocumentDO#getId()}\n     */\n    private Long documentId;\n    /**\n     * 切片内容\n     */\n    private String content;\n    /**\n     * 切片内容长度\n     */\n    private Integer contentLength;\n\n    /**\n     * 向量库的编号\n     */\n    private String vectorId;\n    /**\n     * token 数量\n     */\n    private Integer tokens;\n\n    /**\n     * 召回次数\n     */\n    private Integer retrievalCount;\n\n    /**\n     * 状态\n     * <p>\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.mindmap;\n\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * AI 思维导图 DO\n *\n * @author xiaoxin\n */\n@TableName(value = \"ai_mind_map\")\n@KeySequence(\"ai_mind_map_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\npublic class AiMindMapDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 用户编号\n     * <p>\n     * 关联 AdminUserDO 的 userId 字段\n     */\n    private Long userId;\n\n    /**\n     * 平台\n     * <p>\n     * 枚举 {@link AiPlatformEnum}\n     */\n    private String platform;\n    /**\n     * 模型编号\n     *\n     * 关联 {@link AiModelDO#getId()}\n     */\n    private Long modelId;\n    /**\n     * 模型\n     */\n    private String model;\n\n    /**\n     * 生成内容提示\n     */\n    private String prompt;\n\n    /**\n     * 生成的内容\n     */\n    private String generatedContent;\n\n    /**\n     * 错误信息\n     */\n    private String errorMessage;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.model;\n\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * AI API 秘钥 DO\n *\n * @author 芋道源码\n */\n@TableName(\"ai_api_key\")\n@KeySequence(\"ai_api_key_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AiApiKeyDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名称\n     */\n    private String name;\n    /**\n     * 密钥\n     */\n    private String apiKey;\n    /**\n     * 平台\n     *\n     * 枚举 {@link AiPlatformEnum}\n     */\n    private String platform;\n    /**\n     * API 地址\n     */\n    private String url;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.model;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.util.List;\n\n/**\n * AI 聊天角色 DO\n *\n * @author fansili\n * @since 2024/4/24 19:39\n */\n@TableName(value = \"ai_chat_role\", autoResultMap = true)\n@KeySequence(\"ai_chat_role_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AiChatRoleDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 角色名称\n     */\n    private String name;\n    /**\n     * 角色头像\n     */\n    private String avatar;\n    /**\n     * 角色分类\n     */\n    private String category;\n    /**\n     * 角色描述\n     */\n    private String description;\n    /**\n     * 角色设定\n     */\n    private String systemMessage;\n\n    /**\n     * 用户编号\n     *\n     * 关联 AdminUserDO 的 userId 字段\n     */\n    private Long userId;\n\n    /**\n     * 模型编号\n     *\n     * 关联 {@link AiModelDO#getId()} 字段\n     */\n    private Long modelId;\n\n    /**\n     * 引用的知识库编号列表\n     *\n     * 关联 {@link AiKnowledgeDO#getId()} 字段\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> knowledgeIds;\n    /**\n     * 引用的工具编号列表\n     *\n     * 关联 {@link AiToolDO#getId()} 字段\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> toolIds;\n    /**\n     * 引用的 MCP Client 名字列表\n     *\n     * 关联 spring.ai.mcp.client 下的名字\n     */\n    @TableField(typeHandler = StringListTypeHandler.class)\n    private List<String> mcpClientNames;\n\n    /**\n     * 是否公开\n     *\n     * 1. true - 公开；由管理员在【角色管理】所创建\n     * 2. false - 私有；由个人在【我的角色】所创建\n     */\n    private Boolean publicStatus;\n\n    /**\n     * 排序值\n     */\n    private Integer sort;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.model;\n\nimport cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * AI 模型 DO\n *\n * 默认模型：{@link #status} 为开启，并且 {@link #sort} 排序第一\n *\n * @author fansili\n * @since 2024/4/24 19:39\n */\n@TableName(\"ai_model\")\n@KeySequence(\"ai_model_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AiModelDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * API 秘钥编号\n     *\n     * 关联 {@link AiApiKeyDO#getId()}\n     */\n    private Long keyId;\n    /**\n     * 模型名称\n     */\n    private String name;\n    /**\n     * 模型标志\n     */\n    private String model;\n    /**\n     * 平台\n     *\n     * 枚举 {@link AiPlatformEnum}\n     */\n    private String platform;\n    /**\n     * 类型\n     *\n     * 枚举 {@link AiModelTypeEnum}\n     */\n    private Integer type;\n\n    /**\n     * 排序值\n     */\n    private Integer sort;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n    // ========== 对话配置 ==========\n\n    /**\n     * 温度参数\n     *\n     * 用于调整生成回复的随机性和多样性程度：较低的温度值会使输出更收敛于高频词汇，较高的则增加多样性\n     */\n    private Double temperature;\n    /**\n     * 单条回复的最大 Token 数量\n     */\n    private Integer maxTokens;\n    /**\n     * 上下文的最大 Message 数量\n     */\n    private Integer maxContexts;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.model;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.ai.tool.function.DirectoryListToolFunction;\nimport cn.iocoder.yudao.module.ai.tool.function.WeatherQueryToolFunction;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * AI 工具 DO\n *\n * @author 芋道源码\n */\n@TableName(\"ai_tool\")\n@KeySequence(\"ai_tool_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AiToolDO extends BaseDO {\n\n    /**\n     * 工具编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 工具名称\n     *\n     * 对应 Bean 的名字，例如说：\n     * 1. {@link DirectoryListToolFunction} 的 Bean 名字是 directory_list\n     * 2. {@link WeatherQueryToolFunction} 的 Bean 名字是 weather_query\n     */\n    private String name;\n    /**\n     * 工具描述\n     */\n    private String description;\n    /**\n     * 状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.music;\n\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;\nimport cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * AI 音乐 DO\n *\n * @author xiaoxin\n */\n@TableName(value = \"ai_music\", autoResultMap = true)\n@KeySequence(\"ai_music_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\npublic class AiMusicDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 用户编号\n     * <p>\n     * 关联 AdminUserDO 的 userId 字段\n     */\n    private Long userId;\n\n    /**\n     * 音乐名称\n     */\n    private String title;\n\n    /**\n     * 歌词\n     */\n    private String lyric;\n\n    /**\n     * 图片地址\n     */\n    private String imageUrl;\n    /**\n     * 音频地址\n     */\n    private String audioUrl;\n    /**\n     * 视频地址\n     */\n    private String videoUrl;\n\n    /**\n     * 音乐状态\n     * <p>\n     * 枚举 {@link AiMusicStatusEnum}\n     */\n    private Integer status;\n\n    /**\n     * 生成模式\n     * <p>\n     * 枚举 {@link AiMusicGenerateModeEnum}\n     */\n    private Integer generateMode;\n\n    /**\n     * 描述词\n     */\n    private String description;\n\n    /**\n     * 平台\n     * <p>\n     * 枚举 {@link AiPlatformEnum}\n     */\n    private String platform;\n    // TODO @芋艿：modelId？\n    /**\n     * 模型\n     */\n    private String model;\n\n    /**\n     * 音乐风格标签\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<String> tags;\n\n    /**\n     * 音乐时长\n     */\n    private Double duration;\n\n    /**\n     * 是否公开\n     */\n    private Boolean publicStatus;\n\n    /**\n     * 任务编号\n     */\n    private String taskId;\n\n    /**\n     * 错误信息\n     */\n    private String errorMessage;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.workflow;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * AI 工作流 DO\n *\n * @author lesan\n */\n@TableName(value = \"ai_workflow\", autoResultMap = true)\n@KeySequence(\"ai_workflow\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\npublic class AiWorkflowDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 工作流名称\n     */\n    private String name;\n    /**\n     * 工作流标识\n     */\n    private String code;\n\n    /**\n     * 工作流模型 JSON 数据\n     */\n    private String graph;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.dataobject.write;\n\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * AI 写作 DO\n *\n * @author xiaoxin\n */\n@TableName(\"ai_write\")\n@KeySequence(\"ai_write_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\npublic class AiWriteDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 用户编号\n     *\n     * 关联 AdminUserDO 的 userId 字段\n     */\n    private Long userId;\n\n    /**\n     * 写作类型\n     * <p>\n     * 枚举 {@link AiWriteTypeEnum}\n     */\n    private Integer type;\n\n    /**\n     * 平台\n     *\n     * 枚举 {@link AiPlatformEnum}\n     */\n    private String platform;\n    /**\n     * 模型编号\n     *\n     * 关联 {@link AiModelDO#getId()}\n     */\n    private Long modelId;\n    /**\n     * 模型\n     */\n    private String model;\n\n    /**\n     * 生成内容提示\n     */\n    private String prompt;\n\n    /**\n     * 生成的内容\n     */\n    private String generatedContent;\n    /**\n     * 原文\n     */\n    private String originalContent;\n\n    /**\n     * 长度提示词\n     *\n     * 字典：{@link DictTypeConstants#AI_WRITE_LENGTH}\n     */\n    private Integer length;\n    /**\n     * 格式提示词\n     *\n     * 字典：{@link DictTypeConstants#AI_WRITE_FORMAT}\n     */\n    private Integer format;\n    /**\n     * 语气提示词\n     *\n     * 字典：{@link DictTypeConstants#AI_WRITE_TONE}\n     */\n    private Integer tone;\n    /**\n     * 语言提示词\n     *\n     * 字典：{@link DictTypeConstants#AI_WRITE_LANGUAGE}\n     */\n    private Integer language;\n\n    /**\n     * 错误信息\n     */\n    private String errorMessage;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.chat;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * AI 聊天对话 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface AiChatConversationMapper extends BaseMapperX<AiChatConversationDO> {\n\n    default List<AiChatConversationDO> selectListByUserId(Long userId) {\n        return selectList(AiChatConversationDO::getUserId, userId);\n    }\n\n    default List<AiChatConversationDO> selectListByUserIdAndPinned(Long userId, boolean pinned) {\n        return selectList(new LambdaQueryWrapperX<AiChatConversationDO>()\n                .eq(AiChatConversationDO::getUserId, userId)\n                .eq(AiChatConversationDO::getPinned, pinned));\n    }\n\n    default PageResult<AiChatConversationDO> selectChatConversationPage(AiChatConversationPageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatConversationDO>()\n                .eqIfPresent(AiChatConversationDO::getUserId, pageReqVO.getUserId())\n                .likeIfPresent(AiChatConversationDO::getTitle, pageReqVO.getTitle())\n                .betweenIfPresent(AiChatConversationDO::getCreateTime, pageReqVO.getCreateTime())\n                .orderByDesc(AiChatConversationDO::getId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.chat;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * AI 聊天对话 Mapper\n *\n * @author fansili\n */\n@Mapper\npublic interface AiChatMessageMapper extends BaseMapperX<AiChatMessageDO> {\n\n    default List<AiChatMessageDO> selectListByConversationId(Long conversationId) {\n        return selectList(new LambdaQueryWrapperX<AiChatMessageDO>()\n                .eq(AiChatMessageDO::getConversationId, conversationId)\n                .orderByAsc(AiChatMessageDO::getId));\n    }\n\n    default Map<Long, Integer> selectCountMapByConversationId(Collection<Long> conversationIds) {\n        // SQL count 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<AiChatMessageDO>()\n                .select(\"COUNT(id) AS count, conversation_id AS conversationId\")\n                .in(\"conversation_id\", conversationIds)\n                .groupBy(\"conversation_id\"));\n        if (CollUtil.isEmpty(result)) {\n            return Collections.emptyMap();\n        }\n        // 转换数据\n        return CollectionUtils.convertMap(result,\n                record -> MapUtil.getLong(record, \"conversationId\"),\n                record -> MapUtil.getInt(record, \"count\" ));\n    }\n\n    default PageResult<AiChatMessageDO> selectPage(AiChatMessagePageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatMessageDO>()\n                .eqIfPresent(AiChatMessageDO::getConversationId, pageReqVO.getConversationId())\n                .eqIfPresent(AiChatMessageDO::getUserId, pageReqVO.getUserId())\n                .likeIfPresent(AiChatMessageDO::getContent, pageReqVO.getContent())\n                .betweenIfPresent(AiChatMessageDO::getCreateTime, pageReqVO.getCreateTime())\n                .orderByDesc(AiChatMessageDO::getId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.image;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * AI 绘图 Mapper\n *\n * @author fansili\n */\n@Mapper\npublic interface AiImageMapper extends BaseMapperX<AiImageDO> {\n\n    default AiImageDO selectByTaskId(String taskId) {\n        return selectOne(AiImageDO::getTaskId, taskId);\n    }\n\n    default PageResult<AiImageDO> selectPage(AiImagePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiImageDO>()\n                .eqIfPresent(AiImageDO::getUserId, reqVO.getUserId())\n                .eqIfPresent(AiImageDO::getPlatform, reqVO.getPlatform())\n                .eqIfPresent(AiImageDO::getStatus, reqVO.getStatus())\n                .eqIfPresent(AiImageDO::getPublicStatus, reqVO.getPublicStatus())\n                .betweenIfPresent(AiImageDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(AiImageDO::getId));\n    }\n\n    default PageResult<AiImageDO> selectPageMy(Long userId, AiImagePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiImageDO>()\n                .likeIfPresent(AiImageDO::getPrompt, reqVO.getPrompt())\n                // 情况一：公开\n                .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiImageDO::getPublicStatus, reqVO.getPublicStatus())\n                // 情况二：私有\n                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiImageDO::getUserId, userId)\n                .orderByDesc(AiImageDO::getId));\n    }\n\n    default PageResult<AiImageDO> selectPage(AiImagePublicPageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiImageDO>()\n                .eqIfPresent(AiImageDO::getPublicStatus, Boolean.TRUE)\n                .likeIfPresent(AiImageDO::getPrompt, pageReqVO.getPrompt())\n                .orderByDesc(AiImageDO::getId));\n    }\n\n    default List<AiImageDO> selectListByStatusAndPlatform(Integer status, String platform) {\n        return selectList(AiImageDO::getStatus, status,\n                AiImageDO::getPlatform, platform);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * AI 知识库文档 Mapper\n *\n * @author xiaoxin\n */\n@Mapper\npublic interface AiKnowledgeDocumentMapper extends BaseMapperX<AiKnowledgeDocumentDO> {\n\n    default PageResult<AiKnowledgeDocumentDO> selectPage(AiKnowledgeDocumentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeDocumentDO>()\n                .eqIfPresent(AiKnowledgeDocumentDO::getKnowledgeId, reqVO.getKnowledgeId())\n                .likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName())\n                .orderByDesc(AiKnowledgeDocumentDO::getId));\n    }\n\n    default void updateRetrievalCountIncr(Collection<Long> ids) {\n        update(new LambdaUpdateWrapper<AiKnowledgeDocumentDO>()\n                .setSql(\" retrieval_count = retrieval_count + 1\")\n                .in(AiKnowledgeDocumentDO::getId, ids));\n    }\n\n    default List<AiKnowledgeDocumentDO> selectListByStatus(Integer status) {\n        return selectList(AiKnowledgeDocumentDO::getStatus, status);\n    }\n\n    default List<AiKnowledgeDocumentDO> selectListByKnowledgeId(Long knowledgeId) {\n        return selectList(AiKnowledgeDocumentDO::getKnowledgeId, knowledgeId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * AI 知识库 Mapper\n *\n * @author xiaoxin\n */\n@Mapper\npublic interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> {\n\n    default PageResult<AiKnowledgeDO> selectPage(AiKnowledgePageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiKnowledgeDO>()\n                .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName())\n                .eqIfPresent(AiKnowledgeDO::getStatus, pageReqVO.getStatus())\n                .betweenIfPresent(AiKnowledgeDO::getCreateTime, pageReqVO.getCreateTime())\n                .orderByDesc(AiKnowledgeDO::getId));\n    }\n\n    default List<AiKnowledgeDO> selectListByStatus(Integer status) {\n        return selectList(AiKnowledgeDO::getStatus, status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.github.yulichang.wrapper.MPJLambdaWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * AI 知识库分片 Mapper\n *\n * @author xiaoxin\n */\n@Mapper\npublic interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegmentDO> {\n\n    default PageResult<AiKnowledgeSegmentDO> selectPage(AiKnowledgeSegmentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()\n                .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId())\n                .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getContent())\n                .eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus())\n                .orderByDesc(AiKnowledgeSegmentDO::getId));\n    }\n\n    default List<AiKnowledgeSegmentDO> selectListByVectorIds(List<String> vectorIds) {\n        return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()\n                .in(AiKnowledgeSegmentDO::getVectorId, vectorIds)\n                .orderByDesc(AiKnowledgeSegmentDO::getId));\n    }\n\n    default List<AiKnowledgeSegmentDO> selectListByDocumentId(Long documentId) {\n        return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()\n                .eq(AiKnowledgeSegmentDO::getDocumentId, documentId)\n                .orderByDesc(AiKnowledgeSegmentDO::getId));\n    }\n\n    default List<AiKnowledgeSegmentDO> selectListByKnowledgeIdAndStatus(Long knowledgeId, Integer status) {\n        return selectList(AiKnowledgeSegmentDO::getKnowledgeId, knowledgeId,\n                AiKnowledgeSegmentDO::getStatus, status);\n    }\n\n    default List<AiKnowledgeSegmentProcessRespVO> selectProcessList(Collection<Long> documentIds) {\n        MPJLambdaWrapper<AiKnowledgeSegmentDO> wrapper = new MPJLambdaWrapperX<AiKnowledgeSegmentDO>()\n                .selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId)\n                .selectCount(AiKnowledgeSegmentDO::getId, \"count\")\n                .select(\"COUNT(CASE WHEN vector_id IS NOT NULL AND vector_id <> '\" + AiKnowledgeSegmentDO.VECTOR_ID_EMPTY + \"' THEN 1 ELSE NULL END) AS embeddingCount\")\n                .in(AiKnowledgeSegmentDO::getDocumentId, documentIds)\n                .groupBy(AiKnowledgeSegmentDO::getDocumentId);\n        return selectJoinList(AiKnowledgeSegmentProcessRespVO.class, wrapper);\n    }\n\n    default void updateRetrievalCountIncrByIds(List<Long> ids) {\n        update(new LambdaUpdateWrapper<AiKnowledgeSegmentDO>()\n                .setSql(\" retrieval_count = retrieval_count + 1\")\n                .in(AiKnowledgeSegmentDO::getId, ids));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/mindmap/AiMindMapMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.mindmap;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * AI 思维导图 Mapper\n *\n * @author xiaoxin\n */\n@Mapper\npublic interface AiMindMapMapper extends BaseMapperX<AiMindMapDO> {\n\n    default PageResult<AiMindMapDO> selectPage(AiMindMapPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiMindMapDO>()\n                .eqIfPresent(AiMindMapDO::getUserId, reqVO.getUserId())\n                .eqIfPresent(AiMindMapDO::getPrompt, reqVO.getPrompt())\n                .betweenIfPresent(AiMindMapDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(AiMindMapDO::getId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.model;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * AI API 密钥 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface AiApiKeyMapper extends BaseMapperX<AiApiKeyDO> {\n\n    default PageResult<AiApiKeyDO> selectPage(AiApiKeyPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiApiKeyDO>()\n                .likeIfPresent(AiApiKeyDO::getName, reqVO.getName())\n                .eqIfPresent(AiApiKeyDO::getPlatform, reqVO.getPlatform())\n                .eqIfPresent(AiApiKeyDO::getStatus, reqVO.getStatus())\n                .orderByDesc(AiApiKeyDO::getId));\n    }\n\n    default AiApiKeyDO selectFirstByPlatformAndStatus(String platform, Integer status) {\n        return selectOne(new QueryWrapperX<AiApiKeyDO>()\n                .eq(\"platform\", platform)\n                .eq(\"status\", status)\n                .limitN(1)\n                .orderByAsc(\"id\"));\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.model;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\n/**\n * API 模型 Mapper\n *\n * @author fansili\n */\n@Mapper\npublic interface AiChatMapper extends BaseMapperX<AiModelDO> {\n\n    default AiModelDO selectFirstByStatus(Integer type, Integer status) {\n        return selectOne(new QueryWrapperX<AiModelDO>()\n                .eq(\"type\", type)\n                .eq(\"status\", status)\n                .limitN(1)\n                .orderByAsc(\"sort\"));\n    }\n\n    default PageResult<AiModelDO> selectPage(AiModelPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiModelDO>()\n                .likeIfPresent(AiModelDO::getName, reqVO.getName())\n                .eqIfPresent(AiModelDO::getModel, reqVO.getModel())\n                .eqIfPresent(AiModelDO::getPlatform, reqVO.getPlatform())\n                .orderByAsc(AiModelDO::getSort));\n    }\n\n    default List<AiModelDO> selectListByStatusAndType(Integer status, Integer type,\n                                                      @Nullable String platform) {\n        return selectList(new LambdaQueryWrapperX<AiModelDO>()\n                .eq(AiModelDO::getStatus, status)\n                .eq(AiModelDO::getType, type)\n                .eqIfPresent(AiModelDO::getPlatform, platform)\n                .orderByAsc(AiModelDO::getSort));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatRoleMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.model;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * AI 聊天角色 Mapper\n *\n * @author fansili\n */\n@Mapper\npublic interface AiChatRoleMapper extends BaseMapperX<AiChatRoleDO> {\n\n    default PageResult<AiChatRoleDO> selectPage(AiChatRolePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiChatRoleDO>()\n                .likeIfPresent(AiChatRoleDO::getName, reqVO.getName())\n                .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory())\n                .eqIfPresent(AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus())\n                .orderByAsc(AiChatRoleDO::getSort));\n    }\n\n    default PageResult<AiChatRoleDO> selectPageByMy(AiChatRolePageReqVO reqVO, Long userId) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiChatRoleDO>()\n                .likeIfPresent(AiChatRoleDO::getName, reqVO.getName())\n                .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory())\n                // 情况一：公开\n                .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus())\n                // 情况二：私有\n                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getUserId, userId)\n                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getStatus, CommonStatusEnum.ENABLE.getStatus())\n                .orderByAsc(AiChatRoleDO::getSort));\n    }\n\n    default List<AiChatRoleDO> selectListGroupByCategory(Integer status) {\n        return selectList(new LambdaQueryWrapperX<AiChatRoleDO>()\n                .select(AiChatRoleDO::getCategory)\n                .eq(AiChatRoleDO::getStatus, status)\n                .groupBy(AiChatRoleDO::getCategory));\n    }\n\n    default List<AiChatRoleDO> selectListByName(String name) {\n        return selectList(new LambdaQueryWrapperX<AiChatRoleDO>()\n                .likeIfPresent(AiChatRoleDO::getName, name)\n                .orderByAsc(AiChatRoleDO::getSort));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.model;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * AI 工具 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface AiToolMapper extends BaseMapperX<AiToolDO> {\n\n    default PageResult<AiToolDO> selectPage(AiToolPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiToolDO>()\n                .likeIfPresent(AiToolDO::getName, reqVO.getName())\n                .eqIfPresent(AiToolDO::getDescription, reqVO.getDescription())\n                .eqIfPresent(AiToolDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(AiToolDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(AiToolDO::getId));\n    }\n\n    default List<AiToolDO> selectListByStatus(Integer status) {\n        return selectList(new LambdaQueryWrapperX<AiToolDO>()\n                .eq(AiToolDO::getStatus, status)\n                .orderByDesc(AiToolDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.music;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * AI 音乐 Mapper\n *\n * @author xiaoxin\n */\n@Mapper\npublic interface AiMusicMapper extends BaseMapperX<AiMusicDO> {\n\n    default List<AiMusicDO> selectListByStatus(Integer status) {\n        return selectList(AiMusicDO::getStatus, status);\n    }\n\n    default PageResult<AiMusicDO> selectPage(AiMusicPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()\n                .eqIfPresent(AiMusicDO::getUserId, reqVO.getUserId())\n                .eqIfPresent(AiMusicDO::getTitle, reqVO.getTitle())\n                .eqIfPresent(AiMusicDO::getStatus, reqVO.getStatus())\n                .eqIfPresent(AiMusicDO::getGenerateMode, reqVO.getGenerateMode())\n                .betweenIfPresent(AiMusicDO::getCreateTime, reqVO.getCreateTime())\n                .eqIfPresent(AiMusicDO::getPublicStatus, reqVO.getPublicStatus())\n                .orderByDesc(AiMusicDO::getId));\n    }\n\n    default PageResult<AiMusicDO> selectPageByMy(AiMusicPageReqVO reqVO, Long userId) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()\n                // 情况一：公开\n                .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiMusicDO::getPublicStatus, reqVO.getPublicStatus())\n                // 情况二：私有\n                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiMusicDO::getUserId, userId)\n                .orderByAsc(AiMusicDO::getId));\n    }\n    \n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.workflow;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * AI 工作流 Mapper\n *\n * @author lesan\n */\n@Mapper\npublic interface AiWorkflowMapper extends BaseMapperX<AiWorkflowDO> {\n\n    default AiWorkflowDO selectByCode(String code) {\n        return selectOne(AiWorkflowDO::getCode, code);\n    }\n\n    default PageResult<AiWorkflowDO> selectPage(AiWorkflowPageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiWorkflowDO>()\n                .eqIfPresent(AiWorkflowDO::getStatus, pageReqVO.getStatus())\n                .likeIfPresent(AiWorkflowDO::getName, pageReqVO.getName())\n                .likeIfPresent(AiWorkflowDO::getCode, pageReqVO.getCode())\n                .betweenIfPresent(AiWorkflowDO::getCreateTime, pageReqVO.getCreateTime()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/write/AiWriteMapper.java",
    "content": "package cn.iocoder.yudao.module.ai.dal.mysql.write;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * AI 写作 Mapper\n *\n * @author xiaoxin\n */\n@Mapper\npublic interface AiWriteMapper extends BaseMapperX<AiWriteDO> {\n\n    default PageResult<AiWriteDO> selectPage(AiWritePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<AiWriteDO>()\n                .eqIfPresent(AiWriteDO::getUserId, reqVO.getUserId())\n                .eqIfPresent(AiWriteDO::getType, reqVO.getType())\n                .eqIfPresent(AiWriteDO::getPlatform, reqVO.getPlatform())\n                .betweenIfPresent(AiWriteDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(AiWriteDO::getId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.config;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactoryImpl;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;\nimport cn.iocoder.yudao.module.ai.tool.method.PersonService;\nimport io.micrometer.observation.ObservationRegistry;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.deepseek.DeepSeekChatModel;\nimport org.springframework.ai.deepseek.DeepSeekChatOptions;\nimport org.springframework.ai.deepseek.api.DeepSeekApi;\nimport org.springframework.ai.embedding.BatchingStrategy;\nimport org.springframework.ai.embedding.TokenCountBatchingStrategy;\nimport org.springframework.ai.model.tool.ToolCallingManager;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.OpenAiChatOptions;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport org.springframework.ai.support.ToolCallbacks;\nimport org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;\nimport org.springframework.ai.tokenizer.TokenCountEstimator;\nimport org.springframework.ai.tool.ToolCallback;\nimport org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties;\nimport org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties;\nimport org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties;\nimport org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * 芋道 AI 自动配置\n *\n * @author fansili\n */\n@Configuration\n@EnableConfigurationProperties({ YudaoAiProperties.class,\n        QdrantVectorStoreProperties.class, // 解析 Qdrant 配置\n        RedisVectorStoreProperties.class, // 解析 Redis 配置\n        MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 解析 Milvus 配置\n})\n@Slf4j\npublic class AiAutoConfiguration {\n\n    @Bean\n    public AiModelFactory aiModelFactory() {\n        return new AiModelFactoryImpl();\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    public ObservationRegistry observationRegistry() {\n        // 特殊：兜底有 ObservationRegistry Bean，避免相关的 ChatModel 创建报错。相关 issue：https://t.zsxq.com/CuPu4\n        return ObservationRegistry.NOOP;\n    }\n\n    // ========== 各种 AI Client 创建 ==========\n\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.ai.gemini.enable\", havingValue = \"true\")\n    public GeminiChatModel geminiChatModel(YudaoAiProperties yudaoAiProperties) {\n        YudaoAiProperties.Gemini properties = yudaoAiProperties.getGemini();\n        return buildGeminiChatClient(properties);\n    }\n\n    public GeminiChatModel buildGeminiChatClient(YudaoAiProperties.Gemini properties) {\n        if (StrUtil.isEmpty(properties.getModel())) {\n            properties.setModel(GeminiChatModel.MODEL_DEFAULT);\n        }\n        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()\n                .openAiApi(OpenAiApi.builder()\n                        .baseUrl(GeminiChatModel.BASE_URL)\n                        .completionsPath(GeminiChatModel.COMPLETE_PATH)\n                        .apiKey(properties.getApiKey())\n                        .build())\n                .defaultOptions(OpenAiChatOptions.builder()\n                        .model(properties.getModel())\n                        .temperature(properties.getTemperature())\n                        .maxTokens(properties.getMaxTokens())\n                        .topP(properties.getTopP())\n                        .build())\n                .toolCallingManager(getToolCallingManager())\n                .build();\n        return new GeminiChatModel(openAiChatModel);\n    }\n\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.ai.doubao.enable\", havingValue = \"true\")\n    public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) {\n        YudaoAiProperties.DouBao properties = yudaoAiProperties.getDoubao();\n        return buildDouBaoChatClient(properties);\n    }\n\n    public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBao properties) {\n        if (StrUtil.isEmpty(properties.getModel())) {\n            properties.setModel(DouBaoChatModel.MODEL_DEFAULT);\n        }\n        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()\n                .openAiApi(OpenAiApi.builder()\n                        .baseUrl(DouBaoChatModel.BASE_URL)\n                        .completionsPath(DouBaoChatModel.COMPLETE_PATH)\n                        .apiKey(properties.getApiKey())\n                        .build())\n                .defaultOptions(OpenAiChatOptions.builder()\n                        .model(properties.getModel())\n                        .temperature(properties.getTemperature())\n                        .maxTokens(properties.getMaxTokens())\n                        .topP(properties.getTopP())\n                        .build())\n                .toolCallingManager(getToolCallingManager())\n                .build();\n        return new DouBaoChatModel(openAiChatModel);\n    }\n\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.ai.siliconflow.enable\", havingValue = \"true\")\n    public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) {\n        YudaoAiProperties.SiliconFlow properties = yudaoAiProperties.getSiliconflow();\n        return buildSiliconFlowChatClient(properties);\n    }\n\n    public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlow properties) {\n        if (StrUtil.isEmpty(properties.getModel())) {\n            properties.setModel(SiliconFlowApiConstants.MODEL_DEFAULT);\n        }\n        DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()\n                .deepSeekApi(DeepSeekApi.builder()\n                        .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)\n                        .apiKey(properties.getApiKey())\n                        .build())\n                .defaultOptions(DeepSeekChatOptions.builder()\n                        .model(properties.getModel())\n                        .temperature(properties.getTemperature())\n                        .maxTokens(properties.getMaxTokens())\n                        .topP(properties.getTopP())\n                        .build())\n                .toolCallingManager(getToolCallingManager())\n                .build();\n        return new SiliconFlowChatModel(openAiChatModel);\n    }\n\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.ai.hunyuan.enable\", havingValue = \"true\")\n    public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) {\n        YudaoAiProperties.HunYuan properties = yudaoAiProperties.getHunyuan();\n        return buildHunYuanChatClient(properties);\n    }\n\n    public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuan properties) {\n        if (StrUtil.isEmpty(properties.getModel())) {\n            properties.setModel(HunYuanChatModel.MODEL_DEFAULT);\n        }\n        // 特殊：由于混元大模型不提供 deepseek，而是通过知识引擎，所以需要区分下 URL\n        if (StrUtil.isEmpty(properties.getBaseUrl())) {\n            properties.setBaseUrl(\n                    StrUtil.startWithIgnoreCase(properties.getModel(), \"deepseek\") ? HunYuanChatModel.DEEP_SEEK_BASE_URL\n                            : HunYuanChatModel.BASE_URL);\n        }\n        // 创建 DeepSeekChatModel、HunYuanChatModel 对象\n        DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()\n                .deepSeekApi(DeepSeekApi.builder()\n                        .baseUrl(properties.getBaseUrl())\n                        .completionsPath(HunYuanChatModel.COMPLETE_PATH)\n                        .apiKey(properties.getApiKey())\n                        .build())\n                .defaultOptions(DeepSeekChatOptions.builder()\n                        .model(properties.getModel())\n                        .temperature(properties.getTemperature())\n                        .maxTokens(properties.getMaxTokens())\n                        .topP(properties.getTopP())\n                        .build())\n                .toolCallingManager(getToolCallingManager())\n                .build();\n        return new HunYuanChatModel(openAiChatModel);\n    }\n\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.ai.xinghuo.enable\", havingValue = \"true\")\n    public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) {\n        YudaoAiProperties.XingHuo properties = yudaoAiProperties.getXinghuo();\n        return buildXingHuoChatClient(properties);\n    }\n\n    public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuo properties) {\n        if (StrUtil.isEmpty(properties.getModel())) {\n            properties.setModel(XingHuoChatModel.MODEL_DEFAULT);\n        }\n        OpenAiApi.Builder builder = OpenAiApi.builder()\n                .baseUrl(XingHuoChatModel.BASE_URL_V1)\n                .apiKey(properties.getAppKey() + \":\" + properties.getSecretKey());\n        if (\"x1\".equals(properties.getModel())) {\n            builder.baseUrl(XingHuoChatModel.BASE_URL_V2)\n                    .completionsPath(XingHuoChatModel.BASE_COMPLETIONS_PATH_V2);\n        }\n        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()\n                .openAiApi(builder.build())\n                .defaultOptions(OpenAiChatOptions.builder()\n                        .model(properties.getModel())\n                        .temperature(properties.getTemperature())\n                        .maxTokens(properties.getMaxTokens())\n                        .topP(properties.getTopP())\n                        .build())\n                // TODO @芋艿：星火的 function call 有 bug，会报 ToolResponseMessage must have an id 错误！！！\n                .toolCallingManager(getToolCallingManager())\n                .build();\n        return new XingHuoChatModel(openAiChatModel);\n    }\n\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.ai.baichuan.enable\", havingValue = \"true\")\n    public BaiChuanChatModel baiChuanChatClient(YudaoAiProperties yudaoAiProperties) {\n        YudaoAiProperties.BaiChuan properties = yudaoAiProperties.getBaichuan();\n        return buildBaiChuanChatClient(properties);\n    }\n\n    public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuan properties) {\n        if (StrUtil.isEmpty(properties.getModel())) {\n            properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);\n        }\n        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()\n                .openAiApi(OpenAiApi.builder()\n                        .baseUrl(BaiChuanChatModel.BASE_URL)\n                        .apiKey(properties.getApiKey())\n                        .build())\n                .defaultOptions(OpenAiChatOptions.builder()\n                        .model(properties.getModel())\n                        .temperature(properties.getTemperature())\n                        .maxTokens(properties.getMaxTokens())\n                        .topP(properties.getTopP())\n                        .build())\n                .toolCallingManager(getToolCallingManager())\n                .build();\n        return new BaiChuanChatModel(openAiChatModel);\n    }\n\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.ai.midjourney.enable\", havingValue = \"true\")\n    public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) {\n        YudaoAiProperties.Midjourney config = yudaoAiProperties.getMidjourney();\n        return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl());\n    }\n\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.ai.suno.enable\", havingValue = \"true\")\n    public SunoApi sunoApi(YudaoAiProperties yudaoAiProperties) {\n        return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());\n    }\n\n    public ChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) {\n        if (StrUtil.isEmpty(properties.getModel())) {\n            properties.setModel(GrokChatModel.MODEL_DEFAULT);\n        }\n        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()\n                .openAiApi(OpenAiApi.builder()\n                        .baseUrl(Optional.ofNullable(properties.getBaseUrl())\n                                .orElse(GrokChatModel.BASE_URL))\n                        .completionsPath(GrokChatModel.COMPLETE_PATH)\n                        .apiKey(properties.getApiKey())\n                        .build())\n                .defaultOptions(OpenAiChatOptions.builder()\n                        .model(properties.getModel())\n                        .temperature(properties.getTemperature())\n                        .maxTokens(properties.getMaxTokens())\n                        .topP(properties.getTopP())\n                        .build())\n                .toolCallingManager(getToolCallingManager())\n                .build();\n        return new DouBaoChatModel(openAiChatModel);\n    }\n\n    // ========== RAG 相关 ==========\n\n    @Bean\n    public TokenCountEstimator tokenCountEstimator() {\n        return new JTokkitTokenCountEstimator();\n    }\n\n    @Bean\n    public BatchingStrategy batchingStrategy() {\n        return new TokenCountBatchingStrategy();\n    }\n\n    private static ToolCallingManager getToolCallingManager() {\n        return SpringUtil.getBean(ToolCallingManager.class);\n    }\n\n    // ========== Web Search 相关 ==========\n\n    @Bean\n    @ConditionalOnProperty(value = \"yudao.ai.web-search.enable\", havingValue = \"true\")\n    public AiWebSearchClient webSearchClient(YudaoAiProperties yudaoAiProperties) {\n        return new AiBoChaWebSearchClient(yudaoAiProperties.getWebSearch().getApiKey());\n    }\n\n    // ========== MCP 相关 ==========\n\n    /**\n     * 参考自 <a href=\"https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html\">MCP Server Boot Starter</>\n     */\n    @Bean\n    public List<ToolCallback> toolCallbacks(PersonService personService) {\n        return List.of(ToolCallbacks.from(personService));\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n\n/**\n * 芋道 AI 配置类\n *\n * @author fansili\n * @since 1.0\n */\n@ConfigurationProperties(prefix = \"yudao.ai\")\n@Data\npublic class YudaoAiProperties {\n\n    /**\n     * 谷歌 Gemini\n     */\n    private Gemini gemini;\n\n    /**\n     * 字节豆包\n     */\n    private DouBao doubao;\n\n    /**\n     * 腾讯混元\n     */\n    private HunYuan hunyuan;\n\n    /**\n     * 硅基流动\n     */\n    private SiliconFlow siliconflow;\n\n    /**\n     * 讯飞星火\n     */\n    private XingHuo xinghuo;\n\n    /**\n     * 百川\n     */\n    private BaiChuan baichuan;\n\n    /**\n     * Midjourney 绘图\n     */\n    private Midjourney midjourney;\n\n    /**\n     * Suno 音乐\n     */\n    @SuppressWarnings(\"SpellCheckingInspection\")\n    private Suno suno;\n\n    /**\n     * 网络搜索\n     */\n    private WebSearch webSearch;\n\n    @Data\n    public static class Gemini {\n\n        private String enable;\n        private String apiKey;\n\n        private String model;\n        private Double temperature;\n        private Integer maxTokens;\n        private Double topP;\n\n    }\n\n    @Data\n    public static class DouBao {\n\n        private String enable;\n        private String apiKey;\n\n        private String model;\n        private Double temperature;\n        private Integer maxTokens;\n        private Double topP;\n\n    }\n\n    @Data\n    public static class HunYuan {\n\n        private String enable;\n        private String baseUrl;\n        private String apiKey;\n\n        private String model;\n        private Double temperature;\n        private Integer maxTokens;\n        private Double topP;\n\n    }\n\n    @Data\n    public static class SiliconFlow {\n\n        private String enable;\n        private String apiKey;\n\n        private String model;\n        private Double temperature;\n        private Integer maxTokens;\n        private Double topP;\n\n    }\n\n    @Data\n    public static class XingHuo {\n\n        private String enable;\n        private String appId;\n        private String appKey;\n        private String secretKey;\n\n        private String model;\n        private Double temperature;\n        private Integer maxTokens;\n        private Double topP;\n\n    }\n\n    @Data\n    public static class BaiChuan {\n\n        private String enable;\n        private String apiKey;\n\n        private String model;\n        private Double temperature;\n        private Integer maxTokens;\n        private Double topP;\n\n    }\n\n    @Data\n    public static class Midjourney {\n\n        private String enable;\n        private String baseUrl;\n\n        private String apiKey;\n        private String notifyUrl;\n\n    }\n\n    @Data\n    public static class Suno {\n\n        private boolean enable;\n\n        private String baseUrl;\n\n    }\n\n    @Data\n    public static class Grok {\n\n        private String enable;\n        private String apiKey;\n        private String baseUrl;\n\n        private String model;\n        private Double temperature;\n        private Integer maxTokens;\n        private Double topP;\n\n    }\n\n    @Data\n    public static class WebSearch {\n\n        private boolean enable;\n\n        private String apiKey;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactory.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model;\n\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.embedding.EmbeddingModel;\nimport org.springframework.ai.image.ImageModel;\nimport org.springframework.ai.vectorstore.VectorStore;\n\nimport java.util.Map;\n\n/**\n * AI Model 模型工厂的接口类\n *\n * @author fansili\n */\npublic interface AiModelFactory {\n\n    /**\n     * 基于指定配置，获得 ChatModel 对象\n     *\n     * 如果不存在，则进行创建\n     *\n     * @param platform 平台\n     * @param apiKey API KEY\n     * @param url API URL\n     * @return ChatModel 对象\n     */\n    ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url);\n\n    /**\n     * 基于默认配置，获得 ChatModel 对象\n     *\n     * 默认配置，指的是在 application.yaml 配置文件中的 spring.ai 相关的配置\n     *\n     * @param platform 平台\n     * @return ChatModel 对象\n     */\n    ChatModel getDefaultChatModel(AiPlatformEnum platform);\n\n    /**\n     * 基于默认配置，获得 ImageModel 对象\n     *\n     * 默认配置，指的是在 application.yaml 配置文件中的 spring.ai 相关的配置\n     *\n     * @param platform 平台\n     * @return ImageModel 对象\n     */\n    ImageModel getDefaultImageModel(AiPlatformEnum platform);\n\n    /**\n     * 基于指定配置，获得 ImageModel 对象\n     *\n     * 如果不存在，则进行创建\n     *\n     * @param platform 平台\n     * @param apiKey API KEY\n     * @param url API URL\n     * @return ImageModel 对象\n     */\n    ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url);\n\n    /**\n     * 基于指定配置，获得 MidjourneyApi 对象\n     *\n     * 如果不存在，则进行创建\n     *\n     * @param apiKey API KEY\n     * @param url API URL\n     * @return MidjourneyApi 对象\n     */\n    MidjourneyApi getOrCreateMidjourneyApi(String apiKey, String url);\n\n    /**\n     * 基于指定配置，获得 SunoApi 对象\n     *\n     * 如果不存在，则进行创建\n     *\n     * @param apiKey API KEY\n     * @param url API URL\n     * @return SunoApi 对象\n     */\n    SunoApi getOrCreateSunoApi(String apiKey, String url);\n\n    /**\n     * 基于指定配置，获得 EmbeddingModel 对象\n     *\n     * 如果不存在，则进行创建\n     *\n     * @param platform 平台\n     * @param apiKey   API KEY\n     * @param url      API URL\n     * @param model     模型\n     * @return ChatModel 对象\n     */\n    EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model);\n\n    /**\n     * 基于指定配置，获得 VectorStore 对象\n     *\n     * 如果不存在，则进行创建\n     *\n     * @param type           向量存储类型\n     * @param embeddingModel 向量模型\n     * @param metadataFields 元数据字段\n     * @return VectorStore 对象\n     */\n    VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type,\n                                       EmbeddingModel embeddingModel,\n                                       Map<String, Class<?>> metadataFields);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model;\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.lang.Singleton;\nimport cn.hutool.core.lang.func.Func0;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.RuntimeUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringUtils;\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration;\nimport cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;\nimport com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration;\nimport com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration;\nimport com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration;\nimport com.alibaba.cloud.ai.dashscope.api.DashScopeApi;\nimport com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;\nimport com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;\nimport com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;\nimport com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel;\nimport com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions;\nimport com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;\nimport com.azure.ai.openai.OpenAIClientBuilder;\nimport com.azure.core.credential.KeyCredential;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.milvus.client.MilvusServiceClient;\nimport io.qdrant.client.QdrantClient;\nimport io.qdrant.client.QdrantGrpcClient;\nimport lombok.SneakyThrows;\nimport org.springaicommunity.moonshot.MoonshotChatModel;\nimport org.springaicommunity.moonshot.MoonshotChatOptions;\nimport org.springaicommunity.moonshot.api.MoonshotApi;\nimport org.springaicommunity.moonshot.autoconfigure.MoonshotChatAutoConfiguration;\nimport org.springaicommunity.qianfan.QianFanChatModel;\nimport org.springaicommunity.qianfan.QianFanEmbeddingModel;\nimport org.springaicommunity.qianfan.QianFanEmbeddingOptions;\nimport org.springaicommunity.qianfan.QianFanImageModel;\nimport org.springaicommunity.qianfan.api.QianFanApi;\nimport org.springaicommunity.qianfan.api.QianFanImageApi;\nimport org.springaicommunity.qianfan.autoconfigure.QianFanChatAutoConfiguration;\nimport org.springaicommunity.qianfan.autoconfigure.QianFanEmbeddingAutoConfiguration;\nimport org.springframework.ai.azure.openai.AzureOpenAiChatModel;\nimport org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.deepseek.DeepSeekChatModel;\nimport org.springframework.ai.deepseek.DeepSeekChatOptions;\nimport org.springframework.ai.deepseek.api.DeepSeekApi;\nimport org.springframework.ai.document.MetadataMode;\nimport org.springframework.ai.embedding.BatchingStrategy;\nimport org.springframework.ai.embedding.EmbeddingModel;\nimport org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;\nimport org.springframework.ai.image.ImageModel;\nimport org.springframework.ai.minimax.MiniMaxChatModel;\nimport org.springframework.ai.minimax.MiniMaxChatOptions;\nimport org.springframework.ai.minimax.MiniMaxEmbeddingModel;\nimport org.springframework.ai.minimax.MiniMaxEmbeddingOptions;\nimport org.springframework.ai.minimax.api.MiniMaxApi;\nimport org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration;\nimport org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatAutoConfiguration;\nimport org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingAutoConfiguration;\nimport org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingProperties;\nimport org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration;\nimport org.springframework.ai.model.minimax.autoconfigure.MiniMaxChatAutoConfiguration;\nimport org.springframework.ai.model.minimax.autoconfigure.MiniMaxEmbeddingAutoConfiguration;\nimport org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration;\nimport org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration;\nimport org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration;\nimport org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration;\nimport org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration;\nimport org.springframework.ai.model.tool.ToolCallingManager;\nimport org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiChatAutoConfiguration;\nimport org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiEmbeddingAutoConfiguration;\nimport org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiImageAutoConfiguration;\nimport org.springframework.ai.ollama.OllamaChatModel;\nimport org.springframework.ai.ollama.OllamaEmbeddingModel;\nimport org.springframework.ai.ollama.api.OllamaApi;\nimport org.springframework.ai.ollama.api.OllamaEmbeddingOptions;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.OpenAiEmbeddingModel;\nimport org.springframework.ai.openai.OpenAiEmbeddingOptions;\nimport org.springframework.ai.openai.OpenAiImageModel;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport org.springframework.ai.openai.api.OpenAiImageApi;\nimport org.springframework.ai.openai.api.common.OpenAiApiConstants;\nimport org.springframework.ai.anthropic.AnthropicChatModel;\nimport org.springframework.ai.anthropic.api.AnthropicApi;\nimport org.springframework.ai.stabilityai.StabilityAiImageModel;\nimport org.springframework.ai.stabilityai.api.StabilityAiApi;\nimport org.springframework.ai.vectorstore.SimpleVectorStore;\nimport org.springframework.ai.vectorstore.VectorStore;\nimport org.springframework.ai.vectorstore.milvus.MilvusVectorStore;\nimport org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientConnectionDetails;\nimport org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties;\nimport org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration;\nimport org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties;\nimport org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention;\nimport org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;\nimport org.springframework.ai.vectorstore.qdrant.QdrantVectorStore;\nimport org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration;\nimport org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties;\nimport org.springframework.ai.vectorstore.redis.RedisVectorStore;\nimport org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration;\nimport org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties;\nimport org.springframework.ai.zhipuai.*;\nimport org.springframework.ai.zhipuai.api.ZhiPuAiApi;\nimport org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.boot.autoconfigure.data.redis.RedisProperties;\nimport org.springframework.web.client.RestClient;\nimport redis.clients.jedis.JedisPooled;\n\nimport java.io.File;\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static org.springframework.ai.retry.RetryUtils.DEFAULT_RETRY_TEMPLATE;\n\n/**\n * AI Model 模型工厂的实现类\n *\n * @author 芋道源码\n */\npublic class AiModelFactoryImpl implements AiModelFactory {\n\n    @Override\n    public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url) {\n        String cacheKey = buildClientCacheKey(ChatModel.class, platform, apiKey, url);\n        return Singleton.get(cacheKey, (Func0<ChatModel>) () -> {\n            // noinspection EnhancedSwitchMigration\n            switch (platform) {\n                case TONG_YI:\n                    return buildTongYiChatModel(apiKey);\n                case YI_YAN:\n                    return buildYiYanChatModel(apiKey);\n                case DEEP_SEEK:\n                    return buildDeepSeekChatModel(apiKey);\n                case DOU_BAO:\n                    return buildDouBaoChatModel(apiKey);\n                case HUN_YUAN:\n                    return buildHunYuanChatModel(apiKey, url);\n                case SILICON_FLOW:\n                    return buildSiliconFlowChatModel(apiKey);\n                case ZHI_PU:\n                    return buildZhiPuChatModel(apiKey, url);\n                case MINI_MAX:\n                    return buildMiniMaxChatModel(apiKey, url);\n                case MOONSHOT:\n                    return buildMoonshotChatModel(apiKey, url);\n                case XING_HUO:\n                    return buildXingHuoChatModel(apiKey);\n                case BAI_CHUAN:\n                    return buildBaiChuanChatModel(apiKey);\n                case OPENAI:\n                    return buildOpenAiChatModel(apiKey, url);\n                case AZURE_OPENAI:\n                    return buildAzureOpenAiChatModel(apiKey, url);\n                case ANTHROPIC:\n                    return buildAnthropicChatModel(apiKey, url);\n                case GEMINI:\n                    return buildGeminiChatModel(apiKey);\n                case OLLAMA:\n                    return buildOllamaChatModel(url);\n                case GROK:\n                    return buildGrokChatModel(apiKey,url);\n                default:\n                    throw new IllegalArgumentException(StrUtil.format(\"未知平台({})\", platform));\n            }\n        });\n    }\n\n    @Override\n    public ChatModel getDefaultChatModel(AiPlatformEnum platform) {\n        // noinspection EnhancedSwitchMigration\n        switch (platform) {\n            case TONG_YI:\n                return SpringUtil.getBean(DashScopeChatModel.class);\n            case YI_YAN:\n                return SpringUtil.getBean(QianFanChatModel.class);\n            case DEEP_SEEK:\n                return SpringUtil.getBean(DeepSeekChatModel.class);\n            case DOU_BAO:\n                return SpringUtil.getBean(DouBaoChatModel.class);\n            case HUN_YUAN:\n                return SpringUtil.getBean(HunYuanChatModel.class);\n            case SILICON_FLOW:\n                return SpringUtil.getBean(SiliconFlowChatModel.class);\n            case ZHI_PU:\n                return SpringUtil.getBean(ZhiPuAiChatModel.class);\n            case MINI_MAX:\n                return SpringUtil.getBean(MiniMaxChatModel.class);\n            case MOONSHOT:\n                return SpringUtil.getBean(MoonshotChatModel.class);\n            case XING_HUO:\n                return SpringUtil.getBean(XingHuoChatModel.class);\n            case BAI_CHUAN:\n                return SpringUtil.getBean(BaiChuanChatModel.class);\n            case OPENAI:\n                return SpringUtil.getBean(OpenAiChatModel.class);\n            case AZURE_OPENAI:\n                return SpringUtil.getBean(AzureOpenAiChatModel.class);\n            case ANTHROPIC:\n                return SpringUtil.getBean(AnthropicChatModel.class);\n            case GEMINI:\n                return SpringUtil.getBean(GeminiChatModel.class);\n            case OLLAMA:\n                return SpringUtil.getBean(OllamaChatModel.class);\n            default:\n                throw new IllegalArgumentException(StrUtil.format(\"未知平台({})\", platform));\n        }\n    }\n\n    @Override\n    public ImageModel getDefaultImageModel(AiPlatformEnum platform) {\n        // noinspection EnhancedSwitchMigration\n        switch (platform) {\n            case TONG_YI:\n                return SpringUtil.getBean(DashScopeImageModel.class);\n            case YI_YAN:\n                return SpringUtil.getBean(QianFanImageModel.class);\n            case ZHI_PU:\n                return SpringUtil.getBean(ZhiPuAiImageModel.class);\n            case SILICON_FLOW:\n                return SpringUtil.getBean(SiliconFlowImageModel.class);\n            case OPENAI:\n                return SpringUtil.getBean(OpenAiImageModel.class);\n            case STABLE_DIFFUSION:\n                return SpringUtil.getBean(StabilityAiImageModel.class);\n            default:\n                throw new IllegalArgumentException(StrUtil.format(\"未知平台({})\", platform));\n        }\n    }\n\n    @Override\n    public ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url) {\n        // noinspection EnhancedSwitchMigration\n        switch (platform) {\n            case TONG_YI:\n                return buildTongYiImagesModel(apiKey);\n            case YI_YAN:\n                return buildQianFanImageModel(apiKey);\n            case ZHI_PU:\n                return buildZhiPuAiImageModel(apiKey, url);\n            case OPENAI:\n                return buildOpenAiImageModel(apiKey, url);\n            case SILICON_FLOW:\n                return buildSiliconFlowImageModel(apiKey,url);\n            case STABLE_DIFFUSION:\n                return buildStabilityAiImageModel(apiKey, url);\n            default:\n                throw new IllegalArgumentException(StrUtil.format(\"未知平台({})\", platform));\n        }\n    }\n\n    @Override\n    public MidjourneyApi getOrCreateMidjourneyApi(String apiKey, String url) {\n        String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey,\n                url);\n        return Singleton.get(cacheKey, (Func0<MidjourneyApi>) () -> {\n            YudaoAiProperties.Midjourney properties = SpringUtil.getBean(YudaoAiProperties.class)\n                    .getMidjourney();\n            return new MidjourneyApi(url, apiKey, properties.getNotifyUrl());\n        });\n    }\n\n    @Override\n    public SunoApi getOrCreateSunoApi(String apiKey, String url) {\n        String cacheKey = buildClientCacheKey(SunoApi.class, AiPlatformEnum.SUNO.getPlatform(), apiKey, url);\n        return Singleton.get(cacheKey, (Func0<SunoApi>) () -> new SunoApi(url));\n    }\n\n    @Override\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model) {\n        String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url, model);\n        return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> {\n            switch (platform) {\n                case TONG_YI:\n                    return buildTongYiEmbeddingModel(apiKey, model);\n                case YI_YAN:\n                    return buildYiYanEmbeddingModel(apiKey, model);\n                case ZHI_PU:\n                    return buildZhiPuEmbeddingModel(apiKey, url, model);\n                case MINI_MAX:\n                    return buildMiniMaxEmbeddingModel(apiKey, url, model);\n                case OPENAI:\n                    return buildOpenAiEmbeddingModel(apiKey, url, model);\n                case AZURE_OPENAI:\n                    return buildAzureOpenAiEmbeddingModel(apiKey, url, model);\n                case OLLAMA:\n                    return buildOllamaEmbeddingModel(url, model);\n                default:\n                    throw new IllegalArgumentException(StrUtil.format(\"未知平台({})\", platform));\n            }\n        });\n    }\n\n    @Override\n    public VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type,\n                                              EmbeddingModel embeddingModel,\n                                              Map<String, Class<?>> metadataFields) {\n        String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type);\n        return Singleton.get(cacheKey, (Func0<VectorStore>) () -> {\n            if (type == SimpleVectorStore.class) {\n                return buildSimpleVectorStore(embeddingModel);\n            }\n            if (type == QdrantVectorStore.class) {\n                return buildQdrantVectorStore(embeddingModel);\n            }\n            if (type == RedisVectorStore.class) {\n                return buildRedisVectorStore(embeddingModel, metadataFields);\n            }\n            if (type == MilvusVectorStore.class) {\n                return buildMilvusVectorStore(embeddingModel);\n            }\n            throw new IllegalArgumentException(StrUtil.format(\"未知类型({})\", type));\n        });\n    }\n\n    private static String buildClientCacheKey(Class<?> clazz, Object... params) {\n        if (ArrayUtil.isEmpty(params)) {\n            return clazz.getName();\n        }\n        return StrUtil.format(\"{}#{}\", clazz.getName(), ArrayUtil.join(params, \"_\"));\n    }\n\n    // ========== 各种创建 spring-ai 客户端的方法 ==========\n\n    /**\n     * 可参考 {@link DashScopeChatAutoConfiguration} 的 dashscopeChatModel 方法\n     */\n    private static DashScopeChatModel buildTongYiChatModel(String key) {\n        DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(key).build();\n        DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(DashScopeApi.DEFAULT_CHAT_MODEL)\n                .withTemperature(0.7).build();\n        return DashScopeChatModel.builder()\n                .dashScopeApi(dashScopeApi)\n                .defaultOptions(options)\n                .toolCallingManager(getToolCallingManager())\n                .build();\n    }\n\n    /**\n     * 可参考 {@link DashScopeImageAutoConfiguration} 的 dashScopeImageModel 方法\n     */\n    private static DashScopeImageModel buildTongYiImagesModel(String key) {\n        DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(key).build();\n        return DashScopeImageModel.builder()\n                .dashScopeApi(dashScopeImageApi)\n                .build();\n    }\n\n    /**\n     * 可参考 {@link QianFanChatAutoConfiguration} 的 qianFanChatModel 方法\n     */\n    private static QianFanChatModel buildYiYanChatModel(String key) {\n        // TODO spring ai qianfan 有 bug，无法使用 https://github.com/spring-ai-community/qianfan/issues/6\n        List<String> keys = StrUtil.split(key, '|');\n        Assert.equals(keys.size(), 2, \"YiYanChatClient 的密钥需要 (appKey|secretKey) 格式\");\n        String appKey = keys.get(0);\n        String secretKey = keys.get(1);\n        QianFanApi qianFanApi = new QianFanApi(appKey, secretKey);\n        return new QianFanChatModel(qianFanApi);\n    }\n\n    /**\n     * 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanImageModel 方法\n     */\n    private QianFanImageModel buildQianFanImageModel(String key) {\n        // TODO spring ai qianfan 有 bug，无法使用 https://github.com/spring-ai-community/qianfan/issues/6\n        List<String> keys = StrUtil.split(key, '|');\n        Assert.equals(keys.size(), 2, \"YiYanChatClient 的密钥需要 (appKey|secretKey) 格式\");\n        String appKey = keys.get(0);\n        String secretKey = keys.get(1);\n        QianFanImageApi qianFanApi = new QianFanImageApi(appKey, secretKey);\n        return new QianFanImageModel(qianFanApi);\n    }\n\n    /**\n     * 可参考 {@link DeepSeekChatAutoConfiguration} 的 deepSeekChatModel 方法\n     */\n    private static DeepSeekChatModel buildDeepSeekChatModel(String apiKey) {\n        DeepSeekApi deepSeekApi = DeepSeekApi.builder().apiKey(apiKey).build();\n        DeepSeekChatOptions options = DeepSeekChatOptions.builder().model(DeepSeekApi.DEFAULT_CHAT_MODEL)\n                .temperature(0.7).build();\n        return DeepSeekChatModel.builder()\n                .deepSeekApi(deepSeekApi)\n                .defaultOptions(options)\n                .toolCallingManager(getToolCallingManager())\n                .build();\n    }\n\n    /**\n     * 可参考 {@link AiAutoConfiguration#douBaoChatClient(YudaoAiProperties)}\n     */\n    private ChatModel buildDouBaoChatModel(String apiKey) {\n        YudaoAiProperties.DouBao properties = new YudaoAiProperties.DouBao()\n                .setApiKey(apiKey);\n        return new AiAutoConfiguration().buildDouBaoChatClient(properties);\n    }\n\n    /**\n     * 可参考 {@link AiAutoConfiguration#hunYuanChatClient(YudaoAiProperties)}\n     */\n    private ChatModel buildHunYuanChatModel(String apiKey, String url) {\n        YudaoAiProperties.HunYuan properties = new YudaoAiProperties.HunYuan()\n                .setBaseUrl(url).setApiKey(apiKey);\n        return new AiAutoConfiguration().buildHunYuanChatClient(properties);\n    }\n\n    /**\n     * 可参考 {@link AiAutoConfiguration#siliconFlowChatClient(YudaoAiProperties)}\n     */\n    private ChatModel buildSiliconFlowChatModel(String apiKey) {\n        YudaoAiProperties.SiliconFlow properties = new YudaoAiProperties.SiliconFlow()\n                .setApiKey(apiKey);\n        return new AiAutoConfiguration().buildSiliconFlowChatClient(properties);\n    }\n\n    /**\n     * 可参考 {@link ZhiPuAiChatAutoConfiguration} 的 zhiPuAiChatModel 方法\n     */\n    private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) {\n        ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey);\n        if (StrUtil.isNotEmpty(url)) {\n            zhiPuAiApiBuilder.baseUrl(url);\n        }\n        ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build();\n        return new ZhiPuAiChatModel(zhiPuAiApiBuilder.build(), options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE,\n                getObservationRegistry().getIfAvailable());\n    }\n\n    /**\n     * 可参考 {@link ZhiPuAiImageAutoConfiguration} 的 zhiPuAiImageModel 方法\n     */\n    private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) {\n        ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey)\n                : new ZhiPuAiImageApi(url, apiKey, RestClient.builder());\n        return new ZhiPuAiImageModel(zhiPuAiApi);\n    }\n\n    /**\n     * 可参考 {@link MiniMaxChatAutoConfiguration} 的 miniMaxChatModel 方法\n     */\n    private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) {\n        MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey)\n                : new MiniMaxApi(url, apiKey);\n        MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build();\n        return new MiniMaxChatModel(miniMaxApi, options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE);\n    }\n\n    /**\n     * 可参考 {@link MoonshotChatAutoConfiguration} 的 moonshotChatModel 方法\n     */\n    private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) {\n        MoonshotApi.Builder moonshotApiBuilder = MoonshotApi.builder()\n                .apiKey(apiKey);\n        if (StrUtil.isNotEmpty(url)) {\n            moonshotApiBuilder.baseUrl(url);\n        }\n        MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build();\n        return MoonshotChatModel.builder()\n                .moonshotApi(moonshotApiBuilder.build())\n                .defaultOptions(options)\n                .toolCallingManager(getToolCallingManager())\n                .build();\n    }\n\n    /**\n     * 可参考 {@link AiAutoConfiguration#xingHuoChatClient(YudaoAiProperties)}\n     */\n    private static XingHuoChatModel buildXingHuoChatModel(String key) {\n        List<String> keys = StrUtil.split(key, '|');\n        Assert.equals(keys.size(), 2, \"XingHuoChatClient 的密钥需要 (appKey|secretKey) 格式\");\n        YudaoAiProperties.XingHuo properties = new YudaoAiProperties.XingHuo()\n                .setAppKey(keys.get(0)).setSecretKey(keys.get(1));\n        return new AiAutoConfiguration().buildXingHuoChatClient(properties);\n    }\n\n    /**\n     * 可参考 {@link AiAutoConfiguration#baiChuanChatClient(YudaoAiProperties)}\n     */\n    private BaiChuanChatModel buildBaiChuanChatModel(String apiKey) {\n        YudaoAiProperties.BaiChuan properties = new YudaoAiProperties.BaiChuan()\n                .setApiKey(apiKey);\n        return new AiAutoConfiguration().buildBaiChuanChatClient(properties);\n    }\n\n    /**\n     * 可参考 {@link OpenAiChatAutoConfiguration} 的 openAiChatModel 方法\n     */\n    private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) {\n        url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL);\n        OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build();\n        return OpenAiChatModel.builder()\n                .openAiApi(openAiApi)\n                .toolCallingManager(getToolCallingManager())\n                .build();\n    }\n\n    /**\n     * 可参考 {@link AzureOpenAiChatAutoConfiguration}\n     */\n    private static AzureOpenAiChatModel buildAzureOpenAiChatModel(String apiKey, String url) {\n        // TODO @芋艿：使用前，请测试，暂时没密钥！！！\n        OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder()\n                .endpoint(url).credential(new KeyCredential(apiKey));\n        return AzureOpenAiChatModel.builder()\n                .openAIClientBuilder(openAIClientBuilder)\n                .toolCallingManager(getToolCallingManager())\n                .build();\n    }\n\n    /**\n     * 可参考 {@link AnthropicChatAutoConfiguration} 的 anthropicApi 方法\n     */\n    private static AnthropicChatModel buildAnthropicChatModel(String apiKey, String url) {\n        AnthropicApi.Builder builder = AnthropicApi.builder().apiKey(apiKey);\n        if (StrUtil.isNotEmpty(url)) {\n            builder.baseUrl(url);\n        }\n        AnthropicApi anthropicApi = builder.build();\n        return AnthropicChatModel.builder()\n                .anthropicApi(anthropicApi)\n                .toolCallingManager(getToolCallingManager())\n                .build();\n    }\n\n    /**\n     * 可参考 {@link AiAutoConfiguration#buildGeminiChatClient(YudaoAiProperties.Gemini)}\n     */\n    private static GeminiChatModel buildGeminiChatModel(String apiKey) {\n        YudaoAiProperties.Gemini properties = SpringUtil.getBean(YudaoAiProperties.class)\n                .getGemini().setApiKey(apiKey);\n        return new AiAutoConfiguration().buildGeminiChatClient(properties);\n    }\n\n    /**\n     * 可参考 {@link OpenAiImageAutoConfiguration} 的 openAiImageModel 方法\n     */\n    private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) {\n        url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL);\n        OpenAiImageApi openAiApi = OpenAiImageApi.builder().baseUrl(url).apiKey(openAiToken).build();\n        return new OpenAiImageModel(openAiApi);\n    }\n\n    /**\n     * 创建 SiliconFlowImageModel 对象\n     */\n    private SiliconFlowImageModel buildSiliconFlowImageModel(String apiToken, String url) {\n        url = StrUtil.blankToDefault(url, SiliconFlowApiConstants.DEFAULT_BASE_URL);\n        SiliconFlowImageApi openAiApi = new SiliconFlowImageApi(url, apiToken);\n        return new SiliconFlowImageModel(openAiApi);\n    }\n\n    /**\n     * 可参考 {@link OllamaChatAutoConfiguration} 的 ollamaChatModel 方法\n     */\n    private static OllamaChatModel buildOllamaChatModel(String url) {\n        OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build();\n        return OllamaChatModel.builder()\n                .ollamaApi(ollamaApi)\n                .toolCallingManager(getToolCallingManager())\n                .build();\n    }\n\n    /**\n     * 可参考 {@link StabilityAiImageAutoConfiguration} 的 stabilityAiImageModel 方法\n     */\n    private StabilityAiImageModel buildStabilityAiImageModel(String apiKey, String url) {\n        url = StrUtil.blankToDefault(url, StabilityAiApi.DEFAULT_BASE_URL);\n        StabilityAiApi stabilityAiApi = new StabilityAiApi(apiKey, StabilityAiApi.DEFAULT_IMAGE_MODEL, url);\n        return new StabilityAiImageModel(stabilityAiApi);\n    }\n\n    private ChatModel buildGrokChatModel(String apiKey,String url) {\n        YudaoAiProperties.Grok properties = new YudaoAiProperties.Grok()\n                .setBaseUrl(url)\n                .setApiKey(apiKey);\n        return new AiAutoConfiguration().buildGrokChatClient(properties);\n    }\n\n    // ========== 各种创建 EmbeddingModel 的方法 ==========\n\n    /**\n     * 可参考 {@link DashScopeEmbeddingAutoConfiguration} 的 dashscopeEmbeddingModel 方法\n     */\n    private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) {\n        DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build();\n        DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model).build();\n        return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions);\n    }\n\n    /**\n     * 可参考 {@link ZhiPuAiEmbeddingAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法\n     */\n    private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) {\n        ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey);\n        if (StrUtil.isNotEmpty(url)) {\n            zhiPuAiApiBuilder.baseUrl(url);\n        }\n        ZhiPuAiEmbeddingOptions zhiPuAiEmbeddingOptions = ZhiPuAiEmbeddingOptions.builder().model(model).build();\n        return new ZhiPuAiEmbeddingModel(zhiPuAiApiBuilder.build(), MetadataMode.EMBED, zhiPuAiEmbeddingOptions);\n    }\n\n    /**\n     * 可参考 {@link MiniMaxEmbeddingAutoConfiguration} 的 miniMaxEmbeddingModel 方法\n     */\n    private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) {\n        MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey)\n                : new MiniMaxApi(url, apiKey);\n        MiniMaxEmbeddingOptions miniMaxEmbeddingOptions = MiniMaxEmbeddingOptions.builder().model(model).build();\n        return new MiniMaxEmbeddingModel(miniMaxApi, MetadataMode.EMBED, miniMaxEmbeddingOptions);\n    }\n\n    /**\n     * 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanEmbeddingModel 方法\n     */\n    private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) {\n        List<String> keys = StrUtil.split(key, '|');\n        Assert.equals(keys.size(), 2, \"YiYanChatClient 的密钥需要 (appKey|secretKey) 格式\");\n        String appKey = keys.get(0);\n        String secretKey = keys.get(1);\n        QianFanApi qianFanApi = new QianFanApi(appKey, secretKey);\n        QianFanEmbeddingOptions qianFanEmbeddingOptions = QianFanEmbeddingOptions.builder().model(model).build();\n        return new QianFanEmbeddingModel(qianFanApi, MetadataMode.EMBED, qianFanEmbeddingOptions);\n    }\n\n    private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) {\n        OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build();\n        OllamaEmbeddingOptions ollamaOptions = OllamaEmbeddingOptions.builder().model(model).build();\n        return OllamaEmbeddingModel.builder()\n                .ollamaApi(ollamaApi)\n                .defaultOptions(ollamaOptions)\n                .build();\n    }\n\n    /**\n     * 可参考 {@link OpenAiEmbeddingAutoConfiguration} 的 openAiEmbeddingModel 方法\n     */\n    private OpenAiEmbeddingModel buildOpenAiEmbeddingModel(String openAiToken, String url, String model) {\n        url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL);\n        OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build();\n        OpenAiEmbeddingOptions openAiEmbeddingProperties = OpenAiEmbeddingOptions.builder().model(model).build();\n        return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, openAiEmbeddingProperties);\n    }\n\n    /**\n     * 可参考 {@link AzureOpenAiEmbeddingAutoConfiguration} 的 azureOpenAiEmbeddingModel 方法\n     */\n    private AzureOpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String apiKey, String url, String model) {\n        // TODO @芋艿：手头暂时没密钥，使用建议再测试下\n        AzureOpenAiEmbeddingAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiEmbeddingAutoConfiguration();\n        // 创建 OpenAIClientBuilder 对象\n        OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder()\n                .endpoint(url).credential(new KeyCredential(apiKey));\n        // 获取 AzureOpenAiChatProperties 对象\n        AzureOpenAiEmbeddingProperties embeddingProperties = SpringUtil.getBean(AzureOpenAiEmbeddingProperties.class);\n        return azureOpenAiAutoConfiguration.azureOpenAiEmbeddingModel(openAIClientBuilder, embeddingProperties,\n                getObservationRegistry(), getEmbeddingModelObservationConvention());\n    }\n\n    // ========== 各种创建 VectorStore 的方法 ==========\n\n    /**\n     * 注意：仅适合本地测试使用，生产建议还是使用 Qdrant、Milvus 等\n     */\n    @SneakyThrows\n    @SuppressWarnings(\"ResultOfMethodCallIgnored\")\n    private SimpleVectorStore buildSimpleVectorStore(EmbeddingModel embeddingModel) {\n        SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build();\n        // 启动加载\n        File file = new File(StrUtil.format(\"{}/vector_store/simple_{}.json\",\n                FileUtil.getUserHomePath(), embeddingModel.getClass().getSimpleName()));\n        if (!file.exists()) {\n            FileUtil.mkParentDirs(file);\n            file.createNewFile();\n        } else if (file.length() > 0) {\n            vectorStore.load(file);\n        }\n        // 定时持久化，每分钟一次\n        Timer timer = new Timer(\"SimpleVectorStoreTimer-\" + file.getAbsolutePath());\n        timer.scheduleAtFixedRate(new TimerTask() {\n\n            @Override\n            public void run() {\n                vectorStore.save(file);\n            }\n\n        }, Duration.ofMinutes(1).toMillis(), Duration.ofMinutes(1).toMillis());\n        // 关闭时，进行持久化\n        RuntimeUtil.addShutdownHook(() -> vectorStore.save(file));\n        return vectorStore;\n    }\n\n    /**\n     * 参考 {@link QdrantVectorStoreAutoConfiguration} 的 vectorStore 方法\n     */\n    @SneakyThrows\n    private QdrantVectorStore buildQdrantVectorStore(EmbeddingModel embeddingModel) {\n        QdrantVectorStoreAutoConfiguration configuration = new QdrantVectorStoreAutoConfiguration();\n        QdrantVectorStoreProperties properties = SpringUtil.getBean(QdrantVectorStoreProperties.class);\n        // 参考 QdrantVectorStoreAutoConfiguration 实现，创建 QdrantClient 对象\n        QdrantGrpcClient.Builder grpcClientBuilder = QdrantGrpcClient.newBuilder(\n                properties.getHost(), properties.getPort(), properties.isUseTls());\n        if (StrUtil.isNotEmpty(properties.getApiKey())) {\n            grpcClientBuilder.withApiKey(properties.getApiKey());\n        }\n        QdrantClient qdrantClient = new QdrantClient(grpcClientBuilder.build());\n        // 创建 QdrantVectorStore 对象\n        QdrantVectorStore vectorStore = configuration.vectorStore(embeddingModel, properties, qdrantClient,\n                getObservationRegistry(), getCustomObservationConvention(), getBatchingStrategy());\n        // 初始化索引\n        vectorStore.afterPropertiesSet();\n        return vectorStore;\n    }\n\n    /**\n     * 参考 {@link RedisVectorStoreAutoConfiguration} 的 vectorStore 方法\n     */\n    private RedisVectorStore buildRedisVectorStore(EmbeddingModel embeddingModel,\n                                                   Map<String, Class<?>> metadataFields) {\n        // 创建 JedisPooled 对象\n        RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);\n        JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort(),\n                redisProperties.getUsername(), redisProperties.getPassword());\n        // 创建 RedisVectorStoreProperties 对象\n        RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class);\n        RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel)\n                .indexName(properties.getIndexName()).prefix(properties.getPrefix())\n                .initializeSchema(properties.isInitializeSchema())\n                .metadataFields(convertList(metadataFields.entrySet(), entry -> {\n                    String fieldName = entry.getKey();\n                    Class<?> fieldType = entry.getValue();\n                    if (Number.class.isAssignableFrom(fieldType)) {\n                        return RedisVectorStore.MetadataField.numeric(fieldName);\n                    }\n                    if (Boolean.class.isAssignableFrom(fieldType)) {\n                        return RedisVectorStore.MetadataField.tag(fieldName);\n                    }\n                    return RedisVectorStore.MetadataField.text(fieldName);\n                }))\n                .observationRegistry(getObservationRegistry().getObject())\n                .customObservationConvention(getCustomObservationConvention().getObject())\n                .batchingStrategy(getBatchingStrategy())\n                .build();\n        // 初始化索引\n        redisVectorStore.afterPropertiesSet();\n        return redisVectorStore;\n    }\n\n    /**\n     * 参考 {@link MilvusVectorStoreAutoConfiguration} 的 vectorStore 方法\n     */\n    @SneakyThrows\n    private MilvusVectorStore buildMilvusVectorStore(EmbeddingModel embeddingModel) {\n        MilvusVectorStoreAutoConfiguration configuration = new MilvusVectorStoreAutoConfiguration();\n        // 获取配置属性\n        MilvusVectorStoreProperties serverProperties = SpringUtil.getBean(MilvusVectorStoreProperties.class);\n        MilvusServiceClientProperties clientProperties = SpringUtil.getBean(MilvusServiceClientProperties.class);\n\n        // 创建 MilvusServiceClient 对象\n        MilvusServiceClient milvusClient = configuration.milvusClient(serverProperties, clientProperties,\n                new MilvusServiceClientConnectionDetails() {\n\n                    @Override\n                    public String getHost() {\n                        return clientProperties.getHost();\n                    }\n\n                    @Override\n                    public int getPort() {\n                        return clientProperties.getPort();\n                    }\n\n                }\n        );\n        // 创建 MilvusVectorStore 对象\n        MilvusVectorStore vectorStore = configuration.vectorStore(milvusClient, embeddingModel, serverProperties,\n                getBatchingStrategy(), getObservationRegistry(), getCustomObservationConvention());\n\n        // 初始化索引\n        vectorStore.afterPropertiesSet();\n        return vectorStore;\n    }\n\n    private static ObjectProvider<ObservationRegistry> getObservationRegistry() {\n        return new ObjectProvider<>() {\n\n            @Override\n            public ObservationRegistry getObject() throws BeansException {\n                return SpringUtil.getBean(ObservationRegistry.class);\n            }\n\n        };\n    }\n\n    private static ObjectProvider<VectorStoreObservationConvention> getCustomObservationConvention() {\n        return new ObjectProvider<>() {\n\n            @Override\n            public VectorStoreObservationConvention getObject() throws BeansException {\n                return new DefaultVectorStoreObservationConvention();\n            }\n\n        };\n    }\n\n    private static BatchingStrategy getBatchingStrategy() {\n        return SpringUtil.getBean(BatchingStrategy.class);\n    }\n\n    private static ToolCallingManager getToolCallingManager() {\n        return SpringUtil.getBean(ToolCallingManager.class);\n    }\n\n    private static ObjectProvider<EmbeddingModelObservationConvention> getEmbeddingModelObservationConvention() {\n        return new ObjectProvider<>() {\n\n            @Override\n            public EmbeddingModelObservationConvention getObject() throws BeansException {\n                return SpringUtil.getBean(EmbeddingModelObservationConvention.class);\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport reactor.core.publisher.Flux;\n\n/**\n * 百川 {@link ChatModel} 实现类\n *\n * @author 芋道源码\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class BaiChuanChatModel implements ChatModel {\n\n    public static final String BASE_URL = \"https://api.baichuan-ai.com\";\n\n    public static final String MODEL_DEFAULT = \"Baichuan4-Turbo\";\n\n    /**\n     * 兼容 OpenAI 接口，进行复用\n     */\n    private final OpenAiChatModel openAiChatModel;\n\n    @Override\n    public ChatResponse call(Prompt prompt) {\n        return openAiChatModel.call(prompt);\n    }\n\n    @Override\n    public Flux<ChatResponse> stream(Prompt prompt) {\n        return openAiChatModel.stream(prompt);\n    }\n\n    @Override\n    public ChatOptions getDefaultOptions() {\n        return openAiChatModel.getDefaultOptions();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport reactor.core.publisher.Flux;\n\n/**\n * 字节豆包 {@link ChatModel} 实现类\n *\n * @author fansili\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class DouBaoChatModel implements ChatModel {\n\n    public static final String BASE_URL = \"https://ark.cn-beijing.volces.com/api\";\n    public static final String COMPLETE_PATH = \"/v3/chat/completions\";\n\n    public static final String MODEL_DEFAULT = \"doubao-1-5-lite-32k-250115\";\n\n    /**\n     * 兼容 OpenAI 接口，进行复用\n     */\n    private final ChatModel openAiChatModel;\n\n    @Override\n    public ChatResponse call(Prompt prompt) {\n        return openAiChatModel.call(prompt);\n    }\n\n    @Override\n    public Flux<ChatResponse> stream(Prompt prompt) {\n        return openAiChatModel.stream(prompt);\n    }\n\n    @Override\n    public ChatOptions getDefaultOptions() {\n        return openAiChatModel.getDefaultOptions();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/gemini/GeminiChatModel.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport reactor.core.publisher.Flux;\n\n/**\n * 谷歌 Gemini {@link ChatModel} 实现类，基于 Google AI Studio 提供的 <a href=\"https://ai.google.dev/gemini-api/docs/openai\">OpenAI 兼容方案</a>\n *\n * @author 芋道源码\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class GeminiChatModel implements ChatModel {\n\n    public static final String BASE_URL = \"https://generativelanguage.googleapis.com/v1beta/openai/\";\n    public static final String COMPLETE_PATH = \"/chat/completions\";\n\n    public static final String MODEL_DEFAULT = \"gemini-2.5-flash\";\n\n    /**\n     * 兼容 OpenAI 接口，进行复用\n     */\n    private final OpenAiChatModel openAiChatModel;\n\n    @Override\n    public ChatResponse call(Prompt prompt) {\n        return openAiChatModel.call(prompt);\n    }\n\n    @Override\n    public Flux<ChatResponse> stream(Prompt prompt) {\n        return openAiChatModel.stream(prompt);\n    }\n\n    @Override\n    public ChatOptions getDefaultOptions() {\n        return openAiChatModel.getDefaultOptions();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.grok;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport reactor.core.publisher.Flux;\n\n/**\n * Grok {@link ChatModel} 实现类\n *\n *\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class GrokChatModel implements ChatModel {\n\n    public static final String BASE_URL = \"https://api.x.ai\";\n    public static final String COMPLETE_PATH = \"/v1/chat/completions\";\n    public static final String MODEL_DEFAULT = \"grok-4-fast-reasoning\";\n\n    /**\n     * 兼容 OpenAI 接口，进行复用\n     */\n    private final ChatModel openAiChatModel;\n\n    @Override\n    public ChatResponse call(Prompt prompt) {\n        return openAiChatModel.call(prompt);\n    }\n\n    @Override\n    public Flux<ChatResponse> stream(Prompt prompt) {\n        return openAiChatModel.stream(prompt);\n    }\n\n    @Override\n    public ChatOptions getDefaultOptions() {\n        return openAiChatModel.getDefaultOptions();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport reactor.core.publisher.Flux;\n\n/**\n * 腾云混元 {@link ChatModel} 实现类\n *\n * 1. 混元大模型：基于 <a href=\"https://cloud.tencent.com/document/product/1729/111007\">知识引擎原子能力</a> 实现\n * 2. 知识引擎原子能力：基于 <a href=\"https://cloud.tencent.com/document/product/1772/115969\">知识引擎原子能力</a> 实现\n *\n * @author fansili\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class HunYuanChatModel implements ChatModel {\n\n    public static final String BASE_URL = \"https://api.hunyuan.cloud.tencent.com\";\n    public static final String COMPLETE_PATH = \"/v1/chat/completions\";\n\n    public static final String MODEL_DEFAULT = \"hunyuan-turbo\";\n\n    public static final String DEEP_SEEK_BASE_URL = \"https://api.lkeap.cloud.tencent.com\";\n\n    public static final String DEEP_SEEK_MODEL_DEFAULT = \"deepseek-v3\";\n\n    /**\n     * 兼容 OpenAI 接口，进行复用\n     */\n    private final ChatModel openAiChatModel;\n\n    @Override\n    public ChatResponse call(Prompt prompt) {\n        return openAiChatModel.call(prompt);\n    }\n\n    @Override\n    public Flux<ChatResponse> stream(Prompt prompt) {\n        return openAiChatModel.stream(prompt);\n    }\n\n    @Override\n    public ChatOptions getDefaultOptions() {\n        return openAiChatModel.getDefaultOptions();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/midjourney/api/MidjourneyApi.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.Lists;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.HttpRequest;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactor.core.publisher.Mono;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\n/**\n * Midjourney API\n *\n * @author fansili\n * @since 1.0\n */\n@Slf4j\npublic class MidjourneyApi {\n\n    private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();\n\n    private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =\n            reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {\n                HttpRequest request = response.request();\n                log.error(\"[midjourney-api] 调用失败！请求方式:[{}]，请求地址:[{}]，请求参数:[{}]，响应数据: [{}]\",\n                        request.getMethod(), request.getURI(), reqParam, responseBody);\n                sink.error(new IllegalStateException(\"[midjourney-api] 调用失败！\"));\n            });\n\n    private final WebClient webClient;\n\n    /**\n     * 回调地址\n     */\n    private final String notifyUrl;\n\n    public MidjourneyApi(String baseUrl, String apiKey, String notifyUrl) {\n        this.webClient = WebClient.builder()\n                .baseUrl(baseUrl)\n                .defaultHeaders(httpHeaders -> {\n                    httpHeaders.setContentType(MediaType.APPLICATION_JSON);\n                    httpHeaders.setBearerAuth(apiKey);\n                })\n                .build();\n        this.notifyUrl = notifyUrl;\n    }\n\n    /**\n     * imagine - 根据提示词提交绘画任务\n     *\n     * @param request 请求\n     * @return 提交结果\n     */\n    public SubmitResponse imagine(ImagineRequest request) {\n        if (StrUtil.isEmpty(request.getNotifyHook())) {\n            request.setNotifyHook(notifyUrl);\n        }\n        String response = post(\"/submit/imagine\", request);\n        return JsonUtils.parseObject(response, SubmitResponse.class);\n    }\n\n    /**\n     * action - 放大、缩小、U1、U2...\n     *\n     * @param request 请求\n     * @return 提交结果\n     */\n    public SubmitResponse action(ActionRequest request) {\n        if (StrUtil.isEmpty(request.getNotifyHook())) {\n            request.setNotifyHook(notifyUrl);\n        }\n        String response = post(\"/submit/action\", request);\n        return JsonUtils.parseObject(response, SubmitResponse.class);\n    }\n\n    /**\n     * 批量查询 task 任务\n     *\n     * @param ids 任务编号数组\n     * @return task 任务\n     */\n    public List<Notify> getTaskList(Collection<String> ids) {\n        String res = post(\"/task/list-by-condition\", ImmutableMap.of(\"ids\", ids));\n        return JsonUtils.parseArray(res, Notify.class);\n    }\n\n    private String post(String uri, Object body) {\n        return webClient.post()\n                .uri(uri)\n                .body(Mono.just(JsonUtils.toJsonString(body)), String.class)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(body))\n                .bodyToMono(String.class)\n                .block();\n    }\n\n    // ========== record 结构 ==========\n\n    /**\n     * Imagine 请求（生成图片）\n     */\n    @Data\n    public static final class ImagineRequest {\n\n        /**\n         * 垫图(参考图) base64 数组\n         */\n        private List<String> base64Array;\n        /**\n         * 提示词\n         */\n        private String prompt;\n        /**\n         * 通知地址\n         */\n        private String notifyHook;\n        /**\n         * 自定义参数\n         */\n        private String state;\n\n        public ImagineRequest(List<String> base64Array, String prompt, String notifyHook, String state) {\n            this.base64Array = base64Array;\n            this.prompt = prompt;\n            this.notifyHook = notifyHook;\n            this.state = state;\n        }\n\n        public static String buildState(Integer width, Integer height, String version, String model) {\n            StringBuilder params = new StringBuilder();\n            //  --ar 来设置尺寸\n            params.append(String.format(\" --ar %s:%s \", width, height));\n            // --niji 模型\n            if (ModelEnum.NIJI.getModel().equals(model)) {\n                params.append(String.format(\" --niji %s \", version));\n            } else {\n                params.append(String.format(\" --v %s \", version));\n            }\n            return params.toString();\n        }\n\n    }\n\n    /**\n     * Action 请求\n     */\n    @Data\n    public static final class ActionRequest {\n\n        private String customId;\n        private String taskId;\n        private String notifyHook;\n\n        public ActionRequest(String taskId, String customId, String notifyHook) {\n            this.customId = customId;\n            this.taskId = taskId;\n            this.notifyHook = notifyHook;\n        }\n\n    }\n\n    /**\n     * Submit 统一返回\n     *\n     * @param code 状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)\n     * @param description 描述\n     * @param properties 扩展字段\n     * @param result 任务ID\n     */\n    public record SubmitResponse(String code,\n                                 String description,\n                                 Map<String, Object> properties,\n                                 String result) {\n    }\n\n    /**\n     * 通知 request\n     *\n     * @param id job id\n     * @param action 任务类型 {@link TaskActionEnum}\n     * @param status 任务状态 {@link TaskStatusEnum}\n     * @param prompt 提示词\n     * @param promptEn 提示词-英文\n     * @param description 任务描述\n     * @param state 自定义参数\n     * @param submitTime 提交时间\n     * @param startTime 开始执行时间\n     * @param finishTime 结束时间\n     * @param imageUrl 图片url\n     * @param progress 任务进度\n     * @param failReason 失败原因\n     * @param buttons 任务完成后的可执行按钮\n     */\n    public record Notify(String id,\n                         String action,\n                         String status,\n\n                         String prompt,\n                         String promptEn,\n\n                         String description,\n                         String state,\n\n                         Long submitTime,\n                         Long startTime,\n                         Long finishTime,\n\n                         String imageUrl,\n                         String progress,\n                         String failReason,\n                         List<Button> buttons) {\n\n    }\n\n    /**\n     * button\n     *\n     * @param customId MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识\n     * @param emoji 图标 emoji\n     * @param label Make Variations 文本\n     * @param type 类型，系统内部使用\n     * @param style 样式: 2（Primary）、3（Green）\n     */\n    public record Button(String customId,\n                         String emoji,\n                         String label,\n                         String type,\n                         String style) {\n    }\n\n    // ============ enums ============\n\n    /**\n     * 模型枚举\n     */\n    @AllArgsConstructor\n    @Getter\n    public enum ModelEnum {\n\n        MIDJOURNEY(\"midjourney\", \"midjourney\"),\n        NIJI(\"niji\", \"niji\"),\n        ;\n\n        private final String model;\n        private final String name;\n\n    }\n\n    /**\n     * 提交返回的状态码的枚举\n     */\n    @Getter\n    @AllArgsConstructor\n    public enum SubmitCodeEnum {\n\n        SUBMIT_SUCCESS(\"1\", \"提交成功\"),\n        ALREADY_EXISTS(\"21\", \"已存在\"),\n        QUEUING(\"22\", \"排队中\"),\n        ;\n\n        public static final List<String> SUCCESS_CODES = Lists.newArrayList(\n                SUBMIT_SUCCESS.code,\n                ALREADY_EXISTS.code,\n                QUEUING.code\n        );\n\n        private final String code;\n        private final String name;\n\n    }\n\n    /**\n     * Action 枚举\n     */\n    @Getter\n    @AllArgsConstructor\n    public enum TaskActionEnum {\n\n        /**\n         * 生成图片\n         */\n        IMAGINE,\n        /**\n         * 选中放大\n         */\n        UPSCALE,\n        /**\n         * 选中其中的一张图，生成四张相似的\n         */\n        VARIATION,\n        /**\n         * 重新执行\n         */\n        REROLL,\n        /**\n         * 图转 prompt\n         */\n        DESCRIBE,\n        /**\n         * 多图混合\n         */\n        BLEND\n\n    }\n\n    /**\n     * 任务状态枚举\n     */\n    @Getter\n    @AllArgsConstructor\n    public enum TaskStatusEnum {\n\n        /**\n         * 未启动\n         */\n        NOT_START(0),\n        /**\n         * 已提交\n         */\n        SUBMITTED(1),\n        /**\n         * 执行中\n         */\n        IN_PROGRESS(3),\n        /**\n         * 失败\n         */\n        FAILURE(4),\n        /**\n         * 成功\n         */\n        SUCCESS(4);\n\n        private final int order;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java",
    "content": "/*\n * Copyright 2023-2024 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;\n\n/**\n * SiliconFlow API 枚举类\n *\n * @author zzt\n */\npublic final class SiliconFlowApiConstants {\n\n\tpublic static final String DEFAULT_BASE_URL = \"https://api.siliconflow.cn\";\n\n\tpublic static final String MODEL_DEFAULT = \"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\";\n\n    public static final String DEFAULT_IMAGE_MODEL = \"Kwai-Kolors/Kolors\";\n\n\tpublic static final String PROVIDER_NAME = \"Siiconflow\";\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport reactor.core.publisher.Flux;\n\n/**\n * 硅基流动 {@link ChatModel} 实现类\n *\n * 1. API 文档：<a href=\"https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions\">API 文档</a>\n *\n * @author fansili\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class SiliconFlowChatModel implements ChatModel {\n\n    /**\n     * 兼容 OpenAI 接口，进行复用\n     */\n    private final ChatModel openAiChatModel;\n\n    @Override\n    public ChatResponse call(Prompt prompt) {\n        return openAiChatModel.call(prompt);\n    }\n\n    @Override\n    public Flux<ChatResponse> stream(Prompt prompt) {\n        return openAiChatModel.stream(prompt);\n    }\n\n    @Override\n    public ChatOptions getDefaultOptions() {\n        return openAiChatModel.getDefaultOptions();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java",
    "content": "/*\n * Copyright 2023-2024 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.springframework.ai.model.ApiKey;\nimport org.springframework.ai.model.NoopApiKey;\nimport org.springframework.ai.model.SimpleApiKey;\nimport org.springframework.ai.openai.api.OpenAiImageApi;\nimport org.springframework.ai.retry.RetryUtils;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.client.ResponseErrorHandler;\nimport org.springframework.web.client.RestClient;\n\nimport java.util.Map;\n\n/**\n * 硅基流动 Image API\n *\n * @see <a href= \"https://docs.siliconflow.cn/cn/api-reference/images/images-generations\">Images</a>\n *\n * @author zzt\n */\npublic class SiliconFlowImageApi {\n\n\tprivate final RestClient restClient;\n\n\tpublic SiliconFlowImageApi(String aiToken) {\n\t\tthis(SiliconFlowApiConstants.DEFAULT_BASE_URL, aiToken, RestClient.builder());\n\t}\n\n    public SiliconFlowImageApi(String baseUrl, String openAiToken) {\n        this(baseUrl, openAiToken, RestClient.builder());\n    }\n\n\tpublic SiliconFlowImageApi(String baseUrl, String openAiToken, RestClient.Builder restClientBuilder) {\n\t\tthis(baseUrl, openAiToken, restClientBuilder, RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);\n\t}\n\n\tpublic SiliconFlowImageApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder,\n                               ResponseErrorHandler responseErrorHandler) {\n\t\tthis(baseUrl, apiKey, CollectionUtils.toMultiValueMap(Map.of()), restClientBuilder, responseErrorHandler);\n\t}\n\n\tpublic SiliconFlowImageApi(String baseUrl, String apiKey, MultiValueMap<String, String> headers,\n                               RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {\n\t\tthis(baseUrl, new SimpleApiKey(apiKey), headers, restClientBuilder, responseErrorHandler);\n\t}\n\n\tpublic SiliconFlowImageApi(String baseUrl, ApiKey apiKey, MultiValueMap<String, String> headers,\n                               RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {\n\n\t\t// @formatter:off\n\t\tthis.restClient = restClientBuilder.baseUrl(baseUrl)\n\t\t\t.defaultHeaders(h -> {\n\t\t\t\tif(!(apiKey instanceof NoopApiKey)) {\n\t\t\t\t\th.setBearerAuth(apiKey.getValue());\n\t\t\t\t}\n\t\t\t\th.setContentType(MediaType.APPLICATION_JSON);\n\t\t\t\th.addAll(headers);\n\t\t\t})\n\t\t\t.defaultStatusHandler(responseErrorHandler)\n\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tpublic ResponseEntity<OpenAiImageApi.OpenAiImageResponse> createImage(SiliconflowImageRequest siliconflowImageRequest) {\n\t\tAssert.notNull(siliconflowImageRequest, \"Image request cannot be null.\");\n\t\tAssert.hasLength(siliconflowImageRequest.prompt(), \"Prompt cannot be empty.\");\n\n\t\treturn this.restClient.post()\n\t\t\t.uri(\"v1/images/generations\")\n\t\t\t.body(siliconflowImageRequest)\n\t\t\t.retrieve()\n\t\t\t.toEntity(OpenAiImageApi.OpenAiImageResponse.class);\n\t}\n\n\n\t// @formatter:off\n\t@JsonInclude(JsonInclude.Include.NON_NULL)\n\tpublic record SiliconflowImageRequest (\n\t\t\t@JsonProperty(\"prompt\") String prompt,\n\t\t\t@JsonProperty(\"model\") String model,\n\t\t\t@JsonProperty(\"batch_size\") Integer batchSize,\n\t\t\t@JsonProperty(\"negative_prompt\") String negativePrompt,\n\t\t\t@JsonProperty(\"seed\") Integer seed,\n\t\t\t@JsonProperty(\"num_inference_steps\") Integer numInferenceSteps,\n\t\t\t@JsonProperty(\"guidance_scale\") Float guidanceScale,\n\t\t\t@JsonProperty(\"image\") String image) {\n\n\t\tpublic SiliconflowImageRequest(String prompt, String model) {\n\t\t\tthis(prompt, model, null, null, null, null, null, null);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java",
    "content": "/*\n * Copyright 2023-2024 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport lombok.Setter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.ai.image.*;\nimport org.springframework.ai.image.observation.DefaultImageModelObservationConvention;\nimport org.springframework.ai.image.observation.ImageModelObservationContext;\nimport org.springframework.ai.image.observation.ImageModelObservationConvention;\nimport org.springframework.ai.image.observation.ImageModelObservationDocumentation;\nimport org.springframework.ai.model.ModelOptionsUtils;\nimport org.springframework.ai.openai.OpenAiImageModel;\nimport org.springframework.ai.openai.api.OpenAiImageApi;\nimport org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata;\nimport org.springframework.ai.retry.RetryUtils;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.lang.Nullable;\nimport org.springframework.retry.support.RetryTemplate;\nimport org.springframework.util.Assert;\n\nimport java.util.List;\n\n/**\n * 硅基流动 {@link ImageModel} 实现类\n *\n * 参考 {@link OpenAiImageModel} 实现\n *\n * @author zzt\n */\npublic class SiliconFlowImageModel implements ImageModel {\n\n    private static final Logger logger = LoggerFactory.getLogger(SiliconFlowImageModel.class);\n\n    private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention();\n\n    private final SiliconFlowImageOptions defaultOptions;\n\n    private final RetryTemplate retryTemplate;\n\n    private final SiliconFlowImageApi siliconFlowImageApi;\n\n    private final ObservationRegistry observationRegistry;\n\n    @Setter\n    private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION;\n\n    public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi) {\n        this(siliconFlowImageApi, SiliconFlowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE);\n    }\n\n    public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate) {\n        this(siliconFlowImageApi, options, retryTemplate, ObservationRegistry.NOOP);\n    }\n\n    public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate,\n                                 ObservationRegistry observationRegistry) {\n        Assert.notNull(siliconFlowImageApi, \"OpenAiImageApi must not be null\");\n        Assert.notNull(options, \"options must not be null\");\n        Assert.notNull(retryTemplate, \"retryTemplate must not be null\");\n        Assert.notNull(observationRegistry, \"observationRegistry must not be null\");\n        this.siliconFlowImageApi = siliconFlowImageApi;\n        this.defaultOptions = options;\n        this.retryTemplate = retryTemplate;\n        this.observationRegistry = observationRegistry;\n    }\n\n    @Override\n    public ImageResponse call(ImagePrompt imagePrompt) {\n        SiliconFlowImageOptions requestImageOptions = mergeOptions(imagePrompt.getOptions(), this.defaultOptions);\n        SiliconFlowImageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt, requestImageOptions);\n\n        var observationContext = ImageModelObservationContext.builder()\n                .imagePrompt(imagePrompt)\n                .provider(SiliconFlowApiConstants.PROVIDER_NAME)\n                .imagePrompt(imagePrompt)\n                .build();\n\n        return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION\n                .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,\n                        this.observationRegistry)\n                .observe(() -> {\n                    ResponseEntity<OpenAiImageApi.OpenAiImageResponse> imageResponseEntity = this.retryTemplate\n                            .execute(ctx -> this.siliconFlowImageApi.createImage(imageRequest));\n\n                    ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest);\n\n                    observationContext.setResponse(imageResponse);\n\n                    return imageResponse;\n                });\n    }\n\n    private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt,\n                                                                      SiliconFlowImageOptions requestImageOptions) {\n        String instructions = imagePrompt.getInstructions().get(0).getText();\n\n        SiliconFlowImageApi.SiliconflowImageRequest imageRequest = new SiliconFlowImageApi.SiliconflowImageRequest(instructions,\n                SiliconFlowApiConstants.DEFAULT_IMAGE_MODEL);\n\n        return ModelOptionsUtils.merge(requestImageOptions, imageRequest, SiliconFlowImageApi.SiliconflowImageRequest.class);\n    }\n\n    private ImageResponse convertResponse(ResponseEntity<OpenAiImageApi.OpenAiImageResponse> imageResponseEntity,\n                                          SiliconFlowImageApi.SiliconflowImageRequest siliconflowImageRequest) {\n        OpenAiImageApi.OpenAiImageResponse imageApiResponse = imageResponseEntity.getBody();\n        if (imageApiResponse == null) {\n            logger.warn(\"No image response returned for request: {}\", siliconflowImageRequest);\n            return new ImageResponse(List.of());\n        }\n\n        List<ImageGeneration> imageGenerationList = imageApiResponse.data()\n                .stream()\n                .map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()),\n                        new OpenAiImageGenerationMetadata(entry.revisedPrompt())))\n                .toList();\n\n        ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created());\n        return new ImageResponse(imageGenerationList, openAiImageResponseMetadata);\n    }\n\n    private SiliconFlowImageOptions mergeOptions(@Nullable ImageOptions runtimeOptions, SiliconFlowImageOptions defaultOptions) {\n        var runtimeOptionsForProvider = ModelOptionsUtils.copyToTarget(runtimeOptions, ImageOptions.class,\n                SiliconFlowImageOptions.class);\n\n        if (runtimeOptionsForProvider == null) {\n            return defaultOptions;\n        }\n\n        return SiliconFlowImageOptions.builder()\n                // Handle portable image options\n                .model(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel()))\n                .batchSize(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN()))\n                .width(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth()))\n                .height(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight()))\n                // Handle SiliconFlow specific image options\n                .negativePrompt(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNegativePrompt(), defaultOptions.getNegativePrompt()))\n                .numInferenceSteps(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNumInferenceSteps(), defaultOptions.getNumInferenceSteps()))\n                .guidanceScale(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getGuidanceScale(), defaultOptions.getGuidanceScale()))\n                .seed(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getSeed(), defaultOptions.getSeed()))\n                .build();\n    }\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.ai.image.ImageOptions;\n\n/**\n * 硅基流动 {@link ImageOptions}\n *\n * @author zzt\n */\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class SiliconFlowImageOptions implements ImageOptions {\n\n    @JsonProperty(\"model\")\n    private String model;\n\n    @JsonProperty(\"negative_prompt\")\n    private String negativePrompt;\n\n    /**\n     * The number of images to generate. Must be between 1 and 4.\n     */\n    @JsonProperty(\"image_size\")\n    private String imageSize;\n\n    /**\n     * The number of images to generate. Must be between 1 and 4.\n     */\n    @JsonProperty(\"batch_size\")\n    private Integer batchSize = 1;\n\n    /**\n     * number of inference steps\n     */\n    @JsonProperty(\"num_inference_steps\")\n    private Integer numInferenceSteps = 25;\n\n    /**\n     * This value is used to control the degree of match between the generated image and the given prompt. The higher the value, the more the generated image will tend to strictly match the text prompt. The lower the value, the more creative and diverse the generated image will be, potentially containing more unexpected elements.\n     *\n     * Required range: 0 <= x <= 20\n     */\n    @JsonProperty(\"guidance_scale\")\n    private Float guidanceScale = 0.75F;\n\n    /**\n     * 如果想要每次都生成固定的图片，可以把 seed 设置为固定值\n     *\n     */\n    @JsonProperty(\"seed\")\n    private Integer seed =  (int)(Math.random() * 1_000_000_000);\n\n    /**\n     * The image that needs to be uploaded should be converted into base64 format.\n     */\n    @JsonProperty(\"image\")\n    private String image;\n\n    /**\n     * 宽\n     */\n    private Integer width;\n\n    /**\n     * 高\n     */\n    private Integer height;\n\n    public void setHeight(Integer height) {\n        this.height = height;\n        if (this.width != null && this.height != null) {\n            this.imageSize = this.width + \"x\" + this.height;\n        }\n    }\n\n    public void setWidth(Integer width) {\n        this.width = width;\n        if (this.width != null && this.height != null) {\n            this.imageSize = this.width + \"x\" + this.height;\n        }\n    }\n\n    @Override\n    public Integer getN() {\n        return batchSize;\n    }\n\n    @Override\n    public String getResponseFormat() {\n        return \"url\";\n    }\n\n    @Override\n    public String getStyle() {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/suno/api/SunoApi.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.text.StrPool;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpRequest;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\n/**\n * Suno API\n * <p>\n * 对接 Suno Proxy：<a href=\"https://github.com/gcui-art/suno-api\">suno-api</a>\n *\n * @author xiaoxin\n */\n@Slf4j\npublic class SunoApi {\n\n    private final WebClient webClient;\n\n    private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();\n\n    private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =\n            reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {\n                HttpRequest request = response.request();\n                log.error(\"[suno-api] 调用失败！请求方式:[{}]，请求地址:[{}]，请求参数:[{}]，响应数据: [{}]\",\n                        request.getMethod(), request.getURI(), reqParam, responseBody);\n                sink.error(new IllegalStateException(\"[suno-api] 调用失败！\"));\n            });\n\n    public SunoApi(String baseUrl) {\n        this.webClient = WebClient.builder()\n                .baseUrl(baseUrl)\n                .defaultHeaders((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))\n                .build();\n    }\n\n    public List<MusicData> generate(MusicGenerateRequest request) {\n        return this.webClient.post()\n                .uri(\"/api/generate\")\n                .body(Mono.just(request), MusicGenerateRequest.class)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))\n                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {\n                })\n                .block();\n    }\n\n    public List<MusicData> customGenerate(MusicGenerateRequest request) {\n        return this.webClient.post()\n                .uri(\"/api/custom_generate\")\n                .body(Mono.just(request), MusicGenerateRequest.class)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))\n                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {\n                })\n                .block();\n    }\n\n    public LyricsData generateLyrics(String prompt) {\n        return this.webClient.post()\n                .uri(\"/api/generate_lyrics\")\n                .body(Mono.just(new MusicGenerateRequest(prompt)), MusicGenerateRequest.class)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(prompt))\n                .bodyToMono(LyricsData.class)\n                .block();\n    }\n\n    public List<MusicData> getMusicList(List<String> ids) {\n        return this.webClient.get()\n                .uri(uriBuilder -> uriBuilder\n                        .path(\"/api/get\")\n                        .queryParam(\"ids\", CollUtil.join(ids, StrPool.COMMA))\n                        .build())\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(ids))\n                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {\n                })\n                .block();\n    }\n\n    public LimitUsageData getLimitUsage() {\n        return this.webClient.get()\n                .uri(\"/api/get_limit\")\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(null))\n                .bodyToMono(LimitUsageData.class)\n                .block();\n    }\n\n    /**\n     * 根据提示生成音频\n     *\n     * @param prompt           用于生成音乐音频的提示\n     * @param tags             音乐风格\n     * @param title            音乐名称\n     * @param model            模型\n     * @param waitAudio        false 表示后台模式，仅返回音频任务信息，需要调用 get API 获取详细的音频信息。\n     *                         true 表示同步模式，API 最多等待 100s，音频生成完毕后直接返回音频链接等信息，建议在 GPT 等 agent 中使用。\n     * @param makeInstrumental 指示音乐音频是否为定制，如果为 true，则从歌词生成，否则从提示生成\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record MusicGenerateRequest(\n            String prompt,\n            String tags,\n            String title,\n            String model,\n            @JsonProperty(\"wait_audio\") boolean waitAudio,\n            @JsonProperty(\"make_instrumental\") boolean makeInstrumental\n    ) {\n\n        public MusicGenerateRequest(String prompt) {\n            this(prompt, null, null, null, false, false);\n        }\n\n        public MusicGenerateRequest(String prompt, String model, boolean makeInstrumental) {\n            this(prompt, null, null, model, false, makeInstrumental);\n        }\n\n        public MusicGenerateRequest(String prompt, String model, String tags, String title) {\n            this(prompt, tags, title, model, false, false);\n        }\n\n    }\n\n    /**\n     * Suno API 响应的音频数据\n     *\n     * @param id                   音乐数据的 ID\n     * @param title                音乐音频的标题\n     * @param imageUrl             音乐音频的图片 URL\n     * @param lyric                音乐音频的歌词\n     * @param audioUrl             音乐音频的 URL\n     * @param videoUrl             音乐视频的 URL\n     * @param createdAt            音乐音频的创建时间\n     * @param modelName            模型名称\n     * @param status               submitted、queued、streaming、complete\n     * @param gptDescriptionPrompt 描述词\n     * @param prompt               生成音乐音频的提示\n     * @param type                 操作类型\n     * @param tags                 音乐类型标签\n     * @param duration             音乐时长\n     */\n    public record MusicData(\n            String id,\n            String title,\n            @JsonProperty(\"image_url\") String imageUrl,\n            String lyric,\n            @JsonProperty(\"audio_url\") String audioUrl,\n            @JsonProperty(\"video_url\") String videoUrl,\n            @JsonProperty(\"created_at\") String createdAt,\n            @JsonProperty(\"model_name\") String modelName,\n            String status,\n            @JsonProperty(\"gpt_description_prompt\") String gptDescriptionPrompt,\n            @JsonProperty(\"error_message\") String errorMessage,\n            String prompt,\n            String type,\n            String tags,\n            Double duration\n    ) {\n    }\n\n    /**\n     * Suno API 响应的歌词数据。\n     *\n     * @param text   歌词\n     * @param title  标题\n     * @param status 状态\n     */\n    public record LyricsData(\n            String text,\n            String title,\n            String status\n    ) {\n    }\n\n    /**\n     * Suno API 响应的限额数据，目前每日免费 50\n     */\n    public record LimitUsageData(\n            @JsonProperty(\"credits_left\") Long creditsLeft,\n            String period,\n            @JsonProperty(\"monthly_limit\") Long monthlyLimit,\n            @JsonProperty(\"monthly_usage\") Long monthlyUsage\n    ) {\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/wenduoduo/api/WenDuoDuoPptApi.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.wenduoduo.api;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpRequest;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.http.MediaType;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.multipart.MultipartFile;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\n/**\n * 文多多 API\n *\n * @author xiaoxin\n * @see <a href=\"https://docmee.cn/open-platform/api\">PPT 生成 API</a>\n */\n@Slf4j\npublic class WenDuoDuoPptApi {\n\n    public static final String BASE_URL = \"https://docmee.cn\";\n    public static final String TOKEN_NAME = \"token\";\n\n    private final WebClient webClient;\n\n    private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();\n\n    private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =\n            reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {\n                HttpRequest request = response.request();\n                log.error(\"[WenDuoDuoPptApi] 调用失败！请求方式:[{}]，请求地址:[{}]，请求参数:[{}]，响应数据: [{}]\",\n                        request.getMethod(), request.getURI(), reqParam, responseBody);\n                sink.error(new IllegalStateException(\"[WenDuoDuoPptApi] 调用失败！\"));\n            });\n\n    public WenDuoDuoPptApi(String token) {\n        Assert.hasText(token, \"token 不能为空\");\n        this.webClient = WebClient.builder()\n                .baseUrl(BASE_URL)\n                .defaultHeaders((headers) -> {\n                    headers.setContentType(MediaType.APPLICATION_JSON);\n                    headers.add(TOKEN_NAME, token);\n                })\n                .build();\n    }\n\n    /**\n     * 创建 token\n     *\n     * @param request 请求信息\n     * @return token\n     */\n    public String createApiToken(CreateTokenRequest request) {\n        return this.webClient.post()\n                .uri(\"/api/user/createApiToken\")\n                .header(\"Api-Key\", request.apiKey)\n                .body(Mono.just(request), CreateTokenRequest.class)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))\n                .bodyToMono(ApiResponse.class)\n                .<String>handle((response, sink) -> {\n                    if (response.code != 0) {\n                        sink.error(new IllegalStateException(\"创建 token 异常，\" + response.message));\n                        return;\n                    }\n                    sink.next(response.data.get(\"token\").toString());\n                })\n                .block();\n    }\n\n    /**\n     * 创建任务\n     *\n     * @param type    类型\n     * @param content 内容\n     * @param files   文件列表\n     * @return 任务 ID\n     * @see <a href=\"https://docmee.cn/open-platform/api#%E5%88%9B%E5%BB%BA%E4%BB%BB%E5%8A%A1\">创建任务</a>\n     */\n    public ApiResponse createTask(Integer type, String content, List<MultipartFile> files) {\n        MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();\n        formData.add(\"type\", type);\n        if (content != null) {\n            formData.add(\"content\", content);\n        }\n        if (files != null) {\n            for (MultipartFile file : files) {\n                formData.add(\"file\", file.getResource());\n            }\n        }\n        return this.webClient.post()\n                .uri(\"/api/ppt/v2/createTask\")\n                .contentType(MediaType.MULTIPART_FORM_DATA)\n                .body(BodyInserters.fromMultipartData(formData))\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(formData))\n                .bodyToMono(ApiResponse.class)\n                .block();\n    }\n\n    /**\n     * 获取生成选项\n     *\n     * @param lang 语种\n     * @return 生成选项\n     */\n    public Map<String, Object> getOptions(String lang) {\n        String uri = \"/api/ppt/v2/options\";\n        if (lang != null) {\n            uri += \"?lang=\" + lang;\n        }\n        return this.webClient.get()\n                .uri(uri)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(lang))\n                .bodyToMono(new ParameterizedTypeReference<ApiResponse>() {\n                })\n                .<Map<String, Object>>handle((response, sink) -> {\n                    if (response.code != 0) {\n                        sink.error(new IllegalStateException(\"获取生成选项异常，\" + response.message));\n                        return;\n                    }\n                    sink.next(response.data);\n                })\n                .block();\n    }\n\n    /**\n     * 分页查询 PPT 模板\n     *\n     * @param token   令牌\n     * @param request 请求体\n     * @return 模板列表\n     */\n    public PagePptTemplateInfo getTemplatePage(TemplateQueryRequest request) {\n        return this.webClient.post()\n                .uri(\"/api/ppt/templates\")\n                .bodyValue(request)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))\n                .bodyToMono(new ParameterizedTypeReference<PagePptTemplateInfo>() {\n                })\n                .block();\n    }\n\n    /**\n     * 生成大纲内容\n     *\n     * @return 大纲内容流\n     */\n    public Flux<Map<String, Object>> createOutline(CreateOutlineRequest request) {\n        return this.webClient.post()\n                .uri(\"/api/ppt/v2/generateContent\")\n                .body(Mono.just(request), CreateOutlineRequest.class)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))\n                .bodyToFlux(new ParameterizedTypeReference<>() {\n                });\n    }\n\n    /**\n     * 修改大纲内容\n     *\n     * @param request 请求体\n     * @return 大纲内容流\n     */\n    public Flux<Map<String, Object>> updateOutline(UpdateOutlineRequest request) {\n        return this.webClient.post()\n                .uri(\"/api/ppt/v2/updateContent\")\n                .body(Mono.just(request), UpdateOutlineRequest.class)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))\n                .bodyToFlux(new ParameterizedTypeReference<>() {\n                });\n    }\n\n    /**\n     * 生成 PPT\n     *\n     * @return PPT信息\n     */\n    public PptInfo create(PptCreateRequest request) {\n        return this.webClient.post()\n                .uri(\"/api/ppt/v2/generatePptx\")\n                .body(Mono.just(request), PptCreateRequest.class)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))\n                .bodyToMono(ApiResponse.class)\n                .<PptInfo>handle((response, sink) -> {\n                    if (response.code != 0) {\n                        sink.error(new IllegalStateException(\"生成 PPT 异常，\" + response.message));\n                        return;\n                    }\n                    sink.next(Objects.requireNonNull(JsonUtils.parseObject(JsonUtils.toJsonString(response.data.get(\"pptInfo\")), PptInfo.class)));\n                })\n                .block();\n    }\n\n    /**\n     * 创建 Token 请求参数\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record CreateTokenRequest(\n            String apiKey,\n            String uid,\n            Integer limit\n    ) {\n\n        public CreateTokenRequest(String apiKey) {\n            this(apiKey, null, null);\n        }\n\n    }\n\n    /**\n     * API 通用响应\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record ApiResponse(\n            Integer code,\n            String message,\n            Map<String, Object> data\n    ) {\n    }\n\n    /**\n     * 创建任务\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record CreateTaskRequest(\n            Integer type,\n            String content,\n            List<MultipartFile> files\n    ) {\n    }\n\n    /**\n     * 生成大纲内容请求\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record CreateOutlineRequest(\n            String id,\n            String length,\n            String scene,\n            String audience,\n            String lang,\n            String prompt\n    ) {\n    }\n\n    /**\n     * 修改大纲内容请求\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record UpdateOutlineRequest(\n            String id,\n            String markdown,\n            String question\n    ) {\n    }\n\n    /**\n     * 生成 PPT 请求参数\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record PptCreateRequest(\n            String id,\n            String templateId,\n            String markdown\n    ) {\n    }\n\n    /**\n     * PPT 信息\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record PptInfo(\n            String id,\n            String name,\n            String subject,\n            String coverUrl,\n            String fileUrl,\n            String templateId,\n            String pptxProperty,\n            String userId,\n            String userName,\n            int companyId,\n            @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n            LocalDateTime updateTime,\n            @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n            LocalDateTime createTime,\n            String createUser,\n            String updateUser\n    ) {\n    }\n\n    /**\n     * 模板查询请求参数\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record TemplateQueryRequest(\n            int page,\n            int size,\n            Filter filters\n    ) {\n\n        /**\n         * 模板查询过滤条件\n         */\n        @JsonInclude(value = JsonInclude.Include.NON_NULL)\n        public record Filter(\n                int type,\n                String category,\n                String style,\n                String themeColor\n        ) {\n        }\n\n    }\n\n    /**\n     * PPT模板分页信息\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record PagePptTemplateInfo(\n            List<PptTemplateInfo> data,\n            String total\n    ) {\n    }\n\n    /**\n     * PPT模板信息\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record PptTemplateInfo(\n            String id,\n            int type,\n            Integer subType,\n            String layout,\n            String category,\n            String style,\n            String themeColor,\n            String lang,\n            boolean animation,\n            String subject,\n            String coverUrl,\n            String fileUrl,\n            List<String> pageCoverUrls,\n            String pptxProperty,\n            int sort,\n            int num,\n            Integer imgNum,\n            int isDeleted,\n            String userId,\n            int companyId,\n            @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n            LocalDateTime updateTime,\n            @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n            LocalDateTime createTime,\n            String createUser,\n            String updateUser\n    ) {\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport reactor.core.publisher.Flux;\n\n/**\n * 讯飞星火 {@link ChatModel} 实现类\n *\n * @author fansili\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class XingHuoChatModel implements ChatModel {\n\n    public static final String BASE_URL_V1 = \"https://spark-api-open.xf-yun.com\";\n\n    public static final String BASE_URL_V2 = \"https://spark-api-open.xf-yun.com\";\n    public static final String BASE_COMPLETIONS_PATH_V2 = \"/v2/chat/completions\";\n\n    /**\n     * 已知模型名列表：x1、4.0Ultra、generalv3.5、max-32k、generalv3、pro-128k、lite\n     */\n    public static final String MODEL_DEFAULT = \"4.0Ultra\";\n\n    /**\n     * v1 兼容 OpenAI 接口，进行复用\n     */\n    private final ChatModel openAiChatModelV1;\n\n    @Override\n    public ChatResponse call(Prompt prompt) {\n        return openAiChatModelV1.call(prompt);\n    }\n\n    @Override\n    public Flux<ChatResponse> stream(Prompt prompt) {\n        return openAiChatModelV1.stream(prompt);\n    }\n\n    @Override\n    public ChatOptions getDefaultOptions() {\n        return openAiChatModelV1.getDefaultOptions();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/api/XunFeiPptApi.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.api;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.crypto.SecureUtil;\nimport cn.hutool.crypto.digest.HmacAlgorithm;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport lombok.Builder;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.io.ByteArrayResource;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.http.MediaType;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.multipart.MultipartFile;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactor.core.publisher.Mono;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\n/**\n * 讯飞智能 PPT 生成 API\n *\n * @author xiaoxin\n * @see <a href=\"https://www.xfyun.cn/doc/spark/PPTv2.html\">智能 PPT 生成 API</a>\n */\n@Slf4j\npublic class XunFeiPptApi {\n\n    public static final String BASE_URL = \"https://zwapi.xfyun.cn/api/ppt/v2\";\n    private static final String HEADER_APP_ID = \"appId\";\n    private static final String HEADER_TIMESTAMP = \"timestamp\";\n    private static final String HEADER_SIGNATURE = \"signature\";\n\n    private final WebClient webClient;\n    private final String appId;\n    private final String apiSecret;\n\n    private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();\n\n    private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =\n            reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {\n                log.error(\"[XunFeiPptApi] 调用失败！请求参数:[{}]，响应数据: [{}]\", reqParam, responseBody);\n                sink.error(new IllegalStateException(\"[XunFeiPptApi] 调用失败！\"));\n            });\n\n    public XunFeiPptApi(String appId, String apiSecret) {\n        this.appId = appId;\n        this.apiSecret = apiSecret;\n        this.webClient = WebClient.builder()\n                .baseUrl(BASE_URL)\n                .defaultHeaders((headers) -> {\n                    headers.setContentType(MediaType.APPLICATION_JSON);\n                    headers.add(HEADER_APP_ID, appId);\n                })\n                .build();\n\n    }\n\n    /**\n     * 获取签名\n     *\n     * @return 签名信息\n     */\n    private SignatureInfo getSignature() {\n        long timestamp = System.currentTimeMillis() / 1000;\n        String ts = String.valueOf(timestamp);\n        String signature = generateSignature(appId, apiSecret, timestamp);\n        return new SignatureInfo(ts, signature);\n    }\n\n    /**\n     * 生成签名\n     *\n     * @param appId     应用ID\n     * @param apiSecret 应用密钥\n     * @param timestamp 时间戳（秒）\n     * @return 签名\n     */\n    private String generateSignature(String appId, String apiSecret, long timestamp) {\n        String auth = SecureUtil.md5(appId + timestamp);\n        return SecureUtil.hmac(HmacAlgorithm.HmacSHA1, apiSecret).digestBase64(auth, false);\n    }\n\n    /**\n     * 获取 PPT 模板列表\n     *\n     * @param style    风格，如\"商务\"\n     * @param pageSize 每页数量\n     * @return 模板列表\n     */\n    public TemplatePageResponse getTemplatePage(String style, Integer pageSize) {\n        SignatureInfo signInfo = getSignature();\n        Map<String, Object> requestBody = new HashMap<>();\n        requestBody.put(\"style\", style);\n        requestBody.put(\"pageSize\", ObjUtil.defaultIfNull(pageSize, 20));\n        return this.webClient.post()\n                .uri(\"/template/list\")\n                .header(HEADER_TIMESTAMP, signInfo.timestamp)\n                .header(HEADER_SIGNATURE, signInfo.signature)\n                .contentType(MediaType.APPLICATION_JSON)\n                .bodyValue(requestBody)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(requestBody))\n                .bodyToMono(TemplatePageResponse.class)\n                .block();\n    }\n\n    /**\n     * 创建大纲（通过文本）\n     *\n     * @param query 查询文本\n     * @return 大纲创建响应\n     */\n    public CreateResponse createOutline(String query) {\n        SignatureInfo signInfo = getSignature();\n        MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();\n        formData.add(\"query\", query);\n        return this.webClient.post()\n                .uri(\"/createOutline\")\n                .header(HEADER_TIMESTAMP, signInfo.timestamp)\n                .header(HEADER_SIGNATURE, signInfo.signature)\n                .contentType(MediaType.MULTIPART_FORM_DATA)\n                .body(BodyInserters.fromMultipartData(formData))\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(formData))\n                .bodyToMono(CreateResponse.class)\n                .block();\n    }\n\n\n    /**\n     * 直接创建 PPT（简化版 - 通过文本）\n     *\n     * @param query 查询文本\n     * @return 创建响应\n     */\n    public CreateResponse create(String query) {\n        CreatePptRequest request = CreatePptRequest.builder()\n                .query(query)\n                .build();\n        return create(request);\n    }\n\n    /**\n     * 直接创建 PPT（简化版 - 通过文件）\n     *\n     * @param file     文件\n     * @param fileName 文件名\n     * @return 创建响应\n     */\n    public CreateResponse create(MultipartFile file, String fileName) {\n        CreatePptRequest request = CreatePptRequest.builder()\n                .file(file).fileName(fileName).build();\n        return create(request);\n    }\n\n    /**\n     * 直接创建 PPT（完整版）\n     *\n     * @param request 请求参数\n     * @return 创建响应\n     */\n    public CreateResponse create(CreatePptRequest request) {\n        SignatureInfo signInfo = getSignature();\n        MultiValueMap<String, Object> formData = buildCreatePptFormData(request);\n        return this.webClient.post()\n                .uri(\"/create\")\n                .header(HEADER_TIMESTAMP, signInfo.timestamp)\n                .header(HEADER_SIGNATURE, signInfo.signature)\n                .contentType(MediaType.MULTIPART_FORM_DATA)\n                .body(BodyInserters.fromMultipartData(formData))\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(formData))\n                .bodyToMono(CreateResponse.class)\n                .block();\n    }\n\n\n    /**\n     * 通过大纲创建 PPT（简化版）\n     *\n     * @param outline 大纲内容\n     * @param query   查询文本\n     * @return 创建响应\n     */\n    public CreateResponse createPptByOutline(OutlineData outline, String query) {\n        CreatePptByOutlineRequest request = CreatePptByOutlineRequest.builder()\n                .outline(outline)\n                .query(query)\n                .build();\n        return createPptByOutline(request);\n    }\n\n    /**\n     * 通过大纲创建 PPT（完整版）\n     *\n     * @param request 请求参数\n     * @return 创建响应\n     */\n    public CreateResponse createPptByOutline(CreatePptByOutlineRequest request) {\n        SignatureInfo signInfo = getSignature();\n        return this.webClient.post()\n                .uri(\"/createPptByOutline\")\n                .header(HEADER_TIMESTAMP, signInfo.timestamp)\n                .header(HEADER_SIGNATURE, signInfo.signature)\n                .contentType(MediaType.APPLICATION_JSON)\n                .bodyValue(request)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))\n                .bodyToMono(CreateResponse.class)\n                .block();\n    }\n\n    /**\n     * 检查 PPT 生成进度\n     *\n     * @param sid 任务 ID\n     * @return 进度响应\n     */\n    public ProgressResponse checkProgress(String sid) {\n        SignatureInfo signInfo = getSignature();\n        return this.webClient.get()\n                .uri(uriBuilder -> uriBuilder\n                        .path(\"/progress\")\n                        .queryParam(\"sid\", sid)\n                        .build())\n                .header(HEADER_TIMESTAMP, signInfo.timestamp)\n                .header(HEADER_SIGNATURE, signInfo.signature)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(sid))\n                .bodyToMono(ProgressResponse.class)\n                .block();\n    }\n\n    /**\n     * 签名信息\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    private record SignatureInfo(\n            String timestamp,\n            String signature\n    ) {\n    }\n\n    /**\n     * 模板列表响应\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record TemplatePageResponse(\n            boolean flag,\n            int code,\n            String desc,\n            Integer count,\n            TemplatePageData data\n    ) {\n    }\n\n    /**\n     * 模板列表数据\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record TemplatePageData(\n            String total,\n            List<TemplateInfo> records,\n            Integer pageNum\n    ) {\n    }\n\n    /**\n     * 模板信息\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record TemplateInfo(\n            String templateIndexId,\n            Integer pageCount,\n            String type,\n            String color,\n            String industry,\n            String style,\n            String detailImage\n    ) {\n    }\n\n    /**\n     * 创建响应\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record CreateResponse(\n            boolean flag,\n            int code,\n            String desc,\n            Integer count,\n            CreateResponseData data\n    ) {\n    }\n\n    /**\n     * 创建响应数据\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record CreateResponseData(\n            String sid,\n            String coverImgSrc,\n            String title,\n            String subTitle,\n            OutlineData outline\n    ) {\n    }\n\n    /**\n     * 大纲数据结构\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record OutlineData(\n            String title,\n            String subTitle,\n            List<Chapter> chapters\n    ) {\n\n        /**\n         * 章节结构\n         */\n        @JsonInclude(value = JsonInclude.Include.NON_NULL)\n        public record Chapter(\n                String chapterTitle,\n                List<ChapterContent> chapterContents\n        ) {\n\n            /**\n             * 章节内容\n             */\n            @JsonInclude(value = JsonInclude.Include.NON_NULL)\n            public record ChapterContent(\n                    String chapterTitle\n            ) {\n            }\n\n        }\n\n        /**\n         * 将大纲对象转换为JSON字符串\n         *\n         * @return 大纲JSON字符串\n         */\n        public String toJsonString() {\n            return JsonUtils.toJsonString(this);\n        }\n    }\n\n    /**\n     * 进度响应\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record ProgressResponse(\n            int code,\n            String desc,\n            ProgressResponseData data\n    ) {\n    }\n\n    /**\n     * 进度响应数据\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record ProgressResponseData(\n            int process,\n            String pptId,\n            String pptUrl,\n            String pptStatus,\n            String aiImageStatus,\n            String cardNoteStatus,\n            String errMsg,\n            Integer totalPages,\n            Integer donePages\n    ) {\n\n        /**\n         * 是否全部完成\n         *\n         * @return 是否全部完成\n         */\n        public boolean isAllDone() {\n            return \"done\".equals(pptStatus)\n                    && (\"done\".equals(aiImageStatus) || aiImageStatus == null)\n                    && (\"done\".equals(cardNoteStatus) || cardNoteStatus == null);\n        }\n\n        /**\n         * 是否失败\n         *\n         * @return 是否失败\n         */\n        public boolean isFailed() {\n            return \"build_failed\".equals(pptStatus);\n        }\n\n        /**\n         * 获取进度百分比\n         *\n         * @return 进度百分比\n         */\n        public int getProgressPercent() {\n            if (totalPages == null || totalPages == 0 || donePages == null) {\n                return process; // 兼容旧版返回\n            }\n            return (int) (donePages * 100.0 / totalPages);\n        }\n\n    }\n\n    /**\n     * 通过大纲创建 PPT 请求参数\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    @Builder\n    public record CreatePptByOutlineRequest(\n            String query,                // 用户生成PPT要求（最多8000字）\n            String outlineSid,           // 已生成大纲后，响应返回的请求大纲唯一id\n            OutlineData outline,         // 大纲内容\n            String templateId,           // 模板ID\n            String businessId,           // 业务ID（非必传）\n            String author,               // PPT作者名\n            Boolean isCardNote,          // 是否生成PPT演讲备注\n            Boolean search,              // 是否联网搜索\n            String language,             // 语种\n            String fileUrl,              // 文件地址\n            String fileName,             // 文件名(带文件名后缀)\n            Boolean isFigure,            // 是否自动配图\n            String aiImage               // ai配图类型：normal、advanced\n    ) {\n    }\n\n\n    /**\n     * 构建创建 PPT 的表单数据\n     *\n     * @param request 请求参数\n     * @return 表单数据\n     */\n    private MultiValueMap<String, Object> buildCreatePptFormData(CreatePptRequest request) {\n        MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();\n        if (request.file() != null) {\n            try {\n                formData.add(\"file\", new ByteArrayResource(request.file().getBytes()) {\n                    @Override\n                    public String getFilename() {\n                        return request.file().getOriginalFilename();\n                    }\n                });\n            } catch (IOException e) {\n                log.error(\"[XunFeiPptApi] 文件处理失败\", e);\n                throw new IllegalStateException(\"[XunFeiPptApi] 文件处理失败\", e);\n            }\n        }\n        Map<String, Object> param = new HashMap<>();\n        addIfPresent(param, \"query\", request.query());\n        addIfPresent(param, \"fileUrl\", request.fileUrl());\n        addIfPresent(param, \"fileName\", request.fileName());\n        addIfPresent(param, \"templateId\", request.templateId());\n        addIfPresent(param, \"businessId\", request.businessId());\n        addIfPresent(param, \"author\", request.author());\n        addIfPresent(param, \"isCardNote\", request.isCardNote());\n        addIfPresent(param, \"search\", request.search());\n        addIfPresent(param, \"language\", request.language());\n        addIfPresent(param, \"isFigure\", request.isFigure());\n        addIfPresent(param, \"aiImage\", request.aiImage());\n        param.forEach(formData::add);\n        return formData;\n    }\n\n    public static <K, V> void addIfPresent(Map<K, V> map, K key, V value) {\n        if (ObjUtil.isNull(key) || ObjUtil.isNull(map)) {\n            return;\n        }\n\n        boolean isPresent = false;\n        if (ObjUtil.isNotNull(value)) {\n            if (value instanceof String) {\n                // 字符串：需要有实际内容\n                isPresent = StringUtils.hasText((String) value);\n            } else {\n                // 其他类型：非 null 即视为存在\n                isPresent = true;\n            }\n        }\n        if (isPresent) {\n            map.put(key, value);\n        }\n    }\n\n    /**\n     * 直接生成PPT请求参数\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    @Builder\n    public record CreatePptRequest(\n            String query,                // 用户生成PPT要求（最多8000字）\n            MultipartFile file,          // 上传文件\n            String fileUrl,              // 文件地址\n            String fileName,             // 文件名(带文件名后缀)\n            String templateId,           // 模板ID\n            String businessId,           // 业务ID（非必传）\n            String author,               // PPT作者名\n            Boolean isCardNote,          // 是否生成PPT演讲备注\n            Boolean search,              // 是否联网搜索\n            String language,             // 语种\n            Boolean isFigure,            // 是否自动配图\n            String aiImage               // ai配图类型：normal、advanced\n    ) {\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchClient.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.webserch;\n\n/**\n * 网络搜索客户端接口\n *\n * @author 芋道源码\n */\npublic interface AiWebSearchClient {\n\n    /**\n     * 网页搜索\n     *\n     * @param request 搜索请求\n     * @return 搜索结果\n     */\n    AiWebSearchResponse search(AiWebSearchRequest request);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchRequest.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.webserch;\n\nimport jakarta.validation.constraints.Max;\nimport jakarta.validation.constraints.Min;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class AiWebSearchRequest {\n\n    /**\n     * 用户的搜索词\n     */\n    @NotEmpty(message = \"搜索词不能为空\")\n    private String query;\n\n    /**\n     * 是否显示文本摘要\n     *\n     * true - 显示\n     * false - 不显示（默认）\n     */\n    private Boolean summary;\n\n    /**\n     * 返回结果的条数\n     */\n    @NotNull(message = \"返回结果条数不能为空\")\n    @Min(message = \"返回结果条数最小为 1\", value = 1)\n    @Max(message = \"返回结果条数最大为 50\", value = 50)\n    private Integer count;\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchResponse.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.webserch;\n\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class AiWebSearchResponse {\n\n    /**\n     * 总数（总共匹配的网页数）\n     */\n    private Long total;\n\n    /**\n     * 数据列表\n     */\n    private List<WebPage> lists;\n\n    /**\n     * 网页对象\n     */\n    @Data\n    public static class WebPage {\n\n        /**\n         * 名称\n         *\n         * 例如说：搜狐网\n         */\n        private String name;\n        /**\n         * 图标\n         */\n        private String icon;\n\n        /**\n         * 标题\n         *\n         * 例如说：186页|阿里巴巴：2024年环境、社会和治理（ESG）报告\n         */\n        private String title;\n        /**\n         * URL\n         *\n         * 例如说：https://m.sohu.com/a/815036254_121819701/?pvid=000115_3w_a\n         */\n        @SuppressWarnings(\"JavadocLinkAsPlainText\")\n        private String url;\n\n        /**\n         * 内容的简短描述\n         */\n        private String snippet;\n        /**\n         * 内容的文本摘要\n         */\n        private String summary;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/bocha/AiBoChaWebSearchClient.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchRequest;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * 博查 {@link AiWebSearchClient} 实现类\n *\n * @see <a href=\"https://open.bochaai.com/overview\">博查 AI 开放平台</a>\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class AiBoChaWebSearchClient implements AiWebSearchClient {\n\n    public static final String BASE_URL = \"https://api.bochaai.com\";\n    private static final String AUTHORIZATION_HEADER = \"Authorization\";\n    private static final String BEARER_PREFIX = \"Bearer \";\n\n    private final WebClient webClient;\n\n    private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();\n\n    private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =\n            reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {\n                log.error(\"[AiBoChaWebSearchClient] 调用失败！请求参数:[{}]，响应数据: [{}]\", reqParam, responseBody);\n                sink.error(new IllegalStateException(\"[AiBoChaWebSearchClient] 调用失败！\"));\n            });\n\n    public AiBoChaWebSearchClient(String apiKey) {\n        this.webClient = WebClient.builder()\n                .baseUrl(BASE_URL)\n                .defaultHeaders((headers) -> {\n                    headers.setContentType(MediaType.APPLICATION_JSON);\n                    headers.add(AUTHORIZATION_HEADER, BEARER_PREFIX + apiKey);\n                })\n                .build();\n    }\n\n    @Override\n    public AiWebSearchResponse search(AiWebSearchRequest request) {\n        // 转换请求参数\n        WebSearchRequest webSearchRequest = new WebSearchRequest(\n                request.getQuery(),\n                request.getSummary(),\n                request.getCount()\n        );\n        // 调用博查 API\n        CommonResult<WebSearchResponse> response = this.webClient.post()\n                .uri(\"/v1/web-search\")\n                .bodyValue(webSearchRequest)\n                .retrieve()\n                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(webSearchRequest))\n                .bodyToMono(new ParameterizedTypeReference<CommonResult<WebSearchResponse>>() {})\n                .block();\n        if (response == null) {\n            throw new IllegalStateException(\"[search][搜索结果为空]\");\n        }\n        if (response.getData() == null) {\n            throw new IllegalStateException(String.format(\"[search][搜索失败，code = %s, msg = %s]\",\n                    response.getCode(), response.getMsg()));\n        }\n        WebSearchResponse data = response.getData();\n\n        // 转换结果\n        AiWebSearchResponse result = new AiWebSearchResponse();\n        if (data.webPages() == null || CollUtil.isEmpty(data.webPages().value())) {\n            return result.setTotal(0L).setLists(List.of());\n        }\n        return result.setTotal(data.webPages().totalEstimatedMatches())\n                .setLists(convertList(data.webPages().value(), page -> new AiWebSearchResponse.WebPage()\n                        .setName(page.siteName()).setIcon(page.siteIcon())\n                        .setTitle(page.name()).setUrl(page.url())\n                        .setSnippet(page.snippet()).setSummary(page.summary())));\n    }\n\n    /**\n     * 网页搜索请求参数\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record WebSearchRequest(\n            String query,\n            Boolean summary,\n            Integer count\n    ) {\n        public WebSearchRequest {\n            Assert.notBlank(query, \"query 不能为空\");\n        }\n    }\n\n    /**\n     * 网页搜索响应\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record WebSearchResponse(\n            WebSearchWebPages webPages\n    ) {\n    }\n\n    /**\n     * 网页搜索结果\n     */\n    @JsonInclude(value = JsonInclude.Include.NON_NULL)\n    public record WebSearchWebPages(\n            String webSearchUrl,\n            Long totalEstimatedMatches,\n            List<WebPageValue> value,\n            Boolean someResultsRemoved\n    ) {\n\n        /**\n         * 网页结果值\n         */\n        @JsonInclude(value = JsonInclude.Include.NON_NULL)\n        public record WebPageValue(\n                String id,\n                String name,\n                String url,\n                String displayUrl,\n                String snippet,\n                String summary,\n                String siteName,\n                String siteIcon,\n                String datePublished,\n                String dateLastCrawled,\n                String cachedPageUrl,\n                String language,\n                Boolean isFamilyFriendly,\n                Boolean isNavigational\n        ) {\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/package-info.java",
    "content": "/**\n * AI 大模型组件，基于 Spring AI 拓展\n *\n * models 包路径：\n *  1. xinghuo 包：【讯飞】星火，自己实现\n *  2. deepseek 包：【深度求索】DeepSeek，自己实现\n *  3. doubao 包：【字节豆包】DouBao，自己实现\n *  4. hunyuan 包：【腾讯混元】HunYuan，自己实现\n *  5. siliconflow 包：【硅基硅流】SiliconFlow，自己实现\n *  6. midjourney 包：Midjourney API，对接 https://github.com/novicezk/midjourney-proxy 实现\n *  7. suno 包：Suno API，对接 https://github.com/gcui-art/suno-api 实现\n */\npackage cn.iocoder.yudao.module.ai.framework.ai;"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/package-info.java",
    "content": "/**\n * 属于 ai 模块的 framework 封装\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.ai.framework;\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/rpc/config/RpcConfiguration.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.rpc.config;\n\nimport cn.iocoder.yudao.module.infra.api.file.FileApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(value = \"aiRpcConfiguration\", proxyBeanMethods = false)\n@EnableFeignClients(clients = {FileApi.class, AdminUserApi.class})\npublic class RpcConfiguration {\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/rpc/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.ai.framework.rpc;\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.security.config;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.module.infra.enums.ApiConstants;\nimport jakarta.annotation.Resource;\nimport org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerSseProperties;\nimport org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerStreamableHttpProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\nimport java.util.Optional;\n\n/**\n * AI 模块的 Security 配置\n */\n@Configuration(proxyBeanMethods = false, value = \"aiSecurityConfiguration\")\npublic class SecurityConfiguration {\n\n    @Resource\n    private Optional<McpServerSseProperties> mcpServerSseProperties;\n    @Resource\n    private Optional<McpServerStreamableHttpProperties> mcpServerStreamableHttpProperties;\n\n    @Bean(\"aiAuthorizeRequestsCustomizer\")\n    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {\n        return new AuthorizeRequestsCustomizer() {\n\n            @Override\n            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n                // Swagger 接口文档\n                registry.requestMatchers(\"/v3/api-docs/**\").permitAll()\n                        .requestMatchers(\"/webjars/**\").permitAll()\n                        .requestMatchers(\"/swagger-ui\").permitAll()\n                        .requestMatchers(\"/swagger-ui/**\").permitAll();\n                // Spring Boot Actuator 的安全配置\n                registry.requestMatchers(\"/actuator\").permitAll()\n                        .requestMatchers(\"/actuator/**\").permitAll();\n                // Druid 监控\n                registry.requestMatchers(\"/druid/**\").permitAll();\n\n                // TODO 芋艿：这个每个项目都需要重复配置，得捉摸有没通用的方案\n                // RPC 服务的安全配置\n                registry.requestMatchers(ApiConstants.PREFIX + \"/**\").permitAll();\n\n                // MCP Server\n                mcpServerSseProperties.ifPresent(properties -> {\n                    registry.requestMatchers(properties.getSseEndpoint()).permitAll();\n                    registry.requestMatchers(properties.getSseMessageEndpoint()).permitAll();\n                });\n                mcpServerStreamableHttpProperties.ifPresent(properties ->\n                        registry.requestMatchers(properties.getMcpEndpoint()).permitAll());\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/core/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.ai.framework.security.core;\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/job/image/AiMidjourneySyncJob.java",
    "content": "package cn.iocoder.yudao.module.ai.job.image;\n\nimport cn.iocoder.yudao.module.ai.service.image.AiImageService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * Midjourney 同步 Job：定时拉去 midjourney 绘制状态\n *\n * @author fansili\n */\n@Component\n@Slf4j\npublic class AiMidjourneySyncJob {\n\n    @Resource\n    private AiImageService imageService;\n\n    @XxlJob(\"aiMidjourneySyncJob\")\n    public void execute(String param) {\n        Integer count = imageService.midjourneySync();\n        log.info(\"[execute][同步 Midjourney ({}) 个]\", count);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/job/music/AiSunoSyncJob.java",
    "content": "package cn.iocoder.yudao.module.ai.job.music;\n\nimport cn.iocoder.yudao.module.ai.service.music.AiMusicService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n\n/**\n * 同步 Suno 任务状态以及回写对应的音乐信息 Job\n *\n * @author xiaoxin\n */\n@Component\n@Slf4j\npublic class AiSunoSyncJob {\n\n    @Resource\n    private AiMusicService musicService;\n\n    @XxlJob(\"aiSunoSyncJob\")\n    public void execute(String param) {\n        Integer count = musicService.syncMusic();\n        log.info(\"[execute][同步 Suno ({}) 个]\", count);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/package-info.java",
    "content": "/**\n * ai 模块下，接入 LLM 大模型，支持聊天、绘图、音乐、写作、思维导图等功能。\n * 目前已接入各种模型，不限于：\n * 国内：通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek\n * 国外：OpenAI、Ollama、Midjourney、StableDiffusion、Suno\n *\n * 1. Controller URL：以 /ai/ 开头，避免和其它 Module 冲突\n * 2. DataObject 表名：以 ai_ 开头，方便在数据库中区分\n */\npackage cn.iocoder.yudao.module.ai;\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.chat;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;\n\nimport java.util.List;\n\n/**\n * AI 聊天对话 Service 接口\n *\n * @author fansili\n */\npublic interface AiChatConversationService {\n\n    /**\n     * 创建【我的】聊天对话\n     *\n     * @param createReqVO 创建信息\n     * @param userId 用户编号\n     * @return 编号\n     */\n    Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId);\n\n    /**\n     * 更新【我的】聊天对话\n     *\n     * @param updateReqVO 更新信息\n     * @param userId 用户编号\n     */\n    void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId);\n\n    /**\n     * 获得【我的】聊天对话列表\n     *\n     * @param userId 用户编号\n     * @return 聊天对话列表\n     */\n    List<AiChatConversationDO> getChatConversationListByUserId(Long userId);\n\n    /**\n     * 获得聊天对话\n     *\n     * @param id 编号\n     * @return 聊天对话\n     */\n    AiChatConversationDO getChatConversation(Long id);\n\n    /**\n     * 删除【我的】聊天对话\n     *\n     * @param id 编号\n     * @param userId 用户编号\n     */\n    void deleteChatConversationMy(Long id, Long userId);\n\n    /**\n     * 【管理员】删除聊天对话\n     *\n     * @param id 编号\n     */\n    void deleteChatConversationByAdmin(Long id);\n\n    /**\n     * 校验聊天对话是否存在\n     *\n     * @param id 编号\n     * @return 聊天对话\n     */\n    AiChatConversationDO validateChatConversationExists(Long id);\n\n    /**\n     * 删除【我的】 + 非置顶的聊天对话\n     *\n     * @param userId 用户编号\n     */\n    void deleteChatConversationMyByUnpinned(Long userId);\n\n    /**\n     * 获得聊天对话的分页列表\n     *\n     * @param pageReqVO 分页查询\n     * @return 聊天对话的分页列表\n     */\n    PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.chat;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;\n\n/**\n * AI 聊天对话 Service 实现类\n *\n * @author fansili\n */\n@Service\n@Validated\n@Slf4j\npublic class AiChatConversationServiceImpl implements AiChatConversationService {\n\n    @Resource\n    private AiChatConversationMapper chatConversationMapper;\n\n    @Resource\n    private AiModelService modalService;\n    @Resource\n    private AiChatRoleService chatRoleService;\n    @Resource\n    private AiKnowledgeService knowledgeService;\n\n    @Override\n    public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {\n        // 1.1 获得 AiChatRoleDO 聊天角色\n        AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null;\n        // 1.2 获得 AiModelDO 聊天模型\n        AiModelDO model = role != null && role.getModelId() != null ? modalService.validateModel(role.getModelId())\n                : modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());\n        Assert.notNull(model, \"必须找到默认模型\");\n        validateChatModel(model);\n\n        // 1.3 校验知识库\n        if (Objects.nonNull(createReqVO.getKnowledgeId())) {\n            knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());\n        }\n\n        // 2. 创建 AiChatConversationDO 聊天对话\n        AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false)\n                .setModelId(model.getId()).setModel(model.getModel())\n                .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());\n        if (role != null) {\n            conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage());\n        } else {\n            conversation.setTitle(AiChatConversationDO.TITLE_DEFAULT);\n        }\n        chatConversationMapper.insert(conversation);\n        return conversation.getId();\n    }\n\n    @Override\n    public void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId) {\n        // 1.1 校验对话是否存在\n        AiChatConversationDO conversation = validateChatConversationExists(updateReqVO.getId());\n        if (ObjUtil.notEqual(conversation.getUserId(), userId)) {\n            throw exception(CHAT_CONVERSATION_NOT_EXISTS);\n        }\n        // 1.2 校验模型是否存在（修改模型的情况）\n        AiModelDO model = null;\n        if (updateReqVO.getModelId() != null) {\n            model = modalService.validateModel(updateReqVO.getModelId());\n        }\n\n        // 1.3 校验知识库是否存在\n        if (updateReqVO.getKnowledgeId() != null) {\n            knowledgeService.validateKnowledgeExists(updateReqVO.getKnowledgeId());\n        }\n\n        // 2. 更新对话信息\n        AiChatConversationDO updateObj = BeanUtils.toBean(updateReqVO, AiChatConversationDO.class);\n        if (Boolean.TRUE.equals(updateReqVO.getPinned())) {\n            updateObj.setPinnedTime(LocalDateTime.now());\n        }\n        if (model != null) {\n            updateObj.setModel(model.getModel());\n        }\n        chatConversationMapper.updateById(updateObj);\n    }\n\n    @Override\n    public List<AiChatConversationDO> getChatConversationListByUserId(Long userId) {\n        return chatConversationMapper.selectListByUserId(userId);\n    }\n\n    @Override\n    public AiChatConversationDO getChatConversation(Long id) {\n        return chatConversationMapper.selectById(id);\n    }\n\n    @Override\n    public void deleteChatConversationMy(Long id, Long userId) {\n        // 1. 校验对话是否存在\n        AiChatConversationDO conversation = validateChatConversationExists(id);\n        if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), userId)) {\n            throw exception(CHAT_CONVERSATION_NOT_EXISTS);\n        }\n        // 2. 执行删除\n        chatConversationMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteChatConversationByAdmin(Long id) {\n        // 1. 校验对话是否存在\n        AiChatConversationDO conversation = validateChatConversationExists(id);\n        if (conversation == null) {\n            throw exception(CHAT_CONVERSATION_NOT_EXISTS);\n        }\n        // 2. 执行删除\n        chatConversationMapper.deleteById(id);\n    }\n\n    private void validateChatModel(AiModelDO model) {\n        if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {\n            return;\n        }\n        Assert.equals(model.getType(), AiModelTypeEnum.CHAT.getType(), \"模型类型不正确：\" + model);\n        throw exception(CHAT_CONVERSATION_MODEL_ERROR);\n    }\n\n    public AiChatConversationDO validateChatConversationExists(Long id) {\n        AiChatConversationDO conversation = chatConversationMapper.selectById(id);\n        if (conversation == null) {\n            throw exception(CHAT_CONVERSATION_NOT_EXISTS);\n        }\n        return conversation;\n    }\n\n    @Override\n    public void deleteChatConversationMyByUnpinned(Long userId) {\n        List<AiChatConversationDO> list = chatConversationMapper.selectListByUserIdAndPinned(userId, false);\n        if (CollUtil.isEmpty(list)) {\n            return;\n        }\n        chatConversationMapper.deleteByIds(convertList(list, AiChatConversationDO::getId));\n    }\n\n    @Override\n    public PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {\n        return chatConversationMapper.selectChatConversationPage(pageReqVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.chat;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;\nimport reactor.core.publisher.Flux;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * AI 聊天消息 Service 接口\n *\n * @author fansili\n */\npublic interface AiChatMessageService {\n\n    /**\n     * 发送消息\n     *\n     * @param sendReqVO 发送信息\n     * @param userId 用户编号\n     * @return 发送结果\n     */\n    AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId);\n\n    /**\n     * 发送消息\n     *\n     * @param sendReqVO 发送信息\n     * @param userId 用户编号\n     * @return 发送结果\n     */\n    Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId);\n\n    /**\n     * 获得指定对话的消息列表\n     *\n     * @param conversationId 对话编号\n     * @return 消息列表\n     */\n    List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId);\n\n    /**\n     * 删除消息\n     *\n     * @param id 消息编号\n     * @param userId 用户编号\n     */\n    void deleteChatMessage(Long id, Long userId);\n\n    /**\n     * 删除指定对话的消息\n     *\n     * @param conversationId 对话编号\n     * @param userId 用户编号\n     */\n    void deleteChatMessageByConversationId(Long conversationId, Long userId);\n\n    /**\n     * 【管理员】删除消息\n     *\n     * @param id 消息编号\n     */\n    void deleteChatMessageByAdmin(Long id);\n\n    /**\n     * 获得聊天对话的消息数量 Map\n     *\n     * @param conversationIds 对话编号数组\n     * @return 消息数量 Map\n     */\n    Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds);\n\n    /**\n     * 获得聊天消息的分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 聊天消息的分页\n     */\n    PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.chat;\n\nimport cn.hutool.core.codec.Base64;\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.io.file.FileNameUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.http.HttpUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;\nimport cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchRequest;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;\nimport cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;\nimport cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;\nimport cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport cn.iocoder.yudao.module.ai.service.model.AiToolService;\nimport cn.iocoder.yudao.module.ai.util.AiUtils;\nimport cn.iocoder.yudao.module.ai.util.FileTypeUtils;\nimport com.google.common.collect.Maps;\nimport io.modelcontextprotocol.client.McpSyncClient;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.MessageType;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.model.StreamingChatModel;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.mcp.SyncMcpToolCallbackProvider;\nimport org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;\nimport org.springframework.ai.tool.ToolCallback;\nimport org.springframework.ai.tool.resolution.ToolCallbackResolver;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport reactor.core.publisher.Flux;\n\nimport java.time.LocalDateTime;\nimport java.util.*;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.stream.Collectors;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST;\n\n/**\n * AI 聊天消息 Service 实现类\n *\n * @author fansili\n */\n@Service\n@Slf4j\npublic class AiChatMessageServiceImpl implements AiChatMessageService {\n\n    /**\n     * 联网搜索的结束数\n     */\n    private static final Integer WEB_SEARCH_COUNT = 10;\n\n    // TODO @芋艿：后续优化下对话的 Prompt 整体结构\n\n    /**\n     * 知识库转 {@link UserMessage} 的内容模版\n     */\n    private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = \"使用 <Reference></Reference> 标记中的内容作为本次对话的参考:\\n\\n\" +\n            \"%s\\n\\n\" + // 多个 <Reference></Reference> 的拼接\n            \"回答要求：\\n- 避免提及你是从 <Reference></Reference> 获取的知识。\";\n\n    private static final String WEB_SEARCH_USER_MESSAGE_TEMPLATE = \"使用 <WebSearch></WebSearch> 标记中的内容作为本次对话的参考:\\n\\n\" +\n            \"%s\\n\\n\" + // 多个 <WebSearch></WebSearch> 的拼接\n            \"回答要求：\\n- 避免提及你是从 <WebSearch></WebSearch> 获取的知识。\";\n\n    /**\n     * 附件转 ${@link UserMessage} 的内容模版\n     */\n    @SuppressWarnings(\"TextBlockMigration\")\n    private static final String Attachment_USER_MESSAGE_TEMPLATE = \"使用 <Attachment></Attachment> 标记用户对话上传的附件内容:\\n\\n\" +\n            \"%s\\n\\n\" + // 多个 <Attachment></Attachment> 的拼接\n            \"回答要求：\\n- 避免提及 <Attachment></Attachment> 附件的编码格式。\";\n\n    @Resource\n    private AiChatMessageMapper chatMessageMapper;\n\n    @Resource\n    private AiChatConversationService chatConversationService;\n    @Resource\n    private AiChatRoleService chatRoleService;\n    @Resource\n    private AiModelService modalService;\n    @Resource\n    private AiKnowledgeSegmentService knowledgeSegmentService;\n    @Resource\n    private AiKnowledgeDocumentService knowledgeDocumentService;\n    @Resource\n    private AiToolService toolService;\n\n    @SuppressWarnings(\"SpringJavaAutowiredFieldsWarningInspection\")\n    @Autowired(required = false) // 由于 yudao.ai.web-search.enable 配置项，可以关闭 AiWebSearchClient 的功能，所以这里只能不强制注入\n    private AiWebSearchClient webSearchClient;\n\n    @SuppressWarnings(\"SpringJavaAutowiredFieldsWarningInspection\")\n    @Autowired(required = false) // 由于 yudao.ai.mcp.client.enable 配置项，可以关闭 McpSyncClient 的功能，所以这里只能不强制注入\n    private List<McpSyncClient> mcpClients;\n\n    @SuppressWarnings(\"SpringJavaAutowiredFieldsWarningInspection\")\n    @Autowired(required = false) // 由于 yudao.ai.mcp.client.enable 配置项，可以关闭 McpSyncClient 的功能，所以这里只能不强制注入\n    private McpClientCommonProperties mcpClientCommonProperties;\n\n    @Resource\n    private ToolCallbackResolver toolCallbackResolver;\n\n    @Transactional(rollbackFor = Exception.class)\n    public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {\n        // 1.1 校验对话存在\n        AiChatConversationDO conversation = chatConversationService\n                .validateChatConversationExists(sendReqVO.getConversationId());\n        if (ObjUtil.notEqual(conversation.getUserId(), userId)) {\n            throw exception(CHAT_CONVERSATION_NOT_EXISTS);\n        }\n        List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());\n        // 1.2 校验模型\n        AiModelDO model = modalService.validateModel(conversation.getModelId());\n        ChatModel chatModel = modalService.getChatModel(model.getId());\n\n        // 2.1 知识库召回\n        List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(\n                sendReqVO.getContent(), conversation);\n\n        // 2.2 联网搜索\n        AiWebSearchResponse webSearchResponse = Boolean.TRUE.equals(sendReqVO.getUseSearch()) && webSearchClient != null ?\n                webSearchClient.search(new AiWebSearchRequest().setQuery(sendReqVO.getContent())\n                        .setSummary(true).setCount(WEB_SEARCH_COUNT)) : null;\n\n        // 3. 插入 user 发送消息\n        AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,\n                userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),\n                null, sendReqVO.getAttachmentUrls(), null);\n\n        // 4.1 插入 assistant 接收消息\n        AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,\n                userId, conversation.getRoleId(), MessageType.ASSISTANT, \"\", sendReqVO.getUseContext(),\n                knowledgeSegments, null, webSearchResponse);\n\n        // 4.2 创建 chat 需要的 Prompt\n        Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, webSearchResponse, model, sendReqVO);\n        ChatResponse chatResponse = chatModel.call(prompt);\n\n        // 4.3 更新响应内容\n        String newContent = AiUtils.getChatResponseContent(chatResponse);\n        String newReasoningContent = AiUtils.getChatResponseReasoningContent(chatResponse);\n        chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId())\n                .setContent(newContent).setReasoningContent(newReasoningContent));\n        // 4.4 响应结果\n        Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(\n                convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId));\n        List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments,\n                AiChatMessageRespVO.KnowledgeSegment.class, segment -> {\n                    AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());\n                    segment.setDocumentName(document != null ? document.getName() : null);\n                });\n        return new AiChatMessageSendRespVO()\n                .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))\n                .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)\n                        .setContent(newContent).setSegments(segments)\n                        .setWebSearchPages(webSearchResponse != null ? webSearchResponse.getLists() : null));\n    }\n\n    @Override\n    public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO,\n            Long userId) {\n        // 1.1 校验对话存在\n        AiChatConversationDO conversation = chatConversationService\n                .validateChatConversationExists(sendReqVO.getConversationId());\n        if (ObjUtil.notEqual(conversation.getUserId(), userId)) {\n            throw exception(CHAT_CONVERSATION_NOT_EXISTS);\n        }\n        List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());\n        // 1.2 校验模型\n        AiModelDO model = modalService.validateModel(conversation.getModelId());\n        StreamingChatModel chatModel = modalService.getChatModel(model.getId());\n\n        // 2.1 知识库找回\n        List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(\n                sendReqVO.getContent(), conversation);\n\n        // 2.2 联网搜索\n        AiWebSearchResponse webSearchResponse = Boolean.TRUE.equals(sendReqVO.getUseSearch()) && webSearchClient != null ?\n                webSearchClient.search(new AiWebSearchRequest().setQuery(sendReqVO.getContent())\n                        .setSummary(true).setCount(WEB_SEARCH_COUNT)) : null;\n\n        // 3. 插入 user 发送消息\n        AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,\n                userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),\n                null, sendReqVO.getAttachmentUrls(), null);\n\n        // 4.1 插入 assistant 接收消息\n        AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,\n                userId, conversation.getRoleId(), MessageType.ASSISTANT, \"\", sendReqVO.getUseContext(),\n                knowledgeSegments, null, webSearchResponse);\n\n        // 4.2 构建 Prompt，并进行调用\n        Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, webSearchResponse, model, sendReqVO);\n        Flux<ChatResponse> streamResponse = chatModel.stream(prompt);\n\n        // 4.3 流式返回\n        StringBuffer contentBuffer = new StringBuffer();\n        StringBuffer reasoningContentBuffer = new StringBuffer();\n\n        // 防止执行多次知识库和联网搜索\n        AtomicBoolean firstExecuteFlag = new AtomicBoolean(true);\n        AtomicReference<List<AiChatMessageRespVO.KnowledgeSegment>> cacheSegments = new AtomicReference<>();\n        AtomicReference<List<AiWebSearchResponse.WebPage>> cacheWebSearchPages = new AtomicReference<>();\n        return streamResponse.map(chunk -> {\n            // 仅首次：返回知识库、联网搜索\n            if (StrUtil.isEmpty(contentBuffer)) {\n                if (firstExecuteFlag.compareAndSet(true, false)) { // CAS 操作，确保仅执行一次\n                    Map<Long, AiKnowledgeDocumentDO> documentMap = TenantUtils.executeIgnore(() -> knowledgeDocumentService.getKnowledgeDocumentMap(\n                            convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId)));\n                    cacheSegments.set(BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, segment -> {\n                        AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());\n                        segment.setDocumentName(document != null ? document.getName() : null);\n                    }));\n                    if (webSearchResponse != null) {\n                        cacheWebSearchPages.set(webSearchResponse.getLists());\n                    }\n                }\n            }\n            // 响应结果\n            String newContent = AiUtils.getChatResponseContent(chunk);\n            String newReasoningContent = AiUtils.getChatResponseReasoningContent(chunk);\n            if (StrUtil.isNotEmpty(newContent)) {\n                contentBuffer.append(newContent);\n            }\n            if (StrUtil.isNotEmpty(newReasoningContent)) {\n                reasoningContentBuffer.append(newReasoningContent);\n            }\n            return success(new AiChatMessageSendRespVO()\n                    .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))\n                    .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)\n                            .setContent(StrUtil.nullToDefault(newContent, \"\")) // 避免 null 的 情况\n                            .setReasoningContent(StrUtil.nullToDefault(newReasoningContent, \"\")) // 避免 null 的 情况\n                            .setSegments(cacheSegments.get()).setWebSearchPages(cacheWebSearchPages.get()))); // 知识库 + 联网搜索\n        }).doOnComplete(() -> {\n            // 忽略租户，因为 Flux 异步无法透传租户\n            TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(\n                    new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())\n                            .setReasoningContent(reasoningContentBuffer.toString())));\n        }).doOnError(throwable -> {\n            log.error(\"[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]\", userId, sendReqVO, throwable);\n            // 忽略租户，因为 Flux 异步无法透传租户\n            TenantUtils.executeIgnore(() -> {\n                // 如果有内容，则更新内容\n                if (StrUtil.isNotEmpty(contentBuffer)) {\n                    chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId())\n                            .setContent(contentBuffer.toString()).setReasoningContent(reasoningContentBuffer.toString()));\n                } else {\n                    // 否则，则进行删除\n                    chatMessageMapper.deleteById(assistantMessage.getId());\n                }\n            });\n        }).doOnCancel(() -> {\n            log.info(\"[sendChatMessageStream][userId({}) sendReqVO({}) 取消请求]\", userId, sendReqVO);\n            // 忽略租户，因为 Flux 异步无法透传租户\n            TenantUtils.executeIgnore(() -> {\n                // 如果有内容，则更新内容\n                if (StrUtil.isNotEmpty(contentBuffer)) {\n                    chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId())\n                            .setContent(contentBuffer.toString()).setReasoningContent(reasoningContentBuffer.toString()));\n                } else {\n                    // 否则，则进行删除\n                    chatMessageMapper.deleteById(assistantMessage.getId());\n                }\n            });\n        }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));\n    }\n\n    private List<AiKnowledgeSegmentSearchRespBO> recallKnowledgeSegment(String content,\n            AiChatConversationDO conversation) {\n        // 1. 查询聊天角色\n        if (conversation == null || conversation.getRoleId() == null) {\n            return Collections.emptyList();\n        }\n        AiChatRoleDO role = chatRoleService.getChatRole(conversation.getRoleId());\n        if (role == null || CollUtil.isEmpty(role.getKnowledgeIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 遍历召回\n        List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = new ArrayList<>();\n        for (Long knowledgeId : role.getKnowledgeIds()) {\n            knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO()\n                    .setKnowledgeId(knowledgeId).setContent(content)));\n        }\n        return knowledgeSegments;\n    }\n\n    private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,\n                               List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,\n                               AiWebSearchResponse webSearchResponse,\n                               AiModelDO model, AiChatMessageSendReqVO sendReqVO) {\n        List<Message> chatMessages = new ArrayList<>();\n        // 1.1 System Context 角色设定\n        if (StrUtil.isNotBlank(conversation.getSystemMessage())) {\n            chatMessages.add(new SystemMessage(conversation.getSystemMessage()));\n        }\n\n        // 1.2 历史 history message 历史消息\n        List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO);\n        contextMessages.forEach(message -> {\n            chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent()));\n            UserMessage attachmentUserMessage = buildAttachmentUserMessage(message.getAttachmentUrls());\n            if (attachmentUserMessage != null) {\n                chatMessages.add(attachmentUserMessage);\n            }\n            // TODO @芋艿：历史的知识库；历史的搜索，要不要拼接？\n        });\n\n        // 1.3 当前 user message 新发送消息\n        chatMessages.add(new UserMessage(sendReqVO.getContent()));\n\n        // 1.4 知识库，通过 UserMessage 实现\n        if (CollUtil.isNotEmpty(knowledgeSegments)) {\n            String reference = knowledgeSegments.stream()\n                    .map(segment -> \"<Reference>\" + segment.getContent() + \"</Reference>\")\n                    .collect(Collectors.joining(\"\\n\\n\"));\n            chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference)));\n        }\n\n        // 1.5 联网搜索，通过 UserMessage 实现\n        if (webSearchResponse != null && CollUtil.isNotEmpty(webSearchResponse.getLists())) {\n            String webSearch = webSearchResponse.getLists().stream()\n                    .map(page -> {\n                        String summary = StrUtil.isNotEmpty(page.getSummary()) ?\n                                \"\\nSummary: \" + page.getSummary() : \"\";\n                        return \"<WebSearch title=\\\"\" + page.getTitle() + \"\\\" url=\\\"\" + page.getUrl() + \"\\\">\"\n                                + StrUtil.blankToDefault(page.getSummary(), page.getSnippet()) + \"</WebSearch>\";\n                    })\n                    .collect(Collectors.joining(\"\\n\\n\"));\n            chatMessages.add(new UserMessage(String.format(WEB_SEARCH_USER_MESSAGE_TEMPLATE, webSearch)));\n        }\n\n        // 1.6 附件，通过 UserMessage 实现\n        if (CollUtil.isNotEmpty(sendReqVO.getAttachmentUrls())) {\n            UserMessage attachmentUserMessage = buildAttachmentUserMessage(sendReqVO.getAttachmentUrls());\n            if (attachmentUserMessage != null) {\n                chatMessages.add(attachmentUserMessage);\n            }\n        }\n\n        // 2.1 查询 tool 工具\n        List<ToolCallback> toolCallbacks = getToolCallbackListByRoleId(conversation.getRoleId());\n        Map<String,Object> toolContext = CollUtil.isNotEmpty(toolCallbacks) ? AiUtils.buildCommonToolContext()\n                : Map.of();\n        // 2.2 构建 ChatOptions 对象\n        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());\n        ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(),\n                conversation.getTemperature(), conversation.getMaxTokens(),\n                toolCallbacks, toolContext);\n        return new Prompt(chatMessages, chatOptions);\n    }\n\n    private List<ToolCallback> getToolCallbackListByRoleId(Long roleId) {\n        if (roleId == null) {\n            return null;\n        }\n        AiChatRoleDO chatRole = chatRoleService.getChatRole(roleId);\n        if (chatRole == null) {\n            return null;\n        }\n        List<ToolCallback> toolCallbacks = new ArrayList<>();\n        // 1. 通过 toolIds\n        if (CollUtil.isNotEmpty(chatRole.getToolIds())) {\n            Set<String> toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);\n            toolNames.forEach(toolName -> {\n                ToolCallback toolCallback = toolCallbackResolver.resolve(toolName);\n                if (toolCallback != null) {\n                    toolCallbacks.add(toolCallback);\n                }\n            });\n        }\n        // 2. 通过 mcpClients\n        if (CollUtil.isNotEmpty(mcpClients) && CollUtil.isNotEmpty(chatRole.getMcpClientNames())) {\n            chatRole.getMcpClientNames().forEach(mcpClientName -> {\n                // 2.1 标准化名字，参考 McpClientAutoConfiguration 的 connectedClientName 方法\n                String finalMcpClientName = mcpClientCommonProperties.getName() + \" - \" + mcpClientName;\n                // 2.2 匹配对应的 McpSyncClient\n                mcpClients.forEach(mcpClient -> {\n                    if (ObjUtil.notEqual(mcpClient.getClientInfo().name(), finalMcpClientName)) {\n                        return;\n                    }\n                    ToolCallback[] mcpToolCallBacks = new SyncMcpToolCallbackProvider(mcpClient).getToolCallbacks();\n                    CollUtil.addAll(toolCallbacks, mcpToolCallBacks);\n                });\n            });\n        }\n        return toolCallbacks;\n    }\n\n    /**\n     * 从历史消息中，获得倒序的 n 组消息作为消息上下文\n     * <p>\n     * n 组：指的是 user + assistant 形成一组\n     *\n     * @param messages     消息列表\n     * @param conversation 对话\n     * @param sendReqVO    发送请求\n     * @return 消息上下文\n     */\n    private List<AiChatMessageDO> filterContextMessages(List<AiChatMessageDO> messages,\n            AiChatConversationDO conversation,\n            AiChatMessageSendReqVO sendReqVO) {\n        if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) {\n            return Collections.emptyList();\n        }\n        List<AiChatMessageDO> contextMessages = new ArrayList<>(conversation.getMaxContexts() * 2);\n        for (int i = messages.size() - 1; i >= 0; i--) {\n            AiChatMessageDO assistantMessage = CollUtil.get(messages, i);\n            if (assistantMessage == null || assistantMessage.getReplyId() == null) {\n                continue;\n            }\n            AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);\n            if (userMessage == null\n                    || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())\n                    || StrUtil.isEmpty(assistantMessage.getContent())) {\n                continue;\n            }\n            // 由于后续要 reverse 反转，所以先添加 assistantMessage\n            contextMessages.add(assistantMessage);\n            contextMessages.add(userMessage);\n            // 超过最大上下文，结束\n            if (contextMessages.size() >= conversation.getMaxContexts() * 2) {\n                break;\n            }\n        }\n        Collections.reverse(contextMessages);\n        return contextMessages;\n    }\n\n    private UserMessage buildAttachmentUserMessage(List<String> attachmentUrls) {\n        if (CollUtil.isEmpty(attachmentUrls)) {\n            return null;\n        }\n        // 读取文件内容\n        Map<String, String> attachmentContents = Maps.newLinkedHashMapWithExpectedSize(attachmentUrls.size());\n        for (String attachmentUrl : attachmentUrls) {\n            try {\n                String name = FileNameUtil.getName(attachmentUrl);\n                String mineType = FileTypeUtils.getMineType(name);\n                String content;\n                if (FileTypeUtils.isImage(mineType)) {\n                    // 特殊：图片则转为 Base64\n                    byte[] bytes = HttpUtil.downloadBytes(attachmentUrl);\n                    content = Base64.encode(bytes);\n                } else {\n                    content = knowledgeDocumentService.readUrl(attachmentUrl);\n                }\n                if (StrUtil.isNotEmpty(content)) {\n                    attachmentContents.put(name, content);\n                }\n            } catch (Exception e) {\n                log.error(\"[buildAttachmentUserMessage][读取附件({}) 发生异常]\", attachmentUrl, e);\n            }\n        }\n        if (CollUtil.isEmpty(attachmentContents)) {\n            return null;\n        }\n\n        // 拼接 UserMessage 消息\n        String attachment = attachmentContents.entrySet().stream()\n                .map(entry -> \"<Attachment name=\\\"\" + entry.getKey() + \"\\\">\" + entry.getValue() + \"</Attachment>\")\n                .collect(Collectors.joining(\"\\n\\n\"));\n        return new UserMessage(String.format(Attachment_USER_MESSAGE_TEMPLATE, attachment));\n    }\n\n    private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,\n                                              AiModelDO model, Long userId, Long roleId,\n                                              MessageType messageType, String content, Boolean useContext,\n                                              List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,\n                                              List<String> attachmentUrls,\n                                              AiWebSearchResponse webSearchResponse) {\n        AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)\n                .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)\n                .setType(messageType.getValue()).setContent(content).setUseContext(useContext)\n                .setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId))\n                .setAttachmentUrls(attachmentUrls);\n        if (webSearchResponse != null) {\n            message.setWebSearchPages(webSearchResponse.getLists());\n        }\n        message.setCreateTime(LocalDateTime.now());\n        chatMessageMapper.insert(message);\n        return message;\n    }\n\n    @Override\n    public List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId) {\n        return chatMessageMapper.selectListByConversationId(conversationId);\n    }\n\n    @Override\n    public void deleteChatMessage(Long id, Long userId) {\n        // 1. 校验消息存在\n        AiChatMessageDO message = chatMessageMapper.selectById(id);\n        if (message == null || ObjUtil.notEqual(message.getUserId(), userId)) {\n            throw exception(CHAT_MESSAGE_NOT_EXIST);\n        }\n        // 2. 执行删除\n        chatMessageMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteChatMessageByConversationId(Long conversationId, Long userId) {\n        // 1. 校验消息存在\n        List<AiChatMessageDO> messages = chatMessageMapper.selectListByConversationId(conversationId);\n        if (CollUtil.isEmpty(messages) || ObjUtil.notEqual(messages.get(0).getUserId(), userId)) {\n            throw exception(CHAT_MESSAGE_NOT_EXIST);\n        }\n        // 2. 执行删除\n        chatMessageMapper.deleteByIds(convertList(messages, AiChatMessageDO::getId));\n    }\n\n    @Override\n    public void deleteChatMessageByAdmin(Long id) {\n        // 1. 校验消息存在\n        AiChatMessageDO message = chatMessageMapper.selectById(id);\n        if (message == null) {\n            throw exception(CHAT_MESSAGE_NOT_EXIST);\n        }\n        // 2. 执行删除\n        chatMessageMapper.deleteById(id);\n    }\n\n    @Override\n    public Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds) {\n        return chatMessageMapper.selectCountMapByConversationId(conversationIds);\n    }\n\n    @Override\n    public PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {\n        return chatMessageMapper.selectPage(pageReqVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.image;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.*;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * AI 绘图 Service 接口\n *\n * @author fansili\n */\npublic interface AiImageService {\n\n    /**\n     * 获取【我的】绘图分页\n     *\n     * @param userId 用户编号\n     * @param pageReqVO 分页条件\n     * @return 绘图分页\n     */\n    PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO);\n\n    /**\n     * 获取公开的绘图分页\n     *\n     * @param pageReqVO 分页条件\n     * @return 绘图分页\n     */\n    PageResult<AiImageDO> getImagePagePublic(AiImagePublicPageReqVO pageReqVO);\n\n    /**\n     * 获得绘图记录\n     *\n     * @param id 绘图编号\n     * @return 绘图记录\n     */\n    AiImageDO getImage(Long id);\n\n    /**\n     * 获得绘图列表\n     *\n     * @param ids 绘图编号数组\n     * @return 绘图记录列表\n     */\n    List<AiImageDO> getImageList(List<Long> ids);\n\n    /**\n     * 绘制图片\n     *\n     * @param userId 用户编号\n     * @param drawReqVO 绘制请求\n     * @return 绘画编号\n     */\n    Long drawImage(Long userId, AiImageDrawReqVO drawReqVO);\n\n    /**\n     * 删除【我的】绘画记录\n     *\n     * @param id 绘画编号\n     * @param userId 用户编号\n     */\n    void deleteImageMy(Long id, Long userId);\n\n    /**\n     * 获得绘画分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 绘画分页\n     */\n    PageResult<AiImageDO> getImagePage(AiImagePageReqVO pageReqVO);\n\n    /**\n     * 更新绘画\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateImage(@Valid AiImageUpdateReqVO updateReqVO);\n\n    /**\n     * 删除绘画\n     *\n     * @param id 编号\n     */\n    void deleteImage(Long id);\n\n    // ================ midjourney 专属 ================\n\n    /**\n     * 【Midjourney】生成图片\n     *\n     * @param userId 用户编号\n     * @param reqVO 绘制请求\n     * @return 绘画编号\n     */\n    Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO);\n\n    /**\n     * 【Midjourney】同步图片进展\n     *\n     * @return 同步成功数量\n     */\n    Integer midjourneySync();\n\n    /**\n     * 【Midjourney】通知图片进展\n     *\n     * @param notify 通知\n     */\n    void midjourneyNotify(MidjourneyApi.Notify notify);\n\n    /**\n     * 【Midjourney】Action 操作(放大、缩小、U1、U2...)\n     *\n     * @param userId 用户编号\n     * @param reqVO 绘制请求\n     * @return 绘画编号\n     */\n    Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.image;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.codec.Base64;\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.hutool.http.HttpUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageUpdateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;\nimport cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageOptions;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport cn.iocoder.yudao.module.infra.api.file.FileApi;\nimport com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springaicommunity.qianfan.QianFanImageOptions;\nimport org.springframework.ai.image.ImageModel;\nimport org.springframework.ai.image.ImageOptions;\nimport org.springframework.ai.image.ImagePrompt;\nimport org.springframework.ai.image.ImageResponse;\nimport org.springframework.ai.openai.OpenAiImageOptions;\nimport org.springframework.ai.stabilityai.api.StabilityAiImageOptions;\nimport org.springframework.ai.zhipuai.ZhiPuAiImageOptions;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.time.LocalDateTime;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;\n\n/**\n * AI 绘画 Service 实现类\n *\n * @author fansili\n */\n@Service\n@Slf4j\npublic class AiImageServiceImpl implements AiImageService {\n\n    @Resource\n    private AiModelService modelService;\n\n    @Resource\n    private AiImageMapper imageMapper;\n\n    @Resource\n    private FileApi fileApi;\n\n    @Override\n    public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) {\n        return imageMapper.selectPageMy(userId, pageReqVO);\n    }\n\n    @Override\n    public PageResult<AiImageDO> getImagePagePublic(AiImagePublicPageReqVO pageReqVO) {\n        return imageMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public AiImageDO getImage(Long id) {\n        return imageMapper.selectById(id);\n    }\n\n    @Override\n    public List<AiImageDO> getImageList(List<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return imageMapper.selectByIds(ids);\n    }\n\n    @Override\n    public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) {\n        // 1. 校验模型\n        AiModelDO model = modelService.validateModel(drawReqVO.getModelId());\n\n        // 2. 保存数据库\n        AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId)\n                .setPlatform(model.getPlatform()).setModelId(model.getId()).setModel(model.getModel())\n                .setPublicStatus(false).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());\n        imageMapper.insert(image);\n\n        // 3. 异步绘制，后续前端通过返回的 id 进行轮询结果\n        getSelf().executeDrawImage(image, drawReqVO, model);\n        return image.getId();\n    }\n\n    @Async\n    public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) {\n        try {\n            // 1.1 构建请求\n            ImageOptions request = buildImageOptions(reqVO, model);\n            // 1.2 执行请求\n            ImageModel imageModel = modelService.getImageModel(model.getId());\n            ImageResponse response = imageModel.call(new ImagePrompt(reqVO.getPrompt(), request));\n            if (response.getResult() == null) {\n                throw new IllegalArgumentException(\"生成结果为空\");\n            }\n\n            // 2. 上传到文件服务\n            String b64Json = response.getResult().getOutput().getB64Json();\n            byte[] fileContent = StrUtil.isNotEmpty(b64Json) ? Base64.decode(b64Json)\n                    : HttpUtil.downloadBytes(response.getResult().getOutput().getUrl());\n            String filePath = fileApi.createFile(fileContent);\n\n            // 3. 更新数据库\n            imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus())\n                    .setPicUrl(filePath).setFinishTime(LocalDateTime.now()));\n        } catch (Exception ex) {\n            log.error(\"[executeDrawImage][image({}) 生成异常]\", image, ex);\n            imageMapper.updateById(new AiImageDO().setId(image.getId())\n                    .setStatus(AiImageStatusEnum.FAIL.getStatus())\n                    .setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now()));\n        }\n    }\n\n    private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) {\n        if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {\n            // https://platform.openai.com/docs/api-reference/images/create\n            return OpenAiImageOptions.builder().model(model.getModel())\n                    .height(draw.getHeight()).width(draw.getWidth())\n                    .style(MapUtil.getStr(draw.getOptions(), \"style\")) // 风格\n                    .responseFormat(\"b64_json\")\n                    .build();\n        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) {\n            // https://docs.siliconflow.cn/cn/api-reference/images/images-generations\n            return SiliconFlowImageOptions.builder().model(model.getModel())\n                    .height(draw.getHeight()).width(draw.getWidth())\n                    .build();\n        }  else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {\n            // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage\n            // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage\n            return StabilityAiImageOptions.builder().model(model.getModel())\n                    .height(draw.getHeight()).width(draw.getWidth())\n                    .seed(Long.valueOf(draw.getOptions().get(\"seed\")))\n                    .cfgScale(Float.valueOf(draw.getOptions().get(\"scale\")))\n                    .steps(Integer.valueOf(draw.getOptions().get(\"steps\")))\n                    .sampler(String.valueOf(draw.getOptions().get(\"sampler\")))\n                    .stylePreset(String.valueOf(draw.getOptions().get(\"stylePreset\")))\n                    .clipGuidancePreset(String.valueOf(draw.getOptions().get(\"clipGuidancePreset\")))\n                    .build();\n        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {\n            return DashScopeImageOptions.builder()\n                    .withModel(model.getModel()).withN(1)\n                    .withHeight(draw.getHeight()).withWidth(draw.getWidth())\n                    .build();\n        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {\n            return QianFanImageOptions.builder()\n                    .model(model.getModel()).N(1)\n                    .height(draw.getHeight()).width(draw.getWidth())\n                    .build();\n        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {\n            return ZhiPuAiImageOptions.builder()\n                    .model(model.getModel())\n                    .build();\n        }\n        throw new IllegalArgumentException(\"不支持的 AI 平台：\" + model.getPlatform());\n    }\n\n    @Override\n    public void deleteImageMy(Long id, Long userId) {\n        // 1. 校验是否存在\n        AiImageDO image = validateImageExists(id);\n        if (ObjUtil.notEqual(image.getUserId(), userId)) {\n            throw exception(IMAGE_NOT_EXISTS);\n        }\n        // 2. 删除记录\n        imageMapper.deleteById(id);\n    }\n\n    @Override\n    public PageResult<AiImageDO> getImagePage(AiImagePageReqVO pageReqVO) {\n        return imageMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public void updateImage(AiImageUpdateReqVO updateReqVO) {\n        // 1. 校验存在\n        validateImageExists(updateReqVO.getId());\n        // 2. 更新发布状态\n        imageMapper.updateById(BeanUtils.toBean(updateReqVO, AiImageDO.class));\n    }\n\n    @Override\n    public void deleteImage(Long id) {\n        // 1. 校验存在\n        validateImageExists(id);\n        // 2. 删除\n        imageMapper.deleteById(id);\n    }\n\n    private AiImageDO validateImageExists(Long id) {\n        AiImageDO image = imageMapper.selectById(id);\n        if (image == null) {\n            throw exception(IMAGE_NOT_EXISTS);\n        }\n        return image;\n    }\n\n    // ================ midjourney 专属 ================\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO drawReqVO) {\n        // 1. 校验模型\n        AiModelDO model = modelService.validateModel(drawReqVO.getModelId());\n        Assert.equals(model.getPlatform(), AiPlatformEnum.MIDJOURNEY.getPlatform(), \"平台不匹配\");\n        MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(model.getId());\n\n        // 2. 保存数据库\n        AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)\n                .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())\n                .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()).setModelId(model.getId()).setModel(model.getName());\n        imageMapper.insert(image);\n\n        // 3. 调用 Midjourney Proxy 提交任务\n        List<String> base64Array = StrUtil.isBlank(drawReqVO.getReferImageUrl()) ? null :\n                Collections.singletonList(\"data:image/jpeg;base64,\".concat(Base64.encode(HttpUtil.downloadBytes(drawReqVO.getReferImageUrl()))));\n        MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(\n                base64Array, drawReqVO.getPrompt(),null,\n                MidjourneyApi.ImagineRequest.buildState(drawReqVO.getWidth(),\n                        drawReqVO.getHeight(), drawReqVO.getVersion(), model.getModel()));\n        MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest);\n\n        // 4.1 情况一【失败】：抛出业务异常\n        if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) {\n            String description = imagineResponse.description().contains(\"quota_not_enough\") ?\n                    \"账户余额不足\" : imagineResponse.description();\n            throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);\n        }\n\n        // 4.2 情况二【成功】：更新 taskId 和参数\n        imageMapper.updateById(new AiImageDO().setId(image.getId())\n                .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(drawReqVO)));\n        return image.getId();\n    }\n\n    @Override\n    public Integer midjourneySync() {\n        // 1.1 获取 Midjourney 平台，状态在 “进行中” 的 image\n        List<AiImageDO> images = imageMapper.selectListByStatusAndPlatform(\n                AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform());\n        if (CollUtil.isEmpty(images)) {\n            return 0;\n        }\n        // 1.2 调用 Midjourney Proxy 获取任务进展\n        MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(images.get(0).getModelId());\n        List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(images, AiImageDO::getTaskId));\n        Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id);\n\n        // 2. 逐个处理，更新进展\n        int count = 0;\n        for (AiImageDO image : images) {\n            MidjourneyApi.Notify notify = taskMap.get(image.getTaskId());\n            if (notify == null) {\n                log.error(\"[midjourneySync][image({}) 查询不到进展]\", image);\n                continue;\n            }\n            count++;\n            updateMidjourneyStatus(image, notify);\n        }\n        return count;\n    }\n\n    @Override\n    public void midjourneyNotify(MidjourneyApi.Notify notify) {\n        // 1. 校验 image 存在\n        AiImageDO image = imageMapper.selectByTaskId(notify.id());\n        if (image == null) {\n            log.warn(\"[midjourneyNotify][回调任务({}) 不存在]\", notify.id());\n            return;\n        }\n        // 2. 更新状态\n        updateMidjourneyStatus(image, notify);\n    }\n\n    private void updateMidjourneyStatus(AiImageDO image, MidjourneyApi.Notify notify) {\n        // 1. 转换状态\n        Integer status = null;\n        LocalDateTime finishTime = null;\n        if (StrUtil.isNotBlank(notify.status())) {\n            MidjourneyApi.TaskStatusEnum taskStatusEnum = MidjourneyApi.TaskStatusEnum.valueOf(notify.status());\n            if (MidjourneyApi.TaskStatusEnum.SUCCESS == taskStatusEnum) {\n                status = AiImageStatusEnum.SUCCESS.getStatus();\n                finishTime = LocalDateTime.now();\n            } else if (MidjourneyApi.TaskStatusEnum.FAILURE == taskStatusEnum) {\n                status = AiImageStatusEnum.FAIL.getStatus();\n                finishTime = LocalDateTime.now();\n            }\n        }\n\n        // 2. 上传图片\n        String picUrl = null;\n        if (StrUtil.isNotBlank(notify.imageUrl())) {\n            try {\n                picUrl = fileApi.createFile(HttpUtil.downloadBytes(notify.imageUrl()));\n            } catch (Exception e) {\n                picUrl = notify.imageUrl();\n                log.warn(\"[updateMidjourneyStatus][图片({}) 地址({}) 上传失败]\", image.getId(), notify.imageUrl(), e);\n            }\n        }\n\n        // 3. 更新 image 状态\n        imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(status)\n                .setPicUrl(picUrl).setButtons(notify.buttons()).setErrorMessage(notify.failReason())\n                .setFinishTime(finishTime));\n    }\n\n    @Override\n    public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) {\n        // 1.1 检查 image\n        AiImageDO image = validateImageExists(reqVO.getId());\n        if (ObjUtil.notEqual(userId, image.getUserId())) {\n            throw exception(IMAGE_NOT_EXISTS);\n        }\n        MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(image.getModelId());\n        // 1.2 检查 customId\n        MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(),\n                buttonX -> buttonX.customId().equals(reqVO.getCustomId()));\n        if (button == null) {\n            throw exception(IMAGE_CUSTOM_ID_NOT_EXISTS);\n        }\n\n        // 2. 调用 Midjourney Proxy 提交任务\n        MidjourneyApi.SubmitResponse actionResponse = midjourneyApi.action(\n                new MidjourneyApi.ActionRequest(button.customId(), image.getTaskId(), null));\n        if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(actionResponse.code())) {\n            String description = actionResponse.description().contains(\"quota_not_enough\") ?\n                    \"账户余额不足\" : actionResponse.description();\n            throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);\n        }\n\n        // 3. 新增 image 记录\n        AiImageDO newImage = new AiImageDO().setUserId(image.getUserId()).setPublicStatus(false).setPrompt(image.getPrompt())\n                .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())\n                .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform())\n                .setModel(image.getModel()).setWidth(image.getWidth()).setHeight(image.getHeight())\n                .setOptions(image.getOptions()).setTaskId(actionResponse.result());\n        imageMapper.insert(newImage);\n        return newImage.getId();\n    }\n\n    /**\n     * 获得自身的代理对象，解决 AOP 生效问题\n     *\n     * @return 自己\n     */\n    private AiImageServiceImpl getSelf() {\n        return SpringUtil.getBean(getClass());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * AI 知识库文档 Service 接口\n *\n * @author xiaoxin\n */\npublic interface AiKnowledgeDocumentService {\n\n    /**\n     * 创建文档（单个）\n     *\n     * @param createReqVO 文档创建 Request VO\n     * @return 文档编号\n     */\n    Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO);\n\n    /**\n     * 创建文档（多个）\n     *\n     * @param createListReqVO 批量创建 Request VO\n     * @return 文档编号列表\n     */\n    List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO);\n\n    /**\n     * 获取文档分页\n     *\n     * @param pageReqVO 分页参数\n     * @return 文档分页\n     */\n    PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO);\n\n    /**\n     * 获取文档详情\n     *\n     * @param id 文档编号\n     * @return 文档详情\n     */\n    AiKnowledgeDocumentDO getKnowledgeDocument(Long id);\n\n    /**\n     * 更新文档\n     *\n     * @param reqVO 更新信息\n     */\n    void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO);\n\n    /**\n     * 更新文档状态\n     *\n     * @param reqVO 更新状态信息\n     */\n    void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO);\n\n    /**\n     * 删除文档\n     *\n     * @param id 文档编号\n     */\n    void deleteKnowledgeDocument(Long id);\n\n    /**\n     * 根据知识库编号，批量删除文档\n     *\n     * @param knowledgeId 知识库编号\n     */\n    void deleteKnowledgeDocumentByKnowledgeId(Long knowledgeId);\n\n    /**\n     * 校验文档是否存在\n     *\n     * @param id 文档编号\n     * @return 文档信息\n     */\n    AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id);\n\n    /**\n     * 读取 URL 内容\n     *\n     * @param url URL\n     * @return 内容\n     */\n    String readUrl(String url);\n\n    /**\n     * 获取文档列表\n     *\n     * @param ids 文档编号列表\n     * @return 文档列表\n     */\n    List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids);\n\n    /**\n     * 根据知识库编号获取文档列表\n     *\n     * @param knowledgeId 知识库编号\n     * @return 文档列表\n     */\n    List<AiKnowledgeDocumentDO> getKnowledgeDocumentListByKnowledgeId(Long knowledgeId);\n\n    /**\n     * 获取文档 Map\n     *\n     * @param ids 文档编号列表\n     * @return 文档 Map\n     */\n    default Map<Long, AiKnowledgeDocumentDO> getKnowledgeDocumentMap(Collection<Long> ids) {\n        return convertMap(getKnowledgeDocumentList(ids), AiKnowledgeDocumentDO::getId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.http.HttpUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.document.Document;\nimport org.springframework.ai.reader.tika.TikaDocumentReader;\nimport org.springframework.ai.tokenizer.TokenCountEstimator;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.core.io.ByteArrayResource;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;\n\n/**\n * AI 知识库文档 Service 实现类\n *\n * @author xiaoxin\n */\n@Service\n@Slf4j\npublic class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService {\n\n    @Resource\n    private AiKnowledgeDocumentMapper knowledgeDocumentMapper;\n\n    @Resource\n    private TokenCountEstimator tokenCountEstimator;\n\n    @Resource\n    private AiKnowledgeSegmentService knowledgeSegmentService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private AiKnowledgeService knowledgeService;\n\n    @Override\n    public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) {\n        // 1. 校验参数\n        knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());\n\n        // 2. 下载文档\n        String content = readUrl(createReqVO.getUrl());\n\n        // 3. 文档记录入库\n        AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class)\n                .setContent(content).setContentLength(content.length()).setTokens(tokenCountEstimator.estimate(content))\n                .setStatus(CommonStatusEnum.ENABLE.getStatus());\n        knowledgeDocumentMapper.insert(documentDO);\n\n        // 4. 文档切片入库（异步）\n        knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), content);\n        return documentDO.getId();\n    }\n\n    @Override\n    public List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO) {\n        // 1. 校验参数\n        knowledgeService.validateKnowledgeExists(createListReqVO.getKnowledgeId());\n\n        // 2. 下载文档\n        List<String> contents = convertList(createListReqVO.getList(), document -> readUrl(document.getUrl()));\n\n        // 3. 文档记录入库\n        List<AiKnowledgeDocumentDO> documentDOs = new ArrayList<>(createListReqVO.getList().size());\n        for (int i = 0; i < createListReqVO.getList().size(); i++) {\n            AiKnowledgeDocumentCreateListReqVO.Document documentVO = createListReqVO.getList().get(i);\n            String content = contents.get(i);\n            documentDOs.add(BeanUtils.toBean(documentVO, AiKnowledgeDocumentDO.class)\n                    .setKnowledgeId(createListReqVO.getKnowledgeId())\n                    .setContent(content).setContentLength(content.length())\n                    .setTokens(tokenCountEstimator.estimate(content))\n                    .setSegmentMaxTokens(createListReqVO.getSegmentMaxTokens())\n                    .setStatus(CommonStatusEnum.ENABLE.getStatus()));\n        }\n        knowledgeDocumentMapper.insertBatch(documentDOs);\n\n        // 4. 批量创建文档切片（异步）\n        documentDOs.forEach(documentDO -> knowledgeSegmentService\n                .createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), documentDO.getContent()));\n        return convertList(documentDOs, AiKnowledgeDocumentDO::getId);\n    }\n\n    @Override\n    public PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) {\n        return knowledgeDocumentMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public AiKnowledgeDocumentDO getKnowledgeDocument(Long id) {\n        return knowledgeDocumentMapper.selectById(id);\n    }\n\n    @Override\n    public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) {\n        // 1. 校验文档是否存在\n        AiKnowledgeDocumentDO oldDocument = validateKnowledgeDocumentExists(reqVO.getId());\n\n        // 2. 更新文档\n        AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class);\n        knowledgeDocumentMapper.updateById(document);\n\n        // 3. 如果处于开启状态，并且最大 tokens 发生变化，则 segment 需要重新索引\n        if (CommonStatusEnum.isEnable(oldDocument.getStatus())\n                && reqVO.getSegmentMaxTokens() != null\n                && ObjUtil.notEqual(reqVO.getSegmentMaxTokens(), oldDocument.getSegmentMaxTokens())) {\n            // 删除旧的文档切片\n            knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());\n            // 重新创建文档切片\n            knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), oldDocument.getContent());\n        }\n    }\n\n    @Override\n    public void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO) {\n        // 1. 校验存在\n        AiKnowledgeDocumentDO document = validateKnowledgeDocumentExists(reqVO.getId());\n\n        // 2. 更新状态\n        knowledgeDocumentMapper.updateById(new AiKnowledgeDocumentDO()\n                .setId(reqVO.getId()).setStatus(reqVO.getStatus()));\n\n        // 3. 处理文档切片\n        if (CommonStatusEnum.isEnable(reqVO.getStatus())) {\n            knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), document.getContent());\n        } else {\n            knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteKnowledgeDocument(Long id) {\n        // 1. 校验存在\n        validateKnowledgeDocumentExists(id);\n\n        // 2. 删除\n        knowledgeDocumentMapper.deleteById(id);\n\n        // 3. 删除对应的段落\n        knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(id);\n    }\n\n    @Override\n    public AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) {\n        AiKnowledgeDocumentDO knowledgeDocument = knowledgeDocumentMapper.selectById(id);\n        if (knowledgeDocument == null) {\n            throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS);\n        }\n        return knowledgeDocument;\n    }\n\n    @Override\n    public String readUrl(String url) {\n        // 下载文件\n        ByteArrayResource resource;\n        try {\n            byte[] bytes = HttpUtil.downloadBytes(url);\n            if (bytes.length == 0) {\n                throw exception(KNOWLEDGE_DOCUMENT_FILE_EMPTY);\n            }\n            resource = new ByteArrayResource(bytes);\n        } catch (Exception e) {\n            log.error(\"[readUrl][url({}) 读取失败]\", url, e);\n            throw exception(KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL);\n        }\n\n        // 读取文件\n        TikaDocumentReader loader = new TikaDocumentReader(resource);\n        List<Document> documents = loader.get();\n        Document document = CollUtil.getFirst(documents);\n        if (document == null || StrUtil.isEmpty(document.getText())) {\n            throw exception(KNOWLEDGE_DOCUMENT_FILE_READ_FAIL);\n        }\n        return document.getText();\n    }\n\n    @Override\n    public List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return knowledgeDocumentMapper.selectByIds(ids);\n    }\n\n    @Override\n    public List<AiKnowledgeDocumentDO> getKnowledgeDocumentListByKnowledgeId(Long knowledgeId) {\n        return knowledgeDocumentMapper.selectListByKnowledgeId(knowledgeId);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteKnowledgeDocumentByKnowledgeId(Long knowledgeId) {\n        // 1. 获取该知识库下的所有文档\n        List<AiKnowledgeDocumentDO> documents = knowledgeDocumentMapper.selectListByKnowledgeId(knowledgeId);\n        if (CollUtil.isEmpty(documents)) {\n            return;\n        }\n\n        // 2. 逐个删除文档及其对应的段落\n        for (AiKnowledgeDocumentDO document : documents) {\n            deleteKnowledgeDocument(document.getId());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;\nimport cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;\nimport cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;\nimport org.springframework.scheduling.annotation.Async;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * AI 知识库段落 Service 接口\n *\n * @author xiaoxin\n */\npublic interface AiKnowledgeSegmentService {\n\n    /**\n     * 获取知识库段落详情\n     *\n     * @param id 段落编号\n     * @return 段落详情\n     */\n    AiKnowledgeSegmentDO getKnowledgeSegment(Long id);\n\n    /**\n     * 获取知识库段落列表\n     *\n     * @param ids 段落编号列表\n     * @return 段落列表\n     */\n    List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids);\n\n    /**\n     * 获取知识库段落 Map\n     *\n     * @param ids 段落编号列表\n     * @return 段落 Map\n     */\n    default Map<Long, AiKnowledgeSegmentDO> getKnowledgeSegmentMap(Collection<Long> ids) {\n        return convertMap(getKnowledgeSegmentList(ids), AiKnowledgeSegmentDO::getId);\n    }\n\n    /**\n     * 获取段落分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 文档分页\n     */\n    PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO);\n\n    /**\n     * 基于 content 内容，切片创建多个段落\n     *\n     * @param documentId 知识库文档编号\n     * @param content    文档内容\n     */\n    void createKnowledgeSegmentBySplitContent(Long documentId, String content);\n\n    /**\n     * 【异步】基于 content 内容，切片创建多个段落\n     *\n     * @param documentId 知识库文档编号\n     * @param content    文档内容\n     */\n    @Async\n    default void createKnowledgeSegmentBySplitContentAsync(Long documentId, String content) {\n        createKnowledgeSegmentBySplitContent(documentId, content);\n    }\n\n    /**\n     * 创建知识库段落\n     *\n     * @param createReqVO 创建信息\n     * @return 段落编号\n     */\n    Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO);\n\n    /**\n     * 更新段落的内容\n     *\n     * @param reqVO 更新内容\n     */\n    void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO);\n\n    /**\n     * 更新段落的状态\n     *\n     * @param reqVO 更新内容\n     */\n    void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);\n\n    /**\n     * 重新索引知识库下的所有文档段落\n     *\n     * @param knowledgeId 知识库编号\n     */\n    void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId);\n\n    /**\n     * 【异步】重新索引知识库下的所有文档段落\n     *\n     * @param knowledgeId 知识库编号\n     */\n    @Async\n    default void reindexByKnowledgeIdAsync(Long knowledgeId) {\n        reindexKnowledgeSegmentByKnowledgeId(knowledgeId);\n    }\n\n    /**\n     * 根据文档编号删除段落\n     *\n     * @param documentId 文档编号\n     */\n    void deleteKnowledgeSegmentByDocumentId(Long documentId);\n\n    /**\n     * 搜索知识库段落，并返回结果\n     *\n     * @param reqBO 搜索请求信息\n     * @return 搜索结果段落列表\n     */\n    List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO);\n\n    /**\n     * 根据 URL 内容，切片创建多个段落\n     *\n     * @param url              URL 地址\n     * @param segmentMaxTokens 段落最大 Token 数\n     * @return 切片后的段落列表\n     */\n    List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens);\n\n    /**\n     * 获取文档处理进度（多个）\n     *\n     * @param documentIds 文档编号列表\n     * @return 文档处理列表\n     */\n    List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;\nimport cn.iocoder.yudao.module.ai.enums.AiDocumentSplitStrategyEnum;\nimport cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;\nimport cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;\nimport cn.iocoder.yudao.module.ai.service.knowledge.splitter.MarkdownQaSplitter;\nimport cn.iocoder.yudao.module.ai.service.knowledge.splitter.SemanticTextSplitter;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankOptions;\nimport com.alibaba.cloud.ai.model.RerankModel;\nimport com.alibaba.cloud.ai.model.RerankRequest;\nimport com.alibaba.cloud.ai.model.RerankResponse;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.document.Document;\nimport org.springframework.ai.tokenizer.TokenCountEstimator;\nimport org.springframework.ai.transformer.splitter.TextSplitter;\nimport org.springframework.ai.transformer.splitter.TokenTextSplitter;\nimport org.springframework.ai.vectorstore.SearchRequest;\nimport org.springframework.ai.vectorstore.VectorStore;\nimport org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\n\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;\nimport static org.springframework.ai.vectorstore.SearchRequest.SIMILARITY_THRESHOLD_ACCEPT_ALL;\n\n/**\n * AI 知识库分片 Service 实现类\n *\n * @author xiaoxin\n */\n@Service\n@Slf4j\npublic class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService {\n\n    private static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = \"knowledgeId\";\n    private static final String VECTOR_STORE_METADATA_DOCUMENT_ID = \"documentId\";\n    private static final String VECTOR_STORE_METADATA_SEGMENT_ID = \"segmentId\";\n\n    private static final Map<String, Class<?>> VECTOR_STORE_METADATA_TYPES = Map.of(\n            VECTOR_STORE_METADATA_KNOWLEDGE_ID, String.class,\n            VECTOR_STORE_METADATA_DOCUMENT_ID, String.class,\n            VECTOR_STORE_METADATA_SEGMENT_ID, String.class);\n\n    /**\n     * Rerank 在向量检索时，检索数量 * 该系数，目的是为了提升 Rerank 的效果\n     */\n    private static final Integer RERANK_RETRIEVAL_FACTOR = 4;\n\n    @Resource\n    private AiKnowledgeSegmentMapper segmentMapper;\n\n    @Resource\n    private AiKnowledgeService knowledgeService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private AiKnowledgeDocumentService knowledgeDocumentService;\n    @Resource\n    private AiModelService modelService;\n\n    @Resource\n    private TokenCountEstimator tokenCountEstimator;\n\n    @Autowired(required = false) // 由于 spring.ai.model.rerank 配置项，可以关闭 RerankModel 的功能，所以这里只能不强制注入\n    private RerankModel rerankModel;\n\n    @Override\n    public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) {\n        return segmentMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public void createKnowledgeSegmentBySplitContent(Long documentId, String content) {\n        // 1. 校验\n        AiKnowledgeDocumentDO documentDO = knowledgeDocumentService.validateKnowledgeDocumentExists(documentId);\n        AiKnowledgeDO knowledgeDO = knowledgeService.validateKnowledgeExists(documentDO.getKnowledgeId());\n        VectorStore vectorStore = getVectorStoreById(knowledgeDO);\n\n        // 2. 文档切片（使用自动检测策略）\n        List<Document> documentSegments = splitContentByStrategy(content, documentDO.getSegmentMaxTokens(),\n                AiDocumentSplitStrategyEnum.AUTO, documentDO.getUrl());\n\n        // 3.1 存储切片\n        List<AiKnowledgeSegmentDO> segmentDOs = convertList(documentSegments, segment -> {\n            if (StrUtil.isEmpty(segment.getText())) {\n                return null;\n            }\n            return new AiKnowledgeSegmentDO().setKnowledgeId(documentDO.getKnowledgeId()).setDocumentId(documentId)\n                    .setContent(segment.getText()).setContentLength(segment.getText().length())\n                    .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)\n                    .setTokens(tokenCountEstimator.estimate(segment.getText()))\n                    .setStatus(CommonStatusEnum.ENABLE.getStatus());\n        });\n        segmentMapper.insertBatch(segmentDOs);\n        // 3.2 切片向量化\n        for (int i = 0; i < documentSegments.size(); i++) {\n            Document segment = documentSegments.get(i);\n            AiKnowledgeSegmentDO segmentDO = segmentDOs.get(i);\n            writeVectorStore(vectorStore, segmentDO, segment);\n        }\n    }\n\n    @Override\n    public void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO) {\n        // 1. 校验\n        AiKnowledgeSegmentDO oldSegment = validateKnowledgeSegmentExists(reqVO.getId());\n\n        // 2. 删除向量\n        VectorStore vectorStore = getVectorStoreById(oldSegment.getKnowledgeId());\n        deleteVectorStore(vectorStore, oldSegment);\n\n        // 3.1 更新切片\n        AiKnowledgeSegmentDO newSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);\n        segmentMapper.updateById(newSegment);\n        // 3.2 重新向量化，必须开启状态\n        if (CommonStatusEnum.isEnable(oldSegment.getStatus())) {\n            newSegment.setKnowledgeId(oldSegment.getKnowledgeId()).setDocumentId(oldSegment.getDocumentId());\n            writeVectorStore(vectorStore, newSegment, new Document(newSegment.getContent()));\n        }\n    }\n\n    @Override\n    public void deleteKnowledgeSegmentByDocumentId(Long documentId) {\n        // 1. 查询需要删除的段落\n        List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByDocumentId(documentId);\n        if (CollUtil.isEmpty(segments)) {\n            return;\n        }\n\n        // 2. 批量删除段落记录\n        segmentMapper.deleteByIds(convertList(segments, AiKnowledgeSegmentDO::getId));\n\n        // 3. 删除向量存储中的段落\n        VectorStore vectorStore = getVectorStoreById(segments.get(0).getKnowledgeId());\n        vectorStore.delete(convertList(segments, AiKnowledgeSegmentDO::getVectorId));\n    }\n\n    @Override\n    public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) {\n        // 1. 校验\n        AiKnowledgeSegmentDO segment = validateKnowledgeSegmentExists(reqVO.getId());\n\n        // 2. 获取知识库向量实例\n        VectorStore vectorStore = getVectorStoreById(segment.getKnowledgeId());\n\n        // 3. 更新状态\n        segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(reqVO.getId()).setStatus(reqVO.getStatus()));\n\n        // 4. 更新向量\n        if (CommonStatusEnum.isEnable(reqVO.getStatus())) {\n            writeVectorStore(vectorStore, segment, new Document(segment.getContent()));\n        } else {\n            deleteVectorStore(vectorStore, segment);\n        }\n    }\n\n    @Override\n    public void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId) {\n        // 1.1 校验知识库存在\n        AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);\n        // 1.2 获取知识库向量实例\n        VectorStore vectorStore = getVectorStoreById(knowledge);\n\n        // 2.1 查询知识库下的所有启用状态的段落\n        List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByKnowledgeIdAndStatus(\n                knowledgeId, CommonStatusEnum.ENABLE.getStatus());\n        if (CollUtil.isEmpty(segments)) {\n            return;\n        }\n        // 2.2 遍历所有段落，重新索引\n        for (AiKnowledgeSegmentDO segment : segments) {\n            // 删除旧的向量\n            deleteVectorStore(vectorStore, segment);\n            // 重新创建向量\n            writeVectorStore(vectorStore, segment, new Document(segment.getContent()));\n        }\n        log.info(\"[reindexKnowledgeSegmentByKnowledgeId][知识库({}) 重新索引完成，共处理 {} 个段落]\",\n                knowledgeId, segments.size());\n    }\n\n    private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) {\n        // 1. 向量存储\n        // 为什么要 toString 呢？因为部分 VectorStore 实现，不支持 Long 类型，例如说 QdrantVectorStore\n        segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId().toString());\n        segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId().toString());\n        segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId().toString());\n        vectorStore.add(List.of(segment));\n\n        // 2. 更新向量 ID\n        segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId()).setVectorId(segment.getId()));\n    }\n\n    private void deleteVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO) {\n        // 1. 更新向量 ID\n        if (StrUtil.isEmpty(segmentDO.getVectorId())) {\n            return;\n        }\n        segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId())\n                .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY));\n\n        // 2. 删除向量\n        vectorStore.delete(List.of(segmentDO.getVectorId()));\n    }\n\n    @Override\n    public List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO) {\n        // 1. 校验\n        AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId());\n\n        // 2. 检索\n        List<Document> documents = searchDocument(knowledge, reqBO);\n        if (CollUtil.isEmpty(documents)) {\n            return ListUtil.empty();\n        }\n\n        // 3.1 段落召回\n        List<AiKnowledgeSegmentDO> segments = segmentMapper\n                .selectListByVectorIds(convertList(documents, Document::getId));\n        if (CollUtil.isEmpty(segments)) {\n            return ListUtil.empty();\n        }\n        // 3.2 增加召回次数\n        segmentMapper.updateRetrievalCountIncrByIds(convertList(segments, AiKnowledgeSegmentDO::getId));\n\n        // 4. 构建结果\n        List<AiKnowledgeSegmentSearchRespBO> result = convertList(segments, segment -> {\n            Document document = CollUtil.findOne(documents, // 找到对应的文档\n                    doc -> Objects.equals(doc.getId(), segment.getVectorId()));\n            if (document == null) {\n                return null;\n            }\n            return BeanUtils.toBean(segment, AiKnowledgeSegmentSearchRespBO.class)\n                    .setScore(document.getScore());\n        });\n        result.sort((o1, o2) -> Double.compare(o2.getScore(), o1.getScore())); // 按照分数降序排序\n        return result;\n    }\n\n    /**\n     * 基于 Embedding + Rerank Model，检索知识库中的文档\n     *\n     * @param knowledge 知识库\n     * @param reqBO 检索请求\n     * @return 文档列表\n     */\n    private List<Document> searchDocument(AiKnowledgeDO knowledge, AiKnowledgeSegmentSearchReqBO reqBO) {\n        VectorStore vectorStore = getVectorStoreById(knowledge);\n        Integer topK = ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK());\n        Double similarityThreshold = ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold());\n\n        // 1. 向量检索\n        int searchTopK = rerankModel != null ? topK * RERANK_RETRIEVAL_FACTOR : topK;\n        double searchSimilarityThreshold = rerankModel != null ? SIMILARITY_THRESHOLD_ACCEPT_ALL : similarityThreshold;\n        SearchRequest.Builder searchRequestBuilder = SearchRequest.builder()\n                .query(reqBO.getContent())\n                .topK(searchTopK).similarityThreshold(searchSimilarityThreshold)\n                .filterExpression(new FilterExpressionBuilder()\n                        .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId().toString()).build());\n        List<Document> documents = vectorStore.similaritySearch(searchRequestBuilder.build());\n        if (CollUtil.isEmpty(documents)) {\n            return documents;\n        }\n\n        // 2. Rerank 重排序\n        if (rerankModel != null) {\n            RerankResponse rerankResponse = rerankModel.call(new RerankRequest(reqBO.getContent(), documents,\n                    DashScopeRerankOptions.builder().withTopN(topK).build()));\n            documents = convertList(rerankResponse.getResults(),\n                    documentWithScore -> documentWithScore.getScore() >= similarityThreshold\n                            ? documentWithScore.getOutput() : null);\n        }\n        return documents;\n    }\n\n    @Override\n    public List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens) {\n        // 1. 读取 URL 内容\n        String content = knowledgeDocumentService.readUrl(url);\n\n        // 2.1 自动检测文档类型并选择策略\n        AiDocumentSplitStrategyEnum strategy = detectDocumentStrategy(content, url);\n        // 2.2 文档切片\n        List<Document> documentSegments = splitContentByStrategy(content, segmentMaxTokens, strategy, url);\n\n        // 3. 转换为段落对象\n        return convertList(documentSegments, segment -> {\n            if (StrUtil.isEmpty(segment.getText())) {\n                return null;\n            }\n            return new AiKnowledgeSegmentDO()\n                    .setContent(segment.getText())\n                    .setContentLength(segment.getText().length())\n                    .setTokens(tokenCountEstimator.estimate(segment.getText()));\n        });\n    }\n\n    /**\n     * 校验段落是否存在\n     *\n     * @param id 文档编号\n     * @return 段落信息\n     */\n    private AiKnowledgeSegmentDO validateKnowledgeSegmentExists(Long id) {\n        AiKnowledgeSegmentDO knowledgeSegment = segmentMapper.selectById(id);\n        if (knowledgeSegment == null) {\n            throw exception(KNOWLEDGE_SEGMENT_NOT_EXISTS);\n        }\n        return knowledgeSegment;\n    }\n\n    private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) {\n        return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId(), VECTOR_STORE_METADATA_TYPES);\n    }\n\n    private VectorStore getVectorStoreById(Long knowledgeId) {\n        AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);\n        return getVectorStoreById(knowledge);\n    }\n\n    /**\n     * 根据策略切分内容\n     *\n     * @param content 文档内容\n     * @param segmentMaxTokens 分段的最大 Token 数\n     * @param strategy 切片策略\n     * @param url 文档 URL（用于自动检测文件类型）\n     * @return 切片后的文档列表\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private List<Document> splitContentByStrategy(String content, Integer segmentMaxTokens,\n                                                  AiDocumentSplitStrategyEnum strategy, String url) {\n        // 自动检测策略\n        if (strategy == AiDocumentSplitStrategyEnum.AUTO) {\n            strategy = detectDocumentStrategy(content, url);\n            log.info(\"[splitContentByStrategy][自动检测到文档策略: {}]\", strategy.getName());\n        }\n        // 根据策略切分\n        TextSplitter textSplitter;\n        switch (strategy) {\n            case MARKDOWN_QA:\n                textSplitter = new MarkdownQaSplitter(segmentMaxTokens);\n                break;\n            case SEMANTIC:\n                textSplitter = new SemanticTextSplitter(segmentMaxTokens);\n                break;\n            case PARAGRAPH:\n                textSplitter = new SemanticTextSplitter(segmentMaxTokens, 0); // 段落切分，无重叠\n                break;\n            case TOKEN:\n            default:\n                textSplitter = buildTokenTextSplitter(segmentMaxTokens);\n                break;\n        }\n        // 执行切分\n        return textSplitter.apply(Collections.singletonList(new Document(content)));\n    }\n\n    /**\n     * 自动检测文档类型并选择切片策略\n     *\n     * @param content 文档内容\n     * @param url 文档 URL\n     * @return 推荐的切片策略\n     */\n    private AiDocumentSplitStrategyEnum detectDocumentStrategy(String content, String url) {\n        if (StrUtil.isEmpty(content)) {\n            return AiDocumentSplitStrategyEnum.TOKEN;\n        }\n        // 1. 检测 Markdown QA 格式\n        if (isMarkdownQaFormat(content, url)) {\n            return AiDocumentSplitStrategyEnum.MARKDOWN_QA;\n        }\n        // 2. 检测普通 Markdown 文档\n        if (isMarkdownDocument(url)) {\n            return AiDocumentSplitStrategyEnum.SEMANTIC;\n        }\n        // 3. 默认使用语义切分（比 Token 切分更智能）\n        return AiDocumentSplitStrategyEnum.SEMANTIC;\n    }\n\n    /**\n     * 检测是否为 Markdown QA 格式\n     * 特征：包含多个二级标题（## ）且标题后紧跟答案内容\n     */\n    private boolean isMarkdownQaFormat(String content, String url) {\n        // 文件扩展名判断\n        if (StrUtil.isNotEmpty(url) && !url.toLowerCase().endsWith(\".md\")) {\n            return false;\n        }\n\n        // 统计二级标题数量\n        long h2Count = content.lines()\n                .filter(line -> line.trim().startsWith(\"## \"))\n                .count();\n\n        // 要求一：至少包含 2 个二级标题才认为是 QA 格式\n        if (h2Count < 2) {\n            return false;\n        }\n\n        // 要求二：检查标题占比（QA 文档标题行数相对较多），如果二级标题占比超过 10%，认为是 QA 格式\n        long totalLines = content.lines().count();\n        double h2Ratio = (double) h2Count / totalLines;\n        return h2Ratio > 0.1;\n    }\n\n    /**\n     * 检测是否为 Markdown 文档\n     */\n    private boolean isMarkdownDocument(String url) {\n        return StrUtil.endWithAnyIgnoreCase(url, \".md\", \".markdown\");\n    }\n\n    /**\n     * 构建基于 Token 的文本切片器（原有逻辑保留）\n     */\n    private static TextSplitter buildTokenTextSplitter(Integer segmentMaxTokens) {\n        return TokenTextSplitter.builder()\n                .withChunkSize(segmentMaxTokens)\n                .withMinChunkSizeChars(Integer.MAX_VALUE) // 忽略字符的截断\n                .withMinChunkLengthToEmbed(1) // 允许的最小有效分段长度\n                .withMaxNumChunks(Integer.MAX_VALUE)\n                .withKeepSeparator(true) // 保留分隔符\n                .build();\n    }\n\n    @Override\n    public List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds) {\n        if (CollUtil.isEmpty(documentIds)) {\n            return Collections.emptyList();\n        }\n        return segmentMapper.selectProcessList(documentIds);\n    }\n\n    @Override\n    public Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO) {\n        // 1.1 校验文档是否存在\n        AiKnowledgeDocumentDO document = knowledgeDocumentService\n                .validateKnowledgeDocumentExists(createReqVO.getDocumentId());\n        // 1.2 获取知识库信息\n        AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(document.getKnowledgeId());\n        // 1.3 校验 token 熟练\n        Integer tokens = tokenCountEstimator.estimate(createReqVO.getContent());\n        if (tokens > document.getSegmentMaxTokens()) {\n            throw exception(KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG, tokens, document.getSegmentMaxTokens());\n        }\n\n        // 2. 保存段落\n        AiKnowledgeSegmentDO segment = BeanUtils.toBean(createReqVO, AiKnowledgeSegmentDO.class)\n                .setKnowledgeId(knowledge.getId()).setDocumentId(document.getId())\n                .setContentLength(createReqVO.getContent().length()).setTokens(tokens)\n                .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)\n                .setRetrievalCount(0).setStatus(CommonStatusEnum.ENABLE.getStatus());\n        segmentMapper.insert(segment);\n\n        // 3. 向量化\n        writeVectorStore(getVectorStoreById(knowledge), segment, new Document(segment.getContent()));\n        return segment.getId();\n    }\n\n    @Override\n    public AiKnowledgeSegmentDO getKnowledgeSegment(Long id) {\n        return segmentMapper.selectById(id);\n    }\n\n    @Override\n    public List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return segmentMapper.selectByIds(ids);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;\n\nimport java.util.List;\n\n/**\n * AI 知识库-基础信息 Service 接口\n *\n * @author xiaoxin\n */\npublic interface AiKnowledgeService {\n\n    /**\n     * 创建知识库\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createKnowledge(AiKnowledgeSaveReqVO createReqVO);\n\n    /**\n     * 更新知识库\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO);\n\n    /**\n     * 删除知识库\n     *\n     * @param id 知识库编号\n     */\n    void deleteKnowledge(Long id);\n\n    /**\n     * 获得知识库\n     *\n     * @param id 编号\n     * @return 知识库\n     */\n    AiKnowledgeDO getKnowledge(Long id);\n\n    /**\n     * 校验知识库是否存在\n     *\n     * @param id 记录编号\n     */\n    AiKnowledgeDO validateKnowledgeExists(Long id);\n\n    /**\n     * 获得知识库分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 知识库分页\n     */\n    PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO);\n\n    /**\n     * 获得指定状态的知识库列表\n     *\n     * @param status 状态\n     * @return 知识库列表\n     */\n    List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS;\n\n/**\n * AI 知识库-基础信息 Service 实现类\n *\n * @author xiaoxin\n */\n@Service\n@Slf4j\npublic class AiKnowledgeServiceImpl implements AiKnowledgeService {\n\n    @Resource\n    private AiKnowledgeMapper knowledgeMapper;\n\n    @Resource\n    private AiModelService modelService;\n    @Resource\n    private AiKnowledgeSegmentService knowledgeSegmentService;\n    @Resource\n    private AiKnowledgeDocumentService knowledgeDocumentService;\n\n    @Override\n    public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) {\n        // 1. 校验模型配置\n        AiModelDO model = modelService.validateModel(createReqVO.getEmbeddingModelId());\n\n        // 2. 插入知识库\n        AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)\n                .setEmbeddingModel(model.getModel());\n        knowledgeMapper.insert(knowledge);\n        return knowledge.getId();\n    }\n\n    @Override\n    public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) {\n        // 1.1 校验知识库存在\n        AiKnowledgeDO oldKnowledge = validateKnowledgeExists(updateReqVO.getId());\n        // 1.2 校验模型配置\n        AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId());\n\n        // 2. 更新知识库\n        AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class)\n                .setEmbeddingModel(model.getModel());\n        knowledgeMapper.updateById(updateObj);\n\n        // 3. 如果模型变化，需要 reindex 所有的文档\n        if (ObjUtil.notEqual(oldKnowledge.getEmbeddingModelId(), updateReqVO.getEmbeddingModelId())) {\n            knowledgeSegmentService.reindexByKnowledgeIdAsync(updateReqVO.getId());\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteKnowledge(Long id) {\n        // 1. 校验存在\n        validateKnowledgeExists(id);\n\n        // 2. 删除知识库下的所有文档及段落\n        knowledgeDocumentService.deleteKnowledgeDocumentByKnowledgeId(id);\n\n        // 3. 删除知识库\n        // 特殊：知识库需要最后删除，不然相关的配置会找不到\n        knowledgeMapper.deleteById(id);\n    }\n\n    @Override\n    public AiKnowledgeDO getKnowledge(Long id) {\n        return knowledgeMapper.selectById(id);\n    }\n\n    @Override\n    public AiKnowledgeDO validateKnowledgeExists(Long id) {\n        AiKnowledgeDO knowledge = knowledgeMapper.selectById(id);\n        if (knowledge == null) {\n            throw exception(KNOWLEDGE_NOT_EXISTS);\n        }\n        return knowledge;\n    }\n\n    @Override\n    public PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO) {\n        return knowledgeMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status) {\n        return knowledgeMapper.selectListByStatus(status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge.bo;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\nimport jakarta.validation.constraints.NotEmpty;\n\n/**\n * AI 知识库段落搜索 Request BO\n *\n * @author 芋道源码\n */\n@Data\npublic class AiKnowledgeSegmentSearchReqBO {\n\n    /**\n     * 知识库编号\n     */\n    @NotNull(message = \"知识库编号不能为空\")\n    private Long knowledgeId;\n\n    /**\n     * 内容\n     */\n    @NotEmpty(message = \"内容不能为空\")\n    private String content;\n\n    /**\n     * 最大返回数量\n     */\n    private Integer topK;\n\n    /**\n     * 相似度阈值\n     */\n    private Double similarityThreshold;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge.bo;\n\nimport lombok.Data;\n\n/**\n * AI 知识库段落搜索 Response BO\n *\n * @author 芋道源码\n */\n@Data\npublic class AiKnowledgeSegmentSearchRespBO {\n\n    /**\n     * 段落编号\n     */\n    private Long id;\n    /**\n     * 文档编号\n     */\n    private Long documentId;\n    /**\n     * 知识库编号\n     */\n    private Long knowledgeId;\n\n    /**\n     * 内容\n     */\n    private String content;\n    /**\n     * 内容长度\n     */\n    private Integer contentLength;\n\n    /**\n     * Token 数量\n     */\n    private Integer tokens;\n\n    /**\n     * 相似度分数\n     */\n    private Double score;\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/MarkdownQaSplitter.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge.splitter;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.transformer.splitter.TextSplitter;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Markdown QA 格式专用切片器\n *\n * <p>功能特点：\n * <ul>\n *   <li>识别二级标题（## ）作为问题标记</li>\n *   <li>短 QA 对保持完整（不超过 Token 限制）</li>\n *   <li>长答案智能切分，每个片段保留完整问题作为上下文</li>\n *   <li>支持自定义 Token 估算器</li>\n * </ul>\n *\n * @author runzhen\n */\n@Slf4j\n@SuppressWarnings(\"SizeReplaceableByIsEmpty\")\npublic class MarkdownQaSplitter extends TextSplitter {\n\n    /**\n     * 二级标题正则：匹配 \"## \" 开头的行\n     */\n    private static final Pattern H2_PATTERN = Pattern.compile(\"^##\\\\s+(.+)$\", Pattern.MULTILINE);\n\n    /**\n     * 段落分隔符：双换行\n     */\n    private static final String PARAGRAPH_SEPARATOR = \"\\n\\n\";\n\n    /**\n     * 句子分隔符\n     */\n    private static final Pattern SENTENCE_PATTERN = Pattern.compile(\"[。！？.!?]\\\\s*\");\n\n    /**\n     * 分段的最大 Token 数\n     */\n    private final int chunkSize;\n\n    /**\n     * Token 估算器（简单实现：中文按字符数，英文按单词数的 1.3 倍）\n     */\n    private final TokenEstimator tokenEstimator;\n\n    public MarkdownQaSplitter(int chunkSize) {\n        this.chunkSize = chunkSize;\n        this.tokenEstimator = new SimpleTokenEstimator();\n    }\n\n    @Override\n    protected List<String> splitText(String text) {\n        if (StrUtil.isEmpty(text)) {\n            return Collections.emptyList();\n        }\n\n        // 解析 QA 对\n        List<QaPair> qaPairs = parseQaPairs(text);\n        if (CollUtil.isEmpty(qaPairs)) {\n            // 如果没有识别到 QA 格式，按段落切分\n            return fallbackSplit(text);\n        }\n\n        // 处理每个 QA 对\n        List<String> result = new ArrayList<>();\n        for (QaPair qaPair : qaPairs) {\n            result.addAll(splitQaPair(qaPair));\n        }\n        return result;\n    }\n\n    /**\n     * 解析 Markdown QA 对\n     *\n     * @param content 文本内容\n     * @return QA 对列表\n     */\n    private List<QaPair> parseQaPairs(String content) {\n        // 找到所有二级标题位置\n        List<QaPair> qaPairs = new ArrayList<>();\n        List<Integer> headingPositions = new ArrayList<>();\n        List<String> questions = new ArrayList<>();\n        Matcher matcher = H2_PATTERN.matcher(content);\n        while (matcher.find()) {\n            headingPositions.add(matcher.start());\n            questions.add(matcher.group(1).trim());\n        }\n        if (CollUtil.isEmpty(headingPositions)) {\n            return qaPairs;\n        }\n\n        // 提取每个 QA 对\n        for (int i = 0; i < headingPositions.size(); i++) {\n            int start = headingPositions.get(i);\n            int end = (i + 1 < headingPositions.size())\n                    ? headingPositions.get(i + 1)\n                    : content.length();\n            String qaText = content.substring(start, end).trim();\n            String question = questions.get(i);\n            // 提取答案部分（去掉问题标题）\n            String answer = qaText.substring(qaText.indexOf('\\n') + 1).trim();\n            qaPairs.add(new QaPair(question, answer, qaText));\n        }\n        return qaPairs;\n    }\n\n    /**\n     * 切分单个 QA 对\n     *\n     * @param qaPair QA 对\n     * @return 切分后的文本片段列表\n     */\n    private List<String> splitQaPair(QaPair qaPair) {\n        // 如果整个 QA 对不超过限制，保持完整\n        List<String> chunks = new ArrayList<>();\n        String fullQa = qaPair.fullText;\n        int qaTokens = tokenEstimator.estimate(fullQa);\n        if (qaTokens <= chunkSize) {\n            chunks.add(fullQa);\n            return chunks;\n        }\n\n        // 长答案需要切分\n        log.debug(\"QA 对超过 Token 限制 ({} > {})，开始智能切分: {}\", qaTokens, chunkSize, qaPair.question);\n        List<String> answerChunks = splitLongAnswer(qaPair.answer, qaPair.question);\n        for (String answerChunk : answerChunks) {\n            // 每个片段都包含完整问题\n            String chunkText = \"## \" + qaPair.question + \"\\n\" + answerChunk;\n            chunks.add(chunkText);\n        }\n        return chunks;\n    }\n\n    /**\n     * 切分长答案\n     *\n     * @param answer 答案文本\n     * @param question 问题文本\n     * @return 切分后的答案片段列表\n     */\n    private List<String> splitLongAnswer(String answer, String question) {\n        List<String> chunks = new ArrayList<>();\n        // 预留问题的 Token 空间\n        String questionHeader = \"## \" + question + \"\\n\";\n        int questionTokens = tokenEstimator.estimate(questionHeader);\n        int availableTokens = chunkSize - questionTokens - 10; // 预留 10 个 Token 的缓冲\n\n        // 先按段落切分\n        String[] paragraphs = answer.split(PARAGRAPH_SEPARATOR);\n        StringBuilder currentChunk = new StringBuilder();\n        int currentTokens = 0;\n        for (String paragraph : paragraphs) {\n            if (StrUtil.isEmpty(paragraph)) {\n                continue;\n            }\n            int paragraphTokens = tokenEstimator.estimate(paragraph);\n            // 如果单个段落就超过限制，需要按句子切分\n            if (paragraphTokens > availableTokens) {\n                // 先保存当前块\n                if (currentChunk.length() > 0) {\n                    chunks.add(currentChunk.toString().trim());\n                    currentChunk = new StringBuilder();\n                    currentTokens = 0;\n                }\n                // 按句子切分长段落\n                chunks.addAll(splitLongParagraph(paragraph, availableTokens));\n                continue;\n            }\n            // 如果加上这个段落会超过限制\n            if (currentTokens + paragraphTokens > availableTokens && currentChunk.length() > 0) {\n                chunks.add(currentChunk.toString().trim());\n                currentChunk = new StringBuilder();\n                currentTokens = 0;\n            }\n            if (currentChunk.length() > 0) {\n                currentChunk.append(\"\\n\\n\");\n            }\n            // 添加段落\n            currentChunk.append(paragraph);\n            currentTokens += paragraphTokens;\n        }\n\n        // 添加最后一块\n        if (currentChunk.length() > 0) {\n            chunks.add(currentChunk.toString().trim());\n        }\n        return CollUtil.isEmpty(chunks) ? Collections.singletonList(answer) : chunks;\n    }\n\n    /**\n     * 切分长段落（按句子）\n     *\n     * @param paragraph 段落文本\n     * @param availableTokens 可用的 Token 数\n     * @return 切分后的文本片段列表\n     */\n    private List<String> splitLongParagraph(String paragraph, int availableTokens) {\n        // 按句子切分\n        List<String> chunks = new ArrayList<>();\n        String[] sentences = SENTENCE_PATTERN.split(paragraph);\n\n        // 按句子累积切分\n        StringBuilder currentChunk = new StringBuilder();\n        int currentTokens = 0;\n        for (String sentence : sentences) {\n            if (StrUtil.isEmpty(sentence)) {\n                continue;\n            }\n            int sentenceTokens = tokenEstimator.estimate(sentence);\n            // 如果单个句子就超过限制，强制切分\n            if (sentenceTokens > availableTokens) {\n                if (currentChunk.length() > 0) {\n                    chunks.add(currentChunk.toString().trim());\n                    currentChunk = new StringBuilder();\n                    currentTokens = 0;\n                }\n                chunks.add(sentence.trim());\n                continue;\n            }\n            // 如果加上这个句子会超过限制\n            if (currentTokens + sentenceTokens > availableTokens && currentChunk.length() > 0) {\n                chunks.add(currentChunk.toString().trim());\n                currentChunk = new StringBuilder();\n                currentTokens = 0;\n            }\n            // 添加句子\n            currentChunk.append(sentence);\n            currentTokens += sentenceTokens;\n        }\n\n        // 添加最后一块\n        if (currentChunk.length() > 0) {\n            chunks.add(currentChunk.toString().trim());\n        }\n        return chunks.isEmpty() ? Collections.singletonList(paragraph) : chunks;\n    }\n\n    /**\n     * 降级切分策略（当未识别到 QA 格式时）\n     *\n     * @param content 文本内容\n     * @return 切分后的文本片段列表\n     */\n    private List<String> fallbackSplit(String content) {\n        // 按段落切分\n        List<String> chunks = new ArrayList<>();\n        String[] paragraphs = content.split(PARAGRAPH_SEPARATOR);\n\n        // 按段落累积切分\n        StringBuilder currentChunk = new StringBuilder();\n        int currentTokens = 0;\n        for (String paragraph : paragraphs) {\n            if (StrUtil.isEmpty(paragraph)) {\n                continue;\n            }\n            int paragraphTokens = tokenEstimator.estimate(paragraph);\n            // 如果加上这个段落会超过限制\n            if (currentTokens + paragraphTokens > chunkSize && currentChunk.length() > 0) {\n                chunks.add(currentChunk.toString().trim());\n                currentChunk = new StringBuilder();\n                currentTokens = 0;\n            }\n            // 添加段落\n            if (currentChunk.length() > 0) {\n                currentChunk.append(\"\\n\\n\");\n            }\n            currentChunk.append(paragraph);\n            currentTokens += paragraphTokens;\n        }\n\n        // 添加最后一块\n        if (currentChunk.length() > 0) {\n            chunks.add(currentChunk.toString().trim());\n        }\n        return chunks.isEmpty() ? Collections.singletonList(content) : chunks;\n    }\n\n    /**\n     * QA 对数据结构\n     */\n    @AllArgsConstructor\n    private static class QaPair {\n\n        String question;\n        String answer;\n        String fullText;\n\n    }\n\n    /**\n     * Token 估算器接口\n     */\n    public interface TokenEstimator {\n\n        int estimate(String text);\n\n    }\n\n    /**\n     * 简单的 Token 估算器实现\n     * 中文：1 字符 ≈ 1 Token\n     * 英文：1 单词 ≈ 1.3 Token\n     */\n    private static class SimpleTokenEstimator implements TokenEstimator {\n\n        @Override\n        public int estimate(String text) {\n            if (StrUtil.isEmpty(text)) {\n                return 0;\n            }\n\n            int chineseChars = 0;\n            int englishWords = 0;\n            // 简单统计中英文\n            for (char c : text.toCharArray()) {\n                if (c >= 0x4E00 && c <= 0x9FA5) {\n                    chineseChars++;\n                }\n            }\n            // 英文单词估算\n            String[] words = text.split(\"\\\\s+\");\n            for (String word : words) {\n                if (word.matches(\".*[a-zA-Z].*\")) {\n                    englishWords++;\n                }\n            }\n            return chineseChars + (int) (englishWords * 1.3);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/SemanticTextSplitter.java",
    "content": "package cn.iocoder.yudao.module.ai.service.knowledge.splitter;\n\nimport cn.hutool.core.util.StrUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.transformer.splitter.TextSplitter;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * 语义化文本切片器\n *\n * <p>功能特点：\n * <ul>\n *   <li>优先在段落边界（双换行）处切分</li>\n *   <li>其次在句子边界（句号、问号、感叹号）处切分</li>\n *   <li>避免在句子中间截断，保持语义完整性</li>\n *   <li>支持中英文标点符号识别</li>\n * </ul>\n *\n * @author runzhen\n */\n@Slf4j\npublic class SemanticTextSplitter extends TextSplitter {\n\n    /**\n     * 分段的最大 Token 数\n     */\n    private final int chunkSize;\n\n    /**\n     * 段落重叠大小（用于保持上下文连贯性）\n     */\n    private final int chunkOverlap;\n\n    /**\n     * 段落分隔符（按优先级排序）\n     */\n    private static final List<String> PARAGRAPH_SEPARATORS = Arrays.asList(\n            \"\\n\\n\\n\",    // 三个换行\n            \"\\n\\n\",      // 双换行\n            \"\\n\"         // 单换行\n    );\n\n    /**\n     * 句子结束标记（中英文标点）\n     */\n    private static final Pattern SENTENCE_END_PATTERN = Pattern.compile(\n            \"[。！？.!?]+[\\\\s\\\"'）)】\\\\]]*\"\n    );\n\n    /**\n     * Token 估算器\n     */\n    private final MarkdownQaSplitter.TokenEstimator tokenEstimator;\n\n    public SemanticTextSplitter(int chunkSize, int chunkOverlap) {\n        this.chunkSize = chunkSize;\n        this.chunkOverlap = Math.min(chunkOverlap, chunkSize / 2); // 重叠不超过一半\n        this.tokenEstimator = new SimpleTokenEstimator();\n    }\n\n    public SemanticTextSplitter(int chunkSize) {\n        this(chunkSize, 50); // 默认重叠 50 个 Token\n    }\n\n    @Override\n    protected List<String> splitText(String text) {\n        if (StrUtil.isEmpty(text)) {\n            return Collections.emptyList();\n        }\n        return splitTextRecursive(text);\n    }\n\n    /**\n     * 切分文本（递归策略）\n     *\n     * @param text 待切分文本\n     * @return 切分后的文本块列表\n     */\n    private List<String> splitTextRecursive(String text) {\n        List<String> chunks = new ArrayList<>();\n\n        // 如果文本不超过限制，直接返回\n        int textTokens = tokenEstimator.estimate(text);\n        if (textTokens <= chunkSize) {\n            chunks.add(text.trim());\n            return chunks;\n        }\n\n        // 尝试按不同分隔符切分\n        List<String> splits = null;\n        String usedSeparator = null;\n        for (String separator : PARAGRAPH_SEPARATORS) {\n            if (text.contains(separator)) {\n                splits = Arrays.asList(text.split(Pattern.quote(separator)));\n                usedSeparator = separator;\n                break;\n            }\n        }\n\n        // 如果没有找到段落分隔符，按句子切分\n        if (splits == null || splits.size() == 1) {\n            splits = splitBySentences(text);\n            usedSeparator = \"\"; // 句子切分不需要分隔符\n        }\n\n        // 合并小片段\n        chunks = mergeSplits(splits, usedSeparator);\n        return chunks;\n    }\n\n    /**\n     * 按句子切分\n     *\n     * @param text 待切分文本\n     * @return 句子列表\n     */\n    private List<String> splitBySentences(String text) {\n        // 使用正则表达式匹配句子结束位置\n        List<String> sentences = new ArrayList<>();\n        int lastEnd = 0;\n        Matcher matcher = SENTENCE_END_PATTERN.matcher(text);\n        while (matcher.find()) {\n            String sentence = text.substring(lastEnd, matcher.end()).trim();\n            if (StrUtil.isNotEmpty(sentence)) {\n                sentences.add(sentence);\n            }\n            lastEnd = matcher.end();\n        }\n\n        // 添加剩余部分\n        if (lastEnd < text.length()) {\n            String remaining = text.substring(lastEnd).trim();\n            if (StrUtil.isNotEmpty(remaining)) {\n                sentences.add(remaining);\n            }\n        }\n        return sentences.isEmpty() ? Collections.singletonList(text) : sentences;\n    }\n\n    /**\n     * 合并切分后的小片段\n     *\n     * @param splits 切分后的片段列表\n     * @param separator 片段间的分隔符\n     * @return 合并后的文本块列表\n     */\n    private List<String> mergeSplits(List<String> splits, String separator) {\n        List<String> chunks = new ArrayList<>();\n        List<String> currentChunks = new ArrayList<>();\n        int currentLength = 0;\n\n        for (String split : splits) {\n            if (StrUtil.isEmpty(split)) {\n                continue;\n            }\n            int splitTokens = tokenEstimator.estimate(split);\n            // 如果单个片段就超过限制，进一步递归切分\n            if (splitTokens > chunkSize) {\n                // 先保存当前累积的块\n                if (!currentChunks.isEmpty()) {\n                    String chunkText = String.join(separator, currentChunks);\n                    chunks.add(chunkText.trim());\n                    currentChunks.clear();\n                    currentLength = 0;\n                }\n                // 递归切分大片段\n                if (!separator.isEmpty()) {\n                    // 如果是段落分隔符，尝试按句子切分\n                    chunks.addAll(splitTextRecursive(split));\n                } else {\n                    // 如果已经是句子级别，强制按字符切分\n                    chunks.addAll(forceSplitLongText(split));\n                }\n                continue;\n            }\n            // 计算加上分隔符的 Token 数\n            int separatorTokens = StrUtil.isEmpty(separator) ? 0 : tokenEstimator.estimate(separator);\n            // 如果加上这个片段会超过限制\n            if (!currentChunks.isEmpty() && currentLength + splitTokens + separatorTokens > chunkSize) {\n                // 保存当前块\n                String chunkText = String.join(separator, currentChunks);\n                chunks.add(chunkText.trim());\n\n                // 处理重叠：保留最后几个片段\n                currentChunks = getOverlappingChunks(currentChunks, separator);\n                currentLength = estimateTokens(currentChunks, separator);\n            }\n            // 添加当前片段\n            currentChunks.add(split);\n            currentLength += splitTokens + separatorTokens;\n        }\n\n        // 添加最后一块\n        if (!currentChunks.isEmpty()) {\n            String chunkText = String.join(separator, currentChunks);\n            chunks.add(chunkText.trim());\n        }\n        return chunks;\n    }\n\n    /**\n     * 获取重叠的片段（用于保持上下文）\n     *\n     * @param chunks 当前片段列表\n     * @param separator 片段间的分隔符\n     * @return 重叠的片段列表\n     */\n    private List<String> getOverlappingChunks(List<String> chunks, String separator) {\n        if (chunkOverlap == 0 || chunks.isEmpty()) {\n            return new ArrayList<>();\n        }\n\n        // 从后往前取片段，直到达到重叠大小\n        List<String> overlapping = new ArrayList<>();\n        int tokens = 0;\n        for (int i = chunks.size() - 1; i >= 0; i--) {\n            String chunk = chunks.get(i);\n            int chunkTokens = tokenEstimator.estimate(chunk);\n            if (tokens + chunkTokens > chunkOverlap) {\n                break;\n            }\n            // 添加到重叠列表前端\n            overlapping.add(0, chunk);\n            tokens += chunkTokens + (StrUtil.isEmpty(separator) ? 0 : tokenEstimator.estimate(separator));\n        }\n        return overlapping;\n    }\n\n    /**\n     * 估算片段列表的总 Token 数\n     *\n     * @param chunks 片段列表\n     * @param separator 片段间的分隔符\n     * @return 总 Token 数\n     */\n    private int estimateTokens(List<String> chunks, String separator) {\n        int total = 0;\n        for (int i = 0; i < chunks.size(); i++) {\n            total += tokenEstimator.estimate(chunks.get(i));\n            if (i < chunks.size() - 1 && StrUtil.isNotEmpty(separator)) {\n                total += tokenEstimator.estimate(separator);\n            }\n        }\n        return total;\n    }\n\n    /**\n     * 强制切分长文本（当语义切分失败时）\n     *\n     * @param text 待切分文本\n     * @return 切分后的文本块列表\n     */\n    private List<String> forceSplitLongText(String text) {\n        List<String> chunks = new ArrayList<>();\n        int charsPerChunk = (int) (chunkSize * 0.8); // 保守估计\n        for (int i = 0; i < text.length(); i += charsPerChunk) {\n            int end = Math.min(i + charsPerChunk, text.length());\n            String chunk = text.substring(i, end);\n            chunks.add(chunk.trim());\n        }\n        log.warn(\"文本过长，已强制按字符切分，可能影响语义完整性\");\n        return chunks;\n    }\n\n    /**\n     * 简单的 Token 估算器实现\n     */\n    private static class SimpleTokenEstimator implements MarkdownQaSplitter.TokenEstimator {\n\n        @Override\n        public int estimate(String text) {\n            if (StrUtil.isEmpty(text)) {\n                return 0;\n            }\n\n            int chineseChars = 0;\n            int englishWords = 0;\n            // 简单统计中英文\n            for (char c : text.toCharArray()) {\n                if (c >= 0x4E00 && c <= 0x9FA5) {\n                    chineseChars++;\n                }\n            }\n            // 英文单词估算\n            String[] words = text.split(\"\\\\s+\");\n            for (String word : words) {\n                if (word.matches(\".*[a-zA-Z].*\")) {\n                    englishWords++;\n                }\n            }\n            return chineseChars + (int) (englishWords * 1.3);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.mindmap;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;\nimport reactor.core.publisher.Flux;\n\n/**\n * AI 思维导图 Service 接口\n *\n * @author xiaoxin\n */\npublic interface AiMindMapService {\n\n    /**\n     * 生成思维导图内容\n     *\n     * @param generateReqVO 请求参数\n     * @param userId        用户编号\n     * @return 生成结果\n     */\n    Flux<CommonResult<String>> generateMindMap(AiMindMapGenerateReqVO generateReqVO, Long userId);\n\n    /**\n     * 删除思维导图\n     *\n     * @param id 编号\n     */\n    void deleteMindMap(Long id);\n\n    /**\n     * 获得思维导图分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 思维导图分页\n     */\n    PageResult<AiMindMapDO> getMindMapPage(AiMindMapPageReqVO pageReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.mindmap;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.module.ai.util.AiUtils;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.mindmap.AiMindMapMapper;\nimport cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;\nimport cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;\nimport cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.stereotype.Service;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;\n\n/**\n * AI 思维导图 Service 实现类\n *\n * @author xiaoxin\n */\n@Service\n@Slf4j\npublic class AiMindMapServiceImpl implements AiMindMapService {\n\n    @Resource\n    private AiModelService modalService;\n    @Resource\n    private AiChatRoleService chatRoleService;\n\n    @Resource\n    private AiMindMapMapper mindMapMapper;\n\n    @Override\n    public Flux<CommonResult<String>> generateMindMap(AiMindMapGenerateReqVO generateReqVO, Long userId) {\n        // 1. 获取导图模型。尝试获取思维导图助手角色，如果没有则使用默认模型\n        AiChatRoleDO role = CollUtil.getFirst(\n                chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName()));\n        // 1.1 获取导图执行模型\n        AiModelDO model = getModel(role);\n        // 1.2 获取角色设定消息\n        String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage())\n                ? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage();\n        // 1.3 校验平台\n        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());\n        ChatModel chatModel = modalService.getChatModel(model.getId());\n\n        // 2. 插入思维导图信息\n        AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, mindMap -> mindMap.setUserId(userId)\n                .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));\n        mindMapMapper.insert(mindMapDO);\n\n        // 3.1 构建 Prompt，并进行调用\n        Prompt prompt = buildPrompt(generateReqVO, model, systemMessage);\n        Flux<ChatResponse> streamResponse = chatModel.stream(prompt);\n\n        // 3.2 流式返回\n        StringBuffer contentBuffer = new StringBuffer();\n        return streamResponse.map(chunk -> {\n            String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;\n            newContent = StrUtil.nullToDefault(newContent, \"\"); // 避免 null 的 情况\n            contentBuffer.append(newContent);\n            // 响应结果\n            return success(newContent);\n        }).doOnComplete(() -> {\n            // 忽略租户，因为 Flux 异步无法透传租户\n            TenantUtils.executeIgnore(() ->\n                    mindMapMapper.updateById(new AiMindMapDO().setId(mindMapDO.getId()).setGeneratedContent(contentBuffer.toString())));\n        }).doOnError(throwable -> {\n            log.error(\"[generateWriteContent][generateReqVO({}) 发生异常]\", generateReqVO, throwable);\n            // 忽略租户，因为 Flux 异步无法透传租户\n            TenantUtils.executeIgnore(() ->\n                    mindMapMapper.updateById(new AiMindMapDO().setId(mindMapDO.getId()).setErrorMessage(throwable.getMessage())));\n        }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));\n\n    }\n\n    private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {\n        // 1. 构建 message 列表\n        List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);\n        // 2. 构建 options 对象\n        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());\n        ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());\n        return new Prompt(chatMessages, options);\n    }\n\n    private static List<Message> buildMessages(AiMindMapGenerateReqVO generateReqVO, String systemMessage) {\n        List<Message> chatMessages = new ArrayList<>();\n        // 1. 角色设定\n        if (StrUtil.isNotBlank(systemMessage)) {\n            chatMessages.add(new SystemMessage(systemMessage));\n        }\n        // 2. 用户输入\n        chatMessages.add(new UserMessage(generateReqVO.getPrompt()));\n        return chatMessages;\n    }\n\n    private AiModelDO getModel(AiChatRoleDO role) {\n        AiModelDO model = null;\n        if (role != null && role.getModelId() != null) {\n            model = modalService.getModel(role.getModelId());\n        }\n        if (model == null) {\n            model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());\n        }\n        // 校验模型存在、且合法\n        if (model == null) {\n            throw exception(MODEL_NOT_EXISTS);\n        }\n        if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) {\n            throw exception(MODEL_USE_TYPE_ERROR);\n        }\n        return model;\n    }\n\n    @Override\n    public void deleteMindMap(Long id) {\n        // 校验存在\n        validateMindMapExists(id);\n        // 删除\n        mindMapMapper.deleteById(id);\n    }\n\n    private void validateMindMapExists(Long id) {\n        if (mindMapMapper.selectById(id) == null) {\n            throw exception(MIND_MAP_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public PageResult<AiMindMapDO> getMindMapPage(AiMindMapPageReqVO pageReqVO) {\n        return mindMapMapper.selectPage(pageReqVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.model;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * AI API 密钥 Service 接口\n *\n * @author 芋道源码\n */\npublic interface AiApiKeyService {\n\n    /**\n     * 创建 API 密钥\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createApiKey(@Valid AiApiKeySaveReqVO createReqVO);\n\n    /**\n     * 更新 API 密钥\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateApiKey(@Valid AiApiKeySaveReqVO updateReqVO);\n\n    /**\n     * 删除 API 密钥\n     *\n     * @param id 编号\n     */\n    void deleteApiKey(Long id);\n\n    /**\n     * 获得 API 密钥\n     *\n     * @param id 编号\n     * @return API 密钥\n     */\n    AiApiKeyDO getApiKey(Long id);\n\n    /**\n     * 校验 API 密钥\n     *\n     * @param id 比那好\n     * @return API 密钥\n     */\n    AiApiKeyDO validateApiKey(Long id);\n\n    /**\n     * 获得 API 密钥分页\n     *\n     * @param pageReqVO 分页查询\n     * @return API 密钥分页\n     */\n    PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO);\n\n    /**\n     * 获得 API 密钥列表\n     *\n     * @return API 密钥列表\n     */\n    List<AiApiKeyDO> getApiKeyList();\n\n    /**\n     * 获得默认的 API 密钥\n     *\n     * @param platform 平台\n     * @param status 状态\n     * @return API 密钥\n     */\n    AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status);\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.model;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;\nimport jakarta.annotation.Resource;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_DISABLE;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_NOT_EXISTS;\n\n/**\n * AI API 密钥 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class AiApiKeyServiceImpl implements AiApiKeyService {\n\n    @Resource\n    private AiApiKeyMapper apiKeyMapper;\n\n    @Override\n    public Long createApiKey(AiApiKeySaveReqVO createReqVO) {\n        // 插入\n        AiApiKeyDO apiKey = BeanUtils.toBean(createReqVO, AiApiKeyDO.class);\n        apiKeyMapper.insert(apiKey);\n        // 返回\n        return apiKey.getId();\n    }\n\n    @Override\n    public void updateApiKey(AiApiKeySaveReqVO updateReqVO) {\n        // 校验存在\n        validateApiKeyExists(updateReqVO.getId());\n        // 更新\n        AiApiKeyDO updateObj = BeanUtils.toBean(updateReqVO, AiApiKeyDO.class);\n        apiKeyMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteApiKey(Long id) {\n        // 校验存在\n        validateApiKeyExists(id);\n        // 删除\n        apiKeyMapper.deleteById(id);\n    }\n\n    private AiApiKeyDO validateApiKeyExists(Long id) {\n        AiApiKeyDO apiKey = apiKeyMapper.selectById(id);\n        if (apiKey == null) {\n            throw exception(API_KEY_NOT_EXISTS);\n        }\n        return apiKey;\n    }\n\n    @Override\n    public AiApiKeyDO getApiKey(Long id) {\n        return apiKeyMapper.selectById(id);\n    }\n\n    @Override\n    public AiApiKeyDO validateApiKey(Long id) {\n        AiApiKeyDO apiKey = validateApiKeyExists(id);\n        if (CommonStatusEnum.isDisable(apiKey.getStatus())) {\n            throw exception(API_KEY_DISABLE);\n        }\n        return apiKey;\n    }\n\n    @Override\n    public PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO) {\n        return apiKeyMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<AiApiKeyDO> getApiKeyList() {\n        return apiKeyMapper.selectList();\n    }\n\n    @Override\n    public AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status) {\n        AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform, status);\n        if (apiKey == null) {\n            throw exception(API_KEY_NOT_EXISTS);\n        }\n        return apiKey;\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.model;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * AI 聊天角色 Service 接口\n *\n * @author fansili\n */\npublic interface AiChatRoleService {\n\n    /**\n     * 创建聊天角色\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createChatRole(@Valid AiChatRoleSaveReqVO createReqVO);\n\n    /**\n     * 创建【我的】聊天角色\n     *\n     * @param createReqVO 创建信息\n     * @param userId      用户编号\n     * @return 编号\n     */\n    Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId);\n\n    /**\n     * 更新聊天角色\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateChatRole(@Valid AiChatRoleSaveReqVO updateReqVO);\n\n    /**\n     * 创建【我的】聊天角色\n     *\n     * @param updateReqVO 更新信息\n     * @param userId      用户编号\n     */\n    void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId);\n\n    /**\n     * 删除聊天角色\n     *\n     * @param id 编号\n     */\n    void deleteChatRole(Long id);\n\n    /**\n     * 删除【我的】聊天角色\n     *\n     * @param id     编号\n     * @param userId 用户编号\n     */\n    void deleteChatRoleMy(Long id, Long userId);\n\n    /**\n     * 获得聊天角色\n     *\n     * @param id 编号\n     * @return AI 聊天角色\n     */\n    AiChatRoleDO getChatRole(Long id);\n\n    /**\n     * 获得聊天角色列表\n     *\n     * @param ids 编号数组\n     * @return 聊天角色列表\n     */\n    List<AiChatRoleDO> getChatRoleList(Collection<Long> ids);\n\n    default Map<Long, AiChatRoleDO> getChatRoleMap(Collection<Long> ids) {\n        return convertMap(getChatRoleList(ids), AiChatRoleDO::getId);\n    }\n\n    /**\n     * 校验聊天角色是否合法\n     *\n     * @param id 角色编号\n     */\n    AiChatRoleDO validateChatRole(Long id);\n\n    /**\n     * 获得聊天角色分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 聊天角色分页\n     */\n    PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO);\n\n    /**\n     * 获得【我的】聊天角色分页\n     *\n     * @param pageReqVO 分页查询\n     * @param userId    用户编号\n     * @return 聊天角色分页\n     */\n    PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId);\n\n    /**\n     * 获得聊天角色的分类列表\n     *\n     * @return 分类列表\n     */\n    List<String> getChatRoleCategoryList();\n\n    /**\n     * 根据名字获得聊天角色\n     *\n     * @param name 名字\n     * @return 聊天角色列表\n     */\n    List<AiChatRoleDO> getChatRoleListByName(String name);\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.model;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper;\nimport cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_DISABLE;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_NOT_EXISTS;\n\n/**\n * AI 聊天角色 Service 实现类\n *\n * @author fansili\n */\n@Service\n@Slf4j\npublic class AiChatRoleServiceImpl implements AiChatRoleService {\n\n    @Resource\n    private AiChatRoleMapper chatRoleMapper;\n\n    @Resource\n    private AiKnowledgeService knowledgeService;\n    @Resource\n    private AiToolService toolService;\n\n    @Override\n    public Long createChatRole(AiChatRoleSaveReqVO createReqVO) {\n        // 校验文档\n        validateDocuments(createReqVO.getKnowledgeIds());\n        // 校验工具\n        validateTools(createReqVO.getToolIds());\n\n        // 保存角色\n        AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class);\n        chatRoleMapper.insert(chatRole);\n        return chatRole.getId();\n    }\n\n    @Override\n    public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) {\n        // 校验文档\n        validateDocuments(createReqVO.getKnowledgeIds());\n        // 校验工具\n        validateTools(createReqVO.getToolIds());\n\n        // 保存角色\n        AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId)\n                .setStatus(CommonStatusEnum.ENABLE.getStatus()).setPublicStatus(false);\n        chatRoleMapper.insert(chatRole);\n        return chatRole.getId();\n    }\n\n    @Override\n    public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) {\n        // 校验存在\n        validateChatRoleExists(updateReqVO.getId());\n        // 校验文档\n        validateDocuments(updateReqVO.getKnowledgeIds());\n        // 校验工具\n        validateTools(updateReqVO.getToolIds());\n\n        // 更新角色\n        AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);\n        chatRoleMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId) {\n        // 校验存在\n        AiChatRoleDO chatRole = validateChatRoleExists(updateReqVO.getId());\n        if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {\n            throw exception(CHAT_ROLE_NOT_EXISTS);\n        }\n        // 校验文档\n        validateDocuments(updateReqVO.getKnowledgeIds());\n        // 校验工具\n        validateTools(updateReqVO.getToolIds());\n\n        // 更新\n        AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);\n        chatRoleMapper.updateById(updateObj);\n    }\n\n    /**\n     * 校验知识库是否存在\n     *\n     * @param knowledgeIds 知识库编号列表\n     */\n    private void validateDocuments(List<Long> knowledgeIds) {\n        if (CollUtil.isEmpty(knowledgeIds)) {\n            return;\n        }\n        // 校验文档是否存在\n        knowledgeIds.forEach(knowledgeService::validateKnowledgeExists);\n    }\n\n    /**\n     * 校验工具是否存在\n     *\n     * @param toolIds 工具编号列表\n     */\n    private void validateTools(List<Long> toolIds) {\n        if (CollUtil.isEmpty(toolIds)) {\n            return;\n        }\n        // 遍历校验每个工具是否存在\n        toolIds.forEach(toolService::validateToolExists);\n    }\n\n    @Override\n    public void deleteChatRole(Long id) {\n        // 校验存在\n        validateChatRoleExists(id);\n        // 删除\n        chatRoleMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteChatRoleMy(Long id, Long userId) {\n        // 校验存在\n        AiChatRoleDO chatRole = validateChatRoleExists(id);\n        if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {\n            throw exception(CHAT_ROLE_NOT_EXISTS);\n        }\n        // 删除\n        chatRoleMapper.deleteById(id);\n    }\n\n    private AiChatRoleDO validateChatRoleExists(Long id) {\n        AiChatRoleDO chatRole = chatRoleMapper.selectById(id);\n        if (chatRole == null) {\n            throw exception(CHAT_ROLE_NOT_EXISTS);\n        }\n        return chatRole;\n    }\n\n    @Override\n    public AiChatRoleDO getChatRole(Long id) {\n        return chatRoleMapper.selectById(id);\n    }\n\n    @Override\n    public List<AiChatRoleDO> getChatRoleList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return chatRoleMapper.selectByIds(ids);\n    }\n\n    @Override\n    public AiChatRoleDO validateChatRole(Long id) {\n        AiChatRoleDO chatRole = validateChatRoleExists(id);\n        if (CommonStatusEnum.isDisable(chatRole.getStatus())) {\n            throw exception(CHAT_ROLE_DISABLE, chatRole.getName());\n        }\n        return chatRole;\n    }\n\n    @Override\n    public PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO) {\n        return chatRoleMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId) {\n        return chatRoleMapper.selectPageByMy(pageReqVO, userId);\n    }\n\n    @Override\n    public List<String> getChatRoleCategoryList() {\n        List<AiChatRoleDO> list = chatRoleMapper.selectListGroupByCategory(CommonStatusEnum.ENABLE.getStatus());\n        return convertList(list, AiChatRoleDO::getCategory,\n                role -> role != null && StrUtil.isNotBlank(role.getCategory()));\n    }\n\n    @Override\n    public List<AiChatRoleDO> getChatRoleListByName(String name) {\n        return chatRoleMapper.selectListByName(name);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.model;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport dev.tinyflow.core.Tinyflow;\nimport jakarta.validation.Valid;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.image.ImageModel;\nimport org.springframework.ai.vectorstore.VectorStore;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * AI 模型 Service 接口\n *\n * @author fansili\n * @since 2024/4/24 19:42\n */\npublic interface AiModelService {\n\n    /**\n     * 创建模型\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createModel(@Valid AiModelSaveReqVO createReqVO);\n\n    /**\n     * 更新模型\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateModel(@Valid AiModelSaveReqVO updateReqVO);\n\n    /**\n     * 删除模型\n     *\n     * @param id 编号\n     */\n    void deleteModel(Long id);\n\n    /**\n     * 获得模型\n     *\n     * @param id 编号\n     * @return 模型\n     */\n    AiModelDO getModel(Long id);\n\n    /**\n     * 获得默认的模型\n     *\n     * 如果获取不到，则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常\n     *\n     * @return 模型\n     */\n    AiModelDO getRequiredDefaultModel(Integer type);\n\n    /**\n     * 获得模型分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 模型分页\n     */\n    PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO);\n\n    /**\n     * 校验模型是否可使用\n     *\n     * @param id 编号\n     * @return 模型\n     */\n    AiModelDO validateModel(Long id);\n\n    /**\n     * 获得模型列表\n     *\n     * @param status 状态\n     * @param type 类型\n     * @param platform 平台，允许空\n     * @return 模型列表\n     */\n    List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type,\n                                                @Nullable String platform);\n\n    // ========== 与 Spring AI 集成 ==========\n\n    /**\n     * 获得 ChatModel 对象\n     *\n     * @param id 编号\n     * @return ChatModel 对象\n     */\n    ChatModel getChatModel(Long id);\n\n    /**\n     * 获得 ImageModel 对象\n     *\n     * @param id 编号\n     * @return ImageModel 对象\n     */\n    ImageModel getImageModel(Long id);\n\n    /**\n     * 获得 MidjourneyApi 对象\n     *\n     * @param id 编号\n     * @return MidjourneyApi 对象\n     */\n    MidjourneyApi getMidjourneyApi(Long id);\n\n    /**\n     * 获得 SunoApi 对象\n     *\n     * @return SunoApi 对象\n     */\n    SunoApi getSunoApi();\n\n    /**\n     * 获得 VectorStore 对象\n     *\n     * @param id 编号\n     * @param metadataFields 元数据的定义\n     * @return VectorStore 对象\n     */\n    VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields);\n\n    /**\n     * 获取 TinyFlow 所需 LLm Provider\n     *\n     * @param tinyflow tinyflow\n     * @param modelId AI 模型 ID\n     */\n    void getLLmProvider4Tinyflow(Tinyflow tinyflow, Long modelId);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.model;\n\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper;\nimport com.agentsflex.llm.ollama.OllamaLlm;\nimport com.agentsflex.llm.ollama.OllamaLlmConfig;\nimport com.agentsflex.llm.qwen.QwenLlm;\nimport com.agentsflex.llm.qwen.QwenLlmConfig;\nimport dev.tinyflow.core.Tinyflow;\nimport jakarta.annotation.Resource;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.embedding.EmbeddingModel;\nimport org.springframework.ai.image.ImageModel;\nimport org.springframework.ai.vectorstore.SimpleVectorStore;\nimport org.springframework.ai.vectorstore.VectorStore;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;\n\n/**\n * AI 模型 Service 实现类\n *\n * @author fansili\n */\n@Service\n@Validated\npublic class AiModelServiceImpl implements AiModelService {\n\n    @Resource\n    private AiApiKeyService apiKeyService;\n\n    @Resource\n    private AiChatMapper modelMapper;\n\n    @Resource\n    private AiModelFactory modelFactory;\n\n    @Override\n    public Long createModel(AiModelSaveReqVO createReqVO) {\n        // 1. 校验\n        AiPlatformEnum.validatePlatform(createReqVO.getPlatform());\n        apiKeyService.validateApiKey(createReqVO.getKeyId());\n\n        // 2. 插入\n        AiModelDO model = BeanUtils.toBean(createReqVO, AiModelDO.class);\n        modelMapper.insert(model);\n        return model.getId();\n    }\n\n    @Override\n    public void updateModel(AiModelSaveReqVO updateReqVO) {\n        // 1. 校验\n        validateModelExists(updateReqVO.getId());\n        AiPlatformEnum.validatePlatform(updateReqVO.getPlatform());\n        apiKeyService.validateApiKey(updateReqVO.getKeyId());\n\n        // 2. 更新\n        AiModelDO updateObj = BeanUtils.toBean(updateReqVO, AiModelDO.class);\n        modelMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteModel(Long id) {\n        // 校验存在\n        validateModelExists(id);\n        // 删除\n        modelMapper.deleteById(id);\n    }\n\n    private AiModelDO validateModelExists(Long id) {\n        AiModelDO model = modelMapper.selectById(id);\n        if (modelMapper.selectById(id) == null) {\n            throw exception(MODEL_NOT_EXISTS);\n        }\n        return model;\n    }\n\n    @Override\n    public AiModelDO getModel(Long id) {\n        return modelMapper.selectById(id);\n    }\n\n    @Override\n    public AiModelDO getRequiredDefaultModel(Integer type) {\n        AiModelDO model = modelMapper.selectFirstByStatus(type, CommonStatusEnum.ENABLE.getStatus());\n        if (model == null) {\n            throw exception(MODEL_DEFAULT_NOT_EXISTS);\n        }\n        return model;\n    }\n\n    @Override\n    public PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO) {\n        return modelMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public AiModelDO validateModel(Long id) {\n        AiModelDO model = validateModelExists(id);\n        if (CommonStatusEnum.isDisable(model.getStatus())) {\n            throw exception(MODEL_DISABLE);\n        }\n        return model;\n    }\n\n    @Override\n    public List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type, String platform) {\n        return modelMapper.selectListByStatusAndType(status, type, platform);\n    }\n\n    // ========== 与 Spring AI 集成 ==========\n\n    @Override\n    public ChatModel getChatModel(Long id) {\n        AiModelDO model = validateModel(id);\n        AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());\n        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());\n        return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl());\n    }\n\n    @Override\n    public ImageModel getImageModel(Long id) {\n        AiModelDO model = validateModel(id);\n        AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());\n        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());\n        return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl());\n    }\n\n    @Override\n    public MidjourneyApi getMidjourneyApi(Long id) {\n        AiModelDO model = validateModel(id);\n        AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());\n        return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());\n    }\n\n    @Override\n    public SunoApi getSunoApi() {\n        AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey(\n                AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus());\n        return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());\n    }\n\n    @Override\n    public VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields) {\n        // 获取模型 + 密钥\n        AiModelDO model = validateModel(id);\n        AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());\n        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());\n\n        // 创建或获取 EmbeddingModel 对象\n        EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel(\n                platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel());\n\n        // 创建或获取 VectorStore 对象\n         return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields);\n//         return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields);\n//         return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields);\n//         return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields);\n    }\n\n    // TODO @lesan：是不是返回 Llm 对象会好点哈？\n    @Override\n    public void getLLmProvider4Tinyflow(Tinyflow tinyflow, Long modelId) {\n        AiModelDO model = validateModel(modelId);\n        AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());\n        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());\n        switch (platform) {\n            // TODO @lesan 考虑到未来不需要使用agents-flex 现在仅测试通义千问\n            // TODO @lesan：【重要】是不是可以实现一个 SpringAiLlm，这样的话，内部全部用它就好了。只实现 chat 部分；这样，就把 flex 作为一个 agent 框架，内部调用，还是 spring ai 相关的。成本可能低一点？！\n            case TONG_YI:\n                QwenLlmConfig qwenLlmConfig = new QwenLlmConfig();\n                qwenLlmConfig.setApiKey(apiKey.getApiKey());\n                qwenLlmConfig.setModel(model.getModel());\n                // TODO @lesan：这个有点奇怪。。。如果一个链式里，有多个模型，咋整呀。。。\n                tinyflow.setLlmProvider(id -> new QwenLlm(qwenLlmConfig));\n                break;\n            case OLLAMA:\n                OllamaLlmConfig ollamaLlmConfig = new OllamaLlmConfig();\n                ollamaLlmConfig.setEndpoint(apiKey.getUrl());\n                ollamaLlmConfig.setModel(model.getModel());\n                tinyflow.setLlmProvider(id -> new OllamaLlm(ollamaLlmConfig));\n                break;\n        }\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.model;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * AI 工具 Service 接口\n *\n * @author 芋道源码\n */\npublic interface AiToolService {\n\n    /**\n     * 创建工具\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createTool(@Valid AiToolSaveReqVO createReqVO);\n\n    /**\n     * 更新工具\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateTool(@Valid AiToolSaveReqVO updateReqVO);\n\n    /**\n     * 删除工具\n     *\n     * @param id 编号\n     */\n    void deleteTool(Long id);\n\n    /**\n     * 校验工具是否存在\n     *\n     * @param id 编号\n     */\n    void validateToolExists(Long id);\n\n    /**\n     * 获得工具\n     *\n     * @param id 编号\n     * @return 工具\n     */\n    AiToolDO getTool(Long id);\n\n    /**\n     * 获得工具列表\n     *\n     * @param ids 编号列表\n     * @return 工具列表\n     */\n    List<AiToolDO> getToolList(Collection<Long> ids);\n\n    /**\n     * 获得工具分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 工具分页\n     */\n    PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO);\n\n    /**\n     * 获得工具列表\n     *\n     * @param status 状态\n     * @return 工具列表\n     */\n    List<AiToolDO> getToolListByStatus(Integer status);\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.model;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.model.AiToolMapper;\nimport jakarta.annotation.Resource;\nimport org.springframework.ai.tool.ToolCallback;\nimport org.springframework.ai.tool.resolution.ToolCallbackResolver;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NAME_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NOT_EXISTS;\n\n/**\n * AI 工具 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class AiToolServiceImpl implements AiToolService {\n\n    @Resource\n    private AiToolMapper toolMapper;\n\n    @Resource\n    private ToolCallbackResolver toolCallbackResolver;\n\n    @Override\n    public Long createTool(AiToolSaveReqVO createReqVO) {\n        // 校验名称是否存在\n        validateToolNameExists(createReqVO.getName());\n\n        // 插入\n        AiToolDO tool = BeanUtils.toBean(createReqVO, AiToolDO.class);\n        toolMapper.insert(tool);\n        return tool.getId();\n    }\n\n    @Override\n    public void updateTool(AiToolSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        validateToolExists(updateReqVO.getId());\n        // 1.2 校验名称是否存在\n        validateToolNameExists(updateReqVO.getName());\n\n        // 2. 更新\n        AiToolDO updateObj = BeanUtils.toBean(updateReqVO, AiToolDO.class);\n        toolMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteTool(Long id) {\n        // 校验存在\n        validateToolExists(id);\n        // 删除\n        toolMapper.deleteById(id);\n    }\n\n    @Override\n    public void validateToolExists(Long id) {\n        if (toolMapper.selectById(id) == null) {\n            throw exception(TOOL_NOT_EXISTS);\n        }\n    }\n\n    private void validateToolNameExists(String name) {\n        ToolCallback toolCallback = toolCallbackResolver.resolve(name);\n        if (toolCallback == null) {\n            throw exception(TOOL_NAME_NOT_EXISTS, name);\n        }\n    }\n\n    @Override\n    public AiToolDO getTool(Long id) {\n        return toolMapper.selectById(id);\n    }\n\n    @Override\n    public List<AiToolDO> getToolList(Collection<Long> ids) {\n        return toolMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO) {\n        return toolMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<AiToolDO> getToolListByStatus(Integer status) {\n        return toolMapper.selectListByStatus(status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.music;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * AI 音乐 Service 接口\n *\n * @author xiaoxin\n */\npublic interface AiMusicService {\n\n    /**\n     * 音乐生成\n     *\n     * @param userId 用户编号\n     * @param reqVO  请求参数\n     * @return 生成的音乐ID\n     */\n    List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO);\n\n    /**\n     * 同步音乐任务\n     *\n     * @return 同步数量\n     */\n    Integer syncMusic();\n\n    /**\n     * 更新音乐发布状态\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateMusic(@Valid AiMusicUpdateReqVO updateReqVO);\n\n    /**\n     * 更新我的音乐\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateMyMusic(@Valid AiMusicUpdateMyReqVO updateReqVO, Long userId);\n\n    /**\n     * 删除AI 音乐\n     *\n     * @param id 编号\n     */\n    void deleteMusic(Long id);\n\n    /**\n     * 删除【我的】音乐记录\n     *\n     * @param id     音乐编号\n     * @param userId 用户编号\n     */\n    void deleteMusicMy(Long id, Long userId);\n\n    /**\n     * 获得AI 音乐\n     *\n     * @param id 音乐编号\n     * @return 音乐内容\n     */\n    AiMusicDO getMusic(Long id);\n\n    /**\n     * 获得音乐分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 音乐分页\n     */\n    PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO);\n\n    /**\n     * 获得【我的】音乐分页\n     *\n     * @param pageReqVO 分页查询\n     * @param userId    用户编号\n     * @return 音乐分页\n     */\n    PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.music;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.text.StrPool;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.http.HttpUtil;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdateMyReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;\nimport cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;\nimport cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport cn.iocoder.yudao.module.infra.api.file.FileApi;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport javax.annotation.Resource;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.IMAGE_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXISTS;\n\n/**\n * AI 音乐 Service 实现类\n *\n * @author xiaoxin\n */\n@Service\n@Slf4j\npublic class AiMusicServiceImpl implements AiMusicService {\n\n    @Resource\n    private AiModelService modelService;\n\n    @Resource\n    private AiMusicMapper musicMapper;\n\n    @Resource\n    private FileApi fileApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {\n        // 1. 调用 Suno 生成音乐\n        SunoApi sunoApi = modelService.getSunoApi();\n        List<SunoApi.MusicData> musicDataList;\n        if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {\n            // 1.1 描述模式\n            SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(\n                    reqVO.getPrompt(), reqVO.getModel(), reqVO.getMakeInstrumental());\n            musicDataList = sunoApi.generate(generateRequest);\n        } else if (Objects.equals(AiMusicGenerateModeEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {\n            // 1.2 歌词模式\n            SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(\n                    reqVO.getPrompt(), reqVO.getModel(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());\n            musicDataList = sunoApi.customGenerate(generateRequest);\n        } else {\n            throw new IllegalArgumentException(StrUtil.format(\"未知生成模式({})\", reqVO));\n        }\n\n        // 2. 插入数据库\n        if (CollUtil.isEmpty(musicDataList)) {\n            return Collections.emptyList();\n        }\n        List<AiMusicDO> musicList = buildMusicDOList(musicDataList);\n        musicList.forEach(music -> music.setUserId(userId).setPlatform(reqVO.getPlatform()).setGenerateMode(reqVO.getGenerateMode()));\n        musicMapper.insertBatch(musicList);\n        return convertList(musicList, AiMusicDO::getId);\n    }\n\n    @Override\n    public Integer syncMusic() {\n        List<AiMusicDO> streamingTask = musicMapper.selectListByStatus(AiMusicStatusEnum.IN_PROGRESS.getStatus());\n        if (CollUtil.isEmpty(streamingTask)) {\n            return 0;\n        }\n        log.info(\"[syncMusic][Suno 开始同步, 共 ({}) 个任务]\", streamingTask.size());\n\n        // GET 请求，为避免参数过长，分批次处理\n        SunoApi sunoApi = modelService.getSunoApi();\n        CollUtil.split(streamingTask, 36).forEach(chunkList -> {\n            Map<String, Long> taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId);\n            List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));\n            if (CollUtil.isEmpty(musicTaskList)) {\n                log.warn(\"Suno 任务同步失败, 任务ID: [{}]\", taskIdMap.keySet());\n                return;\n            }\n            // 更新进度\n            List<AiMusicDO> updateMusicList = buildMusicDOList(musicTaskList);\n            updateMusicList.forEach(music -> music.setId(taskIdMap.get(music.getTaskId())));\n            musicMapper.updateBatch(updateMusicList);\n        });\n        return streamingTask.size();\n    }\n\n    @Override\n    public void updateMusic(AiMusicUpdateReqVO updateReqVO) {\n        // 校验存在\n        validateMusicExists(updateReqVO.getId());\n        // 更新\n        musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setPublicStatus(updateReqVO.getPublicStatus()));\n    }\n\n    @Override\n    public void updateMyMusic(AiMusicUpdateMyReqVO updateReqVO, Long userId) {\n        // 校验音乐是否存在\n        AiMusicDO musicDO = validateMusicExists(updateReqVO.getId());\n        if (ObjUtil.notEqual(musicDO.getUserId(), userId)) {\n            throw exception(MUSIC_NOT_EXISTS);\n        }\n        // 更新\n        musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setTitle(updateReqVO.getTitle()));\n    }\n\n    @Override\n    public void deleteMusic(Long id) {\n        // 校验存在\n        validateMusicExists(id);\n        // 删除\n        musicMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteMusicMy(Long id, Long userId) {\n        // 1. 校验是否存在\n        AiMusicDO music = validateMusicExists(id);\n        if (ObjUtil.notEqual(music.getUserId(), userId)) {\n            throw exception(IMAGE_NOT_EXISTS);\n        }\n        // 2. 删除记录\n        musicMapper.deleteById(id);\n    }\n\n    @Override\n    public AiMusicDO getMusic(Long id) {\n        return musicMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO) {\n        return musicMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId) {\n        return musicMapper.selectPageByMy(pageReqVO, userId);\n    }\n\n    /**\n     * 构建 AiMusicDO 集合\n     *\n     * @param musicList suno 音乐任务列表\n     * @return AiMusicDO 集合\n     */\n    private List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {\n        return convertList(musicList, musicData -> {\n            Integer status = Objects.equals(\"complete\", musicData.status()) ? AiMusicStatusEnum.SUCCESS.getStatus()\n                    : Objects.equals(\"error\", musicData.status()) ? AiMusicStatusEnum.FAIL.getStatus()\n                    : AiMusicStatusEnum.IN_PROGRESS.getStatus();\n            return new AiMusicDO()\n                    .setTaskId(musicData.id()).setModel(musicData.modelName())\n                    .setDescription(musicData.gptDescriptionPrompt())\n                    .setAudioUrl(downloadFile(status, musicData.audioUrl()))\n                    .setVideoUrl(downloadFile(status, musicData.videoUrl()))\n                    .setImageUrl(downloadFile(status, musicData.imageUrl()))\n                    .setTitle(musicData.title()).setDuration(musicData.duration())\n                    .setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))\n                    .setErrorMessage(musicData.errorMessage())\n                    .setStatus(status);\n        });\n    }\n\n    /**\n     * 音乐生成好后，将音频文件上传到文件服务器\n     *\n     * @param status 音乐状态\n     * @param url    音频文件地址\n     * @return 内部文件地址\n     */\n    private String downloadFile(Integer status, String url) {\n        if (StrUtil.isBlank(url) || ObjectUtil.notEqual(status, AiMusicStatusEnum.SUCCESS.getStatus())) {\n            return url;\n        }\n        try {\n            byte[] bytes = HttpUtil.downloadBytes(url);\n            return fileApi.createFile(bytes);\n        } catch (Exception e) {\n            log.error(\"[downloadFile][url({}) 下载失败]\", url, e);\n            return url;\n        }\n    }\n\n    /**\n     * 校验音乐是否存在\n     *\n     * @param id 音乐编号\n     * @return 音乐信息\n     */\n    private AiMusicDO validateMusicExists(Long id) {\n        AiMusicDO music = musicMapper.selectById(id);\n        if (music == null) {\n            throw exception(MUSIC_NOT_EXISTS);\n        }\n        return music;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.workflow;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO;\nimport javax.validation.Valid;\n\n/**\n * AI 工作流 Service 接口\n *\n * @author lesan\n */\npublic interface AiWorkflowService {\n\n    /**\n     * 创建 AI 工作流\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createWorkflow(@Valid AiWorkflowSaveReqVO createReqVO);\n\n    /**\n     * 更新 AI 工作流\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateWorkflow(@Valid AiWorkflowSaveReqVO updateReqVO);\n\n    /**\n     * 删除 AI 工作流\n     *\n     * @param id 编号\n     */\n    void deleteWorkflow(Long id);\n\n    /**\n     * 获得 AI 工作流\n     *\n     * @param id 编号\n     * @return AI 工作流\n     */\n    AiWorkflowDO getWorkflow(Long id);\n\n    /**\n     * 获得 AI 工作流分页\n     *\n     * @param pageReqVO 分页查询\n     * @return AI 工作流分页\n     */\n    PageResult<AiWorkflowDO> getWorkflowPage(AiWorkflowPageReqVO pageReqVO);\n\n    /**\n     * 测试 AI 工作流\n     *\n     * @param testReqVO 测试数据\n     */\n    Object testWorkflow(AiWorkflowTestReqVO testReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.workflow;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.workflow.AiWorkflowMapper;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\nimport dev.tinyflow.core.Tinyflow;\nimport javax.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WORKFLOW_CODE_EXISTS;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WORKFLOW_NOT_EXISTS;\n\n/**\n * AI 工作流 Service 实现类\n *\n * @author lesan\n */\n@Service\n@Slf4j\npublic class AiWorkflowServiceImpl implements AiWorkflowService {\n\n    @Resource\n    private AiWorkflowMapper workflowMapper;\n\n    @Resource\n    private AiModelService apiModelService;\n\n    @Override\n    public Long createWorkflow(AiWorkflowSaveReqVO createReqVO) {\n        // 1. 参数校验\n        validateCodeUnique(null, createReqVO.getCode());\n\n        // 2. 插入工作流配置\n        AiWorkflowDO workflow = BeanUtils.toBean(createReqVO, AiWorkflowDO.class);\n        workflowMapper.insert(workflow);\n        return workflow.getId();\n    }\n\n    @Override\n    public void updateWorkflow(AiWorkflowSaveReqVO updateReqVO) {\n        // 1. 参数校验\n        validateWorkflowExists(updateReqVO.getId());\n        validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());\n\n        // 2. 更新工作流配置\n        AiWorkflowDO workflow = BeanUtils.toBean(updateReqVO, AiWorkflowDO.class);\n        workflowMapper.updateById(workflow);\n    }\n\n    @Override\n    public void deleteWorkflow(Long id) {\n        // 1. 校验存在\n        validateWorkflowExists(id);\n\n        // 2. 删除工作流配置\n        workflowMapper.deleteById(id);\n    }\n\n    private AiWorkflowDO validateWorkflowExists(Long id) {\n        if (ObjUtil.isNull(id)) {\n            throw exception(WORKFLOW_NOT_EXISTS);\n        }\n        AiWorkflowDO workflow = workflowMapper.selectById(id);\n        if (ObjUtil.isNull(workflow)) {\n            throw exception(WORKFLOW_NOT_EXISTS);\n        }\n        return workflow;\n    }\n\n    private void validateCodeUnique(Long id, String code) {\n        if (StrUtil.isBlank(code)) {\n            return;\n        }\n        AiWorkflowDO workflow = workflowMapper.selectByCode(code);\n        if (ObjUtil.isNull(workflow)) {\n            return;\n        }\n        if (ObjUtil.isNull(id)) {\n            throw exception(WORKFLOW_CODE_EXISTS);\n        }\n        if (ObjUtil.notEqual(workflow.getId(), id)) {\n            throw exception(WORKFLOW_CODE_EXISTS);\n        }\n    }\n\n    @Override\n    public AiWorkflowDO getWorkflow(Long id) {\n        return workflowMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<AiWorkflowDO> getWorkflowPage(AiWorkflowPageReqVO pageReqVO) {\n        return workflowMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public Object testWorkflow(AiWorkflowTestReqVO testReqVO) {\n        // 加载 graph\n        String graph = testReqVO.getGraph() != null ? testReqVO.getGraph()\n                : validateWorkflowExists(testReqVO.getId()).getGraph();\n\n        // 构建 TinyFlow 执行链\n        Tinyflow tinyflow = parseFlowParam(graph);\n\n        // 执行\n        Map<String, Object> variables = testReqVO.getParams();\n        return tinyflow.toChain().executeForResult(variables);\n    }\n\n    private Tinyflow parseFlowParam(String graph) {\n        // TODO @lesan：可以使用 jackson 哇？\n        JSONObject json = JSONObject.parseObject(graph);\n        JSONArray nodeArr = json.getJSONArray(\"nodes\");\n        Tinyflow tinyflow = new Tinyflow(json.toJSONString());\n        for (int i = 0; i < nodeArr.size(); i++) {\n            JSONObject node = nodeArr.getJSONObject(i);\n            switch (node.getString(\"type\")) {\n                case \"llmNode\":\n                    JSONObject data = node.getJSONObject(\"data\");\n                    apiModelService.getLLmProvider4Tinyflow(tinyflow, data.getLong(\"llmId\"));\n                    break;\n                case \"internalNode\":\n                    break;\n                default:\n                    break;\n            }\n        }\n        return tinyflow;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java",
    "content": "package cn.iocoder.yudao.module.ai.service.write;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;\nimport reactor.core.publisher.Flux;\n\n/**\n * AI 写作 Service 接口\n *\n * @author xiaoxin\n */\npublic interface AiWriteService {\n\n    /**\n     * 生成写作内容\n     *\n     * @param generateReqVO 作文生成请求参数\n     * @param userId        用户编号\n     * @return 生成结果\n     */\n    Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId);\n\n    /**\n     * 删除写作\n     *\n     * @param id 编号\n     */\n    void deleteWrite(Long id);\n\n    /**\n     * 获得写作分页\n     *\n     * @param pageReqVO 分页查询\n     * @return AI 写作分页\n     */\n    PageResult<AiWriteDO> getWritePage(AiWritePageReqVO pageReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.service.write;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport cn.iocoder.yudao.module.ai.util.AiUtils;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;\nimport cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;\nimport cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;\nimport cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper;\nimport cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;\nimport cn.iocoder.yudao.module.ai.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;\nimport cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;\nimport cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;\nimport cn.iocoder.yudao.module.ai.service.model.AiModelService;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.model.StreamingChatModel;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.stereotype.Service;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;\n\n/**\n * AI 写作 Service 实现类\n *\n * @author xiaoxin\n */\n@Service\n@Slf4j\npublic class AiWriteServiceImpl implements AiWriteService {\n\n    @Resource\n    private AiModelService modalService;\n    @Resource\n    private AiChatRoleService chatRoleService;\n\n    @Resource\n    private AiWriteMapper writeMapper;\n\n    @Override\n    public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) {\n        // 1 获取写作模型。尝试获取写作助手角色，没有则使用默认模型\n        AiChatRoleDO writeRole = CollUtil.getFirst(\n                chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName()));\n        // 1.1 获取写作执行模型\n        AiModelDO model = getModel(writeRole);\n        // 1.2 获取角色设定消息\n        String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage())\n                ? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage();\n        // 1.3 校验平台\n        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());\n        StreamingChatModel chatModel = modalService.getChatModel(model.getId());\n\n        // 2. 插入写作信息\n        AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, write -> write.setUserId(userId)\n                        .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));\n        writeMapper.insert(writeDO);\n\n        // 3.1 构建 Prompt，并进行调用\n        Prompt prompt = buildPrompt(generateReqVO, model, systemMessage);\n        Flux<ChatResponse> streamResponse = chatModel.stream(prompt);\n\n        // 3.2 流式返回\n        StringBuffer contentBuffer = new StringBuffer();\n        return streamResponse.map(chunk -> {\n            String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;\n            newContent = StrUtil.nullToDefault(newContent, \"\"); // 避免 null 的 情况\n            contentBuffer.append(newContent);\n            // 响应结果\n            return success(newContent);\n        }).doOnComplete(() -> {\n            // 忽略租户，因为 Flux 异步无法透传租户\n            TenantUtils.executeIgnore(() ->\n                    writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString())));\n        }).doOnError(throwable -> {\n            log.error(\"[generateWriteContent][generateReqVO({}) 发生异常]\", generateReqVO, throwable);\n            // 忽略租户，因为 Flux 异步无法透传租户\n            TenantUtils.executeIgnore(() ->\n                    writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage())));\n        }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));\n    }\n\n    private AiModelDO getModel(AiChatRoleDO writeRole) {\n        AiModelDO model = null;\n        if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) {\n            model = modalService.getModel(writeRole.getModelId());\n        }\n        if (model == null) {\n            model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());\n        }\n        // 校验模型存在、且合法\n        if (model == null) {\n            throw exception(MODEL_NOT_EXISTS);\n        }\n        if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) {\n            throw exception(MODEL_USE_TYPE_ERROR);\n        }\n        return model;\n    }\n\n    private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {\n        // 1. 构建 message 列表\n        List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);\n        // 2. 构建 options 对象\n        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());\n        ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());\n        return new Prompt(chatMessages, options);\n    }\n\n    private List<Message> buildMessages(AiWriteGenerateReqVO generateReqVO, String systemMessage) {\n        List<Message> chatMessages = new ArrayList<>();\n        if (StrUtil.isNotBlank(systemMessage)) {\n            // 1.1 角色设定\n            chatMessages.add(new SystemMessage(systemMessage));\n        }\n        // 1.2 用户输入\n        chatMessages.add(new UserMessage(buildUserMessage(generateReqVO)));\n        return chatMessages;\n    }\n\n    private String buildUserMessage(AiWriteGenerateReqVO generateReqVO) {\n        String format = DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.AI_WRITE_FORMAT, generateReqVO.getFormat());\n        String tone = DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.AI_WRITE_TONE, generateReqVO.getTone());\n        String language = DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.AI_WRITE_LANGUAGE, generateReqVO.getLanguage());\n        String length = DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.AI_WRITE_LENGTH, generateReqVO.getLength());\n        // 格式化 prompt\n        String prompt = generateReqVO.getPrompt();\n        if (Objects.equals(generateReqVO.getType(), AiWriteTypeEnum.WRITING.getType())) {\n            return StrUtil.format(AiWriteTypeEnum.WRITING.getPrompt(), prompt, format, tone, language, length);\n        } else {\n            return StrUtil.format(AiWriteTypeEnum.REPLY.getPrompt(), generateReqVO.getOriginalContent(), prompt, format, tone, language, length);\n        }\n    }\n\n    @Override\n    public void deleteWrite(Long id) {\n        // 校验存在\n        validateWriteExists(id);\n        // 删除\n        writeMapper.deleteById(id);\n    }\n\n    private void validateWriteExists(Long id) {\n        if (writeMapper.selectById(id) == null) {\n            throw exception(WRITE_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public PageResult<AiWriteDO> getWritePage(AiWritePageReqVO pageReqVO) {\n        return writeMapper.selectPage(pageReqVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/tool/function/DirectoryListToolFunction.java",
    "content": "package cn.iocoder.yudao.module.ai.tool.function;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.StrUtil;\nimport com.fasterxml.jackson.annotation.JsonClassDescription;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonPropertyDescription;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.stereotype.Component;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * 工具：列出指定目录的文件列表\n *\n * @author 芋道源码\n */\n@Component(\"directory_list\")\npublic class DirectoryListToolFunction implements Function<DirectoryListToolFunction.Request, DirectoryListToolFunction.Response> {\n\n    @Data\n    @JsonClassDescription(\"列出指定目录的文件列表\")\n    public static class Request {\n\n        /**\n         * 目录路径\n         */\n        @JsonProperty(required = true, value = \"path\")\n        @JsonPropertyDescription(\"目录路径，例如说：/Users/yunai\")\n        private String path;\n\n    }\n\n    @Data\n    @AllArgsConstructor\n    @NoArgsConstructor\n    public static class Response {\n\n        /**\n         * 文件列表\n         */\n        private List<File> files;\n\n        @Data\n        public static class File {\n\n            /**\n             * 是否为目录\n             */\n            private Boolean directory;\n\n            /**\n             * 名称\n             */\n            private String name;\n\n            /**\n             * 大小，仅对文件有效\n             */\n            private String size;\n\n            /**\n             * 最后修改时间\n             */\n            private String lastModified;\n\n        }\n\n    }\n\n    @Override\n    public Response apply(Request request) {\n        // 校验目录存在\n        String path = StrUtil.blankToDefault(request.getPath(), \"/\");\n        if (!FileUtil.exist(path) || !FileUtil.isDirectory(path)) {\n            return new Response(Collections.emptyList());\n        }\n        // 列出目录\n        File[] files = FileUtil.ls(path);\n        if (ArrayUtil.isEmpty(files)) {\n            return new Response(Collections.emptyList());\n        }\n        return new Response(convertList(Arrays.asList(files), file ->\n                new Response.File().setDirectory(file.isDirectory()).setName(file.getName())\n                        .setLastModified(LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN))));\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/tool/function/UserProfileQueryToolFunction.java",
    "content": "package cn.iocoder.yudao.module.ai.tool.function;\n\nimport cn.iocoder.yudao.module.ai.util.AiUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.security.core.LoginUser;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.fasterxml.jackson.annotation.JsonClassDescription;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonPropertyDescription;\nimport jakarta.annotation.Resource;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.ai.chat.model.ToolContext;\nimport org.springframework.stereotype.Component;\n\nimport java.util.function.BiFunction;\n\n/**\n * 工具：用户信息查询\n *\n * 同时，也是展示 ToolContext 上下文的使用\n *\n * @author Ren\n */\n@Component(\"user_profile_query\")\npublic class UserProfileQueryToolFunction\n        implements BiFunction<UserProfileQueryToolFunction.Request, ToolContext, UserProfileQueryToolFunction.Response> {\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Data\n    @JsonClassDescription(\"用户信息查询\")\n    public static class Request {\n\n        /**\n         * 用户编号\n         */\n        @JsonProperty(value = \"id\")\n        @JsonPropertyDescription(\"用户编号，例如说：1。如果查询自己，则 id 为空\")\n        private Long id;\n\n    }\n\n    @Data\n    @AllArgsConstructor\n    @NoArgsConstructor\n    public static class Response {\n\n        /**\n         * 用户ID\n         */\n        private Long id;\n        /**\n         * 用户昵称\n         */\n        private String nickname;\n\n        /**\n         * 手机号码\n         */\n        private String mobile;\n        /**\n         * 用户头像\n         */\n        private String avatar;\n\n    }\n\n    @Override\n    public Response apply(Request request, ToolContext toolContext) {\n        Long tenantId = (Long) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_TENANT_ID);\n        if (tenantId == null) {\n            return new Response();\n        }\n        if (request.getId() == null) {\n            LoginUser loginUser = (LoginUser) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_LOGIN_USER);\n            if (loginUser == null) {\n                return new Response();\n            }\n            request.setId(loginUser.getId());\n        }\n        return TenantUtils.execute(tenantId, () -> {\n            AdminUserRespDTO user = adminUserApi.getUser(request.getId()).getCheckedData();\n            return BeanUtils.toBean(user, Response.class);\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/tool/function/WeatherQueryToolFunction.java",
    "content": "package cn.iocoder.yudao.module.ai.tool.function;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.util.RandomUtil;\nimport cn.hutool.core.util.StrUtil;\nimport com.fasterxml.jackson.annotation.JsonClassDescription;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonPropertyDescription;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.stereotype.Component;\n\nimport java.time.LocalDateTime;\nimport java.util.function.Function;\n\nimport static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;\n\n/**\n * 工具：查询指定城市的天气信息\n *\n * @author 芋道源码\n */\n@Component(\"weather_query\")\npublic class WeatherQueryToolFunction\n        implements Function<WeatherQueryToolFunction.Request, WeatherQueryToolFunction.Response> {\n\n    private static final String[] WEATHER_CONDITIONS = { \"晴朗\", \"多云\", \"阴天\", \"小雨\", \"大雨\", \"雷雨\", \"小雪\", \"大雪\" };\n\n    @Data\n    @JsonClassDescription(\"查询指定城市的天气信息\")\n    public static class Request {\n\n        /**\n         * 城市名称\n         */\n        @JsonProperty(required = true, value = \"city\")\n        @JsonPropertyDescription(\"城市名称，例如：北京、上海、广州\")\n        private String city;\n\n    }\n\n    @Data\n    @AllArgsConstructor\n    @NoArgsConstructor\n    public static class Response {\n\n        /**\n         * 城市名称\n         */\n        private String city;\n\n        /**\n         * 天气信息\n         */\n        private WeatherInfo weatherInfo;\n\n        @Data\n        @AllArgsConstructor\n        @NoArgsConstructor\n        public static class WeatherInfo {\n\n            /**\n             * 温度（摄氏度）\n             */\n            private Integer temperature;\n\n            /**\n             * 天气状况\n             */\n            private String condition;\n\n            /**\n             * 湿度百分比\n             */\n            private Integer humidity;\n\n            /**\n             * 风速（km/h）\n             */\n            private Integer windSpeed;\n\n            /**\n             * 查询时间\n             */\n            private String queryTime;\n\n        }\n\n    }\n\n    @Override\n    public Response apply(Request request) {\n        // 检查城市名称是否为空\n        if (StrUtil.isBlank(request.getCity())) {\n            return new Response(\"未知城市\", null);\n        }\n\n        // 获取天气数据\n        String city = request.getCity();\n        Response.WeatherInfo weatherInfo = generateMockWeatherInfo();\n        return new Response(city, weatherInfo);\n    }\n\n    /**\n     * 生成模拟的天气数据\n     * 在实际应用中，应替换为真实 API 调用\n     */\n    private Response.WeatherInfo generateMockWeatherInfo() {\n        int temperature = RandomUtil.randomInt(-5, 30);\n        int humidity = RandomUtil.randomInt(1, 100);\n        int windSpeed = RandomUtil.randomInt(1, 30);\n        String condition = RandomUtil.randomEle(WEATHER_CONDITIONS);\n        return new Response.WeatherInfo(temperature, condition, humidity, windSpeed,\n                LocalDateTimeUtil.format(LocalDateTime.now(), NORM_DATETIME_PATTERN));\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/tool/function/package-info.java",
    "content": "/**\n * 参考 <a href=\"https://docs.spring.io/spring-ai/reference/api/tools.html#_methods_as_tools\">Tool Calling —— Methods as Tools</a>\n */\npackage cn.iocoder.yudao.module.ai.tool.function;"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/tool/method/Person.java",
    "content": "package cn.iocoder.yudao.module.ai.tool.method;\n\n/**\n * 来自 Spring AI 官方文档\n *\n * Represents a person with basic information.\n * This is an immutable record.\n */\npublic record Person(\n        int id,\n        String firstName,\n        String lastName,\n        String email,\n        String sex,\n        String ipAddress,\n        String jobTitle,\n        int age\n) {\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonService.java",
    "content": "package cn.iocoder.yudao.module.ai.tool.method;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * 来自 Spring AI 官方文档\n *\n * Service interface for managing Person data.\n * Defines the contract for CRUD operations and search/filter functionalities.\n */\npublic interface PersonService {\n\n    /**\n     * Creates a new Person record.\n     * Assigns a unique ID to the person and stores it.\n     *\n     * @param personData The data for the new person (ID field is ignored). Must not be null.\n     * @return The created Person record, including the generated ID.\n     */\n    Person createPerson(Person personData);\n\n    /**\n     * Retrieves a Person by their unique ID.\n     *\n     * @param id The ID of the person to retrieve.\n     * @return An Optional containing the found Person, or an empty Optional if not found.\n     */\n    Optional<Person> getPersonById(int id);\n\n    /**\n     * Retrieves all Person records currently stored.\n     *\n     * @return An unmodifiable List containing all Persons. Returns an empty list if none exist.\n     */\n    List<Person> getAllPersons();\n\n    /**\n     * Updates an existing Person record identified by ID.\n     * Replaces the existing data with the provided data, keeping the original ID.\n     *\n     * @param id                The ID of the person to update.\n     * @param updatedPersonData The new data for the person (ID field is ignored). Must not be null.\n     * @return true if the person was found and updated, false otherwise.\n     */\n    boolean updatePerson(int id, Person updatedPersonData);\n\n    /**\n     * Deletes a Person record identified by ID.\n     *\n     * @param id The ID of the person to delete.\n     * @return true if the person was found and deleted, false otherwise.\n     */\n    boolean deletePerson(int id);\n\n    /**\n     * Searches for Persons whose job title contains the given query string (case-insensitive).\n     *\n     * @param jobTitleQuery The string to search for within job titles. Can be null or blank.\n     * @return An unmodifiable List of matching Persons. Returns an empty list if no matches or query is invalid.\n     */\n    List<Person> searchByJobTitle(String jobTitleQuery);\n\n    /**\n     * Filters Persons by their exact sex (case-insensitive).\n     *\n     * @param sex The sex to filter by (e.g., \"Male\", \"Female\"). Can be null or blank.\n     * @return An unmodifiable List of matching Persons. Returns an empty list if no matches or filter is invalid.\n     */\n    List<Person> filterBySex(String sex);\n\n    /**\n     * Filters Persons by their exact age.\n     *\n     * @param age The age to filter by.\n     * @return An unmodifiable List of matching Persons. Returns an empty list if no matches.\n     */\n    List<Person> filterByAge(int age);\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.ai.tool.method;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.ai.tool.annotation.Tool;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.PostConstruct;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * 来自 Spring AI 官方文档\n *\n * Implementation of the PersonService interface using an in-memory data store.\n * Manages a collection of Person objects loaded from embedded CSV data.\n * This class is thread-safe due to the use of ConcurrentHashMap and AtomicInteger.\n */\n@Service\n@Slf4j\npublic class PersonServiceImpl implements PersonService {\n\n    private final Map<Integer, Person> personStore = new ConcurrentHashMap<>();\n\n    private AtomicInteger idGenerator;\n\n    /**\n     * Embedded CSV data for initial population\n     */\n    private static final String CSV_DATA = \"\"\"\n            Id,FirstName,LastName,Email,Sex,IpAddress,JobTitle,Age\n            1,Fons,Tollfree,ftollfree0@senate.gov,Male,55.1 Tollfree Lane,Research Associate,31\n            2,Emlynne,Tabourier,etabourier1@networksolutions.com,Female,18 Tabourier Way,Associate Professor,38\n            3,Shae,Johncey,sjohncey2@yellowpages.com,Male,1 Johncey Circle,Structural Analysis Engineer,30\n            4,Sebastien,Bradly,sbradly3@mapquest.com,Male,2 Bradly Hill,Chief Executive Officer,40\n            5,Harriott,Kitteringham,hkitteringham4@typepad.com,Female,3 Kitteringham Drive,VP Sales,47\n            6,Anallise,Parradine,aparradine5@miibeian.gov.cn,Female,4 Parradine Street,Analog Circuit Design manager,44\n            7,Gorden,Kirkbright,gkirkbright6@reuters.com,Male,5 Kirkbright Plaza,Senior Editor,40\n            8,Veradis,Ledwitch,vledwitch7@google.com.au,Female,6 Ledwitch Avenue,Computer Systems Analyst IV,44\n            9,Agnesse,Penhalurick,apenhalurick8@google.it,Female,7 Penhalurick Terrace,Automation Specialist IV,41\n            10,Bibby,Hutable,bhutable9@craigslist.org,Female,8 Hutable Place,Account Representative I,43\n            11,Karoly,Lightoller,klightollera@rakuten.co.jp,Female,9 Lightoller Parkway,Senior Developer,46\n            12,Cristine,Durrad,cdurradb@aol.com,Female,10 Durrad Center,Senior Developer,48\n            13,Aggy,Napier,anapierc@hostgator.com,Female,11 Napier Court,VP Product Management,44\n            14,Prisca,Caddens,pcaddensd@vinaora.com,Female,12 Caddens Alley,Business Systems Development Analyst,41\n            15,Khalil,McKernan,kmckernane@google.fr,Male,13 McKernan Pass,Engineer IV,44\n            16,Lorry,MacTrusty,lmactrustyf@eventbrite.com,Male,14 MacTrusty Junction,Design Engineer,42\n            17,Casandra,Worsell,cworsellg@goo.gl,Female,15 Worsell Point,Systems Administrator IV,45\n            18,Ulrikaumeko,Haveline,uhavelineh@usgs.gov,Female,16 Haveline Trail,Financial Advisor,42\n            19,Shurlocke,Albany,salbanyi@artisteer.com,Male,17 Albany Plaza,Software Test Engineer III,46\n            20,Myrilla,Brimilcombe,mbrimilcombej@accuweather.com,Female,18 Brimilcombe Road,Programmer Analyst I,48\n            21,Carlina,Scimonelli,cscimonellik@va.gov,Female,19 Scimonelli Pass,Help Desk Technician,45\n            22,Tina,Goullee,tgoulleel@miibeian.gov.cn,Female,20 Goullee Crossing,Accountant IV,43\n            23,Adriaens,Storek,astorekm@devhub.com,Female,21 Storek Avenue,Recruiting Manager,40\n            24,Tedra,Giraudot,tgiraudotn@wiley.com,Female,22 Giraudot Terrace,Speech Pathologist,47\n            25,Josiah,Soares,jsoareso@google.nl,Male,23 Soares Street,Tax Accountant,45\n            26,Kayle,Gaukrodge,kgaukrodgep@wikispaces.com,Female,24 Gaukrodge Parkway,Accountant II,43\n            27,Ardys,Chuter,achuterq@ustream.tv,Female,25 Chuter Drive,Engineer IV,41\n            28,Francyne,Baudinet,fbaudinetr@newyorker.com,Female,26 Baudinet Center,VP Accounting,48\n            29,Gerick,Bullan,gbullans@seesaa.net,Male,27 Bullan Way,Senior Financial Analyst,43\n            30,Northrup,Grivori,ngrivorit@unc.edu,Male,28 Grivori Plaza,Systems Administrator I,45\n            31,Town,Duguid,tduguidu@squarespace.com,Male,29 Duguid Pass,Safety Technician IV,46\n            32,Pierette,Kopisch,pkopischv@google.com.br,Female,30 Kopisch Lane,Director of Sales,41\n            33,Jacquenetta,Le Prevost,jleprevostw@netlog.com,Female,31 Le Prevost Trail,Senior Developer,47\n            34,Garvy,Rusted,grustedx@aboutads.info,Male,32 Rusted Junction,Senior Developer,42\n            35,Clarice,Aysh,cayshy@merriam-webster.com,Female,33 Aysh Avenue,VP Quality Control,40\n            36,Tracie,Fedorski,tfedorskiz@bloglines.com,Male,34 Fedorski Terrace,Design Engineer,44\n            37,Noelyn,Matushenko,nmatushenko10@globo.com,Female,35 Matushenko Place,VP Sales,48\n            38,Rudiger,Klaesson,rklaesson11@usnews.com,Male,36 Klaesson Road,Database Administrator IV,43\n            39,Mirella,Syddie,msyddie12@geocities.jp,Female,37 Syddie Circle,Geological Engineer,46\n            40,Donalt,O'Lunny,dolunny13@elpais.com,Male,38 O'Lunny Center,Analog Circuit Design manager,41\n            41,Guntar,Deniskevich,gdeniskevich14@google.com.hk,Male,39 Deniskevich Way,Structural Engineer,47\n            42,Hort,Shufflebotham,hshufflebotham15@about.me,Male,40 Shufflebotham Court,Structural Analysis Engineer,45\n            43,Dominique,Thickett,dthickett16@slashdot.org,Male,41 Thickett Crossing,Safety Technician I,42\n            44,Zebulen,Piscopello,zpiscopello17@umich.edu,Male,42 Piscopello Parkway,Web Developer II,40\n            45,Mellicent,Mac Giany,mmacgiany18@state.tx.us,Female,43 Mac Giany Pass,Assistant Manager,44\n            46,Merle,Bounds,mbounds19@amazon.co.jp,Female,44 Bounds Alley,Systems Administrator III,41\n            47,Madelle,Farbrace,mfarbrace1a@xinhuanet.com,Female,45 Farbrace Terrace,Quality Engineer,48\n            48,Galvin,O'Sheeryne,gosheeryne1b@addtoany.com,Male,46 O'Sheeryne Way,Environmental Specialist,43\n            49,Guillemette,Bootherstone,gbootherstone1c@nationalgeographic.com,Female,47 Bootherstone Plaza,Professor,46\n            50,Letti,Aylmore,laylmore1d@vinaora.com,Female,48 Aylmore Circle,Automation Specialist I,40\n            51,Nonie,Rivalland,nrivalland1e@weather.com,Female,49 Rivalland Avenue,Software Test Engineer IV,45\n            52,Jacquelynn,Halfacre,jhalfacre1f@surveymonkey.com,Female,50 Halfacre Pass,Geologist II,42\n            53,Anderea,MacKibbon,amackibbon1g@weibo.com,Female,51 MacKibbon Parkway,Automation Specialist II,47\n            54,Wash,Klimko,wklimko1h@slashdot.org,Male,52 Klimko Alley,Database Administrator I,40\n            55,Flori,Kynett,fkynett1i@auda.org.au,Female,53 Kynett Trail,Quality Control Specialist,46\n            56,Libbey,Penswick,lpenswick1j@google.co.uk,Female,54 Penswick Point,VP Accounting,43\n            57,Silvanus,Skellorne,sskellorne1k@booking.com,Male,55 Skellorne Drive,Account Executive,48\n            58,Carmine,Mateos,cmateos1l@plala.or.jp,Male,56 Mateos Terrace,Systems Administrator I,41\n            59,Sheffie,Blazewicz,sblazewicz1m@google.com.au,Male,57 Blazewicz Center,VP Sales,44\n            60,Leanor,Worsnop,lworsnop1n@uol.com.br,Female,58 Worsnop Plaza,Systems Administrator III,45\n            61,Caspar,Pamment,cpamment1o@google.co.jp,Male,59 Pamment Court,Senior Financial Analyst,42\n            62,Justinian,Pentycost,jpentycost1p@sciencedaily.com,Male,60 Pentycost Way,Senior Quality Engineer,47\n            63,Gerianne,Jarnell,gjarnell1q@bing.com,Female,61 Jarnell Avenue,Help Desk Operator,40\n            64,Boycie,Zanetto,bzanetto1r@about.com,Male,62 Zanetto Place,Quality Engineer,46\n            65,Camilla,Mac Giany,cmacgiany1s@state.gov,Female,63 Mac Giany Parkway,Senior Cost Accountant,43\n            66,Hadlee,Piscopiello,hpiscopiello1t@artisteer.com,Male,64 Piscopiello Street,Account Representative III,48\n            67,Bobbie,Penvarden,bpenvarden1u@google.cn,Male,65 Penvarden Lane,Help Desk Operator,41\n            68,Ali,Gowlett,agowlett1v@parallels.com,Male,66 Gowlett Pass,VP Marketing,44\n            69,Olivette,Acome,oacome1w@qq.com,Female,67 Acome Hill,VP Product Management,45\n            70,Jehanna,Brotherheed,jbrotherheed1x@google.nl,Female,68 Brotherheed Junction,Database Administrator III,42\n            71,Morgan,Berthomieu,mberthomieu1y@artisteer.com,Male,69 Berthomieu Alley,Systems Administrator II,47\n            72,Linzy,Shilladay,lshilladay1z@icq.com,Female,70 Shilladay Trail,Research Assistant IV,40\n            73,Faydra,Brimner,fbrimner20@mozilla.org,Female,71 Brimner Road,Senior Editor,46\n            74,Gwenore,Oxlee,goxlee21@devhub.com,Female,72 Oxlee Terrace,Systems Administrator II,43\n            75,Evangelin,Beinke,ebeinke22@mozilla.com,Female,73 Beinke Circle,Accountant I,48\n            76,Missy,Cockling,mcockling23@si.edu,Female,74 Cockling Way,Software Engineer I,41\n            77,Suzanne,Klimschak,sklimschak24@etsy.com,Female,75 Klimschak Plaza,Tax Accountant,44\n            78,Candide,Goricke,cgoricke25@weebly.com,Female,76 Goricke Pass,Sales Associate,45\n            79,Gerome,Pinsent,gpinsent26@google.com.au,Male,77 Pinsent Junction,Software Consultant,42\n            80,Lezley,Mac Giany,lmacgiany27@scribd.com,Male,78 Mac Giany Alley,Operator,47\n            81,Tobiah,Durn,tdurn28@state.tx.us,Male,79 Durn Court,VP Sales,40\n            82,Sherlocke,Cockshoot,scockshoot29@yelp.com,Male,80 Cockshoot Street,Senior Financial Analyst,46\n            83,Myrle,Speenden,mspeenden2a@utexas.edu,Female,81 Speenden Center,Senior Developer,43\n            84,Isidore,Gorries,igorries2b@flavors.me,Male,82 Gorries Parkway,Sales Representative,48\n            85,Isac,Kitchingman,ikitchingman2c@businessinsider.com,Male,83 Kitchingman Drive,VP Accounting,41\n            86,Benedetta,Purrier,bpurrier2d@admin.ch,Female,84 Purrier Trail,VP Accounting,44\n            87,Tera,Fitchell,tfitchell2e@fotki.com,Female,85 Fitchell Place,Software Engineer IV,45\n            88,Abbe,Pamment,apamment2f@about.com,Male,86 Pamment Avenue,VP Sales,42\n            89,Jandy,Gommowe,jgommowe2g@angelfire.com,Female,87 Gommowe Road,Financial Analyst,47\n            90,Karena,Fussey,kfussey2h@google.com.au,Female,88 Fussey Point,Assistant Professor,40\n            91,Gaspar,Pammenter,gpammenter2i@google.com.br,Male,89 Pammenter Hill,Help Desk Operator,46\n            92,Stanwood,Mac Giany,smacgiany2j@prlog.org,Male,90 Mac Giany Terrace,Research Associate,43\n            93,Byrom,Beedell,bbeedell2k@google.co.jp,Male,91 Beedell Way,VP Sales,48\n            94,Annabella,Rowbottom,arowbottom2l@google.com.au,Female,92 Rowbottom Plaza,Help Desk Operator,41\n            95,Rodolphe,Debell,rdebell2m@imageshack.us,Male,93 Debell Pass,Design Engineer,44\n            96,Tyne,Gommey,tgommey2n@joomla.org,Female,94 Gommey Junction,VP Marketing,45\n            97,Christoper,Pincked,cpincked2o@icq.com,Male,95 Pincked Alley,Human Resources Manager,42\n            98,Kore,Le Prevost,kleprevost2p@tripadvisor.com,Female,96 Le Prevost Street,VP Quality Control,47\n            99,Ceciley,Petrolli,cpetrolli2q@oaic.gov.au,Female,97 Petrolli Court,Senior Developer,40\n            100,Elspeth,Mac Giany,emacgiany2r@icio.us,Female,98 Mac Giany Parkway,Internal Auditor,46\n            \"\"\";\n\n    /**\n     * Initializes the service after dependency injection by loading data from the CSV string.\n     * Uses @PostConstruct to ensure this runs after the bean is created.\n     */\n    @PostConstruct\n    private void initializeData() {\n        log.info(\"Initializing PersonService data store...\");\n        int maxId = loadDataFromCsv();\n        idGenerator = new AtomicInteger(maxId);\n        log.info(\"PersonService initialized with {} records. Next ID: {}\", personStore.size(), idGenerator.get() + 1);\n    }\n\n    /**\n     * Parses the embedded CSV data and populates the in-memory store.\n     * Calculates the maximum ID found in the data to initialize the ID generator.\n     *\n     * @return The maximum ID found in the loaded CSV data.\n     */\n    private int loadDataFromCsv() {\n        final AtomicInteger currentMaxId = new AtomicInteger(0);\n        // Clear existing data before loading (important for tests or re-initialization scenarios)\n        personStore.clear();\n        try (Stream<String> lines = CSV_DATA.lines().skip(1)) { // Skip header row\n            lines.forEach(line -> {\n                try {\n                    // Split carefully, handling potential commas within quoted fields if necessary (simple split here)\n                    String[] fields = line.split(\",\", 8); // Limit split to handle potential commas in job title\n                    if (fields.length == 8) {\n                        int id = Integer.parseInt(fields[0].trim());\n                        String firstName = fields[1].trim();\n                        String lastName = fields[2].trim();\n                        String email = fields[3].trim();\n                        String sex = fields[4].trim();\n                        String ipAddress = fields[5].trim();\n                        String jobTitle = fields[6].trim();\n                        int age = Integer.parseInt(fields[7].trim());\n\n                        Person person = new Person(id, firstName, lastName, email, sex, ipAddress, jobTitle, age);\n                        personStore.put(id, person);\n                        currentMaxId.updateAndGet(max -> Math.max(max, id));\n                    } else {\n                        log.warn(\"Skipping malformed CSV line (expected 8 fields, found {}): {}\", fields.length, line);\n                    }\n                } catch (NumberFormatException e) {\n                    log.warn(\"Skipping line due to parsing error (ID or Age): {} - Error: {}\", line, e.getMessage());\n                } catch (Exception e) {\n                    log.error(\"Skipping line due to unexpected error: {} - Error: {}\", line, e.getMessage(), e);\n                }\n            });\n        } catch (Exception e) {\n            log.error(\"Fatal error reading embedded CSV data: {}\", e.getMessage(), e);\n            // In a real application, might throw a specific initialization exception\n        }\n        return currentMaxId.get();\n    }\n\n    @Override\n    @Tool(\n        name = \"ps_create_person\",\n        description = \"Create a new person record in the in-memory store.\"\n    )\n    public Person createPerson(Person personData) {\n        if (personData == null) {\n            throw new IllegalArgumentException(\"Person data cannot be null\");\n        }\n        int newId = idGenerator.incrementAndGet();\n        // Create a new Person record using data from the input, but with the generated ID\n        Person newPerson = new Person(\n                newId,\n                personData.firstName(),\n                personData.lastName(),\n                personData.email(),\n                personData.sex(),\n                personData.ipAddress(),\n                personData.jobTitle(),\n                personData.age()\n        );\n        personStore.put(newId, newPerson);\n        log.debug(\"Created person: {}\", newPerson);\n        return newPerson;\n    }\n\n    @Override\n    @Tool(\n        name = \"ps_get_person_by_id\",\n        description = \"Retrieve a person record by ID from the in-memory store.\"\n    )\n    public Optional<Person> getPersonById(int id) {\n        Person person = personStore.get(id);\n        log.debug(\"Retrieved person by ID {}: {}\", id, person);\n        return Optional.ofNullable(person);\n    }\n\n    @Override\n    @Tool(\n        name = \"ps_get_all_persons\",\n        description = \"Retrieve all person records from the in-memory store.\"\n    )\n    public List<Person> getAllPersons() {\n        // Return an unmodifiable view of the values\n        List<Person> allPersons = personStore.values().stream().toList();\n        log.debug(\"Retrieved all persons (count: {})\", allPersons.size());\n        return allPersons;\n    }\n\n    @Override\n    @Tool(\n        name = \"ps_update_person\",\n        description = \"Update an existing person record by ID in the in-memory store.\"\n    )\n    public boolean updatePerson(int id, Person updatedPersonData) {\n         if (updatedPersonData == null) {\n            throw new IllegalArgumentException(\"Updated person data cannot be null\");\n        }\n        // Use computeIfPresent for atomic update if the key exists\n        Person result = personStore.computeIfPresent(id, (key, existingPerson) ->\n                // Create a new Person record with the original ID but updated data\n                new Person(\n                        id, // Keep original ID\n                        updatedPersonData.firstName(),\n                        updatedPersonData.lastName(),\n                        updatedPersonData.email(),\n                        updatedPersonData.sex(),\n                        updatedPersonData.ipAddress(),\n                        updatedPersonData.jobTitle(),\n                        updatedPersonData.age()\n                )\n        );\n        boolean updated = result != null;\n        log.debug(\"Update attempt for ID {}: {}\", id, updated ? \"Successful\" : \"Failed (Not Found)\");\n        if(updated) log.trace(\"Updated person data for ID {}: {}\", id, result);\n        return updated;\n    }\n\n    @Override\n    @Tool(\n        name = \"ps_delete_person\",\n        description = \"Delete a person record by ID from the in-memory store.\"\n    )\n    public boolean deletePerson(int id) {\n        boolean removed = personStore.remove(id) != null;\n        log.debug(\"Delete attempt for ID {}: {}\", id, removed ? \"Successful\" : \"Failed (Not Found)\");\n        return removed;\n    }\n\n    @Override\n    @Tool(\n        name = \"ps_search_by_job_title\",\n        description = \"Search for persons by job title in the in-memory store.\"\n    )\n    public List<Person> searchByJobTitle(String jobTitleQuery) {\n        if (jobTitleQuery == null || jobTitleQuery.isBlank()) {\n            log.debug(\"Search by job title skipped due to blank query.\");\n            return Collections.emptyList();\n        }\n        String lowerCaseQuery = jobTitleQuery.toLowerCase();\n        List<Person> results = personStore.values().stream()\n                .filter(person -> person.jobTitle() != null && person.jobTitle().toLowerCase().contains(lowerCaseQuery))\n                .collect(Collectors.toList());\n        log.debug(\"Search by job title '{}' found {} results.\", jobTitleQuery, results.size());\n        return Collections.unmodifiableList(results);\n    }\n\n    @Override\n    @Tool(\n        name = \"ps_filter_by_sex\",\n        description = \"Filters Persons by sex (case-insensitive).\"\n    )\n    public List<Person> filterBySex(String sex) {\n        if (sex == null || sex.isBlank()) {\n             log.debug(\"Filter by sex skipped due to blank filter.\");\n            return Collections.emptyList();\n        }\n        List<Person> results = personStore.values().stream()\n                .filter(person -> person.sex() != null && person.sex().equalsIgnoreCase(sex))\n                .collect(Collectors.toList());\n        log.debug(\"Filter by sex '{}' found {} results.\", sex, results.size());\n        return Collections.unmodifiableList(results);\n    }\n\n    @Override\n    @Tool(\n        name = \"ps_filter_by_age\",\n        description = \"Filters Persons by age.\"\n    )\n    public List<Person> filterByAge(int age) {\n         if (age < 0) {\n            log.debug(\"Filter by age skipped due to negative age: {}\", age);\n            return Collections.emptyList(); // Or throw IllegalArgumentException based on requirements\n        }\n        List<Person> results = personStore.values().stream()\n                .filter(person -> person.age() == age)\n                .collect(Collectors.toList());\n        log.debug(\"Filter by age {} found {} results.\", age, results.size());\n        return Collections.unmodifiableList(results);\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/tool/method/package-info.java",
    "content": "/**\n * 参考 <a href=\"https://docs.spring.io/spring-ai/reference/api/tools.html#_methods_as_tools\">Tool Calling —— Methods as Tools</a>\n */\npackage cn.iocoder.yudao.module.ai.tool.method;"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java",
    "content": "package cn.iocoder.yudao.module.ai.util;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;\nimport com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;\nimport org.springaicommunity.moonshot.MoonshotChatOptions;\nimport org.springaicommunity.qianfan.QianFanChatOptions;\nimport org.springframework.ai.anthropic.AnthropicChatOptions;\nimport org.springframework.ai.azure.openai.AzureOpenAiChatOptions;\nimport org.springframework.ai.chat.messages.*;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.ChatOptions;\nimport org.springframework.ai.deepseek.DeepSeekAssistantMessage;\nimport org.springframework.ai.deepseek.DeepSeekChatOptions;\nimport org.springframework.ai.minimax.MiniMaxChatOptions;\nimport org.springframework.ai.ollama.api.OllamaChatOptions;\nimport org.springframework.ai.openai.OpenAiChatOptions;\nimport org.springframework.ai.tool.ToolCallback;\nimport org.springframework.ai.zhipuai.ZhiPuAiChatOptions;\n\nimport java.util.*;\n\n/**\n * Spring AI 工具类\n *\n * @author 芋道源码\n */\npublic class AiUtils {\n\n    public static final String TOOL_CONTEXT_LOGIN_USER = \"LOGIN_USER\";\n    public static final String TOOL_CONTEXT_TENANT_ID = \"TENANT_ID\";\n\n    public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) {\n        return buildChatOptions(platform, model, temperature, maxTokens, null, null);\n    }\n\n    public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens,\n                                               List<ToolCallback> toolCallbacks, Map<String, Object> toolContext) {\n        toolCallbacks = ObjUtil.defaultIfNull(toolCallbacks, Collections.emptyList());\n        toolContext = ObjUtil.defaultIfNull(toolContext, Collections.emptyMap());\n        // noinspection EnhancedSwitchMigration\n        switch (platform) {\n            case TONG_YI:\n                return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens)\n                        .withEnableThinking(true) // TODO 芋艿：默认都开启 thinking 模式，后续可以让用户配置\n                        .withToolCallbacks(toolCallbacks).withToolContext(toolContext).build();\n            case YI_YAN:\n                return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build();\n            case DEEP_SEEK:\n            case DOU_BAO: // 复用 DeepSeek 客户端\n            case HUN_YUAN: // 复用 DeepSeek 客户端\n            case SILICON_FLOW: // 复用 DeepSeek 客户端\n            case XING_HUO: // 复用 DeepSeek 客户端\n                return DeepSeekChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)\n                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();\n            case ZHI_PU:\n                return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)\n                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();\n            case MINI_MAX:\n                return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)\n                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();\n            case MOONSHOT:\n                return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)\n                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();\n            case OPENAI:\n            case GEMINI: // 复用 OpenAI 客户端\n            case BAI_CHUAN: // 复用 OpenAI 客户端\n            case GROK: // 复用 OpenAI 客户端\n                return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)\n                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();\n            case AZURE_OPENAI:\n                return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens)\n                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();\n            case ANTHROPIC:\n                return AnthropicChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)\n                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();\n            case OLLAMA:\n                return OllamaChatOptions.builder().model(model).temperature(temperature).numPredict(maxTokens)\n                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();\n            default:\n                throw new IllegalArgumentException(StrUtil.format(\"未知平台({})\", platform));\n        }\n    }\n\n    public static Message buildMessage(String type, String content) {\n        if (MessageType.USER.getValue().equals(type)) {\n            return new UserMessage(content);\n        }\n        if (MessageType.ASSISTANT.getValue().equals(type)) {\n            return new AssistantMessage(content);\n        }\n        if (MessageType.SYSTEM.getValue().equals(type)) {\n            return new SystemMessage(content);\n        }\n        if (MessageType.TOOL.getValue().equals(type)) {\n            throw new UnsupportedOperationException(\"暂不支持 tool 消息：\" + content);\n        }\n        throw new IllegalArgumentException(StrUtil.format(\"未知消息类型({})\", type));\n    }\n\n    public static Map<String, Object> buildCommonToolContext() {\n        Map<String, Object> context = new HashMap<>();\n        context.put(TOOL_CONTEXT_LOGIN_USER, SecurityFrameworkUtils.getLoginUser());\n        context.put(TOOL_CONTEXT_TENANT_ID, TenantContextHolder.getTenantId());\n        return context;\n    }\n\n    @SuppressWarnings(\"ConstantValue\")\n    public static String getChatResponseContent(ChatResponse response) {\n        if (response == null\n                || response.getResult() == null\n                || response.getResult().getOutput() == null) {\n            return null;\n        }\n        return response.getResult().getOutput().getText();\n    }\n\n    @SuppressWarnings(\"ConstantValue\")\n    public static String getChatResponseReasoningContent(ChatResponse response) {\n        if (response == null\n                || response.getResult() == null\n                || response.getResult().getOutput() == null) {\n            return null;\n        }\n        if (response.getResult().getOutput() instanceof DeepSeekAssistantMessage) {\n            return ((DeepSeekAssistantMessage) (response.getResult().getOutput())).getReasoningContent();\n        }\n        return null;\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/FileTypeUtils.java",
    "content": "package cn.iocoder.yudao.module.ai.util;\n\nimport cn.hutool.core.util.StrUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.tika.Tika;\n\n/**\n * 文件类型 Utils\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class FileTypeUtils {\n\n    private static final Tika TIKA = new Tika();\n\n    /**\n     * 已知文件名，获取文件类型，在某些情况下比通过字节数组准确，例如使用 jar 文件时，通过名字更为准确\n     *\n     * @param name 文件名\n     * @return mineType 无法识别时会返回“application/octet-stream”\n     */\n    public static String getMineType(String name) {\n        return TIKA.detect(name);\n    }\n\n    /**\n     * 判断是否是图片\n     *\n     * @param mineType 类型\n     * @return 是否是图片\n     */\n    public static boolean isImage(String mineType) {\n        return StrUtil.startWith(mineType, \"image/\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant，手动创建\n      - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus，手动创建\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 5 # 初始连接数\n        min-idle: 10 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n        slave: # 模拟从库，可根据自己需要修改 # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 400-infra.server.iocoder.cn # 地址\n    port: 6379 # 端口\n    database: 1 # 数据库索引\n#      password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n\nspring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n      # Spring Boot Admin Server 服务端的相关配置\n      context-path: /admin # 配置 Spring\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  demo: true # 开启演示模式\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant，手动创建\n      - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus，手动创建\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 1 # 初始连接数\n        min-idle: 1 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例\n          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例\n          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例\n          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例\n          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例\n          username: root\n          password: 123456\n        #          username: sa # SQL Server 连接的示例\n        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例\n        #          username: SYSDBA # DM 连接的示例\n        #          password: SYSDBA # DM 连接的示例\n        slave: # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 6379 # 端口\n    database: 0 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n# 日志文件配置\nlogging:\n  level:\n    # 配置自己写的 MyBatis Mapper 打印日志\n    cn.iocoder.yudao.module.ai.dal.mysql: debug\n    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿：先禁用，Spring Boot 3.X 存在部分错误的 WARN 提示\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  env: # 多环境的配置项\n    tag: ${HOSTNAME}\n  security:\n    mock-enable: true\n  access-log: # 访问日志的配置项\n    enable: false"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: ai-server\n\n  profiles:\n    active: local\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n    allow-bean-definition-overriding: true # 允许 Bean 覆盖，例如说 Feign 等会存在重复定义的服务\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  # Servlet 配置\n  servlet:\n    # 文件上传相关配置项\n    multipart:\n      max-file-size: 16MB # 单个文件大小\n      max-request-size: 32MB # 设置总上传的文件大小\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  # Cache 配置项\n  cache:\n    type: REDIS\n    redis:\n      time-to-live: 1h # 设置过期时间为 1 小时\n\nserver:\n  port: 48090\n  servlet:\n    encoding:\n      enabled: true\n      charset: UTF-8 # 必须设置 UTF-8，避免 WebFlux 流式返回（AI 场景）会乱码问题\n      force: true\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\n--- #################### 接口文档配置 ####################\n\nspringdoc:\n  api-docs:\n    enabled: true # 1. 是否开启 Swagger 接文档的元数据\n    path: /v3/api-docs\n  swagger-ui:\n    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面\n    path: /swagger-ui\n  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档\n\nknife4j:\n  enable: true\n  setting:\n    language: zh_cn\n\n# MyBatis Plus 的配置项\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: NONE # “智能”模式，基于 IdTypeEnvironmentPostProcessor + 数据源的类型，自动适配成 AUTO、INPUT 模式。\n      #      id-type: AUTO # 自增 ID，适合 MySQL 等直接自增的数据库\n      #      id-type: INPUT # 用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n      #      id-type: ASSIGN_ID # 分配 ID，默认使用雪花算法。注意，Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时，需要去除实体类上的 @KeySequence 注解\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n    banner: false # 关闭控制台的 Banner 打印\n  type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject\n  encryptor:\n    password: XDV71a+xqStEA3WH # 加解密的秘钥，可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成\n\nmybatis-plus-join:\n  banner: false # 关闭控制台的 Banner 打印\n\n# VO 转换（数据翻译）相关\neasy-trans:\n  is-enable-global: false # 【默认禁用，对性能确认压力大】启用全局翻译（拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置，或通过 @IgnoreTrans 忽略某个接口\n\n--- #################### RPC 远程调用相关配置 ####################\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    executor:\n      appname: ${spring.application.name} # 执行器 AppName\n      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径\n    accessToken: default_token # 执行器通讯TOKEN\n\n--- #################### AI 相关配置 ####################\n\nspring:\n  ai:\n    vectorstore: # 向量存储\n      redis:\n        initialize-schema: true\n        index-name: knowledge_index # Redis 中向量索引的名称：用于存储和检索向量数据的索引标识符，所有相关的向量搜索操作都会基于这个索引进行\n        prefix: \"knowledge_segment:\" # Redis 中存储向量数据的键名前缀：这个前缀会添加到每个存储在 Redis 中的向量数据键名前，每个 document 都是一个 hash 结构\n      qdrant:\n        initialize-schema: true\n        collection-name: knowledge_segment # Qdrant 中向量集合的名称：用于存储向量数据的集合标识符，所有相关的向量操作都会在这个集合中进行\n        host: 127.0.0.1\n        port: 6334\n      milvus:\n        initialize-schema: true\n        database-name: default # Milvus 中数据库的名称\n        collection-name: knowledge_segment # Milvus 中集合的名称：用于存储向量数据的集合标识符，所有相关的向量操作都会在这个集合中进行\n        client:\n          host: 127.0.0.1\n          port: 19530\n    qianfan: # 文心一言\n      api-key: x0cuLZ7XsaTCU08vuJWO87Lg\n      secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK\n    zhipuai: # 智谱 AI\n      api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs\n    openai: # OpenAI 官方\n      api-key: sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17\n      base-url: https://api.gptsapi.net\n    azure: # OpenAI 微软\n      openai:\n        endpoint: https://eastusprejade.openai.azure.com\n    anthropic: # Anthropic Claude\n      api-key: sk-muubv7cXeLw0Etgs743f365cD5Ea44429946Fa7e672d8942\n    ollama:\n      base-url: http://127.0.0.1:11434\n      chat:\n        model: llama3\n    stabilityai:\n      api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx\n    dashscope: # 通义千问\n      api-key: sk-47aa124781be4bfb95244cc62f6xxxx\n    minimax: # Minimax：https://www.minimaxi.com/\n      api-key: xxxx\n    moonshot: # 月之暗面（KIMI）\n      api-key: sk-abc\n    deepseek: # DeepSeek\n      api-key: sk-e94db327cc7d457d99a8de8810fc6b12\n      chat:\n        options:\n          model: deepseek-chat\n    model:\n      rerank: false # 是否开启“通义千问”的 Rerank 模型，填写 dashscope 开启\n    mcp:\n      server:\n        enabled: false\n        name: yudao-mcp-server\n        version: 1.0.0\n        instructions: 一个 MCP 示例服务\n        sse-endpoint: /sse\n      client:\n        enabled: false\n        name: mcp\n        sse:\n          connections:\n            filesystem:\n              url: http://127.0.0.1:8089\n              sse-endpoint: /sse\n        annotation-scanner:\n          enabled: false # TODO @芋艿：有 bug https://github.com/spring-projects/spring-ai/issues/4917 需要官方修复\n\nyudao:\n  ai:\n    gemini: # 谷歌 Gemini\n      enable: true\n      api-key: AIzaSyAVoBxgoFvvte820vEQMma2LKBnC98bqMQ\n      model: gemini-2.5-flash\n    doubao: # 字节豆包\n      enable: true\n      api-key: 5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272\n      model: doubao-1-5-lite-32k-250115\n    hunyuan: # 腾讯混元\n      enable: true\n      api-key: sk-abc\n      model: hunyuan-turbo\n    siliconflow: # 硅基流动\n      enable: true\n      api-key: sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz\n      model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\n    xinghuo: # 讯飞星火\n      enable: true\n      appKey: 75b161ed2aef4719b275d6e7f2a4d4cd\n      secretKey: YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz\n      model: x1\n    baichuan: # 百川智能\n      enable: true\n      api-key: sk-abc\n      model: Baichuan4-Turbo\n    midjourney:\n      enable: true\n  #    base-url: https://api.holdai.top/mj-relax/mj\n      base-url: https://api.holdai.top/mj\n      api-key: sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf\n      notify-url: http://java.nat300.top/admin-api/ai/image/midjourney/notify\n    suno:\n      enable: true\n  #    base-url: https://suno-55ishh05u-status2xxs-projects.vercel.app\n      base-url: http://127.0.0.1:3001\n    web-search:\n      enable: true\n      api-key: sk-40500e52840f4d24b956d0b1d80d9abe\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0\n    base-package: cn.iocoder.yudao.module.ai\n  web:\n    admin-ui:\n      url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址\n  xss:\n    enable: false\n    exclude-urls: # 如下 url，仅仅是为了演示，去掉配置也没关系\n      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求\n  swagger:\n    title: 管理后台\n    description: 提供管理员管理的所有功能\n    version: ${yudao.info.version}\n  tenant: # 多租户相关配置项\n    enable: true\n\ndebug: false\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.anthropic.AnthropicChatModel;\nimport org.springframework.ai.anthropic.AnthropicChatOptions;\nimport org.springframework.ai.anthropic.api.AnthropicApi;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link AnthropicChatModel} 集成测试类\n *\n * @author 芋道源码\n */\npublic class AnthropicChatModelTest {\n\n    private final AnthropicChatModel chatModel = AnthropicChatModel.builder()\n            .anthropicApi(AnthropicApi.builder()\n                    .apiKey(\"sk-muubv7cXeLw0Etgs743f365cD5Ea44429946Fa7e672d8942\")\n                    .baseUrl(\"https://aihubmix.com\")\n                    .build())\n            .defaultOptions(AnthropicChatOptions.builder()\n                    .model(AnthropicApi.ChatModel.CLAUDE_SONNET_4_5)\n                    .temperature(0.7)\n                    .maxTokens(4096)\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n    // TODO @芋艿：需要等 spring ai 升级：https://github.com/spring-projects/spring-ai/pull/2800\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"thkinking 下，1+1 为什么等于 2 \"));\n        AnthropicChatOptions options = AnthropicChatOptions.builder()\n                .model(AnthropicApi.ChatModel.CLAUDE_SONNET_4_5)\n                .thinking(AnthropicApi.ThinkingType.ENABLED, 3096)\n                .temperature(1D)\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport com.azure.ai.openai.OpenAIClientBuilder;\nimport com.azure.core.credential.AzureKeyCredential;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.azure.openai.AzureOpenAiChatModel;\nimport org.springframework.ai.azure.openai.AzureOpenAiChatOptions;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatProperties.DEFAULT_DEPLOYMENT_NAME;\n\n/**\n * {@link AzureOpenAiChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class AzureOpenAIChatModelTests {\n\n    // TODO @芋艿：晚点在调整\n    private final OpenAIClientBuilder openAiApi = new OpenAIClientBuilder()\n            .endpoint(\"https://eastusprejade.openai.azure.com\")\n            .credential(new AzureKeyCredential(\"xxx\"));\n    private final AzureOpenAiChatModel chatModel = AzureOpenAiChatModel.builder()\n            .openAIClientBuilder(openAiApi)\n            .defaultOptions(AzureOpenAiChatOptions.builder()\n                    .deploymentName(DEFAULT_DEPLOYMENT_NAME)\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.OpenAiChatOptions;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link BaiChuanChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class BaiChuanChatModelTests {\n\n    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()\n            .openAiApi(OpenAiApi.builder()\n                    .baseUrl(BaiChuanChatModel.BASE_URL)\n                    .apiKey(\"sk-61b6766a94c70786ed02673f5e16af3c\") // apiKey\n                    .build())\n            .defaultOptions(OpenAiChatOptions.builder()\n                    .model(\"Baichuan4-Turbo\") // 模型（https://platform.baichuan-ai.com/docs/api）\n                    .temperature(0.7)\n                    .build())\n            .build();\n\n    private final BaiChuanChatModel chatModel = new BaiChuanChatModel(openAiChatModel);\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 基于 {@link OpenAiChatModel} 集成 Coze 测试\n *\n * @author 芋道源码\n */\npublic class CozeChatModelTests {\n\n    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()\n            .openAiApi(OpenAiApi.builder()\n                    .baseUrl(\"http://127.0.0.1:3000\")\n                    .apiKey(\"app-4hy2d7fJauSbrKbzTKX1afuP\") // apiKey\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.deepseek.DeepSeekChatModel;\nimport org.springframework.ai.deepseek.DeepSeekChatOptions;\nimport org.springframework.ai.deepseek.api.DeepSeekApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link DeepSeekChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class DeepSeekChatModelTests {\n\n    private final DeepSeekChatModel chatModel = DeepSeekChatModel.builder()\n            .deepSeekApi(DeepSeekApi.builder()\n                    .apiKey(\"sk-eaf4172a057344dd9bc64b1f806b6axx\") // apiKey\n                    .build())\n            .defaultOptions(DeepSeekChatOptions.builder()\n                    .model(\"deepseek-chat\") // 模型\n                    .temperature(0.7)\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        DeepSeekChatOptions options = DeepSeekChatOptions.builder()\n                .model(\"deepseek-reasoner\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 基于 {@link OpenAiChatModel} 集成 Dify 测试\n *\n * @author 芋道源码\n */\npublic class DifyChatModelTests {\n\n    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()\n            .openAiApi(OpenAiApi.builder()\n                    .baseUrl(\"http://127.0.0.1:3000\")\n                    .apiKey(\"app-4hy2d7fJauSbrKbzTKX1afuP\") // apiKey\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.deepseek.DeepSeekChatModel;\nimport org.springframework.ai.deepseek.DeepSeekChatOptions;\nimport org.springframework.ai.deepseek.api.DeepSeekApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link DouBaoChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class DouBaoChatModelTests {\n\n    /**\n     * 相比 OpenAIChatModel 来说，DeepSeekChatModel 可以兼容豆包的 thinking 能力！\n     */\n    private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()\n            .deepSeekApi(DeepSeekApi.builder()\n                    .baseUrl(DouBaoChatModel.BASE_URL)\n                    .completionsPath(DouBaoChatModel.COMPLETE_PATH)\n                    .apiKey(\"5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272\") // apiKey\n                    .build())\n            .defaultOptions(DeepSeekChatOptions.builder()\n                    .model(\"doubao-1-5-lite-32k-250115\") // 模型（doubao）\n//                    .model(\"doubao-seed-1-6-thinking-250715\") // 模型（doubao）\n//                    .model(\"deepseek-r1-250120\") // 模型（deepseek）\n                    .temperature(0.7)\n                    .build())\n            .build();\n\n    private final DouBaoChatModel chatModel = new DouBaoChatModel(openAiChatModel);\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"详细推理下，帮我设计一个用户中心！\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        DeepSeekChatOptions options = DeepSeekChatOptions.builder()\n                .model(\"doubao-seed-1-6-thinking-250715\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 基于 {@link OpenAiChatModel} 集成 FastGPT 测试\n *\n * @author 芋道源码\n */\npublic class FastGPTChatModelTests {\n\n    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()\n            .openAiApi(OpenAiApi.builder()\n                    .baseUrl(\"https://cloud.fastgpt.cn/api\")\n                    .apiKey(\"fastgpt-aqcc61kFtF8CeaglnGAfQOCIDWwjGdJVJHv6hIlMo28otFlva2aZNK\") // apiKey\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.OpenAiChatOptions;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link GeminiChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class GeminiChatModelTests {\n\n    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()\n        .openAiApi(OpenAiApi.builder()\n                .baseUrl(GeminiChatModel.BASE_URL)\n                .completionsPath(GeminiChatModel.COMPLETE_PATH)\n                .apiKey(\"AIzaSyAVoBxgoFvvte820vEQMma2LKBnC98bqMQ\")\n                .build())\n        .defaultOptions(OpenAiChatOptions.builder()\n                .model(GeminiChatModel.MODEL_DEFAULT) // 模型\n                .temperature(0.7)\n                .build())\n        .build();\n\n    private final GeminiChatModel chatModel = new GeminiChatModel(openAiChatModel);\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.deepseek.DeepSeekChatModel;\nimport org.springframework.ai.deepseek.DeepSeekChatOptions;\nimport org.springframework.ai.deepseek.api.DeepSeekApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link HunYuanChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class HunYuanChatModelTests {\n\n    private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()\n            .deepSeekApi(DeepSeekApi.builder()\n                    .baseUrl(HunYuanChatModel.BASE_URL)\n                    .completionsPath(HunYuanChatModel.COMPLETE_PATH)\n                    .apiKey(\"sk-abc\") // apiKey\n                    .build())\n            .defaultOptions(DeepSeekChatOptions.builder()\n                    .model(HunYuanChatModel.MODEL_DEFAULT) // 模型\n                    .temperature(0.7)\n                    .build())\n            .build();\n\n    private final HunYuanChatModel chatModel = new HunYuanChatModel(openAiChatModel);\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        DeepSeekChatOptions options = DeepSeekChatOptions.builder()\n                .model(\"hunyuan-a13b\")\n//                .model(\"hunyuan-turbos-latest\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n    private final DeepSeekChatModel deepSeekOpenAiChatModel = DeepSeekChatModel.builder()\n            .deepSeekApi(DeepSeekApi.builder()\n                    .baseUrl(HunYuanChatModel.DEEP_SEEK_BASE_URL)\n                    .completionsPath(HunYuanChatModel.COMPLETE_PATH)\n                    .apiKey(\"sk-abc\") // apiKey\n                    .build())\n            .defaultOptions(DeepSeekChatOptions.builder()\n//                    .model(HunYuanChatModel.DEEP_SEEK_MODEL_DEFAULT) // 模型（\"deepseek-v3\"）\n                    .model(\"deepseek-r1\") // 模型（\"deepseek-r1\"）\n                    .temperature(0.7)\n                    .build())\n            .build();\n\n    private final HunYuanChatModel deepSeekChatModel = new HunYuanChatModel(deepSeekOpenAiChatModel);\n\n    @Test\n    @Disabled\n    public void testCall_deepseek() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = deepSeekChatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream_deepseek() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = deepSeekChatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n    @Test\n    @Disabled\n    public void testStream_deepseek_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        DeepSeekChatOptions options = DeepSeekChatOptions.builder()\n                .model(\"deepseek-r1\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = deepSeekChatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.ollama.OllamaChatModel;\nimport org.springframework.ai.ollama.api.OllamaApi;\nimport org.springframework.ai.ollama.api.OllamaChatOptions;\nimport org.springframework.ai.ollama.api.OllamaModel;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link OllamaChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class LlamaChatModelTests {\n\n    private final OllamaChatModel chatModel = OllamaChatModel.builder()\n            .ollamaApi(OllamaApi.builder()\n                    .baseUrl(\"http://127.0.0.1:11434\")  // Ollama 服务地址\n                    .build())\n            .defaultOptions(OllamaChatOptions.builder()\n                    .model(OllamaModel.LLAMA3.getName()) // 模型\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        OllamaChatOptions options = OllamaChatOptions.builder()\n                .model(\"qwen3\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.minimax.MiniMaxChatModel;\nimport org.springframework.ai.minimax.MiniMaxChatOptions;\nimport org.springframework.ai.minimax.api.MiniMaxApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link MiniMaxChatModel} 的集成测试\n *\n * @author 芋道源码\n */\npublic class MiniMaxChatModelTests {\n\n    private final MiniMaxChatModel chatModel = new MiniMaxChatModel(\n            new MiniMaxApi(\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiLnjovmlofmlowiLCJVc2VyTmFtZSI6IueOi-aWh-aWjCIsIkFjY291bnQiOiIiLCJTdWJqZWN0SUQiOiIxODk3Mjg3MjQ5NDU2ODA4MzQ2IiwiUGhvbmUiOiIxNTYwMTY5MTM5OSIsIkdyb3VwSUQiOiIxODk3Mjg3MjQ5NDQ4NDE5NzM4IiwiUGFnZU5hbWUiOiIiLCJNYWlsIjoiIiwiQ3JlYXRlVGltZSI6IjIwMjUtMDMtMTEgMTI6NTI6MDIiLCJUb2tlblR5cGUiOjEsImlzcyI6Im1pbmltYXgifQ.aAuB7gWW_oA4IYhh-CF7c9MfWWxKN49B_HK-DYjXaDwwffhiG-H1571z1WQhp9QytWG-DqgLejneeSxkiq1wQIe3FsEP2wz4BmGBct31LehbJu8ehLxg_vg75Uod1nFAHbm5mZz6JSVLNIlSo87Xr3UtSzJhAXlapEkcqlA4YOzOpKrZ8l5_OJPTORTCmHWZYgJcRS-faNiH62ZnUEHUozesTFhubJHo5GfJCw_edlnmfSUocERV1BjWvenhZ9My-aYXNktcW9WaSj9l6gayV7A0Ium_PL55T9ln1PcI8gayiVUKJGJDoqNyF1AF9_aF9NOKtTnQzwNqnZdlTYH6hw\"), // 密钥\n            MiniMaxChatOptions.builder()\n                    .model(MiniMaxApi.ChatModel.ABAB_6_5_G_Chat.getValue()) // 模型\n                    .build());\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n    // TODO @芋艿：暂时没解析 reasoning_content 结果，需要等官方修复\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        MiniMaxChatOptions options = MiniMaxChatOptions.builder()\n                .model(\"MiniMax-M1\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springaicommunity.moonshot.MoonshotChatModel;\nimport org.springaicommunity.moonshot.MoonshotChatOptions;\nimport org.springaicommunity.moonshot.api.MoonshotApi;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link MoonshotChatModel} 的集成测试\n *\n * @author 芋道源码\n */\npublic class MoonshotChatModelTests {\n\n    private final MoonshotChatModel chatModel = MoonshotChatModel.builder()\n            .moonshotApi(MoonshotApi.builder()\n                    .apiKey(\"sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA\") // 密钥\n                    .build())\n            .defaultOptions(MoonshotChatOptions.builder()\n                    .model(\"kimi-k2-0711-preview\") // 模型\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n    // TODO @芋艿：暂时没解析 reasoning_content 结果，需要等官方修复\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        MoonshotChatOptions options = MoonshotChatOptions.builder()\n//                .model(\"kimi-k2-0711-preview\")\n                .model(\"kimi-thinking-preview\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.ollama.OllamaChatModel;\nimport org.springframework.ai.ollama.api.OllamaApi;\nimport org.springframework.ai.ollama.api.OllamaChatOptions;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link OllamaChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class OllamaChatModelTests {\n\n    private final OllamaChatModel chatModel = OllamaChatModel.builder()\n            .ollamaApi(OllamaApi.builder()\n                    .baseUrl(\"http://127.0.0.1:11434\") // Ollama 服务地址\n                    .build())\n            .defaultOptions(OllamaChatOptions.builder()\n//                    .model(\"qwen\") // 模型（https://ollama.com/library/qwen）\n                    .model(\"deepseek-r1\") // 模型（https://ollama.com/library/deepseek-r1）\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport com.azure.ai.openai.models.ReasoningEffortValue;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.OpenAiChatOptions;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link OpenAiChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class OpenAIChatModelTests {\n\n    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()\n            .openAiApi(OpenAiApi.builder()\n                    .baseUrl(\"https://api.holdai.top\")\n                    .apiKey(\"sk-z5joyRoV1iFEnh2SAi8QPNrIZTXyQSyxTmD5CoNDQbFixK2l\") // apiKey\n                    .build())\n            .defaultOptions(OpenAiChatOptions.builder()\n                    .model(\"gpt-5-nano-2025-08-07\") // 模型\n//                    .model(OpenAiApi.ChatModel.O1) // 模型\n                    .temperature(0.7)\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"帮我推理下，怎么实现一个用户中心！\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n    // TODO @芋艿：无法触发思考的字段返回，需要 response api：https://github.com/spring-projects/spring-ai/issues/2962\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        OpenAiChatOptions options = OpenAiChatOptions.builder()\n                .model(\"gpt-5\")\n//                .model(OpenAiApi.ChatModel.O4_MINI)\n//                .model(\"o3-pro\")\n                .reasoningEffort(ReasoningEffortValue.LOW.getValue())\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.deepseek.DeepSeekChatModel;\nimport org.springframework.ai.deepseek.DeepSeekChatOptions;\nimport org.springframework.ai.deepseek.api.DeepSeekApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link SiliconFlowChatModel} 集成测试\n *\n * @author 芋道源码\n */\npublic class SiliconFlowChatModelTests {\n\n    private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()\n            .deepSeekApi(DeepSeekApi.builder()\n                    .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)\n                    .apiKey(\"sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz\") // apiKey\n                    .build())\n            .defaultOptions(DeepSeekChatOptions.builder()\n                    .model(SiliconFlowApiConstants.MODEL_DEFAULT) // 模型\n//                    .model(\"deepseek-ai/DeepSeek-R1\") // 模型（deepseek-ai/DeepSeek-R1）可用赠费\n//                    .model(\"Pro/deepseek-ai/DeepSeek-R1\") // 模型（Pro/deepseek-ai/DeepSeek-R1）需要付费\n                    .temperature(0.7)\n                    .build())\n            .build();\n\n    private final SiliconFlowChatModel chatModel = new SiliconFlowChatModel(openAiChatModel);\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        DeepSeekChatOptions options = DeepSeekChatOptions.builder()\n                .model(\"deepseek-ai/DeepSeek-R1\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport com.alibaba.cloud.ai.dashscope.api.DashScopeApi;\nimport com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;\nimport com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;\nimport com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankModel;\nimport com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankOptions;\nimport com.alibaba.cloud.ai.model.RerankModel;\nimport com.alibaba.cloud.ai.model.RerankOptions;\nimport com.alibaba.cloud.ai.model.RerankRequest;\nimport com.alibaba.cloud.ai.model.RerankResponse;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.document.Document;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static java.util.Arrays.asList;\n\n/**\n * {@link DashScopeChatModel} 集成测试类\n *\n * @author fansili\n */\npublic class TongYiChatModelTests {\n\n    private final DashScopeChatModel chatModel = DashScopeChatModel.builder()\n            .dashScopeApi(DashScopeApi.builder()\n                    .apiKey(\"sk-47aa124781be4bfb95244cc62f63f7d0\")\n                    .build())\n            .defaultOptions(DashScopeChatOptions.builder()\n//                    .withModel(\"qwen1.5-72b-chat\") // 模型\n                    .withModel(\"qwen3-235b-a22b-thinking-2507\") // 模型\n//                    .withModel(\"deepseek-r1\") // 模型（deepseek-r1）\n//                    .withModel(\"deepseek-v3\") // 模型（deepseek-v3）\n//                    .withModel(\"deepseek-r1-distill-qwen-1.5b\") // 模型（deepseek-r1-distill-qwen-1.5b）\n//                    .withEnableThinking(true)\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n//        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"帮我推理下，怎么实现一个用户中心！\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        DashScopeChatOptions options = DashScopeChatOptions.builder()\n                .withModel(\"qwen3-235b-a22b-thinking-2507\")\n//                .withModel(\"qwen-max-2025-01-25\")\n                .withEnableThinking(true) // 必须设置，否则会报错\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n    @Test\n    @Disabled\n    public void testRerank() {\n        // 准备环境\n        RerankModel rerankModel = new DashScopeRerankModel(\n                DashScopeApi.builder()\n                        .apiKey(\"sk-47aa124781be4bfb95244cc62f63f7d0\")\n                        .build());\n        // 准备参数\n        String query = \"spring\";\n        Document document01 = new Document(\"abc\");\n        Document document02 = new Document(\"sapring\");\n        RerankOptions options = DashScopeRerankOptions.builder()\n                .withTopN(1)\n                .withModel(\"gte-rerank-v2\")\n                .build();\n        RerankRequest rerankRequest = new RerankRequest(\n                query,\n                asList(document01, document02),\n                options);\n\n        // 调用\n        RerankResponse call = rerankModel.call(rerankRequest);\n        // 打印结果\n        System.out.println(JsonUtils.toJsonPrettyString(call));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.deepseek.DeepSeekChatModel;\nimport org.springframework.ai.deepseek.DeepSeekChatOptions;\nimport org.springframework.ai.deepseek.api.DeepSeekApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link XingHuoChatModel} 集成测试\n *\n * @author fansili\n */\npublic class XingHuoChatModelTests {\n\n    private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()\n        .deepSeekApi(DeepSeekApi.builder()\n                .baseUrl(XingHuoChatModel.BASE_URL_V2)\n                .completionsPath(XingHuoChatModel.BASE_COMPLETIONS_PATH_V2)\n                .apiKey(\"75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz\") // appKey:secretKey\n                .build())\n        .defaultOptions(DeepSeekChatOptions.builder()\n//                .model(\"generalv3.5\") // 模型\n                .model(\"x1\") // 模型\n                .temperature(0.7)\n                .build())\n        .build();\n\n    private final XingHuoChatModel chatModel = new XingHuoChatModel(openAiChatModel);\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        DeepSeekChatOptions options = DeepSeekChatOptions.builder()\n                .model(\"x1\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springaicommunity.qianfan.QianFanChatModel;\nimport org.springaicommunity.qianfan.QianFanChatOptions;\nimport org.springaicommunity.qianfan.api.QianFanApi;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n// TODO @芋艿：百度千帆 API 提供了 V2 版本，目前 Spring AI 不兼容，可关键 <https://github.com/spring-projects/spring-ai/issues/2179> 进展\n/**\n * {@link QianFanChatModel} 的集成测试\n *\n * @author fansili\n */\npublic class YiYanChatModelTests {\n\n    private final QianFanChatModel chatModel = new QianFanChatModel(\n            new QianFanApi(\"DGnyzREuaY7av7c38bOM9Ji2\", \"9aR8myflEOPDrEeLhoXv0FdqANOAyIZW\"), // 密钥\n            QianFanChatOptions.builder()\n                    .model(\"ERNIE-4.5-8K-Preview\")\n                    .build()\n    );\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        // TODO @芋艿：文心一言，只要带上 system message 就报错，已经各种测试，很莫名！\n//        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        // TODO @芋艿：文心一言，只要带上 system message 就报错，已经各种测试，很莫名！\n//        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(System.out::println).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.messages.Message;\nimport org.springframework.ai.chat.messages.SystemMessage;\nimport org.springframework.ai.chat.messages.UserMessage;\nimport org.springframework.ai.chat.model.ChatResponse;\nimport org.springframework.ai.chat.prompt.Prompt;\nimport org.springframework.ai.zhipuai.ZhiPuAiChatModel;\nimport org.springframework.ai.zhipuai.ZhiPuAiChatOptions;\nimport org.springframework.ai.zhipuai.api.ZhiPuAiApi;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {@link ZhiPuAiChatModel} 的集成测试\n *\n * @author 芋道源码\n */\npublic class ZhiPuAiChatModelTests {\n\n    private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel(\n            ZhiPuAiApi.builder().apiKey(\"2f35fb6ca4ea41fab898729b7fac086c.6ESSfPcCkxaKEUlR\").build(), // 密钥\n            ZhiPuAiChatOptions.builder()\n                    .model(ZhiPuAiApi.ChatModel.GLM_4.getName()) // 模型\n                    .build()\n    );\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        ChatResponse response = chatModel.call(new Prompt(messages));\n        // 打印结果\n        System.out.println(response);\n        System.out.println(response.getResult().getOutput());\n    }\n\n    @Test\n    @Disabled\n    public void testStream() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new SystemMessage(\"你是一个优质的文言文作者，用文言文描述着各城市的人文风景。\"));\n        messages.add(new UserMessage(\"1 + 1 = ？\"));\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n    // TODO @芋艿：暂时没解析 reasoning_content 结果，需要等官方修复\n    @Test\n    @Disabled\n    public void testStream_thinking() {\n        // 准备参数\n        List<Message> messages = new ArrayList<>();\n        messages.add(new UserMessage(\"详细分析下，如何设计一个电商系统？\"));\n        ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder()\n                .model(\"GLM-4.5\")\n                .build();\n\n        // 调用\n        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));\n        // 打印结果\n        flux.doOnNext(response -> {\n//            System.out.println(response);\n            System.out.println(response.getResult().getOutput());\n        }).then().block();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/MidjourneyApiTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * {@link MidjourneyApi} 集成测试\n *\n * @author 芋道源码\n */\npublic class MidjourneyApiTests {\n\n    private final MidjourneyApi midjourneyApi = new MidjourneyApi(\n            \"https://api.holdai.top/mj\", // 链接\n            \"sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17\", // 密钥\n            null);\n\n    @Test\n    @Disabled\n    public void testImagine() {\n        // 准备参数\n        MidjourneyApi.ImagineRequest request = new MidjourneyApi.ImagineRequest(null,\n                \"生成一个小猫，可爱的\", null,\n                MidjourneyApi.ImagineRequest.buildState(512, 512, \"6.0\", MidjourneyApi.ModelEnum.MIDJOURNEY.getModel()));\n\n        // 方法调用\n        MidjourneyApi.SubmitResponse response = midjourneyApi.imagine(request);\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testAction() {\n        // 准备参数\n        MidjourneyApi.ActionRequest request = new MidjourneyApi.ActionRequest(\"1720277033455953\",\n                \"MJ::JOB::upsample::1::ee267661-ee52-4ced-a530-0343ba95af3b\", null);\n\n        // 方法调用\n        MidjourneyApi.SubmitResponse response = midjourneyApi.action(request);\n        // 打印结果\n        System.out.println(response);\n    }\n\n    @Test\n    @Disabled\n    public void testGetTaskList() {\n        // 准备参数。该参数可以通过 MidjourneyApi.SubmitResponse 的 result 获取\n//        String taskId = \"1720277033455953\";\n        String taskId = \"1720277214045971\";\n\n        // 方法调用\n        List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(Collections.singletonList(taskId));\n        // 打印结果\n        System.out.println(taskList);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.image.ImageOptions;\nimport org.springframework.ai.image.ImagePrompt;\nimport org.springframework.ai.image.ImageResponse;\nimport org.springframework.ai.openai.OpenAiImageModel;\nimport org.springframework.ai.openai.OpenAiImageOptions;\nimport org.springframework.ai.openai.api.OpenAiImageApi;\n\n/**\n * {@link OpenAiImageModel} 集成测试类\n *\n * @author fansili\n */\npublic class OpenAiImageModelTests {\n\n    private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAiImageApi.builder()\n            .baseUrl(\"https://api.holdai.top\") // apiKey\n            .apiKey(\"sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD\")\n            .build());\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        ImageOptions options = OpenAiImageOptions.builder()\n                .model(OpenAiImageApi.ImageModel.DALL_E_2.getValue()) // 这个模型比较便宜\n                .height(256).width(256)\n                .build();\n        ImagePrompt prompt = new ImagePrompt(\"中国长城!\", options);\n\n        // 方法调用\n        ImageResponse response = imageModel.call(prompt);\n        // 打印结果\n        System.out.println(response);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springaicommunity.qianfan.QianFanImageModel;\nimport org.springaicommunity.qianfan.QianFanImageOptions;\nimport org.springaicommunity.qianfan.api.QianFanImageApi;\nimport org.springframework.ai.image.ImagePrompt;\nimport org.springframework.ai.image.ImageResponse;\n\nimport static cn.iocoder.yudao.module.ai.framework.ai.core.model.image.StabilityAiImageModelTests.viewImage;\n\n// TODO @芋艿：百度千帆 API 提供了 V2 版本，目前 Spring AI 不兼容，可关键 <https://github.com/spring-projects/spring-ai/issues/2179> 进展\n\n/**\n * {@link QianFanImageModel} 集成测试类\n */\npublic class QianFanImageTests {\n\n    private final QianFanImageModel imageModel = new QianFanImageModel(\n            new QianFanImageApi(\"qS8k8dYr2nXunagK4SSU8Xjj\", \"pHGbx51ql2f0hOyabQvSZezahVC3hh3e\")); // 密钥\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        // 只支持 1024x1024、768x768、768x1024、1024x768、576x1024、1024x576\n        QianFanImageOptions imageOptions = QianFanImageOptions.builder()\n                .model(QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue())\n                .width(1024).height(1024)\n                .N(1)\n                .build();\n        ImagePrompt prompt = new ImagePrompt(\"good\", imageOptions);\n\n        // 方法调用\n        ImageResponse response = imageModel.call(prompt);\n        // 打印结果\n        String b64Json = response.getResult().getOutput().getB64Json();\n        System.out.println(response);\n        viewImage(b64Json);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/SiliconFlowImageModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageApi;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageOptions;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.image.ImagePrompt;\nimport org.springframework.ai.image.ImageResponse;\n\n/**\n * {@link SiliconFlowImageModel} 集成测试\n */\npublic class SiliconFlowImageModelTests {\n\n    private final SiliconFlowImageModel imageModel = new SiliconFlowImageModel(\n            new SiliconFlowImageApi(\"sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz\") // 密钥\n    );\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        SiliconFlowImageOptions imageOptions = SiliconFlowImageOptions.builder()\n                .model(\"Kwai-Kolors/Kolors\")\n                .build();\n        ImagePrompt prompt = new ImagePrompt(\"万里长城\", imageOptions);\n\n        // 方法调用\n        ImageResponse response = imageModel.call(prompt);\n        // 打印结果\n        System.out.println(response);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/StabilityAiImageModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;\n\nimport cn.hutool.core.codec.Base64;\nimport cn.hutool.core.thread.ThreadUtil;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.image.ImageOptions;\nimport org.springframework.ai.image.ImagePrompt;\nimport org.springframework.ai.image.ImageResponse;\nimport org.springframework.ai.openai.OpenAiImageOptions;\nimport org.springframework.ai.stabilityai.StabilityAiImageModel;\nimport org.springframework.ai.stabilityai.api.StabilityAiApi;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * {@link StabilityAiImageModel} 集成测试类\n *\n * @author fansili\n */\npublic class StabilityAiImageModelTests {\n\n    private final StabilityAiImageModel imageModel = new StabilityAiImageModel(\n            new StabilityAiApi(\"sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx\") // 密钥\n    );\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        ImageOptions options = OpenAiImageOptions.builder()\n                .model(\"stable-diffusion-v1-6\")\n                .height(320).width(320)\n                .build();\n        ImagePrompt prompt = new ImagePrompt(\"great wall\", options);\n\n        // 方法调用\n        ImageResponse response = imageModel.call(prompt);\n        // 打印结果\n        String b64Json = response.getResult().getOutput().getB64Json();\n        System.out.println(response);\n        viewImage(b64Json);\n    }\n\n    public static void viewImage(String b64Json) {\n        // 创建一个 JFrame\n        JFrame frame = new JFrame(\"Byte Image Display\");\n        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n        frame.setSize(800, 600);\n\n        // 创建一个 JLabel 来显示图片\n        byte[] imageBytes = Base64.decode(b64Json);\n        JLabel label = new JLabel(new ImageIcon(imageBytes));\n\n        // 将 JLabel 添加到 JFrame\n        frame.getContentPane().add(label, BorderLayout.CENTER);\n\n        // 显示 JFrame\n        frame.setVisible(true);\n        ThreadUtil.sleep(1, TimeUnit.HOURS);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/TongYiImagesModelTest.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;\n\nimport com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;\nimport com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;\nimport com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.image.ImageOptions;\nimport org.springframework.ai.image.ImagePrompt;\nimport org.springframework.ai.image.ImageResponse;\n\n/**\n * {@link DashScopeImageModel} 集成测试类\n *\n * @author fansili\n */\npublic class TongYiImagesModelTest {\n\n    private final DashScopeImageModel imageModel = DashScopeImageModel.builder()\n            .dashScopeApi(DashScopeImageApi.builder()\n                    .apiKey(\"sk-47aa124781be4bfb95244cc62f63f7d0\")\n                    .build())\n            .build();\n\n    @Test\n    @Disabled\n    public void imageCallTest() {\n        // 准备参数\n        ImageOptions options = DashScopeImageOptions.builder()\n                .withModel(\"wanx-v1\")\n                .withHeight(256).withWidth(256)\n                .build();\n        ImagePrompt prompt = new ImagePrompt(\"中国长城!\", options);\n\n        // 方法调用\n        ImageResponse response = imageModel.call(prompt);\n        // 打印结果\n        System.out.println(response);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/ZhiPuAiImageModelTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.image.ImagePrompt;\nimport org.springframework.ai.image.ImageResponse;\nimport org.springframework.ai.zhipuai.ZhiPuAiImageModel;\nimport org.springframework.ai.zhipuai.ZhiPuAiImageOptions;\nimport org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;\n\n/**\n * {@link ZhiPuAiImageModel} 集成测试\n */\npublic class ZhiPuAiImageModelTests {\n\n    private final ZhiPuAiImageModel imageModel = new ZhiPuAiImageModel(\n            new ZhiPuAiImageApi(\"78d3228c1d9e5e342a3e1ab349e2dd7b.VXLoq5vrwK2ofboy\") // 密钥\n    );\n\n    @Test\n    @Disabled\n    public void testCall() {\n        // 准备参数\n        ZhiPuAiImageOptions imageOptions = ZhiPuAiImageOptions.builder()\n                .model(ZhiPuAiImageApi.ImageModel.CogView_3.getValue())\n                .build();\n        ImagePrompt prompt = new ImagePrompt(\"万里长城\", imageOptions);\n\n        // 方法调用\n        ImageResponse response = imageModel.call(prompt);\n        // 打印结果\n        System.out.println(response);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.mcp;\n\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.ai.chat.client.ChatClient;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.OpenAiChatOptions;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport org.springframework.ai.tool.annotation.Tool;\nimport org.springframework.ai.tool.method.MethodToolCallbackProvider;\n\n@Disabled\npublic class DouBaoMcpTests {\n\n    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()\n            .openAiApi(OpenAiApi.builder()\n                    .baseUrl(DouBaoChatModel.BASE_URL)\n                    .apiKey(\"5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272\") // apiKey\n                    .build())\n            .defaultOptions(OpenAiChatOptions.builder()\n                    .model(\"doubao-1-5-lite-32k-250115\") // 模型（doubao）\n                    .temperature(0.7)\n                    .build())\n            .build();\n\n    private final DouBaoChatModel chatModel = new DouBaoChatModel(openAiChatModel);\n\n    private final MethodToolCallbackProvider provider = MethodToolCallbackProvider.builder()\n            .toolObjects(new UserService())\n            .build();\n\n    private final ChatClient chatClient = ChatClient.builder(chatModel)\n            .defaultTools(provider)\n            .build();\n\n    @Test\n    public void testMcpGetUserInfo() {\n\n        // 打印结果\n        System.out.println(chatClient.prompt()\n                .user(\"目前有哪些工具可以使用\")\n                .call()\n                .content());\n        System.out.println(\"====================================\");\n        // 打印结果\n        System.out.println(chatClient.prompt()\n                .user(\"小新的年龄是多少\")\n                .call()\n                .content());\n        System.out.println(\"====================================\");\n        // 打印结果\n        System.out.println(chatClient.prompt()\n                .user(\"获取小新的基本信息\")\n                .call()\n                .content());\n        System.out.println(\"====================================\");\n        // 打印结果\n        System.out.println(chatClient.prompt()\n                .user(\"小新是什么职业的\")\n                .call()\n                .content());\n        System.out.println(\"====================================\");\n        // 打印结果\n        System.out.println(chatClient.prompt()\n                .user(\"小新的教育背景\")\n                .call()\n                .content());\n        System.out.println(\"====================================\");\n        // 打印结果\n        System.out.println(chatClient.prompt()\n                .user(\"小新的兴趣爱好是什么\")\n                .call()\n                .content());\n        System.out.println(\"====================================\");\n\n    }\n\n\n    static class UserService {\n\n        @Tool(name = \"getUserAge\", description = \"获取用户年龄\")\n        public String getUserAge(String userName) {\n            return \"《\" + userName + \"》的年龄为：18\";\n        }\n\n        @Tool(name = \"getUserSex\", description = \"获取用户性别\")\n        public String getUserSex(String userName) {\n            return \"《\" + userName + \"》的性别为：男\";\n        }\n\n        @Tool(name = \"getUserBasicInfo\", description = \"获取用户基本信息，包括姓名、年龄、性别等\")\n        public String getUserBasicInfo(String userName) {\n            return \"《\" + userName + \"》的基本信息：\\n姓名：\" + userName + \"\\n年龄：18\\n性别：男\\n身高：175cm\\n体重：65kg\";\n        }\n\n        @Tool(name = \"getUserContact\", description = \"获取用户联系方式，包括电话、邮箱等\")\n        public String getUserContact(String userName) {\n            return \"《\" + userName + \"》的联系方式：\\n电话：138****1234\\n邮箱：\" + userName.toLowerCase() + \"@example.com\\nQQ：123456789\";\n        }\n\n        @Tool(name = \"getUserAddress\", description = \"获取用户地址信息\")\n        public String getUserAddress(String userName) {\n            return \"《\" + userName + \"》的地址信息：北京市朝阳区科技园区88号\";\n        }\n\n        @Tool(name = \"getUserJob\", description = \"获取用户职业信息\")\n        public String getUserJob(String userName) {\n            return \"《\" + userName + \"》的职业信息：软件工程师，就职于ABC科技有限公司，工作年限5年\";\n        }\n\n        @Tool(name = \"getUserHobbies\", description = \"获取用户兴趣爱好\")\n        public String getUserHobbies(String userName) {\n            return \"《\" + userName + \"》的兴趣爱好：编程、阅读、旅游、摄影、打篮球\";\n        }\n\n        @Tool(name = \"getUserEducation\", description = \"获取用户教育背景\")\n        public String getUserEducation(String userName) {\n            return \"《\" + userName + \"》的教育背景：\\n本科：计算机科学与技术专业，北京大学\\n硕士：软件工程专业，清华大学\";\n        }\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/music/SunoApiTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.music;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\n\n/**\n * {@link SunoApi} 集成测试\n *\n * @author xiaoxin\n */\npublic class SunoApiTests {\n\n    private final SunoApi sunoApi = new SunoApi(\"https://suno-3tah0ycyt-status2xxs-projects.vercel.app\");\n//    private final SunoApi sunoApi = new SunoApi(\"http://127.0.0.1:3001\");\n\n    @Test // 描述模式\n    @Disabled\n    public void testGenerate() {\n        // 准备参数\n        SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(\n                \"happy music\",\n                \"chirp-v3-5\",\n                false);\n\n        // 调用方法\n        List<SunoApi.MusicData> musicList = sunoApi.generate(generateRequest);\n        // 打印结果\n        System.out.println(musicList);\n    }\n\n    @Test // 歌词模式\n    @Disabled\n    public void testCustomGenerate() {\n        // 准备参数\n        SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(\n                \"创作一首带有轻松吉他旋律的流行歌曲，[verse] 描述夏日海滩的宁静，[chorus] 节奏加快，表达对自由的向往。\",\n                \"Happy\",\n                \"Happy Song\",\n                \"chirp-v3.5\",\n                false,\n                false);\n\n        // 调用方法\n        List<SunoApi.MusicData> musicList = sunoApi.customGenerate(generateRequest);\n        // 打印结果\n        System.out.println(musicList);\n    }\n\n    @Test\n    @Disabled\n    public void testGenerateLyrics() {\n        // 调用方法\n        SunoApi.LyricsData lyricsData = sunoApi.generateLyrics(\"A soothing lullaby\");\n        // 打印结果\n        System.out.println(lyricsData);\n    }\n\n    @Test\n    @Disabled\n    public void testGetMusicList() {\n        // 准备参数\n//        String id = \"d460ddda-7c87-4f34-b751-419b08a590ca\";\n        String id = \"584729e5-0fe9-4157-86da-1b4803ff42bf\";\n\n        // 调用方法\n        List<SunoApi.MusicData> musicList = sunoApi.getMusicList(ListUtil.of(id));\n        // 打印结果\n        System.out.println(musicList);\n    }\n\n    @Test\n    @Disabled\n    public void testGetLimitUsage() {\n        // 调用方法\n        SunoApi.LimitUsageData limitUsageData = sunoApi.getLimitUsage();\n        // 打印结果\n        System.out.println(limitUsageData);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/wdd/WenDuoDuoPptApiTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.ppt.wdd;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.wenduoduo.api.WenDuoDuoPptApi;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\n\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * {@link WenDuoDuoPptApi} 集成测试\n *\n * @author xiaoxin\n */\n@Disabled\npublic class WenDuoDuoPptApiTests {\n\n    private final String token = \"\"; // API Token\n    private final WenDuoDuoPptApi wenDuoDuoPptApi = new WenDuoDuoPptApi(token);\n\n    @Test\n    @Disabled\n    public void testCreateApiToken() {\n        // 准备参数\n        String apiKey = \"\";\n        WenDuoDuoPptApi.CreateTokenRequest request = new WenDuoDuoPptApi.CreateTokenRequest(apiKey);\n        // 调用方法\n        String token = wenDuoDuoPptApi.createApiToken(request);\n        // 打印结果\n        System.out.println(token);\n    }\n\n    /**\n     * 创建任务\n     */\n    @Test\n    @Disabled\n    public void testCreateTask() {\n        WenDuoDuoPptApi.ApiResponse apiResponse = wenDuoDuoPptApi.createTask(1, \"dify 介绍\", null);\n        System.out.println(apiResponse);\n    }\n\n\n    @Test // 创建大纲\n    @Disabled\n    public void testGenerateOutlineRequest() {\n        WenDuoDuoPptApi.CreateOutlineRequest request = new WenDuoDuoPptApi.CreateOutlineRequest(\n                \"1901539019628613632\", \"medium\", null, null, null, null);\n        // 调用\n        Flux<Map<String, Object>> flux = wenDuoDuoPptApi.createOutline(request);\n        StringBuffer contentBuffer = new StringBuffer();\n        flux.doOnNext(chunk -> {\n            contentBuffer.append(chunk.get(\"text\"));\n            if (Objects.equals(Integer.parseInt(String.valueOf(chunk.get(\"status\"))), 4)) {\n                // status 为 4，最终 markdown 结构树\n                System.out.println(JsonUtils.toJsonString(chunk.get(\"result\")));\n                System.out.println(\" ########################################################################\");\n            }\n        }).then().block();\n        // 打印结果\n        System.out.println(contentBuffer);\n    }\n\n    /**\n     * 修改大纲\n     */\n    @Test\n    @Disabled\n    public void testUpdateOutlineRequest() {\n        WenDuoDuoPptApi.UpdateOutlineRequest request = new WenDuoDuoPptApi.UpdateOutlineRequest(\n                \"1901539019628613632\", TEST_OUT_LINE_CONTENT, \"精简一点，三个章节即可\");\n        // 调用\n        Flux<Map<String, Object>> flux = wenDuoDuoPptApi.updateOutline(request);\n        StringBuffer contentBuffer = new StringBuffer();\n        flux.doOnNext(chunk -> {\n            contentBuffer.append(chunk.get(\"text\"));\n            if (Objects.equals(Integer.parseInt(String.valueOf(chunk.get(\"status\"))), 4)) {\n                // status 为 4，最终 markdown 结构树\n                System.out.println(JsonUtils.toJsonString(chunk.get(\"result\")));\n                System.out.println(\" ########################################################################\");\n            }\n        }).then().block();\n        // 打印结果\n        System.out.println(contentBuffer);\n\n    }\n\n    /**\n     * 获取 PPT 模版分页\n     */\n    @Test\n    @Disabled\n    public void testGetPptTemplatePage() {\n        // 准备参数\n        WenDuoDuoPptApi.TemplateQueryRequest.Filter filter = new WenDuoDuoPptApi.TemplateQueryRequest.Filter(\n                1, null, null, null);\n        WenDuoDuoPptApi.TemplateQueryRequest request = new WenDuoDuoPptApi.TemplateQueryRequest(1, 10, filter);\n        // 调用\n        WenDuoDuoPptApi.PagePptTemplateInfo pptTemplatePage = wenDuoDuoPptApi.getTemplatePage(request);\n        // 打印结果\n        System.out.println(pptTemplatePage);\n    }\n\n    /**\n     * 生成 PPT\n     */\n    @Test\n    @Disabled\n    public void testGeneratePptx() {\n        // 准备参数\n        WenDuoDuoPptApi.PptCreateRequest request = new WenDuoDuoPptApi.PptCreateRequest(\"1901539019628613632\", \"1805081814809960448\", TEST_OUT_LINE_CONTENT);\n        // 调用\n        WenDuoDuoPptApi.PptInfo pptInfo = wenDuoDuoPptApi.create(request);\n        // 打印结果\n        System.out.println(pptInfo);\n    }\n\n    private final String TEST_OUT_LINE_CONTENT = \"\"\"\n            # Dify：新一代AI应用开发平台\n            \n            ## 1 什么是Dify\n            ### 1.1 Dify定义：AI应用开发平台\n            #### 1.1.1 低代码开发\n            Dify是一个低代码AI应用开发平台，旨在简化AI应用的构建过程，让开发者无需编写大量代码即可快速创建各种智能应用。\n            #### 1.1.2 核心功能\n            Dify的核心功能包括数据集成、模型选择、流程编排和应用部署，提供一站式解决方案，加速AI应用的落地和迭代。\n            #### 1.1.3 开源与商业\n            Dify提供开源版本和商业版本，满足不同用户的需求，开源版本适合个人开发者和小型团队，商业版本则提供更强大的功能和技术支持。\n            \n            ### 1.2 Dify解决的问题：AI开发痛点\n            #### 1.2.1 开发周期长\n            传统AI应用开发周期长，需要大量的人力和时间投入，Dify通过可视化界面和预置组件，大幅缩短开发周期。\n            #### 1.2.2 技术门槛高\n            AI技术门槛高，需要专业的知识和技能，Dify降低技术门槛，让更多开发者能够参与到AI应用的开发中来。\n            #### 1.2.3 部署和维护复杂\n            AI应用的部署和维护复杂，需要专业的运维团队，Dify提供自动化的部署和维护工具，简化流程，降低成本。\n            \n            ### 1.3 Dify发展历程\n            #### 1.3.1 早期探索\n            Dify的早期版本主要关注于自然语言处理领域的应用，通过集成各种NLP模型，提供文本分类、情感分析等功能。\n            #### 1.3.2 功能扩展\n            随着用户需求的不断增长，Dify的功能逐渐扩展到图像识别、语音识别等领域，支持更多类型的AI应用。\n            #### 1.3.3 生态建设\n            Dify积极建设开发者生态，提供丰富的文档、教程和案例，帮助开发者更好地使用Dify平台，共同推动AI技术的发展。\n            \n            ## 2 Dify的核心功能\n            ### 2.1 数据集成：连接各种数据源\n            #### 2.1.1 支持多种数据源\n            Dify支持连接各种数据源，包括关系型数据库、NoSQL数据库、文件系统、云存储等，满足不同场景的数据需求。\n            #### 2.1.2 数据转换和清洗\n            Dify提供数据转换和清洗功能，可以将不同格式的数据转换为统一的格式，并去除无效数据，提高数据质量。\n            #### 2.1.3 数据安全\n            Dify注重数据安全，采用各种安全措施保护用户的数据，包括数据加密、访问控制、权限管理等。\n            \n            ### 2.2 模型选择：丰富的AI模型库\n            #### 2.2.1 预置模型\n            Dify预置了丰富的AI模型，包括自然语言处理、图像识别、语音识别等领域的模型，开发者可以直接使用这些模型，无需自行训练，极大的简化了开发流程。\n            #### 2.2.2 自定义模型\n            Dify支持开发者上传自定义模型，满足个性化的需求。开发者可以将自己训练的模型部署到Dify平台上，与其他开发者共享。\n            #### 2.2.3 模型评估\n            Dify提供模型评估功能，可以对不同模型进行评估，选择最优的模型，提高应用性能。\n            \n            ### 2.3 流程编排：可视化流程设计器\n            #### 2.3.1 可视化界面\n            Dify提供可视化的流程设计器，开发者可以通过拖拽组件的方式，设计AI应用的流程，无需编写代码，简单高效。\n            #### 2.3.2 灵活的流程控制\n            Dify支持灵活的流程控制，可以根据不同的条件执行不同的分支，实现复杂的业务逻辑。\n            #### 2.3.3 实时调试\n            Dify提供实时调试功能，可以在设计流程的过程中，实时查看流程的执行结果，及时发现和解决问题。\n            \n            ### 2.4 应用部署：一键部署和管理\n            #### 2.4.1 快速部署\n            Dify提供一键部署功能，可以将AI应用快速部署到各种环境，包括本地环境、云环境、容器环境等。\n            #### 2.4.2 自动伸缩\n            Dify支持自动伸缩，可以根据应用的负载自动调整资源，保证应用的稳定性和性能。\n            #### 2.4.3 监控和告警\n            Dify提供监控和告警功能，可以实时监控应用的状态，并在出现问题时及时告警，方便运维人员进行处理。\n            \n            ## 3 Dify的特点和优势\n            ### 3.1 低代码：降低开发门槛\n            #### 3.1.1 可视化开发\n            Dify采用可视化开发模式，开发者无需编写大量代码，只需通过拖拽组件即可完成AI应用的开发，降低了开发门槛。\n            #### 3.1.2 预置组件\n            Dify预置了丰富的组件，包括数据源组件、模型组件、流程控制组件等，开发者可以直接使用这些组件，提高开发效率。\n            #### 3.1.3 减少代码量\n            Dify可以显著减少代码量，降低开发难度，让更多开发者能够参与到AI应用的开发中来。\n            \n            ### 3.2 灵活：满足不同场景需求\n            #### 3.2.1 支持多种数据源\n            Dify支持多种数据源，可以连接各种数据源，满足不同场景的数据需求。\n            #### 3.2.2 支持自定义模型\n            Dify支持自定义模型，开发者可以将自己训练的模型部署到Dify平台上，满足个性化的需求。\n            #### 3.2.3 灵活的流程控制\n            Dify支持灵活的流程控制，可以根据不同的条件执行不同的分支，实现复杂的业务逻辑。\n            \n            ### 3.3 高效：加速应用落地\n            #### 3.3.1 快速开发\n            Dify通过可视化界面和预置组件，大幅缩短开发周期，加速AI应用的落地。\n            #### 3.3.2 快速部署\n            Dify提供一键部署功能，可以将AI应用快速部署到各种环境，提高部署效率。\n            #### 3.3.3 自动化运维\n            Dify提供自动化的运维工具，简化运维流程，降低运维成本。\n            \n            ### 3.4 开放：构建繁荣生态\n            #### 3.4.1 开源社区\n            Dify拥有活跃的开源社区，开发者可以在社区中交流经验、分享资源、共同推动Dify的发展。\n            #### 3.4.2 丰富的文档\n            Dify提供丰富的文档、教程和案例，帮助开发者更好地使用Dify平台。\n            #### 3.4.3 API支持\n            Dify提供API支持，开发者可以通过API将Dify集成到自己的系统中，扩展Dify的功能。\n            \n            ## 4 Dify的使用场景\n            ### 4.1 智能客服：提升客户服务质量\n            #### 4.1.1 自动回复\n            Dify可以用于构建智能客服系统，实现自动回复客户的常见问题，提高客户服务效率。\n            #### 4.1.2 情感分析\n            Dify可以对客户的语音或文本进行情感分析，判断客户的情绪，并根据情绪提供个性化的服务。\n            #### 4.1.3 知识库问答\n            Dify可以构建知识库问答系统，让客户通过提问的方式获取所需的信息，提高客户满意度。\n            \n            ### 4.2 金融风控：提高风险识别能力\n            #### 4.2.1 欺诈检测\n            Dify可以用于构建金融风控系统，实现欺诈检测，识别可疑交易，降低风险。\n            #### 4.2.2 信用评估\n            Dify可以对用户的信用进行评估，并根据评估结果提供不同的金融服务。\n            #### 4.2.3 反洗钱\n            Dify可以用于反洗钱，识别可疑资金流动，防止犯罪行为。\n            \n            ### 4.3 智慧医疗：提升医疗服务水平\n            #### 4.3.1 疾病诊断\n            Dify可以用于辅助疾病诊断，提高诊断准确率，缩短诊断时间。\n            #### 4.3.2 药物研发\n            Dify可以用于药物研发，加速新药的发现和开发。\n            #### 4.3.3 智能健康管理\n            Dify可以构建智能健康管理系统，为用户提供个性化的健康建议和服务。\n            \n            ### 4.4 智慧城市：提升城市管理效率\n            #### 4.4.1 交通优化\n            Dify可以用于交通优化，提高交通效率，缓解交通拥堵。\n            #### 4.4.2 环境监测\n            Dify可以用于环境监测，实时监测空气质量、水质等环境指标，及时发现和解决环境问题。\n            #### 4.4.3 智能安防\n            Dify可以用于智能安防，提高城市安全水平，预防犯罪行为。\n            \n            ## 5 Dify的成功案例\n            ### 5.1 Case 1：某电商平台的智能客服\n            #### 5.1.1 项目背景\n            该电商平台客户服务压力大，人工客服成本高，需要一种智能化的解决方案。\n            #### 5.1.2 解决方案\n            使用Dify构建智能客服系统，实现自动回复客户的常见问题，并根据客户的情绪提供个性化的服务。\n            #### 5.1.3 效果\n            客户服务效率提高50%，客户满意度提高20%，人工客服成本降低30%。\n            \n            ### 5.2 Case 2：某银行的金融风控系统\n            #### 5.2.1 项目背景\n            该银行面临日益增长的金融风险，需要一种更有效的风险识别和控制手段。\n            #### 5.2.2 解决方案\n            使用Dify构建金融风控系统，实现欺诈检测、信用评估和反洗钱等功能，提高风险识别能力。\n            #### 5.2.3 效果\n            欺诈交易识别率提高40%，信用评估准确率提高30%，洗钱风险降低25%。\n            \n            ### 5.3 Case 3：某医院的辅助疾病诊断系统\n            #### 5.3.1 项目背景\n            该医院医生工作压力大，疾病诊断准确率有待提高，需要一种辅助诊断工具。\n            #### 5.3.2 解决方案\n            使用Dify构建辅助疾病诊断系统，根据患者的病历和症状，提供诊断建议，提高诊断准确率。\n            #### 5.3.3 效果\n            疾病诊断准确率提高20%，诊断时间缩短15%，医生工作效率提高10%。\n            \n            ## 6 Dify的未来展望\n            ### 6.1 技术升级\n            #### 6.1.1 模型优化\n            Dify将不断优化预置模型，提高模型性能，并支持更多类型的AI模型。\n            #### 6.1.2 流程引擎升级\n            Dify将升级流程引擎，提高流程的灵活性和可扩展性，支持更复杂的业务逻辑。\n            #### 6.1.3 平台性能优化\n            Dify将不断优化平台性能，提高平台的稳定性和可靠性，满足大规模应用的需求。\n            \n            ### 6.2 生态建设\n            #### 6.2.1 社区建设\n            Dify将继续加强开源社区建设，吸引更多开发者参与，共同推动Dify的发展。\n            #### 6.2.2 合作伙伴拓展\n            Dify将拓展合作伙伴，与更多的企业和机构合作，共同推动AI技术的应用。\n            #### 6.2.3 应用商店\n            Dify将构建应用商店，让开发者可以分享自己的应用，用户可以购买和使用这些应用，构建繁荣的生态系统。\n            \n            ### 6.3 应用领域拓展\n            #### 6.3.1 智能制造\n            Dify将拓展到智能制造领域，为企业提供智能化的生产管理和质量控制解决方案。\n            #### 6.3.2 智慧农业\n            Dify将拓展到智慧农业领域，为农民提供智能化的种植和养殖管理解决方案。\n            #### 6.3.3 更多领域\n            Dify将拓展到更多领域，为各行各业提供智能化的解决方案，推动社会发展。\n            \n            ## 7 总结\n            ### 7.1 Dify的价值\n            #### 7.1.1 降低AI开发门槛\n            Dify通过低代码的方式，让更多开发者能够参与到AI应用的开发中来。\n            #### 7.1.2 加速AI应用落地\n            Dify提供一站式解决方案，加速AI应用的落地和迭代。\n            #### 7.1.3 构建繁荣的AI生态\n            Dify通过开源社区和应用商店，构建繁荣的AI生态系统。\n            \n            ### 7.2 共同发展\n            #### 7.2.1 欢迎加入Dify社区\n            欢迎更多开发者加入Dify社区，共同推动Dify的发展。\n            #### 7.2.2 合作共赢\n            期待与更多的企业和机构合作，共同推动AI技术的应用。\n            #### 7.2.3 共创未来\n            让我们一起用AI技术改变世界，共创美好未来。\n            \"\"\";\n\n}\n"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/xunfei/XunFeiPptApiTests.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.model.ppt.xunfei;\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.api.XunFeiPptApi;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.mock.web.MockMultipartFile;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.io.File;\n\n/**\n * {@link XunFeiPptApi} 集成测试\n *\n * @author xiaoxin\n */\npublic class XunFeiPptApiTests {\n\n    // 讯飞 API 配置信息，实际使用时请替换为您的应用信息\n    private static final String APP_ID = \"6c8ac023\";\n    private static final String API_SECRET = \"Y2RjM2Q1MWJjZTdkYmFiODc0OGE5NmRk\";\n\n    private final XunFeiPptApi xunfeiPptApi = new XunFeiPptApi(APP_ID, API_SECRET);\n\n    /**\n     * 获取 PPT 模板列表\n     */\n    @Test\n    @Disabled\n    public void testGetTemplatePage() {\n        // 调用方法\n        XunFeiPptApi.TemplatePageResponse response = xunfeiPptApi.getTemplatePage(\"商务\", 10);\n        // 打印结果\n        System.out.println(\"模板列表响应：\" + JsonUtils.toJsonString(response));\n\n        if (response != null && response.data() != null && response.data().records() != null) {\n            System.out.println(\"模板总数：\" + response.data().total());\n            System.out.println(\"当前页码：\" + response.data().pageNum());\n            System.out.println(\"模板数量：\" + response.data().records().size());\n\n            // 打印第一个模板的信息（如果存在）\n            if (!response.data().records().isEmpty()) {\n                XunFeiPptApi.TemplateInfo firstTemplate = response.data().records().get(0);\n                System.out.println(\"模板ID：\" + firstTemplate.templateIndexId());\n                System.out.println(\"模板风格：\" + firstTemplate.style());\n                System.out.println(\"模板颜色：\" + firstTemplate.color());\n                System.out.println(\"模板行业：\" + firstTemplate.industry());\n            }\n        }\n    }\n\n    /**\n     * 创建大纲（通过文本）\n     */\n    @Test\n    @Disabled\n    public void testCreateOutline() {\n        XunFeiPptApi.CreateResponse response = getCreateResponse();\n        // 打印结果\n        System.out.println(\"创建大纲响应：\" + JsonUtils.toJsonString(response));\n\n        // 保存 sid 和 outline 用于后续测试\n        if (response != null && response.data() != null) {\n            System.out.println(\"sid: \" + response.data().sid());\n            if (response.data().outline() != null) {\n                // 使用 OutlineData 的 toJsonString 方法\n                System.out.println(\"outline: \" + response.data().outline().toJsonString());\n                // 将 outline 对象转换为 JSON 字符串，用于后续 createPptByOutline 测试\n                String outlineJson = response.data().outline().toJsonString();\n                System.out.println(\"可用于 createPptByOutline 的 outline 字符串: \" + outlineJson);\n            }\n        }\n    }\n\n    /**\n     * 创建大纲（通过文本）\n     *\n     * @return 创建大纲响应\n     */\n    private XunFeiPptApi.CreateResponse getCreateResponse() {\n        String param = \"智能体平台 Dify 介绍\";\n        return xunfeiPptApi.createOutline(param);\n    }\n\n    /**\n     * 通过大纲创建 PPT（完整参数）\n     */\n    @Test\n    @Disabled\n    public void testCreatePptByOutlineWithFullParams() {\n        // 创建大纲对象\n        XunFeiPptApi.CreateResponse createResponse = getCreateResponse();\n        // 调用方法\n        XunFeiPptApi.CreateResponse response = xunfeiPptApi.createPptByOutline(createResponse.data().outline(), \"精简一些，不要超过6个章节\");\n        // 打印结果\n        System.out.println(\"通过大纲创建 PPT 响应：\" + JsonUtils.toJsonString(response));\n\n        // 保存sid用于后续进度查询\n        if (response != null && response.data() != null) {\n            System.out.println(\"sid: \" + response.data().sid());\n            if (response.data().coverImgSrc() != null) {\n                System.out.println(\"封面图片: \" + response.data().coverImgSrc());\n            }\n        }\n    }\n\n    /**\n     * 检查 PPT 生成进度\n     */\n    @Test\n    @Disabled\n    public void testCheckProgress() {\n        // 准备参数 - 使用之前创建 PPT 时返回的 sid\n        String sid = \"e96dac09f2ec4ee289f029a5fb874ecd\"; // 替换为实际的sid\n\n        // 调用方法\n        XunFeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid);\n        // 打印结果\n        System.out.println(\"检查进度响应：\" + JsonUtils.toJsonString(response));\n\n        // 安全地访问响应数据\n        if (response != null && response.data() != null) {\n            XunFeiPptApi.ProgressResponseData data = response.data();\n\n            // 打印PPT生成状态\n            System.out.println(\"PPT 构建状态: \" + data.pptStatus());\n            System.out.println(\"AI 配图状态: \" + data.aiImageStatus());\n            System.out.println(\"演讲备注状态: \" + data.cardNoteStatus());\n\n            // 打印进度信息\n            if (data.totalPages() != null && data.donePages() != null) {\n                System.out.println(\"总页数: \" + data.totalPages());\n                System.out.println(\"已完成页数: \" + data.donePages());\n                System.out.println(\"完成进度: \" + data.getProgressPercent() + \"%\");\n            } else {\n                System.out.println(\"进度: \" + data.process() + \"%\");\n            }\n\n            // 检查是否完成\n            if (data.isAllDone()) {\n                System.out.println(\"PPT 生成已完成!\");\n                System.out.println(\"PPT 下载链接: \" + data.pptUrl());\n            }\n            // 检查是否失败\n            else if (data.isFailed()) {\n                System.out.println(\"PPT 生成失败!\");\n                System.out.println(\"错误信息: \" + data.errMsg());\n            }\n            // 正在进行中\n            else {\n                System.out.println(\"PPT 生成中，请稍后再查询...\");\n            }\n        }\n    }\n\n    /**\n     * 轮询检查 PPT 生成进度直到完成\n     */\n    @Test\n    @Disabled\n    public void testPollCheckProgress() throws InterruptedException {\n        // 准备参数 - 使用之前创建 PP T时返回的 sid\n        String sid = \"1690ef6ee0344e72b5c5434f403b8eaa\"; // 替换为实际的sid\n\n        // 最大轮询次数\n        int maxPolls = 20;\n        // 轮询间隔（毫秒）- 讯飞 API 限流为 3 秒一次\n        long pollInterval = 3500;\n\n        for (int i = 0; i < maxPolls; i++) {\n            System.out.println(\"第\" + (i + 1) + \"次查询进度...\");\n\n            // 调用方法\n            XunFeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid);\n\n            // 安全地访问响应数据\n            if (response != null && response.data() != null) {\n                XunFeiPptApi.ProgressResponseData data = response.data();\n\n                // 打印进度信息\n                System.out.println(\"PPT 构建状态: \" + data.pptStatus());\n                if (data.totalPages() != null && data.donePages() != null) {\n                    System.out.println(\"完成进度: \" + data.donePages() + \"/\" + data.totalPages()\n                            + \" (\" + data.getProgressPercent() + \"%)\");\n                }\n\n                // 检查是否完成\n                if (data.isAllDone()) {\n                    System.out.println(\"PPT 生成已完成!\");\n                    System.out.println(\"PPT 下载链接: \" + data.pptUrl());\n                    break;\n                }\n                // 检查是否失败\n                else if (data.isFailed()) {\n                    System.out.println(\"PPT 生成失败!\");\n                    System.out.println(\"错误信息: \" + data.errMsg());\n                    break;\n                }\n                // 正在进行中，继续轮询\n                else {\n                    System.out.println(\"PPT 生成中，等待\" + (pollInterval / 1000) + \"秒后继续查询...\");\n                    Thread.sleep(pollInterval);\n                }\n            } else {\n                System.out.println(\"查询失败，等待\" + (pollInterval / 1000) + \"秒后重试...\");\n                Thread.sleep(pollInterval);\n            }\n        }\n    }\n\n    /**\n     * 直接创建 PPT（通过文本）\n     */\n    @Test\n    @Disabled\n    public void testCreatePptByText() {\n        // 准备参数\n        String query = \"合肥天气趋势分析，包括近5年的气温变化、降水量变化、极端天气事件，以及对城市生活的影响\";\n\n        // 调用方法\n        XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(query);\n        // 打印结果\n        System.out.println(\"直接创建 PPT 响应：\" + JsonUtils.toJsonString(response));\n\n        // 保存 sid 用于后续进度查询\n        if (response != null && response.data() != null) {\n            System.out.println(\"sid: \" + response.data().sid());\n            if (response.data().coverImgSrc() != null) {\n                System.out.println(\"封面图片: \" + response.data().coverImgSrc());\n            }\n            System.out.println(\"标题: \" + response.data().title());\n            System.out.println(\"副标题: \" + response.data().subTitle());\n        }\n    }\n\n    /**\n     * 直接创建 PPT（通过文件）\n     */\n    @Test\n    @Disabled\n    public void testCreatePptByFile() {\n        // 准备参数\n        File file = new File(\"src/test/resources/test.txt\"); // 请确保此文件存在\n        MultipartFile multipartFile = convertFileToMultipartFile(file);\n\n        // 调用方法\n        XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(multipartFile, file.getName());\n        // 打印结果\n        System.out.println(\"通过文件创建PPT响应：\" + JsonUtils.toJsonString(response));\n\n        // 保存 sid 用于后续进度查询\n        if (response != null && response.data() != null) {\n            System.out.println(\"sid: \" + response.data().sid());\n            if (response.data().coverImgSrc() != null) {\n                System.out.println(\"封面图片: \" + response.data().coverImgSrc());\n            }\n            System.out.println(\"标题: \" + response.data().title());\n            System.out.println(\"副标题: \" + response.data().subTitle());\n        }\n    }\n\n    /**\n     * 直接创建 PPT（完整参数）\n     */\n    @Test\n    @Disabled\n    public void testCreatePptWithFullParams() {\n        // 准备参数\n        String query = \"合肥天气趋势分析，包括近 5 年的气温变化、降水量变化、极端天气事件，以及对城市生活的影响\";\n\n        // 创建请求对象\n        XunFeiPptApi.CreatePptRequest request = XunFeiPptApi.CreatePptRequest.builder()\n                .query(query)\n                .language(\"cn\")\n                .isCardNote(true)\n                .search(true)\n                .isFigure(true)\n                .aiImage(\"advanced\")\n                .author(\"测试用户\")\n                .build();\n\n        // 调用方法\n        XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(request);\n        // 打印结果\n        System.out.println(\"使用完整参数创建 PPT 响应：\" + JsonUtils.toJsonString(response));\n\n        // 保存 sid 用于后续进度查询\n        if (response != null && response.data() != null) {\n            String sid = response.data().sid();\n            System.out.println(\"sid: \" + sid);\n            if (response.data().coverImgSrc() != null) {\n                System.out.println(\"封面图片: \" + response.data().coverImgSrc());\n            }\n            System.out.println(\"标题: \" + response.data().title());\n            System.out.println(\"副标题: \" + response.data().subTitle());\n\n            // 立即查询一次进度\n            System.out.println(\"立即查询进度...\");\n            XunFeiPptApi.ProgressResponse progressResponse = xunfeiPptApi.checkProgress(sid);\n            if (progressResponse != null && progressResponse.data() != null) {\n                XunFeiPptApi.ProgressResponseData progressData = progressResponse.data();\n                System.out.println(\"PPT 构建状态: \" + progressData.pptStatus());\n                if (progressData.totalPages() != null && progressData.donePages() != null) {\n                    System.out.println(\"完成进度: \" + progressData.donePages() + \"/\" + progressData.totalPages()\n                            + \" (\" + progressData.getProgressPercent() + \"%)\");\n                }\n            }\n        }\n    }\n\n    /**\n     * 将 File 转换为 MultipartFile\n     */\n    private MultipartFile convertFileToMultipartFile(File file) {\n        return new MockMultipartFile(\"file\", file.getName(), \"text/plain\", FileUtil.readBytes(file));\n    }\n\n}"
  },
  {
    "path": "yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/websearch/AiBoChaWebSearchClientTest.java",
    "content": "package cn.iocoder.yudao.module.ai.framework.ai.core.websearch;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchRequest;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;\nimport cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * {@link AiBoChaWebSearchClient} 集成测试类\n *\n * @author 芋道源码\n */\npublic class AiBoChaWebSearchClientTest {\n\n    private final AiBoChaWebSearchClient webSearchClient = new AiBoChaWebSearchClient(\n            \"sk-40500e52840f4d24b956d0b1d80d9abe\");\n\n    @Test\n    @Disabled\n    public void testSearch() {\n        AiWebSearchRequest request = new AiWebSearchRequest()\n                .setQuery(\"阿里巴巴\")\n                .setCount(3);\n        AiWebSearchResponse response = webSearchClient.search(request);\n        System.out.println(JsonUtils.toJsonPrettyString(response));\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <modules>\n        <module>yudao-module-bpm-api</module>\n        <module>yudao-module-bpm-server</module>\n    </modules>\n    <artifactId>yudao-module-bpm</artifactId>\n    <packaging>pom</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        bpm 包下，业务流程管理（Business Process Management），我们放工作流的功能。\n        例如说：流程定义、表单配置、审核中心（我的申请、我的待办、我的已办）等等\n        bpm 解释：https://baike.baidu.com/item/BPM/1933\n\n        工作流基于 Flowable 6 实现，分成流程定义、流程表单、流程实例、流程任务等功能模块。\n    </description>\n\n</project>\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-bpm</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-bpm-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        bpm 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springdoc</groupId>\n            <artifactId>springdoc-openapi-ui</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEvent.java",
    "content": "package cn.iocoder.yudao.module.bpm.api.event;\n\nimport lombok.Data;\nimport org.springframework.context.ApplicationEvent;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 流程实例的状态（结果）发生变化的 Event\n *\n * @author 芋道源码\n */\n@SuppressWarnings(\"ALL\")\n@Data\npublic class BpmProcessInstanceStatusEvent extends ApplicationEvent {\n\n    /**\n     * 流程实例的编号\n     */\n    @NotNull(message = \"流程实例的编号不能为空\")\n    private String id;\n    /**\n     * 流程实例的 key\n     */\n    @NotNull(message = \"流程实例的 key 不能为空\")\n    private String processDefinitionKey;\n    /**\n     * 流程实例的结果\n     */\n    @NotNull(message = \"流程实例的状态不能为空\")\n    private Integer status;\n    /**\n     * 流程实例结束的原因\n     */\n    private String reason;\n\n    /**\n     * 流程实例对应的业务标识\n     * 例如说，请假\n     */\n    private String businessKey;\n\n    public BpmProcessInstanceStatusEvent() {\n        // new Object() 保证非空\n        super(new Object());\n    }\n\n    public BpmProcessInstanceStatusEvent(Object source) {\n        super(source);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.api.event;\n\nimport cn.hutool.core.util.StrUtil;\nimport org.springframework.context.ApplicationListener;\n\n/**\n * {@link BpmProcessInstanceStatusEvent} 的监听器\n *\n * @author 芋道源码\n */\npublic abstract class BpmProcessInstanceStatusEventListener\n        implements ApplicationListener<BpmProcessInstanceStatusEvent> {\n\n    @Override\n    public final void onApplicationEvent(BpmProcessInstanceStatusEvent event) {\n        if (!StrUtil.equals(event.getProcessDefinitionKey(), getProcessDefinitionKey())) {\n            return;\n        }\n        onEvent(event);\n    }\n\n    /**\n     * @return 返回监听的流程定义 Key\n     */\n    protected abstract String getProcessDefinitionKey();\n\n    /**\n     * 处理事件\n     *\n     * @param event 事件\n     */\n    protected abstract void onEvent(BpmProcessInstanceStatusEvent event);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/package-info.java",
    "content": "/**\n * bpm API 包，定义暴露给其它模块的 API\n */\npackage cn.iocoder.yudao.module.bpm.api;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java",
    "content": "package cn.iocoder.yudao.module.bpm.api.task;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;\nimport cn.iocoder.yudao.module.bpm.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport javax.validation.Valid;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 流程实例\")\npublic interface BpmProcessInstanceApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/process-instance\";\n\n    @PostMapping(PREFIX + \"/create\")\n    @Operation(summary = \"创建流程实例（提供给内部），返回实例编号\")\n    @Parameter(name = \"userId\", description = \"用户编号\", required = true, example = \"1\")\n    CommonResult<String> createProcessInstance(@RequestParam(\"userId\") Long userId,\n                                               @Valid @RequestBody BpmProcessInstanceCreateReqDTO reqDTO);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java",
    "content": "package cn.iocoder.yudao.module.bpm.api.task.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"RPC 服务 - 流程实例的创建 Request DTO\")\n@Data\npublic class BpmProcessInstanceCreateReqDTO {\n\n    @Schema(description = \"流程定义的标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"leave\")\n    @NotEmpty(message = \"流程定义的标识不能为空\")\n    private String processDefinitionKey;\n\n    @Schema(description = \"变量实例\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Map<String, Object> variables;\n\n    @Schema(description = \"业务的唯一标识\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"业务的唯一标识不能为空\")\n    private String businessKey; // 例如说，请假申请的编号。通过它，可以查询到对应的实例\n\n    /**\n     * 发起人自选审批人 Map\n     *\n     * key：taskKey 任务编码\n     * value：审批人的数组\n     * 例如：{ taskKey1 :[1, 2] }，则表示 taskKey1 这个任务，提前设定了，由 userId 为 1,2 的用户进行审批\n     */\n    @Schema(description = \"发起人自选审批人 Map\")\n    private Map<String, List<Long>> startUserSelectAssignees;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ApiConstants.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\n\n/**\n * API 相关的枚举\n *\n * @author 芋道源码\n */\npublic class ApiConstants {\n\n    /**\n     * 服务名\n     *\n     * 注意，需要保证和 spring.application.name 保持一致\n     */\n    public static final String NAME = \"bpm-server\";\n\n    public static final String PREFIX = RpcConstants.RPC_API_PREFIX + \"/bpm\";\n\n    public static final String VERSION = \"1.0.0\";\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/DictTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums;\n\n/**\n * BPM 字典类型的枚举类\n *\n * @author 芋道源码\n */\npublic interface DictTypeConstants {\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * Bpm 错误码枚举类\n * <p>\n * bpm 系统，使用 1-009-000-000 段\n */\npublic interface ErrorCodeConstants {\n\n    // ==========  通用流程处理 模块 1-009-000-000 ==========\n\n    // ========== OA 流程模块 1-009-001-000 ==========\n    ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, \"请假申请不存在\");\n\n    // ========== 流程模型 1-009-002-000 ==========\n    ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, \"已经存在流程标识为【{}】的流程\");\n    ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_009_002_001, \"流程模型不存在\");\n    ErrorCode MODEL_KEY_VALID = new ErrorCode(1_009_002_002, \"流程标识格式不正确，需要以字母或下划线开头，后接任意字母、数字、中划线、下划线、句点！\");\n    ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, \"部署流程失败，原因：流程表单未配置，请点击【修改流程】按钮进行配置\");\n    ErrorCode MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG = new ErrorCode(1_009_002_004, \"部署流程失败，\" +\n            \"原因：用户任务({})未配置审批人，请点击【流程设计】按钮，选择该它的【任务（审批人）】进行配置\");\n    ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, \"部署流程失败，原因：BPMN 流程图中，没有开始事件\");\n    ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, \"部署流程失败，原因：BPMN 流程图中，用户任务({})的名字不存在\");\n    ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, \"操作流程失败，原因：你不是该流程({})的管理员\");\n    ErrorCode MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR = new ErrorCode(1_009_002_008, \"部署流程失败，原因：首个任务({})的审批人不能是【审批人自选】\");\n\n    // ========== 流程定义 1-009-003-000 ==========\n    ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, \"流程定义的标识期望是({})，当前是({})，请修改 BPMN 流程图\");\n    ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, \"流程定义的名字期望是({})，当前是({})，请修改 BPMN 流程图\");\n    ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, \"流程定义不存在\");\n    ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, \"流程定义处于挂起状态\");\n\n    // ========== 流程实例 1-009-004-000 ==========\n    ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, \"流程实例不存在\");\n    ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, \"流程取消失败，流程不处于运行中\");\n    ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, \"流程取消失败，该流程不是你发起的\");\n    ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, \"任务({})的候选人未配置\");\n    ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, \"任务({})的候选人({})不存在\");\n    ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, \"发起流程失败，你没有权限发起该流程\");\n    ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, \"流程取消失败，该流程不允许取消\");\n    ErrorCode PROCESS_INSTANCE_HTTP_CALL_ERROR = new ErrorCode(1_009_004_006, \"流程 Http 请求调用失败\");\n    ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, \"下一个任务({})的审批人未配置\");\n    ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, \"子流程取消失败，子流程不允许取消\");\n\n    // ========== 流程任务 1-009-005-000 ==========\n    ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, \"操作失败，原因：该任务的审批人不是你\");\n    ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, \"流程任务不存在\");\n    ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, \"当前任务处于挂起状态，不能操作\");\n    ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, \" 目标节点不存在\");\n    ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, \"退回任务失败，目标节点是在并行网关上或非同一路线上，不可跳转\");\n    ErrorCode TASK_DELEGATE_FAIL_USER_REPEAT = new ErrorCode(1_009_005_007, \"任务委派失败，委派人和当前审批人为同一人\");\n    ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, \"任务委派失败，被委派人不存在\");\n    ErrorCode TASK_SIGN_CREATE_USER_NOT_EXIST = new ErrorCode(1_009_005_009, \"任务加签：选择的用户不存在\");\n    ErrorCode TASK_SIGN_CREATE_TYPE_ERROR = new ErrorCode(1_009_005_010, \"任务加签：当前任务已经{}，不能{}\");\n    ErrorCode TASK_SIGN_CREATE_USER_REPEAT = new ErrorCode(1_009_005_011, \"任务加签失败，加签人与现有审批人[{}]重复\");\n    ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, \"任务减签失败，被减签的任务必须是通过加签生成的任务\");\n    ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, \"任务转办失败，转办人和当前审批人为同一人\");\n    ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, \"任务转办失败，转办人不存在\");\n    ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, \"签名不能为空！\");\n    ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, \"审批意见不能为空！\");\n    ErrorCode TASK_WITHDRAW_FAIL_PROCESS_NOT_RUNNING = new ErrorCode(1_009_005_017, \"撤回失败，流程实例未运行！\");\n    ErrorCode TASK_WITHDRAW_FAIL_TASK_NOT_EXISTS = new ErrorCode(1_009_005_018, \"撤回失败，未查询到用户已办任务！\");\n    ErrorCode TASK_WITHDRAW_FAIL_NOT_ALLOW = new ErrorCode(1_009_005_019, \"撤回失败，此流程不允许撤回操作！\");\n    ErrorCode TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW = new ErrorCode(1_009_005_020, \"撤回失败，下一节点不满足撤回条件！\");\n\n    // ========== 动态表单模块 1-009-010-000 ==========\n    ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, \"动态表单不存在\");\n    ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1_009_010_001, \"表单项({}) 和 ({}) 使用了相同的字段名({})\");\n\n    // ========== 用户组模块 1-009-011-000 ==========\n    ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1_009_011_000, \"用户分组不存在\");\n    ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1_009_011_001, \"名字为【{}】的用户分组已被禁用\");\n\n    // ========== 用户组模块 1-009-012-000 ==========\n    ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, \"流程分类不存在\");\n    ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, \"流程分类名字【{}】重复\");\n    ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, \"流程分类编码【{}】重复\");\n    ErrorCode CATEGORY_DELETE_FAIL_MODEL_USED = new ErrorCode(1_009_012_003, \"删除失败，流程分类【{}】已被流程模型使用，请先删除对应的流程模型\");\n\n    // ========== BPM 流程监听器 1-009-013-000 ==========\n    ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, \"流程监听器不存在\");\n    ErrorCode PROCESS_LISTENER_CLASS_NOT_FOUND = new ErrorCode(1_009_013_001, \"流程监听器类({})不存在\");\n    ErrorCode PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR = new ErrorCode(1_009_013_002, \"流程监听器类({})没有实现接口({})\");\n    ErrorCode PROCESS_LISTENER_EXPRESSION_INVALID = new ErrorCode(1_009_013_003, \"流程监听器表达式({})不合法\");\n\n    // ========== BPM 流程表达式 1-009-014-000 ==========\n    ErrorCode PROCESS_EXPRESSION_NOT_EXISTS = new ErrorCode(1_009_014_000, \"流程表达式不存在\");\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 自动去重的类型的枚举\n *\n * @author Lesan\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmAutoApproveTypeEnum implements ArrayValuable<Integer> {\n\n    NONE(0, \"不自动通过\"),\n    APPROVE_ALL(1, \"仅审批一次，后续重复的审批节点均自动通过\"),\n    APPROVE_SEQUENT(2, \"仅针对连续审批的节点自动通过\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmAutoApproveTypeEnum::getType).toArray(Integer[]::new);\n\n    private final Integer type;\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * BPM 边界事件 (boundary event) 自定义类型枚举\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmBoundaryEventTypeEnum {\n\n    USER_TASK_TIMEOUT(1, \"用户任务超时\"),\n    DELAY_TIMER_TIMEOUT(2, \"延迟器超时\"),\n    CHILD_PROCESS_TIMEOUT(3, \"子流程超时\");\n\n    private final Integer type;\n    private final String name;\n\n    public static BpmBoundaryEventTypeEnum typeOf(Integer type) {\n        return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 子流程多实例来源类型枚举\n *\n * @author Lesan\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable<Integer> {\n\n    FIXED_QUANTITY(1, \"固定数量\"),\n    NUMBER_FORM(2, \"数字表单\"),\n    MULTIPLE_FORM(3, \"多选表单\");\n\n    private final Integer type;\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessMultiInstanceSourceTypeEnum::getType).toArray(Integer[]::new);\n\n    public static BpmChildProcessMultiInstanceSourceTypeEnum typeOf(Integer type) {\n        return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 当子流程发起人为空时类型枚举\n *\n * @author Lesan\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmChildProcessStartUserEmptyTypeEnum implements ArrayValuable<Integer> {\n\n    MAIN_PROCESS_START_USER(1, \"同主流程发起人\"),\n    CHILD_PROCESS_ADMIN(2, \"子流程管理员\"),\n    MAIN_PROCESS_ADMIN(3, \"主流程管理员\");\n\n    private final Integer type;\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserEmptyTypeEnum::getType).toArray(Integer[]::new);\n\n    public static BpmChildProcessStartUserEmptyTypeEnum typeOf(Integer type) {\n        return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 子流程发起人类型枚举\n *\n * @author Lesan\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmChildProcessStartUserTypeEnum implements ArrayValuable<Integer> {\n\n    MAIN_PROCESS_START_USER(1, \"同主流程发起人\"),\n    FROM_FORM(2, \"表单\");\n\n    private final Integer type;\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserTypeEnum::getType).toArray(Integer[]::new);\n\n    public static BpmChildProcessStartUserTypeEnum typeOf(Integer type) {\n        return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 延迟器类型枚举\n *\n * @author Lesan\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmDelayTimerTypeEnum implements ArrayValuable<Integer> {\n\n    FIXED_TIME_DURATION(1, \"固定时长\"),\n    FIXED_DATE_TIME(2, \"固定日期\");\n\n    private final Integer type;\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmDelayTimerTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmFieldPermissionEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * BPM 表单权限的枚举\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmFieldPermissionEnum {\n\n    READ(1, \"只读\"),\n    WRITE(2, \"可编辑\"),\n    NONE(3, \"隐藏\");\n\n    /**\n     * 权限\n     */\n    private final Integer permission;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    public static BpmFieldPermissionEnum valueOf(Integer permission) {\n        return ArrayUtil.firstMatch(item -> item.getPermission().equals(permission), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM HTTP 请求参数设置类型。用于 Simple 设计器任务监听器和触发器配置。\n *\n * @author Lesan\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmHttpRequestParamTypeEnum implements ArrayValuable<Integer> {\n\n    FIXED_VALUE(1, \"固定值\"),\n    FROM_FORM(2, \"表单\");\n\n    private final Integer type;\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmHttpRequestParamTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmModelFormTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 模型的表单类型的枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmModelFormTypeEnum implements ArrayValuable<Integer> {\n\n    NORMAL(10, \"流程表单\"), // 对应 BpmFormDO\n    CUSTOM(20, \"业务表单\") // 业务自己定义的表单，自己进行数据的存储\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelFormTypeEnum::getType).toArray(Integer[]::new);\n\n    private final Integer type;\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmModelTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 模型的类型的枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmModelTypeEnum implements ArrayValuable<Integer> {\n\n    BPMN(10, \"BPMN 设计器\"), // https://bpmn.io/toolkit/bpmn-js/\n    SIMPLE(20, \"SIMPLE 设计器\"); // 参考钉钉、飞书工作流的设计器\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelTypeEnum::getType).toArray(Integer[]::new);\n\n    private final Integer type;\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * BPM 流程监听器的类型\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmProcessListenerTypeEnum {\n\n    EXECUTION(\"execution\", \"执行监听器\"),\n    TASK(\"task\", \"任务执行器\");\n\n    private final String type;\n    private final String name;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * BPM 流程监听器的值类型\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmProcessListenerValueTypeEnum {\n\n    CLASS(\"class\", \"Java 类\"),\n    DELEGATE_EXPRESSION(\"delegateExpression\", \"代理表达式\"),\n    EXPRESSION(\"expression\", \"表达式\");\n\n    private final String type;\n    private final String name;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 仿钉钉的流程器设计器条件节点的条件类型\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmSimpleModeConditionTypeEnum implements ArrayValuable<Integer> {\n\n    EXPRESSION(1, \"条件表达式\"),\n    RULE(2, \"条件规则\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModeConditionTypeEnum::getType).toArray(Integer[]::new);\n\n    private final Integer type;\n\n    private final String name;\n\n    public static BpmSimpleModeConditionTypeEnum valueOf(Integer type) {\n        return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values());\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\nimport java.util.Objects;\n\n/**\n * 仿钉钉的流程器设计器的模型节点类型\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> {\n\n    // 0 ~ 1 开始和结束\n    START_NODE(0, \"开始\", \"startEvent\"),\n    END_NODE(1, \"结束\", \"endEvent\"),\n\n    // 10 ~ 49 各种节点\n    START_USER_NODE(10, \"发起人\", \"userTask\"), // 发起人节点。前端的开始节点，Id 固定\n    APPROVE_NODE(11, \"审批人\", \"userTask\"),\n    COPY_NODE(12, \"抄送人\", \"serviceTask\"),\n    TRANSACTOR_NODE(13, \"办理人\", \"userTask\"),\n\n    DELAY_TIMER_NODE(14, \"延迟器\", \"receiveTask\"),\n    TRIGGER_NODE(15, \"触发器\", \"serviceTask\"),\n\n    CHILD_PROCESS(20, \"子流程\", \"callActivity\"),\n\n    // 50 ~ 条件分支\n    CONDITION_NODE(50, \"条件\", \"sequenceFlow\"), // 用于构建流转条件的表达式\n    CONDITION_BRANCH_NODE(51, \"条件分支\", \"exclusiveGateway\"),\n    PARALLEL_BRANCH_NODE(52, \"并行分支\", \"inclusiveGateway\"), // 并行分支使用包容网关实现，条件表达式结果设置为 true\n    INCLUSIVE_BRANCH_NODE(53, \"包容分支\", \"inclusiveGateway\"),\n    ROUTER_BRANCH_NODE(54, \"路由分支\", \"exclusiveGateway\")\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModelNodeTypeEnum::getType).toArray(Integer[]::new);\n\n    private final Integer type;\n    private final String name;\n    private final String bpmnType;\n\n    /**\n     * 判断是否为分支节点\n     *\n     * @param type 节点类型\n     */\n    public static boolean isBranchNode(Integer type) {\n        return Objects.equals(CONDITION_BRANCH_NODE.getType(), type)\n                || Objects.equals(PARALLEL_BRANCH_NODE.getType(), type)\n                || Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type)\n                || Objects.equals(ROUTER_BRANCH_NODE.getType(), type);\n    }\n\n    public static BpmSimpleModelNodeTypeEnum valueOf(Integer type) {\n        return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values());\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM Simple 触发器类型枚举\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmTriggerTypeEnum implements ArrayValuable<Integer> {\n\n    HTTP_REQUEST(1, \"发起 HTTP 请求\"), // BPM => 业务，流程继续执行，无需等待业务\n    HTTP_CALLBACK(2, \"接收 HTTP 回调\"), // BPM => 业务 => BPM，流程卡主，等待业务回调\n\n    FORM_UPDATE(10, \"更新流程表单数据\"),\n    FORM_DELETE(11, \"删除流程表单数据\"),\n    ;\n\n    /**\n     * 触发器执行动作类型\n     */\n    private final Integer type;\n\n    /**\n     * 触发器执行动作描述\n     */\n    private final String desc;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTriggerTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static BpmTriggerTypeEnum typeOf(Integer type) {\n        return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 多人审批方式的枚举\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmUserTaskApproveMethodEnum implements ArrayValuable<Integer> {\n\n    RANDOM(1, \"随机挑选一人审批\", null),\n    RATIO(2, \"多人会签(按通过比例)\", \"${ nrOfCompletedInstances/nrOfInstances >= %s}\"), // 会签（按通过比例）\n    ANY(3, \"多人或签(一人通过或拒绝)\", \"${ nrOfCompletedInstances > 0 }\"), // 或签（通过只需一人，拒绝只需一人）\n    SEQUENTIAL(4, \"依次审批\", \"${ nrOfCompletedInstances >= nrOfInstances }\"); // 依次审批\n\n    /**\n     * 审批方式\n     */\n    private final Integer method;\n    /**\n     * 名字\n     */\n    private final String name;\n    /**\n     * 完成表达式\n     */\n    private final String completionCondition;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveMethodEnum::getMethod).toArray(Integer[]::new);\n\n    public static BpmUserTaskApproveMethodEnum valueOf(Integer method) {\n        return ArrayUtil.firstMatch(item -> item.getMethod().equals(method), values());\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 用户任务的审批类型枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmUserTaskApproveTypeEnum implements ArrayValuable<Integer> {\n\n    USER(1), // 人工审批\n    AUTO_APPROVE(2), // 自动通过\n    AUTO_REJECT(3); // 自动拒绝\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveTypeEnum::getType).toArray(Integer[]::new);\n\n    private final Integer type;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * BPM 用户任务的审批人为空时，处理类型枚举\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum BpmUserTaskAssignEmptyHandlerTypeEnum implements ArrayValuable<Integer> {\n\n    APPROVE(1), // 自动通过\n    REJECT(2), // 自动拒绝\n    ASSIGN_USER(3), // 指定人员审批\n    ASSIGN_ADMIN(4), // 转交给流程管理员\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignEmptyHandlerTypeEnum::getType).toArray(Integer[]::new);\n\n    private final Integer type;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * BPM 用户任务的审批人与发起人相同时，处理类型枚举\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum BpmUserTaskAssignStartUserHandlerTypeEnum implements ArrayValuable<Integer> {\n\n    START_USER_AUDIT(1), // 由发起人对自己审批\n    SKIP(2), // 自动跳过【参考飞书】：1）如果当前节点还有其他审批人，则交由其他审批人进行审批；2）如果当前节点没有其他审批人，则该节点自动通过\n    TRANSFER_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】：若部门负责人为空，则自动通过\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignStartUserHandlerTypeEnum::getType).toArray(Integer[]::new);\n\n    private final Integer type;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 用户任务拒绝处理类型枚举\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmUserTaskRejectHandlerTypeEnum implements ArrayValuable<Integer> {\n\n    FINISH_PROCESS_INSTANCE(1, \"终止流程\"),\n    RETURN_USER_TASK(2, \"驳回到指定任务节点\");\n\n    private final Integer type;\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskRejectHandlerTypeEnum::getType).toArray(Integer[]::new);\n\n    public static BpmUserTaskRejectHandlerTypeEnum typeOf(Integer type) {\n        return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.definition;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 用户任务超时处理类型枚举\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmUserTaskTimeoutHandlerTypeEnum implements ArrayValuable<Integer> {\n\n    REMINDER(1,\"自动提醒\"),\n    APPROVE(2, \"自动同意\"),\n    REJECT(3, \"自动拒绝\");\n\n    private final Integer type;\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskTimeoutHandlerTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/message/BpmMessageEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.message;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * Bpm 消息的枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum BpmMessageEnum {\n\n    PROCESS_INSTANCE_APPROVE(\"bpm_process_instance_approve\"), // 流程任务被审批通过时，发送给申请人\n    PROCESS_INSTANCE_REJECT(\"bpm_process_instance_reject\"), // 流程任务被审批不通过时，发送给申请人\n    TASK_ASSIGNED(\"bpm_task_assigned\"), // 任务被分配时，发送给审批人\n    TASK_TIMEOUT(\"bpm_task_timeout\"); // 任务审批超时时，发送给审批人\n\n    /**\n     * 短信模板的标识\n     *\n     * 关联 SmsTemplateDO 的 code 属性\n     */\n    private final String smsTemplateCode;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.task;\n\nimport cn.hutool.core.util.StrUtil;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 流程任务的 Comment 评论类型枚举\n *\n * @author kehaiyou\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmCommentTypeEnum {\n\n    APPROVE(\"1\", \"审批通过\", \"审批通过，原因是：{}\"),\n    REJECT(\"2\", \"不通过\", \"审批不通过：原因是：{}\"),\n    CANCEL(\"3\", \"已取消\", \"系统自动取消，原因是：{}\"),\n    RETURN(\"4\", \"退回\", \"任务被退回，原因是：{}\"),\n    DELEGATE_START(\"5\", \"委派发起\", \"[{}]将任务委派给[{}]，委派理由为:{}\"),\n    DELEGATE_END(\"6\", \"委派完成\", \"[{}]完成委派任务，任务重新回到[{}]手中，审批建议为:{}\"),\n    TRANSFER(\"7\", \"转派\", \"[{}]将任务转派给[{}]，转派理由为:{}\"),\n    ADD_SIGN(\"8\", \"加签\", \"[{}]{}给了[{}]，理由为：{}\"),\n    SUB_SIGN(\"9\", \"减签\", \"[{}]操作了【减签】,审批人[{}]的任务被取消\"),\n    ;\n\n    /**\n     * 操作类型\n     *\n     * 由于 BPM Comment 类型为 String，所以这里就不使用 Integer\n     */\n    private final String type;\n    /**\n     * 操作名字\n     */\n    private final String name;\n    /**\n     * 操作描述\n     */\n    private final String comment;\n\n    public String formatComment(Object... params) {\n         return StrUtil.format(comment, params);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.task;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 流程实例 ProcessInstance 的状态\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmProcessInstanceStatusEnum implements ArrayValuable<Integer> {\n\n    NOT_START(-1, \"未开始\"),\n    RUNNING(1, \"审批中\"),\n    APPROVE(2, \"审批通过\"),\n    REJECT(3, \"审批不通过\"),\n    CANCEL(4, \"已取消\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmProcessInstanceStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n    /**\n     * 描述\n     */\n    private final String desc;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isRejectStatus(Integer status) {\n        return REJECT.getStatus().equals(status);\n    }\n\n    public static boolean isProcessEndStatus(Integer status) {\n        return ObjectUtils.equalsAny(status,\n                APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus());\n    }\n\n    public static BpmProcessInstanceStatusEnum valueOf(Integer status) {\n        return ArrayUtil.firstMatch(item -> item.getStatus().equals(status), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.task;\n\nimport cn.hutool.core.util.StrUtil;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 流程实例/任务的的处理原因枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmReasonEnum {\n\n    // ========== 流程实例的独有原因 ==========\n\n    REJECT_TASK(\"审批不通过任务，原因：{}\"), // 场景：用户审批不通过任务。修改文案时，需要注意 isRejectReason 方法\n    CANCEL_PROCESS_INSTANCE_BY_START_USER(\"用户主动取消流程，原因：{}\"), // 场景：用户主动取消流程\n    CANCEL_PROCESS_INSTANCE_BY_ADMIN(\"管理员【{}】取消流程，原因：{}\"), // 场景：管理员取消流程\n    CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS(\"子流程自动取消，原因：主流程已取消\"),\n    REJECT_CHILD_PROCESS(\"子流程审批不通过\"),\n\n    // ========== 流程任务的独有原因 ==========\n\n    CANCEL_BY_SYSTEM(\"系统自动取消\"), // 场景：非常多，比如说：1）多任务审批已经满足条件，无需审批该任务；2）流程实例被取消，无需审批该任务；等等\n    TIMEOUT_APPROVE(\"审批超时，系统自动通过\"),\n    TIMEOUT_REJECT(\"审批超时，系统自动不通过\"),\n    ASSIGN_START_USER_APPROVE(\"审批人与提交人为同一人时，自动通过\"),\n    ASSIGN_START_USER_APPROVE_WHEN_SKIP(\"审批人与提交人为同一人时，自动通过\"),\n    ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE(\"发起人节点首次自动通过\"), // 目前仅“子流程”使用\n    ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND(\"审批人与提交人为同一人时，找不到部门负责人，自动通过\"),\n    ASSIGN_START_USER_TRANSFER_DEPT_LEADER(\"审批人与提交人为同一人时，转交给部门负责人审批\"),\n    ASSIGN_EMPTY_APPROVE(\"审批人为空，自动通过\"),\n    ASSIGN_EMPTY_REJECT(\"审批人为空，自动不通过\"),\n    APPROVE_TYPE_AUTO_APPROVE(\"非人工审核，自动通过\"),\n    APPROVE_TYPE_AUTO_REJECT(\"非人工审核，自动不通过\"),\n    CANCEL_BY_PROCESS_CLEAN(\"进程清理自动取消\"),\n    CANCEL_BY_WITHDRAW(\"前一任务撤回，系统自动取消\"),\n    ;\n\n    private final String reason;\n\n    /**\n     * 格式化理由\n     *\n     * @param args 参数\n     * @return 理由\n     */\n    public String format(Object... args) {\n        return StrUtil.format(reason, args);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskSignTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.task;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 流程任务的加签类型枚举\n *\n * @author kehaiyou\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmTaskSignTypeEnum {\n\n    /**\n     * 向前加签，需要前置任务审批完成，才回到原审批人\n     */\n    BEFORE(\"before\", \"向前加签\"),\n    /**\n     * 向后加签，需要后置任务全部审批完，才会通过原审批人节点\n     */\n    AFTER(\"after\", \"向后加签\");\n\n    /**\n     * 类型\n     */\n    private final String type;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    public static String nameOfType(String type) {\n        for (BpmTaskSignTypeEnum value : values()) {\n            if (value.type.equals(type)) {\n                return value.name;\n            }\n        }\n        return null;\n    }\n\n    public static BpmTaskSignTypeEnum of(String type) {\n        return ArrayUtil.firstMatch(value -> value.getType().equals(type), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.enums.task;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 流程任务 Task 的状态枚举\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmTaskStatusEnum implements ArrayValuable<Integer> {\n\n    SKIP(-2, \"跳过\"),\n    NOT_START(-1, \"未开始\"),\n    RUNNING(1, \"审批中\"),\n    APPROVE(2, \"审批通过\"),\n    REJECT(3, \"审批不通过\"),\n    CANCEL(4, \"已取消\"),\n\n    RETURN(5, \"已退回\"),\n\n    /**\n     * 使用场景：\n     * 1. 任务被向后【加签】时，它在审批通过后，会变成 APPROVING 这个状态，然后等到【加签】出来的任务都被审批后，才会变成 APPROVE 审批通过\n     */\n    APPROVING(7, \"审批通过中\"),\n    /**\n     * 使用场景：\n     * 1. 任务被向前【加签】时，它会变成 WAIT 状态，需要等待【加签】出来的任务被审批后，它才能继续变为 RUNNING 继续审批\n     * 2. 任务被向后【加签】时，【加签】出来的任务处于 WAIT 状态，它们需要等待该任务被审批后，它们才能继续变为 RUNNING 继续审批\n     */\n    WAIT(0, \"待审批\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTaskStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     * <p>\n     * 如果新增时，注意 {@link #isEndStatus(Integer)} 是否需要变更\n     */\n    private final Integer status;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isRejectStatus(Integer status) {\n        return REJECT.getStatus().equals(status);\n    }\n\n    /**\n     * 判断该状态是否已经处于 End 最终状态\n     * <p>\n     * 主要用于一些状态更新的逻辑，如果已经是最终状态，就不再进行更新\n     *\n     * @param status 状态\n     * @return 是否\n     */\n    public static boolean isEndStatus(Integer status) {\n        return ObjectUtils.equalsAny(status,\n                APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus(),\n                RETURN.getStatus(), APPROVING.getStatus());\n    }\n\n    public static boolean isCancelStatus(Integer status) {\n        return ObjUtil.equal(status, CANCEL.getStatus());\n    }\n\n    public static BpmTaskStatusEnum valueOf(Integer status) {\n        return ArrayUtil.firstMatch(item -> item.getStatus().equals(status), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:8-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-module-bpm-server\nWORKDIR /yudao-module-bpm-server\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-module-bpm-server.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48080 端口\nEXPOSE 48083\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-bpm</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-bpm-server</artifactId>\n\n    <name>${project.artifactId}</name>\n    <description>\n        bpm 包下，业务流程管理（Business Process Management），我们放工作流的功能，基于 Flowable 6 版本实现。\n        例如说：流程定义、表单配置、审核中心（我的申请、我的待办、我的已办）等等    </description>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-bpm-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-system-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- 服务保障相关 TODO 芋艿：暂时去掉 -->\n        <!--        <dependency>-->\n        <!--            <groupId>cn.iocoder.cloud</groupId>-->\n        <!--            <artifactId>yudao-spring-boot-starter-protection</artifactId>-->\n        <!--        </dependency>-->\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <!-- Flowable 工作流相关 -->\n        <dependency>\n            <groupId>org.flowable</groupId>\n            <artifactId>flowable-spring-boot-starter-process</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.flowable</groupId>\n            <artifactId>flowable-spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/BpmServerApplication.java",
    "content": "package cn.iocoder.yudao.module.bpm;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n *\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication\npublic class BpmServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(BpmServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmContractStatusListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.api.event;\n\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.client.RestTemplate;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\n/**\n * 合同审批的结果的监听器实现类\n *\n * @author 芋道源码\n */\npublic class CrmContractStatusListener extends BpmProcessInstanceStatusEventListener {\n\n    @Resource\n    private RestTemplate loadBalancedRestTemplate;\n\n    @Override\n    public String getProcessDefinitionKey() {\n        return \"crm-contract-audit\";\n    }\n\n    @Override\n    public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {\n        BpmHttpRequestUtils.executeBpmHttpRequest(event,\n                \"http://crm-server/rpc-api/crm/contract/update-audit-status\",\n                loadBalancedRestTemplate);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmReceivableStatusListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.api.event;\n\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.client.RestTemplate;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\n/**\n * 回款审批的结果的监听器实现类\n *\n * @author 芋道源码\n */\npublic class CrmReceivableStatusListener extends BpmProcessInstanceStatusEventListener {\n\n    @Resource\n    private RestTemplate loadBalancedRestTemplate;\n\n    @Override\n    public String getProcessDefinitionKey() {\n        return \"crm-receivable-audit\";\n    }\n\n    @Override\n    public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {\n        BpmHttpRequestUtils.executeBpmHttpRequest(event,\n                \"http://crm-server/rpc-api/crm/receivable/update-audit-status\",\n                loadBalancedRestTemplate);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/package-info.java",
    "content": "/**\n * bpm API 实现类，定义暴露给其它模块的 API\n */\npackage cn.iocoder.yudao.module.bpm.api;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.api.task;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * Flowable 流程实例 Api 实现类\n *\n * @author 芋道源码\n * @author jason\n */\n@RestController\n@Validated\npublic class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi {\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public CommonResult<String> createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) {\n        return success(processInstanceService.createProcessInstance(userId, reqDTO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.base.dept;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"部门精简信息 VO\")\n@Data\npublic class DeptSimpleBaseVO {\n\n    @Schema(description = \"部门编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n    @Schema(description = \"部门名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"技术部\")\n    private String name;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/package-info.java",
    "content": "/**\n * 基础包，放一些通用的 VO 类\n */\npackage cn.iocoder.yudao.module.bpm.controller.admin.base;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.base.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户精简信息 VO\")\n@Data\npublic class UserSimpleBaseVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    private String nickname;\n    @Schema(description = \"用户头像\", example = \"https://www.iocoder.cn/1.png\")\n    private String avatar;\n\n    @Schema(description = \"部门编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long deptId;\n    @Schema(description = \"部门名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"研发部\")\n    private String deptName;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmCategoryController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - BPM 流程分类\")\n@RestController\n@RequestMapping(\"/bpm/category\")\n@Validated\npublic class BpmCategoryController {\n\n    @Resource\n    private BpmCategoryService categoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建流程分类\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:category:create')\")\n    public CommonResult<Long> createCategory(@Valid @RequestBody BpmCategorySaveReqVO createReqVO) {\n        return success(categoryService.createCategory(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新流程分类\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:category:update')\")\n    public CommonResult<Boolean> updateCategory(@Valid @RequestBody BpmCategorySaveReqVO updateReqVO) {\n        categoryService.updateCategory(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-sort-batch\")\n    @Operation(summary = \"批量更新流程分类的排序\")\n    @Parameter(name = \"ids\", description = \"分类编号列表\", required = true, example = \"1,2,3\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:category:update')\")\n    public CommonResult<Boolean> updateCategorySortBatch(@RequestParam(\"ids\") List<Long> ids) {\n        categoryService.updateCategorySortBatch(ids);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除流程分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:category:delete')\")\n    public CommonResult<Boolean> deleteCategory(@RequestParam(\"id\") Long id) {\n        categoryService.deleteCategory(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得流程分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:category:query')\")\n    public CommonResult<BpmCategoryRespVO> getCategory(@RequestParam(\"id\") Long id) {\n        BpmCategoryDO category = categoryService.getCategory(id);\n        return success(BeanUtils.toBean(category, BpmCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得流程分类分页\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:category:query')\")\n    public CommonResult<PageResult<BpmCategoryRespVO>> getCategoryPage(@Valid BpmCategoryPageReqVO pageReqVO) {\n        PageResult<BpmCategoryDO> pageResult = categoryService.getCategoryPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, BpmCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获取流程分类的精简信息列表\", description = \"只包含被开启的分类，主要用于前端的下拉选项\")\n    public CommonResult<List<BpmCategoryRespVO>> getCategorySimpleList() {\n        List<BpmCategoryDO> list = categoryService.getCategoryListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        list.sort(Comparator.comparingInt(BpmCategoryDO::getSort));\n        return success(convertList(list, category -> new BpmCategoryRespVO().setId(category.getId())\n                .setName(category.getName()).setCode(category.getCode())));\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmFormController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - 动态表单\")\n@RestController\n@RequestMapping(\"/bpm/form\")\n@Validated\npublic class BpmFormController {\n\n    @Resource\n    private BpmFormService formService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建动态表单\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:form:create')\")\n    public CommonResult<Long> createForm(@Valid @RequestBody BpmFormSaveReqVO createReqVO) {\n        return success(formService.createForm(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新动态表单\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:form:update')\")\n    public CommonResult<Boolean> updateForm(@Valid @RequestBody BpmFormSaveReqVO updateReqVO) {\n        formService.updateForm(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除动态表单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:form:delete')\")\n    public CommonResult<Boolean> deleteForm(@RequestParam(\"id\") Long id) {\n        formService.deleteForm(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得动态表单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:form:query')\")\n    public CommonResult<BpmFormRespVO> getForm(@RequestParam(\"id\") Long id) {\n        BpmFormDO form = formService.getForm(id);\n        return success(BeanUtils.toBean(form, BpmFormRespVO.class));\n    }\n\n    @GetMapping({\"/list-all-simple\", \"/simple-list\"})\n    @Operation(summary = \"获得动态表单的精简列表\", description = \"用于表单下拉框\")\n    public CommonResult<List<BpmFormRespVO>> getFormSimpleList() {\n        List<BpmFormDO> list = formService.getFormList();\n        return success(convertList(list, formDO -> // 只返回 id、name 字段\n                new BpmFormRespVO().setId(formDO.getId()).setName(formDO.getName())));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得动态表单分页\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:form:query')\")\n    public CommonResult<PageResult<BpmFormRespVO>> getFormPage(@Valid BpmFormPageReqVO pageVO) {\n        PageResult<BpmFormDO> pageResult = formService.getFormPage(pageVO);\n        return success(BeanUtils.toBean(pageResult, BpmFormRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;\nimport cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.flowable.engine.repository.Deployment;\nimport org.flowable.engine.repository.Model;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - 流程模型\")\n@RestController\n@RequestMapping(\"/bpm/model\")\n@Validated\npublic class BpmModelController {\n\n    @Resource\n    private BpmModelService modelService;\n    @Resource\n    private BpmFormService formService;\n    @Resource\n    private BpmCategoryService categoryService;\n    @Resource\n    private BpmProcessDefinitionService processDefinitionService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得模型分页\")\n    @Parameter(name = \"name\", description = \"模型名称\", example = \"芋艿\")\n    public CommonResult<List<BpmModelRespVO>> getModelList(@RequestParam(value = \"name\", required = false) String name) {\n        List<Model> list = modelService.getModelList(name);\n        if (CollUtil.isEmpty(list)) {\n            return success(Collections.emptyList());\n        }\n\n        // 获得 Form 表单\n        Set<Long> formIds = convertSet(list, model -> {\n            BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);\n            return metaInfo != null ? metaInfo.getFormId() : null;\n        });\n        Map<Long, BpmFormDO> formMap = formService.getFormMap(formIds);\n        // 获得 Category Map\n        Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(\n                convertSet(list, Model::getCategory));\n        // 获得 Deployment Map\n        Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(\n                convertSet(list, Model::getDeploymentId));\n        // 获得 ProcessDefinition Map\n        List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(\n                deploymentMap.keySet());\n        Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId);\n        // 获得 User Map、Dept Map\n        Set<Long> userIds = convertSetByFlatMap(list, model -> {\n            BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);\n            return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty();\n        });\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);\n        Set<Long> deptIds = convertSetByFlatMap(list, model -> {\n            BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);\n            return metaInfo != null && metaInfo.getStartDeptIds() != null ? metaInfo.getStartDeptIds().stream() : Stream.empty();\n        });\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(deptIds);\n        return success(BpmModelConvert.INSTANCE.buildModelList(list,\n                formMap, categoryMap, deploymentMap, processDefinitionMap, userMap, deptMap));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得模型\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:model:query')\")\n    public CommonResult<BpmModelRespVO> getModel(@RequestParam(\"id\") String id) {\n        Model model = modelService.getModel(id);\n        if (model == null) {\n            return null;\n        }\n        byte[] bpmnBytes = modelService.getModelBpmnXML(id);\n        BpmSimpleModelNodeVO simpleModel = modelService.getSimpleModel(id);\n        return success(BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes, simpleModel));\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"新建模型\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:model:create')\")\n    public CommonResult<String> createModel(@Valid @RequestBody BpmModelSaveReqVO createRetVO) {\n        return success(modelService.createModel(createRetVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"修改模型\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:model:update')\")\n    public CommonResult<Boolean> updateModel(@Valid @RequestBody BpmModelSaveReqVO modelVO) {\n        modelService.updateModel(getLoginUserId(), modelVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-sort-batch\")\n    @Operation(summary = \"批量修改模型排序\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true, example = \"1,2,3\")\n    public CommonResult<Boolean> updateModelSortBatch(@RequestParam(\"ids\") List<String> ids) {\n        modelService.updateModelSortBatch(getLoginUserId(), ids);\n        return success(true);\n    }\n\n    @PostMapping(\"/deploy\")\n    @Operation(summary = \"部署模型\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:model:deploy')\")\n    public CommonResult<Boolean> deployModel(@RequestParam(\"id\") String id) {\n        modelService.deployModel(getLoginUserId(), id);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-state\")\n    @Operation(summary = \"修改模型的状态\", description = \"实际更新的部署的流程定义的状态\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:model:update')\")\n    public CommonResult<Boolean> updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) {\n        modelService.updateModelState(getLoginUserId(), reqVO.getId(), reqVO.getState());\n        return success(true);\n    }\n\n    @Deprecated\n    @PutMapping(\"/update-bpmn\")\n    @Operation(summary = \"修改模型的 BPMN\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:model:update')\")\n    public CommonResult<Boolean> updateModelBpmn(@Valid @RequestBody BpmModeUpdateBpmnReqVO reqVO) {\n        modelService.updateModelBpmnXml(reqVO.getId(), reqVO.getBpmnXml());\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除模型\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:model:delete')\")\n    public CommonResult<Boolean> deleteModel(@RequestParam(\"id\") String id) {\n        modelService.deleteModel(getLoginUserId(), id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/clean\")\n    @Operation(summary = \"清理模型\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:model:clean')\")\n    public CommonResult<Boolean> cleanModel(@RequestParam(\"id\") String id) {\n        modelService.cleanModel(getLoginUserId(), id);\n        return success(true);\n    }\n\n    // ========== 仿钉钉/飞书的精简模型 =========\n\n    @GetMapping(\"/simple/get\")\n    @Operation(summary = \"获得仿钉钉流程设计模型\")\n    @Parameter(name = \"modelId\", description = \"流程模型编号\", required = true, example = \"a2c5eee0-eb6c-11ee-abf4-0c37967c420a\")\n    public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam(\"id\") String modelId){\n        return success(modelService.getSimpleModel(modelId));\n    }\n\n    @Deprecated\n    @PostMapping(\"/simple/update\")\n    @Operation(summary = \"保存仿钉钉流程设计模型\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:model:update')\")\n    public CommonResult<Boolean> updateSimpleModel(@Valid @RequestBody BpmSimpleModelUpdateReqVO reqVO) {\n        modelService.updateSimpleModel(getLoginUserId(), reqVO);\n        return success(Boolean.TRUE);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;\nimport cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.common.engine.impl.db.SuspensionState;\nimport org.flowable.engine.repository.Deployment;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - 流程定义\")\n@RestController\n@RequestMapping(\"/bpm/process-definition\")\n@Validated\npublic class BpmProcessDefinitionController {\n\n    @Resource\n    private BpmProcessDefinitionService processDefinitionService;\n    @Resource\n    private BpmFormService formService;\n    @Resource\n    private BpmCategoryService categoryService;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得流程定义分页\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-definition:query')\")\n    public CommonResult<PageResult<BpmProcessDefinitionRespVO>> getProcessDefinitionPage(\n            BpmProcessDefinitionPageReqVO pageReqVO) {\n        PageResult<ProcessDefinition> pageResult = processDefinitionService.getProcessDefinitionPage(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 获得 Category Map\n        Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(\n                convertSet(pageResult.getList(), ProcessDefinition::getCategory));\n        // 获得 Deployment Map\n        Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(\n                convertSet(pageResult.getList(), ProcessDefinition::getDeploymentId));\n        // 获得 BpmProcessDefinitionInfoDO Map\n        Map<String, BpmProcessDefinitionInfoDO> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(\n                convertSet(pageResult.getList(), ProcessDefinition::getId));\n        // 获得 Form Map\n        Map<Long, BpmFormDO> formMap = formService.getFormMap(\n               convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfoDO::getFormId));\n        return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage(\n                pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap));\n    }\n\n    @GetMapping (\"/list\")\n    @Operation(summary = \"获得流程定义列表\")\n    @Parameter(name = \"suspensionState\", description = \"挂起状态\", required = true, example = \"1\") // 参见 Flowable SuspensionState 枚举\n    public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(\n            @RequestParam(\"suspensionState\") Integer suspensionState) {\n        // 1.1 获得开启的流程定义\n        List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState);\n        if (CollUtil.isEmpty(list)) {\n            return success(Collections.emptyList());\n        }\n        // 1.2 移除不可见的流程定义\n        Map<String, BpmProcessDefinitionInfoDO> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(\n                convertSet(list, ProcessDefinition::getId));\n        Long userId = getLoginUserId();\n        list.removeIf(processDefinition -> {\n            BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionMap.get(processDefinition.getId());\n            return processDefinitionInfo == null // 不存在\n                    || Boolean.FALSE.equals(processDefinitionInfo.getVisible()) // visible 不可见\n                    || !processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId); // 无权限发起\n        });\n\n        // 2. 拼接 VO 返回\n        return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList(\n                list, null, processDefinitionMap, null, null));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得流程定义精简列表\", description = \"只包含未挂起的流程，主要用于前端的下拉选项\")\n    public CommonResult<List<BpmProcessDefinitionRespVO>> getSimpleProcessDefinitionList() {\n        // 只查询未挂起的流程\n        List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(\n                SuspensionState.ACTIVE.getStateCode());\n        // 拼接 VO 返回，只返回 id、name、key\n        return success(convertList(list, definition -> new BpmProcessDefinitionRespVO()\n                .setId(definition.getId()).setName(definition.getName()).setKey(definition.getKey())));\n    }\n\n    @GetMapping (\"/get\")\n    @Operation(summary = \"获得流程定义\")\n    @Parameter(name = \"id\", description = \"流程编号\", required = true, example = \"1024\")\n    @Parameter(name = \"key\", description = \"流程定义标识\", required = true, example = \"1024\")\n    public CommonResult<BpmProcessDefinitionRespVO> getProcessDefinition(\n            @RequestParam(value = \"id\", required = false) String id,\n            @RequestParam(value = \"key\", required = false) String key) {\n        ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id)\n                : processDefinitionService.getActiveProcessDefinition(key);\n        if (processDefinition == null) {\n            return success(null);\n        }\n        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId());\n        BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());\n        return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(\n                processDefinition, null, processDefinitionInfo, null, null, bpmnModel));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessExpressionController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessExpressionService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - BPM 流程表达式\")\n@RestController\n@RequestMapping(\"/bpm/process-expression\")\n@Validated\npublic class BpmProcessExpressionController {\n\n    @Resource\n    private BpmProcessExpressionService processExpressionService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建流程表达式\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-expression:create')\")\n    public CommonResult<Long> createProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO createReqVO) {\n        return success(processExpressionService.createProcessExpression(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新流程表达式\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-expression:update')\")\n    public CommonResult<Boolean> updateProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO updateReqVO) {\n        processExpressionService.updateProcessExpression(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除流程表达式\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-expression:delete')\")\n    public CommonResult<Boolean> deleteProcessExpression(@RequestParam(\"id\") Long id) {\n        processExpressionService.deleteProcessExpression(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得流程表达式\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-expression:query')\")\n    public CommonResult<BpmProcessExpressionRespVO> getProcessExpression(@RequestParam(\"id\") Long id) {\n        BpmProcessExpressionDO processExpression = processExpressionService.getProcessExpression(id);\n        return success(BeanUtils.toBean(processExpression, BpmProcessExpressionRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得流程表达式分页\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-expression:query')\")\n    public CommonResult<PageResult<BpmProcessExpressionRespVO>> getProcessExpressionPage(\n            @Valid BpmProcessExpressionPageReqVO pageReqVO) {\n        PageResult<BpmProcessExpressionDO> pageResult = processExpressionService.getProcessExpressionPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, BpmProcessExpressionRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessListenerController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessListenerDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessListenerService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - BPM 流程监听器\")\n@RestController\n@RequestMapping(\"/bpm/process-listener\")\n@Validated\npublic class BpmProcessListenerController {\n\n    @Resource\n    private BpmProcessListenerService processListenerService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建流程监听器\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-listener:create')\")\n    public CommonResult<Long> createProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO createReqVO) {\n        return success(processListenerService.createProcessListener(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新流程监听器\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-listener:update')\")\n    public CommonResult<Boolean> updateProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO updateReqVO) {\n        processListenerService.updateProcessListener(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除流程监听器\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-listener:delete')\")\n    public CommonResult<Boolean> deleteProcessListener(@RequestParam(\"id\") Long id) {\n        processListenerService.deleteProcessListener(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得流程监听器\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-listener:query')\")\n    public CommonResult<BpmProcessListenerRespVO> getProcessListener(@RequestParam(\"id\") Long id) {\n        BpmProcessListenerDO processListener = processListenerService.getProcessListener(id);\n        return success(BeanUtils.toBean(processListener, BpmProcessListenerRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得流程监听器分页\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-listener:query')\")\n    public CommonResult<PageResult<BpmProcessListenerRespVO>> getProcessListenerPage(\n            @Valid BpmProcessListenerPageReqVO pageReqVO) {\n        PageResult<BpmProcessListenerDO> pageResult = processListenerService.getProcessListenerPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, BpmProcessListenerRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmUserGroupController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - 用户组\")\n@RestController\n@RequestMapping(\"/bpm/user-group\")\n@Validated\npublic class BpmUserGroupController {\n\n    @Resource\n    private BpmUserGroupService userGroupService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建用户组\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:user-group:create')\")\n    public CommonResult<Long> createUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO createReqVO) {\n        return success(userGroupService.createUserGroup(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新用户组\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:user-group:update')\")\n    public CommonResult<Boolean> updateUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO updateReqVO) {\n        userGroupService.updateUserGroup(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除用户组\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:user-group:delete')\")\n    public CommonResult<Boolean> deleteUserGroup(@RequestParam(\"id\") Long id) {\n        userGroupService.deleteUserGroup(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得用户组\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:user-group:query')\")\n    public CommonResult<BpmUserGroupRespVO> getUserGroup(@RequestParam(\"id\") Long id) {\n        BpmUserGroupDO userGroup = userGroupService.getUserGroup(id);\n        return success(BeanUtils.toBean(userGroup, BpmUserGroupRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得用户组分页\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:user-group:query')\")\n    public CommonResult<PageResult<BpmUserGroupRespVO>> getUserGroupPage(@Valid BpmUserGroupPageReqVO pageVO) {\n        PageResult<BpmUserGroupDO> pageResult = userGroupService.getUserGroupPage(pageVO);\n        return success(BeanUtils.toBean(pageResult, BpmUserGroupRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获取用户组精简信息列表\", description = \"只包含被开启的用户组，主要用于前端的下拉选项\")\n    public CommonResult<List<BpmUserGroupRespVO>> getUserGroupSimpleList() {\n        List<BpmUserGroupDO> list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, group -> new BpmUserGroupRespVO().setId(group.getId()).setName(group.getName())));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - BPM 流程分类分页 Request VO\")\n@Data\npublic class BpmCategoryPageReqVO extends PageParam {\n\n    @Schema(description = \"分类名\", example = \"王五\")\n    private String name;\n\n    @Schema(description = \"分类标志\", example = \"OA\")\n    private String code;\n\n    @Schema(description = \"分类状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - BPM 流程分类 Response VO\")\n@Data\npublic class BpmCategoryRespVO {\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3167\")\n    private Long id;\n\n    @Schema(description = \"分类名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    private String name;\n\n    @Schema(description = \"分类标志\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"OA\")\n    private String code;\n\n    @Schema(description = \"分类描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"分类状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"分类排序\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Integer sort;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - BPM 流程分类新增/修改 Request VO\")\n@Data\npublic class BpmCategorySaveReqVO {\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3167\")\n    private Long id;\n\n    @Schema(description = \"分类名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    @NotEmpty(message = \"分类名不能为空\")\n    private String name;\n\n    @Schema(description = \"分类描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"分类标志\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"OA\")\n    @NotEmpty(message = \"分类标志不能为空\")\n    private String code;\n\n    @Schema(description = \"分类状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"分类状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"分类排序\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"分类排序不能为空\")\n    private Integer sort;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - BPM 流程表达式分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BpmProcessExpressionPageReqVO extends PageParam {\n\n    @Schema(description = \"表达式名字\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"表达式状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression;\n\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - BPM 流程表达式 Response VO\")\n@Data\npublic class BpmProcessExpressionRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3870\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"表达式名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @ExcelProperty(\"表达式名字\")\n    private String name;\n\n    @Schema(description = \"表达式状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"表达式\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String expression;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - BPM 流程表达式新增/修改 Request VO\")\n@Data\npublic class BpmProcessExpressionSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3870\")\n    private Long id;\n\n    @Schema(description = \"表达式名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotEmpty(message = \"表达式名字不能为空\")\n    private String name;\n\n    @Schema(description = \"表达式状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"表达式状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"表达式\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"表达式不能为空\")\n    private String expression;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;\n\nimport lombok.Data;\n\n/**\n * 流程表单字段 VO\n */\n@Data\npublic class BpmFormFieldVO {\n\n    /**\n     * 字段类型\n     */\n    private String type;\n    /**\n     * 字段标识\n     */\n    private String field;\n    /**\n     * 字段标题\n     */\n    private String title;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 动态表单分页 Request VO\")\n@Data\npublic class BpmFormPageReqVO extends PageParam {\n\n    @Schema(description = \"表单名称\", example = \"芋道\")\n    private String name;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 动态表单 Response VO\")\n@Data\npublic class BpmFormRespVO {\n\n    @Schema(description = \"表单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"表单名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    @NotNull(message = \"表单名称不能为空\")\n    private String name;\n\n    @Schema(description = \"表单的配置-JSON 字符串\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"表单的配置不能为空\")\n    private String conf;\n\n    @Schema(description = \"表单项的数组-JSON 字符串的数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"表单项的数组不能为空\")\n    private List<String> fields;\n\n    @Schema(description = \"表单状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"表单状态不能为空\")\n    private Integer status; // 参见 CommonStatusEnum 枚举\n\n    @Schema(description = \"备注\", example = \"我是备注\")\n    private String remark;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 动态表单创建/更新 Request VO\")\n@Data\npublic class BpmFormSaveReqVO {\n\n    @Schema(description = \"表单编号\", example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"表单名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    @NotNull(message = \"表单名称不能为空\")\n    private String name;\n\n    @Schema(description = \"表单的配置-JSON 字符串\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"表单的配置不能为空\")\n    private String conf;\n\n    @Schema(description = \"表单项的数组-JSON 字符串的数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"表单项的数组不能为空\")\n    private List<String> fields;\n\n    @Schema(description = \"表单状态-参见 CommonStatusEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"表单状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"我是备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.date.DateUtils;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 用户组分页 Request VO\")\n@Data\npublic class BpmUserGroupPageReqVO extends PageParam {\n\n    @Schema(description = \"编号\", example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"组名\", example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"状态\", example = \"1\")\n    private Integer status;\n\n    @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"创建时间\")\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - 用户组 Response VO\")\n@Data\npublic class BpmUserGroupRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"组名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String description;\n\n    @Schema(description = \"成员编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1,2,3\")\n    private Set<Long> userIds;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - 用户组创建/修改 Request VO\")\n@Data\npublic class BpmUserGroupSaveReqVO {\n\n    @Schema(description = \"编号\", example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"组名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    @NotNull(message = \"组名不能为空\")\n    private String name;\n\n    @Schema(description = \"描述\", example = \"芋道源码\")\n    private String description;\n\n    @Schema(description = \"成员编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1,2,3\")\n    @NotNull(message = \"成员编号数组不能为空\")\n    private Set<Long> userIds;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - BPM 流程监听器分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BpmProcessListenerPageReqVO extends PageParam {\n\n    @Schema(description = \"监听器名字\", example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"监听器类型\", example = \"execution\")\n    private String type;\n\n    @Schema(description = \"监听事件\", example = \"start\")\n    private String event;\n\n    @Schema(description = \"状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - BPM 流程监听器 Response VO\")\n@Data\npublic class BpmProcessListenerRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13089\")\n    private Long id;\n\n    @Schema(description = \"监听器名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"监听器类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"execution\")\n    private String type;\n\n    @Schema(description = \"监听器状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"监听事件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"start\")\n    private String event;\n\n    @Schema(description = \"监听器值类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"class\")\n    private String valueType;\n\n    @Schema(description = \"监听器值\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String value;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - BPM 流程监听器新增/修改 Request VO\")\n@Data\npublic class BpmProcessListenerSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13089\")\n    private Long id;\n\n    @Schema(description = \"监听器名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    @NotEmpty(message = \"监听器名字不能为空\")\n    private String name;\n\n    @Schema(description = \"监听器类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"execution\")\n    @NotEmpty(message = \"监听器类型不能为空\")\n    private String type;\n\n    @Schema(description = \"监听器状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"监听器状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"监听事件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"start\")\n    @NotEmpty(message = \"监听事件不能为空\")\n    private String event;\n\n    @Schema(description = \"监听器值类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"class\")\n    @NotEmpty(message = \"监听器值类型不能为空\")\n    private String valueType;\n\n    @Schema(description = \"监听器值\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"监听器值不能为空\")\n    private String value;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Schema(description = \"管理后台 - 流程模型的更新 BPMN XML Request VO\")\n@Data\npublic class BpmModeUpdateBpmnReqVO {\n\n    @Schema(description = \"流程编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotEmpty(message = \"流程编号不能为空\")\n    private String id;\n\n    @Schema(description = \"BPMN XML\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"BPMN XML 不能为空\")\n    private String bpmnXml;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;\n\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n/**\n * BPM 流程 MetaInfo Response DTO\n * 主要用于 { Model#setMetaInfo(String)} 的存储\n *\n * 最终，它的字段和\n * {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO}\n * 是一致的\n *\n * @author 芋道源码\n */\n@Data\npublic class BpmModelMetaInfoVO {\n\n    @Schema(description = \"流程图标\", example = \"https://www.iocoder.cn/yudao.jpg\")\n    @URL(message = \"流程图标格式不正确\")\n    private String icon;\n\n    @Schema(description = \"流程描述\", example = \"我是描述\")\n    private String description;\n\n    @Schema(description = \"流程类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @InEnum(BpmModelTypeEnum.class)\n    @NotNull(message = \"流程类型不能为空\")\n    private Integer type;\n\n    @Schema(description = \"表单类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @InEnum(BpmModelFormTypeEnum.class)\n    @NotNull(message = \"表单类型不能为空\")\n    private Integer formType;\n    @Schema(description = \"表单编号\", example = \"1024\")\n    private Long formId; // formType 为 NORMAL 使用，必须非空\n\n    @Schema(description = \"自定义表单的提交路径，使用 Vue 的路由地址\", example = \"/bpm/oa/leave/create\")\n    private String formCustomCreatePath; // 表单类型为 CUSTOM 时，必须非空\n    @Schema(description = \"自定义表单的查看路径，使用 Vue 的路由地址\", example = \"/bpm/oa/leave/view\")\n    private String formCustomViewPath; // 表单类型为 CUSTOM 时，必须非空\n\n    @Schema(description = \"是否可见\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否可见不能为空\")\n    private Boolean visible;\n\n    @Schema(description = \"可发起用户编号数组\", example = \"[1,2,3]\")\n    private List<Long> startUserIds;\n\n    @Schema(description = \"可发起部门编号数组\", example = \"[2,4,6]\")\n    private List<Long> startDeptIds;\n\n    @Schema(description = \"可管理用户编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[2,4,6]\")\n    @NotEmpty(message = \"可管理用户编号数组不能为空\")\n    private List<Long> managerUserIds;\n\n    @Schema(description = \"排序\", example = \"1\")\n    private Long sort; // 创建时，后端自动生成\n\n    @Schema(description = \"允许撤销审批中的申请\", example = \"true\")\n    private Boolean allowCancelRunningProcess;\n\n    @Schema(description = \"允许允许审批人撤回任务\", example = \"false\")\n    private Boolean allowWithdrawTask;\n\n    @Schema(description = \"流程 ID 规则\", example = \"{}\")\n    private ProcessIdRule processIdRule;\n\n    @Schema(description = \"自动去重类型\", example = \"1\")\n    @InEnum(BpmAutoApproveTypeEnum.class)\n    private Integer autoApprovalType;\n\n    @Schema(description = \"标题设置\", example = \"{}\")\n    private TitleSetting titleSetting;\n\n    @Schema(description = \"摘要设置\", example = \"{}\")\n    private SummarySetting summarySetting;\n\n    @Schema(description = \"流程前置通知设置\", example = \"{}\")\n    private HttpRequestSetting processBeforeTriggerSetting;\n\n    @Schema(description = \"流程后置通知设置\", example = \"{}\")\n    private HttpRequestSetting processAfterTriggerSetting;\n\n    @Schema(description = \"任务前置通知设置\", example = \"{}\")\n    private HttpRequestSetting taskBeforeTriggerSetting;\n\n    @Schema(description = \"任务后置通知设置\", example = \"{}\")\n    private HttpRequestSetting taskAfterTriggerSetting;\n\n    @Schema(description = \"自定义打印模板设置\", example = \"{}\")\n    @Valid\n    private PrintTemplateSetting printTemplateSetting;\n\n    @Schema(description = \"流程 ID 规则\")\n    @Data\n    @Valid\n    public static class ProcessIdRule {\n\n        @Schema(description = \"是否启用\", example = \"false\")\n        @NotNull(message = \"是否启用不能为空\")\n        private Boolean enable;\n\n        @Schema(description = \"前缀\", example = \"XX\")\n        private String prefix;\n\n        @Schema(description = \"中缀\", example = \"20250120\")\n        private String infix; // 精确到日、精确到时、精确到分、精确到秒\n\n        @Schema(description = \"后缀\", example = \"YY\")\n        private String postfix;\n\n        @Schema(description = \"序列长度\", example = \"5\")\n        @NotNull(message = \"序列长度不能为空\")\n        private Integer length;\n\n    }\n\n    @Schema(description = \"标题设置\")\n    @Data\n    @Valid\n    public static class TitleSetting {\n\n        @Schema(description = \"是否自定义\", example = \"false\")\n        @NotNull(message = \"是否自定义不能为空\")\n        private Boolean enable;\n\n        @Schema(description = \"标题\", example = \"流程标题\")\n        private String title;\n\n    }\n\n    @Schema(description = \"摘要设置\")\n    @Data\n    @Valid\n    public static class SummarySetting {\n\n        @Schema(description = \"是否自定义\", example = \"false\")\n        @NotNull(message = \"是否自定义不能为空\")\n        private Boolean enable;\n\n        @Schema(description = \"摘要字段数组\", example = \"[]\")\n        private List<String> summary;\n\n    }\n\n    @Schema(description = \"http 请求通知设置\", example = \"{}\")\n    @Data\n    public static class HttpRequestSetting {\n\n        @Schema(description = \"请求路径\", example = \"http://127.0.0.1\")\n        @NotEmpty(message = \"请求 URL 不能为空\")\n        @URL(message = \"请求 URL 格式不正确\")\n        private String url;\n\n        @Schema(description = \"请求头参数设置\", example = \"[]\")\n        @Valid\n        private List<BpmSimpleModelNodeVO.HttpRequestParam> header;\n\n        @Schema(description = \"请求头参数设置\", example = \"[]\")\n        @Valid\n        private List<BpmSimpleModelNodeVO.HttpRequestParam> body;\n\n        /**\n         * 请求返回处理设置，用于修改流程表单值\n         * <p>\n         * key：表示要修改的流程表单字段名(name)\n         * value：接口返回的字段名\n         */\n        @Schema(description = \"请求返回处理设置\", example = \"[]\")\n        private List<KeyValue<String, String>> response;\n\n    }\n\n    @Schema(description = \"自定义打印模板设置\")\n    @Data\n    public static class PrintTemplateSetting {\n\n        @Schema(description = \"是否自定义打印模板\", example = \"false\")\n        @NotNull(message = \"是否自定义打印模板不能为空\")\n        private Boolean enable;\n\n        @Schema(description = \"打印模板\", example = \"<p></p>\")\n        private String template;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;\n\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 流程模型 Response VO\")\n@Data\npublic class BpmModelRespVO extends BpmModelMetaInfoVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String id;\n\n    @Schema(description = \"流程标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"process_yudao\")\n    private String key;\n\n    @Schema(description = \"流程名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"流程图标\", example = \"https://www.iocoder.cn/yudao.jpg\")\n    private String icon;\n\n    @Schema(description = \"流程分类编号\", example = \"1\")\n    private String category;\n    @Schema(description = \"流程分类名字\", example = \"请假\")\n    private String categoryName;\n\n    @Schema(description = \"表单名字\", example = \"请假表单\")\n    private String formName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"可发起的用户数组\")\n    private List<UserSimpleBaseVO> startUsers;\n\n    @Schema(description = \"可发起的部门数组\")\n    private List<DeptSimpleBaseVO> startDepts;\n\n    @Schema(description = \"BPMN XML\")\n    private String bpmnXml;\n\n    @Schema(description = \"仿钉钉流程设计模型对象\")\n    private BpmSimpleModelNodeVO simpleModel;\n\n    /**\n     * 最新部署的流程定义\n     */\n    private BpmProcessDefinitionRespVO processDefinition;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;\n\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\n\n@Schema(description = \"管理后台 - 流程模型的保存 Request VO\")\n@Data\npublic class BpmModelSaveReqVO extends BpmModelMetaInfoVO {\n\n    @Schema(description = \"编号\", example = \"1024\")\n    private String id;\n\n    @Schema(description = \"流程标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"process_yudao\")\n    @NotEmpty(message = \"流程标识不能为空\")\n    private String key;\n\n    @Schema(description = \"流程名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    @NotEmpty(message = \"流程名称不能为空\")\n    private String name;\n\n    @Schema(description = \"流程分类\", example = \"1\")\n    private String category;\n\n    @Schema(description = \"BPMN XML\")\n    private String bpmnXml;\n\n    @Schema(description = \"仿钉钉流程设计模型对象\")\n    @Valid\n    private BpmSimpleModelNodeVO simpleModel;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 流程模型更新状态 Request VO\")\n@Data\npublic class BpmModelUpdateStateReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"编号不能为空\")\n    private String id;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer state; // 参见 Flowable SuspensionState 枚举\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;\n\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.*;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.flowable.bpmn.model.IOParameter;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - 仿钉钉流程设计模型节点 VO\")\n@Data\n@JsonInclude(JsonInclude.Include.NON_NULL)\npublic class BpmSimpleModelNodeVO {\n\n    @Schema(description = \"模型节点编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"StartEvent_1\")\n    @NotEmpty(message = \"模型节点编号不能为空\")\n    private String id;\n\n    @Schema(description = \"模型节点类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"模型节点类型不能为空\")\n    @InEnum(BpmSimpleModelNodeTypeEnum.class)\n    private Integer type;\n\n    @Schema(description = \"模型节点名称\", example = \"领导审批\")\n    private String name;\n\n    @Schema(description = \"节点展示内容\", example = \"指定成员: 芋道源码\")\n    private String showText;\n\n    @Schema(description = \"子节点\")\n    private BpmSimpleModelNodeVO childNode; // 补充说明：在该模型下，子节点有且仅有一个，不会有多个\n\n    @Schema(description = \"候选人策略\", example = \"30\")\n    @InEnum(BpmTaskCandidateStrategyEnum.class)\n    private Integer candidateStrategy; // 用于审批，抄送节点\n\n    @Schema(description = \"候选人参数\")\n    private String candidateParam; // 用于审批，抄送节点\n\n    @Schema(description = \"审批节点类型\", example = \"1\")\n    @InEnum(BpmUserTaskApproveTypeEnum.class)\n    private Integer approveType; // 用于审批节点\n\n    @Schema(description = \"多人审批方式\", example = \"1\")\n    @InEnum(BpmUserTaskApproveMethodEnum.class)\n    private Integer approveMethod; // 用于审批节点\n\n    @Schema(description = \"通过比例\", example = \"100\")\n    private Integer approveRatio; // 通过比例，当多人审批方式为：多人会签(按通过比例) 需要设置\n\n    @Schema(description = \"表单权限\", example = \"[]\")\n    private List<Map<String, String>> fieldsPermission;\n\n    @Schema(description = \"操作按钮设置\", example = \"[]\")\n    private List<OperationButtonSetting> buttonsSetting;  // 用于审批节点\n\n    @Schema(description = \"是否需要签名\", example = \"false\")\n    private Boolean signEnable;\n\n    @Schema(description = \"是否填写审批意见\", example = \"false\")\n    private Boolean reasonRequire;\n\n    @Schema(description = \"跳过表达式\", example = \"{true}\")\n    private String skipExpression;  // 用于审批节点\n\n    /**\n     * 审批节点拒绝处理\n     */\n    private RejectHandler rejectHandler;\n\n    /**\n     * 审批节点超时处理\n     */\n    private TimeoutHandler timeoutHandler;\n\n    @Schema(description = \"审批节点的审批人与发起人相同时，对应的处理类型\", example = \"1\")\n    @InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class)\n    private Integer assignStartUserHandlerType;\n\n    /**\n     * 空处理策略\n     */\n    private AssignEmptyHandler assignEmptyHandler;\n\n    /**\n     * 创建任务监听器\n     */\n    private ListenerHandler taskCreateListener;\n    /**\n     * 指派任务监听器\n     */\n    private ListenerHandler taskAssignListener;\n    /**\n     * 完成任务监听器\n     */\n    private ListenerHandler taskCompleteListener;\n\n    @Schema(description = \"延迟器设置\", example = \"{}\")\n    private DelaySetting delaySetting;\n\n    @Schema(description = \"条件节点\")\n    private List<BpmSimpleModelNodeVO> conditionNodes; // 补充说明：有且仅有条件、并行、包容分支会使用\n\n    /**\n     * 条件节点设置\n     */\n    private ConditionSetting conditionSetting; // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE\n\n    @Schema(description = \"路由分支组\", example = \"[]\")\n    private List<RouterSetting> routerGroups;\n\n    @Schema(description = \"路由分支默认分支 ID\", example = \"Flow_xxx\", hidden = true) // 由后端生成（不从前端传递），所以 hidden = true\n    @JsonIgnore\n    private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE\n\n    /**\n     * 触发器节点设置\n     */\n    private TriggerSetting triggerSetting;\n\n    @Schema(description = \"附加节点 Id\", example = \"UserTask_xxx\", hidden = true) // 由后端生成（不从前端传递），所以 hidden = true\n    @JsonIgnore\n    private String attachNodeId; // 目前用于触发器节点（HTTP 回调）。需要 UserTask 和 ReceiveTask（附加节点) 来完成\n\n    /**\n     * 子流程设置\n     */\n    private ChildProcessSetting childProcessSetting;\n\n    @Schema(description = \"任务监听器\")\n    @Valid\n    @Data\n    public static class ListenerHandler {\n\n        @Schema(description = \"是否开启任务监听器\", example = \"false\")\n        @NotNull(message = \"是否开启任务监听器不能为空\")\n        private Boolean enable;\n\n        @Schema(description = \"请求路径\", example = \"http://xxxxx\")\n        private String path;\n\n        @Schema(description = \"请求头\", example = \"[]\")\n        private List<HttpRequestParam> header;\n\n        @Schema(description = \"请求体\", example = \"[]\")\n        private List<HttpRequestParam> body;\n\n    }\n\n    @Schema(description = \"HTTP 请求参数设置\")\n    @Data\n    public static class HttpRequestParam {\n\n        @Schema(description = \"值类型\", example = \"1\")\n        @InEnum(BpmHttpRequestParamTypeEnum.class)\n        @NotNull(message = \"值类型不能为空\")\n        private Integer type;\n\n        @Schema(description = \"键\", example = \"xxx\")\n        @NotEmpty(message = \"键不能为空\")\n        private String key;\n\n        @Schema(description = \"值\", example = \"xxx\")\n        @NotEmpty(message = \"值不能为空\")\n        private String value;\n\n    }\n\n    @Schema(description = \"审批节点拒绝处理策略\")\n    @Data\n    public static class RejectHandler {\n\n        @Schema(description = \"拒绝处理类型\", example = \"1\")\n        @InEnum(BpmUserTaskRejectHandlerTypeEnum.class)\n        private Integer type;\n\n        @Schema(description = \"任务拒绝后驳回的节点 Id\", example = \"Activity_1\")\n        private String returnNodeId;\n    }\n\n    @Schema(description = \"审批节点超时处理策略\")\n    @Valid\n    @Data\n    public static class TimeoutHandler {\n\n        @Schema(description = \"是否开启超时处理\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n        @NotNull(message = \"是否开启超时处理不能为空\")\n        private Boolean enable;\n\n        @Schema(description = \"任务超时未处理的行为\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        @NotNull(message = \"任务超时未处理的行为不能为空\")\n        @InEnum(BpmUserTaskTimeoutHandlerTypeEnum.class)\n        private Integer type;\n\n        @Schema(description = \"超时时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"PT6H\")\n        @NotEmpty(message = \"超时时间不能为空\")\n        private String timeDuration;\n\n        @Schema(description = \"最大提醒次数\", example = \"1\")\n        private Integer maxRemindCount;\n    }\n\n    @Schema(description = \"空处理策略\")\n    @Data\n    @Valid\n    public static class AssignEmptyHandler {\n\n        @Schema(description = \"空处理类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        @NotNull(message = \"空处理类型不能为空\")\n        @InEnum(BpmUserTaskAssignEmptyHandlerTypeEnum.class)\n        private Integer type;\n\n        @Schema(description = \"指定人员审批的用户编号数组\", example = \"1\")\n        private List<Long> userIds;\n    }\n\n    @Schema(description = \"操作按钮设置\")\n    @Data\n    @Valid\n    public static class OperationButtonSetting {\n\n        // TODO @jason：是不是按钮的标识？id 会和数据库的 id 自增有点模糊，key 标识会更合理一点点哈。\n        @Schema(description = \"按钮 Id\", example = \"1\")\n        private Integer id;\n\n        @Schema(description = \"显示名称\", example = \"审批\")\n        private String displayName;\n\n        @Schema(description = \"是否启用\", example = \"true\")\n        private Boolean enable;\n    }\n\n    @Schema(description = \"条件设置\")\n    @Data\n    @Valid\n    // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE\n    public static class ConditionSetting {\n\n        @Schema(description = \"条件类型\", example = \"1\")\n        @InEnum(BpmSimpleModeConditionTypeEnum.class)\n        private Integer conditionType;\n\n        @Schema(description = \"条件表达式\", example = \"${day>3}\")\n        private String conditionExpression;\n\n        @Schema(description = \"是否默认条件\", example = \"true\")\n        private Boolean defaultFlow;\n\n        /**\n         * 条件组\n         */\n        private ConditionGroups conditionGroups;\n    }\n\n    @Schema(description = \"条件组\")\n    @Data\n    @Valid\n    public static class ConditionGroups {\n\n        @Schema(description = \"条件组下的条件关系是否为与关系\", example = \"true\")\n        @NotNull(message = \"条件关系不能为空\")\n        private Boolean and;\n\n        @Schema(description = \"条件组下的条件\", example = \"[]\")\n        @NotEmpty(message = \"条件不能为空\")\n        private List<Condition> conditions;\n    }\n\n    @Schema(description = \"条件\")\n    @Data\n    @Valid\n    public static class Condition {\n\n        @Schema(description = \"条件下的规则关系是否为与关系\", example = \"true\")\n        @NotNull(message = \"规则关系不能为空\")\n        private Boolean and;\n\n        @Schema(description = \"条件下的规则\", example = \"[]\")\n        @NotEmpty(message = \"规则不能为空\")\n        private List<ConditionRule> rules;\n    }\n\n    @Schema(description = \"条件规则\")\n    @Data\n    @Valid\n    public static class ConditionRule {\n\n        @Schema(description = \"运行符号\", example = \"==\")\n        @NotEmpty(message = \"运行符号不能为空\")\n        private String opCode;\n\n        @Schema(description = \"运算符左边的值,例如某个流程变量\", example = \"startUserId\")\n        @NotEmpty(message = \"运算符左边的值不能为空\")\n        private String leftSide;\n\n        @Schema(description = \"运算符右边的值\", example = \"1\")\n        @NotEmpty(message = \"运算符右边的值不能为空\")\n        private String rightSide;\n    }\n\n    @Schema(description = \"延迟器\")\n    @Data\n    @Valid\n    public static class DelaySetting {\n\n        @Schema(description = \"延迟时间类型\", example = \"1\")\n        @NotNull(message = \"延迟时间类型不能为空\")\n        @InEnum(BpmDelayTimerTypeEnum.class)\n        private Integer delayType;\n\n        @Schema(description = \"延迟时间表达式\", example = \"PT1H,2025-01-01T00:00:00\")\n        @NotEmpty(message = \"延迟时间表达式不能为空\")\n        private String delayTime;\n    }\n\n    @Schema(description = \"路由分支\")\n    @Data\n    @Valid\n    public static class RouterSetting {\n\n        @Schema(description = \"节点 Id\", example = \"Activity_xxx\") // 跳转到该节点\n        @NotEmpty(message = \"节点 Id 不能为空\")\n        private String nodeId;\n\n        @Schema(description = \"条件类型\", example = \"1\")\n        @InEnum(BpmSimpleModeConditionTypeEnum.class)\n        @NotNull(message = \"条件类型不能为空\")\n        private Integer conditionType;\n\n        @Schema(description = \"条件表达式\", example = \"${day>3}\")\n        private String conditionExpression;\n\n        @Schema(description = \"条件组\", example = \"{}\")\n        private ConditionGroups conditionGroups;\n    }\n\n    @Schema(description = \"触发器节点配置\")\n    @Data\n    @Valid\n    public static class TriggerSetting {\n\n        @Schema(description = \"触发器类型\", example = \"1\")\n        @InEnum(BpmTriggerTypeEnum.class)\n        @NotNull(message = \"触发器类型不能为空\")\n        private Integer type;\n\n        /**\n         * http 请求触发器设置\n         */\n        @Valid\n        private HttpRequestTriggerSetting httpRequestSetting;\n\n        /**\n         * 流程表单触发器设置\n         */\n        private List<FormTriggerSetting> formSettings;\n\n        @Schema(description = \"http 请求触发器设置\", example = \"{}\")\n        @Data\n        public static class HttpRequestTriggerSetting {\n\n            @Schema(description = \"请求路径\", example = \"http://127.0.0.1\")\n            @NotEmpty(message = \"请求 URL 不能为空\")\n            @URL(message = \"请求 URL 格式不正确\")\n            private String url;\n\n            @Schema(description = \"请求头参数设置\", example = \"[]\")\n            @Valid\n            private List<HttpRequestParam> header;\n\n            @Schema(description = \"请求头参数设置\", example = \"[]\")\n            @Valid\n            private List<HttpRequestParam> body;\n\n            /**\n             * 请求返回处理设置，用于修改流程表单值\n             * <p>\n             * key：表示要修改的流程表单字段名(name)\n             * value：接口返回的字段名\n             */\n            @Schema(description = \"请求返回处理设置\", example = \"[]\")\n            private List<KeyValue<String, String>> response;\n\n            /**\n             * Http 回调请求，需要指定回调任务 Key，用于回调执行\n             */\n            @Schema(description = \"回调任务 Key\", example = \"xxx\", hidden = true)\n            private String callbackTaskDefineKey;\n\n        }\n\n        @Schema(description = \"流程表单触发器设置\", example = \"{}\")\n        @Data\n        public static class FormTriggerSetting {\n\n            @Schema(description = \"条件类型\", example = \"1\")\n            @InEnum(BpmSimpleModeConditionTypeEnum.class)\n            private Integer conditionType;\n\n            @Schema(description = \"条件表达式\", example = \"${day>3}\")\n            private String conditionExpression;\n\n            @Schema(description = \"条件组\", example = \"{}\")\n            private ConditionGroups conditionGroups;\n\n            @Schema(description = \"修改的表单字段\", example = \"{}\")\n            private Map<String, Object> updateFormFields;\n\n            @Schema(description = \"删除表单字段\", example = \"[]\")\n            private Set<String> deleteFields;\n        }\n    }\n\n    @Schema(description = \"子流程节点配置\")\n    @Data\n    @Valid\n    public static class ChildProcessSetting {\n\n        @Schema(description = \"被调用流程\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"xxx\")\n        @NotEmpty(message = \"被调用流程不能为空\")\n        private String calledProcessDefinitionKey;\n\n        @Schema(description = \"被调用流程名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"xxx\")\n        @NotEmpty(message = \"被调用流程名称不能为空\")\n        private String calledProcessDefinitionName;\n\n        @Schema(description = \"是否异步\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n        @NotNull(message = \"是否异步不能为空\")\n        private Boolean async;\n\n        @Schema(description = \"输入参数(主->子)\", example = \"[]\")\n        private List<IOParameter> inVariables;\n\n        @Schema(description = \"输出参数(子->主)\", example = \"[]\")\n        private List<IOParameter> outVariables;\n\n        @Schema(description = \"是否自动跳过子流程发起节点\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n        @NotNull(message = \"是否自动跳过子流程发起节点不能为空\")\n        private Boolean skipStartUserNode;\n\n        @Schema(description = \"子流程发起人配置\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n        @NotNull(message = \"子流程发起人配置不能为空\")\n        private StartUserSetting startUserSetting;\n\n        @Schema(description = \"超时设置\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n        private TimeoutSetting timeoutSetting;\n\n        @Schema(description = \"多实例设置\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n        private MultiInstanceSetting multiInstanceSetting;\n\n        @Schema(description = \"子流程发起人配置\")\n        @Data\n        @Valid\n        public static class StartUserSetting {\n\n            @Schema(description = \"子流程发起人类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n            @NotNull(message = \"子流程发起人类型\")\n            @InEnum(BpmChildProcessStartUserTypeEnum.class)\n            private Integer type;\n\n            @Schema(description = \"表单\", example = \"xxx\")\n            private String formField;\n\n            @Schema(description = \"当子流程发起人为空时类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n            @NotNull(message = \"当子流程发起人为空时类型不能为空\")\n            @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class)\n            private Integer emptyType;\n\n        }\n\n        @Schema(description = \"超时设置\")\n        @Data\n        @Valid\n        public static class TimeoutSetting {\n\n            @Schema(description = \"是否开启超时设置\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n            @NotNull(message = \"是否开启超时设置不能为空\")\n            private Boolean enable;\n\n            @Schema(description = \"时间类型\", example = \"1\")\n            @InEnum(BpmDelayTimerTypeEnum.class)\n            private Integer type;\n\n            @Schema(description = \"时间表达式\", example = \"PT1H,2025-01-01T00:00:00\")\n            private String timeExpression;\n\n        }\n\n        @Schema(description = \"多实例设置\")\n        @Data\n        @Valid\n        public static class MultiInstanceSetting {\n\n            @Schema(description = \"是否开启多实例\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n            @NotNull(message = \"是否开启多实例不能为空\")\n            private Boolean enable;\n\n            @Schema(description = \"是否串行\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n            @NotNull(message = \"是否串行不能为空\")\n            private Boolean sequential;\n\n            @Schema(description = \"完成比例\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n            @NotNull(message = \"完成比例不能为空\")\n            private Integer approveRatio;\n\n            @Schema(description = \"多实例来源类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n            @NotNull(message = \"多实例来源类型不能为空\")\n            @InEnum(BpmChildProcessMultiInstanceSourceTypeEnum.class)\n            private Integer sourceType;\n\n            @Schema(description = \"多实例来源\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n            @NotNull(message = \"多实例来源不能为空\")\n            private String source;\n\n        }\n\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n// TODO @jason：需要考虑，如果某个节点的配置不正确，需要有提示；具体怎么实现，可以讨论下；\n@Schema(description = \"管理后台 - 仿钉钉流程设计模型的新增/修改 Request VO\")\n@Data\npublic class BpmSimpleModelUpdateReqVO {\n\n    @Schema(description = \"流程模型编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotEmpty(message = \"流程模型编号不能为空\")\n    private String id; // 对应 Flowable act_re_model 表 ID_ 字段\n\n    @Schema(description = \"仿钉钉流程设计模型对象\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"仿钉钉流程设计模型对象不能为空\")\n    @Valid\n    private BpmSimpleModelNodeVO simpleModel;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 流程定义分页 Request VO\")\n@Data\npublic class BpmProcessDefinitionPageReqVO extends PageParam {\n\n    @Schema(description = \"标识-精准匹配\", example = \"process1641042089407\")\n    private String key;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process;\n\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 流程定义 Response VO\")\n@Data\npublic class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String id;\n\n    @Schema(description = \"版本\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer version;\n\n    @Schema(description = \"流程名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"流程标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"youdao\")\n    private String key;\n\n    @Schema(description = \"流程分类\", example = \"1\")\n    private String category;\n    @Schema(description = \"流程分类名字\", example = \"请假\")\n    private String categoryName;\n\n    @Schema(description = \"流程模型的类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer modelType; // 参见 BpmModelTypeEnum 枚举类\n\n    @Schema(description = \"流程模型的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"ABC\")\n    private String modelId;\n\n    @Schema(description = \"表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时，必须非空\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String formConf;\n    @Schema(description = \"表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时，必须非空\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> formFields;\n    @Schema(description = \"表单名字\", example = \"请假表单\")\n    private String formName;\n\n    @Schema(description = \"中断状态-参见 SuspensionState 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer suspensionState; // 参见 SuspensionState 枚举\n\n    @Schema(description = \"部署时间\")\n    private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取，非必须返回\n\n    @Schema(description = \"BPMN XML\")\n    private String bpmnXml; // 需要从对应的 BpmnModel 读取，非必须返回\n\n    @Schema(description = \"SIMPLE 设计器模型数据 json 格式\")\n    private String simpleModel; // 非必须返回\n\n    @Schema(description = \"流程定义排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long sort;\n\n    @Schema(description = \"BPMN UserTask 用户任务\")\n    @Data\n    public static class UserTask {\n\n        @Schema(description = \"任务标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"sudo\")\n        private String id;\n\n        @Schema(description = \"任务名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n        private String name;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/BpmOALeaveController.http",
    "content": "### 请求 /bpm/oa/leave/create 接口 => 成功\nPOST {{baseUrl}}/bpm/oa/leave/create\nContent-Type: application/json\ntenant-id: 1\nAuthorization: Bearer {{token}}\n\n{\n  \"startTime\": \"2022-03-01\",\n  \"endTime\": \"2022-03-05\",\n  \"type\": 1,\n  \"reason\": \"我要请假啦啦啦！\"\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/BpmOALeaveController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.oa;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeaveRespVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;\nimport cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n/**\n * OA 请假申请 Controller，用于演示自己存储数据，接入工作流的例子\n *\n * @author jason\n * @author 芋道源码\n */\n@Tag(name = \"管理后台 - OA 请假申请\")\n@RestController\n@RequestMapping(\"/bpm/oa/leave\")\n@Validated\npublic class BpmOALeaveController {\n\n    @Resource\n    private BpmOALeaveService leaveService;\n\n    @PostMapping(\"/create\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:oa-leave:create')\")\n    @Operation(summary = \"创建请求申请\")\n    public CommonResult<Long> createLeave(@Valid @RequestBody BpmOALeaveCreateReqVO createReqVO) {\n        return success(leaveService.createLeave(getLoginUserId(), createReqVO));\n    }\n\n    @GetMapping(\"/get\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:oa-leave:query')\")\n    @Operation(summary = \"获得请假申请\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    public CommonResult<BpmOALeaveRespVO> getLeave(@RequestParam(\"id\") Long id) {\n        BpmOALeaveDO leave = leaveService.getLeave(id);\n        return success(BeanUtils.toBean(leave, BpmOALeaveRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:oa-leave:query')\")\n    @Operation(summary = \"获得请假申请分页\")\n    public CommonResult<PageResult<BpmOALeaveRespVO>> getLeavePage(@Valid BpmOALeavePageReqVO pageVO) {\n        PageResult<BpmOALeaveDO> pageResult = leaveService.getLeavePage(getLoginUserId(), pageVO);\n        return success(BeanUtils.toBean(pageResult, BpmOALeaveRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/package-info.java",
    "content": "/**\n * OA 示例，用于演示外部业务接入 BPM 工作流的示例\n * 一般的接入方式，只需要调用 接口，后续 Admin 用户在管理后台的【待办事务】进行审批\n */\npackage cn.iocoder.yudao.module.bpm.controller.admin.oa;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 请假申请创建 Request VO\")\n@Data\npublic class BpmOALeaveCreateReqVO {\n\n    @Schema(description = \"请假的开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"开始时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"请假的结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"结束时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"请假类型-参见 bpm_oa_type 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"阅读芋道源码\")\n    private String reason;\n\n    @Schema(description = \"发起人自选审批人 Map\", example = \"{taskKey1: [1, 2]}\")\n    private Map<String, List<Long>> startUserSelectAssignees;\n\n    @AssertTrue(message = \"结束时间，需要在开始时间之后\")\n    public boolean isEndTimeValid() {\n        return !getEndTime().isBefore(getStartTime());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 请假申请分页 Request VO\")\n@Data\npublic class BpmOALeavePageReqVO extends PageParam {\n\n    @Schema(description = \"状态\", example = \"1\")\n    private Integer status; // 参见 BpmProcessInstanceResultEnum 枚举\n\n    @Schema(description = \"请假类型，参见 bpm_oa_type\", example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"原因，模糊匹配\", example = \"阅读芋道源码\")\n    private String reason;\n\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"申请时间\")\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 请假申请 Response VO\")\n@Data\npublic class BpmOALeaveRespVO {\n\n    @Schema(description = \"请假表单主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"请假类型，参见 bpm_oa_type 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"阅读芋道源码\")\n    private String reason;\n\n    @Schema(description = \"申请时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"请假的开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"请假的结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"流程编号\")\n    private String processInstanceId;\n\n    @Schema(description = \"审批结果\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.http",
    "content": "### 请求 /bpm/process-instance/get-bpmn 接口 => 成功\nGET {{baseUrl}}/bpm/process-instance/get-bpmn-model-view?id=1d5fb5a6-85f8-11ef-b717-7e93075f94e3\nContent-Type: application/json\ntenant-id: 1\nAuthorization: Bearer {{token}}\n\n### 请求 /bpm/process-instance/get-bpmn 接口 => 失败\n#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=1d5fb5a6-85f8-11ef-b717-7e93075f94e3\n#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=3ee5c5ba-904a-11ef-a76e-b2ed5d6ef911\n#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=f630dfa2-8f92-11ef-947c-ba5e239a6eb4\n#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=9de8bdbf-9133-11ef-ae97-eaf49df1f932\n#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=dd2188eb-9394-11ef-a039-7a9ac3d9eb6b\nGET {{baseUrl}}/bpm/process-instance/get-approval-detail?processDefinitionId=test-auto:1:c70a799a-9394-11ef-a039-7a9ac3d9eb6b\nContent-Type: application/json\ntenant-id: 1\nAuthorization: Bearer {{token}}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;\nimport cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.flowable.engine.history.HistoricProcessInstance;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.flowable.task.api.Task;\nimport org.flowable.task.api.history.HistoricTaskInstance;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS;\n\n@Tag(name = \"管理后台 - 流程实例\") // 流程实例，通过流程定义创建的一次“申请”\n@RestController\n@RequestMapping(\"/bpm/process-instance\")\n@Validated\npublic class BpmProcessInstanceController {\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n    @Resource\n    private BpmTaskService taskService;\n    @Resource\n    private BpmProcessDefinitionService processDefinitionService;\n    @Resource\n    private BpmCategoryService categoryService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @GetMapping(\"/my-page\")\n    @Operation(summary = \"获得我的实例分页列表\", description = \"在【我的流程】菜单中，进行调用\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance:query')\")\n    public CommonResult<PageResult<BpmProcessInstanceRespVO>> getProcessInstanceMyPage(\n            @Valid BpmProcessInstancePageReqVO pageReqVO) {\n        PageResult<HistoricProcessInstance> pageResult = processInstanceService.getProcessInstancePage(\n                getLoginUserId(), pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接返回\n        Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(\n                convertList(pageResult.getList(), HistoricProcessInstance::getId));\n        Map<String, ProcessDefinition> processDefinitionMap = processDefinitionService.getProcessDefinitionMap(\n                convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));\n        Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(\n                convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory));\n        Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(\n                convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));\n        Set<Long> userIds = convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId()));\n        userIds.addAll(convertSetByFlatMap(taskMap.values(),\n                tasks -> tasks.stream().map(Task::getAssignee).filter(StrUtil::isNotBlank).map(Long::parseLong)));\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(\n                convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult,\n                processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap));\n    }\n\n    @GetMapping(\"/manager-page\")\n    @Operation(summary = \"获得管理流程实例的分页列表\", description = \"在【流程实例】菜单中，进行调用\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance:manager-query')\")\n    public CommonResult<PageResult<BpmProcessInstanceRespVO>> getProcessInstanceManagerPage(\n            @Valid BpmProcessInstancePageReqVO pageReqVO) {\n        PageResult<HistoricProcessInstance> pageResult = processInstanceService.getProcessInstancePage(\n                null, pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接返回\n        Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(\n                convertList(pageResult.getList(), HistoricProcessInstance::getId));\n        Map<String, ProcessDefinition> processDefinitionMap = processDefinitionService.getProcessDefinitionMap(\n                convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));\n        Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(\n                convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory));\n        // 发起人信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId())));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(\n                convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(\n                convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));\n        return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult,\n                processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap));\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"新建流程实例\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance:query')\")\n    public CommonResult<String> createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {\n        return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得指定流程实例\", description = \"在【流程详细】界面中，进行调用\")\n    @Parameter(name = \"id\", description = \"流程实例的编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance:query')\")\n    public CommonResult<BpmProcessInstanceRespVO> getProcessInstance(@RequestParam(\"id\") String id) {\n        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(id);\n        if (processInstance == null) {\n            return success(null);\n        }\n\n        // 拼接返回\n        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(\n                processInstance.getProcessDefinitionId());\n        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(\n                processInstance.getProcessDefinitionId());\n        AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();\n        DeptRespDTO dept = null;\n        if (startUser != null && startUser.getDeptId() != null) {\n            dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();\n        }\n        return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance,\n                processDefinition, processDefinitionInfo, startUser, dept));\n    }\n\n    @DeleteMapping(\"/cancel-by-start-user\")\n    @Operation(summary = \"用户取消流程实例\", description = \"取消发起的流程\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance:cancel')\")\n    public CommonResult<Boolean> cancelProcessInstanceByStartUser(\n            @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) {\n        processInstanceService.cancelProcessInstanceByStartUser(getLoginUserId(), cancelReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/cancel-by-admin\")\n    @Operation(summary = \"管理员取消流程实例\", description = \"管理员撤回流程\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance:cancel-by-admin')\")\n    public CommonResult<Boolean> cancelProcessInstanceByManager(\n            @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) {\n        processInstanceService.cancelProcessInstanceByAdmin(getLoginUserId(), cancelReqVO);\n        return success(true);\n    }\n\n    @GetMapping(\"/get-approval-detail\")\n    @Operation(summary = \"获得审批详情\")\n    @Parameter(name = \"id\", description = \"流程实例的编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance:query')\")\n    @SuppressWarnings(\"unchecked\")\n    public CommonResult<BpmApprovalDetailRespVO> getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) {\n        if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) {\n            reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class));\n        }\n        return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO));\n    }\n\n    @GetMapping(\"/get-next-approval-nodes\")\n    @Operation(summary = \"获取下一个执行的流程节点\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance:query')\")\n    @SuppressWarnings(\"unchecked\")\n    public CommonResult<List<BpmApprovalDetailRespVO.ActivityNode>> getNextApprovalNodes(@Valid BpmApprovalDetailReqVO reqVO) {\n        if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) {\n            reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class));\n        }\n        return success(processInstanceService.getNextApprovalNodes(getLoginUserId(), reqVO));\n    }\n\n    @GetMapping(\"/get-bpmn-model-view\")\n    @Operation(summary = \"获取流程实例的 BPMN 模型视图\", description = \"在【流程详细】界面中，进行调用\")\n    @Parameter(name = \"id\", description = \"流程实例的编号\", required = true)\n    public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(\n            @RequestParam(value = \"id\") String id) {\n        return success(processInstanceService.getProcessInstanceBpmnModelView(id));\n    }\n\n    @GetMapping(\"/get-print-data\")\n    @Operation(summary = \"获得流程实例的打印数据\")\n    @Parameter(name = \"id\", description = \"流程实例的编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance:query')\")\n    public CommonResult<BpmProcessPrintDataRespVO> getProcessInstancePrintData(\n            @RequestParam(\"processInstanceId\") String processInstanceId) {\n        HistoricProcessInstance historicProcessInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);\n        if (historicProcessInstance == null) {\n            throw exception(PROCESS_INSTANCE_NOT_EXISTS);\n        }\n        AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(historicProcessInstance.getStartUserId())).getCheckedData();\n        DeptRespDTO dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();\n        List<HistoricTaskInstance> tasks = taskService.getFinishedTaskListByProcessInstanceIdWithoutCancel(processInstanceId);\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(tasks, item -> Long.valueOf(item.getAssignee())));\n        return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePrintData(historicProcessInstance,\n                processDefinitionService.getProcessDefinitionInfo(historicProcessInstance.getProcessDefinitionId()),\n                tasks, userMap,\n                new UserSimpleBaseVO().setNickname(startUser.getNickname()).setDeptName(dept.getName())));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.date.DateUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc.BpmProcessInstanceCopyRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.flowable.engine.history.HistoricProcessInstance;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - 流程实例抄送\")\n@RestController\n@RequestMapping(\"/bpm/process-instance/copy\")\n@Validated\npublic class BpmProcessInstanceCopyController {\n\n    @Resource\n    private BpmProcessInstanceCopyService processInstanceCopyService;\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n    @Resource\n    private BpmProcessDefinitionService processDefinitionService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得抄送流程分页列表\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:process-instance-cc:query')\")\n    public CommonResult<PageResult<BpmProcessInstanceCopyRespVO>> getProcessInstanceCopyPage(\n            @Valid BpmProcessInstanceCopyPageReqVO pageReqVO) {\n        PageResult<BpmProcessInstanceCopyDO> pageResult = processInstanceCopyService.getProcessInstanceCopyPage(\n                getLoginUserId(), pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(new PageResult<>(pageResult.getTotal()));\n        }\n\n        // 拼接返回\n        Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(\n                convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),\n                copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator()))));\n        Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(\n                convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessDefinitionId));\n        return success(convertPage(pageResult, copy -> {\n            BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class);\n            MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()),\n                    user -> copyVO.setStartUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));\n            MapUtils.findAndThen(userMap, copy.getStartUserId(),\n                    user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));\n            MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(),\n                    processInstance -> {\n                        copyVO.setSummary(FlowableUtils.getSummary(\n                                processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),\n                                processInstance.getProcessVariables()));\n                        copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime()));\n                    });\n            return copyVO;\n        }));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;\nimport cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.flowable.bpmn.model.UserTask;\nimport org.flowable.engine.history.HistoricProcessInstance;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.task.api.Task;\nimport org.flowable.task.api.history.HistoricTaskInstance;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - 流程任务实例\")\n@RestController\n@RequestMapping(\"/bpm/task\")\n@Validated\npublic class BpmTaskController {\n\n    @Resource\n    private BpmTaskService taskService;\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n    @Resource\n    private BpmFormService formService;\n    @Resource\n    private BpmProcessDefinitionService processDefinitionService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @GetMapping(\"todo-page\")\n    @Operation(summary = \"获取 Todo 待办任务分页\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:query')\")\n    public CommonResult<PageResult<BpmTaskRespVO>> getTaskTodoPage(@Valid BpmTaskPageReqVO pageVO) {\n        PageResult<Task> pageResult = taskService.getTaskTodoPage(getLoginUserId(), pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n\n        // 拼接数据\n        Map<String, ProcessInstance> processInstanceMap = processInstanceService.getProcessInstanceMap(\n                convertSet(pageResult.getList(), Task::getProcessInstanceId));\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));\n        Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(\n                convertSet(pageResult.getList(), Task::getProcessDefinitionId));\n        return success(BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap, processDefinitionInfoMap));\n    }\n\n    @GetMapping(\"done-page\")\n    @Operation(summary = \"获取 Done 已办任务分页\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:query')\")\n    public CommonResult<PageResult<BpmTaskRespVO>> getTaskDonePage(@Valid BpmTaskPageReqVO pageVO) {\n        PageResult<HistoricTaskInstance> pageResult = taskService.getTaskDonePage(getLoginUserId(), pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n\n        // 拼接数据\n        Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(\n                convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId));\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));\n        Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(\n                convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId));\n        return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap));\n    }\n\n    @GetMapping(\"manager-page\")\n    @Operation(summary = \"获取全部任务的分页\", description = \"用于【流程任务】菜单\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:manager-query')\")\n    public CommonResult<PageResult<BpmTaskRespVO>> getTaskManagerPage(@Valid BpmTaskPageReqVO pageVO) {\n        PageResult<HistoricTaskInstance> pageResult = taskService.getTaskPage(getLoginUserId(), pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n\n        // 拼接数据\n        Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(\n                convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId));\n        // 获得 User 和 Dept Map\n        Set<Long> userIds = convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()));\n        userIds.addAll(convertSet(pageResult.getList(), task -> NumberUtils.parseLong(task.getAssignee())));\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(\n                convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(\n                convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId));\n        return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap));\n    }\n\n    @GetMapping(\"/list-by-process-instance-id\")\n    @Operation(summary = \"获得指定流程实例的任务列表\", description = \"包括完成的、未完成的\")\n    @Parameter(name = \"processInstanceId\", description = \"流程实例的编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:query')\")\n    public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(\n            @RequestParam(\"processInstanceId\") String processInstanceId) {\n        List<HistoricTaskInstance> taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true);\n        if (CollUtil.isEmpty(taskList)) {\n            return success(Collections.emptyList());\n        }\n\n        // 拼接数据\n        Set<Long> userIds = convertSetByFlatMap(taskList, task ->\n                Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner())));\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(\n                convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        // 获得 Form Map\n        Map<Long, BpmFormDO> formMap = formService.getFormMap(\n                convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey())));\n        return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList,\n                formMap, userMap, deptMap));\n    }\n\n    @PutMapping(\"/approve\")\n    @Operation(summary = \"通过任务\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {\n        taskService.approveTask(getLoginUserId(), reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/reject\")\n    @Operation(summary = \"不通过任务\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<Boolean> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {\n        taskService.rejectTask(getLoginUserId(), reqVO);\n        return success(true);\n    }\n\n    @GetMapping(\"/list-by-return\")\n    @Operation(summary = \"获取所有可退回的节点\", description = \"用于【流程详情】的【退回】按钮\")\n    @Parameter(name = \"taskId\", description = \"当前任务ID\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<List<BpmTaskRespVO>> getTaskListByReturn(@RequestParam(\"id\") String id) {\n        List<UserTask> userTaskList = taskService.getUserTaskListByReturn(id);\n        return success(convertList(userTaskList, userTask -> // 只返回 id 和 name\n                new BpmTaskRespVO().setName(userTask.getName()).setTaskDefinitionKey(userTask.getId())));\n    }\n\n    @PutMapping(\"/return\")\n    @Operation(summary = \"退回任务\", description = \"用于【流程详情】的【退回】按钮\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {\n        taskService.returnTask(getLoginUserId(), reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/delegate\")\n    @Operation(summary = \"委派任务\", description = \"用于【流程详情】的【委派】按钮\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<Boolean> delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) {\n        taskService.delegateTask(getLoginUserId(), reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/transfer\")\n    @Operation(summary = \"转派任务\", description = \"用于【流程详情】的【转派】按钮\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<Boolean> transferTask(@Valid @RequestBody BpmTaskTransferReqVO reqVO) {\n        taskService.transferTask(getLoginUserId(), reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/create-sign\")\n    @Operation(summary = \"加签\", description = \"before 前加签，after 后加签\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<Boolean> createSignTask(@Valid @RequestBody BpmTaskSignCreateReqVO reqVO) {\n        taskService.createSignTask(getLoginUserId(), reqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-sign\")\n    @Operation(summary = \"减签\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<Boolean> deleteSignTask(@Valid @RequestBody BpmTaskSignDeleteReqVO reqVO) {\n        taskService.deleteSignTask(getLoginUserId(), reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/copy\")\n    @Operation(summary = \"抄送任务\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<Boolean> copyTask(@Valid @RequestBody BpmTaskCopyReqVO reqVO) {\n        taskService.copyTask(getLoginUserId(), reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/withdraw\")\n    @Operation(summary = \"撤回任务\")\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:update')\")\n    public CommonResult<Boolean> withdrawTask(@RequestParam(\"taskId\") String taskId) {\n        taskService.withdrawTask(getLoginUserId(), taskId);\n        return success(true);\n    }\n\n    @GetMapping(\"/list-by-parent-task-id\")\n    @Operation(summary = \"获得指定父级任务的子任务列表\") // 目前用于，减签的时候，获得子任务列表\n    @Parameter(name = \"parentTaskId\", description = \"父级任务编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('bpm:task:query')\")\n    public CommonResult<List<BpmTaskRespVO>> getTaskListByParentTaskId(@RequestParam(\"parentTaskId\") String parentTaskId) {\n        List<Task> taskList = taskService.getTaskListByParentTaskId(parentTaskId);\n        if (CollUtil.isEmpty(taskList)) {\n            return success(Collections.emptyList());\n        }\n        // 拼接数据\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(taskList,\n                user -> Stream.of(NumberUtils.parseLong(user.getAssignee()), NumberUtils.parseLong(user.getOwner()))));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(\n                convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        return success(BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 流程活动的 Response VO\")\n@Data\npublic class BpmActivityRespVO {\n\n    @Schema(description = \"流程活动的标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String key;\n    @Schema(description = \"流程活动的类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"StartEvent\")\n    private String type;\n\n    @Schema(description = \"流程活动的开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime startTime;\n    @Schema(description = \"流程活动的结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"关联的流程任务的编号\", example = \"2048\")\n    private String taskId; // 关联的流程任务，只有 UserTask 等类型才有\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc;\n\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 流程实例抄送的分页 Item Response VO\")\n@Data\npublic class BpmProcessInstanceCopyRespVO {\n\n    @Schema(description = \"抄送主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"发起人\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private UserSimpleBaseVO startUser;\n\n    @Schema(description = \"流程实例编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A233\")\n    private String processInstanceId;\n    @Schema(description = \"流程实例的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"测试\")\n    private String processInstanceName;\n    @Schema(description = \"流程实例的发起时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime processInstanceStartTime;\n\n    @Schema(description = \"流程活动的编号\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String activityId;\n    @Schema(description = \"流程活动的名字\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String activityName;\n\n    @Schema(description = \"流程活动的编号\")\n    private String taskId;\n\n    @Schema(description = \"抄送人意见\")\n    private String reason;\n\n    @Schema(description = \"创建人\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private UserSimpleBaseVO createUser;\n\n    @Schema(description = \"抄送时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"流程摘要\", example = \"[]\")\n    private List<KeyValue<String, String>> summary;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.AssertTrue;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - 审批详情 Request VO\")\n@Data\npublic class BpmApprovalDetailReqVO {\n\n    @Schema(description = \"流程定义的编号\", example = \"1024\")\n    private String processDefinitionId; // 使用场景：发起流程时，传流程定义 ID\n\n    @Schema(description = \"流程变量\")\n    private Map<String, Object> processVariables; // 使用场景：同 processDefinitionId，用于流程预测\n\n    @Schema(description = \"流程变量\")\n    private String processVariablesStr; // 解决 GET 无法传递对象的问题，最终转换成 processVariables 变量\n\n    @Schema(description = \"流程实例的编号\", example = \"1024\")\n    private String processInstanceId;  // 使用场景：流程已发起时候传流程实例 ID\n\n    // TODO @芋艿：如果未来 BPMN 增加流程图，它没有发起人节点，会有问题。\n    @Schema(description = \"流程活动编号\", example = \"StartUserNode\")\n    private String activityId; // 用于获取表单权限。1）发起流程时，传“发起人节点” activityId 可获取发起人的表单权限；2）从抄送列表界面进来时，传抄送的 activityId 可获取抄送人的表单权限；\n\n    @Schema(description = \"流程任务编号\", example = \"95f2f08b-621b-11ef-bf39-00ff4722db8b\")\n    private String taskId; // 用于获取表单权限。1）从待审批/已审批界面进来时，传递 taskId 任务编号，可获取任务节点的变得权限\n\n    @AssertTrue(message = \"流程定义的编号和流程实例的编号不能同时为空\")\n    @JsonIgnore\n    public boolean isValidProcessParam() {\n        return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;\n\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n\n@Schema(description = \"管理后台 - 审批详情 Response VO\")\n@Data\npublic class BpmApprovalDetailRespVO {\n\n    @Schema(description = \"流程实例的状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举\n\n    @Schema(description = \"活动节点列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<ActivityNode> activityNodes;\n\n    @Schema(description = \"表单字段权限\")\n    private Map<String, String> formFieldsPermission;\n\n    @Schema(description = \"待办任务\")\n    private BpmTaskRespVO todoTask;\n\n    /**\n     * 所属流程定义信息\n     */\n    private BpmProcessDefinitionRespVO processDefinition;\n\n    /**\n     * 所属流程实例信息\n     */\n    private BpmProcessInstanceRespVO processInstance;\n\n    @Schema(description = \"活动节点信息\")\n    @Data\n    public static class ActivityNode {\n\n        @Schema(description = \"节点编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"StartUserNode\")\n        private String id;\n\n        @Schema(description = \"节点名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"发起人\")\n        private String name;\n\n        @Schema(description = \"节点类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer nodeType; // 参见 BpmSimpleModelNodeType 枚举\n\n        @Schema(description = \"节点状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n        private Integer status; // 参见 BpmTaskStatusEnum 枚举\n\n        @Schema(description = \"节点的开始时间\")\n        private LocalDateTime startTime;\n        @Schema(description = \"节点的结束时间\")\n        private LocalDateTime endTime;\n\n        @Schema(description = \"审批节点的任务信息\")\n        private List<ActivityNodeTask> tasks;\n\n        @Schema(description = \"候选人策略\", example = \"35\")\n        private Integer candidateStrategy; // 参见 BpmTaskCandidateStrategyEnum 枚举。主要用于发起时，审批节点、抄送节点自选\n\n        @Schema(description = \"候选人用户 ID 列表\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"1818\")\n        @JsonIgnore // 不返回，只是方便后续读取，赋值给 candidateUsers\n        private List<Long> candidateUserIds;\n\n        @Schema(description = \"候选人用户列表\")\n        private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表\n\n        @Schema(description = \"流程编号\", example = \"8761d8e0-0922-11f0-bd37-00ff1db677bf\")\n        private String processInstanceId; // 当且仅当，该节点是子流程节点时，才会有值（CallActivity 的 calledProcessInstanceId 字段）\n\n    }\n\n    @Schema(description = \"活动节点的任务信息\")\n    @Data\n    public static class ActivityNodeTask {\n\n        @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private String id;\n\n        @Schema(description = \"任务所属人编号\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"1818\")\n        @JsonIgnore // 不返回，只是方便后续读取，赋值给 ownerUser\n        private Long owner;\n\n        @Schema(description = \"任务所属人\", example = \"1024\")\n        private UserSimpleBaseVO ownerUser;\n\n        @Schema(description = \"任务分配人编号\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"2048\")\n        @JsonIgnore // 不返回，只是方便后续读取，赋值给 assigneeUser\n        private Long assignee;\n\n        @Schema(description = \"任务分配人\", example = \"2048\")\n        private UserSimpleBaseVO assigneeUser;\n\n        @Schema(description = \"任务状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer status;  // 参见 BpmTaskStatusEnum 枚举\n\n        @Schema(description = \"审批意见\", example = \"同意\")\n        private String reason;\n\n        @Schema(description = \"签名\", example = \"https://www.iocoder.cn/sign.png\")\n        private String signPicUrl;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;\n\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - 流程示例的 BPMN 视图 Response VO\")\n@Data\npublic class BpmProcessInstanceBpmnModelViewRespVO {\n\n    // ========== 基本信息 ==========\n\n    @Schema(description = \"流程实例信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private BpmProcessInstanceRespVO processInstance;\n\n    @Schema(description = \"任务列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<BpmTaskRespVO> tasks;\n\n    @Schema(description = \"BPMN XML\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String bpmnXml;\n\n    @Schema(description = \"SIMPLE 模型\")\n    private BpmSimpleModelNodeVO simpleModel;\n\n    // ========== 进度信息 ==========\n\n    @Schema(description = \"进行中的活动节点编号集合\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Set<String> unfinishedTaskActivityIds; // 只包括 UserTask\n\n    @Schema(description = \"已经完成的活动节点编号集合\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Set<String> finishedTaskActivityIds; // 包括 UserTask、Gateway 等，不包括 SequenceFlow\n\n    @Schema(description = \"已经完成的连线节点编号集合\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Set<String> finishedSequenceFlowActivityIds; // 只包括 SequenceFlow\n\n    @Schema(description = \"已经拒绝的活动节点编号集合\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Set<String> rejectedTaskActivityIds; // 只包括 UserTask\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Schema(description = \"管理后台 - 流程实例的取消 Request VO\")\n@Data\npublic class BpmProcessInstanceCancelReqVO {\n\n    @Schema(description = \"流程实例的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotEmpty(message = \"流程实例的编号不能为空\")\n    private String id;\n\n    @Schema(description = \"取消原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"不请假了！\")\n    @NotEmpty(message = \"取消原因不能为空\")\n    private String reason;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 流程实例抄送的分页 Request VO\")\n@Data\npublic class BpmProcessInstanceCopyPageReqVO extends PageParam {\n\n    @Schema(description = \"流程名称\", example = \"芋道\")\n    private String processInstanceName;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - 流程实例的创建 Request VO\")\n@Data\npublic class BpmProcessInstanceCreateReqVO {\n\n    @Schema(description = \"流程定义的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotEmpty(message = \"流程定义编号不能为空\")\n    private String processDefinitionId;\n\n    @Schema(description = \"变量实例（动态表单）\")\n    private Map<String, Object> variables;\n\n    @Schema(description = \"发起人自选审批人 Map\", example = \"{taskKey1: [1, 2]}\")\n    private Map<String, List<Long>> startUserSelectAssignees;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 流程实例分页 Request VO\")\n@Data\npublic class BpmProcessInstancePageReqVO extends PageParam {\n\n    @Schema(description = \"流程名称\", example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"流程定义的标识\", example = \"2048\")\n    private String processDefinitionKey; // 精准匹配\n\n    @Schema(description = \"流程实例的状态\", example = \"1\")\n    @InEnum(BpmProcessInstanceStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"流程分类\", example = \"1\")\n    private String category;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n    @Schema(description = \"结束时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] endTime;\n\n    @Schema(description = \"发起用户编号\", example = \"1024\")\n    private Long startUserId; // 注意，只有在【流程实例】菜单，才使用该参数\n\n    @Schema(description = \"动态表单字段查询 JSON Str\", example = \"{}\")\n    private String formFieldsParams; // SpringMVC 在 get 请求下，无法方便的定义 Map 类型的参数，所以通过 String 接收后，逻辑里面转换\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;\n\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - 流程实例的 Response VO\")\n@Data\npublic class BpmProcessInstanceRespVO {\n\n    @Schema(description = \"流程实例的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String id;\n\n    @Schema(description = \"流程名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"流程摘要\")\n    private List<KeyValue<String, String>> summary; // 只有流程表单，才有摘要！\n\n    @Schema(description = \"流程分类\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private String category;\n    @Schema(description = \"流程分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"请假\")\n    private String categoryName;\n\n    @Schema(description = \"流程实例的状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举\n\n    @Schema(description = \"发起时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"持续时间\", example = \"1000\")\n    private Long durationInMillis;\n\n    @Schema(description = \"提交的表单值\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Map<String, Object> formVariables;\n\n    @Schema(description = \"业务的唯一标识-例如说，请假申请的编号\", example = \"1\")\n    private String businessKey;\n\n    /**\n     * 发起流程的用户\n     */\n    private UserSimpleBaseVO startUser;\n\n    @Schema(description = \"流程定义的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private String processDefinitionId;\n    /**\n     * 流程定义\n     */\n    private BpmProcessDefinitionRespVO processDefinition;\n\n    /**\n     * 当前审批中的任务\n     */\n    private List<Task> tasks; // 仅在流程实例分页才返回\n\n    @Schema(description = \"流程任务\")\n    @Data\n    public static class Task {\n\n        @Schema(description = \"流程任务的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private String id;\n\n        @Schema(description = \"任务名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n        private String name;\n\n        @Schema(description = \"任务分配人编号\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"2048\")\n        @JsonIgnore // 不返回，只是方便后续读取，赋值给 assigneeUser\n        private Long assignee;\n\n        @Schema(description = \"任务分配人\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"2048\")\n        private UserSimpleBaseVO assigneeUser;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessPrintDataRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 流程实例的打印数据 Response VO\")\n@Data\npublic class BpmProcessPrintDataRespVO {\n\n    @Schema(description = \"流程实例数据\")\n    private BpmProcessInstanceRespVO processInstance;\n\n    @Schema(description = \"是否开启自定义打印模板\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean printTemplateEnable;\n\n    @Schema(description = \"自定义打印模板 HTML\")\n    private String printTemplateHtml;\n\n    @Schema(description = \"审批任务列表\")\n    private List<Task> tasks;\n\n    @Schema(description = \"流程任务\")\n    @Data\n    public static class Task {\n\n        @Schema(description = \"流程任务的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private String id;\n\n        @Schema(description = \"任务名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n        private String name;\n\n        @Schema(description = \"签名 URL\", example = \"https://www.iocoder.cn/sign.png\")\n        private String signPicUrl;\n\n        @Schema(description = \"任务描述\", requiredMode = Schema.RequiredMode.REQUIRED)\n        private String description; // 该字段由后端拼接\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - 通过流程任务的 Request VO\")\n@Data\npublic class BpmTaskApproveReqVO {\n\n    @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotEmpty(message = \"任务编号不能为空\")\n    private String id;\n\n    @Schema(description = \"审批意见\", example = \"不错不错！\")\n    private String reason;\n\n    @Schema(description = \"签名\", example = \"https://www.iocoder.cn/sign.png\")\n    private String signPicUrl;\n\n    @Schema(description = \"变量实例（动态表单）\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Map<String, Object> variables;\n\n    @Schema(description = \"下一个节点审批人\", example = \"{nodeId:[1, 2]}\")\n    private Map<String, List<Long>> nextAssignees; // 为什么是 Map，而不是 List 呢？因为下一个节点可能是多个，例如说并行网关的情况\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.Collection;\n\n@Schema(description = \"管理后台 - 抄送流程任务的 Request VO\")\n@Data\npublic class BpmTaskCopyReqVO {\n\n    @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotEmpty(message = \"任务编号不能为空\")\n    private String id;\n\n    @Schema(description = \"抄送的用户编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1,2]\")\n    @NotEmpty(message = \"抄送用户不能为空\")\n    private Collection<Long> copyUserIds;\n\n    @Schema(description = \"抄送意见\", example = \"帮忙看看！\")\n    private String reason;\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 委派流程任务的 Request VO\")\n@Data\npublic class BpmTaskDelegateReqVO {\n\n    @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotEmpty(message = \"任务编号不能为空\")\n    private String id;\n\n    @Schema(description = \"被委派人 ID\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"被委派人 ID 不能为空\")\n    private Long delegateUserId;\n\n    @Schema(description = \"委派原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"做不了决定，需要你先帮忙瞅瞅\")\n    @NotEmpty(message = \"委派原因不能为空\")\n    private String reason;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.date.DateUtils;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 流程任务的的分页 Request VO\") // 待办、已办，都使用该分页\n@Data\npublic class BpmTaskPageReqVO extends PageParam {\n\n    @Schema(description = \"流程任务名\", example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"流程分类\", example = \"1\")\n    private String category;\n\n    @Schema(description = \"流程定义的标识\", example = \"2048\")\n    private String processDefinitionKey; // 精准匹配\n\n    @Schema(description = \"审批状态\", example = \"1\")\n    @InEnum(BpmTaskStatusEnum.class)\n    private Integer status; // 仅【已办】使用\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Schema(description = \"管理后台 - 不通过流程任务的 Request VO\")\n@Data\npublic class BpmTaskRejectReqVO {\n\n    @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotEmpty(message = \"任务编号不能为空\")\n    private String id;\n\n    @Schema(description = \"审批意见\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"不错不错！\")\n    private String reason;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - 流程任务 Response VO\")\n@Data\npublic class BpmTaskRespVO {\n\n    @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String id;\n\n    @Schema(description = \"任务名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"持续时间\", example = \"1000\")\n    private Long durationInMillis;\n\n    @Schema(description = \"任务状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer status; // 参见 BpmTaskStatusEnum 枚举\n\n    @Schema(description = \"审批理由\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private String reason;\n\n    @Schema(description = \"任务负责人编号\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"2048\")\n    @JsonIgnore // 不返回，只是方便后续读取，赋值给 ownerUser\n    private Long owner;\n    /**\n     * 负责人的用户信息\n     */\n    private UserSimpleBaseVO ownerUser;\n\n    @Schema(description = \"任务分配人编号\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"2048\")\n    @JsonIgnore // 不返回，只是方便后续读取，赋值给 assigneeUser\n    private Long assignee;\n    /**\n     * 审核的用户信息\n     */\n    private UserSimpleBaseVO assigneeUser;\n\n    @Schema(description = \"任务定义的标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Activity_one\")\n    private String taskDefinitionKey;\n\n    @Schema(description = \"所属流程实例编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8888\")\n    private String processInstanceId;\n    /**\n     * 所属流程实例\n     */\n    private ProcessInstance processInstance;\n\n    @Schema(description = \"父任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String parentTaskId;\n    @Schema(description = \"子任务列表（由加签生成）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"childrenTask\")\n    private List<BpmTaskRespVO> children; // 由加签生成，包含多层子任务\n\n    @Schema(description = \"表单编号\", example = \"1024\")\n    private Long formId;\n    @Schema(description = \"表单名字\", example = \"请假表单\")\n    private String formName;\n    @Schema(description = \"表单的配置，JSON 字符串\")\n    private String formConf;\n    @Schema(description = \"表单项的数组\")\n    private List<String> formFields;\n    @Schema(description = \"提交的表单值\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Map<String, Object> formVariables;\n    @Schema(description = \"操作按钮设置值\")\n    private Map<Integer, OperationButtonSetting> buttonsSetting;\n\n    @Schema(description = \"是否需要签名\", example = \"false\")\n    private Boolean signEnable;\n\n    @Schema(description = \"是否填写审批意见\", example = \"false\")\n    private Boolean reasonRequire;\n\n    @Schema(description = \"节点类型\", example = \"10\")\n    private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举。\n\n    @Data\n    @Schema(description = \"流程实例\")\n    public static class ProcessInstance {\n\n        @Schema(description = \"流程实例编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private String id;\n\n        @Schema(description = \"流程实例名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n        private String name;\n\n        @Schema(description = \"提交时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n        private LocalDateTime createTime;\n\n        @Schema(description = \"流程定义的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n        private String processDefinitionId;\n\n        @Schema(description = \"流程摘要\", example = \"[]\")\n        private List<KeyValue<String, String>> summary; // 只有流程表单，才有摘要！\n\n        /**\n         * 发起人的用户信息\n         */\n        private UserSimpleBaseVO startUser;\n\n    }\n\n    @Data\n    @Schema(description = \"操作按钮设置\")\n    public static class OperationButtonSetting {\n\n        @Schema(description = \"显示名称\", example = \"审批\")\n        private String displayName;\n\n        @Schema(description = \"是否启用\", example = \"true\")\n        private Boolean enable;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Schema(description = \"管理后台 - 退回流程任务的 Request VO\")\n@Data\npublic class BpmTaskReturnReqVO {\n\n    @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotEmpty(message = \"任务编号不能为空\")\n    private String id;\n\n    @Schema(description = \"退回到的任务 Key\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotEmpty(message = \"退回到的任务 Key 不能为空\")\n    private String targetTaskDefinitionKey;\n\n    @Schema(description = \"退回意见\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我就是想驳回\")\n    @NotEmpty(message = \"退回意见不能为空\")\n    private String reason;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - 加签任务的创建（加签） Request VO\")\n@Data\npublic class BpmTaskSignCreateReqVO {\n\n    @Schema(description = \"需要加签的任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotEmpty(message = \"任务编号不能为空\")\n    private String id;\n\n    @Schema(description = \"加签的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n    @NotEmpty(message = \"加签用户不能为空\")\n    private Set<Long> userIds;\n\n    @Schema(description = \"加签类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"before\")\n    @NotEmpty(message = \"加签类型不能为空\")\n    private String type; // 参见 BpmTaskSignTypeEnum 枚举\n\n    @Schema(description = \"加签原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"需要加签\")\n    @NotEmpty(message = \"加签原因不能为空\")\n    private String reason;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Schema(description = \"管理后台 - 加签任务的删除（减签） Request VO\")\n@Data\npublic class BpmTaskSignDeleteReqVO {\n\n    @Schema(description = \"被减签的任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotEmpty(message = \"任务编号不能为空\")\n    private String id;\n\n    @Schema(description = \"加签原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"需要减签\")\n    @NotEmpty(message = \"加签原因不能为空\")\n    private String reason;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java",
    "content": "package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 流程任务的转办 Request VO\")\n@Data\npublic class BpmTaskTransferReqVO {\n\n    @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotEmpty(message = \"任务编号不能为空\")\n    private String id;\n\n    @Schema(description = \"新审批人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    @NotNull(message = \"新审批人的用户编号不能为空\")\n    private Long assigneeUserId;\n\n    @Schema(description = \"转办原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"做不了决定，需要你先帮忙瞅瞅\")\n    @NotEmpty(message = \"转办原因不能为空\")\n    private String reason;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/app/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.bpm.controller.app;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/package-info.java",
    "content": "/**\n * 提供 RESTful API 给前端：\n * 1. admin 包：提供给管理后台 yudao-ui-admin 前端项目\n * 2. app 包：提供给用户 APP yudao-ui-app 前端项目，它的 Controller 和 VO 都要添加 App 前缀，用于和管理后台进行区分\n */\npackage cn.iocoder.yudao.module.bpm.controller;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java",
    "content": "package cn.iocoder.yudao.module.bpm.convert.definition;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.util.date.DateUtils;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.flowable.common.engine.impl.db.SuspensionState;\nimport org.flowable.engine.repository.Deployment;\nimport org.flowable.engine.repository.Model;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * 流程模型 Convert\n *\n * @author yunlongn\n */\n@Mapper\npublic interface BpmModelConvert {\n\n    BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);\n\n    default List<BpmModelRespVO> buildModelList(List<Model> list,\n                                                Map<Long, BpmFormDO> formMap,\n                                                Map<String, BpmCategoryDO> categoryMap,\n                                                Map<String, Deployment> deploymentMap,\n                                                Map<String, ProcessDefinition> processDefinitionMap,\n                                                Map<Long, AdminUserRespDTO> userMap,\n                                                Map<Long, DeptRespDTO> deptMap) {\n        List<BpmModelRespVO> result = convertList(list, model -> {\n            BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);\n            BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;\n            BpmCategoryDO category = categoryMap.get(model.getCategory());\n            Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;\n            ProcessDefinition processDefinition = model.getDeploymentId() != null ?\n                    processDefinitionMap.get(model.getDeploymentId()) : null;\n            List<AdminUserRespDTO> startUsers = metaInfo != null ? convertList(metaInfo.getStartUserIds(), userMap::get) : null;\n            List<DeptRespDTO> startDepts = metaInfo != null ? convertList(metaInfo.getStartDeptIds(), deptMap::get) : null;\n            return buildModel0(model, metaInfo, form, category, deployment, processDefinition, startUsers, startDepts);\n        });\n        // 排序\n        result.sort(Comparator.comparing(BpmModelMetaInfoVO::getSort));\n        return result;\n    }\n\n    default BpmModelRespVO buildModel(Model model, byte[] bpmnBytes, BpmSimpleModelNodeVO simpleModel) {\n        BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);\n        BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null, null);\n        if (ArrayUtil.isNotEmpty(bpmnBytes)) {\n            modelVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnBytes));\n        }\n        modelVO.setSimpleModel(simpleModel);\n        return modelVO;\n    }\n\n    default BpmModelRespVO buildModel0(Model model,\n                                       BpmModelMetaInfoVO metaInfo, BpmFormDO form, BpmCategoryDO category,\n                                       Deployment deployment, ProcessDefinition processDefinition,\n                                       List<AdminUserRespDTO> startUsers, List<DeptRespDTO> startDepts) {\n        BpmModelRespVO modelRespVO = new BpmModelRespVO().setId(model.getId()).setName(model.getName())\n                .setKey(model.getKey()).setCategory(model.getCategory())\n                .setCreateTime(DateUtils.of(model.getCreateTime()));\n        // Form\n        BeanUtils.copyProperties(metaInfo, modelRespVO);\n        if (form != null) {\n            modelRespVO.setFormName(form.getName());\n        }\n        // Category\n        if (category != null) {\n            modelRespVO.setCategoryName(category.getName());\n        }\n        // ProcessDefinition\n        if (processDefinition != null) {\n            modelRespVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));\n            modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ?\n                    SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());\n            if (deployment != null) {\n                modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime()));\n            }\n        }\n        // User、Dept\n        modelRespVO.setStartUsers(BeanUtils.toBean(startUsers, UserSimpleBaseVO.class))\n                .setStartDepts(BeanUtils.toBean(startDepts, DeptSimpleBaseVO.class));\n        return modelRespVO;\n    }\n\n    default void copyToModel(Model model, BpmModelSaveReqVO reqVO) {\n        model.setName(reqVO.getName());\n        model.setKey(reqVO.getKey());\n        model.setCategory(reqVO.getCategory());\n        model.setMetaInfo(JsonUtils.toJsonString(BeanUtils.toBean(reqVO, BpmModelMetaInfoVO.class)));\n    }\n\n    default BpmModelMetaInfoVO parseMetaInfo(Model model) {\n        BpmModelMetaInfoVO vo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoVO.class);\n        if (vo == null) {\n            return null;\n        }\n        if (vo.getManagerUserIds() == null) {\n            vo.setManagerUserIds(Collections.emptyList());\n        }\n        if (vo.getStartUserIds() == null) {\n            vo.setStartUserIds(Collections.emptyList());\n        }\n        // 如果为空，兜底处理，使用 createTime 创建时间\n        if (vo.getSort() == null) {\n            vo.setSort(model.getCreateTime().getTime());\n        }\n        return vo;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java",
    "content": "package cn.iocoder.yudao.module.bpm.convert.definition;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.common.engine.impl.db.SuspensionState;\nimport org.flowable.engine.repository.Deployment;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.MappingTarget;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Bpm 流程定义的 Convert\n *\n * @author yunlong.li\n */\n@Mapper\npublic interface BpmProcessDefinitionConvert {\n\n    BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class);\n\n    default PageResult<BpmProcessDefinitionRespVO> buildProcessDefinitionPage(PageResult<ProcessDefinition> page,\n                                                                              Map<String, Deployment> deploymentMap,\n                                                                              Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap,\n                                                                              Map<Long, BpmFormDO> formMap,\n                                                                              Map<String, BpmCategoryDO> categoryMap) {\n        List<BpmProcessDefinitionRespVO> list = buildProcessDefinitionList(page.getList(), deploymentMap, processDefinitionInfoMap, formMap, categoryMap);\n        return new PageResult<>(list, page.getTotal());\n    }\n\n    default List<BpmProcessDefinitionRespVO> buildProcessDefinitionList(List<ProcessDefinition> list,\n                                                                        Map<String, Deployment> deploymentMap,\n                                                                        Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap,\n                                                                        Map<Long, BpmFormDO> formMap,\n                                                                        Map<String, BpmCategoryDO> categoryMap) {\n        List<BpmProcessDefinitionRespVO> result = CollectionUtils.convertList(list, definition -> {\n            Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class);\n            BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class);\n            BpmFormDO form = null;\n            if (processDefinitionInfo != null) {\n                form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class);\n            }\n            BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class);\n            return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null);\n        });\n        // 排序\n        result.sort(Comparator.comparing(BpmProcessDefinitionRespVO::getSort));\n        return result;\n    }\n\n    default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition,\n                                                              Deployment deployment,\n                                                              BpmProcessDefinitionInfoDO processDefinitionInfo,\n                                                              BpmFormDO form,\n                                                              BpmCategoryDO category,\n                                                              BpmnModel bpmnModel) {\n        BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class);\n        respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());\n        // Deployment\n        if (deployment != null) {\n            respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime()));\n        }\n        // BpmProcessDefinitionInfoDO\n        if (processDefinitionInfo != null) {\n            copyTo(processDefinitionInfo, respVO);\n            // Form\n            if (form != null) {\n                respVO.setFormName(form.getName());\n            }\n        }\n        // Category\n        if (category != null) {\n            respVO.setCategoryName(category.getName());\n        }\n        // BpmnModel\n        if (bpmnModel != null) {\n            respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));\n        }\n        return respVO;\n    }\n\n    @Mapping(source = \"from.id\", target = \"to.id\", ignore = true)\n    void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/message/BpmMessageConvert.java",
    "content": "package cn.iocoder.yudao.module.bpm.convert.message;\n\nimport cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.Map;\n\n@Mapper\npublic interface BpmMessageConvert {\n\n    BpmMessageConvert INSTANCE = Mappers.getMapper(BpmMessageConvert.class);\n\n    @Mapping(target = \"mobile\", ignore = true)\n    @Mapping(source = \"userId\", target = \"userId\")\n    @Mapping(source = \"templateCode\", target = \"templateCode\")\n    @Mapping(source = \"templateParams\", target = \"templateParams\")\n    SmsSendSingleToUserReqDTO convert(Long userId, String templateCode, Map<String, Object> templateParams);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/package-info.java",
    "content": "/**\n * 提供 POJO 类的实体转换\n *\n * 目前使用 MapStruct 框架\n */\npackage cn.iocoder.yudao.module.bpm.convert;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java",
    "content": "package cn.iocoder.yudao.module.bpm.convert.task;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.framework.common.util.date.DateUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceBpmnModelViewRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessPrintDataRespVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;\nimport cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.history.HistoricProcessInstance;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.task.api.Task;\nimport org.flowable.task.api.history.HistoricTaskInstance;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.MappingTarget;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n/**\n * 流程实例 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface BpmProcessInstanceConvert {\n\n    BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);\n\n    default PageResult<BpmProcessInstanceRespVO> buildProcessInstancePage(PageResult<HistoricProcessInstance> pageResult,\n                                                                          Map<String, ProcessDefinition> processDefinitionMap,\n                                                                          Map<String, BpmCategoryDO> categoryMap,\n                                                                          Map<String, List<Task>> taskMap,\n                                                                          Map<Long, AdminUserRespDTO> userMap,\n                                                                          Map<Long, DeptRespDTO> deptMap,\n                                                                          Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {\n        PageResult<BpmProcessInstanceRespVO> vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceRespVO.class);\n        for (int i = 0; i < pageResult.getList().size(); i++) {\n            BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i);\n            respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i)));\n            MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(),\n                    processDefinition -> respVO.setCategory(processDefinition.getCategory())\n                            .setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)));\n            MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName()));\n            respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceRespVO.Task.class));\n            // user\n            if (userMap != null) {\n                AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId()));\n                if (startUser != null) {\n                    respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));\n                    MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName()));\n                }\n                if (CollUtil.isNotEmpty(respVO.getTasks())) {\n                    respVO.getTasks().forEach(task -> {\n                        AdminUserRespDTO assigneeUser = userMap.get(task.getAssignee());\n                        if (assigneeUser!= null) {\n                            task.setAssigneeUser(BeanUtils.toBean(assigneeUser, UserSimpleBaseVO.class));\n                            MapUtils.findAndThen(deptMap, assigneeUser.getDeptId(), dept -> task.getAssigneeUser().setDeptName(dept.getName()));\n                        }\n                    });\n                }\n            }\n            // 摘要\n            respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()),\n                    pageResult.getList().get(i).getProcessVariables()));\n            // 表单\n            respVO.setFormVariables(pageResult.getList().get(i).getProcessVariables());\n        }\n        return vpPageResult;\n    }\n\n    default BpmProcessInstanceRespVO buildProcessInstance(HistoricProcessInstance processInstance,\n                                                          ProcessDefinition processDefinition,\n                                                          BpmProcessDefinitionInfoDO processDefinitionInfo,\n                                                          AdminUserRespDTO startUser,\n                                                          DeptRespDTO dept) {\n        BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);\n        respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance))\n                .setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));\n        // definition\n        respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));\n        copyTo(processDefinitionInfo, respVO.getProcessDefinition());\n        // user\n        if (startUser != null) {\n            respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));\n            if (dept != null) {\n                respVO.getStartUser().setDeptName(dept.getName());\n            }\n        }\n        return respVO;\n    }\n\n    @Mapping(source = \"from.id\", target = \"to.id\", ignore = true)\n    void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);\n\n    default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance,\n                                                                          Integer status, String reason) {\n        return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status).setReason(reason)\n                .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());\n    }\n\n    default BpmMessageSendWhenProcessInstanceApproveReqDTO buildProcessInstanceApproveMessage(ProcessInstance instance) {\n        return new BpmMessageSendWhenProcessInstanceApproveReqDTO()\n                .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()))\n                .setProcessInstanceId(instance.getId())\n                .setProcessInstanceName(instance.getName());\n    }\n\n    default BpmMessageSendWhenProcessInstanceRejectReqDTO buildProcessInstanceRejectMessage(ProcessInstance instance, String reason) {\n        return new BpmMessageSendWhenProcessInstanceRejectReqDTO()\n                .setProcessInstanceName(instance.getName())\n                .setProcessInstanceId(instance.getId())\n                .setReason(reason)\n                .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));\n    }\n\n    default BpmProcessInstanceBpmnModelViewRespVO buildProcessInstanceBpmnModelView(HistoricProcessInstance processInstance,\n                                                                                    List<HistoricTaskInstance> taskInstances,\n                                                                                    BpmnModel bpmnModel,\n                                                                                    BpmSimpleModelNodeVO simpleModel,\n                                                                                    Set<String> unfinishedTaskActivityIds,\n                                                                                    Set<String> finishedTaskActivityIds,\n                                                                                    Set<String> finishedSequenceFlowActivityIds,\n                                                                                    Set<String> rejectTaskActivityIds,\n                                                                                    Map<Long, AdminUserRespDTO> userMap,\n                                                                                    Map<Long, DeptRespDTO> deptMap) {\n        BpmProcessInstanceBpmnModelViewRespVO respVO = new BpmProcessInstanceBpmnModelViewRespVO();\n        // 基本信息\n        respVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class, o -> o\n                        .setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)))\n                .setStartUser(buildUser(processInstance.getStartUserId(), userMap, deptMap)));\n        respVO.setTasks(convertList(taskInstances, task -> BeanUtils.toBean(task, BpmTaskRespVO.class)\n                .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))\n                .setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap))\n                .setOwnerUser(buildUser(task.getOwner(), userMap, deptMap))));\n        respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));\n        respVO.setSimpleModel(simpleModel);\n        // 进度信息\n        respVO.setUnfinishedTaskActivityIds(unfinishedTaskActivityIds)\n                .setFinishedTaskActivityIds(finishedTaskActivityIds)\n                .setFinishedSequenceFlowActivityIds(finishedSequenceFlowActivityIds)\n                .setRejectedTaskActivityIds(rejectTaskActivityIds);\n        return respVO;\n    }\n\n    default UserSimpleBaseVO buildUser(String userIdStr,\n                                       Map<Long, AdminUserRespDTO> userMap,\n                                       Map<Long, DeptRespDTO> deptMap) {\n        if (StrUtil.isEmpty(userIdStr)) {\n            return null;\n        }\n        Long userId = NumberUtils.parseLong(userIdStr);\n        return buildUser(userId, userMap, deptMap);\n    }\n\n    default UserSimpleBaseVO buildUser(Long userId,\n                                       Map<Long, AdminUserRespDTO> userMap,\n                                       Map<Long, DeptRespDTO> deptMap) {\n        if (userId == null) {\n            return null;\n        }\n        AdminUserRespDTO user = userMap.get(userId);\n        if (user == null) {\n            return null;\n        }\n        UserSimpleBaseVO userVO = BeanUtils.toBean(user, UserSimpleBaseVO.class);\n        DeptRespDTO dept = user.getDeptId() != null ? deptMap.get(user.getDeptId()) : null;\n        if (dept != null) {\n            userVO.setDeptName(dept.getName());\n        }\n        return userVO;\n    }\n\n    default BpmApprovalDetailRespVO.ActivityNodeTask buildApprovalTaskInfo(HistoricTaskInstance task) {\n        if (task == null) {\n            return null;\n        }\n        return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class)\n                .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))\n                .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task));\n    }\n\n    default Set<Long> parseUserIds(HistoricProcessInstance processInstance,\n                                   List<BpmApprovalDetailRespVO.ActivityNode> activityNodes,\n                                   BpmTaskRespVO todoTask) {\n        Set<Long> userIds = new HashSet<>();\n        if (processInstance != null) {\n            userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));\n        }\n        for (BpmApprovalDetailRespVO.ActivityNode activityNode : activityNodes) {\n            CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getAssignee));\n            CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getOwner));\n            CollUtil.addAll(userIds, activityNode.getCandidateUserIds());\n        }\n        if (todoTask != null) {\n            CollUtil.addIfAbsent(userIds, todoTask.getAssignee());\n            CollUtil.addIfAbsent(userIds, todoTask.getOwner());\n            if (CollUtil.isNotEmpty(todoTask.getChildren())) {\n                CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getAssignee));\n                CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getOwner));\n            }\n        }\n        return userIds;\n    }\n\n    default Set<Long> parseUserIds02(HistoricProcessInstance processInstance,\n                                     List<HistoricTaskInstance> tasks) {\n        Set<Long> userIds = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));\n        tasks.forEach(task -> {\n            CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getAssignee())));\n            CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getOwner())));\n        });\n        return userIds;\n    }\n\n    default BpmApprovalDetailRespVO buildApprovalDetail(BpmnModel bpmnModel,\n                                                        ProcessDefinition processDefinition,\n                                                        BpmProcessDefinitionInfoDO processDefinitionInfo,\n                                                        HistoricProcessInstance processInstance,\n                                                        Integer processInstanceStatus,\n                                                        List<BpmApprovalDetailRespVO.ActivityNode> activityNodes,\n                                                        BpmTaskRespVO todoTask,\n                                                        Map<String, String> formFieldsPermission,\n                                                        Map<Long, AdminUserRespDTO> userMap,\n                                                        Map<Long, DeptRespDTO> deptMap) {\n        // 1.1 流程实例\n        BpmProcessInstanceRespVO processInstanceResp = null;\n        if (processInstance != null) {\n            AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));\n            DeptRespDTO dept = startUser != null ? deptMap.get(startUser.getDeptId()) : null;\n            processInstanceResp = buildProcessInstance(processInstance, null, null, startUser, dept);\n        }\n\n        // 1.2 流程定义\n        BpmProcessDefinitionRespVO definitionResp = BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(\n                processDefinition, null, processDefinitionInfo, null, null, bpmnModel);\n\n        // 1.3 流程节点\n        activityNodes.forEach(approveNode -> {\n            if (approveNode.getTasks() != null) {\n                approveNode.getTasks().forEach(task -> {\n                    task.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap));\n                    task.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap));\n                });\n            }\n            approveNode.setCandidateUsers(convertList(approveNode.getCandidateUserIds(), userId -> buildUser(userId, userMap, deptMap)));\n        });\n\n        // 1.4 待办任务\n        if (todoTask != null) {\n            todoTask.setAssigneeUser(buildUser(todoTask.getAssignee(), userMap, deptMap));\n            todoTask.setOwnerUser(buildUser(todoTask.getOwner(), userMap, deptMap));\n            if (CollUtil.isNotEmpty(todoTask.getChildren())) {\n                todoTask.getChildren().forEach(childTask -> {\n                    childTask.setAssigneeUser(buildUser(childTask.getAssignee(), userMap, deptMap));\n                    childTask.setOwnerUser(buildUser(childTask.getOwner(), userMap, deptMap));\n                });\n            }\n        }\n\n        // 2. 拼接起来\n        return new BpmApprovalDetailRespVO().setStatus(processInstanceStatus)\n                .setProcessDefinition(definitionResp)\n                .setProcessInstance(processInstanceResp)\n                .setFormFieldsPermission(formFieldsPermission)\n                .setTodoTask(todoTask)\n                .setActivityNodes(activityNodes);\n    }\n\n    default BpmProcessPrintDataRespVO buildProcessInstancePrintData(HistoricProcessInstance historicProcessInstance,\n                                                                    BpmProcessDefinitionInfoDO processDefinitionInfo,\n                                                                    List<HistoricTaskInstance> tasks,\n                                                                    Map<Long, AdminUserRespDTO> userMap,\n                                                                    UserSimpleBaseVO startUser) {\n        BpmModelMetaInfoVO.PrintTemplateSetting printTemplateSetting = processDefinitionInfo.getPrintTemplateSetting();\n        BpmProcessPrintDataRespVO printData = new BpmProcessPrintDataRespVO();\n        // 打印模板是否开启\n        printData.setPrintTemplateEnable(printTemplateSetting != null && Boolean.TRUE.equals(printTemplateSetting.getEnable()));\n        // 流程相关数据\n        BpmProcessInstanceRespVO processInstance = new BpmProcessInstanceRespVO()\n                .setId(historicProcessInstance.getId()).setName(historicProcessInstance.getName())\n                .setBusinessKey(historicProcessInstance.getBusinessKey())\n                .setStartTime(DateUtils.of(historicProcessInstance.getStartTime()))\n                .setEndTime(DateUtils.of(historicProcessInstance.getEndTime()))\n                .setStartUser(startUser).setStatus(FlowableUtils.getProcessInstanceStatus(historicProcessInstance))\n                .setFormVariables(historicProcessInstance.getProcessVariables())\n                .setProcessDefinition(BeanUtils.toBean(processDefinitionInfo, BpmProcessDefinitionRespVO.class));\n        printData.setProcessInstance(processInstance);\n        // 审批历史\n        List<BpmProcessPrintDataRespVO.Task> approveTasks = new ArrayList<>(tasks.size());\n        tasks.forEach(item -> {\n            Map<String, Object> taskLocalVariables = item.getTaskLocalVariables();\n            BpmProcessPrintDataRespVO.Task approveTask = new BpmProcessPrintDataRespVO.Task();\n            approveTask.setName(item.getName());\n            approveTask.setId(item.getId());\n            approveTask.setSignPicUrl((String) taskLocalVariables.get(BpmnVariableConstants.TASK_SIGN_PIC_URL));\n            approveTask.setDescription(StrUtil.format(\"{} / {} / {} / {} / {}\",\n                    userMap.get(Long.valueOf(item.getAssignee())).getNickname(),\n                    item.getName(),\n                    DateUtil.formatDateTime(item.getEndTime()),\n                    BpmTaskStatusEnum.valueOf((Integer) taskLocalVariables.get(BpmnVariableConstants.TASK_VARIABLE_STATUS)).getName(),\n                    taskLocalVariables.get(BpmnVariableConstants.TASK_VARIABLE_REASON)));\n            approveTasks.add(approveTask);\n        });\n        printData.setTasks(approveTasks);\n        // 自定义模板\n        if (printData.getPrintTemplateEnable() && printTemplateSetting != null) {\n            printData.setPrintTemplateHtml(printTemplateSetting.getTemplate());\n        }\n        return printData;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java",
    "content": "package cn.iocoder.yudao.module.bpm.convert.task;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.date.DateUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.flowable.engine.history.HistoricProcessInstance;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.task.api.Task;\nimport org.flowable.task.api.history.HistoricTaskInstance;\nimport org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\n\n/**\n * Bpm 任务 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface BpmTaskConvert {\n\n    BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);\n\n    default PageResult<BpmTaskRespVO> buildTodoTaskPage(PageResult<Task> pageResult,\n                                                        Map<String, ProcessInstance> processInstanceMap,\n                                                        Map<Long, AdminUserRespDTO> userMap,\n                                                        Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {\n        return BeanUtils.toBean(pageResult, BpmTaskRespVO.class, taskVO -> {\n            ProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId());\n            if (processInstance == null) {\n                return;\n            }\n            taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));\n            AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));\n            taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));\n            taskVO.getProcessInstance().setCreateTime(DateUtils.of(processInstance.getStartTime()));\n            // 摘要\n            taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),\n                    processInstance.getProcessVariables()));\n        });\n    }\n\n    default PageResult<BpmTaskRespVO> buildTaskPage(PageResult<HistoricTaskInstance> pageResult,\n                                                    Map<String, HistoricProcessInstance> processInstanceMap,\n                                                    Map<Long, AdminUserRespDTO> userMap,\n                                                    Map<Long, DeptRespDTO> deptMap,\n                                                    Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {\n        List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> {\n            BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);\n            taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));\n            // 用户信息\n            AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));\n            if (assignUser != null) {\n                taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));\n                findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName()));\n            }\n            // 流程实例\n            HistoricProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId());\n            if (processInstance != null) {\n                AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));\n                taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));\n                taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));\n                // 摘要\n                taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),\n                        processInstance.getProcessVariables()));\n            }\n            return taskVO;\n        });\n        return new PageResult<>(taskVOList, pageResult.getTotal());\n    }\n\n    default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList,\n                                                                 Map<Long, BpmFormDO> formMap,\n                                                                 Map<Long, AdminUserRespDTO> userMap,\n                                                                 Map<Long, DeptRespDTO> deptMap) {\n        return CollectionUtils.convertList(taskList, task -> {\n            // 特殊：已取消的任务，不返回\n            BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);\n            Integer taskStatus = FlowableUtils.getTaskStatus(task);\n            if (BpmTaskStatusEnum.isCancelStatus(taskStatus)) {\n                return null;\n            }\n            taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task));\n            // 表单信息\n            BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class);\n            if (form != null) {\n                taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf())\n                        .setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task));\n            }\n            // 用户信息\n            buildTaskAssignee(taskVO, task.getAssignee(), userMap, deptMap);\n            buildTaskOwner(taskVO, task.getOwner(), userMap, deptMap);\n            return taskVO;\n        });\n    }\n\n    default List<BpmTaskRespVO> buildTaskListByParentTaskId(List<Task> taskList,\n                                                            Map<Long, AdminUserRespDTO> userMap,\n                                                            Map<Long, DeptRespDTO> deptMap) {\n        return convertList(taskList, task -> BeanUtils.toBean(task, BpmTaskRespVO.class, taskVO -> {\n            AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));\n            if (assignUser != null) {\n                taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));\n                DeptRespDTO dept = deptMap.get(assignUser.getDeptId());\n                if (dept != null) {\n                    taskVO.getAssigneeUser().setDeptName(dept.getName());\n                }\n            }\n            AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner()));\n            if (ownerUser != null) {\n                taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class));\n                findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName()));\n            }\n        }));\n    }\n\n    default BpmTaskRespVO buildTodoTask(Task todoTask, List<Task> childrenTasks,\n                                        Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting,\n                                        BpmFormDO form) {\n        BpmTaskRespVO bpmTaskRespVO = BeanUtils.toBean(todoTask, BpmTaskRespVO.class)\n                .setStatus(FlowableUtils.getTaskStatus(todoTask)).setReason(FlowableUtils.getTaskReason(todoTask))\n                .setButtonsSetting(buttonsSetting)\n                .setChildren(convertList(childrenTasks, childTask -> BeanUtils.toBean(childTask, BpmTaskRespVO.class)\n                        .setStatus(FlowableUtils.getTaskStatus(childTask))));\n        if (form != null) {\n            bpmTaskRespVO.setFormId(form.getId()).setFormName(form.getName())\n                    .setFormConf(form.getConf()).setFormFields(form.getFields());\n        }\n        return bpmTaskRespVO;\n    }\n\n    default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser,\n                                                        Task task) {\n        BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();\n        reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId())\n                .setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId())\n                .setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName())\n                .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));\n        return reqDTO;\n    }\n\n    default void buildTaskOwner(BpmTaskRespVO task, String taskOwner,\n                                Map<Long, AdminUserRespDTO> userMap,\n                                Map<Long, DeptRespDTO> deptMap) {\n        AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(taskOwner));\n        if (ownerUser != null) {\n            task.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class));\n            findAndThen(deptMap, ownerUser.getDeptId(), dept -> task.getOwnerUser().setDeptName(dept.getName()));\n        }\n    }\n\n    default void buildTaskChildren(BpmTaskRespVO task, Map<String, List<Task>> childrenTaskMap,\n                                   Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) {\n        List<Task> childTasks = childrenTaskMap.get(task.getId());\n        if (CollUtil.isNotEmpty(childTasks)) {\n            task.setChildren(\n                    convertList(childTasks, childTask -> {\n                        BpmTaskRespVO childTaskVO = BeanUtils.toBean(childTask, BpmTaskRespVO.class);\n                        childTaskVO.setStatus(FlowableUtils.getTaskStatus(childTask));\n                        buildTaskOwner(childTaskVO, childTask.getOwner(), userMap, deptMap);\n                        buildTaskAssignee(childTaskVO, childTask.getAssignee(), userMap, deptMap);\n                        return childTaskVO;\n                    })\n            );\n        }\n    }\n\n    default void buildTaskAssignee(BpmTaskRespVO task, String taskAssignee,\n                                   Map<Long, AdminUserRespDTO> userMap,\n                                   Map<Long, DeptRespDTO> deptMap) {\n        AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(taskAssignee));\n        if (assignUser != null) {\n            task.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));\n            findAndThen(deptMap, assignUser.getDeptId(), dept -> task.getAssigneeUser().setDeptName(dept.getName()));\n        }\n    }\n\n    /**\n     * 将父任务的属性，拷贝到子任务（加签任务）\n     * <p>\n     * 为什么不使用 mapstruct 映射？因为 TaskEntityImpl 还有很多其他属性，这里我们只设置我们需要的。\n     * 使用 mapstruct 会将里面嵌套的各个属性值都设置进去，会出现意想不到的问题。\n     *\n     * @param parentTask 父任务\n     * @param childTask  加签任务\n     */\n    default void copyTo(TaskEntityImpl parentTask, TaskEntityImpl childTask) {\n        childTask.setName(parentTask.getName());\n        childTask.setDescription(parentTask.getDescription());\n        childTask.setCategory(parentTask.getCategory());\n        childTask.setParentTaskId(parentTask.getId());\n        childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId());\n        childTask.setProcessInstanceId(parentTask.getProcessInstanceId());\n        childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());\n        childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId());\n        childTask.setPriority(parentTask.getPriority());\n        childTask.setCreateTime(new Date());\n        childTask.setTenantId(parentTask.getTenantId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmCategoryDO.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * BPM 流程分类 DO\n *\n * @author 芋道源码\n */\n@TableName(\"bpm_category\")\n@KeySequence(\"bpm_category_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BpmCategoryDO extends BaseDO {\n\n    /**\n     * 分类编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 分类名\n     */\n    private String name;\n    /**\n     * 分类标志\n     */\n    private String code;\n    /**\n     * 分类描述\n     */\n    private String description;\n    /**\n     * 分类状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 分类排序\n     */\n    private Integer sort;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmFormDO.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n/**\n * BPM 工作流的表单定义\n * 用于工作流的申请表单，需要动态配置的场景\n *\n * @author 芋道源码\n */\n@TableName(value = \"bpm_form\", autoResultMap = true)\n@KeySequence(\"bpm_form_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BpmFormDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 表单名\n     */\n    private String name;\n    /**\n     * 状态\n     */\n    private Integer status;\n    /**\n     * 表单的配置\n     */\n    private String conf;\n    /**\n     * 表单项的数组\n     *\n     * 目前直接将 https://github.com/JakHuang/form-generator 生成的 JSON 串，直接保存\n     * 定义：https://github.com/JakHuang/form-generator/issues/46\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<String> fields;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.flowable.engine.repository.Model;\nimport org.flowable.engine.repository.ProcessDefinition;\n\nimport java.util.List;\n\n/**\n * BPM 流程定义的拓信息\n * 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段，所以新建该表\n *\n * @author 芋道源码\n */\n@TableName(value = \"bpm_process_definition_info\", autoResultMap = true)\n@KeySequence(\"bpm_process_definition_info_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BpmProcessDefinitionInfoDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 流程定义的编号\n     *\n     * 关联 {@link ProcessDefinition#getId()} 属性\n     */\n    private String processDefinitionId;\n    /**\n     * 流程模型的编号\n     *\n     * 关联 {@link Model#getId()} 属性\n     */\n    private String modelId;\n    /**\n     * 流程模型的类型\n     *\n     * 枚举 {@link BpmModelTypeEnum}\n     */\n    private Integer modelType;\n\n    /**\n     * 流程分类的编码\n     *\n     * 关联 {@link BpmCategoryDO#getCode()}\n     *\n     * 为什么要存储？原因是，{@link ProcessDefinition#getCategory()} 无法设置\n     */\n    private String category;\n    /**\n     * 图标\n     */\n    private String icon;\n    /**\n     * 描述\n     */\n    private String description;\n\n    /**\n     * 表单类型\n     *\n     * 枚举 {@link BpmModelFormTypeEnum}\n     */\n    private Integer formType;\n    /**\n     * 动态表单编号\n     *\n     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时\n     *\n     * 关联 {@link BpmFormDO#getId()}\n     */\n    private Long formId;\n    /**\n     * 表单的配置\n     *\n     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时\n     *\n     * 冗余 {@link BpmFormDO#getConf()}\n     */\n    private String formConf;\n    /**\n     * 表单项的数组\n     *\n     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时\n     *\n     * 冗余 {@link BpmFormDO#getFields()}\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<String> formFields;\n    /**\n     * 自定义表单的提交路径，使用 Vue 的路由地址\n     *\n     * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时\n     */\n    private String formCustomCreatePath;\n    /**\n     * 自定义表单的查看路径，使用 Vue 的路由地址\n     *\n     * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时\n     */\n    private String formCustomViewPath;\n\n    /**\n     * SIMPLE 设计器模型数据 json 格式\n     *\n     * 目的：当使用仿钉钉设计器时。流程模型发布的时候，需要保存流程模型设计器的快照数据。\n     */\n    private String simpleModel;\n    /**\n     * 是否可见\n     *\n     * 目的：如果 false 不可见，则不展示在“发起流程”的列表里\n     */\n    private Boolean visible;\n    /**\n     * 排序值\n     */\n    private Long sort;\n\n    /**\n     * 可发起用户编号数组\n     *\n     * 关联 {@link AdminUserRespDTO#getId()} 字段的数组\n     *\n     * 如果为空，则表示“全部可以发起”！\n     *\n     * 它和 {@link #visible} 的区别在于：\n     * 1. {@link #visible} 只是决定是否可见。即使不可见，还是可以发起\n     * 2. startUserIds 决定某个用户是否可以发起。如果该用户不可发起，则他也是不可见的\n     */\n    @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤\n    private List<Long> startUserIds;\n\n    /**\n     * 可发起部门编号数组\n     *\n     * 关联 {@link AdminUserRespDTO#getDeptId()} 字段的数组\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> startDeptIds;\n\n    /**\n     * 可管理用户编号数组\n     *\n     * 关联 {@link AdminUserRespDTO#getId()} 字段的数组\n     */\n    @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤\n    private List<Long> managerUserIds;\n\n    /**\n     * 是否允许撤销审批中的申请\n     */\n    private Boolean allowCancelRunningProcess;\n\n    /**\n     * 是否允许审批人撤回任务\n     */\n    private Boolean allowWithdrawTask;\n\n    /**\n     * 流程 ID 规则\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private BpmModelMetaInfoVO.ProcessIdRule processIdRule;\n\n    /**\n     * 自动去重类型\n     *\n     * 枚举 {@link BpmAutoApproveTypeEnum}\n     */\n    private Integer autoApprovalType;\n\n    /**\n     * 标题设置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private BpmModelMetaInfoVO.TitleSetting titleSetting;\n    /**\n     * 摘要设置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private BpmModelMetaInfoVO.SummarySetting summarySetting;\n\n    /**\n     * 流程前置通知设置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private BpmModelMetaInfoVO.HttpRequestSetting processBeforeTriggerSetting;\n    /**\n     * 流程后置通知设置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private BpmModelMetaInfoVO.HttpRequestSetting processAfterTriggerSetting;\n\n    /**\n     * 任务前置通知设置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private BpmModelMetaInfoVO.HttpRequestSetting taskBeforeTriggerSetting;\n\n    /**\n     * 任务后置通知设置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private BpmModelMetaInfoVO.HttpRequestSetting taskAfterTriggerSetting;\n\n    /**\n     * 自定义打印模板设置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private BpmModelMetaInfoVO.PrintTemplateSetting printTemplateSetting;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * BPM 流程表达式 DO\n *\n * @author 芋道源码\n */\n@TableName(\"bpm_process_expression\")\n@KeySequence(\"bpm_process_expression_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BpmProcessExpressionDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 表达式名字\n     */\n    private String name;\n    /**\n     * 表达式状态\n     *\n     * 枚举 {@link TODO common_status 对应的类}\n     */\n    private Integer status;\n    /**\n     * 表达式\n     */\n    private String expression;\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * BPM 流程监听器 DO\n *\n * 目的：本质上它是流程监听器的模版，用于 BPMN 在设计时，直接选择这些模版\n *\n * @author 芋道源码\n */\n@TableName(value = \"bpm_process_listener\")\n@KeySequence(\"bpm_process_listener_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BpmProcessListenerDO extends BaseDO {\n\n    /**\n     * 主键 ID，自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * 监听器名字\n     */\n    private String name;\n    /**\n     * 状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 监听类型\n     *\n     * 枚举 {@link BpmProcessListenerTypeEnum}\n     *\n     * 1. execution：ExecutionListener <a href=\"https://tkjohn.github.io/flowable-userguide/#executionListeners\">执行监听器</a>\n     * 2. task：TaskListener <a href=\"https://tkjohn.github.io/flowable-userguide/#taskListeners\">任务监听器</a>\n     */\n    private String type;\n    /**\n     * 监听事件\n     *\n     * execution 时：start、end\n     * task 时：create 创建、assignment 指派、complete 完成、delete 删除、update 更新、timeout 超时\n     */\n    private String event;\n\n    /**\n     * 值类型\n     *\n     * 1. class：Java 类，ExecutionListener 需要 {@link org.flowable.engine.delegate.JavaDelegate}，TaskListener 需要 {@link org.flowable.engine.delegate.TaskListener}\n     * 2. delegateExpression：委托表达式，在 class 的基础上，需要注册到 Spring 容器里，后续表达式通过 Spring Bean 名称即可\n     * 3. expression：表达式，一个普通类的普通方法，将这个普通类注册到 Spring 容器中，然后表达式中还可以执行这个类中的方法\n     */\n    private String valueType;\n    /**\n     * 值\n     */\n    private String value;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Set;\n\n/**\n * BPM 用户组\n *\n * @author 芋道源码\n */\n@TableName(value = \"bpm_user_group\", autoResultMap = true)\n@KeySequence(\"bpm_user_group_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BpmUserGroupDO extends BaseDO {\n\n    /**\n     * 编号，自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * 组名\n     */\n    private String name;\n    /**\n     * 描述\n     */\n    private String description;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 成员用户编号数组\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private Set<Long> userIds;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.dataobject.oa;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.time.LocalDateTime;\n\n/**\n * OA 请假申请 DO\n *\n * {@link #day} 请假天数，目前先简单做。一般是分成请假上午和下午，可以是 1 整天，可以是 0.5 半天\n *\n * @author jason\n * @author 芋道源码\n */\n@TableName(\"bpm_oa_leave\")\n@KeySequence(\"bpm_oa_leave_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BpmOALeaveDO extends BaseDO {\n\n    /**\n     * 请假表单主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 申请人的用户编号\n     *\n     * 关联 AdminUserDO 的 id 属性\n     */\n    private Long userId;\n    /**\n     * 请假类型\n     */\n    private String type;\n    /**\n     * 原因\n     */\n    private String reason;\n    /**\n     * 开始时间\n     */\n    private LocalDateTime startTime;\n    /**\n     * 结束时间\n     */\n    private LocalDateTime endTime;\n    /**\n     * 请假天数\n     */\n    private Long day;\n    /**\n     * 审批结果\n     *\n     * 枚举 {@link BpmTaskStatusEnum}\n     * 考虑到简单，所以直接复用了 BpmProcessInstanceStatusEnum 枚举，也可以自己定义一个枚举哈\n     */\n    private Integer status;\n\n    /**\n     * 对应的流程编号\n     *\n     * 关联 ProcessInstance 的 id 属性\n     */\n    private String processInstanceId;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.dataobject.task;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport org.flowable.bpmn.model.FlowNode;\nimport org.flowable.task.api.history.HistoricTaskInstance;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * 流程抄送 DO\n *\n * @author kyle\n * @since 2024-01-22\n */\n@TableName(value = \"bpm_process_instance_copy\", autoResultMap = true)\n@KeySequence(\"bpm_process_instance_copy_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BpmProcessInstanceCopyDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 发起人 Id\n     *\n     * 冗余 ProcessInstance 的 startUserId 字段\n     */\n    private Long startUserId;\n    /**\n     * 流程名\n     *\n     * 冗余 ProcessInstance 的 name 字段\n     */\n    private String processInstanceName;\n    /**\n     * 流程实例的编号\n     *\n     * 关联 ProcessInstance 的 id 属性\n     */\n    private String processInstanceId;\n    /**\n     * 流程实例的流程定义编号\n     *\n     * 关联 ProcessInstance 的 processDefinitionId 属性\n     */\n    private String processDefinitionId;\n    /**\n     * 流程分类\n     *\n     * 冗余 ProcessInstance 的 category 字段\n     */\n    private String category;\n    /**\n     * 流程活动的编号\n     * <p/>\n     *\n     * 冗余 {@link FlowNode#getId()}，对应 BPMN XML 节点编号\n     * 原因：用于查询抄送节点的表单字段权限。因为仿钉钉/飞书的抄送节点 (ServiceTask)，没有 taskId，只有 activityId\n     */\n    private String activityId;\n    /**\n     * 流程活动的名字\n     *\n     * 冗余 {@link FlowNode#getName()}\n     */\n    private String activityName;\n    /**\n     * 流程活动的编号\n     *\n     * 关联 {@link HistoricTaskInstance#getId()}\n     */\n    private String taskId;\n\n    /**\n     * 用户编号（被抄送的用户编号）\n     *\n     * 关联 system_users 的 id 属性\n     */\n    private Long userId;\n\n    /**\n     * 抄送意见\n     */\n    private String reason;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/category/BpmCategoryMapper.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.mysql.category;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * BPM 流程分类 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface BpmCategoryMapper extends BaseMapperX<BpmCategoryDO> {\n\n    default PageResult<BpmCategoryDO> selectPage(BpmCategoryPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BpmCategoryDO>()\n                .likeIfPresent(BpmCategoryDO::getName, reqVO.getName())\n                .likeIfPresent(BpmCategoryDO::getCode, reqVO.getCode())\n                .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime())\n                .orderByAsc(BpmCategoryDO::getSort));\n    }\n\n    default BpmCategoryDO selectByName(String name) {\n        return selectOne(BpmCategoryDO::getName, name);\n    }\n\n    default BpmCategoryDO selectByCode(String code) {\n        return selectOne(BpmCategoryDO::getCode, code);\n    }\n\n    default List<BpmCategoryDO> selectListByCode(Collection<String> codes) {\n        return selectList(BpmCategoryDO::getCode, codes);\n    }\n\n    default List<BpmCategoryDO> selectListByStatus(Integer status) {\n        return selectList(BpmCategoryDO::getStatus, status);\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/definition/BpmFormMapper.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.mysql.definition;\n\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 动态表单 Mapper\n *\n * @author 风里雾里\n */\n@Mapper\npublic interface BpmFormMapper extends BaseMapperX<BpmFormDO> {\n\n    default PageResult<BpmFormDO> selectPage(BpmFormPageReqVO reqVO) {\n        return selectPage(reqVO, new QueryWrapperX<BpmFormDO>()\n                .likeIfPresent(\"name\", reqVO.getName())\n                .orderByDesc(\"id\"));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.mysql.definition;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n@Mapper\npublic interface BpmProcessDefinitionInfoMapper extends BaseMapperX<BpmProcessDefinitionInfoDO> {\n\n    default List<BpmProcessDefinitionInfoDO> selectListByProcessDefinitionIds(Collection<String> processDefinitionIds) {\n        return selectList(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionIds);\n    }\n\n    default BpmProcessDefinitionInfoDO selectByProcessDefinitionId(String processDefinitionId) {\n        return selectOne(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionId);\n    }\n\n    default void updateByModelId(String modelId, BpmProcessDefinitionInfoDO updateObj) {\n        update(updateObj,\n                new LambdaQueryWrapperX<BpmProcessDefinitionInfoDO>().eq(BpmProcessDefinitionInfoDO::getModelId, modelId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.mysql.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * BPM 流程表达式 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface BpmProcessExpressionMapper extends BaseMapperX<BpmProcessExpressionDO> {\n\n    default PageResult<BpmProcessExpressionDO> selectPage(BpmProcessExpressionPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BpmProcessExpressionDO>()\n                .likeIfPresent(BpmProcessExpressionDO::getName, reqVO.getName())\n                .eqIfPresent(BpmProcessExpressionDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(BpmProcessExpressionDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(BpmProcessExpressionDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.mysql.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessListenerDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * BPM 流程监听器 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface BpmProcessListenerMapper extends BaseMapperX<BpmProcessListenerDO> {\n\n    default PageResult<BpmProcessListenerDO> selectPage(BpmProcessListenerPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BpmProcessListenerDO>()\n                .likeIfPresent(BpmProcessListenerDO::getName, reqVO.getName())\n                .eqIfPresent(BpmProcessListenerDO::getType, reqVO.getType())\n                .eqIfPresent(BpmProcessListenerDO::getEvent, reqVO.getEvent())\n                .eqIfPresent(BpmProcessListenerDO::getStatus, reqVO.getStatus())\n                .orderByDesc(BpmProcessListenerDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.mysql.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 用户组 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface BpmUserGroupMapper extends BaseMapperX<BpmUserGroupDO> {\n\n    default PageResult<BpmUserGroupDO> selectPage(BpmUserGroupPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BpmUserGroupDO>()\n                .likeIfPresent(BpmUserGroupDO::getName, reqVO.getName())\n                .eqIfPresent(BpmUserGroupDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(BpmUserGroupDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(BpmUserGroupDO::getId));\n    }\n\n    default List<BpmUserGroupDO> selectListByStatus(Integer status) {\n        return selectList(BpmUserGroupDO::getStatus, status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.mysql.oa;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 请假申请 Mapper\n *\n * @author jason\n * @author 芋道源码\n */\n@Mapper\npublic interface BpmOALeaveMapper extends BaseMapperX<BpmOALeaveDO> {\n\n    default PageResult<BpmOALeaveDO> selectPage(Long userId, BpmOALeavePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BpmOALeaveDO>()\n                .eqIfPresent(BpmOALeaveDO::getUserId, userId)\n                .eqIfPresent(BpmOALeaveDO::getStatus, reqVO.getStatus())\n                .eqIfPresent(BpmOALeaveDO::getType, reqVO.getType())\n                .likeIfPresent(BpmOALeaveDO::getReason, reqVO.getReason())\n                .betweenIfPresent(BpmOALeaveDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(BpmOALeaveDO::getId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.mysql.task;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface BpmProcessInstanceCopyMapper extends BaseMapperX<BpmProcessInstanceCopyDO> {\n\n    default PageResult<BpmProcessInstanceCopyDO> selectPage(Long loginUserId, BpmProcessInstanceCopyPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BpmProcessInstanceCopyDO>()\n                .eqIfPresent(BpmProcessInstanceCopyDO::getUserId, loginUserId)\n                .likeIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceName, reqVO.getProcessInstanceName())\n                .betweenIfPresent(BpmProcessInstanceCopyDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(BpmProcessInstanceCopyDO::getId));\n    }\n\n    default void deleteByProcessInstanceId(String processInstanceId) {\n        delete(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/redis/BpmProcessIdRedisDAO.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.redis;\n\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.date.DatePattern.*;\n\n/**\n * BPM 流程 Id 编码的 Redis DAO\n *\n * @author Lesan\n */\n@Repository\npublic class BpmProcessIdRedisDAO {\n\n    @Resource\n    private StringRedisTemplate stringRedisTemplate;\n\n    /**\n     * 生成序号，使用定义的 processIdRule 规则生成\n     *\n     * @param processIdRule 规则\n     * @return 序号\n     */\n    public String generate(BpmModelMetaInfoVO.ProcessIdRule processIdRule) {\n        // 生成日期前缀\n        String infix = \"\";\n        switch (processIdRule.getInfix()) {\n            case \"DAY\":\n                infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN);\n                break;\n            case \"HOUR\":\n                infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + \"HH\");\n                break;\n            case \"MINUTE\":\n                infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + \"HHmm\");\n                break;\n            case \"SECOND\":\n                infix = DateUtil.format(LocalDateTime.now(), PURE_DATETIME_PATTERN);\n                break;\n        }\n\n        // 生成序号\n        String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix();\n        String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix;\n        Long no = stringRedisTemplate.opsForValue().increment(key);\n        if (StrUtil.isEmpty(infix)) {\n            // 特殊：没有前缀，则不能过期，不能每次都是从 0 开始。可见 https://t.zsxq.com/MU1E2 讨论\n            stringRedisTemplate.expire(key, Duration.ofDays(1L));\n        }\n        return noPrefix + String.format(\"%0\" + processIdRule.getLength() + \"d\", no);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/dal/redis/RedisKeyConstants.java",
    "content": "package cn.iocoder.yudao.module.bpm.dal.redis;\n\n/**\n * BPM Redis Key 枚举类\n *\n * @author 芋道源码\n */\npublic interface RedisKeyConstants {\n\n    /**\n     * 流程 ID 的缓存\n     */\n    String BPM_PROCESS_ID = \"bpm:process_id:\";\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.config;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.flowable.common.engine.api.delegate.FlowableFunctionDelegate;\nimport org.flowable.common.engine.api.delegate.event.FlowableEventListener;\nimport org.flowable.spring.SpringProcessEngineConfiguration;\nimport org.flowable.spring.boot.EngineConfigurationConfigurer;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.task.AsyncTaskExecutor;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\n\nimport java.util.List;\n\n/**\n * BPM 模块的 Flowable 配置类\n *\n * @author jason\n */\n@Configuration(proxyBeanMethods = false)\npublic class BpmFlowableConfiguration {\n\n    /**\n     * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类，创建对应的 AsyncListenableTaskExecutor Bean\n     * <p>\n     * 如果不创建，会导致项目启动时，Flowable 报错的问题\n     */\n    @Bean(name = \"applicationTaskExecutor\")\n    @ConditionalOnMissingBean(name = \"applicationTaskExecutor\")\n    public AsyncTaskExecutor taskExecutor() {\n        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n        executor.setCorePoolSize(8);\n        executor.setMaxPoolSize(8);\n        executor.setQueueCapacity(100);\n        executor.setThreadNamePrefix(\"flowable-task-Executor-\");\n        executor.setAwaitTerminationSeconds(30);\n        executor.setWaitForTasksToCompleteOnShutdown(true);\n        executor.setAllowCoreThreadTimeOut(true);\n        executor.initialize();\n        return executor;\n    }\n\n    /**\n     * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类：\n     *\n     * 1. 设置各种监听器\n     * 2. 设置自定义的 ActivityBehaviorFactory 实现\n     */\n    @Bean\n    public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> bpmProcessEngineConfigurationConfigurer(\n            ObjectProvider<FlowableEventListener> listeners,\n            ObjectProvider<FlowableFunctionDelegate> customFlowableFunctionDelegates,\n            BpmActivityBehaviorFactory bpmActivityBehaviorFactory) {\n        return configuration -> {\n            // 注册监听器，例如说 BpmActivityEventListener\n            configuration.setEventListeners(ListUtil.toList(listeners.iterator()));\n            // 设置 ActivityBehaviorFactory 实现类，用于流程任务的审核人的自定义\n            configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory);\n            // 设置自定义的函数\n            configuration.setCustomFlowableFunctionDelegates(ListUtil.toList(customFlowableFunctionDelegates.stream().iterator()));\n        };\n    }\n\n    // =========== 审批人相关的 Bean ==========\n\n    @Bean\n    public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskCandidateInvoker bpmTaskCandidateInvoker) {\n        BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory();\n        bpmActivityBehaviorFactory.setTaskCandidateInvoker(bpmTaskCandidateInvoker);\n        return bpmActivityBehaviorFactory;\n    }\n\n    @Bean\n    @SuppressWarnings(\"SpringJavaInjectionPointsAutowiringInspection\") // adminUserApi 可以注入成功\n    public BpmTaskCandidateInvoker bpmTaskCandidateInvoker(List<BpmTaskCandidateStrategy> strategyList,\n                                                           AdminUserApi adminUserApi) {\n        return new BpmTaskCandidateInvoker(strategyList, adminUserApi);\n    }\n\n    // =========== 自己拓展的 Bean ==========\n\n    @Bean\n    public BpmProcessInstanceEventPublisher processInstanceEventPublisher(ApplicationEventPublisher publisher) {\n        return new BpmProcessInstanceEventPublisher(publisher);\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;\n\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;\nimport lombok.Setter;\nimport org.flowable.bpmn.model.Activity;\nimport org.flowable.bpmn.model.UserTask;\nimport org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;\nimport org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;\nimport org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;\nimport org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;\nimport org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory;\n\n/**\n * 自定义的 ActivityBehaviorFactory 实现类，目的如下：\n * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}：实现自定义的流程任务的 assignee 负责人的分配\n *\n * @author 芋道源码\n */\n@Setter\npublic class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {\n\n    private BpmTaskCandidateInvoker taskCandidateInvoker;\n\n    @Override\n    public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {\n        return new BpmUserTaskActivityBehavior(userTask)\n                .setTaskCandidateInvoker(taskCandidateInvoker);\n    }\n\n    @Override\n    public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity,\n                                                                             AbstractBpmnActivityBehavior behavior) {\n        return new BpmParallelMultiInstanceBehavior(activity, behavior)\n                .setTaskCandidateInvoker(taskCandidateInvoker);\n    }\n\n    @Override\n    public SequentialMultiInstanceBehavior createSequentialMultiInstanceBehavior(Activity activity,\n                                                                                 AbstractBpmnActivityBehavior behavior) {\n        return new BpmSequentialMultiInstanceBehavior(activity, behavior)\n                .setTaskCandidateInvoker(taskCandidateInvoker);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport lombok.Setter;\nimport org.flowable.bpmn.model.Activity;\nimport org.flowable.bpmn.model.CallActivity;\nimport org.flowable.bpmn.model.FlowElement;\nimport org.flowable.bpmn.model.UserTask;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;\nimport org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;\nimport org.flowable.common.engine.api.delegate.Expression;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配\n * 第一步，基于分配规则，计算出分配任务的【多个】候选人们。\n * 第二步，将【多个】任务候选人们，设置到 DelegateExecution 的 collectionVariable 变量中，以便 BpmUserTaskActivityBehavior 使用它\n *\n * @author kemengkai\n * @since 2022-04-21 16:57\n */\n@Setter\npublic class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior {\n\n    private BpmTaskCandidateInvoker taskCandidateInvoker;\n\n    public BpmParallelMultiInstanceBehavior(Activity activity,\n                                            AbstractBpmnActivityBehavior innerActivityBehavior) {\n        super(activity, innerActivityBehavior);\n        // 关联 Pull Request：https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1483\n        // 在解析/构造阶段基于 activityId 初始化与 activity 绑定且不变的字段，避免在运行期修改 Behavior 实例状态\n        super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的\n        super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(activity.getId());\n        // 从 execution.getVariable() 读取当前所有任务处理的人的 key\n        super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(activity.getId());\n    }\n\n    /**\n     * 重写该方法，主要实现两个功能：\n     * 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式，而是采用自己定义的\n     * 2. 获得任务的处理人，并设置到 collectionVariable 中，用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人\n     *\n     * 注意，多个任务实例，每个任务实例对应一个处理人，所以返回的数量就是任务处理人的数量\n     *\n     * @param execution 执行任务\n     * @return 数量\n     */\n    @Override\n    protected int resolveNrOfInstances(DelegateExecution execution) {\n        // 情况一：UserTask 节点\n        if (execution.getCurrentFlowElement() instanceof UserTask) {\n            // 获取任务的所有处理人\n            @SuppressWarnings(\"unchecked\")\n            Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);\n            if (assigneeUserIds == null) {\n                assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);\n                if (CollUtil.isEmpty(assigneeUserIds)) {\n                    // 特殊：如果没有处理人的情况下，至少有一个 null 空元素，避免自动通过！\n                    // 这样，保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务\n                    // 用途：1）审批人为空时；2）审批类型为自动通过、自动拒绝时\n                    assigneeUserIds = SetUtils.asSet((Long) null);\n                }\n                execution.setVariableLocal(super.collectionVariable, assigneeUserIds);\n            }\n            return assigneeUserIds.size();\n        }\n\n        // 情况二：CallActivity 节点\n        if (execution.getCurrentFlowElement() instanceof CallActivity) {\n            FlowElement flowElement = execution.getCurrentFlowElement();\n            Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement);\n            if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) {\n                return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class);\n            }\n            if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) {\n                return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size();\n            }\n        }\n\n        return super.resolveNrOfInstances(execution);\n    }\n\n    // ========== 屏蔽解析器覆写 ==========\n\n    @Override\n    public void setCollectionExpression(Expression collectionExpression) {\n        // 保持自定义变量名，忽略解析器写入的 collection 表达式\n    }\n\n    @Override\n    public void setCollectionVariable(String collectionVariable) {\n        // 保持自定义变量名，忽略解析器写入的 collection 变量名\n    }\n\n    @Override\n    public void setCollectionElementVariable(String collectionElementVariable) {\n        // 保持自定义变量名，忽略解析器写入的单元素变量名\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport lombok.Setter;\nimport org.flowable.bpmn.model.*;\nimport org.flowable.common.engine.api.delegate.Expression;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;\nimport org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;\nimport org.flowable.engine.impl.persistence.entity.ExecutionEntity;\n\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配\n *\n * 本质上，实现和 {@link BpmParallelMultiInstanceBehavior} 一样，只是继承的类不一样\n *\n * @author 芋道源码\n */\n@Setter\npublic class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior {\n\n    private BpmTaskCandidateInvoker taskCandidateInvoker;\n\n    public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {\n        super(activity, innerActivityBehavior);\n        // 关联 Pull Request：https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1483\n        // 在解析/构造阶段基于 activityId 初始化与 activity 绑定且不变的字段，避免在运行期修改 Behavior 实例状态\n        super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的\n        super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(activity.getId());\n        // 从 execution.getVariable() 读取当前所有任务处理的人的 key\n        super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(activity.getId());\n    }\n\n    /**\n     * 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似\n     *\n     * 差异的点：是在【第二步】的时候，需要返回 LinkedHashSet 集合！因为它需要有序！\n     */\n    @Override\n    protected int resolveNrOfInstances(DelegateExecution execution) {\n        // 情况一：UserTask 节点\n        if (execution.getCurrentFlowElement() instanceof UserTask) {\n            // 获取任务的所有处理人\n            // 不使用 execution.getVariable 原因：目前依次审批任务回退后 collectionVariable 变量没有清理， 如果重新进入该任务不会重新分配审批人\n            @SuppressWarnings(\"unchecked\")\n            Set<Long> assigneeUserIds = (Set<Long>) execution.getVariableLocal(super.collectionVariable, Set.class);\n            if (assigneeUserIds == null) {\n                assigneeUserIds = new LinkedHashSet<>(taskCandidateInvoker.calculateUsersByTask(execution));\n                if (CollUtil.isEmpty(assigneeUserIds)) {\n                    // 特殊：如果没有处理人的情况下，至少有一个 null 空元素，避免自动通过！\n                    // 这样，保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务\n                    // 用途：1）审批人为空时；2）审批类型为自动通过、自动拒绝时\n                    assigneeUserIds = SetUtils.asSet((Long) null);\n                }\n                execution.setVariableLocal(super.collectionVariable, assigneeUserIds);\n            }\n            return assigneeUserIds.size();\n        }\n\n        // 情况二：CallActivity 节点\n        if (execution.getCurrentFlowElement() instanceof CallActivity) {\n            FlowElement flowElement = execution.getCurrentFlowElement();\n            Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement);\n            if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) {\n                return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class);\n            }\n            if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) {\n                return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size();\n            }\n        }\n\n        return super.resolveNrOfInstances(execution);\n    }\n\n    @Override\n    protected void executeOriginalBehavior(DelegateExecution execution, ExecutionEntity multiInstanceRootExecution, int loopCounter) {\n        // 参见 https://t.zsxq.com/53Meo 情况\n        if (execution.getCurrentFlowElement() instanceof CallActivity\n            || execution.getCurrentFlowElement() instanceof SubProcess) {\n            super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter);\n            return;\n        }\n        super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter);\n    }\n\n    // ========== 屏蔽解析器覆写 ==========\n\n    @Override\n    public void setCollectionExpression(Expression collectionExpression) {\n        // 保持自定义变量名，忽略解析器写入的 collection 表达式\n    }\n\n    @Override\n    public void setCollectionVariable(String collectionVariable) {\n        // 保持自定义变量名，忽略解析器写入的 collection 变量名\n    }\n\n    @Override\n    public void setCollectionElementVariable(String collectionElementVariable) {\n        // 保持自定义变量名，忽略解析器写入的单元素变量名\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.RandomUtil;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;\nimport lombok.Setter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.model.UserTask;\nimport org.flowable.common.engine.impl.el.ExpressionManager;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;\nimport org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;\nimport org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;\nimport org.flowable.engine.impl.util.CommandContextUtil;\nimport org.flowable.engine.impl.util.TaskHelper;\nimport org.flowable.engine.interceptor.CreateUserTaskBeforeContext;\nimport org.flowable.task.service.TaskService;\nimport org.flowable.task.service.impl.persistence.entity.TaskEntity;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 自定义的【单个】流程任务的 assignee 负责人的分配\n * 第一步，基于分配规则，计算出分配任务的【单个】候选人。如果找不到，则直接报业务异常，不继续执行后续的流程；\n * 第二步，随机选择一个候选人，则选择作为 assignee 负责人。\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {\n\n    @Setter\n    private BpmTaskCandidateInvoker taskCandidateInvoker;\n\n    public BpmUserTaskActivityBehavior(UserTask userTask) {\n        super(userTask);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    protected void handleAssignments(TaskService taskService, String assignee, String owner,\n        List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager,\n        DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {\n        // 第一步，获得任务的候选用户\n        Long assigneeUserId = calculateTaskCandidateUsers(execution);\n        // 第二步，设置作为负责人\n        if (assigneeUserId != null) {\n            TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId));\n        }\n    }\n\n    private Long calculateTaskCandidateUsers(DelegateExecution execution) {\n        // 情况一，如果是多实例的任务，例如说会签、或签等情况，则从 Variable 中获取。\n        // 顺序审批可见 BpmSequentialMultiInstanceBehavior，并发审批可见 BpmSequentialMultiInstanceBehavior\n        if (super.multiInstanceActivityBehavior != null) {\n            return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), Long.class);\n        }\n\n        // 情况二，如果非多实例的任务，则计算任务处理人\n        // 第一步，先计算可处理该任务的处理人们\n        Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsersByTask(execution);\n        if (CollUtil.isEmpty(candidateUserIds)) {\n            return null;\n        }\n        // 第二步，后随机选择一个任务的处理人\n        // 疑问：为什么一定要选择一个任务处理人？\n        // 解答：项目对 bpm 的任务是责任到人，所以每个任务有且仅有一个处理人。\n        //      如果希望一个任务可以同时被多个人处理，可以考虑使用 BpmParallelMultiInstanceBehavior 实现的会签 or 或签。\n        int index = RandomUtil.randomInt(candidateUserIds.size());\n        return CollUtil.get(candidateUserIds, index);\n    }\n\n    @Override\n    protected void handleCategory(CreateUserTaskBeforeContext beforeContext, ExpressionManager expressionManager,\n                                  TaskEntity task, DelegateExecution execution) {\n        ProcessDefinitionEntity processDefinitionEntity = CommandContextUtil.getProcessDefinitionEntityManager().findById(execution.getProcessDefinitionId());\n        if (processDefinitionEntity == null) {\n            log.warn(\"[handleCategory][任务编号({}) 找不到流程定义({})]\", task.getId(), execution.getProcessDefinitionId());\n            return;\n        }\n        task.setCategory(processDefinitionEntity.getCategory());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.google.common.annotations.VisibleForTesting;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.model.*;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\n\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG;\n\n/**\n * {@link BpmTaskCandidateStrategy} 的调用者，用于调用对应的策略，实现任务的候选人的计算\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class BpmTaskCandidateInvoker {\n\n    private final Map<BpmTaskCandidateStrategyEnum, BpmTaskCandidateStrategy> strategyMap = new HashMap<>();\n\n    private final AdminUserApi adminUserApi;\n\n    public BpmTaskCandidateInvoker(List<BpmTaskCandidateStrategy> strategyList,\n                                   AdminUserApi adminUserApi) {\n        strategyList.forEach(strategy -> {\n            BpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy);\n            Assert.isNull(oldStrategy, \"策略(%s) 重复\", strategy.getStrategy());\n        });\n        this.adminUserApi = adminUserApi;\n    }\n\n    /**\n     * 校验流程模型的任务分配规则全部都配置了\n     * 目的：如果有规则未配置，会导致流程任务找不到负责人，进而流程无法进行下去！\n     *\n     * @param bpmnBytes BPMN XML\n     */\n    public void validateBpmnConfig(byte[] bpmnBytes) {\n        BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);\n        assert bpmnModel != null;\n        List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);\n        // 遍历所有的 UserTask，校验审批人配置\n        userTaskList.forEach(userTask -> {\n            // 1.1 非人工审批，无需校验审批人配置\n            Integer approveType = BpmnModelUtils.parseApproveType(userTask);\n            if (ObjectUtils.equalsAny(approveType,\n                    BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),\n                    BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {\n                return;\n            }\n            // 1.2 非空校验\n            Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask);\n            String param = BpmnModelUtils.parseCandidateParam(userTask);\n            if (strategy == null) {\n                throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());\n            }\n            BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy);\n            if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) {\n                throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());\n            }\n            // 2. 具体策略校验\n            getCandidateStrategy(strategy).validateParam(param);\n        });\n    }\n\n    /**\n     * 计算任务的候选人\n     *\n     * @param execution 执行任务\n     * @return 用户编号集合\n     */\n    @DataPermission(enable = false) // 忽略数据权限，避免因为过滤，导致找不到候选人\n    public Set<Long> calculateUsersByTask(DelegateExecution execution) {\n        // 注意：解决极端情况下，Flowable 异步调用，导致租户 id 丢失的情况\n        // 例如说，SIMPLE 延迟器在 trigger 的时候！！！\n        return FlowableUtils.execute(execution.getTenantId(), () -> {\n            // 审批类型非人工审核时，不进行计算候选人。原因是：后续会自动通过、不通过\n            FlowElement flowElement = execution.getCurrentFlowElement();\n            Integer approveType = BpmnModelUtils.parseApproveType(flowElement);\n            if (ObjectUtils.equalsAny(approveType,\n                    BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),\n                    BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {\n                return new HashSet<>();\n            }\n\n            // 1.1 计算任务的候选人\n            Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);\n            String param = BpmnModelUtils.parseCandidateParam(flowElement);\n            Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param);\n            // 1.2 移除被禁用的用户\n            removeDisableUsers(userIds);\n\n            // 2. 候选人为空时，根据“审批人为空”的配置补充\n            if (CollUtil.isEmpty(userIds)) {\n                userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())\n                        .calculateUsersByTask(execution, param);\n                // ASSIGN_EMPTY 策略，不需要移除被禁用的用户。原因是，再移除，可能会出现更没审批人了！！！\n            }\n\n            // 3. 移除发起人的用户\n            ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)\n                    .getProcessInstance(execution.getProcessInstanceId());\n            Assert.notNull(processInstance, \"流程实例({}) 不存在\", execution.getProcessInstanceId());\n            removeStartUserIfSkip(userIds, flowElement, Long.valueOf(processInstance.getStartUserId()));\n            return userIds;\n        });\n    }\n\n    @DataPermission(enable = false) // 忽略数据权限，避免因为过滤，导致找不到候选人\n    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId,\n                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        // 如果是 CallActivity 子流程，不进行计算候选人\n        FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);\n        if (flowElement instanceof CallActivity || flowElement instanceof SubProcess) {\n            return new HashSet<>();\n        }\n        // 审批类型非人工审核时，不进行计算候选人。原因是：后续会自动通过、不通过\n        Integer approveType = BpmnModelUtils.parseApproveType(flowElement);\n        if (ObjectUtils.equalsAny(approveType,\n                BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),\n                BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {\n            return new HashSet<>();\n        }\n\n        // 1.1 计算任务的候选人\n        Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);\n        String param = BpmnModelUtils.parseCandidateParam(flowElement);\n        Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param,\n                startUserId, processDefinitionId, processVariables);\n        // 1.2 移除被禁用的用户\n        removeDisableUsers(userIds);\n\n        // 2. 候选人为空时，根据“审批人为空”的配置补充\n        if (CollUtil.isEmpty(userIds)) {\n            userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())\n                    .calculateUsersByActivity(bpmnModel, activityId, param, startUserId, processDefinitionId, processVariables);\n            // ASSIGN_EMPTY 策略，不需要移除被禁用的用户。原因是，再移除，可能会出现更没审批人了！！！\n        }\n\n        // 3. 移除发起人的用户\n        removeStartUserIfSkip(userIds, flowElement, startUserId);\n        return userIds;\n    }\n\n    @VisibleForTesting\n    void removeDisableUsers(Set<Long> assigneeUserIds) {\n        if (CollUtil.isEmpty(assigneeUserIds)) {\n            return;\n        }\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);\n        assigneeUserIds.removeIf(id -> {\n            AdminUserRespDTO user = userMap.get(id);\n            return user == null || CommonStatusEnum.isDisable(user.getStatus());\n        });\n    }\n\n    /**\n     * 如果“审批人与发起人相同时”，配置了 SKIP 跳过，则移除发起人\n     *\n     * 注意：如果只有一个候选人，则不处理，避免无法审批\n     *\n     * @param assigneeUserIds 当前分配的候选人\n     * @param flowElement 当前节点\n     * @param startUserId 发起人\n     */\n    @VisibleForTesting\n    void removeStartUserIfSkip(Set<Long> assigneeUserIds, FlowElement flowElement, Long startUserId) {\n        if (CollUtil.size(assigneeUserIds) <= 1) {\n            return;\n        }\n        Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(flowElement);\n        if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {\n            return;\n        }\n        assigneeUserIds.remove(startUserId);\n    }\n\n    private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {\n        BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);\n        Assert.notNull(strategyEnum, \"策略(%s) 不存在\", strategy);\n        BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);\n        Assert.notNull(strategyObj, \"策略(%s) 不存在\", strategy);\n        return strategyObj;\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;\n\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.delegate.DelegateExecution;\n\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * BPM 任务的候选人的策略接口\n * <p>\n * 例如说：分配审批人\n *\n * @author 芋道源码\n */\npublic interface BpmTaskCandidateStrategy {\n\n    /**\n     * 对应策略\n     *\n     * @return 策略\n     */\n    BpmTaskCandidateStrategyEnum getStrategy();\n\n    /**\n     * 校验参数\n     *\n     * @param param 参数\n     */\n    void validateParam(String param);\n\n    /**\n     * 是否一定要输入参数\n     *\n     * @return 是否\n     */\n    default boolean isParamRequired() {\n        return true;\n    }\n\n    /**\n     * 基于候选人参数，获得任务的候选用户们\n     *\n     * 注意：实现 calculateUsers 系列方法时，有两种选择：\n     * 1. 只重写 calculateUsers 默认方法\n     * 2. 都重写 calculateUsersByTask 和 calculateUsersByActivity 两个方法\n     *\n     * @param param 执行任务\n     * @return 用户编号集合\n     */\n    default Set<Long> calculateUsers(String param) {\n        throw new UnsupportedOperationException(\"该分配方法未实现，请检查！\");\n    }\n\n    /**\n     * 基于【执行任务】，获得任务的候选用户们\n     *\n     * @param execution 执行任务\n     * @return 用户编号集合\n     */\n    default Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        return calculateUsers(param);\n    }\n\n    /**\n     * 基于【流程活动】，获得任务的候选用户们\n     * <p>\n     * 目的：用于获取未执行节点的候选用户们\n     *\n     * @param bpmnModel 流程图\n     * @param activityId 活动 ID (对应 Bpmn XML id)\n     * @param param     节点的参数\n     * @param startUserId  流程发起人编号\n     * @param processDefinitionId 流程定义编号\n     * @param processVariables 流程变量\n     * @return 用户编号集合\n     */\n    @SuppressWarnings(\"unused\")\n    default Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,\n                                               Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        return calculateUsers(param);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression;\n\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.Assert;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\nimport static java.util.Collections.emptySet;\n\n/**\n * 分配给发起人的 Leader 审批的 Expression 流程表达式\n * 目前 Leader 的定义是，发起人所在部门的 Leader\n *\n * @author 芋道源码\n */\n@Component\n@Deprecated // 仅仅是表达式的示例，建议使用 BpmTaskCandidateStartUserDeptLeaderStrategy 替代\npublic class BpmTaskAssignLeaderExpression {\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n\n    /**\n     * 计算审批的候选人\n     *\n     * @param execution 流程执行实体\n     * @param level 指定级别\n     * @return 指定级别的领导\n     */\n    public Set<Long> calculateUsers(DelegateExecution execution, int level) {\n        Assert.isTrue(level > 0, \"level 必须大于 0\");\n        // 获得发起人\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());\n        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());\n        // 获得对应 leve 的部门\n        DeptRespDTO dept = null;\n        for (int i = 0; i < level; i++) {\n            // 获得 level 对应的部门\n            if (dept == null) {\n                dept = getStartUserDept(startUserId);\n                if (dept == null) { // 找不到发起人的部门，所以无法使用该规则\n                    return emptySet();\n                }\n            } else {\n                DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData();\n                if (parentDept == null) { // 找不到父级部门，所以只好结束寻找。原因是：例如说，级别比较高的人，所在部门层级比较少\n                    break;\n                }\n                dept = parentDept;\n            }\n        }\n        return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet();\n    }\n\n    private DeptRespDTO getStartUserDept(Long startUserId) {\n        AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData();\n        if (startUser.getDeptId() == null) { // 找不到部门，所以无法使用该规则\n            return null;\n        }\n        return deptApi.getDept(startUser.getDeptId()).getCheckedData();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression;\n\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\n/**\n * 分配给发起人审批的 Expression 流程表达式\n *\n * @author 芋道源码\n */\n@Component\n@Deprecated // 仅仅是表达式的示例，建议使用 BpmTaskCandidateStartUserStrategy 替代\npublic class BpmTaskAssignStartUserExpression {\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n\n    /**\n     * 计算审批的候选人\n     *\n     * @param execution 流程执行实体\n     * @return 发起人\n     */\n    public Set<Long> calculateUsers(ExecutionEntityImpl execution) {\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());\n        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());\n        return SetUtils.asSet(startUserId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\n\nimport javax.annotation.Resource;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 部门的负责人 {@link BpmTaskCandidateStrategy} 抽象类\n *\n * @author jason\n */\npublic abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {\n\n    @Resource\n    protected DeptApi deptApi;\n    @Resource\n    protected AdminUserApi adminUserApi;\n\n    /**\n     * 获得指定层级的部门负责人，只有第 level 的负责人\n     *\n     * @param dept 指定部门\n     * @param level 第几级\n     * @return 部门负责人的编号\n     */\n    protected Long getAssignLevelDeptLeaderId(DeptRespDTO dept, Integer level) {\n        Assert.isTrue(level > 0, \"level 必须大于 0\");\n        if (dept == null) {\n            return null;\n        }\n        DeptRespDTO currentDept = dept;\n        for (int i = 1; i < level; i++) {\n            DeptRespDTO parentDept = deptApi.getDept(currentDept.getParentId()).getCheckedData();\n            if (parentDept == null) { // 找不到父级部门，到了最高级。返回最高级的部门负责人\n                break;\n            }\n            currentDept = parentDept;\n        }\n        return currentDept.getLeaderUserId();\n    }\n\n    /**\n     * 获得连续层级的部门负责人，包含 [1, level] 的负责人\n     *\n     * @param deptIds 指定部门编号数组\n     * @param level 最大层级\n     * @return 连续部门负责人 Id\n     */\n    protected Set<Long> getMultiLevelDeptLeaderIds(List<Long> deptIds, Integer level) {\n        Assert.isTrue(level > 0, \"level 必须大于 0\");\n        if (CollUtil.isEmpty(deptIds)) {\n            return new HashSet<>();\n        }\n        Set<Long> deptLeaderIds = new LinkedHashSet<>(); // 保证有序\n        for (Long deptId : deptIds) {\n            DeptRespDTO dept = deptApi.getDept(deptId).getCheckedData();\n            for (int i = 0; i < level; i++) {\n                if (dept.getLeaderUserId() != null) {\n                    deptLeaderIds.add(dept.getLeaderUserId());\n                }\n                DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData();\n                if (parentDept == null) { // 找不到父级部门. 已经到了最高层级了\n                    break;\n                }\n                dept = parentDept;\n            }\n        }\n        return deptLeaderIds;\n    }\n\n    /**\n     * 获取发起人的部门\n     *\n     * @param startUserId 发起人 Id\n     */\n    protected DeptRespDTO getStartUserDept(Long startUserId) {\n        AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData();\n        if (startUser.getDeptId() == null) { // 找不到部门\n            return null;\n        }\n        return deptApi.getDept(startUser.getDeptId()).getCheckedData();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport com.google.common.collect.Sets;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类\n * 审批人在审批时选择下一个节点的审批人\n *\n * @author smallNorthLee\n */\n@Component\npublic class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT;\n    }\n\n    @Override\n    public void validateParam(String param) {}\n\n    @Override\n    public boolean isParamRequired() {\n        return false;\n    }\n\n    @Override\n    public LinkedHashSet<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());\n        Assert.notNull(processInstance, \"流程实例({})不能为空\", execution.getProcessInstanceId());\n        Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance);\n        Assert.notNull(approveUserSelectAssignees, \"流程实例({}) 的下一个执行节点审批人不能为空\",\n                execution.getProcessInstanceId());\n        if (approveUserSelectAssignees == null) {\n            return Sets.newLinkedHashSet();\n        }\n        // 获得审批人\n        List<Long> assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId());\n        return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet();\n    }\n\n    @Override\n    public LinkedHashSet<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,\n                                                        Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        if (processVariables == null) {\n            return Sets.newLinkedHashSet();\n        }\n        // 流程预测时会使用，允许审批人为空，如果为空前端会弹出提示选择下一个节点审批人，避免流程无法进行，审批时会真正校验节点是否配置审批人\n        Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables);\n        if (approveUserSelectAssignees == null) {\n            return Sets.newLinkedHashSet();\n        }\n        // 获得审批人\n        List<Long> assignees = approveUserSelectAssignees.get(activityId);\n        return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author jason\n */\n@Component\npublic class BpmTaskCandidateDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.MULTI_DEPT_LEADER_MULTI;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        // 参数格式: | 分隔：1）左边为部门（多个部门用 , 分隔）。2）右边为部门层级\n        String[] params = param.split(\"\\\\|\");\n        Assert.isTrue(params.length == 2, \"参数格式不匹配\");\n        List<Long> deptIds = StrUtils.splitToLong(params[0], \",\");\n        int level = Integer.parseInt(params[1]);\n        // 校验部门存在\n        deptApi.validateDeptList(deptIds).checkError();\n        Assert.isTrue(level > 0, \"部门层级必须大于 0\");\n    }\n\n    @Override\n    public Set<Long> calculateUsers(String param) {\n        String[] params = param.split(\"\\\\|\");\n        List<Long> deptIds = StrUtils.splitToLong(params[0], \",\");\n        int level = Integer.parseInt(params[1]);\n        return super.getMultiLevelDeptLeaderIds(deptIds, level);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n/**\n * 部门的负责人 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author kyle\n */\n@Component\npublic class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {\n\n    @Resource\n    private DeptApi deptApi;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.DEPT_LEADER;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        Set<Long> deptIds = StrUtils.splitToLongSet(param);\n        deptApi.validateDeptList(deptIds).checkError();\n    }\n\n    @Override\n    public Set<Long> calculateUsers(String param) {\n        Set<Long> deptIds = StrUtils.splitToLongSet(param);\n        List<DeptRespDTO> depts = deptApi.getDeptList(deptIds).getCheckedData();\n        return convertSet(depts, DeptRespDTO::getLeaderUserId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n/**\n * 部门的成员 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author kyle\n */\n@Component\npublic class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrategy {\n\n    @Resource\n    private DeptApi deptApi;\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.DEPT_MEMBER;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        Set<Long> deptIds = StrUtils.splitToLongSet(param);\n        deptApi.validateDeptList(deptIds).checkError();\n    }\n\n    @Override\n    public Set<Long> calculateUsers(String param) {\n        Set<Long> deptIds = StrUtils.splitToLongSet(param);\n        List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData();\n        return convertSet(users, AdminUserRespDTO::getId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.hutool.core.collection.ListUtil.toList;\n\n/**\n * 发起人连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author jason\n */\n@Component\npublic class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {\n\n    @Resource\n    @Lazy\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        int level = Integer.parseInt(param); // 参数是部门的层级\n        Assert.isTrue(level > 0, \"部门的层级必须大于 0\");\n    }\n\n    @Override\n    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        int level = Integer.parseInt(param); // 参数是部门的层级\n        // 获得流程发起人\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());\n        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());\n        // 获取发起人的 multi 部门负责人\n        DeptRespDTO dept = super.getStartUserDept(startUserId);\n        if (dept == null) {\n            return new HashSet<>();\n        }\n        return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level);\n    }\n\n    @Override\n    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,\n                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        int level = Integer.parseInt(param); // 参数是部门的层级\n        DeptRespDTO dept = super.getStartUserDept(startUserId);\n        if (dept == null) {\n            return new HashSet<>();\n        }\n        return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\n\n/**\n * 发起人的部门负责人, 可以是上级部门负责人 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author jason\n */\n@Component\npublic class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {\n\n    @Resource\n    @Lazy // 避免循环依赖\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        // 参数是部门的层级\n        Assert.isTrue(Integer.parseInt(param) > 0, \"部门的层级必须大于 0\");\n    }\n\n    @Override\n    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        // 获得流程发起人\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());\n        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());\n        // 获取发起人的部门负责人\n        return getStartUserDeptLeader(startUserId, param);\n    }\n\n    @Override\n    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,\n                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        // 获取发起人的部门负责人\n        return getStartUserDeptLeader(startUserId, param);\n    }\n\n    private Set<Long> getStartUserDeptLeader(Long startUserId, String param) {\n        int level = Integer.parseInt(param); // 参数是部门的层级\n        DeptRespDTO dept = super.getStartUserDept(startUserId);\n        if (dept == null) {\n            return new HashSet<>();\n        }\n        Long deptLeaderId = super.getAssignLevelDeptLeaderId(dept, level);\n        return deptLeaderId != null ? asSet(deptLeaderId) : new HashSet<>();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport com.google.common.collect.Sets;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类\n *\n * @author 芋道源码\n */\n@Component\npublic class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.START_USER_SELECT;\n    }\n\n    @Override\n    public void validateParam(String param) {}\n\n    @Override\n    public boolean isParamRequired() {\n        return false;\n    }\n\n    @Override\n    public LinkedHashSet<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());\n        Assert.notNull(processInstance, \"流程实例({})不能为空\", execution.getProcessInstanceId());\n        Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);\n        Assert.notNull(startUserSelectAssignees, \"流程实例({}) 的发起人自选审批人不能为空\",\n                execution.getProcessInstanceId());\n        // 获得审批人\n        List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());\n        return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet();\n    }\n\n    @Override\n    public LinkedHashSet<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,\n                                                        Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        if (processVariables == null) {\n            return Sets.newLinkedHashSet();\n        }\n        Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processVariables);\n        if (startUserSelectAssignees == null) {\n            return Sets.newLinkedHashSet();\n        }\n        // 获得审批人\n        List<Long> assignees = startUserSelectAssignees.get(activityId);\n        return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.form;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept.AbstractBpmTaskCandidateDeptLeaderStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 表单内部门负责人 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author jason\n */\n@Component\npublic class BpmTaskCandidateFormDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.FORM_DEPT_LEADER;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        // 参数格式: | 分隔：1）左边为表单内部门字段。2）右边为部门层级\n        String[] params = param.split(\"\\\\|\");\n        Assert.isTrue(params.length == 2, \"参数格式不匹配\");\n        Assert.notEmpty(param, \"表单内部门字段不能为空\");\n        int level = Integer.parseInt(params[1]);\n        Assert.isTrue(level > 0, \"部门层级必须大于 0\");\n    }\n\n    @Override\n    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        String[] params = param.split(\"\\\\|\");\n        Object result = execution.getVariable(params[0]);\n        int level = Integer.parseInt(params[1]);\n        return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level);\n    }\n\n    @Override\n    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId,\n                                              String param, Long startUserId, String processDefinitionId,\n                                              Map<String, Object> processVariables) {\n        String[] params = param.split(\"\\\\|\");\n        Object result = processVariables == null ? null : processVariables.get(params[0]);\n        int level = Integer.parseInt(params[1]);\n        return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.form;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 表单内用户字段 {@link BpmTaskCandidateUserStrategy} 实现类\n *\n * @author jason\n */\n@Component\npublic class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrategy {\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.FORM_USER;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        Assert.notEmpty(param, \"表单内用户字段不能为空\");\n    }\n\n    @Override\n    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        Object result = execution.getVariable(param);\n        return CollectionUtils.toLinkedHashSet(Long.class, result);\n    }\n\n    @Override\n    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId,\n                                              String param, Long startUserId, String processDefinitionId,\n                                              Map<String, Object> processVariables) {\n        Object result = processVariables == null ? null : processVariables.get(param);\n        return CollectionUtils.toLinkedHashSet(Long.class, result);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.bpmn.model.FlowElement;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\n/**\n * 审批人为空 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author kyle\n */\n@Component\npublic class BpmTaskCandidateAssignEmptyStrategy implements BpmTaskCandidateStrategy {\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private BpmProcessDefinitionService processDefinitionService;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY;\n    }\n\n    @Override\n    public void validateParam(String param) {\n    }\n\n    @Override\n    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        return getCandidateUsers(execution.getProcessDefinitionId(), execution.getCurrentFlowElement());\n    }\n\n    @Override\n    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,\n                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);\n        return getCandidateUsers(processDefinitionId, flowElement);\n    }\n\n    private Set<Long> getCandidateUsers(String processDefinitionId, FlowElement flowElement) {\n        // 情况一：指定人员审批\n        Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(flowElement);\n        if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType())) {\n            return new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(flowElement));\n        }\n\n        // 情况二：流程管理员\n        if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) {\n            BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processDefinitionId);\n            Assert.notNull(processDefinition, \"流程定义({})不存在\", processDefinitionId);\n            return new HashSet<>(processDefinition.getManagerUserIds());\n        }\n\n        // 都不满足，还是返回空\n        return new HashSet<>();\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;\n\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport com.google.common.collect.Sets;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.common.engine.api.FlowableException;\nimport org.flowable.common.engine.impl.javax.el.PropertyNotFoundException;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.springframework.stereotype.Component;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 流程表达式 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author 芋道源码\n */\n@Component\n@Slf4j\npublic class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy {\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.EXPRESSION;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        // do nothing 因为它基本做不了校验\n    }\n\n    @Override\n    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        Object result = FlowableUtils.getExpressionValue(execution, param);\n        return CollectionUtils.toLinkedHashSet(Long.class, result);\n    }\n\n    @Override\n    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,\n                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        Map<String, Object> variables = processVariables == null ? new HashMap<>() : processVariables;\n        try {\n            Object result = FlowableUtils.getExpressionValue(variables, param);\n            return CollectionUtils.toLinkedHashSet(Long.class, result);\n        } catch (FlowableException ex) {\n            // 预测未运行的节点时候，表达式如果包含 execution 或者不存在的流程变量会抛异常，此时忽略该异常！相当于说，不做流程预测！！！\n            if (ex.getCause() != null && ex.getCause() instanceof PropertyNotFoundException) {\n                return Sets.newHashSet();\n            }\n            log.error(\"[calculateUsersByActivity][表达式({}) 变量({}) 解析报错\", param, variables, ex);\n            throw ex;\n        }\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;\n\n/**\n * 用户组 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author kyle\n */\n@Component\npublic class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy {\n\n    @Resource\n    private BpmUserGroupService userGroupService;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.USER_GROUP;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        Set<Long> groupIds = StrUtils.splitToLongSet(param);\n        userGroupService.validUserGroups(groupIds);\n    }\n\n    @Override\n    public Set<Long> calculateUsers(String param) {\n        Set<Long> groupIds = StrUtils.splitToLongSet(param);\n        List<BpmUserGroupDO> groups = userGroupService.getUserGroupList(groupIds);\n        return convertSetByFlatMap(groups, BpmUserGroupDO::getUserIds, Collection::stream);\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.system.api.dept.PostApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n/**\n * 岗位 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author kyle\n */\n@Component\npublic class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy {\n\n    @Resource\n    private PostApi postApi;\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.POST;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        Set<Long> postIds = StrUtils.splitToLongSet(param);\n        postApi.validPostList(postIds);\n    }\n\n    @Override\n    public Set<Long> calculateUsers(String param) {\n        Set<Long> postIds = StrUtils.splitToLongSet(param);\n        List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(postIds).getCheckedData();\n        return convertSet(users, AdminUserRespDTO::getId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.system.api.permission.PermissionApi;\nimport cn.iocoder.yudao.module.system.api.permission.RoleApi;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\n/**\n * 角色 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author kyle\n */\n@Component\npublic class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy {\n\n    @Resource\n    private RoleApi roleApi;\n    @Resource\n    private PermissionApi permissionApi;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.ROLE;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        Set<Long> roleIds = StrUtils.splitToLongSet(param);\n        roleApi.validRoleList(roleIds);\n    }\n\n    @Override\n    public Set<Long> calculateUsers(String param) {\n        Set<Long> roleIds = StrUtils.splitToLongSet(param);\n        return permissionApi.getUserRoleIdListByRoleIds(roleIds).getCheckedData();\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 发起人自己 {@link BpmTaskCandidateUserStrategy} 实现类\n * <p>\n * 适合场景：用于需要发起人信息复核等场景\n *\n * @author jason\n */\n@Component\npublic class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrategy {\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.START_USER;\n    }\n\n    @Override\n    public void validateParam(String param) {\n    }\n\n    @Override\n    public boolean isParamRequired() {\n        return false;\n    }\n\n    @Override\n    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());\n        return SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));\n    }\n\n    @Override\n    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,\n                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        return SetUtils.asSet(startUserId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.hutool.core.text.StrPool;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.LinkedHashSet;\n\n/**\n * 用户 {@link BpmTaskCandidateStrategy} 实现类\n *\n * @author kyle\n */\n@Component\npublic class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy {\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    public BpmTaskCandidateStrategyEnum getStrategy() {\n        return BpmTaskCandidateStrategyEnum.USER;\n    }\n\n    @Override\n    public void validateParam(String param) {\n        adminUserApi.validateUserList(StrUtils.splitToLongSet(param)).checkError();\n    }\n\n    @Override\n    public LinkedHashSet<Long> calculateUsers(String param) {\n        return new LinkedHashSet<>(StrUtils.splitToLong(param, StrPool.COMMA));\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.el;\n\nimport org.flowable.common.engine.api.variable.VariableContainer;\nimport org.flowable.common.engine.impl.el.function.AbstractFlowableVariableExpressionFunction;\nimport org.springframework.stereotype.Component;\n\n/**\n * 根据流程变量 variable 的类型，转换参数的值\n *\n * 目前用于 ConditionNodeConvert 的 buildConditionExpression 方法中\n *\n * @author jason\n */\n@Component\npublic class VariableConvertByTypeExpressionFunction extends AbstractFlowableVariableExpressionFunction {\n\n    public VariableConvertByTypeExpressionFunction() {\n        super(\"convertByType\");\n    }\n\n    public static Object convertByType(VariableContainer variableContainer, String variableName, Object parmaValue) {\n        Object variable = variableContainer.getVariable(variableName);\n        if (variable != null && parmaValue != null) {\n            // 如果值不是字符串类型，流程变量的类型是字符串，把值转成字符串\n            if (!(parmaValue instanceof String) && variable instanceof String ) {\n                return parmaValue.toString();\n            }\n        }\n        return parmaValue;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * BPM 任务的候选人策略枚举\n *\n * 例如说：分配给指定人审批\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum BpmTaskCandidateStrategyEnum implements ArrayValuable<Integer> {\n\n    ROLE(10, \"角色\"),\n    DEPT_MEMBER(20, \"部门的成员\"), // 包括负责人\n    DEPT_LEADER(21, \"部门的负责人\"),\n    MULTI_DEPT_LEADER_MULTI(23, \"连续多级部门的负责人\"),\n    POST(22, \"岗位\"),\n    USER(30, \"用户\"),\n    APPROVE_USER_SELECT(34, \"审批人自身\"), // 当前审批人，可在审批时，选择下一个节点的审批人\n    START_USER_SELECT(35, \"发起人自选\"), // 申请人自己，可在提交申请时，选择此节点的审批人\n    START_USER(36, \"发起人自己\"), // 申请人自己, 一般紧挨开始节点，常用于发起人信息审核场景\n    START_USER_DEPT_LEADER(37, \"发起人部门负责人\"),\n    START_USER_DEPT_LEADER_MULTI(38, \"发起人连续多级部门的负责人\"),\n    USER_GROUP(40, \"用户组\"),\n    FORM_USER(50, \"表单内用户字段\"),\n    FORM_DEPT_LEADER(51, \"表单内部门负责人\"),\n    EXPRESSION(60, \"流程表达式\"), // 表达式 ExpressionManager\n    ASSIGN_EMPTY(1, \"审批人为空\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTaskCandidateStrategyEnum::getStrategy).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer strategy;\n    /**\n     * 描述\n     */\n    private final String description;\n\n    public static BpmTaskCandidateStrategyEnum valueOf(Integer strategy) {\n        return ArrayUtil.firstMatch(o -> o.getStrategy().equals(strategy), values());\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;\n\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;\n\n/**\n * BPMN XML 常量信息\n *\n * @author 芋道源码\n */\npublic interface BpmnModelConstants {\n\n    String BPMN_FILE_SUFFIX = \".bpmn\";\n\n    /**\n     * BPMN 中的命名空间\n     */\n    String NAMESPACE = \"http://flowable.org/bpmn\";\n\n    /**\n     * BPMN UserTask 的扩展属性，用于标记候选人策略\n     */\n    String USER_TASK_CANDIDATE_STRATEGY = \"candidateStrategy\";\n    /**\n     * BPMN UserTask 的扩展属性，用于标记候选人参数\n     */\n    String USER_TASK_CANDIDATE_PARAM = \"candidateParam\";\n\n    /**\n     * BPMN ExtensionElement 的扩展属性，用于标记边界事件类型\n     */\n    String BOUNDARY_EVENT_TYPE = \"boundaryEventType\";\n\n    /**\n     * BPMN ExtensionElement 的扩展属性，用于标记用户任务超时执行动作\n     */\n    String USER_TASK_TIMEOUT_HANDLER_TYPE = \"timeoutHandlerType\";\n\n    /**\n     * BPMN ExtensionElement 的扩展属性，用于标记用户任务的审批人与发起人相同时，对应的处理类型\n     */\n    String USER_TASK_ASSIGN_START_USER_HANDLER_TYPE = \"assignStartUserHandlerType\";\n\n    /**\n     * BPMN ExtensionElement 的扩展属性，用于标记用户任务的空处理类型\n     */\n    String USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE = \"assignEmptyHandlerType\";\n    /**\n     * BPMN ExtensionElement 的扩展属性，用于标记用户任务的空处理的指定用户编号数组\n     */\n    String USER_TASK_ASSIGN_USER_IDS = \"assignEmptyUserIds\";\n\n    /**\n     * BPMN ExtensionElement 的扩展属性，用于标记用户任务拒绝处理类型\n     */\n    String USER_TASK_REJECT_HANDLER_TYPE = \"rejectHandlerType\";\n    /**\n     * BPMN ExtensionElement 的扩展属性，用于标记用户任务拒绝后的退回的任务 Id\n     */\n    String USER_TASK_REJECT_RETURN_TASK_ID = \"rejectReturnTaskId\";\n\n    /**\n     * BPMN UserTask 的扩展属性，用于标记用户任务的审批类型\n     */\n    String USER_TASK_APPROVE_TYPE = \"approveType\";\n\n    /**\n     * BPMN UserTask 的扩展属性，用于标记用户任务的审批方式\n     */\n    String USER_TASK_APPROVE_METHOD = \"approveMethod\";\n\n    /**\n     * BPMN Child Process 的扩展属性，用于标记多实例来源类型\n     */\n    String CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE = \"childProcessMultiInstanceSourceType\";\n\n    /**\n     * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限\n     */\n    String FORM_FIELD_PERMISSION_ELEMENT = \"fieldsPermission\";\n\n    /**\n     * BPMN ExtensionElement Attribute, 用于标记表单字段\n     */\n    String FORM_FIELD_PERMISSION_ELEMENT_FIELD_ATTRIBUTE = \"field\";\n    /**\n     * BPMN ExtensionElement Attribute, 用于标记表单权限\n     */\n    String FORM_FIELD_PERMISSION_ELEMENT_PERMISSION_ATTRIBUTE = \"permission\";\n\n    /**\n     * BPMN ExtensionElement 操作按钮设置元素, 用于审批节点操作按钮设置\n     */\n    String BUTTON_SETTING_ELEMENT = \"buttonsSetting\";\n\n    /**\n     * BPMN ExtensionElement Attribute, 用于标记按钮编号\n     */\n    String BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE = \"id\";\n\n    /**\n     * BPMN ExtensionElement Attribute, 用于标记按钮显示名称\n     */\n    String BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE = \"displayName\";\n\n    /**\n     * BPMN ExtensionElement Attribute, 用于标记按钮是否启用\n     */\n    String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = \"enable\";\n\n    /**\n     * BPMN ExtensionElement 的扩展属性，用于标记触发器的类型\n     */\n    String TRIGGER_TYPE = \"triggerType\";\n    /**\n     * BPMN ExtensionElement 的扩展属性，用于标记触发器参数\n     */\n    String TRIGGER_PARAM = \"triggerParam\";\n\n    /**\n     * BPMN Start Event Node Id\n     */\n    String START_EVENT_NODE_ID = \"StartEvent\";\n\n    /**\n     * 发起人节点 ID\n     */\n    String START_USER_NODE_ID = \"StartUserNode\";\n\n    /**\n     * 是否需要签名\n     */\n    String SIGN_ENABLE = \"signEnable\";\n\n    /**\n     * 审批意见是否必填\n     */\n    String REASON_REQUIRE = \"reasonRequire\";\n\n    /**\n     * 节点类型\n     *\n     * 目前只有 {@link BpmModelTypeEnum#SIMPLE} 的 UserTask 节点会设置该属性，用于区分是审批节点、还是办理节点\n     */\n    String NODE_TYPE = \"nodeType\";\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;\n\nimport org.flowable.engine.runtime.ProcessInstance;\n\n/**\n * BPM Variable 通用常量\n *\n * @author 芋道源码\n */\npublic class BpmnVariableConstants {\n\n    /**\n     * 流程实例的变量 - 状态\n     *\n     * @see ProcessInstance#getProcessVariables()\n     */\n    public static final String PROCESS_INSTANCE_VARIABLE_STATUS = \"PROCESS_STATUS\";\n    /**\n     * 流程实例的变量 - 理由\n     *\n     * 例如说：审批不通过的理由（目前审核通过暂时不会记录）\n     *\n     * @see ProcessInstance#getProcessVariables()\n     */\n    public static final String PROCESS_INSTANCE_VARIABLE_REASON = \"PROCESS_REASON\";\n    /**\n     * 流程实例的变量 - 发起用户选择的审批人 Map\n     *\n     * @see ProcessInstance#getProcessVariables()\n     * @see BpmTaskCandidateStrategyEnum#START_USER_SELECT\n     */\n    public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = \"PROCESS_START_USER_SELECT_ASSIGNEES\";\n    /**\n     * 流程实例的变量 - 审批人选择的审批人 Map\n     *\n     * @see ProcessInstance#getProcessVariables()\n     * @see BpmTaskCandidateStrategyEnum#APPROVE_USER_SELECT\n     */\n    public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = \"PROCESS_APPROVE_USER_SELECT_ASSIGNEES\";\n    /**\n     * 流程实例的变量 - 发起用户 ID\n     *\n     * @see ProcessInstance#getProcessVariables()\n     */\n    public static final String PROCESS_INSTANCE_VARIABLE_START_USER_ID = \"PROCESS_START_USER_ID\";\n\n    /**\n     * 流程实例的变量 - 用于判断流程实例变量节点是否驳回：格式 RETURN_FLAG_{节点 id}\n     *\n     * 目的是：退回到发起节点时，因为审批人与发起人相同，所以被自动通过。但是，此时还是希望不要自动通过\n     *\n     * @see ProcessInstance#getProcessVariables()\n     */\n    public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = \"RETURN_FLAG_%s\";\n\n    /**\n     * 流程实例的变量 - 用于退回操作，记录需要预测的节点 ids, 变量值类型为 Set\n     *\n     * 目的是：退回操作，预测节点会不准，在流程变量中记录需要预测的节点，来辅助预测\n     */\n    public static final String PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS = \"NEED_SIMULATE_TASK_IDS\";\n\n    /**\n     * 流程实例的变量 - 是否跳过表达式\n     *\n     * @see ProcessInstance#getProcessVariables()\n     * @see <a href=\"https://blog.csdn.net/weixin_42065235/article/details/126039993\">Flowable/Activiti之SkipExpression 完成自动审批</a>\n     */\n    public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = \"_FLOWABLE_SKIP_EXPRESSION_ENABLED\";\n\n    /**\n     * 流程实例的变量 - 用于判断流程是否需要跳过发起人节点\n     *\n     * @see ProcessInstance#getProcessVariables()\n     */\n    public static final String PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE = \"PROCESS_SKIP_START_USER_NODE\";\n\n    /**\n     * 流程实例的变量 - 流程开始时间\n     *\n     * 【非存储变量】用于部分需要 format 的场景，例如说：流程实例的自定义标题\n     */\n    public static final String PROCESS_START_TIME = \"PROCESS_START_TIME\";\n    /**\n     * 流程实例的变量 - 流程定义名称\n     */\n    public static final String PROCESS_DEFINITION_NAME = \"PROCESS_DEFINITION_NAME\";\n\n    /**\n     * 任务的变量 - 状态\n     *\n     * @see org.flowable.task.api.Task#getTaskLocalVariables()\n     */\n    public static final String TASK_VARIABLE_STATUS = \"TASK_STATUS\";\n    /**\n     * 任务的变量 - 理由\n     *\n     * 例如说：审批通过、不通过的理由\n     *\n     * @see org.flowable.task.api.Task#getTaskLocalVariables()\n     */\n    public static final String TASK_VARIABLE_REASON = \"TASK_REASON\";\n    /**\n     * 任务变量 - 签名图片 URL\n     */\n    public static final String TASK_SIGN_PIC_URL = \"TASK_SIGN_PIC_URL\";\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.event;\n\nimport cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;\nimport javax.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.validation.annotation.Validated;\n\n/**\n * {@link BpmProcessInstanceStatusEvent} 的生产者\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Validated\npublic class BpmProcessInstanceEventPublisher {\n\n    private final ApplicationEventPublisher publisher;\n\n    public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceStatusEvent event) {\n        publisher.publishEvent(event);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService;\nimport org.flowable.bpmn.model.FlowElement;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.delegate.JavaDelegate;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate.BEAN_NAME;\n\n/**\n * 处理抄送用户的 {@link JavaDelegate} 的实现类\n * <p>\n * 目前只有仿钉钉/飞书模式的【抄送节点】使用\n *\n * @author jason\n */\n@Component(BEAN_NAME)\npublic class BpmCopyTaskDelegate implements JavaDelegate {\n\n    public static final String BEAN_NAME = \"bpmCopyTaskDelegate\";\n\n    @Resource\n    private BpmTaskCandidateInvoker taskCandidateInvoker;\n\n    @Resource\n    private BpmProcessInstanceCopyService processInstanceCopyService;\n\n    @Override\n    public void execute(DelegateExecution execution) {\n        // 1. 获得抄送人\n        Set<Long> userIds = taskCandidateInvoker.calculateUsersByTask(execution);\n        if (CollUtil.isEmpty(userIds)) {\n            return;\n        }\n        // 2. 执行抄送\n        FlowElement currentFlowElement = execution.getCurrentFlowElement();\n        FlowableUtils.execute(execution.getTenantId(), () ->\n                processInstanceCopyService.createProcessInstanceCopy(userIds, null, execution.getProcessInstanceId(),\n                        currentFlowElement.getId(), currentFlowElement.getName(), null));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;\n\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport com.google.common.collect.ImmutableSet;\nimport org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;\nimport org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;\nimport org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;\nimport org.flowable.engine.delegate.event.FlowableCancelledEvent;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\n/**\n * 监听 {@link ProcessInstance} 的状态变更，更新其对应的 status 状态\n *\n * @author jason\n */\n@Component\npublic class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener {\n\n    public static final Set<FlowableEngineEventType> PROCESS_INSTANCE_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()\n            .add(FlowableEngineEventType.PROCESS_CREATED)\n            .add(FlowableEngineEventType.PROCESS_COMPLETED)\n            .add(FlowableEngineEventType.PROCESS_CANCELLED)\n            .build();\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private BpmProcessInstanceService processInstanceService;\n\n    public BpmProcessInstanceEventListener(){\n        super(PROCESS_INSTANCE_EVENTS);\n    }\n\n    @Override\n    protected void processCreated(FlowableEngineEntityEvent event) {\n        ProcessInstance processInstance = (ProcessInstance) event.getEntity();\n        FlowableUtils.execute(processInstance.getTenantId(),\n                () -> processInstanceService.processProcessInstanceCreated(processInstance));\n    }\n\n    @Override\n    protected void processCompleted(FlowableEngineEntityEvent event) {\n        ProcessInstance processInstance = (ProcessInstance) event.getEntity();\n        FlowableUtils.execute(processInstance.getTenantId(),\n                () -> processInstanceService.processProcessInstanceCompleted(processInstance));\n    }\n\n    @Override\n    protected void processCancelled(FlowableCancelledEvent event) {\n        // 特殊情况：当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId());\n        if (processInstance != null) {\n            FlowableUtils.execute(processInstance.getTenantId(),\n                    () -> processInstanceService.processProcessInstanceCompleted(processInstance));\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.json.JSONObject;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;\nimport com.google.common.collect.ImmutableSet;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.model.BoundaryEvent;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.bpmn.model.FlowElement;\nimport org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;\nimport org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;\nimport org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;\nimport org.flowable.engine.delegate.event.FlowableActivityCancelledEvent;\nimport org.flowable.engine.history.HistoricActivityInstance;\nimport org.flowable.job.api.Job;\nimport org.flowable.task.api.Task;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 监听 {@link Task} 的开始与完成\n *\n * @author jason\n */\n@Component\n@Slf4j\npublic class BpmTaskEventListener extends AbstractFlowableEngineEventListener {\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private BpmModelService modelService;\n    @Resource\n    @Lazy // 解决循环依赖\n    private BpmTaskService taskService;\n\n    public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()\n            .add(FlowableEngineEventType.TASK_CREATED)\n            .add(FlowableEngineEventType.TASK_ASSIGNED)\n            .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时，已经记录了 task 的 status 为通过，这里仅处理任务后置通知。\n            .add(FlowableEngineEventType.ACTIVITY_CANCELLED)\n            .add(FlowableEngineEventType.TIMER_FIRED) // 监听审批超时\n            .build();\n\n    public BpmTaskEventListener() {\n        super(TASK_EVENTS);\n    }\n\n    @Override\n    protected void taskCreated(FlowableEngineEntityEvent event) {\n        Task entity = (Task) event.getEntity();\n        FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskCreated(entity));\n    }\n\n    @Override\n    protected void taskAssigned(FlowableEngineEntityEvent event) {\n        Task entity = (Task) event.getEntity();\n        FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskAssigned(entity));\n    }\n\n    @Override\n    protected void taskCompleted(FlowableEngineEntityEvent event) {\n        Task entity = (Task) event.getEntity();\n        FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskCompleted(entity));\n    }\n\n    @Override\n    protected void activityCancelled(FlowableActivityCancelledEvent event) {\n        List<HistoricActivityInstance> activityList = taskService.getHistoricActivityListByExecutionId(event.getExecutionId());\n        if (CollUtil.isEmpty(activityList)) {\n            log.error(\"[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]\", event.getExecutionId());\n            return;\n        }\n        // 遍历处理\n        activityList.forEach(activity -> {\n            if (StrUtil.isEmpty(activity.getTaskId())) {\n                return;\n            }\n            taskService.processTaskCanceled(activity.getTaskId());\n        });\n    }\n\n    @Override\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    protected void timerFired(FlowableEngineEntityEvent event) {\n        // 1.1 只处理 BoundaryEvent 边界计时时间\n        String processDefinitionId = event.getProcessDefinitionId();\n        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId);\n        Job entity = (Job) event.getEntity();\n        // 特殊 from https://t.zsxq.com/h6oWr ：当 elementId 为空时，尝试从 JobHandlerConfiguration 中解析 JSON 获取\n        String elementId = entity.getElementId();\n        if (elementId == null && entity.getJobHandlerConfiguration() != null) {\n            try {\n                String handlerConfig = entity.getJobHandlerConfiguration();\n                if (handlerConfig.startsWith(\"{\") && handlerConfig.contains(\"activityId\")) {\n                    elementId = new JSONObject(handlerConfig).getStr(\"activityId\");\n                }\n            } catch (Exception e) {\n                log.error(\"[timerFired][解析 entity({}) 失败]\", entity, e);\n                return;\n            }\n        }\n        if (elementId == null) {\n            log.error(\"[timerFired][解析 entity({}) elementId 为空，跳过处理]\", entity);\n            return;\n        }\n        FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId());\n        if (!(element instanceof BoundaryEvent)) {\n            return;\n        }\n        // 1.2 判断是否为超时处理\n        BoundaryEvent boundaryEvent = (BoundaryEvent) element;\n        String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent,\n                BpmnModelConstants.BOUNDARY_EVENT_TYPE);\n        BpmBoundaryEventTypeEnum bpmTimerBoundaryEventType = BpmBoundaryEventTypeEnum.typeOf(NumberUtils.parseInt(boundaryEventType));\n\n        // 2. 处理超时\n        if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT)) {\n            // 2.1 用户任务超时处理\n            String timeoutHandlerType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent,\n                    BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_TYPE);\n            String taskKey = boundaryEvent.getAttachedToRefId();\n            taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutHandlerType));\n            // 2.2 延迟器超时处理\n        } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) {\n            String taskKey = boundaryEvent.getAttachedToRefId();\n            taskService.triggerTask(event.getProcessInstanceId(), taskKey);\n            // 2.3 子流程超时处理\n        } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT)) {\n            String taskKey = boundaryEvent.getAttachedToRefId();\n            taskService.processChildProcessTimeout(event.getProcessInstanceId(), taskKey);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;\n\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.model.FlowElement;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.delegate.JavaDelegate;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.Resource;\nimport java.util.EnumMap;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate.BEAN_NAME;\n\n\n/**\n * 处理触发器任务 {@link JavaDelegate} 的实现类\n * <p>\n * 目前只有 Simple 设计器【触发器节点】使用\n *\n * @author jason\n */\n@Component(BEAN_NAME)\n@Slf4j\npublic class BpmTriggerTaskDelegate implements JavaDelegate {\n\n    public static final String BEAN_NAME = \"bpmTriggerTaskDelegate\";\n\n    @Resource\n    private List<BpmTrigger> triggers;\n\n    private final EnumMap<BpmTriggerTypeEnum, BpmTrigger> triggerMap = new EnumMap<>(BpmTriggerTypeEnum.class);\n\n    @PostConstruct\n    private void init() {\n        triggers.forEach(trigger -> triggerMap.put(trigger.getType(), trigger));\n    }\n\n    @Override\n    public void execute(DelegateExecution execution) {\n        FlowElement flowElement = execution.getCurrentFlowElement();\n        BpmTriggerTypeEnum bpmTriggerType = BpmnModelUtils.parserTriggerType(flowElement);\n        BpmTrigger bpmTrigger = triggerMap.get(bpmTriggerType);\n        if (bpmTrigger == null) {\n            log.error(\"[execute][FlowElement({}), {} 找不到匹配的触发器]\", execution.getCurrentActivityId(), flowElement);\n            return;\n        }\n        bpmTrigger.execute(execution.getProcessInstanceId(), BpmnModelUtils.parserTriggerParam(flowElement));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.exection;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.delegate.JavaDelegate;\n\n/**\n * 类型为 class 的 ExecutionListener 监听器示例\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class DemoDelegateClassExecutionListener implements JavaDelegate {\n\n    @Override\n    public void execute(DelegateExecution execution) {\n        log.info(\"[execute][execution({}) 被调用！变量有：{}]\", execution.getId(),\n                execution.getCurrentFlowableListener().getFieldExtensions());\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.exection;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.delegate.JavaDelegate;\nimport org.springframework.stereotype.Component;\n\n/**\n * 类型为 delegateExpression 的 ExecutionListener 监听器示例\n *\n * 和 {@link DemoDelegateClassExecutionListener} 的差异是，需要注册到 Spring 中\n */\n@Component\n@Slf4j\npublic class DemoDelegateExpressionExecutionListener implements JavaDelegate {\n\n    @Override\n    public void execute(DelegateExecution execution) {\n        log.info(\"[execute][execution({}) 被调用！变量有：{}]\", execution.getId(),\n                execution.getCurrentFlowableListener().getFieldExtensions());\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.exection;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.springframework.stereotype.Component;\n\n/**\n * 类型为 expression 的 ExecutionListener 监听器示例\n *\n * 和 {@link DemoDelegateClassExecutionListener} 的差异是，需要注册到 Spring 中，但不用实现 {@link org.flowable.engine.delegate.JavaDelegate} 接口\n */\n@Component\n@Slf4j\npublic class DemoSpringExpressionExecutionListener {\n\n    public void execute(DelegateExecution execution) {\n        log.info(\"[execute][execution({}) 被调用！变量有：{}]\", execution.getId(),\n                execution.getCurrentFlowableListener().getFieldExtensions());\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.task;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.engine.delegate.TaskListener;\nimport org.flowable.task.service.delegate.DelegateTask;\n\n/**\n * 类型为 class 的 TaskListener 监听器示例\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class DemoDelegateClassTaskListener implements TaskListener {\n\n    @Override\n    public void notify(DelegateTask delegateTask) {\n        log.info(\"[execute][task({}) 被调用]\", delegateTask.getId());\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.task;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.engine.delegate.TaskListener;\nimport org.flowable.task.service.delegate.DelegateTask;\nimport org.springframework.stereotype.Component;\n\n/**\n * 类型为 delegateExpression 的 TaskListener 监听器示例\n *\n * @author 芋道源码\n */\n@Component\n@Slf4j\npublic class DemoDelegateExpressionTaskListener implements TaskListener {\n\n    @Override\n    public void notify(DelegateTask delegateTask) {\n        log.info(\"[execute][task({}) 被调用]\", delegateTask.getId());\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.task;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.task.service.delegate.DelegateTask;\nimport org.springframework.stereotype.Component;\n\n/**\n * 类型为 expression 的 TaskListener 监听器示例\n *\n * @author 芋道源码\n */\n@Slf4j\n@Component\npublic class DemoSpringExpressionTaskListener {\n\n    public void notify(DelegateTask delegateTask) {\n        log.info(\"[execute][task({}) 被调用]\", delegateTask.getId());\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringUtils;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.http.*;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.client.RestClientException;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_CALL_ERROR;\n\n/**\n * 工作流发起 HTTP 请求工具类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class BpmHttpRequestUtils {\n\n    public static void executeBpmHttpRequest(ProcessInstance processInstance,\n                                             String url,\n                                             List<BpmSimpleModelNodeVO.HttpRequestParam> headerParams,\n                                             List<BpmSimpleModelNodeVO.HttpRequestParam> bodyParams,\n                                             Boolean handleResponse,\n                                             List<KeyValue<String, String>> response) {\n        BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class);\n\n        // 1.1 设置请求头\n        MultiValueMap<String, String> headers = buildHttpHeaders(processInstance, headerParams);\n        // 1.2 设置请求体\n        MultiValueMap<String, String> body = buildHttpBody(processInstance, bodyParams);\n\n        // 2. 发起请求\n        RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);\n        ResponseEntity<String> responseEntity = sendHttpRequest(url, headers, body, restTemplate);\n\n        // 3. 处理返回\n        if (Boolean.FALSE.equals(handleResponse)) {\n            return;\n        }\n        // 3.1 判断是否需要解析返回值\n        if (responseEntity == null\n                || StrUtil.isEmpty(responseEntity.getBody())\n                || !responseEntity.getStatusCode().is2xxSuccessful()\n                || CollUtil.isEmpty(response)) {\n            return;\n        }\n        // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。\n        CommonResult<Map<String, Object>> respResult = JsonUtils.parseObjectQuietly(responseEntity.getBody(),\n                new TypeReference<CommonResult<Map<String, Object>>>() {});\n        if (respResult == null || !respResult.isSuccess()) {\n            return;\n        }\n        // 3.3 获取需要更新的流程变量\n        Map<String, Object> updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), response);\n        // 3.4 更新流程变量\n        if (CollUtil.isNotEmpty(updateVariables)) {\n            processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables);\n        }\n    }\n\n    public static void executeBpmHttpRequest(BpmProcessInstanceStatusEvent event,\n                                             String url) {\n        RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);\n        executeBpmHttpRequest(event, url, restTemplate);\n    }\n\n    public static void executeBpmHttpRequest(BpmProcessInstanceStatusEvent event,\n                                             String url,\n                                             RestTemplate restTemplate) {\n        // 1.1 设置请求头\n        HttpHeaders headers = new HttpHeaders();\n        headers.setContentType(MediaType.APPLICATION_JSON);\n        if (TenantContextHolder.getTenantId() != null) {\n            headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId()));\n        } else {\n            BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class);\n            ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getId());\n            if (processInstance != null) {\n                headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId()));\n            }\n        }\n        // 1.2 设置请求体\n//        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();\n//        body.add(\"id\", event.getId());\n//        body.add(\"processDefinitionKey\", event.getProcessDefinitionKey());\n//        body.add(\"status\", event.getStatus().toString());\n//        if (StrUtil.isNotEmpty(event.getBusinessKey())) {\n//            body.add(\"businessKey\", event.getBusinessKey());\n//        }\n\n        // 2. 发起请求\n        sendHttpRequest(url, headers, event, restTemplate);\n    }\n\n    public static ResponseEntity<String> sendHttpRequest(String url,\n                                                         MultiValueMap<String, String> headers,\n                                                         Object body,\n                                                         RestTemplate restTemplate) {\n        HttpEntity<Object> requestEntity = new HttpEntity<>(body, headers);\n        ResponseEntity<String> responseEntity;\n        try {\n            responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);\n            log.info(\"[sendHttpRequest][HTTP 请求，请求头：{}，请求体：{}，响应结果：{}]\", headers, body, responseEntity);\n        } catch (RestClientException e) {\n            log.error(\"[sendHttpRequest][HTTP 请求，请求头：{}，请求体：{}，请求出错：{}]\", headers, body, e.getMessage());\n            throw exception(PROCESS_INSTANCE_HTTP_CALL_ERROR);\n        }\n        return responseEntity;\n    }\n\n    public static MultiValueMap<String, String> buildHttpHeaders(ProcessInstance processInstance,\n                                                                 List<BpmSimpleModelNodeVO.HttpRequestParam> headerSettings) {\n        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();\n        headers.add(HEADER_TENANT_ID, processInstance.getTenantId());\n        Map<String, Object> processVariables = processInstance.getProcessVariables();\n        addHttpRequestParam(headers, headerSettings, processVariables);\n        return headers;\n    }\n\n    public static MultiValueMap<String, String> buildHttpBody(ProcessInstance processInstance,\n                                                              List<BpmSimpleModelNodeVO.HttpRequestParam> bodySettings) {\n        Map<String, Object> processVariables = processInstance.getProcessVariables();\n        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();\n        addHttpRequestParam(body, bodySettings, processVariables);\n        if (!body.containsKey(\"processInstanceId\")) { // 避免重复添加\n            body.add(\"processInstanceId\", processInstance.getId());\n        }\n        return body;\n    }\n\n    /**\n     * 从请求返回值获取需要更新的流程变量\n     *\n     * @param result           请求返回结果\n     * @param responseSettings 返回设置\n     * @return 需要更新的流程变量\n     */\n    public static Map<String, Object> getNeedUpdatedVariablesFromResponse(Map<String, Object> result,\n                                                                          List<KeyValue<String, String>> responseSettings) {\n        Map<String, Object> updateVariables = new HashMap<>();\n        if (CollUtil.isEmpty(result)) {\n            return updateVariables;\n        }\n        responseSettings.forEach(responseSetting -> {\n            if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) {\n                updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue()));\n            }\n        });\n        return updateVariables;\n    }\n\n    /**\n     * 添加 HTTP 请求参数。请求头或者请求体\n     *\n     * @param params           HTTP 请求参数\n     * @param paramSettings    HTTP 请求参数设置\n     * @param processVariables 流程变量\n     */\n    public static void addHttpRequestParam(MultiValueMap<String, String> params,\n                                           List<BpmSimpleModelNodeVO.HttpRequestParam> paramSettings,\n                                           Map<String, Object> processVariables) {\n        if (CollUtil.isEmpty(paramSettings)) {\n            return;\n        }\n        paramSettings.forEach(item -> {\n            if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) {\n                params.add(item.getKey(), item.getValue());\n            } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) {\n                params.add(item.getKey(), processVariables.get(item.getValue()).toString());\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.*;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;\nimport com.google.common.collect.Maps;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.converter.BpmnXMLConverter;\nimport org.flowable.bpmn.model.*;\nimport org.flowable.bpmn.model.Process;\nimport org.flowable.common.engine.api.FlowableException;\nimport org.flowable.common.engine.api.delegate.Expression;\nimport org.flowable.common.engine.impl.util.io.BytesStreamSource;\n\nimport java.util.*;\n\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;\nimport static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;\nimport static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;\n\n/**\n * BPMN Model 操作工具类。目前分成三部分：\n *\n * 1. BPMN 修改 + 解析元素相关的方法\n * 2. BPMN 简单查找相关的方法\n * 3. BPMN 复杂遍历相关的方法\n * 4. BPMN 流程预测相关的方法\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class BpmnModelUtils {\n\n    // ========== BPMN 修改 + 解析元素相关的方法 ==========\n\n    public static void addExtensionElement(FlowElement element, String name, String value) {\n        if (value == null) {\n            return;\n        }\n        ExtensionElement extensionElement = new ExtensionElement();\n        extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);\n        extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);\n        extensionElement.setElementText(value);\n        extensionElement.setName(name);\n        element.addExtensionElement(extensionElement);\n    }\n\n    public static void addExtensionElement(FlowElement element, String name, Integer value) {\n        if (value == null) {\n            return;\n        }\n        addExtensionElement(element, name, String.valueOf(value));\n    }\n\n    public static void addExtensionElementJson(FlowElement element, String name, Object value) {\n        if (value == null) {\n            return;\n        }\n        addExtensionElement(element, name, JsonUtils.toJsonString(value));\n    }\n\n    public static void addExtensionElement(FlowElement element, String name, Map<String, String> attributes) {\n        if (attributes == null) {\n            return;\n        }\n        ExtensionElement extensionElement = new ExtensionElement();\n        extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);\n        extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);\n        extensionElement.setName(name);\n        attributes.forEach((key, value) -> {\n            ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value);\n            extensionElement.addAttribute(extensionAttribute);\n        });\n        element.addExtensionElement(extensionElement);\n    }\n\n    /**\n     * 解析扩展元素\n     *\n     * @param flowElement 节点\n     * @param elementName 元素名称\n     * @return 扩展元素\n     */\n    public static String parseExtensionElement(FlowElement flowElement, String elementName) {\n        if (flowElement == null) {\n            return null;\n        }\n        ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName));\n        return element != null ? element.getElementText() : null;\n    }\n\n    /**\n     * 给节点添加候选人元素\n     *\n     * @param candidateStrategy 候选人策略\n     * @param candidateParam 候选人参数，允许空\n     * @param flowElement 节点\n     */\n    public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) {\n        addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,\n                candidateStrategy == null ? null : candidateStrategy.toString());\n        addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam);\n    }\n\n    /**\n     * 解析候选人策略\n     *\n     * @param userTask 任务节点\n     * @return 候选人策略\n     */\n    public static Integer parseCandidateStrategy(FlowElement userTask) {\n        Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue(\n                BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));\n        // TODO @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限\n        if (candidateStrategy == null) {\n            ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));\n            candidateStrategy = element != null ? NumberUtils.parseInt(element.getElementText()) : null;\n        }\n        return candidateStrategy;\n    }\n\n    /**\n     * 解析候选人参数\n     *\n     * @param userTask 任务节点\n     * @return 候选人参数\n     */\n    public static String parseCandidateParam(FlowElement userTask) {\n        String candidateParam = userTask.getAttributeValue(\n                BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);\n        if (candidateParam == null) {\n            ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));\n            candidateParam = element != null ? element.getElementText() : null;\n        }\n        return candidateParam;\n    }\n\n    /**\n     * 解析审批类型\n     *\n     * @see BpmUserTaskApproveTypeEnum\n     * @param userTask 任务节点\n     * @return 审批类型\n     */\n    public static Integer parseApproveType(FlowElement userTask) {\n        return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE));\n    }\n\n    /**\n     * 解析子流程多实例来源类型\n     *\n     * @see BpmChildProcessMultiInstanceSourceTypeEnum\n     * @param element 任务节点\n     * @return 多实例来源类型\n     */\n    public static Integer parseMultiInstanceSourceType(FlowElement element) {\n        return NumberUtils.parseInt(parseExtensionElement(element, BpmnModelConstants.CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE));\n    }\n\n    /**\n     * 添加任务拒绝处理元素\n     *\n     * @param rejectHandler 任务拒绝处理\n     * @param userTask 任务节点\n     */\n    public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) {\n        if (rejectHandler == null) {\n            return;\n        }\n        addExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType()));\n        addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId());\n    }\n\n    /**\n     * 解析任务拒绝处理类型\n     *\n     * @param userTask 任务节点\n     * @return 任务拒绝处理类型\n     */\n    public static BpmUserTaskRejectHandlerTypeEnum parseRejectHandlerType(FlowElement userTask) {\n        Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE));\n        return BpmUserTaskRejectHandlerTypeEnum.typeOf(rejectHandlerType);\n    }\n\n    /**\n     * 解析任务拒绝返回任务节点 ID\n     *\n     * @param flowElement 任务节点\n     * @return 任务拒绝返回任务节点 ID\n     */\n    public static String parseReturnTaskId(FlowElement flowElement) {\n        return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);\n    }\n\n    /**\n     * 给节点添加用户任务的审批人与发起人相同时，处理类型枚举\n     *\n     * @see BpmUserTaskAssignStartUserHandlerTypeEnum\n     * @param assignStartUserHandlerType 发起人处理类型\n     * @param userTask 任务节点\n     */\n    public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) {\n        if (assignStartUserHandlerType == null) {\n            return;\n        }\n        addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString());\n    }\n\n    /**\n     * 给节点添加用户任务的审批人为空时，处理类型枚举\n     *\n     * @see BpmUserTaskAssignEmptyHandlerTypeEnum\n     * @param emptyHandler 空处理\n     * @param userTask 任务节点\n     */\n    public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) {\n        if (emptyHandler == null) {\n            return;\n        }\n        addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType()));\n        addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(\",\", emptyHandler.getUserIds()));\n    }\n\n    /**\n     * 解析用户任务的审批人与发起人相同时，处理类型枚举\n     *\n     * @param userTask 任务节点\n     * @return 处理类型枚举\n     */\n    public static Integer parseAssignStartUserHandlerType(FlowElement userTask) {\n        return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE));\n    }\n\n    /**\n     * 解析用户任务的审批人为空时，处理类型枚举\n     *\n     * @param userTask 任务节点\n     * @return 处理类型枚举\n     */\n    public static Integer parseAssignEmptyHandlerType(FlowElement userTask) {\n        return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE));\n    }\n\n    /**\n     * 解析用户任务的审批人为空时，处理用户 ID 数组\n     *\n     * @param userTask 任务节点\n     * @return 处理用户 ID 数组\n     */\n    public static List<Long> parseAssignEmptyHandlerUserIds(FlowElement userTask) {\n        return StrUtils.splitToLong(parseExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS), \",\");\n    }\n\n    /**\n     * 给节点添加表单字段权限元素\n     *\n     * @param fieldsPermissions 表单字段权限\n     * @param flowElement 节点\n     */\n    public static void addFormFieldsPermission(List<Map<String, String>> fieldsPermissions, FlowElement flowElement) {\n        if (CollUtil.isNotEmpty(fieldsPermissions)) {\n            fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item));\n        }\n    }\n\n    /**\n     * 解析表单字段权限\n     *\n     * @param bpmnModel bpmnModel 对象\n     * @param flowElementId 元素 ID\n     * @return 表单字段权限\n     */\n    public static Map<String, String> parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) {\n        if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) {\n            return null;\n        }\n        FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);\n        if (flowElement == null) {\n            return null;\n        }\n        List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(FORM_FIELD_PERMISSION_ELEMENT);\n        if (CollUtil.isEmpty(extensionElements)) {\n            return null;\n        }\n        Map<String, String> fieldsPermission = MapUtil.newHashMap();\n        extensionElements.forEach(element -> {\n            String field = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_FIELD_ATTRIBUTE);\n            String permission = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_PERMISSION_ATTRIBUTE);\n            if (StrUtil.isNotEmpty(field) && StrUtil.isNotEmpty(permission)) {\n                fieldsPermission.put(field, permission);\n            }\n        });\n        return fieldsPermission;\n    }\n\n    /**\n     * 给节点添加操作按钮设置元素\n     */\n    public static void addButtonsSetting(List<BpmSimpleModelNodeVO.OperationButtonSetting> buttonsSetting, UserTask userTask) {\n        if (CollUtil.isNotEmpty(buttonsSetting)) {\n            List<Map<String, String>> list = CollectionUtils.convertList(buttonsSetting, item -> {\n                Map<String, String> settingMap = Maps.newHashMapWithExpectedSize(3);\n                settingMap.put(BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE, String.valueOf(item.getId()));\n                settingMap.put(BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE, item.getDisplayName());\n                settingMap.put(BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE, String.valueOf(item.getEnable()));\n                return settingMap;\n            });\n            list.forEach(item -> addExtensionElement(userTask, BUTTON_SETTING_ELEMENT, item));\n        }\n    }\n\n    /**\n     * 解析操作按钮设置\n     *\n     * @param bpmnModel bpmnModel 对象\n     * @param flowElementId 元素 ID\n     * @return 操作按钮设置\n     */\n    public static Map<Integer, BpmTaskRespVO.OperationButtonSetting> parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) {\n        FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);\n        if (flowElement == null) {\n            return null;\n        }\n        List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(BUTTON_SETTING_ELEMENT);\n        if (CollUtil.isEmpty(extensionElements)) {\n            return null;\n        }\n        Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonSettings = Maps.newHashMapWithExpectedSize(extensionElements.size());\n        extensionElements.forEach(element -> {\n            String id = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE);\n            String displayName = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE);\n            String enable = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE);\n            if (StrUtil.isNotEmpty(id)) {\n                BpmTaskRespVO.OperationButtonSetting setting = new BpmTaskRespVO.OperationButtonSetting();\n                buttonSettings.put(Integer.valueOf(id), setting.setDisplayName(displayName).setEnable(Boolean.parseBoolean(enable)));\n            }\n        });\n        return buttonSettings;\n    }\n\n    /**\n     * 解析边界事件扩展元素\n     *\n     * @param boundaryEvent 边界事件\n     * @param customElement 元素\n     * @return 扩展元素\n     */\n    public static String parseBoundaryEventExtensionElement(BoundaryEvent boundaryEvent, String customElement) {\n        if (boundaryEvent == null) {\n            return null;\n        }\n        ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(customElement));\n        return Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null);\n    }\n\n    public static void addSignEnable(Boolean signEnable, FlowElement userTask) {\n        addExtensionElement(userTask, SIGN_ENABLE,\n                ObjUtil.isNotNull(signEnable) ? signEnable.toString() : Boolean.FALSE.toString());\n    }\n\n    public static Boolean parseSignEnable(BpmnModel bpmnModel, String flowElementId) {\n        FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);\n        if (flowElement == null) {\n            return false;\n        }\n        List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(SIGN_ENABLE);\n        if (CollUtil.isEmpty(extensionElements)) {\n            return false;\n        }\n        return Convert.toBool(extensionElements.get(0).getElementText(), false);\n    }\n\n    public static void addReasonRequire(Boolean reasonRequire, FlowElement userTask) {\n        addExtensionElement(userTask, REASON_REQUIRE,\n                ObjUtil.isNotNull(reasonRequire) ? reasonRequire.toString() : Boolean.FALSE.toString());\n    }\n\n    public static Boolean parseReasonRequire(BpmnModel bpmnModel, String flowElementId) {\n        FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);\n        if (flowElement == null) {\n            return false;\n        }\n        List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(REASON_REQUIRE);\n        if (CollUtil.isEmpty(extensionElements)) {\n            return false;\n        }\n        return Convert.toBool(extensionElements.get(0).getElementText(), false);\n    }\n\n    public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) {\n        FieldExtension fieldExtension = new FieldExtension();\n        fieldExtension.setFieldName(\"listenerConfig\");\n        fieldExtension.setStringValue(JsonUtils.toJsonString(handler));\n        flowableListener.getFieldExtensions().add(fieldExtension);\n    }\n\n    public static BpmSimpleModelNodeVO.ListenerHandler parseListenerConfig(Expression fixedValue) {\n        String expressionText = fixedValue.getExpressionText();\n        Assert.notNull(expressionText, \"监听器扩展字段({})不能为空\", expressionText);\n        return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class);\n    }\n\n    public static BpmTriggerTypeEnum parserTriggerType(FlowElement flowElement) {\n        Integer triggerType = NumberUtils.parseInt(parseExtensionElement(flowElement, TRIGGER_TYPE));\n        return BpmTriggerTypeEnum.typeOf(triggerType);\n    }\n\n    public static String parserTriggerParam(FlowElement flowElement) {\n        return parseExtensionElement(flowElement, TRIGGER_PARAM);\n    }\n\n    /**\n     * 给节点添加节点类型\n     *\n     * @param nodeType 节点类型\n     * @param flowElement 节点\n     */\n    public static void addNodeType(Integer nodeType, FlowElement flowElement) {\n        addExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE, nodeType);\n    }\n\n    /**\n     * 解析节点类型\n     *\n     * @param flowElement 节点\n     * @return 节点类型\n     */\n    public static Integer parseNodeType(FlowElement flowElement) {\n        return NumberUtils.parseInt(parseExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE));\n    }\n\n    // ========== BPM 简单查找相关的方法 ==========\n\n    /**\n     * 根据节点，获取入口连线\n     *\n     * @param source 起始节点\n     * @return 入口连线列表\n     */\n    public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {\n        if (source instanceof FlowNode) {\n            return ((FlowNode) source).getIncomingFlows();\n        }\n        return new ArrayList<>();\n    }\n\n    /**\n     * 根据节点，获取出口连线\n     *\n     * @param source 起始节点\n     * @return 出口连线列表\n     */\n    public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {\n        if (source instanceof FlowNode) {\n            return ((FlowNode) source).getOutgoingFlows();\n        }\n        return new ArrayList<>();\n    }\n\n    /**\n     * 获取流程元素信息\n     *\n     * @param model         bpmnModel 对象\n     * @param flowElementId 元素 ID\n     * @return 元素信息\n     */\n    public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {\n        Process process = model.getMainProcess();\n        FlowElement flowElement = process.getFlowElement(flowElementId);\n        if (flowElement != null) {\n            return flowElement;\n        }\n        return model.getFlowElement(flowElementId);\n    }\n\n    /**\n     * 获得 BPMN 流程中，指定的元素们\n     *\n     * @param model 模型\n     * @param clazz 指定元素。例如说，{@link UserTask}、{@link Gateway} 等等\n     * @return 元素们\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {\n        List<T> result = new ArrayList<>();\n        model.getProcesses().forEach(process -> process.getFlowElements().forEach(flowElement -> {\n            if (flowElement.getClass().isAssignableFrom(clazz)) {\n                result.add((T) flowElement);\n            }\n        }));\n        return result;\n    }\n\n    public static StartEvent getStartEvent(BpmnModel model) {\n        Process process = model.getMainProcess();\n        // 从 initialFlowElement 找\n        FlowElement startElement = process.getInitialFlowElement();\n        if (startElement instanceof StartEvent) {\n            return (StartEvent) startElement;\n        }\n        // 从 flowElementList 找\n        return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent);\n    }\n\n    public static EndEvent getEndEvent(BpmnModel model) {\n        Process process = model.getMainProcess();\n        // 从 flowElementList 找 endEvent\n        return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent);\n    }\n\n    public static BpmnModel getBpmnModel(byte[] bpmnBytes) {\n        if (ArrayUtil.isEmpty(bpmnBytes)) {\n            return null;\n        }\n        BpmnXMLConverter converter = new BpmnXMLConverter();\n        // 补充说明：由于在 Flowable 中自定义了属性，所以 validateSchema 传递 false\n        return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false);\n    }\n\n    public static String getBpmnXml(BpmnModel model) {\n        if (model == null) {\n            return null;\n        }\n        BpmnXMLConverter converter = new BpmnXMLConverter();\n        return StrUtil.utf8Str(converter.convertToXML(model));\n    }\n\n    public static String getBpmnXml(byte[] bpmnBytes) {\n        if (ArrayUtil.isEmpty(bpmnBytes)) {\n            return null;\n        }\n        return StrUtil.utf8Str(bpmnBytes);\n    }\n\n    // ========== BPMN 复杂遍历相关的方法 ==========\n\n    /**\n     * 找到 source 节点之前的所有用户任务节点\n     *\n     * @param source          起始节点\n     * @param hasSequenceFlow 已经经过的连线的 ID，用于判断线路是否重复\n     * @param userTaskList    已找到的用户任务节点\n     * @return 用户任务节点 数组\n     */\n    public static List<UserTask> getPreviousUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {\n        userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;\n        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;\n        // 如果该节点为开始节点，且存在上级子节点，则顺着上级子节点继续迭代\n        if (source instanceof StartEvent && source.getSubProcess() != null) {\n            userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList);\n        }\n\n        // 根据类型，获取入口连线\n        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);\n        if (sequenceFlows == null) {\n            return userTaskList;\n        }\n        // 循环找到目标元素\n        for (SequenceFlow sequenceFlow : sequenceFlows) {\n            // 如果发现连线重复，说明循环了，跳过这个循环\n            if (hasSequenceFlow.contains(sequenceFlow.getId())) {\n                continue;\n            }\n            // 添加已经走过的连线\n            hasSequenceFlow.add(sequenceFlow.getId());\n            // 类型为用户节点，则新增父级节点\n            if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {\n                userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());\n            }\n            // 类型为子流程，则添加子流程开始节点出口处相连的节点\n            if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {\n                // 获取子流程用户任务节点\n                List<UserTask> childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);\n                // 如果找到节点，则说明该线路找到节点，不继续向下找，反之继续\n                if (CollUtil.isNotEmpty(childUserTaskList)) {\n                    userTaskList.addAll(childUserTaskList);\n                }\n            }\n            // 继续迭代\n            userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);\n        }\n        return userTaskList;\n    }\n\n    /**\n     * 迭代获取子流程用户任务节点\n     *\n     * @param source          起始节点\n     * @param hasSequenceFlow 已经经过的连线的 ID，用于判断线路是否重复\n     * @param userTaskList    需要撤回的用户任务列表\n     * @return 用户任务节点\n     */\n    public static List<UserTask> findChildProcessUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {\n        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;\n        userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;\n\n        // 根据类型，获取出口连线\n        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);\n        if (sequenceFlows == null) {\n            return userTaskList;\n        }\n        // 循环找到目标元素\n        for (SequenceFlow sequenceFlow : sequenceFlows) {\n            // 如果发现连线重复，说明循环了，跳过这个循环\n            if (hasSequenceFlow.contains(sequenceFlow.getId())) {\n                continue;\n            }\n            // 添加已经走过的连线\n            hasSequenceFlow.add(sequenceFlow.getId());\n            // 如果为用户任务类型，且任务节点的 Key 正在运行的任务中存在，添加\n            if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {\n                userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());\n                continue;\n            }\n            // 如果节点为子流程节点情况，则从节点中的第一个节点开始获取\n            if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {\n                List<UserTask> childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);\n                // 如果找到节点，则说明该线路找到节点，不继续向下找，反之继续\n                if (CollUtil.isNotEmpty(childUserTaskList)) {\n                    userTaskList.addAll(childUserTaskList);\n                    continue;\n                }\n            }\n            // 继续迭代\n            userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);\n        }\n        return userTaskList;\n    }\n\n    /**\n     * 迭代从后向前扫描，判断目标节点相对于当前节点是否是串行\n     * 不存在直接退回到子流程中的情况，但存在从子流程出去到父流程情况\n     *\n     * @param source          起始节点\n     * @param target          目标节点\n     * @param visitedElements 已经经过的连线的 ID，用于判断线路是否重复\n     * @return 结果\n     */\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {\n        visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;\n        // 不能是开始事件和子流程\n        if (source instanceof StartEvent && isInEventSubprocess(source)) {\n            return false;\n        }\n\n        // 根据类型，获取入口连线\n        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);\n        // 1. 没有入口连线，则返回 false\n        if (CollUtil.isEmpty(sequenceFlows)) {\n            return false;\n        }\n        // 2. 循环找目标元素, 找到目标节点\n        for (SequenceFlow sequenceFlow : sequenceFlows) {\n            // 如果发现连线重复，说明循环了，跳过这个循环\n            if (visitedElements.contains(sequenceFlow.getId())) {\n                continue;\n            }\n            // 添加已经走过的连线\n            visitedElements.add(sequenceFlow.getId());\n            // 这条线路存在目标节点，直接返回 true\n            FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();\n            if (target.getId().equals(sourceFlowElement.getId())) {\n                return true;\n            }\n            // 如果目标节点为并行网关，跳过这个循环 (TODO 疑问：这个判断作用是防止回退到并行网关分支上的节点吗？）\n            if (sourceFlowElement instanceof ParallelGateway) {\n                continue;\n            }\n            // 继续迭代，如果找到目标节点直接返回 true\n            if (isSequentialReachable(sourceFlowElement, target, visitedElements)) {\n                return true;\n            }\n        }\n        // 未找到返回 false\n        return false;\n    }\n\n    /**\n     * 判断当前节点是否属于不同的子流程\n     *\n     * @param flowElement 被判断的节点\n     * @return true 表示属于子流程\n     */\n    private static boolean isInEventSubprocess(FlowElement flowElement) {\n        FlowElementsContainer flowElementsContainer = flowElement.getParentContainer();\n        while (flowElementsContainer != null) {\n            if (flowElementsContainer instanceof EventSubProcess) {\n                return true;\n            }\n\n            if (flowElementsContainer instanceof FlowElement) {\n                flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();\n            } else {\n                flowElementsContainer = null;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 根据正在运行的任务节点，迭代获取子级任务节点列表，向后找\n     *\n     * @param source          起始节点\n     * @param runTaskKeyList  正在运行的任务 Key，用于校验任务节点是否是正在运行的节点\n     * @param hasSequenceFlow 已经经过的连线的 ID，用于判断线路是否重复\n     * @param userTaskList    需要撤回的用户任务列表\n     * @return 子级任务节点列表\n     */\n    public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList,\n                                                            Set<String> hasSequenceFlow, List<UserTask> userTaskList) {\n        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;\n        userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;\n        // 如果该节点为开始节点，且存在上级子节点，则顺着上级子节点继续迭代\n        if (source instanceof StartEvent && source.getSubProcess() != null) {\n            userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);\n        }\n\n        // 根据类型，获取出口连线\n        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);\n        if (sequenceFlows == null) {\n            return userTaskList;\n        }\n        // 循环找到目标元素\n        for (SequenceFlow sequenceFlow : sequenceFlows) {\n            // 如果发现连线重复，说明循环了，跳过这个循环\n            if (hasSequenceFlow.contains(sequenceFlow.getId())) {\n                continue;\n            }\n            // 添加已经走过的连线\n            hasSequenceFlow.add(sequenceFlow.getId());\n            // 如果为用户任务类型，且任务节点的 Key 正在运行的任务中存在，添加\n            if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {\n                userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());\n                continue;\n            }\n            // 如果节点为子流程节点情况，则从节点中的第一个节点开始获取\n            if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {\n                List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);\n                // 如果找到节点，则说明该线路找到节点，不继续向下找，反之继续\n                if (CollUtil.isNotEmpty(childUserTaskList)) {\n                    userTaskList.addAll(childUserTaskList);\n                    continue;\n                }\n            }\n            // 继续迭代\n            userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);\n        }\n        return userTaskList;\n    }\n\n    // ========== BPMN 流程预测相关的方法 ==========\n\n    /**\n     * 流程预测，返回 StartEvent、UserTask、ServiceTask、EndEvent 节点元素，最终是 List 串行结果\n     *\n     * @param bpmnModel BPMN 图\n     * @param variables 变量\n     * @return 节点元素数组\n     */\n    public static List<FlowElement> simulateProcess(BpmnModel bpmnModel, Map<String, Object> variables) {\n        List<FlowElement> resultElements = new ArrayList<>();\n        Set<FlowElement> visitElements = new HashSet<>();\n\n        // 从 StartEvent 开始遍历\n        StartEvent startEvent = getStartEvent(bpmnModel);\n        simulateNextFlowElements(startEvent, variables, resultElements, visitElements);\n\n        // 将 EndEvent 放在末尾。原因是，DFS 遍历，可能 EndEvent 在 resultElements 中\n        List<FlowElement> endEvents = CollUtil.removeWithAddIf(resultElements,\n                flowElement -> flowElement instanceof EndEvent);\n        resultElements.addAll(endEvents);\n        return resultElements;\n    }\n\n    private static void simulateNextFlowElements(FlowElement currentElement, Map<String, Object> variables,\n                                                 List<FlowElement> resultElements, Set<FlowElement> visitElements) {\n        // 如果为空，或者已经遍历过，则直接结束\n        if (currentElement == null) {\n            return;\n        }\n        if (visitElements.contains(currentElement)) {\n            return;\n        }\n        visitElements.add(currentElement);\n\n        // 情况：StartEvent/EndEvent/UserTask/ServiceTask\n        if (currentElement instanceof StartEvent\n                || currentElement instanceof EndEvent\n                || currentElement instanceof UserTask\n                || currentElement instanceof ServiceTask) {\n            // 添加节点\n            FlowNode flowNode = (FlowNode) currentElement;\n            resultElements.add(flowNode);\n\n            // 遍历子节点\n            flowNode.getOutgoingFlows().forEach(\n                    nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements));\n            return;\n        }\n\n        // 情况：ExclusiveGateway 排它，只有一个满足条件的。如果没有，就走默认的\n        if (currentElement instanceof ExclusiveGateway) {\n            // 查找满足条件的 SequenceFlow 路径\n            SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway((Gateway) currentElement, variables);\n            // 遍历满足条件的 SequenceFlow 路径\n            if (matchSequenceFlow != null) {\n                simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements);\n            }\n        }\n        // 情况：InclusiveGateway 包容，多个满足条件的。如果没有，就走默认的\n        else if (currentElement instanceof InclusiveGateway) {\n            // 查找满足条件的 SequenceFlow 路径\n            Collection<SequenceFlow> matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway((Gateway) currentElement, variables);\n            // 遍历满足条件的 SequenceFlow 路径\n            matchSequenceFlows.forEach(\n                    flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements));\n        }\n        // 情况：ParallelGateway 并行，都满足，都走\n        else if (currentElement instanceof ParallelGateway) {\n            Gateway gateway = (Gateway) currentElement;\n            // 遍历子节点\n            gateway.getOutgoingFlows().forEach(\n                    nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements));\n        }\n    }\n\n    /**\n     * 判断是否跳过此节点\n     *\n     * @param flowNode 节点\n     * @param variables 流程变量\n     */\n    public static boolean isSkipNode(FlowElement flowNode, Map<String, Object> variables) {\n        // 1. 检查节点是否有跳过表达式（支持多种任务节点类型）\n        String skipExpression = null;\n        if (flowNode instanceof UserTask) {\n            skipExpression = ((UserTask) flowNode).getSkipExpression();\n        } else if (flowNode instanceof ServiceTask) {\n            skipExpression = ((ServiceTask) flowNode).getSkipExpression();\n        } else if (flowNode instanceof ScriptTask) {\n            skipExpression = ((ScriptTask) flowNode).getSkipExpression();\n        }\n\n        if (StrUtil.isEmpty(skipExpression)) {\n            return false;\n        }\n\n        // 2. 计算跳过表达式的值\n        return evalConditionExpress(variables, skipExpression);\n    }\n\n    /**\n     * 根据当前节点，获取下一个节点\n     *\n     * @param currentElement 当前节点\n     * @param bpmnModel  BPMN模型\n     * @param variables 流程变量\n     */\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    public static List<FlowNode> getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel,\n                                                  Map<String, Object> variables){\n        List<FlowNode> nextFlowNodes = new ArrayList<>(); // 下一个执行的流程节点集合\n        FlowNode currentNode = (FlowNode) currentElement;  // 当前执行节点的基本属性\n        List<SequenceFlow> outgoingFlows = currentNode.getOutgoingFlows();  // 当前节点的关联节点\n        if (CollUtil.isEmpty(outgoingFlows)) {\n            log.warn(\"[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]\", currentNode.getId());\n            return nextFlowNodes;\n        }\n\n        // 遍历每个出口流\n        for (SequenceFlow outgoingFlow : outgoingFlows) {\n            // 获取目标节点的基本属性\n            FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef());\n            if (targetElement == null) {\n                continue;\n            }\n            // 如果是结束节点，直接返回\n            if (targetElement instanceof EndEvent) {\n                break;\n            }\n            // 情况一：处理不同类型的网关\n            if (targetElement instanceof Gateway) {\n                Gateway gateway = (Gateway) targetElement;\n                if (gateway instanceof ExclusiveGateway) {\n                    handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes);\n                } else if (gateway instanceof InclusiveGateway) {\n                    handleInclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes);\n                } else if (gateway instanceof ParallelGateway) {\n                    handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes);\n                }\n            } else {\n                // 情况二：如果不是网关，直接添加到下一个节点列表\n                nextFlowNodes.add((FlowNode) targetElement);\n            }\n        }\n        return nextFlowNodes;\n    }\n\n    /**\n     * 查找起始节点下一个用户任务列表列表\n     *\n     * @param source 起始节点\n     * @return 结果\n     */\n    public static List<UserTask> getNextUserTasks(FlowElement source) {\n        return getNextUserTasks(source, null, null);\n    }\n\n    /**\n     * 查找起始节点下一个用户任务列表列表\n     * @param source 起始节点\n     * @param hasSequenceFlow 已经经过的连线的 ID，用于判断线路是否重复\n     * @param userTaskList 用户任务列表\n     * @return 结果\n     */\n    public static List<UserTask> getNextUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {\n        hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>());\n        userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>());\n        // 获取出口连线\n        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);\n        if (!sequenceFlows.isEmpty()) {\n            for (SequenceFlow sequenceFlow : sequenceFlows) {\n                // 如果发现连线重复，说明循环了，跳过这个循环\n                if (hasSequenceFlow.contains(sequenceFlow.getId())) {\n                    continue;\n                }\n                // 添加已经走过的连线\n                hasSequenceFlow.add(sequenceFlow.getId());\n                FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();\n                if (targetFlowElement instanceof UserTask) {\n                    // 若节点为用户任务，加入到结果列表中\n                    userTaskList.add((UserTask) targetFlowElement);\n                } else {\n                    // 若节点非用户任务，继续递归查找下一个节点\n                    getNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList);\n                }\n            }\n        }\n        return userTaskList;\n    }\n\n    /**\n     * 处理排它网关\n     *\n     * @param gateway 排他网关\n     * @param bpmnModel BPMN模型\n     * @param variables 流程变量\n     * @param nextFlowNodes 下一个执行的流程节点集合\n     */\n    private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel,\n                                               Map<String, Object> variables, List<FlowNode> nextFlowNodes) {\n        // 查找满足条件的 SequenceFlow 路径\n        SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables);\n        // 遍历满足条件的 SequenceFlow 路径\n        if (matchSequenceFlow != null) {\n            FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef());\n            if (targetElement instanceof FlowNode) {\n                nextFlowNodes.add((FlowNode) targetElement);\n            }\n        }\n    }\n\n    /**\n     * 处理排它网关（Exclusive Gateway），选择符合条件的路径\n     *\n     * @param gateway 排他网关\n     * @param variables 流程变量\n     * @return 符合条件的路径\n     */\n    private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map<String, Object> variables) {\n        SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),\n                flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())\n                        && (evalConditionExpress(variables, flow.getConditionExpression())));\n        if (matchSequenceFlow == null) {\n            matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),\n                    flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));\n            // 特殊：没有默认的情况下，并且只有 1 个条件，则认为它是默认的\n            if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) {\n                matchSequenceFlow = gateway.getOutgoingFlows().get(0);\n            }\n        }\n        return matchSequenceFlow;\n    }\n\n    /**\n     * 处理包容网关\n     *\n     * @param gateway 排他网关\n     * @param bpmnModel BPMN模型\n     * @param variables 流程变量\n     * @param nextFlowNodes 下一个执行的流程节点集合\n     */\n    private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel,\n                                               Map<String, Object> variables, List<FlowNode> nextFlowNodes) {\n        // 查找满足条件的 SequenceFlow 路径集合\n        Collection<SequenceFlow> matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway(gateway, variables);\n        // 遍历满足条件的 SequenceFlow 路径，获取目标节点\n        matchSequenceFlows.forEach(flow -> {\n            FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef());\n            if (targetElement instanceof FlowNode) {\n                nextFlowNodes.add((FlowNode) targetElement);\n            }\n        });\n    }\n\n    /**\n     * 处理排它网关（Inclusive Gateway），选择符合条件的路径\n     *\n     * @param gateway 排他网关\n     * @param variables 流程变量\n     * @return 符合条件的路径\n     */\n    private static Collection<SequenceFlow> findMatchSequenceFlowsByInclusiveGateway(Gateway gateway, Map<String, Object> variables) {\n        // 查找满足条件的 SequenceFlow 路径\n        Collection<SequenceFlow> matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(),\n                flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())\n                        && evalConditionExpress(variables, flow.getConditionExpression()));\n        if (CollUtil.isEmpty(matchSequenceFlows)) {\n            matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(),\n                    flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));\n            // 特殊：没有默认的情况下，并且只有 1 个条件，则认为它是默认的\n            if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) {\n                matchSequenceFlows = gateway.getOutgoingFlows();\n            }\n        }\n        return matchSequenceFlows;\n    }\n\n\n    /**\n     * 处理并行网关\n     *\n     * @param gateway 排他网关\n     * @param bpmnModel BPMN模型\n     * @param variables 流程变量\n     * @param nextFlowNodes 下一个执行的流程节点集合\n     */\n    private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel,\n                                              Map<String, Object> variables, List<FlowNode> nextFlowNodes) {\n        // 并行网关，遍历所有出口路径，获取目标节点\n        gateway.getOutgoingFlows().forEach(flow -> {\n            FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef());\n            if (targetElement instanceof FlowNode) {\n                nextFlowNodes.add((FlowNode) targetElement);\n            }\n        });\n    }\n\n    /**\n     * 计算条件表达式是否为 true 满足条件\n     *\n     * @param variables 流程实例\n     * @param expression 条件表达式\n     * @return 是否满足条件\n     */\n    public static boolean evalConditionExpress(Map<String, Object> variables, String expression) {\n        if (StrUtil.isEmpty(expression)) {\n            return Boolean.FALSE;\n        }\n        // 如果 variables 为空，则创建一个的原因？可能 expression 的计算，不依赖于 variables\n        if (variables == null) {\n            variables = new HashMap<>();\n        }\n\n        // 执行计算\n        try {\n            Object result = FlowableUtils.getExpressionValue(variables, expression);\n            return Boolean.TRUE.equals(result);\n        } catch (FlowableException ex) {\n            // 为什么使用 info 日志？原因是，expression 如果从 variables 取不到值，会报错。实际这种情况下，可以忽略\n            log.info(\"[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]\", expression, variables, ex);\n            return Boolean.FALSE;\n        }\n    }\n\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    public static boolean isSequentialUserTask(FlowElement flowElement) {\n        if (!(flowElement instanceof UserTask)) {\n            return false;\n        }\n        UserTask userTask = (UserTask) flowElement;\n        MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics();\n        return loopCharacteristics != null && loopCharacteristics.isSequential();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormFieldVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;\nimport lombok.SneakyThrows;\nimport org.flowable.common.engine.api.delegate.Expression;\nimport org.flowable.common.engine.api.variable.VariableContainer;\nimport org.flowable.common.engine.impl.el.ExpressionManager;\nimport org.flowable.common.engine.impl.identity.Authentication;\nimport org.flowable.common.engine.impl.variable.MapDelegateVariableContainer;\nimport org.flowable.engine.ManagementService;\nimport org.flowable.engine.ProcessEngineConfiguration;\nimport org.flowable.engine.history.HistoricProcessInstance;\nimport org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;\nimport org.flowable.engine.impl.util.CommandContextUtil;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.task.api.TaskInfo;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.Callable;\nimport java.util.stream.Collectors;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * Flowable 相关的工具方法\n *\n * @author 芋道源码\n */\npublic class FlowableUtils {\n\n    // ========== User 相关的工具方法 ==========\n\n    public static void setAuthenticatedUserId(Long userId) {\n        Authentication.setAuthenticatedUserId(String.valueOf(userId));\n    }\n\n    public static void clearAuthenticatedUserId() {\n        Authentication.setAuthenticatedUserId(null);\n    }\n\n    public static <V> V executeAuthenticatedUserId(Long userId, Callable<V> callable) {\n        setAuthenticatedUserId(userId);\n        try {\n            return callable.call();\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        } finally {\n            clearAuthenticatedUserId();\n        }\n    }\n\n    public static String getTenantId() {\n        Long tenantId = TenantContextHolder.getTenantId();\n        return tenantId != null ? String.valueOf(tenantId) : ProcessEngineConfiguration.NO_TENANT_ID;\n    }\n\n    public static void execute(String tenantIdStr, Runnable runnable) {\n        if (ObjectUtil.isEmpty(tenantIdStr)\n                || Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) {\n            runnable.run();\n        } else {\n            Long tenantId = Long.valueOf(tenantIdStr);\n            TenantUtils.execute(tenantId, runnable);\n        }\n    }\n\n    @SneakyThrows\n    public static <V> V execute(String tenantIdStr, Callable<V> callable) {\n        if (ObjectUtil.isEmpty(tenantIdStr)\n                || Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) {\n            return callable.call();\n        } else {\n            Long tenantId = Long.valueOf(tenantIdStr);\n            return TenantUtils.execute(tenantId, callable);\n        }\n    }\n\n    // ========== Execution 相关的工具方法 ==========\n\n    /**\n     * 格式化多实例（并签、或签）的 collectionVariable 变量（多实例对应的多审批人列表）\n     *\n     * @param activityId 活动编号\n     * @return collectionVariable 变量\n     */\n    public static String formatExecutionCollectionVariable(String activityId) {\n        return activityId + \"_assignees\";\n    }\n\n    /**\n     * 格式化多实例（并签、或签）的 collectionElementVariable 变量（当前实例对应的一个审批人）\n     *\n     * @param activityId 活动编号\n     * @return collectionElementVariable 变量\n     */\n    public static String formatExecutionCollectionElementVariable(String activityId) {\n        return activityId + \"_assignee\";\n    }\n\n    // ========== ProcessInstance 相关的工具方法 ==========\n\n    public static Integer getProcessInstanceStatus(ProcessInstance processInstance) {\n        return getProcessInstanceStatus(processInstance.getProcessVariables());\n    }\n\n    public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) {\n        return getProcessInstanceStatus(processInstance.getProcessVariables());\n    }\n\n    /**\n     * 获得流程实例的状态\n     *\n     * @param processVariables 流程实例的 variables\n     * @return 状态\n     */\n    private static Integer getProcessInstanceStatus(Map<String, Object> processVariables) {\n        return (Integer) processVariables.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);\n    }\n\n    /**\n     * 获得流程实例的审批原因\n     *\n     * @param processInstance 流程实例\n     * @return 审批原因\n     */\n    public static String getProcessInstanceReason(HistoricProcessInstance processInstance) {\n        return (String) processInstance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);\n    }\n\n    /**\n     * 获得流程实例的表单\n     *\n     * @param processInstance 流程实例\n     * @return 表单\n     */\n    public static Map<String, Object> getProcessInstanceFormVariable(ProcessInstance processInstance) {\n        Map<String, Object> processVariables = new HashMap<>(processInstance.getProcessVariables());\n        return filterProcessInstanceFormVariable(processVariables);\n    }\n\n    /**\n     * 获得流程实例的表单\n     *\n     * @param processInstance 流程实例\n     * @return 表单\n     */\n    public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {\n        Map<String, Object> processVariables = new HashMap<>(processInstance.getProcessVariables());\n        return filterProcessInstanceFormVariable(processVariables);\n    }\n\n    /**\n     * 过滤流程实例的表单\n     *\n     * 为什么要过滤？目前使用 processVariables 存储所有流程实例的拓展字段，需要过滤掉一部分的系统字段，从而实现表单的展示\n     *\n     * @param processVariables 流程实例的 variables\n     * @return 过滤后的表单\n     */\n    public static Map<String, Object> filterProcessInstanceFormVariable(Map<String, Object> processVariables) {\n        processVariables.remove(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);\n        return processVariables;\n    }\n\n    /**\n     * 获得流程实例的发起用户选择的审批人 Map\n     *\n     * @param processInstance 流程实例\n     * @return 发起用户选择的审批人 Map\n     */\n    public static Map<String, List<Long>> getStartUserSelectAssignees(ProcessInstance processInstance) {\n        return processInstance != null ? getStartUserSelectAssignees(processInstance.getProcessVariables()) : null;\n    }\n\n    /**\n     * 获得流程实例的发起用户选择的审批人 Map\n     *\n     * @param processVariables 流程变量\n     * @return 发起用户选择的审批人 Map\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static Map<String, List<Long>> getStartUserSelectAssignees(Map<String, Object> processVariables) {\n        if (processVariables == null) {\n            return new HashMap<>();\n        }\n        return (Map<String, List<Long>>) processVariables.get(\n                BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);\n    }\n\n    /**\n     * 获得流程实例的审批用户选择的下一个节点的审批人 Map\n     *\n     * @param processInstance 流程实例\n     * @return 审批用户选择的下一个节点的审批人Map\n     */\n    public static Map<String, List<Long>> getApproveUserSelectAssignees(ProcessInstance processInstance) {\n        return processInstance != null ? getApproveUserSelectAssignees(processInstance.getProcessVariables()) : null;\n    }\n\n    /**\n     * 获得流程实例的审批用户选择的下一个节点的审批人 Map\n     *\n     * @param processVariables 流程变量\n     * @return 审批用户选择的下一个节点的审批人Map Map\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static Map<String, List<Long>> getApproveUserSelectAssignees(Map<String, Object> processVariables) {\n        if (processVariables == null) {\n            return new HashMap<>();\n        }\n        return (Map<String, List<Long>>) processVariables.get(\n                BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES);\n    }\n\n    /**\n     * 获得流程实例的摘要\n     *\n     * 仅有 {@link BpmModelFormTypeEnum#getType()} 表单，才有摘要。\n     * 原因是，只有它才有表单项的配置，从而可以根据配置，展示摘要。\n     *\n     * @param processDefinitionInfo 流程定义\n     * @param processVariables      流程实例的 variables\n     * @return 摘要\n     */\n    public static List<KeyValue<String, String>> getSummary(BpmProcessDefinitionInfoDO processDefinitionInfo,\n                                                            Map<String, Object> processVariables) {\n        // 只有流程表单才会显示摘要！\n        if (ObjectUtil.isNull(processDefinitionInfo)\n                || !BpmModelFormTypeEnum.NORMAL.getType().equals(processDefinitionInfo.getFormType())) {\n            return null;\n        }\n\n        // 解析表单配置\n        Map<String, BpmFormFieldVO> formFieldsMap = new HashMap<>();\n        processDefinitionInfo.getFormFields().forEach(formFieldStr -> {\n            BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class);\n            if (formField != null) {\n                formFieldsMap.put(formField.getField(), formField);\n            }\n        });\n\n        // 情况一：当自定义了摘要\n        if (ObjectUtil.isNotNull(processDefinitionInfo.getSummarySetting())\n                && Boolean.TRUE.equals(processDefinitionInfo.getSummarySetting().getEnable())) {\n            return convertList(processDefinitionInfo.getSummarySetting().getSummary(), item -> {\n                BpmFormFieldVO formField = formFieldsMap.get(item);\n                if (formField != null) {\n                    return new KeyValue<String, String>(formField.getTitle(),\n                            processVariables.getOrDefault(item, \"\").toString());\n                }\n                return null;\n            });\n        }\n\n        // 情况二：默认摘要展示前三个表单字段\n        return formFieldsMap.entrySet().stream()\n                .limit(3)\n                .map(entry -> new KeyValue<>(entry.getValue().getTitle(),\n                        MapUtil.getStr(processVariables, entry.getValue().getField(), \"\")))\n                .collect(Collectors.toList());\n    }\n\n    // ========== Task 相关的工具方法 ==========\n\n    /**\n     * 获得任务的状态\n     *\n     * @param task 任务\n     * @return 状态\n     */\n    public static Integer getTaskStatus(TaskInfo task) {\n        return (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS);\n    }\n\n    /**\n     * 获得任务的审批原因\n     *\n     * @param task 任务\n     * @return 审批原因\n     */\n    public static String getTaskReason(TaskInfo task) {\n        return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_REASON);\n    }\n\n    /**\n     * 获得任务的签名图片 URL\n     *\n     * @param task 任务\n     * @return 签名图片 URL\n     */\n    public static String getTaskSignPicUrl(TaskInfo task) {\n        return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_SIGN_PIC_URL);\n    }\n\n    /**\n     * 获得任务的表单\n     *\n     * @param task 任务\n     * @return 表单\n     */\n    public static Map<String, Object> getTaskFormVariable(TaskInfo task) {\n        Map<String, Object> formVariables = new HashMap<>(task.getTaskLocalVariables());\n        filterTaskFormVariable(formVariables);\n        return formVariables;\n    }\n\n    /**\n     * 过滤任务的表单\n     *\n     * 为什么要过滤？目前使用 taskLocalVariables 存储所有任务的拓展字段，需要过滤掉一部分的系统字段，从而实现表单的展示\n     *\n     * @param taskLocalVariables 任务的 taskLocalVariables\n     * @return 过滤后的表单\n     */\n    public static Map<String, Object> filterTaskFormVariable(Map<String, Object> taskLocalVariables) {\n        taskLocalVariables.remove(BpmnVariableConstants.TASK_VARIABLE_STATUS);\n        taskLocalVariables.remove(BpmnVariableConstants.TASK_VARIABLE_REASON);\n        return taskLocalVariables;\n    }\n\n    // ========== Expression 相关的工具方法 ==========\n\n    private static Object getExpressionValue(VariableContainer variableContainer, String expressionString,\n                                             ProcessEngineConfigurationImpl processEngineConfiguration) {\n        assert processEngineConfiguration != null;\n        ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();\n        assert expressionManager != null;\n        Expression expression = expressionManager.createExpression(expressionString);\n        return expression.getValue(variableContainer);\n    }\n\n    public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {\n        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();\n        if (processEngineConfiguration != null) {\n            return getExpressionValue(variableContainer, expressionString, processEngineConfiguration);\n        }\n        // 如果 ProcessEngineConfigurationImpl 获取不到，则需要通过 ManagementService 来获取\n        ManagementService managementService = SpringUtil.getBean(ManagementService.class);\n        assert managementService != null;\n        return managementService.executeCommand(context ->\n                getExpressionValue(variableContainer, expressionString, CommandContextUtil.getProcessEngineConfiguration()));\n    }\n\n    public static Object getExpressionValue(Map<String, Object> variable, String expressionString) {\n        VariableContainer variableContainer = new MapDelegateVariableContainer(variable, VariableContainer.empty());\n        return getExpressionValue(variableContainer, expressionString);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.*;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups;\nimport cn.iocoder.yudao.module.bpm.enums.definition.*;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate;\nimport cn.iocoder.yudao.module.bpm.service.task.listener.BpmCallActivityListener;\nimport cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener;\nimport org.flowable.bpmn.BpmnAutoLayout;\nimport org.flowable.bpmn.constants.BpmnXMLConstants;\nimport org.flowable.bpmn.model.*;\nimport org.flowable.bpmn.model.Process;\nimport org.flowable.engine.delegate.ExecutionListener;\nimport org.flowable.engine.delegate.TaskListener;\n\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;\nimport static java.util.Arrays.asList;\n\n/**\n * 仿钉钉/飞书的模型相关的工具方法\n * <p>\n * 1. 核心的逻辑实现，可见 {@link #buildBpmnModel(String, String, BpmSimpleModelNodeVO)} 方法\n * 2. 所有的 BpmSimpleModelNodeVO 转换成 BPMN FlowNode 元素，可见 {@link NodeConvert} 实现类\n *\n * @author jason\n */\npublic class SimpleModelUtils {\n\n    private static final Map<BpmSimpleModelNodeTypeEnum, NodeConvert> NODE_CONVERTS = MapUtil.newHashMap();\n\n    static {\n        List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(),\n                new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(),\n                new DelayTimerNodeConvert(), new TriggerNodeConvert(),\n                new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(),\n                new ChildProcessConvert());\n        converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));\n    }\n\n    /**\n     * 仿钉钉流程设计模型数据结构（json）转换成 Bpmn Model\n     * <p>\n     * 整体逻辑如下：\n     * 1. 创建：BpmnModel、Process 对象\n     * 2. 转换：将 BpmSimpleModelNodeVO 转换成 BPMN FlowNode 元素\n     * 3. 连接：构建并添加节点之间的连线 Sequence Flow\n     *\n     * @param processId       流程标识\n     * @param processName     流程名称\n     * @param simpleModelNode 仿钉钉流程设计模型数据结构\n     * @return Bpmn Model\n     */\n    public static BpmnModel buildBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) {\n        // 1. 创建 BpmnModel\n        BpmnModel bpmnModel = new BpmnModel();\n        bpmnModel.setTargetNamespace(BpmnXMLConstants.BPMN2_NAMESPACE); // 设置命名空间。不加这个，解析 Message 会报 NPE 异常\n        // 创建 Process 对象\n        Process process = new Process();\n        process.setId(processId);\n        process.setName(processName);\n        process.setExecutable(Boolean.TRUE);\n        bpmnModel.addProcess(process);\n\n        // 2.1 创建 StartNode 节点\n        // 原因是：目前前端的第一个节点是“发起人节点”，所以这里构建一个 StartNode，用于创建 Bpmn 的 StartEvent 节点\n        BpmSimpleModelNodeVO startNode = buildStartNode();\n        startNode.setChildNode(simpleModelNode);\n        // 2.2 将前端传递的 simpleModelNode 数据结构（json），转换成从 BPMN FlowNode 元素，并添加到 Main Process 中\n        traverseNodeToBuildFlowNode(startNode, process);\n\n        // 3. 构建并添加节点之间的连线 Sequence Flow\n        EndEvent endEvent = getEndEvent(bpmnModel);\n        traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId());\n\n        // 4. 自动布局\n        new BpmnAutoLayout(bpmnModel).execute();\n        return bpmnModel;\n    }\n\n    private static BpmSimpleModelNodeVO buildStartNode() {\n        return new BpmSimpleModelNodeVO().setId(START_EVENT_NODE_ID)\n                .setName(BpmSimpleModelNodeTypeEnum.START_NODE.getName())\n                .setType(BpmSimpleModelNodeTypeEnum.START_NODE.getType());\n    }\n\n    /**\n     * 遍历节点，构建 FlowNode 元素\n     *\n     * @param node    SIMPLE 节点\n     * @param process BPMN 流程\n     */\n    private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) {\n        // 1. 判断是否有效节点\n        if (!isValidNode(node)) {\n            return;\n        }\n        BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType());\n        Assert.notNull(nodeType, \"模型节点类型({})不支持\", node.getType());\n\n        // 2. 处理当前节点\n        NodeConvert nodeConvert = NODE_CONVERTS.get(nodeType);\n        Assert.notNull(nodeConvert, \"模型节点类型的转换器({})不存在\", node.getType());\n        List<? extends FlowElement> flowElements = nodeConvert.convertList(node);\n        flowElements.forEach(process::addFlowElement);\n\n        // 3.1 情况一：如果当前是分支节点，并且存在条件节点，则处理每个条件的子节点\n        if (BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType())\n                && CollUtil.isNotEmpty(node.getConditionNodes())) {\n            // 注意：这里的 item.getChildNode() 处理的是每个条件的子节点，不是处理条件\n            node.getConditionNodes().forEach(item -> traverseNodeToBuildFlowNode(item.getChildNode(), process));\n        }\n\n        // 3.2 情况二：如果有“子”节点，则递归处理子节点\n        traverseNodeToBuildFlowNode(node.getChildNode(), process);\n    }\n\n    /**\n     * 遍历节点，构建 SequenceFlow 元素\n     *\n     * @param process      Bpmn 流程\n     * @param node         当前节点\n     * @param targetNodeId 目标节点 ID\n     */\n    private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {\n        // 1.1 无效节点返回\n        if (!isValidNode(node)) {\n            return;\n        }\n        // 1.2 END_NODE 直接返回\n        BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType());\n        Assert.notNull(nodeType, \"模型节点类型不支持\");\n        if (nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {\n            return;\n        }\n\n        // 2.1 情况一：普通节点\n        if (!BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType())) {\n            traverseNormalNodeToBuildSequenceFlow(process, node, targetNodeId);\n        } else {\n            // 2.2 情况二：分支节点\n            traverseBranchNodeToBuildSequenceFlow(process, node, targetNodeId);\n        }\n    }\n\n    /**\n     * 遍历普通（非条件）节点，构建 SequenceFlow 元素\n     *\n     * @param process      Bpmn 流程\n     * @param node         当前节点\n     * @param targetNodeId 目标节点 ID\n     */\n    private static void traverseNormalNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {\n        BpmSimpleModelNodeVO childNode = node.getChildNode();\n        boolean isChildNodeValid = isValidNode(childNode);\n        // 情况一：有“子”节点，则建立连线\n        // 情况二：没有“子节点”，则直接跟 targetNodeId 建立连线。例如说，结束节点、条件分支（分支节点的孩子节点或聚合节点）的最后一个节点\n        String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId;\n\n        // 如果没有附加节点：则直接建立连线\n        if (StrUtil.isEmpty(node.getAttachNodeId())) {\n            SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId);\n            process.addFlowElement(sequenceFlow);\n        } else {\n            // 如果有附加节点：需要先建立和附加节点的连线，再建立附加节点和目标节点的连线。例如说，触发器节点（HTTP 回调）\n            List<SequenceFlow> sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), finalTargetNodeId);\n            sequenceFlows.forEach(process::addFlowElement);\n        }\n\n        // 因为有子节点，递归调用后续子节点\n        if (isChildNodeValid) {\n            traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId);\n        }\n    }\n\n    /**\n     * 构建有附加节点的连线\n     *\n     * @param nodeId       当前节点 ID\n     * @param attachNodeId 附属节点 ID\n     * @param targetNodeId 目标节点 ID\n     */\n    private static List<SequenceFlow> buildAttachNodeSequenceFlow(String nodeId, String attachNodeId, String targetNodeId) {\n        SequenceFlow sequenceFlow = buildBpmnSequenceFlow(nodeId, attachNodeId, null, null, null);\n        SequenceFlow attachSequenceFlow = buildBpmnSequenceFlow(attachNodeId, targetNodeId, null, null, null);\n        return CollUtil.newArrayList(sequenceFlow, attachSequenceFlow);\n    }\n\n    /**\n     * 遍历条件节点，构建 SequenceFlow 元素\n     *\n     * @param process      Bpmn 流程\n     * @param node         当前节点\n     * @param targetNodeId 目标节点 ID\n     */\n    private static void traverseBranchNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {\n        BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType());\n        BpmSimpleModelNodeVO childNode = node.getChildNode();\n        List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();\n        // TODO @芋艿 路由分支没有conditionNodes 这里注释会影响吗？@jason：一起帮忙瞅瞅！\n//        Assert.notEmpty(conditionNodes, \"分支节点的条件节点不能为空\");\n        // 分支终点节点 ID\n        String branchEndNodeId = null;\n        if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE\n                || nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { // 条件分支或路由分支\n            // 分两种情况 1. 分支节点有孩子节点为孩子节点 Id 2. 分支节点孩子为无效节点时 (分支嵌套且为分支最后一个节点) 为分支终点节点 ID\n            branchEndNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId;\n        } else if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE\n                || nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) {  // 并行分支或包容分支\n            // 分支节点：分支终点节点 Id 为程序创建的网关集合节点。目前不会从前端传入。\n            branchEndNodeId = buildGatewayJoinId(node.getId());\n        }\n        Assert.notEmpty(branchEndNodeId, \"分支终点节点 Id 不能为空\");\n\n        // 3. 遍历分支节点\n        if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) {\n            // 路由分支遍历\n            for (BpmSimpleModelNodeVO.RouterSetting router : node.getRouterGroups()) {\n                SequenceFlow sequenceFlow = RouteBranchNodeConvert.buildSequenceFlow(node.getId(), router);\n                process.addFlowElement(sequenceFlow);\n            }\n        } else {\n            // 下面的注释，以如下情况举例子。分支 1：A->B->C->D->E，分支 2：A->D->E。其中，A 为分支节点, D 为 A 孩子节点\n            for (BpmSimpleModelNodeVO item : conditionNodes) {\n                Assert.isTrue(Objects.equals(item.getType(), BpmSimpleModelNodeTypeEnum.CONDITION_NODE.getType()),\n                        \"条件节点类型({})不符合\", item.getType());\n                BpmSimpleModelNodeVO conditionChildNode = item.getChildNode();\n                // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况\n                if (isValidNode(conditionChildNode)) {\n                    // 3.1.1 建立与后续的节点的连线。例如说，建立 A->B 的连线\n                    SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), nodeType, item);\n                    process.addFlowElement(sequenceFlow);\n                    // 3.1.2 递归调用后续节点连线。例如说，建立 B->C->D 的连线\n                    traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId);\n                } else {\n                    // 3.2 分支没有后续节点。例如说，建立 A->D 的连线\n                    SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, nodeType, item);\n                    process.addFlowElement(sequenceFlow);\n                }\n            }\n        }\n\n        // 4.1 如果是并行分支、包容分支，由于是程序创建的聚合网关，需要手工创建聚合网关和下一个节点的连线\n        if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE\n                || nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) {\n            String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId;\n            SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId);\n            process.addFlowElement(sequenceFlow);\n            // 4.2 如果是路由分支，需要连接后续节点为默认路由\n        } else if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) {\n            SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getRouterDefaultFlowId(),\n                    null, null);\n            process.addFlowElement(sequenceFlow);\n        }\n\n        // 5. 递归调用后续节点 继续递归。例如说，建立 D->E 的连线\n        traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId);\n    }\n\n    private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId) {\n        return buildBpmnSequenceFlow(sourceId, targetId, null, null, null);\n    }\n\n    private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId,\n                                                      String sequenceFlowId, String sequenceFlowName,\n                                                      String conditionExpression) {\n        Assert.notEmpty(sourceId, \"sourceId 不能为空\");\n        Assert.notEmpty(targetId, \"targetId 不能为空\");\n        // TODO @jason：如果 sequenceFlowId 不存在的时候，是不是要生成一个默认的 sequenceFlowId？ @芋艿： 貌似不需要,Flowable 会默认生成；TODO @jason：建议还是搞一个，主要是后续好排查问题。\n        // TODO @jason：如果 name 不存在的时候，是不是要生成一个默认的 name？ @芋艿： 不需要生成默认的吧？ 这个会在流程图展示的， 一般用户填写的。不好生成默认的吧；TODO @jason：建议还是搞一个，主要是后续好排查问题。\n        SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);\n        if (StrUtil.isNotEmpty(sequenceFlowId)) {\n            sequenceFlow.setId(sequenceFlowId);\n        }\n        if (StrUtil.isNotEmpty(sequenceFlowName)) {\n            sequenceFlow.setName(sequenceFlowName);\n        }\n        if (StrUtil.isNotEmpty(conditionExpression)) {\n            sequenceFlow.setConditionExpression(conditionExpression);\n        }\n        return sequenceFlow;\n    }\n\n    public static boolean isValidNode(BpmSimpleModelNodeVO node) {\n        return node != null && node.getId() != null;\n    }\n\n    public static boolean isSequentialApproveNode(BpmSimpleModelNodeVO node) {\n        return BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType().equals(node.getType())\n                && BpmUserTaskApproveMethodEnum.SEQUENTIAL.getMethod().equals(node.getApproveMethod());\n    }\n\n    // ========== 各种 convert 节点的方法: BpmSimpleModelNodeVO => BPMN FlowElement ==========\n\n    private interface NodeConvert {\n\n        default List<? extends FlowElement> convertList(BpmSimpleModelNodeVO node) {\n            return Collections.singletonList(convert(node));\n        }\n\n        default FlowElement convert(BpmSimpleModelNodeVO node) {\n            throw new UnsupportedOperationException(\"请实现该方法\");\n        }\n\n        BpmSimpleModelNodeTypeEnum getType();\n\n    }\n\n    private static class StartNodeConvert implements NodeConvert {\n\n        @Override\n        public StartEvent convert(BpmSimpleModelNodeVO node) {\n            StartEvent startEvent = new StartEvent();\n            startEvent.setId(node.getId());\n            startEvent.setName(node.getName());\n            return startEvent;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.START_NODE;\n        }\n\n    }\n\n    private static class EndNodeConvert implements NodeConvert {\n\n        @Override\n        public EndEvent convert(BpmSimpleModelNodeVO node) {\n            EndEvent endEvent = new EndEvent();\n            endEvent.setId(node.getId());\n            endEvent.setName(node.getName());\n            // TODO @芋艿 + jason：要不要加一个终止定义？\n            return endEvent;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.END_NODE;\n        }\n\n    }\n\n    private static class StartUserNodeConvert implements NodeConvert {\n\n        @Override\n        public UserTask convert(BpmSimpleModelNodeVO node) {\n            UserTask userTask = new UserTask();\n            userTask.setId(node.getId());\n            userTask.setName(node.getName());\n\n            // 人工审批\n            addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType());\n            // 候选人策略为发起人自己\n            addCandidateElements(BpmTaskCandidateStrategyEnum.START_USER.getStrategy(), null, userTask);\n            // 添加表单字段权限属性元素\n            addFormFieldsPermission(node.getFieldsPermission(), userTask);\n            // 添加操作按钮配置属性元素\n            addButtonsSetting(node.getButtonsSetting(), userTask);\n            // 使用自动通过策略\n            // TODO @芋艿 复用了SKIP， 是否需要新加一个策略；TODO @芋艿：【回复】是不是应该类似飞书，搞个草稿状态。待定；还有一种策略，不标记自动通过，而是首次发起后，第一个节点，自动通过；\n            addAssignStartUserHandlerType(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType(), userTask);\n            return userTask;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.START_USER_NODE;\n        }\n\n    }\n\n    private static class ApproveNodeConvert implements NodeConvert {\n\n        @Override\n        public List<FlowElement> convertList(BpmSimpleModelNodeVO node) {\n            List<FlowElement> flowElements = new ArrayList<>(2);\n            // 1. 构建用户任务\n            UserTask userTask = buildBpmnUserTask(node);\n            flowElements.add(userTask);\n\n            // 2. 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理\n            if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {\n                BoundaryEvent boundaryEvent = buildUserTaskTimeoutBoundaryEvent(userTask, node.getTimeoutHandler());\n                flowElements.add(boundaryEvent);\n            }\n            return flowElements;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.APPROVE_NODE;\n        }\n\n        /**\n         * 添加 UserTask 用户的审批超时 BoundaryEvent 事件\n         *\n         * @param userTask       审批任务\n         * @param timeoutHandler 超时处理器\n         * @return BoundaryEvent 超时事件\n         */\n        private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask,\n                                                                BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) {\n            // 1. 创建 Timeout Boundary Event\n            String timeCycle = null;\n            if (Objects.equals(BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType(), timeoutHandler.getType()) &&\n                    timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) {\n                timeCycle = String.format(\"R%d/%s\",\n                        timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration());\n            }\n            BoundaryEvent boundaryEvent = buildTimeoutBoundaryEvent(userTask, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT.getType(),\n                    timeoutHandler.getTimeDuration(), timeCycle, null);\n\n            // 2 添加超时执行动作元素\n            addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, timeoutHandler.getType());\n            return boundaryEvent;\n        }\n\n        private UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) {\n            UserTask userTask = new UserTask();\n            userTask.setId(node.getId());\n            userTask.setName(node.getName());\n\n            // 如果不是审批人节点，则直接返回\n            addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, node.getApproveType());\n            if (ObjectUtil.notEqual(node.getApproveType(), BpmUserTaskApproveTypeEnum.USER.getType())) {\n                return userTask;\n            }\n\n            // 添加候选人元素\n            addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask);\n            // 添加表单字段权限属性元素\n            addFormFieldsPermission(node.getFieldsPermission(), userTask);\n            // 添加操作按钮配置属性元素\n            addButtonsSetting(node.getButtonsSetting(), userTask);\n            // 处理多实例（审批方式）\n            processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask);\n            // 添加任务被拒绝的处理元素\n            addTaskRejectElements(node.getRejectHandler(), userTask);\n            // 添加用户任务的审批人与发起人相同时的处理元素\n            addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask);\n            // 添加用户任务的空处理元素\n            addAssignEmptyHandlerType(node.getAssignEmptyHandler(), userTask);\n            //  设置审批任务的截止时间\n            if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {\n                userTask.setDueDate(node.getTimeoutHandler().getTimeDuration());\n            }\n            // 设置监听器\n            addUserTaskListener(node, userTask);\n            // 添加是否需要签名\n            addSignEnable(node.getSignEnable(), userTask);\n            // 审批意见\n            addReasonRequire(node.getReasonRequire(), userTask);\n            // 节点类型\n            addNodeType(node.getType(), userTask);\n            // 添加跳过表达式\n            if (StrUtil.isNotEmpty(node.getSkipExpression())) {\n                userTask.setSkipExpression(node.getSkipExpression());\n            }\n            return userTask;\n        }\n\n        private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) {\n            List<FlowableListener> flowableListeners = new ArrayList<>(3);\n            if (node.getTaskCreateListener() != null\n                    && Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) {\n                FlowableListener flowableListener = new FlowableListener();\n                flowableListener.setEvent(TaskListener.EVENTNAME_CREATE);\n                flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);\n                flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION);\n                addListenerConfig(flowableListener, node.getTaskCreateListener());\n                flowableListeners.add(flowableListener);\n            }\n            if (node.getTaskAssignListener() != null\n                    && Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) {\n                FlowableListener flowableListener = new FlowableListener();\n                flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT);\n                flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);\n                flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION);\n                addListenerConfig(flowableListener, node.getTaskAssignListener());\n                flowableListeners.add(flowableListener);\n            }\n            if (node.getTaskCompleteListener() != null\n                    && Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) {\n                FlowableListener flowableListener = new FlowableListener();\n                flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE);\n                flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);\n                flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION);\n                addListenerConfig(flowableListener, node.getTaskCompleteListener());\n                flowableListeners.add(flowableListener);\n            }\n            if (CollUtil.isNotEmpty(flowableListeners)) {\n                userTask.setTaskListeners(flowableListeners);\n            }\n        }\n\n        private void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) {\n            BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod);\n            Assert.notNull(approveMethodEnum, \"审批方式({})不能为空\", approveMethodEnum);\n            // 添加审批方式的扩展属性\n            addExtensionElement(userTask, USER_TASK_APPROVE_METHOD, approveMethod);\n            if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RANDOM) {\n                // 随机审批，不需要设置多实例属性\n                return;\n            }\n\n            // 处理多实例审批方式\n            MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics();\n            // 设置 collectionVariable。本系统用不到，仅仅为了 Flowable 校验不报错\n            multiInstanceCharacteristics.setInputDataItem(\"${coll_userList}\");\n            if (approveMethodEnum == BpmUserTaskApproveMethodEnum.ANY) {\n                multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition());\n                multiInstanceCharacteristics.setSequential(false);\n            } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.SEQUENTIAL) {\n                multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition());\n                multiInstanceCharacteristics.setSequential(true);\n                multiInstanceCharacteristics.setLoopCardinality(\"1\");\n            } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RATIO) {\n                Assert.notNull(approveRatio, \"通过比例不能为空\");\n                multiInstanceCharacteristics.setCompletionCondition(\n                        String.format(approveMethodEnum.getCompletionCondition(), String.format(\"%.2f\", approveRatio / 100D)));\n                multiInstanceCharacteristics.setSequential(false);\n            }\n            userTask.setLoopCharacteristics(multiInstanceCharacteristics);\n        }\n\n    }\n\n    private static class TransactorNodeConvert extends ApproveNodeConvert {\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE;\n        }\n\n    }\n\n    private static class CopyNodeConvert implements NodeConvert {\n\n        @Override\n        public ServiceTask convert(BpmSimpleModelNodeVO node) {\n            ServiceTask serviceTask = new ServiceTask();\n            serviceTask.setId(node.getId());\n            serviceTask.setName(node.getName());\n            serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);\n            serviceTask.setImplementation(\"${\" + BpmCopyTaskDelegate.BEAN_NAME + \"}\");\n\n            // 添加抄送候选人元素\n            addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), serviceTask);\n            // 添加表单字段权限属性元素\n            addFormFieldsPermission(node.getFieldsPermission(), serviceTask);\n            return serviceTask;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.COPY_NODE;\n        }\n\n    }\n\n    private static class ConditionBranchNodeConvert implements NodeConvert {\n\n        @Override\n        public ExclusiveGateway convert(BpmSimpleModelNodeVO node) {\n            ExclusiveGateway exclusiveGateway = new ExclusiveGateway();\n            exclusiveGateway.setId(node.getId());\n            // TODO @jason：setName\n\n            // 设置默认的序列流（条件）\n            BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(),\n                    item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow()));\n            Assert.notNull(defaultSeqFlow, \"条件分支节点({})的默认序列流不能为空\", node.getId());\n            exclusiveGateway.setDefaultFlow(defaultSeqFlow.getId());\n            return exclusiveGateway;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE;\n        }\n\n    }\n\n    private static class ParallelBranchNodeConvert implements NodeConvert {\n\n        /**\n         * 并行分支使用包容网关。需要设置所有出口条件表达式的值为 true 。原因是，解决 https://t.zsxq.com/m6GXh 反馈问题\n         *\n         * @see {@link ConditionNodeConvert#buildSequenceFlow}\n         */\n        @Override\n        public List<InclusiveGateway> convertList(BpmSimpleModelNodeVO node) {\n\n            InclusiveGateway inclusiveGateway = new InclusiveGateway();\n            inclusiveGateway.setId(node.getId());\n            // TODO @jason：setName\n\n            // 合并网关 由程序创建，前端不需要传入\n            InclusiveGateway joinParallelGateway = new InclusiveGateway();\n            joinParallelGateway.setId(buildGatewayJoinId(node.getId()));\n            // TODO @jason：setName\n            return CollUtil.newArrayList(inclusiveGateway, joinParallelGateway);\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE;\n        }\n\n    }\n\n    private static class InclusiveBranchNodeConvert implements NodeConvert {\n\n        @Override\n        public List<InclusiveGateway> convertList(BpmSimpleModelNodeVO node) {\n            InclusiveGateway inclusiveGateway = new InclusiveGateway();\n            inclusiveGateway.setId(node.getId());\n            // 设置默认的序列流（条件）\n            BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(),\n                    item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow()));\n            Assert.notNull(defaultSeqFlow, \"包容分支节点({})的默认序列流不能为空\", node.getId());\n            inclusiveGateway.setDefaultFlow(defaultSeqFlow.getId());\n            // TODO @jason：setName\n\n            // 并行聚合网关由程序创建，前端不需要传入\n            InclusiveGateway joinInclusiveGateway = new InclusiveGateway();\n            joinInclusiveGateway.setId(buildGatewayJoinId(node.getId()));\n            // TODO @jason：setName\n            return CollUtil.newArrayList(inclusiveGateway, joinInclusiveGateway);\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE;\n        }\n\n    }\n\n    public static class ConditionNodeConvert implements NodeConvert {\n\n        @Override\n        public List<? extends FlowElement> convertList(BpmSimpleModelNodeVO node) {\n            // 原因是：正常情况下，它不会被调用到\n            throw new UnsupportedOperationException(\"条件节点不支持转换\");\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.CONDITION_NODE;\n        }\n\n        public static SequenceFlow buildSequenceFlow(String sourceId, String targetId,\n                                                     BpmSimpleModelNodeTypeEnum nodeType, BpmSimpleModelNodeVO node) {\n            String conditionExpression;\n            // 并行分支，使用包容网关实现，强制设置条件表达式为 true\n            if (BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE == nodeType) {\n                conditionExpression =\"${true}\";\n            } else {\n                conditionExpression = buildConditionExpression(node.getConditionSetting());\n            }\n            return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression);\n        }\n    }\n\n    /**\n     * 构造条件表达式\n     */\n    public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) {\n        if (conditionSetting == null) {\n            return null;\n        }\n        return buildConditionExpression(conditionSetting.getConditionType(), conditionSetting.getConditionExpression(),\n                conditionSetting.getConditionGroups());\n    }\n\n    public static String buildConditionExpression(BpmSimpleModelNodeVO.RouterSetting routerSetting) {\n        return buildConditionExpression(routerSetting.getConditionType(), routerSetting.getConditionExpression(),\n                routerSetting.getConditionGroups());\n    }\n\n    public static String buildConditionExpression(Integer conditionType, String conditionExpression, ConditionGroups conditionGroups) {\n        BpmSimpleModeConditionTypeEnum conditionTypeEnum = BpmSimpleModeConditionTypeEnum.valueOf(conditionType);\n        if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.EXPRESSION) {\n            return conditionExpression;\n        }\n        if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.RULE) {\n            if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) {\n                return null;\n            }\n            List<String> strConditionGroups = convertList(conditionGroups.getConditions(), item -> {\n                if (CollUtil.isEmpty(item.getRules())) {\n                    return \"\";\n                }\n                // 构造规则表达式\n                List<String> list = convertList(item.getRules(), (rule) -> {\n                    String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide()\n                            : \"\\\"\" + rule.getRightSide() + \"\\\"\"; // 如果非数值类型加引号\n                    return String.format(\" vars:getOrDefault(%s, null) %s var:convertByType(%s,%s) \",\n                            rule.getLeftSide(), // 左侧：读取变量\n                            rule.getOpCode(), // 中间：操作符，比较\n                            rule.getLeftSide(), rightSide); // 右侧：转换变量，VariableConvertByTypeExpressionFunction\n                });\n                // 构造条件组的表达式\n                Boolean and = item.getAnd();\n                return \"(\" + CollUtil.join(list, and ? \" && \" : \" || \") + \")\";\n            });\n            return String.format(\"${%s}\", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? \" && \" : \" || \"));\n        }\n        return null;\n    }\n\n    public static class DelayTimerNodeConvert implements NodeConvert {\n\n        @Override\n        public List<FlowElement> convertList(BpmSimpleModelNodeVO node) {\n            List<FlowElement> flowElements = new ArrayList<>(2);\n            // 1. 构建接收任务，通过接收任务可卡住节点\n            ReceiveTask receiveTask = new ReceiveTask();\n            receiveTask.setId(node.getId());\n            receiveTask.setName(node.getName());\n            flowElements.add(receiveTask);\n\n            // 2. 添加接收任务的 Timer Boundary Event\n            if (node.getDelaySetting() != null) {\n                BoundaryEvent boundaryEvent = null;\n                if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) {\n                    boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(),\n                            null, null, node.getDelaySetting().getDelayTime());\n                } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) {\n                    boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(),\n                            node.getDelaySetting().getDelayTime(), null, null);\n                } else {\n                    throw new UnsupportedOperationException(\"不支持的延迟类型：\" + node.getDelaySetting());\n                }\n                flowElements.add(boundaryEvent);\n            }\n            return flowElements;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.DELAY_TIMER_NODE;\n        }\n    }\n\n    public static class TriggerNodeConvert implements NodeConvert {\n\n        @Override\n        public List<? extends FlowElement> convertList(BpmSimpleModelNodeVO node) {\n            Assert.notNull(node.getTriggerSetting(), \"触发器节点设置不能为空\");\n            List<FlowElement> flowElements = new ArrayList<>(2);\n            // HTTP 回调请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行\n            if (BpmTriggerTypeEnum.HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) {\n                Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), \"触发器 HTTP 回调请求设置不能为空\");\n                ReceiveTask receiveTask = new ReceiveTask();\n                receiveTask.setId(\"Activity_\" + IdUtil.fastUUID());\n                receiveTask.setName(\"HTTP 回调\");\n                node.setAttachNodeId(receiveTask.getId());\n                flowElements.add(receiveTask);\n                // 重要：设置 callbackTaskDefineKey，用于 HTTP 回调\n                node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(receiveTask.getId());\n            }\n\n            // 触发器使用 ServiceTask 来实现\n            ServiceTask serviceTask = new ServiceTask();\n            serviceTask.setId(node.getId());\n            serviceTask.setName(node.getName());\n            serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);\n            serviceTask.setImplementation(\"${\" + BpmTriggerTaskDelegate.BEAN_NAME + \"}\");\n            addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType());\n            if (node.getTriggerSetting().getHttpRequestSetting() != null) {\n                addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting());\n            }\n            if (node.getTriggerSetting().getFormSettings() != null) {\n                addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getFormSettings());\n            }\n            flowElements.add(serviceTask);\n            return flowElements;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.TRIGGER_NODE;\n        }\n    }\n\n    public static class RouteBranchNodeConvert implements NodeConvert {\n\n        @Override\n        public ExclusiveGateway convert(BpmSimpleModelNodeVO node) {\n            ExclusiveGateway exclusiveGateway = new ExclusiveGateway();\n            exclusiveGateway.setId(node.getId());\n\n            // 设置默认的序列流（条件）\n            node.setRouterDefaultFlowId(\"Flow_\" + IdUtil.fastUUID());\n            exclusiveGateway.setDefaultFlow(node.getRouterDefaultFlowId());\n            return exclusiveGateway;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE;\n        }\n\n        public static SequenceFlow buildSequenceFlow(String nodeId, BpmSimpleModelNodeVO.RouterSetting router) {\n            String conditionExpression = SimpleModelUtils.buildConditionExpression(router);\n            return buildBpmnSequenceFlow(nodeId, router.getNodeId(), null, null, conditionExpression);\n        }\n\n    }\n\n    private static class ChildProcessConvert implements NodeConvert {\n\n        @Override\n        public List<FlowElement> convertList(BpmSimpleModelNodeVO node) {\n            List<FlowElement> flowElements = new ArrayList<>(2);\n            BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting();\n            List<IOParameter> inVariables = childProcessSetting.getInVariables() == null ?\n                    new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariables());\n            CallActivity callActivity = new CallActivity();\n            callActivity.setId(node.getId());\n            callActivity.setName(node.getName());\n            callActivity.setCalledElementType(\"key\");\n            // 1. 是否异步\n            if (node.getChildProcessSetting().getAsync()) {\n                callActivity.setAsynchronous(true);\n            }\n\n            // 2. 调用的子流程\n            callActivity.setCalledElement(childProcessSetting.getCalledProcessDefinitionKey());\n            callActivity.setProcessInstanceName(childProcessSetting.getCalledProcessDefinitionName());\n\n            // 3. 是否自动跳过子流程发起节点\n            IOParameter ioParameter = new IOParameter();\n            ioParameter.setSourceExpression(childProcessSetting.getSkipStartUserNode().toString());\n            ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);\n            inVariables.add(ioParameter);\n\n            // 4. 【默认需要传递的一些变量】流程状态\n            ioParameter = new IOParameter();\n            ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);\n            ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);\n            inVariables.add(ioParameter);\n\n            // 5. 主→子变量传递、子->主变量传递\n            callActivity.setInParameters(inVariables);\n            if (ArrayUtil.isNotEmpty(childProcessSetting.getOutVariables()) && ObjUtil.notEqual(childProcessSetting.getAsync(), Boolean.TRUE)) {\n                callActivity.setOutParameters(childProcessSetting.getOutVariables());\n            }\n\n            // 6. 子流程发起人配置\n            List<FlowableListener> executionListeners = new ArrayList<>();\n            FlowableListener flowableListener = new FlowableListener();\n            flowableListener.setEvent(ExecutionListener.EVENTNAME_START);\n            flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);\n            flowableListener.setImplementation(BpmCallActivityListener.DELEGATE_EXPRESSION);\n            FieldExtension fieldExtension = new FieldExtension();\n            fieldExtension.setFieldName(\"listenerConfig\");\n            fieldExtension.setStringValue(JsonUtils.toJsonString(childProcessSetting.getStartUserSetting()));\n            flowableListener.getFieldExtensions().add(fieldExtension);\n            executionListeners.add(flowableListener);\n            callActivity.setExecutionListeners(executionListeners);\n\n            // 7. 超时设置\n            if (childProcessSetting.getTimeoutSetting() != null && Boolean.TRUE.equals(childProcessSetting.getTimeoutSetting().getEnable())) {\n                BoundaryEvent boundaryEvent = null;\n                if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) {\n                    boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(),\n                            childProcessSetting.getTimeoutSetting().getTimeExpression(), null, null);\n                } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) {\n                    boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType(),\n                            null, null, childProcessSetting.getTimeoutSetting().getTimeExpression());\n                }\n                flowElements.add(boundaryEvent);\n            }\n\n            // 8. 多实例\n            if (childProcessSetting.getMultiInstanceSetting() != null && Boolean.TRUE.equals(childProcessSetting.getMultiInstanceSetting().getEnable())) {\n                MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics();\n                multiInstanceCharacteristics.setSequential(childProcessSetting.getMultiInstanceSetting().getSequential());\n                if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY.getType())) {\n                    multiInstanceCharacteristics.setLoopCardinality(childProcessSetting.getMultiInstanceSetting().getSource());\n                }\n                if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType()) ||\n                        childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) {\n                    multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource());\n                }\n                multiInstanceCharacteristics.setCompletionCondition(String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(),\n                        String.format(\"%.2f\", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D)));\n                callActivity.setLoopCharacteristics(multiInstanceCharacteristics);\n                addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType());\n            }\n\n            // 添加节点类型\n            addNodeType(node.getType(), callActivity);\n            flowElements.add(callActivity);\n            return flowElements;\n        }\n\n        @Override\n        public BpmSimpleModelNodeTypeEnum getType() {\n            return BpmSimpleModelNodeTypeEnum.CHILD_PROCESS;\n        }\n\n    }\n\n    private static String buildGatewayJoinId(String id) {\n        return id + \"_join\";\n    }\n\n    private static BoundaryEvent buildTimeoutBoundaryEvent(Activity attachedToRef, Integer type,\n                                                           String timeDuration, String timeCycle, String timeDate) {\n        // 1.1 定时器边界事件\n        BoundaryEvent boundaryEvent = new BoundaryEvent();\n        boundaryEvent.setId(\"Event-\" + IdUtil.fastUUID());\n        boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断\n        boundaryEvent.setAttachedToRef(attachedToRef);\n        // 1.2 定义超时时间表达式\n        TimerEventDefinition eventDefinition = new TimerEventDefinition();\n        if (ObjUtil.isNotNull(timeDuration)) {\n            eventDefinition.setTimeDuration(timeDuration);\n        }\n        if (ObjUtil.isNotNull(timeDuration)) {\n            eventDefinition.setTimeCycle(timeCycle);\n        }\n        if (ObjUtil.isNotNull(timeDate)) {\n            eventDefinition.setTimeDate(timeDate);\n        }\n        boundaryEvent.addEventDefinition(eventDefinition);\n\n        // 2. 添加定时器边界事件类型\n        addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, type);\n        return boundaryEvent;\n    }\n\n    // ========== SIMPLE 流程预测相关的方法 ==========\n\n    public static List<BpmSimpleModelNodeVO> simulateProcess(BpmSimpleModelNodeVO rootNode, Map<String, Object> variables) {\n        List<BpmSimpleModelNodeVO> resultNodes = new ArrayList<>();\n\n        // 从头开始遍历\n        simulateNextNode(rootNode, variables, resultNodes);\n        return resultNodes;\n    }\n\n    private static void simulateNextNode(BpmSimpleModelNodeVO currentNode, Map<String, Object> variables,\n                                         List<BpmSimpleModelNodeVO> resultNodes) {\n        // 如果不合法（包括为空），则直接结束\n        if (!isValidNode(currentNode)) {\n            return;\n        }\n        BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType());\n        Assert.notNull(nodeType, \"模型节点类型不支持\");\n\n        // 情况：START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE/TRANSACTOR_NODE\n        if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE\n                || nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE\n                || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE\n                || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE\n                || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE\n                || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS\n                || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {\n            // 添加此节点\n            resultNodes.add(currentNode);\n        }\n\n        // 情况：CONDITION_BRANCH_NODE 排它，只有一个满足条件的。如果没有，就走默认的\n        if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) {\n            // 查找满足条件的 BpmSimpleModelNodeVO 节点\n            BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(),\n                    conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())\n                            && evalConditionExpress(variables, conditionNode.getConditionSetting()));\n            if (matchConditionNode == null) {\n                matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(),\n                        conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()));\n            }\n            Assert.notNull(matchConditionNode, \"找不到条件节点({})\", currentNode);\n            // 遍历满足条件的 BpmSimpleModelNodeVO 节点\n            simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes);\n        }\n\n        // 情况：INCLUSIVE_BRANCH_NODE 包容，多个满足条件的。如果没有，就走默认的\n        if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) {\n            // 查找满足条件的 BpmSimpleModelNodeVO 节点\n            Collection<BpmSimpleModelNodeVO> matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(),\n                    conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())\n                            && evalConditionExpress(variables, conditionNode.getConditionSetting()));\n            if (CollUtil.isEmpty(matchConditionNodes)) {\n                matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(),\n                        conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()));\n            }\n            Assert.isTrue(!matchConditionNodes.isEmpty(), \"找不到条件节点({})\", currentNode);\n            // 遍历满足条件的 BpmSimpleModelNodeVO 节点\n            matchConditionNodes.forEach(matchConditionNode ->\n                    simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes));\n        }\n\n        // 情况：PARALLEL_BRANCH_NODE 并行，都满足，都走\n        if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE) {\n            // 遍历所有 BpmSimpleModelNodeVO 节点\n            currentNode.getConditionNodes().forEach(matchConditionNode ->\n                    simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes));\n        }\n\n        // 遍历子节点\n        simulateNextNode(currentNode.getChildNode(), variables, resultNodes);\n    }\n\n    /**\n     * 根据跳过表达式，判断是否跳过此节点\n     */\n    public static boolean isSkipNode(BpmSimpleModelNodeVO currentNode, Map<String, Object> variables) {\n        if (StrUtil.isEmpty(currentNode.getSkipExpression())) {\n            return false;\n        }\n        return BpmnModelUtils.evalConditionExpress(variables, currentNode.getSkipExpression());\n    }\n\n    public static boolean evalConditionExpress(Map<String, Object> variables, BpmSimpleModelNodeVO.ConditionSetting conditionSetting) {\n        return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/package-info.java",
    "content": "/**\n * 属于 bpm 模块的 framework 封装\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.bpm.framework;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/rpc/config/RpcConfiguration.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.rpc.config;\n\nimport cn.iocoder.yudao.module.bpm.api.event.CrmContractStatusListener;\nimport cn.iocoder.yudao.module.bpm.api.event.CrmReceivableStatusListener;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.PostApi;\nimport cn.iocoder.yudao.module.system.api.dict.DictDataApi;\nimport cn.iocoder.yudao.module.system.api.permission.PermissionApi;\nimport cn.iocoder.yudao.module.system.api.permission.RoleApi;\nimport cn.iocoder.yudao.module.system.api.sms.SmsSendApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(value = \"bpmRpcConfiguration\", proxyBeanMethods = false)\n@EnableFeignClients(clients = {RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class,\n        PermissionApi.class})\npublic class RpcConfiguration {\n\n    // ========== 特殊：解决微 yudao-cloud 微服务场景下，跨服务（进程）无法 Listener 的问题 ==========\n\n    @Bean\n    @ConditionalOnMissingBean(name = \"crmReceivableStatusListener\")\n    public CrmReceivableStatusListener crmReceivableStatusListener() {\n        return new CrmReceivableStatusListener();\n    }\n\n    @Bean\n    @ConditionalOnMissingBean(name = \"crmContractStatusListener\")\n    public CrmContractStatusListener crmContractStatusListener() {\n        return new CrmContractStatusListener();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/rpc/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.bpm.framework.rpc;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/security/config/SecurityConfiguration.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.security.config;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.module.bpm.enums.ApiConstants;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\n/**\n * Bpm 模块的 Security 配置\n */\n@Configuration(proxyBeanMethods = false, value = \"bpmSecurityConfiguration\")\npublic class SecurityConfiguration {\n\n    @Bean(\"bpmAuthorizeRequestsCustomizer\")\n    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {\n        return new AuthorizeRequestsCustomizer() {\n\n            @Override\n            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n                // TODO 芋艿：这个每个项目都需要重复配置，得捉摸有没通用的方案\n                // Swagger 接口文档\n                registry.requestMatchers(\"/v3/api-docs/**\").permitAll()\n                        .requestMatchers(\"/webjars/**\").permitAll()\n                        .requestMatchers(\"/swagger-ui\").permitAll()\n                        .requestMatchers(\"/swagger-ui/**\").permitAll();\n                // Druid 监控\n                registry.requestMatchers(\"/druid/**\").permitAll();\n                // Spring Boot Actuator 的安全配置\n                registry.requestMatchers(\"/actuator\").permitAll()\n                        .requestMatchers(\"/actuator/**\").permitAll();\n                // RPC 服务的安全配置\n                registry.requestMatchers(ApiConstants.PREFIX + \"/**\").permitAll();\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/security/core/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.bpm.framework.security.core;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/web/config/BpmWebConfiguration.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.web.config;\n\nimport cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;\nimport cn.iocoder.yudao.module.bpm.framework.web.core.FlowableWebFilter;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * bpm 模块的 web 组件的 Configuration\n *\n * @author 芋道源码\n */\n@Configuration(proxyBeanMethods = false)\npublic class BpmWebConfiguration {\n\n    /**\n     * 配置 Flowable Web 过滤器\n     */\n    @Bean\n    public FilterRegistrationBean<FlowableWebFilter> flowableWebFilter() {\n        FilterRegistrationBean<FlowableWebFilter> registrationBean = new FilterRegistrationBean<>();\n        registrationBean.setFilter(new FlowableWebFilter());\n        registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER);\n        return registrationBean;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/web/core/FlowableWebFilter.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.web.core;\n\nimport cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * Flowable Web 过滤器，将 userId 设置到 {@link org.flowable.common.engine.impl.identity.Authentication} 中\n *\n * @author jason\n */\npublic class FlowableWebFilter extends OncePerRequestFilter {\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        try {\n            // 设置工作流的用户\n            Long userId = SecurityFrameworkUtils.getLoginUserId();\n            if (userId != null) {\n                FlowableUtils.setAuthenticatedUserId(userId);\n            }\n            // 过滤\n            chain.doFilter(request, response);\n        } finally {\n            // 清理\n            FlowableUtils.clearAuthenticatedUserId();\n        }\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/web/package-info.java",
    "content": "/**\n * bpm 模块的 web 配置\n */\npackage cn.iocoder.yudao.module.bpm.framework.web;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/package-info.java",
    "content": "/**\n * bpm 包下，业务流程管理（Business Process Management），我们放工作流的功能，基于 Flowable 6 版本实现。\n * 例如说：流程定义、表单配置、审核中心（我的申请、我的待办、我的已办）等等\n *\n * bpm 解释：https://baike.baidu.com/item/BPM/1933\n *\n * 1. Controller URL：以 /bpm/ 开头，避免和其它 Module 冲突\n * 2. DataObject 表名：以 bpm_ 开头，方便在数据库中区分\n *\n * 注意，由于 Bpm 模块下，容易和其它模块重名，所以类名都加载 Bpm 的前缀~\n */\npackage cn.iocoder.yudao.module.bpm;\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmCategoryService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * BPM 流程分类 Service 接口\n *\n * @author 芋道源码\n */\npublic interface BpmCategoryService {\n\n    /**\n     * 创建流程分类\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createCategory(@Valid BpmCategorySaveReqVO createReqVO);\n\n    /**\n     * 更新流程分类\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCategory(@Valid BpmCategorySaveReqVO updateReqVO);\n\n    /**\n     * 删除流程分类\n     *\n     * @param id 编号\n     */\n    void deleteCategory(Long id);\n\n    /**\n     * 获得流程分类\n     *\n     * @param id 编号\n     * @return BPM 流程分类\n     */\n    BpmCategoryDO getCategory(Long id);\n\n    /**\n     * 获得流程分类分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 流程分类分页\n     */\n    PageResult<BpmCategoryDO> getCategoryPage(BpmCategoryPageReqVO pageReqVO);\n\n    /**\n     * 获得流程分类 Map，基于指定编码\n     *\n     * @param codes 编号数组\n     * @return 流程分类 Map\n     */\n    default Map<String, BpmCategoryDO> getCategoryMap(Collection<String> codes) {\n        return convertMap(getCategoryListByCode(codes), BpmCategoryDO::getCode);\n    }\n\n    /**\n     * 获得流程分类列表，基于指定编码\n     *\n     * @return 流程分类列表\n     */\n    List<BpmCategoryDO> getCategoryListByCode(Collection<String> codes);\n\n    /**\n     * 获得流程分类列表，基于指定状态\n     *\n     * @param status 状态\n     * @return 流程分类列表\n     */\n    List<BpmCategoryDO> getCategoryListByStatus(Integer status);\n\n    /**\n     * 批量更新流程分类的排序：每个分类的 sort 值，从 0 开始递增\n     *\n     * @param ids 分类编号列表\n     */\n    void updateCategorySortBatch(List<Long> ids);\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmCategoryServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;\n\n/**\n * BPM 流程分类 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class BpmCategoryServiceImpl implements BpmCategoryService {\n\n    @Resource\n    private BpmCategoryMapper bpmCategoryMapper;\n\n    @Resource\n    private BpmModelService modelService;\n\n    @Override\n    public Long createCategory(BpmCategorySaveReqVO createReqVO) {\n        // 校验唯一\n        validateCategoryNameUnique(createReqVO);\n        validateCategoryCodeUnique(createReqVO);\n        // 插入\n        BpmCategoryDO category = BeanUtils.toBean(createReqVO, BpmCategoryDO.class);\n        bpmCategoryMapper.insert(category);\n        return category.getId();\n    }\n\n    @Override\n    public void updateCategory(BpmCategorySaveReqVO updateReqVO) {\n        // 校验存在\n        validateCategoryExists(updateReqVO.getId());\n        validateCategoryNameUnique(updateReqVO);\n        validateCategoryCodeUnique(updateReqVO);\n        // 更新\n        BpmCategoryDO updateObj = BeanUtils.toBean(updateReqVO, BpmCategoryDO.class);\n        bpmCategoryMapper.updateById(updateObj);\n    }\n\n    private void validateCategoryNameUnique(BpmCategorySaveReqVO updateReqVO) {\n        BpmCategoryDO category = bpmCategoryMapper.selectByName(updateReqVO.getName());\n        if (category == null\n                || ObjUtil.equal(category.getId(), updateReqVO.getId())) {\n            return;\n        }\n        throw exception(CATEGORY_NAME_DUPLICATE, updateReqVO.getName());\n    }\n\n    private void validateCategoryCodeUnique(BpmCategorySaveReqVO updateReqVO) {\n        BpmCategoryDO category = bpmCategoryMapper.selectByCode(updateReqVO.getCode());\n        if (category == null\n                || ObjUtil.equal(category.getId(), updateReqVO.getId())) {\n            return;\n        }\n        throw exception(CATEGORY_CODE_DUPLICATE, updateReqVO.getCode());\n    }\n\n    @Override\n    public void deleteCategory(Long id) {\n        // 校验存在\n        BpmCategoryDO category = validateCategoryExists(id);\n        // 校验是否被流程模型使用\n        Long count = modelService.getModelCountByCategory(category.getCode());\n        if (count > 0) {\n            throw exception(CATEGORY_DELETE_FAIL_MODEL_USED, category.getName());\n        }\n        // 删除\n        bpmCategoryMapper.deleteById(id);\n    }\n\n    private BpmCategoryDO validateCategoryExists(Long id) {\n        BpmCategoryDO category = bpmCategoryMapper.selectById(id);\n        if (category == null) {\n            throw exception(CATEGORY_NOT_EXISTS);\n        }\n        return category;\n    }\n\n    @Override\n    public BpmCategoryDO getCategory(Long id) {\n        return bpmCategoryMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<BpmCategoryDO> getCategoryPage(BpmCategoryPageReqVO pageReqVO) {\n        return bpmCategoryMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<BpmCategoryDO> getCategoryListByCode(Collection<String> codes) {\n        if (CollUtil.isEmpty(codes)) {\n            return Collections.emptyList();\n        }\n        return bpmCategoryMapper.selectListByCode(codes);\n    }\n\n    @Override\n    public List<BpmCategoryDO> getCategoryListByStatus(Integer status) {\n        return bpmCategoryMapper.selectListByStatus(status);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateCategorySortBatch(List<Long> ids) {\n        // 校验分类都存在\n        List<BpmCategoryDO> categories = bpmCategoryMapper.selectBatchIds(ids);\n        if (categories.size() != ids.size()) {\n            throw exception(CATEGORY_NOT_EXISTS);\n        }\n\n        // 批量更新排序\n        List<BpmCategoryDO> updateList = IntStream.range(0, ids.size())\n                .mapToObj(index -> new BpmCategoryDO().setId(ids.get(index)).setSort(index))\n                .collect(Collectors.toList());\n        bpmCategoryMapper.updateBatch(updateList);\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n\n/**\n * 动态表单 Service 接口\n *\n * @author  @风里雾里\n */\npublic interface BpmFormService {\n\n    /**\n     * 创建动态表单\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createForm(@Valid BpmFormSaveReqVO createReqVO);\n\n    /**\n     * 更新动态表单\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateForm(@Valid BpmFormSaveReqVO updateReqVO);\n\n    /**\n     * 删除动态表单\n     *\n     * @param id 编号\n     */\n    void deleteForm(Long id);\n\n    /**\n     * 获得动态表单\n     *\n     * @param id 编号\n     * @return 动态表单\n     */\n    BpmFormDO getForm(Long id);\n\n    /**\n     * 获得动态表单列表\n     *\n     * @return 动态表单列表\n     */\n    List<BpmFormDO> getFormList();\n\n    /**\n     * 获得动态表单列表\n     *\n     * @param ids 编号\n     * @return 动态表单列表\n     */\n    List<BpmFormDO> getFormList(Collection<Long> ids);\n\n    /**\n     * 获得动态表单 Map\n     *\n     * @param ids 编号\n     * @return 动态表单 Map\n     */\n    default Map<Long, BpmFormDO> getFormMap(Collection<Long> ids) {\n        return CollectionUtils.convertMap(this.getFormList(ids), BpmFormDO::getId);\n    }\n\n    /**\n     * 获得动态表单分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 动态表单分页\n     */\n    PageResult<BpmFormDO> getFormPage(BpmFormPageReqVO pageReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;\nimport cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;\nimport cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\n\n/**\n * 动态表单 Service 实现类\n *\n * @author 风里雾里\n */\n@Service\n@Validated\npublic class BpmFormServiceImpl implements BpmFormService {\n\n    @Resource\n    private BpmFormMapper formMapper;\n\n    @Override\n    public Long createForm(BpmFormSaveReqVO createReqVO) {\n        this.validateFields(createReqVO.getFields());\n        // 插入\n        BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class);\n        formMapper.insert(form);\n        // 返回\n        return form.getId();\n    }\n\n    @Override\n    public void updateForm(BpmFormSaveReqVO updateReqVO) {\n        validateFields(updateReqVO.getFields());\n        // 校验存在\n        validateFormExists(updateReqVO.getId());\n        // 更新\n        BpmFormDO updateObj = BeanUtils.toBean(updateReqVO, BpmFormDO.class);\n        formMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteForm(Long id) {\n        // 校验存在\n        this.validateFormExists(id);\n        // 删除\n        formMapper.deleteById(id);\n    }\n\n    private void validateFormExists(Long id) {\n        if (formMapper.selectById(id) == null) {\n            throw exception(ErrorCodeConstants.FORM_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public BpmFormDO getForm(Long id) {\n        return formMapper.selectById(id);\n    }\n\n    @Override\n    public List<BpmFormDO> getFormList() {\n        return formMapper.selectList();\n    }\n\n    @Override\n    public List<BpmFormDO> getFormList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return formMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<BpmFormDO> getFormPage(BpmFormPageReqVO pageReqVO) {\n        return formMapper.selectPage(pageReqVO);\n    }\n\n    /**\n     * 校验 Field，避免 field 重复\n     *\n     * @param fields field 数组\n     */\n    private void validateFields(List<String> fields) {\n        if (true) { // TODO 芋艿：兼容 Vue3 工作流：因为采用了新的表单设计器，所以暂时不校验\n            return;\n        }\n        Map<String, String> fieldMap = new HashMap<>(); // key 是 vModel，value 是 label\n        for (String field : fields) {\n            BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class);\n            Assert.notNull(fieldDTO);\n            String oldLabel = fieldMap.put(fieldDTO.getVModel(), fieldDTO.getLabel());\n            // 如果不存在，则直接返回\n            if (oldLabel == null) {\n                continue;\n            }\n            // 如果存在，则报错\n            throw exception(ErrorCodeConstants.FORM_FIELD_REPEAT, oldLabel, fieldDTO.getLabel(), fieldDTO.getVModel());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.repository.Model;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 流程模型接口\n *\n * @author yunlongn\n */\npublic interface BpmModelService {\n\n    /**\n     * 获得流程模型列表\n     *\n     * @param name 模型名称\n     * @return 流程模型列表\n     */\n    List<Model> getModelList(String name);\n\n    /**\n     * 根据分类编码获得流程模型数量\n     *\n     * @param category 分类编码\n     * @return 流程模型数量\n     */\n    Long getModelCountByCategory(String category);\n\n    /**\n     * 创建流程模型\n     *\n     * @param modelVO 创建信息\n     * @return 创建的流程模型的编号\n     */\n    String createModel(@Valid BpmModelSaveReqVO modelVO);\n\n    /**\n     * 获得流程模块\n     *\n     * @param id 编号\n     * @return 流程模型\n     */\n    Model getModel(String id);\n\n    /**\n     * 获得流程模型的 BPMN XML\n     *\n     * @param id 编号\n     * @return BPMN XML\n     */\n    byte[] getModelBpmnXML(String id);\n\n    /**\n     * 修改流程模型的 BPMN XML\n     *\n     * @param id      编号\n     * @param bpmnXml BPMN XML\n     */\n    void updateModelBpmnXml(String id, String bpmnXml);\n\n    /**\n     * 修改流程模型\n     *\n     * @param userId 用户编号\n     * @param updateReqVO 更新信息\n     */\n    void updateModel(Long userId, @Valid BpmModelSaveReqVO updateReqVO);\n\n    /**\n     * 批量更新模型排序\n     *\n     * @param userId 用户编号\n     * @param ids 编号列表\n     */\n    void updateModelSortBatch(Long userId, List<String> ids);\n\n    /**\n     * 将流程模型，部署成一个流程定义\n     *\n     * @param userId 用户编号\n     * @param id 编号\n     */\n    void deployModel(Long userId, String id);\n\n    /**\n     * 删除模型\n     *\n     * @param userId  用户编号\n     * @param id 编号\n     */\n    void deleteModel(Long userId, String id);\n\n    /**\n     * 清理模型，包括流程实例\n     *\n     * @param userId  用户编号\n     * @param id 编号\n     */\n    void cleanModel(Long userId, String id);\n\n    /**\n     * 修改模型的状态，实际更新的部署的流程定义的状态\n     *\n     * @param userId 用户编号\n     * @param id    编号\n     * @param state 状态\n     */\n    void updateModelState(Long userId, String id, Integer state);\n\n    /**\n     * 获得流程定义编号对应的 BPMN Model\n     *\n     * @param processDefinitionId 流程定义编号\n     * @return BPMN Model\n     */\n    BpmnModel getBpmnModelByDefinitionId(String processDefinitionId);\n\n    // ========== 仿钉钉/飞书的精简模型 =========\n\n    /**\n     * 获取仿钉钉流程设计模型结构\n     *\n     * @param modelId 流程模型编号\n     * @return 仿钉钉流程设计模型结构\n     */\n    BpmSimpleModelNodeVO getSimpleModel(String modelId);\n\n    /**\n     * 更新仿钉钉流程设计模型\n     *\n     * @param userId 用户编号\n     * @param reqVO 请求信息\n     */\n    void updateSimpleModel(Long userId, @Valid BpmSimpleModelUpdateReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;\nimport cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService;\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.model.*;\nimport org.flowable.common.engine.impl.db.SuspensionState;\nimport org.flowable.engine.HistoryService;\nimport org.flowable.engine.RepositoryService;\nimport org.flowable.engine.RuntimeService;\nimport org.flowable.engine.TaskService;\nimport org.flowable.engine.history.HistoricProcessInstance;\nimport org.flowable.engine.repository.Model;\nimport org.flowable.engine.repository.ModelQuery;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.task.api.Task;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseCandidateStrategy;\n\n/**\n * 流程模型实现：主要进行 Flowable {@link Model} 的维护\n *\n * @author yunlongn\n * @author 芋道源码\n * @author jason\n */\n@Service\n@Validated\n@Slf4j\npublic class BpmModelServiceImpl implements BpmModelService {\n\n    @Resource\n    private RepositoryService repositoryService;\n    @Resource\n    private BpmProcessDefinitionService processDefinitionService;\n    @Resource\n    private BpmFormService bpmFormService;\n\n    @Resource\n    private BpmTaskCandidateInvoker taskCandidateInvoker;\n\n    @Resource\n    private HistoryService historyService;\n    @Resource\n    private RuntimeService runtimeService;\n    @Resource\n    private TaskService taskService;\n    @Resource\n    private BpmProcessInstanceCopyService processInstanceCopyService;\n\n    @Override\n    public List<Model> getModelList(String name) {\n        ModelQuery modelQuery = repositoryService.createModelQuery();\n        if (StrUtil.isNotEmpty(name)) {\n            modelQuery.modelNameLike(\"%\" + name + \"%\");\n        }\n        modelQuery.modelTenantId(FlowableUtils.getTenantId());\n        return modelQuery.list();\n    }\n\n    @Override\n    public Long getModelCountByCategory(String category) {\n        return repositoryService.createModelQuery()\n                .modelCategory(category)\n                .modelTenantId(FlowableUtils.getTenantId())\n                .count();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public String createModel(@Valid BpmModelSaveReqVO createReqVO) {\n        if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) {\n            throw exception(MODEL_KEY_VALID);\n        }\n        // 1. 校验流程标识已经存在\n        Model keyModel = getModelByKey(createReqVO.getKey());\n        if (keyModel != null) {\n            throw exception(MODEL_KEY_EXISTS, createReqVO.getKey());\n        }\n\n        // 2. 创建 Model 对象\n        createReqVO.setSort(System.currentTimeMillis()); // 使用当前时间，作为排序\n        Model model = repositoryService.newModel();\n        BpmModelConvert.INSTANCE.copyToModel(model, createReqVO);\n        model.setTenantId(FlowableUtils.getTenantId());\n\n        // 3. 保存模型\n        saveModel(model, createReqVO);\n        return model.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class) // 因为进行多个操作，所以开启事务\n    public void updateModel(Long userId, BpmModelSaveReqVO updateReqVO) {\n        // 1. 校验流程模型存在\n        Model model = validateModelManager(updateReqVO.getId(), userId);\n\n        // 2. 填充 Model 信息\n        BpmModelConvert.INSTANCE.copyToModel(model, updateReqVO);\n\n        // 3. 保存模型\n        saveModel(model, updateReqVO);\n    }\n\n    /**\n     * 保存模型的基本信息、流程图\n     *\n     * @param model 模型\n     * @param saveReqVO 保存信息\n     */\n    private void saveModel(Model model, BpmModelSaveReqVO saveReqVO) {\n        // 1. 保存模型的基础信息\n        repositoryService.saveModel(model);\n\n        // 2. 保存流程图\n        if (ObjUtil.equals(BpmModelTypeEnum.BPMN.getType(), saveReqVO.getType())\n                && StrUtil.isNotEmpty(saveReqVO.getBpmnXml())) {\n            updateModelBpmnXml(model.getId(), saveReqVO.getBpmnXml());\n        } else if (ObjUtil.equals(BpmModelTypeEnum.SIMPLE.getType(), saveReqVO.getType())\n                && saveReqVO.getSimpleModel() != null) {\n            // JSON 转换成 bpmnModel\n            BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(),\n                    saveReqVO.getSimpleModel());\n            // 保存 Bpmn XML\n            updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));\n            // 保存 JSON 数据\n            updateModelSimpleJson(model.getId(), saveReqVO.getSimpleModel());\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateModelSortBatch(Long userId, List<String> ids) {\n        // 1.1 校验流程模型存在\n        List<Model> models = repositoryService.createModelQuery()\n                .modelTenantId(FlowableUtils.getTenantId()).list();\n        models.removeIf(model -> !ids.contains(model.getId()));\n        if (ids.size() != models.size()) {\n            throw exception(MODEL_NOT_EXISTS);\n        }\n        Map<String, Model> modelMap = convertMap(models, Model::getId);\n        // 1.2 校验是否为管理员\n        ids.forEach(id -> validateModelManager(id, userId));\n\n        // 保存排序\n        long sort = System.currentTimeMillis(); // 使用时间戳 - i 作为排序\n        for (int i = ids.size() - 1; i > 0; i--) {\n            Model model = modelMap.get(ids.get(i));\n            // 更新模型\n            BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model).setSort(sort);\n            model.setMetaInfo(JsonUtils.toJsonString(metaInfo));\n            repositoryService.saveModel(model);\n            // 更新排序\n            processDefinitionService.updateProcessDefinitionSortByModelId(model.getId(), sort);\n            sort--;\n        }\n    }\n\n    private Model validateModelExists(String id) {\n        Model model = repositoryService.getModel(id);\n        if (model == null) {\n            throw exception(MODEL_NOT_EXISTS);\n        }\n        return model;\n    }\n\n    /**\n     * 校验是否有流程模型的管理权限\n     *\n     * @param id     流程模型编号\n     * @param userId 用户编号\n     * @return 流程模型\n     */\n    private Model validateModelManager(String id, Long userId) {\n        Model model = validateModelExists(id);\n        BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);\n        if (metaInfo == null || !CollUtil.contains(metaInfo.getManagerUserIds(), userId)) {\n            throw exception(MODEL_UPDATE_FAIL_NOT_MANAGER, model.getName());\n        }\n        return model;\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class) // 因为进行多个操作，所以开启事务\n    public void deployModel(Long userId, String id) {\n        // 1.1 校验流程模型存在\n        Model model = validateModelManager(id, userId);\n        BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);\n        // 1.2 校验流程图\n        byte[] bpmnBytes = getModelBpmnXML(model.getId());\n        validateBpmnXml(bpmnBytes, metaInfo.getType());\n        // 1.3 校验表单已配\n        BpmFormDO form = validateFormConfig(metaInfo);\n        // 1.4 校验任务分配规则已配置\n        taskCandidateInvoker.validateBpmnConfig(bpmnBytes);\n        // 1.5 获取仿钉钉流程设计器模型数据\n        String simpleJson = getModelSimpleJson(model.getId());\n\n        // 2.1 创建流程定义\n        String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleJson,\n                form);\n\n        // 2.2 将老的流程定义进行挂起。也就是说，只有最新部署的流程定义，才可以发起任务。\n        updateProcessDefinitionSuspended(model.getDeploymentId());\n\n        // 2.3 更新 model 的 deploymentId，进行关联\n        ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId);\n        model.setDeploymentId(definition.getDeploymentId());\n        repositoryService.saveModel(model);\n    }\n\n    private void validateBpmnXml(byte[] bpmnBytes, Integer type) {\n        BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);\n        if (bpmnModel == null) {\n            throw exception(MODEL_NOT_EXISTS);\n        }\n        // 1. 没有 StartEvent\n        StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel);\n        if (startEvent == null) {\n            throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS);\n        }\n        // 2. 校验 UserTask 的 name 都配置了\n        List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);\n        userTasks.forEach(userTask -> {\n            if (StrUtil.isEmpty(userTask.getName())) {\n                throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId());\n            }\n        });\n        // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”，BPMN 设计器，校验第一个用户任务节点，SIMPLE 设计器，第一个节点固定为发起人所以校验第二个用户任务节点\n        UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1);\n        if (firUserTask == null) {\n            return;\n        }\n        Integer candidateStrategy = parseCandidateStrategy(firUserTask);\n        if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {\n            throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, firUserTask.getName());\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteModel(Long userId, String id) {\n        // 校验流程模型存在\n        Model model = validateModelManager(id, userId);\n\n        // 执行删除\n        repositoryService.deleteModel(id);\n        // 禁用流程定义\n        updateProcessDefinitionSuspended(model.getDeploymentId());\n    }\n\n    @Override\n    public void cleanModel(Long userId, String id) {\n        // 1. 校验流程模型存在\n        Model model = validateModelManager(id, userId);\n\n        // 2. 清理所有流程数据\n        // 2.1 先取消所有正在运行的流程\n        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()\n                .processDefinitionKey(model.getKey()).list();\n        processInstances.forEach(processInstance -> {\n            runtimeService.deleteProcessInstance(processInstance.getId(),\n                    BpmReasonEnum.CANCEL_BY_SYSTEM.getReason());\n            historyService.deleteHistoricProcessInstance(processInstance.getId());\n            processInstanceCopyService.deleteProcessInstanceCopy(processInstance.getId());\n        });\n        // 2.2 再从历史中删除所有相关的流程数据\n        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()\n                .processDefinitionKey(model.getKey()).list();\n        historicProcessInstances.forEach(historicProcessInstance -> {\n            historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());\n            processInstanceCopyService.deleteProcessInstanceCopy(historicProcessInstance.getId());\n        });\n        // 2.3 清理所有 Task\n        List<Task> tasks = taskService.createTaskQuery()\n                .processDefinitionKey(model.getKey()).list();\n        tasks.forEach(task -> taskService.deleteTask(task.getId(),BpmReasonEnum.CANCEL_BY_PROCESS_CLEAN.getReason()));\n    }\n\n    @Override\n    public void updateModelState(Long userId, String id, Integer state) {\n        // 1.1 校验流程模型存在\n        Model model = validateModelManager(id, userId);\n        // 1.2 校验流程定义存在\n        ProcessDefinition definition = processDefinitionService\n                .getProcessDefinitionByDeploymentId(model.getDeploymentId());\n        if (definition == null) {\n            throw exception(PROCESS_DEFINITION_NOT_EXISTS);\n        }\n\n        // 2. 更新状态\n        processDefinitionService.updateProcessDefinitionState(definition.getId(), state);\n    }\n\n    @Override\n    public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) {\n        return repositoryService.getBpmnModel(processDefinitionId);\n    }\n\n    @Override\n    public BpmSimpleModelNodeVO getSimpleModel(String modelId) {\n        Model model = validateModelExists(modelId);\n        // 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ，获取仿钉钉快搭模型的 JSON 数据\n        String json = getModelSimpleJson(model.getId());\n        return JsonUtils.parseObject(json, BpmSimpleModelNodeVO.class);\n    }\n\n    @Override\n    public void updateSimpleModel(Long userId, BpmSimpleModelUpdateReqVO reqVO) {\n        // 1. 校验流程模型存在\n        Model model = validateModelManager(reqVO.getId(), userId);\n\n        // 2.1 JSON 转换成 bpmnModel\n        BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModel());\n        // 2.2 保存 Bpmn XML\n        updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));\n        // 2.3 保存 JSON 数据\n        updateModelSimpleJson(model.getId(), reqVO.getSimpleModel());\n    }\n\n    /**\n     * 校验流程表单已配置\n     *\n     * @param metaInfo 流程模型元数据\n     * @return 表单配置\n     */\n    private BpmFormDO validateFormConfig(BpmModelMetaInfoVO metaInfo) {\n        if (metaInfo == null || metaInfo.getFormType() == null) {\n            throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);\n        }\n        // 校验表单存在\n        if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {\n            if (metaInfo.getFormId() == null) {\n                throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);\n            }\n            BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId());\n            if (form == null) {\n                throw exception(FORM_NOT_EXISTS);\n            }\n            return form;\n        } else {\n            if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath())\n                    || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) {\n                throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);\n            }\n            return null;\n        }\n    }\n\n    @Override\n    public void updateModelBpmnXml(String id, String bpmnXml) {\n        if (StrUtil.isEmpty(bpmnXml)) {\n            return;\n        }\n        repositoryService.addModelEditorSource(id, StrUtil.utf8Bytes(bpmnXml));\n    }\n\n    @SuppressWarnings(\"JavaExistingMethodCanBeUsed\")\n    private String getModelSimpleJson(String id) {\n        byte[] bytes = repositoryService.getModelEditorSourceExtra(id);\n        if (ArrayUtil.isEmpty(bytes)) {\n            return null;\n        }\n        return StrUtil.utf8Str(bytes);\n    }\n\n    private void updateModelSimpleJson(String id, BpmSimpleModelNodeVO node) {\n        if (node == null) {\n            return;\n        }\n        byte[] bytes = JsonUtils.toJsonByte(node);\n        repositoryService.addModelEditorSourceExtra(id, bytes);\n    }\n\n    /**\n     * 挂起 deploymentId 对应的流程定义\n     * <p>\n     * 注意：这里一个 deploymentId 只关联一个流程定义\n     *\n     * @param deploymentId 流程发布Id\n     */\n    private void updateProcessDefinitionSuspended(String deploymentId) {\n        if (StrUtil.isEmpty(deploymentId)) {\n            return;\n        }\n        ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId);\n        if (oldDefinition == null) {\n            return;\n        }\n        processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(),\n                SuspensionState.SUSPENDED.getStateCode());\n    }\n\n    private Model getModelByKey(String key) {\n        return repositoryService.createModelQuery()\n                .modelTenantId(FlowableUtils.getTenantId())\n                .modelKey(key).singleResult();\n    }\n\n    @Override\n    public Model getModel(String id) {\n        return repositoryService.getModel(id);\n    }\n\n    @Override\n    public byte[] getModelBpmnXML(String id) {\n        return repositoryService.getModelEditorSource(id);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.engine.repository.Deployment;\nimport org.flowable.engine.repository.Model;\nimport org.flowable.engine.repository.ProcessDefinition;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 流程定义接口\n *\n * @author yunlong.li\n * @author ZJQ\n * @author 芋道源码\n */\npublic interface BpmProcessDefinitionService {\n\n    /**\n     * 获得流程定义分页\n     *\n     * @param pageReqVO 分页入参\n     * @return 流程定义 Page\n     */\n    PageResult<ProcessDefinition> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageReqVO);\n\n    /**\n     * 获得流程定义列表\n     *\n     * @param suspensionState 中断状态\n     * @return 流程定义列表\n     */\n    List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState);\n\n    /**\n     * 基于流程模型，创建流程定义\n     *\n     * @param model 流程模型\n     * @param modelMetaInfo 流程模型元信息\n     * @param bpmnBytes BPMN XML 字节数组\n     * @param simpleJson SIMPLE Model JSON\n     * @param form 表单\n     * @return 流程编号\n     */\n    String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo,\n                                   byte[] bpmnBytes, String simpleJson, BpmFormDO form);\n\n    /**\n     * 更新流程定义状态\n     *\n     * @param id 流程定义的编号\n     * @param state 状态\n     */\n    void updateProcessDefinitionState(String id, Integer state);\n\n    /**\n     * 更新模型编号\n     *\n     * @param modelId 流程定义编号\n     * @param sort 排序\n     */\n    void updateProcessDefinitionSortByModelId(String modelId, Long sort);\n\n    /**\n     * 获得流程定义对应的 BPMN\n     *\n     * @param id 流程定义编号\n     * @return BPMN\n     */\n    BpmnModel getProcessDefinitionBpmnModel(String id);\n\n    /**\n     * 获得流程定义的信息\n     *\n     * @param id 流程定义编号\n     * @return 流程定义信息\n     */\n    BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id);\n\n    /**\n     * 获得流程定义的信息 List\n     *\n     * @param ids 流程定义编号数组\n     * @return 流程额定义信息数组\n     */\n    List<BpmProcessDefinitionInfoDO> getProcessDefinitionInfoList(Collection<String> ids);\n\n    default Map<String, BpmProcessDefinitionInfoDO> getProcessDefinitionInfoMap(Set<String> ids) {\n        return convertMap(getProcessDefinitionInfoList(ids), BpmProcessDefinitionInfoDO::getProcessDefinitionId);\n    }\n\n    /**\n     * 获得流程定义编号对应的 ProcessDefinition\n     *\n     * @param id 流程定义编号\n     * @return 流程定义\n     */\n    ProcessDefinition getProcessDefinition(String id);\n\n    /**\n     * 获得 ids 对应的 ProcessDefinition 数组\n     *\n     * @param ids 编号的数组\n     * @return 流程定义的数组\n     */\n    List<ProcessDefinition> getProcessDefinitionList(Set<String> ids);\n\n    default Map<String, ProcessDefinition> getProcessDefinitionMap(Set<String> ids) {\n        return convertMap(getProcessDefinitionList(ids), ProcessDefinition::getId);\n    }\n\n    /**\n     * 获得 deploymentId 对应的 ProcessDefinition\n     *\n     * @param deploymentId 部署编号\n     * @return 流程定义\n     */\n    ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId);\n\n    /**\n     * 获得 deploymentIds 对应的 ProcessDefinition 数组\n     *\n     * @param deploymentIds 部署编号的数组\n     * @return 流程定义的数组\n     */\n    List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds);\n\n    /**\n     * 获得流程定义标识对应的激活的流程定义\n     *\n     * @param key 流程定义的标识\n     * @return 流程定义\n     */\n    ProcessDefinition getActiveProcessDefinition(String key);\n\n    /**\n     * 判断用户是否可以使用该流程定义，进行流程的发起\n     *\n     * @param processDefinition 流程定义\n     * @param userId 用户编号\n     * @return 是否可以发起流程\n     */\n    boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId);\n\n    /**\n     * 获得 ids 对应的 Deployment Map\n     *\n     * @param ids 部署编号的数组\n     * @return 流程部署 Map\n     */\n    default Map<String, Deployment> getDeploymentMap(Set<String> ids) {\n        return convertMap(getDeploymentList(ids), Deployment::getId);\n    }\n\n    /**\n     * 获得 ids 对应的 Deployment 数组\n     *\n     * @param ids 部署编号的数组\n     * @return 流程部署的数组\n     */\n    List<Deployment> getDeploymentList(Set<String> ids);\n\n    /**\n     * 获得 id 对应的 Deployment\n     *\n     * @param id 部署编号\n     * @return 流程部署\n     */\n    Deployment getDeployment(String id);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.common.util.object.PageUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport javax.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.common.engine.impl.db.SuspensionState;\nimport org.flowable.engine.RepositoryService;\nimport org.flowable.engine.repository.Deployment;\nimport org.flowable.engine.repository.Model;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.flowable.engine.repository.ProcessDefinitionQuery;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.addIfNotNull;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;\nimport static java.util.Collections.emptyList;\n\n/**\n * 流程定义实现\n * 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护\n *\n * @author yunlongn\n * @author ZJQ\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService {\n\n    @Resource\n    private RepositoryService repositoryService;\n\n    @Resource\n    private BpmProcessDefinitionInfoMapper processDefinitionMapper;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    public ProcessDefinition getProcessDefinition(String id) {\n        return repositoryService.getProcessDefinition(id);\n    }\n\n    @Override\n    public List<ProcessDefinition> getProcessDefinitionList(Set<String> ids) {\n        return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list();\n    }\n\n    @Override\n    public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) {\n        if (StrUtil.isEmpty(deploymentId)) {\n            return null;\n        }\n        return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();\n    }\n\n    @Override\n    public List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds) {\n        if (CollUtil.isEmpty(deploymentIds)) {\n            return emptyList();\n        }\n        return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list();\n    }\n\n    @Override\n    public ProcessDefinition getActiveProcessDefinition(String key) {\n        return repositoryService.createProcessDefinitionQuery()\n                .processDefinitionTenantId(FlowableUtils.getTenantId())\n                .processDefinitionKey(key).active().singleResult();\n    }\n\n    @Override\n    public boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId) {\n        if (processDefinition == null) {\n            return false;\n        }\n\n        // 校验用户是否在允许发起的用户列表中\n        if (CollUtil.isNotEmpty(processDefinition.getStartUserIds())) {\n            return processDefinition.getStartUserIds().contains(userId);\n        }\n\n        // 校验用户是否在允许发起的部门列表中\n        if (CollUtil.isNotEmpty(processDefinition.getStartDeptIds())) {\n            AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData();\n            return user != null\n                    && user.getDeptId() != null\n                    && processDefinition.getStartDeptIds().contains(user.getDeptId());\n        }\n\n        // 都为空，则所有人都可以发起\n        return true;\n    }\n\n    @Override\n    public List<Deployment> getDeploymentList(Set<String> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return emptyList();\n        }\n        List<Deployment> list = new ArrayList<>(ids.size());\n        for (String id : ids) {\n            addIfNotNull(list, getDeployment(id));\n        }\n        return list;\n    }\n\n    @Override\n    public Deployment getDeployment(String id) {\n        if (StrUtil.isEmpty(id)) {\n            return null;\n        }\n        return repositoryService.createDeploymentQuery().deploymentId(id).singleResult();\n    }\n\n    @Override\n    public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo,\n                                          byte[] bpmnBytes, String simpleJson, BpmFormDO form) {\n        // 创建 Deployment 部署\n        Deployment deploy = repositoryService.createDeployment()\n                .key(model.getKey()).name(model.getName()).category(model.getCategory())\n                .addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes)\n                .tenantId(FlowableUtils.getTenantId())\n                .disableSchemaValidation() // 禁用 XML Schema 验证，因为有自定义的属性\n                .deploy();\n\n        // 设置 ProcessDefinition 的 category 分类\n        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()\n                .deploymentId(deploy.getId()).singleResult();\n        repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());\n        // 注意 1，ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定\n        // 注意 2，目前该项目的设计上，需要保证 Model、Deployment、ProcessDefinition 使用相同的 key，保证关联性。\n        //          否则，会导致 ProcessDefinition 的分页无法查询到。\n        if (!Objects.equals(definition.getKey(), model.getKey())) {\n            throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, model.getKey(), definition.getKey());\n        }\n        if (!Objects.equals(definition.getName(), model.getName())) {\n            throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, model.getName(), definition.getName());\n        }\n\n        // 插入拓展表\n        BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class)\n                .setModelId(model.getId()).setCategory(model.getCategory()).setProcessDefinitionId(definition.getId())\n                .setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson);\n        if (form != null) {\n            definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf());\n        }\n        processDefinitionMapper.insert(definitionDO);\n        return definition.getId();\n    }\n\n    @Override\n    public void updateProcessDefinitionState(String id, Integer state) {\n        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id);\n        if (processDefinition == null) {\n            throw exception(PROCESS_DEFINITION_NOT_EXISTS);\n        }\n\n        // 激活\n        if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) {\n            if (processDefinition.isSuspended()) {\n                repositoryService.activateProcessDefinitionById(id, false, null);\n            }\n            return;\n        }\n        // 挂起\n        if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) {\n            // suspendProcessInstances = false，进行中的任务，不进行挂起。\n            // 原因：只要新的流程不允许发起即可，老流程继续可以执行。\n            if (!processDefinition.isSuspended()) {\n                repositoryService.suspendProcessDefinitionById(id, false, null);\n            }\n            return;\n        }\n        log.error(\"[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]\", id, state);\n    }\n\n    @Override\n    public void updateProcessDefinitionSortByModelId(String modelId, Long sort) {\n        processDefinitionMapper.updateByModelId(modelId, new BpmProcessDefinitionInfoDO().setSort(sort));\n    }\n\n    @Override\n    public BpmnModel getProcessDefinitionBpmnModel(String id) {\n        return repositoryService.getBpmnModel(id);\n    }\n\n    @Override\n    public BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id) {\n        return processDefinitionMapper.selectByProcessDefinitionId(id);\n    }\n\n    @Override\n    public List<BpmProcessDefinitionInfoDO> getProcessDefinitionInfoList(Collection<String> ids) {\n        return processDefinitionMapper.selectListByProcessDefinitionIds(ids);\n    }\n\n    @Override\n    public PageResult<ProcessDefinition> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) {\n        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();\n        query.processDefinitionTenantId(FlowableUtils.getTenantId());\n        if (StrUtil.isNotBlank(pageVO.getKey())) {\n            query.processDefinitionKey(pageVO.getKey());\n        }\n        // 执行查询\n        long count = query.count();\n        if (count == 0) {\n            return PageResult.empty(count);\n        }\n        List<ProcessDefinition> list = query.orderByProcessDefinitionVersion().desc()\n                .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());\n        return new PageResult<>(list, count);\n    }\n\n    @Override\n    public List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState) {\n        // 拼接查询条件\n        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();\n        if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), suspensionState)) {\n            query.suspended();\n        } else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), suspensionState)) {\n            query.active();\n        }\n        // 执行查询\n        query.processDefinitionTenantId(FlowableUtils.getTenantId());\n        return query.list();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessExpressionService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO;\n\nimport javax.validation.Valid;\n\n/**\n * BPM 流程表达式 Service 接口\n *\n * @author 芋道源码\n */\npublic interface BpmProcessExpressionService {\n\n    /**\n     * 创建流程表达式\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProcessExpression(@Valid BpmProcessExpressionSaveReqVO createReqVO);\n\n    /**\n     * 更新流程表达式\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProcessExpression(@Valid BpmProcessExpressionSaveReqVO updateReqVO);\n\n    /**\n     * 删除流程表达式\n     *\n     * @param id 编号\n     */\n    void deleteProcessExpression(Long id);\n\n    /**\n     * 获得流程表达式\n     *\n     * @param id 编号\n     * @return 流程表达式\n     */\n    BpmProcessExpressionDO getProcessExpression(Long id);\n\n    /**\n     * 获得流程表达式分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 流程表达式分页\n     */\n    PageResult<BpmProcessExpressionDO> getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO);\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessExpressionMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_EXPRESSION_NOT_EXISTS;\n\n/**\n * BPM 流程表达式 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class BpmProcessExpressionServiceImpl implements BpmProcessExpressionService {\n\n    @Resource\n    private BpmProcessExpressionMapper processExpressionMapper;\n\n    @Override\n    public Long createProcessExpression(BpmProcessExpressionSaveReqVO createReqVO) {\n        // 插入\n        BpmProcessExpressionDO processExpression = BeanUtils.toBean(createReqVO, BpmProcessExpressionDO.class);\n        processExpressionMapper.insert(processExpression);\n        // 返回\n        return processExpression.getId();\n    }\n\n    @Override\n    public void updateProcessExpression(BpmProcessExpressionSaveReqVO updateReqVO) {\n        // 校验存在\n        validateProcessExpressionExists(updateReqVO.getId());\n        // 更新\n        BpmProcessExpressionDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessExpressionDO.class);\n        processExpressionMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteProcessExpression(Long id) {\n        // 校验存在\n        validateProcessExpressionExists(id);\n        // 删除\n        processExpressionMapper.deleteById(id);\n    }\n\n    private void validateProcessExpressionExists(Long id) {\n        if (processExpressionMapper.selectById(id) == null) {\n            throw exception(PROCESS_EXPRESSION_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public BpmProcessExpressionDO getProcessExpression(Long id) {\n        return processExpressionMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<BpmProcessExpressionDO> getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO) {\n        return processExpressionMapper.selectPage(pageReqVO);\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessListenerService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessListenerDO;\n\nimport javax.validation.Valid;\n\n/**\n * BPM 流程监听器 Service 接口\n *\n * @author 芋道源码\n */\npublic interface BpmProcessListenerService {\n\n    /**\n     * 创建流程监听器\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProcessListener(@Valid BpmProcessListenerSaveReqVO createReqVO);\n\n    /**\n     * 更新流程监听器\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProcessListener(@Valid BpmProcessListenerSaveReqVO updateReqVO);\n\n    /**\n     * 删除流程监听器\n     *\n     * @param id 编号\n     */\n    void deleteProcessListener(Long id);\n\n    /**\n     * 获得流程监听器\n     *\n     * @param id 编号\n     * @return 流程监听器\n     */\n    BpmProcessListenerDO getProcessListener(Long id);\n\n    /**\n     * 获得流程监听器分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 流程监听器分页\n     */\n    PageResult<BpmProcessListenerDO> getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO);\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessListenerServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessListenerDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessListenerMapper;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerValueTypeEnum;\nimport org.flowable.engine.delegate.JavaDelegate;\nimport org.flowable.engine.delegate.TaskListener;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;\n\n/**\n * BPM 流程监听器 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class BpmProcessListenerServiceImpl implements BpmProcessListenerService {\n\n    @Resource\n    private BpmProcessListenerMapper processListenerMapper;\n\n    @Override\n    public Long createProcessListener(BpmProcessListenerSaveReqVO createReqVO) {\n        // 校验\n        validateCreateProcessListenerValue(createReqVO);\n        // 插入\n        BpmProcessListenerDO processListener = BeanUtils.toBean(createReqVO, BpmProcessListenerDO.class);\n        processListenerMapper.insert(processListener);\n        return processListener.getId();\n    }\n\n    @Override\n    public void updateProcessListener(BpmProcessListenerSaveReqVO updateReqVO) {\n        // 校验存在\n        validateProcessListenerExists(updateReqVO.getId());\n        validateCreateProcessListenerValue(updateReqVO);\n        // 更新\n        BpmProcessListenerDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessListenerDO.class);\n        processListenerMapper.updateById(updateObj);\n    }\n\n    private void validateCreateProcessListenerValue(BpmProcessListenerSaveReqVO createReqVO) {\n        // class 类型\n        if (createReqVO.getValueType().equals(BpmProcessListenerValueTypeEnum.CLASS.getType())) {\n            try {\n                Class<?> clazz = Class.forName(createReqVO.getValue());\n                if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.EXECUTION.getType())\n                        && !JavaDelegate.class.isAssignableFrom(clazz)) {\n                    throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(),\n                            JavaDelegate.class.getName());\n                } else if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.TASK.getType())\n                        && !TaskListener.class.isAssignableFrom(clazz)) {\n                    throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(),\n                            TaskListener.class.getName());\n                }\n            } catch (ClassNotFoundException e) {\n                throw exception(PROCESS_LISTENER_CLASS_NOT_FOUND, createReqVO.getValue());\n            }\n            return;\n        }\n        // 表达式\n        if (!StrUtil.startWith(createReqVO.getValue(), \"${\") || !StrUtil.endWith(createReqVO.getValue(), \"}\")) {\n            throw exception(PROCESS_LISTENER_EXPRESSION_INVALID, createReqVO.getValue());\n        }\n    }\n\n    @Override\n    public void deleteProcessListener(Long id) {\n        // 校验存在\n        validateProcessListenerExists(id);\n        // 删除\n        processListenerMapper.deleteById(id);\n    }\n\n    private void validateProcessListenerExists(Long id) {\n        if (processListenerMapper.selectById(id) == null) {\n            throw exception(PROCESS_LISTENER_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public BpmProcessListenerDO getProcessListener(Long id) {\n        return processListenerMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<BpmProcessListenerDO> getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO) {\n        return processListenerMapper.selectPage(pageReqVO);\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 用户组 Service 接口\n *\n * @author 芋道源码\n */\npublic interface BpmUserGroupService {\n\n    /**\n     * 创建用户组\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createUserGroup(@Valid BpmUserGroupSaveReqVO createReqVO);\n\n    /**\n     * 更新用户组\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateUserGroup(@Valid BpmUserGroupSaveReqVO updateReqVO);\n\n    /**\n     * 删除用户组\n     *\n     * @param id 编号\n     */\n    void deleteUserGroup(Long id);\n\n    /**\n     * 获得用户组\n     *\n     * @param id 编号\n     * @return 用户组\n     */\n    BpmUserGroupDO getUserGroup(Long id);\n\n    /**\n     * 获得用户组列表\n     *\n     * @param ids 编号\n     * @return 用户组列表\n     */\n    List<BpmUserGroupDO> getUserGroupList(Collection<Long> ids);\n\n    /**\n     * 获得指定状态的用户组列表\n     *\n     * @param status 状态\n     * @return 用户组列表\n     */\n    List<BpmUserGroupDO> getUserGroupListByStatus(Integer status);\n\n    /**\n     * 获得用户组分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 用户组分页\n     */\n    PageResult<BpmUserGroupDO> getUserGroupPage(BpmUserGroupPageReqVO pageReqVO);\n\n    /**\n     * 校验用户组们是否有效。如下情况，视为无效：\n     * 1. 用户组编号不存在\n     * 2. 用户组被禁用\n     *\n     * @param ids 用户组编号数组\n     */\n    void validUserGroups(Collection<Long> ids);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmUserGroupMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_IS_DISABLE;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS;\n\n/**\n * 用户组 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class BpmUserGroupServiceImpl implements BpmUserGroupService {\n\n    @Resource\n    private BpmUserGroupMapper userGroupMapper;\n\n    @Override\n    public Long createUserGroup(BpmUserGroupSaveReqVO createReqVO) {\n        BpmUserGroupDO userGroup = BeanUtils.toBean(createReqVO, BpmUserGroupDO.class);\n        userGroupMapper.insert(userGroup);\n        return userGroup.getId();\n    }\n\n    @Override\n    public void updateUserGroup(BpmUserGroupSaveReqVO updateReqVO) {\n        // 校验存在\n        validateUserGroupExists(updateReqVO.getId());\n        // 更新\n        BpmUserGroupDO updateObj = BeanUtils.toBean(updateReqVO, BpmUserGroupDO.class);\n        userGroupMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteUserGroup(Long id) {\n        // 校验存在\n        this.validateUserGroupExists(id);\n        // 删除\n        userGroupMapper.deleteById(id);\n    }\n\n    private void validateUserGroupExists(Long id) {\n        if (userGroupMapper.selectById(id) == null) {\n            throw exception(USER_GROUP_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public BpmUserGroupDO getUserGroup(Long id) {\n        return userGroupMapper.selectById(id);\n    }\n\n    @Override\n    public List<BpmUserGroupDO> getUserGroupList(Collection<Long> ids) {\n        return userGroupMapper.selectByIds(ids);\n    }\n\n\n    @Override\n    public List<BpmUserGroupDO> getUserGroupListByStatus(Integer status) {\n        return userGroupMapper.selectListByStatus(status);\n    }\n\n    @Override\n    public PageResult<BpmUserGroupDO> getUserGroupPage(BpmUserGroupPageReqVO pageReqVO) {\n        return userGroupMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public void validUserGroups(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return;\n        }\n        // 获得用户组信息\n        List<BpmUserGroupDO> userGroups = userGroupMapper.selectByIds(ids);\n        Map<Long, BpmUserGroupDO> userGroupMap = convertMap(userGroups, BpmUserGroupDO::getId);\n        // 校验\n        ids.forEach(id -> {\n            BpmUserGroupDO userGroup = userGroupMap.get(id);\n            if (userGroup == null) {\n                throw exception(USER_GROUP_NOT_EXISTS);\n            }\n            if (!CommonStatusEnum.ENABLE.getStatus().equals(userGroup.getStatus())) {\n                throw exception(USER_GROUP_IS_DISABLE, userGroup.getName());\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition.dto;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n/**\n * Bpm 表单的 Field 表单项 Response DTO\n * 字段的定义，可见 https://github.com/JakHuang/form-generator/issues/46 文档\n *\n * @author 芋道源码\n */\n@Data\npublic class BpmFormFieldRespDTO {\n\n    /**\n     * 表单标题\n     */\n    private String label;\n    /**\n     * 表单字段的属性名，可自定义\n     */\n    @JsonProperty(value = \"vModel\")\n    private String vModel;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition.dto;\n\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;\nimport lombok.Data;\n\n/**\n * BPM 流程 MetaInfo Response DTO\n * 主要用于 { Model#setMetaInfo(String)} 的存储\n *\n * 最终，它的字段和 {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的\n *\n * @author 芋道源码\n */\n@Data\npublic class BpmModelMetaInfoRespDTO {\n\n    /**\n     * 流程图标\n     */\n    private String icon;\n    /**\n     * 流程描述\n     */\n    private String description;\n\n    /**\n     * 表单类型\n     */\n    private Integer formType;\n    /**\n     * 表单编号\n     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时\n     */\n    private Long formId;\n    /**\n     * 自定义表单的提交路径，使用 Vue 的路由地址\n     * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时\n     */\n    private String formCustomCreatePath;\n    /**\n     * 自定义表单的查看路径，使用 Vue 的路由地址\n     * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时\n     */\n    private String formCustomViewPath;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition.dto;\n\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n/**\n * 流程定义创建 Request DTO\n */\n@Data\npublic class BpmProcessDefinitionCreateReqDTO {\n\n    // ========== 模型相关 ==========\n\n    /**\n     * 流程模型的编号\n     */\n    @NotEmpty(message = \"流程模型编号不能为空\")\n    private String modelId;\n    /**\n     * 流程标识\n     */\n    @NotEmpty(message = \"流程标识不能为空\")\n    private String key;\n    /**\n     * 流程名称\n     */\n    @NotEmpty(message = \"流程名称不能为空\")\n    private String name;\n    /**\n     * 流程描述\n     */\n    private String description;\n    /**\n     * 流程分类\n     */\n    @NotEmpty(message = \"流程分类不能为空\")\n    private String category;\n    /**\n     * BPMN XML\n     */\n    @NotEmpty(message = \"BPMN XML 不能为空\")\n    private byte[] bpmnBytes;\n\n    // ========== 表单相关 ==========\n\n    /**\n     * 表单类型\n     */\n    @NotNull(message = \"表单类型不能为空\")\n    private Integer formType;\n    /**\n     * 动态表单编号\n     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时\n     */\n    private Long formId;\n    /**\n     * 表单的配置\n     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时\n     */\n    private String formConf;\n    /**\n     * 表单项的数组\n     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时\n     */\n    private List<String> formFields;\n    /**\n     * 自定义表单的提交路径，使用 Vue 的路由地址\n     * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时\n     */\n    private String formCustomCreatePath;\n    /**\n     * 自定义表单的查看路径，使用 Vue 的路由地址\n     * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时\n     */\n    private String formCustomViewPath;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.message;\n\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;\n\nimport javax.validation.Valid;\n\n/**\n * BPM 消息 Service 接口\n *\n * TODO 芋艿：未来支持消息的可配置；不同的流程，在什么场景下，需要发送什么消息，消息的内容是什么；\n *\n * @author 芋道源码\n */\npublic interface BpmMessageService {\n\n    /**\n     * 发送流程实例被通过的消息\n     *\n     * @param reqDTO 发送信息\n     */\n    void sendMessageWhenProcessInstanceApprove(@Valid BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO);\n\n    /**\n     * 发送流程实例被不通过的消息\n     *\n     * @param reqDTO 发送信息\n     */\n    void sendMessageWhenProcessInstanceReject(@Valid BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO);\n\n    /**\n     * 发送任务被分配的消息\n     *\n     * @param reqDTO 发送信息\n     */\n    void sendMessageWhenTaskAssigned(@Valid BpmMessageSendWhenTaskCreatedReqDTO reqDTO);\n\n    /**\n     * 发送任务审批超时的消息\n     *\n     * @param reqDTO 发送信息\n     */\n    void sendMessageWhenTaskTimeout(@Valid BpmMessageSendWhenTaskTimeoutReqDTO reqDTO);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.message;\n\nimport cn.iocoder.yudao.framework.web.config.WebProperties;\nimport cn.iocoder.yudao.module.bpm.convert.message.BpmMessageConvert;\nimport cn.iocoder.yudao.module.bpm.enums.message.BpmMessageEnum;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;\nimport cn.iocoder.yudao.module.system.api.sms.SmsSendApi;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * BPM 消息 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class BpmMessageServiceImpl implements BpmMessageService {\n\n    @Resource\n    private SmsSendApi smsSendApi;\n\n    @Resource\n    private WebProperties webProperties;\n\n    @Override\n    public void sendMessageWhenProcessInstanceApprove(BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO) {\n        Map<String, Object> templateParams = new HashMap<>();\n        templateParams.put(\"processInstanceName\", reqDTO.getProcessInstanceName());\n        templateParams.put(\"detailUrl\", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));\n        smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(),\n                BpmMessageEnum.PROCESS_INSTANCE_APPROVE.getSmsTemplateCode(), templateParams)).checkError();\n    }\n\n    @Override\n    public void sendMessageWhenProcessInstanceReject(BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO) {\n        Map<String, Object> templateParams = new HashMap<>();\n        templateParams.put(\"processInstanceName\", reqDTO.getProcessInstanceName());\n        templateParams.put(\"reason\", reqDTO.getReason());\n        templateParams.put(\"detailUrl\", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));\n        smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(),\n                BpmMessageEnum.PROCESS_INSTANCE_REJECT.getSmsTemplateCode(), templateParams)).checkError();\n    }\n\n    @Override\n    public void sendMessageWhenTaskAssigned(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) {\n        Map<String, Object> templateParams = new HashMap<>();\n        templateParams.put(\"processInstanceName\", reqDTO.getProcessInstanceName());\n        templateParams.put(\"taskName\", reqDTO.getTaskName());\n        templateParams.put(\"startUserNickname\", reqDTO.getStartUserNickname());\n        templateParams.put(\"detailUrl\", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));\n        smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(),\n                BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams)).checkError();\n    }\n\n    @Override\n    public void sendMessageWhenTaskTimeout(BpmMessageSendWhenTaskTimeoutReqDTO reqDTO) {\n        Map<String, Object> templateParams = new HashMap<>();\n        templateParams.put(\"processInstanceName\", reqDTO.getProcessInstanceName());\n        templateParams.put(\"taskName\", reqDTO.getTaskName());\n        templateParams.put(\"detailUrl\", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));\n        smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(),\n                BpmMessageEnum.TASK_TIMEOUT.getSmsTemplateCode(), templateParams)).checkError();\n    }\n\n    private String getProcessInstanceDetailUrl(String taskId) {\n        return webProperties.getAdminUi().getUrl() + \"/bpm/process-instance/detail?id=\" + taskId;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.message.dto;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * BPM 发送流程实例被通过 Request DTO\n */\n@Data\npublic class BpmMessageSendWhenProcessInstanceApproveReqDTO {\n\n    /**\n     * 流程实例的编号\n     */\n    @NotEmpty(message = \"流程实例的编号不能为空\")\n    private String processInstanceId;\n    /**\n     * 流程实例的名字\n     */\n    @NotEmpty(message = \"流程实例的名字不能为空\")\n    private String processInstanceName;\n    @NotNull(message = \"发起人的用户编号\")\n    private Long startUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.message.dto;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * BPM 发送流程实例被不通过 Request DTO\n */\n@Data\npublic class BpmMessageSendWhenProcessInstanceRejectReqDTO {\n\n    /**\n     * 流程实例的编号\n     */\n    @NotEmpty(message = \"流程实例的编号不能为空\")\n    private String processInstanceId;\n    /**\n     * 流程实例的名字\n     */\n    @NotEmpty(message = \"流程实例的名字不能为空\")\n    private String processInstanceName;\n    @NotNull(message = \"发起人的用户编号\")\n    private Long startUserId;\n\n    /**\n     * 不通过理由\n     */\n    @NotEmpty(message = \"不通过理由不能为空\")\n    private String reason;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.message.dto;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * BPM 发送任务被分配 Request DTO\n */\n@Data\npublic class BpmMessageSendWhenTaskCreatedReqDTO {\n\n    /**\n     * 流程实例的编号\n     */\n    @NotEmpty(message = \"流程实例的编号不能为空\")\n    private String processInstanceId;\n    /**\n     * 流程实例的名字\n     */\n    @NotEmpty(message = \"流程实例的名字不能为空\")\n    private String processInstanceName;\n    @NotNull(message = \"发起人的用户编号\")\n    private Long startUserId;\n    @NotEmpty(message = \"发起人的昵称\")\n    private String startUserNickname;\n\n    /**\n     * 流程任务的编号\n     */\n    @NotEmpty(message = \"流程任务的编号不能为空\")\n    private String taskId;\n    /**\n     * 流程任务的名字\n     */\n    @NotEmpty(message = \"流程任务的名字不能为空\")\n    private String taskName;\n\n    /**\n     * 审批人的用户编号\n     */\n    @NotNull(message = \"审批人的用户编号不能为空\")\n    private Long assigneeUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.message.dto;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * BPM 发送任务审批超时 Request DTO\n */\n@Data\npublic class BpmMessageSendWhenTaskTimeoutReqDTO {\n\n    /**\n     * 流程实例的编号\n     */\n    @NotEmpty(message = \"流程实例的编号不能为空\")\n    private String processInstanceId;\n    /**\n     * 流程实例的名字\n     */\n    @NotEmpty(message = \"流程实例的名字不能为空\")\n    private String processInstanceName;\n\n    /**\n     * 流程任务的编号\n     */\n    @NotEmpty(message = \"流程任务的编号不能为空\")\n    private String taskId;\n    /**\n     * 流程任务的名字\n     */\n    @NotEmpty(message = \"流程任务的名字不能为空\")\n    private String taskName;\n\n    /**\n     * 审批人的用户编号\n     */\n    @NotNull(message = \"审批人的用户编号不能为空\")\n    private Long assigneeUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.oa;\n\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;\n\nimport javax.validation.Valid;\n\n/**\n * 请假申请 Service 接口\n *\n * @author jason\n * @author 芋道源码\n */\npublic interface BpmOALeaveService {\n\n    /**\n     * 创建请假申请\n     *\n     * @param userId 用户编号\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createLeave(Long userId, @Valid BpmOALeaveCreateReqVO createReqVO);\n\n    /**\n     * 更新请假申请的状态\n     *\n     * @param id 编号\n     * @param status 结果\n     */\n    void updateLeaveStatus(Long id, Integer status);\n\n    /**\n     * 获得请假申请\n     *\n     * @param id 编号\n     * @return 请假申请\n     */\n    BpmOALeaveDO getLeave(Long id);\n\n    /**\n     * 获得请假申请分页\n     *\n     * @param userId 用户编号\n     * @param pageReqVO 分页查询\n     * @return 请假申请分页\n     */\n    PageResult<BpmOALeaveDO> getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.oa;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;\nimport cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOALeaveMapper;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_LEAVE_NOT_EXISTS;\n\n/**\n * OA 请假申请 Service 实现类\n *\n * @author jason\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class BpmOALeaveServiceImpl implements BpmOALeaveService {\n\n    /**\n     * OA 请假对应的流程定义 KEY\n     */\n    public static final String PROCESS_KEY = \"oa_leave\";\n\n    @Resource\n    private BpmOALeaveMapper leaveMapper;\n\n    @Resource\n    private BpmProcessInstanceApi processInstanceApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createLeave(Long userId, BpmOALeaveCreateReqVO createReqVO) {\n        // 插入 OA 请假单\n        long day = LocalDateTimeUtil.between(createReqVO.getStartTime(), createReqVO.getEndTime()).toDays();\n        BpmOALeaveDO leave = BeanUtils.toBean(createReqVO, BpmOALeaveDO.class)\n                .setUserId(userId).setDay(day).setStatus(BpmTaskStatusEnum.RUNNING.getStatus());\n        leaveMapper.insert(leave);\n\n        // 发起 BPM 流程\n        Map<String, Object> processInstanceVariables = new HashMap<>();\n        processInstanceVariables.put(\"day\", day);\n        String processInstanceId = processInstanceApi.createProcessInstance(userId,\n                new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)\n                        .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId()))\n                        .setStartUserSelectAssignees(createReqVO.getStartUserSelectAssignees())).getCheckedData();\n\n        // 将工作流的编号，更新到 OA 请假单中\n        leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId));\n        return leave.getId();\n    }\n\n    @Override\n    public void updateLeaveStatus(Long id, Integer status) {\n        validateLeaveExists(id);\n        leaveMapper.updateById(new BpmOALeaveDO().setId(id).setStatus(status));\n    }\n\n    private void validateLeaveExists(Long id) {\n        if (leaveMapper.selectById(id) == null) {\n            throw exception(OA_LEAVE_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public BpmOALeaveDO getLeave(Long id) {\n        return leaveMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<BpmOALeaveDO> getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO) {\n        return leaveMapper.selectPage(userId, pageReqVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.oa.listener;\n\nimport cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;\nimport cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;\nimport cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveService;\nimport cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveServiceImpl;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * OA 请假单的结果的监听器实现类\n *\n * @author 芋道源码\n */\n@Component\npublic class BpmOALeaveStatusListener extends BpmProcessInstanceStatusEventListener {\n\n    @Resource\n    private BpmOALeaveService leaveService;\n\n    @Override\n    protected String getProcessDefinitionKey() {\n        return BpmOALeaveServiceImpl.PROCESS_KEY;\n    }\n\n    @Override\n    protected void onEvent(BpmProcessInstanceStatusEvent event) {\n        leaveService.updateLeaveStatus(Long.parseLong(event.getBusinessKey()), event.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;\nimport org.flowable.bpmn.model.FlowNode;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.Collection;\n\n/**\n * 流程抄送 Service 接口\n *\n * 现在是在审批的时候进行流程抄送\n */\npublic interface BpmProcessInstanceCopyService {\n\n    /**\n     * 【管理员】流程实例的抄送\n     *\n     * @param userIds 抄送的用户编号\n     * @param reason 抄送意见\n     * @param taskId 流程任务编号\n     */\n    void createProcessInstanceCopy(Collection<Long> userIds, String reason, String taskId);\n\n    /**\n     * 【自动抄送】流程实例的抄送\n     *\n     * @param userIds 抄送的用户编号\n     * @param reason 抄送意见\n     * @param processInstanceId 流程编号\n     * @param activityId 流程活动编号（对应 {@link FlowNode#getId()}）\n     * @param activityName 任务编号（对应 {@link FlowNode#getName()}）\n     * @param taskId 任务编号，允许空\n     */\n    void createProcessInstanceCopy(Collection<Long> userIds, String reason,\n                                   @NotEmpty(message = \"流程实例编号不能为空\") String processInstanceId,\n                                   @NotEmpty(message = \"流程活动编号不能为空\") String activityId,\n                                   @NotEmpty(message = \"流程活动名字不能为空\") String activityName,\n                                   String taskId);\n\n    /**\n     * 获得抄送的流程的分页\n     *\n     * @param userId 当前登录用户\n     * @param pageReqVO 分页请求\n     * @return 抄送的分页结果\n     */\n    PageResult<BpmProcessInstanceCopyDO> getProcessInstanceCopyPage(Long userId,\n                                                                    BpmProcessInstanceCopyPageReqVO pageReqVO);\n\n    /**\n     * 删除抄送流程\n     *\n     * @param processInstanceId 流程实例 ID\n     */\n    void deleteProcessInstanceCopy(String processInstanceId);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper;\nimport cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.task.api.Task;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * 流程抄送 Service 实现类\n *\n * @author kyle\n */\n@Service\n@Validated\n@Slf4j\npublic class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService {\n\n    @Resource\n    private BpmProcessInstanceCopyMapper processInstanceCopyMapper;\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private BpmTaskService taskService;\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private BpmProcessInstanceService processInstanceService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private BpmProcessDefinitionService processDefinitionService;\n\n    @Override\n    public void createProcessInstanceCopy(Collection<Long> userIds, String reason, String taskId) {\n        Task task = taskService.getTask(taskId);\n        if (ObjectUtil.isNull(task)) {\n            throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);\n        }\n        // 执行抄送\n        createProcessInstanceCopy(userIds, reason,\n                task.getProcessInstanceId(), task.getTaskDefinitionKey(), task.getName(), task.getId());\n    }\n\n    @Override\n    public void createProcessInstanceCopy(Collection<Long> userIds, String reason, String processInstanceId,\n                                          String activityId, String activityName, String taskId) {\n        // 1.1 校验流程实例存在\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n        if (processInstance == null) {\n            throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);\n        }\n        // 1.2 校验流程定义存在\n        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(\n                processInstance.getProcessDefinitionId());\n        if (processDefinition == null) {\n            throw exception(ErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS);\n        }\n\n        // 2. 创建抄送流程\n        List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()\n                .setUserId(userId).setReason(reason).setStartUserId(Long.valueOf(processInstance.getStartUserId()))\n                .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())\n                .setCategory(processDefinition.getCategory()).setTaskId(taskId)\n                .setActivityId(activityId).setActivityName(activityName)\n                .setProcessDefinitionId(processInstance.getProcessDefinitionId()));\n        processInstanceCopyMapper.insertBatch(copyList);\n    }\n\n    @Override\n    public PageResult<BpmProcessInstanceCopyDO> getProcessInstanceCopyPage(Long userId,\n                                                                           BpmProcessInstanceCopyPageReqVO pageReqVO) {\n        return processInstanceCopyMapper.selectPage(userId, pageReqVO);\n    }\n\n    @Override\n    public void deleteProcessInstanceCopy(String processInstanceId) {\n        processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;\nimport org.flowable.engine.history.HistoricProcessInstance;\nimport org.flowable.engine.runtime.ProcessInstance;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 流程实例 Service 接口\n *\n * @author 芋道源码\n */\npublic interface BpmProcessInstanceService {\n\n    // ========== Query 查询相关方法 ==========\n\n    /**\n     * 获得流程实例\n     *\n     * @param id 流程实例的编号\n     * @return 流程实例\n     */\n    ProcessInstance getProcessInstance(String id);\n\n    /**\n     * 获得流程实例列表\n     *\n     * @param ids 流程实例的编号集合\n     * @return 流程实例列表\n     */\n    List<ProcessInstance> getProcessInstances(Set<String> ids);\n\n    /**\n     * 获得流程实例 Map\n     *\n     * @param ids 流程实例的编号集合\n     * @return 流程实例列表 Map\n     */\n    default Map<String, ProcessInstance> getProcessInstanceMap(Set<String> ids) {\n        return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId);\n    }\n\n    /**\n     * 获得历史的流程实例\n     *\n     * @param id 流程实例的编号\n     * @return 历史的流程实例\n     */\n    HistoricProcessInstance getHistoricProcessInstance(String id);\n\n    /**\n     * 获得历史的流程实例列表\n     *\n     * @param ids 流程实例的编号集合\n     * @return 历史的流程实例列表\n     */\n    List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids);\n\n    /**\n     * 获得历史的流程实例 Map\n     *\n     * @param ids 流程实例的编号集合\n     * @return 历史的流程实例列表 Map\n     */\n    default Map<String, HistoricProcessInstance> getHistoricProcessInstanceMap(Set<String> ids) {\n        return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId);\n    }\n\n    /**\n     * 获得流程实例的分页\n     *\n     * @param userId    用户编号\n     * @param pageReqVO 分页请求\n     * @return 流程实例的分页\n     */\n    PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,\n                                                               @Valid BpmProcessInstancePageReqVO pageReqVO);\n\n    /**\n     * 获取审批详情。\n     * <p>\n     * 可以是准备发起的流程、进行中的流程、已经结束的流程\n     *\n     * @param loginUserId  登录人的用户编号\n     * @param reqVO 请求信息\n     * @return 流程实例的进度\n     */\n    BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO);\n\n    /**\n     * 获取下一个执行节点信息\n     *\n     * @param loginUserId 登录人的用户编号\n     * @param reqVO 请求信息\n     * @return 下一个执行节点信息\n     */\n    List<BpmApprovalDetailRespVO.ActivityNode> getNextApprovalNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO);\n\n    /**\n     * 获取流程实例的 BPMN 模型视图\n     *\n     * @param id 流程实例的编号\n     * @return BPMN 模型视图\n     */\n    BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id);\n\n    // ========== Update 写入相关方法 ==========\n\n    /**\n     * 创建流程实例（提供给前端）\n     *\n     * @param userId      用户编号\n     * @param createReqVO 创建信息\n     * @return 实例的编号\n     */\n    String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO);\n\n    /**\n     * 创建流程实例（提供给内部）\n     *\n     * @param userId       用户编号\n     * @param createReqDTO 创建信息\n     * @return 实例的编号\n     */\n    String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO);\n\n    /**\n     * 发起人取消流程实例\n     *\n     * @param userId      用户编号\n     * @param cancelReqVO 取消信息\n     */\n    void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO);\n\n    /**\n     * 管理员取消流程实例\n     *\n     * @param userId      用户编号\n     * @param cancelReqVO 取消信息\n     */\n    void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO);\n\n    /**\n     * 更新 ProcessInstance 为不通过\n     *\n     * @param processInstance 流程实例\n     * @param reason          理由。例如说，审批不通过时，需要传递该值\n     */\n    void updateProcessInstanceReject(ProcessInstance processInstance, String reason);\n\n    /**\n     * 更新 ProcessInstance 的变量\n     *\n     * @param id 流程编号\n     * @param variables 流程变量\n     */\n    void updateProcessInstanceVariables(String id, Map<String, Object> variables);\n\n    /**\n     * 删除 ProcessInstance 的变量\n     *\n     * @param id  流程编号\n     * @param variableNames 流程变量名\n     */\n    void removeProcessInstanceVariables(String id, Collection<String> variableNames);\n\n    // ========== Event 事件相关方法 ==========\n\n    /**\n     * 处理 ProcessInstance 完成事件，例如说：审批通过、不通过、取消\n     *\n     * @param instance 流程任务\n     */\n    void processProcessInstanceCompleted(ProcessInstance instance);\n\n    /**\n     * 处理 ProcessInstance 开始事件，例如说：流程前置通知\n     *\n     * @param instance 流程任务\n     */\n    void processProcessInstanceCreated(ProcessInstance instance);\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.*;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.date.DateUtils;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.common.util.object.PageUtils;\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNodeTask;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;\nimport cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.dal.redis.BpmProcessIdRedisDAO;\nimport cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.constants.BpmnXMLConstants;\nimport org.flowable.bpmn.model.*;\nimport org.flowable.engine.HistoryService;\nimport org.flowable.engine.RuntimeService;\nimport org.flowable.engine.history.HistoricActivityInstance;\nimport org.flowable.engine.history.HistoricProcessInstance;\nimport org.flowable.engine.history.HistoricProcessInstanceQuery;\nimport org.flowable.engine.repository.ProcessDefinition;\nimport org.flowable.engine.runtime.Execution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.engine.runtime.ProcessInstanceBuilder;\nimport org.flowable.task.api.Task;\nimport org.flowable.task.api.history.HistoricTaskInstance;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.transaction.support.TransactionSynchronization;\nimport org.springframework.transaction.support.TransactionSynchronizationManager;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType;\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.singletonList;\nimport static org.flowable.bpmn.constants.BpmnXMLConstants.*;\n\n/**\n * 流程实例 Service 实现类\n * <p>\n * ProcessDefinition & ProcessInstance & Execution & Task 的关系：\n * 1. <a href=\"https://blog.csdn.net/bobozai86/article/details/105210414\" />\n * <p>\n * HistoricProcessInstance & ProcessInstance 的关系：\n * 1. <a href=\" https://my.oschina.net/843294669/blog/71902\" />\n * <p>\n * 简单来说，前者 = 历史 + 运行中的流程实例，后者仅是运行中的流程实例\n *\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {\n\n    @Resource\n    private RuntimeService runtimeService;\n    @Resource\n    private HistoryService historyService;\n\n    @Resource\n    private BpmProcessDefinitionService processDefinitionService;\n    @Resource\n    @Lazy // 避免循环依赖\n    private BpmTaskService taskService;\n    @Resource\n    private BpmMessageService messageService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @Resource\n    private BpmProcessInstanceEventPublisher processInstanceEventPublisher;\n\n    @Resource\n    private BpmTaskCandidateInvoker taskCandidateInvoker;\n\n    @Resource\n    private BpmProcessIdRedisDAO processIdRedisDAO;\n\n    // ========== Query 查询相关方法 ==========\n\n    @Override\n    public ProcessInstance getProcessInstance(String id) {\n        return runtimeService.createProcessInstanceQuery()\n                .includeProcessVariables()\n                .processInstanceId(id)\n                .singleResult();\n    }\n\n    @Override\n    public List<ProcessInstance> getProcessInstances(Set<String> ids) {\n        return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables().list();\n    }\n\n    @Override\n    public HistoricProcessInstance getHistoricProcessInstance(String id) {\n        return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables()\n                .singleResult();\n    }\n\n    @Override\n    public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) {\n        return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables()\n                .list();\n    }\n\n    private Map<String, String> getFormFieldsPermission(BpmnModel bpmnModel,\n                                                        String activityId, String taskId) {\n        // 1. 获取流程活动编号。流程活动 Id 为空事，从流程任务中获取流程活动 Id\n        if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) {\n            activityId = Optional.ofNullable(taskService.getHistoricTask(taskId))\n                    .map(HistoricTaskInstance::getTaskDefinitionKey).orElse(null);\n        }\n        if (StrUtil.isEmpty(activityId)) {\n            return null;\n        }\n\n        // 2. 从 BpmnModel 中解析表单字段权限\n        return BpmnModelUtils.parseFormFieldsPermission(bpmnModel, activityId);\n    }\n\n    @Override\n    public BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, BpmApprovalDetailReqVO reqVO) {\n        // 1.1 从 reqVO 中，读取公共变量\n        Long startUserId = loginUserId; // 流程发起人\n        HistoricProcessInstance historicProcessInstance = null; // 流程实例\n        Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态\n        Map<String, Object> processVariables = new HashMap<>(); // 流程变量\n        // 1.2 如果是流程已发起的场景，则使用流程实例的数据\n        if (reqVO.getProcessInstanceId() != null) {\n            historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId());\n            if (historicProcessInstance == null) {\n                throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);\n            }\n            startUserId = Long.valueOf(historicProcessInstance.getStartUserId());\n            processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance);\n            // 合并 DB 和前端传递的流量变量，以前端的为主\n            if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) {\n                processVariables.putAll(historicProcessInstance.getProcessVariables());\n            }\n        }\n        if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) {\n            processVariables.putAll(reqVO.getProcessVariables());\n        }\n        // 特殊：如果是未发起的场景，则设置发起用户，解决“发起流程”时，需要使用到该变量的问题。例如说：https://t.zsxq.com/fMw5g\n        if (historicProcessInstance == null) {\n            processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, loginUserId);\n        }\n        // 1.3 读取其它相关数据\n        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(\n                historicProcessInstance != null ? historicProcessInstance.getProcessDefinitionId()\n                        : reqVO.getProcessDefinitionId());\n        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService\n                .getProcessDefinitionInfo(processDefinition.getId());\n        BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());\n\n        // 2.1 已结束 + 进行中的活动节点\n        List<ActivityNode> endActivityNodes = null; // 已结束的审批信息\n        List<ActivityNode> runActivityNodes = null; // 进行中的审批信息\n        List<HistoricActivityInstance> activities = null; // 流程实例列表\n        if (reqVO.getProcessInstanceId() != null) {\n            activities = taskService.getActivityListByProcessInstanceId(reqVO.getProcessInstanceId());\n            List<HistoricTaskInstance> tasks = taskService.getTaskListByProcessInstanceId(reqVO.getProcessInstanceId(),\n                    true);\n            endActivityNodes = getEndActivityNodeList(startUserId, bpmnModel, processDefinitionInfo,\n                    historicProcessInstance, processInstanceStatus, activities, tasks);\n            runActivityNodes = getRunApproveNodeList(startUserId, bpmnModel, processDefinition, processVariables,\n                    activities, tasks);\n        }\n\n        // 2.2 流程已经结束，直接 return，无需预测\n        if (BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)) {\n            return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo,\n                    historicProcessInstance,\n                    processInstanceStatus, endActivityNodes, runActivityNodes, null, null);\n        }\n\n        // 3.1 计算当前登录用户的待办任务\n        BpmTaskRespVO todoTask = taskService.getTodoTask(loginUserId, reqVO.getTaskId(), reqVO.getProcessInstanceId());\n\n        // 3.2 获取由于退回操作，需要预测的节点。从流程变量中获取，回退操作会设置这些变量\n        Set<String> needSimulateTaskDefKeysByReturn = new HashSet<>();\n        if (StrUtil.isNotEmpty(reqVO.getProcessInstanceId())) {\n            Object needSimulateTaskIds = runtimeService.getVariable(reqVO.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS);\n            needSimulateTaskDefKeysByReturn.addAll(Convert.toSet(String.class, needSimulateTaskIds));\n        }\n        // 移除运行中的节点，运行中的节点无需预测\n        if (CollUtil.isNotEmpty(runActivityNodes)) {\n            runActivityNodes.forEach( activityNode -> needSimulateTaskDefKeysByReturn.remove(activityNode.getId()));\n        }\n\n        // 3.3 预测未运行节点的审批信息\n        List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel,\n                processDefinitionInfo,\n                processVariables, activities, needSimulateTaskDefKeysByReturn);\n\n        // 4. 拼接最终数据\n        return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance,\n                processInstanceStatus, endActivityNodes, runActivityNodes, simulateActivityNodes, todoTask);\n    }\n\n    @Override\n    public List<ActivityNode> getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) {\n        // 1.1 校验任务存在，且是当前用户的\n        Task task = taskService.validateTask(loginUserId, reqVO.getTaskId());\n        // 1.2 校验流程实例存在\n        ProcessInstance instance = getProcessInstance(task.getProcessInstanceId());\n        if (instance == null) {\n            throw exception(PROCESS_INSTANCE_NOT_EXISTS);\n        }\n        HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId());\n        if (historicProcessInstance == null) {\n            throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);\n        }\n        // 1.3 校验BpmnModel\n        BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId());\n        if (bpmnModel == null) {\n            return null;\n        }\n\n        // 2. 设置流程变量\n        Map<String, Object> processVariables = new HashMap<>();\n        // 2.1 获取历史中流程变量\n        if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) {\n            processVariables.putAll(historicProcessInstance.getProcessVariables());\n        }\n        // 2.2 合并前端传递的流程变量，以前端为准\n        if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) {\n            processVariables.putAll(reqVO.getProcessVariables());\n        }\n\n        // 3. 获取下一个将要执行的节点集合\n        FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());\n        List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables);\n        // 仅仅获取 UserTask 节点  TODO add from jason：如果网关节点和网关节点相连，获取下个 UserTask. 貌似有点不准。\n        List<FlowNode> nextUserTaskList = CollectionUtils.filterList(nextFlowNodes, node -> node instanceof UserTask);\n        List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId())\n                .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())\n                .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())\n                .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node))\n                .setCandidateUserIds(getTaskCandidateUserList(bpmnModel, node.getId(),\n                        loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables)));\n        if (CollUtil.isEmpty(nextActivityNodes)) {\n            return nextActivityNodes;\n        }\n\n        // 4. 拼接基础信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSetByFlatMap(nextActivityNodes, ActivityNode::getCandidateUserIds, Collection::stream));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        nextActivityNodes.forEach(node -> node.setCandidateUsers(convertList(node.getCandidateUserIds(), userId -> {\n            AdminUserRespDTO user = userMap.get(userId);\n            if (user != null) {\n                return BpmProcessInstanceConvert.INSTANCE.buildUser(userId, userMap, deptMap);\n            }\n            return null;\n        })));\n        return nextActivityNodes;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,\n                                                                      BpmProcessInstancePageReqVO pageReqVO) {\n        // 1. 构建查询条件\n        HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery()\n                .includeProcessVariables()\n                .processInstanceTenantId(FlowableUtils.getTenantId())\n                .orderByProcessInstanceStartTime().desc();\n        if (userId != null) { // 【我的流程】菜单时，需要传递该字段\n            processInstanceQuery.startedBy(String.valueOf(userId));\n        } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时，才会传递该字段\n            processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId()));\n        }\n        if (StrUtil.isNotEmpty(pageReqVO.getName())) {\n            processInstanceQuery.processInstanceNameLike(\"%\" + pageReqVO.getName() + \"%\");\n        }\n        if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) {\n            processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey());\n        }\n        if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {\n            processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory());\n        }\n        if (pageReqVO.getStatus() != null) {\n            processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,\n                    pageReqVO.getStatus());\n        }\n        if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) {\n            processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0]));\n            processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1]));\n        }\n        if (ArrayUtil.isNotEmpty(pageReqVO.getEndTime())) {\n            processInstanceQuery.finishedAfter(DateUtils.of(pageReqVO.getEndTime()[0]));\n            processInstanceQuery.finishedBefore(DateUtils.of(pageReqVO.getEndTime()[1]));\n        }\n        // 表单字段查询\n        Map<String, Object> formFieldsParams = JsonUtils.parseObject(pageReqVO.getFormFieldsParams(), Map.class);\n        if (CollUtil.isNotEmpty(formFieldsParams)) {\n            formFieldsParams.forEach((key, value) -> {\n                if (StrUtil.isEmpty(String.valueOf(value))) {\n                    return;\n                }\n                // TODO @lesan：应支持多种类型的查询方式，目前只有字符串全等\n                processInstanceQuery.variableValueEquals(key, value);\n            });\n        }\n\n        // 2.1 查询数量\n        long processInstanceCount = processInstanceQuery.count();\n        if (processInstanceCount == 0) {\n            return PageResult.empty(processInstanceCount);\n        }\n        // 2.2 查询列表\n        List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO),\n                pageReqVO.getPageSize());\n        return new PageResult<>(processInstanceList, processInstanceCount);\n    }\n\n    /**\n     * 拼接审批详情的最终数据\n     * <p>\n     * 主要是，拼接审批人的用户信息、部门信息\n     */\n    private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO,\n                                                        BpmnModel bpmnModel,\n                                                        ProcessDefinition processDefinition,\n                                                        BpmProcessDefinitionInfoDO processDefinitionInfo,\n                                                        HistoricProcessInstance processInstance,\n                                                        Integer processInstanceStatus,\n                                                        List<ActivityNode> endApprovalNodeInfos,\n                                                        List<ActivityNode> runningApprovalNodeInfos,\n                                                        List<ActivityNode> simulateApprovalNodeInfos,\n                                                        BpmTaskRespVO todoTask) {\n        // 1. 获取所有需要读取用户信息的 userIds\n        List<ActivityNode> approveNodes = newArrayList(\n                asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos));\n        Set<Long> userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds(processInstance, approveNodes, todoTask);\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n\n        // 2. 表单权限\n        String taskId = reqVO.getTaskId() == null && todoTask != null ? todoTask.getId() : reqVO.getTaskId();\n        Map<String, String> formFieldsPermission = getFormFieldsPermission(bpmnModel, reqVO.getActivityId(), taskId);\n\n        // 3. 拼接数据\n        return BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, processDefinition,\n                processDefinitionInfo, processInstance,\n                processInstanceStatus, approveNodes, todoTask, formFieldsPermission, userMap, deptMap);\n    }\n\n    /**\n     * 获得【已结束】的活动节点们\n     */\n    private List<ActivityNode> getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel,\n                                                      BpmProcessDefinitionInfoDO processDefinitionInfo,\n                                                      HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus,\n                                                      List<HistoricActivityInstance> activities, List<HistoricTaskInstance> tasks) {\n        // 遍历 tasks 列表，只处理已结束的 UserTask\n        // 为什么不通过 activities 呢？因为，加签场景下，它只存在于 tasks，没有 activities，导致如果遍历 activities 的话，它无法成为一个节点\n        List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null);\n        List<ActivityNode> approvalNodes = convertList(endTasks, task -> {\n            FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());\n            ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName())\n                    .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey())\n                            ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()\n                            : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的：解决“办理节点”的识别\n                            BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()))\n                    .setStatus(getEndActivityNodeStatus(task))\n                    .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))\n                    .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime()))\n                    .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task)));\n            // 如果是取消状态，则跳过\n            if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) {\n                return null;\n            }\n            return activityNode;\n        });\n\n        // 遍历 activities，只处理已结束的 StartEvent、EndEvent\n        List<HistoricActivityInstance> endActivities = filterList(activities, activity -> activity.getEndTime() != null\n                && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_EVENT_START, ELEMENT_CALL_ACTIVITY, ELEMENT_EVENT_END)));\n        endActivities.forEach(activity -> {\n            // StartEvent：只处理 BPMN 的场景。因为，SIMPLE 情况下，已经有 START_USER_NODE 节点\n            if (ELEMENT_EVENT_START.equals(activity.getActivityType())\n                    && BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())\n                    && !CollUtil.contains(activities, // 特殊：如果已经存在用户手动创建的 START_USER_NODE_ID 节点，则忽略 StartEvent\n                    historicActivity -> historicActivity.getActivityId().equals(START_USER_NODE_ID))) {\n                ActivityNodeTask startTask = new ActivityNodeTask().setId(BpmnModelConstants.START_USER_NODE_ID)\n                        .setAssignee(startUserId).setStatus(BpmTaskStatusEnum.APPROVE.getStatus());\n                ActivityNode startNode = new ActivityNode().setId(startTask.getId())\n                        .setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName())\n                        .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType())\n                        .setStatus(startTask.getStatus()).setTasks(ListUtil.of(startTask))\n                        .setStartTime(DateUtils.of(activity.getStartTime()))\n                        .setEndTime(DateUtils.of(activity.getEndTime()));\n                approvalNodes.add(0, startNode);\n                return;\n            }\n            // EndEvent\n            if (ELEMENT_EVENT_END.equals(activity.getActivityType())) {\n                if (BpmProcessInstanceStatusEnum.isRejectStatus(processInstanceStatus)) {\n                    // 拒绝情况下，不需要展示 EndEvent 结束节点。原因是：前端已经展示 x 效果，无需重复展示\n                    return;\n                }\n                ActivityNode endNode = new ActivityNode().setId(activity.getId())\n                        .setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName())\n                        .setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType()).setStatus(processInstanceStatus)\n                        .setStartTime(DateUtils.of(activity.getStartTime()))\n                        .setEndTime(DateUtils.of(activity.getEndTime()));\n                String reason = FlowableUtils.getProcessInstanceReason(historicProcessInstance);\n                if (StrUtil.isNotEmpty(reason)) {\n                    endNode.setTasks(singletonList(new ActivityNodeTask().setId(endNode.getId())\n                            .setStatus(endNode.getStatus()).setReason(reason)));\n                }\n                approvalNodes.add(endNode);\n            }\n            // CallActivity\n            if (ELEMENT_CALL_ACTIVITY.equals(activity.getActivityType())) {\n                ActivityNode callActivity = new ActivityNode().setId(activity.getId())\n                        .setName(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getName())\n                        .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus)\n                        .setStartTime(DateUtils.of(activity.getStartTime()))\n                        .setEndTime(DateUtils.of(activity.getEndTime()))\n                        .setProcessInstanceId(activity.getCalledProcessInstanceId());\n                approvalNodes.add(callActivity);\n            }\n        });\n\n        // 按照时间排序\n        approvalNodes.sort(Comparator.comparing(ActivityNode::getStartTime));\n        return approvalNodes;\n    }\n\n    /**\n     * 获取结束节点的状态\n     */\n    private Integer getEndActivityNodeStatus(HistoricTaskInstance task) {\n        Integer status = FlowableUtils.getTaskStatus(task);\n        if (status != null) {\n            return status;\n        }\n        // 结束节点未获取到状态，为跳过状态。可见 bpmn 或者 simple 的 skipExpression\n        return BpmTaskStatusEnum.SKIP.getStatus();\n    }\n\n    /**\n     * 获得【进行中】的活动节点们\n     */\n    private List<ActivityNode> getRunApproveNodeList(Long startUserId,\n                                                     BpmnModel bpmnModel,\n                                                     ProcessDefinition processDefinition,\n                                                     Map<String, Object> processVariables,\n                                                     List<HistoricActivityInstance> activities,\n                                                     List<HistoricTaskInstance> tasks) {\n        // 构建运行中的任务、子流程，基于 activityId 分组\n        List<HistoricActivityInstance> runActivities = filterList(activities, activity -> activity.getEndTime() == null\n                && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY)));\n        Map<String, List<HistoricActivityInstance>> runningTaskMap = convertMultiMap(runActivities,\n                HistoricActivityInstance::getActivityId);\n\n        // 按照 activityId 分组，构建 ApprovalNodeInfo 节点\n        Map<String, HistoricTaskInstance> taskMap = convertMap(tasks, HistoricTaskInstance::getId);\n        return convertList(runningTaskMap.entrySet(), entry -> {\n            String activityId = entry.getKey();\n            List<HistoricActivityInstance> taskActivities = entry.getValue();\n            // 构建活动节点\n            FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);\n            HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务，会签/或签的任务，开始时间相同\n            ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId())\n                    .setName(firstActivity.getActivityName())\n                    .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的：解决“办理节点”和\"子流程\"的识别\n                            BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()))\n                    .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())\n                    .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))\n                    .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime()))\n                    .setTasks(new ArrayList<>());\n            // 处理每个任务的 tasks 属性\n            for (HistoricActivityInstance activity : taskActivities) {\n                HistoricTaskInstance task = taskMap.get(activity.getTaskId());\n                // 特殊情况：子流程节点 ChildProcess 仅存在于 activity 中，并且没有自身的 task，需要跳过执行\n                // TODO @芋艿：后续看看怎么优化！\n                if (task == null) {\n                    continue;\n                }\n                activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task));\n                // 加签子任务，需要过滤掉已经完成的加签子任务\n                List<HistoricTaskInstance> childrenTasks = filterList(\n                        taskService.getAllChildrenTaskListByParentTaskId(activity.getTaskId(), tasks),\n                        childTask -> childTask.getEndTime() == null);\n                if (CollUtil.isNotEmpty(childrenTasks)) {\n                    activityNode.getTasks().addAll(\n                            convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo));\n                }\n            }\n            // 处理每个任务的 candidateUsers 属性：如果是依次审批，需要预测它的后续审批人。因为 Task 是审批完一个，创建一个新的 Task\n            if (BpmnModelUtils.isSequentialUserTask(flowNode)) {\n                List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, flowNode.getId(),\n                        startUserId, processDefinition.getId(), processVariables);\n                // 截取当前审批人位置后面的候选人，不包含当前审批人\n                ActivityNodeTask approvalTaskInfo = CollUtil.getFirst(activityNode.getTasks());\n                Assert.notNull(approvalTaskInfo, \"任务不能为空\");\n                int index = CollUtil.indexOf(candidateUserIds,\n                        userId -> ObjectUtils.equalsAny(userId, approvalTaskInfo.getOwner(),\n                                approvalTaskInfo.getAssignee())); // 委派或者向前加签情况，需要先比较 owner\n                activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size()));\n            }\n            if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) {\n                activityNode.setProcessInstanceId(firstActivity.getCalledProcessInstanceId());\n            }\n            return activityNode;\n        });\n    }\n\n    /**\n     * 获得【预测（未来）】的活动节点们\n     */\n    private List<ActivityNode> getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel,\n                                                          BpmProcessDefinitionInfoDO processDefinitionInfo,\n                                                          Map<String, Object> processVariables,\n                                                          List<HistoricActivityInstance> activities,\n                                                          Set<String> needSimulateTaskDefKeysByReturn) {\n        // TODO @芋艿：【可优化】在驳回场景下，未来的预测准确性不高。原因是，驳回后，HistoricActivityInstance\n        // 包括了历史的操作，不是只有 startEvent 到当前节点的记录\n        Set<String> runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId);\n        // 情况一：BPMN 设计器\n        if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) {\n            List<FlowElement> flowElements = BpmnModelUtils.simulateProcess(bpmnModel, processVariables);\n            return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(\n                    startUserId, bpmnModel, flowElements,\n                    processDefinitionInfo, processVariables, flowElement, runActivityIds, needSimulateTaskDefKeysByReturn));\n        }\n        // 情况二：SIMPLE 设计器\n        if (Objects.equals(BpmModelTypeEnum.SIMPLE.getType(), processDefinitionInfo.getModelType())) {\n            BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(),\n                    BpmSimpleModelNodeVO.class);\n            List<BpmSimpleModelNodeVO> simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables);\n            return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(\n                    startUserId, bpmnModel,\n                    processDefinitionInfo, processVariables, simpleNode, runActivityIds, needSimulateTaskDefKeysByReturn));\n        }\n        throw new IllegalArgumentException(\"未知设计器类型：\" + processDefinitionInfo.getModelType());\n    }\n\n    private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel,\n                                                         BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,\n                                                         BpmSimpleModelNodeVO node, Set<String> runActivityIds,\n                                                         Set<String> needSimulateTaskDefKeysByReturn) {\n        // TODO @芋艿：【可优化】在驳回场景下，未来的预测准确性不高。原因是，驳回后，HistoricActivityInstance\n        // 包括了历史的操作，不是只有 startEvent 到当前节点的记录\n        if (runActivityIds.contains(node.getId())\n                && !needSimulateTaskDefKeysByReturn.contains(node.getId())) { // 特殊：回退操作时候，会记录需要预测的节点到流程变量中。即使在历史操作中，也需要预测\n            return null;\n        }\n        Integer status = BpmTaskStatusEnum.NOT_START.getStatus();\n        // 如果节点被跳过。设置状态为跳过\n        if (SimpleModelUtils.isSkipNode(node, processVariables)) {\n            status = BpmTaskStatusEnum.SKIP.getStatus();\n        }\n        ActivityNode activityNode = new ActivityNode().setId(node.getId()).setName(node.getName())\n                .setNodeType(node.getType()).setCandidateStrategy(node.getCandidateStrategy())\n                .setStatus(status);\n\n        // 1. 开始节点/审批节点\n        if (ObjectUtils.equalsAny(node.getType(),\n                BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(),\n                BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType(),\n                BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE.getType())) {\n            List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(),\n                    startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables);\n            activityNode.setCandidateUserIds(candidateUserIds);\n            return activityNode;\n        }\n\n        // 2. 结束节点\n        if (BpmSimpleModelNodeTypeEnum.END_NODE.getType().equals(node.getType())) {\n            return activityNode;\n        }\n\n        // 3. 抄送节点\n        if (CollUtil.isEmpty(runActivityIds) && // 流程发起时：需要展示抄送节点，用于选择抄送人\n                BpmSimpleModelNodeTypeEnum.COPY_NODE.getType().equals(node.getType())) {\n            List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(),\n                    startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables);\n            activityNode.setCandidateUserIds(candidateUserIds);\n            return activityNode;\n        }\n\n        // 4. 子流程节点\n        if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType())) {\n            return activityNode;\n        }\n        return null;\n    }\n\n    private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, List<FlowElement> flowElements,\n                                                       BpmProcessDefinitionInfoDO processDefinitionInfo,\n                                                       Map<String, Object> processVariables,\n                                                       FlowElement node, Set<String> runActivityIds,\n                                                       Set<String> needSimulateTaskDefKeysByReturn) {\n        // 回退操作时候，会记录需要预测的节点到流程变量中。即使节点在历史操作中，也需要预测。\n        if (!needSimulateTaskDefKeysByReturn.contains(node.getId()) && runActivityIds.contains(node.getId())) {\n            return null;\n        }\n\n        Integer status = BpmTaskStatusEnum.NOT_START.getStatus();\n        // 如果节点被跳过，状态设置为跳过\n        if (BpmnModelUtils.isSkipNode(node, processVariables)) {\n            status = BpmTaskStatusEnum.SKIP.getStatus();\n        }\n        ActivityNode activityNode = new ActivityNode().setId(node.getId())\n                .setStatus(status);\n\n        // 1. 开始节点\n        if (node instanceof StartEvent) {\n            if (CollUtil.contains(flowElements, // 特殊：如果已经存在用户手动创建的 START_USER_NODE_ID 节点，则忽略 StartEvent\n                    flowElement -> flowElement.getId().equals(START_USER_NODE_ID))) {\n                return null;\n            }\n            return activityNode.setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName())\n                    .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType());\n        }\n\n        // 2. 审批节点\n        if (node instanceof UserTask) {\n            List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(),\n                    startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables);\n            return activityNode.setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())\n                    .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node))\n                    .setCandidateUserIds(candidateUserIds);\n        }\n\n        // 3. 结束节点\n        if (node instanceof EndEvent) {\n            return activityNode.setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName())\n                    .setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType());\n        }\n        return null;\n    }\n\n    private List<Long> getTaskCandidateUserList(BpmnModel bpmnModel, String activityId,\n                                                Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {\n        Set<Long> userIds = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,\n                startUserId, processDefinitionId, processVariables);\n        return new ArrayList<>(userIds);\n    }\n\n    @Override\n    public BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id) {\n        // 1.1 获得流程实例\n        HistoricProcessInstance processInstance = getHistoricProcessInstance(id);\n        if (processInstance == null) {\n            return null;\n        }\n        // 1.2 获得流程定义\n        BpmnModel bpmnModel = processDefinitionService\n                .getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId());\n        if (bpmnModel == null) {\n            return null;\n        }\n        BpmSimpleModelNodeVO simpleModel = null;\n        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(\n                processInstance.getProcessDefinitionId());\n        if (processDefinitionInfo != null\n                && BpmModelTypeEnum.SIMPLE.getType().equals(processDefinitionInfo.getModelType())) {\n            simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class);\n        }\n        // 1.3 获得流程实例对应的活动实例列表 + 任务列表\n        List<HistoricActivityInstance> activities = taskService.getActivityListByProcessInstanceId(id);\n        List<HistoricTaskInstance> tasks = taskService.getTaskListByProcessInstanceId(id, true);\n\n        // 2.1 拼接进度信息\n        Set<String> unfinishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,\n                activityInstance -> activityInstance.getEndTime() == null);\n        Set<String> finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,\n                activityInstance -> activityInstance.getEndTime() != null\n                        && ObjectUtil.notEqual(activityInstance.getActivityType(),\n                        BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));\n        Set<String> finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,\n                activityInstance -> activityInstance.getEndTime() != null\n                        && ObjectUtil.equals(activityInstance.getActivityType(),\n                        BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));\n        // 特殊：会签情况下，会有部分已完成（审批）、部分未完成（待审批），此时需要 finishedTaskActivityIds 移除掉\n        finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds);\n        // 特殊：如果流程实例被拒绝，则需要计算是哪个活动节点。\n        // 注意，只取最后一个。因为会存在多次拒绝的情况，拒绝驳回到指定节点\n        Set<String> rejectTaskActivityIds = CollUtil.newHashSet();\n        if (BpmProcessInstanceStatusEnum.isRejectStatus(FlowableUtils.getProcessInstanceStatus(processInstance))) {\n            tasks.stream()\n                    .filter(task -> BpmTaskStatusEnum.isRejectStatus(FlowableUtils.getTaskStatus(task)))\n                    .max(Comparator.comparing(HistoricTaskInstance::getEndTime))\n                    .ifPresent(reject -> rejectTaskActivityIds.add(reject.getTaskDefinitionKey()));\n            finishedTaskActivityIds.removeAll(rejectTaskActivityIds);\n        }\n\n        // 2.2 拼接基础信息\n        Set<Long> userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds02(processInstance, tasks);\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        return BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceBpmnModelView(processInstance, tasks, bpmnModel,\n                simpleModel,\n                unfinishedTaskActivityIds, finishedTaskActivityIds, finishedSequenceFlowActivityIds,\n                rejectTaskActivityIds,\n                userMap, deptMap);\n    }\n\n    // ========== Update 写入相关方法 ==========\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {\n        // 获得流程定义\n        ProcessDefinition definition = processDefinitionService\n                .getProcessDefinition(createReqVO.getProcessDefinitionId());\n        // 发起流程\n        return createProcessInstance0(userId, definition, createReqVO.getVariables(), null,\n                createReqVO.getStartUserSelectAssignees());\n    }\n\n    @Override\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {\n        return FlowableUtils.executeAuthenticatedUserId(userId, () -> {\n            // 获得流程定义\n            ProcessDefinition definition = processDefinitionService\n                    .getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());\n            // 发起流程\n            return createProcessInstance0(userId, definition, createReqDTO.getVariables(),\n                    createReqDTO.getBusinessKey(),\n                    createReqDTO.getStartUserSelectAssignees());\n        });\n    }\n\n    private String createProcessInstance0(Long userId, ProcessDefinition definition,\n                                          Map<String, Object> variables, String businessKey,\n                                          Map<String, List<Long>> startUserSelectAssignees) {\n        // 1.1 校验流程定义\n        if (definition == null) {\n            throw exception(PROCESS_DEFINITION_NOT_EXISTS);\n        }\n        if (definition.isSuspended()) {\n            throw exception(PROCESS_DEFINITION_IS_SUSPENDED);\n        }\n        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService\n                .getProcessDefinitionInfo(definition.getId());\n        if (processDefinitionInfo == null) {\n            throw exception(PROCESS_DEFINITION_NOT_EXISTS);\n        }\n        // 1.2 校验是否能够发起\n        if (!processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId)) {\n            throw exception(PROCESS_INSTANCE_START_USER_CAN_START);\n        }\n        // 1.3 校验发起人自选审批人\n        validateStartUserSelectAssignees(userId, definition, startUserSelectAssignees, variables);\n\n        // 2. 创建流程实例\n        if (variables == null) {\n            variables = new HashMap<>();\n        }\n        FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下，避免 ProcessInstance 系统级的变量被占用\n        variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量，发起人 ID\n        variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态：审批中\n                BpmProcessInstanceStatusEnum.RUNNING.getStatus());\n        variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true，不影响没配置 skipExpression 的节点\n        if (CollUtil.isNotEmpty(startUserSelectAssignees)) {\n            // 设置流程变量，发起人自选审批人\n            variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,\n                    startUserSelectAssignees);\n        }\n\n        // 3. 创建流程\n        ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder()\n                .processDefinitionId(definition.getId())\n                .businessKey(businessKey)\n                .variables(variables);\n        // 3.1 创建流程 ID\n        BpmModelMetaInfoVO.ProcessIdRule processIdRule = processDefinitionInfo.getProcessIdRule();\n        if (processIdRule != null && Boolean.TRUE.equals(processIdRule.getEnable())) {\n            processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule));\n        }\n        // 3.2 流程名称\n        processInstanceBuilder.name(generateProcessInstanceName(userId, definition, processDefinitionInfo, variables));\n        // 3.3 发起流程实例\n        ProcessInstance instance = processInstanceBuilder.start();\n        return instance.getId();\n    }\n\n    private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition,\n                                                  Map<String, List<Long>> startUserSelectAssignees,\n                                                  Map<String, Object> variables) {\n        // 1. 获取预测的节点信息\n        BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO()\n                .setProcessDefinitionId(definition.getId())\n                .setProcessVariables(variables));\n        List<ActivityNode> activityNodes = detailRespVO.getActivityNodes();\n        if (CollUtil.isEmpty(activityNodes)) {\n            return;\n        }\n\n        // 2.1 移除掉不是发起人自选审批人节点\n        activityNodes.removeIf(task ->\n                ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()));\n        // 2.2 流程发起时要先获取当前流程的预测走向节点，发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了\n        activityNodes.forEach(task -> {\n            List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null;\n            if (CollUtil.isEmpty(assignees)) {\n                throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName());\n            }\n            Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees);\n            assignees.forEach(assignee -> {\n                if (userMap.get(assignee) == null) {\n                    throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, task.getName(), assignee);\n                }\n            });\n        });\n    }\n\n    private String generateProcessInstanceName(Long userId,\n                                               ProcessDefinition definition,\n                                               BpmProcessDefinitionInfoDO definitionInfo,\n                                               Map<String, Object> variables) {\n        if (definition == null || definitionInfo == null) {\n            return null;\n        }\n        BpmModelMetaInfoVO.TitleSetting titleSetting = definitionInfo.getTitleSetting();\n        if (titleSetting == null || !BooleanUtil.isTrue(titleSetting.getEnable())) {\n            return definition.getName();\n        }\n        AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData();\n        Map<String, Object> cloneVariables = new HashMap<>(variables);\n        cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname());\n        cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now());\n        cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim());\n        return StrUtil.format(definitionInfo.getTitleSetting().getTitle(), cloneVariables);\n    }\n\n    @Override\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {\n        // 1.1 校验流程实例存在\n        ProcessInstance instance = getProcessInstance(cancelReqVO.getId());\n        if (instance == null) {\n            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);\n        }\n        // 1.2 只能取消自己的\n        if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {\n            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);\n        }\n        // 1.3 校验允许撤销审批中的申请\n        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService\n                .getProcessDefinitionInfo(instance.getProcessDefinitionId());\n        Assert.notNull(processDefinitionInfo, \"流程定义({})不存在\", processDefinitionInfo);\n        if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消\n                && BooleanUtil.isFalse(processDefinitionInfo.getAllowCancelRunningProcess())) {\n            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW);\n        }\n        // 1.4 子流程不允许取消\n        if (StrUtil.isNotBlank(instance.getSuperExecutionId())) {\n            throw exception(PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW);\n        }\n\n        // 2. 取消流程\n        updateProcessInstanceCancel(cancelReqVO.getId(),\n                BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason()));\n    }\n\n    @Override\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) {\n        // 1.1 校验流程实例存在\n        ProcessInstance instance = getProcessInstance(cancelReqVO.getId());\n        if (instance == null) {\n            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);\n        }\n\n        // 2. 取消流程\n        AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData();\n        updateProcessInstanceCancel(cancelReqVO.getId(),\n                BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason()));\n    }\n\n    private void updateProcessInstanceCancel(String id, String reason) {\n        // 1. 更新流程实例 status\n        runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,\n                BpmProcessInstanceStatusEnum.CANCEL.getStatus());\n        runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, reason);\n\n        // 2. 取消所有子流程\n        List<ProcessInstance> childProcessInstances = runtimeService.createProcessInstanceQuery()\n                .superProcessInstanceId(id).list();\n        childProcessInstances.forEach(processInstance -> updateProcessInstanceCancel(\n                processInstance.getProcessInstanceId(), BpmReasonEnum.CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS.getReason()));\n\n        // 3. 结束流程\n        taskService.moveTaskToEnd(id, reason);\n    }\n\n    @Override\n    public void updateProcessInstanceReject(ProcessInstance processInstance, String reason) {\n        runtimeService.setVariable(processInstance.getProcessInstanceId(),\n                BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,\n                BpmProcessInstanceStatusEnum.REJECT.getStatus());\n        runtimeService.setVariable(processInstance.getProcessInstanceId(),\n                BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON,\n                BpmReasonEnum.REJECT_TASK.format(reason));\n    }\n\n    @Override\n    public void updateProcessInstanceVariables(String id, Map<String, Object> variables) {\n        runtimeService.setVariables(id, variables);\n    }\n\n    @Override\n    public void removeProcessInstanceVariables(String id, Collection<String> variableNames) {\n        runtimeService.removeVariables(id, variableNames);\n    }\n\n    // ========== Event 事件相关方法 ==========\n\n    @Override\n    public void processProcessInstanceCompleted(ProcessInstance instance) {\n        // 1.1 获取当前状态\n        Integer status = (Integer) instance.getProcessVariables()\n                .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);\n        String reason = (String) instance.getProcessVariables()\n                .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);\n        // 1.2 当流程状态还是审批状态中，说明审批通过了，则变更下它的状态\n        // 为什么这么处理？因为流程完成，并且完成了，说明审批通过了\n        if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) {\n            status = BpmProcessInstanceStatusEnum.APPROVE.getStatus();\n            runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,\n                    status);\n        }\n\n        // 1.3 如果子流程拒绝，设置其父流程也为拒绝状态，且结束父流程\n        // 相关问题链接：https://t.zsxq.com/kZhyb\n        if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())\n                && StrUtil.isNotBlank(instance.getSuperExecutionId())) {\n            // 1.3.1 获取父流程实例 并标记为不通过\n            Execution execution = runtimeService.createExecutionQuery().executionId(instance.getSuperExecutionId()).singleResult();\n            ProcessInstance parentProcessInstance = getProcessInstance(execution.getProcessInstanceId());\n            updateProcessInstanceReject(parentProcessInstance, BpmReasonEnum.REJECT_CHILD_PROCESS.getReason());\n\n            // 1.3.2 结束父流程。需要在子流程结束事务提交后执行\n            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {\n\n                @Override\n                public void afterCompletion(int transactionStatus) {\n                    // 回滚情况，直接返回\n                    if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) {\n                        return;\n                    }\n                    taskService.moveTaskToEnd(parentProcessInstance.getId(), BpmReasonEnum.REJECT_CHILD_PROCESS.getReason());\n                }\n            });\n        }\n\n        // 2. 发送对应的消息通知\n        if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {\n            messageService.sendMessageWhenProcessInstanceApprove(\n                    BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));\n        } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) {\n            messageService.sendMessageWhenProcessInstanceReject(\n                    BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason));\n        }\n\n        // 3. 发送流程实例的状态事件\n        processInstanceEventPublisher.sendProcessInstanceResultEvent(\n                BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status, reason));\n\n        // 4. 流程后置通知\n        if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {\n            BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.\n                    getProcessDefinitionInfo(instance.getProcessDefinitionId());\n            if (ObjUtil.isNotNull(processDefinitionInfo) &&\n                    ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) {\n                BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting();\n\n                BpmHttpRequestUtils.executeBpmHttpRequest(instance,\n                        setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());\n            }\n        }\n    }\n\n    @Override\n    public void processProcessInstanceCreated(ProcessInstance instance) {\n        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.\n                getProcessDefinitionInfo(instance.getProcessDefinitionId());\n        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(instance.getProcessDefinitionId());\n        if (processDefinition == null || processDefinitionInfo == null) {\n            return;\n        }\n\n        // 自定义标题。目的：主要处理子流程的标题无法处理\n        // 注意：必须使用 TransactionSynchronizationManager 事务提交后，否则不生效！！！\n        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {\n\n            @Override\n            public void afterCommit() {\n                String name = generateProcessInstanceName(Long.valueOf(instance.getStartUserId()),\n                        processDefinition, processDefinitionInfo, instance.getProcessVariables());\n                if (ObjUtil.notEqual(instance.getName(), name)) {\n                    runtimeService.setProcessInstanceName(instance.getProcessInstanceId(), name);\n                }\n\n                // 流程前置通知：需要在流程启动后(事务提交后)，保证 variables 已设置\n                // 相关问题链接：https://t.zsxq.com/DF7Kq\n                if (ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {\n                    return;\n                }\n                BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting();\n                BpmHttpRequestUtils.executeBpmHttpRequest(instance,\n                        setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());\n            }\n\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum;\nimport org.flowable.bpmn.model.UserTask;\nimport org.flowable.engine.history.HistoricActivityInstance;\nimport org.flowable.task.api.Task;\nimport org.flowable.task.api.TaskInfo;\nimport org.flowable.task.api.history.HistoricTaskInstance;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 流程任务实例 Service 接口\n *\n * @author jason\n * @author 芋道源码\n */\npublic interface BpmTaskService {\n\n    // ========== Query 查询相关方法 ==========\n\n    /**\n     * 获得待办的流程任务分页\n     *\n     * @param userId    用户编号\n     * @param pageReqVO 分页请求\n     * @return 流程任务分页\n     */\n    PageResult<Task> getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO);\n\n    /**\n     * 获得用户（待办）的任务：\n     * 1. 根据 taskId 查询待办任务\n     * 2. 如果任务不存在（或者已审核），获取指定流程下，首个需要处理任务\n     *\n     * @param userId 用户编号\n     * @param taskId 任务编号\n     * @param processInstanceId 流程实例编号\n     * @return 待办任务\n     */\n    BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId);\n\n    /**\n     * 获得已办的流程任务分页\n     *\n     * @param userId    用户编号\n     * @param pageReqVO 分页请求\n     * @return 流程任务分页\n     */\n    PageResult<HistoricTaskInstance> getTaskDonePage(Long userId, BpmTaskPageReqVO pageReqVO);\n\n    /**\n     * 获得全部的流程任务分页\n     *\n     * @param userId    用户编号\n     * @param pageReqVO 分页请求\n     * @return 流程任务分页\n     */\n    PageResult<HistoricTaskInstance> getTaskPage(Long userId, BpmTaskPageReqVO pageReqVO);\n\n    /**\n     * 获得流程任务 Map\n     *\n     * @param processInstanceIds 流程实例的编号数组\n     * @return 流程任务 Map\n     */\n    default Map<String, List<Task>> getTaskMapByProcessInstanceIds(List<String> processInstanceIds) {\n        return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds),\n                Task::getProcessInstanceId);\n    }\n\n    /**\n     * 获得流程任务列表\n     *\n     * @param processInstanceIds 流程实例的编号数组\n     * @return 流程任务列表\n     */\n    List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds);\n\n    /**\n     * 获得指定流程实例的流程任务列表，包括所有状态的\n     *\n     * @param processInstanceId 流程实例的编号\n     * @param asc               是否升序\n     * @return 流程任务列表\n     */\n    List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc);\n\n    /**\n     * 校验任务是否存在，并且是否是分配给自己的任务\n     *\n     * @param userId 用户 id\n     * @param taskId task id\n     */\n    Task validateTask(Long userId, String taskId);\n\n    /**\n     * 获取任务\n     *\n     * @param id 任务编号\n     * @return 任务\n     */\n    Task getTask(String id);\n\n    /**\n     * 获取历史任务\n     *\n     * @param id 任务编号\n     * @return 历史任务\n     */\n    HistoricTaskInstance getHistoricTask(String id);\n\n    /**\n     * 获取历史任务列表\n     *\n     * @param taskIds 任务编号集合\n     * @return 历史任务列表\n     */\n    List<HistoricTaskInstance> getHistoricTasks(Collection<String> taskIds);\n\n    /**\n     * 根据条件查询正在进行中的任务\n     *\n     * @param processInstanceId 流程实例编号，不允许为空\n     * @param assigned          是否分配了审批人，允许空\n     * @param taskDefineKey     任务定义 Key，允许空\n     */\n    List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId,\n                                                     Boolean assigned,\n                                                     String taskDefineKey);\n\n    /**\n     * 获取当前任务的可退回的 UserTask 集合\n     *\n     * @param id 当前的任务 ID\n     * @return 可以退回的节点列表\n     */\n    List<UserTask> getUserTaskListByReturn(String id);\n\n    /**\n     * 获取指定任务的子任务列表（多层）\n     *\n     * @param parentTaskId 父任务 ID\n     * @param tasks 任务列表\n     * @return 子任务列表\n     */\n    <T extends TaskInfo> List<T> getAllChildrenTaskListByParentTaskId(String parentTaskId, List<T> tasks);\n\n    /**\n     * 获取指定任务的子任务列表\n     *\n     * @param parentTaskId 父任务ID\n     * @return 子任务列表\n     */\n    List<Task> getTaskListByParentTaskId(String parentTaskId);\n\n    /**\n     * 获得指定流程实例的活动实例列表\n     *\n     * @param processInstanceId 流程实例的编号\n     * @return 活动实例列表\n     */\n    List<HistoricActivityInstance> getActivityListByProcessInstanceId(String processInstanceId);\n\n    /**\n     * 获得执行编号对应的活动实例\n     *\n     * @param executionId 执行编号\n     * @return 活动实例\n     */\n    List<HistoricActivityInstance> getHistoricActivityListByExecutionId(String executionId);\n\n    /**\n     * 获得指定流程实例的已完成的流程任务列表，不包含取消状态\n     *\n     * @param processInstanceId 流程实例的编号\n     * @return 流程任务列表\n     */\n    List<HistoricTaskInstance> getFinishedTaskListByProcessInstanceIdWithoutCancel(String processInstanceId);\n\n    // ========== Update 写入相关方法 ==========\n\n    /**\n     * 通过任务\n     *\n     * @param userId 用户编号\n     * @param reqVO  通过请求\n     */\n    void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO);\n\n    /**\n     * 不通过任务\n     *\n     * @param userId 用户编号\n     * @param reqVO  不通过请求\n     */\n    void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO);\n\n    /**\n     * 将流程任务分配给指定用户\n     *\n     * @param userId 用户编号\n     * @param reqVO  分配请求\n     */\n    void transferTask(Long userId, BpmTaskTransferReqVO reqVO);\n\n    /**\n     * 将指定流程实例的、进行中的流程任务，移动到结束节点\n     *\n     * @param processInstanceId 流程编号\n     * @param reason 原因\n     */\n    void moveTaskToEnd(String processInstanceId, String reason);\n\n    /**\n     * 将任务退回到指定的 targetDefinitionKey 位置\n     *\n     * @param userId 用户编号\n     * @param reqVO  退回的任务key和当前所在的任务ID\n     */\n    void returnTask(Long userId, BpmTaskReturnReqVO reqVO);\n\n    /**\n     * 将指定任务委派给其他人处理，等接收人处理后再回到原审批人手中审批\n     *\n     * @param userId 用户编号\n     * @param reqVO  被委派人和被委派的任务编号理由参数\n     */\n    void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO);\n\n    /**\n     * 任务加签\n     *\n     * @param userId 被加签的用户和任务 ID，加签类型\n     * @param reqVO  当前用户 ID\n     */\n    void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO);\n\n    /**\n     * 任务减签\n     *\n     * @param userId 当前用户ID\n     * @param reqVO  被减签的任务 ID，理由\n     */\n    void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO);\n\n    /**\n     * 抄送任务\n     *\n     * @param userId 用户编号\n     * @param reqVO  通过请求\n     */\n    void copyTask(Long userId, @Valid BpmTaskCopyReqVO reqVO);\n\n    /**\n     * 撤回任务\n     *\n     * @param userId 用户编号\n     * @param taskId 任务编号\n     */\n    void withdrawTask(Long userId, String taskId);\n\n    // ========== Event 事件相关方法 ==========\n\n    /**\n     * 处理 Task 创建事件，目前是\n     * <p>\n     * 1. 更新它的状态为审批中\n     * 2. 处理自动通过的情况，例如说：1）无审批人时，是否自动通过、不通过；2）非【人工审核】时，是否自动通过、不通过\n     * <p>\n     * 注意：它的触发时机，晚于 {@link #processTaskAssigned(Task)} 之后\n     *\n     * @param task 任务实体\n     */\n    void processTaskCreated(Task task);\n\n    /**\n     * 处理 Task 取消事件，目前是更新它的状态为已取消\n     *\n     * @param taskId 任务的编号\n     */\n    void processTaskCanceled(String taskId);\n\n    /**\n     * 处理 Task 设置审批人事件，目前是发送审批消息\n     *\n     * @param task 任务实体\n     */\n    void processTaskAssigned(Task task);\n\n    /**\n     * 处理 Task 完成事件，目前是发送任务后置通知\n     *\n     * @param task 任务实体\n     */\n    void processTaskCompleted(Task task);\n\n    /**\n     * 处理 Task 审批超时事件，可能会处理多个当前审批中的任务\n     *\n     * @param processInstanceId 流程示例编号\n     * @param taskDefineKey     任务 Key\n     * @param handlerType       处理类型，参见 {@link BpmUserTaskTimeoutHandlerTypeEnum}\n     */\n    void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType);\n\n    /**\n     * 处理 ChildProcess 子流程的审批超时事件\n     *\n     * @param processInstanceId 流程示例编号\n     * @param taskDefineKey     任务 Key\n     */\n    void processChildProcessTimeout(String processInstanceId, String taskDefineKey);\n\n    /**\n     * 触发流程任务 (ReceiveTask) 的执行\n     * <p>\n     * 1. Simple 模型 HTTP 回调请求触发器节点的回调，触发流程继续执行\n     * 2. Simple 模型延迟器节点，到时触发流程继续执行\n     *\n     * @param processInstanceId 流程示例编号\n     * @param taskDefineKey     任务 Key\n     */\n    void triggerTask(String processInstanceId, String taskDefineKey);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.*;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.date.DateUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.common.util.object.PageUtils;\nimport cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;\nimport cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.*;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;\nimport cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.bpmn.model.*;\nimport org.flowable.engine.HistoryService;\nimport org.flowable.engine.ManagementService;\nimport org.flowable.engine.RuntimeService;\nimport org.flowable.engine.TaskService;\nimport org.flowable.engine.history.HistoricActivityInstance;\nimport org.flowable.engine.runtime.ActivityInstance;\nimport org.flowable.engine.runtime.Execution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.task.api.DelegationState;\nimport org.flowable.task.api.Task;\nimport org.flowable.task.api.TaskInfo;\nimport org.flowable.task.api.TaskQuery;\nimport org.flowable.task.api.history.HistoricTaskInstance;\nimport org.flowable.task.api.history.HistoricTaskInstanceQuery;\nimport org.flowable.task.service.impl.persistence.entity.TaskEntity;\nimport org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.transaction.support.TransactionSynchronization;\nimport org.springframework.transaction.support.TransactionSynchronizationManager;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.*;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;\n\n/**\n * 流程任务实例 Service 实现类\n *\n * @author 芋道源码\n * @author jason\n */\n@Slf4j\n@Service\npublic class BpmTaskServiceImpl implements BpmTaskService {\n\n    @Resource\n    private TaskService taskService;\n    @Resource\n    private HistoryService historyService;\n    @Resource\n    private RuntimeService runtimeService;\n    @Resource\n    private ManagementService managementService;\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n    @Resource\n    private BpmProcessDefinitionService bpmProcessDefinitionService;\n    @Resource\n    private BpmProcessInstanceCopyService processInstanceCopyService;\n    @Resource\n    private BpmModelService modelService;\n    @Resource\n    private BpmMessageService messageService;\n    @Resource\n    private BpmFormService formService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    // ========== Query 查询相关方法 ==========\n\n    @Override\n    public PageResult<Task> getTaskTodoPage(Long userId, BpmTaskPageReqVO pageVO) {\n        TaskQuery taskQuery = taskService.createTaskQuery()\n                .taskAssignee(String.valueOf(userId)) // 分配给自己\n                .active()\n                .includeProcessVariables()\n                .taskTenantId(FlowableUtils.getTenantId())\n                .orderByTaskCreateTime().desc(); // 创建时间倒序\n        if (StrUtil.isNotBlank(pageVO.getName())) {\n            taskQuery.taskNameLike(\"%\" + pageVO.getName() + \"%\");\n        }\n        if (StrUtil.isNotEmpty(pageVO.getCategory())) {\n            taskQuery.taskCategory(pageVO.getCategory());\n        }\n        if (StrUtil.isNotEmpty(pageVO.getProcessDefinitionKey())) {\n            taskQuery.processDefinitionKey(pageVO.getProcessDefinitionKey());\n        }\n        if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {\n            taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));\n            taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));\n        }\n        long count = taskQuery.count();\n        if (count == 0) {\n            return PageResult.empty();\n        }\n        List<Task> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());\n        return new PageResult<>(tasks, count);\n    }\n\n    @Override\n    public BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId) {\n        // 1.1 获取指定的用户待办任务\n        Task todoTask = getMyTodoTask(userId, taskId);\n        // 1.2 获取不到，则获取该流程实例下，第一个用户的待办任务\n        if (todoTask == null) {\n            todoTask = getMyFirstTodoTask(userId, processInstanceId);\n        }\n        if (todoTask == null) {\n            return null;\n        }\n\n        // 2. 查询该任务的子任务\n        List<Task> childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), CollUtil.newArrayList(todoTask));\n\n        // 3. 转换返回\n        BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(todoTask.getProcessDefinitionId());\n        Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting = BpmnModelUtils.parseButtonsSetting(\n                bpmnModel, todoTask.getTaskDefinitionKey());\n        Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey());\n        Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey());\n        Integer nodeType = parseNodeType(BpmnModelUtils.getFlowElementById(bpmnModel, todoTask.getTaskDefinitionKey()));\n\n        // 4. 任务表单\n        BpmFormDO taskForm = null;\n        if (StrUtil.isNotBlank(todoTask.getFormKey())) {\n            taskForm = formService.getForm(NumberUtils.parseLong(todoTask.getFormKey()));\n        }\n\n        return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm)\n                .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire);\n    }\n\n    /**\n     * 获得用户指定 taskId 任务编号的“待办”（未审批、且可审核）的任务\n     *\n     * @param userId 用户编号\n     * @param taskId 任务编号\n     * @return 任务\n     */\n    private Task getMyTodoTask(Long userId, String taskId) {\n        if (StrUtil.isEmpty(taskId)) {\n            return null;\n        }\n        Task task = getTask(taskId);\n        if (task == null) {\n            return null;\n        }\n        if (!isAssignUserTask(userId, task) && !isAddSignUserTask(userId, task)) {\n            return null;\n        }\n        return task;\n    }\n\n    /**\n     * 获得用户指定 processInstanceId 流程编号下的首个“待办”（未审批、且可审核）的任务\n     *\n     * @param userId            用户编号\n     * @param processInstanceId 流程编号\n     * @return 任务\n     */\n    private Task getMyFirstTodoTask(Long userId, String processInstanceId) {\n        if (processInstanceId == null) {\n            return null;\n        }\n        // 1. 查询所有任务\n        List<Task> tasks = taskService.createTaskQuery()\n                .active()\n                .processInstanceId(processInstanceId)\n                .includeTaskLocalVariables()\n                .includeProcessVariables()\n                .orderByTaskCreateTime().asc() // 按创建时间升序\n                .list();\n\n        // 2. 查询我的首个任务\n        return CollUtil.findOne(tasks, task -> {\n            return isAssignUserTask(userId, task) // 当前用户为审批人\n                    || isAddSignUserTask(userId, task); // 当前用户为加签人（为了减签）\n        });\n    }\n\n    @Override\n    public PageResult<HistoricTaskInstance> getTaskDonePage(Long userId, BpmTaskPageReqVO pageVO) {\n        HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery()\n                .finished() // 已完成\n                .taskAssignee(String.valueOf(userId)) // 分配给自己\n                .includeTaskLocalVariables()\n                .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序\n        if (StrUtil.isNotBlank(pageVO.getName())) {\n            taskQuery.taskNameLike(\"%\" + pageVO.getName() + \"%\");\n        }\n        if (pageVO.getStatus() != null) {\n            taskQuery.taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, pageVO.getStatus());\n        }\n//        if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {\n//            taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));\n//            taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));\n//        }\n        // 执行查询\n        long count = taskQuery.count();\n        if (count == 0) {\n            return PageResult.empty();\n        }\n        List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());\n\n        // 特殊：强制移除自动完成的“发起人”节点\n        // 补充说明：由于 taskQuery 无法方面的过滤，所以暂时通过内存过滤\n        tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID));\n        // TODO @芋艿：https://t.zsxq.com/MNzqp 【flowable bug】：taskCreatedAfter、taskCreatedBefore 拼接的是 OR\n        if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {\n            tasks.removeIf(task -> task.getCreateTime() == null\n                    || task.getCreateTime().before(DateUtils.of(pageVO.getCreateTime()[0]))\n                    || task.getCreateTime().after(DateUtils.of(pageVO.getCreateTime()[1])));\n        }\n        return new PageResult<>(tasks, count);\n    }\n\n    @Override\n    public PageResult<HistoricTaskInstance> getTaskPage(Long userId, BpmTaskPageReqVO pageVO) {\n        HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery()\n                .includeTaskLocalVariables()\n                .taskTenantId(FlowableUtils.getTenantId())\n                .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序\n        if (StrUtil.isNotBlank(pageVO.getName())) {\n            taskQuery.taskNameLike(\"%\" + pageVO.getName() + \"%\");\n        }\n        if (StrUtil.isNotEmpty(pageVO.getCategory())) {\n            taskQuery.taskCategory(pageVO.getCategory());\n        }\n//        if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {\n//            taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));\n//            taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));\n//        }\n        // 执行查询\n        long count = taskQuery.count();\n        if (count == 0) {\n            return PageResult.empty();\n        }\n        List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());\n        // TODO @芋艿：https://t.zsxq.com/MNzqp 【flowable bug】：taskCreatedAfter、taskCreatedBefore 拼接的是 OR\n        if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {\n            tasks.removeIf(task -> task.getCreateTime() == null\n                    || task.getCreateTime().before(DateUtils.of(pageVO.getCreateTime()[0]))\n                    || task.getCreateTime().after(DateUtils.of(pageVO.getCreateTime()[1])));\n        }\n        return new PageResult<>(tasks, count);\n    }\n\n    @Override\n    public List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds) {\n        if (CollUtil.isEmpty(processInstanceIds)) {\n            return Collections.emptyList();\n        }\n        return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list();\n    }\n\n    @Override\n    public List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc) {\n        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()\n                .includeTaskLocalVariables()\n                .processInstanceId(processInstanceId);\n        if (Boolean.TRUE.equals(asc)) {\n            query.orderByHistoricTaskInstanceStartTime().asc();\n        } else {\n            query.orderByHistoricTaskInstanceStartTime().desc();\n        }\n        return query.list();\n    }\n\n    @Override\n    public Task validateTask(Long userId, String taskId) {\n        Task task = validateTaskExist(taskId);\n        // 为什么判断 assignee 非空的情况下？\n        // 例如说：在审批人为空时，我们会有“自动审批通过”的策略，此时 userId 为 null，允许通过\n        if (StrUtil.isNotBlank(task.getAssignee())\n                && ObjectUtil.notEqual(userId, NumberUtils.parseLong(task.getAssignee()))) {\n            throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF);\n        }\n        return task;\n    }\n\n    private Task validateTaskExist(String id) {\n        Task task = getTask(id);\n        if (task == null) {\n            throw exception(TASK_NOT_EXISTS);\n        }\n        return task;\n    }\n\n    @Override\n    public Task getTask(String id) {\n        return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult();\n    }\n\n    @Override\n    public HistoricTaskInstance getHistoricTask(String id) {\n        return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult();\n    }\n\n    @Override\n    public List<HistoricTaskInstance> getHistoricTasks(Collection<String> taskIds) {\n        return historyService.createHistoricTaskInstanceQuery().taskIds(taskIds).includeTaskLocalVariables().list();\n    }\n\n    @Override\n    public List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String defineKey) {\n        Assert.notNull(processInstanceId, \"processInstanceId 不能为空\");\n        TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active()\n                .includeTaskLocalVariables();\n        if (BooleanUtil.isTrue(assigned)) {\n            taskQuery.taskAssigned();\n        } else if (BooleanUtil.isFalse(assigned)) {\n            taskQuery.taskUnassigned();\n        }\n        if (StrUtil.isNotEmpty(defineKey)) {\n            taskQuery.taskDefinitionKey(defineKey);\n        }\n        return taskQuery.list();\n    }\n\n    @Override\n    public List<UserTask> getUserTaskListByReturn(String id) {\n        // 1.1 校验当前任务 task 存在\n        Task task = validateTaskExist(id);\n        // 1.2 根据流程定义获取流程模型信息\n        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());\n        FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());\n        if (source == null) {\n            throw exception(TASK_NOT_EXISTS);\n        }\n\n        // 2.1 查询该任务的前置任务节点的 key 集合\n        List<UserTask> previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null);\n        if (CollUtil.isEmpty(previousUserList)) {\n            return Collections.emptyList();\n        }\n        // 2.2 过滤：只有串行可到达的节点，才可以退回。类似非串行、子流程无法退回\n        previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));\n\n        // 2.3 过滤：只能退回到已经处理过的节点（排除审批未经过的节点）。相关 issue：https://github.com/YunaiV/ruoyi-vue-pro/issues/982\n        List<HistoricTaskInstance> finishedTasks = getFinishedTaskListByProcessInstanceIdWithoutCancel(task.getProcessInstanceId());\n        Set<String> finishedTaskDefinitionKeys = convertSet(finishedTasks, HistoricTaskInstance::getTaskDefinitionKey);\n        previousUserList.removeIf(userTask -> !finishedTaskDefinitionKeys.contains(userTask.getId()));\n        return previousUserList;\n    }\n\n    @Override\n    public <T extends TaskInfo> List<T> getAllChildrenTaskListByParentTaskId(String parentTaskId, List<T> tasks) {\n        if (CollUtil.isEmpty(tasks)) {\n            return Collections.emptyList();\n        }\n        Map<String, List<T>> parentTaskMap = convertMultiMap(\n                filterList(tasks, task -> StrUtil.isNotEmpty(task.getParentTaskId())), TaskInfo::getParentTaskId);\n        if (CollUtil.isEmpty(parentTaskMap)) {\n            return Collections.emptyList();\n        }\n\n        List<T> result = new ArrayList<>();\n        // 1. 递归获取子级\n        Stack<String> stack = new Stack<>();\n        stack.push(parentTaskId);\n        // 2. 递归遍历\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            if (stack.isEmpty()) {\n                break;\n            }\n            // 2.1 获取子任务们\n            String taskId = stack.pop();\n            List<T> childTaskList = filterList(tasks, task -> StrUtil.equals(task.getParentTaskId(), taskId));\n            // 2.2 如果非空，则添加到 stack 进一步递归\n            if (CollUtil.isNotEmpty(childTaskList)) {\n                stack.addAll(convertList(childTaskList, TaskInfo::getId));\n                result.addAll(childTaskList);\n            }\n        }\n        return result;\n    }\n\n    /**\n     * 获得所有子任务列表\n     *\n     * @param parentTask 父任务\n     * @return 所有子任务列表\n     */\n    private List<Task> getAllChildTaskList(Task parentTask) {\n        List<Task> result = new ArrayList<>();\n        // 1. 递归获取子级\n        Stack<Task> stack = new Stack<>();\n        stack.push(parentTask);\n        // 2. 递归遍历\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            if (stack.isEmpty()) {\n                break;\n            }\n            // 2.1 获取子任务们\n            Task task = stack.pop();\n            List<Task> childTaskList = getTaskListByParentTaskId(task.getId());\n            // 2.2 如果非空，则添加到 stack 进一步递归\n            if (CollUtil.isNotEmpty(childTaskList)) {\n                stack.addAll(childTaskList);\n                result.addAll(childTaskList);\n            }\n        }\n        return result;\n    }\n\n    @Override\n    public List<Task> getTaskListByParentTaskId(String parentTaskId) {\n        String tableName = managementService.getTableName(TaskEntity.class);\n        // taskService.createTaskQuery() 没有 parentId 参数，所以写 sql 查询\n        String sql = \"select ID_,NAME_,OWNER_,ASSIGNEE_ from \" + tableName + \" where PARENT_TASK_ID_=#{parentTaskId}\";\n        return taskService.createNativeTaskQuery().sql(sql).parameter(\"parentTaskId\", parentTaskId).list();\n    }\n\n    /**\n     * 获取子任务个数\n     *\n     * @param parentTaskId 父任务 ID\n     * @return 剩余子任务个数\n     */\n    private Long getTaskCountByParentTaskId(String parentTaskId) {\n        String tableName = managementService.getTableName(TaskEntity.class);\n        String sql = \"SELECT COUNT(1) from \" + tableName + \" WHERE PARENT_TASK_ID_=#{parentTaskId}\";\n        return taskService.createNativeTaskQuery().sql(sql).parameter(\"parentTaskId\", parentTaskId).count();\n    }\n\n    /**\n     * 获得任务根任务的父任务编号\n     *\n     * @param task 任务\n     * @return 根任务的父任务编号\n     */\n    private String getTaskRootParentId(Task task) {\n        if (task == null || task.getParentTaskId() == null) {\n            return null;\n        }\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            Task parentTask = getTask(task.getParentTaskId());\n            if (parentTask == null) {\n                return null;\n            }\n            if (parentTask.getParentTaskId() == null) {\n                return parentTask.getId();\n            }\n            task = parentTask;\n        }\n        throw new IllegalArgumentException(String.format(\"Task(%s) 层级过深，无法获取父节点编号\", task.getId()));\n    }\n\n    @Override\n    public List<HistoricActivityInstance> getActivityListByProcessInstanceId(String processInstanceId) {\n        return historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)\n                .orderByHistoricActivityInstanceStartTime().asc().list();\n    }\n\n    @Override\n    public List<HistoricActivityInstance> getHistoricActivityListByExecutionId(String executionId) {\n        return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list();\n    }\n\n    @Override\n    public List<HistoricTaskInstance> getFinishedTaskListByProcessInstanceIdWithoutCancel(String processInstanceId) {\n        return historyService.createHistoricTaskInstanceQuery()\n                .finished()\n                .includeTaskLocalVariables()\n                .processInstanceId(processInstanceId)\n                .taskVariableValueNotEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS,\n                        BpmTaskStatusEnum.CANCEL.getStatus())\n                .orderByHistoricTaskInstanceStartTime().asc().list();\n    }\n\n    /**\n     * 判断指定用户，是否是当前任务的审批人\n     *\n     * @param userId 用户编号\n     * @param task   任务\n     * @return 是否\n     */\n    private boolean isAssignUserTask(Long userId, Task task) {\n        Long assignee = NumberUtil.parseLong(task.getAssignee(), null);\n        return ObjectUtil.equals(userId, assignee);\n    }\n\n    /**\n     * 判断指定用户，是否是当前任务的拥有人\n     *\n     * @param userId 用户编号\n     * @param task   任务\n     * @return 是否\n     */\n    private boolean isOwnerUserTask(Long userId, Task task) {\n        Long assignee = NumberUtil.parseLong(task.getOwner(), null);\n        return ObjectUtil.equal(userId, assignee);\n    }\n\n    /**\n     * 判断指定用户，是否是当前任务的加签人\n     *\n     * @param userId 用户 Id\n     * @param task   任务\n     * @return 是否\n     */\n    private boolean isAddSignUserTask(Long userId, Task task) {\n        return (isAssignUserTask(userId, task) || isOwnerUserTask(userId, task))\n                && BpmTaskSignTypeEnum.of(task.getScopeType()) != null;\n    }\n\n    // ========== Update 写入相关方法 ==========\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) {\n        // 1.1 校验任务存在\n        Task task = validateTask(userId, reqVO.getId());\n        // 1.2 校验流程实例存在\n        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());\n        if (instance == null) {\n            throw exception(PROCESS_INSTANCE_NOT_EXISTS);\n        }\n        // 1.3 校验签名\n        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());\n        Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey());\n        if (signEnable && StrUtil.isEmpty(reqVO.getSignPicUrl())) {\n            throw exception(TASK_SIGNATURE_NOT_EXISTS);\n        }\n        // 1.4 校验审批意见\n        Boolean reasonRequire = parseReasonRequire(bpmnModel, task.getTaskDefinitionKey());\n        if (reasonRequire && StrUtil.isEmpty(reqVO.getReason())) {\n            throw exception(TASK_REASON_REQUIRE);\n        }\n\n        // 情况一：被委派的任务，不调用 complete 去完成任务\n        if (DelegationState.PENDING.equals(task.getDelegationState())) {\n            approveDelegateTask(reqVO, task);\n            return;\n        }\n\n        // 情况二：审批有【后】加签的任务\n        if (BpmTaskSignTypeEnum.AFTER.getType().equals(task.getScopeType())) {\n            approveAfterSignTask(task, reqVO);\n            return;\n        }\n\n        // 情况三：审批普通的任务。大多数情况下，都是这样\n        // 2.1 更新 task 状态、原因、签字\n        updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason());\n        if (signEnable) {\n            taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_SIGN_PIC_URL, reqVO.getSignPicUrl());\n        }\n        // 2.2 添加评论\n        taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),\n                BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));\n\n        // 3. 设置流程变量。如果流程变量前端传空，需要从历史实例中获取，原因：前端表单如果在当前节点无可编辑的字段时 variables 一定会为空\n        // 场景一：A 节点发起，B 节点表单无可编辑字段，审批通过时，C 节点需要流程变量获取下一个执行节点，但因为 B 节点无可编辑的字段，variables 为空，流程可能出现问题。\n        // 场景二：A 节点发起，B 节点只有某一个字段可编辑（比如 day），但 C 节点需要多个节点。\n        //       （比如 work + day 变量，在发起时填写，因为 B 节点只有 day 的编辑权限，在审批后，variables 会缺少 work 的值）\n        Map<String, Object> processVariables = new HashMap<>();\n        if (CollUtil.isNotEmpty(instance.getProcessVariables())) { // 获取历史中流程变量\n            processVariables.putAll(instance.getProcessVariables());\n        }\n        if (CollUtil.isNotEmpty(reqVO.getVariables())) { // 合并前端传递的流程变量，以前端为准\n            processVariables.putAll(reqVO.getVariables());\n        }\n\n        // 4. 校验并处理 APPROVE_USER_SELECT 当前审批人，选择下一节点审批人的逻辑\n        Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables,\n                bpmnModel, reqVO.getNextAssignees(), instance);\n        runtimeService.setVariables(task.getProcessInstanceId(), variables);\n\n        // 5. 如果当前节点 Id 存在于需要预测的流程节点中，从中移除。 流程变量在回退操作中设置\n        Object needSimulateTaskIds = runtimeService.getVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS);\n        Set<String> needSimulateTaskIdsByReturn = Convert.toSet(String.class, needSimulateTaskIds);\n        if (needSimulateTaskIdsByReturn.contains(task.getTaskDefinitionKey())) {\n            needSimulateTaskIdsByReturn.remove(task.getTaskDefinitionKey());\n            runtimeService.setVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskIdsByReturn);\n        }\n\n        // 6. 调用 BPM complete 去完成任务\n        taskService.complete(task.getId(), variables, true);\n\n        // 【加签专属】处理加签任务\n        handleParentTaskIfSign(task.getParentTaskId());\n    }\n\n    /**\n     * 校验选择的下一个节点的审批人，是否合法\n     * <p>\n     * 1. 是否有漏选：没有选择审批人\n     * 2. 是否有多选：非下一个节点\n     *\n     * @param taskDefinitionKey 当前任务节点标识\n     * @param variables         流程变量\n     * @param bpmnModel         流程模型\n     * @param nextAssignees     下一个节点审批人集合（参数）\n     * @param processInstance   流程实例\n     */\n    @SuppressWarnings(\"unchecked\")\n    private Map<String, Object> validateAndSetNextAssignees(String taskDefinitionKey, Map<String, Object> variables, BpmnModel bpmnModel,\n                                                            Map<String, List<Long>> nextAssignees, ProcessInstance processInstance) {\n        // simple 设计器第一个节点默认为发起人节点，不校验是否存在审批人\n        if (Objects.equals(taskDefinitionKey, START_USER_NODE_ID)) {\n            return variables;\n        }\n        // 1. 获取下一个将要执行的节点集合\n        FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);\n        List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);\n\n        // 2. 校验选择的下一个节点的审批人，是否合法\n        for (FlowNode nextFlowNode : nextFlowNodes) {\n            Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);\n            // 2.1 情况一：如果节点中的审批人策略为 发起人自选\n            if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {\n                // 特殊：如果当前节点已经存在审批人，则不允许覆盖\n                Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables());\n                if (startUserSelectAssignees != null && CollUtil.isNotEmpty(startUserSelectAssignees.get(nextFlowNode.getId()))) {\n                    continue;\n                }\n                // 如果节点存在，但未配置审批人\n                List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null;\n                if (CollUtil.isEmpty(assignees)) {\n                    throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());\n                }\n\n                // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES\n                if (startUserSelectAssignees == null) {\n                    startUserSelectAssignees = new HashMap<>();\n                }\n                startUserSelectAssignees.put(nextFlowNode.getId(), assignees);\n                variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);\n                continue;\n            }\n\n            // 2.2 情况二：如果节点中的审批人策略为 审批人，在审批时选择下一个节点的审批人，并且该节点的审批人为空\n            if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {\n                // 如果节点存在，但未配置审批人\n                Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables());\n                List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null;\n                if (CollUtil.isEmpty(assignees)) {\n                    throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());\n                }\n\n                // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES\n                if (approveUserSelectAssignees == null) {\n                    approveUserSelectAssignees = new HashMap<>();\n                }\n                approveUserSelectAssignees.put(nextFlowNode.getId(), assignees);\n                Map<String, List<Long>> existingApproveUserSelectAssignees = (Map<String, List<Long>>) variables.get(\n                        BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES);\n                if (CollUtil.isNotEmpty(existingApproveUserSelectAssignees)) {\n                    approveUserSelectAssignees.putAll(existingApproveUserSelectAssignees);\n                }\n                variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, approveUserSelectAssignees);\n            }\n        }\n        return variables;\n    }\n\n    /**\n     * 审批通过存在“后加签”的任务。\n     * <p>\n     * 注意：该任务不能马上完成，需要一个中间状态（APPROVING），并激活剩余所有子任务（PROCESS）为可审批处理\n     * 如果马上完成，则会触发下一个任务，甚至如果没有下一个任务则流程实例就直接结束了！\n     *\n     * @param task  当前任务\n     * @param reqVO 前端请求参数\n     */\n    private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) {\n        // 更新父 task 状态 + 原因\n        updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVING.getStatus(), reqVO.getReason());\n\n        // 2. 激活子任务\n        List<Task> childrenTaskList = getTaskListByParentTaskId(task.getId());\n        for (Task childrenTask : childrenTaskList) {\n            taskService.resolveTask(childrenTask.getId());\n            // 更新子 task 状态\n            updateTaskStatus(childrenTask.getId(), BpmTaskStatusEnum.RUNNING.getStatus());\n        }\n    }\n\n    /**\n     * 如果父任务是有前后【加签】的任务，如果它【加签】出来的子任务都被处理，需要处理父任务：\n     * <p>\n     * 1. 如果是【向前】加签，则需要重新激活父任务，让它可以被审批\n     * 2. 如果是【向后】加签，则需要完成父任务，让它完成审批\n     *\n     * @param parentTaskId 父任务编号\n     */\n    private void handleParentTaskIfSign(String parentTaskId) {\n        if (StrUtil.isBlank(parentTaskId)) {\n            return;\n        }\n        // 1.1 判断是否还有子任务。如果没有，就不处理\n        Long childrenTaskCount = getTaskCountByParentTaskId(parentTaskId);\n        if (childrenTaskCount > 0) {\n            return;\n        }\n        // 1.2 只处理加签的父任务\n        Task parentTask = validateTaskExist(parentTaskId);\n        String scopeType = parentTask.getScopeType();\n        if (BpmTaskSignTypeEnum.of(scopeType) == null) {\n            return;\n        }\n\n        // 2. 子任务已处理完成，清空 scopeType 字段，修改 parentTask 信息，方便后续可以继续向前后向后加签\n        TaskEntityImpl parentTaskImpl = (TaskEntityImpl) parentTask;\n        parentTaskImpl.setScopeType(null);\n        taskService.saveTask(parentTaskImpl);\n\n        // 3.1 情况一：处理向【向前】加签\n        if (BpmTaskSignTypeEnum.BEFORE.getType().equals(scopeType)) {\n            // 3.1.1 owner 重新赋值给父任务的 assignee，这样它就可以被审批\n            taskService.resolveTask(parentTaskId);\n            // 3.1.2 更新流程任务 status\n            updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus());\n            // 3.2 情况二：处理向【向后】加签\n        } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) {\n            // 只有 parentTask 处于 APPROVING 的情况下，才可以继续 complete 完成\n            // 否则，一个未审批的 parentTask 任务，在加签出来的任务都被减签的情况下，就直接完成审批，这样会存在问题\n            Integer status = (Integer) parentTask.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS);\n            if (ObjectUtil.notEqual(status, BpmTaskStatusEnum.APPROVING.getStatus())) {\n                return;\n            }\n            // 3.2.2 完成自己（因为它已经没有子任务，所以也可以完成）\n            updateTaskStatus(parentTaskId, BpmTaskStatusEnum.APPROVE.getStatus());\n            taskService.complete(parentTaskId);\n        }\n\n        // 4. 递归处理父任务\n        handleParentTaskIfSign(parentTask.getParentTaskId());\n    }\n\n    /**\n     * 审批被委派的任务\n     *\n     * @param reqVO 前端请求参数，包含当前任务ID，审批意见等\n     * @param task  当前被审批的任务\n     */\n    private void approveDelegateTask(BpmTaskApproveReqVO reqVO, Task task) {\n        // 1. 添加审批意见\n        AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId()).getCheckedData();\n        AdminUserRespDTO ownerUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())).getCheckedData(); // 发起委托的用户\n        Assert.notNull(ownerUser, \"委派任务找不到原审批人，需要检查数据\");\n        taskService.addComment(reqVO.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_END.getType(),\n                BpmCommentTypeEnum.DELEGATE_END.formatComment(currentUser.getNickname(), ownerUser.getNickname(), reqVO.getReason()));\n\n        // 2.1 调用 resolveTask 完成任务。\n        // 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner())：将 owner 设置为 assignee\n        taskService.resolveTask(task.getId());\n        // 2.2 更新 task 状态 + 原因\n        updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus(), reqVO.getReason());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) {\n        // 1.1 校验任务存在\n        Task task = validateTask(userId, reqVO.getId());\n        // 1.2 校验流程实例存在\n        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());\n        if (instance == null) {\n            throw exception(PROCESS_INSTANCE_NOT_EXISTS);\n        }\n\n        // 2.1 更新流程任务为不通过\n        updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason());\n        // 2.2 添加流程评论\n        taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),\n                BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason()));\n        // 2.3 如果当前任务时被加签的，则加它的根任务也标记成未通过\n        // 疑问：为什么要标记未通过呢？\n        // 回答：例如说 A 任务被向前加签除 B 任务时，B 任务被审批不通过，此时 A 会被取消。而 yudao-ui-admin-vue3 不展示“已取消”的任务，导致展示不出审批不通过的细节。\n        if (task.getParentTaskId() != null) {\n            String rootParentId = getTaskRootParentId(task);\n            updateTaskStatusAndReason(rootParentId, BpmTaskStatusEnum.REJECT.getStatus(),\n                    BpmCommentTypeEnum.REJECT.formatComment(\"加签任务不通过\"));\n            taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),\n                    BpmCommentTypeEnum.REJECT.formatComment(\"加签任务不通过\"));\n        }\n\n        // 3. 根据不同的 RejectHandler 处理策略\n        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());\n        FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());\n        // 3.1 情况一：驳回到指定的任务节点\n        BpmUserTaskRejectHandlerTypeEnum userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);\n        if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerTypeEnum.RETURN_USER_TASK) {\n            String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement);\n            Assert.notNull(returnTaskId, \"退回的节点不能为空\");\n            returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())\n                    .setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));\n            return;\n        }\n\n        // 3.2 情况二： 标记流程为不通过并结束流程\n        processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过\n        moveTaskToEnd(task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); // 结束流程\n    }\n\n    /**\n     * 更新流程任务的 status 状态\n     *\n     * @param id     任务编号\n     * @param status 状态\n     */\n    private void updateTaskStatus(String id, Integer status) {\n        taskService.setVariableLocal(id, BpmnVariableConstants.TASK_VARIABLE_STATUS, status);\n    }\n\n    /**\n     * 更新流程任务的 status 状态、reason 理由\n     *\n     * @param id     任务编号\n     * @param status 状态\n     * @param reason 理由（审批通过、审批不通过的理由）\n     */\n    private void updateTaskStatusAndReason(String id, Integer status, String reason) {\n        updateTaskStatus(id, status);\n        taskService.setVariableLocal(id, BpmnVariableConstants.TASK_VARIABLE_REASON, reason);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) {\n        // 1.1 当前任务 task\n        Task task = validateTask(userId, reqVO.getId());\n        if (task.isSuspended()) {\n            throw exception(TASK_IS_PENDING);\n        }\n        // 1.2 获取流程模型信息\n        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());\n        // 1.3 校验源头和目标节点的关系，并返回目标元素\n        FlowElement targetElement = validateTargetTaskCanReturn(bpmnModel, task.getTaskDefinitionKey(),\n                reqVO.getTargetTaskDefinitionKey());\n\n        // 2. 调用 Flowable 框架的退回逻辑\n        returnTask(userId, bpmnModel, task, targetElement, reqVO);\n    }\n\n    /**\n     * 退回流程节点时，校验目标任务节点是否可退回\n     *\n     * @param bpmnModel 流程模型\n     * @param sourceKey 当前任务节点 Key\n     * @param targetKey 目标任务节点 key\n     * @return 目标任务节点元素\n     */\n    private FlowElement validateTargetTaskCanReturn(BpmnModel bpmnModel, String sourceKey, String targetKey) {\n        // 1.1 获取当前任务节点元素\n        FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey);\n        // 1.2 获取跳转的节点元素\n        FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey);\n        if (target == null) {\n            throw exception(TASK_TARGET_NODE_NOT_EXISTS);\n        }\n\n        // 2. 只有串行可到达的节点，才可以退回。类似非串行、子流程无法退回\n        if (!BpmnModelUtils.isSequentialReachable(source, target, null)) {\n            throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR);\n        }\n        return target;\n    }\n\n    /**\n     * 执行退回逻辑\n     *\n     * @param userId        用户编号\n     * @param bpmnModel     流程模型\n     * @param currentTask   当前退回的任务\n     * @param targetElement 需要退回到的目标任务\n     * @param reqVO         前端参数封装\n     */\n    public void returnTask(Long userId, BpmnModel bpmnModel, Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {\n        // 1. 获得所有需要回撤的任务 taskDefinitionKey，用于稍后的 moveActivityIdsToSingleActivityId 回撤\n        // 1.1 获取所有正常进行的任务节点 Key\n        List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list();\n        List<String> runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey);\n        // 1.2 通过 targetElement 的出口连线，计算在 runTaskKeyList 有哪些 key 需要被撤回\n        // 为什么不直接使用 runTaskKeyList 呢？因为可能存在多个审批分支，例如说：A -> B -> C 和 D -> F，而只要 C 撤回到 A，需要排除掉 F\n        List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);\n        List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);\n\n        // 2. 给当前要被退回的 task 数组，设置退回意见\n        taskList.forEach(task -> {\n            // 需要排除掉，不需要设置退回意见的任务\n            if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {\n                return;\n            }\n\n            // 判断是否分配给自己任务，因为会签任务，一个节点会有多个任务\n            if (isAssignUserTask(userId, task)) { // 情况一：自己的任务，进行 RETURN 标记\n                // 2.1.1 添加评论\n                taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(),\n                        BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason()));\n                // 2.1.2 更新 task 状态 + 原因\n                updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason());\n            } else { // 情况二：别人的任务，进行 CANCEL 标记\n                processTaskCanceled(task.getId());\n            }\n        });\n\n        // 3. 构建需要预测的任务流程变量\n        Set<String> needSimulateTaskDefinitionKeys = getNeedSimulateTaskDefinitionKeys(bpmnModel, currentTask, targetElement);\n\n        // 4. 执行驳回\n        // ① 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId。原因：当多实例任务回退的时候有问题。\n        //    相关 issue: https://github.com/flowable/flowable-engine/issues/3944\n        // ② flowable 7.2.0 版本后，继续使用 moveActivityIdsToSingleActivityId 方法。原因：flowable 7.2.0 版本修复了该问题。\n        //    相关 issue：https://github.com/YunaiV/ruoyi-vue-pro/issues/1018\n        runtimeService.createChangeActivityStateBuilder()\n                .processInstanceId(currentTask.getProcessInstanceId())\n                .moveActivityIdsToSingleActivityId(returnTaskKeyList, reqVO.getTargetTaskDefinitionKey())\n                // 设置需要预测的任务 ids 的流程变量，用于辅助预测\n                .processVariable(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskDefinitionKeys)\n                // 设置流程变量（local）节点退回标记, 用于退回到节点，不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略，导致自动通过\n                .localVariable(reqVO.getTargetTaskDefinitionKey(),\n                        String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE)\n                .changeState();\n    }\n\n    private Set<String> getNeedSimulateTaskDefinitionKeys(BpmnModel bpmnModel, Task currentTask, FlowElement targetElement) {\n        // 1. 获取需要预测的任务的 definition key。因为当前任务还没完成，也需要预测\n        Set<String> taskDefinitionKeys = CollUtil.newHashSet(currentTask.getTaskDefinitionKey());\n\n        // 2.1 获取已结束任务按时间倒序排序\n        List<HistoricTaskInstance> endTaskList = CollectionUtils.filterList(\n                getTaskListByProcessInstanceId(currentTask.getProcessInstanceId(), Boolean.FALSE),\n                item -> item.getEndTime() != null);\n        // 2.2 从结束任务中找到最近一个的目标任务\n        HistoricTaskInstance targetTask = findFirst(endTaskList,\n                item -> item.getTaskDefinitionKey().equals(targetElement.getId()));\n        if (targetTask == null) {\n            return taskDefinitionKeys;\n        }\n        // 2.3 遍历已结束的任务，找到在 targetTask 之后生成的任务，且串行可达的任务\n        endTaskList.forEach(item -> {\n            FlowElement element = getFlowElementById(bpmnModel, item.getTaskDefinitionKey());\n            // 如果已结束的任务在回退目标节点之后生成，且串行可达，则加到需要预测节点中\n            // TODO 串行可达的方法需要和判断可回退节点 validateTargetTaskCanReturn 分开吗？ 并行网关可能会有问题。\n            if (item.getCreateTime().compareTo(targetTask.getCreateTime()) > 0\n                    && BpmnModelUtils.isSequentialReachable(element, targetElement, null)) {\n                taskDefinitionKeys.add(item.getTaskDefinitionKey());\n            }\n        });\n        return taskDefinitionKeys;\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO) {\n        String taskId = reqVO.getId();\n        // 1.1 校验任务\n        Task task = validateTask(userId, reqVO.getId());\n        if (task.getAssignee().equals(reqVO.getDelegateUserId().toString())) { // 校验当前审批人和被委派人不是同一人\n            throw exception(TASK_DELEGATE_FAIL_USER_REPEAT);\n        }\n        // 1.2 校验目标用户存在\n        AdminUserRespDTO delegateUser = adminUserApi.getUser(reqVO.getDelegateUserId()).getCheckedData();\n        if (delegateUser == null) {\n            throw exception(TASK_DELEGATE_FAIL_USER_NOT_EXISTS);\n        }\n\n        // 2. 添加委托意见\n        AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData();\n        taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_START.getType(),\n                BpmCommentTypeEnum.DELEGATE_START.formatComment(currentUser.getNickname(), delegateUser.getNickname(), reqVO.getReason()));\n\n        // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee)\n        // 特殊：如果已经被委派（owner 非空），则不需要更新 owner：https://gitee.com/zhijiantianya/yudao-cloud/issues/ICJ153\n        if (StrUtil.isEmpty(task.getOwner())) {\n            taskService.setOwner(taskId, task.getAssignee());\n        }\n        // 3.2 执行委派，将任务委派给 delegateUser\n        taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString());\n        // 补充说明：委托不单独设置状态。如果需要，可通过 Task 的 DelegationState 字段，判断是否为 DelegationState.PENDING 委托中\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void transferTask(Long userId, BpmTaskTransferReqVO reqVO) {\n        String taskId = reqVO.getId();\n        // 1.1 校验任务\n        Task task = validateTask(userId, reqVO.getId());\n        if (task.getAssignee().equals(reqVO.getAssigneeUserId().toString())) { // 校验当前审批人和被转派人不是同一人\n            throw exception(TASK_TRANSFER_FAIL_USER_REPEAT);\n        }\n        // 1.2 校验目标用户存在\n        AdminUserRespDTO assigneeUser = adminUserApi.getUser(reqVO.getAssigneeUserId()).getCheckedData();\n        if (assigneeUser == null) {\n            throw exception(TASK_TRANSFER_FAIL_USER_NOT_EXISTS);\n        }\n\n        // 2. 添加委托意见\n        AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData();\n        taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.TRANSFER.getType(),\n                BpmCommentTypeEnum.TRANSFER.formatComment(currentUser.getNickname(), assigneeUser.getNickname(), reqVO.getReason()));\n\n        // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee)\n        // 特殊：如果已经被转派（owner 非空），则不需要更新 owner：https://gitee.com/zhijiantianya/yudao-cloud/issues/ICJ153\n        if (StrUtil.isEmpty(task.getOwner())) {\n            taskService.setOwner(taskId, task.getAssignee());\n        }\n        // 3.2 执行转派（审批人），将任务转派给 assigneeUser\n        // 委托（ delegate）和转派（transfer）的差别，就在这块的调用！！！！\n        taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void moveTaskToEnd(String processInstanceId, String reason) {\n        List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null);\n        if (CollUtil.isEmpty(taskList)) {\n            return;\n        }\n\n        // 1. 其它未结束的任务，直接取消\n        // 疑问：为什么不通过 updateTaskStatusWhenCanceled 监听取消，而是直接提前调用呢？\n        // 回答：详细见 updateTaskStatusWhenCanceled 的方法，加签的场景\n        taskList.forEach(task -> {\n            Integer otherTaskStatus = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS);\n            if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) {\n                return;\n            }\n            processTaskCanceled(task.getId());\n        });\n\n        // 2. 终止流程\n        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId());\n        List<String> activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey));\n        EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel);\n        Assert.notNull(endEvent, \"结束节点不能为空\");\n        runtimeService.createChangeActivityStateBuilder()\n                .processInstanceId(processInstanceId)\n                .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId())\n                .changeState();\n\n        // 3. 特殊：如果跳转到 EndEvent 流程还未结束， 执行 deleteProcessInstance 方法\n        // TODO 芋艿：目前发现并行分支情况下，会存在这个情况，后续看看有没更好的方案；\n        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();\n        if (CollUtil.isNotEmpty(executions)) {\n            log.warn(\"[moveTaskToEnd][执行跳转到 EndEvent 后, 流程实例未结束，强制执行 deleteProcessInstance 方法]\");\n            runtimeService.deleteProcessInstance(processInstanceId, reason);\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO) {\n        // 1. 获取和校验任务\n        TaskEntityImpl taskEntity = validateTaskCanCreateSign(userId, reqVO);\n        List<AdminUserRespDTO> userList = adminUserApi.getUserList(reqVO.getUserIds()).getCheckedData();\n        if (CollUtil.isEmpty(userList)) {\n            throw exception(TASK_SIGN_CREATE_USER_NOT_EXIST);\n        }\n\n        // 2. 处理当前任务\n        // 2.1 开启计数功能，主要用于为了让表 ACT_RU_TASK 中的 SUB_TASK_COUNT_ 字段记录下总共有多少子任务，后续可能有用\n        taskEntity.setCountEnabled(true);\n        // 2.2 向前加签，设置 owner，置空 assign。等子任务都完成后，再调用 resolveTask 重新将 owner 设置为 assign\n        // 原因是：不能和向前加签的子任务一起审批，需要等前面的子任务都完成才能审批\n        if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) {\n            taskEntity.setOwner(taskEntity.getAssignee());\n            taskEntity.setAssignee(null);\n        }\n        // 2.4 记录加签方式，完成任务时需要用到判断\n        taskEntity.setScopeType(reqVO.getType());\n        // 2.5 保存当前任务修改后的值\n        taskService.saveTask(taskEntity);\n        // 2.6 更新 task 状态为 WAIT，只有在向前加签的时候\n        if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) {\n            updateTaskStatus(taskEntity.getId(), BpmTaskStatusEnum.WAIT.getStatus());\n        }\n\n        // 3. 创建加签任务\n        createSignTaskList(convertList(reqVO.getUserIds(), String::valueOf), taskEntity);\n\n        // 4. 记录加签的评论到 task 任务\n        AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData();\n        String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getComment(),\n                currentUser.getNickname(), BpmTaskSignTypeEnum.nameOfType(reqVO.getType()),\n                String.join(\",\", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason());\n        taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), BpmCommentTypeEnum.ADD_SIGN.getType(), comment);\n    }\n\n    /**\n     * 校验任务是否可以加签，主要校验加签类型是否一致：\n     * <p>\n     * 1. 如果存在“向前加签”的任务，则不能“向后加签”\n     * 2. 如果存在“向后加签”的任务，则不能“向前加签”\n     *\n     * @param userId 当前用户 ID\n     * @param reqVO  请求参数，包含任务 ID 和加签类型\n     * @return 当前任务\n     */\n    private TaskEntityImpl validateTaskCanCreateSign(Long userId, BpmTaskSignCreateReqVO reqVO) {\n        TaskEntityImpl taskEntity = (TaskEntityImpl) validateTask(userId, reqVO.getId());\n        // 向前加签和向后加签不能同时存在\n        if (taskEntity.getScopeType() != null\n                && ObjectUtil.notEqual(taskEntity.getScopeType(), reqVO.getType())) {\n            throw exception(TASK_SIGN_CREATE_TYPE_ERROR,\n                    BpmTaskSignTypeEnum.nameOfType(taskEntity.getScopeType()), BpmTaskSignTypeEnum.nameOfType(reqVO.getType()));\n        }\n\n        // 同一个 key 的任务，审批人不重复\n        List<Task> taskList = taskService.createTaskQuery().processInstanceId(taskEntity.getProcessInstanceId())\n                .taskDefinitionKey(taskEntity.getTaskDefinitionKey()).list();\n        List<Long> currentAssigneeList = convertListByFlatMap(taskList, task -> // 需要考虑 owner 的情况，因为向后加签时，它暂时没 assignee 而是 owner\n                Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner())));\n        if (CollUtil.containsAny(currentAssigneeList, reqVO.getUserIds())) {\n            List<AdminUserRespDTO> userList = adminUserApi.getUserList(CollUtil.intersection(currentAssigneeList, reqVO.getUserIds())).getCheckedData();\n            throw exception(TASK_SIGN_CREATE_USER_REPEAT, String.join(\",\", convertList(userList, AdminUserRespDTO::getNickname)));\n        }\n        return taskEntity;\n    }\n\n    /**\n     * 创建加签子任务\n     *\n     * @param userIds    被加签的用户 ID\n     * @param taskEntity 被加签的任务\n     */\n    private void createSignTaskList(List<String> userIds, TaskEntityImpl taskEntity) {\n        if (CollUtil.isEmpty(userIds)) {\n            return;\n        }\n        // 创建加签人的新任务，全部基于 taskEntity 为父任务来创建\n        for (String addSignId : userIds) {\n            if (StrUtil.isBlank(addSignId)) {\n                continue;\n            }\n            createSignTask(taskEntity, addSignId);\n        }\n    }\n\n    /**\n     * 创建加签子任务\n     *\n     * @param parentTask 父任务\n     * @param assignee   子任务的执行人\n     */\n    private void createSignTask(TaskEntityImpl parentTask, String assignee) {\n        // 1. 生成子任务\n        TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID());\n        BpmTaskConvert.INSTANCE.copyTo(parentTask, task);\n\n        // 2.1 向前加签，设置审批人\n        if (BpmTaskSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) {\n            task.setAssignee(assignee);\n            // 2.2 向后加签，设置 owner 不设置 assignee 是因为不能同时审批，需要等父任务完成\n        } else {\n            task.setOwner(assignee);\n        }\n        // 2.3 保存子任务\n        taskService.saveTask(task);\n\n        // 3. 向后前签，设置子任务的状态为 WAIT，因为需要等父任务审批完\n        if (BpmTaskSignTypeEnum.AFTER.getType().equals(parentTask.getScopeType())) {\n            updateTaskStatus(task.getId(), BpmTaskStatusEnum.WAIT.getStatus());\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    @SuppressWarnings(\"DataFlowIssue\")\n    public void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO) {\n        // 1.1 校验 task 可以被减签\n        Task task = validateTaskCanSignDelete(reqVO.getId());\n        // 1.2 校验取消人存在\n        AdminUserRespDTO cancelUser = null;\n        if (StrUtil.isNotBlank(task.getAssignee())) {\n            cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee())).getCheckedData();\n        }\n        if (cancelUser == null && StrUtil.isNotBlank(task.getOwner())) {\n            cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())).getCheckedData();\n        }\n        Assert.notNull(cancelUser, \"任务中没有所有者和审批人，数据错误\");\n\n        // 2.1 获得子任务列表，包括子任务的子任务\n        List<Task> childTaskList = getAllChildTaskList(task);\n        childTaskList.add(task);\n        // 2.2 更新子任务为已取消\n        String cancelReason = StrUtil.format(\"任务被取消，原因：由于[{}]操作[减签]，\", cancelUser.getNickname());\n        childTaskList.forEach(childTask -> updateTaskStatusAndReason(childTask.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), cancelReason));\n        // 2.3 删除任务和所有子任务\n        taskService.deleteTasks(convertList(childTaskList, Task::getId));\n\n        // 3. 记录日志到父任务中。先记录日志是因为，通过 handleParentTask 方法之后，任务可能被完成了，并且不存在了，会报异常，所以先记录\n        AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData();\n        taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), BpmCommentTypeEnum.SUB_SIGN.getType(),\n                StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getComment(), user.getNickname(), cancelUser.getNickname()));\n\n        // 4. 处理当前任务的父任务\n        handleParentTaskIfSign(task.getParentTaskId());\n    }\n\n    @Override\n    public void copyTask(Long userId, BpmTaskCopyReqVO reqVO) {\n        processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getReason(), reqVO.getId());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @DataPermission(enable = false) // 关闭数据权限，避免查询不到用户数据。相关案例：https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA\n    public void withdrawTask(Long userId, String taskId) {\n        // 1.1 查询本人已办任务\n        HistoricTaskInstance taskInstance = historyService.createHistoricTaskInstanceQuery()\n                .taskId(taskId).taskAssignee(userId.toString()).finished().singleResult();\n        if (ObjUtil.isNull(taskInstance)) {\n            throw exception(TASK_WITHDRAW_FAIL_TASK_NOT_EXISTS);\n        }\n        // 1.2 校验流程是否结束\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(taskInstance.getProcessInstanceId());\n        if (ObjUtil.isNull(processInstance)) {\n            throw exception(TASK_WITHDRAW_FAIL_PROCESS_NOT_RUNNING);\n        }\n        // 1.3 判断此流程是否允许撤回\n        BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(\n                processInstance.getProcessDefinitionId());\n        if (ObjUtil.isNull(processDefinitionInfo) || !Boolean.TRUE.equals(processDefinitionInfo.getAllowWithdrawTask())) {\n            throw exception(TASK_WITHDRAW_FAIL_NOT_ALLOW);\n        }\n        // 1.4 判断下一个节点是否被审批过，如果是则无法撤回\n        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskInstance.getProcessDefinitionId());\n        UserTask userTask = (UserTask) BpmnModelUtils.getFlowElementById(bpmnModel, taskInstance.getTaskDefinitionKey());\n        List<String> nextUserTaskKeys = convertList(BpmnModelUtils.getNextUserTasks(userTask), UserTask::getId);\n        if (CollUtil.isEmpty(nextUserTaskKeys)) {\n            throw exception(TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW);\n        }\n        // TODO @芋艿：是否选择升级flowable版本解决taskCreatedAfter、taskCreatedBefore问题，升级7.1.0可以；包括 todo 和 done 那边的查询哇？？？ 是的！\n        long nextUserTaskFinishedCount = historyService.createHistoricTaskInstanceQuery()\n                .processInstanceId(processInstance.getProcessInstanceId()).taskDefinitionKeys(nextUserTaskKeys)\n                .taskCreatedAfter(taskInstance.getEndTime()).finished().count();\n        if (nextUserTaskFinishedCount > 0) {\n            throw exception(TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW);\n        }\n        // 1.5 获取需要撤回的运行任务\n        List<Task> runningTasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId())\n                .taskDefinitionKeys(nextUserTaskKeys).active().list();\n        if (CollUtil.isEmpty(runningTasks)) {\n            throw exception(TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW);\n        }\n\n        // 2.1 取消当前任务\n        List<String> withdrawExecutionIds = new ArrayList<>();\n        for (Task task : runningTasks) {\n            // 标记撤回任务为取消\n            taskService.addComment(task.getId(), taskInstance.getProcessInstanceId(), BpmCommentTypeEnum.CANCEL.getType(),\n                    BpmCommentTypeEnum.CANCEL.formatComment(\"前一节点撤回\"));\n            updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_WITHDRAW.getReason());\n            withdrawExecutionIds.add(task.getExecutionId());\n        }\n        // 2.2 执行撤回操作\n        runtimeService.createChangeActivityStateBuilder()\n                .processInstanceId(processInstance.getProcessInstanceId())\n                .moveExecutionsToSingleActivityId(withdrawExecutionIds, taskInstance.getTaskDefinitionKey())\n                .changeState();\n    }\n\n    /**\n     * 校验任务是否能被减签\n     *\n     * @param id 任务编号\n     * @return 任务信息\n     */\n    private Task validateTaskCanSignDelete(String id) {\n        Task task = validateTaskExist(id);\n        if (task.getParentTaskId() == null) {\n            throw exception(TASK_SIGN_DELETE_NO_PARENT);\n        }\n        Task parentTask = getTask(task.getParentTaskId());\n        if (parentTask == null) {\n            throw exception(TASK_SIGN_DELETE_NO_PARENT);\n        }\n        if (BpmTaskSignTypeEnum.of(parentTask.getScopeType()) == null) {\n            throw exception(TASK_SIGN_DELETE_NO_PARENT);\n        }\n        return task;\n    }\n\n    // ========== Event 事件相关方法 ==========\n\n    @Override\n    public void processTaskCreated(Task task) {\n        // 1. 设置为待办中\n        Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS);\n        if (status != null) {\n            log.error(\"[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]\", task.getId(), status);\n            return;\n        }\n        updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus());\n\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());\n        if (processInstance == null) {\n            log.error(\"[processTaskCreated][taskId({}) 没有找到流程实例]\", task.getId());\n            return;\n        }\n        BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.\n                getProcessDefinitionInfo(processInstance.getProcessDefinitionId());\n        if (processDefinitionInfo == null) {\n            log.error(\"[processTaskCreated][processDefinitionId({}) 没有找到流程定义]\", processInstance.getProcessDefinitionId());\n            return;\n        }\n\n        // 2. 任务前置通知\n        if (ObjUtil.isNotNull(processDefinitionInfo.getTaskBeforeTriggerSetting())) {\n            BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskBeforeTriggerSetting();\n            BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,\n                    setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());\n        }\n\n        // 3. 处理自动通过的情况，例如说：1）无审批人时，是否自动通过、不通过；2）非【人工审核】时，是否自动通过、不通过\n        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());\n        FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());\n        Integer approveType = BpmnModelUtils.parseApproveType(userTaskElement);\n        Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(userTaskElement);\n        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {\n\n            /**\n             * 特殊情况：部分情况下，TransactionSynchronizationManager 注册 afterCommit 监听时，不会被调用，但是 afterCompletion 可以\n             * 例如说：第一个 task 就是配置【自动通过】或者【自动拒绝】时\n             * 参见 <a href=\"https://gitee.com/zhijiantianya/yudao-cloud/issues/IB7V7Q\">issue</a> 反馈\n             */\n            @Override\n            public void afterCompletion(int transactionStatus) {\n                // 回滚情况，直接返回\n                if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) {\n                    return;\n                }\n                // 特殊情况：第一个 task 【自动通过】时，第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN，不知道啥原因\n                if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN)\n                        && getTask(task.getId()) == null) {\n                    return;\n                }\n                // 特殊情况一：【人工审核】审批人为空，根据配置是否要自动通过、自动拒绝\n                if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) {\n                    // 如果有审批人、或者拥有人，则说明不满足情况一，不自动通过、不自动拒绝\n                    if (!ObjectUtil.isAllEmpty(task.getAssignee(), task.getOwner())) {\n                        return;\n                    }\n                    if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) {\n                        getSelf().approveTask(null, new BpmTaskApproveReqVO()\n                                .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_APPROVE.getReason()));\n                    } else if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.REJECT.getType())) {\n                        getSelf().rejectTask(null, new BpmTaskRejectReqVO()\n                                .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_REJECT.getReason()));\n                    }\n                    // 特殊情况二：【自动审核】审批类型为自动通过、不通过\n                } else {\n                    if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType())) {\n                        getSelf().approveTask(null, new BpmTaskApproveReqVO()\n                                .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_APPROVE.getReason()));\n                    } else if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {\n                        getSelf().rejectTask(null, new BpmTaskRejectReqVO()\n                                .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_REJECT.getReason()));\n                    }\n                }\n            }\n\n        });\n    }\n\n    /**\n     * 重要补充说明：该方法目前主要有两个情况会调用到：\n     * <p>\n     * 1. 或签场景 + 审批通过：一个或签有多个审批时，如果 A 审批通过，其它或签 B、C 等任务会被 Flowable 自动删除，此时需要通过该方法更新状态为已取消\n     * 2. 审批不通过：在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时，对于加签的任务，不会被 Flowable 删除，此时需要通过该方法更新状态为已取消\n     */\n    @Override\n    public void processTaskCanceled(String taskId) {\n        Task task = getTask(taskId);\n        // 1. 可能只是活动，不是任务，所以查询不到\n        if (task == null) {\n            log.error(\"[updateTaskStatusWhenCanceled][taskId({}) 任务不存在]\", taskId);\n            return;\n        }\n\n        // 2. 更新 task 状态 + 原因\n        Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS);\n        if (BpmTaskStatusEnum.isEndStatus(status)) {\n            log.error(\"[updateTaskStatusWhenCanceled][taskId({}) 处于结果({})，无需进行更新]\", taskId, status);\n            return;\n        }\n        updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason());\n        // 补充说明：由于 Task 被删除成 HistoricTask 后，无法通过 taskService.addComment 添加理由，所以无法存储具体的取消理由\n    }\n\n    @Override\n    @DataPermission(enable = false) // 忽略数据权限，避免因为过滤，导致找不到候选人\n    public void processTaskAssigned(Task task) {\n        // 发送通知。在事务提交时，批量执行操作，所以直接查询会无法查询到 ProcessInstance，所以这里是通过监听事务的提交来实现。\n        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {\n\n            /**\n             * 特殊情况：部分情况下，TransactionSynchronizationManager 注册 afterCommit 监听时，不会被调用，但是 afterCompletion 可以\n             * 例如说：第一个 task 就是配置【自动通过】或者【自动拒绝】时\n             * 参见 <a href=\"https://gitee.com/zhijiantianya/yudao-cloud/issues/IB7V7Q\">issue</a> 反馈\n             */\n            @Override\n            public void afterCompletion(int transactionStatus) {\n                // 回滚情况，直接返回\n                if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) {\n                    return;\n                }\n                // 特殊情况：第一个 task 【自动通过】时，第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN，不知道啥原因\n                if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN)\n                        && getTask(task.getId()) == null) {\n                    return;\n                }\n                if (StrUtil.isEmpty(task.getAssignee())) {\n                    log.error(\"[processTaskAssigned][taskId({}) 没有分配到负责人]\", task.getId());\n                    return;\n                }\n                ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());\n                if (processInstance == null) {\n                    log.error(\"[processTaskAssigned][taskId({}) 没有找到流程实例]\", task.getId());\n                    return;\n                }\n\n                // 自动去重，通过自动审批的方式\n                BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId());\n                if (processDefinitionInfo == null) {\n                    log.error(\"[processTaskAssigned][taskId({}) 没有找到流程定义({})]\", task.getId(), task.getProcessDefinitionId());\n                    return;\n                }\n                if (processDefinitionInfo.getAutoApprovalType() != null) {\n                    HistoricTaskInstanceQuery sameAssigneeQuery = historyService.createHistoricTaskInstanceQuery()\n                            .processInstanceId(task.getProcessInstanceId())\n                            .taskAssignee(task.getAssignee()) // 相同审批人\n                            .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus())\n                            .finished();\n                    if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType())\n                            && sameAssigneeQuery.count() > 0) {\n                        getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())\n                                .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName()));\n                        return;\n                    }\n                    if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) {\n                        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());\n                        if (bpmnModel == null) {\n                            log.error(\"[processTaskAssigned][taskId({}) 没有找到流程模型({})]\", task.getId(), task.getProcessDefinitionId());\n                            return;\n                        }\n                        List<String> sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingFlows( // 获取所有上一个节点\n                                        BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())),\n                                SequenceFlow::getSourceRef);\n                        if (sameAssigneeQuery.taskDefinitionKeys(sourceTaskIds).count() > 0) {\n                            getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())\n                                    .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName()));\n                            return;\n                        }\n                    }\n                }\n\n                // 获取发起人节点\n                BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());\n                if (bpmnModel == null) {\n                    log.error(\"[processTaskAssigned][taskId({}) 没有找到流程模型]\", task.getId());\n                    return;\n                }\n                FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());\n                // 判断是否为退回或者驳回：如果是退回或者驳回不走这个策略（使用 local variable）\n                Boolean returnTaskFlag = runtimeService.getVariableLocal(task.getExecutionId(),\n                        String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);\n                Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(),\n                        BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class));\n                if (userTaskElement.getId().equals(START_USER_NODE_ID)\n                        && (skipStartUserNodeFlag == null // 目的：一般是“主流程”，发起人节点，自动通过审核\n                        || BooleanUtil.isTrue(skipStartUserNodeFlag)) // 目的：一般是“子流程”，发起人节点，按配置自动通过审核\n                        && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {\n                    getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())\n                            .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));\n                    return;\n                }\n                // 当不为发起人节点时，审批人与提交人为同一人时，根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理\n                if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID)\n                        && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {\n                    if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {\n                        Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement);\n\n                        // 情况一：自动跳过\n                        if (ObjectUtils.equalsAny(assignStartUserHandlerType,\n                                BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {\n                            getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())\n                                    .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason()));\n                            return;\n                        }\n                        // 情况二：转交给部门负责人审批\n                        if (ObjectUtils.equalsAny(assignStartUserHandlerType,\n                                BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) {\n                            AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData();\n                            Assert.notNull(startUser, \"提交人({})信息为空\", processInstance.getStartUserId());\n                            DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()).getCheckedData() : null;\n                            Assert.notNull(dept, \"提交人({})部门({})信息为空\", processInstance.getStartUserId(), startUser.getDeptId());\n                            // 找不到部门负责人的情况下，自动审批通过\n                            // noinspection DataFlowIssue\n                            if (dept.getLeaderUserId() == null) {\n                                getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())\n                                        .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason()));\n                                return;\n                            }\n                            // 找得到部门负责人的情况下，修改负责人\n                            if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) {\n                                getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO()\n                                        .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId())\n                                        .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason()));\n                                return;\n                            }\n                            // 如果部门负责人是自己，还是自己审批吧~\n                        }\n                    }\n                }\n                // 注意：需要基于 instance 设置租户编号，避免 Flowable 内部异步时，丢失租户编号\n                FlowableUtils.execute(processInstance.getTenantId(), () -> {\n                    AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData();\n                    messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));\n                });\n            }\n\n        });\n    }\n\n    @Override\n    public void processTaskCompleted(Task task) {\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());\n        if (processInstance == null) {\n            log.error(\"[processTaskCompleted][taskId({}) 没有找到流程实例]\", task.getId());\n            return;\n        }\n        BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.\n                getProcessDefinitionInfo(processInstance.getProcessDefinitionId());\n        if (processDefinitionInfo == null) {\n            log.error(\"[processTaskCompleted][processDefinitionId({}) 没有找到流程定义]\", processInstance.getProcessDefinitionId());\n            return;\n        }\n\n        // 任务后置通知\n        if (ObjUtil.isNotNull(processDefinitionInfo.getTaskAfterTriggerSetting())) {\n            BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskAfterTriggerSetting();\n            BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,\n                    setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType) {\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n        if (processInstance == null) {\n            log.error(\"[processTaskTimeout][processInstanceId({}) 没有找到流程实例]\", processInstanceId);\n            return;\n        }\n        List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, true, taskDefineKey);\n        // TODO 优化：未来需要考虑加签的情况\n        if (CollUtil.isEmpty(taskList)) {\n            log.error(\"[processTaskTimeout][processInstanceId({}) 定义Key({}) 没有找到任务]\", processInstanceId, taskDefineKey);\n            return;\n        }\n\n        taskList.forEach(task -> FlowableUtils.execute(task.getTenantId(), () -> {\n            // 情况一：自动提醒\n            if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType())) {\n                messageService.sendMessageWhenTaskTimeout(new BpmMessageSendWhenTaskTimeoutReqDTO()\n                        .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())\n                        .setTaskId(task.getId()).setTaskName(task.getName()).setAssigneeUserId(Long.parseLong(task.getAssignee())));\n                return;\n            }\n\n            // 情况二：自动同意\n            if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.APPROVE.getType())) {\n                approveTask(Long.parseLong(task.getAssignee()),\n                        new BpmTaskApproveReqVO().setId(task.getId()).setReason(BpmReasonEnum.TIMEOUT_APPROVE.getReason()));\n                return;\n            }\n\n            // 情况三：自动拒绝\n            if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REJECT.getType())) {\n                rejectTask(Long.parseLong(task.getAssignee()),\n                        new BpmTaskRejectReqVO().setId(task.getId()).setReason(BpmReasonEnum.REJECT_TASK.getReason()));\n            }\n        }));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) {\n        List<ActivityInstance> activityInstances = runtimeService.createActivityInstanceQuery()\n                .processInstanceId(processInstanceId)\n                .activityId(taskDefineKey).list();\n        activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(),\n                () -> moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason())));\n    }\n\n    @Override\n    public void triggerTask(String processInstanceId, String taskDefineKey) {\n        Execution execution = runtimeService.createExecutionQuery()\n                .processInstanceId(processInstanceId)\n                .activityId(taskDefineKey)\n                .singleResult();\n        if (execution == null) {\n            log.error(\"[triggerTask][processInstanceId({}) activityId({}) 没有找到执行活动]\", processInstanceId, taskDefineKey);\n            return;\n        }\n\n        // 若存在直接触发接收任务，执行后续节点\n        FlowableUtils.execute(execution.getTenantId(),\n                () -> runtimeService.trigger(execution.getId()));\n    }\n\n    /**\n     * 获得自身的代理对象，解决 AOP 生效问题\n     *\n     * @return 自己\n     */\n    private BpmTaskServiceImpl getSelf() {\n        return SpringUtil.getBean(getClass());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task.listener;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserEmptyTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport lombok.Setter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.common.engine.api.delegate.Expression;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.delegate.ExecutionListener;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\n/**\n * BPM 子流程监听器：设置流程的发起人\n *\n * @author Lesan\n */\n@Component\n@Slf4j\npublic class BpmCallActivityListener implements ExecutionListener {\n\n    public static final String DELEGATE_EXPRESSION = \"${bpmCallActivityListener}\";\n\n    @Setter\n    private Expression listenerConfig;\n\n    @Resource\n    private BpmProcessDefinitionService processDefinitionService;\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public void notify(DelegateExecution execution) {\n        String expressionText = listenerConfig.getExpressionText();\n        Assert.notNull(expressionText, \"监听器扩展字段({})不能为空\", expressionText);\n        BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject(\n                expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class);\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getRootProcessInstanceId());\n\n        // 1. 当发起人来源为主流程发起人时，并兜底 startUserSetting 为空时\n        if (startUserSetting == null\n                || startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) {\n            FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId()));\n            return;\n        }\n\n        // 2. 当发起人来源为表单时\n        if (startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) {\n            String formFieldValue = MapUtil.getStr(processInstance.getProcessVariables(), startUserSetting.getFormField());\n            // 2.1 当表单值为空时\n            if (StrUtil.isEmpty(formFieldValue)) {\n                // 2.1.1 来自主流程发起人\n                if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) {\n                    FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId()));\n                    return;\n                }\n                // 2.1.2 来自子流程管理员\n                if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) {\n                    BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId());\n                    List<Long> managerUserIds = processDefinition.getManagerUserIds();\n                    FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0));\n                    return;\n                }\n                // 2.1.3 来自主流程管理员\n                if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) {\n                    BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processInstance.getProcessDefinitionId());\n                    List<Long> managerUserIds = processDefinition.getManagerUserIds();\n                    FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0));\n                    return;\n                }\n            }\n            // 2.2 使用表单值，并兜底字符串转 Long 失败时使用主流程发起人\n            try {\n                FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue));\n            } catch (NumberFormatException ex) {\n                try {\n                    List<Long> formFieldValues = JsonUtils.parseArray(formFieldValue, Long.class);\n                    FlowableUtils.setAuthenticatedUserId(formFieldValues.get(0));\n                } catch (Exception e) {\n                    log.error(\"[notify][监听器：{}，子流程监听器设置流程的发起人字符串转 Long 失败，字符串：{}]\",\n                            DELEGATE_EXPRESSION, formFieldValue);\n                    FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId()));\n                }\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task.listener;\n\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport lombok.Setter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.common.engine.api.delegate.Expression;\nimport org.flowable.engine.delegate.TaskListener;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.task.service.delegate.DelegateTask;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig;\n\n// TODO @芋艿：可能会想换个包地址\n/**\n * BPM 用户任务通用监听器\n *\n * @author Lesan\n */\n@Component\n@Slf4j\n@Scope(\"prototype\")\npublic class BpmUserTaskListener implements TaskListener {\n\n    public static final String DELEGATE_EXPRESSION = \"${bpmUserTaskListener}\";\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n\n    @Setter\n    private Expression listenerConfig;\n\n    @Override\n    public void notify(DelegateTask delegateTask) {\n        // 1. 获取所需基础信息\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(delegateTask.getProcessInstanceId());\n        BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig);\n\n        // 2. 发起请求\n        // TODO @芋艿：哪些默认参数，后续再调研下；感觉可以搞个 task 字段，把整个 delegateTask 放进去；\n        listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey(\"processInstanceId\")\n                .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getProcessInstanceId()));\n        listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey(\"assignee\")\n                .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getAssignee()));\n        listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey(\"taskDefinitionKey\")\n                .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getTaskDefinitionKey()));\n        listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey(\"taskId\")\n                .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getId()));\n        BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,\n                listenerHandler.getPath(), listenerHandler.getHeader(), listenerHandler.getBody(), false, null);\n\n        // 3. 是否需要后续操作？TODO 芋艿：待定！\n    }\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task.trigger;\n\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;\n\n// TODO @芋艿：可能会想换个包地址\n/**\n * BPM 触发器接口\n * <p>\n * 处理不同的动作\n *\n * @author jason\n */\npublic interface BpmTrigger {\n\n    /**\n     * 对应触发器类型\n     *\n     * @return 触发器类型\n     */\n    BpmTriggerTypeEnum getType();\n\n    /**\n     * 触发器执行\n     *\n     * @param processInstanceId 流程实例编号\n     * @param param 触发器参数\n     */\n    void execute(String processInstanceId, String param);\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task.trigger.form;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * BPM 删除流程表单数据触发器\n *\n * @author jason\n */\n@Component\n@Slf4j\npublic class BpmFormDeleteTrigger implements BpmTrigger {\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public BpmTriggerTypeEnum getType() {\n        return BpmTriggerTypeEnum.FORM_DELETE;\n    }\n\n    @Override\n    public void execute(String processInstanceId, String param) {\n        // 1. 解析删除流程表单数据配置\n        List<BpmSimpleModelNodeVO.TriggerSetting.FormTriggerSetting> settings = JsonUtils.parseObject(param, new TypeReference<List<BpmSimpleModelNodeVO.TriggerSetting.FormTriggerSetting>>() {});\n        if (CollUtil.isEmpty(settings)) {\n            log.error(\"[execute][流程({}) 删除流程表单数据触发器配置为空]\", processInstanceId);\n            return;\n        }\n\n        // 2. 获取流程变量\n        Map<String, Object> processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables();\n\n        // 3.1 获取需要删除的表单字段\n        Set<String> deleteFields = new HashSet<>();\n        settings.forEach(setting -> {\n            if (CollUtil.isEmpty(setting.getDeleteFields())) {\n                return;\n            }\n            // 配置了条件，判断条件是否满足\n            boolean isFieldDeletedNeeded = true;\n            if (setting.getConditionType() != null) {\n                String conditionExpression = SimpleModelUtils.buildConditionExpression(\n                        setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups());\n                isFieldDeletedNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression);\n            }\n            if (isFieldDeletedNeeded) {\n                deleteFields.addAll(setting.getDeleteFields());\n            }\n        });\n\n        // 3.2 删除流程变量\n        if (CollUtil.isNotEmpty(deleteFields)) {\n            processInstanceService.removeProcessInstanceVariables(processInstanceId, deleteFields);\n        }\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task.trigger.form;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.FormTriggerSetting;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * BPM 更新流程表单触发器\n *\n * @author jason\n */\n@Component\n@Slf4j\npublic class BpmFormUpdateTrigger implements BpmTrigger {\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public BpmTriggerTypeEnum getType() {\n        return BpmTriggerTypeEnum.FORM_UPDATE;\n    }\n\n    @Override\n    public void execute(String processInstanceId, String param) {\n        // 1. 解析更新流程表单配置\n        List<FormTriggerSetting> settings = JsonUtils.parseObject(param, new TypeReference<List<FormTriggerSetting>>() {});\n        if (CollUtil.isEmpty(settings)) {\n            log.error(\"[execute][流程({}) 更新流程表单触发器配置为空]\", processInstanceId);\n            return;\n        }\n\n        // 2. 获取流程变量\n        Map<String, Object> processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables();\n\n        // 3. 更新流程变量\n        for (FormTriggerSetting setting : settings) {\n            if (CollUtil.isEmpty(setting.getUpdateFormFields())) {\n                continue;\n            }\n            // 配置了条件，判断条件是否满足\n            boolean isFormUpdateNeeded = true;\n            if (setting.getConditionType() != null) {\n                String conditionExpression = SimpleModelUtils.buildConditionExpression(\n                        setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups());\n                isFormUpdateNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression);\n            }\n            // 更新流程表单\n            if (isFormUpdateNeeded) {\n                processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task.trigger.http;\n\nimport cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * BPM 发送 HTTP 请求触发器抽象类\n *\n * @author jason\n */\n@Slf4j\npublic abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger {\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task.trigger.http;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.client.RestTemplate;\n\nimport javax.annotation.Resource;\n\n/**\n * BPM HTTP 回调触发器\n *\n * @author jason\n */\n@Component\n@Slf4j\npublic class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger {\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public BpmTriggerTypeEnum getType() {\n        return BpmTriggerTypeEnum.HTTP_CALLBACK;\n    }\n\n    @Override\n    public void execute(String processInstanceId, String param) {\n        // 1. 解析 http 请求配置\n        BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting setting = JsonUtils.parseObject(param,\n                BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting.class);\n        if (setting == null) {\n            log.error(\"[execute][流程({}) HTTP 回调触发器配置为空]\", processInstanceId);\n            return;\n        }\n\n        // 2. 发起请求\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n        setting.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam()\n                .setKey(\"taskDefineKey\") // 重要：回调请求 taskDefineKey 需要传给被调用方，用于回调执行\n                .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(setting.getCallbackTaskDefineKey()));\n        BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,\n                setting.getUrl(), setting.getHeader(), setting.getBody(), false, null);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.task.trigger.http;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.client.RestTemplate;\n\nimport javax.annotation.Resource;\n\n/**\n * BPM 发送同步 HTTP 请求触发器\n *\n * @author jason\n */\n@Component\n@Slf4j\npublic class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger {\n\n    @Resource\n    private BpmProcessInstanceService processInstanceService;\n\n    @Override\n    public BpmTriggerTypeEnum getType() {\n        return BpmTriggerTypeEnum.HTTP_REQUEST;\n    }\n\n    @Override\n    public void execute(String processInstanceId, String param) {\n        // 1. 解析 http 请求配置\n        HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, HttpRequestTriggerSetting.class);\n        if (setting == null) {\n            log.error(\"[execute][流程({}) HTTP 触发器请求配置为空]\", processInstanceId);\n            return;\n        }\n\n        // 2. 发起请求\n        ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n        BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,\n                setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 5 # 初始连接数\n        min-idle: 10 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n        slave: # 模拟从库，可根据自己需要修改 # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 400-infra.server.iocoder.cn # 地址\n    port: 6379 # 端口\n    database: 1 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  demo: true # 开启演示模式\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 1 # 初始连接数\n        min-idle: 1 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒（1 分钟）\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro-jdk8-new?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例\n          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例\n          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例\n          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例\n          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例\n          username: root\n          password: 123456\n        #          username: sa # SQL Server 连接的示例\n        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例\n        #          username: SYSDBA # DM 连接的示例\n        #          password: SYSDBA # DM 连接的示例\n        slave: # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro-jdk8-new?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 6379 # 端口\n    database: 0 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n# 日志文件配置\nlogging:\n  level:\n    # 配置自己写的 MyBatis Mapper 打印日志\n    cn.iocoder.yudao.module.bpm.dal.mysql: debug\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  env: # 多环境的配置项\n    tag: ${HOSTNAME}\n  security:\n    mock-enable: true\n  access-log: # 访问日志的配置项\n    enable: false\n  demo: false # 关闭演示模式"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: bpm-server\n\n  profiles:\n    active: local\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n    allow-bean-definition-overriding: true # 允许 Bean 覆盖，例如说 Feign 等会存在重复定义的服务\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  # Servlet 配置\n  servlet:\n    # 文件上传相关配置项\n    multipart:\n      max-file-size: 16MB # 单个文件大小\n      max-request-size: 32MB # 设置总上传的文件大小\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  # Cache 配置项\n  cache:\n    type: REDIS\n    redis:\n      time-to-live: 1h # 设置过期时间为 1 小时\n\nserver:\n  port: 48083\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\n--- #################### 接口文档配置 ####################\n\nspringdoc:\n  api-docs:\n    enabled: true # 1. 是否开启 Swagger 接文档的元数据\n    path: /v3/api-docs\n  swagger-ui:\n    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面\n    path: /swagger-ui\n  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档\n\nknife4j:\n  enable: true\n  setting:\n    language: zh_cn\n\n# 工作流 Flowable 配置\nflowable:\n  # 1. false: 默认值，Flowable 启动时，对比数据库表中保存的版本，如果不匹配。将抛出异常\n  # 2. true: 启动时会对数据库中所有表进行更新操作，如果表存在，不做处理，反之，自动创建表\n  # 3. create_drop: 启动时自动创建表，关闭时自动删除表\n  # 4. drop_create: 启动时，删除旧表，再创建新表\n  database-schema-update: true # 设置为 false，可通过 https://github.com/flowable/flowable-sql 初始化\n  db-history-used: true # flowable6 默认 true 生成信息表，无需手动设置\n  check-process-definitions: false # 设置为 false，禁用 /resources/processes 自动部署 BPMN XML 流程\n  history-level: audit # full：保存历史数据的最高级别，可保存全部流程相关细节，包括流程流转各节点参数\n\n# MyBatis Plus 的配置项\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: NONE # “智能”模式，基于 IdTypeEnvironmentPostProcessor + 数据源的类型，自动适配成 AUTO、INPUT 模式。\n      #      id-type: AUTO # 自增 ID，适合 MySQL 等直接自增的数据库\n      #      id-type: INPUT # 用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n      #      id-type: ASSIGN_ID # 分配 ID，默认使用雪花算法。注意，Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时，需要去除实体类上的 @KeySequence 注解\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n    banner: false # 关闭控制台的 Banner 打印\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  encryptor:\n    password: XDV71a+xqStEA3WH # 加解密的秘钥，可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成\n\nmybatis-plus-join:\n  banner: false # 关闭控制台的 Banner 打印\n\n# Spring Data Redis 配置\nspring:\n  data:\n    redis:\n      repositories:\n        enabled: false # 项目未使用到 Spring Data Redis 的 Repository，所以直接禁用，保证启动速度\n\n# VO 转换（数据翻译）相关\neasy-trans:\n  is-enable-global: false # 【默认禁用，对性能确认压力大】启用全局翻译（拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置，或通过 @IgnoreTrans 忽略某个接口\n\n--- #################### RPC 远程调用相关配置 ####################\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    executor:\n      appname: ${spring.application.name} # 执行器 AppName\n      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径\n    accessToken: default_token # 执行器通讯TOKEN\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0\n    base-package: cn.iocoder.yudao.module.bpm\n  web:\n    admin-ui:\n      url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址\n  xss:\n    enable: false\n    exclude-urls: # 如下 url，仅仅是为了演示，去掉配置也没关系\n      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求\n  swagger:\n    title: 管理后台\n    description: 提供管理员管理的所有功能\n    version: ${yudao.info.version}\n  tenant: # 多租户相关配置项\n    enable: true\n\ndebug: false\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other.BpmTaskCandidateAssignEmptyStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.bpmn.model.ExtensionElement;\nimport org.flowable.bpmn.model.FlowElement;\nimport org.flowable.bpmn.model.UserTask;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.Spy;\nimport org.mockito.internal.util.collections.Sets;\n\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;\nimport static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link BpmTaskCandidateInvoker} 的单元测试\n *\n * @author 芋道源码\n */\npublic class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {\n\n    private BpmTaskCandidateInvoker taskCandidateInvoker;\n\n    @Mock\n    private AdminUserApi adminUserApi;\n\n    @Mock\n    private BpmProcessInstanceService processInstanceService;\n\n    @Spy\n    private BpmTaskCandidateStrategy userStrategy;\n    @Mock\n    private BpmTaskCandidateAssignEmptyStrategy emptyStrategy;\n\n    @Spy\n    private List<BpmTaskCandidateStrategy> strategyList;\n\n    @BeforeEach\n    public void setUp() {\n        userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例\n        when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY);\n        strategyList = ListUtil.of(userStrategy, emptyStrategy); // 创建 strategyList\n        taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi);\n    }\n\n    /**\n     * 场景：成功计算到候选人，但是移除了发起人的用户\n     */\n    @Test\n    public void testCalculateUsersByTask_some() {\n        try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {\n            // 准备参数\n            String param = \"1,2\";\n            DelegateExecution execution = mock(DelegateExecution.class);\n            // mock 方法（DelegateExecution）\n            UserTask userTask = mock(UserTask.class);\n            String processInstanceId = randomString();\n            when(execution.getProcessInstanceId()).thenReturn(processInstanceId);\n            when(execution.getCurrentFlowElement()).thenReturn(userTask);\n            when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))\n                    .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());\n            when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))\n                    .thenReturn(param);\n            // mock 方法（adminUserApi）\n            AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)\n                    .setStatus(CommonStatusEnum.ENABLE.getStatus()));\n            AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)\n                    .setStatus(CommonStatusEnum.ENABLE.getStatus()));\n            Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)\n                    .put(user2.getId(), user2).build();\n            when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);\n            // mock 移除发起人的用户\n            springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class))\n                    .thenReturn(processInstanceService);\n            ProcessInstance processInstance = mock(ProcessInstance.class);\n            when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance);\n            when(processInstance.getStartUserId()).thenReturn(\"1\");\n            mockFlowElementExtensionElement(userTask, BpmnModelConstants.USER_TASK_ASSIGN_START_USER_HANDLER_TYPE,\n                    String.valueOf(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType()));\n\n            // 调用\n            Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);\n            // 断言\n            assertEquals(asSet(2L), results);\n        }\n    }\n\n    /**\n     * 场景：没有计算到候选人，但是被禁用移除，最终通过 empty 进行分配\n     */\n    @Test\n    public void testCalculateUsersByTask_none() {\n        try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {\n            // 准备参数\n            String param = \"1,2\";\n            DelegateExecution execution = mock(DelegateExecution.class);\n            // mock 方法（DelegateExecution）\n            UserTask userTask = mock(UserTask.class);\n            String processInstanceId = randomString();\n            when(execution.getProcessInstanceId()).thenReturn(processInstanceId);\n            when(execution.getCurrentFlowElement()).thenReturn(userTask);\n            when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))\n                    .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());\n            when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))\n                    .thenReturn(param);\n            // mock 方法（adminUserApi）\n            AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)\n                    .setStatus(CommonStatusEnum.DISABLE.getStatus()));\n            AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)\n                    .setStatus(CommonStatusEnum.DISABLE.getStatus()));\n            Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)\n                    .put(user2.getId(), user2).build();\n            when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);\n            // mock 方法（empty）\n            when(emptyStrategy.calculateUsersByTask(same(execution), same(param)))\n                    .thenReturn(Sets.newSet(2L));\n            // mock 移除发起人的用户\n            springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class))\n                    .thenReturn(processInstanceService);\n            ProcessInstance processInstance = mock(ProcessInstance.class);\n            when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance);\n            when(processInstance.getStartUserId()).thenReturn(\"1\");\n\n            // 调用\n            Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);\n            // 断言\n            assertEquals(asSet(2L), results);\n        }\n    }\n\n    /**\n     * 场景：没有计算到候选人，但是被禁用移除，最终通过 empty 进行分配\n     */\n    @Test\n    public void testCalculateUsersByActivity_some() {\n        try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {\n            // 准备参数\n            String param = \"1,2\";\n            BpmnModel bpmnModel = mock(BpmnModel.class);\n            String activityId = randomString();\n            Long startUserId = 1L;\n            String processDefinitionId = randomString();\n            Map<String, Object> processVariables = new HashMap<>();\n            // mock 方法（DelegateExecution）\n            UserTask userTask = mock(UserTask.class);\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask)))\n                    .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy());\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask)))\n                    .thenReturn(param);\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask);\n            // mock 方法（adminUserApi）\n            AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)\n                    .setStatus(CommonStatusEnum.ENABLE.getStatus()));\n            AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)\n                    .setStatus(CommonStatusEnum.ENABLE.getStatus()));\n            Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)\n                    .put(user2.getId(), user2).build();\n            when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);\n            // mock 移除发起人的用户\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignStartUserHandlerType(same(userTask)))\n                    .thenReturn(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType());\n\n            // 调用\n            Set<Long> results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,\n                    startUserId, processDefinitionId, processVariables);\n            // 断言\n            assertEquals(asSet(2L), results);\n        }\n    }\n\n    /**\n     * 场景：成功计算到候选人，但是移除了发起人的用户\n     */\n    @Test\n    public void testCalculateUsersByActivity_none() {\n        try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {\n            // 准备参数\n            String param = \"1,2\";\n            BpmnModel bpmnModel = mock(BpmnModel.class);\n            String activityId = randomString();\n            Long startUserId = 1L;\n            String processDefinitionId = randomString();\n            Map<String, Object> processVariables = new HashMap<>();\n            // mock 方法（DelegateExecution）\n            UserTask userTask = mock(UserTask.class);\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask)))\n                    .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy());\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask)))\n                    .thenReturn(param);\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask);\n            // mock 方法（adminUserApi）\n            AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)\n                    .setStatus(CommonStatusEnum.DISABLE.getStatus()));\n            AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)\n                    .setStatus(CommonStatusEnum.DISABLE.getStatus()));\n            Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)\n                    .put(user2.getId(), user2).build();\n            when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);\n            // mock 方法（empty）\n            when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId),\n                    eq(param), same(startUserId), same(processDefinitionId), same(processVariables)))\n                    .thenReturn(Sets.newSet(2L));\n\n            // 调用\n            Set<Long> results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,\n                    startUserId, processDefinitionId, processVariables);\n            // 断言\n            assertEquals(asSet(2L), results);\n        }\n    }\n\n    private static void mockFlowElementExtensionElement(FlowElement element, String name, String value) {\n        if (value == null) {\n            return;\n        }\n        ExtensionElement extensionElement = new ExtensionElement();\n        extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);\n        extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);\n        extensionElement.setElementText(value);\n        extensionElement.setName(name);\n        // mock\n        Map<String, List<ExtensionElement>> extensionElements = element.getExtensionElements();\n        if (extensionElements == null) {\n            extensionElements = new LinkedHashMap<>();\n        }\n        extensionElements.put(name, Collections.singletonList(extensionElement));\n        when(element.getExtensionElements()).thenReturn(extensionElements);\n    }\n\n    @Test\n    public void testRemoveDisableUsers() {\n        // 准备参数. 1L 可以找到；2L 是禁用的；3L 找不到\n        Set<Long> assigneeUserIds = asSet(1L, 2L, 3L);\n        // mock 方法\n        AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)\n                .setStatus(CommonStatusEnum.ENABLE.getStatus()));\n        AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)\n                .setStatus(CommonStatusEnum.DISABLE.getStatus()));\n        Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)\n                .put(user2.getId(), user2).build();\n        when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);\n\n        // 调用\n        taskCandidateInvoker.removeDisableUsers(assigneeUserIds);\n        // 断言\n        assertEquals(asSet(1L), assigneeUserIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\n\npublic class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskAssignLeaderExpression expression;\n\n    @Mock\n    private AdminUserApi adminUserApi;\n    @Mock\n    private DeptApi deptApi;\n\n    @Mock\n    private BpmProcessInstanceService processInstanceService;\n\n    @Test\n    public void testCalculateUsers_noDept() {\n        // 准备参数\n        DelegateExecution execution = mockDelegateExecution(1L);\n        // mock 方法(startUser)\n        AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L));\n        when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser));\n        // mock 方法(getStartUserDept)没有部门\n        when(deptApi.getDept(eq(10L))).thenReturn(success(null));\n\n        // 调用\n        Set<Long> result = expression.calculateUsers(execution, 1);\n        // 断言\n        assertEquals(0, result.size());\n    }\n\n    @Test\n    public void testCalculateUsers_noParentDept() {\n        // 准备参数\n        DelegateExecution execution = mockDelegateExecution(1L);\n        // mock 方法(startUser)\n        AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L));\n        when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser));\n        DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L)\n                .setLeaderUserId(20L));\n        // mock 方法（getDept）\n        when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept));\n        when(deptApi.getDept(eq(100L))).thenReturn(success(null));\n\n        // 调用\n        Set<Long> result = expression.calculateUsers(execution, 2);\n        // 断言\n        assertEquals(asSet(20L), result);\n    }\n\n    @Test\n    public void testCalculateUsers_existParentDept() {\n        // 准备参数\n        DelegateExecution execution = mockDelegateExecution(1L);\n        // mock 方法(startUser)\n        AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L));\n        when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser));\n        DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L)\n                .setLeaderUserId(20L));\n        when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept));\n        // mock 方法（父 dept）\n        DeptRespDTO parentDept = randomPojo(DeptRespDTO.class, o -> o.setId(100L).setParentId(1000L)\n                .setLeaderUserId(200L));\n        when(deptApi.getDept(eq(100L))).thenReturn(success(parentDept));\n\n        // 调用\n        Set<Long> result = expression.calculateUsers(execution, 2);\n        // 断言\n        assertEquals(asSet(200L), result);\n    }\n\n    @SuppressWarnings(\"SameParameterValue\")\n    private DelegateExecution mockDelegateExecution(Long startUserId) {\n        ExecutionEntityImpl execution = new ExecutionEntityImpl();\n        execution.setProcessInstanceId(randomString());\n        // mock 返回 startUserId\n        ExecutionEntityImpl processInstance = new ExecutionEntityImpl();\n        processInstance.setStartUserId(String.valueOf(startUserId));\n        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId())))\n                .thenReturn(processInstance);\n        return execution;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport org.assertj.core.util.Sets;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.stubbing.Answer;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.when;\n\npublic class BpmTaskCandidateDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateDeptLeaderMultiStrategy strategy;\n\n    @Mock\n    private DeptApi deptApi;\n\n    @Test\n    public void testCalculateUsers() {\n        // 准备参数\n        String param = \"10,20|2\";\n        // mock 方法\n        when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult<DeptRespDTO>>) invocationOnMock -> {\n            Long deptId = invocationOnMock.getArgument(0);\n            return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1)));\n        });\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsers(param);\n        // 断言结果\n        assertEquals(Sets.newLinkedHashSet(11L, 1001L, 21L, 2001L), userIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport org.assertj.core.util.Sets;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static java.util.Arrays.asList;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\n\npublic class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateDeptLeaderStrategy strategy;\n\n    @Mock\n    private DeptApi deptApi;\n\n    @Test\n    public void testCalculateUsers() {\n        // 准备参数\n        String param = \"10,20\";\n        // mock 方法\n        when(deptApi.getDeptList(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList(\n                randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(10L).setLeaderUserId(11L)),\n                randomPojo(DeptRespDTO.class, o -> o.setId(20L).setParentId(20L).setLeaderUserId(21L)))));\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsers(param);\n        // 断言结果\n        assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.assertj.core.util.Sets;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static java.util.Arrays.asList;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\n\npublic class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateDeptMemberStrategy strategy;\n\n    @Mock\n    private DeptApi deptApi;\n    @Mock\n    private AdminUserApi adminUserApi;\n\n    @Test\n    public void testCalculateUsers() {\n        // 准备参数\n        String param = \"10,20\";\n        // mock 方法\n        when(adminUserApi.getUserListByDeptIds(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList(\n                randomPojo(AdminUserRespDTO.class, o -> o.setId(11L)),\n                randomPojo(AdminUserRespDTO.class, o -> o.setId(21L)))));\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsers(param);\n        // 断言结果\n        assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.assertj.core.util.Sets;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.stubbing.Answer;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateStartUserDeptLeaderMultiStrategy strategy;\n\n    @Mock\n    private BpmProcessInstanceService processInstanceService;\n\n    @Mock\n    private AdminUserApi adminUserApi;\n    @Mock\n    private DeptApi deptApi;\n\n    @Test\n    public void testCalculateUsersByTask() {\n        // 准备参数\n        String param = \"2\";\n        // mock 方法（获得流程发起人）\n        Long startUserId = 1L;\n        ProcessInstance processInstance = mock(ProcessInstance.class);\n        DelegateExecution execution = mock(DelegateExecution.class);\n        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);\n        when(processInstance.getStartUserId()).thenReturn(startUserId.toString());\n        // mock 方法（获取发起人的 multi 部门负责人）\n        mockGetStartUserDept(startUserId);\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByTask(execution, param);\n        // 断言\n        assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);\n    }\n\n    @Test\n    public void testCalculateUsersByActivity() {\n        // 准备参数\n        String param = \"2\";\n        // mock 方法\n        Long startUserId = 1L;\n        mockGetStartUserDept(startUserId);\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,\n                startUserId, null, null);\n        // 断言\n        assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);\n    }\n\n    private void mockGetStartUserDept(Long startUserId) {\n        when(adminUserApi.getUser(eq(startUserId))).thenReturn(\n                success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptId(10L))));\n        when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult<DeptRespDTO>>) invocationOnMock -> {\n            Long deptId = invocationOnMock.getArgument(0);\n            return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1)));\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.assertj.core.util.Sets;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.stubbing.Answer;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateStartUserDeptLeaderStrategy strategy;\n\n    @Mock\n    private BpmProcessInstanceService processInstanceService;\n\n    @Mock\n    private AdminUserApi adminUserApi;\n    @Mock\n    private DeptApi deptApi;\n\n    @Test\n    public void testCalculateUsersByTask() {\n        // 准备参数\n        String param = \"2\";\n        // mock 方法（获得流程发起人）\n        Long startUserId = 1L;\n        ProcessInstance processInstance = mock(ProcessInstance.class);\n        DelegateExecution execution = mock(DelegateExecution.class);\n        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);\n        when(processInstance.getStartUserId()).thenReturn(startUserId.toString());\n        // mock 方法（获取发起人的部门负责人）\n        mockGetStartUserDeptLeader(startUserId);\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByTask(execution, param);\n        // 断言\n        assertEquals(Sets.newLinkedHashSet(1001L), userIds);\n    }\n\n    @Test\n    public void testGetStartUserDeptLeader() {\n        // 准备参数\n        String param = \"2\";\n        // mock 方法\n        Long startUserId = 1L;\n        mockGetStartUserDeptLeader(startUserId);\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,\n                startUserId, null, null);\n        // 断言\n        assertEquals(Sets.newLinkedHashSet(1001L), userIds);\n    }\n\n    private void mockGetStartUserDeptLeader(Long startUserId) {\n        when(adminUserApi.getUser(eq(startUserId))).thenReturn(\n                success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptId(10L))));\n        when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult<DeptRespDTO>>) invocationOnMock -> {\n            Long deptId = invocationOnMock.getArgument(0);\n            return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1)));\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport org.assertj.core.util.Sets;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class BpmTaskCandidateStartUserSelectStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateStartUserSelectStrategy strategy;\n\n    @Mock\n    private BpmProcessInstanceService processInstanceService;\n\n    @Test\n    public void testCalculateUsersByTask() {\n        // 准备参数\n        String param = \"2\";\n        // mock 方法（获得流程发起人）\n        ProcessInstance processInstance = mock(ProcessInstance.class);\n        DelegateExecution execution = mock(DelegateExecution.class);\n        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);\n        when(execution.getCurrentActivityId()).thenReturn(\"activity_001\");\n        // mock 方法（FlowableUtils）\n        Map<String, Object> processVariables = new HashMap<>();\n        processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,\n                MapUtil.of(\"activity_001\", ListUtil.of(1L, 2L)));\n        when(processInstance.getProcessVariables()).thenReturn(processVariables);\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByTask(execution, param);\n        // 断言\n        assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds);\n    }\n\n    @Test\n    public void testCalculateUsersByActivity() {\n        // 准备参数\n        String activityId = \"activity_001\";\n        Map<String, Object> processVariables = new HashMap<>();\n        processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,\n                MapUtil.of(\"activity_001\", ListUtil.of(1L, 2L)));\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByActivity(null, activityId, null,\n                null, null, processVariables);\n        // 断言\n        assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;\nimport cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;\nimport org.flowable.bpmn.model.BpmnModel;\nimport org.flowable.bpmn.model.FlowElement;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.*;\n\npublic class BpmTaskCandidateAssignEmptyStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateAssignEmptyStrategy strategy;\n\n    @Mock\n    private BpmProcessDefinitionService processDefinitionService;\n\n    @Test\n    public void testCalculateUsersByTask() {\n        try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class);\n             MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {\n            // 准备参数\n            DelegateExecution execution = mock(DelegateExecution.class);\n            String param = randomString();\n            // mock 方法（execution）\n            String processDefinitionId = randomString();\n            when(execution.getProcessDefinitionId()).thenReturn(processDefinitionId);\n            FlowElement flowElement = mock(FlowElement.class);\n            when(execution.getCurrentFlowElement()).thenReturn(flowElement);\n            // mock 方法（parseAssignEmptyHandlerType）\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement)))\n                    .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType());\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerUserIds(same(flowElement)))\n                    .thenReturn(ListUtil.of(1L, 2L));\n\n            // 调用\n            Set<Long> userIds = strategy.calculateUsersByTask(execution, param);\n            // 断言\n            assertEquals(SetUtils.asSet(1L, 2L), userIds);\n        }\n\n    }\n\n    @Test\n    public void testCalculateUsersByActivity() {\n        try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {\n            // 准备参数\n            String processDefinitionId = randomString();\n            String activityId = randomString();\n            String param = randomString();\n            // mock 方法（getFlowElementById）\n            FlowElement flowElement = mock(FlowElement.class);\n            BpmnModel bpmnModel = mock(BpmnModel.class);\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId)))\n                    .thenReturn(flowElement);\n            // mock 方法（parseAssignEmptyHandlerType）\n            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement)))\n                 .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType());\n            // mock 方法（getProcessDefinitionInfo）\n            BpmProcessDefinitionInfoDO processDefinition = randomPojo(BpmProcessDefinitionInfoDO.class,\n                    o -> o.setManagerUserIds(ListUtil.of(1L, 2L)));\n            when(processDefinitionService.getProcessDefinitionInfo(eq(processDefinitionId))).thenReturn(processDefinition);\n\n            // 调用\n            Set<Long> userIds = strategy.calculateUsersByActivity(bpmnModel, activityId, param,\n                    null, processDefinitionId, null);\n            // 断言\n            assertEquals(SetUtils.asSet(1L, 2L), userIds);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.MockedStatic;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\n@Disabled // TODO 芋艿：临时注释\npublic class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateExpressionStrategy strategy;\n\n    @Test\n    public void testCalculateUsersByTask() {\n        try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {\n            // 准备参数\n            String param = \"1,2\";\n            DelegateExecution execution = mock(DelegateExecution.class);\n            // mock 方法\n            flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(execution), eq(param)))\n                    .thenReturn(asSet(1L, 2L));\n\n            // 调用\n            Set<Long> results = strategy.calculateUsersByTask(execution, param);\n            // 断言\n            assertEquals(asSet(1L, 2L), results);\n        }\n    }\n\n    @Test\n    public void testCalculateUsersByActivity() {\n        try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {\n            // 准备参数\n            String param = \"1,2\";\n            Map<String, Object> processVariables = new HashMap<>();\n            // mock 方法\n            flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(processVariables), eq(param)))\n                    .thenReturn(asSet(1L, 2L));\n\n            // 调用\n            Set<Long> results = strategy.calculateUsersByActivity(null, null, param,\n                    null, null, processVariables);\n            // 断言\n            assertEquals(asSet(1L, 2L), results);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.Arrays;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\n\n@Disabled // TODO 芋艿：临时注释\npublic class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateGroupStrategy strategy;\n\n    @Mock\n    private BpmUserGroupService userGroupService;\n\n    @Test\n    public void testCalculateUsers() {\n        // 准备参数\n        String param = \"1,2\";\n        // mock 方法\n        BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(11L, 12L)));\n        BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(21L, 22L)));\n        when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2));\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByTask(null, param);\n        // 断言\n        assertEquals(asSet(11L, 12L, 21L, 22L), userIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.system.api.dept.PostApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\n\n@Disabled // TODO 芋艿：临时注释\npublic class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidatePostStrategy strategy;\n\n    @Mock\n    private PostApi postApi;\n    @Mock\n    private AdminUserApi adminUserApi;\n\n    @Test\n    public void testCalculateUsers() {\n        // 准备参数\n        String param = \"1,2\";\n        // mock 方法\n        List<AdminUserRespDTO> users = convertList(asSet(11L, 22L),\n                id -> new AdminUserRespDTO().setId(id));\n        when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(success(users));\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByTask(null, param);\n        // 断言\n        assertEquals(asSet(11L, 22L), userIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.system.api.permission.PermissionApi;\nimport cn.iocoder.yudao.module.system.api.permission.RoleApi;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\n\n@Disabled // TODO 芋艿：临时注释\npublic class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateRoleStrategy strategy;\n\n    @Mock\n    private RoleApi roleApi;\n    @Mock\n    private PermissionApi permissionApi;\n\n    @Test\n    public void testCalculateUsers() {\n        // 准备参数\n        String param = \"1,2\";\n        // mock 方法\n        when(permissionApi.getUserRoleIdListByRoleIds(eq(asSet(1L, 2L))))\n            .thenReturn(success(asSet(11L, 22L)));\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByTask(null, param);\n        // 断言\n        assertEquals(asSet(11L, 22L), userIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;\nimport org.assertj.core.util.Sets;\nimport org.flowable.engine.delegate.DelegateExecution;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.Set;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class BpmTaskCandidateStartUserStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateStartUserStrategy strategy;\n\n    @Mock\n    private BpmProcessInstanceService processInstanceService;\n\n    @Test\n    public void testCalculateUsersByTask() {\n        // 准备参数\n        String param = \"2\";\n        // mock 方法（获得流程发起人）\n        Long startUserId = 1L;\n        ProcessInstance processInstance = mock(ProcessInstance.class);\n        DelegateExecution execution = mock(DelegateExecution.class);\n        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);\n        when(processInstance.getStartUserId()).thenReturn(startUserId.toString());\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByTask(execution, param);\n        // 断言\n        assertEquals(Sets.newLinkedHashSet(startUserId), userIds);\n    }\n\n    @Test\n    public void testCalculateUsersByActivity() {\n        // 准备参数\n        Long startUserId = 1L;\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByActivity(null, null, null,\n                startUserId, null, null);\n        // 断言\n        assertEquals(Sets.newLinkedHashSet(startUserId), userIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\n\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n@Disabled // TODO 芋艿：临时注释\npublic class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private BpmTaskCandidateUserStrategy strategy;\n\n    @Test\n    public void test() {\n        // 准备参数\n        String param = \"1,2\";\n\n        // 调用\n        Set<Long> userIds = strategy.calculateUsersByTask(null, param);\n        // 断言\n        assertEquals(asSet(1L, 2L), userIds);\n    }\n\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/service/category/BpmCategoryServiceImplTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.category;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl;\nimport cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link BpmCategoryServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(BpmCategoryServiceImpl.class)\npublic class BpmCategoryServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private BpmCategoryServiceImpl categoryService;\n\n    @MockBean\n    private BpmModelService modelService;\n\n    @Resource\n    private BpmCategoryMapper categoryMapper;\n\n    @Test\n    public void testCreateCategory_success() {\n        // 准备参数\n        BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null)\n                .setStatus(randomCommonStatus());\n\n        // 调用\n        Long categoryId = categoryService.createCategory(createReqVO);\n        // 断言\n        assertNotNull(categoryId);\n        // 校验记录的属性是否正确\n        BpmCategoryDO category = categoryMapper.selectById(categoryId);\n        assertPojoEquals(createReqVO, category, \"id\");\n    }\n\n    @Test\n    public void testUpdateCategory_success() {\n        // mock 数据\n        BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class);\n        categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> {\n            o.setId(dbCategory.getId()); // 设置更新的 ID\n            o.setStatus(randomCommonStatus());\n        });\n\n        // 调用\n        categoryService.updateCategory(updateReqVO);\n        // 校验是否更新正确\n        BpmCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, category);\n    }\n\n    @Test\n    public void testUpdateCategory_notExists() {\n        // 准备参数\n        BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteCategory_success() {\n        // mock 数据\n        BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class);\n        categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbCategory.getId();\n\n        // 调用\n        categoryService.deleteCategory(id);\n        // 校验数据不存在了\n        assertNull(categoryMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteCategory_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);\n    }\n\n    @Test\n    public void testGetCategoryPage() {\n        // mock 数据\n        BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到\n            o.setName(\"芋头\");\n            o.setCode(\"xiaodun\");\n            o.setStatus(CommonStatusEnum.ENABLE.getStatus());\n            o.setCreateTime(buildTime(2023, 2, 2));\n        });\n        categoryMapper.insert(dbCategory);\n        // 测试 name 不匹配\n        categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(\"小盾\")));\n        // 测试 code 不匹配\n        categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode(\"tudou\")));\n        // 测试 status 不匹配\n        categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));\n        // 测试 createTime 不匹配\n        categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2))));\n        // 准备参数\n        BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO();\n        reqVO.setName(\"芋\");\n        reqVO.setCode(\"xiao\");\n        reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());\n        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n\n        // 调用\n        PageResult<BpmCategoryDO> pageResult = categoryService.getCategoryPage(reqVO);\n        // 断言\n        assertEquals(1, pageResult.getTotal());\n        assertEquals(1, pageResult.getList().size());\n        assertPojoEquals(dbCategory, pageResult.getList().get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.hutool.core.util.RandomUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;\nimport cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.FORM_NOT_EXISTS;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link BpmFormServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(BpmFormServiceImpl.class)\npublic class BpmFormServiceTest extends BaseDbUnitTest {\n\n    @Resource\n    private BpmFormServiceImpl formService;\n\n    @Resource\n    private BpmFormMapper formMapper;\n\n    @Test\n    public void testCreateForm_success() {\n        // 准备参数\n        BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {\n            o.setConf(\"{}\");\n            o.setFields(randomFields());\n        });\n\n        // 调用\n        Long formId = formService.createForm(reqVO);\n        // 断言\n        assertNotNull(formId);\n        // 校验记录的属性是否正确\n        BpmFormDO form = formMapper.selectById(formId);\n        assertPojoEquals(reqVO, form);\n    }\n\n    @Test\n    public void testUpdateForm_success() {\n        // mock 数据\n        BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> {\n            o.setConf(\"{}\");\n            o.setFields(randomFields());\n        });\n        formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {\n            o.setId(dbForm.getId()); // 设置更新的 ID\n            o.setConf(\"{'yudao': 'yuanma'}\");\n            o.setFields(randomFields());\n        });\n\n        // 调用\n        formService.updateForm(reqVO);\n        // 校验是否更新正确\n        BpmFormDO form = formMapper.selectById(reqVO.getId()); // 获取最新的\n        assertPojoEquals(reqVO, form);\n    }\n\n    @Test\n    public void testUpdateForm_notExists() {\n        // 准备参数\n        BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {\n            o.setConf(\"{'yudao': 'yuanma'}\");\n            o.setFields(randomFields());\n        });\n\n        // 调用, 并断言异常\n        assertServiceException(() -> formService.updateForm(reqVO), FORM_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteForm_success() {\n        // mock 数据\n        BpmFormDO dbForm = randomPojo(BpmFormDO.class);\n        formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbForm.getId();\n\n        // 调用\n        formService.deleteForm(id);\n        // 校验数据不存在了\n        assertNull(formMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteForm_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> formService.deleteForm(id), FORM_NOT_EXISTS);\n    }\n\n    @Test\n    public void testGetFormPage() {\n        // mock 数据\n        BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { // 等会查询到\n            o.setName(\"芋道源码\");\n        });\n        formMapper.insert(dbForm);\n        // 测试 name 不匹配\n        formMapper.insert(cloneIgnoreId(dbForm, o -> o.setName(\"源码\")));\n        // 准备参数\n        BpmFormPageReqVO reqVO = new BpmFormPageReqVO();\n        reqVO.setName(\"芋道\");\n\n        // 调用\n        PageResult<BpmFormDO> pageResult = formService.getFormPage(reqVO);\n        // 断言\n        assertEquals(1, pageResult.getTotal());\n        assertEquals(1, pageResult.getList().size());\n        assertPojoEquals(dbForm, pageResult.getList().get(0));\n    }\n\n    private List<String> randomFields() {\n        int size = RandomUtil.randomInt(1, 3);\n        return Stream.iterate(0, i -> i).limit(size)\n                .map(i -> JsonUtils.toJsonString(randomPojo(BpmFormFieldRespDTO.class)))\n                .collect(Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java",
    "content": "package cn.iocoder.yudao.module.bpm.service.definition;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.framework.test.core.util.AssertUtils;\nimport cn.iocoder.yudao.framework.test.core.util.RandomUtils;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO;\nimport cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;\nimport cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;\nimport cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmUserGroupMapper;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;\nimport static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS;\n\n/**\n * {@link BpmUserGroupServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(BpmUserGroupServiceImpl.class)\npublic class BpmUserGroupServiceTest extends BaseDbUnitTest {\n\n    @Resource\n    private BpmUserGroupServiceImpl userGroupService;\n\n    @Resource\n    private BpmUserGroupMapper userGroupMapper;\n\n    @Test\n    public void testCreateUserGroup_success() {\n        // 准备参数\n        BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class);\n\n        // 调用\n        Long userGroupId = userGroupService.createUserGroup(reqVO);\n        // 断言\n        Assertions.assertNotNull(userGroupId);\n        // 校验记录的属性是否正确\n        BpmUserGroupDO userGroup = userGroupMapper.selectById(userGroupId);\n        AssertUtils.assertPojoEquals(reqVO, userGroup);\n    }\n\n    @Test\n    public void testUpdateUserGroup_success() {\n        // mock 数据\n        BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class);\n        userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class, o -> {\n            o.setId(dbUserGroup.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        userGroupService.updateUserGroup(reqVO);\n        // 校验是否更新正确\n        BpmUserGroupDO userGroup = userGroupMapper.selectById(reqVO.getId()); // 获取最新的\n        AssertUtils.assertPojoEquals(reqVO, userGroup);\n    }\n\n    @Test\n    public void testUpdateUserGroup_notExists() {\n        // 准备参数\n        BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class);\n\n        // 调用, 并断言异常\n        AssertUtils.assertServiceException(() -> userGroupService.updateUserGroup(reqVO), USER_GROUP_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteUserGroup_success() {\n        // mock 数据\n        BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class);\n        userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbUserGroup.getId();\n\n        // 调用\n        userGroupService.deleteUserGroup(id);\n       // 校验数据不存在了\n       Assertions.assertNull(userGroupMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteUserGroup_notExists() {\n        // 准备参数\n        Long id = RandomUtils.randomLongId();\n\n        // 调用, 并断言异常\n        AssertUtils.assertServiceException(() -> userGroupService.deleteUserGroup(id), USER_GROUP_NOT_EXISTS);\n    }\n\n    @Test\n    public void testGetUserGroupPage() {\n       // mock 数据\n       BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class, o -> { // 等会查询到\n           o.setName(\"芋道源码\");\n           o.setStatus(CommonStatusEnum.ENABLE.getStatus());\n           o.setCreateTime(buildTime(2021, 11, 11));\n       });\n       userGroupMapper.insert(dbUserGroup);\n       // 测试 name 不匹配\n       userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName(\"芋道\")));\n       // 测试 status 不匹配\n       userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));\n       // 测试 createTime 不匹配\n       userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setCreateTime(buildTime(2021, 12, 12))));\n       // 准备参数\n       BpmUserGroupPageReqVO reqVO = new BpmUserGroupPageReqVO();\n       reqVO.setName(\"源码\");\n       reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());\n       reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021, 11, 10),buildTime(2021, 11, 12)}));\n\n       // 调用\n       PageResult<BpmUserGroupDO> pageResult = userGroupService.getUserGroupPage(reqVO);\n       // 断言\n       Assertions.assertEquals(1, pageResult.getTotal());\n       Assertions.assertEquals(1, pageResult.getList().size());\n       AssertUtils.assertPojoEquals(dbUserGroup, pageResult.getList().get(0));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/resources/application-unit-test.yaml",
    "content": "spring:\n  main:\n    lazy-initialization: true # 开启懒加载，加快速度\n    banner-mode: off # 单元测试，禁用 Banner\n\n--- #################### 数据库相关配置 ####################\n\nspring:\n  # 数据源配置项\n  datasource:\n    name: ruoyi-vue-pro\n    url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式；DATABASE_TO_UPPER 配置表和字段使用小写\n    driver-class-name: org.h2.Driver\n    username: sa\n    password:\n    druid:\n      async-init: true # 单元测试，异步初始化 Druid 连接池，提升启动速度\n      initial-size: 1 # 单元测试，配置为 1，提升启动速度\n  sql:\n    init:\n      schema-locations: classpath:/sql/create_tables.sql\n\nmybatis-plus:\n  lazy-initialization: true # 单元测试，设置 MyBatis Mapper 延迟加载，加速每个单元测试\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  global-config:\n    db-config:\n      id-type: AUTO # H2 主键递增\n\n--- #################### 定时任务相关配置 ####################\n\n--- #################### 配置中心相关配置 ####################\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项（单元测试，禁用 Lock4j）\n\n--- #################### 监控相关配置 ####################\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  info:\n    base-package: cn.iocoder.yudao.module.bpm\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/resources/logback.xml",
    "content": "<configuration>\n    <!-- 引用 Spring Boot 的 logback 基础配置 -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n</configuration>\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/resources/sql/clean.sql",
    "content": "DELETE FROM \"bpm_form\";\nDELETE FROM \"bpm_user_group\";\nDELETE FROM \"bpm_category\";\n"
  },
  {
    "path": "yudao-module-bpm/yudao-module-bpm-server/src/test/resources/sql/create_tables.sql",
    "content": "CREATE TABLE IF NOT EXISTS \"bpm_user_group\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(63) NOT NULL,\n    \"description\" varchar(255) NOT NULL,\n    \"status\" tinyint NOT NULL,\n    \"user_ids\" varchar(255) NOT NULL,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '用户组';\n\nCREATE TABLE IF NOT EXISTS \"bpm_category\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(63) NOT NULL,\n    \"code\" varchar(63) NOT NULL,\n    \"description\" varchar(255) NOT NULL,\n    \"status\" tinyint NOT NULL,\n    \"sort\" int NOT NULL,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '分类';\n\nCREATE TABLE IF NOT EXISTS \"bpm_form\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(63) NOT NULL,\n    \"status\" tinyint NOT NULL,\n    \"fields\" varchar(255) NOT NULL,\n    \"conf\" varchar(255) NOT NULL,\n    \"remark\" varchar(255),\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '动态表单';\n"
  },
  {
    "path": "yudao-module-crm/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modules>\n        <module>yudao-module-crm-api</module>\n        <module>yudao-module-crm-server</module>\n    </modules>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-crm</artifactId>\n    <packaging>pom</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        crm 包下，客户关系管理（Customer Relationship Management）。\n        例如说：客户、联系人、商机、合同、回款等等\n        商业智能 BI 模块，包括：报表、图表、数据大屏等等\n    </description>\n\n</project>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-crm</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-crm-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        crm 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java",
    "content": "/**\n * crm API 包，定义暴露给其它模块的 API\n */\npackage cn.iocoder.yudao.module.crm.api;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ApiConstants.java",
    "content": "package cn.iocoder.yudao.module.crm.enums;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\n\n/**\n * API 相关的枚举\n *\n * @author 芋道源码\n */\npublic class ApiConstants {\n\n    /**\n     * 服务名\n     *\n     * 注意，需要保证和 spring.application.name 保持一致\n     */\n    public static final String NAME = \"crm-server\";\n\n    public static final String PREFIX = RpcConstants.RPC_API_PREFIX + \"/crm\";\n\n    public static final String VERSION = \"1.0.0\";\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.crm.enums;\n\n/**\n * CRM 字典类型的枚举类\n *\n * @author 芋道源码\n */\npublic interface DictTypeConstants {\n\n    String CRM_CUSTOMER_INDUSTRY = \"crm_customer_industry\"; // CRM 客户所属行业\n    String CRM_CUSTOMER_LEVEL = \"crm_customer_level\"; // CRM 客户等级\n    String CRM_CUSTOMER_SOURCE = \"crm_customer_source\"; // CRM 客户来源\n    String CRM_AUDIT_STATUS = \"crm_audit_status\"; // CRM 审批状态\n    String CRM_PRODUCT_UNIT = \"crm_product_unit\"; // CRM 产品单位\n    String CRM_PRODUCT_STATUS = \"crm_product_status\"; // CRM 产品状态\n    String CRM_FOLLOW_UP_TYPE = \"crm_follow_up_type\"; // CRM 跟进方式\n    String CRM_RECEIVABLE_RETURN_TYPE = \"crm_receivable_return_type\"; // CRM 回款方式\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.crm.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * CRM 错误码枚举类\n * <p>\n * crm 系统，使用 1-020-000-000 段\n */\npublic interface ErrorCodeConstants {\n\n    // ========== 合同管理 1-020-000-000 ==========\n    ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, \"合同不存在\");\n    ErrorCode CONTRACT_UPDATE_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_001, \"合同更新失败，原因：合同不是草稿状态\");\n    ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, \"合同提交审核失败，原因：合同没处在未提交状态\");\n    ErrorCode CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_000_003, \"更新合同审核状态失败，原因：合同不是审核中状态\");\n    ErrorCode CONTRACT_NO_EXISTS = new ErrorCode(1_020_000_004, \"生成合同序列号重复，请重试\");\n    ErrorCode CONTRACT_DELETE_FAIL = new ErrorCode(1_020_000_005, \"删除合同失败，原因：有被回款所使用\");\n\n    // ========== 线索管理 1-020-001-000 ==========\n    ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, \"线索不存在\");\n    ErrorCode CLUE_TRANSFORM_FAIL_ALREADY = new ErrorCode(1_020_001_001, \"线索已经转化过了，请勿重复转化\");\n\n    // ========== 商机管理 1-020-002-000 ==========\n    ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, \"商机不存在\");\n    ErrorCode BUSINESS_DELETE_FAIL_CONTRACT_EXISTS = new ErrorCode(1_020_002_001, \"商机已关联合同，不能删除\");\n    ErrorCode BUSINESS_UPDATE_STATUS_FAIL_END_STATUS = new ErrorCode(1_020_002_002, \"更新商机状态失败，原因：已经是结束状态\");\n    ErrorCode BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS = new ErrorCode(1_020_002_003, \"更新商机状态失败，原因：已经是该状态\");\n\n    // ========== 联系人管理 1-020-003-000 ==========\n    ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, \"联系人不存在\");\n    ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, \"联系人已关联合同，不能删除\");\n    ErrorCode CONTACT_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_003_003, \"更新联系人负责人失败\");\n\n    // ========== 回款 1-020-004-000 ==========\n    ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, \"回款不存在\");\n    ErrorCode RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_004_001, \"更新回款失败，原因：禁止编辑\");\n    ErrorCode RECEIVABLE_DELETE_FAIL = new ErrorCode(1_020_004_002, \"删除回款失败，原因： 被回款计划所使用，不允许删除\");\n    ErrorCode RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_004_003, \"回款提交审核失败，原因：回款没处在未提交状态\");\n    ErrorCode RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_004_004, \"更新回款审核状态失败，原因：回款不是审核中状态\");\n    ErrorCode RECEIVABLE_NO_EXISTS = new ErrorCode(1_020_004_005, \"生成回款序列号重复，请重试\");\n    ErrorCode RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_004_006, \"创建回款失败，原因：合同不是审核通过状态\");\n    ErrorCode RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_020_004_007, \"创建回款失败，原因：回款金额超出合同金额，目前剩余可退：{} 元\");\n    ErrorCode RECEIVABLE_DELETE_FAIL_IS_APPROVE = new ErrorCode(1_020_004_008, \"删除回款失败，原因：回款审批已通过\");\n\n    // ========== 回款计划 1-020-005-000 ==========\n    ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, \"回款计划不存在\");\n    ErrorCode RECEIVABLE_PLAN_UPDATE_FAIL = new ErrorCode(1_020_006_000, \"更想回款计划失败，原因：已经有对应的还款\");\n    ErrorCode RECEIVABLE_PLAN_EXISTS_RECEIVABLE = new ErrorCode(1_020_006_001, \"回款计划已经有对应的回款，不能使用\");\n\n    // ========== 客户管理 1_020_006_000 ==========\n    ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, \"客户不存在\");\n    ErrorCode CUSTOMER_OWNER_EXISTS = new ErrorCode(1_020_006_001, \"客户【{}】已存在所属负责人\");\n    ErrorCode CUSTOMER_LOCKED = new ErrorCode(1_020_006_002, \"客户【{}】状态已锁定\");\n    ErrorCode CUSTOMER_ALREADY_DEAL = new ErrorCode(1_020_006_003, \"客户已交易\");\n    ErrorCode CUSTOMER_IN_POOL = new ErrorCode(1_020_006_004, \"客户【{}】放入公海失败，原因：已经是公海客户\");\n    ErrorCode CUSTOMER_LOCKED_PUT_POOL_FAIL = new ErrorCode(1_020_006_005, \"客户【{}】放入公海失败，原因：客户已锁定\");\n    ErrorCode CUSTOMER_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_006_006, \"更新客户【{}】负责人失败, 原因：系统异常\");\n    ErrorCode CUSTOMER_LOCK_FAIL_IS_LOCK = new ErrorCode(1_020_006_007, \"锁定客户失败，它已经处于锁定状态\");\n    ErrorCode CUSTOMER_UNLOCK_FAIL_IS_UNLOCK = new ErrorCode(1_020_006_008, \"解锁客户失败，它已经处于未锁定状态\");\n    ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, \"锁定客户失败，超出锁定规则上限\");\n    ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, \"操作失败，超出客户数拥有上限\");\n    ErrorCode CUSTOMER_DELETE_FAIL_HAVE_REFERENCE = new ErrorCode(1_020_006_011, \"删除客户失败，有关联{}\");\n    ErrorCode CUSTOMER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_020_006_012, \"导入客户数据不能为空！\");\n    ErrorCode CUSTOMER_CREATE_NAME_NOT_NULL = new ErrorCode(1_020_006_013, \"客户名称不能为空！\");\n    ErrorCode CUSTOMER_NAME_EXISTS = new ErrorCode(1_020_006_014, \"已存在名为【{}】的客户！\");\n    ErrorCode CUSTOMER_UPDATE_DEAL_STATUS_FAIL = new ErrorCode(1_020_006_015, \"更新客户的成交状态失败，原因：已经是该状态，无需更新\");\n\n    // ========== 权限管理 1_020_007_000 ==========\n    ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, \"数据权限不存在\");\n    ErrorCode CRM_PERMISSION_DENIED = new ErrorCode(1_020_007_001, \"{}操作失败，原因：没有权限\");\n    ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_007_003, \"{}操作失败，原因：转移对象已经是该负责人\");\n    ErrorCode CRM_PERMISSION_DELETE_FAIL = new ErrorCode(1_020_007_004, \"删除数据权限失败，原因：批量删除权限的时候，只能属于同一个 bizId 下\");\n    ErrorCode CRM_PERMISSION_DELETE_DENIED = new ErrorCode(1_020_007_006, \"删除数据权限失败，原因：没有权限\");\n    ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, \"删除数据权限失败，原因：不能删除负责人\");\n    ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, \"创建数据权限失败，原因：所加用户已有权限\");\n    ErrorCode CRM_PERMISSION_CREATE_FAIL_EXISTS = new ErrorCode(1_020_007_009, \"同时添加数据权限失败，原因：用户【{}】已有模块【{}】数据【{}】的【{}】权限\");\n\n    // ========== 产品 1_020_008_000 ==========\n    ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, \"产品不存在\");\n    ErrorCode PRODUCT_NO_EXISTS = new ErrorCode(1_020_008_001, \"产品编号已存在\");\n    ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_020_008_002, \"产品【{}】已禁用\");\n\n    // ========== 产品分类 1_020_009_000 ==========\n    ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_009_000, \"产品分类不存在\");\n    ErrorCode PRODUCT_CATEGORY_EXISTS = new ErrorCode(1_020_009_001, \"产品分类已存在\");\n    ErrorCode PRODUCT_CATEGORY_USED = new ErrorCode(1_020_009_002, \"产品分类已关联产品\");\n    ErrorCode PRODUCT_CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_020_009_003, \"父分类不存在\");\n    ErrorCode PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_020_009_004, \"父分类不能是二级分类\");\n    ErrorCode PRODUCT_CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_020_009_005, \"存在子分类，无法删除\");\n\n    // ========== 商机状态 1_020_010_000 ==========\n    ErrorCode BUSINESS_STATUS_TYPE_NOT_EXISTS = new ErrorCode(1_020_010_000, \"商机状态组不存在\");\n    ErrorCode BUSINESS_STATUS_TYPE_NAME_EXISTS = new ErrorCode(1_020_010_001, \"商机状态组的名称已存在\");\n    ErrorCode BUSINESS_STATUS_UPDATE_FAIL_USED = new ErrorCode(1_020_010_002, \"已经被使用的商机状态组，无法进行更新\");\n    ErrorCode BUSINESS_STATUS_DELETE_FAIL_USED = new ErrorCode(1_020_010_002, \"已经被使用的商机状态组，无法进行删除\");\n    ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_010_003, \"商机状态不存在\");\n\n    // ========== 客户公海规则设置 1_020_012_000 ==========\n    ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_001, \"客户限制配置不存在\");\n\n    // ========== 跟进记录 1_020_013_000 ==========\n    ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, \"跟进记录不存在\");\n    ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, \"删除跟进记录失败，原因：没有权限\");\n\n    // ========== 数据统计 1_020_014_000 ==========\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java",
    "content": "package cn.iocoder.yudao.module.crm.enums;\n\n/**\n * CRM 操作日志枚举\n * 目的：统一管理，也减少 Service 里各种“复杂”字符串\n *\n * @author HUIHUI\n */\npublic interface LogRecordConstants {\n\n    // ======================= CRM_CLUE 线索 =======================\n\n    String CRM_CLUE_TYPE = \"CRM 线索\";\n    String CRM_CLUE_CREATE_SUB_TYPE = \"创建线索\";\n    String CRM_CLUE_CREATE_SUCCESS = \"创建了线索{{#clue.name}}\";\n    String CRM_CLUE_UPDATE_SUB_TYPE = \"更新线索\";\n    String CRM_CLUE_UPDATE_SUCCESS = \"更新了线索【{{#clueName}}】: {_DIFF{#updateReqVO}}\";\n    String CRM_CLUE_DELETE_SUB_TYPE = \"删除线索\";\n    String CRM_CLUE_DELETE_SUCCESS = \"删除了线索【{{#clueName}}】\";\n    String CRM_CLUE_TRANSFER_SUB_TYPE = \"转移线索\";\n    String CRM_CLUE_TRANSFER_SUCCESS = \"将线索【{{#clue.name}}】的负责人从【{getAdminUserById{#clue.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】\";\n    String CRM_CLUE_TRANSLATE_SUB_TYPE = \"线索转化为客户\";\n    String CRM_CLUE_TRANSLATE_SUCCESS = \"将线索【{{#clueName}}】转化为客户\";\n    String CRM_CLUE_FOLLOW_UP_SUB_TYPE = \"线索跟进\";\n    String CRM_CLUE_FOLLOW_UP_SUCCESS = \"线索跟进【{{#clueName}}】\";\n\n    // ======================= CRM_CUSTOMER 客户 =======================\n\n    String CRM_CUSTOMER_TYPE = \"CRM 客户\";\n    String CRM_CUSTOMER_CREATE_SUB_TYPE = \"创建客户\";\n    String CRM_CUSTOMER_CREATE_SUCCESS = \"创建了客户{{#customer.name}}\";\n    String CRM_CUSTOMER_UPDATE_SUB_TYPE = \"更新客户\";\n    String CRM_CUSTOMER_UPDATE_SUCCESS = \"更新了客户【{{#customerName}}】: {_DIFF{#updateReqVO}}\";\n    String CRM_CUSTOMER_DELETE_SUB_TYPE = \"删除客户\";\n    String CRM_CUSTOMER_DELETE_SUCCESS = \"删除了客户【{{#customerName}}】\";\n    String CRM_CUSTOMER_TRANSFER_SUB_TYPE = \"转移客户\";\n    String CRM_CUSTOMER_TRANSFER_SUCCESS = \"将客户【{{#customer.name}}】的负责人从【{getAdminUserById{#customer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】\";\n    String CRM_CUSTOMER_LOCK_SUB_TYPE = \"{{#customer.lockStatus ? '解锁客户' : '锁定客户'}}\";\n    String CRM_CUSTOMER_LOCK_SUCCESS = \"{{#customer.lockStatus ? '将客户【' + #customer.name + '】解锁' : '将客户【' + #customer.name + '】锁定'}}\";\n    String CRM_CUSTOMER_POOL_SUB_TYPE = \"客户放入公海\";\n    String CRM_CUSTOMER_POOL_SUCCESS = \"将客户【{{#customerName}}】放入了公海\";\n    String CRM_CUSTOMER_RECEIVE_SUB_TYPE = \"{{#ownerUserName != null ? '分配客户' : '领取客户'}}\";\n    String CRM_CUSTOMER_RECEIVE_SUCCESS = \"{{#ownerUserName != null ? '将客户【' + #customer.name + '】分配给【' + #ownerUserName + '】' : '领取客户【' + #customer.name + '】'}}\";\n    String CRM_CUSTOMER_IMPORT_SUB_TYPE = \"{{#isUpdate ? '导入并更新客户' : '导入客户'}}\";\n    String CRM_CUSTOMER_IMPORT_SUCCESS = \"{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}\";\n    String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE = \"更新客户成交状态\";\n    String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS = \"更新了客户【{{#customerName}}】的成交状态为【{{#dealStatus ? '已成交' : '未成交'}}】\";\n    String CRM_CUSTOMER_FOLLOW_UP_SUB_TYPE = \"客户跟进\";\n    String CRM_CUSTOMER_FOLLOW_UP_SUCCESS = \"客户跟进【{{#customerName}}】\";\n\n    // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 =======================\n\n    String CRM_CUSTOMER_LIMIT_CONFIG_TYPE = \"CRM 客户限制配置\";\n    String CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUB_TYPE = \"创建客户限制配置\";\n    String CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUCCESS = \"创建了【{{#limitType}}】类型的客户限制配置\";\n    String CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUB_TYPE = \"更新客户限制配置\";\n    String CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUCCESS = \"更新了客户限制配置: {_DIFF{#updateReqVO}}\";\n    String CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUB_TYPE = \"删除客户限制配置\";\n    String CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUCCESS = \"删除了【{{#limitType}}】类型的客户限制配置\";\n\n    // ======================= CRM_CUSTOMER_POOL_CONFIG 客户公海规则 =======================\n\n    String CRM_CUSTOMER_POOL_CONFIG_TYPE = \"CRM 客户公海规则\";\n    String CRM_CUSTOMER_POOL_CONFIG_SUB_TYPE = \"{{#isPoolConfigUpdate ? '更新客户公海规则' : '创建客户公海规则'}}\";\n    String CRM_CUSTOMER_POOL_CONFIG_SUCCESS = \"{{#isPoolConfigUpdate ? '更新了客户公海规则' : '创建了客户公海规则'}}\";\n\n    // ======================= CRM_CONTACT 联系人 =======================\n\n    String CRM_CONTACT_TYPE = \"CRM 联系人\";\n    String CRM_CONTACT_CREATE_SUB_TYPE = \"创建联系人\";\n    String CRM_CONTACT_CREATE_SUCCESS = \"创建了联系人{{#contact.name}}\";\n    String CRM_CONTACT_UPDATE_SUB_TYPE = \"更新联系人\";\n    String CRM_CONTACT_UPDATE_SUCCESS = \"更新了联系人【{{#contactName}}】: {_DIFF{#updateReqVO}}\";\n    String CRM_CONTACT_DELETE_SUB_TYPE = \"删除联系人\";\n    String CRM_CONTACT_DELETE_SUCCESS = \"删除了联系人【{{#contactName}}】\";\n    String CRM_CONTACT_TRANSFER_SUB_TYPE = \"转移联系人\";\n    String CRM_CONTACT_TRANSFER_SUCCESS = \"将联系人【{{#contact.name}}】的负责人从【{getAdminUserById{#contact.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】\";\n    String CRM_CONTACT_FOLLOW_UP_SUB_TYPE = \"联系人跟进\";\n    String CRM_CONTACT_FOLLOW_UP_SUCCESS = \"联系人跟进【{{#contactName}}】\";\n    String CRM_CONTACT_UPDATE_OWNER_USER_SUB_TYPE = \"更新联系人负责人\";\n    String CRM_CONTACT_UPDATE_OWNER_USER_SUCCESS = \"将联系人【{{#contact.name}}】的负责人从【{getAdminUserById{#contact.ownerUserId}}】变更为了【{getAdminUserById{#ownerUserId}}】\";\n\n    // ======================= CRM_BUSINESS 商机 =======================\n\n    String CRM_BUSINESS_TYPE = \"CRM 商机\";\n    String CRM_BUSINESS_CREATE_SUB_TYPE = \"创建商机\";\n    String CRM_BUSINESS_CREATE_SUCCESS = \"创建了商机{{#business.name}}\";\n    String CRM_BUSINESS_UPDATE_SUB_TYPE = \"更新商机\";\n    String CRM_BUSINESS_UPDATE_SUCCESS = \"更新了商机【{{#businessName}}】: {_DIFF{#updateReqVO}}\";\n    String CRM_BUSINESS_DELETE_SUB_TYPE = \"删除商机\";\n    String CRM_BUSINESS_DELETE_SUCCESS = \"删除了商机【{{#businessName}}】\";\n    String CRM_BUSINESS_TRANSFER_SUB_TYPE = \"转移商机\";\n    String CRM_BUSINESS_TRANSFER_SUCCESS = \"将商机【{{#business.name}}】的负责人从【{getAdminUserById{#business.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】\";\n    String CRM_BUSINESS_FOLLOW_UP_SUB_TYPE = \"商机跟进\";\n    String CRM_BUSINESS_FOLLOW_UP_SUCCESS = \"商机跟进【{{#businessName}}】\";\n    String CRM_BUSINESS_UPDATE_STATUS_SUB_TYPE = \"更新商机状态\";\n    String CRM_BUSINESS_UPDATE_STATUS_SUCCESS = \"更新了商机【{{#businessName}}】的状态从【{{#oldStatusName}}】变更为了【{{#newStatusName}}】\";\n\n    // ======================= CRM_CONTRACT_CONFIG 合同配置 =======================\n\n    String CRM_CONTRACT_CONFIG_TYPE = \"CRM 合同配置\";\n    String CRM_CONTRACT_CONFIG_SUB_TYPE = \"{{#isPoolConfigUpdate ? '更新合同配置' : '创建合同配置'}}\";\n    String CRM_CONTRACT_CONFIG_SUCCESS = \"{{#isPoolConfigUpdate ? '更新了合同配置' : '创建了合同配置'}}\";\n\n    // ======================= CRM_CONTRACT 合同 =======================\n\n    String CRM_CONTRACT_TYPE = \"CRM 合同\";\n    String CRM_CONTRACT_CREATE_SUB_TYPE = \"创建合同\";\n    String CRM_CONTRACT_CREATE_SUCCESS = \"创建了合同{{#contract.name}}\";\n    String CRM_CONTRACT_UPDATE_SUB_TYPE = \"更新合同\";\n    String CRM_CONTRACT_UPDATE_SUCCESS = \"更新了合同【{{#contractName}}】: {_DIFF{#updateReqVO}}\";\n    String CRM_CONTRACT_DELETE_SUB_TYPE = \"删除合同\";\n    String CRM_CONTRACT_DELETE_SUCCESS = \"删除了合同【{{#contractName}}】\";\n    String CRM_CONTRACT_TRANSFER_SUB_TYPE = \"转移合同\";\n    String CRM_CONTRACT_TRANSFER_SUCCESS = \"将合同【{{#contract.name}}】的负责人从【{getAdminUserById{#contract.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】\";\n    String CRM_CONTRACT_SUBMIT_SUB_TYPE = \"提交合同审批\";\n    String CRM_CONTRACT_SUBMIT_SUCCESS = \"提交合同【{{#contractName}}】审批成功\";\n    String CRM_CONTRACT_FOLLOW_UP_SUB_TYPE = \"合同跟进\";\n    String CRM_CONTRACT_FOLLOW_UP_SUCCESS = \"合同跟进【{{#contractName}}】\";\n\n    // ======================= CRM_PRODUCT 产品 =======================\n\n    String CRM_PRODUCT_TYPE = \"CRM 产品\";\n    String CRM_PRODUCT_CREATE_SUB_TYPE = \"创建产品\";\n    String CRM_PRODUCT_CREATE_SUCCESS = \"创建了产品【{{#createReqVO.name}}】\";\n    String CRM_PRODUCT_UPDATE_SUB_TYPE = \"更新产品\";\n    String CRM_PRODUCT_UPDATE_SUCCESS = \"更新了产品【{{#updateReqVO.name}}】: {_DIFF{#updateReqVO}}\";\n    String CRM_PRODUCT_DELETE_SUB_TYPE = \"删除产品\";\n    String CRM_PRODUCT_DELETE_SUCCESS = \"删除了产品【{{#product.name}}】\";\n\n    // ======================= CRM_PRODUCT_CATEGORY 产品分类 =======================\n\n    String CRM_PRODUCT_CATEGORY_TYPE = \"CRM 产品分类\";\n    String CRM_PRODUCT_CATEGORY_CREATE_SUB_TYPE = \"创建产品分类\";\n    String CRM_PRODUCT_CATEGORY_CREATE_SUCCESS = \"创建了产品分类【{{#createReqVO.name}}】\";\n    String CRM_PRODUCT_CATEGORY_UPDATE_SUB_TYPE = \"更新产品分类\";\n    String CRM_PRODUCT_CATEGORY_UPDATE_SUCCESS = \"更新了产品分类【{{#updateReqVO.name}}】: {_DIFF{#updateReqVO}}\";\n    String CRM_PRODUCT_CATEGORY_DELETE_SUB_TYPE = \"删除产品分类\";\n    String CRM_PRODUCT_CATEGORY_DELETE_SUCCESS = \"删除了产品分类【{{#productCategory.name}}】\";\n\n    // ======================= CRM_RECEIVABLE 回款 =======================\n\n    String CRM_RECEIVABLE_TYPE = \"CRM 回款\";\n    String CRM_RECEIVABLE_CREATE_SUB_TYPE = \"创建回款\";\n    String CRM_RECEIVABLE_CREATE_SUCCESS = \"创建了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款\";\n    String CRM_RECEIVABLE_UPDATE_SUB_TYPE = \"更新回款\";\n    String CRM_RECEIVABLE_UPDATE_SUCCESS = \"更新了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款: {_DIFF{#updateReqVO}}\";\n    String CRM_RECEIVABLE_DELETE_SUB_TYPE = \"删除回款\";\n    String CRM_RECEIVABLE_DELETE_SUCCESS = \"删除了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款\";\n    String CRM_RECEIVABLE_SUBMIT_SUB_TYPE = \"提交回款审批\";\n    String CRM_RECEIVABLE_SUBMIT_SUCCESS = \"提交编号为【{{#receivableNo}}】的回款审批成功\";\n\n    // ======================= CRM_RECEIVABLE_PLAN 回款计划 =======================\n\n    String CRM_RECEIVABLE_PLAN_TYPE = \"CRM 回款计划\";\n    String CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE = \"创建回款计划\";\n    String CRM_RECEIVABLE_PLAN_CREATE_SUCCESS = \"创建了合同【{getContractById{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划\";\n    String CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE = \"更新回款计划\";\n    String CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS = \"更新了合同【{getContractById{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划: {_DIFF{#updateReqVO}}\";\n    String CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE = \"删除回款计划\";\n    String CRM_RECEIVABLE_PLAN_DELETE_SUCCESS = \"删除了合同【{getContractById{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划\";\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBusinessEndStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.crm.enums.business;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * 商机的结束状态枚举\n *\n * @author lzxhqs\n */\n@RequiredArgsConstructor\n@Getter\npublic enum CrmBusinessEndStatusEnum implements ArrayValuable<Integer> {\n\n    WIN(1, \"赢单\"),\n    LOSE(2, \"输单\"),\n    INVALID(3, \"无效\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmBusinessEndStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 场景类型\n     */\n    private final Integer status;\n    /**\n     * 场景名称\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static CrmBusinessEndStatusEnum fromStatus(Integer status) {\n        return Arrays.stream(values())\n                .filter(value -> value.getStatus().equals(status))\n                .findFirst()\n                .orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.crm.enums.common;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * CRM 的审批状态\n *\n * @author 赤焰\n */\n@RequiredArgsConstructor\n@Getter\npublic enum CrmAuditStatusEnum implements ArrayValuable<Integer> {\n\n    DRAFT(0, \"未提交\"),\n    PROCESS(10, \"审批中\"),\n    APPROVE(20, \"审核通过\"),\n\tREJECT(30, \"审核不通过\"),\n    CANCEL(40, \"已取消\");\n\n    private final Integer status;\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmAuditStatusEnum::getStatus).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.crm.enums.common;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * CRM 业务类型枚举\n *\n * @author HUIHUI\n */\n@RequiredArgsConstructor\n@Getter\npublic enum CrmBizTypeEnum implements ArrayValuable<Integer> {\n\n    CRM_CLUE(1, \"线索\"),\n    CRM_CUSTOMER(2, \"客户\"),\n    CRM_CONTACT(3, \"联系人\"),\n    CRM_BUSINESS(4, \"商机\"),\n    CRM_CONTRACT(5, \"合同\"),\n    CRM_PRODUCT(6, \"产品\"),\n    CRM_RECEIVABLE(7, \"回款\"),\n    CRM_RECEIVABLE_PLAN(8, \"回款计划\")\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmBizTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 名称\n     */\n    private final String name;\n\n    public static String getNameByType(Integer type) {\n        CrmBizTypeEnum typeEnum = CollUtil.findOne(CollUtil.newArrayList(CrmBizTypeEnum.values()),\n                item -> ObjUtil.equal(item.type, type));\n        return typeEnum == null ? null : typeEnum.getName();\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.crm.enums.common;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * CRM 列表检索场景\n *\n * @author HUIHUI\n */\n@Getter\n@AllArgsConstructor\npublic enum CrmSceneTypeEnum implements ArrayValuable<Integer> {\n\n    OWNER(1, \"我负责的\"),\n    INVOLVED(2, \"我参与的\"),\n    SUBORDINATE(3, \"下属负责的\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmSceneTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 场景类型\n     */\n    private final Integer type;\n    /**\n     * 场景名称\n     */\n    private final String name;\n\n    public static boolean isOwner(Integer type) {\n        return ObjUtil.equal(OWNER.getType(), type);\n    }\n\n    public static boolean isInvolved(Integer type) {\n        return ObjUtil.equal(INVOLVED.getType(), type);\n    }\n\n    public static boolean isSubordinate(Integer type) {\n        return ObjUtil.equal(SUBORDINATE.getType(), type);\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java",
    "content": "package cn.iocoder.yudao.module.crm.enums.customer;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * CRM 客户等级\n *\n * @author Wanwan\n */\n@Getter\n@AllArgsConstructor\npublic enum CrmCustomerLevelEnum implements ArrayValuable<Integer> {\n\n    IMPORTANT(1, \"A（重点客户）\"),\n    GENERAL(2, \"B（普通客户）\"),\n    LOW_PRIORITY(3, \"C（非优先客户）\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmCustomerLevelEnum::getLevel).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer level;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.crm.enums.customer;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * CRM 客户限制配置规则类型\n *\n * @author Wanwan\n */\n@Getter\n@AllArgsConstructor\npublic enum CrmCustomerLimitConfigTypeEnum implements ArrayValuable<Integer> {\n\n    /**\n     * 拥有客户数限制\n     */\n    CUSTOMER_OWNER_LIMIT(1, \"拥有客户数限制\"),\n    /**\n     * 锁定客户数限制\n     */\n    CUSTOMER_LOCK_LIMIT(2, \"锁定客户数限制\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmCustomerLimitConfigTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer type;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    public static String getNameByType(Integer type) {\n        CrmCustomerLimitConfigTypeEnum typeEnum = CollUtil.findOne(CollUtil.newArrayList(CrmCustomerLimitConfigTypeEnum.values()),\n                item -> ObjUtil.equal(item.type, type));\n        return typeEnum == null ? null : typeEnum.getName();\n    }\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java",
    "content": "package cn.iocoder.yudao.module.crm.enums.permission;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * CRM 数据权限级别枚举\n *\n * OWNER > WRITE > READ\n *\n * @author HUIHUI\n */\n@Getter\n@AllArgsConstructor\npublic enum CrmPermissionLevelEnum implements ArrayValuable<Integer> {\n\n    OWNER(1, \"负责人\"),\n    READ(2, \"只读\"),\n    WRITE(3, \"读写\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmPermissionLevelEnum::getLevel).toArray(Integer[]::new);\n\n    /**\n     * 级别\n     */\n    private final Integer level;\n    /**\n     * 级别名称\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isOwner(Integer level) {\n        return ObjUtil.equal(OWNER.level, level);\n    }\n\n    public static boolean isRead(Integer level) {\n        return ObjUtil.equal(READ.level, level);\n    }\n\n    public static boolean isWrite(Integer level) {\n        return ObjUtil.equal(WRITE.level, level);\n    }\n\n    public static String getNameByLevel(Integer level) {\n        CrmPermissionLevelEnum typeEnum = CollUtil.findOne(CollUtil.newArrayList(CrmPermissionLevelEnum.values()),\n                item -> ObjUtil.equal(item.level, level));\n        return typeEnum == null ? null : typeEnum.getName();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/product/CrmProductStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.crm.enums.product;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * CRM 商品状态\n *\n * @author ZanGe丶\n * @since 2023-11-30 21:53\n */\n@Getter\n@AllArgsConstructor\npublic enum CrmProductStatusEnum implements ArrayValuable<Integer> {\n\n    DISABLE(0, \"下架\"),\n    ENABLE(1, \"上架\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmProductStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isEnable(Integer status) {\n        return ObjUtil.equal(ENABLE.status, status);\n    }\n\n    public static boolean isDisable(Integer status) {\n        return ObjUtil.equal(DISABLE.status, status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/receivable/CrmReceivableReturnTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.crm.enums.receivable;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * CRM 回款方式枚举\n *\n * @author HUIHUI\n */\n@Getter\n@AllArgsConstructor\npublic enum CrmReceivableReturnTypeEnum implements ArrayValuable<Integer> {\n\n    CHECK(1, \"支票\"),\n    CASH(2, \"现金\"),\n    POSTAL_REMITTANCE(3, \"邮政汇款\"),\n    TELEGRAPHIC_TRANSFER(4, \"电汇\"),\n    ONLINE_TRANSFER(5, \"网上转账\"),\n    ALIPAY(6, \"支付宝\"),\n    WECHAT_PAY(7, \"微信支付\"),\n    OTHER(8, \"其它\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmReceivableReturnTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 名称\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:8-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-module-crm-server\nWORKDIR /yudao-module-crm-server\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-module-crm-server.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48089 端口\nEXPOSE 48089\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-crm</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-crm-server</artifactId>\n\n    <name>${project.artifactId}</name>\n    <description>\n        crm 包下，客户关系管理（Customer Relationship Management）。\n        例如说：客户、联系人、商机、合同、回款等等\n    </description>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-infra-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-system-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-crm-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-bpm-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- Job 定时任务相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-job</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/CrmServerApplication.java",
    "content": "package cn.iocoder.yudao.module.crm;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n * <p>\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication\npublic class CrmServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(CrmServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java",
    "content": "/**\n * crm API 实现类，定义暴露给其它模块的 API\n */\npackage cn.iocoder.yudao.module.crm.api;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.business;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.crm.service.product.CrmProductService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS;\n\n@Tag(name = \"管理后台 - CRM 商机\")\n@RestController\n@RequestMapping(\"/crm/business\")\n@Validated\npublic class CrmBusinessController {\n\n    @Resource\n    private CrmBusinessService businessService;\n    @Resource\n    private CrmCustomerService customerService;\n    @Resource\n    private CrmBusinessStatusService businessStatusTypeService;\n    @Resource\n    private CrmBusinessStatusService businessStatusService;\n    @Resource\n    private CrmProductService productService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建商机\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:create')\")\n    public CommonResult<Long> createBusiness(@Valid @RequestBody CrmBusinessSaveReqVO createReqVO) {\n        return success(businessService.createBusiness(createReqVO, getLoginUserId()));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新商机\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:update')\")\n    public CommonResult<Boolean> updateBusiness(@Valid @RequestBody CrmBusinessSaveReqVO updateReqVO) {\n        businessService.updateBusiness(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新商机状态\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:update')\")\n    public CommonResult<Boolean> updateBusinessStatus(@Valid @RequestBody CrmBusinessUpdateStatusReqVO updateStatusReqVO) {\n        businessService.updateBusinessStatus(updateStatusReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除商机\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:business:delete')\")\n    public CommonResult<Boolean> deleteBusiness(@RequestParam(\"id\") Long id) {\n        businessService.deleteBusiness(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得商机\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:query')\")\n    public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam(\"id\") Long id) {\n        CrmBusinessDO business = businessService.getBusiness(id);\n        return success(buildBusinessDetail(business));\n    }\n\n    private CrmBusinessRespVO buildBusinessDetail(CrmBusinessDO business) {\n        if (business == null) {\n            return null;\n        }\n        CrmBusinessRespVO businessVO = buildBusinessDetailList(Collections.singletonList(business)).get(0);\n        // 拼接产品项\n        List<CrmBusinessProductDO> businessProducts = businessService.getBusinessProductListByBusinessId(businessVO.getId());\n        Map<Long, CrmProductDO> productMap = productService.getProductMap(\n                convertSet(businessProducts, CrmBusinessProductDO::getProductId));\n        businessVO.setProducts(BeanUtils.toBean(businessProducts, CrmBusinessRespVO.Product.class, businessProductVO ->\n                MapUtils.findAndThen(productMap, businessProductVO.getProductId(),\n                        product -> businessProductVO.setProductName(product.getName())\n                                .setProductNo(product.getNo()).setProductUnit(product.getUnit()))));\n        return businessVO;\n    }\n\n    @GetMapping(\"/simple-all-list\")\n    @Operation(summary = \"获得商机的精简列表\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:query')\")\n    public CommonResult<List<CrmBusinessRespVO>> getSimpleContactList() {\n        CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO();\n        reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页\n        PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(reqVO, getLoginUserId());\n        return success(convertList(pageResult.getList(), business -> // 只返回 id、name 字段\n                new CrmBusinessRespVO().setId(business.getId()).setName(business.getName())\n                        .setCustomerId(business.getCustomerId())));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得商机分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:query')\")\n    public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) {\n        PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, getLoginUserId());\n        return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/page-by-customer\")\n    @Operation(summary = \"获得商机分页，基于指定客户\")\n    public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByCustomer(@Valid CrmBusinessPageReqVO pageReqVO) {\n        if (pageReqVO.getCustomerId() == null) {\n            throw exception(CUSTOMER_NOT_EXISTS);\n        }\n        PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByCustomerId(pageReqVO);\n        return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/page-by-contact\")\n    @Operation(summary = \"获得联系人的商机分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:query')\")\n    public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessContactPage(@Valid CrmBusinessPageReqVO pageReqVO) {\n        PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByContact(pageReqVO);\n        return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出商机 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportBusinessExcel(@Valid CrmBusinessPageReqVO exportReqVO,\n                                    HttpServletResponse response) throws IOException {\n        exportReqVO.setPageSize(PAGE_SIZE_NONE);\n        List<CrmBusinessDO> list = businessService.getBusinessPage(exportReqVO, getLoginUserId()).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"商机.xls\", \"数据\", CrmBusinessRespVO.class,\n                buildBusinessDetailList(list));\n    }\n\n    public List<CrmBusinessRespVO> buildBusinessDetailList(List<CrmBusinessDO> list) {\n        if (CollUtil.isEmpty(list)) {\n            return Collections.emptyList();\n        }\n        // 1.1 获取客户列表\n        Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(list, CrmBusinessDO::getCustomerId));\n        // 1.2 获取创建人、负责人列表\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,\n                contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        // 1.3 获得商机状态组\n        Map<Long, CrmBusinessStatusTypeDO> statusTypeMap = businessStatusTypeService.getBusinessStatusTypeMap(\n                convertSet(list, CrmBusinessDO::getStatusTypeId));\n        Map<Long, CrmBusinessStatusDO> statusMap = businessStatusService.getBusinessStatusMap(\n                convertSet(list, CrmBusinessDO::getStatusId));\n        // 2. 拼接数据\n        return BeanUtils.toBean(list, CrmBusinessRespVO.class, businessVO -> {\n            // 2.1 设置客户名称\n            MapUtils.findAndThen(customerMap, businessVO.getCustomerId(), customer -> businessVO.setCustomerName(customer.getName()));\n            // 2.2 设置创建人、负责人名称\n            MapUtils.findAndThen(userMap, NumberUtils.parseLong(businessVO.getCreator()),\n                    user -> businessVO.setCreatorName(user.getNickname()));\n            MapUtils.findAndThen(userMap, businessVO.getOwnerUserId(), user -> {\n                businessVO.setOwnerUserName(user.getNickname());\n                MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> businessVO.setOwnerUserDeptName(dept.getName()));\n            });\n            // 2.3 设置商机状态\n            MapUtils.findAndThen(statusTypeMap, businessVO.getStatusTypeId(), statusType -> businessVO.setStatusTypeName(statusType.getName()));\n            MapUtils.findAndThen(statusMap, businessVO.getStatusId(), status -> businessVO.setStatusName(\n                    businessService.getBusinessStatusName(businessVO.getEndStatus(), status)));\n        });\n    }\n\n    @PutMapping(\"/transfer\")\n    @Operation(summary = \"商机转移\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:update')\")\n    public CommonResult<Boolean> transferBusiness(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) {\n        businessService.transferBusiness(reqVO, getLoginUserId());\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.business;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - CRM 商机状态\")\n@RestController\n@RequestMapping(\"/crm/business-status\")\n@Validated\npublic class CrmBusinessStatusController {\n\n    @Resource\n    private CrmBusinessStatusService businessStatusTypeService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建商机状态\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business-status:create')\")\n    public CommonResult<Long> createBusinessStatus(@Valid @RequestBody CrmBusinessStatusSaveReqVO createReqVO) {\n        return success(businessStatusTypeService.createBusinessStatus(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新商机状态\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business-status:update')\")\n    public CommonResult<Boolean> updateBusinessStatus(@Valid @RequestBody CrmBusinessStatusSaveReqVO updateReqVO) {\n        businessStatusTypeService.updateBusinessStatus(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除商机状态\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:business-status:delete')\")\n    public CommonResult<Boolean> deleteBusinessStatusType(@RequestParam(\"id\") Long id) {\n        businessStatusTypeService.deleteBusinessStatusType(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得商机状态\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business-status:query')\")\n    public CommonResult<CrmBusinessStatusRespVO> getBusinessStatusType(@RequestParam(\"id\") Long id) {\n        CrmBusinessStatusTypeDO statusType = businessStatusTypeService.getBusinessStatusType(id);\n        if (statusType == null) {\n            return success(null);\n        }\n        List<CrmBusinessStatusDO> statuses = businessStatusTypeService.getBusinessStatusListByTypeId(id);\n        return success(BeanUtils.toBean(statusType, CrmBusinessStatusRespVO.class,\n                statusTypeVO -> statusTypeVO.setStatuses(BeanUtils.toBean(statuses, CrmBusinessStatusRespVO.Status.class))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得商机状态分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business-status:query')\")\n    public CommonResult<PageResult<CrmBusinessStatusRespVO>> getBusinessStatusPage(@Valid PageParam pageReqVO) {\n        // 1. 查询数据\n        PageResult<CrmBusinessStatusTypeDO> pageResult = businessStatusTypeService.getBusinessStatusTypePage(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n        // 2. 拼接数据\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), statusType -> Long.parseLong(statusType.getCreator())));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(\n                convertSetByFlatMap(pageResult.getList(), CrmBusinessStatusTypeDO::getDeptIds, Collection::stream));\n        return success(BeanUtils.toBean(pageResult, CrmBusinessStatusRespVO.class, statusTypeVO -> {\n            statusTypeVO.setCreator(userMap.get(NumberUtils.parseLong(statusTypeVO.getCreator())).getNickname());\n            statusTypeVO.setDeptNames(convertList(statusTypeVO.getDeptIds(),\n                    deptId -> deptMap.containsKey(deptId) ? deptMap.get(deptId).getName() : null));\n        }));\n    }\n\n    @GetMapping(\"/type-simple-list\")\n    @Operation(summary = \"获得商机状态组列表\")\n    public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusTypeSimpleList() {\n        List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeList();\n        // 过滤掉部门不匹配的\n        Long deptId = adminUserApi.getUser(getLoginUserId()).getCheckedData().getDeptId();\n        list.removeIf(statusType -> CollUtil.isNotEmpty(statusType.getDeptIds()) && !statusType.getDeptIds().contains(deptId));\n        return success(BeanUtils.toBean(list, CrmBusinessStatusRespVO.class));\n    }\n\n    @GetMapping(\"/status-simple-list\")\n    @Operation(summary = \"获得商机状态列表\")\n    @Parameter(name = \"typeId\", description = \"商机状态组\", required = true, example = \"1024\")\n    public CommonResult<List<CrmBusinessStatusRespVO.Status>> getBusinessStatusSimpleList(@RequestParam(\"typeId\") Long typeId) {\n        List<CrmBusinessStatusDO> list = businessStatusTypeService.getBusinessStatusListByTypeId(typeId);\n        return success(BeanUtils.toBean(list, CrmBusinessStatusRespVO.Status.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 商机分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmBusinessPageReqVO extends PageParam {\n\n    @Schema(description = \"商机名称\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"客户编号\", example = \"10795\")\n    private Long customerId;\n\n    @Schema(description = \"联系人编号\", example = \"10795\")\n    private Long contactId;\n\n    @Schema(description = \"场景类型\", example = \"1\")\n    @InEnum(CrmSceneTypeEnum.class)\n    private Integer sceneType; // 场景类型，为 null 时则表示全部\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - CRM 商机 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class CrmBusinessRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"32129\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"商机名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @ExcelProperty(\"商机名称\")\n    private String name;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10299\")\n    private Long customerId;\n    @Schema(description = \"客户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @ExcelProperty(\"客户名称\")\n    private String customerName;\n\n    @Schema(description = \"跟进状态\", requiredMode = Schema.RequiredMode.REQUIRED, example =\"true\")\n    @ExcelProperty(\"跟进状态\")\n    private Boolean followUpStatus;\n\n    @Schema(description = \"最后跟进时间\")\n    @ExcelProperty(\"最后跟进时间\")\n    private LocalDateTime contactLastTime;\n\n    @Schema(description = \"下次联系时间\")\n    @ExcelProperty(\"下次联系时间\")\n    private LocalDateTime contactNextTime;\n\n    @Schema(description = \"负责人的用户编号\", example = \"25682\")\n    @ExcelProperty(\"负责人的用户编号\")\n    private Long ownerUserId;\n    @Schema(description = \"负责人名字\", example = \"25682\")\n    @ExcelProperty(\"负责人名字\")\n    private String ownerUserName;\n    @Schema(description = \"负责人部门\")\n    @ExcelProperty(\"负责人部门\")\n    private String ownerUserDeptName;\n\n    @Schema(description = \"商机状态组编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25714\")\n    private Long statusTypeId;\n    @Schema(description = \"商机状组名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"进行中\")\n    @ExcelProperty(\"商机状态组\")\n    private String statusTypeName;\n\n    @Schema(description = \"商机状态编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"30320\")\n    private Long statusId;\n    @Schema(description = \"状态名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"跟进中\")\n    @ExcelProperty(\"商机状态\")\n    private String statusName;\n\n    @Schema\n    @ExcelProperty(\"结束状态\")\n    private Integer endStatus;\n\n    @ExcelProperty(\"结束时的备注\")\n    private String endRemark;\n\n    @Schema(description = \"预计成交日期\")\n    @ExcelProperty(\"预计成交日期\")\n    private LocalDateTime dealTime;\n\n    @Schema(description = \"产品总金额\", example = \"12025\")\n    @ExcelProperty(\"产品总金额\")\n    private BigDecimal totalProductPrice;\n\n    @Schema(description = \"整单折扣\")\n    @ExcelProperty(\"整单折扣\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"商机总金额\", example = \"12371\")\n    @ExcelProperty(\"商机总金额\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"1024\")\n    @ExcelProperty(\"创建人\")\n    private String creator;\n    @Schema(description = \"创建人名字\", example = \"芋道源码\")\n    @ExcelProperty(\"创建人名字\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"更新时间\")\n    private LocalDateTime updateTime;\n\n    @Schema(description = \"产品列表\")\n    private List<Product> products;\n\n    @Schema(description = \"产品列表\")\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Product {\n\n        @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n        private Long id;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20529\")\n        private Long productId;\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20529\")\n        private String productNo;\n        @Schema(description = \"产品单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n        private Integer productUnit;\n\n        @Schema(description = \"产品单价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"商机价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        private BigDecimal businessPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8911\")\n        private BigDecimal count;\n\n        @Schema(description = \"总计价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        private BigDecimal totalPrice;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;\n\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - CRM 商机创建/更新 Request VO\")\n@Data\npublic class CrmBusinessSaveReqVO {\n\n    @Schema(description = \"主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"32129\")\n    private Long id;\n\n    @Schema(description = \"商机名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @DiffLogField(name = \"商机名称\")\n    @NotNull(message = \"商机名称不能为空\")\n    private String name;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10299\")\n    @DiffLogField(name = \"客户\", function = CrmCustomerParseFunction.NAME)\n    @NotNull(message = \"客户不能为空\")\n    private Long customerId;\n\n    @Schema(description = \"下次联系时间\")\n    @DiffLogField(name = \"下次联系时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime contactNextTime;\n\n    @Schema(description = \"负责人用户编号\", example = \"14334\")\n    @NotNull(message = \"负责人不能为空\")\n    @DiffLogField(name = \"负责人\", function = SysAdminUserParseFunction.NAME)\n    private Long ownerUserId;\n\n    @Schema(description = \"商机状态组编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25714\")\n    @DiffLogField(name = \"商机状态组\")\n    @NotNull(message = \"商机状态组不能为空\")\n    private Long statusTypeId;\n\n    @Schema(description = \"预计成交日期\")\n    @DiffLogField(name = \"预计成交日期\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime dealTime;\n\n    @Schema(description = \"整单折扣\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"55.00\")\n    @DiffLogField(name = \"整单折扣\")\n    @NotNull(message = \"整单折扣不能为空\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @DiffLogField(name = \"备注\")\n    private String remark;\n\n    @Schema(description = \"联系人编号\", example = \"110\")\n    private Long contactId; // 使用场景，在【联系人详情】添加商机时，如果需要关联两者，需要传递 contactId 字段\n\n    @Schema(description = \"产品列表\")\n    private List<BusinessProduct> products;\n\n    @Schema(description = \"产品列表\")\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class BusinessProduct {\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20529\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        @NotNull(message = \"产品单价不能为空\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"商机价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        @NotNull(message = \"商机价格不能为空\")\n        private BigDecimal businessPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8911\")\n        @NotNull(message = \"产品数量不能为空\")\n        private Integer count;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;\n\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 商机转移 Request VO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmBusinessTransferReqVO {\n\n    @Schema(description = \"商机编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"商机编号不能为空\")\n    private Long id;\n\n    /**\n     * 新负责人的用户编号\n     */\n    @Schema(description = \"新负责人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"新负责人的用户编号不能为空\")\n    private Long newOwnerUserId;\n\n    /**\n     * 老负责人加入团队后的权限级别。如果 null 说明移除\n     *\n     * 关联 {@link CrmPermissionLevelEnum}\n     */\n    @Schema(description = \"老负责人加入团队后的权限级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer oldOwnerPermissionLevel;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessUpdateStatusReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.business.CrmBusinessEndStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - CRM 商机更新状态 Request VO\")\n@Data\npublic class CrmBusinessUpdateStatusReqVO {\n\n    @Schema(description = \"商机编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"32129\")\n    @NotNull(message = \"商机编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"状态编号\", example = \"1\")\n    private Long statusId;\n\n    @Schema(description = \"结束状态\", example = \"1\")\n    @InEnum(value = CrmBusinessEndStatusEnum.class)\n    private Integer endStatus;\n\n    @AssertTrue(message = \"变更状态不正确\")\n    public boolean isStatusValid() {\n        return statusId != null || endStatus != null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.business.vo.status;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 商机状态 Response VO\")\n@Data\npublic class CrmBusinessStatusRespVO {\n\n    @Schema(description = \"状态组编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2934\")\n    private Long id;\n\n    @Schema(description = \"状态组名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    private String name;\n\n    @Schema(description = \"使用的部门编号\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Long> deptIds;\n    @Schema(description = \"使用的部门名称\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> deptNames;\n\n    @Schema(description = \"创建人\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String creator;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"状态集合\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Status> statuses;\n\n    @Data\n    public static class Status {\n\n        @Schema(description = \"状态编号\", example = \"23899\")\n        private Long id;\n\n        @Schema(description = \"状态名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n        private String name;\n\n        @Schema(description = \"赢单率\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n        private BigDecimal percent;\n\n        @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer sort;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.business.vo.status;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.math.BigDecimal;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 商机状态组新增/修改 Request VO\")\n@Data\npublic class CrmBusinessStatusSaveReqVO {\n\n    @Schema(description = \"主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2934\")\n    private Long id;\n\n    @Schema(description = \"状态类型名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotEmpty(message = \"状态类型名不能为空\")\n    private String name;\n\n    @Schema(description = \"使用的部门编号\")\n    private List<Long> deptIds;\n\n    @Schema(description = \"商机状态集合\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"商机状态集合不能为空\")\n    @Valid\n    private List<Status> statuses;\n\n    @Data\n    public static class Status {\n\n        @Schema(description = \"状态编号\", example = \"23899\")\n        private Long id;\n\n        @Schema(description = \"状态名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n        @NotEmpty(message = \"状态名不能为空\")\n        private String name;\n\n        @Schema(description = \"赢单率\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n        @NotNull(message = \"赢单率不能为空\")\n        private BigDecimal percent;\n\n        @Schema(description = \"排序\", hidden = true, example = \"1\")\n        private Integer sort;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.clue;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.service.clue.CrmClueService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\nimport static java.util.Collections.singletonList;\n\n@Tag(name = \"管理后台 - 线索\")\n@RestController\n@RequestMapping(\"/crm/clue\")\n@Validated\npublic class CrmClueController {\n\n    @Resource\n    private CrmClueService clueService;\n    @Resource\n    private CrmCustomerService customerService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建线索\")\n    @PreAuthorize(\"@ss.hasPermission('crm:clue:create')\")\n    public CommonResult<Long> createClue(@Valid @RequestBody CrmClueSaveReqVO createReqVO) {\n        return success(clueService.createClue(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新线索\")\n    @PreAuthorize(\"@ss.hasPermission('crm:clue:update')\")\n    public CommonResult<Boolean> updateClue(@Valid @RequestBody CrmClueSaveReqVO updateReqVO) {\n        clueService.updateClue(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除线索\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:clue:delete')\")\n    public CommonResult<Boolean> deleteClue(@RequestParam(\"id\") Long id) {\n        clueService.deleteClue(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得线索\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:clue:query')\")\n    public CommonResult<CrmClueRespVO> getClue(@RequestParam(\"id\") Long id) {\n        CrmClueDO clue = clueService.getClue(id);\n        return success(buildClueDetail(clue));\n    }\n\n    private CrmClueRespVO buildClueDetail(CrmClueDO clue) {\n        if (clue == null) {\n            return null;\n        }\n        return buildClueDetailList(singletonList(clue)).get(0);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得线索分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:clue:query')\")\n    public CommonResult<PageResult<CrmClueRespVO>> getCluePage(@Valid CrmCluePageReqVO pageVO) {\n        PageResult<CrmClueDO> pageResult = clueService.getCluePage(pageVO, getLoginUserId());\n        return success(new PageResult<>(buildClueDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出线索 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('crm:clue:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportClueExcel(@Valid CrmCluePageReqVO pageReqVO, HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PAGE_SIZE_NONE);\n        List<CrmClueDO> list = clueService.getCluePage(pageReqVO, getLoginUserId()).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"线索.xls\", \"数据\", CrmClueRespVO.class, buildClueDetailList(list));\n    }\n\n    private List<CrmClueRespVO> buildClueDetailList(List<CrmClueDO> list) {\n        if (CollUtil.isEmpty(list)) {\n            return Collections.emptyList();\n        }\n        // 1.1 获取客户列表\n        Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(list, CrmClueDO::getCustomerId));\n        // 1.2 获取创建人、负责人列表\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,\n                contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        // 2. 转换成 VO\n        return BeanUtils.toBean(list, CrmClueRespVO.class, clueVO -> {\n            clueVO.setAreaName(AreaUtils.format(clueVO.getAreaId()));\n            // 2.1 设置客户名称\n            MapUtils.findAndThen(customerMap, clueVO.getCustomerId(), customer -> clueVO.setCustomerName(customer.getName()));\n            // 2.2 设置创建人、负责人名称\n            MapUtils.findAndThen(userMap, NumberUtils.parseLong(clueVO.getCreator()),\n                    user -> clueVO.setCreatorName(user.getNickname()));\n            MapUtils.findAndThen(userMap, clueVO.getOwnerUserId(), user -> {\n                clueVO.setOwnerUserName(user.getNickname());\n                MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> clueVO.setOwnerUserDeptName(dept.getName()));\n            });\n        });\n    }\n\n    @PutMapping(\"/transfer\")\n    @Operation(summary = \"线索转移\")\n    @PreAuthorize(\"@ss.hasPermission('crm:clue:update')\")\n    public CommonResult<Boolean> transferClue(@Valid @RequestBody CrmClueTransferReqVO reqVO) {\n        clueService.transferClue(reqVO, getLoginUserId());\n        return success(true);\n    }\n\n    @PutMapping(\"/transform\")\n    @Operation(summary = \"线索转化为客户\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:clue:update')\")\n    public CommonResult<Boolean> transformClue(@RequestParam(\"id\") Long id) {\n        clueService.transformClue(id, getLoginUserId());\n        return success(Boolean.TRUE);\n    }\n\n    @GetMapping(\"/follow-count\")\n    @Operation(summary = \"获得分配给我的、待跟进的线索数量\")\n    @PreAuthorize(\"@ss.hasPermission('crm:clue:query')\")\n    public CommonResult<Long> getFollowClueCount() {\n        return success(clueService.getFollowClueCount(getLoginUserId()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 线索分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmCluePageReqVO extends PageParam {\n\n    @Schema(description = \"线索名称\", example = \"线索xxx\")\n    private String name;\n\n    @Schema(description = \"转化状态\", example = \"2048\")\n    private Boolean transformStatus;\n\n    @Schema(description = \"电话\", example = \"18000000000\")\n    private String telephone;\n\n    @Schema(description = \"手机号\", example = \"18000000000\")\n    private String mobile;\n\n    @Schema(description = \"场景类型\", example = \"1\")\n    @InEnum(CrmSceneTypeEnum.class)\n    private Integer sceneType; // 场景类型，为 null 时则表示全部\n\n    @Schema(description = \"所属行业\", example = \"1\")\n    private Integer industryId;\n\n    @Schema(description = \"客户等级\", example = \"1\")\n    private Integer level;\n\n    @Schema(description = \"客户来源\", example = \"1\")\n    private Integer source;\n\n    @Schema(description = \"跟进状态\", example = \"true\")\n    private Boolean followUpStatus;\n\n    @Schema(description = \"创建时间\", example = \"[2023-01-01 00:00:00, 2023-01-31 23:59:59]\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.infra.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 线索 Response VO\")\n@Data\n@ToString(callSuper = true)\n@ExcelIgnoreUnannotated\npublic class CrmClueRespVO {\n\n    @Schema(description = \"编号，主键自增\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10969\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"线索名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"线索xxx\")\n    @ExcelProperty(\"线索名称\")\n    private String name;\n\n    @Schema(description = \"跟进状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"跟进状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.BOOLEAN_STRING)\n    private Boolean followUpStatus;\n\n    @Schema(description = \"最后跟进时间\")\n    @ExcelProperty(\"最后跟进时间\")\n    private LocalDateTime contactLastTime;\n\n    @Schema(description = \"最后跟进内容\", example = \"吃饭、睡觉、打逗逗\")\n    @ExcelProperty(\"最后跟进内容\")\n    private String contactLastContent;\n\n    @Schema(description = \"下次联系时间\", example = \"2023-10-18 01:00:00\")\n    @ExcelProperty(\"下次联系时间\")\n    private LocalDateTime contactNextTime;\n\n    @Schema(description = \"负责人编号\")\n    private Long ownerUserId;\n    @Schema(description = \"负责人名字\", example = \"25682\")\n    @ExcelProperty(\"负责人名字\")\n    private String ownerUserName;\n    @Schema(description = \"负责人部门\")\n    @ExcelProperty(\"负责人部门\")\n    private String ownerUserDeptName;\n\n    @Schema(description = \"转化状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"转化状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.BOOLEAN_STRING)\n    private Boolean transformStatus;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"520\")\n    private Long customerId;\n    @Schema(description = \"客户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"客户名称\")\n    @ExcelProperty(\"客户名称\")\n    private String customerName;\n\n    @Schema(description = \"手机号\", example = \"18000000000\")\n    @ExcelProperty(\"手机号\")\n    private String mobile;\n\n    @Schema(description = \"电话\", example = \"18000000000\")\n    @ExcelProperty(\"电话\")\n    private String telephone;\n\n    @Schema(description = \"QQ\", example = \"25682\")\n    @ExcelProperty(\"QQ\")\n    private String qq;\n\n    @Schema(description = \"wechat\", example = \"25682\")\n    @ExcelProperty(\"wechat\")\n    private String wechat;\n\n    @Schema(description = \"email\", example = \"25682\")\n    @ExcelProperty(\"email\")\n    private String email;\n\n    @Schema(description = \"地区编号\", example = \"1024\")\n    @ExcelProperty(\"地区编号\")\n    private Integer areaId;\n    @Schema(description = \"地区名称\", example = \"北京市\")\n    @ExcelProperty(\"地区名称\")\n    private String areaName;\n    @Schema(description = \"详细地址\", example = \"北京市成华大道\")\n    @ExcelProperty(\"详细地址\")\n    private String detailAddress;\n\n    @Schema(description = \"所属行业\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(value = \"所属行业\", converter = DictConvert.class)\n    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY)\n    private Integer industryId;\n\n    @Schema(description = \"客户等级\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(value = \"客户等级\", converter = DictConvert.class)\n    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL)\n    private Integer level;\n\n    @Schema(description = \"客户来源\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(value = \"客户来源\", converter = DictConvert.class)\n    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE)\n    private Integer source;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"1024\")\n    @ExcelProperty(\"创建人\")\n    private String creator;\n    @Schema(description = \"创建人名字\", example = \"芋道源码\")\n    @ExcelProperty(\"创建人名字\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"更新时间\")\n    private LocalDateTime updateTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.framework.common.validation.Mobile;\nimport cn.iocoder.yudao.framework.common.validation.Telephone;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerIndustryParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerLevelParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerSourceParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAreaParseFunction;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\nimport static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;\n\n@Schema(description = \"管理后台 - CRM 线索创建/更新 Request VO\")\n@Data\npublic class CrmClueSaveReqVO {\n\n    @Schema(description = \"编号\", example = \"10969\")\n    private Long id;\n\n    @Schema(description = \"线索名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"线索xxx\")\n    @DiffLogField(name = \"线索名称\")\n    @NotEmpty(message = \"线索名称不能为空\")\n    private String name;\n\n    @Schema(description = \"最后跟进时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @DiffLogField(name = \"最后跟进时间\")\n    private LocalDateTime contactLastTime;\n\n    @Schema(description = \"下次联系时间\", example = \"2023-10-18 01:00:00\")\n    @DiffLogField(name = \"下次联系时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime contactNextTime;\n\n    @Schema(description = \"负责人编号\", example = \"2048\")\n    @NotNull(message = \"负责人编号不能为空\")\n    private Long ownerUserId;\n\n    @Schema(description = \"手机号\", example = \"18000000000\")\n    @DiffLogField(name = \"手机号\")\n    @Mobile\n    private String mobile;\n\n    @Schema(description = \"电话\", example = \"18000000000\")\n    @DiffLogField(name = \"电话\")\n    @Telephone\n    private String telephone;\n\n    @Schema(description = \"QQ\", example = \"123456789\")\n    @DiffLogField(name = \"QQ\")\n    @Size(max = 20, message = \"QQ长度不能超过 20 个字符\")\n    private String qq;\n\n    @Schema(description = \"微信\", example = \"123456789\")\n    @DiffLogField(name = \"微信\")\n    @Size(max = 255, message = \"微信长度不能超过 255 个字符\")\n    private String wechat;\n\n    @Schema(description = \"邮箱\", example = \"123456789@qq.com\")\n    @DiffLogField(name = \"邮箱\")\n    @Email(message = \"邮箱格式不正确\")\n    @Size(max = 255, message = \"邮箱长度不能超过 255 个字符\")\n    private String email;\n\n    @Schema(description = \"地区编号\", example = \"20158\")\n    @DiffLogField(name = \"地区编号\", function = SysAreaParseFunction.NAME)\n    private Integer areaId;\n\n    @Schema(description = \"详细地址\", example = \"北京市海淀区\")\n    @DiffLogField(name = \"详细地址\")\n    private String detailAddress;\n\n    @Schema(description = \"所属行业\", example = \"1\")\n    @DiffLogField(name = \"所属行业\", function = CrmCustomerIndustryParseFunction.NAME)\n    @DictFormat(CRM_CUSTOMER_INDUSTRY)\n    private Integer industryId;\n\n    @Schema(description = \"客户等级\", example = \"2\")\n    @DiffLogField(name = \"客户等级\", function = CrmCustomerLevelParseFunction.NAME)\n    @InEnum(CrmCustomerLevelEnum.class)\n    private Integer level;\n\n    @Schema(description = \"客户来源\", example = \"3\")\n    @DiffLogField(name = \"客户来源\", function = CrmCustomerSourceParseFunction.NAME)\n    private Integer source;\n\n    @Schema(description = \"客户描述\", example = \"任意文字\")\n    @DiffLogField(name = \"客户描述\")\n    @Size(max = 4096, message = \"客户描述长度不能超过 4096 个字符\")\n    private String description;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @DiffLogField(name = \"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 线索转移 Request VO\")\n@Data\npublic class CrmClueTransferReqVO {\n\n    @Schema(description = \"线索编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"线索编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"新负责人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"新负责人的用户编号不能为空\")\n    private Long newOwnerUserId;\n\n    @Schema(description = \"老负责人加入团队后的权限级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @InEnum(value = CrmPermissionLevelEnum.class)\n    private Integer oldOwnerPermissionLevel; // 老负责人加入团队后的权限级别。如果 null 说明移除\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contact;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\nimport static java.util.Collections.singletonList;\n\n@Tag(name = \"管理后台 - CRM 联系人\")\n@RestController\n@RequestMapping(\"/crm/contact\")\n@Validated\n@Slf4j\npublic class CrmContactController {\n\n    @Resource\n    private CrmContactService contactService;\n    @Resource\n    private CrmCustomerService customerService;\n    @Resource\n    private CrmContactBusinessService contactBusinessLinkService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建联系人\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:create')\")\n    public CommonResult<Long> createContact(@Valid @RequestBody CrmContactSaveReqVO createReqVO) {\n        return success(contactService.createContact(createReqVO, getLoginUserId()));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新联系人\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:update')\")\n    public CommonResult<Boolean> updateContact(@Valid @RequestBody CrmContactSaveReqVO updateReqVO) {\n        contactService.updateContact(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除联系人\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:delete')\")\n    public CommonResult<Boolean> deleteContact(@RequestParam(\"id\") Long id) {\n        contactService.deleteContact(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得联系人\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:query')\")\n    public CommonResult<CrmContactRespVO> getContact(@RequestParam(\"id\") Long id) {\n        CrmContactDO contact = contactService.getContact(id);\n        return success(buildContactDetail(contact));\n    }\n\n    private CrmContactRespVO buildContactDetail(CrmContactDO contact) {\n        if (contact == null) {\n            return null;\n        }\n        return buildContactDetailList(singletonList(contact)).get(0);\n    }\n\n    @GetMapping(\"/simple-all-list\")\n    @Operation(summary = \"获得联系人的精简列表\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:query')\")\n    public CommonResult<List<CrmContactRespVO>> getSimpleContactList() {\n        List<CrmContactDO> list = contactService.getContactList(getLoginUserId());\n        return success(convertList(list, contact -> // 只返回 id、name 字段\n                new CrmContactRespVO().setId(contact.getId()).setName(contact.getName())\n                        .setCustomerId(contact.getCustomerId())));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得联系人分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:query')\")\n    public CommonResult<PageResult<CrmContactRespVO>> getContactPage(@Valid CrmContactPageReqVO pageVO) {\n        PageResult<CrmContactDO> pageResult = contactService.getContactPage(pageVO, getLoginUserId());\n        return success(new PageResult<>(buildContactDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/page-by-customer\")\n    @Operation(summary = \"获得联系人分页，基于指定客户\")\n    public CommonResult<PageResult<CrmContactRespVO>> getContactPageByCustomer(@Valid CrmContactPageReqVO pageVO) {\n        Assert.notNull(pageVO.getCustomerId(), \"客户编号不能为空\");\n        PageResult<CrmContactDO> pageResult = contactService.getContactPageByCustomerId(pageVO);\n        return success(new PageResult<>(buildContactDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/page-by-business\")\n    @Operation(summary = \"获得联系人分页，基于指定商机\")\n    public CommonResult<PageResult<CrmContactRespVO>> getContactPageByBusiness(@Valid CrmContactPageReqVO pageVO) {\n        Assert.notNull(pageVO.getBusinessId(), \"商机编号不能为空\");\n        PageResult<CrmContactDO> pageResult = contactService.getContactPageByBusinessId(pageVO);\n        return success(new PageResult<>(buildContactDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出联系人 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportContactExcel(@Valid CrmContactPageReqVO exportReqVO,\n                                   HttpServletResponse response) throws IOException {\n        exportReqVO.setPageNo(PAGE_SIZE_NONE);\n        List<CrmContactDO> list = contactService.getContactPage(exportReqVO, getLoginUserId()).getList();\n        ExcelUtils.write(response, \"联系人.xls\", \"数据\", CrmContactRespVO.class, buildContactDetailList(list));\n    }\n\n    private List<CrmContactRespVO> buildContactDetailList(List<CrmContactDO> contactList) {\n        if (CollUtil.isEmpty(contactList)) {\n            return Collections.emptyList();\n        }\n        // 1.1 获取客户列表\n        Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(contactList, CrmContactDO::getCustomerId));\n        // 1.2 获取创建人、负责人列表\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList,\n                contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        // 1.3 直属上级 Map\n        Map<Long, CrmContactDO> parentContactMap = contactService.getContactMap(\n                convertSet(contactList, CrmContactDO::getParentId));\n        // 2. 转换成 VO\n        return BeanUtils.toBean(contactList, CrmContactRespVO.class, contactVO -> {\n            contactVO.setAreaName(AreaUtils.format(contactVO.getAreaId()));\n            // 2.1 设置客户名称\n            MapUtils.findAndThen(customerMap, contactVO.getCustomerId(), customer -> contactVO.setCustomerName(customer.getName()));\n            // 2.2 设置创建人、负责人名称\n            MapUtils.findAndThen(userMap, NumberUtils.parseLong(contactVO.getCreator()),\n                    user -> contactVO.setCreatorName(user.getNickname()));\n            MapUtils.findAndThen(userMap, contactVO.getOwnerUserId(), user -> {\n                contactVO.setOwnerUserName(user.getNickname());\n                MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> contactVO.setOwnerUserDeptName(dept.getName()));\n            });\n            // 2.3 设置直属上级名称\n            findAndThen(parentContactMap, contactVO.getParentId(), contact -> contactVO.setParentName(contact.getName()));\n        });\n    }\n\n    @PutMapping(\"/transfer\")\n    @Operation(summary = \"联系人转移\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:update')\")\n    public CommonResult<Boolean> transferContact(@Valid @RequestBody CrmContactTransferReqVO reqVO) {\n        contactService.transferContact(reqVO, getLoginUserId());\n        return success(true);\n    }\n\n    // ================== 关联/取关商机 ===================\n\n    @PostMapping(\"/create-business-list\")\n    @Operation(summary = \"创建联系人与商机的关联\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:create-business')\")\n    public CommonResult<Boolean> createContactBusinessList(@Valid @RequestBody CrmContactBusinessReqVO createReqVO) {\n        contactBusinessLinkService.createContactBusinessList(createReqVO);\n        return success(true);\n    }\n\n\n    @PostMapping(\"/create-business-list2\")\n    @Operation(summary = \"创建联系人与商机的关联\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:create-business')\")\n    public CommonResult<Boolean> createContactBusinessList2(@Valid @RequestBody CrmContactBusiness2ReqVO createReqVO) {\n        contactBusinessLinkService.createContactBusinessList2(createReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-business-list\")\n    @Operation(summary = \"删除联系人与联系人的关联\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:delete-business')\")\n    public CommonResult<Boolean> deleteContactBusinessList(@Valid @RequestBody CrmContactBusinessReqVO deleteReqVO) {\n        contactBusinessLinkService.deleteContactBusinessList(deleteReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-business-list2\")\n    @Operation(summary = \"删除联系人与联系人的关联\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contact:delete-business')\")\n    public CommonResult<Boolean> deleteContactBusinessList(@Valid @RequestBody CrmContactBusiness2ReqVO deleteReqVO) {\n        contactBusinessLinkService.deleteContactBusinessList2(deleteReqVO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactBusiness2ReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - CRM 联系人商机 Request VO\") // 【商机关联联系人】用于关联，取消关联的操作\n@Data\npublic class CrmContactBusiness2ReqVO {\n\n    @Schema(description = \"商机编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7638\")\n    @NotNull(message=\"商机不能为空\")\n    private Long businessId;\n\n    @Schema(description = \"联系人编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20878\")\n    @NotEmpty(message=\"联系人数组不能为空\")\n    private List<Long> contactIds;\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactBusinessReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - CRM 联系人商机 Request VO\") // 【联系人关联商机】用于关联，取消关联的操作\n@Data\npublic class CrmContactBusinessReqVO {\n\n    @Schema(description = \"联系人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20878\")\n    @NotNull(message=\"联系人不能为空\")\n    private Long contactId;\n\n    @Schema(description = \"商机编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7638\")\n    @NotEmpty(message=\"商机不能为空\")\n    private List<Long> businessIds;\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - CRM 联系人分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmContactPageReqVO extends PageParam {\n\n    @Schema(description = \"姓名\", example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"客户编号\", example = \"10795\")\n    private Long customerId;\n\n    @Schema(description = \"手机号\", example = \"13898273941\")\n    private String mobile;\n\n    @Schema(description = \"电话\", example = \"021-383773\")\n    private String telephone;\n\n    @Schema(description = \"电子邮箱\", example = \"111@22.com\")\n    private String email;\n\n    @Schema(description = \"QQ\", example = \"3882872\")\n    private Long qq;\n\n    @Schema(description = \"微信\", example = \"zzZ98373\")\n    private String wechat;\n\n    @Schema(description = \"场景类型\", example = \"1\")\n    @InEnum(CrmSceneTypeEnum.class)\n    private Integer sceneType; // 场景类型，为 null 时则表示全部\n\n    @Schema(description = \"商机编号\", example = \"10430\")\n    private Long businessId;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.infra.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 联系人 Response VO\")\n@Data\n@ToString(callSuper = true)\n@ExcelIgnoreUnannotated\npublic class CrmContactRespVO {\n\n    @Schema(description = \"主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3167\")\n    private Long id;\n\n    @Schema(description = \"联系人姓名\", example = \"芋艿\")\n    @ExcelProperty(value = \"联系人姓名\", order = 1)\n    private String name;\n\n    @Schema(description = \"客户编号\", example = \"10795\")\n    private Long customerId;\n    @ExcelProperty(value = \"客户名称\", order = 2)\n    @Schema(description = \"客户名字\", example = \"test\")\n    private String customerName;\n\n    @Schema(description = \"最后跟进时间\")\n    @ExcelProperty(value = \"最后跟进时间\", order = 6)\n    private LocalDateTime contactLastTime;\n\n    @Schema(description = \"最后跟进内容\")\n    @ExcelProperty(value = \"最后跟进内容\", order = 6)\n    private String contactLastContent;\n\n    @Schema(description = \"下次联系时间\")\n    @ExcelProperty(value = \"下次联系时间\", order = 6)\n    private LocalDateTime contactNextTime;\n\n    @Schema(description = \"负责人编号\")\n    private Long ownerUserId;\n    @Schema(description = \"负责人名字\", example = \"25682\")\n    @ExcelProperty(\"负责人名字\")\n    private String ownerUserName;\n    @Schema(description = \"负责人部门\")\n    @ExcelProperty(\"负责人部门\")\n    private String ownerUserDeptName;\n\n    @Schema(description = \"手机号\", example = \"1387171766\")\n    @ExcelProperty(value = \"手机号\", order = 4)\n    private String mobile;\n\n    @Schema(description = \"电话\", example = \"021-0029922\")\n    @ExcelProperty(value = \"电话\", order = 4)\n    private String telephone;\n\n    @Schema(description = \"电子邮箱\", example = \"1111@22.com\")\n    @ExcelProperty(value = \"邮箱\", order = 4)\n    private String email;\n\n    @Schema(description = \"QQ\", example = \"197272662\")\n    @ExcelProperty(value = \"QQ\", order = 4)\n    private Long qq;\n\n    @Schema(description = \"微信\", example = \"zzz3883\")\n    @ExcelProperty(value = \"微信\", order = 4)\n    private String wechat;\n\n    @Schema(description = \"地区编号\", example = \"20158\")\n    private Integer areaId;\n    @Schema(description = \"地区名\", example = \"上海上海市浦东新区\")\n    @ExcelProperty(value = \"地区\", order = 5)\n    private String areaName;\n\n    @Schema(description = \"地址\")\n    @ExcelProperty(value = \"地址\", order = 5)\n    private String detailAddress;\n\n    @Schema(description = \"性别\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class, order = 3)\n    @DictFormat(cn.iocoder.yudao.module.system.enums.DictTypeConstants.USER_SEX)\n    private Integer sex;\n\n    @Schema(description = \"是否关键决策人\")\n    @ExcelProperty(value = \"是否关键决策人\", converter = DictConvert.class, order = 3)\n    @DictFormat(DictTypeConstants.BOOLEAN_STRING)\n    private Boolean master;\n\n    @Schema(description = \"职位\")\n    @ExcelProperty(value = \"职位\", order = 3)\n    private String post;\n\n    @Schema(description = \"直属上级\", example = \"23457\")\n    private Long parentId;\n    @Schema(description = \"直属上级名\", example = \"芋头\")\n    @ExcelProperty(value = \"直属上级\", order = 4)\n    private String parentName;\n\n    @Schema(description = \"备注\", example = \"你说的对\")\n    @ExcelProperty(value = \"备注\", order = 6)\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"25682\")\n    private String creator;\n    @Schema(description = \"创建人名字\", example = \"test\")\n    @ExcelProperty(value = \"创建人\", order = 8)\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"更新时间\")\n    private LocalDateTime updateTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.Mobile;\nimport cn.iocoder.yudao.framework.common.validation.Telephone;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.*;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;\n\n@Schema(description = \"管理后台 - CRM 联系人创建/更新 Request VO\")\n@Data\npublic class CrmContactSaveReqVO {\n\n    @Schema(description = \"主键\", example = \"3167\")\n    private Long id;\n\n    @Schema(description = \"姓名\", example = \"芋艿\")\n    @NotNull(message = \"姓名不能为空\")\n    @DiffLogField(name = \"姓名\")\n    private String name;\n\n    @Schema(description = \"客户编号\", example = \"10795\")\n    @NotNull(message = \"客户编号不能为空\")\n    @DiffLogField(name = \"客户\", function = CrmCustomerParseFunction.NAME)\n    private Long customerId;\n\n    @Schema(description = \"下次联系时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)\n    @DiffLogField(name = \"下次联系时间\")\n    private LocalDateTime contactNextTime;\n\n    @Schema(description = \"负责人用户编号\", example = \"14334\")\n    @NotNull(message = \"负责人不能为空\")\n    @DiffLogField(name = \"负责人\", function = SysAdminUserParseFunction.NAME)\n    private Long ownerUserId;\n\n    @Schema(description = \"手机号\", example = \"1387171766\")\n    @Mobile\n    @DiffLogField(name = \"手机号\")\n    private String mobile;\n\n    @Schema(description = \"电话\", example = \"021-0029922\")\n    @Telephone\n    @DiffLogField(name = \"电话\")\n    private String telephone;\n\n    @Schema(description = \"QQ\", example = \"197272662\")\n    @DiffLogField(name = \"QQ\")\n    private Long qq;\n\n    @Schema(description = \"微信\", example = \"zzz3883\")\n    @DiffLogField(name = \"微信\")\n    private String wechat;\n\n    @Schema(description = \"电子邮箱\", example = \"1111@22.com\")\n    @DiffLogField(name = \"邮箱\")\n    @Email\n    private String email;\n\n    @Schema(description = \"地区编号\", example = \"20158\")\n    @DiffLogField(name = \"所在地\", function = SysAreaParseFunction.NAME)\n    private Integer areaId;\n\n    @Schema(description = \"地址\")\n    @DiffLogField(name = \"地址\")\n    private String detailAddress;\n\n    @Schema(description = \"性别\")\n    @DiffLogField(name = \"性别\", function = SysSexParseFunction.NAME)\n    private Integer sex;\n\n    @Schema(description = \"是否关键决策人\")\n    @DiffLogField(name = \"关键决策人\", function = SysBooleanParseFunction.NAME)\n    private Boolean master;\n\n    @Schema(description = \"职位\")\n    @DiffLogField(name = \"职位\")\n    private String post;\n\n    @Schema(description = \"直属上级\", example = \"23457\")\n    @DiffLogField(name = \"直属上级\", function = CrmContactParseFunction.NAME)\n    private Long parentId;\n\n    @Schema(description = \"备注\", example = \"你说的对\")\n    @DiffLogField(name = \"备注\")\n    private String remark;\n\n    @Schema(description = \"关联商机 ID\", example = \"122233\")\n    private Long businessId; // 注意：该字段用于在【商机】详情界面「新建联系人」时，自动进行关联\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;\n\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - CRM 联系人转移 Request VO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmContactTransferReqVO {\n\n    @Schema(description = \"联系人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"联系人编号不能为空\")\n    private Long id;\n\n    /**\n     * 新负责人的用户编号\n     */\n    @Schema(description = \"新负责人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"新负责人的用户编号不能为空\")\n    private Long newOwnerUserId;\n\n    /**\n     * 老负责人加入团队后的权限级别。如果 null 说明移除\n     *\n     * 关联 {@link CrmPermissionLevelEnum}\n     */\n    @Schema(description = \"老负责人加入团队后的权限级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer oldOwnerPermissionLevel;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractConfigController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contract;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config.CrmContractConfigRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config.CrmContractConfigSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - CRM 合同配置\")\n@RestController\n@RequestMapping(\"/crm/contract-config\")\n@Validated\npublic class CrmContractConfigController {\n\n    @Resource\n    private CrmContractConfigService contractConfigService;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获取合同配置\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract-config:query')\")\n    public CommonResult<CrmContractConfigRespVO> getCustomerPoolConfig() {\n        CrmContractConfigDO config = contractConfigService.getContractConfig();\n        return success(BeanUtils.toBean(config, CrmContractConfigRespVO.class));\n    }\n\n    @PutMapping(\"/save\")\n    @Operation(summary = \"更新合同配置\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract-config:update')\")\n    public CommonResult<Boolean> saveCustomerPoolConfig(@Valid @RequestBody CrmContractConfigSaveReqVO updateReqVO) {\n        contractConfigService.saveContractConfig(updateReqVO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contract;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactService;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.crm.service.product.CrmProductService;\nimport cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\nimport static java.util.Collections.singletonList;\n\n@Tag(name = \"管理后台 - CRM 合同\")\n@RestController\n@RequestMapping(\"/crm/contract\")\n@Validated\npublic class CrmContractController {\n\n    @Resource\n    private CrmContractService contractService;\n    @Resource\n    private CrmCustomerService customerService;\n    @Resource\n    private CrmContactService contactService;\n    @Resource\n    private CrmBusinessService businessService;\n    @Resource\n    private CrmProductService productService;\n    @Resource\n    private CrmReceivableService receivableService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建合同\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:create')\")\n    public CommonResult<Long> createContract(@Valid @RequestBody CrmContractSaveReqVO createReqVO) {\n        return success(contractService.createContract(createReqVO, getLoginUserId()));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新合同\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:update')\")\n    public CommonResult<Boolean> updateContract(@Valid @RequestBody CrmContractSaveReqVO updateReqVO) {\n        contractService.updateContract(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除合同\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:delete')\")\n    public CommonResult<Boolean> deleteContract(@RequestParam(\"id\") Long id) {\n        contractService.deleteContract(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得合同\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:query')\")\n    public CommonResult<CrmContractRespVO> getContract(@RequestParam(\"id\") Long id) {\n        CrmContractDO contract = contractService.getContract(id);\n        return success(buildContractDetail(contract));\n    }\n\n    private CrmContractRespVO buildContractDetail(CrmContractDO contract) {\n        if (contract == null) {\n            return null;\n        }\n        CrmContractRespVO contractVO = buildContractDetailList(singletonList(contract)).get(0);\n        // 拼接产品项\n        List<CrmContractProductDO> businessProducts = contractService.getContractProductListByContractId(contractVO.getId());\n        Map<Long, CrmProductDO> productMap = productService.getProductMap(\n                convertSet(businessProducts, CrmContractProductDO::getProductId));\n        contractVO.setProducts(BeanUtils.toBean(businessProducts, CrmContractRespVO.Product.class, businessProductVO ->\n                MapUtils.findAndThen(productMap, businessProductVO.getProductId(),\n                        product -> businessProductVO.setProductName(product.getName())\n                                .setProductNo(product.getNo()).setProductUnit(product.getUnit()))));\n        return contractVO;\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得合同分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:query')\")\n    public CommonResult<PageResult<CrmContractRespVO>> getContractPage(@Valid CrmContractPageReqVO pageVO) {\n        PageResult<CrmContractDO> pageResult = contractService.getContractPage(pageVO, getLoginUserId());\n        return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList())));\n    }\n\n    @GetMapping(\"/page-by-customer\")\n    @Operation(summary = \"获得合同分页，基于指定客户\")\n    public CommonResult<PageResult<CrmContractRespVO>> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) {\n        Assert.notNull(pageVO.getCustomerId(), \"客户编号不能为空\");\n        PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageVO);\n        return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList())));\n    }\n\n    @GetMapping(\"/page-by-business\")\n    @Operation(summary = \"获得合同分页，基于指定商机\")\n    public CommonResult<PageResult<CrmContractRespVO>> getContractPageByBusiness(@Valid CrmContractPageReqVO pageVO) {\n        Assert.notNull(pageVO.getBusinessId(), \"商机编号不能为空\");\n        PageResult<CrmContractDO> pageResult = contractService.getContractPageByBusinessId(pageVO);\n        return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList())));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出合同 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportContractExcel(@Valid CrmContractPageReqVO exportReqVO,\n                                    HttpServletResponse response) throws IOException {\n        PageResult<CrmContractDO> pageResult = contractService.getContractPage(exportReqVO, getLoginUserId());\n        // 导出 Excel\n        ExcelUtils.write(response, \"合同.xls\", \"数据\", CrmContractRespVO.class,\n                BeanUtils.toBean(pageResult.getList(), CrmContractRespVO.class));\n    }\n\n    @PutMapping(\"/transfer\")\n    @Operation(summary = \"合同转移\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:update')\")\n    public CommonResult<Boolean> transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) {\n        contractService.transferContract(reqVO, getLoginUserId());\n        return success(true);\n    }\n\n    @PutMapping(\"/submit\")\n    @Operation(summary = \"提交合同审批\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:update')\")\n    public CommonResult<Boolean> submitContract(@RequestParam(\"id\") Long id) {\n        contractService.submitContract(id, getLoginUserId());\n        return success(true);\n    }\n\n    private List<CrmContractRespVO> buildContractDetailList(List<CrmContractDO> contractList) {\n        if (CollUtil.isEmpty(contractList)) {\n            return Collections.emptyList();\n        }\n        // 1.1 获取客户列表\n        Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(contractList, CrmContractDO::getCustomerId));\n        // 1.2 获取创建人、负责人列表\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contractList,\n                contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        // 1.3 获取联系人\n        Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactList(convertSet(contractList,\n                CrmContractDO::getSignContactId)), CrmContactDO::getId);\n        // 1.4 获取商机\n        Map<Long, CrmBusinessDO> businessMap = businessService.getBusinessMap(\n                convertSet(contractList, CrmContractDO::getBusinessId));\n        // 1.5 获得已回款金额\n        Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByContractId(\n                convertSet(contractList, CrmContractDO::getId));\n        // 2. 拼接数据\n        return BeanUtils.toBean(contractList, CrmContractRespVO.class, contractVO -> {\n            // 2.1 设置客户信息\n            findAndThen(customerMap, contractVO.getCustomerId(), customer -> contractVO.setCustomerName(customer.getName()));\n            // 2.2 设置用户信息\n            findAndThen(userMap, Long.parseLong(contractVO.getCreator()), user -> contractVO.setCreatorName(user.getNickname()));\n            MapUtils.findAndThen(userMap, contractVO.getOwnerUserId(), user -> {\n                contractVO.setOwnerUserName(user.getNickname());\n                MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> contractVO.setOwnerUserDeptName(dept.getName()));\n            });\n            findAndThen(userMap, contractVO.getSignUserId(), user -> contractVO.setSignUserName(user.getNickname()));\n            // 2.3 设置联系人信息\n            findAndThen(contactMap, contractVO.getSignContactId(), contact -> contractVO.setSignContactName(contact.getName()));\n            // 2.4 设置商机信息\n            findAndThen(businessMap, contractVO.getBusinessId(), business -> contractVO.setBusinessName(business.getName()));\n            // 2.5 设置已回款金额\n            contractVO.setTotalReceivablePrice(receivablePriceMap.getOrDefault(contractVO.getId(), BigDecimal.ZERO));\n        });\n    }\n\n    @GetMapping(\"/audit-count\")\n    @Operation(summary = \"获得待审核合同数量\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:query')\")\n    public CommonResult<Long> getAuditContractCount() {\n        return success(contractService.getAuditContractCount(getLoginUserId()));\n    }\n\n    @GetMapping(\"/remind-count\")\n    @Operation(summary = \"获得即将到期（提醒）的合同数量\")\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:query')\")\n    public CommonResult<Long> getRemindContractCount() {\n        return success(contractService.getRemindContractCount(getLoginUserId()));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得合同精简列表\", description = \"只包含的合同，主要用于前端的下拉选项\")\n    @Parameter(name = \"customerId\", description = \"客户编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:contract:query')\")\n    public CommonResult<List<CrmContractRespVO>> getContractSimpleList(@RequestParam(\"customerId\") Long customerId) {\n        CrmContractPageReqVO pageReqVO = new CrmContractPageReqVO().setCustomerId(customerId);\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); // 不分页\n        PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(Collections.emptyList());\n        }\n        // 拼接数据\n        Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByContractId(\n                convertSet(pageResult.getList(), CrmContractDO::getId));\n        return success(convertList(pageResult.getList(), contract -> new CrmContractRespVO() // 只返回 id、name 等精简字段\n                .setId(contract.getId()).setName(contract.getName()).setAuditStatus(contract.getAuditStatus())\n                .setTotalPrice(contract.getTotalPrice())\n                .setTotalReceivablePrice(receivablePriceMap.getOrDefault(contract.getId(), BigDecimal.ZERO))));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/config/CrmContractConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 合同配置 Response VO\")\n@Data\npublic class CrmContractConfigRespVO {\n\n    @Schema(description = \"是否开启提前提醒\", example = \"true\")\n    private Boolean notifyEnabled;\n\n    @Schema(description = \"提前提醒天数\", example = \"2\")\n    private Integer notifyDays;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/config/CrmContractConfigSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config;\n\nimport cn.hutool.core.util.BooleanUtil;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.AssertTrue;\nimport java.util.Objects;\n\n@Schema(description = \"管理后台 - CRM 合同配置 Request VO\")\n@Data\npublic class CrmContractConfigSaveReqVO {\n\n    @Schema(description = \"是否开启提前提醒\", example = \"true\")\n    @DiffLogField(name = \"是否开启提前提醒\")\n    private Boolean notifyEnabled;\n\n    @Schema(description = \"提前提醒天数\", example = \"2\")\n    @DiffLogField(name = \"提前提醒天数\")\n    private Integer notifyDays;\n\n    @AssertTrue(message = \"提前提醒天数不能为空\")\n    @JsonIgnore\n    public boolean isNotifyDaysValid() {\n        if (!BooleanUtil.isTrue(getNotifyEnabled())) {\n            return true;\n        }\n        return Objects.nonNull(getNotifyDays());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - CRM 合同分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmContractPageReqVO extends PageParam {\n\n    /**\n     * 过期类型 - 即将过期\n     */\n    public static final Integer EXPIRY_TYPE_ABOUT_TO_EXPIRE = 1;\n    /**\n     * 过期类型 - 已过期\n     */\n    public static  final Integer EXPIRY_TYPE_EXPIRED = 2;\n\n    @Schema(description = \"合同编号\", example = \"XYZ008\")\n    private String no;\n\n    @Schema(description = \"合同名称\", example = \"王五\")\n    private String name;\n\n    @Schema(description = \"客户编号\", example = \"18336\")\n    private Long customerId;\n\n    @Schema(description = \"商机编号\", example = \"10864\")\n    private Long businessId;\n\n    @Schema(description = \"场景类型\", example = \"1\")\n    @InEnum(CrmSceneTypeEnum.class)\n    private Integer sceneType; // 场景类型，为 null 时则表示全部\n\n    @Schema(description = \"审批状态\", example = \"20\")\n    @InEnum(CrmAuditStatusEnum.class)\n    private Integer auditStatus;\n\n    @Schema(description = \"过期类型\", example = \"1\")\n    private Integer expiryType; // 过期类型，为 null 时则表示全部\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - CRM 合同 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class CrmContractRespVO {\n\n    @Schema(description = \"合同编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @ExcelProperty(\"合同编号\")\n    private Long id;\n\n    @Schema(description = \"合同名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    @ExcelProperty(\"合同名称\")\n    private String name;\n\n    @Schema(description = \"合同编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20230101\")\n    @ExcelProperty(\"合同编号\")\n    private String no;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18336\")\n    @ExcelProperty(\"客户编号\")\n    private Long customerId;\n    @Schema(description = \"客户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18336\")\n    @ExcelProperty(\"客户名称\")\n    private String customerName;\n\n    @Schema(description = \"商机编号\", example = \"10864\")\n    @ExcelProperty(\"商机编号\")\n    private Long businessId;\n    @Schema(description = \"商机名称\", example = \"10864\")\n    @ExcelProperty(\"商机名称\")\n    private String businessName;\n\n    @Schema(description = \"最后跟进时间\")\n    @ExcelProperty(\"最后跟进时间\")\n    private LocalDateTime contactLastTime;\n\n    @Schema(description = \"负责人的用户编号\", example = \"25682\")\n    @ExcelProperty(\"负责人的用户编号\")\n    private Long ownerUserId;\n    @Schema(description = \"负责人名字\", example = \"25682\")\n    @ExcelProperty(\"负责人名字\")\n    private String ownerUserName;\n    @Schema(description = \"负责人部门\")\n    @ExcelProperty(\"负责人部门\")\n    private String ownerUserDeptName;\n\n    @Schema(description = \"工作流编号\", example = \"1043\")\n    @ExcelProperty(\"工作流编号\")\n    private String processInstanceId;\n\n    @Schema(description = \"审批状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @ExcelProperty(\"审批状态\")\n    private Integer auditStatus;\n\n    @Schema(description = \"下单日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"下单日期\")\n    private LocalDateTime orderDate;\n\n    @Schema(description = \"开始时间\")\n    @ExcelProperty(\"开始时间\")\n    private LocalDateTime startTime;\n\n    @Schema(description = \"结束时间\")\n    @ExcelProperty(\"结束时间\")\n    private LocalDateTime endTime;\n\n    @Schema(description = \"产品总金额\", example = \"19510\")\n    @ExcelProperty(\"产品总金额\")\n    private BigDecimal totalProductPrice;\n\n    @Schema(description = \"整单折扣\")\n    @ExcelProperty(\"整单折扣\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"合同金额\", example = \"5617\")\n    @ExcelProperty(\"合同金额\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"已回款金额\", example = \"5617\")\n    @ExcelProperty(\"已回款金额\")\n    private BigDecimal totalReceivablePrice;\n\n    @Schema(description = \"客户签约人编号\", example = \"18546\")\n    private Long signContactId;\n    @Schema(description = \"客户签约人\", example = \"小豆\")\n    @ExcelProperty(\"客户签约人\")\n    private String signContactName;\n\n    @Schema(description = \"公司签约人\", example = \"14036\")\n    private Long signUserId;\n    @Schema(description = \"公司签约人\", example = \"小明\")\n    @ExcelProperty(\"公司签约人\")\n    private String signUserName;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"创建人\", example = \"25682\")\n    @ExcelProperty(\"创建人\")\n    private String creator;\n\n    @Schema(description = \"创建人名字\", example = \"test\")\n    @ExcelProperty(\"创建人名字\")\n    private String creatorName;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"更新时间\")\n    private LocalDateTime updateTime;\n\n    @Schema(description = \"产品列表\")\n    private List<Product> products;\n\n    @Schema(description = \"产品列表\")\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Product {\n\n        @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n        private Long id;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20529\")\n        private Long productId;\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20529\")\n        private String productNo;\n        @Schema(description = \"产品单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n        private Integer productUnit;\n\n        @Schema(description = \"产品单价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"合同价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        private BigDecimal contractPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8911\")\n        private BigDecimal count;\n\n        @Schema(description = \"总计价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        private BigDecimal totalPrice;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract;\n\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmBusinessParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmContactParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - CRM 合同创建/更新 Request VO\")\n@Data\npublic class CrmContractSaveReqVO {\n\n    @Schema(description = \"合同编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    private Long id;\n\n    @Schema(description = \"合同名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    @DiffLogField(name = \"合同名称\")\n    @NotNull(message = \"合同名称不能为空\")\n    private String name;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18336\")\n    @DiffLogField(name = \"客户\", function = CrmCustomerParseFunction.NAME)\n    @NotNull(message = \"客户编号不能为空\")\n    private Long customerId;\n\n    @Schema(description = \"商机编号\", example = \"10864\")\n    @DiffLogField(name = \"商机\", function = CrmBusinessParseFunction.NAME)\n    private Long businessId;\n\n    @Schema(description = \"负责人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17144\")\n    @DiffLogField(name = \"负责人\", function = SysAdminUserParseFunction.NAME)\n    @NotNull(message = \"负责人不能为空\")\n    private Long ownerUserId;\n\n    @Schema(description = \"下单日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @DiffLogField(name = \"下单日期\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @NotNull(message = \"下单日期不能为空\")\n    private LocalDateTime orderDate;\n\n    @Schema(description = \"开始时间\")\n    @DiffLogField(name = \"开始时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"结束时间\")\n    @DiffLogField(name = \"结束时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"整单折扣\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"55.00\")\n    @DiffLogField(name = \"整单折扣\")\n    @NotNull(message = \"整单折扣不能为空\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"合同金额\", example = \"5617\")\n    @DiffLogField(name = \"合同金额\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"客户签约人编号\", example = \"18546\")\n    @DiffLogField(name = \"客户签约人\", function = CrmContactParseFunction.NAME)\n    private Long signContactId;\n\n    @Schema(description = \"公司签约人\", example = \"14036\")\n    @DiffLogField(name = \"公司签约人\", function = SysAdminUserParseFunction.NAME)\n    private Long signUserId;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @DiffLogField(name = \"备注\")\n    private String remark;\n\n    @Schema(description = \"产品列表\")\n    private List<Product> products;\n\n    @Schema(description = \"产品列表\")\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Product {\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20529\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        @NotNull(message = \"产品单价不能为空\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"合同价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123.00\")\n        @NotNull(message = \"合同价格不能为空\")\n        private BigDecimal contractPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8911\")\n        @NotNull(message = \"产品数量不能为空\")\n        private Integer count;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractTransferReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - CRM 合同转移 Request VO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmContractTransferReqVO {\n\n    @Schema(description = \"合同编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"联系人编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"新负责人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"新负责人的用户编号不能为空\")\n    private Long newOwnerUserId;\n\n    @Schema(description = \"老负责人加入团队后的权限级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @InEnum(value = CrmPermissionLevelEnum.class)\n    private Integer oldOwnerPermissionLevel;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.time.LocalDateTime;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\nimport static java.util.Collections.singletonList;\n\n@Tag(name = \"管理后台 - CRM 客户\")\n@RestController\n@RequestMapping(\"/crm/customer\")\n@Validated\npublic class CrmCustomerController {\n\n    @Resource\n    private CrmCustomerService customerService;\n    @Resource\n    private CrmCustomerPoolConfigService customerPoolConfigService;\n\n    @Resource\n    private DeptApi deptApi;\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建客户\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:create')\")\n    public CommonResult<Long> createCustomer(@Valid @RequestBody CrmCustomerSaveReqVO createReqVO) {\n        return success(customerService.createCustomer(createReqVO, getLoginUserId()));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新客户\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:update')\")\n    public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerSaveReqVO updateReqVO) {\n        customerService.updateCustomer(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-deal-status\")\n    @Operation(summary = \"更新客户的成交状态\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"客户编号\", required = true),\n            @Parameter(name = \"dealStatus\", description = \"成交状态\", required = true)\n    })\n    public CommonResult<Boolean> updateCustomerDealStatus(@RequestParam(\"id\") Long id,\n                                                          @RequestParam(\"dealStatus\") Boolean dealStatus) {\n        customerService.updateCustomerDealStatus(id, dealStatus);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除客户\")\n    @Parameter(name = \"id\", description = \"客户编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:delete')\")\n    public CommonResult<Boolean> deleteCustomer(@RequestParam(\"id\") Long id) {\n        customerService.deleteCustomer(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得客户\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:query')\")\n    public CommonResult<CrmCustomerRespVO> getCustomer(@RequestParam(\"id\") Long id) {\n        // 1. 获取客户\n        CrmCustomerDO customer = customerService.getCustomer(id);\n        // 2. 拼接数据\n        return success(buildCustomerDetail(customer));\n    }\n\n    public CrmCustomerRespVO buildCustomerDetail(CrmCustomerDO customer) {\n        if (customer == null) {\n            return null;\n        }\n        return buildCustomerDetailList(singletonList(customer)).get(0);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得客户分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:query')\")\n    public CommonResult<PageResult<CrmCustomerRespVO>> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {\n        // 1. 查询客户分页\n        PageResult<CrmCustomerDO> pageResult = customerService.getCustomerPage(pageVO, getLoginUserId());\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n        // 2. 拼接数据\n        return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    public List<CrmCustomerRespVO> buildCustomerDetailList(List<CrmCustomerDO> list) {\n        if (CollUtil.isEmpty(list)) {\n            return java.util.Collections.emptyList();\n        }\n        // 1.1 获取创建人、负责人列表\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(list,\n                contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        // 1.2 获取距离进入公海的时间\n        Map<Long, Long> poolDayMap = getPoolDayMap(list);\n        // 2. 转换成 VO\n        return BeanUtils.toBean(list, CrmCustomerRespVO.class, customerVO -> {\n            customerVO.setAreaName(AreaUtils.format(customerVO.getAreaId()));\n            // 2.1 设置创建人、负责人名称\n            MapUtils.findAndThen(userMap, NumberUtils.parseLong(customerVO.getCreator()),\n                    user -> customerVO.setCreatorName(user.getNickname()));\n            MapUtils.findAndThen(userMap, customerVO.getOwnerUserId(), user -> {\n                customerVO.setOwnerUserName(user.getNickname());\n                MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> customerVO.setOwnerUserDeptName(dept.getName()));\n            });\n            // 2.2 设置距离进入公海的时间\n            if (customerVO.getOwnerUserId() != null) {\n                customerVO.setPoolDay(poolDayMap.get(customerVO.getId()));\n            }\n        });\n    }\n\n    @GetMapping(\"/put-pool-remind-page\")\n    @Operation(summary = \"获得待进入公海客户分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:query')\")\n    public CommonResult<PageResult<CrmCustomerRespVO>> getPutPoolRemindCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {\n        // 1. 查询客户分页\n        PageResult<CrmCustomerDO> pageResult = customerService.getPutPoolRemindCustomerPage(pageVO, getLoginUserId());\n        // 2. 拼接数据\n        return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/put-pool-remind-count\")\n    @Operation(summary = \"获得待进入公海客户数量\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:query')\")\n    public CommonResult<Long> getPutPoolRemindCustomerCount() {\n        return success(customerService.getPutPoolRemindCustomerCount(getLoginUserId()));\n    }\n\n    @GetMapping(\"/today-contact-count\")\n    @Operation(summary = \"获得今日需联系客户数量\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:query')\")\n    public CommonResult<Long> getTodayContactCustomerCount() {\n        return success(customerService.getTodayContactCustomerCount(getLoginUserId()));\n    }\n\n    @GetMapping(\"/follow-count\")\n    @Operation(summary = \"获得分配给我、待跟进的线索数量的客户数量\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:query')\")\n    public CommonResult<Long> getFollowCustomerCount() {\n        return success(customerService.getFollowCustomerCount(getLoginUserId()));\n    }\n\n    /**\n     * 获取距离进入公海的时间 Map\n     *\n     * @param list 客户列表\n     * @return key 客户编号, value 距离进入公海的时间\n     */\n    private Map<Long, Long> getPoolDayMap(List<CrmCustomerDO> list) {\n        CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();\n        if (poolConfig == null || !poolConfig.getEnabled()) {\n            return MapUtil.empty();\n        }\n        list = CollectionUtils.filterList(list, customer -> {\n            // 特殊：如果没负责人，则说明已经在公海，不用计算\n            if (customer.getOwnerUserId() == null) {\n                return false;\n            }\n            // 已成交 or 已锁定，不进入公海\n            return !customer.getDealStatus() && !customer.getLockStatus();\n        });\n        return convertMap(list, CrmCustomerDO::getId, customer -> {\n            // 1.1 未成交放入公海天数\n            long dealExpireDay = poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getOwnerTime());\n            // 1.2 未跟进放入公海天数\n            LocalDateTime lastTime = customer.getOwnerTime();\n            if (customer.getContactLastTime() != null && customer.getContactLastTime().isAfter(lastTime)) {\n                lastTime = customer.getContactLastTime();\n            }\n            long contactExpireDay = poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime);\n            // 2. 返回最小的天数\n            long poolDay = Math.min(dealExpireDay, contactExpireDay);\n            return poolDay > 0 ? poolDay : 0;\n        });\n    }\n\n    @GetMapping(value = \"/simple-list\")\n    @Operation(summary = \"获取客户精简信息列表\", description = \"只包含有读权限的客户，主要用于前端的下拉选项\")\n    public CommonResult<List<CrmCustomerRespVO>> getCustomerSimpleList() {\n        CrmCustomerPageReqVO reqVO = new CrmCustomerPageReqVO();\n        reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页\n        List<CrmCustomerDO> list = customerService.getCustomerPage(reqVO, getLoginUserId()).getList();\n        return success(convertList(list, customer -> // 只返回 id、name 精简字段\n                new CrmCustomerRespVO().setId(customer.getId()).setName(customer.getName())));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出客户 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportCustomerExcel(@Valid CrmCustomerPageReqVO pageVO,\n                                    HttpServletResponse response) throws IOException {\n        pageVO.setPageSize(PAGE_SIZE_NONE); // 不分页\n        List<CrmCustomerDO> list = customerService.getCustomerPage(pageVO, getLoginUserId()).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"客户.xls\", \"数据\", CrmCustomerRespVO.class,\n                buildCustomerDetailList(list));\n    }\n\n    @GetMapping(\"/get-import-template\")\n    @Operation(summary = \"获得导入客户模板\")\n    public void importTemplate(HttpServletResponse response) throws IOException {\n        // 手动创建导出 demo\n        List<CrmCustomerImportExcelVO> list = Arrays.asList(\n                CrmCustomerImportExcelVO.builder().name(\"芋道\").industryId(1).level(1).source(1)\n                        .mobile(\"15601691300\").telephone(\"\").qq(\"\").wechat(\"\").email(\"yunai@iocoder.cn\")\n                        .areaId(null).detailAddress(\"\").remark(\"\").build(),\n                CrmCustomerImportExcelVO.builder().name(\"源码\").industryId(1).level(1).source(1)\n                        .mobile(\"15601691300\").telephone(\"\").qq(\"\").wechat(\"\").email(\"yunai@iocoder.cn\")\n                        .areaId(null).detailAddress(\"\").remark(\"\").build()\n        );\n        // 输出\n        ExcelUtils.write(response, \"客户导入模板.xls\", \"客户列表\", CrmCustomerImportExcelVO.class, list);\n    }\n\n    @PostMapping(\"/import\")\n    @Operation(summary = \"导入客户\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:import')\")\n    public CommonResult<CrmCustomerImportRespVO> importExcel(@Valid CrmCustomerImportReqVO importReqVO)\n            throws Exception {\n        List<CrmCustomerImportExcelVO> list = ExcelUtils.read(importReqVO.getFile(), CrmCustomerImportExcelVO.class);\n        return success(customerService.importCustomerList(list, importReqVO));\n    }\n\n    @PutMapping(\"/transfer\")\n    @Operation(summary = \"转移客户\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:update')\")\n    public CommonResult<Boolean> transferCustomer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {\n        customerService.transferCustomer(reqVO, getLoginUserId());\n        return success(true);\n    }\n\n    @PutMapping(\"/lock\")\n    @Operation(summary = \"锁定/解锁客户\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:update')\")\n    public CommonResult<Boolean> lockCustomer(@Valid @RequestBody CrmCustomerLockReqVO lockReqVO) {\n        customerService.lockCustomer(lockReqVO, getLoginUserId());\n        return success(true);\n    }\n\n    // ==================== 公海相关操作 ====================\n\n    @PutMapping(\"/put-pool\")\n    @Operation(summary = \"数据放入公海\")\n    @Parameter(name = \"id\", description = \"客户编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:update')\")\n    public CommonResult<Boolean> putCustomerPool(@RequestParam(\"id\") Long id) {\n        customerService.putCustomerPool(id);\n        return success(true);\n    }\n\n    @PutMapping(\"/receive\")\n    @Operation(summary = \"领取公海客户\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true, example = \"1,2,3\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:receive')\")\n    public CommonResult<Boolean> receiveCustomer(@RequestParam(value = \"ids\") List<Long> ids) {\n        customerService.receiveCustomer(ids, getLoginUserId(), Boolean.TRUE);\n        return success(true);\n    }\n\n    @PutMapping(\"/distribute\")\n    @Operation(summary = \"分配公海给对应负责人\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer:distribute')\")\n    public CommonResult<Boolean> distributeCustomer(@Valid @RequestBody CrmCustomerDistributeReqVO distributeReqVO) {\n        customerService.receiveCustomer(distributeReqVO.getIds(), distributeReqVO.getOwnerUserId(), Boolean.FALSE);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerLimitConfigController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerLimitConfigService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;\n\n@Tag(name = \"管理后台 - CRM 客户限制配置\")\n@RestController\n@RequestMapping(\"/crm/customer-limit-config\")\n@Validated\npublic class CrmCustomerLimitConfigController {\n\n    @Resource\n    private CrmCustomerLimitConfigService customerLimitConfigService;\n\n    @Resource\n    private DeptApi deptApi;\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建客户限制配置\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer-limit-config:create')\")\n    public CommonResult<Long> createCustomerLimitConfig(@Valid @RequestBody CrmCustomerLimitConfigSaveReqVO createReqVO) {\n        return success(customerLimitConfigService.createCustomerLimitConfig(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新客户限制配置\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer-limit-config:update')\")\n    public CommonResult<Boolean> updateCustomerLimitConfig(@Valid @RequestBody CrmCustomerLimitConfigSaveReqVO updateReqVO) {\n        customerLimitConfigService.updateCustomerLimitConfig(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除客户限制配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:customer-limit-config:delete')\")\n    public CommonResult<Boolean> deleteCustomerLimitConfig(@RequestParam(\"id\") Long id) {\n        customerLimitConfigService.deleteCustomerLimitConfig(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得客户限制配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer-limit-config:query')\")\n    public CommonResult<CrmCustomerLimitConfigRespVO> getCustomerLimitConfig(@RequestParam(\"id\") Long id) {\n        CrmCustomerLimitConfigDO limitConfig = customerLimitConfigService.getCustomerLimitConfig(id);\n        // 拼接数据\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(limitConfig.getUserIds());\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(limitConfig.getDeptIds());\n        return success(BeanUtils.toBean(limitConfig, CrmCustomerLimitConfigRespVO.class, configVO -> {\n            configVO.setUsers(CollectionUtils.convertList(configVO.getUserIds(), userMap::get));\n            configVO.setDepts(CollectionUtils.convertList(configVO.getDeptIds(), deptMap::get));\n        }));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得客户限制配置分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer-limit-config:query')\")\n    public CommonResult<PageResult<CrmCustomerLimitConfigRespVO>> getCustomerLimitConfigPage(@Valid CrmCustomerLimitConfigPageReqVO pageVO) {\n        PageResult<CrmCustomerLimitConfigDO> pageResult = customerLimitConfigService.getCustomerLimitConfigPage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n        // 拼接数据\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSetByFlatMap(pageResult.getList(), CrmCustomerLimitConfigDO::getUserIds, Collection::stream));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(\n                convertSetByFlatMap(pageResult.getList(), CrmCustomerLimitConfigDO::getDeptIds, Collection::stream));\n        return success(BeanUtils.toBean(pageResult, CrmCustomerLimitConfigRespVO.class, configVO -> {\n            configVO.setUsers(CollectionUtils.convertList(configVO.getUserIds(), userMap::get));\n            configVO.setDepts(CollectionUtils.convertList(configVO.getDeptIds(), deptMap::get));\n        }));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerPoolConfigController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - CRM 客户公海配置\")\n@RestController\n@RequestMapping(\"/crm/customer-pool-config\")\n@Validated\npublic class CrmCustomerPoolConfigController {\n\n    @Resource\n    private CrmCustomerPoolConfigService customerPoolConfigService;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获取客户公海规则设置\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer-pool-config:query')\")\n    public CommonResult<CrmCustomerPoolConfigRespVO> getCustomerPoolConfig() {\n        CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();\n        return success(BeanUtils.toBean(poolConfig, CrmCustomerPoolConfigRespVO.class));\n    }\n\n    @PutMapping(\"/save\")\n    @Operation(summary = \"更新客户公海规则设置\")\n    @PreAuthorize(\"@ss.hasPermission('crm:customer-pool-config:update')\")\n    public CommonResult<Boolean> saveCustomerPoolConfig(@Valid @RequestBody CrmCustomerPoolConfigSaveReqVO updateReqVO) {\n        customerPoolConfigService.saveCustomerPoolConfig(updateReqVO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerDistributeReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - CRM 客户分配公海给对应负责人 Request VO\")\n@Data\npublic class CrmCustomerDistributeReqVO {\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1024]\")\n    @NotEmpty(message = \"客户编号不能为空\")\n    private List<Long> ids;\n\n    @Schema(description = \"负责人\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"负责人不能为空\")\n    private Long ownerUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;\nimport cn.iocoder.yudao.framework.excel.core.convert.AreaConvert;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunction;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;\n\n/**\n * 客户 Excel 导入 VO\n */\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class CrmCustomerImportExcelVO {\n\n    @ExcelProperty(\"客户名称\")\n    private String name;\n\n    @ExcelProperty(\"手机\")\n    private String mobile;\n\n    @ExcelProperty(\"电话\")\n    private String telephone;\n\n    @ExcelProperty(\"QQ\")\n    private String qq;\n\n    @ExcelProperty(\"微信\")\n    private String wechat;\n\n    @ExcelProperty(\"邮箱\")\n    private String email;\n\n    @ExcelProperty(value = \"地区\", converter = AreaConvert.class)\n    @ExcelColumnSelect(functionName = AreaExcelColumnSelectFunction.NAME)\n    private Integer areaId;\n\n    @ExcelProperty(\"详细地址\")\n    private String detailAddress;\n\n    @ExcelProperty(value = \"所属行业\", converter = DictConvert.class)\n    @DictFormat(CRM_CUSTOMER_INDUSTRY)\n    @ExcelColumnSelect(dictType = CRM_CUSTOMER_INDUSTRY)\n    private Integer industryId;\n\n    @ExcelProperty(value = \"客户等级\", converter = DictConvert.class)\n    @DictFormat(CRM_CUSTOMER_LEVEL)\n    @ExcelColumnSelect(dictType = CRM_CUSTOMER_LEVEL)\n    private Integer level;\n\n    @ExcelProperty(value = \"客户来源\", converter = DictConvert.class)\n    @DictFormat(CRM_CUSTOMER_SOURCE)\n    @ExcelColumnSelect(dictType = CRM_CUSTOMER_SOURCE)\n    private Integer source;\n\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Builder;\nimport lombok.Data;\nimport org.springframework.web.multipart.MultipartFile;\n\n@Schema(description = \"管理后台 - 客户导入 Request VO\")\n@Data\n@Builder\npublic class CrmCustomerImportReqVO {\n\n    @Schema(description = \"Excel 文件\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"Excel 文件不能为空\")\n    private MultipartFile file;\n\n    @Schema(description = \"是否支持更新\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否支持更新不能为空\")\n    private Boolean updateSupport;\n\n    @Schema(description = \"负责人\", example = \"1\")\n    private Long ownerUserId; // 为 null 则客户进入公海\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Builder;\nimport lombok.Data;\n\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - 客户导入 Response VO\")\n@Data\n@Builder\npublic class CrmCustomerImportRespVO {\n\n    @Schema(description = \"创建成功的客户名数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> createCustomerNames;\n\n    @Schema(description = \"更新成功的客户名数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> updateCustomerNames;\n\n    @Schema(description = \"导入失败的客户集合，key 为客户名，value 为失败原因\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Map<String, String> failureCustomerNames;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerLockReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户锁定/解锁 Request VO\")\n@Data\npublic class CrmCustomerLockReqVO {\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    private Long id;\n\n    @Schema(description = \"客户锁定状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Boolean lockStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - CRM 客户分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmCustomerPageReqVO extends PageParam {\n\n    /**\n     * 联系状态 - 今日需联系\n     */\n    public static final int CONTACT_TODAY = 1;\n    /**\n     * 联系状态 - 已逾期\n     */\n    public static final int CONTACT_EXPIRED = 2;\n    /**\n     * 联系状态 - 已联系\n     */\n    public static final int CONTACT_ALREADY = 3;\n\n    @Schema(description = \"客户名称\", example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"手机\", example = \"18000000000\")\n    private String mobile;\n\n    @Schema(description = \"所属行业\", example = \"1\")\n    private Integer industryId;\n\n    @Schema(description = \"客户等级\", example = \"1\")\n    private Integer level;\n\n    @Schema(description = \"客户来源\", example = \"1\")\n    private Integer source;\n\n    @Schema(description = \"场景类型\", example = \"1\")\n    @InEnum(CrmSceneTypeEnum.class)\n    private Integer sceneType; // 场景类型，为 null 时则表示全部\n\n    @Schema(description = \"是否为公海数据\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    private Boolean pool; // null 则表示为不是公海数据\n\n    @Schema(description = \"联系状态\", example = \"1\")\n    private Integer contactStatus; // backlog 查询条件\n\n    @Schema(description = \"跟进状态\", example = \"true\")\n    private Boolean followUpStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.infra.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 客户 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class CrmCustomerRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"客户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(\"客户名称\")\n    private String name;\n\n    @Schema(description = \"跟进状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(value = \"跟进状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.BOOLEAN_STRING)\n    private Boolean followUpStatus;\n\n    @Schema(description = \"最后跟进时间\")\n    @ExcelProperty(\"最后跟进时间\")\n    private LocalDateTime contactLastTime;\n\n    @Schema(description = \"最后跟进内容\", example = \"吃饭、睡觉、打逗逗\")\n    @ExcelProperty(\"最后跟进内容\")\n    private String contactLastContent;\n\n    @Schema(description = \"下次联系时间\")\n    @ExcelProperty(\"下次联系时间\")\n    private LocalDateTime contactNextTime;\n\n    @Schema(description = \"负责人的用户编号\", example = \"25682\")\n    @ExcelProperty(\"负责人的用户编号\")\n    private Long ownerUserId;\n    @Schema(description = \"负责人名字\", example = \"25682\")\n    @ExcelProperty(\"负责人名字\")\n    private String ownerUserName;\n    @Schema(description = \"负责人部门\")\n    @ExcelProperty(\"负责人部门\")\n    private String ownerUserDeptName;\n\n    @Schema(description = \"锁定状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(value = \"锁定状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.BOOLEAN_STRING)\n    private Boolean lockStatus;\n\n    @Schema(description = \"成交状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(value = \"成交状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.BOOLEAN_STRING)\n    private Boolean dealStatus;\n\n    @Schema(description = \"手机\", example = \"25682\")\n    @ExcelProperty(\"手机\")\n    private String mobile;\n\n    @Schema(description = \"电话\", example = \"25682\")\n    @ExcelProperty(\"电话\")\n    private String telephone;\n\n    @Schema(description = \"QQ\", example = \"25682\")\n    @ExcelProperty(\"QQ\")\n    private String qq;\n\n    @Schema(description = \"wechat\", example = \"25682\")\n    @ExcelProperty(\"wechat\")\n    private String wechat;\n\n    @Schema(description = \"email\", example = \"25682\")\n    @ExcelProperty(\"email\")\n    private String email;\n\n    @Schema(description = \"地区编号\", example = \"1024\")\n    @ExcelProperty(\"地区编号\")\n    private Integer areaId;\n    @Schema(description = \"地区名称\", example = \"北京市\")\n    @ExcelProperty(\"地区名称\")\n    private String areaName;\n    @Schema(description = \"详细地址\", example = \"北京市成华大道\")\n    @ExcelProperty(\"详细地址\")\n    private String detailAddress;\n\n    @Schema(description = \"所属行业\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(value = \"所属行业\", converter = DictConvert.class)\n    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY)\n    private Integer industryId;\n\n    @Schema(description = \"客户等级\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(value = \"客户等级\", converter = DictConvert.class)\n    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL)\n    private Integer level;\n\n    @Schema(description = \"客户来源\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @ExcelProperty(value = \"客户来源\", converter = DictConvert.class)\n    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE)\n    private Integer source;\n\n    @Schema(description = \"负责人的用户编号\", example = \"25682\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"更新时间\")\n    private LocalDateTime updateTime;\n\n    @Schema(description = \"创建人\", example = \"1024\")\n    @ExcelProperty(\"创建人\")\n    private String creator;\n    @Schema(description = \"创建人名字\", example = \"芋道源码\")\n    @ExcelProperty(\"创建人名字\")\n    private String creatorName;\n\n    @Schema(description = \"距离加入公海时间\", example = \"1\")\n    private Long poolDay;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.framework.common.validation.Mobile;\nimport cn.iocoder.yudao.framework.common.validation.Telephone;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerIndustryParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerLevelParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerSourceParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAreaParseFunction;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\nimport static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;\n\n@Schema(description = \"管理后台 - CRM 客户新增/修改 Request VO\")\n@Data\npublic class CrmCustomerSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    private Long id;\n\n    @Schema(description = \"客户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    @DiffLogField(name = \"客户名称\")\n    @NotEmpty(message = \"客户名称不能为空\")\n    private String name;\n\n    @Schema(description = \"下次联系时间\")\n    @DiffLogField(name = \"下次联系时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime contactNextTime;\n\n    @Schema(description = \"负责人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    @NotNull(message = \"负责人的用户编号不能为空\")\n    private Long ownerUserId;\n\n    @Schema(description = \"手机\", example = \"18000000000\")\n    @DiffLogField(name = \"手机\")\n    @Mobile\n    private String mobile;\n\n    @Schema(description = \"电话\", example = \"18000000000\")\n    @DiffLogField(name = \"电话\")\n    @Telephone\n    private String telephone;\n\n    @Schema(description = \"QQ\", example = \"123456789\")\n    @DiffLogField(name = \"QQ\")\n    @Size(max = 20, message = \"QQ长度不能超过 20 个字符\")\n    private String qq;\n\n    @Schema(description = \"微信\", example = \"123456789\")\n    @DiffLogField(name = \"微信\")\n    @Size(max = 255, message = \"微信长度不能超过 255 个字符\")\n    private String wechat;\n\n    @Schema(description = \"邮箱\", example = \"123456789@qq.com\")\n    @DiffLogField(name = \"邮箱\")\n    @Email(message = \"邮箱格式不正确\")\n    @Size(max = 255, message = \"邮箱长度不能超过 255 个字符\")\n    private String email;\n\n    @Schema(description = \"地区编号\", example = \"20158\")\n    @DiffLogField(name = \"地区编号\", function = SysAreaParseFunction.NAME)\n    private Integer areaId;\n\n    @Schema(description = \"详细地址\", example = \"北京市海淀区\")\n    @DiffLogField(name = \"详细地址\")\n    private String detailAddress;\n\n    @Schema(description = \"所属行业\", example = \"1\")\n    @DiffLogField(name = \"所属行业\", function = CrmCustomerIndustryParseFunction.NAME)\n    @DictFormat(CRM_CUSTOMER_INDUSTRY)\n    private Integer industryId;\n\n    @Schema(description = \"客户等级\", example = \"2\")\n    @DiffLogField(name = \"客户等级\", function = CrmCustomerLevelParseFunction.NAME)\n    @InEnum(CrmCustomerLevelEnum.class)\n    private Integer level;\n\n    @Schema(description = \"客户来源\", example = \"3\")\n    @DiffLogField(name = \"客户来源\", function = CrmCustomerSourceParseFunction.NAME)\n    private Integer source;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @DiffLogField(name = \"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;\n\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - CRM 客户转移 Request VO\")\n@Data\npublic class CrmCustomerTransferReqVO {\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"客户编号不能为空\")\n    private Long id;\n\n    /**\n     * 新负责人的用户编号\n     */\n    @Schema(description = \"新负责人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    @NotNull(message = \"新负责人的用户编号不能为空\")\n    private Long newOwnerUserId;\n\n    /**\n     * 老负责人加入团队后的权限级别。如果 null 说明移除\n     *\n     * 关联 {@link CrmPermissionLevelEnum}\n     */\n    @Schema(description = \"老负责人加入团队后的权限级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer oldOwnerPermissionLevel;\n\n    /**\n     * 转移客户时，需要额外有【联系人】【商机】【合同】的 checkbox 选择。选中时，也一起转移\n     */\n    @Schema(description = \"同时转移\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    private List<Integer> toBizTypes;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 客户限制配置分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmCustomerLimitConfigPageReqVO extends PageParam {\n\n    @Schema(description = \"规则类型\", example = \"1\")\n    private Integer type;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig;\n\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 客户限制配置 Response VO\")\n@Data\npublic class CrmCustomerLimitConfigRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"27930\")\n    private Long id;\n\n    @Schema(description = \"规则类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer type;\n\n    @Schema(description = \"规则适用人群\")\n    private List<Long> userIds;\n\n    @Schema(description = \"规则适用部门\")\n    private List<Long> deptIds;\n\n    @Schema(description = \"数量上限\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28384\")\n    private Integer maxCount;\n\n    @Schema(description = \"成交客户是否占有拥有客户数\")\n    private Boolean dealCountEnabled;\n\n    @Schema(description = \"规则适用人群名称\")\n    private List<AdminUserRespDTO> users;\n\n    @Schema(description = \"规则适用部门名称\")\n    private List<DeptRespDTO> depts;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig;\n\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.SysDeptParseFunction;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 客户限制配置创建/更新 Request VO\")\n@Data\npublic class CrmCustomerLimitConfigSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"27930\")\n    private Long id;\n\n    @Schema(description = \"规则类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"规则类型不能为空\")\n    @DiffLogField(name = \"规则类型\")\n    private Integer type;\n\n    @Schema(description = \"规则适用人群\")\n    @DiffLogField(name = \"规则适用人群\", function = SysAdminUserParseFunction.NAME)\n    private List<Long> userIds;\n\n    @Schema(description = \"规则适用部门\")\n    @DiffLogField(name = \"规则适用部门\", function = SysDeptParseFunction.NAME)\n    private List<Long> deptIds;\n\n    @Schema(description = \"数量上限\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28384\")\n    @NotNull(message = \"数量上限不能为空\")\n    @DiffLogField(name = \"数量上限\")\n    private Integer maxCount;\n\n    @Schema(description = \"成交客户是否占有拥有客户数(当 type = 1 时)\")\n    @DiffLogField(name = \"成交客户是否占有拥有客户数\")\n    private Boolean dealCountEnabled;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户公海规则 Response VO\")\n@Data\npublic class CrmCustomerPoolConfigRespVO {\n\n    @Schema(description = \"是否启用客户公海\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否启用客户公海不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"未跟进放入公海天数\", example = \"2\")\n    private Integer contactExpireDays;\n\n    @Schema(description = \"未成交放入公海天数\", example = \"2\")\n    private Integer dealExpireDays;\n\n    @Schema(description = \"是否开启提前提醒\", example = \"true\")\n    private Boolean notifyEnabled;\n\n    @Schema(description = \"提前提醒天数\", example = \"2\")\n    private Integer notifyDays;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig;\n\nimport cn.hutool.core.util.BooleanUtil;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.Objects;\n\n@Schema(description = \"管理后台 - CRM 客户公海配置的创建/更新 Request VO\")\n@Data\npublic class CrmCustomerPoolConfigSaveReqVO {\n\n    @Schema(description = \"是否启用客户公海\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @DiffLogField(name = \"是否启用客户公海\")\n    @NotNull(message = \"是否启用客户公海不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"未跟进放入公海天数\", example = \"2\")\n    @DiffLogField(name = \"未跟进放入公海天数\")\n    private Integer contactExpireDays;\n\n    @Schema(description = \"未成交放入公海天数\", example = \"2\")\n    @DiffLogField(name = \"未成交放入公海天数\")\n    private Integer dealExpireDays;\n\n    @Schema(description = \"是否开启提前提醒\", example = \"true\")\n    @DiffLogField(name = \"是否开启提前提醒\")\n    private Boolean notifyEnabled;\n\n    @Schema(description = \"提前提醒天数\", example = \"2\")\n    @DiffLogField(name = \"提前提醒天数\")\n    private Integer notifyDays;\n\n    @AssertTrue(message = \"未成交放入公海天数不能为空\")\n    @JsonIgnore\n    public boolean isDealExpireDaysValid() {\n        if (!BooleanUtil.isTrue(getEnabled())) {\n            return true;\n        }\n        return Objects.nonNull(getDealExpireDays());\n    }\n\n    @AssertTrue(message = \"未跟进放入公海天数不能为空\")\n    @JsonIgnore\n    public boolean isContactExpireDaysValid() {\n        if (!BooleanUtil.isTrue(getEnabled())) {\n            return true;\n        }\n        return Objects.nonNull(getContactExpireDays());\n    }\n\n    @AssertTrue(message = \"提前提醒天数不能为空\")\n    @JsonIgnore\n    public boolean isNotifyDaysValid() {\n        if (!BooleanUtil.isTrue(getNotifyEnabled())) {\n            return true;\n        }\n        return Objects.nonNull(getNotifyDays());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.followup;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactService;\nimport cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.ArrayList;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n\n@Tag(name = \"管理后台 - 跟进记录\")\n@RestController\n@RequestMapping(\"/crm/follow-up-record\")\n@Validated\npublic class CrmFollowUpRecordController {\n\n    @Resource\n    private CrmFollowUpRecordService followUpRecordService;\n    @Resource\n    private CrmContactService contactService;\n    @Resource\n    private CrmBusinessService businessService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建跟进记录\")\n    public CommonResult<Long> createFollowUpRecord(@Valid @RequestBody CrmFollowUpRecordSaveReqVO createReqVO) {\n        return success(followUpRecordService.createFollowUpRecord(createReqVO));\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除跟进记录\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    public CommonResult<Boolean> deleteFollowUpRecord(@RequestParam(\"id\") Long id) {\n        followUpRecordService.deleteFollowUpRecord(id, getLoginUserId());\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得跟进记录\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    public CommonResult<CrmFollowUpRecordRespVO> getFollowUpRecord(@RequestParam(\"id\") Long id) {\n        CrmFollowUpRecordDO followUpRecord = followUpRecordService.getFollowUpRecord(id);\n        return success(BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得跟进记录分页\")\n    public CommonResult<PageResult<CrmFollowUpRecordRespVO>> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) {\n        PageResult<CrmFollowUpRecordDO> pageResult = followUpRecordService.getFollowUpRecordPage(pageReqVO);\n        // 1.1 查询联系人和商机\n        Map<Long, CrmContactDO> contactMap = contactService.getContactMap(\n                convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream()));\n        Map<Long, CrmBusinessDO> businessMap = businessService.getBusinessMap(\n                convertSetByFlatMap(pageResult.getList(), item -> item.getBusinessIds().stream()));\n        // 1.2 查询用户\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), item -> Long.valueOf(item.getCreator())));\n        // 2. 拼接数据\n        PageResult<CrmFollowUpRecordRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmFollowUpRecordRespVO.class, record -> {\n            // 2.1 设置联系人和商机信息\n            record.setBusinesses(new ArrayList<>()).setContacts(new ArrayList<>());\n            record.getContactIds().forEach(id -> MapUtils.findAndThen(contactMap, id, contact ->\n                    record.getContacts().add(new CrmBusinessRespVO().setId(contact.getId()).setName(contact.getName()))));\n            record.getBusinessIds().forEach(id -> MapUtils.findAndThen(businessMap, id, business ->\n                    record.getBusinesses().add(new CrmBusinessRespVO().setId(business.getId()).setName(business.getName()))));\n            // 2.2 设置用户信息\n            MapUtils.findAndThen(userMap, Long.valueOf(record.getCreator()), user -> record.setCreatorName(user.getNickname()));\n        });\n        return success(voPageResult);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 跟进记录分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmFollowUpRecordPageReqVO extends PageParam {\n\n    @Schema(description = \"数据类型\", example = \"2\")\n    private Integer bizType;\n\n    @Schema(description = \"数据编号\", example = \"5564\")\n    private Long bizId;\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_FOLLOW_UP_TYPE;\n\n@Schema(description = \"管理后台 - 跟进记录 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class CrmFollowUpRecordRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28800\")\n    private Long id;\n\n    @Schema(description = \"数据类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer bizType;\n\n    @Schema(description = \"数据编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5564\")\n    private Long bizId;\n\n    @Schema(description = \"跟进类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @DictFormat(CRM_FOLLOW_UP_TYPE)\n    private Integer type;\n\n    @Schema(description = \"跟进内容\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String content;\n\n    @Schema(description = \"下次联系时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime nextTime;\n\n    @Schema(description = \"关联的商机编号数组\")\n    private List<Long> businessIds;\n    @Schema(description = \"关联的商机数组\")\n    private List<CrmBusinessRespVO> businesses;\n\n    @Schema(description = \"关联的联系人编号数组\")\n    private List<Long> contactIds;\n    @Schema(description = \"关联的联系人名称数组\")\n    private List<CrmBusinessRespVO> contacts;\n\n    @Schema(description = \"图片\")\n    private List<String> picUrls;\n    @Schema(description = \"附件\")\n    private List<String> fileUrls;\n\n    @Schema(description = \"创建人\", example = \"1024\")\n    @ExcelProperty(\"创建人\")\n    private String creator;\n    @Schema(description = \"创建人名字\", example = \"芋道源码\")\n    @ExcelProperty(\"创建人名字\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 跟进记录新增/修改 Request VO\")\n@Data\npublic class CrmFollowUpRecordSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28800\")\n    private Long id;\n\n    @Schema(description = \"数据类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"数据类型不能为空\")\n    private Integer bizType;\n\n    @Schema(description = \"数据编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5564\")\n    @NotNull(message = \"数据编号不能为空\")\n    private Long bizId;\n\n    @Schema(description = \"跟进类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"跟进类型不能为空\")\n    private Integer type;\n\n    @Schema(description = \"跟进内容\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"跟进内容不能为空\")\n    private String content;\n\n    @Schema(description = \"下次联系时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"下次联系时间不能为空\")\n    private LocalDateTime nextTime;\n\n    @Schema(description = \"关联的商机编号数组\")\n    private List<Long> businessIds;\n    @Schema(description = \"关联的联系人编号数组\")\n    private List<Long> contactIds;\n\n    @Schema(description = \"图片\")\n    private List<String> picUrls;\n    @Schema(description = \"附件\")\n    private List<String> fileUrls;\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.operatelog;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogRespVO;\nimport cn.iocoder.yudao.module.crm.enums.LogRecordConstants;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.system.api.logger.OperateLogApi;\nimport cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\n\n@Tag(name = \"管理后台 - CRM 操作日志\")\n@RestController\n@RequestMapping(\"/crm/operate-log\")\n@Validated\npublic class CrmOperateLogController {\n\n    @Resource\n    private OperateLogApi operateLogApi;\n\n    /**\n     * {@link CrmBizTypeEnum} 与 {@link LogRecordConstants} 的映射关系\n     */\n    private static final Map<Integer, String> BIZ_TYPE_MAP = new HashMap<>();\n\n    static {\n        BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CLUE.getType(), CRM_CLUE_TYPE);\n        BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CUSTOMER.getType(), CRM_CUSTOMER_TYPE);\n        BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTACT.getType(), CRM_CONTACT_TYPE);\n        BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_BUSINESS.getType(), CRM_BUSINESS_TYPE);\n        BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTRACT.getType(), CRM_CONTRACT_TYPE);\n        BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_PRODUCT.getType(), CRM_PRODUCT_TYPE);\n        BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), CRM_RECEIVABLE_TYPE);\n        BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), CRM_RECEIVABLE_PLAN_TYPE);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得操作日志\")\n    public CommonResult<PageResult<CrmOperateLogRespVO>> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) {\n        OperateLogPageReqDTO reqDTO = new OperateLogPageReqDTO();\n        reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页，需要分页需注释\n        reqDTO.setType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId());\n        return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO).getCheckedData(), CrmOperateLogRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - CRM 操作日志 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmOperateLogPageReqVO extends PageParam {\n\n    @Schema(description = \"数据类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @InEnum(CrmBizTypeEnum.class)\n    @NotNull(message = \"数据类型不能为空\")\n    private Integer bizType;\n\n    @Schema(description = \"数据编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"数据编号不能为空\")\n    private Long bizId;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 操作日志 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class CrmOperateLogRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    private Long id;\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long userId;\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    private String userName;\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer userType;\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    private String type;\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"修改客户\")\n    private String subType;\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    private Long bizId;\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"将什么从什么改为了什么\")\n    private String action;\n\n    @Schema(description = \"编号\", example = \"{orderId: 1}\")\n    private String extra;\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2024-01-01\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http",
    "content": "### 请求 /add\nPOST {{baseUrl}}/crm/permission/create\nContent-Type: application/json\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"userId\": 1,\n  \"bizType\": 2,\n  \"bizId\": 2,\n  \"level\": 1\n}\n\n### 请求 /update\nPUT {{baseUrl}}/crm/permission/update\nContent-Type: application/json\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n{\n  \"userId\": 1,\n  \"bizType\": 2,\n  \"bizId\": 2,\n  \"level\": 1,\n  \"id\": 1\n}\n\n### 请求 /delete\nDELETE {{baseUrl}}/crm/permission/delete?bizType=2&bizId=1&id=1\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.permission;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.PostApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.google.common.collect.Multimaps;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - CRM 数据权限\")\n@RestController\n@RequestMapping(\"/crm/permission\")\n@Validated\npublic class CrmPermissionController {\n\n    @Resource\n    private CrmPermissionService permissionService;\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n    @Resource\n    private PostApi postApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建数据权限\")\n    public CommonResult<Boolean> create(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) {\n        permissionService.createPermission(reqVO, getLoginUserId());\n        return success(true);\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"编辑数据权限\")\n    @CrmPermission(bizTypeValue = \"#updateReqVO.bizType\", bizId = \"#updateReqVO.bizId\"\n            , level = CrmPermissionLevelEnum.OWNER)\n    public CommonResult<Boolean> updatePermission(@Valid @RequestBody CrmPermissionUpdateReqVO updateReqVO) {\n        permissionService.updatePermission(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除数据权限\")\n    @Parameter(name = \"ids\", description = \"数据权限编号\", required = true, example = \"1024\")\n    public CommonResult<Boolean> deletePermission(@RequestParam(\"ids\") Collection<Long> ids) {\n        permissionService.deletePermissionBatch(ids, getLoginUserId());\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-self\")\n    @Operation(summary = \"删除自己的数据权限\")\n    @Parameter(name = \"id\", description = \"数据权限编号\", required = true, example = \"1024\")\n    public CommonResult<Boolean> deleteSelfPermission(@RequestParam(\"id\") Long id) {\n        permissionService.deleteSelfPermission(id, getLoginUserId());\n        return success(true);\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得数据权限列表\")\n    @Parameters({\n            @Parameter(name = \"bizType\", description = \"CRM 类型\", required = true, example = \"2\"),\n            @Parameter(name = \"bizId\", description = \"CRM 类型数据编号\", required = true, example = \"1024\")\n    })\n    public CommonResult<List<CrmPermissionRespVO>> getPermissionList(@RequestParam(\"bizType\") Integer bizType,\n                                                                     @RequestParam(\"bizId\") Long bizId) {\n        List<CrmPermissionDO> permissions = permissionService.getPermissionListByBiz(bizType, bizId);\n        if (CollUtil.isEmpty(permissions)) {\n            return success(Collections.emptyList());\n        }\n\n        // 查询相关数据\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(permissions, CrmPermissionDO::getUserId));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        Map<Long, PostRespDTO> postMap = postApi.getPostMap(\n                convertSetByFlatMap(userMap.values(), AdminUserRespDTO::getPostIds,\n                        item -> item != null ? item.stream() : Stream.empty()));\n        // 拼接数据\n        return success(CollectionUtils.convertList(BeanUtils.toBean(permissions, CrmPermissionRespVO.class), item -> {\n            findAndThen(userMap, item.getUserId(), user -> {\n                item.setNickname(user.getNickname());\n                findAndThen(deptMap, user.getDeptId(), deptRespDTO -> item.setDeptName(deptRespDTO.getName()));\n                if (CollUtil.isEmpty(user.getPostIds())) {\n                    item.setPostNames(Collections.emptySet());\n                    return;\n                }\n                List<PostRespDTO> postList = MapUtils.getList(Multimaps.forMap(postMap), user.getPostIds());\n                item.setPostNames(CollectionUtils.convertSet(postList, PostRespDTO::getName));\n            });\n            return item;\n        }));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.permission.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - CRM 数据权限 Response VO\")\n@Data\npublic class CrmPermissionRespVO {\n\n    @Schema(description = \"数据权限编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13563\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123456\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    @Schema(description = \"CRM 类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @InEnum(CrmBizTypeEnum.class)\n    @NotNull(message = \"CRM 类型不能为空\")\n    private Integer bizType;\n\n    @Schema(description = \"CRM 类型数据编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"CRM 类型数据编号不能为空\")\n    private Long bizId;\n\n    @Schema(description = \"权限级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @InEnum(CrmPermissionLevelEnum.class)\n    @NotNull(message = \"权限级别不能为空\")\n    private Integer level;\n\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    private String nickname;\n\n    @Schema(description = \"部门名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"研发部\")\n    private String deptName;\n\n    @Schema(description = \"岗位名称数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[BOOS,经理]\")\n    private Set<String> postNames;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2023-01-01 00:00:00\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.permission.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - CRM 数据权限创建/更新 Request VO\")\n@Data\npublic class CrmPermissionSaveReqVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123456\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    @Schema(description = \"CRM 类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @InEnum(CrmBizTypeEnum.class)\n    @NotNull(message = \"CRM 类型不能为空\")\n    private Integer bizType;\n\n    @Schema(description = \"CRM 类型数据编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"CRM 类型数据编号不能为空\")\n    private Long bizId;\n\n    @Schema(description = \"权限级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @InEnum(CrmPermissionLevelEnum.class)\n    @NotNull(message = \"权限级别不能为空\")\n    private Integer level;\n\n    /**\n     * 添加客户团队成员时，需要额外有【联系人】【商机】【合同】的 checkbox 选择。\n     * 选中时，同时添加对应的权限\n     */\n    @Schema(description = \"同时添加\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10430\")\n    private List<Integer> toBizTypes;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.permission.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - CRM 数据权限更新 Request VO\")\n@Data\npublic class CrmPermissionUpdateReqVO {\n\n    @Schema(description = \"数据权限编号列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1,2]\")\n    @NotNull(message = \"数据权限编号列表不能为空\")\n    private List<Long> ids;\n\n    @Schema(description = \"Crm 类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @InEnum(CrmBizTypeEnum.class)\n    @NotNull(message = \"Crm 类型不能为空\")\n    private Integer bizType;\n\n    @Schema(description = \"Crm 类型数据编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"Crm 类型数据编号不能为空\")\n    private Long bizId;\n\n    @Schema(description = \"权限级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @InEnum(CrmPermissionLevelEnum.class)\n    @NotNull(message = \"权限级别不能为空\")\n    private Integer level;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductCategoryController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryRespVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;\nimport cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - CRM 产品分类\")\n@RestController\n@RequestMapping(\"/crm/product-category\")\n@Validated\npublic class CrmProductCategoryController {\n\n    @Resource\n    private CrmProductCategoryService productCategoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建产品分类\")\n    @PreAuthorize(\"@ss.hasPermission('crm:product-category:create')\")\n    public CommonResult<Long> createProductCategory(@Valid @RequestBody CrmProductCategoryCreateReqVO createReqVO) {\n        return success(productCategoryService.createProductCategory(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新产品分类\")\n    @PreAuthorize(\"@ss.hasPermission('crm:product-category:update')\")\n    public CommonResult<Boolean> updateProductCategory(@Valid @RequestBody CrmProductCategoryCreateReqVO updateReqVO) {\n        productCategoryService.updateProductCategory(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除产品分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:product-category:delete')\")\n    public CommonResult<Boolean> deleteProductCategory(@RequestParam(\"id\") Long id) {\n        productCategoryService.deleteProductCategory(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:product-category:query')\")\n    public CommonResult<CrmProductCategoryRespVO> getProductCategory(@RequestParam(\"id\") Long id) {\n        CrmProductCategoryDO category = productCategoryService.getProductCategory(id);\n        return success(BeanUtils.toBean(category, CrmProductCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得产品分类列表\")\n    @PreAuthorize(\"@ss.hasPermission('crm:product-category:query')\")\n    public CommonResult<List<CrmProductCategoryRespVO>> getProductCategoryList(@Valid CrmProductCategoryListReqVO listReqVO) {\n        List<CrmProductCategoryDO> list = productCategoryService.getProductCategoryList(listReqVO);\n        return success(BeanUtils.toBean(list, CrmProductCategoryRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.product;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.framework.translate.core.TranslateUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;\nimport cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum;\nimport cn.iocoder.yudao.module.crm.service.product.CrmProductService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport com.fhs.core.trans.anno.TransMethodResult;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - CRM 产品\")\n@RestController\n@RequestMapping(\"/crm/product\")\n@Validated\npublic class CrmProductController {\n\n    @Resource\n    private CrmProductService productService;\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建产品\")\n    @PreAuthorize(\"@ss.hasPermission('crm:product:create')\")\n    public CommonResult<Long> createProduct(@Valid @RequestBody CrmProductSaveReqVO createReqVO) {\n        return success(productService.createProduct(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新产品\")\n    @PreAuthorize(\"@ss.hasPermission('crm:product:update')\")\n    public CommonResult<Boolean> updateProduct(@Valid @RequestBody CrmProductSaveReqVO updateReqVO) {\n        productService.updateProduct(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除产品\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:product:delete')\")\n    public CommonResult<Boolean> deleteProduct(@RequestParam(\"id\") Long id) {\n        productService.deleteProduct(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:product:query')\")\n    @TransMethodResult\n    public CommonResult<CrmProductRespVO> getProduct(@RequestParam(\"id\") Long id) {\n        CrmProductDO product = productService.getProduct(id);\n        return success(BeanUtils.toBean(product, CrmProductRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得产品精简列表\", description = \"只包含被开启的产品，主要用于前端的下拉选项\")\n    public CommonResult<List<CrmProductRespVO>> getProductSimpleList() {\n        List<CrmProductDO> list = productService.getProductListByStatus(CrmProductStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, product -> new CrmProductRespVO().setId(product.getId()).setName(product.getName())\n                .setUnit(product.getUnit()).setNo(product.getNo()).setPrice(product.getPrice())));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得产品分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:product:query')\")\n    @TransMethodResult\n    public CommonResult<PageResult<CrmProductRespVO>> getProductPage(@Valid CrmProductPageReqVO pageVO) {\n        PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO);\n        return success(BeanUtils.toBean(pageResult, CrmProductRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出产品 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('crm:product:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportProductExcel(@Valid CrmProductPageReqVO exportReqVO,\n                                   HttpServletResponse response) throws IOException {\n        exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<CrmProductDO> list = productService.getProductPage(exportReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"产品.xls\", \"数据\", CrmProductRespVO.class,\n                TranslateUtils.translate(BeanUtils.toBean(list, CrmProductRespVO.class)));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.product.vo.category;\n\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - CRM 产品分类创建/更新 Request VO\")\n@Data\npublic class CrmProductCategoryCreateReqVO{\n\n    @Schema(description = \"分类编号\", example = \"23902\")\n    private Long id;\n\n    @Schema(description = \"分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    @NotNull(message = \"分类名称不能为空\")\n    @DiffLogField(name = \"分类名称\")\n    private String name;\n\n    @Schema(description = \"父级编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4680\")\n    @NotNull(message = \"父级编号不能为空\")\n    private Long parentId;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.product.vo.category;\n\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 产品分类列表 Request VO\")\n@Data\npublic class CrmProductCategoryListReqVO {\n\n    @ExcelProperty(\"名称\")\n    private String name;\n\n    @ExcelProperty(\"父级 id\")\n    private Long parentId;\n\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.product.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 产品分类 Response VO\")\n@Data\npublic class CrmProductCategoryRespVO {\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23902\")\n    private Long id;\n\n    @Schema(description = \"分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"父级编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4680\")\n    private Long parentId;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.product.vo.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - CRM 产品分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmProductPageReqVO extends PageParam {\n\n    @Schema(description = \"产品名称\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"状态\", example = \"1\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.product.vo.product;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport com.fhs.core.trans.anno.Trans;\nimport com.fhs.core.trans.constant.TransType;\nimport com.fhs.core.trans.vo.VO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 产品 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class CrmProductRespVO implements VO {\n\n    @Schema(description = \"产品编号\", example = \"20529\")\n    @ExcelProperty(\"产品编号\")\n    private Long id;\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"好产品\")\n    @ExcelProperty(\"产品名称\")\n    private String name;\n\n    @Schema(description = \"产品编码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"12306\")\n    @ExcelProperty(\"产品编码\")\n    private String no;\n\n    @Schema(description = \"单位\", example = \"2\")\n    @ExcelProperty(value = \"单位\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.CRM_PRODUCT_UNIT)\n    private Integer unit;\n\n    @Schema(description = \"价格, 单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8911\")\n    @ExcelProperty(\"价格，单位：分\")\n    private BigDecimal price;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"上架\")\n    @ExcelProperty(value = \"单位\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.CRM_PRODUCT_STATUS)\n    private Integer status;\n\n    @Schema(description = \"产品分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @Trans(type = TransType.SIMPLE, target = CrmProductCategoryDO.class, fields = \"name\", ref = \"categoryName\")\n    private Long categoryId;\n    @Schema(description = \"产品分类名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"衣服\")\n    @ExcelProperty(\"产品分类\")\n    private String categoryName;\n\n    @Schema(description = \"产品描述\", example = \"你说的对\")\n    @ExcelProperty(\"产品描述\")\n    private String description;\n\n    @Schema(description = \"负责人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31926\")\n    @Trans(type = TransType.AUTO_TRANS, key = AdminUserApi.PREFIX,\n            fields = \"nickname\", ref = \"ownerUserName\")\n    private Long ownerUserId;\n    @Schema(description = \"负责人的用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    @ExcelProperty(\"负责人\")\n    private String ownerUserName;\n\n    @Schema(description = \"创建人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @Trans(type = TransType.AUTO_TRANS, key = AdminUserApi.PREFIX,\n            fields = \"nickname\", ref = \"creatorName\")\n    private String creator;\n    @Schema(description = \"创建人名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    @ExcelProperty(\"创建人\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"更新时间\")\n    private LocalDateTime updateTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.product.vo.product;\n\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmProductStatusParseFunction;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmProductUnitParseFunction;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - CRM 产品创建/修改 Request VO\")\n@Data\npublic class CrmProductSaveReqVO {\n\n    @Schema(description = \"产品编号\", example = \"20529\")\n    private Long id;\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"好产品\")\n    @NotNull(message = \"产品名称不能为空\")\n    @DiffLogField(name = \"产品名称\")\n    private String name;\n\n    @Schema(description = \"产品编码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"12306\")\n    @NotNull(message = \"产品编码不能为空\")\n    @DiffLogField(name = \"产品编码\")\n    private String no;\n\n    @Schema(description = \"单位\", example = \"2\")\n    @DiffLogField(name = \"单位\", function = CrmProductUnitParseFunction.NAME)\n    private Integer unit;\n\n    @Schema(description = \"价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8911\")\n    @NotNull(message = \"价格不能为空\")\n    @DiffLogField(name = \"价格\")\n    private BigDecimal price;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"上架\")\n    @NotNull(message = \"状态不能为空\")\n    @DiffLogField(name = \"状态\", function = CrmProductStatusParseFunction.NAME)\n    private Integer status;\n\n    @Schema(description = \"产品分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"产品分类编号不能为空\")\n    @DiffLogField(name = \"产品分类编号\")\n    private Long categoryId;\n\n    @Schema(description = \"产品描述\", example = \"你说的对\")\n    @DiffLogField(name = \"产品描述\")\n    private String description;\n\n    @Schema(description = \"负责人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31926\")\n    @NotNull(message = \"负责人的用户编号不能为空\")\n    private Long ownerUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.receivable;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - CRM 回款\")\n@RestController\n@RequestMapping(\"/crm/receivable\")\n@Validated\npublic class CrmReceivableController {\n\n    @Resource\n    private CrmReceivableService receivableService;\n    @Resource\n    private CrmContractService contractService;\n    @Resource\n    private CrmCustomerService customerService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建回款\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable:create')\")\n    public CommonResult<Long> createReceivable(@Valid @RequestBody CrmReceivableSaveReqVO createReqVO) {\n        return success(receivableService.createReceivable(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新回款\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable:update')\")\n    public CommonResult<Boolean> updateReceivable(@Valid @RequestBody CrmReceivableSaveReqVO updateReqVO) {\n        receivableService.updateReceivable(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除回款\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable:delete')\")\n    public CommonResult<Boolean> deleteReceivable(@RequestParam(\"id\") Long id) {\n        receivableService.deleteReceivable(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得回款\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable:query')\")\n    public CommonResult<CrmReceivableRespVO> getReceivable(@RequestParam(\"id\") Long id) {\n        CrmReceivableDO receivable = receivableService.getReceivable(id);\n        return success(buildReceivableDetail(receivable));\n    }\n\n    private CrmReceivableRespVO buildReceivableDetail(CrmReceivableDO receivable) {\n        if (receivable == null) {\n            return null;\n        }\n        return buildReceivableDetailList(Collections.singletonList(receivable)).get(0);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得回款分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable:query')\")\n    public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) {\n        PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId());\n        return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/page-by-customer\")\n    @Operation(summary = \"获得回款分页，基于指定客户\")\n    public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) {\n        Assert.notNull(pageReqVO.getCustomerId(), \"客户编号不能为空\");\n        PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO);\n        return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出回款 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO,\n                                      HttpServletResponse response) throws IOException {\n        exportReqVO.setPageSize(PAGE_SIZE_NONE);\n        List<CrmReceivableDO> list = receivableService.getReceivablePage(exportReqVO, getLoginUserId()).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"回款.xls\", \"数据\", CrmReceivableRespVO.class,\n                buildReceivableDetailList(list));\n    }\n\n    private List<CrmReceivableRespVO> buildReceivableDetailList(List<CrmReceivableDO> receivableList) {\n        if (CollUtil.isEmpty(receivableList)) {\n            return Collections.emptyList();\n        }\n        // 1.1 获取客户列表\n        Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(receivableList, CrmReceivableDO::getCustomerId));\n        // 1.2 获取创建人、负责人列表\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivableList,\n                contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        // 1.3 获得合同列表\n        Map<Long, CrmContractDO> contractMap = contractService.getContractMap(\n                convertSet(receivableList, CrmReceivableDO::getContractId));\n        // 2. 拼接结果\n        return BeanUtils.toBean(receivableList, CrmReceivableRespVO.class, (receivableVO) -> {\n            // 2.1 拼接客户名称\n            findAndThen(customerMap, receivableVO.getCustomerId(), customer -> receivableVO.setCustomerName(customer.getName()));\n            // 2.2 拼接负责人、创建人名称\n            MapUtils.findAndThen(userMap, NumberUtils.parseLong(receivableVO.getCreator()),\n                    user -> receivableVO.setCreatorName(user.getNickname()));\n            MapUtils.findAndThen(userMap, receivableVO.getOwnerUserId(), user -> {\n                receivableVO.setOwnerUserName(user.getNickname());\n                MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> receivableVO.setOwnerUserDeptName(dept.getName()));\n            });\n            // 2.3 拼接合同信息\n            findAndThen(contractMap, receivableVO.getContractId(), contract ->\n                    receivableVO.setContract(BeanUtils.toBean(contract, CrmContractRespVO.class)));\n        });\n    }\n\n    @PutMapping(\"/submit\")\n    @Operation(summary = \"提交回款审批\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable:update')\")\n    public CommonResult<Boolean> submitContract(@RequestParam(\"id\") Long id) {\n        receivableService.submitReceivable(id, getLoginUserId());\n        return success(true);\n    }\n\n    @GetMapping(\"/audit-count\")\n    @Operation(summary = \"获得待审核回款数量\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable:query')\")\n    public CommonResult<Long> getAuditReceivableCount() {\n        return success(receivableService.getAuditReceivableCount(getLoginUserId()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.receivable;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.crm.service.receivable.CrmReceivablePlanService;\nimport cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - CRM 回款计划\")\n@RestController\n@RequestMapping(\"/crm/receivable-plan\")\n@Validated\npublic class CrmReceivablePlanController {\n\n    @Resource\n    private CrmReceivablePlanService receivablePlanService;\n    @Resource\n    private CrmReceivableService receivableService;\n    @Resource\n    private CrmContractService contractService;\n    @Resource\n    private CrmCustomerService customerService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建回款计划\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable-plan:create')\")\n    public CommonResult<Long> createReceivablePlan(@Valid @RequestBody CrmReceivablePlanSaveReqVO createReqVO) {\n        return success(receivablePlanService.createReceivablePlan(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新回款计划\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable-plan:update')\")\n    public CommonResult<Boolean> updateReceivablePlan(@Valid @RequestBody CrmReceivablePlanSaveReqVO updateReqVO) {\n        receivablePlanService.updateReceivablePlan(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除回款计划\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable-plan:delete')\")\n    public CommonResult<Boolean> deleteReceivablePlan(@RequestParam(\"id\") Long id) {\n        receivablePlanService.deleteReceivablePlan(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得回款计划\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable-plan:query')\")\n    public CommonResult<CrmReceivablePlanRespVO> getReceivablePlan(@RequestParam(\"id\") Long id) {\n        CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(id);\n        return success(buildReceivablePlanDetail(receivablePlan));\n    }\n\n    private CrmReceivablePlanRespVO buildReceivablePlanDetail(CrmReceivablePlanDO receivablePlan) {\n        if (receivablePlan == null) {\n            return null;\n        }\n        return buildReceivableDetailList(Collections.singletonList(receivablePlan)).get(0);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得回款计划分页\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable-plan:query')\")\n    public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPage(@Valid CrmReceivablePlanPageReqVO pageReqVO) {\n        PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO, getLoginUserId());\n        return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/page-by-customer\")\n    @Operation(summary = \"获得回款计划分页，基于指定客户\")\n    public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPageByCustomer(@Valid CrmReceivablePlanPageReqVO pageReqVO) {\n        Assert.notNull(pageReqVO.getCustomerId(), \"客户编号不能为空\");\n        PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO);\n        return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出回款计划 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable-plan:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO,\n                                          HttpServletResponse response) throws IOException {\n        exportReqVO.setPageSize(PAGE_SIZE_NONE);\n        List<CrmReceivablePlanDO> list = receivablePlanService.getReceivablePlanPage(exportReqVO, getLoginUserId()).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"回款计划.xls\", \"数据\", CrmReceivablePlanRespVO.class,\n                buildReceivableDetailList(list));\n    }\n\n    private List<CrmReceivablePlanRespVO> buildReceivableDetailList(List<CrmReceivablePlanDO> receivablePlanList) {\n        if (CollUtil.isEmpty(receivablePlanList)) {\n            return Collections.emptyList();\n        }\n        // 1.1 获取客户 Map\n        Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(receivablePlanList, CrmReceivablePlanDO::getCustomerId));\n        // 1.2 获取创建人、负责人列表\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivablePlanList,\n                contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));\n        // 1.3 获得合同 Map\n        Map<Long, CrmContractDO> contractMap = contractService.getContractMap(\n                convertSet(receivablePlanList, CrmReceivablePlanDO::getContractId));\n        // 1.4 获得回款 Map\n        Map<Long, CrmReceivableDO> receivableMap = receivableService.getReceivableMap(\n                convertSet(receivablePlanList, CrmReceivablePlanDO::getReceivableId));\n        // 2. 拼接数据\n        return BeanUtils.toBean(receivablePlanList, CrmReceivablePlanRespVO.class, (receivablePlanVO) -> {\n            // 2.1 拼接客户信息\n            findAndThen(customerMap, receivablePlanVO.getCustomerId(), customer -> receivablePlanVO.setCustomerName(customer.getName()));\n            // 2.2 拼接用户信息\n            findAndThen(userMap, receivablePlanVO.getOwnerUserId(), user -> receivablePlanVO.setOwnerUserName(user.getNickname()));\n            findAndThen(userMap, Long.parseLong(receivablePlanVO.getCreator()), user -> receivablePlanVO.setCreatorName(user.getNickname()));\n            // 2.3 拼接合同信息\n            findAndThen(contractMap, receivablePlanVO.getContractId(), contract -> receivablePlanVO.setContractNo(contract.getNo()));\n            // 2.4 拼接回款信息\n            receivablePlanVO.setReceivable(BeanUtils.toBean(receivableMap.get(receivablePlanVO.getReceivableId()), CrmReceivableRespVO.class));\n        });\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得回款计划精简列表\", description = \"获得回款计划精简列表，主要用于前端的下拉选项\")\n    @Parameters({\n            @Parameter(name = \"customerId\", description = \"客户编号\", required = true),\n            @Parameter(name = \"contractId\", description = \"合同编号\", required = true)\n    })\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable-plan:query')\")\n    public CommonResult<List<CrmReceivablePlanRespVO>> getReceivablePlanSimpleList(@RequestParam(\"customerId\") Long customerId,\n                                                                                   @RequestParam(\"contractId\") Long contractId) {\n        CrmReceivablePlanPageReqVO pageReqVO = new CrmReceivablePlanPageReqVO().setCustomerId(customerId).setContractId(contractId);\n        pageReqVO.setPageNo(PAGE_SIZE_NONE);\n        PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO);\n        return success(convertList(pageResult.getList(), receivablePlan -> new CrmReceivablePlanRespVO() // 只返回 id、period 等信息\n                .setId(receivablePlan.getId()).setPeriod(receivablePlan.getPeriod()).setReceivableId(receivablePlan.getReceivableId())\n                .setPrice(receivablePlan.getPrice()).setReturnType(receivablePlan.getReturnType())));\n    }\n\n    @GetMapping(\"/remind-count\")\n    @Operation(summary = \"获得待回款提醒数量\")\n    @PreAuthorize(\"@ss.hasPermission('crm:receivable-plan:query')\")\n    public CommonResult<Long> getReceivablePlanRemindCount() {\n        return success(receivablePlanService.getReceivablePlanRemindCount(getLoginUserId()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - CRM 回款计划分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmReceivablePlanPageReqVO extends PageParam {\n\n    /**\n     * 提醒类型 - 待回款\n     */\n    public final static Integer REMIND_TYPE_NEEDED = 1;\n    /**\n     * 提醒类型 - 已逾期\n     */\n    public final static Integer REMIND_TYPE_EXPIRED = 2;\n    /**\n     * 提醒类型 - 已回款\n     */\n    public final static Integer REMIND_TYPE_RECEIVED = 3;\n\n    @Schema(description = \"客户编号\", example = \"18026\")\n    private Long customerId;\n\n    @Schema(description = \"合同编号\", example = \"H3473\")\n    private String contractNo;\n\n    @Schema(description = \"合同编号\", example = \"3473\")\n    private Long contractId;\n\n    @Schema(description = \"场景类型\", example = \"1\")\n    @InEnum(CrmSceneTypeEnum.class)\n    private Integer sceneType; // 场景类型，为 null 时则表示全部\n\n    @Schema(description = \"提醒类型\", example = \"1\")\n    private Integer remindType; // 提醒类型，为 null 时则表示全部\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 回款计划 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class CrmReceivablePlanRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"期数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"期数\")\n    private Integer period;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"客户编号\")\n    private Long customerId;\n    @Schema(description = \"客户名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"test\")\n    @ExcelProperty(\"客户名字\")\n    private String customerName;\n\n    @Schema(description = \"合同编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"合同编号\")\n    private Long contractId;\n    @Schema(description = \"合同编号\", example = \"Q110\")\n    @ExcelProperty(\"合同编号\")\n    private String contractNo;\n\n    @Schema(description = \"负责人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"负责人编号\")\n    private Long ownerUserId;\n    @Schema(description = \"负责人\", example = \"test\")\n    @ExcelProperty(\"负责人\")\n    private String ownerUserName;\n\n    @Schema(description = \"计划回款日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2024-02-02\")\n    @ExcelProperty(\"计划回款日期\")\n    private LocalDateTime returnTime;\n\n    @Schema(description = \"计划回款方式\", example = \"1\")\n    @ExcelProperty(\"计划回款方式\")\n    private Integer returnType;\n\n    @Schema(description = \"计划回款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"9000\")\n    @ExcelProperty(\"计划回款金额\")\n    private BigDecimal price;\n\n    @Schema(description = \"回款编号\", example = \"19852\")\n    @ExcelProperty(\"回款编号\")\n    private Long receivableId;\n    @Schema(description = \"回款信息\")\n    private CrmReceivableRespVO receivable;\n\n    @Schema(description = \"提前几天提醒\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(\"提前几天提醒\")\n    private Integer remindDays;\n\n    @Schema(description = \"提醒日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2024-02-02\")\n    @ExcelProperty(\"提醒日期\")\n    private LocalDateTime remindTime;\n\n    @Schema(description = \"备注\", example = \"备注\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"更新时间\")\n    private LocalDateTime updateTime;\n\n    @Schema(description = \"创建人\", example = \"1024\")\n    @ExcelProperty(\"创建人\")\n    private String creator;\n    @Schema(description = \"创建人名字\", example = \"芋道源码\")\n    @ExcelProperty(\"创建人名字\")\n    private String creatorName;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 回款计划新增/修改 Request VO\")\n@Data\npublic class CrmReceivablePlanSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Long id;\n\n    @Schema(description = \"客户编号\", hidden = true, example = \"2\")\n    private Long customerId; // 该字段不通过前端传递，而是 contractId 查询出来设置进去\n\n    @Schema(description = \"合同编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"合同编号不能为空\")\n    private Long contractId;\n\n    @Schema(description = \"负责人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"负责人编号不能为空\")\n    private Long ownerUserId;\n\n    @Schema(description = \"计划回款日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2024-02-02\")\n    @NotNull(message = \"计划回款日期不能为空\")\n    private LocalDateTime returnTime;\n\n    @Schema(description = \"回款方式\", example = \"1\")\n    private Integer returnType;\n\n    @Schema(description = \"计划回款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"9000\")\n    @NotNull(message = \"计划回款金额不能为空\")\n    private BigDecimal price;\n\n    @Schema(description = \"提前几天提醒\", example = \"1\")\n    private Integer remindDays;\n\n    @Schema(description = \"备注\", example = \"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - CRM 回款分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CrmReceivablePageReqVO extends PageParam {\n\n    @Schema(description = \"回款编号\")\n    private String no;\n\n    @Schema(description = \"回款计划编号\", example = \"31177\")\n    private Long planId;\n\n    @Schema(description = \"客户编号\", example = \"4963\")\n    private Long customerId;\n\n    @Schema(description = \"合同编号\", example = \"4963\")\n    private Long contractId;\n\n    @Schema(description = \"场景类型\", example = \"1\")\n    @InEnum(CrmSceneTypeEnum.class)\n    private Integer sceneType; // 场景类型，为 null 时则表示全部\n\n    @Schema(description = \"审批状态\", example = \"20\")\n    @InEnum(CrmAuditStatusEnum.class)\n    private Integer auditStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 回款 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class CrmReceivableRespVO {\n\n    @Schema(description = \"编号\", example = \"25787\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"回款编号\", example = \"31177\")\n    @ExcelProperty(\"回款编号\")\n    private String no;\n\n    @Schema(description = \"回款计划编号\", example = \"1024\")\n    @ExcelProperty(\"回款计划编号\")\n    private Long planId;\n\n    @Schema(description = \"回款方式\", example = \"2\")\n    @ExcelProperty(value = \"回款方式\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.CRM_RECEIVABLE_RETURN_TYPE)\n    private Integer returnType;\n\n    @Schema(description = \"回款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"9000\")\n    @ExcelProperty(\"回款金额\")\n    private BigDecimal price;\n\n    @Schema(description = \"计划回款日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2024-02-02\")\n    @ExcelProperty(\"计划回款日期\")\n    private LocalDateTime returnTime;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Long customerId;\n    @Schema(description = \"客户名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"test\")\n    @ExcelProperty(\"客户名字\")\n    private String customerName;\n\n    @Schema(description = \"合同编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"合同编号\")\n    private Long contractId;\n    @Schema(description = \"合同信息\")\n    private CrmContractRespVO contract;\n\n    @Schema(description = \"负责人的用户编号\", example = \"25682\")\n    private Long ownerUserId;\n    @Schema(description = \"负责人名字\", example = \"25682\")\n    @ExcelProperty(\"负责人名字\")\n    private String ownerUserName;\n    @Schema(description = \"负责人部门\")\n    @ExcelProperty(\"负责人部门\")\n    private String ownerUserDeptName;\n\n    @Schema(description = \"工作流编号\", example = \"1043\")\n    @ExcelProperty(\"工作流编号\")\n    private String processInstanceId;\n\n    @Schema(description = \"审批状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @ExcelProperty(value = \"审批状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.CRM_AUDIT_STATUS)\n    private Integer auditStatus;\n\n    @Schema(description = \"工作流编号\", example = \"备注\")\n    @ExcelProperty(\"工作流编号\")\n    private String remark;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"更新时间\")\n    private LocalDateTime updateTime;\n\n    @Schema(description = \"创建人\", example = \"25682\")\n    private String creator;\n    @Schema(description = \"创建人名字\", example = \"test\")\n    @ExcelProperty(\"创建人名字\")\n    private String creatorName;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;\nimport cn.iocoder.yudao.module.crm.framework.operatelog.core.*;\nimport com.mzt.logapi.starter.annotation.DiffLogField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 回款新增/修改 Request VO\")\n@Data\npublic class CrmReceivableSaveReqVO {\n\n    @Schema(description = \"编号\", example = \"25787\")\n    private Long id;\n\n    @Schema(description = \"负责人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @DiffLogField(name = \"负责人\", function = SysAdminUserParseFunction.NAME)\n    @NotNull(message = \"负责人编号不能为空\")\n    private Long ownerUserId;\n\n    @Schema(description = \"客户编号\", example = \"2\")\n    @DiffLogField(name = \"客户\", function = CrmCustomerParseFunction.NAME)\n    private Long customerId; // 该字段不通过前端传递，而是 contractId 查询出来设置进去\n\n    @Schema(description = \"合同编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @DiffLogField(name = \"合同\", function = CrmContractParseFunction.NAME)\n    @NotNull(message = \"合同编号不能为空\")\n    private Long contractId;\n\n    @Schema(description = \"回款计划编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @DiffLogField(name = \"合同\", function = CrmReceivablePlanParseFunction.NAME)\n    private Long planId;\n\n    @Schema(description = \"回款方式\", example = \"2\")\n    @DiffLogField(name = \"回款方式\", function = CrmReceivableReturnTypeParseFunction.NAME)\n    @InEnum(CrmReceivableReturnTypeEnum.class)\n    private Integer returnType;\n\n    @Schema(description = \"回款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"9000\")\n    @DiffLogField(name = \"回款金额\")\n    @NotNull(message = \"回款金额不能为空\")\n    private BigDecimal price;\n\n    @Schema(description = \"回款日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2024-02-02\")\n    @NotNull(message = \"回款日期不能为空\")\n    @DiffLogField(name = \"回款日期\")\n    private LocalDateTime returnTime;\n\n    @Schema(description = \"备注\", example = \"备注\")\n    @DiffLogField(name = \"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http",
    "content": "# == 1. 客户总量分析 ==\n### 1.1 客户总量分析(按日期)\nGET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&interval=2&times[0]=2024-01-01 00:00:00&times[1]=2024-01-29 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 1.2 客户总量统计(按用户)\nGET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n# == 2. 客户跟进次数分析 ==\n### 2.1 客户跟进次数分析(按日期)\nGET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-date?deptId=100&interval=2&times[0]=2024-01-01 00:00:00&times[1]=2024-01-29 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 2.2 客户总量统计(按用户)\nGET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-user?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n# == 3. 客户跟进方式分析 ==\n### 3.1 客户跟进方式分析\nGET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-type?deptId=100&interval=2&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n# == 4. 客户成交周期 ==\n### 4.1 合同摘要信息(客户转化率页面)\nGET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100&interval=2&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n# == 5. 客户成交周期 ==\n### 5.1 获取客户公海分析(按日期)\nGET {{baseUrl}}/crm/statistics-customer/get-pool-summary-by-date?deptId=100&interval=2&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 5.2 获取客户公海分析(按用户)\nGET {{baseUrl}}/crm/statistics-customer/get-pool-summary-by-user?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n# == 6. 客户成交周期 ==\n### 6.1 客户成交周期(按日期)\nGET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&interval=2&times[0]=2024-01-01 00:00:00&times[1]=2024-01-29 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 6.2 获取客户成交周期(按用户)\nGET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 6.3 获取客户成交周期(按区域)\nGET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-area?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 6.4 获取客户成交周期(按产品)\nGET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-product?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;\nimport cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - CRM 客户统计\")\n@RestController\n@RequestMapping(\"/crm/statistics-customer\")\n@Validated\npublic class CrmStatisticsCustomerController {\n\n    @Resource\n    private CrmStatisticsCustomerService customerService;\n\n    @GetMapping(\"/get-customer-summary-by-date\")\n    @Operation(summary = \"获取客户总量分析(按日期)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsCustomerSummaryByDateRespVO>> getCustomerSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getCustomerSummaryByDate(reqVO));\n    }\n\n    @GetMapping(\"/get-customer-summary-by-user\")\n    @Operation(summary = \"获取客户总量分析(按用户)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsCustomerSummaryByUserRespVO>> getCustomerSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getCustomerSummaryByUser(reqVO));\n    }\n\n    @GetMapping(\"/get-follow-up-summary-by-date\")\n    @Operation(summary = \"获取客户跟进次数分析(按日期)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsFollowUpSummaryByDateRespVO>> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getFollowUpSummaryByDate(reqVO));\n    }\n\n    @GetMapping(\"/get-follow-up-summary-by-user\")\n    @Operation(summary = \"获取客户跟进次数分析(按用户)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsFollowUpSummaryByUserRespVO>> getFollowUpSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getFollowUpSummaryByUser(reqVO));\n    }\n\n    @GetMapping(\"/get-follow-up-summary-by-type\")\n    @Operation(summary = \"获取客户跟进次数分析(按类型)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsFollowUpSummaryByTypeRespVO>> getFollowUpSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getFollowUpSummaryByType(reqVO));\n    }\n\n    @GetMapping(\"/get-contract-summary\")\n    @Operation(summary = \"获取客户的首次合同、回款信息列表\", description = \"用于【客户转化率】页面\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsCustomerContractSummaryRespVO>> getContractSummary(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getContractSummary(reqVO));\n    }\n\n    @GetMapping(\"/get-pool-summary-by-date\")\n    @Operation(summary = \"获取公海客户分析(按日期)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsPoolSummaryByDateRespVO>> getPoolSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getPoolSummaryByDate(reqVO));\n    }\n\n    @GetMapping(\"/get-pool-summary-by-user\")\n    @Operation(summary = \"获取公海客户分析(按用户)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsPoolSummaryByUserRespVO>> getPoolSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getPoolSummaryByUser(reqVO));\n    }\n\n    @GetMapping(\"/get-customer-deal-cycle-by-date\")\n    @Operation(summary = \"获取客户成交周期(按日期)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsCustomerDealCycleByDateRespVO>> getCustomerDealCycleByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getCustomerDealCycleByDate(reqVO));\n    }\n\n    @GetMapping(\"/get-customer-deal-cycle-by-user\")\n    @Operation(summary = \"获取客户成交周期(按用户)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsCustomerDealCycleByUserRespVO>> getCustomerDealCycleByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getCustomerDealCycleByUser(reqVO));\n    }\n\n    @GetMapping(\"/get-customer-deal-cycle-by-area\")\n    @Operation(summary = \"获取客户成交周期(按用户)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsCustomerDealCycleByAreaRespVO>> getCustomerDealCycleByArea(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getCustomerDealCycleByArea(reqVO));\n    }\n\n    @GetMapping(\"/get-customer-deal-cycle-by-product\")\n    @Operation(summary = \"获取客户成交周期(按用户)\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-customer:query')\")\n    public CommonResult<List<CrmStatisticsCustomerDealCycleByProductRespVO>> getCustomerDealCycleByProduct(@Valid CrmStatisticsCustomerReqVO reqVO) {\n        return success(customerService.getCustomerDealCycleByProduct(reqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsFunnelController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.CrmBusinessController;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.*;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsFunnelService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - CRM 销售漏斗\")\n@RestController\n@RequestMapping(\"/crm/statistics-funnel\")\n@Validated\npublic class CrmStatisticsFunnelController {\n\n    @Resource\n    private CrmStatisticsFunnelService funnelService;\n\n    @GetMapping(\"/get-funnel-summary\")\n    @Operation(summary = \"获取销售漏斗统计数据\", description = \"用于【销售漏斗】页面的【销售漏斗分析】\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-funnel:query')\")\n    public CommonResult<CrmStatisticFunnelSummaryRespVO> getFunnelSummary(@Valid CrmStatisticsFunnelReqVO reqVO) {\n        return success(funnelService.getFunnelSummary(reqVO));\n    }\n\n    @GetMapping(\"/get-business-summary-by-end-status\")\n    @Operation(summary = \"获取商机结束状态统计\", description = \"用于【销售漏斗】页面的【销售漏斗分析】\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-funnel:query')\")\n    public CommonResult<List<CrmStatisticsBusinessSummaryByEndStatusRespVO>> getBusinessSummaryByEndStatus(@Valid CrmStatisticsFunnelReqVO reqVO) {\n        return success(funnelService.getBusinessSummaryByEndStatus(reqVO));\n    }\n\n    @GetMapping(\"/get-business-summary-by-date\")\n    @Operation(summary = \"获取新增商机分析(按日期)\", description = \"用于【销售漏斗】页面\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-funnel:query')\")\n    public CommonResult<List<CrmStatisticsBusinessSummaryByDateRespVO>> getBusinessSummaryByDate(@Valid CrmStatisticsFunnelReqVO reqVO) {\n        return success(funnelService.getBusinessSummaryByDate(reqVO));\n    }\n\n    @GetMapping(\"/get-business-inversion-rate-summary-by-date\")\n    @Operation(summary = \"获取商机转化率分析(按日期)\", description = \"用于【销售漏斗】页面\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-funnel:query')\")\n    public CommonResult<List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO>> getBusinessInversionRateSummaryByDate(@Valid CrmStatisticsFunnelReqVO reqVO) {\n        return success(funnelService.getBusinessInversionRateSummaryByDate(reqVO));\n    }\n\n    @GetMapping(\"/get-business-page-by-date\")\n    @Operation(summary = \"获得商机分页(按日期)\", description = \"用于【销售漏斗】页面的【新增商机分析】\")\n    @PreAuthorize(\"@ss.hasPermission('crm:business:query')\")\n    public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByDate(@Valid CrmStatisticsFunnelReqVO pageVO) {\n        PageResult<CrmBusinessDO> pageResult = funnelService.getBusinessPageByDate(pageVO);\n        return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));\n    }\n\n    private List<CrmBusinessRespVO> buildBusinessDetailList(List<CrmBusinessDO> list) {\n        return SpringUtil.getBean(CrmBusinessController.class).buildBusinessDetailList(list);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPerformanceController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;\nimport cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPerformanceService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n\n@Tag(name = \"管理后台 - CRM 员工业绩统计\")\n@RestController\n@RequestMapping(\"/crm/statistics-performance\")\n@Validated\npublic class CrmStatisticsPerformanceController {\n\n    @Resource\n    private CrmStatisticsPerformanceService performanceService;\n\n    @GetMapping(\"/get-contract-count-performance\")\n    @Operation(summary = \"合同数量统计\", description = \"用于【合同数量分析】页面\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-performance:query')\")\n    public CommonResult<List<CrmStatisticsPerformanceRespVO>> getContractCountPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) {\n        return success(performanceService.getContractCountPerformance(performanceReqVO));\n    }\n\n    @GetMapping(\"/get-contract-price-performance\")\n    @Operation(summary = \"合同金额统计\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-performance:query')\")\n    public CommonResult<List<CrmStatisticsPerformanceRespVO>> getContractPriceStaffPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) {\n        return success(performanceService.getContractPricePerformance(performanceReqVO));\n    }\n\n    @GetMapping(\"/get-receivable-price-performance\")\n    @Operation(summary = \"回款金额统计\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-performance:query')\")\n    public CommonResult<List<CrmStatisticsPerformanceRespVO>> getReceivablePriceStaffPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) {\n        return success(performanceService.getReceivablePricePerformance(performanceReqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*;\nimport cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPortraitService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - CRM 客户画像\")\n@RestController\n@RequestMapping(\"/crm/statistics-portrait\")\n@Validated\npublic class CrmStatisticsPortraitController {\n\n    @Resource\n    private CrmStatisticsPortraitService statisticsPortraitService;\n\n    @GetMapping(\"/get-customer-area-summary\")\n    @Operation(summary = \"获取客户地区统计数据\", description = \"用于【城市分布分析】页面\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-portrait:query')\")\n    public CommonResult<List<CrmStatisticCustomerAreaRespVO>> getCustomerAreaSummary(@Valid CrmStatisticsPortraitReqVO reqVO) {\n        return success(statisticsPortraitService.getCustomerSummaryByArea(reqVO));\n    }\n\n    @GetMapping(\"/get-customer-industry-summary\")\n    @Operation(summary = \"获取客户行业统计数据\", description = \"用于【客户行业分析】页面\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-portrait:query')\")\n    public CommonResult<List<CrmStatisticCustomerIndustryRespVO>> getCustomerIndustrySummary(@Valid CrmStatisticsPortraitReqVO reqVO) {\n        return success(statisticsPortraitService.getCustomerSummaryByIndustry(reqVO));\n    }\n\n    @GetMapping(\"/get-customer-level-summary\")\n    @Operation(summary = \"获取客户级别统计数据\", description = \"用于【客户级别分析】页面\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-portrait:query')\")\n    public CommonResult<List<CrmStatisticCustomerLevelRespVO>> getCustomerLevelSummary(@Valid CrmStatisticsPortraitReqVO reqVO) {\n        return success(statisticsPortraitService.getCustomerSummaryByLevel(reqVO));\n    }\n\n    @GetMapping(\"/get-customer-source-summary\")\n    @Operation(summary = \"获取客户来源统计数据\", description = \"用于【客户来源分析】页面\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-portrait:query')\")\n    public CommonResult<List<CrmStatisticCustomerSourceRespVO>> getCustomerSourceSummary(@Valid CrmStatisticsPortraitReqVO reqVO) {\n        return success(statisticsPortraitService.getCustomerSummaryBySource(reqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.http",
    "content": "### 合同金额排行榜\nGET {{baseUrl}}/crm/statistics-rank/get-contract-price-rank?deptId=100&times[0]=2022-12-12 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 回款金额排行榜\nGET {{baseUrl}}/crm/statistics-rank/get-receivable-price-rank?deptId=100&times[0]=2022-12-12 00:00:00&times[1]=2024-12-12 23:59:59\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO;\nimport cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsRankService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - CRM 排行榜统计\")\n@RestController\n@RequestMapping(\"/crm/statistics-rank\")\n@Validated\npublic class CrmStatisticsRankController {\n\n    @Resource\n    private CrmStatisticsRankService rankService;\n\n    @GetMapping(\"/get-contract-price-rank\")\n    @Operation(summary = \"获得合同金额排行榜\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-rank:query')\")\n    public CommonResult<List<CrmStatisticsRankRespVO>> getContractPriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {\n        return success(rankService.getContractPriceRank(rankingReqVO));\n    }\n\n    @GetMapping(\"/get-receivable-price-rank\")\n    @Operation(summary = \"获得回款金额排行榜\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-rank:query')\")\n    public CommonResult<List<CrmStatisticsRankRespVO>> getReceivablePriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {\n        return success(rankService.getReceivablePriceRank(rankingReqVO));\n    }\n\n    @GetMapping(\"/get-contract-count-rank\")\n    @Operation(summary = \"获得签约合同数量排行榜\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-rank:query')\")\n    public CommonResult<List<CrmStatisticsRankRespVO>> getContractCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {\n        return success(rankService.getContractCountRank(rankingReqVO));\n    }\n\n    @GetMapping(\"/get-product-sales-rank\")\n    @Operation(summary = \"获得产品销量排行榜\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-rank:query')\")\n    public CommonResult<List<CrmStatisticsRankRespVO>> getProductSalesRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {\n        return success(rankService.getProductSalesRank(rankingReqVO));\n    }\n\n    @GetMapping(\"/get-customer-count-rank\")\n    @Operation(summary = \"获得新增客户数排行榜\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-rank:query')\")\n    public CommonResult<List<CrmStatisticsRankRespVO>> getCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {\n        return success(rankService.getCustomerCountRank(rankingReqVO));\n    }\n\n    @GetMapping(\"/get-contacts-count-rank\")\n    @Operation(summary = \"获得新增联系人数排行榜\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-rank:query')\")\n    public CommonResult<List<CrmStatisticsRankRespVO>> getContactsCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {\n        return success(rankService.getContactsCountRank(rankingReqVO));\n    }\n\n    @GetMapping(\"/get-follow-count-rank\")\n    @Operation(summary = \"获得跟进次数排行榜\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-rank:query')\")\n    public CommonResult<List<CrmStatisticsRankRespVO>> getFollowCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {\n        return success(rankService.getFollowCountRank(rankingReqVO));\n    }\n\n    @GetMapping(\"/get-follow-customer-count-rank\")\n    @Operation(summary = \"获得跟进客户数排行榜\")\n    @PreAuthorize(\"@ss.hasPermission('crm:statistics-rank:query')\")\n    public CommonResult<List<CrmStatisticsRankRespVO>> getFollowCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {\n        return success(rankService.getFollowCustomerCountRank(rankingReqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n/**\n * 用户客户统计响应 Base Response VO\n *\n * 目的：可以统一拼接子 VO 的 ownerUserId、ownerUserName 属性\n */\n@Data\npublic class CrmStatisticsCustomerByUserBaseRespVO {\n\n    @Schema(description = \"负责人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long ownerUserId;\n\n    @Schema(description = \"负责人\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String ownerUserName;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - CRM 客户转化率分析 VO\")\n@Data\npublic class CrmStatisticsCustomerContractSummaryRespVO {\n\n    @Schema(description = \"客户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String customerName;\n\n    @Schema(description = \"合同名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"演示合同\")\n    private String contractName;\n\n    @Schema(description = \"合同总金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1200.00\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"回款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1200.00\")\n    private BigDecimal receivablePrice;\n\n    @Schema(description = \"客户行业编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer industryId;\n\n    @Schema(description = \"客户来源编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer source;\n\n    @Schema(description = \"负责人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long ownerUserId;\n    @Schema(description = \"负责人\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String ownerUserName;\n\n    @Schema(description = \"创建人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private String creator;\n    @Schema(description = \"创建人\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"源码\")\n    private String creatorUserName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2024-02-01 13:24:26\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"下单日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2024-02-02 00:00:00\")\n    private LocalDateTime orderDate;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByAreaRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户成交周期分析(按区域) VO\")\n@Data\npublic class CrmStatisticsCustomerDealCycleByAreaRespVO {\n\n    @Schema(description = \"省份编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @JsonIgnore\n    private Integer areaId;\n\n    @Schema(description = \"省份名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"浙江省\")\n    private String areaName;\n\n    @Schema(description = \"成交周期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1.0\")\n    private Double customerDealCycle;\n\n    @Schema(description = \"成交客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerDealCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户成交周期分析(按日期) VO\")\n@Data\npublic class CrmStatisticsCustomerDealCycleByDateRespVO {\n\n    @Schema(description = \"时间轴\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"202401\")\n    private String time;\n\n    @Schema(description = \"成交周期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1.0\")\n    private Double customerDealCycle;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByProductRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户成交周期分析(按产品) VO\")\n@Data\npublic class CrmStatisticsCustomerDealCycleByProductRespVO {\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"演示产品\")\n    private String productName;\n\n    @Schema(description = \"成交周期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1.0\")\n    private Double customerDealCycle;\n\n    @Schema(description = \"成交客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerDealCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 成交周期分析(按用户) VO\")\n@Data\npublic class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {\n\n    @Schema(description = \"成交周期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1.0\")\n    private Double customerDealCycle;\n\n    @Schema(description = \"成交客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerDealCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - CRM 数据统计的员工客户分析 Request VO\")\n@Data\npublic class CrmStatisticsCustomerReqVO {\n\n    @Schema(description = \"部门 id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"部门 id 不能为空\")\n    private Long deptId;\n\n    /**\n     * 负责人用户 id, 当用户为空, 则计算部门下用户\n     */\n    @Schema(description = \"负责人用户 id\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"1\")\n    private Long userId;\n\n    /**\n     * userIds 目前不用前端传递，目前是方便后端通过 deptId 读取编号后，设置回来\n     * 后续，可能会支持选择部分用户进行查询\n     */\n    @Schema(description = \"负责人用户 id 集合\", hidden = true, example = \"2\")\n    private List<Long> userIds;\n\n    @Schema(description = \"时间间隔类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @InEnum(value = DateIntervalEnum.class, message = \"时间间隔类型，必须是 {value}\")\n    private Integer interval;\n\n    @Schema(description = \"时间范围\", requiredMode = Schema.RequiredMode.NOT_REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Size(min = 2, max = 2, message = \"请选择时间范围\")\n    private LocalDateTime[] times;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户总量分析(按日期) VO\")\n@Data\npublic class CrmStatisticsCustomerSummaryByDateRespVO {\n\n    @Schema(description = \"时间轴\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"202401\")\n    private String time;\n\n    @Schema(description = \"新建客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerCreateCount;\n\n    @Schema(description = \"成交客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerDealCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - CRM 客户总量分析(按用户) VO\")\n@Data\npublic class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {\n\n    @Schema(description = \"新建客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerCreateCount;\n\n    @Schema(description = \"成交客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerDealCount;\n\n    @Schema(description = \"合同总金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n    private BigDecimal contractPrice;\n\n    @Schema(description = \"回款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n    private BigDecimal receivablePrice;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByDateRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 跟进次数分析(按日期) VO\")\n@Data\npublic class CrmStatisticsFollowUpSummaryByDateRespVO {\n\n    @Schema(description = \"时间轴\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"202401\")\n    private String time;\n\n    @Schema(description = \"跟进次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer followUpRecordCount;\n\n    @Schema(description = \"跟进客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer followUpCustomerCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByTypeRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 跟进次数分析(按类型) VO\")\n@Data\npublic class CrmStatisticsFollowUpSummaryByTypeRespVO {\n\n    @Schema(description = \"跟进类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer followUpType;\n\n    @Schema(description = \"跟进次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer followUpRecordCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByUserRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 跟进次数分析(按用户) VO\")\n@Data\npublic class CrmStatisticsFollowUpSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {\n\n    @Schema(description = \"跟进次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer followUpRecordCount;\n\n    @Schema(description = \"跟进客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer followUpCustomerCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsPoolSummaryByDateRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 公海客户分析(按日期) VO\")\n@Data\npublic class CrmStatisticsPoolSummaryByDateRespVO {\n\n    @Schema(description = \"时间轴\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"202401\")\n    private String time;\n\n    @Schema(description = \"进入公海客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerPutCount;\n\n    @Schema(description = \"公海领取客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerTakeCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsPoolSummaryByUserRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 公海客户分析(按用户) VO\")\n@Data\npublic class CrmStatisticsPoolSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {\n\n    @Schema(description = \"进入公海客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerPutCount;\n\n    @Schema(description = \"公海领取客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerTakeCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticFunnelSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Schema(description = \"管理后台 - CRM 销售漏斗 Response VO\")\n@NoArgsConstructor\n@AllArgsConstructor\n@Data\npublic class CrmStatisticFunnelSummaryRespVO {\n\n    @Schema(description = \"客户数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long customerCount;\n\n    @Schema(description = \"商机数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long businessCount;\n\n    @Schema(description = \"赢单数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long businessWinCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessInversionRateSummaryByDateRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 商机转化率分析(按日期) VO\")\n@Data\npublic class CrmStatisticsBusinessInversionRateSummaryByDateRespVO {\n\n    @Schema(description = \"时间轴\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"202401\")\n    private String time;\n\n    @Schema(description = \"商机数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long businessCount;\n\n    @Schema(description = \"赢单商机数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long businessWinCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessSummaryByDateRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - CRM 新增商机分析(按日期) VO\")\n@Data\npublic class CrmStatisticsBusinessSummaryByDateRespVO {\n\n    @Schema(description = \"时间轴\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"202401\")\n    private String time;\n\n    @Schema(description = \"新增商机数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long businessCreateCount;\n\n    @Schema(description = \"新增商机金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private BigDecimal totalPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessSummaryByEndStatusRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - CRM 商机结束状态统计 Response VO\")\n@NoArgsConstructor\n@AllArgsConstructor\n@Data\npublic class CrmStatisticsBusinessSummaryByEndStatusRespVO {\n\n    @Schema(description = \"结束状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer endStatus;\n\n    @Schema(description = \"商机数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long businessCount;\n\n    @Schema(description = \"商机总金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private BigDecimal totalPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsFunnelReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;\n\nimport cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - CRM 销售漏斗 Request VO\")\n@Data\npublic class CrmStatisticsFunnelReqVO extends PageParam {\n\n    @Schema(description = \"部门 id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"部门 id 不能为空\")\n    private Long deptId;\n\n    /**\n     * 负责人用户 id, 当用户为空, 则计算部门下用户\n     */\n    @Schema(description = \"负责人用户 id\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"1\")\n    private Long userId;\n\n    /**\n     * userIds 目前不用前端传递，目前是方便后端通过 deptId 读取编号后，设置回来\n     * 后续，可能会支持选择部分用户进行查询\n     */\n    @Schema(description = \"负责人用户 id 集合\", hidden = true, example = \"2\")\n    private List<Long> userIds;\n\n    @Schema(description = \"时间间隔类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @InEnum(value = DateIntervalEnum.class, message = \"时间间隔类型，必须是 {value}\")\n    private Integer interval;\n\n    @Schema(description = \"时间范围\", requiredMode = Schema.RequiredMode.NOT_REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Size(min = 2, max = 2, message = \"请选择时间范围\")\n    private LocalDateTime[] times;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - CRM 员工业绩统计 Request VO\")\n@Data\npublic class CrmStatisticsPerformanceReqVO {\n\n    @Schema(description = \"部门 id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"部门 id 不能为空\")\n    private Long deptId;\n\n    /**\n     * 负责人用户 id, 当用户为空, 则计算部门下用户\n     */\n    @Schema(description = \"负责人用户 id\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"1\")\n    private Long userId;\n\n    /**\n     * userIds 目前不用前端传递，目前是方便后端通过 deptId 读取编号后，设置回来\n     * <p>\n     * 后续，可能会支持选择部分用户进行查询\n     */\n    @Schema(description = \"负责人用户 id 集合\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"2\")\n    private List<Long> userIds;\n\n    @Schema(description = \"时间范围\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @NotEmpty(message = \"时间范围不能为空\")\n    private LocalDateTime[] times;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n\n@Schema(description = \"管理后台 - CRM 员工业绩统计 Response VO\")\n@Data\npublic class CrmStatisticsPerformanceRespVO {\n\n    @Schema(description = \"时间轴\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"202401\")\n    private String time;\n\n    @Schema(description = \"当月统计结果\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private BigDecimal currentMonthCount;\n\n    @Schema(description = \"上月统计结果\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private BigDecimal lastMonthCount;\n\n    @Schema(description = \"去年同期统计结果\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3\")\n    private BigDecimal lastYearCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户省份分析 VO\")\n@Data\npublic class CrmStatisticCustomerAreaRespVO {\n\n    @Schema(description = \"省份编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer areaId;\n    @Schema(description = \"省份名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"浙江省\")\n    private String areaName;\n\n    @Schema(description = \"客户个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerCount;\n\n    @Schema(description = \"成交个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer dealCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户行业分析 VO\")\n@Data\npublic class CrmStatisticCustomerIndustryRespVO {\n\n    @Schema(description = \"客户行业ID\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer industryId;\n\n    @Schema(description = \"客户个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerCount;\n\n    @Schema(description = \"成交个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer dealCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户级别分析 VO\")\n@Data\npublic class CrmStatisticCustomerLevelRespVO {\n\n    @Schema(description = \"客户级别编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer level;\n\n    @Schema(description = \"客户个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerCount;\n\n    @Schema(description = \"成交个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer dealCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - CRM 客户来源分析 VO\")\n@Data\npublic class CrmStatisticCustomerSourceRespVO {\n\n    @Schema(description = \"客户来源编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer source;\n\n    @Schema(description = \"客户个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer customerCount;\n\n    @Schema(description = \"成交个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer dealCount;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticsPortraitReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - CRM 客户画像 Request VO\")\n@Data\npublic class CrmStatisticsPortraitReqVO {\n\n    @Schema(description = \"部门 id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"部门 id 不能为空\")\n    private Long deptId;\n\n    /**\n     * 负责人用户 id, 当用户为空, 则计算部门下用户\n     */\n    @Schema(description = \"负责人用户 id\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"1\")\n    private Long userId;\n\n    /**\n     * userIds 目前不用前端传递，目前是方便后端通过 deptId 读取编号后，设置回来\n     * 后续，可能会支持选择部分用户进行查询\n     */\n    @Schema(description = \"负责人用户 id 集合\", hidden = true, example = \"2\")\n    private List<Long> userIds;\n\n    @Schema(description = \"时间范围\", requiredMode = Schema.RequiredMode.NOT_REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Size(min = 2, max = 2, message = \"请选择时间范围\")\n    private LocalDateTime[] times;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankReqVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - CRM 排行榜统计 Request VO\")\n@Data\npublic class CrmStatisticsRankReqVO {\n\n    @Schema(description = \"部门 id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"部门 id 不能为空\")\n    private Long deptId;\n\n    /**\n     * userIds 目前不用前端传递，目前是方便后端通过 deptId 读取编号后，设置回来\n     * <p>\n     * 后续，可能会支持选择部分用户进行查询\n     */\n    @Schema(description = \"负责人用户 id 集合\", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = \"2\")\n    private List<Long> userIds;\n\n    @Schema(description = \"时间范围\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @NotEmpty(message = \"时间范围不能为空\")\n    private LocalDateTime[] times;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java",
    "content": "package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n\n@Schema(description = \"管理后台 - CRM 排行榜统计 Response VO\")\n@Data\npublic class CrmStatisticsRankRespVO {\n\n    @Schema(description = \"负责人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long ownerUserId;\n\n    @Schema(description = \"姓名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private String nickname;\n\n    @Schema(description = \"部门名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private String deptName;\n\n    /**\n     * 数量是个特别“抽象”的概念，在不同排行下，代表不同含义\n     *\n     * 1. 金额：合同金额排行、回款金额排行\n     * 2. 个数：签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行\n     *\n     * 为什么使用 BigDecimal 的原因：\n     */\n    @Schema(description = \"数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private BigDecimal count;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/app/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.crm.controller.app;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/package-info.java",
    "content": "/**\n * 提供 RESTful API 给前端：\n * 1. admin 包：提供给管理后台 yudao-ui-admin 前端项目\n * 2. app 包：提供给用户 APP yudao-ui-app 前端项目，它的 Controller 和 VO 都要添加 App 前缀，用于和管理后台进行区分\n */\npackage cn.iocoder.yudao.module.crm.controller;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/convert/package-info.java",
    "content": "/**\n * 提供 POJO 类的实体转换\n *\n * 目前使用 MapStruct 框架\n */\npackage cn.iocoder.yudao.module.crm.convert;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.business;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.enums.business.CrmBusinessEndStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * CRM 商机 DO\n *\n * @author ljlleo\n */\n@TableName(\"crm_business\")\n@KeySequence(\"crm_business_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmBusinessDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 商机名称\n     */\n    private String name;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link CrmCustomerDO#getId()}\n     */\n    private Long customerId;\n\n    /**\n     * 跟进状态\n     */\n    private Boolean followUpStatus;\n    /**\n     * 最后跟进时间\n     */\n    private LocalDateTime contactLastTime;\n    /**\n     * 下次联系时间\n     */\n    private LocalDateTime contactNextTime;\n\n    /**\n     * 负责人的用户编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long ownerUserId;\n\n    /**\n     * 商机状态组编号\n     *\n     *  关联 {@link CrmBusinessStatusTypeDO#getId()}\n     */\n    private Long statusTypeId;\n    /**\n     * 商机状态编号\n     *\n     * 关联 {@link CrmBusinessStatusDO#getId()}\n     */\n    private Long statusId;\n    /**\n     * 结束状态\n     *\n     * 枚举 {@link CrmBusinessEndStatusEnum}\n     */\n    private Integer endStatus;\n    /**\n     * 结束时的备注\n     */\n    private String endRemark;\n\n    /**\n     * 预计成交日期\n     */\n    private LocalDateTime dealTime;\n    /**\n     * 产品总金额，单位：元\n     *\n     * productPrice = ∑({@link CrmBusinessProductDO#getTotalPrice()})\n     */\n    private BigDecimal totalProductPrice;\n    /**\n     * 整单折扣，百分比\n     */\n    private BigDecimal discountPercent;\n    /**\n     * 商机总金额，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.business;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * CRM 商机产品关联表 DO\n *\n * CrmBusinessDO : CrmBusinessProductDO = 1 : N\n *\n * @author lzxhqs\n */\n@TableName(\"crm_business_product\")\n@KeySequence(\"crm_business_product_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmBusinessProductDO extends BaseDO {\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 商机编号\n     *\n     * 关联 {@link CrmBusinessDO#getId()}\n     */\n    private Long businessId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link CrmProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单价，单位：元\n     *\n     * 冗余 {@link CrmProductDO#getPrice()}\n     */\n    private BigDecimal productPrice;\n    /**\n     * 商机价格, 单位：元\n     */\n    private BigDecimal businessPrice;\n    /**\n     * 数量\n     */\n    private BigDecimal count;\n    /**\n     * 总计价格，单位：元\n     *\n     * totalPrice = businessPrice * count\n     */\n    private BigDecimal totalPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.business;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * CRM 商机状态 DO\n *\n * 注意，它是个配置表\n *\n * @author ljlleo\n */\n@TableName(\"crm_business_status\")\n@KeySequence(\"crm_business_status_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmBusinessStatusDO extends BaseDO {\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 状态类型编号\n     *\n     * 关联 {@link CrmBusinessStatusTypeDO#getId()}\n     */\n    private Long typeId;\n    /**\n     * 状态名\n     */\n    private String name;\n    /**\n     * 赢单率，百分比\n     */\n    private Integer percent;\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusTypeDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.business;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.util.List;\n\n/**\n * CRM 商机状态组 DO\n *\n * 注意，它是个配置表\n *\n * @author ljlleo\n */\n@TableName(value = \"crm_business_status_type\", autoResultMap = true)\n@KeySequence(\"crm_business_status_type_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmBusinessStatusTypeDO extends BaseDO {\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 状态类型名\n     */\n    private String name;\n\n    /**\n     * 使用的部门编号\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> deptIds;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.clue;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * CRM 线索 DO\n *\n * @author Wanwan\n */\n@TableName(\"crm_clue\")\n@KeySequence(\"crm_clue_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmClueDO extends BaseDO {\n\n    /**\n     * 编号，主键自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * 线索名称\n     */\n    private String name;\n\n    /**\n     * 跟进状态\n     */\n    private Boolean followUpStatus;\n    /**\n     * 最后跟进时间\n     */\n    private LocalDateTime contactLastTime;\n    /**\n     * 最后跟进内容\n     */\n    private String contactLastContent;\n    /**\n     * 下次联系时间\n     */\n    private LocalDateTime contactNextTime;\n\n    /**\n     * 负责人的用户编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long ownerUserId;\n\n    /**\n     * 转化状态\n     *\n     * true 表示已转换，会更新 {@link #customerId} 字段\n     */\n    private Boolean transformStatus;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link CrmCustomerDO#getId()}\n     */\n    private Long customerId;\n\n    /**\n     * 手机号\n     */\n    private String mobile;\n    /**\n     * 电话\n     */\n    private String telephone;\n    /**\n     * QQ\n     */\n    private String qq;\n    /**\n     * wechat\n     */\n    private String wechat;\n    /**\n     * email\n     */\n    private String email;\n    /**\n     * 所在地\n     *\n     * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段\n     */\n    private Integer areaId;\n    /**\n     * 详细地址\n     */\n    private String detailAddress;\n    /**\n     * 所属行业\n     *\n     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY}\n     */\n    private Integer industryId;\n    /**\n     * 客户等级\n     *\n     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL}\n     */\n    private Integer level;\n    /**\n     * 客户来源\n     *\n     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE}\n     */\n    private Integer source;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/package-info.java",
    "content": "/**\n * 线索\n */\npackage cn.iocoder.yudao.module.crm.dal.dataobject.clue;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactBusinessDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.contact;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * CRM 联系人与商机的关联 DO\n *\n * @author 芋道源码\n */\n@TableName(\"crm_contact_business\")\n@KeySequence(\"crm_contact_business_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmContactBusinessDO extends BaseDO {\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 联系人编号\n     *\n     * 关联 {@link CrmContactDO#getId()} 字段\n     */\n    private Long contactId;\n    /**\n     * 商机编号\n     *\n     * 关联 {@link CrmBusinessDO#getId()} 字段\n     */\n    private Long businessId;\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.contact;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * CRM 联系人 DO\n *\n * @author 芋道源码\n */\n@TableName(\"crm_contact\")\n@KeySequence(\"crm_contact_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmContactDO extends BaseDO {\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 联系人姓名\n     */\n    private String name;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link CrmCustomerDO#getId()}\n     */\n    private Long customerId;\n\n    /**\n     * 最后跟进时间\n     */\n    private LocalDateTime contactLastTime;\n    /**\n     * 最后跟进内容\n     */\n    private String contactLastContent;\n    /**\n     * 下次联系时间\n     */\n    private LocalDateTime contactNextTime;\n\n    /**\n     * 负责人用户编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long ownerUserId;\n\n    /**\n     * 手机号\n     */\n    private String mobile;\n    /**\n     * 电话\n     */\n    private String telephone;\n    /**\n     * 电子邮箱\n     */\n    private String email;\n    /**\n     * QQ\n     */\n    private Long qq;\n    /**\n     * 微信\n     */\n    private String wechat;\n    /**\n     * 所在地\n     *\n     * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段\n     */\n    private Integer areaId;\n    /**\n     * 详细地址\n     */\n    private String detailAddress;\n    /**\n     * 性别\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.system.enums.common.SexEnum}\n     */\n    private Integer sex;\n    /**\n     * 是否关键决策人\n     */\n    private Boolean master;\n    /**\n     * 职位\n     */\n    private String post;\n    /**\n     * 直属上级\n     *\n     * 关联 {@link CrmContactDO#id}\n     */\n    private Long parentId;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/package-info.java",
    "content": "/**\n * 联系人\n */\npackage cn.iocoder.yudao.module.crm.dal.dataobject.contact;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractConfigDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.contract;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.*;\n\n@TableName(\"crm_contract_config\")\n@KeySequence(\"crm_contract_config_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmContractConfigDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 是否开启提前提醒\n     */\n    @TableField(updateStrategy = FieldStrategy.ALWAYS)\n    private Boolean notifyEnabled;\n    /**\n     * 提前提醒天数\n     */\n    @TableField(updateStrategy = FieldStrategy.ALWAYS)\n    private Integer notifyDays;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.contract;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * CRM 合同 DO\n *\n * @author dhb52\n */\n@TableName(\"crm_contract\")\n@KeySequence(\"crm_contract_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmContractDO extends BaseDO {\n\n    /**\n     * 合同编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 合同名称\n     */\n    private String name;\n    /**\n     * 合同编号\n     */\n    private String no;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link CrmCustomerDO#getId()}\n     */\n    private Long customerId;\n    /**\n     * 商机编号，非必须\n     *\n     * 关联 {@link CrmBusinessDO#getId()}\n     */\n    private Long businessId;\n\n    /**\n     * 最后跟进时间\n     */\n    private LocalDateTime contactLastTime;\n\n    /**\n     * 负责人的用户编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long ownerUserId;\n\n    /**\n     * 工作流编号\n     *\n     * 关联 ProcessInstance 的 id 属性\n     */\n    private String processInstanceId;\n    /**\n     * 审批状态\n     *\n     * 枚举 {@link CrmAuditStatusEnum}\n     */\n    private Integer auditStatus;\n\n    /**\n     * 下单日期\n     */\n    private LocalDateTime orderDate;\n    /**\n     * 开始时间\n     */\n    private LocalDateTime startTime;\n    /**\n     * 结束时间\n     */\n    private LocalDateTime endTime;\n    /**\n     * 产品总金额，单位：元\n     */\n    private BigDecimal totalProductPrice;\n    /**\n     * 整单折扣\n     */\n    private BigDecimal discountPercent;\n    /**\n     * 合同总金额，单位：分\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 客户签约人，非必须\n     *\n     * 关联 {@link CrmContactDO#getId()}\n     */\n    private Long signContactId;\n    /**\n     * 公司签约人，非必须\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long signUserId;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.contract;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * CRM 合同产品关联表 DO\n *\n * @author HUIHUI\n */\n@TableName(\"crm_contract_product\")\n@KeySequence(\"crm_contract_product_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmContractProductDO extends BaseDO {\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 合同编号\n     *\n     * 关联 {@link CrmContractDO#getId()}\n     */\n    private Long contractId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link CrmProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单价，单位：元\n     */\n    private BigDecimal productPrice;\n    /**\n     * 合同价格, 单位：元\n     */\n    private BigDecimal contractPrice;\n    /**\n     * 数量\n     */\n    private BigDecimal count;\n    /**\n     * 总计价格，单位：元\n     *\n     * totalPrice = businessPrice * count\n     */\n    private BigDecimal totalPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.customer;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * CRM 客户 DO\n *\n * @author Wanwan\n */\n@TableName(value = \"crm_customer\")\n@KeySequence(\"crm_customer_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmCustomerDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 客户名称\n     */\n    private String name;\n\n    /**\n     * 跟进状态\n     */\n    private Boolean followUpStatus;\n    /**\n     * 最后跟进时间\n     */\n    private LocalDateTime contactLastTime;\n    /**\n     * 最后跟进内容\n     */\n    private String contactLastContent;\n    /**\n     * 下次联系时间\n     */\n    private LocalDateTime contactNextTime;\n\n    /**\n     * 负责人的用户编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long ownerUserId;\n    /**\n     * 成为负责人的时间\n     */\n    private LocalDateTime ownerTime;\n\n    /**\n     * 锁定状态\n     */\n    private Boolean lockStatus;\n    /**\n     * 成交状态\n     */\n    private Boolean dealStatus;\n\n    /**\n     * 手机\n     */\n    private String mobile;\n    /**\n     * 电话\n     */\n    private String telephone;\n    /**\n     * QQ\n     */\n    private String qq;\n    /**\n     * wechat\n     */\n    private String wechat;\n    /**\n     * email\n     */\n    private String email;\n    /**\n     * 所在地\n     *\n     * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段\n     */\n    private Integer areaId;\n    /**\n     * 详细地址\n     */\n    private String detailAddress;\n    /**\n     * 所属行业\n     *\n     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY}\n     */\n    private Integer industryId;\n    /**\n     * 客户等级\n     *\n     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL}\n     */\n    private Integer level;\n    /**\n     * 客户来源\n     *\n     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE}\n     */\n    private Integer source;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerLimitConfigDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.customer;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.util.List;\n\n/**\n * 客户限制配置 DO\n *\n * @author Wanwan\n */\n@TableName(value = \"crm_customer_limit_config\", autoResultMap = true)\n@KeySequence(\"crm_customer_limit_config_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmCustomerLimitConfigDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 规则类型\n     * <p>\n     * 枚举 {@link CrmCustomerLimitConfigTypeEnum}\n     */\n    private Integer type;\n    /**\n     * 规则适用人群\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> userIds;\n    /**\n     * 规则适用部门\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> deptIds;\n    /**\n     * 数量上限\n     */\n    private Integer maxCount;\n    /**\n     * 成交客户是否占有拥有客户数\n     *\n     * 当且仅当 {@link #type} 为 1 时，进行使用\n     */\n    private Boolean dealCountEnabled;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerPoolConfigDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.customer;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.*;\n\n/**\n * 客户公海配置 DO\n *\n * @author Wanwan\n */\n@TableName(value = \"crm_customer_pool_config\")\n@KeySequence(\"crm_customer_pool_config_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmCustomerPoolConfigDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 是否启用客户公海\n     */\n    private Boolean enabled;\n    /**\n     * 未跟进放入公海天数\n     */\n    @TableField(updateStrategy = FieldStrategy.ALWAYS)\n    private Integer contactExpireDays;\n    /**\n     * 未成交放入公海天数\n     */\n    @TableField(updateStrategy = FieldStrategy.ALWAYS)\n    private Integer dealExpireDays;\n    /**\n     * 是否开启提前提醒\n     */\n    @TableField(updateStrategy = FieldStrategy.ALWAYS)\n    private Boolean notifyEnabled;\n    /**\n     * 提前提醒天数\n     */\n    @TableField(updateStrategy = FieldStrategy.ALWAYS)\n    private Integer notifyDays;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.followup;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 跟进记录 DO\n *\n * 用于记录客户、联系人的每一次跟进\n *\n * @author 芋道源码\n */\n@TableName(value = \"crm_follow_up_record\", autoResultMap = true)\n@KeySequence(\"crm_follow_up_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmFollowUpRecordDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 数据类型\n     *\n     * 枚举 {@link CrmBizTypeEnum}\n     */\n    private Integer bizType;\n    /**\n     * 数据编号\n     *\n     * 关联 {@link CrmBizTypeEnum} 对应模块 DO 的 id 字段\n     */\n    private Long bizId;\n\n    /**\n     * 跟进类型\n     *\n     * 关联 {@link DictTypeConstants#CRM_FOLLOW_UP_TYPE} 字典\n     */\n    private Integer type;\n    /**\n     * 跟进内容\n     */\n    private String content;\n    /**\n     * 下次联系时间\n     */\n    private LocalDateTime nextTime;\n\n    /**\n     * 图片\n     */\n    @TableField(typeHandler = StringListTypeHandler.class)\n    private List<String> picUrls;\n    /**\n     * 附件\n     */\n    @TableField(typeHandler = StringListTypeHandler.class)\n    private List<String> fileUrls;\n\n    /**\n     * 关联的商机编号数组\n     *\n     * 关联 {@link CrmBusinessDO#getId()}\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> businessIds;\n    /**\n     * 关联的联系人编号数组\n     *\n     * 关联 {@link CrmContactDO#getId()}\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> contactIds;\n\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/permission/CrmPermissionDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.permission;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * CRM 数据权限 DO\n *\n * @author HUIHUI\n */\n@TableName(\"crm_permission\")\n@KeySequence(\"crm_permission_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmPermissionDO extends BaseDO {\n\n    /**\n     * 编号，主键自增\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 数据类型\n     *\n     * 枚举 {@link CrmBizTypeEnum}\n     */\n    private Integer bizType;\n    /**\n     * 数据编号\n     *\n     * 关联 {@link CrmBizTypeEnum} 对应模块 DO 的 id 字段\n     */\n    private Long bizId;\n\n    /**\n     * 用户编号\n     *\n     * 关联 AdminUser 的 id 字段\n     */\n    private Long userId;\n\n    /**\n     * 权限级别\n     *\n     * 关联 {@link CrmPermissionLevelEnum}\n     */\n    private Integer level;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductCategoryDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.product;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 产品分类 DO\n *\n * @author ZanGe丶\n */\n@TableName(\"crm_product_category\")\n@KeySequence(\"crm_product_category_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmProductCategoryDO extends BaseDO {\n\n    /**\n     * 父分类编号 - 根分类\n     */\n    public static final Long PARENT_ID_NULL = 0L;\n    /**\n     * 限定分类层级\n     */\n    public static final int CATEGORY_LEVEL = 2;\n\n    /**\n     * 分类编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 分类名称\n     */\n    private String name;\n    /**\n     * 父级编号\n     *\n     * 关联 {@link CrmProductCategoryDO#getId()}\n     */\n    private Long parentId;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.product;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * CRM 产品 DO\n *\n * @author ZanGe丶\n */\n@TableName(\"crm_product\")\n@KeySequence(\"crm_product_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmProductDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 产品名称\n     */\n    private String name;\n    /**\n     * 产品编码\n     */\n    private String no;\n    /**\n     * 单位\n     *\n     * 字典 {@link DictTypeConstants#CRM_PRODUCT_UNIT}\n     */\n    private Integer unit;\n    /**\n     * 价格，单位：元\n     */\n    private BigDecimal price;\n    /**\n     * 状态\n     *\n     * 关联 {@link CrmProductStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 产品分类 ID\n     *\n     * 关联 {@link CrmProductCategoryDO#getId()} 字段\n     */\n    private Long categoryId;\n    /**\n     * 产品描述\n     */\n    private String description;\n    /**\n     * 负责人的用户编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long ownerUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/package-info.java",
    "content": "/**\n * 产品表\n */\npackage cn.iocoder.yudao.module.crm.dal.dataobject.product;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.receivable;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;\nimport cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * 回款 DO\n *\n * @author 赤焰\n */\n@TableName(\"crm_receivable\")\n@KeySequence(\"crm_receivable_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmReceivableDO extends BaseDO {\n\n    /**\n     * ID\n     */\n    @TableId\n    private Long id;\n    /**\n     * 回款编号\n     */\n    private String no;\n    /**\n     * 回款计划编号\n     *\n     * 关联 {@link CrmReceivablePlanDO#getId()}，非必须\n     */\n    private Long planId;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link CrmCustomerDO#getId()}\n     */\n    private Long customerId;\n    /**\n     * 合同编号\n     *\n     * 关联 {@link CrmContractDO#getId()}\n     */\n    private Long contractId;\n    /**\n     * 负责人编号，关联 {@link AdminUserRespDTO#getId()}\n     */\n    private Long ownerUserId;\n\n    /**\n     * 回款日期\n     */\n    private LocalDateTime returnTime;\n    /**\n     * 回款方式\n     *\n     * 枚举 {@link CrmReceivableReturnTypeEnum}\n     */\n    private Integer returnType;\n    /**\n     * 计划回款金额，单位：元\n     */\n    private BigDecimal price;\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 工作流编号\n     *\n     * 关联 ProcessInstance 的 id 属性\n     */\n    private String processInstanceId;\n    /**\n     * 审批状态\n     *\n     * 枚举 {@link CrmAuditStatusEnum}\n     */\n    private Integer auditStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.dataobject.receivable;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * CRM 回款计划 DO\n *\n * @author 芋道源码\n */\n@TableName(\"crm_receivable_plan\")\n@KeySequence(\"crm_receivable_plan_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmReceivablePlanDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 期数\n     */\n    private Integer period;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link CrmCustomerDO#getId()}\n     */\n    private Long customerId;\n    /**\n     * 合同编号\n     *\n     * 关联 {@link CrmContractDO#getId()}\n     */\n    private Long contractId;\n\n    /**\n     * 负责人编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long ownerUserId;\n\n    /**\n     * 计划回款日期\n     */\n    private LocalDateTime returnTime;\n    /**\n     * 计划回款类型\n     *\n     * 枚举 {@link CrmReceivableReturnTypeEnum}\n     */\n    private Integer returnType;\n    /**\n     * 计划回款金额，单位：元\n     */\n    private BigDecimal price;\n\n    /**\n     * 回款编号，关联 {@link CrmReceivableDO#getId()}\n     */\n    private Long receivableId;\n\n    /**\n     * 提前几天提醒\n     */\n    private Integer remindDays;\n    /**\n     * 提醒日期\n     */\n    private LocalDateTime remindTime;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.business;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 商机 Mapper\n *\n * @author ljlleo\n */\n@Mapper\npublic interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {\n\n    default int updateOwnerUserIdById(Long id, Long ownerUserId) {\n        return update(new LambdaUpdateWrapper<CrmBusinessDO>()\n                .eq(CrmBusinessDO::getId, id)\n                .set(CrmBusinessDO::getOwnerUserId, ownerUserId));\n    }\n\n    default PageResult<CrmBusinessDO> selectPageByCustomerId(CrmBusinessPageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmBusinessDO>()\n                .eq(CrmBusinessDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号\n                .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())\n                .orderByDesc(CrmBusinessDO::getId));\n    }\n\n    default PageResult<CrmBusinessDO> selectPageByContactId(CrmBusinessPageReqVO pageReqVO, Collection<Long> businessIds) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmBusinessDO>()\n                .in(CrmBusinessDO::getId, businessIds) // 指定商机编号\n                .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())\n                .orderByDesc(CrmBusinessDO::getId));\n    }\n\n    default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) {\n        MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>();\n        // 拼接数据权限的查询条件\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(),\n                CrmBusinessDO::getId, userId, pageReqVO.getSceneType());\n        // 拼接自身的查询条件\n        query.selectAll(CrmBusinessDO.class)\n                .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())\n                .orderByDesc(CrmBusinessDO::getId);\n        return selectJoinPage(pageReqVO, CrmBusinessDO.class, query);\n    }\n\n    default Long selectCountByStatusTypeId(Long statusTypeId) {\n        return selectCount(CrmBusinessDO::getStatusTypeId, statusTypeId);\n    }\n\n    default List<CrmBusinessDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {\n        return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()\n                .eq(CrmBusinessDO::getCustomerId, customerId)\n                .eq(CrmBusinessDO::getOwnerUserId, ownerUserId));\n    }\n\n    default PageResult<CrmBusinessDO> selectPage(CrmStatisticsFunnelReqVO pageVO) {\n        return selectPage(pageVO, new LambdaQueryWrapperX<CrmBusinessDO>()\n                .in(CrmBusinessDO::getOwnerUserId, pageVO.getUserIds())\n                .betweenIfPresent(CrmBusinessDO::getCreateTime, pageVO.getTimes()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.business;\n\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 商机产品 Mapper\n *\n * @author lzxhqs\n */\n@Mapper\npublic interface CrmBusinessProductMapper extends BaseMapperX<CrmBusinessProductDO> {\n\n    default List<CrmBusinessProductDO> selectListByBusinessId(Long businessId) {\n        return selectList(CrmBusinessProductDO::getBusinessId, businessId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.business;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 商机状态 Mapper\n *\n * @author ljlleo\n */\n@Mapper\npublic interface CrmBusinessStatusMapper extends BaseMapperX<CrmBusinessStatusDO> {\n\n    default int deleteByTypeId(Long typeId) {\n        return delete(CrmBusinessStatusDO::getTypeId, typeId);\n    }\n\n    default List<CrmBusinessStatusDO> selectListByTypeId(Long typeId) {\n        return selectList(CrmBusinessStatusDO::getTypeId, typeId);\n    }\n\n    default CrmBusinessStatusDO selectByTypeIdAndId(Long statusTypeId, Long statusId) {\n        return selectOne(CrmBusinessStatusDO::getTypeId, statusTypeId,\n                CrmBusinessStatusDO::getId, statusId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusTypeMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.business;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 商机状态组 Mapper\n *\n * @author ljlleo\n */\n@Mapper\npublic interface CrmBusinessStatusTypeMapper extends BaseMapperX<CrmBusinessStatusTypeDO> {\n\n    default PageResult<CrmBusinessStatusTypeDO> selectPage(PageParam reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<CrmBusinessStatusTypeDO>()\n                .orderByDesc(CrmBusinessStatusTypeDO::getId));\n    }\n\n    default CrmBusinessStatusTypeDO selectByName(String name) {\n        return selectOne(CrmBusinessStatusTypeDO::getName, name);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.clue;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 线索 Mapper\n *\n * @author Wanwan\n */\n@Mapper\npublic interface CrmClueMapper extends BaseMapperX<CrmClueDO> {\n\n    default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO pageReqVO, Long userId) {\n        MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>();\n        // 拼接数据权限的查询条件\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CLUE.getType(),\n                CrmClueDO::getId, userId, pageReqVO.getSceneType());\n        // 拼接自身的查询条件\n        query.selectAll(CrmClueDO.class)\n                .likeIfPresent(CrmClueDO::getName, pageReqVO.getName())\n                .eqIfPresent(CrmClueDO::getTransformStatus, pageReqVO.getTransformStatus())\n                .likeIfPresent(CrmClueDO::getTelephone, pageReqVO.getTelephone())\n                .likeIfPresent(CrmClueDO::getMobile, pageReqVO.getMobile())\n                .eqIfPresent(CrmClueDO::getIndustryId, pageReqVO.getIndustryId())\n                .eqIfPresent(CrmClueDO::getLevel, pageReqVO.getLevel())\n                .eqIfPresent(CrmClueDO::getSource, pageReqVO.getSource())\n                .eqIfPresent(CrmClueDO::getFollowUpStatus, pageReqVO.getFollowUpStatus())\n                .betweenIfPresent(CrmClueDO::getCreateTime, pageReqVO.getCreateTime())\n                .orderByDesc(CrmClueDO::getId);\n        return selectJoinPage(pageReqVO, CrmClueDO.class, query);\n    }\n\n    default Long selectCountByFollow(Long userId) {\n        MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>();\n        // 我负责的 + 非公海\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CLUE.getType(),\n                CrmClueDO::getId, userId, CrmSceneTypeEnum.OWNER.getType());\n        // 未跟进 + 未转化\n        query.eq(CrmClueDO::getFollowUpStatus, false)\n                .eq(CrmClueDO::getTransformStatus, false);\n        return selectCount(query);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/package-info.java",
    "content": "/**\n * 线索\n */\npackage cn.iocoder.yudao.module.crm.dal.mysql.clue;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactBusinessMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.contact;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * CRM 联系人商机关联 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface CrmContactBusinessMapper extends BaseMapperX<CrmContactBusinessDO> {\n\n    default CrmContactBusinessDO selectByContactIdAndBusinessId(Long contactId, Long businessId) {\n        return selectOne(CrmContactBusinessDO::getContactId, contactId,\n                CrmContactBusinessDO::getBusinessId, businessId);\n    }\n\n    default void deleteByContactIdAndBusinessId(Long contactId, Collection<Long> businessIds) {\n        delete(new LambdaQueryWrapper<CrmContactBusinessDO>()\n                .eq(CrmContactBusinessDO::getContactId, contactId)\n                .in(CrmContactBusinessDO::getBusinessId, businessIds));\n    }\n\n    default void deleteByBusinessIdAndContactId(Long businessId, List<Long> contactIds) {\n        delete(new LambdaQueryWrapper<CrmContactBusinessDO>()\n                .eq(CrmContactBusinessDO::getBusinessId, businessId)\n                .in(CrmContactBusinessDO::getContactId, contactIds));\n    }\n\n    default List<CrmContactBusinessDO> selectListByContactId(Long contactId) {\n        return selectList(CrmContactBusinessDO::getContactId, contactId);\n    }\n\n    default List<CrmContactBusinessDO> selectListByBusinessId(Long businessId) {\n        return selectList(CrmContactBusinessDO::getBusinessId, businessId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.contact;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * CRM 联系人 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface CrmContactMapper extends BaseMapperX<CrmContactDO> {\n\n    default int updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) {\n        return update(new LambdaUpdateWrapper<CrmContactDO>()\n                .eq(CrmContactDO::getCustomerId, customerId)\n                .set(CrmContactDO::getOwnerUserId, ownerUserId));\n    }\n\n    default PageResult<CrmContactDO> selectPageByCustomerId(CrmContactPageReqVO pageVO) {\n        return selectPage(pageVO, new LambdaQueryWrapperX<CrmContactDO>()\n                .eq(CrmContactDO::getCustomerId, pageVO.getCustomerId()) // 指定客户编号\n                .likeIfPresent(CrmContactDO::getName, pageVO.getName())\n                .eqIfPresent(CrmContactDO::getMobile, pageVO.getMobile())\n                .eqIfPresent(CrmContactDO::getTelephone, pageVO.getTelephone())\n                .eqIfPresent(CrmContactDO::getEmail, pageVO.getEmail())\n                .eqIfPresent(CrmContactDO::getQq, pageVO.getQq())\n                .eqIfPresent(CrmContactDO::getWechat, pageVO.getWechat())\n                .orderByDesc(CrmContactDO::getId));\n    }\n\n    default PageResult<CrmContactDO> selectPageByBusinessId(CrmContactPageReqVO pageVO, Collection<Long> ids) {\n        return selectPage(pageVO, new LambdaQueryWrapperX<CrmContactDO>()\n                .in(CrmContactDO::getId, ids) // 指定联系人编号\n                .likeIfPresent(CrmContactDO::getName, pageVO.getName())\n                .eqIfPresent(CrmContactDO::getMobile, pageVO.getMobile())\n                .eqIfPresent(CrmContactDO::getTelephone, pageVO.getTelephone())\n                .eqIfPresent(CrmContactDO::getEmail, pageVO.getEmail())\n                .eqIfPresent(CrmContactDO::getQq, pageVO.getQq())\n                .eqIfPresent(CrmContactDO::getWechat, pageVO.getWechat())\n                .orderByDesc(CrmContactDO::getId));\n    }\n\n    default PageResult<CrmContactDO> selectPage(CrmContactPageReqVO pageReqVO, Long userId) {\n        MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>();\n        // 拼接数据权限的查询条件\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(),\n                CrmContactDO::getId, userId, pageReqVO.getSceneType());\n        // 拼接自身的查询条件\n        query.selectAll(CrmContactDO.class)\n                .likeIfPresent(CrmContactDO::getName, pageReqVO.getName())\n                .eqIfPresent(CrmContactDO::getMobile, pageReqVO.getMobile())\n                .eqIfPresent(CrmContactDO::getTelephone, pageReqVO.getTelephone())\n                .eqIfPresent(CrmContactDO::getEmail, pageReqVO.getEmail())\n                .eqIfPresent(CrmContactDO::getQq, pageReqVO.getQq())\n                .eqIfPresent(CrmContactDO::getWechat, pageReqVO.getWechat())\n                .orderByDesc(CrmContactDO::getId);\n        return selectJoinPage(pageReqVO, CrmContactDO.class, query);\n    }\n\n    default List<CrmContactDO> selectListByCustomerId(Long customerId) {\n        return selectList(CrmContactDO::getCustomerId, customerId);\n    }\n\n    default List<CrmContactDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {\n        return selectList(CrmContactDO::getCustomerId, customerId,\n                CrmContactDO::getOwnerUserId, ownerUserId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractConfigMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.contract;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 合同配置 Mapper\n *\n * @author Wanwan\n */\n@Mapper\npublic interface CrmContractConfigMapper extends BaseMapperX<CrmContractConfigDO> {\n\n    default CrmContractConfigDO selectOne() {\n        return selectOne(new QueryWrapperX<CrmContractConfigDO>().limitN(1));\n    }\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.contract;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * CRM 合同 Mapper\n *\n * @author dhb52\n */\n@Mapper\npublic interface CrmContractMapper extends BaseMapperX<CrmContractDO> {\n\n    default CrmContractDO selectByNo(String no) {\n        return selectOne(CrmContractDO::getNo, no);\n    }\n\n    default PageResult<CrmContractDO> selectPageByCustomerId(CrmContractPageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmContractDO>()\n                .eq(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())\n                .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())\n                .likeIfPresent(CrmContractDO::getName, pageReqVO.getName())\n                .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())\n                .eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())\n                .orderByDesc(CrmContractDO::getId));\n    }\n\n    default PageResult<CrmContractDO> selectPageByBusinessId(CrmContractPageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmContractDO>()\n                .eq(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())\n                .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())\n                .likeIfPresent(CrmContractDO::getName, pageReqVO.getName())\n                .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())\n                .eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())\n                .orderByDesc(CrmContractDO::getId));\n    }\n\n    default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId, CrmContractConfigDO config) {\n        MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();\n        // 拼接数据权限的查询条件\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),\n                CrmContractDO::getId, userId, pageReqVO.getSceneType());\n        // 拼接自身的查询条件\n        query.selectAll(CrmContractDO.class)\n                .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())\n                .likeIfPresent(CrmContractDO::getName, pageReqVO.getName())\n                .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())\n                .eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())\n                .eqIfPresent(CrmContractDO::getAuditStatus, pageReqVO.getAuditStatus())\n                .orderByDesc(CrmContractDO::getId);\n\n        // Backlog: 即将到期的合同\n        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());\n        LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());\n        if (CrmContractPageReqVO.EXPIRY_TYPE_ABOUT_TO_EXPIRE.equals(pageReqVO.getExpiryType())) { // 即将到期\n            query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus())\n                    .between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(config.getNotifyDays()));\n        } else if (CrmContractPageReqVO.EXPIRY_TYPE_EXPIRED.equals(pageReqVO.getExpiryType())) { // 已到期\n            query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus())\n                    .lt(CrmContractDO::getEndTime, endOfToday);\n        }\n        return selectJoinPage(pageReqVO, CrmContractDO.class, query);\n    }\n\n    default Long selectCountByContactId(Long contactId) {\n        return selectCount(CrmContractDO::getSignContactId, contactId);\n    }\n\n    default Long selectCountByBusinessId(Long businessId) {\n        return selectCount(CrmContractDO::getBusinessId, businessId);\n    }\n\n    default Long selectCountByAudit(Long userId) {\n        MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();\n        // 我负责的 + 非公海\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),\n                CrmContractDO::getId, userId, CrmSceneTypeEnum.OWNER.getType());\n        // 未审核\n        query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.PROCESS.getStatus());\n        return selectCount(query);\n    }\n\n    default Long selectCountByRemind(Long userId, CrmContractConfigDO config) {\n        MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();\n        // 我负责的 + 非公海\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),\n                CrmContractDO::getId, userId, CrmSceneTypeEnum.OWNER.getType());\n        // 即将到期\n        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());\n        LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());\n        query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus()) // 必须审批通过！\n                .between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(config.getNotifyDays()));\n        return selectCount(query);\n    }\n\n    default List<CrmContractDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {\n        return selectList(new LambdaQueryWrapperX<CrmContractDO>()\n                .eq(CrmContractDO::getCustomerId, customerId)\n                .eq(CrmContractDO::getOwnerUserId, ownerUserId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.contract;\n\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 合同产品 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface CrmContractProductMapper extends BaseMapperX<CrmContractProductDO> {\n\n    default List<CrmContractProductDO> selectListByContractId(Long contractId) {\n        return selectList(new LambdaQueryWrapperX<CrmContractProductDO>().eq(CrmContractProductDO::getContractId, contractId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerLimitConfigMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.customer;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 客户限制配置 Mapper\n *\n * @author Wanwan\n */\n@Mapper\npublic interface CrmCustomerLimitConfigMapper extends BaseMapperX<CrmCustomerLimitConfigDO> {\n\n    default PageResult<CrmCustomerLimitConfigDO> selectPage(CrmCustomerLimitConfigPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<CrmCustomerLimitConfigDO>()\n                .eqIfPresent(CrmCustomerLimitConfigDO::getType, reqVO.getType())\n                .orderByDesc(CrmCustomerLimitConfigDO::getId));\n    }\n\n    default List<CrmCustomerLimitConfigDO> selectListByTypeAndUserIdAndDeptId(\n            Integer type, Long userId, Long deptId) {\n        LambdaQueryWrapperX<CrmCustomerLimitConfigDO> query = new LambdaQueryWrapperX<CrmCustomerLimitConfigDO>()\n                .eq(CrmCustomerLimitConfigDO::getType, type);\n        query.apply(\"FIND_IN_SET({0}, user_ids) > 0\", userId);\n        if (deptId != null) {\n            query.apply(\"FIND_IN_SET({0}, dept_ids) > 0\", deptId);\n        }\n        return selectList(query);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.customer;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.CrmCustomerPageReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.springframework.lang.Nullable;\nimport org.springframework.util.Assert;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 客户 Mapper\n *\n * @author Wanwan\n */\n@Mapper\npublic interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {\n\n    default Long selectCountByLockStatusAndOwnerUserId(Boolean lockStatus, Long ownerUserId) {\n        return selectCount(new LambdaUpdateWrapper<CrmCustomerDO>()\n                .eq(CrmCustomerDO::getLockStatus, lockStatus)\n                .eq(CrmCustomerDO::getOwnerUserId, ownerUserId));\n    }\n\n    default Long selectCountByDealStatusAndOwnerUserId(@Nullable Boolean dealStatus, Long ownerUserId) {\n        return selectCount(new LambdaQueryWrapperX<CrmCustomerDO>()\n                .eqIfPresent(CrmCustomerDO::getDealStatus, dealStatus)\n                .eq(CrmCustomerDO::getOwnerUserId, ownerUserId));\n    }\n\n    default int updateOwnerUserIdById(Long id, Long ownerUserId) {\n        return update(new LambdaUpdateWrapper<CrmCustomerDO>()\n                .eq(CrmCustomerDO::getId, id)\n                .set(CrmCustomerDO::getOwnerUserId, ownerUserId));\n    }\n\n    default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long ownerUserId) {\n        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();\n        // 拼接数据权限的查询条件\n        if (Boolean.TRUE.equals(pageReqVO.getPool())) {\n            query.isNull(CrmCustomerDO::getOwnerUserId);\n        } else {\n            CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),\n                    CrmCustomerDO::getId, ownerUserId, pageReqVO.getSceneType());\n        }\n        // 拼接自身的查询条件\n        query.selectAll(CrmCustomerDO.class)\n                .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())\n                .eqIfPresent(CrmCustomerDO::getMobile, pageReqVO.getMobile())\n                .eqIfPresent(CrmCustomerDO::getIndustryId, pageReqVO.getIndustryId())\n                .eqIfPresent(CrmCustomerDO::getLevel, pageReqVO.getLevel())\n                .eqIfPresent(CrmCustomerDO::getSource, pageReqVO.getSource())\n                .eqIfPresent(CrmCustomerDO::getFollowUpStatus, pageReqVO.getFollowUpStatus());\n\n        // backlog 查询\n        if (ObjUtil.isNotNull(pageReqVO.getContactStatus())) {\n            Assert.isNull(pageReqVO.getPool(), \"pool 必须是 null\");\n            LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());\n            LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());\n            if (pageReqVO.getContactStatus().equals(CrmCustomerPageReqVO.CONTACT_TODAY)) { // 今天需联系\n                query.between(CrmCustomerDO::getContactNextTime, beginOfToday, endOfToday);\n            } else if (pageReqVO.getContactStatus().equals(CrmCustomerPageReqVO.CONTACT_EXPIRED)) { // 已逾期\n                query.lt(CrmCustomerDO::getContactNextTime, beginOfToday);\n            } else if (pageReqVO.getContactStatus().equals(CrmCustomerPageReqVO.CONTACT_ALREADY)) { // 已联系\n                query.between(CrmCustomerDO::getContactLastTime, beginOfToday, endOfToday);\n            } else {\n                throw new IllegalArgumentException(\"未知联系状态：\" + pageReqVO.getContactStatus());\n            }\n        }\n        return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);\n    }\n\n    default CrmCustomerDO selectByCustomerName(String name) {\n        return selectOne(CrmCustomerDO::getName, name);\n    }\n\n    default PageResult<CrmCustomerDO> selectPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,\n                                                                      CrmCustomerPoolConfigDO poolConfig,\n                                                                      Long ownerUserId) {\n        final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfig, ownerUserId);\n        return selectJoinPage(pageReqVO, CrmCustomerDO.class, query.selectAll(CrmCustomerDO.class));\n    }\n\n    default Long selectPutPoolRemindCustomerCount(CrmCustomerPageReqVO pageReqVO,\n                                                  CrmCustomerPoolConfigDO poolConfig,\n                                                  Long userId) {\n        final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfig, userId);\n        return selectCount(query);\n    }\n\n    static MPJLambdaWrapperX<CrmCustomerDO> buildPutPoolRemindCustomerQuery(CrmCustomerPageReqVO pageReqVO,\n                                                                                    CrmCustomerPoolConfigDO poolConfig,\n                                                                                    Long ownerUserId) {\n        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();\n        // 拼接数据权限的查询条件\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),\n                CrmCustomerDO::getId, ownerUserId, pageReqVO.getSceneType());\n\n        // 未锁定 + 未成交\n        query.eq(CrmCustomerDO::getLockStatus, false).eq(CrmCustomerDO::getDealStatus, false);\n\n        // 情况一：未成交提醒日期区间\n        Integer dealExpireDays = poolConfig.getDealExpireDays();\n        LocalDateTime startDealRemindTime = LocalDateTime.now().minusDays(dealExpireDays);\n        LocalDateTime endDealRemindTime = LocalDateTime.now()\n                .minusDays(Math.max(dealExpireDays - poolConfig.getNotifyDays(), 0));\n        // 情况二：未跟进提醒日期区间\n        Integer contactExpireDays = poolConfig.getContactExpireDays();\n        LocalDateTime startContactRemindTime = LocalDateTime.now().minusDays(contactExpireDays);\n        LocalDateTime endContactRemindTime = LocalDateTime.now()\n                .minusDays(Math.max(contactExpireDays - poolConfig.getNotifyDays(), 0));\n        query.and(q -> {\n            // 情况一：成交超时提醒\n            q.between(CrmCustomerDO::getOwnerTime, startDealRemindTime, endDealRemindTime)\n            // 情况二：跟进超时提醒\n            .or(w -> w.between(CrmCustomerDO::getOwnerTime, startContactRemindTime, endContactRemindTime)\n                    .and(p -> p.between(CrmCustomerDO::getContactLastTime, startContactRemindTime, endContactRemindTime)\n                            .or().isNull(CrmCustomerDO::getContactLastTime)));\n        });\n        return query;\n    }\n\n    /**\n     * 获得需要过期到公海的客户列表\n     *\n     * @return 客户列表\n     */\n    default List<CrmCustomerDO> selectListByAutoPool(CrmCustomerPoolConfigDO poolConfig) {\n        LambdaQueryWrapper<CrmCustomerDO> query = new LambdaQueryWrapper<>();\n        query.gt(CrmCustomerDO::getOwnerUserId, 0);\n        // 未锁定 + 未成交\n        query.eq(CrmCustomerDO::getLockStatus, false).eq(CrmCustomerDO::getDealStatus, false);\n        // 已经超时\n        LocalDateTime dealExpireTime = LocalDateTime.now().minusDays(poolConfig.getDealExpireDays());\n        LocalDateTime contactExpireTime = LocalDateTime.now().minusDays(poolConfig.getContactExpireDays());\n        query.and(q -> {\n            // 情况一：成交超时\n            q.lt(CrmCustomerDO::getOwnerTime, dealExpireTime)\n            // 情况二：跟进超时\n            .or(w -> w.lt(CrmCustomerDO::getOwnerTime, contactExpireTime)\n                    .and(p -> p.lt(CrmCustomerDO::getContactLastTime, contactExpireTime)\n                            .or().isNull(CrmCustomerDO::getContactLastTime)));\n        });\n        return selectList(query);\n    }\n\n    default Long selectCountByTodayContact(Long ownerUserId) {\n        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();\n        // 我负责的 + 非公海\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),\n                CrmCustomerDO::getId, ownerUserId, CrmSceneTypeEnum.OWNER.getType());\n        // 今天需联系\n        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());\n        LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());\n        query.between(CrmCustomerDO::getContactNextTime, beginOfToday, endOfToday);\n        return selectCount(query);\n    }\n\n    default Long selectCountByFollow(Long ownerUserId) {\n        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();\n        // 我负责的 + 非公海\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),\n                CrmCustomerDO::getId, ownerUserId, CrmSceneTypeEnum.OWNER.getType());\n        // 未跟进\n        query.eq(CrmClueDO::getFollowUpStatus, false);\n        return selectCount(query);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerPoolConfigMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.customer;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 客户公海配置 Mapper\n *\n * @author Wanwan\n */\n@Mapper\npublic interface CrmCustomerPoolConfigMapper extends BaseMapperX<CrmCustomerPoolConfigDO> {\n\n    default CrmCustomerPoolConfigDO selectOne() {\n        return selectOne(new LambdaQueryWrapperX<CrmCustomerPoolConfigDO>().last(\"LIMIT 1\"));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.followup;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 跟进记录 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface CrmFollowUpRecordMapper extends BaseMapperX<CrmFollowUpRecordDO> {\n\n    default PageResult<CrmFollowUpRecordDO> selectPage(CrmFollowUpRecordPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<CrmFollowUpRecordDO>()\n                .eqIfPresent(CrmFollowUpRecordDO::getBizType, reqVO.getBizType())\n                .eqIfPresent(CrmFollowUpRecordDO::getBizId, reqVO.getBizId())\n                .orderByDesc(CrmFollowUpRecordDO::getId));\n    }\n\n    default void deleteByBiz(Integer bizType, Long bizId) {\n        delete(new LambdaQueryWrapperX<CrmFollowUpRecordDO>()\n                .eq(CrmFollowUpRecordDO::getBizType, bizType)\n                .eq(CrmFollowUpRecordDO::getBizId, bizId));\n    }\n\n    default List<CrmFollowUpRecordDO> selectListByBiz(Integer bizType, Collection<Long> bizIds) {\n        return selectList(new LambdaQueryWrapperX<CrmFollowUpRecordDO>()\n                .eq(CrmFollowUpRecordDO::getBizType, bizType)\n                .in(CrmFollowUpRecordDO::getBizId, bizIds));\n    }\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.permission;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * crm 数据权限 mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface CrmPermissionMapper extends BaseMapperX<CrmPermissionDO> {\n\n    default CrmPermissionDO selectByBizTypeAndBizIdByUserId(Integer bizType, Long bizId, Long userId) {\n        return selectOne(new LambdaQueryWrapperX<CrmPermissionDO>()\n                .eq(CrmPermissionDO::getBizType, bizType)\n                .eq(CrmPermissionDO::getBizId, bizId)\n                .eq(CrmPermissionDO::getUserId, userId));\n    }\n\n    default List<CrmPermissionDO> selectByBizTypeAndBizId(Integer bizType, Long bizId) {\n        return selectList(new LambdaQueryWrapperX<CrmPermissionDO>()\n                .eq(CrmPermissionDO::getBizType, bizType)\n                .eq(CrmPermissionDO::getBizId, bizId));\n    }\n\n    default List<CrmPermissionDO> selectByBizTypeAndBizIds(Integer bizType, Collection<Long> bizIds) {\n        return selectList(new LambdaQueryWrapperX<CrmPermissionDO>()\n                .eq(CrmPermissionDO::getBizType, bizType)\n                .in(CrmPermissionDO::getBizId, bizIds));\n    }\n\n    default List<CrmPermissionDO> selectListByBizTypeAndUserId(Integer bizType, Long userId) {\n        return selectList(new LambdaQueryWrapperX<CrmPermissionDO>()\n                .eq(CrmPermissionDO::getBizType, bizType)\n                .eq(CrmPermissionDO::getUserId, userId));\n    }\n\n    default List<CrmPermissionDO> selectListByBizTypeAndBizIdAndLevel(Integer bizType, Long bizId, Integer level) {\n        return selectList(new LambdaQueryWrapperX<CrmPermissionDO>()\n                .eq(CrmPermissionDO::getBizType, bizType)\n                .eq(CrmPermissionDO::getBizId, bizId)\n                .eq(CrmPermissionDO::getLevel, level));\n    }\n\n    default CrmPermissionDO selectByIdAndUserId(Long id, Long userId) {\n        return selectOne(CrmPermissionDO::getId, id,\n                CrmPermissionDO::getUserId, userId);\n    }\n\n    default CrmPermissionDO selectByBizAndUserId(Integer bizType, Long bizId, Long userId) {\n        return selectOne(new LambdaQueryWrapperX<CrmPermissionDO>()\n                .eq(CrmPermissionDO::getBizType, bizType)\n                .eq(CrmPermissionDO::getBizId, bizId)\n                .eq(CrmPermissionDO::getUserId, userId));\n    }\n\n    default int deletePermission(Integer bizType, Long bizId) {\n        return delete(new LambdaQueryWrapperX<CrmPermissionDO>()\n                .eq(CrmPermissionDO::getBizType, bizType)\n                .eq(CrmPermissionDO::getBizId, bizId));\n    }\n\n    default Long selectListByBiz(Collection<Integer> bizTypes, Collection<Long> bizIds, Collection<Long> userIds) {\n        return selectCount(new LambdaQueryWrapperX<CrmPermissionDO>()\n                .in(CrmPermissionDO::getBizType, bizTypes)\n                .in(CrmPermissionDO::getBizId, bizIds)\n                .in(CrmPermissionDO::getUserId, userIds));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/package-info.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.permission;"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductCategoryMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.product;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * CRM 产品分类 Mapper\n *\n * @author ZanGe丶\n */\n@Mapper\npublic interface CrmProductCategoryMapper extends BaseMapperX<CrmProductCategoryDO> {\n\n    default List<CrmProductCategoryDO> selectList(CrmProductCategoryListReqVO reqVO) {\n        return selectList(new LambdaQueryWrapperX<CrmProductCategoryDO>()\n                .likeIfPresent(CrmProductCategoryDO::getName, reqVO.getName())\n                .eqIfPresent(CrmProductCategoryDO::getParentId, reqVO.getParentId())\n                .orderByDesc(CrmProductCategoryDO::getId));\n    }\n\n    default CrmProductCategoryDO selectByParentIdAndName(Long parentId, String name) {\n        return selectOne(CrmProductCategoryDO::getParentId, parentId, CrmProductCategoryDO::getName, name);\n    }\n\n    default Long selectCountByParentId(Long parentId) {\n        return selectCount(CrmProductCategoryDO::getParentId, parentId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * CRM 产品 Mapper\n *\n * @author ZanGe丶\n */\n@Mapper\npublic interface CrmProductMapper extends BaseMapperX<CrmProductDO> {\n\n    default PageResult<CrmProductDO> selectPage(CrmProductPageReqVO reqVO) {\n        return selectPage(reqVO, new MPJLambdaWrapperX<CrmProductDO>()\n                .likeIfPresent(CrmProductDO::getName, reqVO.getName())\n                .eqIfPresent(CrmProductDO::getStatus, reqVO.getStatus())\n                .orderByDesc(CrmProductDO::getId));\n    }\n\n    default CrmProductDO selectByNo(String no) {\n        return selectOne(CrmProductDO::getNo, no);\n    }\n\n    default Long selectCountByCategoryId(Long categoryId) {\n        return selectCount(CrmProductDO::getCategoryId, categoryId);\n    }\n\n    default List<CrmProductDO> selectListByStatus(Integer status) {\n        return selectList(CrmProductDO::getStatus, status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.receivable;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 回款 Mapper\n *\n * @author 赤焰\n */\n@Mapper\npublic interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {\n\n    default CrmReceivableDO selectByNo(String no) {\n        return selectOne(CrmReceivableDO::getNo, no);\n    }\n\n    default PageResult<CrmReceivableDO> selectPageByCustomerId(CrmReceivablePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>()\n                .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递\n                .eqIfPresent(CrmReceivableDO::getNo, reqVO.getNo())\n                .eqIfPresent(CrmReceivableDO::getContractId, reqVO.getContractId())\n                .eqIfPresent(CrmReceivableDO::getPlanId, reqVO.getPlanId())\n                .orderByDesc(CrmReceivableDO::getId));\n    }\n\n    default PageResult<CrmReceivableDO> selectPage(CrmReceivablePageReqVO pageReqVO, Long userId) {\n        MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();\n        // 拼接数据权限的查询条件\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),\n                CrmReceivableDO::getId, userId, pageReqVO.getSceneType());\n        // 拼接自身的查询条件\n        query.selectAll(CrmReceivableDO.class)\n                .eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo())\n                .eqIfPresent(CrmReceivableDO::getPlanId, pageReqVO.getPlanId())\n                .eqIfPresent(CrmReceivableDO::getContractId, pageReqVO.getContractId())\n                .eqIfPresent(CrmReceivableDO::getAuditStatus, pageReqVO.getAuditStatus())\n                .orderByDesc(CrmReceivableDO::getId);\n        return selectJoinPage(pageReqVO, CrmReceivableDO.class, query);\n    }\n\n    default Long selectCountByAudit(Long userId) {\n        MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();\n        // 我负责的 + 非公海\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),\n                CrmReceivableDO::getId, userId, CrmSceneTypeEnum.OWNER.getType());\n        // 未审核\n        query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.PROCESS.getStatus());\n        return selectCount(query);\n    }\n\n    default List<CrmReceivableDO> selectListByContractIdAndStatus(Long contractId, Collection<Integer> auditStatuses) {\n        return selectList(new LambdaQueryWrapperX<CrmReceivableDO>()\n                .eq(CrmReceivableDO::getContractId, contractId)\n                .in(CrmReceivableDO::getAuditStatus, auditStatuses));\n    }\n\n    default Map<Long, BigDecimal> selectReceivablePriceMapByContractId(Collection<Long> contractIds) {\n        if (CollUtil.isEmpty(contractIds)) {\n            return Collections.emptyMap();\n        }\n        // SQL sum 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmReceivableDO>()\n                .select(\"contract_id, SUM(price) AS total_price\")\n                .in(\"audit_status\", CrmAuditStatusEnum.DRAFT.getStatus(), // 草稿 + 审批中 + 审批通过\n                        CrmAuditStatusEnum.PROCESS.getStatus(), CrmAuditStatusEnum.APPROVE.getStatus())\n                .groupBy(\"contract_id\")\n                .in(\"contract_id\", contractIds));\n        // 获得金额\n        return convertMap(result, obj -> (Long) obj.get(\"contract_id\"), obj -> (BigDecimal) obj.get(\"total_price\"));\n    }\n\n    default Long selectCountByContractId(Long contractId) {\n        return selectCount(CrmReceivableDO::getContractId, contractId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.receivable;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.Objects;\n\n/**\n * 回款计划 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface CrmReceivablePlanMapper extends BaseMapperX<CrmReceivablePlanDO> {\n\n    default CrmReceivablePlanDO selectMaxPeriodByContractId(Long contractId) {\n        return selectOne(new MPJLambdaWrapperX<CrmReceivablePlanDO>()\n                .eq(CrmReceivablePlanDO::getContractId, contractId)\n                .orderByDesc(CrmReceivablePlanDO::getPeriod)\n                .last(\"LIMIT 1\"));\n    }\n\n    default PageResult<CrmReceivablePlanDO> selectPageByCustomerId(CrmReceivablePlanPageReqVO reqVO) {\n        MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();\n        if (Objects.nonNull(reqVO.getContractNo())) { // 根据合同编号检索\n            query.innerJoin(CrmContractDO.class, on -> on.like(CrmContractDO::getNo, reqVO.getContractNo())\n                    .eq(CrmContractDO::getId, CrmReceivablePlanDO::getContractId));\n        }\n        query.eq(CrmReceivablePlanDO::getCustomerId, reqVO.getCustomerId()) // 必须传递\n                .eqIfPresent(CrmReceivablePlanDO::getContractId, reqVO.getContractId())\n                .orderByDesc(CrmReceivablePlanDO::getPeriod);\n        return selectJoinPage(reqVO, CrmReceivablePlanDO.class, query);\n    }\n\n    default PageResult<CrmReceivablePlanDO> selectPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) {\n        MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();\n        // 拼接数据权限的查询条件\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(),\n                CrmReceivablePlanDO::getId, userId, pageReqVO.getSceneType());\n        // 拼接自身的查询条件\n        query.selectAll(CrmReceivablePlanDO.class)\n                .eqIfPresent(CrmReceivablePlanDO::getCustomerId, pageReqVO.getCustomerId())\n                .eqIfPresent(CrmReceivablePlanDO::getContractId, pageReqVO.getContractId())\n                .orderByDesc(CrmReceivablePlanDO::getPeriod);\n        if (Objects.nonNull(pageReqVO.getContractNo())) { // 根据合同编号检索\n            query.innerJoin(CrmContractDO.class, on -> on.like(CrmContractDO::getNo, pageReqVO.getContractNo())\n                    .eq(CrmContractDO::getId, CrmReceivablePlanDO::getContractId));\n        }\n\n        // Backlog: 回款提醒类型\n        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());\n        if (CrmReceivablePlanPageReqVO.REMIND_TYPE_NEEDED.equals(pageReqVO.getRemindType())) { // 待回款\n            // 查询条件：未回款 + 提醒时间 <= 当前时间（反过来即当前时间 >= 提醒时间，已经到达提醒的时间点）\n            query.isNull(CrmReceivablePlanDO::getReceivableId) // 未回款\n                    .le(CrmReceivablePlanDO::getRemindTime, beginOfToday); // 今天开始提醒\n        } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_EXPIRED.equals(pageReqVO.getRemindType())) { // 已逾期\n            // 查询条件：未回款 + 回款时间 < 当前时间（反过来即当前时间 > 回款时间，已经过了回款时间点）\n            query.isNull(CrmReceivablePlanDO::getReceivableId) // 未回款\n                    .lt(CrmReceivablePlanDO::getReturnTime, beginOfToday); // 已逾期\n        } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_RECEIVED.equals(pageReqVO.getRemindType())) { // 已回款\n            query.isNotNull(CrmReceivablePlanDO::getReceivableId);\n        }\n        return selectJoinPage(pageReqVO, CrmReceivablePlanDO.class, query);\n    }\n\n    default Long selectReceivablePlanCountByRemind(Long userId) {\n        MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();\n        // 我负责的 + 非公海\n        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(),\n                CrmReceivablePlanDO::getId, userId, CrmSceneTypeEnum.OWNER.getType());\n        // 未回款 + 已逾期 + 今天开始提醒\n        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());\n        query.isNull(CrmReceivablePlanDO::getReceivableId) // 未回款\n                .lt(CrmReceivablePlanDO::getReturnTime, beginOfToday) // 已逾期\n                .lt(CrmReceivablePlanDO::getRemindTime, beginOfToday); // 今天开始提醒\n        return selectCount(query);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.statistics;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.util.RandomUtil;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * CRM 客户分析 Mapper\n *\n * @author dhb52\n */\n@Mapper\npublic interface CrmStatisticsCustomerMapper {\n\n    /**\n     * 新建客户数(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 成交客户数(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 新建客户数(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 成交客户数(按用户)\n     *\n     * @param reqVO 请求参数@param reqVO 请求参数@param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 合同总金额(按用户)\n     *\n     * @return 统计数据@return 统计数据@param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 合同回款金额(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 跟进次数(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 跟进客户数(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 跟进次数(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 跟进客户数(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);\n\n\n    /**\n     * 首次合同、回款信息(用于【客户转化率】页面)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 跟进次数(按类型)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsFollowUpSummaryByTypeRespVO> selectFollowUpRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO);\n\n\n    /**\n     * 进入公海客户数(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表\n    default List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO) {\n        LocalDateTime currrentDate = LocalDateTimeUtil.beginOfDay(reqVO.getTimes()[0]);\n        LocalDateTime endDate = LocalDateTimeUtil.endOfDay(reqVO.getTimes()[1]);\n        List<CrmStatisticsPoolSummaryByDateRespVO> voList = new ArrayList<>();\n        while (currrentDate.isBefore(endDate)) {\n            voList.add(new CrmStatisticsPoolSummaryByDateRespVO()\n                .setTime(LocalDateTimeUtil.format(currrentDate, \"yyyy-MM-dd\"))\n                .setCustomerPutCount(RandomUtil.randomInt(0, 10))\n                .setCustomerTakeCount(RandomUtil.randomInt(0, 10)));\n            currrentDate = currrentDate.plusDays(1);\n        }\n\n        return voList;\n    }\n\n    /**\n     * 公海领取客户数(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表\n    default List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO) {\n        return selectPoolCustomerPutCountByDate(reqVO);\n    }\n\n    /**\n     * 进入公海客户数(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表\n    default List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO) {\n        return convertList(reqVO.getUserIds(), userId ->\n            (CrmStatisticsPoolSummaryByUserRespVO) new CrmStatisticsPoolSummaryByUserRespVO()\n                .setCustomerPutCount(RandomUtil.randomInt(0, 10))\n                .setCustomerTakeCount(RandomUtil.randomInt(0, 10))\n                .setOwnerUserId(userId));\n    }\n\n    /**\n     * 公海领取客户数(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表\n    default List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO) {\n        return selectPoolCustomerPutCountByUser(reqVO);\n    }\n\n    /**\n     * 客户成交周期(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 客户成交周期(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 客户成交周期(按区域)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerDealCycleByAreaRespVO> selectCustomerDealCycleGroupByAreaId(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 客户成交周期(按产品)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerDealCycleByProductRespVO> selectCustomerDealCycleGroupByProductId(CrmStatisticsCustomerReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsFunnelMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.statistics;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessInversionRateSummaryByDateRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * CRM 销售漏斗 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface CrmStatisticsFunnelMapper {\n\n    Long selectCustomerCountByDate(CrmStatisticsFunnelReqVO reqVO);\n\n    Long selectBusinessCountByDateAndEndStatus(@Param(\"reqVO\") CrmStatisticsFunnelReqVO reqVO, @Param(\"status\") Integer status);\n\n    List<CrmStatisticsBusinessSummaryByEndStatusRespVO> selectBusinessSummaryListGroupByEndStatus(CrmStatisticsFunnelReqVO reqVO);\n\n    List<CrmStatisticsBusinessSummaryByDateRespVO> selectBusinessSummaryGroupByDate(CrmStatisticsFunnelReqVO reqVO);\n\n    List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> selectBusinessInversionRateSummaryByDate(CrmStatisticsFunnelReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPerformanceMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.statistics;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * CRM 员工业绩分析 Mapper\n *\n * @author scholar\n */\n@Mapper\npublic interface CrmStatisticsPerformanceMapper {\n\n    /**\n     * 员工签约合同数量\n     *\n     * @param performanceReqVO 参数\n     * @return 员工签约合同数量\n     */\n    List<CrmStatisticsPerformanceRespVO> selectContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO);\n\n    /**\n     * 员工签约合同金额\n     *\n     * @param performanceReqVO 参数\n     * @return 员工签约合同金额\n     */\n    List<CrmStatisticsPerformanceRespVO> selectContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);\n\n    /**\n     * 员工回款金额\n     *\n     * @param performanceReqVO 参数\n     * @return 员工回款金额\n     */\n    List<CrmStatisticsPerformanceRespVO> selectReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.statistics;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * CRM 数据画像 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface CrmStatisticsPortraitMapper {\n\n    List<CrmStatisticCustomerAreaRespVO> selectSummaryListGroupByAreaId(CrmStatisticsPortraitReqVO reqVO);\n\n    List<CrmStatisticCustomerIndustryRespVO> selectCustomerIndustryListGroupByIndustryId(CrmStatisticsPortraitReqVO reqVO);\n\n    List<CrmStatisticCustomerSourceRespVO> selectCustomerSourceListGroupBySource(CrmStatisticsPortraitReqVO reqVO);\n\n    List<CrmStatisticCustomerLevelRespVO> selectCustomerLevelListGroupByLevel(CrmStatisticsPortraitReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankMapper.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.mysql.statistics;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * CRM 排行榜统计 Mapper\n *\n * @author anhaohao\n */\n@Mapper\npublic interface CrmStatisticsRankMapper {\n\n    /**\n     * 查询合同金额排行榜\n     *\n     * @param rankReqVO 参数\n     * @return 合同金额排行榜\n     */\n    List<CrmStatisticsRankRespVO> selectContractPriceRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 查询回款金额排行榜\n     *\n     * @param rankReqVO 参数\n     * @return 回款金额排行榜\n     */\n    List<CrmStatisticsRankRespVO> selectReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 查询签约合同数量排行榜\n     *\n     * @param rankReqVO 参数\n     * @return 签约合同数量排行榜\n     */\n    List<CrmStatisticsRankRespVO> selectContractCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 查询产品销量排行榜\n     *\n     * @param rankReqVO 参数\n     * @return 产品销量排行榜\n     */\n    List<CrmStatisticsRankRespVO> selectProductSalesRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 查询新增客户数排行榜\n     *\n     * @param rankReqVO 参数\n     * @return 新增客户数排行榜\n     */\n    List<CrmStatisticsRankRespVO> selectCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 查询联系人数量排行榜\n     *\n     * @param rankReqVO 参数\n     * @return 联系人数量排行榜\n     */\n    List<CrmStatisticsRankRespVO> selectContactsCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 查询跟进次数排行榜\n     *\n     * @param rankReqVO 参数\n     * @return 跟进次数排行榜\n     */\n    List<CrmStatisticsRankRespVO> selectFollowCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 查询跟进客户数排行榜\n     *\n     * @param rankReqVO 参数\n     * @return 跟进客户数排行榜\n     */\n    List<CrmStatisticsRankRespVO> selectFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/RedisKeyConstants.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.redis;\n\n/**\n * CRM Redis Key 枚举类\n *\n * @author 芋道源码\n */\npublic interface RedisKeyConstants {\n\n    /**\n     * 序号的缓存\n     *\n     * KEY 格式：trade_no:{prefix}\n     * VALUE 数据格式：编号自增\n     */\n    String NO = \"crm:seq_no:\";\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java",
    "content": "package cn.iocoder.yudao.module.crm.dal.redis.no;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport cn.iocoder.yudao.module.crm.dal.redis.RedisKeyConstants;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\n\n\n/**\n * Crm 订单序号的 Redis DAO\n *\n * @author HUIHUI\n */\n@Repository\npublic class CrmNoRedisDAO {\n\n    /**\n     * 合同 {@link cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO}\n     */\n    public static final String CONTRACT_NO_PREFIX = \"HT\";\n\n    /**\n     * 回款 {@link cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO}\n     */\n    public static final String RECEIVABLE_PREFIX = \"HK\";\n\n    @Resource\n    private StringRedisTemplate stringRedisTemplate;\n\n    /**\n     * 生成序号，使用当前日期，格式为 {PREFIX} + yyyyMMdd + 6 位自增\n     * 例如说：QTRK 202109 000001 （没有中间空格）\n     *\n     * @param prefix 前缀\n     * @return 序号\n     */\n    public String generate(String prefix) {\n        // 递增序号\n        String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN);\n        String key = RedisKeyConstants.NO + noPrefix;\n        Long no = stringRedisTemplate.opsForValue().increment(key);\n        // 设置过期时间\n        stringRedisTemplate.expire(key, Duration.ofDays(1L));\n        return noPrefix + String.format(\"%06d\", no);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.excel.core;\n\nimport cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction;\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n/**\n * 地区下拉框数据源的 {@link ExcelColumnSelectFunction} 实现类\n *\n * @author HUIHUI\n */\n@Service\npublic class AreaExcelColumnSelectFunction implements ExcelColumnSelectFunction {\n\n    public static final String NAME = \"getCrmAreaNameList\"; // 防止和别的模块重名\n\n    @Override\n    public String getName() {\n        return NAME;\n    }\n\n    @Override\n    public List<String> getOptions() {\n        // 获取地区下拉数据\n        // TODO @puhui999：嘿嘿，这里改成省份、城市、区域，三个选项，难度大么？\n        Area area = AreaUtils.getArea(Area.ID_CHINA);\n        return AreaUtils.getAreaNodePathList(area.getChildren());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java",
    "content": "/**\n * crm 模块的 excel 拓展封装\n */\npackage cn.iocoder.yudao.module.crm.framework.excel;"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmBusinessParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport com.mzt.logapi.service.IParseFunction;\nimport javax.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * CRM 商机的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class CrmBusinessParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getBusinessById\";\n\n    @Resource\n    private CrmBusinessService businessService;\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        CrmBusinessDO businessDO = businessService.getBusiness(Long.parseLong(value.toString()));\n        return businessDO == null ? \"\" : businessDO.getName();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContactParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactService;\nimport com.mzt.logapi.service.IParseFunction;\nimport javax.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * CRM 联系人的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class CrmContactParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getContactById\";\n\n    @Resource\n    private CrmContactService contactService;\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        CrmContactDO contactDO = contactService.getContact(Long.parseLong(value.toString()));\n        return contactDO == null ? \"\" : contactDO.getName();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContractParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport com.mzt.logapi.service.IParseFunction;\nimport javax.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * CRM 合同的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class CrmContractParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getContractById\";\n\n    @Resource\n    private CrmContractService contractService;\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        CrmContractDO contract = contractService.getContract(Long.parseLong(value.toString()));\n        return contract == null ? \"\" : contract.getName();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerIndustryParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;\n\n/**\n * 行业的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class CrmCustomerIndustryParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getCustomerIndustry\";\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        return DictFrameworkUtils.parseDictDataLabel(CRM_CUSTOMER_INDUSTRY, value.toString());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerLevelParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL;\n\n/**\n * 客户等级的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class CrmCustomerLevelParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getCustomerLevel\";\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        return DictFrameworkUtils.parseDictDataLabel(CRM_CUSTOMER_LEVEL, value.toString());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport com.mzt.logapi.service.IParseFunction;\nimport javax.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * CRM 客户的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class CrmCustomerParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getCustomerById\";\n\n    @Resource\n    private CrmCustomerService customerService;\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        CrmCustomerDO crmCustomerDO = customerService.getCustomer(Long.parseLong(value.toString()));\n        return crmCustomerDO == null ? \"\" : crmCustomerDO.getName();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerSourceParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE;\n\n/**\n * CRM 客户来源的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class CrmCustomerSourceParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getCustomerSource\";\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        return DictFrameworkUtils.parseDictDataLabel(CRM_CUSTOMER_SOURCE, value.toString());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductStatusParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * 产品状态的 {@link IParseFunction} 实现类\n *\n * @author anhaohao\n */\n@Component\n@Slf4j\npublic class CrmProductStatusParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getProductStatusName\";\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        return DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.CRM_PRODUCT_STATUS, value.toString());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductUnitParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * 产品单位的 {@link IParseFunction} 实现类\n *\n * @author anhaohao\n */\n@Component\n@Slf4j\npublic class CrmProductUnitParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getProductUnitName\";\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        return DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.CRM_PRODUCT_UNIT, value.toString());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmReceivablePlanParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;\nimport cn.iocoder.yudao.module.crm.service.receivable.CrmReceivablePlanService;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * CRM 回款计划的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class CrmReceivablePlanParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getReceivablePlanServiceById\";\n\n    @Resource\n    private CrmReceivablePlanService receivablePlanService;\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(Long.parseLong(value.toString()));\n        return receivablePlan == null ? \"\" : receivablePlan.getPeriod().toString();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmReceivableReturnTypeParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_RECEIVABLE_RETURN_TYPE;\n\n/**\n * CRM 回款方式的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Slf4j\n@Component\npublic class CrmReceivableReturnTypeParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getReceivableReturnType\";\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        return DictFrameworkUtils.parseDictDataLabel(CRM_RECEIVABLE_RETURN_TYPE, value.toString());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAdminUserParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * 管理员名字的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Slf4j\n@Component\npublic class SysAdminUserParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getAdminUserById\";\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n\n        // 获取用户信息\n        AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString())).getCheckedData();\n        if (user == null) {\n            log.warn(\"[apply][获取用户{{}}为空\", value);\n            return \"\";\n        }\n        // 返回格式 芋道源码(13888888888)\n        String nickname = user.getNickname();\n        if (StrUtil.isEmpty(user.getMobile())) {\n            return nickname;\n        }\n        return StrUtil.format(\"{}({})\", nickname, user.getMobile());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAreaParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * 地名的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Slf4j\n@Component\npublic class SysAreaParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getArea\";\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        return AreaUtils.format(Integer.parseInt(value.toString()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysBooleanParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.module.infra.enums.DictTypeConstants;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * 是否类型的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class SysBooleanParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getBoolean\";\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        return DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.BOOLEAN_STRING, value.toString());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysDeptParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport com.mzt.logapi.service.IParseFunction;\nimport javax.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * 管理员名字的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Slf4j\n@Component\npublic class SysDeptParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getDeptById\";\n\n    @Resource\n    private DeptApi deptApi;\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n\n        // 获取部门信息\n        DeptRespDTO dept = deptApi.getDept(Long.parseLong(value.toString())).getCheckedData();\n        if (dept == null) {\n            log.warn(\"[apply][获取部门{{}}为空\", value);\n            return \"\";\n        }\n        return dept.getName();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysSexParseFunction.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.operatelog.core;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.module.system.enums.DictTypeConstants;\nimport com.mzt.logapi.service.IParseFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * 行业的 {@link IParseFunction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class SysSexParseFunction implements IParseFunction {\n\n    public static final String NAME = \"getSex\";\n\n    @Override\n    public boolean executeBefore() {\n        return true; // 先转换值后对比\n    }\n\n    @Override\n    public String functionName() {\n        return NAME;\n    }\n\n    @Override\n    public String apply(Object value) {\n        if (StrUtil.isEmptyIfStr(value)) {\n            return \"\";\n        }\n        return DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.USER_SEX, value.toString());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java",
    "content": "/**\n * crm 模块的 operatelog 拓展封装\n */\npackage cn.iocoder.yudao.module.crm.framework.operatelog;"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/package-info.java",
    "content": "/**\n * 属于 crm 模块的 framework 封装\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.crm.framework;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/annotations/CrmPermission.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.permission.core.annotations;\n\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.ANNOTATION_TYPE;\nimport static java.lang.annotation.ElementType.METHOD;\n\n/**\n * CRM 数据操作权限校验 AOP 注解\n *\n * @author HUIHUI\n */\n@Target({METHOD, ANNOTATION_TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface CrmPermission {\n\n    /**\n     * CRM 类型\n     */\n    CrmBizTypeEnum[] bizType() default {};\n\n    /**\n     * CRM 类型扩展，通过 Spring EL 表达式获取到 {@link #bizType()}\n     *\n     * 目的：用于 CrmPermissionController 团队权限校验\n     */\n    String bizTypeValue() default \"\";\n\n    /**\n     * 数据编号，通过 Spring EL 表达式获取\n     */\n    String bizId();\n\n    /**\n     * 操作所需权限级别\n     */\n    CrmPermissionLevelEnum level();\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.permission.core.aop;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;\nimport cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED;\n\n/**\n * Crm 数据权限校验 AOP 切面\n *\n * @author HUIHUI\n */\n@Component\n@Aspect\n@Slf4j\npublic class CrmPermissionAspect {\n\n    @Resource\n    private CrmPermissionService crmPermissionService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Before(\"@annotation(crmPermission)\")\n    public void doBefore(JoinPoint joinPoint, CrmPermission crmPermission) {\n        // 1.1 获取相关属性值\n        Map<String, Object> expressionValues = parseExpressions(joinPoint, crmPermission);\n        Integer bizType = StrUtil.isEmpty(crmPermission.bizTypeValue()) ?\n                crmPermission.bizType()[0].getType() : (Integer) expressionValues.get(crmPermission.bizTypeValue()); // 模块类型\n        // 1.2 处理兼容多个 bizId 的情况\n        Object object = expressionValues.get(crmPermission.bizId()); // 模块数据编号\n        Set<Long> bizIds = new HashSet<>();\n        if (object instanceof Collection<?>) {\n            bizIds.addAll(convertSet((Collection<?>) object, item -> Long.parseLong(item.toString())));\n        } else {\n            bizIds.add(Long.parseLong(object.toString()));\n        }\n        Integer permissionLevel = crmPermission.level().getLevel(); // 需要的权限级别\n\n        // 2. 逐个校验权限\n        List<CrmPermissionDO> permissionList = crmPermissionService.getPermissionListByBiz(bizType, bizIds);\n        Map<Long, List<CrmPermissionDO>> multiMap = convertMultiMap(permissionList, CrmPermissionDO::getBizId);\n        bizIds.forEach(bizId -> validatePermission(bizType, multiMap.get(bizId), permissionLevel));\n    }\n\n    private void validatePermission(Integer bizType, List<CrmPermissionDO> bizPermissions, Integer permissionLevel) {\n        // 1. 如果是超级管理员则直接通过\n        if (CrmPermissionUtils.isCrmAdmin()) {\n            return;\n        }\n        // 特殊：没有数据权限的情况，针对 READ 的特殊处理\n        if (CollUtil.isEmpty(bizPermissions)) {\n            // 1.1 公海数据，如果没有团队成员，大家也应该有 READ 权限才对\n            if (CrmPermissionLevelEnum.isRead(permissionLevel)) {\n                return;\n            }\n            // 没有数据权限的情况下超出了读权限直接报错，避免后面校验空指针\n            throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType));\n        } else { // 1.2 有数据权限但是没有负责人的情况\n            if (!anyMatch(bizPermissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel()))\n                    && CrmPermissionLevelEnum.isRead(permissionLevel)) {\n                return;\n            }\n        }\n\n        // 2. 只考虑自的身权限\n        Long userId = getUserId();\n        CrmPermissionDO userPermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), userId));\n        if (userPermission != null) {\n            if (isUserPermissionValid(userPermission, permissionLevel)) {\n                return;\n            }\n        }\n\n        // 3. 考虑下级的权限\n        List<AdminUserRespDTO> subordinateUserIds = adminUserApi.getUserListBySubordinate(userId).getCheckedData();\n        for (Long subordinateUserId : convertSet(subordinateUserIds, AdminUserRespDTO::getId)) {\n            CrmPermissionDO subordinatePermission = CollUtil.findOne(bizPermissions,\n                    permission -> ObjUtil.equal(permission.getUserId(), subordinateUserId));\n            if (subordinatePermission != null && isUserPermissionValid(subordinatePermission, permissionLevel)) {\n                return;\n            }\n        }\n\n        // 4. 没有权限，抛出异常\n        log.info(\"[doBefore][userId({}) 要求权限({}) 实际权限({}) 数据校验错误]\", // 打个 info 日志，方便后续排查问题、审计\n                userId, permissionLevel, toJsonString(userPermission));\n        throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType));\n    }\n\n    /**\n     * 校验用户权限是否有效\n     *\n     * @param userPermission   用户拥有的权限\n     * @param permissionLevel  需要的权限级别\n     * @return 是否有效\n     */\n    @SuppressWarnings(\"RedundantIfStatement\")\n    private boolean isUserPermissionValid(CrmPermissionDO userPermission, Integer permissionLevel) {\n        // 2.1 情况一：如果自己是负责人，则默认有所有权限\n        if (CrmPermissionLevelEnum.isOwner(userPermission.getLevel())) {\n            return true;\n        }\n        // 2.2 情况二：校验自己是否有读权限\n        if (CrmPermissionLevelEnum.isRead(permissionLevel)) {\n            if (CrmPermissionLevelEnum.isRead(userPermission.getLevel()) // 校验当前用户是否有读权限\n                    || CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限\n                return true;\n            }\n        }\n        // 2.3 情况三：校验自己是否有写权限\n        if (CrmPermissionLevelEnum.isWrite(permissionLevel)) {\n            if (CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 获得用户编号\n     *\n     * @return 用户编号\n     */\n    private static Long getUserId() {\n        return WebFrameworkUtils.getLoginUserId();\n    }\n\n    private static Map<String, Object> parseExpressions(JoinPoint joinPoint, CrmPermission crmPermission) {\n        // 1. 需要解析的表达式\n        List<String> expressionStrings = new ArrayList<>(2);\n        expressionStrings.add(crmPermission.bizId());\n        if (StrUtil.isNotEmpty(crmPermission.bizTypeValue())) { // 为空则表示 bizType 有值\n            expressionStrings.add(crmPermission.bizTypeValue());\n        }\n        // 2. 执行解析\n        return SpringExpressionUtils.parseExpressions(joinPoint, expressionStrings);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/package-info.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.permission.core;"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java",
    "content": "/**\n * crm 模块的 permission 拓展封装\n */\npackage cn.iocoder.yudao.module.crm.framework.permission;"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/config/RpcConfiguration.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.rpc.config;\n\nimport cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.PostApi;\nimport cn.iocoder.yudao.module.system.api.logger.OperateLogApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(value = \"crmRpcConfiguration\", proxyBeanMethods = false)\n@EnableFeignClients(clients = {AdminUserApi.class, DeptApi.class, PostApi.class,\n        OperateLogApi.class,\n        BpmProcessInstanceApi.class})\npublic class RpcConfiguration {\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.crm.framework.rpc;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/security/config/SecurityConfiguration.java",
    "content": "package cn.iocoder.yudao.module.crm.framework.security.config;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.module.crm.enums.ApiConstants;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\n/**\n * Crm 模块的 Security 配置\n */\n@Configuration(\"crmSecurityConfiguration\")\npublic class SecurityConfiguration {\n\n    @Bean(\"crmAuthorizeRequestsCustomizer\")\n    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {\n        return new AuthorizeRequestsCustomizer() {\n\n            @Override\n            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n                // Swagger 接口文档\n                registry.requestMatchers(\"/v3/api-docs/**\").permitAll()\n                        .requestMatchers(\"/webjars/**\").permitAll()\n                        .requestMatchers(\"/swagger-ui\").permitAll()\n                        .requestMatchers(\"/swagger-ui/**\").permitAll();\n                // Spring Boot Actuator 的安全配置\n                registry.requestMatchers(\"/actuator\").permitAll()\n                        .requestMatchers(\"/actuator/**\").permitAll();\n                // Druid 监控\n                registry.requestMatchers(\"/druid/**\").permitAll();\n                // RPC 服务的安全配置\n                registry.requestMatchers(ApiConstants.PREFIX + \"/**\").permitAll();\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/framework/security/core/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.crm.framework.security.core;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java",
    "content": "package cn.iocoder.yudao.module.crm.job.customer;\n\nimport cn.iocoder.yudao.framework.tenant.core.job.TenantJob;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport javax.annotation.Resource;\nimport org.springframework.stereotype.Component;\n\n/**\n * 客户自动掉入公海 Job\n *\n * @author 芋道源码\n */\n@Component\npublic class CrmCustomerAutoPutPoolJob {\n\n    @Resource\n    private CrmCustomerService customerService;\n\n    @XxlJob(\"customerAutoPutPoolJob\")\n    @TenantJob\n    public String execute() {\n        int count = customerService.autoPutCustomerPool();\n        return String.format(\"掉入公海客户 %s 个\", count);\n    }\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java",
    "content": "/**\n * TODO 芋艿：临时占位，后续可删除\n */\npackage cn.iocoder.yudao.module.crm.job;"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/package-info.java",
    "content": "/**\n * crm 包下，客户关系管理（Customer Relationship Management）。\n * 例如说：客户、联系人、商机、合同、回款等等\n *\n * 1. Controller URL：以 /crm/ 开头，避免和其它 Module 冲突\n * 2. DataObject 表名：以 crm_ 开头，方便在数据库中区分\n *\n * 注意，由于 Crm 模块下，容易和其它模块重名，所以类名都加载 Crm 的前缀~\n */\npackage cn.iocoder.yudao.module.crm;\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.business;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.enums.business.CrmBusinessEndStatusEnum;\n\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 商机 Service 接口\n *\n * @author ljlleo\n */\npublic interface CrmBusinessService {\n\n    /**\n     * 创建商机\n     *\n     * @param createReqVO 创建信息\n     * @param userId      用户编号\n     * @return 编号\n     */\n    Long createBusiness(@Valid CrmBusinessSaveReqVO createReqVO, Long userId);\n\n    /**\n     * 更新商机\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateBusiness(@Valid CrmBusinessSaveReqVO updateReqVO);\n\n    /**\n     * 更新商机相关跟进信息\n     *\n     * @param id                 编号\n     * @param contactNextTime    下次联系时间\n     * @param contactLastContent 最后联系内容\n     */\n    void updateBusinessFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);\n\n    /**\n     * 更新商机的下次联系时间\n     *\n     * @param ids             编号数组\n     * @param contactNextTime 下次联系时间\n     */\n    void updateBusinessContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime);\n\n    /**\n     * 更新商机的状态\n     *\n     * @param reqVO 更新请求\n     */\n    void updateBusinessStatus(CrmBusinessUpdateStatusReqVO reqVO);\n\n    /**\n     * 删除商机\n     *\n     * @param id 编号\n     */\n    void deleteBusiness(Long id);\n\n    /**\n     * 商机转移\n     *\n     * @param reqVO  请求\n     * @param userId 用户编号\n     */\n    void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId);\n\n    /**\n     * 获得商机\n     *\n     * @param id 编号\n     * @return 商机\n     */\n    CrmBusinessDO getBusiness(Long id);\n\n    /**\n     * 校验商机是否有效\n     *\n     * @param id 编号\n     * @return 商机\n     */\n    CrmBusinessDO validateBusiness(Long id);\n\n    /**\n     * 获得商机列表\n     *\n     * @param ids 编号\n     * @return 商机列表\n     */\n    List<CrmBusinessDO> getBusinessList(Collection<Long> ids);\n\n    /**\n     * 获得商机 Map\n     *\n     * @param ids 编号\n     * @return 商机 Map\n     */\n    default Map<Long, CrmBusinessDO> getBusinessMap(Collection<Long> ids) {\n        return convertMap(getBusinessList(ids), CrmBusinessDO::getId);\n    }\n\n    /**\n     * 获得指定商机编号的产品列表\n     *\n     * @param businessId 商机编号\n     * @return 商机产品列表\n     */\n    List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId);\n\n    /**\n     * 获得商机分页\n     *\n     * 数据权限：基于 {@link CrmBusinessDO}\n     *\n     * @param pageReqVO 分页查询\n     * @param userId    用户编号\n     * @return 商机分页\n     */\n    PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId);\n\n    /**\n     * 获得商机分页，基于指定客户\n     *\n     * 数据权限：基于 {@link CrmCustomerDO} 读取\n     *\n     * @param pageReqVO 分页查询\n     * @return 商机分页\n     */\n    PageResult<CrmBusinessDO> getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO);\n\n    /**\n     * 获得商机分页，基于指定联系人\n     *\n     * 数据权限：基于 {@link CrmContactDO} 读取\n     *\n     * @param pageReqVO 分页参数\n     * @return 商机分页\n     */\n    PageResult<CrmBusinessDO> getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO);\n\n    /**\n     * 获取关联客户的商机数量\n     *\n     * @param customerId 客户编号\n     * @return 数量\n     */\n    Long getBusinessCountByCustomerId(Long customerId);\n\n    /**\n     * 获得使用指定商机状态组的商机数量\n     *\n     * @param statusTypeId 商机状态组编号\n     * @return 数量\n     */\n    Long getBusinessCountByStatusTypeId(Long statusTypeId);\n\n    /**\n     * 获得商机状态名称\n     *\n     * @param endStatus 结束状态\n     * @param status    商机状态\n     * @return 商机状态名称\n     */\n    default String getBusinessStatusName(Integer endStatus, CrmBusinessStatusDO status) {\n        if (endStatus != null) {\n            return CrmBusinessEndStatusEnum.fromStatus(endStatus).getName();\n        }\n        return status.getName();\n    }\n\n    /**\n     * 获得商机列表\n     *\n     * @param customerId  客户编号\n     * @param ownerUserId 负责人编号\n     * @return 商机列表\n     */\n    List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);\n\n    /**\n     * 获得商机分页，目前用于【数据统计】\n     *\n     * @param pageVO 请求\n     * @return 商机分页\n     */\n    PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.business;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;\nimport cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactService;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;\nimport cn.iocoder.yudao.module.crm.service.product.CrmProductService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.service.impl.DiffParseFunction;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\n\n/**\n * 商机 Service 实现类\n *\n * @author ljlleo\n */\n@Service\n@Validated\npublic class CrmBusinessServiceImpl implements CrmBusinessService {\n\n    @Resource\n    private CrmBusinessMapper businessMapper;\n    @Resource\n    private CrmBusinessProductMapper businessProductMapper;\n\n    @Resource\n    private CrmBusinessStatusService businessStatusService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private CrmContractService contractService;\n    @Resource\n    private CrmCustomerService customerService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private CrmContactService contactService;\n    @Resource\n    private CrmPermissionService permissionService;\n    @Resource\n    private CrmContactBusinessService contactBusinessService;\n    @Resource\n    private CrmProductService productService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_CREATE_SUB_TYPE, bizNo = \"{{#business.id}}\",\n            success = CRM_BUSINESS_CREATE_SUCCESS)\n    public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) {\n        // 1.1 校验产品项的有效性\n        List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getProducts());\n        // 1.2 校验关联字段\n        validateRelationDataExists(createReqVO);\n\n        // 2.1 插入商机\n        CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class);\n        business.setStatusId(businessStatusService.getBusinessStatusListByTypeId(createReqVO.getStatusTypeId()).get(0).getId()); // 默认状态\n        calculateTotalPrice(business, businessProducts);\n        businessMapper.insert(business);\n        // 2.2 插入商机关联商品\n        if (CollUtil.isNotEmpty(businessProducts)) {\n            businessProducts.forEach(item -> item.setBusinessId(business.getId()));\n            businessProductMapper.insertBatch(businessProducts);\n        }\n\n        // 3. 创建数据权限\n        permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(business.getOwnerUserId())\n                .setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()).setBizId(business.getId())\n                .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));\n\n        // 4. 在联系人的详情页，如果直接【新建商机】，则需要关联下\n        if (createReqVO.getContactId() != null) {\n            contactBusinessService.createContactBusinessList(new CrmContactBusinessReqVO().setContactId(createReqVO.getContactId())\n                    .setBusinessIds(Collections.singletonList(business.getId())));\n        }\n\n        // 5. 记录操作日志上下文\n        LogRecordContext.putVariable(\"business\", business);\n        return business.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_BUSINESS_UPDATE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#updateReqVO.id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateBusiness(CrmBusinessSaveReqVO updateReqVO) {\n        updateReqVO.setOwnerUserId(null).setStatusTypeId(null); // 不允许更新的字段\n        // 1.1 校验存在\n        CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());\n        // 1.2 校验产品项的有效性\n        List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getProducts());\n        // 1.3 校验关联字段\n        validateRelationDataExists(updateReqVO);\n\n        // 2.1 更新商机\n        CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);\n        calculateTotalPrice(updateObj, businessProducts);\n        businessMapper.updateById(updateObj);\n        // 2.2 更新商机关联商品\n        updateBusinessProduct(updateObj.getId(), businessProducts);\n\n        // 3. 记录操作日志上下文\n        updateReqVO.setOwnerUserId(oldBusiness.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况\n        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessSaveReqVO.class));\n        LogRecordContext.putVariable(\"businessName\", oldBusiness.getName());\n    }\n\n    @Override\n    @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_FOLLOW_UP_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_BUSINESS_FOLLOW_UP_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateBusinessFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {\n        // 1. 校验存在\n        CrmBusinessDO business = validateBusinessExists(id);\n\n        // 2. 更新联系人的跟进信息\n        businessMapper.updateById(new CrmBusinessDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)\n                .setContactLastTime(LocalDateTime.now()));\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"businessName\", business.getName());\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#ids\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateBusinessContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime) {\n        businessMapper.updateBatch(convertList(ids, id -> new CrmBusinessDO().setId(id).setContactNextTime(contactNextTime)));\n    }\n\n    private void updateBusinessProduct(Long id, List<CrmBusinessProductDO> newList) {\n        List<CrmBusinessProductDO> oldList = businessProductMapper.selectListByBusinessId(id);\n        List<List<CrmBusinessProductDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setBusinessId(id));\n            businessProductMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            businessProductMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            businessProductMapper.deleteByIds(convertSet(diffList.get(2), CrmBusinessProductDO::getId));\n        }\n    }\n\n    private void validateRelationDataExists(CrmBusinessSaveReqVO saveReqVO) {\n        // 校验商机状态\n        if (saveReqVO.getStatusTypeId() != null) {\n            businessStatusService.validateBusinessStatusType(saveReqVO.getStatusTypeId());\n        }\n        // 校验客户\n        if (saveReqVO.getCustomerId() != null) {\n            customerService.validateCustomer(saveReqVO.getCustomerId());\n        }\n        // 校验联系人\n        if (saveReqVO.getContactId() != null) {\n            contactService.validateContact(saveReqVO.getContactId());\n        }\n        // 校验负责人\n        if (saveReqVO.getOwnerUserId() != null) {\n            adminUserApi.validateUser(saveReqVO.getOwnerUserId());\n        }\n    }\n\n    private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.BusinessProduct> list) {\n        // 1. 校验产品存在\n        productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.BusinessProduct::getProductId));\n        // 2. 转化为 CrmBusinessProductDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class,\n                item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount()))));\n    }\n\n    private void calculateTotalPrice(CrmBusinessDO business, List<CrmBusinessProductDO> businessProducts) {\n        business.setTotalProductPrice(getSumValue(businessProducts, CrmBusinessProductDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));\n        BigDecimal discountPrice = MoneyUtils.priceMultiplyPercent(business.getTotalProductPrice(), business.getDiscountPercent());\n        business.setTotalPrice(business.getTotalProductPrice().subtract(discountPrice));\n    }\n\n    @Override\n    @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_UPDATE_STATUS_SUB_TYPE, bizNo = \"{{#reqVO.id}}\",\n            success = CRM_BUSINESS_UPDATE_STATUS_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#reqVO.id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateBusinessStatus(CrmBusinessUpdateStatusReqVO reqVO) {\n        // 1.1 校验存在\n        CrmBusinessDO business = validateBusinessExists(reqVO.getId());\n        // 1.2 校验商机未结束\n        if (business.getEndStatus() != null) {\n            throw exception(BUSINESS_UPDATE_STATUS_FAIL_END_STATUS);\n        }\n        // 1.3 校验商机状态\n        CrmBusinessStatusDO status = null;\n        if (reqVO.getStatusId() != null) {\n            status = businessStatusService.validateBusinessStatus(business.getStatusTypeId(), reqVO.getStatusId());\n        }\n        // 1.4 校验是不是状态没变更\n        if ((reqVO.getStatusId() != null && reqVO.getStatusId().equals(business.getStatusId()))\n                || (reqVO.getEndStatus() != null && reqVO.getEndStatus().equals(business.getEndStatus()))) {\n            throw exception(BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS);\n        }\n\n        // 2. 更新商机状态\n        businessMapper.updateById(new CrmBusinessDO().setId(reqVO.getId()).setStatusId(reqVO.getStatusId())\n                .setEndStatus(reqVO.getEndStatus()));\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"businessName\", business.getName());\n        LogRecordContext.putVariable(\"oldStatusName\", getBusinessStatusName(business.getEndStatus(),\n                businessStatusService.getBusinessStatus(business.getStatusId())));\n        LogRecordContext.putVariable(\"newStatusName\", getBusinessStatusName(reqVO.getEndStatus(), status));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_BUSINESS_DELETE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void deleteBusiness(Long id) {\n        // 1.1 校验存在\n        CrmBusinessDO business = validateBusinessExists(id);\n        // 1.2 校验是否关联合同\n        validateContractExists(id);\n\n        // 删除商机\n        businessMapper.deleteById(id);\n        // 删除数据权限\n        permissionService.deletePermission(CrmBizTypeEnum.CRM_BUSINESS.getType(), id);\n\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"businessName\", business.getName());\n    }\n\n    /**\n     * 删除校验合同是关联合同\n     *\n     * @param businessId 商机id\n     * @author lzxhqs\n     */\n    private void validateContractExists(Long businessId) {\n        if (contractService.getContractCountByBusinessId(businessId) > 0) {\n            throw exception(BUSINESS_DELETE_FAIL_CONTRACT_EXISTS);\n        }\n    }\n\n    private CrmBusinessDO validateBusinessExists(Long id) {\n        CrmBusinessDO crmBusiness = businessMapper.selectById(id);\n        if (crmBusiness == null) {\n            throw exception(BUSINESS_NOT_EXISTS);\n        }\n        return crmBusiness;\n    }\n\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_TRANSFER_SUB_TYPE, bizNo = \"{{#reqVO.id}}\",\n            success = CRM_BUSINESS_TRANSFER_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#reqVO.id\", level = CrmPermissionLevelEnum.OWNER)\n    public void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId) {\n        // 1 校验商机是否存在\n        CrmBusinessDO business = validateBusinessExists(reqVO.getId());\n\n        // 2.1 数据权限转移\n        permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_BUSINESS.getType(),\n                reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));\n        // 2.2 设置新的负责人\n        businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());\n\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"business\", business);\n    }\n\n    //======================= 查询相关 =======================\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#id\", level = CrmPermissionLevelEnum.READ)\n    public CrmBusinessDO getBusiness(Long id) {\n        return businessMapper.selectById(id);\n    }\n\n    @Override\n    public CrmBusinessDO validateBusiness(Long id) {\n        return validateBusinessExists(id);\n    }\n\n    @Override\n    public List<CrmBusinessDO> getBusinessList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return ListUtil.empty();\n        }\n        return businessMapper.selectByIds(ids);\n    }\n\n    @Override\n    public List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId) {\n        return businessProductMapper.selectListByBusinessId(businessId);\n    }\n\n    @Override\n    public PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId) {\n        return businessMapper.selectPage(pageReqVO, userId);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#pageReqVO.customerId\", level = CrmPermissionLevelEnum.READ)\n    public PageResult<CrmBusinessDO> getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO) {\n        return businessMapper.selectPageByCustomerId(pageReqVO);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#pageReqVO.contactId\", level = CrmPermissionLevelEnum.READ)\n    public PageResult<CrmBusinessDO> getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO) {\n        // 1. 查询关联的商机编号\n        List<CrmContactBusinessDO> contactBusinessList = contactBusinessService.getContactBusinessListByContactId(\n                pageReqVO.getContactId());\n        if (CollUtil.isEmpty(contactBusinessList)) {\n            return PageResult.empty();\n        }\n        // 2. 查询商机分页\n        return businessMapper.selectPageByContactId(pageReqVO,\n                convertSet(contactBusinessList, CrmContactBusinessDO::getBusinessId));\n    }\n\n    @Override\n    public Long getBusinessCountByCustomerId(Long customerId) {\n        return businessMapper.selectCount(CrmBusinessDO::getCustomerId, customerId);\n    }\n\n    @Override\n    public Long getBusinessCountByStatusTypeId(Long statusTypeId) {\n        return businessMapper.selectCountByStatusTypeId(statusTypeId);\n    }\n\n    @Override\n    public List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {\n        return businessMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);\n    }\n\n    @Override\n    public PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO) {\n        return businessMapper.selectPage(pageVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.business;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 商机状态 Service 接口\n *\n * @author ljlleo\n */\npublic interface CrmBusinessStatusService {\n\n    /**\n     * 创建商机状态\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createBusinessStatus(@Valid CrmBusinessStatusSaveReqVO createReqVO);\n\n    /**\n     * 更新商机状态\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateBusinessStatus(@Valid CrmBusinessStatusSaveReqVO updateReqVO);\n\n    /**\n     * 删除商机状态\n     *\n     * @param id 编号\n     */\n    void deleteBusinessStatusType(Long id);\n\n    /**\n     * 获得商机状态组\n     *\n     * @param id 编号\n     * @return 商机状态组\n     */\n    CrmBusinessStatusTypeDO getBusinessStatusType(Long id);\n\n    /**\n     * 校验商机状态组\n     *\n     * @param id 编号\n     */\n    void validateBusinessStatusType(Long id);\n\n    /**\n     * 获得商机状态组列表\n     *\n     * @return 商机状态组列表\n     */\n    List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList();\n\n    /**\n     * 获得商机状态组分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 商机状态组分页\n     */\n    PageResult<CrmBusinessStatusTypeDO> getBusinessStatusTypePage(PageParam pageReqVO);\n\n    /**\n     * 获得商机状态组列表\n     *\n     * @param ids 编号数组\n     * @return 商机状态组列表\n     */\n    List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids);\n\n    /**\n     * 获得商机状态组 Map\n     *\n     * @param ids 编号数组\n     * @return 商机状态组 Map\n     */\n    default Map<Long, CrmBusinessStatusTypeDO> getBusinessStatusTypeMap(Collection<Long> ids) {\n        return convertMap(getBusinessStatusTypeList(ids), CrmBusinessStatusTypeDO::getId);\n    }\n\n    /**\n     * 获得指定类型的商机状态列表\n     *\n     * @param typeId 商机状态组编号\n     * @return 商机状态列表\n     */\n    List<CrmBusinessStatusDO> getBusinessStatusListByTypeId(Long typeId);\n\n    /**\n     * 获得商机状态列表\n     *\n     * @param ids 编号数组\n     * @return 商机状态列表\n     */\n    List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids);\n\n    /**\n     * 获得商机状态 Map\n     *\n     * @param ids 编号数组\n     * @return 商机状态 Map\n     */\n    default Map<Long, CrmBusinessStatusDO> getBusinessStatusMap(Collection<Long> ids) {\n        return convertMap(getBusinessStatusList(ids), CrmBusinessStatusDO::getId);\n    }\n\n    /**\n     * 获得商机状态\n     *\n     * @param id 编号\n     * @return 商机状态\n     */\n    CrmBusinessStatusDO getBusinessStatus(Long id);\n\n    /**\n     * 校验商机状态\n     *\n     * @param statusTypeId 商机状态组编号\n     * @param statusId     商机状态编号\n     */\n    CrmBusinessStatusDO validateBusinessStatus(Long statusTypeId, Long statusId);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.business;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusMapper;\nimport cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusTypeMapper;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;\n\n/**\n * 商机状态 Service 实现类\n *\n * @author ljlleo\n */\n@Service\n@Validated\npublic class CrmBusinessStatusServiceImpl implements CrmBusinessStatusService {\n\n    @Resource\n    private CrmBusinessStatusTypeMapper businessStatusTypeMapper;\n    @Resource\n    private CrmBusinessStatusMapper businessStatusMapper;\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private CrmBusinessService businessService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createBusinessStatus(CrmBusinessStatusSaveReqVO createReqVO) {\n        // 1.1 检验名称是否存在\n        validateBusinessStatusTypeNameUnique(createReqVO.getName(), null);\n        // 1.2 设置状态的排序\n        int sort = 0;\n        for (CrmBusinessStatusSaveReqVO.Status status : createReqVO.getStatuses()) {\n            status.setSort(sort++);\n        }\n\n        // 2.1 插入类型\n        CrmBusinessStatusTypeDO statusType = BeanUtils.toBean(createReqVO, CrmBusinessStatusTypeDO.class);\n        businessStatusTypeMapper.insert(statusType);\n        // 2.2 插入状态\n        List<CrmBusinessStatusDO> statuses = BeanUtils.toBean(createReqVO.getStatuses(), CrmBusinessStatusDO.class,\n                status -> status.setTypeId(statusType.getId()));\n        businessStatusMapper.insertBatch(statuses);\n        return statusType.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateBusinessStatus(CrmBusinessStatusSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        validateBusinessStatusTypeExists(updateReqVO.getId());\n        // 1.2 校验名称是否存在\n        validateBusinessStatusTypeNameUnique(updateReqVO.getName(), updateReqVO.getId());\n        // 1.3 设置状态的排序\n        int sort = 0;\n        for (CrmBusinessStatusSaveReqVO.Status status : updateReqVO.getStatuses()) {\n            status.setSort(sort++);\n        }\n        // 1.4 已经使用，无法更新\n        if (businessService.getBusinessCountByStatusTypeId(updateReqVO.getId()) > 0) {\n            throw exception(BUSINESS_STATUS_UPDATE_FAIL_USED);\n        }\n\n        // 2.1 更新类型\n        CrmBusinessStatusTypeDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessStatusTypeDO.class);\n        businessStatusTypeMapper.updateById(updateObj);\n        // 2.2 更新状态\n        updateBusinessStatus(updateReqVO.getId(), BeanUtils.toBean(updateReqVO.getStatuses(), CrmBusinessStatusDO.class));\n    }\n\n    private void updateBusinessStatus(Long id, List<CrmBusinessStatusDO> newList) {\n        List<CrmBusinessStatusDO> oldList = businessStatusMapper.selectListByTypeId(id);\n        List<List<CrmBusinessStatusDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setTypeId(id));\n            businessStatusMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            businessStatusMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            businessStatusMapper.deleteByIds(convertSet(diffList.get(2), CrmBusinessStatusDO::getId));\n        }\n    }\n\n    private void validateBusinessStatusTypeExists(Long id) {\n        if (businessStatusTypeMapper.selectById(id) == null) {\n            throw exception(BUSINESS_STATUS_TYPE_NOT_EXISTS);\n        }\n    }\n\n    private void validateBusinessStatusTypeNameUnique(String name, Long id) {\n        CrmBusinessStatusTypeDO statusType = businessStatusTypeMapper.selectByName(name);\n        if (statusType == null\n                || statusType.getId().equals(id)) {\n            return;\n        }\n        throw exception(BUSINESS_STATUS_TYPE_NAME_EXISTS);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteBusinessStatusType(Long id) {\n        // 1.1 校验存在\n        validateBusinessStatusTypeExists(id);\n        // 1.2 已经使用，无法更新\n        if (businessService.getBusinessCountByStatusTypeId(id) > 0) {\n            throw exception(BUSINESS_STATUS_DELETE_FAIL_USED);\n        }\n\n        // 2.1 删除类型\n        businessStatusTypeMapper.deleteById(id);\n        // 2.2 删除状态\n        businessStatusMapper.deleteByTypeId(id);\n    }\n\n    @Override\n    public CrmBusinessStatusTypeDO getBusinessStatusType(Long id) {\n        return businessStatusTypeMapper.selectById(id);\n    }\n\n    @Override\n    public void validateBusinessStatusType(Long id) {\n        validateBusinessStatusTypeExists(id);\n    }\n\n    @Override\n    public List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList() {\n        return businessStatusTypeMapper.selectList();\n    }\n\n    @Override\n    public PageResult<CrmBusinessStatusTypeDO> getBusinessStatusTypePage(PageParam pageReqVO) {\n        return businessStatusTypeMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return businessStatusTypeMapper.selectByIds(ids);\n    }\n\n    @Override\n    public List<CrmBusinessStatusDO> getBusinessStatusListByTypeId(Long typeId) {\n        List<CrmBusinessStatusDO> list = businessStatusMapper.selectListByTypeId(typeId);\n        list.sort(Comparator.comparingInt(CrmBusinessStatusDO::getSort));\n        return list;\n    }\n\n    @Override\n    public List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return businessStatusMapper.selectByIds(ids);\n    }\n\n    @Override\n    public CrmBusinessStatusDO getBusinessStatus(Long id) {\n        return businessStatusMapper.selectById(id);\n    }\n\n    @Override\n    public CrmBusinessStatusDO validateBusinessStatus(Long statusTypeId, Long statusId) {\n        CrmBusinessStatusDO status = businessStatusMapper.selectByTypeIdAndId(statusTypeId, statusId);\n        if (status == null) {\n            throw exception(BUSINESS_STATUS_NOT_EXISTS);\n        }\n        return status;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.clue;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;\n\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\n\n/**\n * 线索 Service 接口\n *\n * @author Wanwan\n */\npublic interface CrmClueService {\n\n    /**\n     * 创建线索\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createClue(@Valid CrmClueSaveReqVO createReqVO);\n\n    /**\n     * 更新线索\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateClue(@Valid CrmClueSaveReqVO updateReqVO);\n\n    /**\n     * 更新线索相关的跟进信息\n     *\n     * @param id 编号\n     * @param contactNextTime 下次联系时间\n     * @param contactLastContent 最后联系内容\n     */\n    void updateClueFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);\n\n    /**\n     * 删除线索\n     *\n     * @param id 编号\n     */\n    void deleteClue(Long id);\n\n    /**\n     * 获得线索\n     *\n     * @param id 编号\n     * @return 线索\n     */\n    CrmClueDO getClue(Long id);\n\n    /**\n     * 获得线索分页\n     *\n     * @param pageReqVO 分页查询\n     * @param userId    用户编号\n     * @return 线索分页\n     */\n    PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId);\n\n    /**\n     * 线索转移\n     *\n     * @param reqVO  请求\n     * @param userId 用户编号\n     */\n    void transferClue(CrmClueTransferReqVO reqVO, Long userId);\n\n    /**\n     * 线索转化为客户\n     *\n     * @param id  线索编号\n     * @param userId 用户编号\n     */\n    void transformClue(Long id, Long userId);\n\n    /**\n     * 获得分配给我的、待跟进的线索数量\n     *\n     * @param userId 用户编号\n     * @return 数量\n     */\n    Long getFollowClueCount(Long userId);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.clue;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;\nimport cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.service.impl.DiffParseFunction;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport javax.annotation.Resource;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.singleton;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CLUE_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CLUE_TRANSFORM_FAIL_ALREADY;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\nimport static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;\n\n/**\n * 线索 Service 实现类\n *\n * @author Wanwan\n */\n@Service\n@Validated\npublic class CrmClueServiceImpl implements CrmClueService {\n\n    @Resource\n    private CrmClueMapper clueMapper;\n\n    @Resource\n    private CrmCustomerService customerService;\n    @Resource\n    private CrmPermissionService crmPermissionService;\n    @Resource\n    private CrmFollowUpRecordService followUpRecordService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_CREATE_SUB_TYPE, bizNo = \"{{#clue.id}}\",\n            success = CRM_CLUE_CREATE_SUCCESS)\n    public Long createClue(CrmClueSaveReqVO createReqVO) {\n        // 1.1 校验关联数据\n        validateRelationDataExists(createReqVO);\n        // 1.2 校验负责人是否存在\n        adminUserApi.validateUser(createReqVO.getOwnerUserId());\n\n        // 2. 插入线索\n        CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class);\n        clueMapper.insert(clue);\n\n        // 3. 创建数据权限\n        CrmPermissionCreateReqBO createReqBO = new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CLUE.getType())\n                .setBizId(clue.getId()).setUserId(clue.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel());\n        crmPermissionService.createPermission(createReqBO);\n\n        // 4. 记录操作日志上下文\n        LogRecordContext.putVariable(\"clue\", clue);\n        return clue.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_CLUE_UPDATE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = \"#updateReqVO.id\", level = CrmPermissionLevelEnum.OWNER)\n    public void updateClue(CrmClueSaveReqVO updateReqVO) {\n        Assert.notNull(updateReqVO.getId(), \"线索编号不能为空\");\n        // 1.1 校验线索是否存在\n        CrmClueDO oldClue = validateClueExists(updateReqVO.getId());\n        // 1.2 校验关联数据\n        validateRelationDataExists(updateReqVO);\n\n        // 2. 更新线索\n        CrmClueDO updateObj = BeanUtils.toBean(updateReqVO, CrmClueDO.class);\n        clueMapper.updateById(updateObj);\n\n        // 3. 记录操作日志上下文\n        updateReqVO.setOwnerUserId(oldClue.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况\n        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmClueSaveReqVO.class));\n        LogRecordContext.putVariable(\"clueName\", oldClue.getName());\n    }\n\n    private void validateRelationDataExists(CrmClueSaveReqVO reqVO) {\n        // 校验负责人\n        if (Objects.nonNull(reqVO.getOwnerUserId()) &&\n                Objects.isNull(adminUserApi.getUser(reqVO.getOwnerUserId()).getCheckedData())) {\n            throw exception(USER_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_FOLLOW_UP_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CLUE_FOLLOW_UP_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = \"#id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateClueFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {\n        // 校验线索是否存在\n        CrmClueDO oldClue = validateClueExists(id);\n\n        // 更新线索\n        clueMapper.updateById(new CrmClueDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)\n                .setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent));\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"clueName\", oldClue.getName());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CLUE_DELETE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void deleteClue(Long id) {\n        // 1. 校验存在\n        CrmClueDO clue = validateClueExists(id);\n\n        // 2. 删除\n        clueMapper.deleteById(id);\n\n        // 3. 删除数据权限\n        crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CLUE.getType(), id);\n\n        // 4. 删除跟进\n        followUpRecordService.deleteFollowUpRecordByBiz(CrmBizTypeEnum.CRM_CLUE.getType(), id);\n\n        // 5. 记录操作日志上下文\n        LogRecordContext.putVariable(\"clueName\", clue.getName());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_TRANSFER_SUB_TYPE, bizNo = \"{{#reqVO.id}}\",\n            success = CRM_CLUE_TRANSFER_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = \"#reqVO.id\", level = CrmPermissionLevelEnum.OWNER)\n    public void transferClue(CrmClueTransferReqVO reqVO, Long userId) {\n        // 1 校验线索是否存在\n        CrmClueDO clue = validateClueExists(reqVO.getId());\n\n        // 2.1 数据权限转移\n        crmPermissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CLUE.getType(),\n                        reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));\n        // 2.2 设置新的负责人\n        clueMapper.updateById(new CrmClueDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId()));\n\n        // 3. 记录转移日志\n        LogRecordContext.putVariable(\"clue\", clue);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_TRANSLATE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CLUE_TRANSLATE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void transformClue(Long id, Long userId) {\n        // 1.1 校验线索都存在\n        CrmClueDO clue = validateClueExists(id);\n        // 1.2 存在已经转化的\n        if (clue.getTransformStatus()) {\n            throw exception(CLUE_TRANSFORM_FAIL_ALREADY);\n        }\n\n        // 2.1 遍历线索(未转化的线索)，创建对应的客户\n        Long customerId = customerService.createCustomer(BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class), userId);\n        // 2.2 更新线索\n        clueMapper.updateById(new CrmClueDO().setId(id).setTransformStatus(Boolean.TRUE).setCustomerId(customerId));\n        // 2.3 复制跟进记录\n        List<CrmFollowUpRecordDO> followUpRecords = followUpRecordService.getFollowUpRecordByBiz(\n                CrmBizTypeEnum.CRM_CLUE.getType(), singleton(clue.getId()));\n        if (CollUtil.isNotEmpty(followUpRecords)) {\n            followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, record ->\n                    BeanUtils.toBean(record, CrmFollowUpCreateReqBO.class)\n                            .setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()).setBizId(customerId)));\n        }\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"clueName\", clue.getName());\n    }\n\n    private CrmClueDO validateClueExists(Long id) {\n        CrmClueDO crmClueDO = clueMapper.selectById(id);\n        if (crmClueDO == null) {\n            throw exception(CLUE_NOT_EXISTS);\n        }\n        return crmClueDO;\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = \"#id\", level = CrmPermissionLevelEnum.READ)\n    public CrmClueDO getClue(Long id) {\n        return clueMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId) {\n        return clueMapper.selectPage(pageReqVO, userId);\n    }\n\n    @Override\n    public Long getFollowClueCount(Long userId) {\n        return clueMapper.selectCountByFollow(userId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.contact;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusiness2ReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;\nimport javax.validation.Valid;\n\nimport java.util.List;\n\n/**\n * CRM 联系人与商机的关联 Service 接口\n *\n * @author 芋道源码\n */\npublic interface CrmContactBusinessService {\n\n    /**\n     * 创建联系人与商机的关联【通过联系人，关联商机】\n     *\n     * @param createReqVO 创建信息\n     */\n    void createContactBusinessList(@Valid CrmContactBusinessReqVO createReqVO);\n\n    /**\n     * 创建联系人与商机的关联【通过商机，关联联系人】\n     *\n     * @param createReqVO 创建信息\n     */\n    void createContactBusinessList2(@Valid CrmContactBusiness2ReqVO createReqVO);\n\n    /**\n     * 删除联系人与商机的关联【通过联系人，取关商机】\n     *\n     * @param deleteReqVO 删除信息\n     */\n    void deleteContactBusinessList(@Valid CrmContactBusinessReqVO deleteReqVO);\n\n    /**\n     * 删除联系人与商机的关联【通过商机，取关联系人】\n     *\n     * @param deleteReqVO 删除信息\n     */\n    void deleteContactBusinessList2(@Valid CrmContactBusiness2ReqVO deleteReqVO);\n\n    /**\n     * 删除联系人与商机的关联，基于联系人编号\n     *\n     * @param contactId 联系人编号\n     */\n    void deleteContactBusinessByContactId(Long contactId);\n\n    /**\n     * 获得联系人与商机的关联列表，基于联系人编号\n     *\n     * @param contactId 联系人编号\n     * @return 联系人商机关联\n     */\n    List<CrmContactBusinessDO> getContactBusinessListByContactId(Long contactId);\n\n    /**\n     * 获得联系人与商机的关联列表，基于商机编号\n     *\n     * @param businessId 商机编号\n     * @return 联系人商机关联\n     */\n    List<CrmContactBusinessDO> getContactBusinessListByBusinessId(Long businessId);\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.contact;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusiness2ReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactBusinessMapper;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_NOT_EXISTS;\n\n/**\n * 联系人与商机的关联 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class CrmContactBusinessServiceImpl implements CrmContactBusinessService {\n\n    @Resource\n    private CrmContactBusinessMapper contactBusinessMapper;\n\n    @Resource\n    @Lazy // 延迟加载，为了解决延迟加载\n    private CrmBusinessService businessService;\n    @Resource\n    @Lazy // 延迟加载，为了解决延迟加载\n    private CrmContactService contactService;\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#createReqVO.contactId\", level = CrmPermissionLevelEnum.WRITE)\n    public void createContactBusinessList(CrmContactBusinessReqVO createReqVO) {\n        CrmContactDO contact = contactService.getContact(createReqVO.getContactId());\n        if (contact == null) {\n            throw exception(CONTACT_NOT_EXISTS);\n        }\n        // 遍历处理，考虑到一般数量不会太多，代码处理简单\n        List<CrmContactBusinessDO> saveDOList = new ArrayList<>();\n        createReqVO.getBusinessIds().forEach(businessId -> {\n            CrmBusinessDO business = businessService.getBusiness(businessId);\n            if (business == null) {\n                throw exception(BUSINESS_NOT_EXISTS);\n            }\n            // 关联判重\n            if (contactBusinessMapper.selectByContactIdAndBusinessId(createReqVO.getContactId(), businessId) != null) {\n                return;\n            }\n            saveDOList.add(new CrmContactBusinessDO(null, createReqVO.getContactId(), businessId));\n        });\n        // 批量插入\n        if (CollUtil.isNotEmpty(saveDOList)) {\n            contactBusinessMapper.insertBatch(saveDOList);\n        }\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#createReqVO.businessId\", level = CrmPermissionLevelEnum.WRITE)\n    public void createContactBusinessList2(CrmContactBusiness2ReqVO createReqVO) {\n        CrmBusinessDO business = businessService.getBusiness(createReqVO.getBusinessId());\n        if (business == null) {\n            throw exception(BUSINESS_NOT_EXISTS);\n        }\n        // 遍历处理，考虑到一般数量不会太多，代码处理简单\n        List<CrmContactBusinessDO> saveDOList = new ArrayList<>();\n        createReqVO.getContactIds().forEach(contactId -> {\n            CrmContactDO contact = contactService.getContact(contactId);\n            if (contact == null) {\n                throw exception(CONTACT_NOT_EXISTS);\n            }\n            // 关联判重\n            if (contactBusinessMapper.selectByContactIdAndBusinessId(contactId, createReqVO.getBusinessId()) != null) {\n                return;\n            }\n            saveDOList.add(new CrmContactBusinessDO(null, contactId, createReqVO.getBusinessId()));\n        });\n        // 批量插入\n        if (CollUtil.isNotEmpty(saveDOList)) {\n            contactBusinessMapper.insertBatch(saveDOList);\n        }\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#deleteReqVO.contactId\", level = CrmPermissionLevelEnum.WRITE)\n    public void deleteContactBusinessList(CrmContactBusinessReqVO deleteReqVO) {\n        CrmContactDO contact = contactService.getContact(deleteReqVO.getContactId());\n        if (contact == null) {\n            throw exception(CONTACT_NOT_EXISTS);\n        }\n        // 直接删除\n        contactBusinessMapper.deleteByContactIdAndBusinessId(\n                deleteReqVO.getContactId(), deleteReqVO.getBusinessIds());\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#deleteReqVO.businessId\", level = CrmPermissionLevelEnum.WRITE)\n    public void deleteContactBusinessList2(CrmContactBusiness2ReqVO deleteReqVO) {\n        CrmBusinessDO business = businessService.getBusiness(deleteReqVO.getBusinessId());\n        if (business == null) {\n            throw exception(BUSINESS_NOT_EXISTS);\n        }\n        // 直接删除\n        contactBusinessMapper.deleteByBusinessIdAndContactId(\n                deleteReqVO.getBusinessId(), deleteReqVO.getContactIds());\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#contactId\", level = CrmPermissionLevelEnum.WRITE)\n    public void deleteContactBusinessByContactId(Long contactId) {\n        contactBusinessMapper.delete(CrmContactBusinessDO::getContactId, contactId);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#contactId\", level = CrmPermissionLevelEnum.READ)\n    public List<CrmContactBusinessDO> getContactBusinessListByContactId(Long contactId) {\n        return contactBusinessMapper.selectListByContactId(contactId);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#businessId\", level = CrmPermissionLevelEnum.READ)\n    public List<CrmContactBusinessDO> getContactBusinessListByBusinessId(Long businessId) {\n        return contactBusinessMapper.selectListByBusinessId(businessId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.contact;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\n\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * CRM 联系人 Service 接口\n *\n * @author 芋道源码\n */\npublic interface CrmContactService {\n\n    /**\n     * 创建联系人\n     *\n     * @param createReqVO 创建信息\n     * @param userId      用户编号\n     * @return 编号\n     */\n    Long createContact(@Valid CrmContactSaveReqVO createReqVO, Long userId);\n\n    /**\n     * 更新联系人\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateContact(@Valid CrmContactSaveReqVO updateReqVO);\n\n    /**\n     * 删除联系人\n     *\n     * @param id 编号\n     */\n    void deleteContact(Long id);\n\n    /**\n     * 联系人转移\n     *\n     * @param reqVO  请求\n     * @param userId 用户编号\n     */\n    void transferContact(CrmContactTransferReqVO reqVO, Long userId);\n\n    /**\n     * 更新指定客户的联系人的负责人\n     * 数据权限基于 【客户】\n     *\n     * @param customerId  客户编号\n     * @param ownerUserId 用户编号\n     */\n    void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId);\n\n    /**\n     * 更新联系人相关跟进信息\n     *\n     * @param id                 编号\n     * @param contactNextTime    下次联系时间\n     * @param contactLastContent 最后联系内容\n     */\n    void updateContactFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);\n\n    /**\n     * 更新联系人的下次联系时间\n     *\n     * @param ids             编号数组\n     * @param contactNextTime 下次联系时间\n     */\n    void updateContactContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime);\n\n    /**\n     * 获得联系人\n     *\n     * @param id 编号\n     * @return 联系人\n     */\n    CrmContactDO getContact(Long id);\n\n    /**\n     * 校验联系人\n     *\n     * @param id 编号\n     */\n    void validateContact(Long id);\n\n    /**\n     * 获得联系人列表\n     *\n     * @param ids 编号\n     * @return 联系人列表\n     */\n    List<CrmContactDO> getContactList(Collection<Long> ids);\n\n    /**\n     * 获得联系人 Map\n     *\n     * @param ids 编号\n     * @return 联系人 Map\n     */\n    default Map<Long, CrmContactDO> getContactMap(Collection<Long> ids) {\n        return convertMap(getContactList(ids), CrmContactDO::getId);\n    }\n\n    /**\n     * 获取联系人列表（校验权限）\n     *\n     * @param userId 用户编号\n     * @return 联系人列表\n     */\n    List<CrmContactDO> getContactList(Long userId);\n\n    /**\n     * 获得联系人分页\n     *\n     * 数据权限：基于 {@link CrmContactDO}\n     *\n     * @param pageReqVO 分页查询\n     * @param userId    用户编号\n     * @return 联系人分页\n     */\n    PageResult<CrmContactDO> getContactPage(CrmContactPageReqVO pageReqVO, Long userId);\n\n    /**\n     * 获得联系人分页\n     *\n     * 数据权限：基于 {@link CrmCustomerDO}\n     *\n     * @param pageVO 分页查询\n     * @return 联系人分页\n     */\n    PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO);\n\n    /**\n     * 获得联系人分页\n     *\n     * 数据权限：基于 {@link CrmBusinessDO}\n     *\n     * @param pageVO 分页查询\n     * @return 联系人分页\n     */\n    PageResult<CrmContactDO> getContactPageByBusinessId(CrmContactPageReqVO pageVO);\n\n    /**\n     * 获取关联客户的联系人数量\n     *\n     * @param customerId 客户编号\n     * @return 数量\n     */\n    Long getContactCountByCustomerId(Long customerId);\n\n    /**\n     * 获得联系人列表\n     *\n     * @param customerId  客户编号\n     * @param ownerUserId 负责人编号\n     * @return 联系人列表\n     */\n    List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.contact;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactMapper;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.service.impl.DiffParseFunction;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\nimport static java.util.Collections.singletonList;\n\n/**\n * CRM 联系人 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class CrmContactServiceImpl implements CrmContactService {\n\n    @Resource\n    private CrmContactMapper contactMapper;\n\n    @Resource\n    private CrmCustomerService customerService;\n    @Resource\n    private CrmPermissionService permissionService;\n    @Resource\n    @Lazy\n    private CrmContractService contractService;\n    @Resource\n    private CrmContactBusinessService contactBusinessService;\n    @Resource\n    private CrmBusinessService businessService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_CREATE_SUB_TYPE, bizNo = \"{{#contact.id}}\",\n            success = CRM_CONTACT_CREATE_SUCCESS)\n    public Long createContact(CrmContactSaveReqVO createReqVO, Long userId) {\n        createReqVO.setId(null);\n        // 1. 校验关联数据\n        validateRelationDataExists(createReqVO);\n\n        // 2. 插入联系人\n        CrmContactDO contact = BeanUtils.toBean(createReqVO, CrmContactDO.class);\n        contactMapper.insert(contact);\n\n        // 3. 创建数据权限\n        permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)\n                .setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()).setBizId(contact.getId())\n                .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));\n\n        // 4. 如果有关联商机，则需要创建关联\n        if (createReqVO.getBusinessId() != null) {\n            contactBusinessService.createContactBusinessList(new CrmContactBusinessReqVO()\n                    .setContactId(contact.getId()).setBusinessIds(singletonList(createReqVO.getBusinessId())));\n        }\n\n        // 5. 记录操作日志\n        LogRecordContext.putVariable(\"contact\", contact);\n        return contact.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_CONTACT_UPDATE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#updateReqVO.id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateContact(CrmContactSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        CrmContactDO oldContact = validateContactExists(updateReqVO.getId());\n        // 1.2 校验关联数据\n        validateRelationDataExists(updateReqVO);\n\n        // 2. 更新联系人\n        CrmContactDO updateObj = BeanUtils.toBean(updateReqVO, CrmContactDO.class);\n        contactMapper.updateById(updateObj);\n\n        // 3. 记录操作日志\n        updateReqVO.setOwnerUserId(oldContact.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况\n        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContact, CrmContactSaveReqVO.class));\n        LogRecordContext.putVariable(\"contactName\", oldContact.getName());\n    }\n\n    /**\n     * 校验关联的数据都存在\n     *\n     * @param saveReqVO 新增/修改请求 VO\n     */\n    private void validateRelationDataExists(CrmContactSaveReqVO saveReqVO) {\n        // 1. 校验客户\n        if (saveReqVO.getCustomerId() != null && customerService.getCustomer(saveReqVO.getCustomerId()) == null) {\n            customerService.validateCustomer(saveReqVO.getCustomerId());\n        }\n        // 2. 校验负责人\n        if (saveReqVO.getOwnerUserId() != null) {\n            adminUserApi.validateUser(saveReqVO.getOwnerUserId());\n        }\n        // 3. 直属上级\n        if (saveReqVO.getParentId() != null) {\n            validateContactExists(saveReqVO.getParentId());\n        }\n        // 4. 如果有关联商机，则需要校验存在\n        if (saveReqVO.getBusinessId() != null && businessService.getBusiness(saveReqVO.getBusinessId()) == null) {\n            throw exception(BUSINESS_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CONTACT_DELETE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void deleteContact(Long id) {\n        // 1.1 校验存在\n        CrmContactDO contact = validateContactExists(id);\n        // 1.2 校验是否关联合同\n        if (contractService.getContractCountByContactId(id) > 0) {\n            throw exception(CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS);\n        }\n\n        // 2. 删除联系人\n        contactMapper.deleteById(id);\n\n        // 4.1 删除商机关联\n        contactBusinessService.deleteContactBusinessByContactId(id);\n        // 4.2 删除数据权限\n        permissionService.deletePermission(CrmBizTypeEnum.CRM_CONTACT.getType(), id);\n\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"contactName\", contact.getName());\n    }\n\n    private CrmContactDO validateContactExists(Long id) {\n        CrmContactDO contact = contactMapper.selectById(id);\n        if (contact == null) {\n            throw exception(CONTACT_NOT_EXISTS);\n        }\n        return contact;\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_TRANSFER_SUB_TYPE, bizNo = \"{{#reqVO.id}}\",\n            success = CRM_CONTACT_TRANSFER_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#reqVO.id\", level = CrmPermissionLevelEnum.OWNER)\n    public void transferContact(CrmContactTransferReqVO reqVO, Long userId) {\n        // 1 校验联系人是否存在\n        CrmContactDO contact = validateContactExists(reqVO.getId());\n\n        // 2.1 数据权限转移\n        permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTACT.getType(),\n                reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));\n        // 2.2 设置新的负责人\n        contactMapper.updateById(new CrmContactDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId()));\n\n        // 3. 记录转移日志\n        LogRecordContext.putVariable(\"contact\", contact);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#customerId\", level = CrmPermissionLevelEnum.OWNER)\n    public void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) {\n        // 1. 校验存在\n        List<CrmContactDO> contacts = contactMapper.selectListByCustomerId(customerId);\n        if (CollUtil.isEmpty(contacts)) {\n            return;\n        }\n        int count = contactMapper.updateOwnerUserIdByCustomerId(customerId, ownerUserId);\n        if (count == 0) {\n            throw exception(CONTACT_UPDATE_OWNER_USER_FAIL);\n        }\n\n        // 2. 记录操作日志\n        for (CrmContactDO contact : contacts) {\n            receiveContactLog(contact, ownerUserId);\n        }\n    }\n\n    @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_UPDATE_OWNER_USER_SUB_TYPE, bizNo = \"{{#contact.id}}\",\n            success = CRM_CONTACT_UPDATE_OWNER_USER_SUCCESS)\n    public void receiveContactLog(CrmContactDO contact, Long ownerUserId) {\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"contact\", contact);\n        LogRecordContext.putVariable(\"ownerUserId\", ownerUserId);\n    }\n\n    @Override\n    @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_FOLLOW_UP_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CONTACT_FOLLOW_UP_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateContactFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {\n        // 1. 校验存在\n        CrmContactDO contact = validateContactExists(id);\n\n        // 2. 更新联系人的跟进信息\n        contactMapper.updateById(new CrmContactDO().setId(id).setContactNextTime(contactNextTime)\n                .setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent));\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"contactName\", contact.getName());\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#ids\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateContactContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime) {\n        contactMapper.updateBatch(convertList(ids, id -> new CrmContactDO().setId(id).setContactNextTime(contactNextTime)));\n    }\n\n    //======================= 查询相关 =======================\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = \"#id\", level = CrmPermissionLevelEnum.READ)\n    public CrmContactDO getContact(Long id) {\n        return contactMapper.selectById(id);\n    }\n\n    @Override\n    public void validateContact(Long id) {\n        validateContactExists(id);\n    }\n\n    @Override\n    public List<CrmContactDO> getContactList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return ListUtil.empty();\n        }\n        return contactMapper.selectByIds(ids);\n    }\n\n    @Override\n    public List<CrmContactDO> getContactList(Long userId) {\n        CrmContactPageReqVO reqVO = new CrmContactPageReqVO();\n        reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页\n        return contactMapper.selectPage(reqVO, userId).getList();\n    }\n\n    @Override\n    public PageResult<CrmContactDO> getContactPage(CrmContactPageReqVO pageReqVO, Long userId) {\n        return contactMapper.selectPage(pageReqVO, userId);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#pageVO.customerId\", level = CrmPermissionLevelEnum.READ)\n    public PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO) {\n        return contactMapper.selectPageByCustomerId(pageVO);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#pageVO.businessId\", level = CrmPermissionLevelEnum.READ)\n    public PageResult<CrmContactDO> getContactPageByBusinessId(CrmContactPageReqVO pageVO) {\n        List<CrmContactBusinessDO> contactBusinessList = contactBusinessService.getContactBusinessListByBusinessId(pageVO.getBusinessId());\n        if (CollUtil.isEmpty(contactBusinessList)) {\n            return PageResult.empty();\n        }\n        return contactMapper.selectPageByBusinessId(pageVO, convertSet(contactBusinessList, CrmContactBusinessDO::getContactId));\n    }\n\n    @Override\n    public Long getContactCountByCustomerId(Long customerId) {\n        return contactMapper.selectCount(CrmContactDO::getCustomerId, customerId);\n    }\n\n    @Override\n    public List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {\n        return contactMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractConfigService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.contract;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config.CrmContractConfigSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;\n\nimport javax.validation.Valid;\n\n/**\n * 合同配置 Service 接口\n *\n * @author 芋道源码\n */\npublic interface CrmContractConfigService {\n\n    /**\n     * 获得合同配置\n     *\n     * @return 合同配置\n     */\n    CrmContractConfigDO getContractConfig();\n\n    /**\n     * 保存合同配置\n     *\n     * @param saveReqVO 更新信息\n     */\n    void saveContractConfig(@Valid CrmContractConfigSaveReqVO saveReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractConfigServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.contract;\n\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config.CrmContractConfigSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractConfigMapper;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\n\n/**\n * 合同配置 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class CrmContractConfigServiceImpl implements CrmContractConfigService {\n\n    @Resource\n    private CrmContractConfigMapper contractConfigMapper;\n\n    @Override\n    public CrmContractConfigDO getContractConfig() {\n        return contractConfigMapper.selectOne();\n    }\n\n    @Override\n    @LogRecord(type = CRM_CONTRACT_CONFIG_TYPE, subType = CRM_CONTRACT_CONFIG_SUB_TYPE, bizNo = \"{{#configId}}\",\n            success = CRM_CONTRACT_CONFIG_SUCCESS)\n    public void saveContractConfig(CrmContractConfigSaveReqVO saveReqVO) {\n        // 1. 存在，则进行更新\n        CrmContractConfigDO dbConfig = getContractConfig();\n        CrmContractConfigDO config = BeanUtils.toBean(saveReqVO, CrmContractConfigDO.class);\n        if (Objects.nonNull(dbConfig)) {\n            contractConfigMapper.updateById(config.setId(dbConfig.getId()));\n            // 记录操作日志上下文\n            LogRecordContext.putVariable(\"isConfigUpdate\", Boolean.TRUE);\n            LogRecordContext.putVariable(\"configId\", config.getId());\n            return;\n        }\n\n        // 2. 不存在，则进行插入\n        contractConfigMapper.insert(config);\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"isConfigUpdate\", Boolean.FALSE);\n        LogRecordContext.putVariable(\"configId\", config.getId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.contract;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\n\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * CRM 合同 Service 接口\n *\n * @author dhb52\n */\npublic interface CrmContractService {\n\n    /**\n     * 创建合同\n     *\n     * @param createReqVO 创建信息\n     * @param userId      用户编号\n     * @return 编号\n     */\n    Long createContract(@Valid CrmContractSaveReqVO createReqVO, Long userId);\n\n    /**\n     * 更新合同\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateContract(@Valid CrmContractSaveReqVO updateReqVO);\n\n    /**\n     * 删除合同\n     *\n     * @param id 编号\n     */\n    void deleteContract(Long id);\n\n    /**\n     * 合同转移\n     *\n     * @param reqVO  请求\n     * @param userId 用户编号\n     */\n    void transferContract(CrmContractTransferReqVO reqVO, Long userId);\n\n    /**\n     * 更新合同相关的更进信息\n     *\n     * @param id                 合同编号\n     * @param contactNextTime    下次联系时间\n     * @param contactLastContent 最后联系内容\n     */\n    void updateContractFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);\n\n    /**\n     * 发起合同审批流程\n     *\n     * @param id     合同编号\n     * @param userId 用户编号\n     */\n    void submitContract(Long id, Long userId);\n\n    /**\n     * 更新合同流程审批结果\n     *\n     * @param id        合同编号\n     * @param bpmResult BPM 审批结果\n     */\n    void updateContractAuditStatus(Long id, Integer bpmResult);\n\n    /**\n     * 获得合同\n     *\n     * @param id 编号\n     * @return 合同\n     */\n    CrmContractDO getContract(Long id);\n\n    /**\n     * 校验合同是否合法\n     *\n     * @param id 编号\n     * @return 合同\n     */\n    CrmContractDO validateContract(Long id);\n\n    /**\n     * 获得合同列表\n     *\n     * @param ids 编号\n     * @return 合同列表\n     */\n    List<CrmContractDO> getContractList(Collection<Long> ids);\n\n    /**\n     * 获得合同 Map\n     *\n     * @param ids 编号\n     * @return 合同 Map\n     */\n    default Map<Long, CrmContractDO> getContractMap(Collection<Long> ids) {\n        return convertMap(getContractList(ids), CrmContractDO::getId);\n    }\n\n    /**\n     * 获得合同分页\n     *\n     * 数据权限：基于 {@link CrmContractDO} 读取\n     *\n     * @param pageReqVO 分页查询\n     * @param userId    用户编号\n     * @return 合同分页\n     */\n    PageResult<CrmContractDO> getContractPage(CrmContractPageReqVO pageReqVO, Long userId);\n\n    /**\n     * 获得合同分页，基于指定客户\n     *\n     * 数据权限：基于 {@link CrmCustomerDO} 读取\n     *\n     * @param pageReqVO 分页查询\n     * @return 合同分页\n     */\n    PageResult<CrmContractDO> getContractPageByCustomerId(CrmContractPageReqVO pageReqVO);\n\n    /**\n     * 获得合同分页，基于指定商机\n     *\n     * 数据权限：基于 {@link CrmBusinessDO} 读取\n     *\n     * @param pageReqVO 分页查询\n     * @return 合同分页\n     */\n    PageResult<CrmContractDO> getContractPageByBusinessId(CrmContractPageReqVO pageReqVO);\n\n    /**\n     * 查询属于某个联系人的合同数量\n     *\n     * @param contactId 联系人ID\n     * @return 合同\n     */\n    Long getContractCountByContactId(Long contactId);\n\n    /**\n     * 获取关联客户的合同数量\n     *\n     * @param customerId 客户编号\n     * @return 数量\n     */\n    Long getContractCountByCustomerId(Long customerId);\n\n    /**\n     * 根据商机编号，获取关联客户的合同数量\n     *\n     * @param businessId 商机编号\n     * @return 数量\n     */\n    Long getContractCountByBusinessId(Long businessId);\n\n    /**\n     * 根据合同编号，获得合同的产品列表\n     *\n     * @param contactId 合同编号\n     * @return 产品列表\n     */\n    List<CrmContractProductDO> getContractProductListByContractId(Long contactId);\n\n    /**\n     * 获得待审核合同数量\n     *\n     * @param userId 用户编号\n     * @return 提醒数量\n     */\n    Long getAuditContractCount(Long userId);\n\n    /**\n     * 获得即将到期（提醒）的合同数量\n     *\n     * @param userId 用户编号\n     * @return 提醒数量\n     */\n    Long getRemindContractCount(Long userId);\n\n    /**\n     * 获得合同列表\n     *\n     * @param customerId  客户编号\n     * @param ownerUserId 负责人编号\n     * @return 合同列表\n     */\n    List<CrmContractDO> getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.contract;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;\nimport cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;\nimport cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractProductMapper;\nimport cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;\nimport cn.iocoder.yudao.module.crm.service.product.CrmProductService;\nimport cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.service.impl.DiffParseFunction;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\nimport static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmResultToAuditStatus;\n\n/**\n * CRM 合同 Service 实现类\n *\n * @author dhb52\n */\n@Service\n@Validated\n@Slf4j\npublic class CrmContractServiceImpl implements CrmContractService {\n\n    /**\n     * BPM 合同审批流程标识\n     */\n    public static final String BPM_PROCESS_DEFINITION_KEY = \"crm-contract-audit\";\n\n    @Resource\n    private CrmContractMapper contractMapper;\n    @Resource\n    private CrmContractProductMapper contractProductMapper;\n\n    @Resource\n    private CrmNoRedisDAO noRedisDAO;\n\n    @Resource\n    private CrmPermissionService crmPermissionService;\n    @Resource\n    private CrmProductService productService;\n    @Resource\n    private CrmCustomerService customerService;\n    @Resource\n    private CrmBusinessService businessService;\n    @Resource\n    private CrmContactService contactService;\n    @Resource\n    private CrmContractConfigService contractConfigService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private CrmReceivableService receivableService;\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private BpmProcessInstanceApi bpmProcessInstanceApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = \"{{#contract.id}}\",\n            success = CRM_CONTRACT_CREATE_SUCCESS)\n    public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) {\n        // 1.1 校验产品项的有效性\n        List<CrmContractProductDO> contractProducts = validateContractProducts(createReqVO.getProducts());\n        // 1.2 校验关联字段\n        validateRelationDataExists(createReqVO);\n        // 1.3 生成序号\n        String no = noRedisDAO.generate(CrmNoRedisDAO.CONTRACT_NO_PREFIX);\n        if (contractMapper.selectByNo(no) != null) {\n            throw exception(CONTRACT_NO_EXISTS);\n        }\n\n        // 2.1 插入合同\n        CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setNo(no);\n        calculateTotalPrice(contract, contractProducts);\n        contractMapper.insert(contract);\n        // 2.2 插入合同关联商品\n        if (CollUtil.isNotEmpty(contractProducts)) {\n            contractProducts.forEach(item -> item.setContractId(contract.getId()));\n            contractProductMapper.insertBatch(contractProducts);\n        }\n\n        // 3. 创建数据权限\n        crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(contract.getOwnerUserId())\n                .setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId())\n                .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));\n\n        // 4. 记录操作日志上下文\n        LogRecordContext.putVariable(\"contract\", contract);\n        return contract.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_CONTRACT_UPDATE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = \"#updateReqVO.id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateContract(CrmContractSaveReqVO updateReqVO) {\n        Assert.notNull(updateReqVO.getId(), \"合同编号不能为空\");\n        updateReqVO.setOwnerUserId(null); // 不允许更新的字段\n        // 1.1 校验存在\n        CrmContractDO oldContract = validateContractExists(updateReqVO.getId());\n        // 1.2 只有草稿、审批中，可以编辑；\n        if (!ObjectUtils.equalsAny(oldContract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),\n                CrmAuditStatusEnum.PROCESS.getStatus())) {\n            throw exception(CONTRACT_UPDATE_FAIL_NOT_DRAFT);\n        }\n        // 1.3 校验产品项的有效性\n        List<CrmContractProductDO> contractProducts = validateContractProducts(updateReqVO.getProducts());\n        // 1.4 校验关联字段\n        validateRelationDataExists(updateReqVO);\n\n        // 2.1 更新合同\n        CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class);\n        calculateTotalPrice(updateObj, contractProducts);\n        contractMapper.updateById(updateObj);\n        // 2.2 更新合同关联商品\n        updateContractProduct(updateReqVO.getId(), contractProducts);\n\n        // 3. 记录操作日志上下文\n        updateReqVO.setOwnerUserId(oldContract.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况\n        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class));\n        LogRecordContext.putVariable(\"contractName\", oldContract.getName());\n    }\n\n    private void updateContractProduct(Long id, List<CrmContractProductDO> newList) {\n        List<CrmContractProductDO> oldList = contractProductMapper.selectListByContractId(id);\n        List<List<CrmContractProductDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setContractId(id));\n            contractProductMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            contractProductMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            contractProductMapper.deleteByIds(convertSet(diffList.get(2), CrmContractProductDO::getId));\n        }\n    }\n\n    /**\n     * 校验关联数据是否存在\n     *\n     * @param reqVO 请求\n     */\n    private void validateRelationDataExists(CrmContractSaveReqVO reqVO) {\n        // 1. 校验客户\n        if (reqVO.getCustomerId() != null) {\n            customerService.validateCustomer(reqVO.getCustomerId());\n        }\n        // 2. 校验负责人\n        if (reqVO.getOwnerUserId() != null) {\n            adminUserApi.validateUser(reqVO.getOwnerUserId());\n        }\n        // 3. 如果有关联商机，则需要校验存在\n        if (reqVO.getBusinessId() != null) {\n            businessService.validateBusiness(reqVO.getBusinessId());\n        }\n        // 4. 校验签约相关字段\n        if (reqVO.getSignContactId() != null) {\n            contactService.validateContact(reqVO.getSignContactId());\n        }\n        if (reqVO.getSignUserId() != null) {\n            adminUserApi.validateUser(reqVO.getSignUserId());\n        }\n    }\n\n    private List<CrmContractProductDO> validateContractProducts(List<CrmContractSaveReqVO.Product> list) {\n        // 1. 校验产品存在\n        productService.validProductList(convertSet(list, CrmContractSaveReqVO.Product::getProductId));\n        // 2. 转化为 CrmContractProductDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, CrmContractProductDO.class,\n                item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getContractPrice(), item.getCount()))));\n    }\n\n    private void calculateTotalPrice(CrmContractDO contract, List<CrmContractProductDO> contractProducts) {\n        contract.setTotalProductPrice(getSumValue(contractProducts, CrmContractProductDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));\n        BigDecimal discountPrice = MoneyUtils.priceMultiplyPercent(contract.getTotalProductPrice(), contract.getDiscountPercent());\n        contract.setTotalPrice(contract.getTotalProductPrice().subtract(discountPrice));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CONTRACT_DELETE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void deleteContract(Long id) {\n        // 1.1 校验存在\n        CrmContractDO contract = validateContractExists(id);\n        // 1.2 如果被 CrmReceivableDO 所使用，则不允许删除\n        if (receivableService.getReceivableCountByContractId(contract.getId()) > 0) {\n            throw exception(CONTRACT_DELETE_FAIL);\n        }\n\n        // 2.1 删除合同\n        contractMapper.deleteById(id);\n        // 2.2 删除数据权限\n        crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CONTRACT.getType(), id);\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"contractName\", contract.getName());\n    }\n\n    private CrmContractDO validateContractExists(Long id) {\n        CrmContractDO contract = contractMapper.selectById(id);\n        if (contract == null) {\n            throw exception(CONTRACT_NOT_EXISTS);\n        }\n        return contract;\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_TRANSFER_SUB_TYPE, bizNo = \"{{#reqVO.id}}\",\n            success = CRM_CONTRACT_TRANSFER_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = \"#reqVO.id\", level = CrmPermissionLevelEnum.OWNER)\n    public void transferContract(CrmContractTransferReqVO reqVO, Long userId) {\n        // 1. 校验合同是否存在\n        CrmContractDO contract = validateContractExists(reqVO.getId());\n\n        // 2.1 数据权限转移\n        crmPermissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTRACT.getType(),\n                reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));\n        // 2.2 设置负责人\n        contractMapper.updateById(new CrmContractDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId()));\n\n        // 3. 记录转移日志\n        LogRecordContext.putVariable(\"contract\", contract);\n    }\n\n    @Override\n    @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_FOLLOW_UP_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CONTRACT_FOLLOW_UP_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = \"#id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateContractFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {\n        // 1. 校验存在\n        CrmContractDO contract = validateContractExists(id);\n\n        // 2. 更新联系人的跟进信息\n        contractMapper.updateById(new CrmContractDO().setId(id).setContactLastTime(LocalDateTime.now()));\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"contractName\", contract.getName());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_SUBMIT_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CONTRACT_SUBMIT_SUCCESS)\n    public void submitContract(Long id, Long userId) {\n        // 1. 校验合同是否在审批\n        CrmContractDO contract = validateContractExists(id);\n        if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {\n            throw exception(CONTRACT_SUBMIT_FAIL_NOT_DRAFT);\n        }\n\n        // 2. 创建合同审批流程实例\n        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()\n                .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id))).getCheckedData();\n\n        // 3. 更新合同工作流编号\n        contractMapper.updateById(new CrmContractDO().setId(id).setProcessInstanceId(processInstanceId)\n                .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));\n\n        // 3. 记录日志\n        LogRecordContext.putVariable(\"contractName\", contract.getName());\n    }\n\n    @Override\n    public void updateContractAuditStatus(Long id, Integer bpmResult) {\n        // 1.1 校验合同是否存在\n        CrmContractDO contract = validateContractExists(id);\n        // 1.2 只有审批中，可以更新审批结果\n        if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {\n            log.error(\"[updateContractAuditStatus][contract({}) 不处于审批中，无法更新审批结果({})]\",\n                    contract.getId(), bpmResult);\n            throw exception(CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);\n        }\n\n        // 2. 更新合同审批结果\n        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);\n        contractMapper.updateById(new CrmContractDO().setId(id).setAuditStatus(auditStatus));\n    }\n\n    // ======================= 查询相关 =======================\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = \"#id\", level = CrmPermissionLevelEnum.READ)\n    public CrmContractDO getContract(Long id) {\n        return contractMapper.selectById(id);\n    }\n\n    @Override\n    public CrmContractDO validateContract(Long id) {\n        return validateContractExists(id);\n    }\n\n    @Override\n    public List<CrmContractDO> getContractList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return ListUtil.empty();\n        }\n        return contractMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<CrmContractDO> getContractPage(CrmContractPageReqVO pageReqVO, Long userId) {\n        // 1. 即将到期，需要查询合同配置\n        CrmContractConfigDO config = null;\n        if (CrmContractPageReqVO.EXPIRY_TYPE_ABOUT_TO_EXPIRE.equals(pageReqVO.getExpiryType())) {\n            config = contractConfigService.getContractConfig();\n            if (config != null && Boolean.FALSE.equals(config.getNotifyEnabled())) {\n                config = null;\n            }\n            if (config == null) {\n                return PageResult.empty();\n            }\n        }\n        // 2. 查询分页\n        return contractMapper.selectPage(pageReqVO, userId, config);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#pageReqVO.customerId\", level = CrmPermissionLevelEnum.READ)\n    public PageResult<CrmContractDO> getContractPageByCustomerId(CrmContractPageReqVO pageReqVO) {\n        return contractMapper.selectPageByCustomerId(pageReqVO);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = \"#pageReqVO.businessId\", level = CrmPermissionLevelEnum.READ)\n    public PageResult<CrmContractDO> getContractPageByBusinessId(CrmContractPageReqVO pageReqVO) {\n        return contractMapper.selectPageByBusinessId(pageReqVO);\n    }\n\n    @Override\n    public Long getContractCountByContactId(Long contactId) {\n        return contractMapper.selectCountByContactId(contactId);\n    }\n\n    @Override\n    public Long getContractCountByCustomerId(Long customerId) {\n        return contractMapper.selectCount(CrmContractDO::getCustomerId, customerId);\n    }\n\n    @Override\n    public Long getContractCountByBusinessId(Long businessId) {\n        return contractMapper.selectCountByBusinessId(businessId);\n    }\n\n    @Override\n    public List<CrmContractProductDO> getContractProductListByContractId(Long contactId) {\n        return contractProductMapper.selectListByContractId(contactId);\n    }\n\n    @Override\n    public Long getAuditContractCount(Long userId) {\n        return contractMapper.selectCountByAudit(userId);\n    }\n\n    @Override\n    public Long getRemindContractCount(Long userId) {\n        CrmContractConfigDO config = contractConfigService.getContractConfig();\n        if (config == null || Boolean.FALSE.equals(config.getNotifyEnabled())) {\n            return 0L;\n        }\n        return contractMapper.selectCountByRemind(userId, config);\n    }\n\n    @Override\n    public List<CrmContractDO> getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {\n        return contractMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractStatusListener.java",
    "content": "package cn.iocoder.yudao.module.crm.service.contract.listener;\n\nimport cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;\nimport cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;\nimport cn.iocoder.yudao.module.crm.enums.ApiConstants;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractServiceImpl;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\n/**\n * 合同审批的结果的监听器实现类\n *\n * @author HUIHUI\n */\n@RestController\n@Validated\npublic class CrmContractStatusListener extends BpmProcessInstanceStatusEventListener {\n\n    private static final String PREFIX = ApiConstants.PREFIX + \"/contract\";\n\n    @Resource\n    private CrmContractService contractService;\n\n    @Override\n    public String getProcessDefinitionKey() {\n        return CrmContractServiceImpl.BPM_PROCESS_DEFINITION_KEY;\n    }\n\n    @Override\n    @PostMapping(PREFIX + \"/update-audit-status\") // 目的：提供给 bpm-server rpc 调用\n    protected void onEvent(@RequestBody BpmProcessInstanceStatusEvent event) {\n        contractService.updateContractAuditStatus(Long.parseLong(event.getBusinessKey()), event.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.customer;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;\nimport javax.validation.Valid;\n\nimport java.util.List;\n\n/**\n * 客户限制配置 Service 接口\n *\n * @author Wanwan\n */\npublic interface CrmCustomerLimitConfigService {\n\n    /**\n     * 创建客户限制配置\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createCustomerLimitConfig(@Valid CrmCustomerLimitConfigSaveReqVO createReqVO);\n\n    /**\n     * 更新客户限制配置\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCustomerLimitConfig(@Valid CrmCustomerLimitConfigSaveReqVO updateReqVO);\n\n    /**\n     * 删除客户限制配置\n     *\n     * @param id 编号\n     */\n    void deleteCustomerLimitConfig(Long id);\n\n    /**\n     * 获得客户限制配置\n     *\n     * @param id 编号\n     * @return 客户限制配置\n     */\n    CrmCustomerLimitConfigDO getCustomerLimitConfig(Long id);\n\n    /**\n     * 获得客户限制配置分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 客户限制配置分页\n     */\n    PageResult<CrmCustomerLimitConfigDO> getCustomerLimitConfigPage(CrmCustomerLimitConfigPageReqVO pageReqVO);\n\n    /**\n     * 查询用户对应的配置列表\n     *\n     * @param type   类型\n     * @param userId 用户类型\n     */\n    List<CrmCustomerLimitConfigDO> getCustomerLimitConfigListByUserId(Integer type, Long userId);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.customer;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerLimitConfigMapper;\nimport cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.service.impl.DiffParseFunction;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_LIMIT_CONFIG_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\n\n/**\n * 客户限制配置 Service 实现类\n *\n * @author Wanwan\n */\n@Service\n@Validated\npublic class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfigService {\n\n    @Resource\n    private CrmCustomerLimitConfigMapper customerLimitConfigMapper;\n\n    @Resource\n    private DeptApi deptApi;\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @LogRecord(type = CRM_CUSTOMER_LIMIT_CONFIG_TYPE, subType = CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUB_TYPE, bizNo = \"{{#limitId}}\",\n            success = CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUCCESS)\n    public Long createCustomerLimitConfig(CrmCustomerLimitConfigSaveReqVO createReqVO) {\n        validateUserAndDept(createReqVO.getUserIds(), createReqVO.getDeptIds());\n        // 插入\n        CrmCustomerLimitConfigDO limitConfig = BeanUtils.toBean(createReqVO, CrmCustomerLimitConfigDO.class);\n        customerLimitConfigMapper.insert(limitConfig);\n\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"limitType\", CrmCustomerLimitConfigTypeEnum.getNameByType(limitConfig.getType()));\n        LogRecordContext.putVariable(\"limitId\", limitConfig.getId());\n        return limitConfig.getId();\n    }\n\n    @Override\n    @LogRecord(type = CRM_CUSTOMER_LIMIT_CONFIG_TYPE, subType = CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUCCESS)\n    public void updateCustomerLimitConfig(CrmCustomerLimitConfigSaveReqVO updateReqVO) {\n        // 校验存在\n        CrmCustomerLimitConfigDO oldLimitConfig = validateCustomerLimitConfigExists(updateReqVO.getId());\n        validateUserAndDept(updateReqVO.getUserIds(), updateReqVO.getDeptIds());\n        // 更新\n        CrmCustomerLimitConfigDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerLimitConfigDO.class);\n        customerLimitConfigMapper.updateById(updateObj);\n\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldLimitConfig, CrmCustomerLimitConfigSaveReqVO.class));\n    }\n\n    @Override\n    @LogRecord(type = CRM_CUSTOMER_LIMIT_CONFIG_TYPE, subType = CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUCCESS)\n    public void deleteCustomerLimitConfig(Long id) {\n        // 校验存在\n        CrmCustomerLimitConfigDO limitConfig = validateCustomerLimitConfigExists(id);\n        // 删除\n        customerLimitConfigMapper.deleteById(id);\n\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"limitType\", CrmCustomerLimitConfigTypeEnum.getNameByType(limitConfig.getType()));\n    }\n\n    @Override\n    public CrmCustomerLimitConfigDO getCustomerLimitConfig(Long id) {\n        return customerLimitConfigMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<CrmCustomerLimitConfigDO> getCustomerLimitConfigPage(CrmCustomerLimitConfigPageReqVO pageReqVO) {\n        return customerLimitConfigMapper.selectPage(pageReqVO);\n    }\n\n    private CrmCustomerLimitConfigDO validateCustomerLimitConfigExists(Long id) {\n        CrmCustomerLimitConfigDO limitConfigDO = customerLimitConfigMapper.selectById(id);\n        if (limitConfigDO == null) {\n            throw exception(CUSTOMER_LIMIT_CONFIG_NOT_EXISTS);\n        }\n        return limitConfigDO;\n    }\n\n    /**\n     * 校验入参的用户和部门\n     *\n     * @param userIds 用户 ids\n     * @param deptIds 部门 ids\n     */\n    private void validateUserAndDept(Collection<Long> userIds, Collection<Long> deptIds) {\n        deptApi.validateDeptList(deptIds).checkError();\n        adminUserApi.validateUserList(userIds).checkError();\n    }\n\n    @Override\n    public List<CrmCustomerLimitConfigDO> getCustomerLimitConfigListByUserId(Integer type, Long userId) {\n        AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData();\n        Assert.notNull(user, \"用户({})不存在\", userId);\n        return customerLimitConfigMapper.selectListByTypeAndUserIdAndDeptId(type, userId, user.getDeptId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.customer;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;\n\nimport javax.validation.Valid;\n\n/**\n * 客户公海配置 Service 接口\n *\n * @author Wanwan\n */\npublic interface CrmCustomerPoolConfigService {\n\n    /**\n     * 获得客户公海配置\n     *\n     * @return 客户公海配置\n     */\n    CrmCustomerPoolConfigDO getCustomerPoolConfig();\n\n    /**\n     * 保存客户公海配置\n     *\n     * @param saveReqVO 更新信息\n     */\n    void saveCustomerPoolConfig(@Valid CrmCustomerPoolConfigSaveReqVO saveReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.customer;\n\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerPoolConfigMapper;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport javax.annotation.Resource;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\n\n/**\n * 客户公海配置 Service 实现类\n *\n * @author Wanwan\n */\n@Service\n@Validated\npublic class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigService {\n\n    @Resource\n    private CrmCustomerPoolConfigMapper customerPoolConfigMapper;\n\n    @Override\n    public CrmCustomerPoolConfigDO getCustomerPoolConfig() {\n        return customerPoolConfigMapper.selectOne();\n    }\n\n    @Override\n    @LogRecord(type = CRM_CUSTOMER_POOL_CONFIG_TYPE, subType = CRM_CUSTOMER_POOL_CONFIG_SUB_TYPE, bizNo = \"{{#poolConfigId}}\",\n            success = CRM_CUSTOMER_POOL_CONFIG_SUCCESS)\n    public void saveCustomerPoolConfig(CrmCustomerPoolConfigSaveReqVO saveReqVO) {\n        // 1. 存在，则进行更新\n        CrmCustomerPoolConfigDO dbConfig = getCustomerPoolConfig();\n        CrmCustomerPoolConfigDO poolConfig = BeanUtils.toBean(saveReqVO, CrmCustomerPoolConfigDO.class);\n        if (Objects.nonNull(dbConfig)) {\n            customerPoolConfigMapper.updateById(poolConfig.setId(dbConfig.getId()));\n            // 记录操作日志上下文\n            LogRecordContext.putVariable(\"isPoolConfigUpdate\", Boolean.TRUE);\n            LogRecordContext.putVariable(\"poolConfigId\", poolConfig.getId());\n            return;\n        }\n\n        // 2. 不存在，则进行插入\n        customerPoolConfigMapper.insert(poolConfig);\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"isPoolConfigUpdate\", Boolean.FALSE);\n        LogRecordContext.putVariable(\"poolConfigId\", poolConfig.getId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.customer;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;\n\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 客户 Service 接口\n *\n * @author Wanwan\n */\npublic interface CrmCustomerService {\n\n    /**\n     * 创建客户\n     *\n     * @param createReqVO 创建信息\n     * @param userId      用户编号\n     * @return 编号\n     */\n    Long createCustomer(@Valid CrmCustomerSaveReqVO createReqVO, Long userId);\n\n    /**\n     * 更新客户\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCustomer(@Valid CrmCustomerSaveReqVO updateReqVO);\n\n    /**\n     * 更新客户的跟进状态\n     *\n     * @param id         编号\n     * @param dealStatus 跟进状态\n     */\n    void updateCustomerDealStatus(Long id, Boolean dealStatus);\n\n    /**\n     * 更新客户相关的跟进信息\n     *\n     * @param id                 编号\n     * @param contactNextTime    下次联系时间\n     * @param contactLastContent 最后联系内容\n     */\n    void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);\n\n    /**\n     * 删除客户\n     *\n     * @param id 编号\n     */\n    void deleteCustomer(Long id);\n\n    /**\n     * 获得客户\n     *\n     * @param id 编号\n     * @return 客户\n     */\n    CrmCustomerDO getCustomer(Long id);\n\n    /**\n     * 获得客户列表\n     *\n     * @param ids 客户编号数组\n     * @return 客户列表\n     * @author ljlleo\n     */\n    List<CrmCustomerDO> getCustomerList(Collection<Long> ids);\n\n    /**\n     * 获得客户 Map\n     *\n     * @param ids 客户编号数组\n     * @return 客户 Map\n     */\n    default Map<Long, CrmCustomerDO> getCustomerMap(Collection<Long> ids) {\n        return convertMap(getCustomerList(ids), CrmCustomerDO::getId);\n    }\n\n    /**\n     * 获得客户分页\n     *\n     * @param pageReqVO 分页查询\n     * @param userId    用户编号\n     * @return 客户分页\n     */\n    PageResult<CrmCustomerDO> getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId);\n\n    /**\n     * 获得放入公海提醒的客户分页\n     *\n     * @param pageVO 分页查询\n     * @param userId 用户编号\n     * @return 客户分页\n     */\n    PageResult<CrmCustomerDO> getPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, Long userId);\n\n    /**\n     * 获得待进入公海的客户数量\n     *\n     * @param userId 用户编号\n     * @return 提醒数量\n     */\n    Long getPutPoolRemindCustomerCount(Long userId);\n\n    /**\n     * 获得今日需联系客户数量\n     *\n     * @param userId 用户编号\n     * @return 提醒数量\n     */\n    Long getTodayContactCustomerCount(Long userId);\n\n    /**\n     * 获得分配给我的客户数量\n     *\n     * @param userId 用户编号\n     * @return 提醒数量\n     */\n    Long getFollowCustomerCount(Long userId);\n\n    /**\n     * 校验客户是否存在\n     *\n     * @param id 编号\n     */\n    void validateCustomer(Long id);\n\n    /**\n     * 客户转移\n     *\n     * @param reqVO  请求\n     * @param userId 用户编号\n     */\n    void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId);\n\n    /**\n     * 锁定/解锁客户\n     *\n     * @param lockReqVO 更新信息\n     * @param userId    用户编号\n     */\n    void lockCustomer(@Valid CrmCustomerLockReqVO lockReqVO, Long userId);\n\n    /**\n     * 创建客户\n     *\n     * @param customerCreateReq 请求信息\n     * @param userId            用户编号\n     * @return 客户列表\n     */\n    Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId);\n\n    /**\n     * 批量导入客户\n     *\n     * @param importCustomers 导入客户列表\n     * @param importReqVO     请求\n     * @return 导入结果\n     */\n    CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, CrmCustomerImportReqVO importReqVO);\n\n    // ==================== 公海相关操作 ====================\n\n    /**\n     * 客户放入公海\n     *\n     * @param id 客户编号\n     */\n    void putCustomerPool(Long id);\n\n    /**\n     * 领取公海客户\n     *\n     * @param ids         要领取的客户编号数组\n     * @param ownerUserId 负责人\n     * @param isReceive   是/否领取；true - 领取；false - 分配\n     */\n    void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive);\n\n    /**\n     * 【系统】客户自动掉入公海\n     *\n     * @return 掉入公海数量\n     */\n    int autoPutCustomerPool();\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.customer;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactService;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.service.impl.DiffParseFunction;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\nimport static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;\nimport static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT;\nimport static java.util.Collections.singletonList;\n\n/**\n * 客户 Service 实现类\n *\n * @author Wanwan\n */\n@Service\n@Slf4j\n@Validated\npublic class CrmCustomerServiceImpl implements CrmCustomerService {\n\n    @Resource\n    private CrmCustomerMapper customerMapper;\n\n    @Resource\n    private CrmPermissionService permissionService;\n    @Resource\n    private CrmCustomerLimitConfigService customerLimitConfigService;\n    @Resource\n    @Lazy\n    private CrmCustomerPoolConfigService customerPoolConfigService;\n    @Resource\n    @Lazy\n    private CrmContactService contactService;\n    @Resource\n    @Lazy\n    private CrmBusinessService businessService;\n    @Resource\n    @Lazy\n    private CrmContractService contractService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = \"{{#customer.id}}\",\n            success = CRM_CUSTOMER_CREATE_SUCCESS)\n    public Long createCustomer(CrmCustomerSaveReqVO createReqVO, Long userId) {\n        createReqVO.setId(null);\n        // 1. 校验拥有客户是否到达上限\n        validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1);\n\n        // 2. 插入客户\n        CrmCustomerDO customer = initCustomer(createReqVO, userId);\n        customerMapper.insert(customer);\n\n        // 3. 创建数据权限\n        permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())\n                .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人\n\n        // 4. 记录操作日志上下文\n        LogRecordContext.putVariable(\"customer\", customer);\n        return customer.getId();\n    }\n\n    /**\n     * 初始化客户的通用字段\n     *\n     * @param customer    客户信息\n     * @param ownerUserId 负责人编号\n     * @return 客户信息 DO\n     */\n    private static CrmCustomerDO initCustomer(Object customer, Long ownerUserId) {\n        return BeanUtils.toBean(customer, CrmCustomerDO.class).setOwnerUserId(ownerUserId)\n                .setOwnerTime(LocalDateTime.now());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_CUSTOMER_UPDATE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#updateReqVO.id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateCustomer(CrmCustomerSaveReqVO updateReqVO) {\n        Assert.notNull(updateReqVO.getId(), \"客户编号不能为空\");\n        updateReqVO.setOwnerUserId(null);  // 更新的时候，要把 updateReqVO 负责人设置为空，避免修改\n        // 1. 校验存在\n        CrmCustomerDO oldCustomer = validateCustomerExists(updateReqVO.getId());\n\n        // 2. 更新客户\n        CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class);\n        customerMapper.updateById(updateObj);\n\n        // 3. 记录操作日志上下文\n        updateReqVO.setOwnerUserId(oldCustomer.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况\n        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomer, CrmCustomerSaveReqVO.class));\n        LogRecordContext.putVariable(\"customerName\", oldCustomer.getName());\n    }\n\n    @Override\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateCustomerDealStatus(Long id, Boolean dealStatus) {\n        // 1.1 校验存在\n        CrmCustomerDO customer = validateCustomerExists(id);\n        // 1.2 校验是否重复操作\n        if (Objects.equals(customer.getDealStatus(), dealStatus)) {\n            throw exception(CUSTOMER_UPDATE_DEAL_STATUS_FAIL);\n        }\n\n        // 2. 更新客户的成交状态\n        customerMapper.updateById(new CrmCustomerDO().setId(id).setDealStatus(dealStatus));\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"customerName\", customer.getName());\n        LogRecordContext.putVariable(\"dealStatus\", dealStatus);\n    }\n\n    @Override\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_FOLLOW_UP_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CUSTOMER_FOLLOW_UP_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {\n        // 1.1 校验存在\n        CrmCustomerDO customer = validateCustomerExists(id);\n\n        // 2. 更新客户的跟进信息\n        customerMapper.updateById(new CrmCustomerDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)\n                .setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent));\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"customerName\", customer.getName());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CUSTOMER_DELETE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void deleteCustomer(Long id) {\n        // 1.1 校验存在\n        CrmCustomerDO customer = validateCustomerExists(id);\n        // 1.2 检查引用\n        validateCustomerReference(id);\n\n        // 2. 删除客户\n        customerMapper.deleteById(id);\n        // 3. 删除数据权限\n        permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);\n\n        // 4. 记录操作日志上下文\n        LogRecordContext.putVariable(\"customerName\", customer.getName());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = \"{{#reqVO.id}}\",\n            success = CRM_CUSTOMER_TRANSFER_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#reqVO.id\", level = CrmPermissionLevelEnum.OWNER)\n    public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) {\n        // 1.1 校验客户是否存在\n        CrmCustomerDO customer = validateCustomerExists(reqVO.getId());\n        // 1.2 校验拥有客户是否到达上限\n        validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1);\n        // 2.1 数据权限转移\n        permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CUSTOMER.getType(),\n                reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));\n        // 2.2 转移后重新设置负责人\n        customerMapper.updateById(new CrmCustomerDO().setId(reqVO.getId())\n                .setOwnerUserId(reqVO.getNewOwnerUserId()).setOwnerTime(LocalDateTime.now()));\n\n        // 2.3 同时转移\n        if (CollUtil.isNotEmpty(reqVO.getToBizTypes())) {\n            transfer(reqVO, userId);\n        }\n\n        // 3. 记录转移日志\n        LogRecordContext.putVariable(\"customer\", customer);\n    }\n\n    /**\n     * 转移客户时，需要额外有【联系人】【商机】【合同】\n     *\n     * @param reqVO  请求\n     * @param userId 用户编号\n     */\n    private void transfer(CrmCustomerTransferReqVO reqVO, Long userId) {\n        if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTACT.getType())) {\n            List<CrmContactDO> contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getId(), userId);\n            contactList.forEach(item -> contactService.transferContact(new CrmContactTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(),\n                    reqVO.getOldOwnerPermissionLevel()), userId));\n        }\n        if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_BUSINESS.getType())) {\n            List<CrmBusinessDO> businessList = businessService.getBusinessListByCustomerIdOwnerUserId(reqVO.getId(), userId);\n            businessList.forEach(item -> businessService.transferBusiness(new CrmBusinessTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(),\n                    reqVO.getOldOwnerPermissionLevel()), userId));\n        }\n        if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTRACT.getType())) {\n            List<CrmContractDO> contractList = contractService.getContractListByCustomerIdOwnerUserId(reqVO.getId(), userId);\n            contractList.forEach(item -> contractService.transferContract(new CrmContractTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(),\n                    reqVO.getOldOwnerPermissionLevel()), userId));\n        }\n    }\n\n    @Override\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_LOCK_SUB_TYPE, bizNo = \"{{#lockReqVO.id}}\",\n            success = CRM_CUSTOMER_LOCK_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#lockReqVO.id\", level = CrmPermissionLevelEnum.OWNER)\n    public void lockCustomer(CrmCustomerLockReqVO lockReqVO, Long userId) {\n        // 1.1 校验当前客户是否存在\n        CrmCustomerDO customer = validateCustomerExists(lockReqVO.getId());\n        // 1.2 校验当前是否重复操作锁定/解锁状态\n        if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) {\n            throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK);\n        }\n        // 1.3 校验锁定上限\n        if (lockReqVO.getLockStatus()) {\n            validateCustomerExceedLockLimit(userId);\n        }\n\n        // 2. 更新锁定状态\n        customerMapper.updateById(BeanUtils.toBean(lockReqVO, CrmCustomerDO.class));\n\n        // 3. 记录操作日志上下文\n        // tips: 因为这里使用的是老的状态所以记录时反着记录，也就是 lockStatus 为 true 那么就是解锁反之为锁定\n        LogRecordContext.putVariable(\"customer\", customer);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = \"{{#customer.id}}\",\n            success = CRM_CUSTOMER_CREATE_SUCCESS)\n    public Long createCustomer(CrmCustomerCreateReqBO createReqBO, Long userId) {\n        // 1. 插入客户\n        CrmCustomerDO customer = initCustomer(createReqBO, userId);\n        customerMapper.insert(customer);\n\n        // 2. 创建数据权限\n        permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())\n                .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"customer\", customer);\n        return customer.getId();\n    }\n\n    @Override\n    public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers,\n                                                      CrmCustomerImportReqVO importReqVO) {\n        // 校验非空\n        importCustomers = filterList(importCustomers, item -> Objects.nonNull(item.getName()));\n        if (CollUtil.isEmpty(importCustomers)) {\n            throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY);\n        }\n\n        // 逐条处理\n        CrmCustomerImportRespVO respVO = CrmCustomerImportRespVO.builder().createCustomerNames(new ArrayList<>())\n                .updateCustomerNames(new ArrayList<>()).failureCustomerNames(new LinkedHashMap<>()).build();\n        importCustomers.forEach(importCustomer -> {\n            // 校验，判断是否有不符合的原因\n            try {\n                validateCustomerForCreate(importCustomer);\n            } catch (ServiceException ex) {\n                respVO.getFailureCustomerNames().put(importCustomer.getName(), ex.getMessage());\n                return;\n            }\n            // 情况一：判断如果不存在，在进行插入\n            CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName());\n            if (existCustomer == null) {\n                // 1.1 插入客户信息\n                CrmCustomerDO customer = initCustomer(importCustomer, importReqVO.getOwnerUserId());\n                customerMapper.insert(customer);\n                respVO.getCreateCustomerNames().add(importCustomer.getName());\n                // 1.2 创建数据权限\n                if (importReqVO.getOwnerUserId() != null) {\n                    permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())\n                            .setBizId(customer.getId()).setUserId(importReqVO.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));\n                }\n                // 1.3 记录操作日志\n                getSelf().importCustomerLog(customer, false);\n                return;\n            }\n\n            // 情况二：如果存在，判断是否允许更新\n            if (!importReqVO.getUpdateSupport()) {\n                respVO.getFailureCustomerNames().put(importCustomer.getName(),\n                        StrUtil.format(CUSTOMER_NAME_EXISTS.getMsg(), importCustomer.getName()));\n                return;\n            }\n            // 2.1 更新客户信息\n            CrmCustomerDO updateCustomer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class)\n                    .setId(existCustomer.getId());\n            customerMapper.updateById(updateCustomer);\n            respVO.getUpdateCustomerNames().add(importCustomer.getName());\n            // 2.2 记录操作日志\n            getSelf().importCustomerLog(updateCustomer, true);\n        });\n        return respVO;\n    }\n\n    /**\n     * 记录导入客户时的操作日志\n     *\n     * @param customer 客户信息\n     * @param isUpdate 是否更新；true - 更新，false - 新增\n     */\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_IMPORT_SUB_TYPE, bizNo = \"{{#customer.id}}\",\n            success = CRM_CUSTOMER_IMPORT_SUCCESS)\n    public void importCustomerLog(CrmCustomerDO customer, boolean isUpdate) {\n        LogRecordContext.putVariable(\"customer\", customer);\n        LogRecordContext.putVariable(\"isUpdate\", isUpdate);\n    }\n\n    // ==================== 公海相关操作 ====================\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_POOL_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_CUSTOMER_POOL_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void putCustomerPool(Long id) {\n        // 1. 校验存在\n        CrmCustomerDO customer = customerMapper.selectById(id);\n        if (customer == null) {\n            throw exception(CUSTOMER_NOT_EXISTS);\n        }\n        // 1.2. 校验是否为公海数据\n        validateCustomerOwnerExists(customer, true);\n        // 1.3. 校验客户是否锁定\n        validateCustomerIsLocked(customer, true);\n\n        // 2. 客户放入公海\n        putCustomerPool(customer);\n\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"customerName\", customer.getName());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive) {\n        // 1.1 校验存在\n        List<CrmCustomerDO> customers = customerMapper.selectByIds(ids);\n        if (customers.size() != ids.size()) {\n            throw exception(CUSTOMER_NOT_EXISTS);\n        }\n        // 1.2 校验负责人是否存在\n        adminUserApi.validateUserList(singletonList(ownerUserId)).checkError();\n        // 1.3 校验状态\n        customers.forEach(customer -> {\n            // 校验是否已有负责人\n            validateCustomerOwnerExists(customer, false);\n            // 校验是否锁定\n            validateCustomerIsLocked(customer, false);\n            // 校验成交状态\n            validateCustomerDeal(customer);\n        });\n        // 1.4  校验负责人是否到达上限\n        validateCustomerExceedOwnerLimit(ownerUserId, customers.size());\n\n        // 2. 领取公海数据\n        List<CrmCustomerDO> updateCustomers = new ArrayList<>();\n        List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();\n        customers.forEach(customer -> {\n            // 2.1. 设置负责人\n            updateCustomers.add(new CrmCustomerDO().setId(customer.getId())\n                    .setOwnerUserId(ownerUserId).setOwnerTime(LocalDateTime.now()));\n            // 2.2. 创建负责人数据权限\n            createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())\n                    .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));\n        });\n        // 2.2 更新客户负责人\n        customerMapper.updateBatch(updateCustomers);\n        // 2.3 创建负责人数据权限\n        permissionService.createPermissionBatch(createPermissions);\n        // TODO @芋艿：要不要处理关联的联系人？？？\n\n        // 3. 记录操作日志\n        AdminUserRespDTO user = null;\n        if (!isReceive) {\n            user = adminUserApi.getUser(ownerUserId).getCheckedData();\n        }\n        for (CrmCustomerDO customer : customers) {\n            getSelf().receiveCustomerLog(customer, user == null ? null : user.getNickname());\n        }\n    }\n\n    @Override\n    public int autoPutCustomerPool() {\n        CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();\n        if (poolConfig == null || !poolConfig.getEnabled()) {\n            return 0;\n        }\n        // 1. 获得需要放到的客户列表\n        List<CrmCustomerDO> customerList = customerMapper.selectListByAutoPool(poolConfig);\n        // 2. 逐个放入公海\n        int count = 0;\n        for (CrmCustomerDO customer : customerList) {\n            try {\n                getSelf().putCustomerPool(customer);\n                count++;\n            } catch (Throwable e) {\n                log.error(\"[autoPutCustomerPool][客户({}) 放入公海异常]\", customer.getId(), e);\n            }\n        }\n        return count;\n    }\n\n    @Transactional(rollbackFor = Exception.class) // 需要 protected 修饰，因为需要在事务中调用\n    protected void putCustomerPool(CrmCustomerDO customer) {\n        // 1. 设置负责人为 NULL\n        int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);\n        if (updateOwnerUserIncr == 0) {\n            throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);\n        }\n\n        // 2. 联系人的负责人，也要设置为 null。因为：因为领取后，负责人也要关联过来，这块和 receiveCustomer 是对应的\n        contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);\n\n        // 3. 删除负责人数据权限\n        // 注意：需要放在 contactService 后面，不然【客户】数据权限已经被删除，无法操作！\n        permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),\n                CrmPermissionLevelEnum.OWNER.getLevel());\n    }\n\n    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = \"{{#customer.id}}\",\n            success = CRM_CUSTOMER_RECEIVE_SUCCESS)\n    public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) {\n        // 记录操作日志上下文\n        LogRecordContext.putVariable(\"customer\", customer);\n        LogRecordContext.putVariable(\"ownerUserName\", ownerUserName);\n    }\n\n    //======================= 查询相关 =======================\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#id\", level = CrmPermissionLevelEnum.READ)\n    public CrmCustomerDO getCustomer(Long id) {\n        return customerMapper.selectById(id);\n    }\n\n    @Override\n    public List<CrmCustomerDO> getCustomerList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return customerMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<CrmCustomerDO> getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId) {\n        return customerMapper.selectPage(pageReqVO, userId);\n    }\n\n    @Override\n    public PageResult<CrmCustomerDO> getPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, Long userId) {\n        CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();\n        if (ObjUtil.isNull(poolConfig)\n                || Boolean.FALSE.equals(poolConfig.getEnabled())\n                || Boolean.FALSE.equals(poolConfig.getNotifyEnabled())) {\n            return PageResult.empty();\n        }\n        return customerMapper.selectPutPoolRemindCustomerPage(pageVO, poolConfig, userId);\n    }\n\n    @Override\n    public Long getPutPoolRemindCustomerCount(Long userId) {\n        CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();\n        if (ObjUtil.isNull(poolConfig)\n                || Boolean.FALSE.equals(poolConfig.getEnabled())\n                || Boolean.FALSE.equals(poolConfig.getNotifyEnabled())) {\n            return 0L;\n        }\n        CrmCustomerPageReqVO pageVO = new CrmCustomerPageReqVO()\n                .setPool(null)\n                .setContactStatus(CrmCustomerPageReqVO.CONTACT_TODAY)\n                .setSceneType(CrmSceneTypeEnum.OWNER.getType());\n        return customerMapper.selectPutPoolRemindCustomerCount(pageVO, poolConfig, userId);\n    }\n\n    @Override\n    public Long getTodayContactCustomerCount(Long userId) {\n        return customerMapper.selectCountByTodayContact(userId);\n    }\n\n    @Override\n    public Long getFollowCustomerCount(Long userId) {\n        return customerMapper.selectCountByFollow(userId);\n    }\n\n    // ======================= 校验相关 =======================\n\n    private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) {\n        // 校验客户名称不能为空\n        if (StrUtil.isEmptyIfStr(importCustomer.getName())) {\n            throw exception(CUSTOMER_CREATE_NAME_NOT_NULL);\n        }\n    }\n\n    /**\n     * 校验客户是否被引用\n     *\n     * @param id 客户编号\n     */\n    private void validateCustomerReference(Long id) {\n        if (contactService.getContactCountByCustomerId(id) > 0) {\n            throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTACT.getName());\n        }\n        if (businessService.getBusinessCountByCustomerId(id) > 0) {\n            throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_BUSINESS.getName());\n        }\n        if (contractService.getContractCountByCustomerId(id) > 0) {\n            throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTRACT.getName());\n        }\n    }\n\n    /**\n     * 校验客户是否存在\n     *\n     * @param id 客户 id\n     */\n    @Override\n    public void validateCustomer(Long id) {\n        validateCustomerExists(id);\n    }\n\n    private void validateCustomerOwnerExists(CrmCustomerDO customer, Boolean pool) {\n        if (customer == null) { // 防御一下\n            throw exception(CUSTOMER_NOT_EXISTS);\n        }\n        // 校验是否为公海数据\n        if (pool && customer.getOwnerUserId() == null) {\n            throw exception(CUSTOMER_IN_POOL, customer.getName());\n        }\n        // 负责人已存在\n        if (!pool && customer.getOwnerUserId() != null) {\n            throw exception(CUSTOMER_OWNER_EXISTS, customer.getName());\n        }\n    }\n\n    private CrmCustomerDO validateCustomerExists(Long id) {\n        CrmCustomerDO customerDO = customerMapper.selectById(id);\n        if (customerDO == null) {\n            throw exception(CUSTOMER_NOT_EXISTS);\n        }\n        return customerDO;\n    }\n\n    private void validateCustomerIsLocked(CrmCustomerDO customer, Boolean pool) {\n        if (customer.getLockStatus()) {\n            throw exception(pool ? CUSTOMER_LOCKED_PUT_POOL_FAIL : CUSTOMER_LOCKED, customer.getName());\n        }\n    }\n\n    private void validateCustomerDeal(CrmCustomerDO customer) {\n        if (customer.getDealStatus()) {\n            throw exception(CUSTOMER_ALREADY_DEAL);\n        }\n    }\n\n    /**\n     * 校验用户拥有的客户数量，是否到达上限\n     *\n     * @param userId   用户编号\n     * @param newCount 附加数量\n     */\n    private void validateCustomerExceedOwnerLimit(Long userId, int newCount) {\n        List<CrmCustomerLimitConfigDO> limitConfigs = customerLimitConfigService.getCustomerLimitConfigListByUserId(\n                CUSTOMER_OWNER_LIMIT.getType(), userId);\n        if (CollUtil.isEmpty(limitConfigs)) {\n            return;\n        }\n        Long ownerCount = customerMapper.selectCountByDealStatusAndOwnerUserId(null, userId);\n        Long dealOwnerCount = customerMapper.selectCountByDealStatusAndOwnerUserId(true, userId);\n        limitConfigs.forEach(limitConfig -> {\n            long nowCount = limitConfig.getDealCountEnabled() ? ownerCount : ownerCount - dealOwnerCount;\n            if (nowCount + newCount > limitConfig.getMaxCount()) {\n                throw exception(CUSTOMER_OWNER_EXCEED_LIMIT);\n            }\n        });\n    }\n\n    /**\n     * 校验用户锁定的客户数量，是否到达上限\n     *\n     * @param userId 用户编号\n     */\n    private void validateCustomerExceedLockLimit(Long userId) {\n        List<CrmCustomerLimitConfigDO> limitConfigs = customerLimitConfigService.getCustomerLimitConfigListByUserId(\n                CUSTOMER_LOCK_LIMIT.getType(), userId);\n        if (CollUtil.isEmpty(limitConfigs)) {\n            return;\n        }\n        Long lockCount = customerMapper.selectCountByLockStatusAndOwnerUserId(true, userId);\n        Integer maxCount = CollectionUtils.getMaxValue(limitConfigs, CrmCustomerLimitConfigDO::getMaxCount);\n        assert maxCount != null;\n        if (lockCount >= maxCount) {\n            throw exception(CUSTOMER_LOCK_EXCEED_LIMIT);\n        }\n    }\n\n    /**\n     * 获得自身的代理对象，解决 AOP 生效问题\n     *\n     * @return 自己\n     */\n    private CrmCustomerServiceImpl getSelf() {\n        return SpringUtil.getBean(getClass());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java",
    "content": "package cn.iocoder.yudao.module.crm.service.customer.bo;\n\nimport cn.iocoder.yudao.framework.common.validation.Mobile;\nimport cn.iocoder.yudao.framework.common.validation.Telephone;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.Size;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n/**\n * 客户创建 Create Req BO\n *\n * @author HUIHUI\n */\n@Data\npublic class CrmCustomerCreateReqBO {\n\n    /**\n     * 客户名称\n     */\n    @NotEmpty(message = \"客户名称不能为空\")\n    private String name;\n    /**\n     * 跟进状态\n     */\n    private Boolean followUpStatus;\n    /**\n     * 锁定状态\n     */\n    private Boolean lockStatus;\n    /**\n     * 成交状态\n     */\n    private Boolean dealStatus;\n    /**\n     * 所属行业\n     *\n     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY}\n     */\n    private Integer industryId;\n    /**\n     * 客户等级\n     *\n     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL}\n     */\n    private Integer level;\n    /**\n     * 客户来源\n     *\n     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE}\n     */\n    private Integer source;\n\n    /**\n     * 手机\n     */\n    @Mobile\n    private String mobile;\n    /**\n     * 电话\n     */\n    @Telephone\n    private String telephone;\n    /**\n     * QQ\n     */\n    private String qq;\n    /**\n     * wechat\n     */\n    private String wechat;\n\n    /**\n     * 邮箱\n     */\n    @Email(message = \"邮箱格式不正确\")\n    private String email;\n\n    /**\n     * 客户描述\n     */\n    @Size(max = 4096, message = \"客户描述长度不能超过 4096 个字符\")\n    private String description;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 负责人的用户编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long ownerUserId;\n    /**\n     * 所在地\n     *\n     * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段\n     */\n    private Integer areaId;\n    /**\n     * 详细地址\n     */\n    private String detailAddress;\n\n    /**\n     * 最后跟进时间\n     */\n    private LocalDateTime contactLastTime;\n    /**\n     * 最后跟进内容\n     */\n    private String contactLastContent;\n    /**\n     * 下次联系时间\n     */\n    private LocalDateTime contactNextTime;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.followup;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;\nimport cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 跟进记录 Service 接口\n *\n * @author 芋道源码\n */\npublic interface CrmFollowUpRecordService {\n\n    /**\n     * 创建跟进记录 (数据权限基于 bizType、 bizId)\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO createReqVO);\n\n    /**\n     * 创建更进\n     *\n     * @param list 请求\n     */\n    void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> list);\n\n    /**\n     * 删除跟进记录 (数据权限基于 bizType、 bizId)\n     *\n     * @param id     编号\n     * @param userId 用户编号\n     */\n    void deleteFollowUpRecord(Long id, Long userId);\n\n    /**\n     * 删除跟进\n     *\n     * @param bizType 模块类型\n     * @param bizId   模块数据编号\n     */\n    void deleteFollowUpRecordByBiz(Integer bizType, Long bizId);\n\n    /**\n     * 获得跟进记录\n     *\n     * @param id 编号\n     * @return 跟进记录\n     */\n    CrmFollowUpRecordDO getFollowUpRecord(Long id);\n\n    /**\n     * 获得跟进记录分页 (数据权限基于 bizType、 bizId)\n     *\n     * @param pageReqVO 分页查询\n     * @return 跟进记录分页\n     */\n    PageResult<CrmFollowUpRecordDO> getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO);\n\n    /**\n     * 获取跟进记录\n     *\n     * @param bizType 模块类型\n     * @param bizIds  模块数据编号\n     * @return 跟进列表\n     */\n    List<CrmFollowUpRecordDO> getFollowUpRecordByBiz(Integer bizType, Collection<Long> bizIds);\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.followup;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.followup.CrmFollowUpRecordMapper;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport cn.iocoder.yudao.module.crm.service.clue.CrmClueService;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactService;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;\nimport cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_DELETE_DENIED;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;\n\n/**\n * 跟进记录 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {\n\n    @Resource\n    private CrmFollowUpRecordMapper crmFollowUpRecordMapper;\n\n    @Resource\n    @Lazy\n    private CrmPermissionService permissionService;\n    @Resource\n    @Lazy\n    private CrmBusinessService businessService;\n    @Resource\n    @Lazy\n    private CrmClueService clueService;\n    @Resource\n    @Lazy\n    private CrmContactService contactService;\n    @Resource\n    @Lazy\n    private CrmContractService contractService;\n    @Resource\n    @Lazy\n    private CrmCustomerService customerService;\n\n    @Override\n    @CrmPermission(bizTypeValue = \"#createReqVO.bizType\", bizId = \"#createReqVO.bizId\", level = CrmPermissionLevelEnum.WRITE)\n    public Long createFollowUpRecord(CrmFollowUpRecordSaveReqVO createReqVO) {\n        // 1. 创建更进记录\n        CrmFollowUpRecordDO record = BeanUtils.toBean(createReqVO, CrmFollowUpRecordDO.class);\n        crmFollowUpRecordMapper.insert(record);\n\n        // 2. 更新 bizId 对应的记录\n        if (ObjUtil.equal(CrmBizTypeEnum.CRM_CUSTOMER.getType(), record.getBizType())) { // 更新客户跟进信息\n            customerService.updateCustomerFollowUp(record.getBizId(), record.getNextTime(), record.getContent());\n        }\n        if (ObjUtil.equal(CrmBizTypeEnum.CRM_BUSINESS.getType(), record.getBizType())) { // 更新商机跟进信息\n            businessService.updateBusinessFollowUp(record.getBizId(), record.getNextTime(), record.getContent());\n        }\n        if (ObjUtil.equal(CrmBizTypeEnum.CRM_CLUE.getType(), record.getBizType())) { // 更新线索跟进信息\n            clueService.updateClueFollowUp(record.getBizId(), record.getNextTime(), record.getContent());\n        }\n        if (ObjUtil.equal(CrmBizTypeEnum.CRM_CONTACT.getType(), record.getBizType())) { // 更新联系人跟进信息\n            contactService.updateContactFollowUp(record.getBizId(), record.getNextTime(), record.getContent());\n        }\n        if (ObjUtil.equal(CrmBizTypeEnum.CRM_CONTRACT.getType(), record.getBizType())) { // 更新合同跟进信息\n            contractService.updateContractFollowUp(record.getBizId(), record.getNextTime(), record.getContent());\n        }\n\n        // 3.1 更新 contactIds 对应的记录，只更新 nextTime\n        if (CollUtil.isNotEmpty(createReqVO.getContactIds())) {\n            contactService.updateContactContactNextTime(createReqVO.getContactIds(), createReqVO.getNextTime());\n        }\n        // 3.2 需要更新 businessIds 对应的记录，只更新 nextTime\n        if (CollUtil.isNotEmpty(createReqVO.getBusinessIds())) {\n            businessService.updateBusinessContactNextTime(createReqVO.getBusinessIds(), createReqVO.getNextTime());\n        }\n        return record.getId();\n    }\n\n    @Override\n    public void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> list) {\n        if (CollUtil.isEmpty(list)) {\n            return;\n        }\n        crmFollowUpRecordMapper.insertBatch(BeanUtils.toBean(list, CrmFollowUpRecordDO.class));\n    }\n\n    @Override\n    public void deleteFollowUpRecord(Long id, Long userId) {\n        // 校验存在\n        CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id);\n        // 校验权限\n        if (!permissionService.hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(), userId, CrmPermissionLevelEnum.OWNER)) {\n            throw exception(FOLLOW_UP_RECORD_DELETE_DENIED);\n        }\n\n        // 删除\n        crmFollowUpRecordMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteFollowUpRecordByBiz(Integer bizType, Long bizId) {\n        crmFollowUpRecordMapper.deleteByBiz(bizType, bizId);\n    }\n\n    private CrmFollowUpRecordDO validateFollowUpRecordExists(Long id) {\n        CrmFollowUpRecordDO followUpRecord = crmFollowUpRecordMapper.selectById(id);\n        if (followUpRecord == null) {\n            throw exception(FOLLOW_UP_RECORD_NOT_EXISTS);\n        }\n        return followUpRecord;\n    }\n\n    @Override\n    public CrmFollowUpRecordDO getFollowUpRecord(Long id) {\n        return crmFollowUpRecordMapper.selectById(id);\n    }\n\n    @Override\n    @CrmPermission(bizTypeValue = \"#pageReqVO.bizType\", bizId = \"#pageReqVO.bizId\", level = CrmPermissionLevelEnum.READ)\n    public PageResult<CrmFollowUpRecordDO> getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO) {\n        return crmFollowUpRecordMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<CrmFollowUpRecordDO> getFollowUpRecordByBiz(Integer bizType, Collection<Long> bizIds) {\n        return crmFollowUpRecordMapper.selectListByBiz(bizType, bizIds);\n    }\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java",
    "content": "package cn.iocoder.yudao.module.crm.service.followup.bo;\n\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 跟进信息 Create Req BO\n *\n * @author HUIHUI\n */\n@Data\npublic class CrmFollowUpCreateReqBO {\n\n    /**\n     * 数据类型\n     *\n     * 枚举 {@link CrmBizTypeEnum}\n     */\n    @NotNull(message = \"数据类型不能为空\")\n    private Integer bizType;\n    /**\n     * 数据编号\n     *\n     * 关联 {@link CrmBizTypeEnum} 对应模块 DO 的 id 字段\n     */\n    @NotNull(message = \"数据编号不能为空\")\n    private Long bizId;\n\n    /**\n     * 跟进类型\n     *\n     * 关联 {@link DictTypeConstants#CRM_FOLLOW_UP_TYPE} 字典\n     */\n    @NotNull(message = \"跟进类型不能为空\")\n    private Integer type;\n    /**\n     * 跟进内容\n     */\n    @NotEmpty(message = \"跟进内容不能为空\")\n    private String content;\n    /**\n     * 下次联系时间\n     */\n    @NotNull(message = \"下次联系时间不能为空\")\n    private LocalDateTime nextTime;\n\n    /**\n     * 图片\n     */\n    private List<String> picUrls;\n    /**\n     * 附件\n     */\n    private List<String> fileUrls;\n\n    /**\n     * 关联的商机编号数组\n     *\n     * 关联 {@link CrmBusinessDO#getId()}\n     */\n    private List<Long> businessIds;\n\n    /**\n     * 关联的联系人编号数组\n     *\n     * 关联 {@link CrmContactDO#getId()}\n     */\n    private List<Long> contactIds;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.permission;\n\n\nimport cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * crm 数据权限 Service 接口\n *\n * @author HUIHUI\n */\npublic interface CrmPermissionService {\n\n    /**\n     * 创建数据权限\n     *\n     * @param reqVO  创建信息\n     * @param userId 用户编号\n     */\n    void createPermission(CrmPermissionSaveReqVO reqVO, Long userId);\n\n    /**\n     * 创建数据权限\n     *\n     * @param createReqBO 创建信息\n     * @return 编号\n     */\n    Long createPermission(@Valid CrmPermissionCreateReqBO createReqBO);\n\n    /**\n     * 创建数据权限\n     *\n     * @param createReqBOs 创建信息\n     */\n    void createPermissionBatch(@Valid List<CrmPermissionCreateReqBO> createReqBOs);\n\n    /**\n     * 更新数据权限\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updatePermission(CrmPermissionUpdateReqVO updateReqVO);\n\n    /**\n     * 数据权限转移\n     *\n     * @param crmPermissionTransferReqBO 数据权限转移请求\n     */\n    void transferPermission(@Valid CrmPermissionTransferReqBO crmPermissionTransferReqBO);\n\n    /**\n     * 删除数据权限\n     *\n     * @param bizType 数据类型，关联 {@link CrmBizTypeEnum}\n     * @param bizId   数据编号，关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()\n     * @param level   数据权限级别，关联 {@link CrmPermissionLevelEnum}\n     */\n    void deletePermission(Integer bizType, Long bizId, Integer level);\n\n    /**\n     * 删除数据权限\n     *\n     * @param bizType 数据类型，关联 {@link CrmBizTypeEnum}\n     * @param bizId   数据编号，关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()\n     */\n    void deletePermission(Integer bizType, Long bizId);\n\n    /**\n     * 批量删除数据权限\n     *\n     * @param ids    权限编号\n     * @param userId 用户编号\n     */\n    void deletePermissionBatch(Collection<Long> ids, Long userId);\n\n    /**\n     * 删除指定用户数据权限\n     *\n     * @param id     权限编号\n     * @param userId 用户编号\n     */\n    void deleteSelfPermission(Long id, Long userId);\n\n    /**\n     * 获取数据权限列表，通过 数据类型 x 某个数据\n     *\n     * @param bizType 数据类型，关联 {@link CrmBizTypeEnum}\n     * @param bizId   数据编号，关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()\n     * @return Crm 数据权限列表\n     */\n    List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Long bizId);\n\n    /**\n     * 获取数据权限列表，通过 数据类型 x 某个数据\n     *\n     * @param bizType 数据类型，关联 {@link CrmBizTypeEnum}\n     * @param bizIds  数据编号，关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()\n     * @return Crm 数据权限列表\n     */\n    List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Collection<Long> bizIds);\n\n    /**\n     * 获取用户参与的模块数据列表\n     *\n     * @param bizType 模块类型\n     * @param userId  用户编号\n     * @return 模块数据列表\n     */\n    List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId);\n\n    /**\n     * 校验是否有指定数据的操作权限\n     *\n     * @param bizType 数据类型，关联 {@link CrmBizTypeEnum}\n     * @param bizId   数据编号，关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()\n     * @param userId  用户编号\n     * @param level   权限级别\n     * @return 是否有权限\n     */\n    boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.permission;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmPermissionMapper;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport cn.iocoder.yudao.module.crm.service.contact.CrmContactService;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;\nimport cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner;\n\n/**\n * CRM 数据权限 Service 接口实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class CrmPermissionServiceImpl implements CrmPermissionService {\n\n    @Resource\n    private CrmPermissionMapper permissionMapper;\n    @Resource\n    @Lazy // 解决依赖循环\n    private CrmContactService contactService;\n    @Resource\n    @Lazy // 解决依赖循环\n    private CrmBusinessService businessService;\n    @Resource\n    @Lazy // 解决依赖循环\n    private CrmContractService contractService;\n    @Resource\n    private AdminUserApi adminUserApi;\n\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @CrmPermission(bizTypeValue = \"#reqVO.bizType\", bizId = \"#reqVO.bizId\", level = CrmPermissionLevelEnum.OWNER)\n    public void createPermission(CrmPermissionSaveReqVO reqVO, Long userId) {\n        // 1. 创建数据权限\n        createPermission0(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class));\n\n        // 2. 处理【同时添加至】的权限\n        if (CollUtil.isEmpty(reqVO.getToBizTypes())) {\n            return;\n        }\n        List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();\n        buildContactPermissions(reqVO, userId, createPermissions);\n        buildBusinessPermissions(reqVO, userId, createPermissions);\n        buildContractPermissions(reqVO, userId, createPermissions);\n        if (CollUtil.isEmpty(createPermissions)) {\n            return;\n        }\n        createPermissionBatch(createPermissions);\n    }\n\n    /**\n     * 处理同时添加至联系人\n     *\n     * @param reqVO             请求\n     * @param userId            操作人\n     * @param createPermissions 待添加权限列表\n     */\n    private void buildContactPermissions(CrmPermissionSaveReqVO reqVO, Long userId, List<CrmPermissionCreateReqBO> createPermissions) {\n        // 1. 校验是否被同时添加\n        Integer type = CrmBizTypeEnum.CRM_CONTACT.getType();\n        if (!reqVO.getToBizTypes().contains(type)) {\n            return;\n        }\n        // 2. 添加数据权限\n        List<CrmContactDO> contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getBizId(), userId);\n        contactList.forEach(item -> createBizTypePermissions(reqVO, type, item.getId(), item.getName(), createPermissions));\n    }\n\n    /**\n     * 处理同时添加至商机\n     *\n     * @param reqVO             请求\n     * @param userId            操作人\n     * @param createPermissions 待添加权限列表\n     */\n    private void buildBusinessPermissions(CrmPermissionSaveReqVO reqVO, Long userId, List<CrmPermissionCreateReqBO> createPermissions) {\n        // 1. 校验是否被同时添加\n        Integer type = CrmBizTypeEnum.CRM_BUSINESS.getType();\n        if (!reqVO.getToBizTypes().contains(type)) {\n            return;\n        }\n        // 2. 添加数据权限\n        List<CrmBusinessDO> businessList = businessService.getBusinessListByCustomerIdOwnerUserId(reqVO.getBizId(), userId);\n        businessList.forEach(item -> createBizTypePermissions(reqVO, type, item.getId(), item.getName(), createPermissions));\n    }\n\n    /**\n     * 处理同时添加至合同\n     *\n     * @param reqVO             请求\n     * @param userId            操作人\n     * @param createPermissions 待添加权限列表\n     */\n    private void buildContractPermissions(CrmPermissionSaveReqVO reqVO, Long userId, List<CrmPermissionCreateReqBO> createPermissions) {\n        // 1. 校验是否被同时添加\n        Integer type = CrmBizTypeEnum.CRM_CONTRACT.getType();\n        if (!reqVO.getToBizTypes().contains(type)) {\n            return;\n        }\n        // 2. 添加数据权限\n        List<CrmContractDO> contractList = contractService.getContractListByCustomerIdOwnerUserId(reqVO.getBizId(), userId);\n        contractList.forEach(item -> createBizTypePermissions(reqVO, type, item.getId(), item.getName(), createPermissions));\n    }\n\n    private void createBizTypePermissions(CrmPermissionSaveReqVO reqVO, Integer type, Long bizId, String name,\n                                          List<CrmPermissionCreateReqBO> createPermissions) {\n        AdminUserRespDTO user = adminUserApi.getUser(reqVO.getUserId()).getCheckedData();\n        // 1. 需要考虑，被添加人，是不是应该有对应的权限了；\n        CrmPermissionDO permission = hasAnyPermission(type, bizId, reqVO.getUserId());\n        if (ObjUtil.isNotNull(permission)) {\n            throw exception(CRM_PERMISSION_CREATE_FAIL_EXISTS, user.getNickname(), CrmBizTypeEnum.getNameByType(type),\n                    name, CrmPermissionLevelEnum.getNameByLevel(permission.getLevel()));\n        }\n        // 2. 添加数据权限\n        createPermissions.add(new CrmPermissionCreateReqBO().setBizType(type)\n                .setBizId(bizId).setUserId(reqVO.getUserId()).setLevel(reqVO.getLevel()));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createPermission(CrmPermissionCreateReqBO createReqBO) {\n        return createPermission0(createReqBO);\n    }\n\n    private Long createPermission0(CrmPermissionCreateReqBO createReqBO) {\n        validatePermissionNotExists(Collections.singletonList(createReqBO));\n        // 1. 校验用户是否存在\n        adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId())).checkError();\n        // 2. 插入权限\n        CrmPermissionDO permission = BeanUtils.toBean(createReqBO, CrmPermissionDO.class);\n        permissionMapper.insert(permission);\n        return permission.getId();\n    }\n\n    @Override\n    public void createPermissionBatch(List<CrmPermissionCreateReqBO> createReqBOs) {\n        validatePermissionNotExists(createReqBOs);\n        // 1. 校验用户是否存在\n        adminUserApi.validateUserList(convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId)).checkError();\n\n        // 2. 创建\n        List<CrmPermissionDO> permissions = BeanUtils.toBean(createReqBOs, CrmPermissionDO.class);\n        permissionMapper.insertBatch(permissions);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePermission(CrmPermissionUpdateReqVO updateReqVO) {\n        // 1. 校验存在\n        validatePermissionExists(updateReqVO.getIds());\n        // 2. 更新\n        List<CrmPermissionDO> updateList = CollectionUtils.convertList(updateReqVO.getIds(),\n                id -> new CrmPermissionDO().setId(id).setLevel(updateReqVO.getLevel()));\n        permissionMapper.updateBatch(updateList);\n    }\n\n    private void validatePermissionExists(Collection<Long> ids) {\n        List<CrmPermissionDO> permissionList = permissionMapper.selectByIds(ids);\n        if (ObjUtil.notEqual(permissionList.size(), ids.size())) {\n            throw exception(CRM_PERMISSION_NOT_EXISTS);\n        }\n    }\n\n    private void validatePermissionNotExists(Collection<CrmPermissionCreateReqBO> createReqBOs) {\n        Set<Integer> bizTypes = convertSet(createReqBOs, CrmPermissionCreateReqBO::getBizType);\n        Set<Long> bizIds = convertSet(createReqBOs, CrmPermissionCreateReqBO::getBizId);\n        Set<Long> userIds = convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId);\n        Long count = permissionMapper.selectListByBiz(bizTypes, bizIds, userIds);\n        if (count > 0) {\n            throw exception(CRM_PERMISSION_CREATE_FAIL);\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void transferPermission(CrmPermissionTransferReqBO transferReqBO) {\n        // 1. 校验数据权限：是否是负责人，只有负责人才可以转移\n        CrmPermissionDO oldPermission = permissionMapper.selectByBizTypeAndBizIdByUserId(\n                transferReqBO.getBizType(), transferReqBO.getBizId(), transferReqBO.getUserId());\n        String bizTypeName = CrmBizTypeEnum.getNameByType(transferReqBO.getBizType());\n        if ((oldPermission == null || !isOwner(oldPermission.getLevel()))\n                && !CrmPermissionUtils.isCrmAdmin()) { // 并且不是超管\n            throw exception(CRM_PERMISSION_DENIED, bizTypeName);\n        }\n        // 1.1 校验转移对象是否已经是该负责人\n        if (oldPermission != null && ObjUtil.equal(transferReqBO.getNewOwnerUserId(), oldPermission.getUserId())) {\n            throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS, bizTypeName);\n        }\n        // 1.2 校验新负责人是否存在\n        adminUserApi.validateUserList(Collections.singletonList(transferReqBO.getNewOwnerUserId())).checkError();\n\n        // 2. 修改新负责人的权限\n        List<CrmPermissionDO> permissions = permissionMapper.selectByBizTypeAndBizId(\n                transferReqBO.getBizType(), transferReqBO.getBizId()); // 获得所有数据权限\n        CrmPermissionDO permission = CollUtil.findOne(permissions,\n                item -> ObjUtil.equal(item.getUserId(), transferReqBO.getNewOwnerUserId()));\n        if (permission == null) {\n            permissionMapper.insert(new CrmPermissionDO().setBizType(transferReqBO.getBizType())\n                    .setBizId(transferReqBO.getBizId()).setUserId(transferReqBO.getNewOwnerUserId())\n                    .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));\n        } else {\n            permissionMapper.updateById(new CrmPermissionDO().setId(permission.getId())\n                    .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));\n        }\n\n        // 3. 修改老负责人的权限\n        if (transferReqBO.getOldOwnerPermissionLevel() != null) {\n            permissionMapper.updateById(new CrmPermissionDO().setId(oldPermission.getId())\n                    .setLevel(transferReqBO.getOldOwnerPermissionLevel()));\n        } else {\n            permissionMapper.deleteById(oldPermission.getId());\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deletePermission(Integer bizType, Long bizId, Integer level) {\n        // 校验存在\n        List<CrmPermissionDO> permissions = permissionMapper.selectListByBizTypeAndBizIdAndLevel(\n                bizType, bizId, level);\n        if (CollUtil.isEmpty(permissions)) {\n            throw exception(CRM_PERMISSION_NOT_EXISTS);\n        }\n\n        // 删除数据权限\n        permissionMapper.deleteByIds(convertSet(permissions, CrmPermissionDO::getId));\n    }\n\n    @Override\n    public void deletePermission(Integer bizType, Long bizId) {\n        int deletedCount = permissionMapper.deletePermission(bizType, bizId);\n        if (deletedCount == 0) {\n            throw exception(CRM_PERMISSION_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public void deletePermissionBatch(Collection<Long> ids, Long userId) {\n        List<CrmPermissionDO> permissions = permissionMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(permissions)) {\n            throw exception(CRM_PERMISSION_NOT_EXISTS);\n        }\n        // 校验：数据权限的模块数据编号是一致的不可能存在两个\n        if (convertSet(permissions, CrmPermissionDO::getBizId).size() > 1) {\n            throw exception(CRM_PERMISSION_DELETE_FAIL);\n        }\n        // 校验操作人是否为负责人\n        CrmPermissionDO permission = permissionMapper.selectByBizAndUserId(permissions.get(0).getBizType(), permissions.get(0).getBizId(), userId);\n        if (permission == null) {\n            throw exception(CRM_PERMISSION_DELETE_DENIED);\n        }\n        if (!CrmPermissionLevelEnum.isOwner(permission.getLevel())) {\n            throw exception(CRM_PERMISSION_DELETE_DENIED);\n        }\n\n        // 删除数据权限\n        permissionMapper.deleteByIds(ids);\n    }\n\n    @Override\n    public void deleteSelfPermission(Long id, Long userId) {\n        // 校验数据存在且是自己\n        CrmPermissionDO permission = permissionMapper.selectByIdAndUserId(id, userId);\n        if (permission == null) {\n            throw exception(CRM_PERMISSION_NOT_EXISTS);\n        }\n        // 校验是否是负责人\n        if (CrmPermissionLevelEnum.isOwner(permission.getLevel())) {\n            throw exception(CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER);\n        }\n\n        // 删除\n        permissionMapper.deleteById(id);\n    }\n\n    @Override\n    public List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Long bizId) {\n        return permissionMapper.selectByBizTypeAndBizId(bizType, bizId);\n    }\n\n    @Override\n    public List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Collection<Long> bizIds) {\n        return permissionMapper.selectByBizTypeAndBizIds(bizType, bizIds);\n    }\n\n    @Override\n    public List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId) {\n        return permissionMapper.selectListByBizTypeAndUserId(bizType, userId);\n    }\n\n    @Override\n    public boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level) {\n        List<CrmPermissionDO> permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId);\n        return anyMatch(permissionList, permission ->\n                ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), level.getLevel()));\n    }\n\n    public CrmPermissionDO hasAnyPermission(Integer bizType, Long bizId, Long userId) {\n        List<CrmPermissionDO> permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId);\n        return findFirst(permissionList, permission -> ObjUtil.equal(permission.getUserId(), userId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionCreateReqBO.java",
    "content": "package cn.iocoder.yudao.module.crm.service.permission.bo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * crm 数据权限 Create Req BO\n *\n * @author HUIHUI\n */\n@Data\npublic class CrmPermissionCreateReqBO {\n\n    /**\n     * 当前登录用户编号\n     */\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    /**\n     * Crm 类型\n     */\n    @NotNull(message = \"Crm 类型不能为空\")\n    @InEnum(CrmBizTypeEnum.class)\n    private Integer bizType;\n    /**\n     * 数据编号\n     */\n    @NotNull(message = \"Crm 数据编号不能为空\")\n    private Long bizId;\n\n    /**\n     * 权限级别\n     */\n    @NotNull(message = \"权限级别不能为空\")\n    @InEnum(CrmPermissionLevelEnum.class)\n    private Integer level;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionTransferReqBO.java",
    "content": "package cn.iocoder.yudao.module.crm.service.permission.bo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 数据权限转移 Request BO\n *\n * @author HUIHUI\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CrmPermissionTransferReqBO {\n\n    /**\n     * 当前登录用户编号\n     */\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    /**\n     * CRM 类型\n     */\n    @NotNull(message = \"Crm 类型不能为空\")\n    @InEnum(CrmBizTypeEnum.class)\n    private Integer bizType;\n    /**\n     * 数据编号\n     */\n    @NotNull(message = \"CRM 数据编号不能为空\")\n    private Long bizId;\n\n    /**\n     * 新负责人的用户编号\n     */\n    @NotNull(message = \"新负责人的用户编号不能为空\")\n    private Long newOwnerUserId;\n\n    /**\n     * 老负责人加入团队后的权限级别。如果 null 说明移除\n     *\n     * 关联 {@link CrmPermissionLevelEnum}\n     */\n    private Integer oldOwnerPermissionLevel;\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.product;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * CRM 产品分类 Service 接口\n *\n * @author ZanGe丶\n */\npublic interface CrmProductCategoryService {\n\n    /**\n     * 创建产品分类\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProductCategory(@Valid CrmProductCategoryCreateReqVO createReqVO);\n\n    /**\n     * 更新产品分类\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProductCategory(@Valid CrmProductCategoryCreateReqVO updateReqVO);\n\n    /**\n     * 删除产品分类\n     *\n     * @param id 编号\n     */\n    void deleteProductCategory(Long id);\n\n    /**\n     * 获得产品分类\n     *\n     * @param id 编号\n     * @return 产品分类\n     */\n    CrmProductCategoryDO getProductCategory(Long id);\n\n    /**\n     * 获得产品分类列表\n     *\n     * @param listReqVO 列表请求\n     * @return 产品分类列表\n     */\n    List<CrmProductCategoryDO> getProductCategoryList(CrmProductCategoryListReqVO listReqVO);\n\n    /**\n     * 获得产品分类列表\n     *\n     * @param ids 编号数组\n     * @return 产品分类列表\n     */\n    List<CrmProductCategoryDO> getProductCategoryList(Collection<Long> ids);\n\n    /**\n     * 获得产品分类 Map\n     *\n     * @param ids 编号数组\n     * @return 产品分类 Map\n     */\n    default Map<Long, CrmProductCategoryDO> getProductCategoryMap(Collection<Long> ids) {\n        return convertMap(getProductCategoryList(ids), CrmProductCategoryDO::getId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.product;\n\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.product.CrmProductCategoryMapper;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO.PARENT_ID_NULL;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\n\n/**\n * CRM 产品分类 Service 实现类\n *\n * @author ZanGe丶\n */\n@Service\n@Validated\npublic class CrmProductCategoryServiceImpl implements CrmProductCategoryService {\n\n    @Resource(name = \"crmProductCategoryMapper\")\n    private CrmProductCategoryMapper productCategoryMapper;\n\n    @Resource\n    @Lazy // 延迟加载，解决循环依赖问题\n    private CrmProductService crmProductService;\n\n    @Override\n    @LogRecord(type = CRM_PRODUCT_CATEGORY_TYPE, subType = CRM_PRODUCT_CATEGORY_CREATE_SUB_TYPE, bizNo = \"{{#productCategoryId}}\",\n            success = CRM_PRODUCT_CATEGORY_CREATE_SUCCESS)\n    public Long createProductCategory(CrmProductCategoryCreateReqVO createReqVO) {\n        // 1.1 校验父分类存在\n        validateParentProductCategory(createReqVO.getParentId());\n        // 1.2 分类名称是否存在\n        validateProductNameExists(null, createReqVO.getParentId(), createReqVO.getName());\n\n        // 2. 插入分类\n        CrmProductCategoryDO category = BeanUtils.toBean(createReqVO, CrmProductCategoryDO.class);\n        productCategoryMapper.insert(category);\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"productCategoryId\", category.getId());\n        return category.getId();\n    }\n\n    @Override\n    @LogRecord(type = CRM_PRODUCT_CATEGORY_TYPE, subType = CRM_PRODUCT_CATEGORY_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_PRODUCT_CATEGORY_UPDATE_SUCCESS)\n    public void updateProductCategory(CrmProductCategoryCreateReqVO updateReqVO) {\n        // 1.1 校验存在\n        validateProductCategoryExists(updateReqVO.getId());\n        // 1.2 校验父分类存在\n        validateParentProductCategory(updateReqVO.getParentId());\n        // 1.3 分类名称是否存在\n        validateProductNameExists(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());\n\n        // 2. 更新分类\n        CrmProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, CrmProductCategoryDO.class);\n        productCategoryMapper.updateById(updateObj);\n    }\n\n    private void validateProductCategoryExists(Long id) {\n        if (productCategoryMapper.selectById(id) == null) {\n            throw exception(PRODUCT_CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    private void validateParentProductCategory(Long id) {\n        // 如果是根分类，无需验证\n        if (Objects.equals(id, PARENT_ID_NULL)) {\n            return;\n        }\n        // 父分类不存在\n        CrmProductCategoryDO category = productCategoryMapper.selectById(id);\n        if (category == null) {\n            throw exception(PRODUCT_CATEGORY_PARENT_NOT_EXISTS);\n        }\n        // 父分类不能是二级分类\n        if (!Objects.equals(category.getParentId(), PARENT_ID_NULL)) {\n            throw exception(PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL);\n        }\n    }\n\n    private void validateProductNameExists(Long id, Long parentId, String name) {\n        CrmProductCategoryDO category = productCategoryMapper.selectByParentIdAndName(parentId, name);\n        if (category == null\n            || category.getId().equals(id)) {\n            return;\n        }\n        throw exception(PRODUCT_CATEGORY_EXISTS);\n    }\n\n    @Override\n    @LogRecord(type = CRM_PRODUCT_CATEGORY_TYPE, subType = CRM_PRODUCT_CATEGORY_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_PRODUCT_CATEGORY_DELETE_SUCCESS)\n    public void deleteProductCategory(Long id) {\n        // 1.1 校验存在\n        validateProductCategoryExists(id);\n        // 1.2 校验是否还有子分类\n        if (productCategoryMapper.selectCountByParentId(id) > 0) {\n            throw exception(PRODUCT_CATEGORY_EXISTS_CHILDREN);\n        }\n        // 1.3 校验是否被产品使用\n        if (crmProductService.getProductByCategoryId(id) > 0) {\n            throw exception(PRODUCT_CATEGORY_USED);\n        }\n        // 2. 删除\n        productCategoryMapper.deleteById(id);\n    }\n\n    @Override\n    public CrmProductCategoryDO getProductCategory(Long id) {\n        return productCategoryMapper.selectById(id);\n    }\n\n    @Override\n    public List<CrmProductCategoryDO> getProductCategoryList(CrmProductCategoryListReqVO listReqVO) {\n        return productCategoryMapper.selectList(listReqVO);\n    }\n\n    @Override\n    public List<CrmProductCategoryDO> getProductCategoryList(Collection<Long> ids) {\n        return productCategoryMapper.selectByIds(ids);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * CRM 产品 Service 接口\n *\n * @author ZanGe丶\n */\npublic interface CrmProductService {\n\n    /**\n     * 创建产品\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProduct(@Valid CrmProductSaveReqVO createReqVO);\n\n    /**\n     * 更新产品\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProduct(@Valid CrmProductSaveReqVO updateReqVO);\n\n    /**\n     * 删除产品\n     *\n     * @param id 编号\n     */\n    void deleteProduct(Long id);\n\n    /**\n     * 获得产品\n     *\n     * @param id 编号\n     * @return 产品\n     */\n    CrmProductDO getProduct(Long id);\n\n    /**\n     * 获得产品列表\n     *\n     * @param ids 编号\n     * @return 产品列表\n     */\n    List<CrmProductDO> getProductList(Collection<Long> ids);\n\n    /**\n     * 获得产品 Map\n     *\n     * @param ids 编号\n     * @return 产品 Map\n     */\n    default Map<Long, CrmProductDO> getProductMap(Collection<Long> ids) {\n        return convertMap(getProductList(ids), CrmProductDO::getId);\n    }\n\n    /**\n     * 获得产品分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 产品分页\n     */\n    PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO);\n\n    /**\n     * 获得产品数量\n     *\n     * @param categoryId 分类编号\n     * @return 产品\n     */\n    Long getProductByCategoryId(Long categoryId);\n\n    /**\n     * 获得指定状态的产品列表\n     *\n     * @param status 状态\n     * @return 产品列表\n     */\n    List<CrmProductDO> getProductListByStatus(Integer status);\n\n    /**\n     * 校验产品们的有效性\n     *\n     * @param ids 编号数组\n     * @return 产品列表\n     */\n    List<CrmProductDO> validProductList(Collection<Long> ids);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.product;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.product.CrmProductMapper;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.service.impl.DiffParseFunction;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport javax.annotation.Resource;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\n\n\n/**\n * CRM 产品 Service 实现类\n *\n * @author ZanGe丶\n */\n@Service\n@Validated\npublic class CrmProductServiceImpl implements CrmProductService {\n\n    @Resource(name = \"crmProductMapper\")\n    private CrmProductMapper productMapper;\n\n    @Resource\n    private CrmProductCategoryService productCategoryService;\n    @Resource\n    private CrmPermissionService permissionService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_PRODUCT_TYPE, subType = CRM_PRODUCT_CREATE_SUB_TYPE, bizNo = \"{{#productId}}\",\n            success = CRM_PRODUCT_CREATE_SUCCESS)\n    public Long createProduct(CrmProductSaveReqVO createReqVO) {\n        // 1. 校验产品\n        adminUserApi.validateUserList(Collections.singleton(createReqVO.getOwnerUserId())).checkError();\n        validateProductNoDuplicate(null, createReqVO.getNo());\n        validateProductCategoryExists(createReqVO.getCategoryId());\n\n        // 2. 插入产品\n        CrmProductDO product = BeanUtils.toBean(createReqVO, CrmProductDO.class);\n        productMapper.insert(product);\n\n        // 3. 插入数据权限\n        permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(product.getOwnerUserId())\n                .setBizType(CrmBizTypeEnum.CRM_PRODUCT.getType()).setBizId(product.getId())\n                .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));\n\n        // 4. 记录操作日志上下文\n        LogRecordContext.putVariable(\"productId\", product.getId());\n        return product.getId();\n    }\n\n    @Override\n    @LogRecord(type = CRM_PRODUCT_TYPE, subType = CRM_PRODUCT_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_PRODUCT_UPDATE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_PRODUCT, bizId = \"#updateReqVO.id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateProduct(CrmProductSaveReqVO updateReqVO) {\n        // 1. 校验产品\n        updateReqVO.setOwnerUserId(null); // 不修改负责人\n        CrmProductDO crmProductDO = validateProductExists(updateReqVO.getId());\n        validateProductNoDuplicate(updateReqVO.getId(), updateReqVO.getNo());\n        validateProductCategoryExists(updateReqVO.getCategoryId());\n\n        // 2. 更新产品\n        CrmProductDO updateObj = BeanUtils.toBean(updateReqVO, CrmProductDO.class);\n        productMapper.updateById(updateObj);\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(crmProductDO, CrmProductSaveReqVO.class));\n    }\n\n    private CrmProductDO validateProductExists(Long id) {\n        CrmProductDO product = productMapper.selectById(id);\n        if (product == null) {\n            throw exception(PRODUCT_NOT_EXISTS);\n        }\n        return product;\n    }\n\n    private void validateProductNoDuplicate(Long id, String no) {\n        CrmProductDO product = productMapper.selectByNo(no);\n        if (product == null\n                || product.getId().equals(id)) {\n            return;\n        }\n        throw exception(PRODUCT_NO_EXISTS);\n    }\n\n    private void validateProductCategoryExists(Long categoryId) {\n        CrmProductCategoryDO category = productCategoryService.getProductCategory(categoryId);\n        if (category == null) {\n            throw exception(PRODUCT_CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    @LogRecord(type = CRM_PRODUCT_TYPE, subType = CRM_PRODUCT_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_PRODUCT_DELETE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_PRODUCT, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void deleteProduct(Long id) {\n        // 校验存在\n        validateProductExists(id);\n        // 删除\n        productMapper.deleteById(id);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_PRODUCT, bizId = \"#id\", level = CrmPermissionLevelEnum.READ)\n    public CrmProductDO getProduct(Long id) {\n        return productMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO) {\n        return productMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public Long getProductByCategoryId(Long categoryId) {\n        return productMapper.selectCountByCategoryId(categoryId);\n    }\n\n    @Override\n    public List<CrmProductDO> getProductListByStatus(Integer status) {\n        return productMapper.selectListByStatus(status);\n    }\n\n    @Override\n    public List<CrmProductDO> validProductList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        List<CrmProductDO> list = productMapper.selectByIds(ids);\n        Map<Long, CrmProductDO> productMap = convertMap(list, CrmProductDO::getId);\n        for (Long id : ids) {\n            CrmProductDO product = productMap.get(id);\n            if (productMap.get(id) == null) {\n                throw exception(PRODUCT_NOT_EXISTS);\n            }\n            if (CrmProductStatusEnum.isDisable(product.getStatus())) {\n                throw exception(PRODUCT_NOT_ENABLE, product.getName());\n            }\n        }\n        return list;\n    }\n\n    @Override\n    public List<CrmProductDO> getProductList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return productMapper.selectByIds(ids);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.receivable;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * CRM 回款计划 Service 接口\n *\n * @author 芋道源码\n */\npublic interface CrmReceivablePlanService {\n\n    /**\n     * 创建回款计划\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createReceivablePlan(@Valid CrmReceivablePlanSaveReqVO createReqVO);\n\n    /**\n     * 更新回款计划\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateReceivablePlan(@Valid CrmReceivablePlanSaveReqVO updateReqVO);\n\n    /**\n     * 更新回款计划关联的回款编号\n     *\n     * @param id           编号\n     * @param receivableId 回款编号\n     */\n    void updateReceivablePlanReceivableId(Long id, Long receivableId);\n\n    /**\n     * 删除回款计划\n     *\n     * @param id 编号\n     */\n    void deleteReceivablePlan(Long id);\n\n    /**\n     * 获得回款计划\n     *\n     * @param id 编号\n     * @return 回款计划\n     */\n    CrmReceivablePlanDO getReceivablePlan(Long id);\n\n    /**\n     * 获得回款计划列表\n     *\n     * @param ids 编号\n     * @return 回款计划列表\n     */\n    List<CrmReceivablePlanDO> getReceivablePlanList(Collection<Long> ids);\n\n    /**\n     * 获得回款计划分页\n     *\n     * 数据权限：基于 {@link CrmReceivablePlanDO} 读取\n     *\n     * @param pageReqVO 分页查询\n     * @param userId    用户编号\n     * @return 回款计划分页\n     */\n    PageResult<CrmReceivablePlanDO> getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId);\n\n    /**\n     * 获得回款计划分页，基于指定客户\n     *\n     * 数据权限：基于 {@link CrmCustomerDO} 读取\n     *\n     * @param pageReqVO 分页查询\n     * @return 回款计划分页\n     */\n    PageResult<CrmReceivablePlanDO> getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO);\n\n    /**\n     * 获得待回款提醒数量\n     *\n     * @param userId 用户编号\n     * @return 提醒数量\n     */\n    Long getReceivablePlanRemindCount(Long userId);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.receivable;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.service.impl.DiffParseFunction;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PLAN_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PLAN_UPDATE_FAIL;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\n\n/**\n * 回款计划 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {\n\n    @Resource\n    private CrmReceivablePlanMapper receivablePlanMapper;\n\n    @Resource\n    private CrmContractService contractService;\n    @Resource\n    private CrmPermissionService permissionService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE, bizNo = \"{{#receivablePlan.id}}\",\n            success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS)\n    public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) {\n        // 1. 校验关联数据是否存在\n        validateRelationDataExists(createReqVO);\n\n        // 2. 插入回款计划\n        CrmReceivablePlanDO maxPeriodReceivablePlan = receivablePlanMapper.selectMaxPeriodByContractId(createReqVO.getContractId());\n        int period = maxPeriodReceivablePlan == null ? 1 : maxPeriodReceivablePlan.getPeriod() + 1;\n        CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class).setPeriod(period);\n        if (createReqVO.getReturnTime() != null && createReqVO.getRemindDays() != null) {\n            receivablePlan.setRemindTime(createReqVO.getReturnTime().minusDays(createReqVO.getRemindDays()));\n        }\n        receivablePlanMapper.insert(receivablePlan);\n\n        // 3. 创建数据权限\n        permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(createReqVO.getOwnerUserId())\n                .setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()).setBizId(receivablePlan.getId())\n                .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));\n\n        // 4. 记录操作日志上下文\n        LogRecordContext.putVariable(\"receivablePlan\", receivablePlan);\n        return receivablePlan.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = \"#updateReqVO.id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) {\n        updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null); // 防止修改这些字段\n        // 1.1 校验存在\n        validateRelationDataExists(updateReqVO);\n        // 1.2 校验关联数据是否存在\n        CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId());\n        // 1.3 如果已经有对应的回款，则不允许编辑\n        if (Objects.nonNull(oldReceivablePlan.getReceivableId())) {\n            throw exception(RECEIVABLE_PLAN_UPDATE_FAIL);\n        }\n\n        // 2. 更新回款计划\n        CrmReceivablePlanDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivablePlanDO.class);\n        if (updateReqVO.getReturnTime() != null && updateReqVO.getRemindDays() != null) {\n            updateObj.setRemindTime(updateReqVO.getReturnTime().minusDays(updateReqVO.getRemindDays()));\n        }\n        receivablePlanMapper.updateById(updateObj);\n\n        // 3. 记录操作日志上下文\n        updateReqVO.setOwnerUserId(oldReceivablePlan.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况\n        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivablePlan, CrmReceivablePlanSaveReqVO.class));\n        LogRecordContext.putVariable(\"receivablePlan\", oldReceivablePlan);\n    }\n\n    private void validateRelationDataExists(CrmReceivablePlanSaveReqVO reqVO) {\n        // 校验负责人存在\n        if (reqVO.getOwnerUserId() != null) {\n            adminUserApi.validateUser(reqVO.getOwnerUserId());\n        }\n        // 校验合同存在\n        if (reqVO.getContractId() != null) {\n            CrmContractDO contract = contractService.getContract(reqVO.getContractId());\n            reqVO.setCustomerId(contract.getCustomerId());\n        }\n    }\n\n    @Override\n    public void updateReceivablePlanReceivableId(Long id, Long receivableId) {\n        // 校验存在\n        validateReceivablePlanExists(id);\n        // 更新回款计划\n        receivablePlanMapper.updateById(new CrmReceivablePlanDO().setId(id).setReceivableId(receivableId));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_RECEIVABLE_PLAN_DELETE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void deleteReceivablePlan(Long id) {\n        // 1. 校验存在\n        CrmReceivablePlanDO receivablePlan = validateReceivablePlanExists(id);\n\n        // 2. 删除\n        receivablePlanMapper.deleteById(id);\n        // 3. 删除数据权限\n        permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), id);\n\n        // 4. 记录操作日志上下文\n        LogRecordContext.putVariable(\"receivablePlan\", receivablePlan);\n    }\n\n    private CrmReceivablePlanDO validateReceivablePlanExists(Long id) {\n        CrmReceivablePlanDO receivablePlan = receivablePlanMapper.selectById(id);\n        if (receivablePlan == null) {\n            throw exception(RECEIVABLE_PLAN_NOT_EXISTS);\n        }\n        return receivablePlan;\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = \"#id\", level = CrmPermissionLevelEnum.READ)\n    public CrmReceivablePlanDO getReceivablePlan(Long id) {\n        return receivablePlanMapper.selectById(id);\n    }\n\n    @Override\n    public List<CrmReceivablePlanDO> getReceivablePlanList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return ListUtil.empty();\n        }\n        return receivablePlanMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<CrmReceivablePlanDO> getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) {\n        return receivablePlanMapper.selectPage(pageReqVO, userId);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#pageReqVO.customerId\", level = CrmPermissionLevelEnum.READ)\n    public PageResult<CrmReceivablePlanDO> getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO) {\n        return receivablePlanMapper.selectPageByCustomerId(pageReqVO);\n    }\n\n    @Override\n    public Long getReceivablePlanRemindCount(Long userId) {\n        return receivablePlanMapper.selectReceivablePlanCountByRemind(userId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.receivable;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;\nimport javax.validation.Valid;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * CRM 回款 Service 接口\n *\n * @author 赤焰\n */\npublic interface CrmReceivableService {\n\n    /**\n     * 创建回款\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createReceivable(@Valid CrmReceivableSaveReqVO createReqVO);\n\n    /**\n     * 更新回款\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateReceivable(@Valid CrmReceivableSaveReqVO updateReqVO);\n\n    /**\n     * 更新回款流程审批结果\n     *\n     * @param id        回款编号\n     * @param bpmResult BPM 审批结果\n     */\n    void updateReceivableAuditStatus(Long id, Integer bpmResult);\n\n    /**\n     * 删除回款\n     *\n     * @param id 编号\n     */\n    void deleteReceivable(Long id);\n\n    /**\n     * 发起回款审批流程\n     *\n     * @param id     回款编号\n     * @param userId 用户编号\n     */\n    void submitReceivable(Long id, Long userId);\n\n    /**\n     * 获得回款\n     *\n     * @param id 编号\n     * @return 回款\n     */\n    CrmReceivableDO getReceivable(Long id);\n\n    /**\n     * 获得回款列表\n     *\n     * @param ids 编号\n     * @return 回款列表\n     */\n    List<CrmReceivableDO> getReceivableList(Collection<Long> ids);\n\n    /**\n     * 获得回款 Map\n     *\n     * @param ids 编号\n     * @return 回款 Map\n     */\n    default Map<Long, CrmReceivableDO> getReceivableMap(Collection<Long> ids) {\n        return convertMap(getReceivableList(ids), CrmReceivableDO::getId);\n    }\n\n    /**\n     * 获得回款分页\n     *\n     * 数据权限：基于 {@link CrmReceivableDO} 读取\n     *\n     * @param pageReqVO 分页查询\n     * @param userId    用户编号\n     * @return 回款分页\n     */\n    PageResult<CrmReceivableDO> getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId);\n\n    /**\n     * 获得回款分页，基于指定客户\n     *\n     * 数据权限：基于 {@link CrmCustomerDO} 读取\n     *\n     * @param pageReqVO 分页查询\n     * @return 回款分页\n     */\n    PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO);\n\n    /**\n     * 获得待审核回款数量\n     *\n     * @param userId 用户编号\n     * @return 待审批数量\n     */\n    Long getAuditReceivableCount(Long userId);\n\n    /**\n     * 获得合同已回款金额 Map\n     *\n     * @param contractIds 合同编号\n     * @return 回款金额 Map\n     */\n    Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds);\n\n    /**\n     * 根据合同编号查询回款数量\n     *\n     * @param contractId 合同编号\n     * @return 回款数量\n     */\n    Long getReceivableCountByContractId(Long contractId);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.receivable;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;\nimport cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper;\nimport cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;\nimport cn.iocoder.yudao.module.crm.service.contract.CrmContractService;\nimport cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;\nimport cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport com.mzt.logapi.context.LogRecordContext;\nimport com.mzt.logapi.service.impl.DiffParseFunction;\nimport com.mzt.logapi.starter.annotation.LogRecord;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;\nimport static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmResultToAuditStatus;\n\n/**\n * CRM 回款 Service 实现类\n *\n * @author 赤焰\n */\n@Service\n@Validated\n@Slf4j\npublic class CrmReceivableServiceImpl implements CrmReceivableService {\n\n    /**\n     * BPM 合同审批流程标识\n     */\n    public static final String BPM_PROCESS_DEFINITION_KEY = \"crm-receivable-audit\";\n\n    @Resource\n    private CrmReceivableMapper receivableMapper;\n\n    @Resource\n    private CrmNoRedisDAO noRedisDAO;\n\n    @Resource\n    private CrmContractService contractService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private CrmReceivablePlanService receivablePlanService;\n    @Resource\n    private CrmPermissionService permissionService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private BpmProcessInstanceApi bpmProcessInstanceApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = \"{{#receivable.id}}\",\n            success = CRM_RECEIVABLE_CREATE_SUCCESS)\n    public Long createReceivable(CrmReceivableSaveReqVO createReqVO) {\n        // 1.1 校验可回款金额超过上限\n        validateReceivablePriceExceedsLimit(createReqVO);\n        // 1.2 校验关联数据存在\n        validateRelationDataExists(createReqVO);\n        // 1.3 生成回款编号\n        String no = noRedisDAO.generate(CrmNoRedisDAO.RECEIVABLE_PREFIX);\n        if (receivableMapper.selectByNo(no) != null) {\n            throw exception(RECEIVABLE_NO_EXISTS);\n        }\n\n        // 2.1 插入回款\n        CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)\n                .setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());\n        receivableMapper.insert(receivable);\n        // 2.2\n\n        // 3. 创建数据权限\n        permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())\n                .setBizId(receivable.getId()).setUserId(createReqVO.getOwnerUserId())\n                .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人\n\n        // 4. 更新关联的回款计划\n        if (createReqVO.getPlanId() != null) {\n            receivablePlanService.updateReceivablePlanReceivableId(receivable.getPlanId(), receivable.getId());\n        }\n\n        // 5. 记录操作日志上下文\n        LogRecordContext.putVariable(\"receivable\", receivable);\n        LogRecordContext.putVariable(\"period\", getReceivablePeriod(receivable.getPlanId()));\n        return receivable.getId();\n    }\n\n    private void validateReceivablePriceExceedsLimit(CrmReceivableSaveReqVO reqVO) {\n        // 1. 计算剩余可退款金额，不包括 reqVO 自身\n        CrmContractDO contract = contractService.validateContract(reqVO.getContractId());\n        List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(),\n                Arrays.asList(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus()));\n        if (reqVO.getId() != null) {\n            receivables.removeIf(receivable -> ObjectUtil.equal(receivable.getId(), reqVO.getId()));\n        }\n        BigDecimal notReceivablePrice = contract.getTotalPrice().subtract(\n                CollectionUtils.getSumValue(receivables, CrmReceivableDO::getPrice, BigDecimal::add, BigDecimal.ZERO));\n        // 2. 校验金额是否超过\n        if (reqVO.getPrice().compareTo(notReceivablePrice) > 0) {\n            throw exception(RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT, notReceivablePrice);\n        }\n    }\n\n    private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) {\n        if (reqVO.getOwnerUserId() != null) {\n            adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在\n        }\n        if (reqVO.getContractId() != null) {\n            CrmContractDO contract = contractService.validateContract(reqVO.getContractId());\n            if (ObjectUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {\n                throw exception(RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE);\n            }\n            reqVO.setCustomerId(contract.getCustomerId()); // 设置客户编号\n        }\n        if (reqVO.getPlanId() != null) {\n            CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(reqVO.getPlanId());\n            if (receivablePlan == null) {\n                throw exception(RECEIVABLE_PLAN_NOT_EXISTS);\n            }\n            if (receivablePlan.getReceivableId() != null) {\n                throw exception(RECEIVABLE_PLAN_EXISTS_RECEIVABLE);\n            }\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = \"{{#updateReqVO.id}}\",\n            success = CRM_RECEIVABLE_UPDATE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = \"#updateReqVO.id\", level = CrmPermissionLevelEnum.WRITE)\n    public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) {\n        Assert.notNull(updateReqVO.getId(), \"回款编号不能为空\");\n        updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段\n        // 1.1 校验存在\n        CrmReceivableDO oldReceivable = validateReceivableExists(updateReqVO.getId());\n        updateReqVO.setOwnerUserId(oldReceivable.getOwnerUserId()).setCustomerId(oldReceivable.getCustomerId())\n                .setContractId(oldReceivable.getContractId()).setPlanId(oldReceivable.getPlanId()); // 设置已存在的值\n        // 1.2 校验可回款金额超过上限\n        validateReceivablePriceExceedsLimit(updateReqVO);\n\n        // 1.3 只有草稿、审批中，可以编辑；\n        if (!ObjectUtils.equalsAny(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),\n                CrmAuditStatusEnum.PROCESS.getStatus())) {\n            throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);\n        }\n\n        // 2. 更新回款\n        CrmReceivableDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivableDO.class);\n        receivableMapper.updateById(updateObj);\n\n        // 3. 记录操作日志上下文\n        updateReqVO.setOwnerUserId(oldReceivable.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况\n        LogRecordContext.putVariable(\"oldReceivable\", oldReceivable);\n        LogRecordContext.putVariable(\"period\", getReceivablePeriod(oldReceivable.getPlanId()));\n        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivable, CrmReceivableSaveReqVO.class));\n    }\n\n    private Integer getReceivablePeriod(Long planId) {\n        if (Objects.isNull(planId)) {\n            return null;\n        }\n        CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(planId);\n        return receivablePlan.getPeriod();\n    }\n\n    @Override\n    public void updateReceivableAuditStatus(Long id, Integer bpmResult) {\n        // 1.1 校验存在\n        CrmReceivableDO receivable = validateReceivableExists(id);\n        // 1.2 只有审批中，可以更新审批结果\n        if (ObjUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {\n            log.error(\"[updateReceivableAuditStatus][receivable({}) 不处于审批中，无法更新审批结果({})]\",\n                    receivable.getId(), bpmResult);\n            throw exception(RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);\n        }\n\n        // 2. 更新回款审批状态\n        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);\n        receivableMapper.updateById(new CrmReceivableDO().setId(id).setAuditStatus(auditStatus));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_RECEIVABLE_DELETE_SUCCESS)\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = \"#id\", level = CrmPermissionLevelEnum.OWNER)\n    public void deleteReceivable(Long id) {\n        // 1.1 校验存在\n        CrmReceivableDO receivable = validateReceivableExists(id);\n        // 1.2 如果被 CrmReceivablePlanDO 所使用，则不允许删除\n        if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {\n            throw exception(RECEIVABLE_DELETE_FAIL);\n        }\n        // 1.3 审批通过时，不允许删除\n        if (ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {\n            throw exception(RECEIVABLE_DELETE_FAIL_IS_APPROVE);\n        }\n\n        // 2.1 删除回款\n        receivableMapper.deleteById(id);\n        // 2.2 删除数据权限\n        permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id);\n\n        // 3. 记录操作日志上下文\n        LogRecordContext.putVariable(\"receivable\", receivable);\n        LogRecordContext.putVariable(\"period\", getReceivablePeriod(receivable.getPlanId()));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_SUBMIT_SUB_TYPE, bizNo = \"{{#id}}\",\n            success = CRM_RECEIVABLE_SUBMIT_SUCCESS)\n    public void submitReceivable(Long id, Long userId) {\n        // 1. 校验回款是否在审批\n        CrmReceivableDO receivable = validateReceivableExists(id);\n        if (ObjUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {\n            throw exception(RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT);\n        }\n\n        // 2. 创建回款审批流程实例\n        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()\n                .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id))).getCheckedData();\n\n        // 3. 更新回款工作流编号\n        receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId(processInstanceId)\n                .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));\n\n        // 4. 记录日志\n        LogRecordContext.putVariable(\"receivableNo\", receivable.getNo());\n    }\n\n    private CrmReceivableDO validateReceivableExists(Long id) {\n        CrmReceivableDO receivable = receivableMapper.selectById(id);\n        if (receivable == null) {\n            throw exception(RECEIVABLE_NOT_EXISTS);\n        }\n        return receivable;\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = \"#id\", level = CrmPermissionLevelEnum.READ)\n    public CrmReceivableDO getReceivable(Long id) {\n        return receivableMapper.selectById(id);\n    }\n\n    @Override\n    public List<CrmReceivableDO> getReceivableList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return ListUtil.empty();\n        }\n        return receivableMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<CrmReceivableDO> getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId) {\n        return receivableMapper.selectPage(pageReqVO, userId);\n    }\n\n    @Override\n    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = \"#pageReqVO.customerId\", level = CrmPermissionLevelEnum.READ)\n    public PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO) {\n        return receivableMapper.selectPageByCustomerId(pageReqVO);\n    }\n\n    @Override\n    public Long getAuditReceivableCount(Long userId) {\n        return receivableMapper.selectCountByAudit(userId);\n    }\n\n    @Override\n    public Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds) {\n        return receivableMapper.selectReceivablePriceMapByContractId(contractIds);\n    }\n\n    @Override\n    public Long getReceivableCountByContractId(Long contractId) {\n        return receivableMapper.selectCountByContractId(contractId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableStatusListener.java",
    "content": "package cn.iocoder.yudao.module.crm.service.receivable.listener;\n\nimport cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;\nimport cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;\nimport cn.iocoder.yudao.module.crm.enums.ApiConstants;\nimport cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;\nimport cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableServiceImpl;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\n/**\n * 回款审批的结果的监听器实现类\n *\n * @author HUIHUI\n */\n@RestController\n@Validated\npublic class CrmReceivableStatusListener extends BpmProcessInstanceStatusEventListener {\n\n    private static final String PREFIX = ApiConstants.PREFIX + \"/receivable\";\n\n    @Resource\n    private CrmReceivableService receivableService;\n\n    @Override\n    public String getProcessDefinitionKey() {\n        return CrmReceivableServiceImpl.BPM_PROCESS_DEFINITION_KEY;\n    }\n\n    @Override\n    @PostMapping(PREFIX + \"/update-audit-status\") // 目的：提供给 bpm-server rpc 调用\n    public void onEvent(@RequestBody BpmProcessInstanceStatusEvent event) {\n        receivableService.updateReceivableAuditStatus(Long.parseLong(event.getBusinessKey()), event.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;\n\nimport java.util.List;\n\n/**\n * CRM 客户分析 Service 接口\n *\n * @author dhb52\n */\npublic interface CrmStatisticsCustomerService {\n\n    /**\n     * 总量分析(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 总量分析(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 跟进次数分析(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 跟进次数分析(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 客户跟进次数分析(按类型)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 获取客户的首次合同、回款信息列表，用于【客户转化率】页面\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 公海客户分析(按日期)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsPoolSummaryByDateRespVO> getPoolSummaryByDate(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 公海客户分析(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsPoolSummaryByUserRespVO> getPoolSummaryByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 客户成交周期(按日期)\n     *\n     * 成交周期的定义：客户 customer 在创建出来，到合同 contract 第一次成交的时间差\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 客户成交周期(按用户)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 客户成交周期(按区域)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerDealCycleByAreaRespVO> getCustomerDealCycleByArea(CrmStatisticsCustomerReqVO reqVO);\n\n    /**\n     * 客户成交周期(按产品)\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticsCustomerDealCycleByProductRespVO> getCustomerDealCycleByProduct(CrmStatisticsCustomerReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;\nimport cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\n\n/**\n * CRM 客户分析 Service 实现类\n *\n * @author dhb52\n */\n@Service\n@Validated\npublic class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService {\n\n    @Resource\n    private CrmStatisticsCustomerMapper customerMapper;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @Override\n    public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按天统计，获取分项统计数据\n        List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO);\n        List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByDate(reqVO);\n\n        // 3. 按照日期间隔，合并数据\n        List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());\n        return convertList(timeRanges, times -> {\n            Integer customerCreateCount = customerCreateCountList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                    .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount).sum();\n            Integer customerDealCount = customerDealCountList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                    .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount).sum();\n            return new CrmStatisticsCustomerSummaryByDateRespVO()\n                    .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))\n                    .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount);\n        });\n    }\n\n    @Override\n    public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按用户统计，获取分项统计数据\n        List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByUser(reqVO);\n        List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO);\n        List<CrmStatisticsCustomerSummaryByUserRespVO> contractPriceList = customerMapper.selectContractPriceGroupByUser(reqVO);\n        List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePriceList = customerMapper.selectReceivablePriceGroupByUser(reqVO);\n\n        // 3.1 按照用户，合并统计数据\n        List<CrmStatisticsCustomerSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {\n            Integer customerCreateCount = customerCreateCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                    .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount).sum();\n            Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                    .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum();\n            BigDecimal contractPrice = contractPriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                    .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getContractPrice()), BigDecimal::add);\n            BigDecimal receivablePrice = receivablePriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                    .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getReceivablePrice()), BigDecimal::add);\n            return (CrmStatisticsCustomerSummaryByUserRespVO) new CrmStatisticsCustomerSummaryByUserRespVO()\n                    .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount)\n                    .setContractPrice(contractPrice).setReceivablePrice(receivablePrice).setOwnerUserId(userId);\n        });\n        // 3.2 拼接用户信息\n        appendUserInfo(summaryList);\n        return summaryList;\n    }\n\n    @Override\n    public List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按天统计，获取分项统计数据\n        List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByDate(reqVO);\n        List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByDate(reqVO);\n\n        // 3. 按照时间间隔，合并统计数据\n        List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());\n        return convertList(timeRanges, times -> {\n            Integer followUpRecordCount = followUpRecordCountList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                    .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpRecordCount).sum();\n            Integer followUpCustomerCount = followUpCustomerCountList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                    .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpCustomerCount).sum();\n            return new CrmStatisticsFollowUpSummaryByDateRespVO()\n                    .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))\n                    .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount);\n        });\n    }\n\n    @Override\n    public List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按用户统计，获取分项统计数据\n        List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByUser(reqVO);\n        List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByUser(reqVO);\n\n        // 3.1 按照用户，合并统计数据\n        List<CrmStatisticsFollowUpSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {\n            Integer followUpRecordCount = followUpRecordCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                    .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpRecordCount).sum();\n            Integer followUpCustomerCount = followUpCustomerCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                    .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpCustomerCount).sum();\n            return (CrmStatisticsFollowUpSummaryByUserRespVO) new CrmStatisticsFollowUpSummaryByUserRespVO()\n                    .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount).setOwnerUserId(userId);\n        });\n        // 3.2 拼接用户信息\n        appendUserInfo(summaryList);\n        return summaryList;\n    }\n\n    @Override\n    public List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 获得跟进数据\n        return customerMapper.selectFollowUpRecordCountGroupByType(reqVO);\n    }\n\n    @Override\n    public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按用户统计，获取统计数据\n        List<CrmStatisticsCustomerContractSummaryRespVO> summaryList = customerMapper.selectContractSummary(reqVO);\n\n        // 3. 拼接信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSetByFlatMap(summaryList, vo -> Stream.of(NumberUtils.parseLong(vo.getCreator()), vo.getOwnerUserId())));\n        summaryList.forEach(vo -> {\n            findAndThen(userMap, NumberUtils.parseLong(vo.getCreator()), user -> vo.setCreatorUserName(user.getNickname()));\n            findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()));\n        });\n        return summaryList;\n    }\n\n    @Override\n    public List<CrmStatisticsPoolSummaryByDateRespVO> getPoolSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按天统计，获取分项统计数据\n        List<CrmStatisticsPoolSummaryByDateRespVO> customerPutCountList = customerMapper.selectPoolCustomerPutCountByDate(reqVO);\n        List<CrmStatisticsPoolSummaryByDateRespVO> customerTakeCountList = customerMapper.selectPoolCustomerTakeCountByDate(reqVO);\n\n        // 3. 按照日期间隔，合并数据\n        List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());\n        return convertList(timeRanges, times -> {\n            Integer customerPutCount = customerPutCountList.stream()\n                .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                .mapToInt(CrmStatisticsPoolSummaryByDateRespVO::getCustomerPutCount).sum();\n            Integer customerTakeCount = customerTakeCountList.stream()\n                .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                .mapToInt(CrmStatisticsPoolSummaryByDateRespVO::getCustomerTakeCount).sum();\n            return new CrmStatisticsPoolSummaryByDateRespVO()\n                .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))\n                .setCustomerPutCount(customerPutCount).setCustomerTakeCount(customerTakeCount);\n        });\n    }\n\n    @Override\n    public List<CrmStatisticsPoolSummaryByUserRespVO> getPoolSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按用户统计，获取分项统计数据\n        List<CrmStatisticsPoolSummaryByUserRespVO> customerPutCountList = customerMapper.selectPoolCustomerPutCountByUser(reqVO);\n        List<CrmStatisticsPoolSummaryByUserRespVO> customerTakeCountList = customerMapper.selectPoolCustomerTakeCountByUser(reqVO);\n\n        // 3.1 按照用户，合并统计数据\n        List<CrmStatisticsPoolSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {\n            Integer customerPutCount = customerPutCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                .mapToInt(CrmStatisticsPoolSummaryByUserRespVO::getCustomerPutCount).sum();\n            Integer customerTakeCount = customerTakeCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                .mapToInt(CrmStatisticsPoolSummaryByUserRespVO::getCustomerTakeCount).sum();\n            return (CrmStatisticsPoolSummaryByUserRespVO) new CrmStatisticsPoolSummaryByUserRespVO()\n                .setCustomerPutCount(customerPutCount).setCustomerTakeCount(customerTakeCount)\n                .setOwnerUserId(userId);\n        });\n        // 3.2 拼接用户信息\n        appendUserInfo(summaryList);\n        return summaryList;\n    }\n\n    @Override\n    public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按天统计，获取分项统计数据\n        List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByDate(reqVO);\n\n        // 3. 按照日期间隔，合并统计数据\n        List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());\n        return convertList(timeRanges, times -> {\n            Double customerDealCycle = customerDealCycleList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                    .mapToDouble(CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle).sum();\n            return new CrmStatisticsCustomerDealCycleByDateRespVO()\n                    .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))\n                    .setCustomerDealCycle(customerDealCycle);\n        });\n    }\n\n    @Override\n    public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按用户统计，获取分项统计数据\n        List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByUser(reqVO);\n        List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO);\n\n        // 3.1 按照用户，合并统计数据\n        List<CrmStatisticsCustomerDealCycleByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {\n            Double customerDealCycle = customerDealCycleList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                    .mapToDouble(CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle).sum();\n            Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))\n                    .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum();\n            return (CrmStatisticsCustomerDealCycleByUserRespVO) new CrmStatisticsCustomerDealCycleByUserRespVO()\n                    .setCustomerDealCycle(customerDealCycle).setCustomerDealCount(customerDealCount).setOwnerUserId(userId);\n        });\n        // 3.2 拼接用户信息\n        appendUserInfo(summaryList);\n        return summaryList;\n    }\n\n    @Override\n    public List<CrmStatisticsCustomerDealCycleByAreaRespVO> getCustomerDealCycleByArea(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        List<Long> userIds = getUserIds(reqVO);\n        if (CollUtil.isEmpty(userIds)) {\n            return Collections.emptyList();\n        }\n        reqVO.setUserIds(userIds);\n\n        // 2. 获取客户地区统计数据\n        List<CrmStatisticsCustomerDealCycleByAreaRespVO> dealCycleByAreaList = customerMapper.selectCustomerDealCycleGroupByAreaId(reqVO);\n        if (CollUtil.isEmpty(dealCycleByAreaList)) {\n            return Collections.emptyList();\n        }\n\n        // 3. 拼接数据\n        Map<Integer, Area> areaMap = convertMap(AreaUtils.getByType(AreaTypeEnum.PROVINCE, Function.identity()), Area::getId);\n        return convertList(dealCycleByAreaList, vo -> {\n            if (vo.getAreaId() != null) {\n                Integer parentId = AreaUtils.getParentIdByType(vo.getAreaId(), AreaTypeEnum.PROVINCE);\n                findAndThen(areaMap, parentId, area -> vo.setAreaId(parentId).setAreaName(area.getName()));\n            }\n            return vo;\n        });\n    }\n\n    @Override\n    public List<CrmStatisticsCustomerDealCycleByProductRespVO> getCustomerDealCycleByProduct(CrmStatisticsCustomerReqVO reqVO) {\n        // 1. 获得用户编号数组\n        List<Long> userIds = getUserIds(reqVO);\n        if (CollUtil.isEmpty(userIds)) {\n            return Collections.emptyList();\n        }\n        reqVO.setUserIds(userIds);\n\n        // 2. 获取客户产品统计数据\n        // TODO @dhb52：未读取产品名\n        return customerMapper.selectCustomerDealCycleGroupByProductId(reqVO);\n    }\n\n    /**\n     * 拼接用户信息（昵称）\n     *\n     * @param voList 统计数据\n     */\n    private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> voList) {\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(voList, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));\n        voList.forEach(vo -> findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));\n    }\n\n    /**\n     * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组，包括子部门的所有用户编号\n     *\n     * @param reqVO 请求参数\n     * @return 用户编号数组\n     */\n    private List<Long> getUserIds(CrmStatisticsCustomerReqVO reqVO) {\n        // 情况一：选中某个用户\n        if (ObjUtil.isNotNull(reqVO.getUserId())) {\n            return ListUtil.of(reqVO.getUserId());\n        }\n        // 情况二：选中某个部门\n        // 2.1 获得部门列表\n        List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()).getCheckedData(), DeptRespDTO::getId);\n        deptIds.add(reqVO.getDeptId());\n        // 2.2 获得用户编号\n        return convertList(adminUserApi.getUserListByDeptIds(deptIds).getCheckedData(), AdminUserRespDTO::getId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsFunnelService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.*;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\n\nimport java.util.List;\n\n/**\n * CRM 销售漏斗分析 Service\n *\n * @author HUIHUI\n */\npublic interface CrmStatisticsFunnelService {\n\n    /**\n     * 获得销售漏斗数据\n     *\n     * @param reqVO 请求\n     * @return 销售漏斗数据\n     */\n    CrmStatisticFunnelSummaryRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO);\n\n    /**\n     * 获得商机结束状态统计\n     *\n     * @param reqVO 请求\n     * @return 商机结束状态统计\n     */\n    List<CrmStatisticsBusinessSummaryByEndStatusRespVO> getBusinessSummaryByEndStatus(CrmStatisticsFunnelReqVO reqVO);\n\n    /**\n     * 获取新增商机分析(按日期)\n     *\n     * @param reqVO 请求\n     * @return 新增商机分析\n     */\n    List<CrmStatisticsBusinessSummaryByDateRespVO> getBusinessSummaryByDate(CrmStatisticsFunnelReqVO reqVO);\n\n    /**\n     * 获得商机转化率分析(按日期)\n     *\n     * @param reqVO 请求\n     * @return 商机转化率分析\n     */\n    List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> getBusinessInversionRateSummaryByDate(CrmStatisticsFunnelReqVO reqVO);\n\n    /**\n     * 获得商机分页(按日期)\n     *\n     * @param pageVO 请求\n     * @return 商机分页\n     */\n    PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsFunnelServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.*;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsFunnelMapper;\nimport cn.iocoder.yudao.module.crm.enums.business.CrmBusinessEndStatusEnum;\nimport cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * CRM 销售漏斗分析 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\npublic class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelService {\n\n    @Resource\n    private CrmStatisticsFunnelMapper funnelMapper;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private CrmBusinessService businessService;\n    @Resource\n    private DeptApi deptApi;\n\n    @Override\n    public CrmStatisticFunnelSummaryRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO) {\n        // 1. 获得用户编号数组\n        List<Long> userIds = getUserIds(reqVO);\n        if (CollUtil.isEmpty(userIds)) {\n            return null;\n        }\n        reqVO.setUserIds(userIds);\n\n        // 2. 获得漏斗数据\n        Long customerCount = funnelMapper.selectCustomerCountByDate(reqVO);\n        Long businessCount = funnelMapper.selectBusinessCountByDateAndEndStatus(reqVO, null);\n        Long businessWinCount = funnelMapper.selectBusinessCountByDateAndEndStatus(reqVO, CrmBusinessEndStatusEnum.WIN.getStatus());\n        return new CrmStatisticFunnelSummaryRespVO(customerCount, businessCount, businessWinCount);\n    }\n\n    @Override\n    public List<CrmStatisticsBusinessSummaryByEndStatusRespVO> getBusinessSummaryByEndStatus(CrmStatisticsFunnelReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 获得统计数据\n        return funnelMapper.selectBusinessSummaryListGroupByEndStatus(reqVO);\n    }\n\n    @Override\n    public List<CrmStatisticsBusinessSummaryByDateRespVO> getBusinessSummaryByDate(CrmStatisticsFunnelReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按天统计，获取分项统计数据\n        List<CrmStatisticsBusinessSummaryByDateRespVO> businessSummaryList = funnelMapper.selectBusinessSummaryGroupByDate(reqVO);\n        // 3. 按照日期间隔，合并数据\n        List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());\n        return convertList(timeRanges, times -> {\n            Long businessCreateCount = businessSummaryList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                    .mapToLong(CrmStatisticsBusinessSummaryByDateRespVO::getBusinessCreateCount).sum();\n            BigDecimal businessDealCount = businessSummaryList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                    .map(CrmStatisticsBusinessSummaryByDateRespVO::getTotalPrice)\n                    .reduce(BigDecimal.ZERO, BigDecimal::add);\n            return new CrmStatisticsBusinessSummaryByDateRespVO()\n                    .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))\n                    .setBusinessCreateCount(businessCreateCount).setTotalPrice(businessDealCount);\n        });\n    }\n\n    @Override\n    public List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> getBusinessInversionRateSummaryByDate(CrmStatisticsFunnelReqVO reqVO) {\n        // 1. 获得用户编号数组\n        reqVO.setUserIds(getUserIds(reqVO));\n        if (CollUtil.isEmpty(reqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n\n        // 2. 按天统计，获取分项统计数据\n        List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> businessSummaryList = funnelMapper.selectBusinessInversionRateSummaryByDate(reqVO);\n        // 3. 按照日期间隔，合并数据\n        List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());\n        return convertList(timeRanges, times -> {\n            Long businessCount = businessSummaryList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                    .mapToLong(CrmStatisticsBusinessInversionRateSummaryByDateRespVO::getBusinessCount).sum();\n            Long businessWinCount = businessSummaryList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))\n                    .mapToLong(CrmStatisticsBusinessInversionRateSummaryByDateRespVO::getBusinessWinCount).sum();\n            return new CrmStatisticsBusinessInversionRateSummaryByDateRespVO()\n                    .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))\n                    .setBusinessCount(businessCount).setBusinessWinCount(businessWinCount);\n        });\n    }\n\n    @Override\n    public PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO) {\n        // 1. 获得用户编号数组\n        pageVO.setUserIds(getUserIds(pageVO));\n        if (CollUtil.isEmpty(pageVO.getUserIds())) {\n            return PageResult.empty();\n        }\n        // 2. 执行查询\n        return businessService.getBusinessPageByDate(pageVO);\n    }\n\n    /**\n     * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组，包括子部门的所有用户编号\n     *\n     * @param reqVO 请求参数\n     * @return 用户编号数组\n     */\n    private List<Long> getUserIds(CrmStatisticsFunnelReqVO reqVO) {\n        // 情况一：选中某个用户\n        if (ObjUtil.isNotNull(reqVO.getUserId())) {\n            return ListUtil.of(reqVO.getUserId());\n        }\n        // 情况二：选中某个部门\n        // 2.1 获得部门列表\n        List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()).getCheckedData(), DeptRespDTO::getId);\n        deptIds.add(reqVO.getDeptId());\n        // 2.2 获得用户编号\n        return convertList(adminUserApi.getUserListByDeptIds(deptIds).getCheckedData(), AdminUserRespDTO::getId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\n\n\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;\n\nimport java.util.List;\n\n/**\n * CRM 员工绩效统计 Service 接口\n *\n * @author scholar\n */\npublic interface CrmStatisticsPerformanceService {\n\n    /**\n     * 员工签约合同数量分析\n     *\n     * @param performanceReqVO 排行参数\n     * @return 员工签约合同数量排行分析\n     */\n    List<CrmStatisticsPerformanceRespVO> getContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO);\n\n    /**\n     * 员工签约合同金额分析\n     *\n     * @param performanceReqVO 排行参数\n     * @return 员工签约合同金额分析\n     */\n    List<CrmStatisticsPerformanceRespVO> getContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);\n\n    /**\n     * 员工获得回款金额分析\n     *\n     * @param performanceReqVO 排行参数\n     * @return 员工获得回款金额分析\n     */\n    List<CrmStatisticsPerformanceRespVO> getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);\n\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceMapper;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * CRM 员工业绩分析 Service 实现类\n *\n * @author scholar\n */\n@Service\n@Validated\npublic class CrmStatisticsPerformanceServiceImpl implements CrmStatisticsPerformanceService {\n\n    @Resource\n    private CrmStatisticsPerformanceMapper performanceMapper;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @Override\n    public List<CrmStatisticsPerformanceRespVO> getContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO) {\n        return getPerformance(performanceReqVO, performanceMapper::selectContractCountPerformance);\n    }\n\n    @Override\n    public List<CrmStatisticsPerformanceRespVO> getContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO) {\n        return getPerformance(performanceReqVO, performanceMapper::selectContractPricePerformance);\n    }\n\n    @Override\n    public List<CrmStatisticsPerformanceRespVO> getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO) {\n        return getPerformance(performanceReqVO, performanceMapper::selectReceivablePricePerformance);\n    }\n\n    /**\n     * 获得员工业绩数据\n     *\n     * 1. 获得今年 + 去年的数据\n     * 2. 遍历今年的月份，逐个拼接去年的月份数据\n     *\n     * @param performanceReqVO  参数\n     * @param performanceFunction 员工业绩统计方法\n     * @return 员工业绩数据\n     */\n    private List<CrmStatisticsPerformanceRespVO> getPerformance(CrmStatisticsPerformanceReqVO performanceReqVO,\n                                                                Function<CrmStatisticsPerformanceReqVO, List<CrmStatisticsPerformanceRespVO>> performanceFunction) {\n\n        // 1. 获得用户编号数组\n        List<Long> userIds = getUserIds(performanceReqVO);\n        if (CollUtil.isEmpty(userIds)) {\n            return Collections.emptyList();\n        }\n        performanceReqVO.setUserIds(userIds);\n\n        // 2. 获得业绩数据\n        int year = performanceReqVO.getTimes()[0].getYear(); // 获取查询的年份\n        performanceReqVO.getTimes()[0] = performanceReqVO.getTimes()[0].minusYears(1);\n        List<CrmStatisticsPerformanceRespVO> performanceList = performanceFunction.apply(performanceReqVO);\n        Map<String, BigDecimal> performanceMap = convertMap(performanceList, CrmStatisticsPerformanceRespVO::getTime,\n                CrmStatisticsPerformanceRespVO::getCurrentMonthCount);\n\n        // 3. 组装数据返回\n        List<CrmStatisticsPerformanceRespVO> result = new ArrayList<>();\n        for (int month = 1; month <= 12; month++) {\n            String currentMonth = String.format(\"%d%02d\", year, month);\n            String lastMonth = month == 1 ? String.format(\"%d%02d\", year - 1, 12) : String.format(\"%d%02d\", year, month - 1);\n            String lastYear = String.format(\"%d%02d\", year - 1, month);\n            result.add(new CrmStatisticsPerformanceRespVO().setTime(currentMonth)\n                    .setCurrentMonthCount(performanceMap.getOrDefault(currentMonth, BigDecimal.ZERO))\n                    .setLastMonthCount(performanceMap.getOrDefault(lastMonth, BigDecimal.ZERO))\n                    .setLastYearCount(performanceMap.getOrDefault(lastYear, BigDecimal.ZERO)));\n        }\n        return result;\n    }\n\n    /**\n     * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组，包括子部门的所有用户编号\n     *\n     * @param reqVO 请求参数\n     * @return 用户编号数组\n     */\n    private List<Long> getUserIds(CrmStatisticsPerformanceReqVO reqVO) {\n        // 情况一：选中某个用户\n        if (ObjUtil.isNotNull(reqVO.getUserId())) {\n            return ListUtil.of(reqVO.getUserId());\n        }\n        // 情况二：选中某个部门\n        // 2.1 获得部门列表\n        final Long deptId = reqVO.getDeptId();\n        List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId).getCheckedData(), DeptRespDTO::getId);\n        deptIds.add(deptId);\n        // 2.2 获得用户编号\n        return convertList(adminUserApi.getUserListByDeptIds(deptIds).getCheckedData(), AdminUserRespDTO::getId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*;\n\nimport java.util.List;\n\n/**\n * CRM 客户画像 Service 接口\n *\n * @author HUIHUI\n */\npublic interface CrmStatisticsPortraitService {\n\n    /**\n     * 获取客户地区统计数据\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticCustomerAreaRespVO> getCustomerSummaryByArea(CrmStatisticsPortraitReqVO reqVO);\n\n    /**\n     * 获取客户行业统计数据\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticCustomerIndustryRespVO> getCustomerSummaryByIndustry(CrmStatisticsPortraitReqVO reqVO);\n\n    /**\n     * 获取客户级别统计数据\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticCustomerLevelRespVO> getCustomerSummaryByLevel(CrmStatisticsPortraitReqVO reqVO);\n\n    /**\n     * 获取客户来源统计数据\n     *\n     * @param reqVO 请求参数\n     * @return 统计数据\n     */\n    List<CrmStatisticCustomerSourceRespVO> getCustomerSummaryBySource(CrmStatisticsPortraitReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*;\nimport cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * CRM 客户画像 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\npublic class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitService {\n\n    @Resource\n    private CrmStatisticsPortraitMapper portraitMapper;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @Override\n    public List<CrmStatisticCustomerAreaRespVO> getCustomerSummaryByArea(CrmStatisticsPortraitReqVO reqVO) {\n        // 1. 获得用户编号数组\n        List<Long> userIds = getUserIds(reqVO);\n        if (CollUtil.isEmpty(userIds)) {\n            return Collections.emptyList();\n        }\n        reqVO.setUserIds(userIds);\n\n        // 2. 获取客户地区统计数据\n        List<CrmStatisticCustomerAreaRespVO> list = portraitMapper.selectSummaryListGroupByAreaId(reqVO);\n        if (CollUtil.isEmpty(list)) {\n            return Collections.emptyList();\n        }\n\n        // 3. 拼接数据\n        List<Area> areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area);\n        Map<Integer, Area> areaMap = convertMap(areaList, Area::getId);\n        return convertList(list, item -> {\n            Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE);\n            if (parentId != null) {\n                Area area = areaMap.get(parentId);\n                if (area != null) {\n                    item.setAreaId(parentId).setAreaName(area.getName());\n                    return item;\n                }\n            }\n            // 找不到，归到未知\n            return item.setAreaId(null).setAreaName(\"未知\");\n        });\n    }\n\n    @Override\n    public List<CrmStatisticCustomerIndustryRespVO> getCustomerSummaryByIndustry(CrmStatisticsPortraitReqVO reqVO) {\n        // 1. 获得用户编号数组\n        List<Long> userIds = getUserIds(reqVO);\n        if (CollUtil.isEmpty(userIds)) {\n            return Collections.emptyList();\n        }\n        reqVO.setUserIds(userIds);\n\n        // 2. 获取客户行业统计数据\n        return portraitMapper.selectCustomerIndustryListGroupByIndustryId(reqVO);\n    }\n\n    @Override\n    public List<CrmStatisticCustomerSourceRespVO> getCustomerSummaryBySource(CrmStatisticsPortraitReqVO reqVO) {\n        // 1. 获得用户编号数组\n        List<Long> userIds = getUserIds(reqVO);\n        if (CollUtil.isEmpty(userIds)) {\n            return Collections.emptyList();\n        }\n        reqVO.setUserIds(userIds);\n\n        // 2. 获取客户行业统计数据\n        return portraitMapper.selectCustomerSourceListGroupBySource(reqVO);\n    }\n\n    @Override\n    public List<CrmStatisticCustomerLevelRespVO> getCustomerSummaryByLevel(CrmStatisticsPortraitReqVO reqVO) {\n        // 1. 获得用户编号数组\n        List<Long> userIds = getUserIds(reqVO);\n        if (CollUtil.isEmpty(userIds)) {\n            return Collections.emptyList();\n        }\n        reqVO.setUserIds(userIds);\n\n        // 2. 获取客户级别统计数据\n        return portraitMapper.selectCustomerLevelListGroupByLevel(reqVO);\n    }\n\n    /**\n     * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组，包括子部门的所有用户编号\n     *\n     * @param reqVO 请求参数\n     * @return 用户编号数组\n     */\n    private List<Long> getUserIds(CrmStatisticsPortraitReqVO reqVO) {\n        // 情况一：选中某个用户\n        if (ObjUtil.isNotNull(reqVO.getUserId())) {\n            return ListUtil.of(reqVO.getUserId());\n        }\n        // 情况二：选中某个部门\n        // 2.1 获得部门列表\n        List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()).getCheckedData(), DeptRespDTO::getId);\n        deptIds.add(reqVO.getDeptId());\n        // 2.2 获得用户编号\n        return convertList(adminUserApi.getUserListByDeptIds(deptIds).getCheckedData(), AdminUserRespDTO::getId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankService.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\n\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO;\n\nimport java.util.List;\n\n/**\n * CRM 排行榜统计 Service 接口\n *\n * @author anhaohao\n */\npublic interface CrmStatisticsRankService {\n\n    /**\n     * 获得合同金额排行榜\n     *\n     * @param rankReqVO 排行参数\n     * @return 合同金额排行榜\n     */\n    List<CrmStatisticsRankRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 获得回款金额排行榜\n     *\n     * @param rankReqVO 排行参数\n     * @return 回款金额排行榜\n     */\n    List<CrmStatisticsRankRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 获得签约合同数量排行榜\n     *\n     * @param rankReqVO 排行参数\n     * @return 签约合同数量排行榜\n     */\n    List<CrmStatisticsRankRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 获得产品销量排行榜\n     *\n     * @param rankReqVO 排行参数\n     * @return 产品销量排行榜\n     */\n    List<CrmStatisticsRankRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 获得新增客户数排行榜\n     *\n     * @param rankReqVO 排行参数\n     * @return 新增客户数排行榜\n     */\n    List<CrmStatisticsRankRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 获得联系人数量排行榜\n     *\n     * @param rankReqVO 排行参数\n     * @return 联系人数量排行榜\n     */\n    List<CrmStatisticsRankRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 获得跟进次数排行榜\n     *\n     * @param rankReqVO 排行参数\n     * @return 跟进次数排行榜\n     */\n    List<CrmStatisticsRankRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n    /**\n     * 获得跟进客户数排行榜\n     *\n     * @param rankReqVO 排行参数\n     * @return 跟进客户数排行榜\n     */\n    List<CrmStatisticsRankRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.crm.service.statistics;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO;\nimport cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO;\nimport cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankMapper;\nimport cn.iocoder.yudao.module.system.api.dept.DeptApi;\nimport cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n/**\n * CRM 排行榜统计 Service 实现类\n *\n * @author anhaohao\n */\n@Service\n@Validated\npublic class CrmStatisticsRankServiceImpl implements CrmStatisticsRankService {\n\n    @Resource\n    private CrmStatisticsRankMapper rankMapper;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private DeptApi deptApi;\n\n    @Override\n    public List<CrmStatisticsRankRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO) {\n        return getRank(rankReqVO, rankMapper::selectContractPriceRank);\n    }\n\n    @Override\n    public List<CrmStatisticsRankRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO) {\n        return getRank(rankReqVO, rankMapper::selectReceivablePriceRank);\n    }\n\n    @Override\n    public List<CrmStatisticsRankRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO) {\n        return getRank(rankReqVO, rankMapper::selectContractCountRank);\n    }\n\n    @Override\n    public List<CrmStatisticsRankRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO) {\n        return getRank(rankReqVO, rankMapper::selectProductSalesRank);\n    }\n\n    @Override\n    public List<CrmStatisticsRankRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {\n        return getRank(rankReqVO, rankMapper::selectCustomerCountRank);\n    }\n\n    @Override\n    public List<CrmStatisticsRankRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO) {\n        return getRank(rankReqVO, rankMapper::selectContactsCountRank);\n    }\n\n    @Override\n    public List<CrmStatisticsRankRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO) {\n        return getRank(rankReqVO, rankMapper::selectFollowCountRank);\n    }\n\n    @Override\n    public List<CrmStatisticsRankRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {\n        return getRank(rankReqVO, rankMapper::selectFollowCustomerCountRank);\n    }\n\n    /**\n     * 获得排行版数据\n     *\n     * @param rankReqVO    参数\n     * @param rankFunction 排行榜方法\n     * @return 排行版数据\n     */\n    private List<CrmStatisticsRankRespVO> getRank(CrmStatisticsRankReqVO rankReqVO, Function<CrmStatisticsRankReqVO, List<CrmStatisticsRankRespVO>> rankFunction) {\n        // 1. 获得用户编号数组\n        rankReqVO.setUserIds(getUserIds(rankReqVO.getDeptId()));\n        if (CollUtil.isEmpty(rankReqVO.getUserIds())) {\n            return Collections.emptyList();\n        }\n        // 2. 获得排行数据\n        List<CrmStatisticsRankRespVO> ranks = rankFunction.apply(rankReqVO);\n        if (CollUtil.isEmpty(ranks)) {\n            return Collections.emptyList();\n        }\n        ranks.sort(Comparator.comparing(CrmStatisticsRankRespVO::getCount).reversed());\n        // 3. 拼接用户信息\n        appendUserInfo(ranks);\n        return ranks;\n    }\n\n    /**\n     * 拼接用户信息（昵称、部门）\n     *\n     * @param ranks 排行榜数据\n     */\n    private void appendUserInfo(List<CrmStatisticsRankRespVO> ranks) {\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(ranks, CrmStatisticsRankRespVO::getOwnerUserId));\n        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));\n        ranks.forEach(rank -> MapUtils.findAndThen(userMap, rank.getOwnerUserId(), user -> {\n            rank.setNickname(user.getNickname());\n            MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> rank.setDeptName(dept.getName()));\n        }));\n    }\n\n    /**\n     * 获得部门下的用户编号数组，包括子部门的\n     *\n     * @param deptId 部门编号\n     * @return 用户编号数组\n     */\n    public List<Long> getUserIds(Long deptId) {\n        // 1. 获得部门列表\n        List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId).getCheckedData(), DeptRespDTO::getId);\n        deptIds.add(deptId);\n        // 2. 获得用户编号\n        return convertList(adminUserApi.getUserListByDeptIds(deptIds).getCheckedData(), AdminUserRespDTO::getId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/util/CrmAuditStatusUtils.java",
    "content": "package cn.iocoder.yudao.module.crm.util;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;\n\n/**\n * CRM 流程工具类\n *\n * @author HUIHUI\n */\npublic class CrmAuditStatusUtils {\n\n    /**\n     * BPM 审批结果转换\n     *\n     * @param bpmResult BPM 审批结果\n     */\n    public static Integer convertBpmResultToAuditStatus(Integer bpmResult) {\n        Integer auditStatus = BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult) ? CrmAuditStatusEnum.APPROVE.getStatus()\n                : BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult) ? CrmAuditStatusEnum.REJECT.getStatus()\n                : BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult) ? BpmTaskStatusEnum.CANCEL.getStatus() : null;\n        Assert.notNull(auditStatus, \"BPM 审批结果({}) 转换失败\", bpmResult);\n        return auditStatus;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java",
    "content": "package cn.iocoder.yudao.module.crm.util;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;\nimport cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;\nimport cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.github.yulichang.autoconfigure.MybatisPlusJoinProperties;\nimport com.github.yulichang.wrapper.MPJLambdaWrapper;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n/**\n * 数据权限工具类\n *\n * @author HUIHUI\n */\npublic class CrmPermissionUtils {\n\n    /**\n     * 校验用户是否是 CRM 管理员\n     *\n     * @return 是/否\n     */\n    public static boolean isCrmAdmin() {\n        PermissionCommonApi permissionApi = SpringUtil.getBean(PermissionCommonApi.class);\n        return permissionApi.hasAnyRoles(getLoginUserId(), RoleCodeEnum.CRM_ADMIN.getCode()).getCheckedData();\n    }\n\n    /**\n     * 构造 CRM 数据类型数据【分页】查询条件\n     *\n     * @param query     连表查询对象\n     * @param bizType   数据类型 {@link CrmBizTypeEnum}\n     * @param bizId     数据编号\n     * @param userId    用户编号\n     * @param sceneType 场景类型\n     */\n    public static <T extends MPJLambdaWrapper<?>, S> void appendPermissionCondition(T query, Integer bizType, SFunction<S, ?> bizId,\n                                                                                    Long userId, Integer sceneType) {\n        MybatisPlusJoinProperties mybatisPlusJoinProperties = SpringUtil.getBean(MybatisPlusJoinProperties.class);\n        final String ownerUserIdField = mybatisPlusJoinProperties.getTableAlias() + \".owner_user_id\";\n        // 场景一：我负责的数据\n        if (CrmSceneTypeEnum.isOwner(sceneType)) {\n            query.eq(ownerUserIdField, userId);\n        }\n        // 场景二：我参与的数据（我有读或写权限，并且不是负责人）\n        if (CrmSceneTypeEnum.isInvolved(sceneType)) {\n            if (CrmPermissionUtils.isCrmAdmin()) { // 特殊逻辑：如果是超管，直接查询所有，不过滤数据权限\n                return;\n            }\n            query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType)\n                    .eq(CrmPermissionDO::getBizId, bizId)\n                    .in(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel(), CrmPermissionLevelEnum.WRITE.getLevel())\n                    .eq(CrmPermissionDO::getUserId,userId));\n            query.ne(ownerUserIdField, userId);\n        }\n        // 场景三：下属负责的数据（下属是负责人）\n        if (CrmSceneTypeEnum.isSubordinate(sceneType)) {\n            AdminUserApi adminUserApi = SpringUtil.getBean(AdminUserApi.class);\n            List<AdminUserRespDTO> subordinateUsers = adminUserApi.getUserListBySubordinate(userId).getCheckedData();\n            if (CollUtil.isEmpty(subordinateUsers)) {\n                query.eq(ownerUserIdField, -1); // 不返回任何结果\n            } else {\n                query.in(ownerUserIdField, convertSet(subordinateUsers, AdminUserRespDTO::getId));\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 5 # 初始连接数\n        min-idle: 10 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n        slave: # 模拟从库，可根据自己需要修改 # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 400-infra.server.iocoder.cn # 地址\n    port: 6379 # 端口\n    database: 1 # 数据库索引\n#      password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  access-log: # 访问日志的配置项\n    enable: false\n  demo: false # 关闭演示模式\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 1 # 初始连接数\n        min-idle: 1 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例\n          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例\n          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例\n          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例\n          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例\n          username: root\n          password: 123456\n        #          username: sa # SQL Server 连接的示例\n        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例\n        #          username: SYSDBA # DM 连接的示例\n        #          password: SYSDBA # DM 连接的示例\n        slave: # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 6379 # 端口\n    database: 0 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n# 日志文件配置\nlogging:\n  level:\n    # 配置自己写的 MyBatis Mapper 打印日志\n    cn.iocoder.yudao.module.crm.dal.mysql: debug\n    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿：先禁用，Spring Boot 3.X 存在部分错误的 WARN 提示\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  env: # 多环境的配置项\n    tag: ${HOSTNAME}\n  security:\n    mock-enable: true\n  access-log: # 访问日志的配置项\n    enable: false\n  demo: false # 关闭演示模式\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: crm-server\n\n  profiles:\n    active: local\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n    allow-bean-definition-overriding: true # 允许 Bean 覆盖，例如说 Feign 等会存在重复定义的服务\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  # Servlet 配置\n  servlet:\n    # 文件上传相关配置项\n    multipart:\n      max-file-size: 16MB # 单个文件大小\n      max-request-size: 32MB # 设置总上传的文件大小\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  # Cache 配置项\n  cache:\n    type: REDIS\n    redis:\n      time-to-live: 1h # 设置过期时间为 1 小时\n\nserver:\n  port: 48089\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\n--- #################### 接口文档配置 ####################\n\nspringdoc:\n  api-docs:\n    enabled: true # 1. 是否开启 Swagger 接文档的元数据\n    path: /v3/api-docs\n  swagger-ui:\n    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面\n    path: /swagger-ui\n  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档\n\nknife4j:\n  enable: true\n  setting:\n    language: zh_cn\n\n# MyBatis Plus 的配置项\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: NONE # “智能”模式，基于 IdTypeEnvironmentPostProcessor + 数据源的类型，自动适配成 AUTO、INPUT 模式。\n      #      id-type: AUTO # 自增 ID，适合 MySQL 等直接自增的数据库\n      #      id-type: INPUT # 用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n      #      id-type: ASSIGN_ID # 分配 ID，默认使用雪花算法。注意，Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时，需要去除实体类上的 @KeySequence 注解\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n    banner: false # 关闭控制台的 Banner 打印\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  encryptor:\n    password: XDV71a+xqStEA3WH # 加解密的秘钥，可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成\n\nmybatis-plus-join:\n  banner: false # 关闭控制台的 Banner 打印\n\n# Spring Data Redis 配置\nspring:\n  data:\n    redis:\n      repositories:\n        enabled: false # 项目未使用到 Spring Data Redis 的 Repository，所以直接禁用，保证启动速度\n\n# VO 转换（数据翻译）相关\neasy-trans:\n  is-enable-global: false # 【默认禁用，对性能确认压力大】启用全局翻译（拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置，或通过 @IgnoreTrans 忽略某个接口\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    executor:\n      appname: ${spring.application.name} # 执行器 AppName\n      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径\n    accessToken: default_token # 执行器通讯TOKEN\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0\n    base-package: cn.iocoder.yudao.module.crm\n  web:\n    admin-ui:\n      url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址\n  xss:\n    enable: false\n    exclude-urls: # 如下 url，仅仅是为了演示，去掉配置也没关系\n      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求\n  swagger:\n    title: 管理后台\n    description: 提供管理员管理的所有功能\n    version: ${yudao.info.version}\n  tenant: # 多租户相关配置项\n    enable: true\n    ignore-urls:\n\ndebug: false\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper\">\n\n    <select id=\"selectCustomerCreateCountGroupByDate\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO\">\n        SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,\n               COUNT(*) AS customerCreateCount\n          FROM crm_customer\n         WHERE deleted = 0\n           AND owner_user_id IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n         GROUP BY time\n    </select>\n\n    <select id=\"selectCustomerDealCountGroupByDate\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO\">\n        SELECT DATE_FORMAT(customer.create_time, '%Y-%m-%d')    AS time,\n               COUNT(DISTINCT customer.id)                      AS customer_deal_count\n          FROM crm_customer AS customer\n                LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id\n         WHERE customer.deleted = 0\n           AND contract.deleted = 0\n           AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n           AND customer.owner_user_id IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n         GROUP BY time\n    </select>\n\n    <select id=\"selectCustomerCreateCountGroupByUser\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO\">\n        SELECT owner_user_id,\n               COUNT(*) AS customer_create_count\n          FROM crm_customer\n         WHERE deleted = 0\n           AND owner_user_id in\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n         GROUP BY owner_user_id\n    </select>\n\n    <select id=\"selectCustomerDealCountGroupByUser\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO\">\n        SELECT customer.owner_user_id,\n               COUNT(DISTINCT customer.id) AS customer_deal_count\n          FROM crm_customer AS customer\n                LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id\n         WHERE customer.deleted = 0\n           AND contract.deleted = 0\n           AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n           AND customer.owner_user_id IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n         GROUP BY customer.owner_user_id\n    </select>\n\n    <select id=\"selectContractPriceGroupByUser\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO\">\n        SELECT owner_user_id,\n               IFNULL(SUM(total_price), 0) AS contract_price\n          FROM crm_contract\n         WHERE deleted = 0\n           AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n           AND owner_user_id in\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n         GROUP BY owner_user_id\n    </select>\n\n    <select id=\"selectReceivablePriceGroupByUser\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO\">\n        SELECT owner_user_id,\n               IFNULL(SUM(price), 0) AS receivable_price\n          FROM crm_receivable\n         WHERE deleted = 0\n           AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n           AND owner_user_id IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n         GROUP BY owner_user_id\n    </select>\n\n    <select id=\"selectFollowUpRecordCountGroupByDate\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO\">\n        SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,\n               COUNT(*) AS follow_up_record_count\n          FROM crm_follow_up_record\n         WHERE creator IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n           AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}\n         GROUP BY time\n    </select>\n\n    <select id=\"selectFollowUpCustomerCountGroupByDate\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO\">\n        SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,\n               COUNT(DISTINCT biz_id) AS follow_up_customer_count\n          FROM crm_follow_up_record\n         WHERE creator IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n           AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}\n         GROUP BY time\n    </select>\n\n    <select id=\"selectFollowUpRecordCountGroupByUser\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO\">\n        SELECT creator as owner_user_id,\n               COUNT(*) AS follow_up_record_count\n          FROM crm_follow_up_record\n         WHERE creator IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n           AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}\n         GROUP BY creator\n    </select>\n\n    <select id=\"selectFollowUpCustomerCountGroupByUser\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO\">\n        SELECT\n            creator as owner_user_id,\n            COUNT(DISTINCT biz_id) AS follow_up_customer_count\n        FROM crm_follow_up_record\n        WHERE creator IN\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n        AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}\n        GROUP BY creator\n    </select>\n\n    <select id=\"selectFollowUpRecordCountGroupByType\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByTypeRespVO\">\n        SELECT type AS follow_up_type,\n               COUNT(*) AS follow_up_record_count\n          FROM crm_follow_up_record\n         WHERE creator IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n           AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}\n         GROUP BY follow_up_type\n    </select>\n\n    <select id=\"selectContractSummary\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO\">\n        SELECT customer.name AS customer_name,\n               customer.industry_id,\n               customer.source,\n               customer.owner_user_id,\n               customer.creator,\n               customer.create_time,\n               contract.name AS contract_name,\n               contract.total_price,\n               contract.order_date,\n               IFNULL(receivable.price, 0) AS receivable_price\n          FROM crm_customer AS customer\n                INNER JOIN crm_contract AS contract ON customer.id = contract.customer_id\n                LEFT JOIN crm_receivable AS receivable ON contract.id = receivable.contract_id\n         WHERE customer.deleted = 0 AND contract.deleted = 0 AND receivable.deleted = 0\n           AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n           AND customer.owner_user_id IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n    </select>\n\n    <!-- TIMESTAMPDIFF 用于求差值；AVG 求平均；TRUNCATE 去掉小数点、只保留整数 -->\n    <select id=\"selectCustomerDealCycleGroupByDate\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO\">\n        SELECT DATE_FORMAT(contract.order_date, '%Y-%m-%d') AS time,\n               IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0) AS customer_deal_cycle\n          FROM crm_customer AS customer\n                LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id\n         WHERE customer.deleted = 0 AND contract.deleted = 0\n           AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n           AND customer.owner_user_id IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n         GROUP BY time\n    </select>\n\n    <select id=\"selectCustomerDealCycleGroupByUser\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByUserRespVO\">\n        SELECT customer.owner_user_id,\n               IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0) AS customer_deal_cycle\n          FROM crm_customer AS customer\n                LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id\n         WHERE customer.deleted = 0 AND contract.deleted = 0\n           AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n           AND customer.owner_user_id IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n           AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n         GROUP BY customer.owner_user_id\n    </select>\n\n    <select id=\"selectCustomerDealCycleGroupByAreaId\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByAreaRespVO\">\n        SELECT customer.area_id AS area_id,\n               IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0) AS customer_deal_cycle,\n               COUNT(DISTINCT customer.id) AS customer_deal_count\n        FROM crm_customer AS customer\n                LEFT JOIN crm_contract AS contract ON customer.id = contract.customer_id\n        WHERE customer.deleted = 0\n          AND contract.deleted = 0\n          AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n          AND customer.owner_user_id IN\n                <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                    #{userId}\n                </foreach>\n          AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY\n            customer.area_id\n    </select>\n\n    <select id=\"selectCustomerDealCycleGroupByProductId\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByProductRespVO\">\n        SELECT product.name                                                                                 AS product_name,\n               IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0)   AS customer_deal_cycle,\n               COUNT(DISTINCT customer.id)                                                                  AS customer_deal_count\n          FROM crm_customer AS customer\n                LEFT JOIN crm_contract AS contract ON customer.id = contract.customer_id\n                LEFT JOIN crm_contract_product AS contract_product ON contract_product.contract_id = contract.id\n                LEFT JOIN crm_product AS product ON contract_product.product_id = product.id\n         WHERE customer.deleted = 0\n           AND contract.deleted = 0\n           AND contract_product.deleted = 0\n           AND product.deleted = 0\n           AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n           AND customer.owner_user_id IN\n                 <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                     #{userId}\n                 </foreach>\n           AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY product.id\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/resources/mapper/statistics/CrmStatisticsFunnelMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsFunnelMapper\">\n\n    <select id=\"selectCustomerCountByDate\" resultType=\"java.lang.Long\">\n        SELECT\n            COUNT(*)\n        FROM crm_customer\n        WHERE deleted = 0\n        AND owner_user_id IN\n        <!-- TODO @puhui999：这个 foreach 搞个缩进哈 - -->\n        <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n            #{userId}\n        </foreach>\n        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND\n        <!-- TODO @puhui999：下面这个，就不缩进啦 - -->\n        #{times[1],javaType=java.time.LocalDateTime}\n    </select>\n\n    <select id=\"selectBusinessCountByDateAndEndStatus\" resultType=\"java.lang.Long\">\n        SELECT\n            COUNT(*)\n        FROM crm_business\n        WHERE deleted = 0\n        <if test=\"status != null\">\n            AND end_status = #{status}\n        </if>\n        AND owner_user_id IN\n        <foreach collection=\"reqVO.userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n            #{userId}\n        </foreach>\n        AND create_time BETWEEN #{reqVO.times[0],javaType=java.time.LocalDateTime} AND\n        #{reqVO.times[1],javaType=java.time.LocalDateTime}\n    </select>\n\n    <select id=\"selectBusinessSummaryListGroupByEndStatus\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO\">\n        SELECT\n            end_status AS endStatus,\n            COUNT(*) AS businessCount,\n            SUM(total_price) AS totalPrice\n        FROM crm_business\n        WHERE deleted = 0 AND end_status IS NOT NULL\n        AND owner_user_id IN\n        <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n            #{userId}\n        </foreach>\n        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND\n        #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY end_status\n    </select>\n\n    <select id=\"selectBusinessSummaryGroupByDate\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO\">\n        SELECT\n            DATE_FORMAT(create_time, '%Y-%m-%d') AS time,\n            COUNT(*) AS businessCreateCount,\n            SUM(total_price) AS totalPrice\n        FROM crm_business\n        WHERE deleted = 0\n        AND owner_user_id IN\n        <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n            #{userId}\n        </foreach>\n        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND\n        #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY time\n    </select>\n\n    <select id=\"selectBusinessInversionRateSummaryByDate\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessInversionRateSummaryByDateRespVO\">\n        SELECT\n            DATE_FORMAT(create_time, '%Y-%m-%d') AS time,\n            COUNT(*) AS businessCount,\n            SUM(IF(end_status = 1, 1, 0)) AS businessWinCount\n        FROM crm_business\n        WHERE deleted = 0\n        AND owner_user_id IN\n        <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n            #{userId}\n        </foreach>\n        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND\n        #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY time\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/resources/mapper/statistics/CrmStatisticsPerformanceMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceMapper\">\n\n    <select id=\"selectContractCountPerformance\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO\">\n        SELECT\n            DATE_FORMAT(order_date, '%Y%m') AS time,\n            COUNT(1) AS currentMonthCount\n        FROM crm_contract\n        WHERE deleted = 0\n        AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n        AND owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND order_date between #{times[0],javaType=java.time.LocalDateTime} and\n            #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY time\n    </select>\n\n    <select id=\"selectContractPricePerformance\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO\">\n        SELECT\n            DATE_FORMAT(order_date, '%Y%m') AS time,\n            IFNULL(SUM(total_price), 0) AS currentMonthCount\n        FROM crm_contract\n        WHERE deleted = 0\n        AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n        AND owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND order_date between #{times[0],javaType=java.time.LocalDateTime} and\n            #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY time\n    </select>\n\n    <select id=\"selectReceivablePricePerformance\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO\">\n        SELECT\n            DATE_FORMAT(return_time, '%Y%m') AS time,\n            IFNULL(SUM(price), 0) AS currentMonthCount\n        FROM crm_receivable\n        WHERE deleted = 0\n        AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n        AND owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND return_time between #{times[0],javaType=java.time.LocalDateTime} and\n            #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY time\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper\">\n\n    <select id=\"selectSummaryListGroupByAreaId\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO\">\n        SELECT area_id, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount\n        FROM crm_customer\n        WHERE deleted = 0 AND area_id IS NOT NULL\n        AND owner_user_id IN\n        <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n            #{userId}\n        </foreach>\n        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime}\n        AND #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY area_id\n    </select>\n\n    <select id=\"selectCustomerIndustryListGroupByIndustryId\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO\">\n        SELECT industry_id, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount\n        FROM crm_customer\n        WHERE deleted = 0 AND industry_id IS NOT NULL\n        AND owner_user_id IN\n        <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n            #{userId}\n        </foreach>\n        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime}\n        AND #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY industry_id\n    </select>\n\n    <select id=\"selectCustomerSourceListGroupBySource\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO\">\n        SELECT source, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount\n        FROM crm_customer\n        WHERE deleted = 0 AND source IS NOT NULL\n        AND owner_user_id IN\n        <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n            #{userId}\n        </foreach>\n        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime}\n        AND #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY source\n    </select>\n\n    <select id=\"selectCustomerLevelListGroupByLevel\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO\">\n        SELECT level, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount\n        FROM crm_customer\n        WHERE deleted = 0 AND level IS NOT NULL\n        AND owner_user_id IN\n        <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n            #{userId}\n        </foreach>\n        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime}\n        AND #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY level\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankMapper\">\n\n    <select id=\"selectContractPriceRank\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO\">\n        SELECT IFNULL(SUM(total_price), 0) AS count, owner_user_id\n        FROM crm_contract\n        WHERE deleted = 0\n        AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n        and owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY owner_user_id\n    </select>\n\n    <select id=\"selectReceivablePriceRank\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO\">\n        SELECT IFNULL(SUM(price), 0) AS count, owner_user_id\n        FROM crm_receivable\n        WHERE deleted = 0\n        AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n        AND owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND return_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY owner_user_id\n    </select>\n\n    <select id=\"selectContractCountRank\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO\">\n        SELECT COUNT(1) AS count, owner_user_id\n        FROM crm_contract\n        WHERE deleted = 0\n        AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n        AND owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY owner_user_id\n    </select>\n\n    <select id=\"selectProductSalesRank\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO\">\n        SELECT COUNT(product.count) AS count, contract.owner_user_id\n        FROM crm_contract_product product\n        INNER JOIN crm_contract contract ON product.contract_id = contract.id\n        WHERE contract.deleted = 0 AND contract.deleted = 0\n        AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}\n        AND contract.owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND contract.order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY contract.owner_user_id\n    </select>\n\n    <select id=\"selectCustomerCountRank\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO\">\n        SELECT COUNT(1) AS count, owner_user_id\n        FROM crm_customer\n        WHERE deleted = 0\n        AND owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY owner_user_id\n    </select>\n\n    <select id=\"selectContactsCountRank\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO\">\n        SELECT COUNT(1) AS count, owner_user_id\n        FROM crm_contact\n        WHERE deleted = 0\n        AND owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY owner_user_id\n    </select>\n\n    <select id=\"selectFollowCountRank\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO\">\n        SELECT COUNT(1) AS count, cc.owner_user_id\n        FROM crm_follow_up_record AS cfur\n        LEFT JOIN crm_contact AS cc ON FIND_IN_SET(cc.id, cfur.contact_ids)\n        WHERE cfur.deleted = 0\n        AND cc.deleted = 0\n        AND cc.owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY cc.owner_user_id\n    </select>\n\n    <select id=\"selectFollowCustomerCountRank\"\n            resultType=\"cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO\">\n        SELECT COUNT(DISTINCT cc.id) AS count, cc.owner_user_id\n        FROM crm_follow_up_record AS cfur\n        LEFT JOIN crm_contact AS cc ON FIND_IN_SET(cc.id, cfur.contact_ids)\n        WHERE cfur.deleted = 0\n        AND cc.deleted = 0\n        AND cc.owner_user_id in\n            <foreach collection=\"userIds\" item=\"userId\" open=\"(\" close=\")\" separator=\",\">\n                #{userId}\n            </foreach>\n        AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}\n        GROUP BY cc.owner_user_id\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/test/resources/application-unit-test.yaml",
    "content": "spring:\n    main:\n        lazy-initialization: true # 开启懒加载，加快速度\n        banner-mode: off # 单元测试，禁用 Banner\n\n--- #################### 数据库相关配置 ####################\n\nspring:\n    # 数据源配置项\n    datasource:\n        name: ruoyi-vue-pro\n        url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式；DATABASE_TO_UPPER 配置表和字段使用小写\n        driver-class-name: org.h2.Driver\n        username: sa\n        password:\n        druid:\n            async-init: true # 单元测试，异步初始化 Druid 连接池，提升启动速度\n            initial-size: 1 # 单元测试，配置为 1，提升启动速度\n    sql:\n        init:\n            schema-locations: classpath:/sql/create_tables.sql\n\n    # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n    redis:\n      host: 127.0.0.1 # 地址\n      port: 16379 # 端口（单元测试，使用 16379 端口）\n      database: 0 # 数据库索引\n\nmybatis-plus:\n    lazy-initialization: true # 单元测试，设置 MyBatis Mapper 延迟加载，加速每个单元测试\n    type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject\n\n--- #################### 定时任务相关配置 ####################\n\n--- #################### 配置中心相关配置 ####################\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项（单元测试，禁用 Lock4j）\n\n--- #################### 监控相关配置 ####################\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  info:\n    base-package: cn.iocoder.yudao\n"
  },
  {
    "path": "yudao-module-crm/yudao-module-crm-server/src/test/resources/logback.xml",
    "content": "<configuration>\n    <!-- 引用 Spring Boot 的 logback 基础配置 -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n</configuration>\n"
  },
  {
    "path": "yudao-module-erp/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modules>\n        <module>yudao-module-erp-api</module>\n        <module>yudao-module-erp-server</module>\n    </modules>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-erp</artifactId>\n    <packaging>pom</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        erp 包下，企业资源管理（Enterprise Resource Planning）。\n        例如说：采购、销售、库存、财务、产品等等\n    </description>\n\n</project>"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-erp</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-erp-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        erp 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java",
    "content": "/**\n * erp API 包，定义暴露给其它模块的 API\n */\npackage cn.iocoder.yudao.module.erp.api;\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ApiConstants.java",
    "content": "package cn.iocoder.yudao.module.erp.enums;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\n\n/**\n * API 相关的枚举\n *\n * @author 芋道源码\n */\npublic class ApiConstants {\n\n    /**\n     * 服务名\n     *\n     * 注意，需要保证和 spring.application.name 保持一致\n     */\n    public static final String NAME = \"erp-server\";\n\n    public static final String PREFIX = RpcConstants.RPC_API_PREFIX + \"/erp\";\n\n    public static final String VERSION = \"1.0.0\";\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.erp.enums;\n\n/**\n * ERP 字典类型的枚举类\n *\n * @author 芋道源码\n */\npublic interface DictTypeConstants {\n\n    String AUDIT_STATUS = \"erp_audit_status\"; // 审核状态\n    String STOCK_RECORD_BIZ_TYPE = \"erp_stock_record_biz_type\"; // 库存明细的业务类型\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java",
    "content": "package cn.iocoder.yudao.module.erp.enums;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * ERP 审核状态枚举\n *\n * TODO 芋艿：目前只有待审批、已审批两个状态，未来接入工作流后，会丰富下：待提交（草稿）=》已提交（待审核）=》审核通过、审核不通过；另外，工作流需要支持“反审核”，把工作流退回到原点；\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum ErpAuditStatus implements ArrayValuable<Integer> {\n\n    PROCESS(10, \"未审核\"), // 审核中\n    APPROVE(20, \"已审核\"); // 审核通过\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpAuditStatus::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.erp.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * ERP 错误码枚举类\n * <p>\n * erp 系统，使用 1-030-000-000 段\n */\npublic interface ErrorCodeConstants {\n\n    // ========== ERP 供应商（1-030-100-000） ==========\n    ErrorCode SUPPLIER_NOT_EXISTS = new ErrorCode(1_030_100_000, \"供应商不存在\");\n    ErrorCode SUPPLIER_NOT_ENABLE = new ErrorCode(1_030_100_000, \"供应商({})未启用\");\n\n    // ========== ERP 采购订单（1-030-101-000） ==========\n    ErrorCode PURCHASE_ORDER_NOT_EXISTS = new ErrorCode(1_030_101_000, \"采购订单不存在\");\n    ErrorCode PURCHASE_ORDER_DELETE_FAIL_APPROVE = new ErrorCode(1_030_101_001, \"采购订单({})已审核，无法删除\");\n    ErrorCode PURCHASE_ORDER_PROCESS_FAIL = new ErrorCode(1_030_101_002, \"反审核失败，只有已审核的采购订单才能反审核\");\n    ErrorCode PURCHASE_ORDER_APPROVE_FAIL = new ErrorCode(1_030_101_003, \"审核失败，只有未审核的采购订单才能审核\");\n    ErrorCode PURCHASE_ORDER_NO_EXISTS = new ErrorCode(1_030_101_004, \"生成采购单号失败，请重新提交\");\n    ErrorCode PURCHASE_ORDER_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_101_005, \"采购订单({})已审核，无法修改\");\n    ErrorCode PURCHASE_ORDER_NOT_APPROVE = new ErrorCode(1_030_101_006, \"采购订单未审核，无法操作\");\n    ErrorCode PURCHASE_ORDER_ITEM_IN_FAIL_PRODUCT_EXCEED = new ErrorCode(1_030_101_007, \"采购订单项({})超过最大允许入库数量({})\");\n    ErrorCode PURCHASE_ORDER_PROCESS_FAIL_EXISTS_IN = new ErrorCode(1_030_101_008, \"反审核失败，已存在对应的采购入库单\");\nErrorCode PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED = new ErrorCode(1_030_101_009, \"采购订单项({})超过最大允许退货数量({})\");\n    ErrorCode PURCHASE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_030_101_010, \"反审核失败，已存在对应的采购退货单\");\n\n    // ========== ERP 采购入库（1-030-102-000） ==========\n    ErrorCode PURCHASE_IN_NOT_EXISTS = new ErrorCode(1_030_102_000, \"采购入库单不存在\");\n    ErrorCode PURCHASE_IN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_102_001, \"采购入库单({})已审核，无法删除\");\n    ErrorCode PURCHASE_IN_PROCESS_FAIL = new ErrorCode(1_030_102_002, \"反审核失败，只有已审核的入库单才能反审核\");\n    ErrorCode PURCHASE_IN_APPROVE_FAIL = new ErrorCode(1_030_102_003, \"审核失败，只有未审核的入库单才能审核\");\n    ErrorCode PURCHASE_IN_NO_EXISTS = new ErrorCode(1_030_102_004, \"生成入库单失败，请重新提交\");\n    ErrorCode PURCHASE_IN_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_102_005, \"采购入库单({})已审核，无法修改\");\n    ErrorCode PURCHASE_IN_NOT_APPROVE = new ErrorCode(1_030_102_006, \"采购入库单未审核，无法操作\");\n    ErrorCode PURCHASE_IN_FAIL_PAYMENT_PRICE_EXCEED = new ErrorCode(1_030_102_007, \"付款金额({})超过采购入库单总金额({})\");\n    ErrorCode PURCHASE_IN_PROCESS_FAIL_EXISTS_PAYMENT = new ErrorCode(1_030_102_008, \"反审核失败，已存在对应的付款单\");\n\n    // ========== ERP 采购退货（1-030-103-000） ==========\n    ErrorCode PURCHASE_RETURN_NOT_EXISTS = new ErrorCode(1_030_103_000, \"采购退货单不存在\");\n    ErrorCode PURCHASE_RETURN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_103_001, \"采购退货单({})已审核，无法删除\");\n    ErrorCode PURCHASE_RETURN_PROCESS_FAIL = new ErrorCode(1_030_103_002, \"反审核失败，只有已审核的退货单才能反审核\");\n    ErrorCode PURCHASE_RETURN_APPROVE_FAIL = new ErrorCode(1_030_103_003, \"审核失败，只有未审核的退货单才能审核\");\n    ErrorCode PURCHASE_RETURN_NO_EXISTS = new ErrorCode(1_030_103_004, \"生成退货单失败，请重新提交\");\n    ErrorCode PURCHASE_RETURN_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_103_005, \"采购退货单({})已审核，无法修改\");\n    ErrorCode PURCHASE_RETURN_NOT_APPROVE = new ErrorCode(1_030_103_006, \"采购退货单未审核，无法操作\");\n    ErrorCode PURCHASE_RETURN_FAIL_REFUND_PRICE_EXCEED = new ErrorCode(1_030_103_007, \"退款金额({})超过采购退货单总金额({})\");\n    ErrorCode PURCHASE_RETURN_PROCESS_FAIL_EXISTS_REFUND = new ErrorCode(1_030_103_008, \"反审核失败，已存在对应的退款单\");\n\n    // ========== ERP 客户（1-030-200-000）==========\n    ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_200_000, \"客户不存在\");\n    ErrorCode CUSTOMER_NOT_ENABLE = new ErrorCode(1_020_200_001, \"客户({})未启用\");\n\n    // ========== ERP 销售订单（1-030-201-000） ==========\n    ErrorCode SALE_ORDER_NOT_EXISTS = new ErrorCode(1_020_201_000, \"销售订单不存在\");\n    ErrorCode SALE_ORDER_DELETE_FAIL_APPROVE = new ErrorCode(1_020_201_001, \"销售订单({})已审核，无法删除\");\n    ErrorCode SALE_ORDER_PROCESS_FAIL = new ErrorCode(1_020_201_002, \"反审核失败，只有已审核的销售订单才能反审核\");\n    ErrorCode SALE_ORDER_APPROVE_FAIL = new ErrorCode(1_020_201_003, \"审核失败，只有未审核的销售订单才能审核\");\n    ErrorCode SALE_ORDER_NO_EXISTS = new ErrorCode(1_020_201_004, \"生成销售单号失败，请重新提交\");\n    ErrorCode SALE_ORDER_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_201_005, \"销售订单({})已审核，无法修改\");\n    ErrorCode SALE_ORDER_NOT_APPROVE = new ErrorCode(1_020_201_006, \"销售订单未审核，无法操作\");\n    ErrorCode SALE_ORDER_ITEM_OUT_FAIL_PRODUCT_EXCEED = new ErrorCode(1_020_201_007, \"销售订单项({})超过最大允许出库数量({})\");\n    ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_OUT = new ErrorCode(1_020_201_008, \"反审核失败，已存在对应的销售出库单\");\n    ErrorCode SALE_ORDER_ITEM_RETURN_FAIL_OUT_EXCEED = new ErrorCode(1_020_201_009, \"销售订单项({})超过最大允许退货数量({})\");\n    ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_020_201_010, \"反审核失败，已存在对应的销售退货单\");\n\n    // ========== ERP 销售出库（1-030-202-000） ==========\n    ErrorCode SALE_OUT_NOT_EXISTS = new ErrorCode(1_020_202_000, \"销售出库单不存在\");\n    ErrorCode SALE_OUT_DELETE_FAIL_APPROVE = new ErrorCode(1_020_202_001, \"销售出库单({})已审核，无法删除\");\n    ErrorCode SALE_OUT_PROCESS_FAIL = new ErrorCode(1_020_202_002, \"反审核失败，只有已审核的出库单才能反审核\");\n    ErrorCode SALE_OUT_APPROVE_FAIL = new ErrorCode(1_020_202_003, \"审核失败，只有未审核的出库单才能审核\");\n    ErrorCode SALE_OUT_NO_EXISTS = new ErrorCode(1_020_202_004, \"生成出库单失败，请重新提交\");\n    ErrorCode SALE_OUT_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_202_005, \"销售出库单({})已审核，无法修改\");\n    ErrorCode SALE_OUT_NOT_APPROVE = new ErrorCode(1_020_202_006, \"销售出库单未审核，无法操作\");\n    ErrorCode SALE_OUT_FAIL_RECEIPT_PRICE_EXCEED = new ErrorCode(1_020_202_007, \"收款金额({})超过销售出库单总金额({})\");\n    ErrorCode SALE_OUT_PROCESS_FAIL_EXISTS_RECEIPT = new ErrorCode(1_020_202_008, \"反审核失败，已存在对应的收款单\");\n\n    // ========== ERP 销售退货（1-030-203-000） ==========\n    ErrorCode SALE_RETURN_NOT_EXISTS = new ErrorCode(1_020_203_000, \"销售退货单不存在\");\n    ErrorCode SALE_RETURN_DELETE_FAIL_APPROVE = new ErrorCode(1_020_203_001, \"销售退货单({})已审核，无法删除\");\n    ErrorCode SALE_RETURN_PROCESS_FAIL = new ErrorCode(1_020_203_002, \"反审核失败，只有已审核的退货单才能反审核\");\n    ErrorCode SALE_RETURN_APPROVE_FAIL = new ErrorCode(1_020_203_003, \"审核失败，只有未审核的退货单才能审核\");\n    ErrorCode SALE_RETURN_NO_EXISTS = new ErrorCode(1_020_203_004, \"生成退货单失败，请重新提交\");\n    ErrorCode SALE_RETURN_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_203_005, \"销售退货单({})已审核，无法修改\");\n    ErrorCode SALE_RETURN_NOT_APPROVE = new ErrorCode(1_020_203_006, \"销售退货单未审核，无法操作\");\n    ErrorCode SALE_RETURN_FAIL_REFUND_PRICE_EXCEED = new ErrorCode(1_020_203_007, \"退款金额({})超过销售退货单总金额({})\");\n    ErrorCode SALE_RETURN_PROCESS_FAIL_EXISTS_REFUND = new ErrorCode(1_020_203_008, \"反审核失败，已存在对应的退款单\");\n\n    // ========== ERP 仓库 1-030-400-000 ==========\n    ErrorCode WAREHOUSE_NOT_EXISTS = new ErrorCode(1_030_400_000, \"仓库不存在\");\n    ErrorCode WAREHOUSE_NOT_ENABLE = new ErrorCode(1_030_400_001, \"仓库({})未启用\");\n\n    // ========== ERP 其它入库单 1-030-401-000 ==========\n    ErrorCode STOCK_IN_NOT_EXISTS = new ErrorCode(1_030_401_000, \"其它入库单不存在\");\n    ErrorCode STOCK_IN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_401_001, \"其它入库单({})已审核，无法删除\");\n    ErrorCode STOCK_IN_PROCESS_FAIL = new ErrorCode(1_030_401_002, \"反审核失败，只有已审核的入库单才能反审核\");\n    ErrorCode STOCK_IN_APPROVE_FAIL = new ErrorCode(1_030_401_003, \"审核失败，只有未审核的入库单才能审核\");\n    ErrorCode STOCK_IN_NO_EXISTS = new ErrorCode(1_030_401_004, \"生成入库单失败，请重新提交\");\n    ErrorCode STOCK_IN_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_401_005, \"其它入库单({})已审核，无法修改\");\n\n    // ========== ERP 其它出库单 1-030-402-000 ==========\n    ErrorCode STOCK_OUT_NOT_EXISTS = new ErrorCode(1_030_402_000, \"其它出库单不存在\");\n    ErrorCode STOCK_OUT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_402_001, \"其它出库单({})已审核，无法删除\");\n    ErrorCode STOCK_OUT_PROCESS_FAIL = new ErrorCode(1_030_402_002, \"反审核失败，只有已审核的出库单才能反审核\");\n    ErrorCode STOCK_OUT_APPROVE_FAIL = new ErrorCode(1_030_402_003, \"审核失败，只有未审核的出库单才能审核\");\n    ErrorCode STOCK_OUT_NO_EXISTS = new ErrorCode(1_030_402_004, \"生成出库单失败，请重新提交\");\n    ErrorCode STOCK_OUT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_402_005, \"其它出库单({})已审核，无法修改\");\n\n    // ========== ERP 库存调拨单 1-030-403-000 ==========\n    ErrorCode STOCK_MOVE_NOT_EXISTS = new ErrorCode(1_030_402_000, \"库存调拨单不存在\");\n    ErrorCode STOCK_MOVE_DELETE_FAIL_APPROVE = new ErrorCode(1_030_402_001, \"库存调拨单({})已审核，无法删除\");\n    ErrorCode STOCK_MOVE_PROCESS_FAIL = new ErrorCode(1_030_402_002, \"反审核失败，只有已审核的调拨单才能反审核\");\n    ErrorCode STOCK_MOVE_APPROVE_FAIL = new ErrorCode(1_030_402_003, \"审核失败，只有未审核的调拨单才能审核\");\n    ErrorCode STOCK_MOVE_NO_EXISTS = new ErrorCode(1_030_402_004, \"生成调拨号失败，请重新提交\");\n    ErrorCode STOCK_MOVE_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_402_005, \"库存调拨单({})已审核，无法修改\");\n\n    // ========== ERP 库存盘点单 1-030-403-000 ==========\n    ErrorCode STOCK_CHECK_NOT_EXISTS = new ErrorCode(1_030_403_000, \"库存盘点单不存在\");\n    ErrorCode STOCK_CHECK_DELETE_FAIL_APPROVE = new ErrorCode(1_030_403_001, \"库存盘点单({})已审核，无法删除\");\n    ErrorCode STOCK_CHECK_PROCESS_FAIL = new ErrorCode(1_030_403_002, \"反审核失败，只有已审核的盘点单才能反审核\");\n    ErrorCode STOCK_CHECK_APPROVE_FAIL = new ErrorCode(1_030_403_003, \"审核失败，只有未审核的盘点单才能审核\");\n    ErrorCode STOCK_CHECK_NO_EXISTS = new ErrorCode(1_030_403_004, \"生成盘点号失败，请重新提交\");\n    ErrorCode STOCK_CHECK_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_403_005, \"库存盘点单({})已审核，无法修改\");\n\n    // ========== ERP 产品库存 1-030-404-000 ==========\n    ErrorCode STOCK_COUNT_NEGATIVE = new ErrorCode(1_030_404_000, \"操作失败，产品({})所在仓库({})的库存：{}，小于变更数量：{}\");\n    ErrorCode STOCK_COUNT_NEGATIVE2 = new ErrorCode(1_030_404_001, \"操作失败，产品({})所在仓库({})的库存不足\");\n\n    // ========== ERP 产品 1-030-500-000 ==========\n    ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_030_500_000, \"产品不存在\");\n    ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_030_500_001, \"产品({})未启用\");\n\n    // ========== ERP 产品分类 1-030-501-000 ==========\n    ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_501_000, \"产品分类不存在\");\n    ErrorCode PRODUCT_CATEGORY_EXITS_CHILDREN = new ErrorCode(1_030_501_001, \"存在存在子产品分类，无法删除\");\n    ErrorCode PRODUCT_CATEGORY_PARENT_NOT_EXITS = new ErrorCode(1_030_501_002,\"父级产品分类不存在\");\n    ErrorCode PRODUCT_CATEGORY_PARENT_ERROR = new ErrorCode(1_030_501_003, \"不能设置自己为父产品分类\");\n    ErrorCode PRODUCT_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_030_501_004, \"已经存在该分类名称的产品分类\");\n    ErrorCode PRODUCT_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_030_501_005, \"不能设置自己的子分类为父分类\");\n    ErrorCode PRODUCT_CATEGORY_EXITS_PRODUCT = new ErrorCode(1_030_502_002, \"存在产品使用该分类，无法删除\");\n\n    // ========== ERP 产品单位 1-030-502-000 ==========\n    ErrorCode PRODUCT_UNIT_NOT_EXISTS = new ErrorCode(1_030_502_000, \"产品单位不存在\");\n    ErrorCode PRODUCT_UNIT_NAME_DUPLICATE = new ErrorCode(1_030_502_001, \"已存在该名字的产品单位\");\n    ErrorCode PRODUCT_UNIT_EXITS_PRODUCT = new ErrorCode(1_030_502_002, \"存在产品使用该单位，无法删除\");\n\n    // ========== ERP 结算账户 1-030-600-000 ==========\n    ErrorCode ACCOUNT_NOT_EXISTS = new ErrorCode(1_030_600_000, \"结算账户不存在\");\n    ErrorCode ACCOUNT_NOT_ENABLE = new ErrorCode(1_030_600_001, \"结算账户({})未启用\");\n\n    // ========== ERP 付款单 1-030-601-000 ==========\n    ErrorCode FINANCE_PAYMENT_NOT_EXISTS = new ErrorCode(1_030_601_000, \"付款单不存在\");\n    ErrorCode FINANCE_PAYMENT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_601_001, \"付款单({})已审核，无法删除\");\n    ErrorCode FINANCE_PAYMENT_PROCESS_FAIL = new ErrorCode(1_030_601_002, \"反审核失败，只有已审核的付款单才能反审核\");\n    ErrorCode FINANCE_PAYMENT_APPROVE_FAIL = new ErrorCode(1_030_601_003, \"审核失败，只有未审核的付款单才能审核\");\n    ErrorCode FINANCE_PAYMENT_NO_EXISTS = new ErrorCode(1_030_601_004, \"生成付款单号失败，请重新提交\");\n    ErrorCode FINANCE_PAYMENT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_601_005, \"付款单({})已审核，无法修改\");\n\n    // ========== ERP 收款单 1-030-602-000 ==========\n    ErrorCode FINANCE_RECEIPT_NOT_EXISTS = new ErrorCode(1_030_602_000, \"收款单不存在\");\n    ErrorCode FINANCE_RECEIPT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_602_001, \"收款单({})已审核，无法删除\");\n    ErrorCode FINANCE_RECEIPT_PROCESS_FAIL = new ErrorCode(1_030_602_002, \"反审核失败，只有已审核的收款单才能反审核\");\n    ErrorCode FINANCE_RECEIPT_APPROVE_FAIL = new ErrorCode(1_030_602_003, \"审核失败，只有未审核的收款单才能审核\");\n    ErrorCode FINANCE_RECEIPT_NO_EXISTS = new ErrorCode(1_030_602_004, \"生成收款单号失败，请重新提交\");\n    ErrorCode FINANCE_RECEIPT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_602_005, \"收款单({})已审核，无法修改\");\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java",
    "content": "package cn.iocoder.yudao.module.erp.enums;\n\n/**\n * ERP 操作日志枚举\n * 目的：统一管理，也减少 Service 里各种“复杂”字符串\n *\n * @author 芋道源码\n */\npublic interface LogRecordConstants {\n\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.erp.enums.common;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * ERP 业务类型枚举\n *\n * @author HUIHUI\n */\n@RequiredArgsConstructor\n@Getter\npublic enum ErpBizTypeEnum implements ArrayValuable<Integer> {\n\n    PURCHASE_ORDER(10, \"采购订单\"),\n    PURCHASE_IN(11, \"采购入库\"),\n    PURCHASE_RETURN(12, \"采购退货\"),\n\n    SALE_ORDER(20, \"销售订单\"),\n    SALE_OUT(21, \"销售出库\"),\n    SALE_RETURN(22, \"销售退货\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpBizTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 名称\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.erp.enums.stock;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * ERP 库存明细 - 业务类型枚举\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum ErpStockRecordBizTypeEnum implements ArrayValuable<Integer> {\n\n    OTHER_IN(10, \"其它入库\"),\n    OTHER_IN_CANCEL(11, \"其它入库（作废）\"),\n\n    OTHER_OUT(20, \"其它出库\"),\n    OTHER_OUT_CANCEL(21, \"其它出库（作废）\"),\n\n    MOVE_IN(30, \"调拨入库\"),\n    MOVE_IN_CANCEL(31, \"调拨入库（作废）\"),\n    MOVE_OUT(32, \"调拨出库\"),\n    MOVE_OUT_CANCEL(33, \"调拨出库（作废）\"),\n\n    CHECK_MORE_IN(40, \"盘盈入库\"),\n    CHECK_MORE_IN_CANCEL(41, \"盘盈入库（作废）\"),\n    CHECK_LESS_OUT(42, \"盘亏出库\"),\n    CHECK_LESS_OUT_CANCEL(43, \"盘亏出库（作废）\"),\n\n    SALE_OUT(50, \"销售出库\"),\n    SALE_OUT_CANCEL(51, \"销售出库（作废）\"),\n\n    SALE_RETURN(60, \"销售退货入库\"),\n    SALE_RETURN_CANCEL(61, \"销售退货入库（作废）\"),\n\n    PURCHASE_IN(70, \"采购入库\"),\n    PURCHASE_IN_CANCEL(71, \"采购入库（作废）\"),\n\n    PURCHASE_RETURN(80, \"采购退货出库\"),\n    PURCHASE_RETURN_CANCEL(81, \"采购退货出库（作废）\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpStockRecordBizTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:8-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-module-erp-server\nWORKDIR /yudao-module-erp-server\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-module-erp-server.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48088 端口\nEXPOSE 48088\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-erp</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-erp-server</artifactId>\n\n    <name>${project.artifactId}</name>\n    <description>\n        erp 包下，企业资源管理（Enterprise Resource Planning）。\n        例如说：采购、销售、库存、财务、产品等等\n    </description>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-system-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-erp-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/ErpServerApplication.java",
    "content": "package cn.iocoder.yudao.module.erp;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n * <p>\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication\npublic class ErpServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(ErpServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - ERP 结算账户\")\n@RestController\n@RequestMapping(\"/erp/account\")\n@Validated\npublic class ErpAccountController {\n\n    @Resource\n    private ErpAccountService accountService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建结算账户\")\n    @PreAuthorize(\"@ss.hasPermission('erp:account:create')\")\n    public CommonResult<Long> createAccount(@Valid @RequestBody ErpAccountSaveReqVO createReqVO) {\n        return success(accountService.createAccount(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新结算账户\")\n    @PreAuthorize(\"@ss.hasPermission('erp:account:update')\")\n    public CommonResult<Boolean> updateAccount(@Valid @RequestBody ErpAccountSaveReqVO updateReqVO) {\n        accountService.updateAccount(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-default-status\")\n    @Operation(summary = \"更新结算账户默认状态\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"编号\", required = true),\n            @Parameter(name = \"status\", description = \"状态\", required = true)\n    })\n    public CommonResult<Boolean> updateAccountDefaultStatus(@RequestParam(\"id\") Long id,\n                                                              @RequestParam(\"defaultStatus\") Boolean defaultStatus) {\n        accountService.updateAccountDefaultStatus(id, defaultStatus);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除结算账户\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:account:delete')\")\n    public CommonResult<Boolean> deleteAccount(@RequestParam(\"id\") Long id) {\n        accountService.deleteAccount(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得结算账户\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:account:query')\")\n    public CommonResult<ErpAccountRespVO> getAccount(@RequestParam(\"id\") Long id) {\n        ErpAccountDO account = accountService.getAccount(id);\n        return success(BeanUtils.toBean(account, ErpAccountRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得结算账户精简列表\", description = \"只包含被开启的结算账户，主要用于前端的下拉选项\")\n    public CommonResult<List<ErpAccountRespVO>> getWarehouseSimpleList() {\n        List<ErpAccountDO> list = accountService.getAccountListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, account -> new ErpAccountRespVO().setId(account.getId())\n                .setName(account.getName()).setDefaultStatus(account.getDefaultStatus())));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得结算账户分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:account:query')\")\n    public CommonResult<PageResult<ErpAccountRespVO>> getAccountPage(@Valid ErpAccountPageReqVO pageReqVO) {\n        PageResult<ErpAccountDO> pageResult = accountService.getAccountPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, ErpAccountRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出结算账户 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:account:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportAccountExcel(@Valid ErpAccountPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpAccountDO> list = accountService.getAccountPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"结算账户.xls\", \"数据\", ErpAccountRespVO.class,\n                        BeanUtils.toBean(list, ErpAccountRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpFinancePaymentService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\n\n@Tag(name = \"管理后台 - ERP 付款单\")\n@RestController\n@RequestMapping(\"/erp/finance-payment\")\n@Validated\npublic class ErpFinancePaymentController {\n\n    @Resource\n    private ErpFinancePaymentService financePaymentService;\n    @Resource\n    private ErpSupplierService supplierService;\n    @Resource\n    private ErpAccountService accountService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建付款单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-payment:create')\")\n    public CommonResult<Long> createFinancePayment(@Valid @RequestBody ErpFinancePaymentSaveReqVO createReqVO) {\n        return success(financePaymentService.createFinancePayment(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新付款单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-payment:update')\")\n    public CommonResult<Boolean> updateFinancePayment(@Valid @RequestBody ErpFinancePaymentSaveReqVO updateReqVO) {\n        financePaymentService.updateFinancePayment(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新付款单的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-payment:update-status')\")\n    public CommonResult<Boolean> updateFinancePaymentStatus(@RequestParam(\"id\") Long id,\n                                                           @RequestParam(\"status\") Integer status) {\n        financePaymentService.updateFinancePaymentStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除付款单\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-payment:delete')\")\n    public CommonResult<Boolean> deleteFinancePayment(@RequestParam(\"ids\") List<Long> ids) {\n        financePaymentService.deleteFinancePayment(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得付款单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-payment:query')\")\n    public CommonResult<ErpFinancePaymentRespVO> getFinancePayment(@RequestParam(\"id\") Long id) {\n        ErpFinancePaymentDO payment = financePaymentService.getFinancePayment(id);\n        if (payment == null) {\n            return success(null);\n        }\n        List<ErpFinancePaymentItemDO> paymentItemList = financePaymentService.getFinancePaymentItemListByPaymentId(id);\n        return success(BeanUtils.toBean(payment, ErpFinancePaymentRespVO.class, financePaymentVO ->\n                financePaymentVO.setItems(BeanUtils.toBean(paymentItemList, ErpFinancePaymentRespVO.Item.class))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得付款单分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-payment:query')\")\n    public CommonResult<PageResult<ErpFinancePaymentRespVO>> getFinancePaymentPage(@Valid ErpFinancePaymentPageReqVO pageReqVO) {\n        PageResult<ErpFinancePaymentDO> pageResult = financePaymentService.getFinancePaymentPage(pageReqVO);\n        return success(buildFinancePaymentVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出付款单 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-payment:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportFinancePaymentExcel(@Valid ErpFinancePaymentPageReqVO pageReqVO,\n                                         HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpFinancePaymentRespVO> list = buildFinancePaymentVOPageResult(financePaymentService.getFinancePaymentPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"付款单.xls\", \"数据\", ErpFinancePaymentRespVO.class, list);\n    }\n\n    private PageResult<ErpFinancePaymentRespVO> buildFinancePaymentVOPageResult(PageResult<ErpFinancePaymentDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 付款项\n        List<ErpFinancePaymentItemDO> paymentItemList = financePaymentService.getFinancePaymentItemListByPaymentIds(\n                convertSet(pageResult.getList(), ErpFinancePaymentDO::getId));\n        Map<Long, List<ErpFinancePaymentItemDO>> financePaymentItemMap = convertMultiMap(paymentItemList, ErpFinancePaymentItemDO::getPaymentId);\n        // 1.2 供应商信息\n        Map<Long, ErpSupplierDO> supplierMap = supplierService.getSupplierMap(\n                convertSet(pageResult.getList(), ErpFinancePaymentDO::getSupplierId));\n        // 1.3 结算账户信息\n        Map<Long, ErpAccountDO> accountMap = accountService.getAccountMap(\n                convertSet(pageResult.getList(), ErpFinancePaymentDO::getAccountId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),\n                contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getFinanceUserId())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpFinancePaymentRespVO.class, payment -> {\n            payment.setItems(BeanUtils.toBean(financePaymentItemMap.get(payment.getId()), ErpFinancePaymentRespVO.Item.class));\n            MapUtils.findAndThen(supplierMap, payment.getSupplierId(), supplier -> payment.setSupplierName(supplier.getName()));\n            MapUtils.findAndThen(accountMap, payment.getAccountId(), account -> payment.setAccountName(account.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(payment.getCreator()), user -> payment.setCreatorName(user.getNickname()));\n            MapUtils.findAndThen(userMap, payment.getFinanceUserId(), user -> payment.setFinanceUserName(user.getNickname()));\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinanceReceiptController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpFinanceReceiptService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\n\n@Tag(name = \"管理后台 - ERP 收款单\")\n@RestController\n@RequestMapping(\"/erp/finance-receipt\")\n@Validated\npublic class ErpFinanceReceiptController {\n\n    @Resource\n    private ErpFinanceReceiptService financeReceiptService;\n    @Resource\n    private ErpCustomerService customerService;\n    @Resource\n    private ErpAccountService accountService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建收款单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-receipt:create')\")\n    public CommonResult<Long> createFinanceReceipt(@Valid @RequestBody ErpFinanceReceiptSaveReqVO createReqVO) {\n        return success(financeReceiptService.createFinanceReceipt(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新收款单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-receipt:update')\")\n    public CommonResult<Boolean> updateFinanceReceipt(@Valid @RequestBody ErpFinanceReceiptSaveReqVO updateReqVO) {\n        financeReceiptService.updateFinanceReceipt(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新收款单的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-receipt:update-status')\")\n    public CommonResult<Boolean> updateFinanceReceiptStatus(@RequestParam(\"id\") Long id,\n                                                           @RequestParam(\"status\") Integer status) {\n        financeReceiptService.updateFinanceReceiptStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除收款单\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-receipt:delete')\")\n    public CommonResult<Boolean> deleteFinanceReceipt(@RequestParam(\"ids\") List<Long> ids) {\n        financeReceiptService.deleteFinanceReceipt(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得收款单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-receipt:query')\")\n    public CommonResult<ErpFinanceReceiptRespVO> getFinanceReceipt(@RequestParam(\"id\") Long id) {\n        ErpFinanceReceiptDO receipt = financeReceiptService.getFinanceReceipt(id);\n        if (receipt == null) {\n            return success(null);\n        }\n        List<ErpFinanceReceiptItemDO> receiptItemList = financeReceiptService.getFinanceReceiptItemListByReceiptId(id);\n        return success(BeanUtils.toBean(receipt, ErpFinanceReceiptRespVO.class, financeReceiptVO ->\n                financeReceiptVO.setItems(BeanUtils.toBean(receiptItemList, ErpFinanceReceiptRespVO.Item.class))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得收款单分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-receipt:query')\")\n    public CommonResult<PageResult<ErpFinanceReceiptRespVO>> getFinanceReceiptPage(@Valid ErpFinanceReceiptPageReqVO pageReqVO) {\n        PageResult<ErpFinanceReceiptDO> pageResult = financeReceiptService.getFinanceReceiptPage(pageReqVO);\n        return success(buildFinanceReceiptVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出收款单 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:finance-receipt:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportFinanceReceiptExcel(@Valid ErpFinanceReceiptPageReqVO pageReqVO,\n                                         HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpFinanceReceiptRespVO> list = buildFinanceReceiptVOPageResult(financeReceiptService.getFinanceReceiptPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"收款单.xls\", \"数据\", ErpFinanceReceiptRespVO.class, list);\n    }\n\n    private PageResult<ErpFinanceReceiptRespVO> buildFinanceReceiptVOPageResult(PageResult<ErpFinanceReceiptDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 收款项\n        List<ErpFinanceReceiptItemDO> receiptItemList = financeReceiptService.getFinanceReceiptItemListByReceiptIds(\n                convertSet(pageResult.getList(), ErpFinanceReceiptDO::getId));\n        Map<Long, List<ErpFinanceReceiptItemDO>> financeReceiptItemMap = convertMultiMap(receiptItemList, ErpFinanceReceiptItemDO::getReceiptId);\n        // 1.2 客户信息\n        Map<Long, ErpCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(pageResult.getList(), ErpFinanceReceiptDO::getCustomerId));\n        // 1.3 结算账户信息\n        Map<Long, ErpAccountDO> accountMap = accountService.getAccountMap(\n                convertSet(pageResult.getList(), ErpFinanceReceiptDO::getAccountId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),\n                contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getFinanceUserId())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpFinanceReceiptRespVO.class, receipt -> {\n            receipt.setItems(BeanUtils.toBean(financeReceiptItemMap.get(receipt.getId()), ErpFinanceReceiptRespVO.Item.class));\n            MapUtils.findAndThen(customerMap, receipt.getCustomerId(), customer -> receipt.setCustomerName(customer.getName()));\n            MapUtils.findAndThen(accountMap, receipt.getAccountId(), account -> receipt.setAccountName(account.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(receipt.getCreator()), user -> receipt.setCreatorName(user.getNickname()));\n            MapUtils.findAndThen(userMap, receipt.getFinanceUserId(), user -> receipt.setFinanceUserName(user.getNickname()));\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - ERP 结算账户分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpAccountPageReqVO extends PageParam {\n\n    @Schema(description = \"账户编码\", example = \"A88\")\n    private String no;\n\n    @Schema(description = \"账户名称\", example = \"张三\")\n    private String name;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.module.system.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - ERP 结算账户 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpAccountRespVO {\n\n    @Schema(description = \"结算账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28684\")\n    @ExcelProperty(\"结算账户编号\")\n    private Long id;\n\n    @Schema(description = \"账户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    @ExcelProperty(\"账户名称\")\n    private String name;\n\n    @Schema(description = \"账户编码\", example = \"A88\")\n    @ExcelProperty(\"账户编码\")\n    private String no;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(\"开启状态\")\n    @DictFormat(DictTypeConstants.COMMON_STATUS)\n    private Integer status;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(\"排序\")\n    private Integer sort;\n\n    @Schema(description = \"是否默认\", example = \"1\")\n    @ExcelProperty(\"是否默认\")\n    private Boolean defaultStatus;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - ERP 结算账户新增/修改 Request VO\")\n@Data\npublic class ErpAccountSaveReqVO {\n\n    @Schema(description = \"结算账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28684\")\n    private Long id;\n\n    @Schema(description = \"账户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    @NotEmpty(message = \"账户名称不能为空\")\n    private String name;\n\n    @Schema(description = \"账户编码\", example = \"A88\")\n    private String no;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"开启状态不能为空\")\n    @InEnum(value = CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 付款单分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpFinancePaymentPageReqVO extends PageParam {\n\n    @Schema(description = \"付款单编号\", example = \"XS001\")\n    private String no;\n\n    @Schema(description = \"付款时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] paymentTime;\n\n    @Schema(description = \"供应商编号\", example = \"1724\")\n    private Long supplierId;\n\n    @Schema(description = \"创建者\", example = \"666\")\n    private String creator;\n\n    @Schema(description = \"财务人员编号\", example = \"888\")\n    private String financeUserId;\n\n    @Schema(description = \"结算账户编号\", example = \"31189\")\n    private Long accountId;\n\n    @Schema(description = \"付款状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"业务编号\", example = \"123\")\n    private String bizNo;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 付款单 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpFinancePaymentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23752\")\n    private Long id;\n\n    @Schema(description = \"付款单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"FKD888\")\n    private String no;\n\n    @Schema(description = \"付款状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"付款时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime paymentTime;\n\n    @Schema(description = \"财务人员编号\", example = \"19690\")\n    private Long financeUserId;\n    @Schema(description = \"财务人员名称\", example = \"张三\")\n    private String financeUserName;\n\n    @Schema(description = \"供应商编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29399\")\n    private Long supplierId;\n    @Schema(description = \"供应商名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"小番茄公司\")\n    private String supplierName;\n\n    @Schema(description = \"付款账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28989\")\n    private Long accountId;\n    @Schema(description = \"付款账户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    private String accountName;\n\n    @Schema(description = \"合计价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13832\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11600\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"实际价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n    private BigDecimal paymentPrice;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"付款项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"付款项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"业务类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer bizType;\n\n        @Schema(description = \"业务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        private Long bizId;\n\n        @Schema(description = \"业务单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        private String bizNo;\n\n        @Schema(description = \"应付金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        private BigDecimal totalPrice;\n\n        @Schema(description = \"已付金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        private BigDecimal paidPrice;\n\n        @Schema(description = \"本次付款，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        @NotNull(message = \"本次付款不能为空\")\n        private BigDecimal paymentPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 付款单新增/修改 Request VO\")\n@Data\npublic class ErpFinancePaymentSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23752\")\n    private Long id;\n\n    @Schema(description = \"付款时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"付款时间不能为空\")\n    private LocalDateTime paymentTime;\n\n    @Schema(description = \"财务人员编号\", example = \"19690\")\n    private Long financeUserId;\n\n    @Schema(description = \"供应商编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29399\")\n    @NotNull(message = \"供应商编号不能为空\")\n    private Long supplierId;\n\n    @Schema(description = \"付款账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28989\")\n    @NotNull(message = \"付款账户编号不能为空\")\n    private Long accountId;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11600\")\n    @NotNull(message = \"优惠金额不能为空\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"付款项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"付款项列表不能为空\")\n    @Valid\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"付款项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"业务类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        @NotNull(message = \"业务类型不能为空\")\n        private Integer bizType;\n\n        @Schema(description = \"业务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        @NotNull(message = \"业务编号不能为空\")\n        private Long bizId;\n\n        @Schema(description = \"已付金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        @NotNull(message = \"已付金额不能为空\")\n        private BigDecimal paidPrice;\n\n        @Schema(description = \"本次付款，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        @NotNull(message = \"本次付款不能为空\")\n        private BigDecimal paymentPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 收款单分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpFinanceReceiptPageReqVO extends PageParam {\n\n    @Schema(description = \"收款单编号\", example = \"XS001\")\n    private String no;\n\n    @Schema(description = \"收款时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] receiptTime;\n\n    @Schema(description = \"客户编号\", example = \"1724\")\n    private Long customerId;\n\n    @Schema(description = \"创建者\", example = \"666\")\n    private String creator;\n\n    @Schema(description = \"财务人员编号\", example = \"888\")\n    private String financeUserId;\n\n    @Schema(description = \"收款账户编号\", example = \"31189\")\n    private Long accountId;\n\n    @Schema(description = \"收款状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"业务编号\", example = \"123\")\n    private String bizNo;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 收款单 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpFinanceReceiptRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23752\")\n    private Long id;\n\n    @Schema(description = \"收款单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"FKD888\")\n    private String no;\n\n    @Schema(description = \"收款状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"收款时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime receiptTime;\n\n    @Schema(description = \"财务人员编号\", example = \"19690\")\n    private Long financeUserId;\n    @Schema(description = \"财务人员名称\", example = \"张三\")\n    private String financeUserName;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29399\")\n    private Long customerId;\n    @Schema(description = \"客户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"小番茄公司\")\n    private String customerName;\n\n    @Schema(description = \"收款账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28989\")\n    private Long accountId;\n    @Schema(description = \"收款账户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    private String accountName;\n\n    @Schema(description = \"合计价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13832\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11600\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"实际价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n    private BigDecimal receiptPrice;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"收款项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"收款项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"业务类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer bizType;\n\n        @Schema(description = \"业务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        private Long bizId;\n\n        @Schema(description = \"业务单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        private String bizNo;\n\n        @Schema(description = \"应收金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        private BigDecimal totalPrice;\n\n        @Schema(description = \"已收金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        private BigDecimal receiptedPrice;\n\n        @Schema(description = \"本次收款，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        @NotNull(message = \"本次收款不能为空\")\n        private BigDecimal receiptPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 收款单新增/修改 Request VO\")\n@Data\npublic class ErpFinanceReceiptSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23752\")\n    private Long id;\n\n    @Schema(description = \"收款时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"收款时间不能为空\")\n    private LocalDateTime receiptTime;\n\n    @Schema(description = \"财务人员编号\", example = \"19690\")\n    private Long financeUserId;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29399\")\n    @NotNull(message = \"客户编号不能为空\")\n    private Long customerId;\n\n    @Schema(description = \"收款账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28989\")\n    @NotNull(message = \"收款账户编号不能为空\")\n    private Long accountId;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11600\")\n    @NotNull(message = \"优惠金额不能为空\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"收款项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"收款项列表不能为空\")\n    @Valid\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"收款项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"业务类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        @NotNull(message = \"业务类型不能为空\")\n        private Integer bizType;\n\n        @Schema(description = \"业务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        @NotNull(message = \"业务编号不能为空\")\n        private Long bizId;\n\n        @Schema(description = \"已收金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        @NotNull(message = \"已收金额不能为空\")\n        private BigDecimal receiptedPrice;\n\n        @Schema(description = \"本次收款，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n        @NotNull(message = \"本次收款不能为空\")\n        private BigDecimal receiptPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategorySaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductCategoryService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - ERP 产品分类\")\n@RestController\n@RequestMapping(\"/erp/product-category\")\n@Validated\npublic class ErpProductCategoryController {\n\n    @Resource\n    private ErpProductCategoryService productCategoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建产品分类\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-category:create')\")\n    public CommonResult<Long> createProductCategory(@Valid @RequestBody ErpProductCategorySaveReqVO createReqVO) {\n        return success(productCategoryService.createProductCategory(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新产品分类\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-category:update')\")\n    public CommonResult<Boolean> updateProductCategory(@Valid @RequestBody ErpProductCategorySaveReqVO updateReqVO) {\n        productCategoryService.updateProductCategory(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除产品分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:product-category:delete')\")\n    public CommonResult<Boolean> deleteProductCategory(@RequestParam(\"id\") Long id) {\n        productCategoryService.deleteProductCategory(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-category:query')\")\n    public CommonResult<ErpProductCategoryRespVO> getProductCategory(@RequestParam(\"id\") Long id) {\n        ErpProductCategoryDO category = productCategoryService.getProductCategory(id);\n        return success(BeanUtils.toBean(category, ErpProductCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得产品分类列表\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-category:query')\")\n    public CommonResult<List<ErpProductCategoryRespVO>> getProductCategoryList(@Valid ErpProductCategoryListReqVO listReqVO) {\n        List<ErpProductCategoryDO> list = productCategoryService.getProductCategoryList(listReqVO);\n        return success(BeanUtils.toBean(list, ErpProductCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得产品分类精简列表\", description = \"只包含被开启的分类，主要用于前端的下拉选项\")\n    public CommonResult<List<ErpProductCategoryRespVO>> getProductCategorySimpleList() {\n        List<ErpProductCategoryDO> list = productCategoryService.getProductCategoryList(\n                new ErpProductCategoryListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));\n        return success(convertList(list, category -> new ErpProductCategoryRespVO()\n                .setId(category.getId()).setName(category.getName()).setParentId(category.getParentId())));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出产品分类 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-category:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportProductCategoryExcel(@Valid ErpProductCategoryListReqVO listReqVO,\n              HttpServletResponse response) throws IOException {\n        List<ErpProductCategoryDO> list = productCategoryService.getProductCategoryList(listReqVO);\n        // 导出 Excel\n        ExcelUtils.write(response, \"产品分类.xls\", \"数据\", ErpProductCategoryRespVO.class,\n                        BeanUtils.toBean(list, ErpProductCategoryRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - ERP 产品\")\n@RestController\n@RequestMapping(\"/erp/product\")\n@Validated\npublic class ErpProductController {\n\n    @Resource\n    private ErpProductService productService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建产品\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product:create')\")\n    public CommonResult<Long> createProduct(@Valid @RequestBody ProductSaveReqVO createReqVO) {\n        return success(productService.createProduct(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新产品\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product:update')\")\n    public CommonResult<Boolean> updateProduct(@Valid @RequestBody ProductSaveReqVO updateReqVO) {\n        productService.updateProduct(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除产品\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:product:delete')\")\n    public CommonResult<Boolean> deleteProduct(@RequestParam(\"id\") Long id) {\n        productService.deleteProduct(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product:query')\")\n    public CommonResult<ErpProductRespVO> getProduct(@RequestParam(\"id\") Long id) {\n        ErpProductDO product = productService.getProduct(id);\n        return success(BeanUtils.toBean(product, ErpProductRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得产品分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product:query')\")\n    public CommonResult<PageResult<ErpProductRespVO>> getProductPage(@Valid ErpProductPageReqVO pageReqVO) {\n        return success(productService.getProductVOPage(pageReqVO));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得产品精简列表\", description = \"只包含被开启的产品，主要用于前端的下拉选项\")\n    public CommonResult<List<ErpProductRespVO>> getProductSimpleList() {\n        List<ErpProductRespVO> list = productService.getProductVOListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, product -> new ErpProductRespVO().setId(product.getId())\n                .setName(product.getName()).setBarCode(product.getBarCode())\n                .setCategoryId(product.getCategoryId()).setCategoryName(product.getCategoryName())\n                .setUnitId(product.getUnitId()).setUnitName(product.getUnitName())\n                .setPurchasePrice(product.getPurchasePrice()).setSalePrice(product.getSalePrice()).setMinPrice(product.getMinPrice())));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出产品 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportProductExcel(@Valid ErpProductPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        PageResult<ErpProductRespVO> pageResult = productService.getProductVOPage(pageReqVO);\n        // 导出 Excel\n        ExcelUtils.write(response, \"产品.xls\", \"数据\", ErpProductRespVO.class,\n                pageResult.getList());\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductUnitController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductUnitService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - ERP 产品单位\")\n@RestController\n@RequestMapping(\"/erp/product-unit\")\n@Validated\npublic class ErpProductUnitController {\n\n    @Resource\n    private ErpProductUnitService productUnitService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建产品单位\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-unit:create')\")\n    public CommonResult<Long> createProductUnit(@Valid @RequestBody ErpProductUnitSaveReqVO createReqVO) {\n        return success(productUnitService.createProductUnit(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新产品单位\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-unit:update')\")\n    public CommonResult<Boolean> updateProductUnit(@Valid @RequestBody ErpProductUnitSaveReqVO updateReqVO) {\n        productUnitService.updateProductUnit(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除产品单位\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:product-unit:delete')\")\n    public CommonResult<Boolean> deleteProductUnit(@RequestParam(\"id\") Long id) {\n        productUnitService.deleteProductUnit(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品单位\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-unit:query')\")\n    public CommonResult<ErpProductUnitRespVO> getProductUnit(@RequestParam(\"id\") Long id) {\n        ErpProductUnitDO productUnit = productUnitService.getProductUnit(id);\n        return success(BeanUtils.toBean(productUnit, ErpProductUnitRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得产品单位分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-unit:query')\")\n    public CommonResult<PageResult<ErpProductUnitRespVO>> getProductUnitPage(@Valid ErpProductUnitPageReqVO pageReqVO) {\n        PageResult<ErpProductUnitDO> pageResult = productUnitService.getProductUnitPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, ErpProductUnitRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得产品单位精简列表\", description = \"只包含被开启的单位，主要用于前端的下拉选项\")\n    public CommonResult<List<ErpProductUnitRespVO>> getProductUnitSimpleList() {\n        List<ErpProductUnitDO> list = productUnitService.getProductUnitListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, unit -> new ErpProductUnitRespVO().setId(unit.getId()).setName(unit.getName())));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出产品单位 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:product-unit:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportProductUnitExcel(@Valid ErpProductUnitPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpProductUnitDO> list = productUnitService.getProductUnitPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"产品单位.xls\", \"数据\", ErpProductUnitRespVO.class,\n                        BeanUtils.toBean(list, ErpProductUnitRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryListReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - ERP 产品分类列表 Request VO\")\n@Data\npublic class ErpProductCategoryListReqVO {\n\n    @Schema(description = \"分类名称\", example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"开启状态\", example = \"1\")\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product.vo.category;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.system.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - ERP 产品分类 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpProductCategoryRespVO {\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5860\")\n    @ExcelProperty(\"分类编号\")\n    private Long id;\n\n    @Schema(description = \"父分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"21829\")\n    @ExcelProperty(\"父分类编号\")\n    private Long parentId;\n\n    @Schema(description = \"分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @ExcelProperty(\"分类名称\")\n    private String name;\n\n    @Schema(description = \"分类编码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"S110\")\n    @ExcelProperty(\"分类编码\")\n    private String code;\n\n    @Schema(description = \"分类排序\", example = \"10\")\n    @ExcelProperty(\"分类排序\")\n    private Integer sort;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"开启状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.COMMON_STATUS)\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategorySaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - ERP 产品分类新增/修改 Request VO\")\n@Data\npublic class ErpProductCategorySaveReqVO {\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5860\")\n    private Long id;\n\n    @Schema(description = \"父分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"21829\")\n    @NotNull(message = \"父分类编号不能为空\")\n    private Long parentId;\n\n    @Schema(description = \"分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @NotEmpty(message = \"分类名称不能为空\")\n    private String name;\n\n    @Schema(description = \"分类编码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"S110\")\n    @NotEmpty(message = \"分类编码不能为空\")\n    private String code;\n\n    @Schema(description = \"分类排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"分类排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"开启状态不能为空\")\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 产品分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpProductPageReqVO extends PageParam {\n\n    @Schema(description = \"产品名称\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"产品分类编号\", example = \"11161\")\n    private Long categoryId;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - ERP 产品 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpProductRespVO {\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15672\")\n    @ExcelProperty(\"产品编号\")\n    private Long id;\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @ExcelProperty(\"产品名称\")\n    private String name;\n\n    @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"X110\")\n    @ExcelProperty(\"产品条码\")\n    private String barCode;\n\n    @Schema(description = \"产品分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11161\")\n    private Long categoryId;\n    @Schema(description = \"产品分类\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"水果\")\n    @ExcelProperty(\"产品分类\")\n    private String categoryName;\n\n    @Schema(description = \"单位编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8869\")\n    private Long unitId;\n    @Schema(description = \"单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"个\")\n    @ExcelProperty(\"单位\")\n    private String unitName;\n\n    @Schema(description = \"产品状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"产品状态\")\n    private Integer status;\n\n    @Schema(description = \"产品规格\", example = \"红色\")\n    @ExcelProperty(\"产品规格\")\n    private String standard;\n\n    @Schema(description = \"产品备注\", example = \"你猜\")\n    @ExcelProperty(\"产品备注\")\n    private String remark;\n\n    @Schema(description = \"保质期天数\", example = \"10\")\n    @ExcelProperty(\"保质期天数\")\n    private Integer expiryDay;\n\n    @Schema(description = \"基础重量（kg）\", example = \"1.00\")\n    @ExcelProperty(\"基础重量（kg）\")\n    private BigDecimal weight;\n\n    @Schema(description = \"采购价格，单位：元\", example = \"10.30\")\n    @ExcelProperty(\"采购价格，单位：元\")\n    private BigDecimal purchasePrice;\n\n    @Schema(description = \"销售价格，单位：元\", example = \"74.32\")\n    @ExcelProperty(\"销售价格，单位：元\")\n    private BigDecimal salePrice;\n\n    @Schema(description = \"最低价格，单位：元\", example = \"161.87\")\n    @ExcelProperty(\"最低价格，单位：元\")\n    private BigDecimal minPrice;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ProductSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - ERP 产品新增/修改 Request VO\")\n@Data\npublic class ProductSaveReqVO {\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15672\")\n    private Long id;\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotEmpty(message = \"产品名称不能为空\")\n    private String name;\n\n    @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"X110\")\n    @NotEmpty(message = \"产品条码不能为空\")\n    private String barCode;\n\n    @Schema(description = \"产品分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11161\")\n    @NotNull(message = \"产品分类编号不能为空\")\n    private Long categoryId;\n\n    @Schema(description = \"单位编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8869\")\n    @NotNull(message = \"单位编号不能为空\")\n    private Long unitId;\n\n    @Schema(description = \"产品状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"产品状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"产品规格\", example = \"红色\")\n    private String standard;\n\n    @Schema(description = \"产品备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"保质期天数\", example = \"10\")\n    private Integer expiryDay;\n\n    @Schema(description = \"基础重量（kg）\", example = \"1.00\")\n    private BigDecimal weight;\n\n    @Schema(description = \"采购价格，单位：元\", example = \"10.30\")\n    private BigDecimal purchasePrice;\n\n    @Schema(description = \"销售价格，单位：元\", example = \"74.32\")\n    private BigDecimal salePrice;\n\n    @Schema(description = \"最低价格，单位：元\", example = \"161.87\")\n    private BigDecimal minPrice;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - ERP 产品单位分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpProductUnitPageReqVO extends PageParam {\n\n    @Schema(description = \"单位名字\", example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"单位状态\", example = \"1\")\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.module.system.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - ERP 产品单位 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpProductUnitRespVO {\n\n    @Schema(description = \"单位编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31254\")\n    @ExcelProperty(\"单位编号\")\n    private Long id;\n\n    @Schema(description = \"单位名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @ExcelProperty(\"单位名字\")\n    private String name;\n\n    @Schema(description = \"单位状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(\"单位状态\")\n    @DictFormat(DictTypeConstants.COMMON_STATUS)\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - ERP 产品单位新增/修改 Request VO\")\n@Data\npublic class ErpProductUnitSaveReqVO {\n\n    @Schema(description = \"单位编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31254\")\n    private Long id;\n\n    @Schema(description = \"单位名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @NotEmpty(message = \"单位名字不能为空\")\n    private String name;\n\n    @Schema(description = \"单位状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"单位状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 采购入库\")\n@RestController\n@RequestMapping(\"/erp/purchase-in\")\n@Validated\npublic class ErpPurchaseInController {\n\n    @Resource\n    private ErpPurchaseInService purchaseInService;\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpSupplierService supplierService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建采购入库\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-in:create')\")\n    public CommonResult<Long> createPurchaseIn(@Valid @RequestBody ErpPurchaseInSaveReqVO createReqVO) {\n        return success(purchaseInService.createPurchaseIn(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新采购入库\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-in:update')\")\n    public CommonResult<Boolean> updatePurchaseIn(@Valid @RequestBody ErpPurchaseInSaveReqVO updateReqVO) {\n        purchaseInService.updatePurchaseIn(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新采购入库的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-in:update-status')\")\n    public CommonResult<Boolean> updatePurchaseInStatus(@RequestParam(\"id\") Long id,\n                                                      @RequestParam(\"status\") Integer status) {\n        purchaseInService.updatePurchaseInStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除采购入库\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-in:delete')\")\n    public CommonResult<Boolean> deletePurchaseIn(@RequestParam(\"ids\") List<Long> ids) {\n        purchaseInService.deletePurchaseIn(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得采购入库\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-in:query')\")\n    public CommonResult<ErpPurchaseInRespVO> getPurchaseIn(@RequestParam(\"id\") Long id) {\n        ErpPurchaseInDO purchaseIn = purchaseInService.getPurchaseIn(id);\n        if (purchaseIn == null) {\n            return success(null);\n        }\n        List<ErpPurchaseInItemDO> purchaseInItemList = purchaseInService.getPurchaseInItemListByInId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(purchaseInItemList, ErpPurchaseInItemDO::getProductId));\n        return success(BeanUtils.toBean(purchaseIn, ErpPurchaseInRespVO.class, purchaseInVO ->\n                purchaseInVO.setItems(BeanUtils.toBean(purchaseInItemList, ErpPurchaseInRespVO.Item.class, item -> {\n                    ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId());\n                    item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO);\n                    MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));\n                }))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得采购入库分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-in:query')\")\n    public CommonResult<PageResult<ErpPurchaseInRespVO>> getPurchaseInPage(@Valid ErpPurchaseInPageReqVO pageReqVO) {\n        PageResult<ErpPurchaseInDO> pageResult = purchaseInService.getPurchaseInPage(pageReqVO);\n        return success(buildPurchaseInVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出采购入库 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-in:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportPurchaseInExcel(@Valid ErpPurchaseInPageReqVO pageReqVO,\n                                    HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpPurchaseInRespVO> list = buildPurchaseInVOPageResult(purchaseInService.getPurchaseInPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"采购入库.xls\", \"数据\", ErpPurchaseInRespVO.class, list);\n    }\n\n    private PageResult<ErpPurchaseInRespVO> buildPurchaseInVOPageResult(PageResult<ErpPurchaseInDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 入库项\n        List<ErpPurchaseInItemDO> purchaseInItemList = purchaseInService.getPurchaseInItemListByInIds(\n                convertSet(pageResult.getList(), ErpPurchaseInDO::getId));\n        Map<Long, List<ErpPurchaseInItemDO>> purchaseInItemMap = convertMultiMap(purchaseInItemList, ErpPurchaseInItemDO::getInId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(purchaseInItemList, ErpPurchaseInItemDO::getProductId));\n        // 1.3 供应商信息\n        Map<Long, ErpSupplierDO> supplierMap = supplierService.getSupplierMap(\n                convertSet(pageResult.getList(), ErpPurchaseInDO::getSupplierId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), purchaseIn -> Long.parseLong(purchaseIn.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpPurchaseInRespVO.class, purchaseIn -> {\n            purchaseIn.setItems(BeanUtils.toBean(purchaseInItemMap.get(purchaseIn.getId()), ErpPurchaseInRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            purchaseIn.setProductNames(CollUtil.join(purchaseIn.getItems(), \"，\", ErpPurchaseInRespVO.Item::getProductName));\n            MapUtils.findAndThen(supplierMap, purchaseIn.getSupplierId(), supplier -> purchaseIn.setSupplierName(supplier.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(purchaseIn.getCreator()), user -> purchaseIn.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseOrderService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 采购订单\")\n@RestController\n@RequestMapping(\"/erp/purchase-order\")\n@Validated\npublic class ErpPurchaseOrderController {\n\n    @Resource\n    private ErpPurchaseOrderService purchaseOrderService;\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpSupplierService supplierService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建采购订单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-order:create')\")\n    public CommonResult<Long> createPurchaseOrder(@Valid @RequestBody ErpPurchaseOrderSaveReqVO createReqVO) {\n        return success(purchaseOrderService.createPurchaseOrder(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新采购订单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-order:update')\")\n    public CommonResult<Boolean> updatePurchaseOrder(@Valid @RequestBody ErpPurchaseOrderSaveReqVO updateReqVO) {\n        purchaseOrderService.updatePurchaseOrder(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新采购订单的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-order:update-status')\")\n    public CommonResult<Boolean> updatePurchaseOrderStatus(@RequestParam(\"id\") Long id,\n                                                           @RequestParam(\"status\") Integer status) {\n        purchaseOrderService.updatePurchaseOrderStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除采购订单\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-order:delete')\")\n    public CommonResult<Boolean> deletePurchaseOrder(@RequestParam(\"ids\") List<Long> ids) {\n        purchaseOrderService.deletePurchaseOrder(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得采购订单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-order:query')\")\n    public CommonResult<ErpPurchaseOrderRespVO> getPurchaseOrder(@RequestParam(\"id\") Long id) {\n        ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.getPurchaseOrder(id);\n        if (purchaseOrder == null) {\n            return success(null);\n        }\n        List<ErpPurchaseOrderItemDO> purchaseOrderItemList = purchaseOrderService.getPurchaseOrderItemListByOrderId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(purchaseOrderItemList, ErpPurchaseOrderItemDO::getProductId));\n        return success(BeanUtils.toBean(purchaseOrder, ErpPurchaseOrderRespVO.class, purchaseOrderVO ->\n                purchaseOrderVO.setItems(BeanUtils.toBean(purchaseOrderItemList, ErpPurchaseOrderRespVO.Item.class, item -> {\n                    BigDecimal purchaseCount = stockService.getStockCount(item.getProductId());\n                    item.setStockCount(purchaseCount != null ? purchaseCount : BigDecimal.ZERO);\n                    MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));\n                }))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得采购订单分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-order:query')\")\n    public CommonResult<PageResult<ErpPurchaseOrderRespVO>> getPurchaseOrderPage(@Valid ErpPurchaseOrderPageReqVO pageReqVO) {\n        PageResult<ErpPurchaseOrderDO> pageResult = purchaseOrderService.getPurchaseOrderPage(pageReqVO);\n        return success(buildPurchaseOrderVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出采购订单 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-order:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportPurchaseOrderExcel(@Valid ErpPurchaseOrderPageReqVO pageReqVO,\n                                         HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpPurchaseOrderRespVO> list = buildPurchaseOrderVOPageResult(purchaseOrderService.getPurchaseOrderPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"采购订单.xls\", \"数据\", ErpPurchaseOrderRespVO.class, list);\n    }\n\n    private PageResult<ErpPurchaseOrderRespVO> buildPurchaseOrderVOPageResult(PageResult<ErpPurchaseOrderDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 订单项\n        List<ErpPurchaseOrderItemDO> purchaseOrderItemList = purchaseOrderService.getPurchaseOrderItemListByOrderIds(\n                convertSet(pageResult.getList(), ErpPurchaseOrderDO::getId));\n        Map<Long, List<ErpPurchaseOrderItemDO>> purchaseOrderItemMap = convertMultiMap(purchaseOrderItemList, ErpPurchaseOrderItemDO::getOrderId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(purchaseOrderItemList, ErpPurchaseOrderItemDO::getProductId));\n        // 1.3 供应商信息\n        Map<Long, ErpSupplierDO> supplierMap = supplierService.getSupplierMap(\n                convertSet(pageResult.getList(), ErpPurchaseOrderDO::getSupplierId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), purchaseOrder -> Long.parseLong(purchaseOrder.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpPurchaseOrderRespVO.class, purchaseOrder -> {\n            purchaseOrder.setItems(BeanUtils.toBean(purchaseOrderItemMap.get(purchaseOrder.getId()), ErpPurchaseOrderRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            purchaseOrder.setProductNames(CollUtil.join(purchaseOrder.getItems(), \"，\", ErpPurchaseOrderRespVO.Item::getProductName));\n            MapUtils.findAndThen(supplierMap, purchaseOrder.getSupplierId(), supplier -> purchaseOrder.setSupplierName(supplier.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(purchaseOrder.getCreator()), user -> purchaseOrder.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseReturnController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseReturnService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 采购退货\")\n@RestController\n@RequestMapping(\"/erp/purchase-return\")\n@Validated\npublic class ErpPurchaseReturnController {\n\n    @Resource\n    private ErpPurchaseReturnService purchaseReturnService;\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpSupplierService supplierService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建采购退货\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-return:create')\")\n    public CommonResult<Long> createPurchaseReturn(@Valid @RequestBody ErpPurchaseReturnSaveReqVO createReqVO) {\n        return success(purchaseReturnService.createPurchaseReturn(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新采购退货\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-return:update')\")\n    public CommonResult<Boolean> updatePurchaseReturn(@Valid @RequestBody ErpPurchaseReturnSaveReqVO updateReqVO) {\n        purchaseReturnService.updatePurchaseReturn(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新采购退货的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-return:update-status')\")\n    public CommonResult<Boolean> updatePurchaseReturnStatus(@RequestParam(\"id\") Long id,\n                                                      @RequestParam(\"status\") Integer status) {\n        purchaseReturnService.updatePurchaseReturnStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除采购退货\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-return:delete')\")\n    public CommonResult<Boolean> deletePurchaseReturn(@RequestParam(\"ids\") List<Long> ids) {\n        purchaseReturnService.deletePurchaseReturn(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得采购退货\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-return:query')\")\n    public CommonResult<ErpPurchaseReturnRespVO> getPurchaseReturn(@RequestParam(\"id\") Long id) {\n        ErpPurchaseReturnDO purchaseReturn = purchaseReturnService.getPurchaseReturn(id);\n        if (purchaseReturn == null) {\n            return success(null);\n        }\n        List<ErpPurchaseReturnItemDO> purchaseReturnItemList = purchaseReturnService.getPurchaseReturnItemListByReturnId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(purchaseReturnItemList, ErpPurchaseReturnItemDO::getProductId));\n        return success(BeanUtils.toBean(purchaseReturn, ErpPurchaseReturnRespVO.class, purchaseReturnVO ->\n                purchaseReturnVO.setItems(BeanUtils.toBean(purchaseReturnItemList, ErpPurchaseReturnRespVO.Item.class, item -> {\n                    ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId());\n                    item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO);\n                    MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));\n                }))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得采购退货分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-return:query')\")\n    public CommonResult<PageResult<ErpPurchaseReturnRespVO>> getPurchaseReturnPage(@Valid ErpPurchaseReturnPageReqVO pageReqVO) {\n        PageResult<ErpPurchaseReturnDO> pageResult = purchaseReturnService.getPurchaseReturnPage(pageReqVO);\n        return success(buildPurchaseReturnVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出采购退货 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:purchase-return:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportPurchaseReturnExcel(@Valid ErpPurchaseReturnPageReqVO pageReqVO,\n                                    HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpPurchaseReturnRespVO> list = buildPurchaseReturnVOPageResult(purchaseReturnService.getPurchaseReturnPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"采购退货.xls\", \"数据\", ErpPurchaseReturnRespVO.class, list);\n    }\n\n    private PageResult<ErpPurchaseReturnRespVO> buildPurchaseReturnVOPageResult(PageResult<ErpPurchaseReturnDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 退货项\n        List<ErpPurchaseReturnItemDO> purchaseReturnItemList = purchaseReturnService.getPurchaseReturnItemListByReturnIds(\n                convertSet(pageResult.getList(), ErpPurchaseReturnDO::getId));\n        Map<Long, List<ErpPurchaseReturnItemDO>> purchaseReturnItemMap = convertMultiMap(purchaseReturnItemList, ErpPurchaseReturnItemDO::getReturnId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(purchaseReturnItemList, ErpPurchaseReturnItemDO::getProductId));\n        // 1.3 供应商信息\n        Map<Long, ErpSupplierDO> supplierMap = supplierService.getSupplierMap(\n                convertSet(pageResult.getList(), ErpPurchaseReturnDO::getSupplierId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), purchaseReturn -> Long.parseLong(purchaseReturn.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpPurchaseReturnRespVO.class, purchaseReturn -> {\n            purchaseReturn.setItems(BeanUtils.toBean(purchaseReturnItemMap.get(purchaseReturn.getId()), ErpPurchaseReturnRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            purchaseReturn.setProductNames(CollUtil.join(purchaseReturn.getItems(), \"，\", ErpPurchaseReturnRespVO.Item::getProductName));\n            MapUtils.findAndThen(supplierMap, purchaseReturn.getSupplierId(), supplier -> purchaseReturn.setSupplierName(supplier.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(purchaseReturn.getCreator()), user -> purchaseReturn.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - ERP 供应商\")\n@RestController\n@RequestMapping(\"/erp/supplier\")\n@Validated\npublic class ErpSupplierController {\n\n    @Resource\n    private ErpSupplierService supplierService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建供应商\")\n    @PreAuthorize(\"@ss.hasPermission('erp:supplier:create')\")\n    public CommonResult<Long> createSupplier(@Valid @RequestBody ErpSupplierSaveReqVO createReqVO) {\n        return success(supplierService.createSupplier(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新供应商\")\n    @PreAuthorize(\"@ss.hasPermission('erp:supplier:update')\")\n    public CommonResult<Boolean> updateSupplier(@Valid @RequestBody ErpSupplierSaveReqVO updateReqVO) {\n        supplierService.updateSupplier(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除供应商\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:supplier:delete')\")\n    public CommonResult<Boolean> deleteSupplier(@RequestParam(\"id\") Long id) {\n        supplierService.deleteSupplier(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得供应商\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:supplier:query')\")\n    public CommonResult<ErpSupplierRespVO> getSupplier(@RequestParam(\"id\") Long id) {\n        ErpSupplierDO supplier = supplierService.getSupplier(id);\n        return success(BeanUtils.toBean(supplier, ErpSupplierRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得供应商分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:supplier:query')\")\n    public CommonResult<PageResult<ErpSupplierRespVO>> getSupplierPage(@Valid ErpSupplierPageReqVO pageReqVO) {\n        PageResult<ErpSupplierDO> pageResult = supplierService.getSupplierPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, ErpSupplierRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得供应商精简列表\", description = \"只包含被开启的供应商，主要用于前端的下拉选项\")\n    public CommonResult<List<ErpSupplierRespVO>> getSupplierSimpleList() {\n        List<ErpSupplierDO> list = supplierService.getSupplierListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, supplier -> new ErpSupplierRespVO().setId(supplier.getId()).setName(supplier.getName())));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出供应商 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:supplier:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportSupplierExcel(@Valid ErpSupplierPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpSupplierDO> list = supplierService.getSupplierPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"供应商.xls\", \"数据\", ErpSupplierRespVO.class,\n                        BeanUtils.toBean(list, ErpSupplierRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 采购入库分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpPurchaseInPageReqVO extends PageParam {\n\n    public static final Integer PAYMENT_STATUS_NONE = 0;\n    public static final Integer PAYMENT_STATUS_PART = 1;\n    public static final Integer PAYMENT_STATUS_ALL = 2;\n\n    @Schema(description = \"采购单编号\", example = \"XS001\")\n    private String no;\n\n    @Schema(description = \"供应商编号\", example = \"1724\")\n    private Long supplierId;\n\n    @Schema(description = \"入库时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] inTime;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"入库状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", example = \"1\")\n    private Long warehouseId;\n\n    @Schema(description = \"结算账号编号\", example = \"1\")\n    private Long accountId;\n\n    @Schema(description = \"付款状态\", example = \"1\")\n    private Integer paymentStatus;\n\n    @Schema(description = \"是否可付款\", example = \"true\")\n    private Boolean paymentEnable; // 对应 paymentStatus = [0, 1]\n\n    @Schema(description = \"采购单号\", example = \"1\")\n    private String orderNo;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 采购入库 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpPurchaseInRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"入库单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    @ExcelProperty(\"入库单编号\")\n    private String no;\n\n    @Schema(description = \"入库状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"入库状态\")\n    private Integer status;\n\n    @Schema(description = \"供应商编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1724\")\n    private Long supplierId;\n    @Schema(description = \"供应商名称\", example = \"芋道\")\n    @ExcelProperty(\"供应商名称\")\n    private String supplierName;\n\n    @Schema(description = \"结算账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"311.89\")\n    @ExcelProperty(\"结算账户编号\")\n    private Long accountId;\n\n    @Schema(description = \"入库时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"入库时间\")\n    private LocalDateTime inTime;\n\n    @Schema(description = \"采购订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long orderId;\n    @Schema(description = \"采购订单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    private String orderNo;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n    @Schema(description = \"最终合计价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"最终合计价格\")\n    private BigDecimal totalPrice;\n    @Schema(description = \"已付款金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal paymentPrice;\n\n    @Schema(description = \"合计产品价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalProductPrice;\n\n    @Schema(description = \"合计税额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalTaxPrice;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"定金金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal otherPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    @ExcelProperty(\"附件地址\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"入库项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"入库项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"采购订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        private Long orderItemId;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"税额，单位：元\", example = \"100.00\")\n        private BigDecimal taxPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n        @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 采购入库新增/修改 Request VO\")\n@Data\npublic class ErpPurchaseInSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long id;\n\n    @Schema(description = \"结算账户编号\", example = \"31189\")\n    private Long accountId;\n\n    @Schema(description = \"入库时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"入库时间不能为空\")\n    private LocalDateTime inTime;\n\n    @Schema(description = \"采购订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @NotNull(message = \"采购订单编号不能为空\")\n    private Long orderId;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"其它金额，单位：元\", example = \"7127\")\n    private BigDecimal otherPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"入库清单列表\")\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"入库项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"采购订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        @NotNull(message = \"采购订单项编号不能为空\")\n        private Long orderItemId;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"仓库编号不能为空\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品单位单位不能为空\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 采购订单分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpPurchaseOrderPageReqVO extends PageParam {\n\n    /**\n     * 入库状态 - 无\n     */\n    public static final Integer IN_STATUS_NONE = 0;\n    /**\n     * 入库状态 - 部分\n     */\n    public static final Integer IN_STATUS_PART = 1;\n    /**\n     * 入库状态 - 全部\n     */\n    public static final Integer IN_STATUS_ALL = 2;\n\n    /**\n     * 退货状态 - 无\n     */\n    public static final Integer RETURN_STATUS_NONE = 0;\n    /**\n     * 退货状态 - 部分\n     */\n    public static final Integer RETURN_STATUS_PART = 1;\n    /**\n     * 退货状态 - 全部\n     */\n    public static final Integer RETURN_STATUS_ALL = 2;\n\n    @Schema(description = \"采购单编号\", example = \"XS001\")\n    private String no;\n\n    @Schema(description = \"供应商编号\", example = \"1724\")\n    private Long supplierId;\n\n    @Schema(description = \"采购时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] orderTime;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"采购状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n    @Schema(description = \"入库状态\", example = \"2\")\n    private Integer inStatus;\n\n    @Schema(description = \"退货状态\", example = \"2\")\n    private Integer returnStatus;\n\n    @Schema(description = \"是否可入库\", example = \"true\")\n    private Boolean inEnable;\n\n    @Schema(description = \"是否可退货\", example = \"true\")\n    private Boolean returnEnable;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 采购订单 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpPurchaseOrderRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"采购单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    @ExcelProperty(\"采购单编号\")\n    private String no;\n\n    @Schema(description = \"采购状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"采购状态\")\n    private Integer status;\n\n    @Schema(description = \"供应商编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1724\")\n    private Long supplierId;\n    @Schema(description = \"供应商名称\", example = \"芋道\")\n    @ExcelProperty(\"供应商名称\")\n    private String supplierName;\n\n    @Schema(description = \"结算账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"311.89\")\n    @ExcelProperty(\"结算账户编号\")\n    private Long accountId;\n\n    @Schema(description = \"采购时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"采购时间\")\n    private LocalDateTime orderTime;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n    @Schema(description = \"最终合计价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"最终合计价格\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"合计产品价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalProductPrice;\n\n    @Schema(description = \"合计税额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalTaxPrice;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"定金金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal depositPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    @ExcelProperty(\"附件地址\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"订单项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    // ========== 采购入库 ==========\n\n    @Schema(description = \"采购入库数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n    private BigDecimal inCount;\n\n    // ========== 采购退货（出库）） ==========\n\n    @Schema(description = \"采购退货数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n    private BigDecimal returnCount;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"订单项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"税额，单位：元\", example = \"100.00\")\n        private BigDecimal taxPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 采购入库 ==========\n\n        @Schema(description = \"采购入库数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal inCount;\n\n        // ========== 采购退货（入库）） ==========\n\n        @Schema(description = \"采购退货数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal returnCount;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n        @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 采购订单新增/修改 Request VO\")\n@Data\npublic class ErpPurchaseOrderSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long id;\n\n    @Schema(description = \"供应商编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1724\")\n    @NotNull(message = \"供应商编号不能为空\")\n    private Long supplierId;\n\n    @Schema(description = \"结算账户编号\", example = \"31189\")\n    private Long accountId;\n\n    @Schema(description = \"采购时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"采购时间不能为空\")\n    private LocalDateTime orderTime;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"定金金额，单位：元\", example = \"7127\")\n    private BigDecimal depositPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"订单清单列表\")\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"订单项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品单位单位不能为空\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 采购退货分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpPurchaseReturnPageReqVO extends PageParam {\n\n    public static final Integer REFUND_STATUS_NONE = 0;\n    public static final Integer REFUND_STATUS_PART = 1;\n    public static final Integer REFUND_STATUS_ALL = 2;\n\n    @Schema(description = \"采购单编号\", example = \"XS001\")\n    private String no;\n\n    @Schema(description = \"供应商编号\", example = \"1724\")\n    private Long supplierId;\n\n    @Schema(description = \"退货时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] returnTime;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"退货状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", example = \"1\")\n    private Long warehouseId;\n\n    @Schema(description = \"结算账号编号\", example = \"1\")\n    private Long accountId;\n\n    @Schema(description = \"采购单号\", example = \"1\")\n    private String orderNo;\n\n    @Schema(description = \"退款状态\", example = \"1\")\n    private Integer refundStatus;\n\n    @Schema(description = \"是否可退款\", example = \"true\")\n    private Boolean refundEnable; // 对应 refundStatus = [0, 1]\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 采购退货 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpPurchaseReturnRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"退货单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    @ExcelProperty(\"退货单编号\")\n    private String no;\n\n    @Schema(description = \"退货状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"退货状态\")\n    private Integer status;\n\n    @Schema(description = \"供应商编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1724\")\n    private Long supplierId;\n    @Schema(description = \"供应商名称\", example = \"芋道\")\n    @ExcelProperty(\"供应商名称\")\n    private String supplierName;\n\n    @Schema(description = \"结算账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"311.89\")\n    @ExcelProperty(\"结算账户编号\")\n    private Long accountId;\n\n    @Schema(description = \"退货时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"退货时间\")\n    private LocalDateTime returnTime;\n\n    @Schema(description = \"采购订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long orderId;\n    @Schema(description = \"采购订单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    private String orderNo;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n    @Schema(description = \"最终合计价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"最终合计价格\")\n    private BigDecimal totalPrice;\n    @Schema(description = \"已退款金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal refundPrice;\n\n    @Schema(description = \"合计产品价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalProductPrice;\n\n    @Schema(description = \"合计税额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalTaxPrice;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"定金金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal otherPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    @ExcelProperty(\"附件地址\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"退货项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"退货项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"采购订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        private Long orderItemId;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"税额，单位：元\", example = \"100.00\")\n        private BigDecimal taxPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n        @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 采购退货新增/修改 Request VO\")\n@Data\npublic class ErpPurchaseReturnSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long id;\n\n    @Schema(description = \"结算账户编号\", example = \"31189\")\n    private Long accountId;\n\n    @Schema(description = \"退货时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"退货时间不能为空\")\n    private LocalDateTime returnTime;\n\n    @Schema(description = \"采购订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @NotNull(message = \"采购订单编号不能为空\")\n    private Long orderId;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"其它金额，单位：元\", example = \"7127\")\n    private BigDecimal otherPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"退货清单列表\")\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"退货项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"采购订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        @NotNull(message = \"采购订单项编号不能为空\")\n        private Long orderItemId;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"仓库编号不能为空\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品单位单位不能为空\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - ERP 供应商分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpSupplierPageReqVO extends PageParam {\n\n    @Schema(description = \"供应商名称\", example = \"芋道源码\")\n    private String name;\n\n    @Schema(description = \"手机号码\", example = \"15601691300\")\n    private String mobile;\n\n    @Schema(description = \"联系电话\", example = \"18818288888\")\n    private String telephone;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.system.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - ERP 供应商 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpSupplierRespVO {\n\n    @Schema(description = \"供应商编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17791\")\n    @ExcelProperty(\"供应商编号\")\n    private Long id;\n\n    @Schema(description = \"供应商名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    @ExcelProperty(\"供应商名称\")\n    private String name;\n\n    @Schema(description = \"联系人\", example = \"芋艿\")\n    @ExcelProperty(\"联系人\")\n    private String contact;\n\n    @Schema(description = \"手机号码\", example = \"15601691300\")\n    @ExcelProperty(\"手机号码\")\n    private String mobile;\n\n    @Schema(description = \"联系电话\", example = \"18818288888\")\n    @ExcelProperty(\"联系电话\")\n    private String telephone;\n\n    @Schema(description = \"电子邮箱\", example = \"76853@qq.com\")\n    @ExcelProperty(\"电子邮箱\")\n    private String email;\n\n    @Schema(description = \"传真\", example = \"20 7123 4567\")\n    @ExcelProperty(\"传真\")\n    private String fax;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"开启状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.COMMON_STATUS)\n    private Integer status;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @ExcelProperty(\"排序\")\n    private Integer sort;\n\n    @Schema(description = \"纳税人识别号\", example = \"91130803MA098BY05W\")\n    @ExcelProperty(\"纳税人识别号\")\n    private String taxNo;\n\n    @Schema(description = \"税率\", example = \"10\")\n    @ExcelProperty(\"税率\")\n    private BigDecimal taxPercent;\n\n    @Schema(description = \"开户行\", example = \"张三\")\n    @ExcelProperty(\"开户行\")\n    private String bankName;\n\n    @Schema(description = \"开户账号\", example = \"622908212277228617\")\n    @ExcelProperty(\"开户账号\")\n    private String bankAccount;\n\n    @Schema(description = \"开户地址\", example = \"兴业银行浦东支行\")\n    @ExcelProperty(\"开户地址\")\n    private String bankAddress;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.framework.common.validation.Mobile;\nimport cn.iocoder.yudao.framework.common.validation.Telephone;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - ERP 供应商新增/修改 Request VO\")\n@Data\npublic class ErpSupplierSaveReqVO {\n\n    @Schema(description = \"供应商编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17791\")\n    private Long id;\n\n    @Schema(description = \"供应商名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    @NotEmpty(message = \"供应商名称不能为空\")\n    private String name;\n\n    @Schema(description = \"联系人\", example = \"芋艿\")\n    private String contact;\n\n    @Schema(description = \"手机号码\", example = \"15601691300\")\n    @Mobile\n    private String mobile;\n\n    @Schema(description = \"联系电话\", example = \"18818288888\")\n    @Telephone\n    private String telephone;\n\n    @Schema(description = \"电子邮箱\", example = \"76853@qq.com\")\n    @Email\n    private String email;\n\n    @Schema(description = \"传真\", example = \"20 7123 4567\")\n    private String fax;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"开启状态不能为空\")\n    @InEnum(value = CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"纳税人识别号\", example = \"91130803MA098BY05W\")\n    private String taxNo;\n\n    @Schema(description = \"税率\", example = \"10\")\n    private BigDecimal taxPercent;\n\n    @Schema(description = \"开户行\", example = \"张三\")\n    private String bankName;\n\n    @Schema(description = \"开户账号\", example = \"622908212277228617\")\n    private String bankAccount;\n\n    @Schema(description = \"开户地址\", example = \"兴业银行浦东支行\")\n    private String bankAddress;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpCustomerController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - ERP 客户\")\n@RestController\n@RequestMapping(\"/erp/customer\")\n@Validated\npublic class ErpCustomerController {\n\n    @Resource\n    private ErpCustomerService customerService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建客户\")\n    @PreAuthorize(\"@ss.hasPermission('erp:customer:create')\")\n    public CommonResult<Long> createCustomer(@Valid @RequestBody ErpCustomerSaveReqVO createReqVO) {\n        return success(customerService.createCustomer(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新客户\")\n    @PreAuthorize(\"@ss.hasPermission('erp:customer:update')\")\n    public CommonResult<Boolean> updateCustomer(@Valid @RequestBody ErpCustomerSaveReqVO updateReqVO) {\n        customerService.updateCustomer(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除客户\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:customer:delete')\")\n    public CommonResult<Boolean> deleteCustomer(@RequestParam(\"id\") Long id) {\n        customerService.deleteCustomer(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得客户\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:customer:query')\")\n    public CommonResult<ErpCustomerRespVO> getCustomer(@RequestParam(\"id\") Long id) {\n        ErpCustomerDO customer = customerService.getCustomer(id);\n        return success(BeanUtils.toBean(customer, ErpCustomerRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得客户分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:customer:query')\")\n    public CommonResult<PageResult<ErpCustomerRespVO>> getCustomerPage(@Valid ErpCustomerPageReqVO pageReqVO) {\n        PageResult<ErpCustomerDO> pageResult = customerService.getCustomerPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, ErpCustomerRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得客户精简列表\", description = \"只包含被开启的客户，主要用于前端的下拉选项\")\n    public CommonResult<List<ErpCustomerRespVO>> getCustomerSimpleList() {\n        List<ErpCustomerDO> list = customerService.getCustomerListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, customer -> new ErpCustomerRespVO().setId(customer.getId()).setName(customer.getName())));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出客户 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:customer:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportCustomerExcel(@Valid ErpCustomerPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpCustomerDO> list = customerService.getCustomerPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"客户.xls\", \"数据\", ErpCustomerRespVO.class,\n                        BeanUtils.toBean(list, ErpCustomerRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpSaleOrderService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 销售订单\")\n@RestController\n@RequestMapping(\"/erp/sale-order\")\n@Validated\npublic class ErpSaleOrderController {\n\n    @Resource\n    private ErpSaleOrderService saleOrderService;\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpCustomerService customerService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建销售订单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:create')\")\n    public CommonResult<Long> createSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO createReqVO) {\n        return success(saleOrderService.createSaleOrder(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新销售订单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:update')\")\n    public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO updateReqVO) {\n        saleOrderService.updateSaleOrder(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新销售订单的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:update-status')\")\n    public CommonResult<Boolean> updateSaleOrderStatus(@RequestParam(\"id\") Long id,\n                                                      @RequestParam(\"status\") Integer status) {\n        saleOrderService.updateSaleOrderStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除销售订单\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:delete')\")\n    public CommonResult<Boolean> deleteSaleOrder(@RequestParam(\"ids\") List<Long> ids) {\n        saleOrderService.deleteSaleOrder(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得销售订单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:query')\")\n    public CommonResult<ErpSaleOrderRespVO> getSaleOrder(@RequestParam(\"id\") Long id) {\n        ErpSaleOrderDO saleOrder = saleOrderService.getSaleOrder(id);\n        if (saleOrder == null) {\n            return success(null);\n        }\n        List<ErpSaleOrderItemDO> saleOrderItemList = saleOrderService.getSaleOrderItemListByOrderId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(saleOrderItemList, ErpSaleOrderItemDO::getProductId));\n        return success(BeanUtils.toBean(saleOrder, ErpSaleOrderRespVO.class, saleOrderVO ->\n                saleOrderVO.setItems(BeanUtils.toBean(saleOrderItemList, ErpSaleOrderRespVO.Item.class, item -> {\n                    BigDecimal stockCount = stockService.getStockCount(item.getProductId());\n                    item.setStockCount(stockCount != null ? stockCount : BigDecimal.ZERO);\n                    MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));\n                }))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得销售订单分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:query')\")\n    public CommonResult<PageResult<ErpSaleOrderRespVO>> getSaleOrderPage(@Valid ErpSaleOrderPageReqVO pageReqVO) {\n        PageResult<ErpSaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO);\n        return success(buildSaleOrderVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出销售订单 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO,\n                                    HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpSaleOrderRespVO> list = buildSaleOrderVOPageResult(saleOrderService.getSaleOrderPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"销售订单.xls\", \"数据\", ErpSaleOrderRespVO.class, list);\n    }\n\n    private PageResult<ErpSaleOrderRespVO> buildSaleOrderVOPageResult(PageResult<ErpSaleOrderDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 订单项\n        List<ErpSaleOrderItemDO> saleOrderItemList = saleOrderService.getSaleOrderItemListByOrderIds(\n                convertSet(pageResult.getList(), ErpSaleOrderDO::getId));\n        Map<Long, List<ErpSaleOrderItemDO>> saleOrderItemMap = convertMultiMap(saleOrderItemList, ErpSaleOrderItemDO::getOrderId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(saleOrderItemList, ErpSaleOrderItemDO::getProductId));\n        // 1.3 客户信息\n        Map<Long, ErpCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(pageResult.getList(), ErpSaleOrderDO::getCustomerId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), saleOrder -> Long.parseLong(saleOrder.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpSaleOrderRespVO.class, saleOrder -> {\n            saleOrder.setItems(BeanUtils.toBean(saleOrderItemMap.get(saleOrder.getId()), ErpSaleOrderRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            saleOrder.setProductNames(CollUtil.join(saleOrder.getItems(), \"，\", ErpSaleOrderRespVO.Item::getProductName));\n            MapUtils.findAndThen(customerMap, saleOrder.getCustomerId(), supplier -> saleOrder.setCustomerName(supplier.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(saleOrder.getCreator()), user -> saleOrder.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpSaleOutService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 销售出库\")\n@RestController\n@RequestMapping(\"/erp/sale-out\")\n@Validated\npublic class ErpSaleOutController {\n\n    @Resource\n    private ErpSaleOutService saleOutService;\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpCustomerService customerService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建销售出库\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:create')\")\n    public CommonResult<Long> createSaleOut(@Valid @RequestBody ErpSaleOutSaveReqVO createReqVO) {\n        return success(saleOutService.createSaleOut(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新销售出库\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:update')\")\n    public CommonResult<Boolean> updateSaleOut(@Valid @RequestBody ErpSaleOutSaveReqVO updateReqVO) {\n        saleOutService.updateSaleOut(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新销售出库的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:update-status')\")\n    public CommonResult<Boolean> updateSaleOutStatus(@RequestParam(\"id\") Long id,\n                                                      @RequestParam(\"status\") Integer status) {\n        saleOutService.updateSaleOutStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除销售出库\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:delete')\")\n    public CommonResult<Boolean> deleteSaleOut(@RequestParam(\"ids\") List<Long> ids) {\n        saleOutService.deleteSaleOut(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得销售出库\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:query')\")\n    public CommonResult<ErpSaleOutRespVO> getSaleOut(@RequestParam(\"id\") Long id) {\n        ErpSaleOutDO saleOut = saleOutService.getSaleOut(id);\n        if (saleOut == null) {\n            return success(null);\n        }\n        List<ErpSaleOutItemDO> saleOutItemList = saleOutService.getSaleOutItemListByOutId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(saleOutItemList, ErpSaleOutItemDO::getProductId));\n        return success(BeanUtils.toBean(saleOut, ErpSaleOutRespVO.class, saleOutVO ->\n                saleOutVO.setItems(BeanUtils.toBean(saleOutItemList, ErpSaleOutRespVO.Item.class, item -> {\n                    ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId());\n                    item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO);\n                    MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));\n                }))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得销售出库分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:query')\")\n    public CommonResult<PageResult<ErpSaleOutRespVO>> getSaleOutPage(@Valid ErpSaleOutPageReqVO pageReqVO) {\n        PageResult<ErpSaleOutDO> pageResult = saleOutService.getSaleOutPage(pageReqVO);\n        return success(buildSaleOutVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出销售出库 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-out:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportSaleOutExcel(@Valid ErpSaleOutPageReqVO pageReqVO,\n                                    HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpSaleOutRespVO> list = buildSaleOutVOPageResult(saleOutService.getSaleOutPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"销售出库.xls\", \"数据\", ErpSaleOutRespVO.class, list);\n    }\n\n    private PageResult<ErpSaleOutRespVO> buildSaleOutVOPageResult(PageResult<ErpSaleOutDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 出库项\n        List<ErpSaleOutItemDO> saleOutItemList = saleOutService.getSaleOutItemListByOutIds(\n                convertSet(pageResult.getList(), ErpSaleOutDO::getId));\n        Map<Long, List<ErpSaleOutItemDO>> saleOutItemMap = convertMultiMap(saleOutItemList, ErpSaleOutItemDO::getOutId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(saleOutItemList, ErpSaleOutItemDO::getProductId));\n        // 1.3 客户信息\n        Map<Long, ErpCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(pageResult.getList(), ErpSaleOutDO::getCustomerId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), stockOut -> Long.parseLong(stockOut.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpSaleOutRespVO.class, saleOut -> {\n            saleOut.setItems(BeanUtils.toBean(saleOutItemMap.get(saleOut.getId()), ErpSaleOutRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            saleOut.setProductNames(CollUtil.join(saleOut.getItems(), \"，\", ErpSaleOutRespVO.Item::getProductName));\n            MapUtils.findAndThen(customerMap, saleOut.getCustomerId(), supplier -> saleOut.setCustomerName(supplier.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(saleOut.getCreator()), user -> saleOut.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleReturnController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpSaleReturnService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 销售退货\")\n@RestController\n@RequestMapping(\"/erp/sale-return\")\n@Validated\npublic class ErpSaleReturnController {\n\n    @Resource\n    private ErpSaleReturnService saleReturnService;\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpCustomerService customerService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建销售退货\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-return:create')\")\n    public CommonResult<Long> createSaleReturn(@Valid @RequestBody ErpSaleReturnSaveReqVO createReqVO) {\n        return success(saleReturnService.createSaleReturn(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新销售退货\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-return:update')\")\n    public CommonResult<Boolean> updateSaleReturn(@Valid @RequestBody ErpSaleReturnSaveReqVO updateReqVO) {\n        saleReturnService.updateSaleReturn(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新销售退货的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-return:update-status')\")\n    public CommonResult<Boolean> updateSaleReturnStatus(@RequestParam(\"id\") Long id,\n                                                      @RequestParam(\"status\") Integer status) {\n        saleReturnService.updateSaleReturnStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除销售退货\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-return:delete')\")\n    public CommonResult<Boolean> deleteSaleReturn(@RequestParam(\"ids\") List<Long> ids) {\n        saleReturnService.deleteSaleReturn(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得销售退货\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-return:query')\")\n    public CommonResult<ErpSaleReturnRespVO> getSaleReturn(@RequestParam(\"id\") Long id) {\n        ErpSaleReturnDO saleReturn = saleReturnService.getSaleReturn(id);\n        if (saleReturn == null) {\n            return success(null);\n        }\n        List<ErpSaleReturnItemDO> saleReturnItemList = saleReturnService.getSaleReturnItemListByReturnId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(saleReturnItemList, ErpSaleReturnItemDO::getProductId));\n        return success(BeanUtils.toBean(saleReturn, ErpSaleReturnRespVO.class, saleReturnVO ->\n                saleReturnVO.setItems(BeanUtils.toBean(saleReturnItemList, ErpSaleReturnRespVO.Item.class, item -> {\n                    ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId());\n                    item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO);\n                    MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));\n                }))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得销售退货分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-return:query')\")\n    public CommonResult<PageResult<ErpSaleReturnRespVO>> getSaleReturnPage(@Valid ErpSaleReturnPageReqVO pageReqVO) {\n        PageResult<ErpSaleReturnDO> pageResult = saleReturnService.getSaleReturnPage(pageReqVO);\n        return success(buildSaleReturnVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出销售退货 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:sale-return:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportSaleReturnExcel(@Valid ErpSaleReturnPageReqVO pageReqVO,\n                                    HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpSaleReturnRespVO> list = buildSaleReturnVOPageResult(saleReturnService.getSaleReturnPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"销售退货.xls\", \"数据\", ErpSaleReturnRespVO.class, list);\n    }\n\n    private PageResult<ErpSaleReturnRespVO> buildSaleReturnVOPageResult(PageResult<ErpSaleReturnDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 退货项\n        List<ErpSaleReturnItemDO> saleReturnItemList = saleReturnService.getSaleReturnItemListByReturnIds(\n                convertSet(pageResult.getList(), ErpSaleReturnDO::getId));\n        Map<Long, List<ErpSaleReturnItemDO>> saleReturnItemMap = convertMultiMap(saleReturnItemList, ErpSaleReturnItemDO::getReturnId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(saleReturnItemList, ErpSaleReturnItemDO::getProductId));\n        // 1.3 客户信息\n        Map<Long, ErpCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(pageResult.getList(), ErpSaleReturnDO::getCustomerId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), saleReturn -> Long.parseLong(saleReturn.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpSaleReturnRespVO.class, saleReturn -> {\n            saleReturn.setItems(BeanUtils.toBean(saleReturnItemMap.get(saleReturn.getId()), ErpSaleReturnRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            saleReturn.setProductNames(CollUtil.join(saleReturn.getItems(), \"，\", ErpSaleReturnRespVO.Item::getProductName));\n            MapUtils.findAndThen(customerMap, saleReturn.getCustomerId(), supplier -> saleReturn.setCustomerName(supplier.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(saleReturn.getCreator()), user -> saleReturn.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - ERP 客户分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpCustomerPageReqVO extends PageParam {\n\n    @Schema(description = \"客户名称\", example = \"张三\")\n    private String name;\n\n    @Schema(description = \"手机号码\", example = \"15601691300\")\n    private String mobile;\n\n    @Schema(description = \"联系电话\", example = \"15601691300\")\n    private String telephone;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - ERP 客户 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpCustomerRespVO {\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"27520\")\n    @ExcelProperty(\"客户编号\")\n    private Long id;\n\n    @Schema(description = \"客户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    @ExcelProperty(\"客户名称\")\n    private String name;\n\n    @Schema(description = \"联系人\", example = \"老王\")\n    @ExcelProperty(\"联系人\")\n    private String contact;\n\n    @Schema(description = \"手机号码\", example = \"15601691300\")\n    @ExcelProperty(\"手机号码\")\n    private String mobile;\n\n    @Schema(description = \"联系电话\", example = \"15601691300\")\n    @ExcelProperty(\"联系电话\")\n    private String telephone;\n\n    @Schema(description = \"电子邮箱\", example = \"7685323@qq.com\")\n    @ExcelProperty(\"电子邮箱\")\n    private String email;\n\n    @Schema(description = \"传真\", example = \"20 7123 4567\")\n    @ExcelProperty(\"传真\")\n    private String fax;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"开启状态\", converter = DictConvert.class)\n    @DictFormat(\"common_status\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer status;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @ExcelProperty(\"排序\")\n    private Integer sort;\n\n    @Schema(description = \"纳税人识别号\", example = \"91130803MA098BY05W\")\n    @ExcelProperty(\"纳税人识别号\")\n    private String taxNo;\n\n    @Schema(description = \"税率\", example = \"10\")\n    @ExcelProperty(\"税率\")\n    private BigDecimal taxPercent;\n\n    @Schema(description = \"开户行\", example = \"芋艿\")\n    @ExcelProperty(\"开户行\")\n    private String bankName;\n\n    @Schema(description = \"开户账号\", example = \"622908212277228617\")\n    @ExcelProperty(\"开户账号\")\n    private String bankAccount;\n\n    @Schema(description = \"开户地址\", example = \"兴业银行浦东支行\")\n    @ExcelProperty(\"开户地址\")\n    private String bankAddress;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - ERP 客户新增/修改 Request VO\")\n@Data\npublic class ErpCustomerSaveReqVO {\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"27520\")\n    private Long id;\n\n    @Schema(description = \"客户名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    @NotEmpty(message = \"客户名称不能为空\")\n    private String name;\n\n    @Schema(description = \"联系人\", example = \"老王\")\n    private String contact;\n\n    @Schema(description = \"手机号码\", example = \"15601691300\")\n    private String mobile;\n\n    @Schema(description = \"联系电话\", example = \"15601691300\")\n    private String telephone;\n\n    @Schema(description = \"电子邮箱\", example = \"7685323@qq.com\")\n    private String email;\n\n    @Schema(description = \"传真\", example = \"20 7123 4567\")\n    private String fax;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"开启状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"纳税人识别号\", example = \"91130803MA098BY05W\")\n    private String taxNo;\n\n    @Schema(description = \"税率\", example = \"10\")\n    private BigDecimal taxPercent;\n\n    @Schema(description = \"开户行\", example = \"芋艿\")\n    private String bankName;\n\n    @Schema(description = \"开户账号\", example = \"622908212277228617\")\n    private String bankAccount;\n\n    @Schema(description = \"开户地址\", example = \"兴业银行浦东支行\")\n    private String bankAddress;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 销售订单分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpSaleOrderPageReqVO extends PageParam {\n\n    /**\n     * 出库状态 - 无\n     */\n    public static final Integer OUT_STATUS_NONE = 0;\n    /**\n     * 出库状态 - 部分\n     */\n    public static final Integer OUT_STATUS_PART = 1;\n    /**\n     * 出库状态 - 全部\n     */\n    public static final Integer OUT_STATUS_ALL = 2;\n\n    /**\n     * 退货状态 - 无\n     */\n    public static final Integer RETURN_STATUS_NONE = 0;\n    /**\n     * 退货状态 - 部分\n     */\n    public static final Integer RETURN_STATUS_PART = 1;\n    /**\n     * 退货状态 - 全部\n     */\n    public static final Integer RETURN_STATUS_ALL = 2;\n\n    @Schema(description = \"销售单编号\", example = \"XS001\")\n    private String no;\n\n    @Schema(description = \"客户编号\", example = \"1724\")\n    private Long customerId;\n\n    @Schema(description = \"下单时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] orderTime;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"销售状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n    @Schema(description = \"出库状态\", example = \"2\")\n    private Integer outStatus;\n\n    @Schema(description = \"退货状态\", example = \"2\")\n    private Integer returnStatus;\n\n    @Schema(description = \"是否可出库\", example = \"true\")\n    private Boolean outEnable;\n\n    @Schema(description = \"是否可退货\", example = \"true\")\n    private Boolean returnEnable;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 销售订单 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpSaleOrderRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"销售单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    @ExcelProperty(\"销售单编号\")\n    private String no;\n\n    @Schema(description = \"销售状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"销售状态\")\n    private Integer status;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1724\")\n    private Long customerId;\n    @Schema(description = \"客户名称\", example = \"芋道\")\n    @ExcelProperty(\"客户名称\")\n    private String customerName;\n\n    @Schema(description = \"结算账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"311.89\")\n    @ExcelProperty(\"结算账户编号\")\n    private Long accountId;\n\n    @Schema(description = \"销售员编号\", example = \"1888\")\n    private Long saleUserId;\n\n    @Schema(description = \"下单时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"下单时间\")\n    private LocalDateTime orderTime;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n    @Schema(description = \"最终合计价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"最终合计价格\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"合计产品价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalProductPrice;\n\n    @Schema(description = \"合计税额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalTaxPrice;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"定金金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal depositPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    @ExcelProperty(\"附件地址\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"订单项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    // ========== 销售出库 ==========\n\n    @Schema(description = \"销售出库数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n    private BigDecimal outCount;\n\n    // ========== 销售退货（出库）） ==========\n\n    @Schema(description = \"销售退货数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n    private BigDecimal returnCount;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"订单项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"税额，单位：元\", example = \"100.00\")\n        private BigDecimal taxPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 销售出库 ==========\n\n        @Schema(description = \"销售出库数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal outCount;\n\n        // ========== 销售退货（入库）） ==========\n\n        @Schema(description = \"销售退货数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal returnCount;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n        @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 销售订单新增/修改 Request VO\")\n@Data\npublic class ErpSaleOrderSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long id;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1724\")\n    @NotNull(message = \"客户编号不能为空\")\n    private Long customerId;\n\n    @Schema(description = \"下单时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"下单时间不能为空\")\n    private LocalDateTime orderTime;\n\n    @Schema(description = \"销售员编号\", example = \"1888\")\n    private Long saleUserId;\n\n    @Schema(description = \"结算账户编号\", example = \"31189\")\n    private Long accountId;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"定金金额，单位：元\", example = \"7127\")\n    private BigDecimal depositPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"订单清单列表\")\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"订单项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品单位单位不能为空\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 销售出库分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpSaleOutPageReqVO extends PageParam {\n\n    public static final Integer RECEIPT_STATUS_NONE = 0;\n    public static final Integer RECEIPT_STATUS_PART = 1;\n    public static final Integer RECEIPT_STATUS_ALL = 2;\n\n    @Schema(description = \"销售单编号\", example = \"XS001\")\n    private String no;\n\n    @Schema(description = \"客户编号\", example = \"1724\")\n    private Long customerId;\n\n    @Schema(description = \"出库时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] outTime;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"出库状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", example = \"1\")\n    private Long warehouseId;\n\n    @Schema(description = \"结算账号编号\", example = \"1\")\n    private Long accountId;\n\n    @Schema(description = \"收款状态\", example = \"1\")\n    private Integer receiptStatus;\n\n    @Schema(description = \"是否可收款\", example = \"true\")\n    private Boolean receiptEnable; // 对应 receiptStatus = [0, 1]\n\n    @Schema(description = \"销售单号\", example = \"1\")\n    private String orderNo;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 销售出库 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpSaleOutRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"出库单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    @ExcelProperty(\"出库单编号\")\n    private String no;\n\n    @Schema(description = \"出库状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"出库状态\")\n    private Integer status;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1724\")\n    private Long customerId;\n    @Schema(description = \"客户名称\", example = \"芋道\")\n    @ExcelProperty(\"客户名称\")\n    private String customerName;\n\n    @Schema(description = \"结算账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"311.89\")\n    @ExcelProperty(\"结算账户编号\")\n    private Long accountId;\n\n    @Schema(description = \"出库员编号\", example = \"1888\")\n    private Long saleUserId;\n\n    @Schema(description = \"出库时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出库时间\")\n    private LocalDateTime outTime;\n\n    @Schema(description = \"销售订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long orderId;\n    @Schema(description = \"销售订单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    private String orderNo;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n    @Schema(description = \"最终合计价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"最终合计价格\")\n    private BigDecimal totalPrice;\n    @Schema(description = \"已收款金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal receiptPrice;\n\n    @Schema(description = \"合计产品价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalProductPrice;\n\n    @Schema(description = \"合计税额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalTaxPrice;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"其它金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal otherPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    @ExcelProperty(\"附件地址\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"出库项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"出库项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"销售订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        private Long orderItemId;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"税额，单位：元\", example = \"100.00\")\n        private BigDecimal taxPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n        @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 销售出库新增/修改 Request VO\")\n@Data\npublic class ErpSaleOutSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long id;\n\n    @Schema(description = \"结算账户编号\", example = \"31189\")\n    private Long accountId;\n\n    @Schema(description = \"销售员编号\", example = \"1888\")\n    private Long saleUserId;\n\n    @Schema(description = \"出库时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出库时间不能为空\")\n    private LocalDateTime outTime;\n\n    @Schema(description = \"销售订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @NotNull(message = \"销售订单编号不能为空\")\n    private Long orderId;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"其它金额，单位：元\", example = \"7127\")\n    private BigDecimal otherPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"出库清单列表\")\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"出库项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"销售订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        @NotNull(message = \"销售订单项编号不能为空\")\n        private Long orderItemId;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"仓库编号不能为空\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品单位单位不能为空\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 销售退货分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpSaleReturnPageReqVO extends PageParam {\n\n    public static final Integer REFUND_STATUS_NONE = 0;\n    public static final Integer REFUND_STATUS_PART = 1;\n    public static final Integer REFUND_STATUS_ALL = 2;\n\n    @Schema(description = \"销售单编号\", example = \"XS001\")\n    private String no;\n\n    @Schema(description = \"客户编号\", example = \"1724\")\n    private Long customerId;\n\n    @Schema(description = \"退货时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] returnTime;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"退货状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", example = \"1\")\n    private Long warehouseId;\n\n    @Schema(description = \"结算账号编号\", example = \"1\")\n    private Long accountId;\n\n    @Schema(description = \"销售单号\", example = \"1\")\n    private String orderNo;\n\n    @Schema(description = \"退款状态\", example = \"1\")\n    private Integer refundStatus;\n\n    @Schema(description = \"是否可退款\", example = \"true\")\n    private Boolean refundEnable; // 对应 refundStatus = [0, 1]\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 销售退货 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpSaleReturnRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"退货单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    @ExcelProperty(\"退货单编号\")\n    private String no;\n\n    @Schema(description = \"退货状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"退货状态\")\n    private Integer status;\n\n    @Schema(description = \"客户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1724\")\n    private Long customerId;\n    @Schema(description = \"客户名称\", example = \"芋道\")\n    @ExcelProperty(\"客户名称\")\n    private String customerName;\n\n    @Schema(description = \"结算账户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"311.89\")\n    @ExcelProperty(\"结算账户编号\")\n    private Long accountId;\n\n    @Schema(description = \"退货员编号\", example = \"1888\")\n    private Long saleUserId;\n\n    @Schema(description = \"退货时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"退货时间\")\n    private LocalDateTime returnTime;\n\n    @Schema(description = \"销售订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long orderId;\n    @Schema(description = \"销售订单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"XS001\")\n    private String orderNo;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n    @Schema(description = \"最终合计价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"最终合计价格\")\n    private BigDecimal totalPrice;\n    @Schema(description = \"已退款金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal refundPrice;\n\n    @Schema(description = \"合计产品价格，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalProductPrice;\n\n    @Schema(description = \"合计税额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal totalTaxPrice;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"优惠金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal discountPrice;\n\n    @Schema(description = \"其它金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7127\")\n    private BigDecimal otherPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    @ExcelProperty(\"附件地址\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"退货项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"退货项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"销售订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        private Long orderItemId;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"税额，单位：元\", example = \"100.00\")\n        private BigDecimal taxPrice;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n        @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 销售退货新增/修改 Request VO\")\n@Data\npublic class ErpSaleReturnSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    private Long id;\n\n    @Schema(description = \"结算账户编号\", example = \"31189\")\n    private Long accountId;\n\n    @Schema(description = \"销售员编号\", example = \"1888\")\n    private Long saleUserId;\n\n    @Schema(description = \"退货时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"退货时间不能为空\")\n    private LocalDateTime returnTime;\n\n    @Schema(description = \"销售订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17386\")\n    @NotNull(message = \"销售订单编号不能为空\")\n    private Long orderId;\n\n    @Schema(description = \"优惠率，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99.88\")\n    private BigDecimal discountPercent;\n\n    @Schema(description = \"其它金额，单位：元\", example = \"7127\")\n    private BigDecimal otherPrice;\n\n    @Schema(description = \"附件地址\", example = \"https://www.iocoder.cn\")\n    private String fileUrl;\n\n    @Schema(description = \"备注\", example = \"你猜\")\n    private String remark;\n\n    @Schema(description = \"退货清单列表\")\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"退货项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"销售订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n        @NotNull(message = \"销售订单项编号不能为空\")\n        private Long orderItemId;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"仓库编号不能为空\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单位单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品单位单位不能为空\")\n        private Long productUnitId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"税率，百分比\", example = \"99.88\")\n        private BigDecimal taxPercent;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpPurchaseStatisticsController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.statistics;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.statistics.vo.purchase.ErpPurchaseSummaryRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.statistics.vo.purchase.ErpPurchaseTimeSummaryRespVO;\nimport cn.iocoder.yudao.module.erp.service.statistics.ErpPurchaseStatisticsService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static cn.hutool.core.date.DatePattern.NORM_MONTH_PATTERN;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - ERP 采购统计\")\n@RestController\n@RequestMapping(\"/erp/purchase-statistics\")\n@Validated\npublic class ErpPurchaseStatisticsController {\n\n    @Resource\n    private ErpPurchaseStatisticsService purchaseStatisticsService;\n\n    @GetMapping(\"/summary\")\n    @Operation(summary = \"获得采购统计\")\n    @PreAuthorize(\"@ss.hasPermission('erp:statistics:query')\")\n    public CommonResult<ErpPurchaseSummaryRespVO> getPurchaseSummary() {\n        LocalDateTime today = LocalDateTimeUtils.getToday();\n        LocalDateTime yesterday = LocalDateTimeUtils.getYesterday();\n        LocalDateTime month = LocalDateTimeUtils.getMonth();\n        LocalDateTime year = LocalDateTimeUtils.getYear();\n        ErpPurchaseSummaryRespVO summary = new ErpPurchaseSummaryRespVO()\n                .setTodayPrice(purchaseStatisticsService.getPurchasePrice(today, null))\n                .setYesterdayPrice(purchaseStatisticsService.getPurchasePrice(yesterday, today))\n                .setMonthPrice(purchaseStatisticsService.getPurchasePrice(month, null))\n                .setYearPrice(purchaseStatisticsService.getPurchasePrice(year, null));\n        return success(summary);\n    }\n\n    @GetMapping(\"/time-summary\")\n    @Operation(summary = \"获得采购时间段统计\")\n    @Parameter(name = \"count\", description = \"时间段数量\", example = \"6\")\n    @PreAuthorize(\"@ss.hasPermission('erp:statistics:query')\")\n    public CommonResult<List<ErpPurchaseTimeSummaryRespVO>> getPurchaseTimeSummary(\n            @RequestParam(value = \"count\", defaultValue = \"6\") Integer count) {\n        List<ErpPurchaseTimeSummaryRespVO> summaryList = new ArrayList<>();\n        for (int i = count - 1; i >= 0; i--) {\n            LocalDateTime startTime = LocalDateTimeUtils.beginOfMonth(LocalDateTime.now().minusMonths(i));\n            LocalDateTime endTime = LocalDateTimeUtils.endOfMonth(startTime);\n            summaryList.add(new ErpPurchaseTimeSummaryRespVO()\n                    .setTime(LocalDateTimeUtil.format(startTime, NORM_MONTH_PATTERN))\n                    .setPrice(purchaseStatisticsService.getPurchasePrice(startTime, endTime)));\n        }\n        return success(summaryList);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpSaleStatisticsController.http",
    "content": "### 请求 /erp/sale-statistics/summary 接口 => 成功\nGET {{baseUrl}}/erp/sale-statistics/summary\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n### 请求 /erp/sale-statistics/time-summary 接口 => 成功\nGET {{baseUrl}}/erp/sale-statistics/time-summary\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpSaleStatisticsController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.statistics;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.statistics.vo.sale.ErpSaleSummaryRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.statistics.vo.sale.ErpSaleTimeSummaryRespVO;\nimport cn.iocoder.yudao.module.erp.service.statistics.ErpSaleStatisticsService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static cn.hutool.core.date.DatePattern.NORM_MONTH_PATTERN;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - ERP 销售统计\")\n@RestController\n@RequestMapping(\"/erp/sale-statistics\")\n@Validated\npublic class ErpSaleStatisticsController {\n\n    @Resource\n    private ErpSaleStatisticsService saleStatisticsService;\n\n    @GetMapping(\"/summary\")\n    @Operation(summary = \"获得销售统计\")\n    @PreAuthorize(\"@ss.hasPermission('erp:statistics:query')\")\n    public CommonResult<ErpSaleSummaryRespVO> getSaleSummary() {\n        LocalDateTime today = LocalDateTimeUtils.getToday();\n        LocalDateTime yesterday = LocalDateTimeUtils.getYesterday();\n        LocalDateTime month = LocalDateTimeUtils.getMonth();\n        LocalDateTime year = LocalDateTimeUtils.getYear();\n        ErpSaleSummaryRespVO summary = new ErpSaleSummaryRespVO()\n                .setTodayPrice(saleStatisticsService.getSalePrice(today, null))\n                .setYesterdayPrice(saleStatisticsService.getSalePrice(yesterday, today))\n                .setMonthPrice(saleStatisticsService.getSalePrice(month, null))\n                .setYearPrice(saleStatisticsService.getSalePrice(year, null));\n        return success(summary);\n    }\n\n    @GetMapping(\"/time-summary\")\n    @Operation(summary = \"获得销售时间段统计\")\n    @Parameter(name = \"count\", description = \"时间段数量\", example = \"6\")\n    @PreAuthorize(\"@ss.hasPermission('erp:statistics:query')\")\n    public CommonResult<List<ErpSaleTimeSummaryRespVO>> getSaleTimeSummary(\n            @RequestParam(value = \"count\", defaultValue = \"6\") Integer count) {\n        List<ErpSaleTimeSummaryRespVO> summaryList = new ArrayList<>();\n        for (int i = count - 1; i >= 0; i--) {\n            LocalDateTime startTime = LocalDateTimeUtils.beginOfMonth(LocalDateTime.now().minusMonths(i));\n            LocalDateTime endTime = LocalDateTimeUtils.endOfMonth(startTime);\n            summaryList.add(new ErpSaleTimeSummaryRespVO()\n                    .setTime(LocalDateTimeUtil.format(startTime, NORM_MONTH_PATTERN))\n                    .setPrice(saleStatisticsService.getSalePrice(startTime, endTime)));\n        }\n        return success(summaryList);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/vo/purchase/ErpPurchaseSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.statistics.vo.purchase;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - ERP 采购全局统计 Response VO\")\n@Data\npublic class ErpPurchaseSummaryRespVO {\n\n    @Schema(description = \"今日采购金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private BigDecimal todayPrice;\n\n    @Schema(description = \"昨日采购金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n    private BigDecimal yesterdayPrice;\n\n    @Schema(description = \"本月采购金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private BigDecimal monthPrice;\n\n    @Schema(description = \"今年采购金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"88888\")\n    private BigDecimal yearPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/vo/purchase/ErpPurchaseTimeSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.statistics.vo.purchase;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - ERP 采购某个时间段的统计 Response VO\")\n@Data\npublic class ErpPurchaseTimeSummaryRespVO {\n\n    @Schema(description = \"时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2022-03\")\n    private String time;\n\n    @Schema(description = \"采购金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private BigDecimal price;\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/vo/sale/ErpSaleSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.statistics.vo.sale;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - ERP 销售全局统计 Response VO\")\n@Data\npublic class ErpSaleSummaryRespVO {\n\n    @Schema(description = \"今日销售金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private BigDecimal todayPrice;\n\n    @Schema(description = \"昨日销售金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n    private BigDecimal yesterdayPrice;\n\n    @Schema(description = \"本月销售金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private BigDecimal monthPrice;\n\n    @Schema(description = \"今年销售金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"88888\")\n    private BigDecimal yearPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/vo/sale/ErpSaleTimeSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.statistics.vo.sale;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - ERP 销售某个时间段的统计 Response VO\")\n@Data\npublic class ErpSaleTimeSummaryRespVO {\n\n    @Schema(description = \"时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2022-03\")\n    private String time;\n\n    @Schema(description = \"销售金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private BigDecimal price;\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockCheckService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 库存调拨单\")\n@RestController\n@RequestMapping(\"/erp/stock-check\")\n@Validated\npublic class ErpStockCheckController {\n\n    @Resource\n    private ErpStockCheckService stockCheckService;\n    @Resource\n    private ErpProductService productService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建库存调拨单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-check:create')\")\n    public CommonResult<Long> createStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO createReqVO) {\n        return success(stockCheckService.createStockCheck(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新库存调拨单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-check:update')\")\n    public CommonResult<Boolean> updateStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO updateReqVO) {\n        stockCheckService.updateStockCheck(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新库存调拨单的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-check:update-status')\")\n    public CommonResult<Boolean> updateStockCheckStatus(@RequestParam(\"id\") Long id,\n                                                     @RequestParam(\"status\") Integer status) {\n        stockCheckService.updateStockCheckStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除库存调拨单\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-check:delete')\")\n    public CommonResult<Boolean> deleteStockCheck(@RequestParam(\"ids\") List<Long> ids) {\n        stockCheckService.deleteStockCheck(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得库存调拨单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-check:query')\")\n    public CommonResult<ErpStockCheckRespVO> getStockCheck(@RequestParam(\"id\") Long id) {\n        ErpStockCheckDO stockCheck = stockCheckService.getStockCheck(id);\n        if (stockCheck == null) {\n            return success(null);\n        }\n        List<ErpStockCheckItemDO> stockCheckItemList = stockCheckService.getStockCheckItemListByCheckId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(stockCheckItemList, ErpStockCheckItemDO::getProductId));\n        return success(BeanUtils.toBean(stockCheck, ErpStockCheckRespVO.class, stockCheckVO ->\n                stockCheckVO.setItems(BeanUtils.toBean(stockCheckItemList, ErpStockCheckRespVO.Item.class, item ->\n                        MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                                .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得库存调拨单分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-check:query')\")\n    public CommonResult<PageResult<ErpStockCheckRespVO>> getStockCheckPage(@Valid ErpStockCheckPageReqVO pageReqVO) {\n        PageResult<ErpStockCheckDO> pageResult = stockCheckService.getStockCheckPage(pageReqVO);\n        return success(buildStockCheckVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出库存调拨单 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-check:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportStockCheckExcel(@Valid ErpStockCheckPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpStockCheckRespVO> list = buildStockCheckVOPageResult(stockCheckService.getStockCheckPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"库存调拨单.xls\", \"数据\", ErpStockCheckRespVO.class, list);\n    }\n\n    private PageResult<ErpStockCheckRespVO> buildStockCheckVOPageResult(PageResult<ErpStockCheckDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 盘点项\n        List<ErpStockCheckItemDO> stockCheckItemList = stockCheckService.getStockCheckItemListByCheckIds(\n                convertSet(pageResult.getList(), ErpStockCheckDO::getId));\n        Map<Long, List<ErpStockCheckItemDO>> stockCheckItemMap = convertMultiMap(stockCheckItemList, ErpStockCheckItemDO::getCheckId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(stockCheckItemList, ErpStockCheckItemDO::getProductId));\n        // 1.3 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), stockCheck -> Long.parseLong(stockCheck.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpStockCheckRespVO.class, stockCheck -> {\n            stockCheck.setItems(BeanUtils.toBean(stockCheckItemMap.get(stockCheck.getId()), ErpStockCheckRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            stockCheck.setProductNames(CollUtil.join(stockCheck.getItems(), \"，\", ErpStockCheckRespVO.Item::getProductName));\n            MapUtils.findAndThen(userMap, Long.parseLong(stockCheck.getCreator()), user -> stockCheck.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockRespVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 产品库存\")\n@RestController\n@RequestMapping(\"/erp/stock\")\n@Validated\npublic class ErpStockController {\n\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpWarehouseService warehouseService;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品库存\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"编号\", example = \"1\"), // 方案一：传递 id\n            @Parameter(name = \"productId\", description = \"产品编号\", example = \"10\"), // 方案二：传递 productId + warehouseId\n            @Parameter(name = \"warehouseId\", description = \"仓库编号\", example = \"2\")\n    })\n    @PreAuthorize(\"@ss.hasPermission('erp:stock:query')\")\n    public CommonResult<ErpStockRespVO> getStock(@RequestParam(value = \"id\", required = false) Long id,\n                                                 @RequestParam(value = \"productId\", required = false) Long productId,\n                                                 @RequestParam(value = \"warehouseId\", required = false) Long warehouseId) {\n        ErpStockDO stock = id != null ? stockService.getStock(id) : stockService.getStock(productId, warehouseId);\n        return success(BeanUtils.toBean(stock, ErpStockRespVO.class));\n    }\n\n    @GetMapping(\"/get-count\")\n    @Operation(summary = \"获得产品库存数量\")\n    @Parameter(name = \"productId\", description = \"产品编号\", example = \"10\")\n    public CommonResult<BigDecimal> getStockCount(@RequestParam(\"productId\") Long productId) {\n        return success(stockService.getStockCount(productId));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得产品库存分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock:query')\")\n    public CommonResult<PageResult<ErpStockRespVO>> getStockPage(@Valid ErpStockPageReqVO pageReqVO) {\n        PageResult<ErpStockDO> pageResult = stockService.getStockPage(pageReqVO);\n        return success(buildStockVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出产品库存 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportStockExcel(@Valid ErpStockPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpStockRespVO> list = buildStockVOPageResult(stockService.getStockPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"产品库存.xls\", \"数据\", ErpStockRespVO.class, list);\n    }\n\n    private PageResult<ErpStockRespVO> buildStockVOPageResult(PageResult<ErpStockDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(pageResult.getList(), ErpStockDO::getProductId));\n        Map<Long, ErpWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(\n                convertSet(pageResult.getList(), ErpStockDO::getWarehouseId));\n        return BeanUtils.toBean(pageResult, ErpStockRespVO.class, stock -> {\n            MapUtils.findAndThen(productMap, stock.getProductId(), product -> stock.setProductName(product.getName())\n                    .setCategoryName(product.getCategoryName()).setUnitName(product.getUnitName()));\n            MapUtils.findAndThen(warehouseMap, stock.getWarehouseId(), warehouse -> stock.setWarehouseName(warehouse.getName()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockInService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 其它入库单\")\n@RestController\n@RequestMapping(\"/erp/stock-in\")\n@Validated\npublic class ErpStockInController {\n\n    @Resource\n    private ErpStockInService stockInService;\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpSupplierService supplierService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建其它入库单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-in:create')\")\n    public CommonResult<Long> createStockIn(@Valid @RequestBody ErpStockInSaveReqVO createReqVO) {\n        return success(stockInService.createStockIn(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新其它入库单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-in:update')\")\n    public CommonResult<Boolean> updateStockIn(@Valid @RequestBody ErpStockInSaveReqVO updateReqVO) {\n        stockInService.updateStockIn(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新其它入库单的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-in:update-status')\")\n    public CommonResult<Boolean> updateStockInStatus(@RequestParam(\"id\") Long id,\n                                                     @RequestParam(\"status\") Integer status) {\n        stockInService.updateStockInStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除其它入库单\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-in:delete')\")\n    public CommonResult<Boolean> deleteStockIn(@RequestParam(\"ids\") List<Long> ids) {\n        stockInService.deleteStockIn(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得其它入库单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-in:query')\")\n    public CommonResult<ErpStockInRespVO> getStockIn(@RequestParam(\"id\") Long id) {\n        ErpStockInDO stockIn = stockInService.getStockIn(id);\n        if (stockIn == null) {\n            return success(null);\n        }\n        List<ErpStockInItemDO> stockInItemList = stockInService.getStockInItemListByInId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(stockInItemList, ErpStockInItemDO::getProductId));\n        return success(BeanUtils.toBean(stockIn, ErpStockInRespVO.class, stockInVO ->\n                stockInVO.setItems(BeanUtils.toBean(stockInItemList, ErpStockInRespVO.Item.class, item -> {\n                    ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId());\n                    item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO);\n                    MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));\n                }))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得其它入库单分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-in:query')\")\n    public CommonResult<PageResult<ErpStockInRespVO>> getStockInPage(@Valid ErpStockInPageReqVO pageReqVO) {\n        PageResult<ErpStockInDO> pageResult = stockInService.getStockInPage(pageReqVO);\n        return success(buildStockInVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出其它入库单 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-in:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportStockInExcel(@Valid ErpStockInPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpStockInRespVO> list = buildStockInVOPageResult(stockInService.getStockInPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"其它入库单.xls\", \"数据\", ErpStockInRespVO.class, list);\n    }\n\n    private PageResult<ErpStockInRespVO> buildStockInVOPageResult(PageResult<ErpStockInDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 入库项\n        List<ErpStockInItemDO> stockInItemList = stockInService.getStockInItemListByInIds(\n                convertSet(pageResult.getList(), ErpStockInDO::getId));\n        Map<Long, List<ErpStockInItemDO>> stockInItemMap = convertMultiMap(stockInItemList, ErpStockInItemDO::getInId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(stockInItemList, ErpStockInItemDO::getProductId));\n        // 1.3 供应商信息\n        Map<Long, ErpSupplierDO> supplierMap = supplierService.getSupplierMap(\n                convertSet(pageResult.getList(), ErpStockInDO::getSupplierId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), stockIn -> Long.parseLong(stockIn.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpStockInRespVO.class, stockIn -> {\n            stockIn.setItems(BeanUtils.toBean(stockInItemMap.get(stockIn.getId()), ErpStockInRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            stockIn.setProductNames(CollUtil.join(stockIn.getItems(), \"，\", ErpStockInRespVO.Item::getProductName));\n            MapUtils.findAndThen(supplierMap, stockIn.getSupplierId(), supplier -> stockIn.setSupplierName(supplier.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(stockIn.getCreator()), user -> stockIn.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMovePageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMoveRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMoveSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockMoveService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 库存调拨单\")\n@RestController\n@RequestMapping(\"/erp/stock-move\")\n@Validated\npublic class ErpStockMoveController {\n\n    @Resource\n    private ErpStockMoveService stockMoveService;\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建库存调拨单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-move:create')\")\n    public CommonResult<Long> createStockMove(@Valid @RequestBody ErpStockMoveSaveReqVO createReqVO) {\n        return success(stockMoveService.createStockMove(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新库存调拨单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-move:update')\")\n    public CommonResult<Boolean> updateStockMove(@Valid @RequestBody ErpStockMoveSaveReqVO updateReqVO) {\n        stockMoveService.updateStockMove(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新库存调拨单的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-move:update-status')\")\n    public CommonResult<Boolean> updateStockMoveStatus(@RequestParam(\"id\") Long id,\n                                                     @RequestParam(\"status\") Integer status) {\n        stockMoveService.updateStockMoveStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除库存调拨单\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-move:delete')\")\n    public CommonResult<Boolean> deleteStockMove(@RequestParam(\"ids\") List<Long> ids) {\n        stockMoveService.deleteStockMove(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得库存调拨单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-move:query')\")\n    public CommonResult<ErpStockMoveRespVO> getStockMove(@RequestParam(\"id\") Long id) {\n        ErpStockMoveDO stockMove = stockMoveService.getStockMove(id);\n        if (stockMove == null) {\n            return success(null);\n        }\n        List<ErpStockMoveItemDO> stockMoveItemList = stockMoveService.getStockMoveItemListByMoveId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(stockMoveItemList, ErpStockMoveItemDO::getProductId));\n        return success(BeanUtils.toBean(stockMove, ErpStockMoveRespVO.class, stockMoveVO ->\n                stockMoveVO.setItems(BeanUtils.toBean(stockMoveItemList, ErpStockMoveRespVO.Item.class, item -> {\n                    ErpStockDO stock = stockService.getStock(item.getProductId(), item.getFromWarehouseId());\n                    item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO);\n                    MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));\n                }))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得库存调拨单分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-move:query')\")\n    public CommonResult<PageResult<ErpStockMoveRespVO>> getStockMovePage(@Valid ErpStockMovePageReqVO pageReqVO) {\n        PageResult<ErpStockMoveDO> pageResult = stockMoveService.getStockMovePage(pageReqVO);\n        return success(buildStockMoveVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出库存调拨单 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-move:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportStockMoveExcel(@Valid ErpStockMovePageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpStockMoveRespVO> list = buildStockMoveVOPageResult(stockMoveService.getStockMovePage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"库存调拨单.xls\", \"数据\", ErpStockMoveRespVO.class, list);\n    }\n\n    private PageResult<ErpStockMoveRespVO> buildStockMoveVOPageResult(PageResult<ErpStockMoveDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 调拨项\n        List<ErpStockMoveItemDO> stockMoveItemList = stockMoveService.getStockMoveItemListByMoveIds(\n                convertSet(pageResult.getList(), ErpStockMoveDO::getId));\n        Map<Long, List<ErpStockMoveItemDO>> stockMoveItemMap = convertMultiMap(stockMoveItemList, ErpStockMoveItemDO::getMoveId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(stockMoveItemList, ErpStockMoveItemDO::getProductId));\n        // 1.3 TODO 芋艿：搞仓库信息\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), stockMove -> Long.parseLong(stockMove.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpStockMoveRespVO.class, stockMove -> {\n            stockMove.setItems(BeanUtils.toBean(stockMoveItemMap.get(stockMove.getId()), ErpStockMoveRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            stockMove.setProductNames(CollUtil.join(stockMove.getItems(), \"，\", ErpStockMoveRespVO.Item::getProductName));\n            // TODO 芋艿：\n//            MapUtils.findAndThen(customerMap, stockMove.getCustomerId(), supplier -> stockMove.setCustomerName(supplier.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(stockMove.getCreator()), user -> stockMove.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockOutController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockOutService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 其它出库单\")\n@RestController\n@RequestMapping(\"/erp/stock-out\")\n@Validated\npublic class ErpStockOutController {\n\n    @Resource\n    private ErpStockOutService stockOutService;\n    @Resource\n    private ErpStockService stockService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpCustomerService customerService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建其它出库单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-out:create')\")\n    public CommonResult<Long> createStockOut(@Valid @RequestBody ErpStockOutSaveReqVO createReqVO) {\n        return success(stockOutService.createStockOut(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新其它出库单\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-out:update')\")\n    public CommonResult<Boolean> updateStockOut(@Valid @RequestBody ErpStockOutSaveReqVO updateReqVO) {\n        stockOutService.updateStockOut(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新其它出库单的状态\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-out:update-status')\")\n    public CommonResult<Boolean> updateStockOutStatus(@RequestParam(\"id\") Long id,\n                                                     @RequestParam(\"status\") Integer status) {\n        stockOutService.updateStockOutStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除其它出库单\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-out:delete')\")\n    public CommonResult<Boolean> deleteStockOut(@RequestParam(\"ids\") List<Long> ids) {\n        stockOutService.deleteStockOut(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得其它出库单\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-out:query')\")\n    public CommonResult<ErpStockOutRespVO> getStockOut(@RequestParam(\"id\") Long id) {\n        ErpStockOutDO stockOut = stockOutService.getStockOut(id);\n        if (stockOut == null) {\n            return success(null);\n        }\n        List<ErpStockOutItemDO> stockOutItemList = stockOutService.getStockOutItemListByOutId(id);\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(stockOutItemList, ErpStockOutItemDO::getProductId));\n        return success(BeanUtils.toBean(stockOut, ErpStockOutRespVO.class, stockOutVO ->\n                stockOutVO.setItems(BeanUtils.toBean(stockOutItemList, ErpStockOutRespVO.Item.class, item -> {\n                    ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId());\n                    item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO);\n                    MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));\n                }))));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得其它出库单分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-out:query')\")\n    public CommonResult<PageResult<ErpStockOutRespVO>> getStockOutPage(@Valid ErpStockOutPageReqVO pageReqVO) {\n        PageResult<ErpStockOutDO> pageResult = stockOutService.getStockOutPage(pageReqVO);\n        return success(buildStockOutVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出其它出库单 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-out:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportStockOutExcel(@Valid ErpStockOutPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpStockOutRespVO> list = buildStockOutVOPageResult(stockOutService.getStockOutPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"其它出库单.xls\", \"数据\", ErpStockOutRespVO.class, list);\n    }\n\n    private PageResult<ErpStockOutRespVO> buildStockOutVOPageResult(PageResult<ErpStockOutDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        // 1.1 出库项\n        List<ErpStockOutItemDO> stockOutItemList = stockOutService.getStockOutItemListByOutIds(\n                convertSet(pageResult.getList(), ErpStockOutDO::getId));\n        Map<Long, List<ErpStockOutItemDO>> stockOutItemMap = convertMultiMap(stockOutItemList, ErpStockOutItemDO::getOutId);\n        // 1.2 产品信息\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(stockOutItemList, ErpStockOutItemDO::getProductId));\n        // 1.3 客户信息\n        Map<Long, ErpCustomerDO> customerMap = customerService.getCustomerMap(\n                convertSet(pageResult.getList(), ErpStockOutDO::getCustomerId));\n        // 1.4 管理员信息\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), stockOut -> Long.parseLong(stockOut.getCreator())));\n        // 2. 开始拼接\n        return BeanUtils.toBean(pageResult, ErpStockOutRespVO.class, stockOut -> {\n            stockOut.setItems(BeanUtils.toBean(stockOutItemMap.get(stockOut.getId()), ErpStockOutRespVO.Item.class,\n                    item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())\n                            .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));\n            stockOut.setProductNames(CollUtil.join(stockOut.getItems(), \"，\", ErpStockOutRespVO.Item::getProductName));\n            MapUtils.findAndThen(customerMap, stockOut.getCustomerId(), supplier -> stockOut.setCustomerName(supplier.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(stockOut.getCreator()), user -> stockOut.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockRecordController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordRespVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - ERP 产品库存明细\")\n@RestController\n@RequestMapping(\"/erp/stock-record\")\n@Validated\npublic class ErpStockRecordController {\n\n    @Resource\n    private ErpStockRecordService stockRecordService;\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpWarehouseService warehouseService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品库存明细\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-record:query')\")\n    public CommonResult<ErpStockRecordRespVO> getStockRecord(@RequestParam(\"id\") Long id) {\n        ErpStockRecordDO stockRecord = stockRecordService.getStockRecord(id);\n        return success(BeanUtils.toBean(stockRecord, ErpStockRecordRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得产品库存明细分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-record:query')\")\n    public CommonResult<PageResult<ErpStockRecordRespVO>> getStockRecordPage(@Valid ErpStockRecordPageReqVO pageReqVO) {\n        PageResult<ErpStockRecordDO> pageResult = stockRecordService.getStockRecordPage(pageReqVO);\n        return success(buildStockRecrodVOPageResult(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出产品库存明细 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:stock-record:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportStockRecordExcel(@Valid ErpStockRecordPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpStockRecordRespVO> list = buildStockRecrodVOPageResult(stockRecordService.getStockRecordPage(pageReqVO)).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"产品库存明细.xls\", \"数据\", ErpStockRecordRespVO.class, list);\n    }\n\n    private PageResult<ErpStockRecordRespVO> buildStockRecrodVOPageResult(PageResult<ErpStockRecordDO> pageResult) {\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return PageResult.empty(pageResult.getTotal());\n        }\n        Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(\n                convertSet(pageResult.getList(), ErpStockRecordDO::getProductId));\n        Map<Long, ErpWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(\n                convertSet(pageResult.getList(), ErpStockRecordDO::getWarehouseId));\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSet(pageResult.getList(), record -> Long.parseLong(record.getCreator())));\n        return BeanUtils.toBean(pageResult, ErpStockRecordRespVO.class, stock -> {\n            MapUtils.findAndThen(productMap, stock.getProductId(), product -> stock.setProductName(product.getName())\n                    .setCategoryName(product.getCategoryName()).setUnitName(product.getUnitName()));\n            MapUtils.findAndThen(warehouseMap, stock.getWarehouseId(), warehouse -> stock.setWarehouseName(warehouse.getName()));\n            MapUtils.findAndThen(userMap, Long.parseLong(stock.getCreator()), user -> stock.setCreatorName(user.getNickname()));\n        });\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - ERP 仓库\")\n@RestController\n@RequestMapping(\"/erp/warehouse\")\n@Validated\npublic class ErpWarehouseController {\n\n    @Resource\n    private ErpWarehouseService warehouseService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建仓库\")\n    @PreAuthorize(\"@ss.hasPermission('erp:warehouse:create')\")\n    public CommonResult<Long> createWarehouse(@Valid @RequestBody ErpWarehouseSaveReqVO createReqVO) {\n        return success(warehouseService.createWarehouse(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新仓库\")\n    @PreAuthorize(\"@ss.hasPermission('erp:warehouse:update')\")\n    public CommonResult<Boolean> updateWarehouse(@Valid @RequestBody ErpWarehouseSaveReqVO updateReqVO) {\n        warehouseService.updateWarehouse(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-default-status\")\n    @Operation(summary = \"更新仓库默认状态\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"编号\", required = true),\n            @Parameter(name = \"status\", description = \"状态\", required = true)\n    })\n    public CommonResult<Boolean> updateWarehouseDefaultStatus(@RequestParam(\"id\") Long id,\n                                                              @RequestParam(\"defaultStatus\") Boolean defaultStatus) {\n        warehouseService.updateWarehouseDefaultStatus(id, defaultStatus);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除仓库\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('erp:warehouse:delete')\")\n    public CommonResult<Boolean> deleteWarehouse(@RequestParam(\"id\") Long id) {\n        warehouseService.deleteWarehouse(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得仓库\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('erp:warehouse:query')\")\n    public CommonResult<ErpWarehouseRespVO> getWarehouse(@RequestParam(\"id\") Long id) {\n        ErpWarehouseDO warehouse = warehouseService.getWarehouse(id);\n        return success(BeanUtils.toBean(warehouse, ErpWarehouseRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得仓库分页\")\n    @PreAuthorize(\"@ss.hasPermission('erp:warehouse:query')\")\n    public CommonResult<PageResult<ErpWarehouseRespVO>> getWarehousePage(@Valid ErpWarehousePageReqVO pageReqVO) {\n        PageResult<ErpWarehouseDO> pageResult = warehouseService.getWarehousePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, ErpWarehouseRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得仓库精简列表\", description = \"只包含被开启的仓库，主要用于前端的下拉选项\")\n    public CommonResult<List<ErpWarehouseRespVO>> getWarehouseSimpleList() {\n        List<ErpWarehouseDO> list = warehouseService.getWarehouseListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, warehouse -> new ErpWarehouseRespVO().setId(warehouse.getId())\n                .setName(warehouse.getName()).setDefaultStatus(warehouse.getDefaultStatus())));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出仓库 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('erp:warehouse:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportWarehouseExcel(@Valid ErpWarehousePageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ErpWarehouseDO> list = warehouseService.getWarehousePage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"仓库.xls\", \"数据\", ErpWarehouseRespVO.class,\n                        BeanUtils.toBean(list, ErpWarehouseRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 库存盘点单分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpStockCheckPageReqVO extends PageParam {\n\n    @Schema(description = \"盘点单号\", example = \"S123\")\n    private String no;\n\n    @Schema(description = \"仓库编号\", example = \"3113\")\n    private Long warehouseId;\n\n    @Schema(description = \"盘点时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] checkTime;\n\n    @Schema(description = \"状态\", example = \"10\")\n    @InEnum(ErpAuditStatus.class)\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.AUDIT_STATUS;\n\n@Schema(description = \"管理后台 - ERP 库存盘点单 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpStockCheckRespVO {\n\n    @Schema(description = \"盘点编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n    @ExcelProperty(\"盘点编号\")\n    private Long id;\n\n    @Schema(description = \"盘点单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"S123\")\n    @ExcelProperty(\"盘点单号\")\n    private String no;\n\n    @Schema(description = \"盘点时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"盘点时间\")\n    private LocalDateTime checkTime;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n\n    @Schema(description = \"合计金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"合计金额\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @ExcelProperty(value = \"状态\", converter = DictConvert.class)\n    @DictFormat(AUDIT_STATUS)\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"附件 URL\", example = \"https://www.iocoder.cn/1.doc\")\n    private String fileUrl;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"盘点项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"盘点项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"账面数量（当前库存）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"账面数量不能为空\")\n        private BigDecimal stockCount;\n\n        @Schema(description = \"实际数量（实际库存）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"实际数量不能为空\")\n        private BigDecimal actualCount;\n\n        @Schema(description = \"盈亏数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"盈亏数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 其它出库单新增/修改 Request VO\")\n@Data\npublic class ErpStockCheckSaveReqVO {\n\n    @Schema(description = \"出库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n    private Long id;\n\n    @Schema(description = \"出库时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出库时间不能为空\")\n    private LocalDateTime checkTime;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"附件 URL\", example = \"https://www.iocoder.cn/1.doc\")\n    private String fileUrl;\n\n    @Schema(description = \"出库项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"出库项列表不能为空\")\n    @Valid\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"出库项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"仓库编号不能为空\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"账面数量（当前库存）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"账面数量不能为空\")\n        private BigDecimal stockCount;\n\n        @Schema(description = \"实际数量（实际库存）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"实际数量不能为空\")\n        private BigDecimal actualCount;\n\n        @Schema(description = \"盈亏数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"盈亏数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 其它入库单分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpStockInPageReqVO extends PageParam {\n\n    @Schema(description = \"入库单号\", example = \"S123\")\n    private String no;\n\n    @Schema(description = \"供应商编号\", example = \"3113\")\n    private Long supplierId;\n\n    @Schema(description = \"入库时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] inTime;\n\n    @Schema(description = \"状态\", example = \"10\")\n    @InEnum(ErpAuditStatus.class)\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", example = \"1\")\n    private Long warehouseId;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.AUDIT_STATUS;\n\n@Schema(description = \"管理后台 - ERP 其它入库单 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpStockInRespVO {\n\n    @Schema(description = \"入库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n    @ExcelProperty(\"入库编号\")\n    private Long id;\n\n    @Schema(description = \"入库单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"S123\")\n    @ExcelProperty(\"入库单号\")\n    private String no;\n\n    @Schema(description = \"供应商编号\", example = \"3113\")\n    private Long supplierId;\n    @Schema(description = \"供应商名称\", example = \"芋道\")\n    @ExcelProperty(\"供应商名称\")\n    private String supplierName;\n\n    @Schema(description = \"入库时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"入库时间\")\n    private LocalDateTime inTime;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n\n    @Schema(description = \"合计金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"合计金额\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @ExcelProperty(value = \"状态\", converter = DictConvert.class)\n    @DictFormat(AUDIT_STATUS)\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"附件 URL\", example = \"https://www.iocoder.cn/1.doc\")\n    private String fileUrl;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"入库项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"入库项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal count;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n        @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 其它入库单新增/修改 Request VO\")\n@Data\npublic class ErpStockInSaveReqVO {\n\n    @Schema(description = \"入库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n    private Long id;\n\n    @Schema(description = \"供应商编号\", example = \"3113\")\n    private Long supplierId;\n\n    @Schema(description = \"入库时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"入库时间不能为空\")\n    private LocalDateTime inTime;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"附件 URL\", example = \"https://www.iocoder.cn/1.doc\")\n    private String fileUrl;\n\n    @Schema(description = \"入库项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"入库项列表不能为空\")\n    @Valid\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"入库项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"仓库编号不能为空\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMovePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 库存调拨单分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpStockMovePageReqVO extends PageParam {\n\n    @Schema(description = \"调拨单号\", example = \"S123\")\n    private String no;\n\n    @Schema(description = \"调拨时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] moveTime;\n\n    @Schema(description = \"状态\", example = \"10\")\n    @InEnum(ErpAuditStatus.class)\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n    @Schema(description = \"调出仓库编号\", example = \"1\")\n    private Long fromWarehouseId;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.AUDIT_STATUS;\n\n@Schema(description = \"管理后台 - ERP 库存调拨单 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpStockMoveRespVO {\n\n    @Schema(description = \"调拨编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n    @ExcelProperty(\"调拨编号\")\n    private Long id;\n\n    @Schema(description = \"调拨单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"S123\")\n    @ExcelProperty(\"调拨单号\")\n    private String no;\n\n    @Schema(description = \"调拨时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"调拨时间\")\n    private LocalDateTime moveTime;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n\n    @Schema(description = \"合计金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"合计金额\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @ExcelProperty(value = \"状态\", converter = DictConvert.class)\n    @DictFormat(AUDIT_STATUS)\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"附件 URL\", example = \"https://www.iocoder.cn/1.doc\")\n    private String fileUrl;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"调拨项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"调拨项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"调出仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long fromWarehouseId;\n\n        @Schema(description = \"调入仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n        private Long toWarehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal count;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n        @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.Valid;\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 库存调拨单新增/修改 Request VO\")\n@Data\npublic class ErpStockMoveSaveReqVO {\n\n    @Schema(description = \"调拨编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n    private Long id;\n\n    @Schema(description = \"客户编号\", example = \"3113\")\n    private Long customerId;\n\n    @Schema(description = \"调拨时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"调拨时间不能为空\")\n    private LocalDateTime moveTime;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"附件 URL\", example = \"https://www.iocoder.cn/1.doc\")\n    private String fileUrl;\n\n    @Schema(description = \"调拨项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"调拨项列表不能为空\")\n    @Valid\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"调拨项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"调出仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"调出仓库编号不能为空\")\n        private Long fromWarehouseId;\n\n        @Schema(description = \"调入仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n        @NotNull(message = \"调入仓库编号不能为空\")\n        private Long toWarehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        @AssertTrue(message = \"调出、调仓仓库不能相同\")\n        @JsonIgnore\n        public boolean isWarehouseValid() {\n            return ObjectUtil.notEqual(fromWarehouseId, toWarehouseId);\n        }\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 其它出库单分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpStockOutPageReqVO extends PageParam {\n\n    @Schema(description = \"出库单号\", example = \"S123\")\n    private String no;\n\n    @Schema(description = \"客户编号\", example = \"3113\")\n    private Long customerId;\n\n    @Schema(description = \"出库时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] outTime;\n\n    @Schema(description = \"状态\", example = \"10\")\n    @InEnum(ErpAuditStatus.class)\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"创建者\")\n    private String creator;\n\n    @Schema(description = \"产品编号\", example = \"1\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", example = \"1\")\n    private Long warehouseId;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.AUDIT_STATUS;\n\n@Schema(description = \"管理后台 - ERP 其它出库单 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpStockOutRespVO {\n\n    @Schema(description = \"出库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n    @ExcelProperty(\"出库编号\")\n    private Long id;\n\n    @Schema(description = \"出库单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"S123\")\n    @ExcelProperty(\"出库单号\")\n    private String no;\n\n    @Schema(description = \"客户编号\", example = \"3113\")\n    private Long customerId;\n    @Schema(description = \"客户名称\", example = \"芋道\")\n    @ExcelProperty(\"客户名称\")\n    private String customerName;\n\n    @Schema(description = \"出库时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出库时间\")\n    private LocalDateTime outTime;\n\n    @Schema(description = \"合计数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15663\")\n    @ExcelProperty(\"合计数量\")\n    private BigDecimal totalCount;\n\n    @Schema(description = \"合计金额，单位：元\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24906\")\n    @ExcelProperty(\"合计金额\")\n    private BigDecimal totalPrice;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @ExcelProperty(value = \"状态\", converter = DictConvert.class)\n    @DictFormat(AUDIT_STATUS)\n    private Integer status;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"附件 URL\", example = \"https://www.iocoder.cn/1.doc\")\n    private String fileUrl;\n\n    @Schema(description = \"创建人\", example = \"芋道\")\n    private String creator;\n    @Schema(description = \"创建人名称\", example = \"芋道\")\n    private String creatorName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"出库项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"产品信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品信息\")\n    private String productNames;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"出库项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal count;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n        // ========== 关联字段 ==========\n\n        @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"巧克力\")\n        private String productName;\n        @Schema(description = \"产品条码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"A9985\")\n        private String productBarCode;\n        @Schema(description = \"产品单位名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"盒\")\n        private String productUnitName;\n\n        @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - ERP 其它出库单新增/修改 Request VO\")\n@Data\npublic class ErpStockOutSaveReqVO {\n\n    @Schema(description = \"出库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11756\")\n    private Long id;\n\n    @Schema(description = \"客户编号\", example = \"3113\")\n    private Long customerId;\n\n    @Schema(description = \"出库时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出库时间不能为空\")\n    private LocalDateTime outTime;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"附件 URL\", example = \"https://www.iocoder.cn/1.doc\")\n    private String fileUrl;\n\n    @Schema(description = \"出库项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"出库项列表不能为空\")\n    @Valid\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"出库项编号\", example = \"11756\")\n        private Long id;\n\n        @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"仓库编号不能为空\")\n        private Long warehouseId;\n\n        @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3113\")\n        @NotNull(message = \"产品编号不能为空\")\n        private Long productId;\n\n        @Schema(description = \"产品单价\", example = \"100.00\")\n        private BigDecimal productPrice;\n\n        @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100.00\")\n        @NotNull(message = \"产品数量不能为空\")\n        private BigDecimal count;\n\n        @Schema(description = \"备注\", example = \"随便\")\n        private String remark;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - ERP 产品库存明细分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpStockRecordPageReqVO extends PageParam {\n\n    @Schema(description = \"产品编号\", example = \"10625\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", example = \"32407\")\n    private Long warehouseId;\n\n    @Schema(description = \"业务类型\", example = \"10\")\n    private Integer bizType;\n\n    @Schema(description = \"业务单号\", example = \"Z110\")\n    private String bizNo;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.erp.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - ERP 产品库存明细 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpStockRecordRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18909\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10625\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"32407\")\n    private Long warehouseId;\n\n    @Schema(description = \"出入库数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11084\")\n    @ExcelProperty(\"出入库数量\")\n    private BigDecimal count;\n\n    @Schema(description = \"总库存量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4307\")\n    @ExcelProperty(\"总库存量\")\n    private BigDecimal totalCount;\n\n    @Schema(description = \"业务类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @ExcelProperty(value = \"业务类型\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.STOCK_RECORD_BIZ_TYPE)\n    private Integer bizType;\n\n    @Schema(description = \"业务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"27093\")\n    @ExcelProperty(\"业务编号\")\n    private Long bizId;\n\n    @Schema(description = \"业务项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23516\")\n    @ExcelProperty(\"业务项编号\")\n    private Long bizItemId;\n\n    @Schema(description = \"业务单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Z110\")\n    @ExcelProperty(\"业务单号\")\n    private String bizNo;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"创建人\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25682\")\n    private String creator;\n\n    // ========== 产品信息 ==========\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"苹果\")\n    @ExcelProperty(\"产品名称\")\n    private String productName;\n\n    @Schema(description = \"产品分类\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"水果\")\n    @ExcelProperty(\"产品分类\")\n    private String categoryName;\n\n    @Schema(description = \"单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"个\")\n    @ExcelProperty(\"单位\")\n    private String unitName;\n\n    // ========== 仓库信息 ==========\n\n    @Schema(description = \"仓库名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @ExcelProperty(\"仓库名称\")\n    private String warehouseName;\n\n    // ========== 用户信息 ==========\n\n    @Schema(description = \"创建人\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    @ExcelProperty(\"创建人\")\n    private String creatorName;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - ERP 库存分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpStockPageReqVO extends PageParam {\n\n    @Schema(description = \"产品编号\", example = \"19614\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", example = \"2802\")\n    private Long warehouseId;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - ERP 库存 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpStockRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17086\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19614\")\n    private Long productId;\n\n    @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2802\")\n    private Long warehouseId;\n\n    @Schema(description = \"库存数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"21935\")\n    @ExcelProperty(\"库存数量\")\n    private BigDecimal count;\n\n    // ========== 产品信息 ==========\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"苹果\")\n    @ExcelProperty(\"产品名称\")\n    private String productName;\n\n    @Schema(description = \"产品分类\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"水果\")\n    @ExcelProperty(\"产品分类\")\n    private String categoryName;\n\n    @Schema(description = \"单位\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"个\")\n    @ExcelProperty(\"单位\")\n    private String unitName;\n\n    // ========== 仓库信息 ==========\n\n    @Schema(description = \"仓库名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @ExcelProperty(\"仓库名称\")\n    private String warehouseName;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehousePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - ERP 仓库分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ErpWarehousePageReqVO extends PageParam {\n\n    @Schema(description = \"仓库名称\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"开启状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseRespVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.system.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - ERP 仓库 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ErpWarehouseRespVO {\n\n    @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11614\")\n    @ExcelProperty(\"仓库编号\")\n    private Long id;\n\n    @Schema(description = \"仓库名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @ExcelProperty(\"仓库名称\")\n    private String name;\n\n    @Schema(description = \"仓库地址\", example = \"上海陆家嘴\")\n    @ExcelProperty(\"仓库地址\")\n    private String address;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @ExcelProperty(\"排序\")\n    private Long sort;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"负责人\", example = \"芋头\")\n    @ExcelProperty(\"负责人\")\n    private String principal;\n\n    @Schema(description = \"仓储费，单位：元\", example = \"13973\")\n    @ExcelProperty(\"仓储费，单位：元\")\n    private BigDecimal warehousePrice;\n\n    @Schema(description = \"搬运费，单位：元\", example = \"9903\")\n    @ExcelProperty(\"搬运费，单位：元\")\n    private BigDecimal truckagePrice;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"开启状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.COMMON_STATUS)\n    private Integer status;\n\n    @Schema(description = \"是否默认\", example = \"1\")\n    @ExcelProperty(\"是否默认\")\n    private Boolean defaultStatus;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - ERP 仓库新增/修改 Request VO\")\n@Data\npublic class ErpWarehouseSaveReqVO {\n\n    @Schema(description = \"仓库编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11614\")\n    private Long id;\n\n    @Schema(description = \"仓库名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotEmpty(message = \"仓库名称不能为空\")\n    private String name;\n\n    @Schema(description = \"仓库地址\", example = \"上海陆家嘴\")\n    private String address;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"排序不能为空\")\n    private Long sort;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"负责人\", example = \"芋头\")\n    private String principal;\n\n    @Schema(description = \"仓储费，单位：元\", example = \"13973\")\n    private BigDecimal warehousePrice;\n\n    @Schema(description = \"搬运费，单位：元\", example = \"9903\")\n    private BigDecimal truckagePrice;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"开启状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/controller/package-info.java",
    "content": "/**\n * 提供 RESTful API 给前端：\n * 1. admin 包：提供给管理后台 yudao-ui-admin 前端项目\n * 2. app 包：提供给用户 APP yudao-ui-app 前端项目，它的 Controller 和 VO 都要添加 App 前缀，用于和管理后台进行区分\n */\npackage cn.iocoder.yudao.module.erp.controller;\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpAccountDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.finance;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * ERP 结算账户 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_account\")\n@KeySequence(\"erp_account_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpAccountDO extends BaseDO {\n\n    /**\n     * 结算账户编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 账户名称\n     */\n    private String name;\n    /**\n     * 账户编码\n     */\n    private String no;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 开启状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 排序\n     */\n    private Integer sort;\n    /**\n     * 是否默认\n     */\n    private Boolean defaultStatus;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.finance;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 付款单 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_finance_payment\")\n@KeySequence(\"erp_finance_payment_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpFinancePaymentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 付款单号\n     */\n    private String no;\n    /**\n     * 付款状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 付款时间\n     */\n    private LocalDateTime paymentTime;\n    /**\n     * 财务人员编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long financeUserId;\n    /**\n     * 供应商编号\n     *\n     * 关联 {@link ErpSupplierDO#getId()}\n     */\n    private Long supplierId;\n    /**\n     * 付款账户编号\n     *\n     * 关联 {@link ErpAccountDO#getId()}\n     */\n    private Long accountId;\n\n    /**\n     * 合计价格，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 优惠金额，单位：元\n     */\n    private BigDecimal discountPrice;\n    /**\n     * 实付金额，单位：分\n     *\n     * paymentPrice = totalPrice - discountPrice\n     */\n    private BigDecimal paymentPrice;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.finance;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 付款项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_finance_payment_item\")\n@KeySequence(\"erp_finance_payment_item_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpFinancePaymentItemDO extends BaseDO {\n\n    /**\n     * 入库项编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 付款单编号\n     *\n     * 关联 {@link ErpFinancePaymentDO#getId()}\n     */\n    private Long paymentId;\n\n    /**\n     * 业务类型\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum} 的采购入库、退货\n     */\n    private Integer bizType;\n    /**\n     * 业务编号\n     *\n     * 例如说：{@link ErpPurchaseInDO#getId()}\n     */\n    private Long bizId;\n    /**\n     * 业务单号\n     *\n     * 例如说：{@link ErpPurchaseInDO#getNo()}\n     */\n    private String bizNo;\n\n    /**\n     * 应付金额，单位：分\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 已付金额，单位：分\n     */\n    private BigDecimal paidPrice;\n    /**\n     * 本次付款，单位：分\n     */\n    private BigDecimal paymentPrice;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.finance;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 收款单 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_finance_receipt\")\n@KeySequence(\"erp_finance_receipt_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpFinanceReceiptDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 收款单号\n     */\n    private String no;\n    /**\n     * 收款状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 收款时间\n     */\n    private LocalDateTime receiptTime;\n    /**\n     * 财务人员编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long financeUserId;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link ErpCustomerDO#getId()}\n     */\n    private Long customerId;\n    /**\n     * 收款账户编号\n     *\n     * 关联 {@link ErpAccountDO#getId()}\n     */\n    private Long accountId;\n\n    /**\n     * 合计价格，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 优惠金额，单位：元\n     */\n    private BigDecimal discountPrice;\n    /**\n     * 实付金额，单位：分\n     *\n     * receiptPrice = totalPrice - discountPrice\n     */\n    private BigDecimal receiptPrice;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.finance;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 收款项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_finance_receipt_item\")\n@KeySequence(\"erp_finance_receipt_item_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpFinanceReceiptItemDO extends BaseDO {\n\n    /**\n     * 入库项编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 收款单编号\n     *\n     * 关联 {@link ErpFinanceReceiptDO#getId()}\n     */\n    private Long receiptId;\n\n    /**\n     * 业务类型\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum} 的销售出库、退货\n     */\n    private Integer bizType;\n    /**\n     * 业务编号\n     *\n     * 例如说：{@link ErpSaleOutDO#getId()}\n     */\n    private Long bizId;\n    /**\n     * 业务单号\n     *\n     * 例如说：{@link ErpSaleOutDO#getNo()}\n     */\n    private String bizNo;\n\n    /**\n     * 应收金额，单位：分\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 已收金额，单位：分\n     */\n    private BigDecimal receiptedPrice;\n    /**\n     * 本次收款，单位：分\n     */\n    private BigDecimal receiptPrice;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductCategoryDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.product;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * ERP 产品分类 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_product_category\")\n@KeySequence(\"erp_product_category_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpProductCategoryDO extends BaseDO {\n\n    public static final Long PARENT_ID_ROOT = 0L;\n\n    /**\n     * 分类编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 父分类编号\n     */\n    private Long parentId;\n    /**\n     * 分类名称\n     */\n    private String name;\n    /**\n     * 分类编码\n     */\n    private String code;\n    /**\n     * 分类排序\n     */\n    private Integer sort;\n    /**\n     * 开启状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.product;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 产品 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_product\")\n@KeySequence(\"erp_product_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpProductDO extends BaseDO {\n\n    /**\n     * 产品编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 产品名称\n     */\n    private String name;\n    /**\n     * 产品条码\n     */\n    private String barCode;\n    /**\n     * 产品分类编号\n     *\n     * 关联 {@link ErpProductCategoryDO#getId()}\n     */\n    private Long categoryId;\n    /**\n     * 单位编号\n     *\n     * 关联 {@link ErpProductUnitDO#getId()}\n     */\n    private Long unitId;\n    /**\n     * 产品状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 产品规格\n     */\n    private String standard;\n    /**\n     * 产品备注\n     */\n    private String remark;\n    /**\n     * 保质期天数\n     */\n    private Integer expiryDay;\n    /**\n     * 基础重量（kg）\n     */\n    private BigDecimal weight;\n    /**\n     * 采购价格，单位：元\n     */\n    private BigDecimal purchasePrice;\n    /**\n     * 销售价格，单位：元\n     */\n    private BigDecimal salePrice;\n    /**\n     * 最低价格，单位：元\n     */\n    private BigDecimal minPrice;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductUnitDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.product;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * ERP 产品单位 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_product_unit\")\n@KeySequence(\"erp_product_unit_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpProductUnitDO extends BaseDO {\n\n    /**\n     * 单位编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 单位名字\n     */\n    private String name;\n    /**\n     * 单位状态\n     */\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 采购入库 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"erp_purchase_in\")\n@KeySequence(\"erp_purchase_in_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpPurchaseInDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 采购入库单号\n     */\n    private String no;\n    /**\n     * 入库状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 供应商编号\n     *\n     * 关联 {@link ErpSupplierDO#getId()}\n     */\n    private Long supplierId;\n    /**\n     * 结算账户编号\n     *\n     * 关联 {@link ErpAccountDO#getId()}\n     */\n    private Long accountId;\n    /**\n     * 入库时间\n     */\n    private LocalDateTime inTime;\n\n    /**\n     * 采购订单编号\n     *\n     * 关联 {@link ErpPurchaseOrderDO#getId()}\n     */\n    private Long orderId;\n    /**\n     * 采购订单号\n     *\n     * 冗余 {@link ErpPurchaseOrderDO#getNo()}\n     */\n    private String orderNo;\n\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 最终合计价格，单位：元\n     *\n     * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 已支付金额，单位：元\n     *\n     * 目的：和 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO} 结合，记录已支付金额\n     */\n    private BigDecimal paymentPrice;\n\n    /**\n     * 合计产品价格，单位：元\n     */\n    private BigDecimal totalProductPrice;\n    /**\n     * 合计税额，单位：元\n     */\n    private BigDecimal totalTaxPrice;\n    /**\n     * 优惠率，百分比\n     */\n    private BigDecimal discountPercent;\n    /**\n     * 优惠金额，单位：元\n     *\n     * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent\n     */\n    private BigDecimal discountPrice;\n    /**\n     * 其它金额，单位：元\n     */\n    private BigDecimal otherPrice;\n\n    /**\n     * 附件地址\n     */\n    private String fileUrl;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 采购入库项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_purchase_in_items\")\n@KeySequence(\"erp_purchase_in_items_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpPurchaseInItemDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 采购入库编号\n     *\n     * 关联 {@link ErpPurchaseInDO##getId()}\n     */\n    private Long inId;\n    /**\n     * 采购订单项编号\n     *\n     * 关联 {@link ErpPurchaseOrderItemDO#getId()}\n     * 目的：方便更新关联的采购订单项的入库数量\n     */\n    private Long orderItemId;\n    /**\n     * 仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long warehouseId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位单位\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n\n    /**\n     * 产品单位单价，单位：元\n     */\n    private BigDecimal productPrice;\n    /**\n     * 数量\n     */\n    private BigDecimal count;\n    /**\n     * 总价，单位：元\n     *\n     * totalPrice = productPrice * count\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 税率，百分比\n     */\n    private BigDecimal taxPercent;\n    /**\n     * 税额，单位：元\n     *\n     * taxPrice = totalPrice * taxPercent\n     */\n    private BigDecimal taxPrice;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 采购订单 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"erp_purchase_order\")\n@KeySequence(\"erp_purchase_order_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpPurchaseOrderDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 采购订单号\n     */\n    private String no;\n    /**\n     * 采购状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 供应商编号\n     *\n     * 关联 {@link ErpSupplierDO#getId()}\n     */\n    private Long supplierId;\n    /**\n     * 结算账户编号\n     *\n     * 关联 {@link ErpAccountDO#getId()}\n     */\n    private Long accountId;\n    /**\n     * 下单时间\n     */\n    private LocalDateTime orderTime;\n\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 最终合计价格，单位：元\n     *\n     * totalPrice = totalProductPrice + totalTaxPrice - discountPrice\n     */\n    private BigDecimal totalPrice;\n\n    /**\n     * 合计产品价格，单位：元\n     */\n    private BigDecimal totalProductPrice;\n    /**\n     * 合计税额，单位：元\n     */\n    private BigDecimal totalTaxPrice;\n    /**\n     * 优惠率，百分比\n     */\n    private BigDecimal discountPercent;\n    /**\n     * 优惠金额，单位：元\n     *\n     * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent\n     */\n    private BigDecimal discountPrice;\n    /**\n     * 定金金额，单位：元\n     */\n    private BigDecimal depositPrice;\n\n    /**\n     * 附件地址\n     */\n    private String fileUrl;\n    /**\n     * 备注\n     */\n    private String remark;\n\n    // ========== 采购入库 ==========\n    /**\n     * 采购入库数量\n     */\n    private BigDecimal inCount;\n\n    // ========== 采购退货（出库）） ==========\n    /**\n     * 采购退货数量\n     */\n    private BigDecimal returnCount;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 采购订单项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_purchase_order_items\")\n@KeySequence(\"erp_purchase_order_items_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpPurchaseOrderItemDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 采购订单编号\n     *\n     * 关联 {@link ErpPurchaseOrderDO#getId()}\n     */\n    private Long orderId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位单位\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n\n    /**\n     * 产品单位单价，单位：元\n     */\n    private BigDecimal productPrice;\n    /**\n     * 数量\n     */\n    private BigDecimal count;\n    /**\n     * 总价，单位：元\n     *\n     * totalPrice = productPrice * count\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 税率，百分比\n     */\n    private BigDecimal taxPercent;\n    /**\n     * 税额，单位：元\n     *\n     * taxPrice = totalPrice * taxPercent\n     */\n    private BigDecimal taxPrice;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    // ========== 采购入库 ==========\n    /**\n     * 采购入库数量\n     */\n    private BigDecimal inCount;\n\n    // ========== 采购退货（出库）） ==========\n    /**\n     * 采购退货数量\n     */\n    private BigDecimal returnCount;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 采购退货 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"erp_purchase_return\")\n@KeySequence(\"erp_purchase_return_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpPurchaseReturnDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 采购退货单号\n     */\n    private String no;\n    /**\n     * 退货状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 供应商编号\n     *\n     * 关联 {@link ErpSupplierDO#getId()}\n     */\n    private Long supplierId;\n    /**\n     * 结算账户编号\n     *\n     * 关联 {@link ErpAccountDO#getId()}\n     */\n    private Long accountId;\n    /**\n     * 退货时间\n     */\n    private LocalDateTime returnTime;\n\n    /**\n     * 采购订单编号\n     *\n     * 关联 {@link ErpPurchaseOrderDO#getId()}\n     */\n    private Long orderId;\n    /**\n     * 采购订单号\n     *\n     * 冗余 {@link ErpPurchaseOrderDO#getNo()}\n     */\n    private String orderNo;\n\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 最终合计价格，单位：元\n     *\n     * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 已退款金额，单位：元\n     *\n     * 目的：和 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO} 结合，记录已支付金额\n     */\n    private BigDecimal refundPrice;\n\n    /**\n     * 合计产品价格，单位：元\n     */\n    private BigDecimal totalProductPrice;\n    /**\n     * 合计税额，单位：元\n     */\n    private BigDecimal totalTaxPrice;\n    /**\n     * 优惠率，百分比\n     */\n    private BigDecimal discountPercent;\n    /**\n     * 优惠金额，单位：元\n     *\n     * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent\n     */\n    private BigDecimal discountPrice;\n    /**\n     * 其它金额，单位：元\n     */\n    private BigDecimal otherPrice;\n\n    /**\n     * 附件地址\n     */\n    private String fileUrl;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 采购退货项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_purchase_return_items\")\n@KeySequence(\"erp_purchase_return_items_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpPurchaseReturnItemDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 采购退货编号\n     *\n     * 关联 {@link ErpPurchaseReturnDO##getId()}\n     */\n    private Long returnId;\n    /**\n     * 采购订单项编号\n     *\n     * 关联 {@link ErpPurchaseOrderItemDO#getId()}\n     * 目的：方便更新关联的采购订单项的退货数量\n     */\n    private Long orderItemId;\n    /**\n     * 仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long warehouseId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位单位\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n\n    /**\n     * 产品单位单价，单位：元\n     */\n    private BigDecimal productPrice;\n    /**\n     * 数量\n     */\n    private BigDecimal count;\n    /**\n     * 总价，单位：元\n     *\n     * totalPrice = productPrice * count\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 税率，百分比\n     */\n    private BigDecimal taxPercent;\n    /**\n     * 税额，单位：元\n     *\n     * taxPrice = totalPrice * taxPercent\n     */\n    private BigDecimal taxPrice;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpSupplierDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 供应商 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_supplier\")\n@KeySequence(\"erp_supplier_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpSupplierDO extends BaseDO {\n\n    /**\n     * 供应商编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 供应商名称\n     */\n    private String name;\n    /**\n     * 联系人\n     */\n    private String contact;\n    /**\n     * 手机号码\n     */\n    private String mobile;\n    /**\n     * 联系电话\n     */\n    private String telephone;\n    /**\n     * 电子邮箱\n     */\n    private String email;\n    /**\n     * 传真\n     */\n    private String fax;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 开启状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 排序\n     */\n    private Integer sort;\n    /**\n     * 纳税人识别号\n     */\n    private String taxNo;\n    /**\n     * 税率\n     */\n    private BigDecimal taxPercent;\n    /**\n     * 开户行\n     */\n    private String bankName;\n    /**\n     * 开户账号\n     */\n    private String bankAccount;\n    /**\n     * 开户地址\n     */\n    private String bankAddress;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpCustomerDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.sale;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 客户 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_customer\")\n@KeySequence(\"erp_customer_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpCustomerDO extends BaseDO {\n\n    /**\n     * 客户编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 客户名称\n     */\n    private String name;\n    /**\n     * 联系人\n     */\n    private String contact;\n    /**\n     * 手机号码\n     */\n    private String mobile;\n    /**\n     * 联系电话\n     */\n    private String telephone;\n    /**\n     * 电子邮箱\n     */\n    private String email;\n    /**\n     * 传真\n     */\n    private String fax;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 开启状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 排序\n     */\n    private Integer sort;\n    /**\n     * 纳税人识别号\n     */\n    private String taxNo;\n    /**\n     * 税率\n     */\n    private BigDecimal taxPercent;\n    /**\n     * 开户行\n     */\n    private String bankName;\n    /**\n     * 开户账号\n     */\n    private String bankAccount;\n    /**\n     * 开户地址\n     */\n    private String bankAddress;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.sale;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 销售订单 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"erp_sale_order\")\n@KeySequence(\"erp_sale_order_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpSaleOrderDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 销售订单号\n     */\n    private String no;\n    /**\n     * 销售状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link ErpCustomerDO#getId()}\n     */\n    private Long customerId;\n    /**\n     * 结算账户编号\n     *\n     * 关联 {@link ErpAccountDO#getId()}\n     */\n    private Long accountId;\n    /**\n     * 销售员编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long saleUserId;\n    /**\n     * 下单时间\n     */\n    private LocalDateTime orderTime;\n\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 最终合计价格，单位：元\n     *\n     * totalPrice = totalProductPrice + totalTaxPrice - discountPrice\n     */\n    private BigDecimal totalPrice;\n\n    /**\n     * 合计产品价格，单位：元\n     */\n    private BigDecimal totalProductPrice;\n    /**\n     * 合计税额，单位：元\n     */\n    private BigDecimal totalTaxPrice;\n    /**\n     * 优惠率，百分比\n     */\n    private BigDecimal discountPercent;\n    /**\n     * 优惠金额，单位：元\n     *\n     * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent\n     */\n    private BigDecimal discountPrice;\n    /**\n     * 定金金额，单位：元\n     */\n    private BigDecimal depositPrice;\n\n    /**\n     * 附件地址\n     */\n    private String fileUrl;\n    /**\n     * 备注\n     */\n    private String remark;\n\n    // ========== 销售出库 ==========\n    /**\n     * 销售出库数量\n     */\n    private BigDecimal outCount;\n\n    // ========== 销售退货（入库）） ==========\n    /**\n     * 销售退货数量\n     */\n    private BigDecimal returnCount;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.sale;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 销售订单项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_sale_order_items\")\n@KeySequence(\"erp_sale_order_items_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpSaleOrderItemDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 销售订单编号\n     *\n     * 关联 {@link ErpSaleOrderDO#getId()}\n     */\n    private Long orderId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位单位\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n\n    /**\n     * 产品单位单价，单位：元\n     */\n    private BigDecimal productPrice;\n    /**\n     * 数量\n     */\n    private BigDecimal count;\n    /**\n     * 总价，单位：元\n     *\n     * totalPrice = productPrice * count\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 税率，百分比\n     */\n    private BigDecimal taxPercent;\n    /**\n     * 税额，单位：元\n     *\n     * taxPrice = totalPrice * taxPercent\n     */\n    private BigDecimal taxPrice;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    // ========== 销售出库 ==========\n    /**\n     * 销售出库数量\n     */\n    private BigDecimal outCount;\n\n    // ========== 销售退货（入库）） ==========\n    /**\n     * 销售退货数量\n     */\n    private BigDecimal returnCount;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.sale;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 销售出库 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"erp_sale_out\")\n@KeySequence(\"erp_sale_out_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpSaleOutDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 销售出库单号\n     */\n    private String no;\n    /**\n     * 出库状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link ErpCustomerDO#getId()}\n     */\n    private Long customerId;\n    /**\n     * 结算账户编号\n     *\n     * 关联 {@link ErpAccountDO#getId()}\n     */\n    private Long accountId;\n    /**\n     * 销售员编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long saleUserId;\n    /**\n     * 出库时间\n     */\n    private LocalDateTime outTime;\n\n    /**\n     * 销售订单编号\n     *\n     * 关联 {@link ErpSaleOrderDO#getId()}\n     */\n    private Long orderId;\n    /**\n     * 销售订单号\n     *\n     * 冗余 {@link ErpSaleOrderDO#getNo()}\n     */\n    private String orderNo;\n\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 最终合计价格，单位：元\n     *\n     * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 已收款金额，单位：元\n     *\n     * 目的：和 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO} 结合，记录已收款金额\n     */\n    private BigDecimal receiptPrice;\n\n    /**\n     * 合计产品价格，单位：元\n     */\n    private BigDecimal totalProductPrice;\n    /**\n     * 合计税额，单位：元\n     */\n    private BigDecimal totalTaxPrice;\n    /**\n     * 优惠率，百分比\n     */\n    private BigDecimal discountPercent;\n    /**\n     * 优惠金额，单位：元\n     *\n     * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent\n     */\n    private BigDecimal discountPrice;\n    /**\n     * 其它金额，单位：元\n     */\n    private BigDecimal otherPrice;\n\n    /**\n     * 附件地址\n     */\n    private String fileUrl;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.sale;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 销售出库项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_sale_out_items\")\n@KeySequence(\"erp_sale_out_items_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpSaleOutItemDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 销售出库编号\n     *\n     * 关联 {@link ErpStockOutDO##getId()}\n     */\n    private Long outId;\n    /**\n     * 销售订单项编号\n     *\n     * 关联 {@link ErpSaleOrderItemDO#getId()}\n     * 目的：方便更新关联的销售订单项的出库数量\n     */\n    private Long orderItemId;\n    /**\n     * 仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long warehouseId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位单位\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n\n    /**\n     * 产品单位单价，单位：元\n     */\n    private BigDecimal productPrice;\n    /**\n     * 数量\n     */\n    private BigDecimal count;\n    /**\n     * 总价，单位：元\n     *\n     * totalPrice = productPrice * count\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 税率，百分比\n     */\n    private BigDecimal taxPercent;\n    /**\n     * 税额，单位：元\n     *\n     * taxPrice = totalPrice * taxPercent\n     */\n    private BigDecimal taxPrice;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.sale;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 销售退货 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"erp_sale_return\")\n@KeySequence(\"erp_sale_return_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpSaleReturnDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 销售退货单号\n     */\n    private String no;\n    /**\n     * 退货状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 客户编号\n     *\n     * 关联 {@link ErpCustomerDO#getId()}\n     */\n    private Long customerId;\n    /**\n     * 结算账户编号\n     *\n     * 关联 {@link ErpAccountDO#getId()}\n     */\n    private Long accountId;\n    /**\n     * 销售员编号\n     *\n     * 关联 AdminUserDO 的 id 字段\n     */\n    private Long saleUserId;\n    /**\n     * 退货时间\n     */\n    private LocalDateTime returnTime;\n\n    /**\n     * 销售订单编号\n     *\n     * 关联 {@link ErpSaleOrderDO#getId()}\n     */\n    private Long orderId;\n    /**\n     * 销售订单号\n     *\n     * 冗余 {@link ErpSaleOrderDO#getNo()}\n     */\n    private String orderNo;\n\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 最终合计价格，单位：元\n     *\n     * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 已退款金额，单位：元\n     *\n     * 目的：和 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO} 结合，记录已退款金额\n     */\n    private BigDecimal refundPrice;\n\n    /**\n     * 合计产品价格，单位：元\n     */\n    private BigDecimal totalProductPrice;\n    /**\n     * 合计税额，单位：元\n     */\n    private BigDecimal totalTaxPrice;\n    /**\n     * 优惠率，百分比\n     */\n    private BigDecimal discountPercent;\n    /**\n     * 优惠金额，单位：元\n     *\n     * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent\n     */\n    private BigDecimal discountPrice;\n    /**\n     * 其它金额，单位：元\n     */\n    private BigDecimal otherPrice;\n\n    /**\n     * 附件地址\n     */\n    private String fileUrl;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.sale;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 销售退货项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_sale_return_items\")\n@KeySequence(\"erp_sale_return_items_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpSaleReturnItemDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 销售退货编号\n     *\n     * 关联 {@link ErpSaleReturnDO##getId()}\n     */\n    private Long returnId;\n    /**\n     * 销售订单项编号\n     *\n     * 关联 {@link ErpSaleOrderItemDO#getId()}\n     * 目的：方便更新关联的销售订单项的退货数量\n     */\n    private Long orderItemId;\n    /**\n     * 仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long warehouseId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位单位\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n\n    /**\n     * 产品单位单价，单位：元\n     */\n    private BigDecimal productPrice;\n    /**\n     * 数量\n     */\n    private BigDecimal count;\n    /**\n     * 总价，单位：元\n     *\n     * totalPrice = productPrice * count\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 税率，百分比\n     */\n    private BigDecimal taxPercent;\n    /**\n     * 税额，单位：元\n     *\n     * taxPrice = totalPrice * taxPercent\n     */\n    private BigDecimal taxPrice;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 库存盘点单 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock_check\")\n@KeySequence(\"erp_stock_check_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockCheckDO extends BaseDO {\n\n    /**\n     * 盘点编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 盘点单号\n     */\n    private String no;\n    /**\n     * 盘点时间\n     */\n    private LocalDateTime checkTime;\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 合计金额，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 附件 URL\n     */\n    private String fileUrl;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 库存盘点单项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock_check_item\")\n@KeySequence(\"erp_stock_check_item_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockCheckItemDO extends BaseDO {\n\n    /**\n     * 盘点项编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 盘点编号\n     *\n     * 关联 {@link ErpStockCheckDO#getId()}\n     */\n    private Long checkId;\n    /**\n     * 仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long warehouseId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位编号\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n    /**\n     * 产品单价\n     */\n    private BigDecimal productPrice;\n    /**\n     * 账面数量（当前库存）\n     */\n    private BigDecimal stockCount;\n    /**\n     * 实际数量（实际库存）\n     */\n    private BigDecimal actualCount;\n    /**\n     * 盈亏数量\n     *\n     * count = stockCount - actualCount\n     */\n    private BigDecimal count;\n    /**\n     * 合计金额，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 产品库存 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock\")\n@KeySequence(\"erp_stock_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long warehouseId;\n    /**\n     * 库存数量\n     */\n    private BigDecimal count;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 其它入库单 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock_in\")\n@KeySequence(\"erp_stock_in_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockInDO extends BaseDO {\n\n    /**\n     * 入库编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 入库单号\n     */\n    private String no;\n    /**\n     * 供应商编号\n     *\n     * 关联 {@link ErpSupplierDO#getId()}\n     */\n    private Long supplierId;\n    /**\n     * 入库时间\n     */\n    private LocalDateTime inTime;\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 合计金额，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 附件 URL\n     */\n    private String fileUrl;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 其它入库单项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock_in_item\")\n@KeySequence(\"erp_stock_in_item_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockInItemDO extends BaseDO {\n\n    /**\n     * 入库项编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 入库编号\n     *\n     * 关联 {@link ErpStockInDO#getId()}\n     */\n    private Long inId;\n    /**\n     * 仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long warehouseId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位编号\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n    /**\n     * 产品单价\n     */\n    private BigDecimal productPrice;\n    /**\n     * 产品数量\n     */\n    private BigDecimal count;\n    /**\n     * 合计金额，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 库存调拨单 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock_move\")\n@KeySequence(\"erp_stock_move_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockMoveDO extends BaseDO {\n\n    /**\n     * 调拨编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 调拨单号\n     */\n    private String no;\n    /**\n     * 调拨时间\n     */\n    private LocalDateTime moveTime;\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 合计金额，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 附件 URL\n     */\n    private String fileUrl;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 库存调拨单项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock_move_item\")\n@KeySequence(\"erp_stock_move_item_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockMoveItemDO extends BaseDO {\n\n    /**\n     * 调拨项编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 调拨编号\n     *\n     * 关联 {@link ErpStockMoveDO#getId()}\n     */\n    private Long moveId;\n    /**\n     * 调出仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long fromWarehouseId;\n    /**\n     * 调入仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long toWarehouseId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位编号\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n    /**\n     * 产品单价\n     */\n    private BigDecimal productPrice;\n    /**\n     * 产品数量\n     */\n    private BigDecimal count;\n    /**\n     * 合计金额，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 其它出库单 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock_out\")\n@KeySequence(\"erp_stock_out_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockOutDO extends BaseDO {\n\n    /**\n     * 出库编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 出库单号\n     */\n    private String no;\n    /**\n     * 客户编号\n     *\n     * TODO 芋艿：待关联\n     */\n    private Long customerId;\n    /**\n     * 出库时间\n     */\n    private LocalDateTime outTime;\n    /**\n     * 合计数量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 合计金额，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}\n     */\n    private Integer status;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 附件 URL\n     */\n    private String fileUrl;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutItemDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 其它出库单项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock_out_item\")\n@KeySequence(\"erp_stock_out_item_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockOutItemDO extends BaseDO {\n\n    /**\n     * 出库项编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 出库编号\n     *\n     * 关联 {@link ErpStockOutDO#getId()}\n     */\n    private Long outId;\n    /**\n     * 仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long warehouseId;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品单位编号\n     *\n     * 冗余 {@link ErpProductDO#getUnitId()}\n     */\n    private Long productUnitId;\n    /**\n     * 产品单价\n     */\n    private BigDecimal productPrice;\n    /**\n     * 产品数量\n     */\n    private BigDecimal count;\n    /**\n     * 合计金额，单位：元\n     */\n    private BigDecimal totalPrice;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockRecordDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 产品库存明细 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_stock_record\")\n@KeySequence(\"erp_stock_record_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockRecordDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link ErpProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 仓库编号\n     *\n     * 关联 {@link ErpWarehouseDO#getId()}\n     */\n    private Long warehouseId;\n    /**\n     * 出入库数量\n     *\n     * 正数，表示入库；负数，表示出库\n     */\n    private BigDecimal count;\n    /**\n     * 总库存量\n     *\n     * 出入库之后，目前的库存量\n     */\n    private BigDecimal totalCount;\n    /**\n     * 业务类型\n     *\n     * 枚举 {@link ErpStockRecordBizTypeEnum}\n     */\n    private Integer bizType;\n    /**\n     * 业务编号\n     *\n     * 例如说：{@link ErpStockInDO#getId()}\n     */\n    private Long bizId;\n    /**\n     * 业务项编号\n     *\n     * 例如说：{@link ErpStockInItemDO#getId()}\n     */\n    private Long bizItemId;\n    /**\n     * 业务单号\n     *\n     * 例如说：{@link ErpStockInDO#getNo()}\n     */\n    private String bizNo;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpWarehouseDO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.dataobject.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 仓库 DO\n *\n * @author 芋道源码\n */\n@TableName(\"erp_warehouse\")\n@KeySequence(\"erp_warehouse_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpWarehouseDO extends BaseDO {\n\n    /**\n     * 仓库编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 仓库名称\n     */\n    private String name;\n    /**\n     * 仓库地址\n     */\n    private String address;\n    /**\n     * 排序\n     */\n    private Long sort;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 负责人\n     */\n    private String principal;\n    /**\n     * 仓储费，单位：元\n     */\n    private BigDecimal warehousePrice;\n    /**\n     * 搬运费，单位：元\n     */\n    private BigDecimal truckagePrice;\n    /**\n     * 开启状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 是否默认\n     */\n    private Boolean defaultStatus;\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpAccountMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.finance;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * ERP 结算账户 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpAccountMapper extends BaseMapperX<ErpAccountDO> {\n\n    default PageResult<ErpAccountDO> selectPage(ErpAccountPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ErpAccountDO>()\n                .likeIfPresent(ErpAccountDO::getName, reqVO.getName())\n                .likeIfPresent(ErpAccountDO::getNo, reqVO.getNo())\n                .eqIfPresent(ErpAccountDO::getRemark, reqVO.getRemark())\n                .orderByDesc(ErpAccountDO::getId));\n    }\n\n    default ErpAccountDO selectByDefaultStatus() {\n        return selectOne(ErpAccountDO::getDefaultStatus, true);\n    }\n\n    default List<ErpAccountDO> selectListByStatus(Integer status) {\n        return selectList(ErpAccountDO::getStatus, status);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.finance;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * ERP 付款单项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpFinancePaymentItemMapper extends BaseMapperX<ErpFinancePaymentItemDO> {\n\n    default List<ErpFinancePaymentItemDO> selectListByPaymentId(Long paymentId) {\n        return selectList(ErpFinancePaymentItemDO::getPaymentId, paymentId);\n    }\n\n    default List<ErpFinancePaymentItemDO> selectListByPaymentIds(Collection<Long> paymentIds) {\n        return selectList(ErpFinancePaymentItemDO::getPaymentId, paymentIds);\n    }\n\n    default BigDecimal selectPaymentPriceSumByBizIdAndBizType(Long bizId, Integer bizType) {\n        // SQL sum 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpFinancePaymentItemDO>()\n                .select(\"SUM(payment_price) AS payment_price_sum\")\n                .eq(\"biz_id\", bizId)\n                .eq(\"biz_type\", bizType));\n        // 获得数量\n        if (CollUtil.isEmpty(result)) {\n            return BigDecimal.ZERO;\n        }\n        return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), \"payment_price_sum\", 0D));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.finance;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * ERP 付款单 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpFinancePaymentMapper extends BaseMapperX<ErpFinancePaymentDO> {\n\n    default PageResult<ErpFinancePaymentDO> selectPage(ErpFinancePaymentPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpFinancePaymentDO> query = new MPJLambdaWrapperX<ErpFinancePaymentDO>()\n                .likeIfPresent(ErpFinancePaymentDO::getNo, reqVO.getNo())\n                .betweenIfPresent(ErpFinancePaymentDO::getPaymentTime, reqVO.getPaymentTime())\n                .eqIfPresent(ErpFinancePaymentDO::getSupplierId, reqVO.getSupplierId())\n                .eqIfPresent(ErpFinancePaymentDO::getCreator, reqVO.getCreator())\n                .eqIfPresent(ErpFinancePaymentDO::getFinanceUserId, reqVO.getFinanceUserId())\n                .eqIfPresent(ErpFinancePaymentDO::getAccountId, reqVO.getAccountId())\n                .eqIfPresent(ErpFinancePaymentDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpFinancePaymentDO::getRemark, reqVO.getRemark())\n                .orderByDesc(ErpFinancePaymentDO::getId);\n        if (reqVO.getBizNo() != null) {\n            query.leftJoin(ErpFinancePaymentItemDO.class, ErpFinancePaymentItemDO::getPaymentId, ErpFinancePaymentDO::getId)\n                    .eq(reqVO.getBizNo() != null, ErpFinancePaymentItemDO::getBizNo, reqVO.getBizNo())\n                    .groupBy(ErpFinancePaymentDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpFinancePaymentDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpFinancePaymentDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpFinancePaymentDO>()\n                .eq(ErpFinancePaymentDO::getId, id).eq(ErpFinancePaymentDO::getStatus, status));\n    }\n\n    default ErpFinancePaymentDO selectByNo(String no) {\n        return selectOne(ErpFinancePaymentDO::getNo, no);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.finance;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * ERP 收款单项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpFinanceReceiptItemMapper extends BaseMapperX<ErpFinanceReceiptItemDO> {\n\n    default List<ErpFinanceReceiptItemDO> selectListByReceiptId(Long receiptId) {\n        return selectList(ErpFinanceReceiptItemDO::getReceiptId, receiptId);\n    }\n\n    default List<ErpFinanceReceiptItemDO> selectListByReceiptIds(Collection<Long> receiptIds) {\n        return selectList(ErpFinanceReceiptItemDO::getReceiptId, receiptIds);\n    }\n\n    default BigDecimal selectReceiptPriceSumByBizIdAndBizType(Long bizId, Integer bizType) {\n        // SQL sum 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpFinanceReceiptItemDO>()\n                .select(\"SUM(receipt_price) AS receipt_price_sum\")\n                .eq(\"biz_id\", bizId)\n                .eq(\"biz_type\", bizType));\n        // 获得数量\n        if (CollUtil.isEmpty(result)) {\n            return BigDecimal.ZERO;\n        }\n        return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), \"receipt_price_sum\", 0D));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.finance;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * ERP 收款单 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpFinanceReceiptMapper extends BaseMapperX<ErpFinanceReceiptDO> {\n\n    default PageResult<ErpFinanceReceiptDO> selectPage(ErpFinanceReceiptPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpFinanceReceiptDO> query = new MPJLambdaWrapperX<ErpFinanceReceiptDO>()\n                .likeIfPresent(ErpFinanceReceiptDO::getNo, reqVO.getNo())\n                .betweenIfPresent(ErpFinanceReceiptDO::getReceiptTime, reqVO.getReceiptTime())\n                .eqIfPresent(ErpFinanceReceiptDO::getCustomerId, reqVO.getCustomerId())\n                .eqIfPresent(ErpFinanceReceiptDO::getCreator, reqVO.getCreator())\n                .eqIfPresent(ErpFinanceReceiptDO::getFinanceUserId, reqVO.getFinanceUserId())\n                .eqIfPresent(ErpFinanceReceiptDO::getAccountId, reqVO.getAccountId())\n                .eqIfPresent(ErpFinanceReceiptDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpFinanceReceiptDO::getRemark, reqVO.getRemark())\n                .orderByDesc(ErpFinanceReceiptDO::getId);\n        if (reqVO.getBizNo() != null) {\n            query.leftJoin(ErpFinanceReceiptItemDO.class, ErpFinanceReceiptItemDO::getReceiptId, ErpFinanceReceiptDO::getId)\n                    .eq(reqVO.getBizNo() != null, ErpFinanceReceiptItemDO::getBizNo, reqVO.getBizNo())\n                    .groupBy(ErpFinanceReceiptDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpFinanceReceiptDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpFinanceReceiptDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpFinanceReceiptDO>()\n                .eq(ErpFinanceReceiptDO::getId, id).eq(ErpFinanceReceiptDO::getStatus, status));\n    }\n\n    default ErpFinanceReceiptDO selectByNo(String no) {\n        return selectOne(ErpFinanceReceiptDO::getNo, no);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductCategoryMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.product;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * ERP 产品分类 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpProductCategoryMapper extends BaseMapperX<ErpProductCategoryDO> {\n\n    default List<ErpProductCategoryDO> selectList(ErpProductCategoryListReqVO reqVO) {\n        return selectList(new LambdaQueryWrapperX<ErpProductCategoryDO>()\n                .likeIfPresent(ErpProductCategoryDO::getName, reqVO.getName())\n                .eqIfPresent(ErpProductCategoryDO::getStatus, reqVO.getStatus())\n                .orderByDesc(ErpProductCategoryDO::getId));\n    }\n\n\tdefault ErpProductCategoryDO selectByParentIdAndName(Long parentId, String name) {\n\t    return selectOne(ErpProductCategoryDO::getParentId, parentId, ErpProductCategoryDO::getName, name);\n\t}\n\n    default Long selectCountByParentId(Long parentId) {\n        return selectCount(ErpProductCategoryDO::getParentId, parentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * ERP 产品 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpProductMapper extends BaseMapperX<ErpProductDO> {\n\n    default PageResult<ErpProductDO> selectPage(ErpProductPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ErpProductDO>()\n                .likeIfPresent(ErpProductDO::getName, reqVO.getName())\n                .eqIfPresent(ErpProductDO::getCategoryId, reqVO.getCategoryId())\n                .betweenIfPresent(ErpProductDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(ErpProductDO::getId));\n    }\n\n    default Long selectCountByCategoryId(Long categoryId) {\n        return selectCount(ErpProductDO::getCategoryId, categoryId);\n    }\n\n    default Long selectCountByUnitId(Long unitId) {\n        return selectCount(ErpProductDO::getUnitId, unitId);\n    }\n\n    default List<ErpProductDO> selectListByStatus(Integer status) {\n        return selectList(ErpProductDO::getStatus, status);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductUnitMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * ERP 产品单位 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpProductUnitMapper extends BaseMapperX<ErpProductUnitDO> {\n\n    default PageResult<ErpProductUnitDO> selectPage(ErpProductUnitPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ErpProductUnitDO>()\n                .likeIfPresent(ErpProductUnitDO::getName, reqVO.getName())\n                .eqIfPresent(ErpProductUnitDO::getStatus, reqVO.getStatus())\n                .orderByDesc(ErpProductUnitDO::getId));\n    }\n\n    default ErpProductUnitDO selectByName(String name) {\n        return selectOne(ErpProductUnitDO::getName, name);\n    }\n\n    default List<ErpProductUnitDO> selectListByStatus(Integer status) {\n        return selectList(ErpProductUnitDO::getStatus, status);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.purchase;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 采购入库项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpPurchaseInItemMapper extends BaseMapperX<ErpPurchaseInItemDO> {\n\n    default List<ErpPurchaseInItemDO> selectListByInId(Long inId) {\n        return selectList(ErpPurchaseInItemDO::getInId, inId);\n    }\n\n    default List<ErpPurchaseInItemDO> selectListByInIds(Collection<Long> inIds) {\n        return selectList(ErpPurchaseInItemDO::getInId, inIds);\n    }\n\n    default int deleteByInId(Long inId) {\n        return delete(ErpPurchaseInItemDO::getInId, inId);\n    }\n\n    /**\n     * 基于采购订单编号，查询每个采购订单项的入库数量之和\n     *\n     * @param inIds 入库订单项编号数组\n     * @return key：采购订单项编号；value：入库数量之和\n     */\n    default Map<Long, BigDecimal> selectOrderItemCountSumMapByInIds(Collection<Long> inIds) {\n        if (CollUtil.isEmpty(inIds)) {\n            return Collections.emptyMap();\n        }\n        // SQL sum 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpPurchaseInItemDO>()\n                .select(\"order_item_id, SUM(count) AS sum_count\")\n                .groupBy(\"order_item_id\")\n                .in(\"in_id\", inIds));\n        // 获得数量\n        return convertMap(result, obj -> (Long) obj.get(\"order_item_id\"), obj -> (BigDecimal) obj.get(\"sum_count\"));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.purchase;\n\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ERP 采购入库 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpPurchaseInMapper extends BaseMapperX<ErpPurchaseInDO> {\n\n    default PageResult<ErpPurchaseInDO> selectPage(ErpPurchaseInPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpPurchaseInDO> query = new MPJLambdaWrapperX<ErpPurchaseInDO>()\n                .likeIfPresent(ErpPurchaseInDO::getNo, reqVO.getNo())\n                .eqIfPresent(ErpPurchaseInDO::getSupplierId, reqVO.getSupplierId())\n                .betweenIfPresent(ErpPurchaseInDO::getInTime, reqVO.getInTime())\n                .eqIfPresent(ErpPurchaseInDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpPurchaseInDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpPurchaseInDO::getCreator, reqVO.getCreator())\n                .eqIfPresent(ErpPurchaseInDO::getAccountId, reqVO.getAccountId())\n                .likeIfPresent(ErpPurchaseInDO::getOrderNo, reqVO.getOrderNo())\n                .orderByDesc(ErpPurchaseInDO::getId);\n        // 付款状态。为什么需要 t. 的原因，是因为联表查询时，需要指定表名，不然会报字段不存在的错误\n        if (Objects.equals(reqVO.getPaymentStatus(), ErpPurchaseInPageReqVO.PAYMENT_STATUS_NONE)) {\n            query.eq(ErpPurchaseInDO::getPaymentPrice, 0);\n        } else if (Objects.equals(reqVO.getPaymentStatus(), ErpPurchaseInPageReqVO.PAYMENT_STATUS_PART)) {\n            query.gt(ErpPurchaseInDO::getPaymentPrice, 0).apply(\"t.payment_price < t.total_price\");\n        } else if (Objects.equals(reqVO.getPaymentStatus(), ErpPurchaseInPageReqVO.PAYMENT_STATUS_ALL)) {\n            query.apply(\"t.payment_price = t.total_price\");\n        }\n        if (Boolean.TRUE.equals(reqVO.getPaymentEnable())) {\n            query.eq(ErpPurchaseInDO::getStatus, ErpAuditStatus.APPROVE.getStatus())\n                    .apply(\"t.payment_price < t.total_price\");\n        }\n        if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) {\n            query.leftJoin(ErpPurchaseInItemDO.class, ErpPurchaseInItemDO::getInId, ErpPurchaseInDO::getId)\n                    .eq(reqVO.getWarehouseId() != null, ErpPurchaseInItemDO::getWarehouseId, reqVO.getWarehouseId())\n                    .eq(reqVO.getProductId() != null, ErpPurchaseInItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpPurchaseInDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpPurchaseInDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpPurchaseInDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpPurchaseInDO>()\n                .eq(ErpPurchaseInDO::getId, id).eq(ErpPurchaseInDO::getStatus, status));\n    }\n\n    default ErpPurchaseInDO selectByNo(String no) {\n        return selectOne(ErpPurchaseInDO::getNo, no);\n    }\n\n    default List<ErpPurchaseInDO> selectListByOrderId(Long orderId) {\n        return selectList(ErpPurchaseInDO::getOrderId, orderId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.purchase;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 采购订单明项目 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpPurchaseOrderItemMapper extends BaseMapperX<ErpPurchaseOrderItemDO> {\n\n    default List<ErpPurchaseOrderItemDO> selectListByOrderId(Long orderId) {\n        return selectList(ErpPurchaseOrderItemDO::getOrderId, orderId);\n    }\n\n    default List<ErpPurchaseOrderItemDO> selectListByOrderIds(Collection<Long> orderIds) {\n        return selectList(ErpPurchaseOrderItemDO::getOrderId, orderIds);\n    }\n\n    default int deleteByOrderId(Long orderId) {\n        return delete(ErpPurchaseOrderItemDO::getOrderId, orderId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.purchase;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Objects;\n\n/**\n * ERP 采购订单 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpPurchaseOrderMapper extends BaseMapperX<ErpPurchaseOrderDO> {\n\n    default PageResult<ErpPurchaseOrderDO> selectPage(ErpPurchaseOrderPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpPurchaseOrderDO> query = new MPJLambdaWrapperX<ErpPurchaseOrderDO>()\n                .likeIfPresent(ErpPurchaseOrderDO::getNo, reqVO.getNo())\n                .eqIfPresent(ErpPurchaseOrderDO::getSupplierId, reqVO.getSupplierId())\n                .betweenIfPresent(ErpPurchaseOrderDO::getOrderTime, reqVO.getOrderTime())\n                .eqIfPresent(ErpPurchaseOrderDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpPurchaseOrderDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpPurchaseOrderDO::getCreator, reqVO.getCreator())\n                .orderByDesc(ErpPurchaseOrderDO::getId);\n        // 入库状态。为什么需要 t. 的原因，是因为联表查询时，需要指定表名，不然会报 in_count 错误\n        if (Objects.equals(reqVO.getInStatus(), ErpPurchaseOrderPageReqVO.IN_STATUS_NONE)) {\n            query.eq(ErpPurchaseOrderDO::getInCount, 0);\n        } else if (Objects.equals(reqVO.getInStatus(), ErpPurchaseOrderPageReqVO.IN_STATUS_PART)) {\n            query.gt(ErpPurchaseOrderDO::getInCount, 0).apply(\"t.in_count < t.total_count\");\n        } else if (Objects.equals(reqVO.getInStatus(), ErpPurchaseOrderPageReqVO.IN_STATUS_ALL)) {\n            query.apply(\"t.in_count = t.total_count\");\n        }\n        // 退货状态\n        if (Objects.equals(reqVO.getReturnStatus(), ErpPurchaseOrderPageReqVO.RETURN_STATUS_NONE)) {\n            query.eq(ErpPurchaseOrderDO::getReturnCount, 0);\n        } else if (Objects.equals(reqVO.getReturnStatus(), ErpPurchaseOrderPageReqVO.RETURN_STATUS_PART)) {\n            query.gt(ErpPurchaseOrderDO::getReturnCount, 0).apply(\"t.return_count < t.total_count\");\n        } else if (Objects.equals(reqVO.getReturnStatus(), ErpPurchaseOrderPageReqVO.RETURN_STATUS_ALL)) {\n            query.apply(\"t.return_count = t.total_count\");\n        }\n        // 可采购入库\n        if (Boolean.TRUE.equals(reqVO.getInEnable())) {\n            query.eq(ErpPurchaseOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus())\n                    .apply(\"t.in_count < t.total_count\");\n        }\n        // 可采购退货\n        if (Boolean.TRUE.equals(reqVO.getReturnEnable())) {\n            query.eq(ErpPurchaseOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus())\n                    .apply(\"t.return_count < t.in_count\");\n        }\n        if (reqVO.getProductId() != null) {\n            query.leftJoin(ErpPurchaseOrderItemDO.class, ErpPurchaseOrderItemDO::getOrderId, ErpPurchaseOrderDO::getId)\n                    .eq(reqVO.getProductId() != null, ErpPurchaseOrderItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpPurchaseOrderDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpPurchaseOrderDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpPurchaseOrderDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpPurchaseOrderDO>()\n                .eq(ErpPurchaseOrderDO::getId, id).eq(ErpPurchaseOrderDO::getStatus, status));\n    }\n\n    default ErpPurchaseOrderDO selectByNo(String no) {\n        return selectOne(ErpPurchaseOrderDO::getNo, no);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.purchase;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 采购退货项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpPurchaseReturnItemMapper extends BaseMapperX<ErpPurchaseReturnItemDO> {\n\n    default List<ErpPurchaseReturnItemDO> selectListByReturnId(Long returnId) {\n        return selectList(ErpPurchaseReturnItemDO::getReturnId, returnId);\n    }\n\n    default List<ErpPurchaseReturnItemDO> selectListByReturnIds(Collection<Long> returnIds) {\n        return selectList(ErpPurchaseReturnItemDO::getReturnId, returnIds);\n    }\n\n    default int deleteByReturnId(Long returnId) {\n        return delete(ErpPurchaseReturnItemDO::getReturnId, returnId);\n    }\n\n    /**\n     * 基于采购订单编号，查询每个采购订单项的退货数量之和\n     *\n     * @param returnIds 入库订单项编号数组\n     * @return key：采购订单项编号；value：退货数量之和\n     */\n    default Map<Long, BigDecimal> selectOrderItemCountSumMapByReturnIds(Collection<Long> returnIds) {\n        if (CollUtil.isEmpty(returnIds)) {\n            return Collections.emptyMap();\n        }\n        // SQL sum 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpPurchaseReturnItemDO>()\n                .select(\"order_item_id, SUM(count) AS sum_count\")\n                .groupBy(\"order_item_id\")\n                .in(\"return_id\", returnIds));\n        // 获得数量\n        return convertMap(result, obj -> (Long) obj.get(\"order_item_id\"), obj -> (BigDecimal) obj.get(\"sum_count\"));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.purchase;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ERP 采购退货 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpPurchaseReturnMapper extends BaseMapperX<ErpPurchaseReturnDO> {\n\n    default PageResult<ErpPurchaseReturnDO> selectPage(ErpPurchaseReturnPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpPurchaseReturnDO> query = new MPJLambdaWrapperX<ErpPurchaseReturnDO>()\n                .likeIfPresent(ErpPurchaseReturnDO::getNo, reqVO.getNo())\n                .eqIfPresent(ErpPurchaseReturnDO::getSupplierId, reqVO.getSupplierId())\n                .betweenIfPresent(ErpPurchaseReturnDO::getReturnTime, reqVO.getReturnTime())\n                .eqIfPresent(ErpPurchaseReturnDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpPurchaseReturnDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpPurchaseReturnDO::getCreator, reqVO.getCreator())\n                .eqIfPresent(ErpPurchaseReturnDO::getAccountId, reqVO.getAccountId())\n                .likeIfPresent(ErpPurchaseReturnDO::getOrderNo, reqVO.getOrderNo())\n                .orderByDesc(ErpPurchaseReturnDO::getId);\n        // 退款状态。为什么需要 t. 的原因，是因为联表查询时，需要指定表名，不然会报字段不存在的错误\n        if (Objects.equals(reqVO.getRefundStatus(), ErpPurchaseReturnPageReqVO.REFUND_STATUS_NONE)) {\n            query.eq(ErpPurchaseReturnDO::getRefundPrice, 0);\n        } else if (Objects.equals(reqVO.getRefundStatus(), ErpPurchaseReturnPageReqVO.REFUND_STATUS_PART)) {\n            query.gt(ErpPurchaseReturnDO::getRefundPrice, 0).apply(\"t.refund_price < t.total_price\");\n        } else if (Objects.equals(reqVO.getRefundStatus(), ErpPurchaseReturnPageReqVO.REFUND_STATUS_ALL)) {\n            query.apply(\"t.refund_price = t.total_price\");\n        }\n        if (Boolean.TRUE.equals(reqVO.getRefundEnable())) {\n            query.eq(ErpPurchaseInDO::getStatus, ErpAuditStatus.APPROVE.getStatus())\n                    .apply(\"t.refund_price < t.total_price\");\n        }\n        if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) {\n            query.leftJoin(ErpPurchaseReturnItemDO.class, ErpPurchaseReturnItemDO::getReturnId, ErpPurchaseReturnDO::getId)\n                    .eq(reqVO.getWarehouseId() != null, ErpPurchaseReturnItemDO::getWarehouseId, reqVO.getWarehouseId())\n                    .eq(reqVO.getProductId() != null, ErpPurchaseReturnItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpPurchaseReturnDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpPurchaseReturnDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpPurchaseReturnDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpPurchaseReturnDO>()\n                .eq(ErpPurchaseReturnDO::getId, id).eq(ErpPurchaseReturnDO::getStatus, status));\n    }\n\n    default ErpPurchaseReturnDO selectByNo(String no) {\n        return selectOne(ErpPurchaseReturnDO::getNo, no);\n    }\n\n    default List<ErpPurchaseReturnDO> selectListByOrderId(Long orderId) {\n        return selectList(ErpPurchaseReturnDO::getOrderId, orderId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpSupplierMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.purchase;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * ERP 供应商 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpSupplierMapper extends BaseMapperX<ErpSupplierDO> {\n\n    default PageResult<ErpSupplierDO> selectPage(ErpSupplierPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ErpSupplierDO>()\n                .likeIfPresent(ErpSupplierDO::getName, reqVO.getName())\n                .likeIfPresent(ErpSupplierDO::getMobile, reqVO.getMobile())\n                .likeIfPresent(ErpSupplierDO::getTelephone, reqVO.getTelephone())\n                .orderByDesc(ErpSupplierDO::getId));\n    }\n\n    default List<ErpSupplierDO> selectListByStatus(Integer status) {\n        return selectList(ErpSupplierDO::getStatus, status);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpCustomerMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.sale;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * ERP 客户 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpCustomerMapper extends BaseMapperX<ErpCustomerDO> {\n\n    default PageResult<ErpCustomerDO> selectPage(ErpCustomerPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ErpCustomerDO>()\n                .likeIfPresent(ErpCustomerDO::getName, reqVO.getName())\n                .eqIfPresent(ErpCustomerDO::getMobile, reqVO.getMobile())\n                .eqIfPresent(ErpCustomerDO::getTelephone, reqVO.getTelephone())\n                .orderByDesc(ErpCustomerDO::getId));\n    }\n\n    default List<ErpCustomerDO> selectListByStatus(Integer status) {\n        return selectList(ErpCustomerDO::getStatus, status);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.sale;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 销售订单明项目 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpSaleOrderItemMapper extends BaseMapperX<ErpSaleOrderItemDO> {\n\n    default List<ErpSaleOrderItemDO> selectListByOrderId(Long orderId) {\n        return selectList(ErpSaleOrderItemDO::getOrderId, orderId);\n    }\n\n    default List<ErpSaleOrderItemDO> selectListByOrderIds(Collection<Long> orderIds) {\n        return selectList(ErpSaleOrderItemDO::getOrderId, orderIds);\n    }\n\n    default int deleteByOrderId(Long orderId) {\n        return delete(ErpSaleOrderItemDO::getOrderId, orderId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.sale;\n\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Objects;\n\n/**\n * ERP 销售订单 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpSaleOrderMapper extends BaseMapperX<ErpSaleOrderDO> {\n\n    default PageResult<ErpSaleOrderDO> selectPage(ErpSaleOrderPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpSaleOrderDO> query = new MPJLambdaWrapperX<ErpSaleOrderDO>()\n                .likeIfPresent(ErpSaleOrderDO::getNo, reqVO.getNo())\n                .eqIfPresent(ErpSaleOrderDO::getCustomerId, reqVO.getCustomerId())\n                .betweenIfPresent(ErpSaleOrderDO::getOrderTime, reqVO.getOrderTime())\n                .eqIfPresent(ErpSaleOrderDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpSaleOrderDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpSaleOrderDO::getCreator, reqVO.getCreator())\n                .orderByDesc(ErpSaleOrderDO::getId);\n        // 入库状态。为什么需要 t. 的原因，是因为联表查询时，需要指定表名，不然会报 out_count 错误\n        if (Objects.equals(reqVO.getOutStatus(), ErpSaleOrderPageReqVO.OUT_STATUS_NONE)) {\n            query.eq(ErpSaleOrderDO::getOutCount, 0);\n        } else if (Objects.equals(reqVO.getOutStatus(), ErpSaleOrderPageReqVO.OUT_STATUS_PART)) {\n            query.gt(ErpSaleOrderDO::getOutCount, 0).apply(\"t.out_count < t.total_count\");\n        } else if (Objects.equals(reqVO.getOutStatus(), ErpSaleOrderPageReqVO.OUT_STATUS_ALL)) {\n            query.apply(\"t.out_count = t.total_count\");\n        }\n        // 退货状态\n        if (Objects.equals(reqVO.getReturnStatus(), ErpSaleOrderPageReqVO.RETURN_STATUS_NONE)) {\n            query.eq(ErpSaleOrderDO::getReturnCount, 0);\n        } else if (Objects.equals(reqVO.getReturnStatus(), ErpSaleOrderPageReqVO.RETURN_STATUS_PART)) {\n            query.gt(ErpSaleOrderDO::getReturnCount, 0).apply(\"t.return_count < t.total_count\");\n        } else if (Objects.equals(reqVO.getReturnStatus(), ErpSaleOrderPageReqVO.RETURN_STATUS_ALL)) {\n            query.apply(\"t.return_count = t.total_count\");\n        }\n        // 可销售出库\n        if (Boolean.TRUE.equals(reqVO.getOutEnable())) {\n            query.eq(ErpSaleOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus())\n                    .apply(\"t.out_count < t.total_count\");\n        }\n        // 可销售退货\n        if (Boolean.TRUE.equals(reqVO.getReturnEnable())) {\n            query.eq(ErpSaleOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus())\n                    .apply(\"t.return_count < t.out_count\");\n        }\n        if (reqVO.getProductId() != null) {\n            query.leftJoin(ErpSaleOrderItemDO.class, ErpSaleOrderItemDO::getOrderId, ErpSaleOrderDO::getId)\n                    .eq(reqVO.getProductId() != null, ErpSaleOrderItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpSaleOrderDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpSaleOrderDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpSaleOrderDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpSaleOrderDO>()\n                .eq(ErpSaleOrderDO::getId, id).eq(ErpSaleOrderDO::getStatus, status));\n    }\n\n    default ErpSaleOrderDO selectByNo(String no) {\n        return selectOne(ErpSaleOrderDO::getNo, no);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.sale;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 销售出库项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpSaleOutItemMapper extends BaseMapperX<ErpSaleOutItemDO> {\n\n    default List<ErpSaleOutItemDO> selectListByOutId(Long outId) {\n        return selectList(ErpSaleOutItemDO::getOutId, outId);\n    }\n\n    default List<ErpSaleOutItemDO> selectListByOutIds(Collection<Long> outIds) {\n        return selectList(ErpSaleOutItemDO::getOutId, outIds);\n    }\n\n    default int deleteByOutId(Long outId) {\n        return delete(ErpSaleOutItemDO::getOutId, outId);\n    }\n\n    /**\n     * 基于销售订单编号，查询每个销售订单项的出库数量之和\n     *\n     * @param outIds 出库订单项编号数组\n     * @return key：销售订单项编号；value：出库数量之和\n     */\n    default Map<Long, BigDecimal> selectOrderItemCountSumMapByOutIds(Collection<Long> outIds) {\n        if (CollUtil.isEmpty(outIds)) {\n            return Collections.emptyMap();\n        }\n        // SQL sum 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpSaleOutItemDO>()\n                .select(\"order_item_id, SUM(count) AS sum_count\")\n                .groupBy(\"order_item_id\")\n                .in(\"out_id\", outIds));\n        // 获得数量\n        return convertMap(result, obj -> (Long) obj.get(\"order_item_id\"), obj -> (BigDecimal) obj.get(\"sum_count\"));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.sale;\n\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ERP 销售出库 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpSaleOutMapper extends BaseMapperX<ErpSaleOutDO> {\n\n    default PageResult<ErpSaleOutDO> selectPage(ErpSaleOutPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpSaleOutDO> query = new MPJLambdaWrapperX<ErpSaleOutDO>()\n                .likeIfPresent(ErpSaleOutDO::getNo, reqVO.getNo())\n                .eqIfPresent(ErpSaleOutDO::getCustomerId, reqVO.getCustomerId())\n                .betweenIfPresent(ErpSaleOutDO::getOutTime, reqVO.getOutTime())\n                .eqIfPresent(ErpSaleOutDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpSaleOutDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpSaleOutDO::getCreator, reqVO.getCreator())\n                .eqIfPresent(ErpSaleOutDO::getAccountId, reqVO.getAccountId())\n                .likeIfPresent(ErpSaleOutDO::getOrderNo, reqVO.getOrderNo())\n                .orderByDesc(ErpSaleOutDO::getId);\n        // 收款状态。为什么需要 t. 的原因，是因为联表查询时，需要指定表名，不然会报字段不存在的错误\n        if (Objects.equals(reqVO.getReceiptStatus(), ErpSaleOutPageReqVO.RECEIPT_STATUS_NONE)) {\n            query.eq(ErpSaleOutDO::getReceiptPrice, 0);\n        } else if (Objects.equals(reqVO.getReceiptStatus(), ErpSaleOutPageReqVO.RECEIPT_STATUS_PART)) {\n            query.gt(ErpSaleOutDO::getReceiptPrice, 0).apply(\"t.receipt_price < t.total_price\");\n        } else if (Objects.equals(reqVO.getReceiptStatus(), ErpSaleOutPageReqVO.RECEIPT_STATUS_ALL)) {\n            query.apply(\"t.receipt_price = t.total_price\");\n        }\n        if (Boolean.TRUE.equals(reqVO.getReceiptEnable())) {\n            query.eq(ErpSaleOutDO::getStatus, ErpAuditStatus.APPROVE.getStatus())\n                    .apply(\"t.receipt_price < t.total_price\");\n        }\n        if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) {\n            query.leftJoin(ErpSaleOutItemDO.class, ErpSaleOutItemDO::getOutId, ErpSaleOutDO::getId)\n                    .eq(reqVO.getWarehouseId() != null, ErpSaleOutItemDO::getWarehouseId, reqVO.getWarehouseId())\n                    .eq(reqVO.getProductId() != null, ErpSaleOutItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpSaleOutDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpSaleOutDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpSaleOutDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpSaleOutDO>()\n                .eq(ErpSaleOutDO::getId, id).eq(ErpSaleOutDO::getStatus, status));\n    }\n\n    default ErpSaleOutDO selectByNo(String no) {\n        return selectOne(ErpSaleOutDO::getNo, no);\n    }\n\n    default List<ErpSaleOutDO> selectListByOrderId(Long orderId) {\n        return selectList(ErpSaleOutDO::getOrderId, orderId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.sale;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 销售退货项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpSaleReturnItemMapper extends BaseMapperX<ErpSaleReturnItemDO> {\n\n    default List<ErpSaleReturnItemDO> selectListByReturnId(Long returnId) {\n        return selectList(ErpSaleReturnItemDO::getReturnId, returnId);\n    }\n\n    default List<ErpSaleReturnItemDO> selectListByReturnIds(Collection<Long> returnIds) {\n        return selectList(ErpSaleReturnItemDO::getReturnId, returnIds);\n    }\n\n    default int deleteByReturnId(Long returnId) {\n        return delete(ErpSaleReturnItemDO::getReturnId, returnId);\n    }\n\n    /**\n     * 基于销售订单编号，查询每个销售订单项的退货数量之和\n     *\n     * @param returnIds 出库订单项编号数组\n     * @return key：销售订单项编号；value：退货数量之和\n     */\n    default Map<Long, BigDecimal> selectOrderItemCountSumMapByReturnIds(Collection<Long> returnIds) {\n        if (CollUtil.isEmpty(returnIds)) {\n            return Collections.emptyMap();\n        }\n        // SQL sum 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpSaleReturnItemDO>()\n                .select(\"order_item_id, SUM(count) AS sum_count\")\n                .groupBy(\"order_item_id\")\n                .in(\"return_id\", returnIds));\n        // 获得数量\n        return convertMap(result, obj -> (Long) obj.get(\"order_item_id\"), obj -> (BigDecimal) obj.get(\"sum_count\"));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.sale;\n\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ERP 销售退货 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpSaleReturnMapper extends BaseMapperX<ErpSaleReturnDO> {\n\n    default PageResult<ErpSaleReturnDO> selectPage(ErpSaleReturnPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpSaleReturnDO> query = new MPJLambdaWrapperX<ErpSaleReturnDO>()\n                .likeIfPresent(ErpSaleReturnDO::getNo, reqVO.getNo())\n                .eqIfPresent(ErpSaleReturnDO::getCustomerId, reqVO.getCustomerId())\n                .betweenIfPresent(ErpSaleReturnDO::getReturnTime, reqVO.getReturnTime())\n                .eqIfPresent(ErpSaleReturnDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpSaleReturnDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpSaleReturnDO::getCreator, reqVO.getCreator())\n                .eqIfPresent(ErpSaleReturnDO::getAccountId, reqVO.getAccountId())\n                .likeIfPresent(ErpSaleReturnDO::getOrderNo, reqVO.getOrderNo())\n                .orderByDesc(ErpSaleReturnDO::getId);\n        // 退款状态。为什么需要 t. 的原因，是因为联表查询时，需要指定表名，不然会报字段不存在的错误\n        if (Objects.equals(reqVO.getRefundStatus(), ErpSaleReturnPageReqVO.REFUND_STATUS_NONE)) {\n            query.eq(ErpSaleReturnDO::getRefundPrice, 0);\n        } else if (Objects.equals(reqVO.getRefundStatus(), ErpSaleReturnPageReqVO.REFUND_STATUS_PART)) {\n            query.gt(ErpSaleReturnDO::getRefundPrice, 0).apply(\"t.refund_price < t.total_price\");\n        } else if (Objects.equals(reqVO.getRefundStatus(), ErpSaleReturnPageReqVO.REFUND_STATUS_ALL)) {\n            query.apply(\"t.refund_price = t.total_price\");\n        }\n        if (Boolean.TRUE.equals(reqVO.getRefundEnable())) {\n            query.eq(ErpSaleOutDO::getStatus, ErpAuditStatus.APPROVE.getStatus())\n                    .apply(\"t.refund_price < t.total_price\");\n        }\n        if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) {\n            query.leftJoin(ErpSaleReturnItemDO.class, ErpSaleReturnItemDO::getReturnId, ErpSaleReturnDO::getId)\n                    .eq(reqVO.getWarehouseId() != null, ErpSaleReturnItemDO::getWarehouseId, reqVO.getWarehouseId())\n                    .eq(reqVO.getProductId() != null, ErpSaleReturnItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpSaleReturnDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpSaleReturnDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpSaleReturnDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpSaleReturnDO>()\n                .eq(ErpSaleReturnDO::getId, id).eq(ErpSaleReturnDO::getStatus, status));\n    }\n\n    default ErpSaleReturnDO selectByNo(String no) {\n        return selectOne(ErpSaleReturnDO::getNo, no);\n    }\n\n    default List<ErpSaleReturnDO> selectListByOrderId(Long orderId) {\n        return selectList(ErpSaleReturnDO::getOrderId, orderId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/statistics/ErpPurchaseStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.statistics;\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 采购统计 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpPurchaseStatisticsMapper {\n\n    BigDecimal getPurchasePrice(@Param(\"beginTime\") LocalDateTime beginTime,\n                                @Param(\"endTime\") LocalDateTime endTime);\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/statistics/ErpSaleStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.statistics;\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 销售统计 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpSaleStatisticsMapper {\n\n    BigDecimal getSalePrice(@Param(\"beginTime\") LocalDateTime beginTime,\n                            @Param(\"endTime\") LocalDateTime endTime);\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 库存盘点单项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockCheckItemMapper extends BaseMapperX<ErpStockCheckItemDO> {\n\n    default List<ErpStockCheckItemDO> selectListByCheckId(Long checkId) {\n        return selectList(ErpStockCheckItemDO::getCheckId, checkId);\n    }\n\n    default List<ErpStockCheckItemDO> selectListByCheckIds(Collection<Long> checkIds) {\n        return selectList(ErpStockCheckItemDO::getCheckId, checkIds);\n    }\n\n    default int deleteByCheckId(Long checkId) {\n        return delete(ErpStockCheckItemDO::getCheckId, checkId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * ERP 库存调拨单 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockCheckMapper extends BaseMapperX<ErpStockCheckDO> {\n\n    default PageResult<ErpStockCheckDO> selectPage(ErpStockCheckPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpStockCheckDO> query = new MPJLambdaWrapperX<ErpStockCheckDO>()\n                .likeIfPresent(ErpStockCheckDO::getNo, reqVO.getNo())\n                .betweenIfPresent(ErpStockCheckDO::getCheckTime, reqVO.getCheckTime())\n                .eqIfPresent(ErpStockCheckDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpStockCheckDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpStockCheckDO::getCreator, reqVO.getCreator())\n                .orderByDesc(ErpStockCheckDO::getId);\n        if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) {\n            query.leftJoin(ErpStockCheckItemDO.class, ErpStockCheckItemDO::getCheckId, ErpStockCheckDO::getId)\n                    .eq(reqVO.getWarehouseId() != null, ErpStockCheckItemDO::getWarehouseId, reqVO.getWarehouseId())\n                    .eq(reqVO.getProductId() != null, ErpStockCheckItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpStockCheckDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpStockCheckDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpStockCheckDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpStockCheckDO>()\n                .eq(ErpStockCheckDO::getId, id).eq(ErpStockCheckDO::getStatus, status));\n    }\n\n    default ErpStockCheckDO selectByNo(String no) {\n        return selectOne(ErpStockCheckDO::getNo, no);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 其它入库单项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockInItemMapper extends BaseMapperX<ErpStockInItemDO> {\n\n    default List<ErpStockInItemDO> selectListByInId(Long inId) {\n        return selectList(ErpStockInItemDO::getInId, inId);\n    }\n\n    default List<ErpStockInItemDO> selectListByInIds(Collection<Long> inIds) {\n        return selectList(ErpStockInItemDO::getInId, inIds);\n    }\n\n    default int deleteByInId(Long inId) {\n        return delete(ErpStockInItemDO::getInId, inId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * ERP 其它入库单 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockInMapper extends BaseMapperX<ErpStockInDO> {\n\n    default PageResult<ErpStockInDO> selectPage(ErpStockInPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpStockInDO> query = new MPJLambdaWrapperX<ErpStockInDO>()\n                .likeIfPresent(ErpStockInDO::getNo, reqVO.getNo())\n                .eqIfPresent(ErpStockInDO::getSupplierId, reqVO.getSupplierId())\n                .betweenIfPresent(ErpStockInDO::getInTime, reqVO.getInTime())\n                .eqIfPresent(ErpStockInDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpStockInDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpStockInDO::getCreator, reqVO.getCreator())\n                .orderByDesc(ErpStockInDO::getId);\n        if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) {\n            query.leftJoin(ErpStockInItemDO.class, ErpStockInItemDO::getInId, ErpStockInDO::getId)\n                    .eq(reqVO.getWarehouseId() != null, ErpStockInItemDO::getWarehouseId, reqVO.getWarehouseId())\n                    .eq(reqVO.getProductId() != null, ErpStockInItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpStockInDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpStockInDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpStockInDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpStockInDO>()\n                .eq(ErpStockInDO::getId, id).eq(ErpStockInDO::getStatus, status));\n    }\n\n    default ErpStockInDO selectByNo(String no) {\n        return selectOne(ErpStockInDO::getNo, no);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * ERP 产品库存 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockMapper extends BaseMapperX<ErpStockDO> {\n\n    default PageResult<ErpStockDO> selectPage(ErpStockPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ErpStockDO>()\n                .eqIfPresent(ErpStockDO::getProductId, reqVO.getProductId())\n                .eqIfPresent(ErpStockDO::getWarehouseId, reqVO.getWarehouseId())\n                .orderByDesc(ErpStockDO::getId));\n    }\n\n    default ErpStockDO selectByProductIdAndWarehouseId(Long productId, Long warehouseId) {\n        return selectOne(ErpStockDO::getProductId, productId,\n                ErpStockDO::getWarehouseId, warehouseId);\n    }\n\n    default int updateCountIncrement(Long id, BigDecimal count, boolean negativeEnable) {\n        LambdaUpdateWrapper<ErpStockDO> updateWrapper = new LambdaUpdateWrapper<ErpStockDO>()\n                .eq(ErpStockDO::getId, id);\n        if (count.compareTo(BigDecimal.ZERO) > 0) {\n            updateWrapper.setSql(\"count = count + \" + count);\n        } else if (count.compareTo(BigDecimal.ZERO) < 0) {\n            if (!negativeEnable) {\n                updateWrapper.ge(ErpStockDO::getCount, count.abs());\n            }\n            updateWrapper.setSql(\"count = count - \" + count.abs());\n        }\n        return update(null, updateWrapper);\n    }\n\n    default BigDecimal selectSumByProductId(Long productId) {\n        // SQL sum 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpStockDO>()\n                .select(\"SUM(count) AS sum_count\")\n                .eq(\"product_id\", productId));\n        // 获得数量\n        if (CollUtil.isEmpty(result)) {\n            return BigDecimal.ZERO;\n        }\n        return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), \"sum_count\", 0D));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 库存调拨单项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockMoveItemMapper extends BaseMapperX<ErpStockMoveItemDO> {\n\n    default List<ErpStockMoveItemDO> selectListByMoveId(Long moveId) {\n        return selectList(ErpStockMoveItemDO::getMoveId, moveId);\n    }\n\n    default List<ErpStockMoveItemDO> selectListByMoveIds(Collection<Long> moveIds) {\n        return selectList(ErpStockMoveItemDO::getMoveId, moveIds);\n    }\n\n    default int deleteByMoveId(Long moveId) {\n        return delete(ErpStockMoveItemDO::getMoveId, moveId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMovePageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * ERP 库存调拨单 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockMoveMapper extends BaseMapperX<ErpStockMoveDO> {\n\n    default PageResult<ErpStockMoveDO> selectPage(ErpStockMovePageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpStockMoveDO> query = new MPJLambdaWrapperX<ErpStockMoveDO>()\n                .likeIfPresent(ErpStockMoveDO::getNo, reqVO.getNo())\n                .betweenIfPresent(ErpStockMoveDO::getMoveTime, reqVO.getMoveTime())\n                .eqIfPresent(ErpStockMoveDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpStockMoveDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpStockMoveDO::getCreator, reqVO.getCreator())\n                .orderByDesc(ErpStockMoveDO::getId);\n        if (reqVO.getFromWarehouseId() != null || reqVO.getProductId() != null) {\n            query.leftJoin(ErpStockMoveItemDO.class, ErpStockMoveItemDO::getMoveId, ErpStockMoveDO::getId)\n                    .eq(reqVO.getFromWarehouseId() != null, ErpStockMoveItemDO::getFromWarehouseId, reqVO.getFromWarehouseId())\n                    .eq(reqVO.getProductId() != null, ErpStockMoveItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpStockMoveDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpStockMoveDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpStockMoveDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpStockMoveDO>()\n                .eq(ErpStockMoveDO::getId, id).eq(ErpStockMoveDO::getStatus, status));\n    }\n\n    default ErpStockMoveDO selectByNo(String no) {\n        return selectOne(ErpStockMoveDO::getNo, no);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutItemMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 其它出库单项 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockOutItemMapper extends BaseMapperX<ErpStockOutItemDO> {\n\n    default List<ErpStockOutItemDO> selectListByOutId(Long outId) {\n        return selectList(ErpStockOutItemDO::getOutId, outId);\n    }\n\n    default List<ErpStockOutItemDO> selectListByOutIds(Collection<Long> outIds) {\n        return selectList(ErpStockOutItemDO::getOutId, outIds);\n    }\n\n    default int deleteByOutId(Long outId) {\n        return delete(ErpStockOutItemDO::getOutId, outId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * ERP 其它出库单 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockOutMapper extends BaseMapperX<ErpStockOutDO> {\n\n    default PageResult<ErpStockOutDO> selectPage(ErpStockOutPageReqVO reqVO) {\n        MPJLambdaWrapperX<ErpStockOutDO> query = new MPJLambdaWrapperX<ErpStockOutDO>()\n                .likeIfPresent(ErpStockOutDO::getNo, reqVO.getNo())\n                .eqIfPresent(ErpStockOutDO::getCustomerId, reqVO.getCustomerId())\n                .betweenIfPresent(ErpStockOutDO::getOutTime, reqVO.getOutTime())\n                .eqIfPresent(ErpStockOutDO::getStatus, reqVO.getStatus())\n                .likeIfPresent(ErpStockOutDO::getRemark, reqVO.getRemark())\n                .eqIfPresent(ErpStockOutDO::getCreator, reqVO.getCreator())\n                .orderByDesc(ErpStockOutDO::getId);\n        if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) {\n            query.leftJoin(ErpStockOutItemDO.class, ErpStockOutItemDO::getOutId, ErpStockOutDO::getId)\n                    .eq(reqVO.getWarehouseId() != null, ErpStockOutItemDO::getWarehouseId, reqVO.getWarehouseId())\n                    .eq(reqVO.getProductId() != null, ErpStockOutItemDO::getProductId, reqVO.getProductId())\n                    .groupBy(ErpStockOutDO::getId); // 避免 1 对多查询，产生相同的 1\n        }\n        return selectJoinPage(reqVO, ErpStockOutDO.class, query);\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, ErpStockOutDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<ErpStockOutDO>()\n                .eq(ErpStockOutDO::getId, id).eq(ErpStockOutDO::getStatus, status));\n    }\n\n    default ErpStockOutDO selectByNo(String no) {\n        return selectOne(ErpStockOutDO::getNo, no);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockRecordMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * ERP 产品库存明细 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpStockRecordMapper extends BaseMapperX<ErpStockRecordDO> {\n\n    default PageResult<ErpStockRecordDO> selectPage(ErpStockRecordPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ErpStockRecordDO>()\n                .eqIfPresent(ErpStockRecordDO::getProductId, reqVO.getProductId())\n                .eqIfPresent(ErpStockRecordDO::getWarehouseId, reqVO.getWarehouseId())\n                .eqIfPresent(ErpStockRecordDO::getBizType, reqVO.getBizType())\n                .likeIfPresent(ErpStockRecordDO::getBizNo, reqVO.getBizNo())\n                .betweenIfPresent(ErpStockRecordDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(ErpStockRecordDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpWarehouseMapper.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.mysql.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * ERP 仓库 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ErpWarehouseMapper extends BaseMapperX<ErpWarehouseDO> {\n\n    default PageResult<ErpWarehouseDO> selectPage(ErpWarehousePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ErpWarehouseDO>()\n                .likeIfPresent(ErpWarehouseDO::getName, reqVO.getName())\n                .eqIfPresent(ErpWarehouseDO::getStatus, reqVO.getStatus())\n                .orderByDesc(ErpWarehouseDO::getId));\n    }\n\n    default ErpWarehouseDO selectByDefaultStatus() {\n        return selectOne(ErpWarehouseDO::getDefaultStatus, true);\n    }\n\n    default List<ErpWarehouseDO> selectListByStatus(Integer status) {\n        return selectList(ErpWarehouseDO::getStatus, status);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/RedisKeyConstants.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.redis;\n\n/**\n * ERP Redis Key 枚举类\n *\n * @author 芋道源码\n */\npublic interface RedisKeyConstants {\n\n    /**\n     * 序号的缓存\n     *\n     * KEY 格式：trade_no:{prefix}\n     * VALUE 数据格式：编号自增\n     */\n    String NO = \"erp:seq_no:\";\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java",
    "content": "package cn.iocoder.yudao.module.erp.dal.redis.no;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport cn.iocoder.yudao.module.erp.dal.redis.RedisKeyConstants;\nimport javax.annotation.Resource;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport java.time.Duration;\nimport java.time.LocalDateTime;\n\n\n/**\n * Erp 订单序号的 Redis DAO\n *\n * @author HUIHUI\n */\n@Repository\npublic class ErpNoRedisDAO {\n\n    /**\n     * 其它入库 {@link cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO}\n     */\n    public static final String STOCK_IN_NO_PREFIX = \"QTRK\";\n    /**\n     * 其它出库 {@link cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO}\n     */\n    public static final String STOCK_OUT_NO_PREFIX = \"QCKD\";\n\n    /**\n     * 库存调拨 {@link cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO}\n     */\n    public static final String STOCK_MOVE_NO_PREFIX = \"QCDB\";\n\n    /**\n     * 库存盘点 {@link cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO}\n     */\n    public static final String STOCK_CHECK_NO_PREFIX = \"QCPD\";\n\n    /**\n     * 销售订单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO}\n     */\n    public static final String SALE_ORDER_NO_PREFIX = \"XSDD\";\n    /**\n     * 销售出库 {@link cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO}\n     */\n    public static final String SALE_OUT_NO_PREFIX = \"XSCK\";\n    /**\n     * 销售退货 {@link cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO}\n     */\n    public static final String SALE_RETURN_NO_PREFIX = \"XSTH\";\n\n    /**\n     * 采购订单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO}\n     */\n    public static final String PURCHASE_ORDER_NO_PREFIX = \"CGDD\";\n    /**\n     * 采购入库 {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO}\n     */\n    public static final String PURCHASE_IN_NO_PREFIX = \"CGRK\";\n    /**\n     * 采购退货 {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO}\n     */\n    public static final String PURCHASE_RETURN_NO_PREFIX = \"CGTH\";\n\n    /**\n     * 付款单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO}\n     */\n    public static final String FINANCE_PAYMENT_NO_PREFIX = \"FKD\";\n    /**\n     * 收款单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO}\n     */\n    public static final String FINANCE_RECEIPT_NO_PREFIX = \"SKD\";\n\n    @Resource\n    private StringRedisTemplate stringRedisTemplate;\n\n    /**\n     * 生成序号，使用当前日期，格式为 {PREFIX} + yyyyMMdd + 6 位自增\n     * 例如说：QTRK 202109 000001 （没有中间空格）\n     *\n     * @param prefix 前缀\n     * @return 序号\n     */\n    public String generate(String prefix) {\n        // 递增序号\n        String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN);\n        String key = RedisKeyConstants.NO + noPrefix;\n        Long no = stringRedisTemplate.opsForValue().increment(key);\n        // 设置过期时间\n        stringRedisTemplate.expire(key, Duration.ofDays(1L));\n        return noPrefix + String.format(\"%06d\", no);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/framework/package-info.java",
    "content": "/**\n * 属于 erp 模块的 framework 封装\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.erp.framework;\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/framework/rpc/config/RpcConfiguration.java",
    "content": "package cn.iocoder.yudao.module.erp.framework.rpc.config;\n\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(value = \"erpRpcConfiguration\", proxyBeanMethods = false)\n@EnableFeignClients(clients = AdminUserApi.class)\npublic class RpcConfiguration {\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/framework/rpc/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.erp.framework.rpc;\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/framework/security/config/SecurityConfiguration.java",
    "content": "package cn.iocoder.yudao.module.erp.framework.security.config;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.module.erp.enums.ApiConstants;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\n/**\n * Erp 模块的 Security 配置\n */\n@Configuration(\"erpSecurityConfiguration\")\npublic class SecurityConfiguration {\n\n    @Bean(\"erpAuthorizeRequestsCustomizer\")\n    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {\n        return new AuthorizeRequestsCustomizer() {\n\n            @Override\n            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n                // Swagger 接口文档\n                registry.requestMatchers(\"/v3/api-docs/**\").permitAll()\n                        .requestMatchers(\"/webjars/**\").permitAll()\n                        .requestMatchers(\"/swagger-ui\").permitAll()\n                        .requestMatchers(\"/swagger-ui/**\").permitAll();\n                // Spring Boot Actuator 的安全配置\n                registry.requestMatchers(\"/actuator\").permitAll()\n                        .requestMatchers(\"/actuator/**\").permitAll();\n                // Druid 监控\n                registry.requestMatchers(\"/druid/**\").permitAll();\n                // RPC 服务的安全配置\n                registry.requestMatchers(ApiConstants.PREFIX + \"/**\").permitAll();\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/framework/security/core/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.erp.framework.security.core;\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/package-info.java",
    "content": "/**\n * erp 包下，企业资源管理（Enterprise Resource Planning）。\n * 例如说：采购、销售、库存、财务、产品等等\n *\n * 1. Controller URL：以 /erp/ 开头，避免和其它 Module 冲突\n * 2. DataObject 表名：以 erp_ 开头，方便在数据库中区分\n *\n * 注意，由于 Erp 模块下，容易和其它模块重名，所以类名都加载 Erp 的前缀~\n */\npackage cn.iocoder.yudao.module.erp;\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.finance;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 结算账户 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpAccountService {\n\n    /**\n     * 创建结算账户\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createAccount(@Valid ErpAccountSaveReqVO createReqVO);\n\n    /**\n     * 更新ERP 结算账户\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateAccount(@Valid ErpAccountSaveReqVO updateReqVO);\n\n    /**\n     * 更新结算账户默认状态\n     *\n     * @param id 编号\n     * @param defaultStatus 默认状态\n     */\n    void updateAccountDefaultStatus(Long id, Boolean defaultStatus);\n\n    /**\n     * 删除结算账户\n     *\n     * @param id 编号\n     */\n    void deleteAccount(Long id);\n\n    /**\n     * 获得结算账户\n     *\n     * @param id 编号\n     * @return 结算账户\n     */\n    ErpAccountDO getAccount(Long id);\n\n    /**\n     * 校验结算账户\n     *\n     * @param id 编号\n     * @return 结算账户\n     */\n    ErpAccountDO validateAccount(Long id);\n\n    /**\n     * 获得指定状态的结算账户列表\n     *\n     * @param status 状态\n     * @return 结算账户\n     */\n    List<ErpAccountDO> getAccountListByStatus(Integer status);\n\n    /**\n     * 获得结算账户列表\n     *\n     * @param ids 编号数组\n     * @return 结算账户列表\n     */\n    List<ErpAccountDO> getAccountList(Collection<Long> ids);\n\n    /**\n     * 获得结算账户 Map\n     *\n     * @param ids 编号数组\n     * @return 结算账户 Map\n     */\n    default Map<Long, ErpAccountDO> getAccountMap(Collection<Long> ids) {\n        return convertMap(getAccountList(ids), ErpAccountDO::getId);\n    }\n\n    /**\n     * 获得结算账户分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 结算账户分页\n     */\n    PageResult<ErpAccountDO> getAccountPage(ErpAccountPageReqVO pageReqVO);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.finance;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpAccountMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n/**\n * ERP 结算账户 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpAccountServiceImpl implements ErpAccountService {\n\n    @Resource\n    private ErpAccountMapper accountMapper;\n\n    @Override\n    public Long createAccount(ErpAccountSaveReqVO createReqVO) {\n        // 插入\n        ErpAccountDO account = BeanUtils.toBean(createReqVO, ErpAccountDO.class);\n        accountMapper.insert(account);\n        // 返回\n        return account.getId();\n    }\n\n    @Override\n    public void updateAccount(ErpAccountSaveReqVO updateReqVO) {\n        // 校验存在\n        validateAccountExists(updateReqVO.getId());\n        // 更新\n        ErpAccountDO updateObj = BeanUtils.toBean(updateReqVO, ErpAccountDO.class);\n        accountMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void updateAccountDefaultStatus(Long id, Boolean defaultStatus) {\n        // 1. 校验存在\n        validateAccountExists(id);\n\n        // 2.1 如果开启，则需要关闭所有其它的默认\n        if (defaultStatus) {\n            ErpAccountDO account = accountMapper.selectByDefaultStatus();\n            if (account != null) {\n                accountMapper.updateById(new ErpAccountDO().setId(account.getId()).setDefaultStatus(false));\n            }\n        }\n        // 2.2 更新对应的默认状态\n        accountMapper.updateById(new ErpAccountDO().setId(id).setDefaultStatus(defaultStatus));\n    }\n\n    @Override\n    public void deleteAccount(Long id) {\n        // 校验存在\n        validateAccountExists(id);\n        // 删除\n        accountMapper.deleteById(id);\n    }\n\n    private void validateAccountExists(Long id) {\n        if (accountMapper.selectById(id) == null) {\n            throw exception(ACCOUNT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ErpAccountDO getAccount(Long id) {\n        return accountMapper.selectById(id);\n    }\n\n    @Override\n    public ErpAccountDO validateAccount(Long id) {\n        ErpAccountDO account = accountMapper.selectById(id);\n        if (account == null) {\n            throw exception(ACCOUNT_NOT_EXISTS);\n        }\n        if (CommonStatusEnum.isDisable(account.getStatus())) {\n            throw exception(ACCOUNT_NOT_ENABLE, account.getName());\n        }\n        return account;\n    }\n\n    @Override\n    public List<ErpAccountDO> getAccountListByStatus(Integer status) {\n        return accountMapper.selectListByStatus(status);\n    }\n\n    @Override\n    public List<ErpAccountDO> getAccountList(Collection<Long> ids) {\n        return accountMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<ErpAccountDO> getAccountPage(ErpAccountPageReqVO pageReqVO) {\n        return accountMapper.selectPage(pageReqVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.finance;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 付款单 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpFinancePaymentService {\n\n    /**\n     * 创建付款单\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createFinancePayment(@Valid ErpFinancePaymentSaveReqVO createReqVO);\n\n    /**\n     * 更新付款单\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateFinancePayment(@Valid ErpFinancePaymentSaveReqVO updateReqVO);\n\n    /**\n     * 更新付款单的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateFinancePaymentStatus(Long id, Integer status);\n\n    /**\n     * 删除付款单\n     *\n     * @param ids 编号数组\n     */\n    void deleteFinancePayment(List<Long> ids);\n\n    /**\n     * 获得付款单\n     *\n     * @param id 编号\n     * @return 付款单\n     */\n    ErpFinancePaymentDO getFinancePayment(Long id);\n\n    /**\n     * 获得付款单分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 付款单分页\n     */\n    PageResult<ErpFinancePaymentDO> getFinancePaymentPage(ErpFinancePaymentPageReqVO pageReqVO);\n\n    // ==================== 付款单项 ====================\n\n    /**\n     * 获得付款单项列表\n     *\n     * @param paymentId 付款单编号\n     * @return 付款单项列表\n     */\n    List<ErpFinancePaymentItemDO> getFinancePaymentItemListByPaymentId(Long paymentId);\n\n    /**\n     * 获得付款单项 List\n     *\n     * @param paymentIds 付款单编号数组\n     * @return 付款单项 List\n     */\n    List<ErpFinancePaymentItemDO> getFinancePaymentItemListByPaymentIds(Collection<Long> paymentIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.finance;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinancePaymentItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinancePaymentMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseReturnService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 付款单 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpFinancePaymentServiceImpl implements ErpFinancePaymentService {\n\n    @Resource\n    private ErpFinancePaymentMapper financePaymentMapper;\n    @Resource\n    private ErpFinancePaymentItemMapper financePaymentItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpSupplierService supplierService;\n    @Resource\n    private ErpAccountService accountService;\n    @Resource\n    private ErpPurchaseInService purchaseInService;\n    @Resource\n    private ErpPurchaseReturnService purchaseReturnService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createFinancePayment(ErpFinancePaymentSaveReqVO createReqVO) {\n        // 1.1 校验订单项的有效性\n        List<ErpFinancePaymentItemDO> paymentItems = validateFinancePaymentItems(\n                createReqVO.getSupplierId(), createReqVO.getItems());\n        // 1.2 校验供应商\n        supplierService.validateSupplier(createReqVO.getSupplierId());\n        // 1.3 校验结算账户\n        if (createReqVO.getAccountId() != null) {\n            accountService.validateAccount(createReqVO.getAccountId());\n        }\n        // 1.4 校验财务人员\n        if (createReqVO.getFinanceUserId() != null) {\n            adminUserApi.validateUser(createReqVO.getFinanceUserId());\n        }\n        // 1.5 生成付款单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.FINANCE_PAYMENT_NO_PREFIX);\n        if (financePaymentMapper.selectByNo(no) != null) {\n            throw exception(FINANCE_PAYMENT_NO_EXISTS);\n        }\n\n        // 2.1 插入付款单\n        ErpFinancePaymentDO payment = BeanUtils.toBean(createReqVO, ErpFinancePaymentDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()));\n        calculateTotalPrice(payment, paymentItems);\n        financePaymentMapper.insert(payment);\n        // 2.2 插入付款单项\n        paymentItems.forEach(o -> o.setPaymentId(payment.getId()));\n        financePaymentItemMapper.insertBatch(paymentItems);\n\n        // 3. 更新采购入库、退货的付款金额情况\n        updatePurchasePrice(paymentItems);\n        return payment.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateFinancePayment(ErpFinancePaymentSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpFinancePaymentDO payment = validateFinancePaymentExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(payment.getStatus())) {\n            throw exception(FINANCE_PAYMENT_UPDATE_FAIL_APPROVE, payment.getNo());\n        }\n        // 1.2 校验供应商\n        supplierService.validateSupplier(updateReqVO.getSupplierId());\n        // 1.3 校验结算账户\n        if (updateReqVO.getAccountId() != null) {\n            accountService.validateAccount(updateReqVO.getAccountId());\n        }\n        // 1.4 校验财务人员\n        if (updateReqVO.getFinanceUserId() != null) {\n            adminUserApi.validateUser(updateReqVO.getFinanceUserId());\n        }\n        // 1.5 校验付款单项的有效性\n        List<ErpFinancePaymentItemDO> paymentItems = validateFinancePaymentItems(\n                updateReqVO.getSupplierId(), updateReqVO.getItems());\n\n        // 2.1 更新付款单\n        ErpFinancePaymentDO updateObj = BeanUtils.toBean(updateReqVO, ErpFinancePaymentDO.class);\n        calculateTotalPrice(updateObj, paymentItems);\n        financePaymentMapper.updateById(updateObj);\n        // 2.2 更新付款单项\n        updateFinancePaymentItemList(updateReqVO.getId(), paymentItems);\n    }\n\n    private void calculateTotalPrice(ErpFinancePaymentDO payment, List<ErpFinancePaymentItemDO> paymentItems) {\n        payment.setTotalPrice(getSumValue(paymentItems, ErpFinancePaymentItemDO::getPaymentPrice, BigDecimal::add, BigDecimal.ZERO));\n        payment.setPaymentPrice(payment.getTotalPrice().subtract(payment.getDiscountPrice()));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateFinancePaymentStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpFinancePaymentDO payment = validateFinancePaymentExists(id);\n        // 1.2 校验状态\n        if (payment.getStatus().equals(status)) {\n            throw exception(approve ? FINANCE_PAYMENT_APPROVE_FAIL : FINANCE_PAYMENT_PROCESS_FAIL);\n        }\n\n        // 2. 更新状态\n        int updateCount = financePaymentMapper.updateByIdAndStatus(id, payment.getStatus(),\n                new ErpFinancePaymentDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? FINANCE_PAYMENT_APPROVE_FAIL : FINANCE_PAYMENT_PROCESS_FAIL);\n        }\n    }\n\n    private List<ErpFinancePaymentItemDO> validateFinancePaymentItems(\n            Long supplierId,\n            List<ErpFinancePaymentSaveReqVO.Item> list) {\n        return convertList(list, o -> BeanUtils.toBean(o, ErpFinancePaymentItemDO.class, item -> {\n            if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.PURCHASE_IN.getType())) {\n                ErpPurchaseInDO purchaseIn = purchaseInService.validatePurchaseIn(item.getBizId());\n                Assert.equals(purchaseIn.getSupplierId(), supplierId, \"供应商必须相同\");\n                item.setTotalPrice(purchaseIn.getTotalPrice()).setBizNo(purchaseIn.getNo());\n            } else if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.PURCHASE_RETURN.getType())) {\n                ErpPurchaseReturnDO purchaseReturn = purchaseReturnService.validatePurchaseReturn(item.getBizId());\n                Assert.equals(purchaseReturn.getSupplierId(), supplierId, \"供应商必须相同\");\n                item.setTotalPrice(purchaseReturn.getTotalPrice().negate()).setBizNo(purchaseReturn.getNo());\n            } else {\n                throw new IllegalArgumentException(\"业务类型不正确：\" + item.getBizType());\n            }\n        }));\n    }\n\n    private void updateFinancePaymentItemList(Long id, List<ErpFinancePaymentItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpFinancePaymentItemDO> oldList = financePaymentItemMapper.selectListByPaymentId(id);\n        List<List<ErpFinancePaymentItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setPaymentId(id));\n            financePaymentItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            financePaymentItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            financePaymentItemMapper.deleteByIds(convertList(diffList.get(2), ErpFinancePaymentItemDO::getId));\n        }\n\n        // 第三步，更新采购入库、退货的付款金额情况\n        updatePurchasePrice(CollectionUtils.newArrayList(diffList));\n    }\n\n    private void updatePurchasePrice(List<ErpFinancePaymentItemDO> paymentItems) {\n        paymentItems.forEach(paymentItem -> {\n            BigDecimal totalPaymentPrice = financePaymentItemMapper.selectPaymentPriceSumByBizIdAndBizType(\n                    paymentItem.getBizId(), paymentItem.getBizType());\n            if (ErpBizTypeEnum.PURCHASE_IN.getType().equals(paymentItem.getBizType())) {\n                purchaseInService.updatePurchaseInPaymentPrice(paymentItem.getBizId(), totalPaymentPrice);\n            } else if (ErpBizTypeEnum.PURCHASE_RETURN.getType().equals(paymentItem.getBizType())) {\n                purchaseReturnService.updatePurchaseReturnRefundPrice(paymentItem.getBizId(), totalPaymentPrice.negate());\n            } else {\n                throw new IllegalArgumentException(\"业务类型不正确：\" + paymentItem.getBizType());\n            }\n        });\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteFinancePayment(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpFinancePaymentDO> payments = financePaymentMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(payments)) {\n            return;\n        }\n        payments.forEach(payment -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(payment.getStatus())) {\n                throw exception(FINANCE_PAYMENT_DELETE_FAIL_APPROVE, payment.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        payments.forEach(payment -> {\n            // 2.1 删除付款单\n            financePaymentMapper.deleteById(payment.getId());\n            // 2.2 删除付款单项\n            List<ErpFinancePaymentItemDO> paymentItems = financePaymentItemMapper.selectListByPaymentId(payment.getId());\n            financePaymentItemMapper.deleteByIds(convertSet(paymentItems, ErpFinancePaymentItemDO::getId));\n\n            // 2.3 更新采购入库、退货的付款金额情况\n            updatePurchasePrice(paymentItems);\n        });\n    }\n\n    private ErpFinancePaymentDO validateFinancePaymentExists(Long id) {\n        ErpFinancePaymentDO payment = financePaymentMapper.selectById(id);\n        if (payment == null) {\n            throw exception(FINANCE_PAYMENT_NOT_EXISTS);\n        }\n        return payment;\n    }\n\n    @Override\n    public ErpFinancePaymentDO getFinancePayment(Long id) {\n        return financePaymentMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ErpFinancePaymentDO> getFinancePaymentPage(ErpFinancePaymentPageReqVO pageReqVO) {\n        return financePaymentMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 付款单项 ====================\n\n    @Override\n    public List<ErpFinancePaymentItemDO> getFinancePaymentItemListByPaymentId(Long paymentId) {\n        return financePaymentItemMapper.selectListByPaymentId(paymentId);\n    }\n\n    @Override\n    public List<ErpFinancePaymentItemDO> getFinancePaymentItemListByPaymentIds(Collection<Long> paymentIds) {\n        if (CollUtil.isEmpty(paymentIds)) {\n            return Collections.emptyList();\n        }\n        return financePaymentItemMapper.selectListByPaymentIds(paymentIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.finance;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 收款单 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpFinanceReceiptService {\n\n    /**\n     * 创建收款单\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createFinanceReceipt(@Valid ErpFinanceReceiptSaveReqVO createReqVO);\n\n    /**\n     * 更新收款单\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateFinanceReceipt(@Valid ErpFinanceReceiptSaveReqVO updateReqVO);\n\n    /**\n     * 更新收款单的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateFinanceReceiptStatus(Long id, Integer status);\n\n    /**\n     * 删除收款单\n     *\n     * @param ids 编号数组\n     */\n    void deleteFinanceReceipt(List<Long> ids);\n\n    /**\n     * 获得收款单\n     *\n     * @param id 编号\n     * @return 收款单\n     */\n    ErpFinanceReceiptDO getFinanceReceipt(Long id);\n\n    /**\n     * 获得收款单分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 收款单分页\n     */\n    PageResult<ErpFinanceReceiptDO> getFinanceReceiptPage(ErpFinanceReceiptPageReqVO pageReqVO);\n\n    // ==================== 收款单项 ====================\n\n    /**\n     * 获得收款单项列表\n     *\n     * @param receiptId 收款单编号\n     * @return 收款单项列表\n     */\n    List<ErpFinanceReceiptItemDO> getFinanceReceiptItemListByReceiptId(Long receiptId);\n\n    /**\n     * 获得收款单项 List\n     *\n     * @param receiptIds 收款单编号数组\n     * @return 收款单项 List\n     */\n    List<ErpFinanceReceiptItemDO> getFinanceReceiptItemListByReceiptIds(Collection<Long> receiptIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.finance;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinanceReceiptItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinanceReceiptMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpSaleOutService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpSaleReturnService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 收款单 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpFinanceReceiptServiceImpl implements ErpFinanceReceiptService {\n\n    @Resource\n    private ErpFinanceReceiptMapper financeReceiptMapper;\n    @Resource\n    private ErpFinanceReceiptItemMapper financeReceiptItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpCustomerService customerService;\n    @Resource\n    private ErpAccountService accountService;\n    @Resource\n    private ErpSaleOutService saleOutService;\n    @Resource\n    private ErpSaleReturnService saleReturnService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createFinanceReceipt(ErpFinanceReceiptSaveReqVO createReqVO) {\n        // 1.1 校验订单项的有效性\n        List<ErpFinanceReceiptItemDO> receiptItems = validateFinanceReceiptItems(\n                createReqVO.getCustomerId(), createReqVO.getItems());\n        // 1.2 校验客户\n        customerService.validateCustomer(createReqVO.getCustomerId());\n        // 1.3 校验结算账户\n        if (createReqVO.getAccountId() != null) {\n            accountService.validateAccount(createReqVO.getAccountId());\n        }\n        // 1.4 校验财务人员\n        if (createReqVO.getFinanceUserId() != null) {\n            adminUserApi.validateUser(createReqVO.getFinanceUserId());\n        }\n        // 1.5 生成收款单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.FINANCE_RECEIPT_NO_PREFIX);\n        if (financeReceiptMapper.selectByNo(no) != null) {\n            throw exception(FINANCE_RECEIPT_NO_EXISTS);\n        }\n\n        // 2.1 插入收款单\n        ErpFinanceReceiptDO receipt = BeanUtils.toBean(createReqVO, ErpFinanceReceiptDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()));\n        calculateTotalPrice(receipt, receiptItems);\n        financeReceiptMapper.insert(receipt);\n        // 2.2 插入收款单项\n        receiptItems.forEach(o -> o.setReceiptId(receipt.getId()));\n        financeReceiptItemMapper.insertBatch(receiptItems);\n\n        // 3. 更新销售出库、退货的收款金额情况\n        updateSalePrice(receiptItems);\n        return receipt.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateFinanceReceipt(ErpFinanceReceiptSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpFinanceReceiptDO receipt = validateFinanceReceiptExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(receipt.getStatus())) {\n            throw exception(FINANCE_RECEIPT_UPDATE_FAIL_APPROVE, receipt.getNo());\n        }\n        // 1.2 校验客户\n        customerService.validateCustomer(updateReqVO.getCustomerId());\n        // 1.3 校验结算账户\n        if (updateReqVO.getAccountId() != null) {\n            accountService.validateAccount(updateReqVO.getAccountId());\n        }\n        // 1.4 校验财务人员\n        if (updateReqVO.getFinanceUserId() != null) {\n            adminUserApi.validateUser(updateReqVO.getFinanceUserId());\n        }\n        // 1.5 校验收款单项的有效性\n        List<ErpFinanceReceiptItemDO> receiptItems = validateFinanceReceiptItems(\n                updateReqVO.getCustomerId(), updateReqVO.getItems());\n\n        // 2.1 更新收款单\n        ErpFinanceReceiptDO updateObj = BeanUtils.toBean(updateReqVO, ErpFinanceReceiptDO.class);\n        calculateTotalPrice(updateObj, receiptItems);\n        financeReceiptMapper.updateById(updateObj);\n        // 2.2 更新收款单项\n        updateFinanceReceiptItemList(updateReqVO.getId(), receiptItems);\n    }\n\n    private void calculateTotalPrice(ErpFinanceReceiptDO receipt, List<ErpFinanceReceiptItemDO> receiptItems) {\n        receipt.setTotalPrice(getSumValue(receiptItems, ErpFinanceReceiptItemDO::getReceiptPrice, BigDecimal::add, BigDecimal.ZERO));\n        receipt.setReceiptPrice(receipt.getTotalPrice().subtract(receipt.getDiscountPrice()));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateFinanceReceiptStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpFinanceReceiptDO receipt = validateFinanceReceiptExists(id);\n        // 1.2 校验状态\n        if (receipt.getStatus().equals(status)) {\n            throw exception(approve ? FINANCE_RECEIPT_APPROVE_FAIL : FINANCE_RECEIPT_PROCESS_FAIL);\n        }\n\n        // 2. 更新状态\n        int updateCount = financeReceiptMapper.updateByIdAndStatus(id, receipt.getStatus(),\n                new ErpFinanceReceiptDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? FINANCE_RECEIPT_APPROVE_FAIL : FINANCE_RECEIPT_PROCESS_FAIL);\n        }\n    }\n\n    private List<ErpFinanceReceiptItemDO> validateFinanceReceiptItems(\n            Long customerId,\n            List<ErpFinanceReceiptSaveReqVO.Item> list) {\n        return convertList(list, o -> BeanUtils.toBean(o, ErpFinanceReceiptItemDO.class, item -> {\n            if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.SALE_OUT.getType())) {\n                ErpSaleOutDO saleOut = saleOutService.validateSaleOut(item.getBizId());\n                Assert.equals(saleOut.getCustomerId(), customerId, \"客户必须相同\");\n                item.setTotalPrice(saleOut.getTotalPrice()).setBizNo(saleOut.getNo());\n            } else if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.SALE_RETURN.getType())) {\n                ErpSaleReturnDO saleReturn = saleReturnService.validateSaleReturn(item.getBizId());\n                Assert.equals(saleReturn.getCustomerId(), customerId, \"客户必须相同\");\n                item.setTotalPrice(saleReturn.getTotalPrice().negate()).setBizNo(saleReturn.getNo());\n            } else {\n                throw new IllegalArgumentException(\"业务类型不正确：\" + item.getBizType());\n            }\n        }));\n    }\n\n    private void updateFinanceReceiptItemList(Long id, List<ErpFinanceReceiptItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpFinanceReceiptItemDO> oldList = financeReceiptItemMapper.selectListByReceiptId(id);\n        List<List<ErpFinanceReceiptItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setReceiptId(id));\n            financeReceiptItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            financeReceiptItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            financeReceiptItemMapper.deleteByIds(convertList(diffList.get(2), ErpFinanceReceiptItemDO::getId));\n        }\n\n        // 第三步，更新销售出库、退货的收款金额情况\n        updateSalePrice(CollectionUtils.newArrayList(diffList));\n    }\n\n    private void updateSalePrice(List<ErpFinanceReceiptItemDO> receiptItems) {\n        receiptItems.forEach(receiptItem -> {\n            BigDecimal totalReceiptPrice = financeReceiptItemMapper.selectReceiptPriceSumByBizIdAndBizType(\n                    receiptItem.getBizId(), receiptItem.getBizType());\n            if (ErpBizTypeEnum.SALE_OUT.getType().equals(receiptItem.getBizType())) {\n                saleOutService.updateSaleInReceiptPrice(receiptItem.getBizId(), totalReceiptPrice);\n            } else if (ErpBizTypeEnum.SALE_RETURN.getType().equals(receiptItem.getBizType())) {\n                saleReturnService.updateSaleReturnRefundPrice(receiptItem.getBizId(), totalReceiptPrice.negate());\n            } else {\n                throw new IllegalArgumentException(\"业务类型不正确：\" + receiptItem.getBizType());\n            }\n        });\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteFinanceReceipt(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpFinanceReceiptDO> receipts = financeReceiptMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(receipts)) {\n            return;\n        }\n        receipts.forEach(receipt -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(receipt.getStatus())) {\n                throw exception(FINANCE_RECEIPT_DELETE_FAIL_APPROVE, receipt.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        receipts.forEach(receipt -> {\n            // 2.1 删除收款单\n            financeReceiptMapper.deleteById(receipt.getId());\n            // 2.2 删除收款单项\n            List<ErpFinanceReceiptItemDO> receiptItems = financeReceiptItemMapper.selectListByReceiptId(receipt.getId());\n            financeReceiptItemMapper.deleteByIds(convertSet(receiptItems, ErpFinanceReceiptItemDO::getId));\n\n            // 2.3 更新销售出库、退货的收款金额情况\n            updateSalePrice(receiptItems);\n        });\n    }\n\n    private ErpFinanceReceiptDO validateFinanceReceiptExists(Long id) {\n        ErpFinanceReceiptDO receipt = financeReceiptMapper.selectById(id);\n        if (receipt == null) {\n            throw exception(FINANCE_RECEIPT_NOT_EXISTS);\n        }\n        return receipt;\n    }\n\n    @Override\n    public ErpFinanceReceiptDO getFinanceReceipt(Long id) {\n        return financeReceiptMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ErpFinanceReceiptDO> getFinanceReceiptPage(ErpFinanceReceiptPageReqVO pageReqVO) {\n        return financeReceiptMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 收款单项 ====================\n\n    @Override\n    public List<ErpFinanceReceiptItemDO> getFinanceReceiptItemListByReceiptId(Long receiptId) {\n        return financeReceiptItemMapper.selectListByReceiptId(receiptId);\n    }\n\n    @Override\n    public List<ErpFinanceReceiptItemDO> getFinanceReceiptItemListByReceiptIds(Collection<Long> receiptIds) {\n        if (CollUtil.isEmpty(receiptIds)) {\n            return Collections.emptyList();\n        }\n        return financeReceiptItemMapper.selectListByReceiptIds(receiptIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.product;\n\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategorySaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 产品分类 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpProductCategoryService {\n\n    /**\n     * 创建产品分类\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProductCategory(@Valid ErpProductCategorySaveReqVO createReqVO);\n\n    /**\n     * 更新产品分类\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProductCategory(@Valid ErpProductCategorySaveReqVO updateReqVO);\n\n    /**\n     * 删除产品分类\n     *\n     * @param id 编号\n     */\n    void deleteProductCategory(Long id);\n\n    /**\n     * 获得产品分类\n     *\n     * @param id 编号\n     * @return 产品分类\n     */\n    ErpProductCategoryDO getProductCategory(Long id);\n\n    /**\n     * 获得产品分类列表\n     *\n     * @param listReqVO 查询条件\n     * @return 产品分类列表\n     */\n    List<ErpProductCategoryDO> getProductCategoryList(ErpProductCategoryListReqVO listReqVO);\n\n    /**\n     * 获得产品分类列表\n     *\n     * @param ids 编号数组\n     * @return 产品分类列表\n     */\n    List<ErpProductCategoryDO> getProductCategoryList(Collection<Long> ids);\n\n    /**\n     * 获得产品分类 Map\n     *\n     * @param ids 编号数组\n     * @return 产品分类 Map\n     */\n    default Map<Long, ErpProductCategoryDO> getProductCategoryMap(Collection<Long> ids) {\n        return convertMap(getProductCategoryList(ids), ErpProductCategoryDO::getId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.product;\n\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategorySaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductCategoryMapper;\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n/**\n * ERP 产品分类 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpProductCategoryServiceImpl implements ErpProductCategoryService {\n\n    @Resource\n    private ErpProductCategoryMapper erpProductCategoryMapper;\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private ErpProductService productService;\n\n    @Override\n    public Long createProductCategory(ErpProductCategorySaveReqVO createReqVO) {\n        // 校验父分类编号的有效性\n        validateParentProductCategory(null, createReqVO.getParentId());\n        // 校验分类名称的唯一性\n        validateProductCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());\n\n        // 插入\n        ErpProductCategoryDO category = BeanUtils.toBean(createReqVO, ErpProductCategoryDO.class);\n        erpProductCategoryMapper.insert(category);\n        // 返回\n        return category.getId();\n    }\n\n    @Override\n    public void updateProductCategory(ErpProductCategorySaveReqVO updateReqVO) {\n        // 校验存在\n        validateProductCategoryExists(updateReqVO.getId());\n        // 校验父分类编号的有效性\n        validateParentProductCategory(updateReqVO.getId(), updateReqVO.getParentId());\n        // 校验分类名称的唯一性\n        validateProductCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());\n\n        // 更新\n        ErpProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, ErpProductCategoryDO.class);\n        erpProductCategoryMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteProductCategory(Long id) {\n        // 1.1 校验存在\n        validateProductCategoryExists(id);\n        // 1.2 校验是否有子产品分类\n        if (erpProductCategoryMapper.selectCountByParentId(id) > 0) {\n            throw exception(PRODUCT_CATEGORY_EXITS_CHILDREN);\n        }\n        // 1.3 校验是否有产品\n        if (productService.getProductCountByCategoryId(id) > 0) {\n            throw exception(PRODUCT_CATEGORY_EXITS_PRODUCT);\n        }\n        // 2. 删除\n        erpProductCategoryMapper.deleteById(id);\n    }\n\n    private void validateProductCategoryExists(Long id) {\n        if (erpProductCategoryMapper.selectById(id) == null) {\n            throw exception(PRODUCT_CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    private void validateParentProductCategory(Long id, Long parentId) {\n        if (parentId == null || ErpProductCategoryDO.PARENT_ID_ROOT.equals(parentId)) {\n            return;\n        }\n        // 1. 不能设置自己为父产品分类\n        if (Objects.equals(id, parentId)) {\n            throw exception(PRODUCT_CATEGORY_PARENT_ERROR);\n        }\n        // 2. 父产品分类不存在\n        ErpProductCategoryDO parentCategory = erpProductCategoryMapper.selectById(parentId);\n        if (parentCategory == null) {\n            throw exception(PRODUCT_CATEGORY_PARENT_NOT_EXITS);\n        }\n        // 3. 递归校验父产品分类，如果父产品分类是自己的子产品分类，则报错，避免形成环路\n        if (id == null) { // id 为空，说明新增，不需要考虑环路\n            return;\n        }\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            // 3.1 校验环路\n            parentId = parentCategory.getParentId();\n            if (Objects.equals(id, parentId)) {\n                throw exception(PRODUCT_CATEGORY_PARENT_IS_CHILD);\n            }\n            // 3.2 继续递归下一级父产品分类\n            if (parentId == null || ErpProductCategoryDO.PARENT_ID_ROOT.equals(parentId)) {\n                break;\n            }\n            parentCategory = erpProductCategoryMapper.selectById(parentId);\n            if (parentCategory == null) {\n                break;\n            }\n        }\n    }\n\n    private void validateProductCategoryNameUnique(Long id, Long parentId, String name) {\n        ErpProductCategoryDO productCategory = erpProductCategoryMapper.selectByParentIdAndName(parentId, name);\n        if (productCategory == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的产品分类\n        if (id == null) {\n            throw exception(PRODUCT_CATEGORY_NAME_DUPLICATE);\n        }\n        if (!Objects.equals(productCategory.getId(), id)) {\n            throw exception(PRODUCT_CATEGORY_NAME_DUPLICATE);\n        }\n    }\n\n    @Override\n    public ErpProductCategoryDO getProductCategory(Long id) {\n        return erpProductCategoryMapper.selectById(id);\n    }\n\n    @Override\n    public List<ErpProductCategoryDO> getProductCategoryList(ErpProductCategoryListReqVO listReqVO) {\n        return erpProductCategoryMapper.selectList(listReqVO);\n    }\n\n    @Override\n    public List<ErpProductCategoryDO> getProductCategoryList(Collection<Long> ids) {\n        return erpProductCategoryMapper.selectByIds(ids);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 产品 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpProductService {\n\n    /**\n     * 创建产品\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProduct(@Valid ProductSaveReqVO createReqVO);\n\n    /**\n     * 更新产品\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProduct(@Valid ProductSaveReqVO updateReqVO);\n\n    /**\n     * 删除产品\n     *\n     * @param id 编号\n     */\n    void deleteProduct(Long id);\n\n    /**\n     * 校验产品们的有效性\n     *\n     * @param ids 编号数组\n     * @return 产品列表\n     */\n    List<ErpProductDO> validProductList(Collection<Long> ids);\n\n    /**\n     * 获得产品\n     *\n     * @param id 编号\n     * @return 产品\n     */\n    ErpProductDO getProduct(Long id);\n\n    /**\n     * 获得指定状态的产品 VO 列表\n     *\n     * @param status 状态\n     * @return 产品 VO 列表\n     */\n    List<ErpProductRespVO> getProductVOListByStatus(Integer status);\n\n    /**\n     * 获得产品 VO 列表\n     *\n     * @param ids 编号数组\n     * @return 产品 VO 列表\n     */\n    List<ErpProductRespVO> getProductVOList(Collection<Long> ids);\n\n    /**\n     * 获得产品 VO Map\n     *\n     * @param ids 编号数组\n     * @return 产品 VO Map\n     */\n    default Map<Long, ErpProductRespVO> getProductVOMap(Collection<Long> ids) {\n        return convertMap(getProductVOList(ids), ErpProductRespVO::getId);\n    }\n\n    /**\n     * 获得产品 VO 分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 产品分页\n     */\n    PageResult<ErpProductRespVO> getProductVOPage(ErpProductPageReqVO pageReqVO);\n\n    /**\n     * 基于产品分类编号，获得产品数量\n     *\n     * @param categoryId 产品分类编号\n     * @return 产品数量\n     */\n    Long getProductCountByCategoryId(Long categoryId);\n\n    /**\n     * 基于产品单位编号，获得产品数量\n     *\n     * @param unitId 产品单位编号\n     * @return 产品数量\n     */\n    Long getProductCountByUnitId(Long unitId);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.product;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.PRODUCT_NOT_ENABLE;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.PRODUCT_NOT_EXISTS;\n\n/**\n * ERP 产品 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpProductServiceImpl implements ErpProductService {\n\n    @Resource\n    private ErpProductMapper productMapper;\n\n    @Resource\n    private ErpProductCategoryService productCategoryService;\n    @Resource\n    private ErpProductUnitService productUnitService;\n\n    @Override\n    public Long createProduct(ProductSaveReqVO createReqVO) {\n        // TODO 芋艿：校验分类\n        // 插入\n        ErpProductDO product = BeanUtils.toBean(createReqVO, ErpProductDO.class);\n        productMapper.insert(product);\n        // 返回\n        return product.getId();\n    }\n\n    @Override\n    public void updateProduct(ProductSaveReqVO updateReqVO) {\n        // TODO 芋艿：校验分类\n        // 校验存在\n        validateProductExists(updateReqVO.getId());\n        // 更新\n        ErpProductDO updateObj = BeanUtils.toBean(updateReqVO, ErpProductDO.class);\n        productMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteProduct(Long id) {\n        // 校验存在\n        validateProductExists(id);\n        // 删除\n        productMapper.deleteById(id);\n    }\n\n    @Override\n    public List<ErpProductDO> validProductList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        List<ErpProductDO> list = productMapper.selectByIds(ids);\n        Map<Long, ErpProductDO> productMap = convertMap(list, ErpProductDO::getId);\n        for (Long id : ids) {\n            ErpProductDO product = productMap.get(id);\n            if (productMap.get(id) == null) {\n                throw exception(PRODUCT_NOT_EXISTS);\n            }\n            if (CommonStatusEnum.isDisable(product.getStatus())) {\n                throw exception(PRODUCT_NOT_ENABLE, product.getName());\n            }\n        }\n        return list;\n    }\n\n    private void validateProductExists(Long id) {\n        if (productMapper.selectById(id) == null) {\n            throw exception(PRODUCT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ErpProductDO getProduct(Long id) {\n        return productMapper.selectById(id);\n    }\n\n    @Override\n    public List<ErpProductRespVO> getProductVOListByStatus(Integer status) {\n        List<ErpProductDO> list = productMapper.selectListByStatus(status);\n        return buildProductVOList(list);\n    }\n\n    @Override\n    public List<ErpProductRespVO> getProductVOList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        List<ErpProductDO> list = productMapper.selectByIds(ids);\n        return buildProductVOList(list);\n    }\n\n    @Override\n    public PageResult<ErpProductRespVO> getProductVOPage(ErpProductPageReqVO pageReqVO) {\n        PageResult<ErpProductDO> pageResult = productMapper.selectPage(pageReqVO);\n        return new PageResult<>(buildProductVOList(pageResult.getList()), pageResult.getTotal());\n    }\n\n    private List<ErpProductRespVO> buildProductVOList(List<ErpProductDO> list) {\n        if (CollUtil.isEmpty(list)) {\n            return Collections.emptyList();\n        }\n        Map<Long, ErpProductCategoryDO> categoryMap = productCategoryService.getProductCategoryMap(\n                convertSet(list, ErpProductDO::getCategoryId));\n        Map<Long, ErpProductUnitDO> unitMap = productUnitService.getProductUnitMap(\n                convertSet(list, ErpProductDO::getUnitId));\n        return BeanUtils.toBean(list, ErpProductRespVO.class, product -> {\n            MapUtils.findAndThen(categoryMap, product.getCategoryId(),\n                    category -> product.setCategoryName(category.getName()));\n            MapUtils.findAndThen(unitMap, product.getUnitId(),\n                    unit -> product.setUnitName(unit.getName()));\n        });\n    }\n\n    @Override\n    public Long getProductCountByCategoryId(Long categoryId) {\n        return productMapper.selectCountByCategoryId(categoryId);\n    }\n\n    @Override\n    public Long getProductCountByUnitId(Long unitId) {\n        return productMapper.selectCountByUnitId(unitId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 产品单位 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpProductUnitService {\n\n    /**\n     * 创建产品单位\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProductUnit(@Valid ErpProductUnitSaveReqVO createReqVO);\n\n    /**\n     * 更新产品单位\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProductUnit(@Valid ErpProductUnitSaveReqVO updateReqVO);\n\n    /**\n     * 删除产品单位\n     *\n     * @param id 编号\n     */\n    void deleteProductUnit(Long id);\n\n    /**\n     * 获得产品单位\n     *\n     * @param id 编号\n     * @return 产品单位\n     */\n    ErpProductUnitDO getProductUnit(Long id);\n\n    /**\n     * 获得产品单位分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 产品单位分页\n     */\n    PageResult<ErpProductUnitDO> getProductUnitPage(ErpProductUnitPageReqVO pageReqVO);\n\n    /**\n     * 获得指定状态的产品单位列表\n     *\n     * @param status 状态\n     * @return 产品单位列表\n     */\n    List<ErpProductUnitDO> getProductUnitListByStatus(Integer status);\n\n    /**\n     * 获得产品单位列表\n     *\n     * @param ids 编号数组\n     * @return 产品单位列表\n     */\n    List<ErpProductUnitDO> getProductUnitList(Collection<Long> ids);\n\n    /**\n     * 获得产品单位 Map\n     *\n     * @param ids 编号数组\n     * @return 产品单位 Map\n     */\n    default Map<Long, ErpProductUnitDO> getProductUnitMap(Collection<Long> ids) {\n        return convertMap(getProductUnitList(ids), ErpProductUnitDO::getId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductUnitMapper;\nimport com.google.common.annotations.VisibleForTesting;\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n/**\n * ERP 产品单位 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpProductUnitServiceImpl implements ErpProductUnitService {\n\n    @Resource\n    private ErpProductUnitMapper productUnitMapper;\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private ErpProductService productService;\n\n    @Override\n    public Long createProductUnit(ErpProductUnitSaveReqVO createReqVO) {\n        // 1. 校验名字唯一\n        validateProductUnitNameUnique(null, createReqVO.getName());\n        // 2. 插入\n        ErpProductUnitDO unit = BeanUtils.toBean(createReqVO, ErpProductUnitDO.class);\n        productUnitMapper.insert(unit);\n        return unit.getId();\n    }\n\n    @Override\n    public void updateProductUnit(ErpProductUnitSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        validateProductUnitExists(updateReqVO.getId());\n        // 1.2 校验名字唯一\n        validateProductUnitNameUnique(updateReqVO.getId(), updateReqVO.getName());\n        // 2. 更新\n        ErpProductUnitDO updateObj = BeanUtils.toBean(updateReqVO, ErpProductUnitDO.class);\n        productUnitMapper.updateById(updateObj);\n    }\n\n    @VisibleForTesting\n    void validateProductUnitNameUnique(Long id, String name) {\n        ErpProductUnitDO unit = productUnitMapper.selectByName(name);\n        if (unit == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的字典类型\n        if (id == null) {\n            throw exception(PRODUCT_UNIT_NAME_DUPLICATE);\n        }\n        if (!unit.getId().equals(id)) {\n            throw exception(PRODUCT_UNIT_NAME_DUPLICATE);\n        }\n    }\n\n    @Override\n    public void deleteProductUnit(Long id) {\n        // 1.1 校验存在\n        validateProductUnitExists(id);\n        // 1.2 校验产品是否使用\n        if (productService.getProductCountByUnitId(id) > 0) {\n            throw exception(PRODUCT_UNIT_EXITS_PRODUCT);\n        }\n        // 2. 删除\n        productUnitMapper.deleteById(id);\n    }\n\n    private void validateProductUnitExists(Long id) {\n        if (productUnitMapper.selectById(id) == null) {\n            throw exception(PRODUCT_UNIT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ErpProductUnitDO getProductUnit(Long id) {\n        return productUnitMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ErpProductUnitDO> getProductUnitPage(ErpProductUnitPageReqVO pageReqVO) {\n        return productUnitMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<ErpProductUnitDO> getProductUnitListByStatus(Integer status) {\n        return productUnitMapper.selectListByStatus(status);\n    }\n\n    @Override\n    public List<ErpProductUnitDO> getProductUnitList(Collection<Long> ids) {\n         return productUnitMapper.selectByIds(ids);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.purchase;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO;\nimport javax.validation.Valid;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 采购入库 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpPurchaseInService {\n\n    /**\n     * 创建采购入库\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createPurchaseIn(@Valid ErpPurchaseInSaveReqVO createReqVO);\n\n    /**\n     * 更新采购入库\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updatePurchaseIn(@Valid ErpPurchaseInSaveReqVO updateReqVO);\n\n    /**\n     * 更新采购入库的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updatePurchaseInStatus(Long id, Integer status);\n\n    /**\n     * 更新采购入库的付款金额\n     *\n     * @param id 编号\n     * @param paymentPrice 付款金额\n     */\n    void updatePurchaseInPaymentPrice(Long id, BigDecimal paymentPrice);\n\n    /**\n     * 删除采购入库\n     *\n     * @param ids 编号数组\n     */\n    void deletePurchaseIn(List<Long> ids);\n\n    /**\n     * 获得采购入库\n     *\n     * @param id 编号\n     * @return 采购入库\n     */\n    ErpPurchaseInDO getPurchaseIn(Long id);\n\n    /**\n     * 校验采购入库，已经审核通过\n     *\n     * @param id 编号\n     * @return 采购入库\n     */\n    ErpPurchaseInDO validatePurchaseIn(Long id);\n\n    /**\n     * 获得采购入库分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 采购入库分页\n     */\n    PageResult<ErpPurchaseInDO> getPurchaseInPage(ErpPurchaseInPageReqVO pageReqVO);\n\n    // ==================== 采购入库项 ====================\n\n    /**\n     * 获得采购入库项列表\n     *\n     * @param inId 采购入库编号\n     * @return 采购入库项列表\n     */\n    List<ErpPurchaseInItemDO> getPurchaseInItemListByInId(Long inId);\n\n    /**\n     * 获得采购入库项 List\n     *\n     * @param inIds 采购入库编号数组\n     * @return 采购入库项 List\n     */\n    List<ErpPurchaseInItemDO> getPurchaseInItemListByInIds(Collection<Long> inIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.purchase;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseInItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseInMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 采购入库 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpPurchaseInServiceImpl implements ErpPurchaseInService {\n\n    @Resource\n    private ErpPurchaseInMapper purchaseInMapper;\n    @Resource\n    private ErpPurchaseInItemMapper purchaseInItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private ErpPurchaseOrderService purchaseOrderService;\n    @Resource\n    private ErpAccountService accountService;\n    @Resource\n    private ErpStockRecordService stockRecordService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createPurchaseIn(ErpPurchaseInSaveReqVO createReqVO) {\n        // 1.1 校验采购订单已审核\n        ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(createReqVO.getOrderId());\n        // 1.2 校验入库项的有效性\n        List<ErpPurchaseInItemDO> purchaseInItems = validatePurchaseInItems(createReqVO.getItems());\n        // 1.3 校验结算账户\n        accountService.validateAccount(createReqVO.getAccountId());\n        // 1.4 生成入库单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_IN_NO_PREFIX);\n        if (purchaseInMapper.selectByNo(no) != null) {\n            throw exception(PURCHASE_IN_NO_EXISTS);\n        }\n\n        // 2.1 插入入库\n        ErpPurchaseInDO purchaseIn = BeanUtils.toBean(createReqVO, ErpPurchaseInDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()))\n                .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId());\n        calculateTotalPrice(purchaseIn, purchaseInItems);\n        purchaseInMapper.insert(purchaseIn);\n        // 2.2 插入入库项\n        purchaseInItems.forEach(o -> o.setInId(purchaseIn.getId()));\n        purchaseInItemMapper.insertBatch(purchaseInItems);\n\n        // 3. 更新采购订单的入库数量\n        updatePurchaseOrderInCount(createReqVO.getOrderId());\n        return purchaseIn.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePurchaseIn(ErpPurchaseInSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpPurchaseInDO purchaseIn = validatePurchaseInExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseIn.getStatus())) {\n            throw exception(PURCHASE_IN_UPDATE_FAIL_APPROVE, purchaseIn.getNo());\n        }\n        // 1.2 校验采购订单已审核\n        ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(updateReqVO.getOrderId());\n        // 1.3 校验结算账户\n        accountService.validateAccount(updateReqVO.getAccountId());\n        // 1.4 校验订单项的有效性\n        List<ErpPurchaseInItemDO> purchaseInItems = validatePurchaseInItems(updateReqVO.getItems());\n\n        // 2.1 更新入库\n        ErpPurchaseInDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseInDO.class)\n                .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId());\n        calculateTotalPrice(updateObj, purchaseInItems);\n        purchaseInMapper.updateById(updateObj);\n        // 2.2 更新入库项\n        updatePurchaseInItemList(updateReqVO.getId(), purchaseInItems);\n\n        // 3.1 更新采购订单的入库数量\n        updatePurchaseOrderInCount(updateObj.getOrderId());\n        // 3.2 注意：如果采购订单编号变更了，需要更新“老”采购订单的入库数量\n        if (ObjectUtil.notEqual(purchaseIn.getOrderId(), updateObj.getOrderId())) {\n            updatePurchaseOrderInCount(purchaseIn.getOrderId());\n        }\n    }\n\n    private void calculateTotalPrice(ErpPurchaseInDO purchaseIn, List<ErpPurchaseInItemDO> purchaseInItems) {\n        purchaseIn.setTotalCount(getSumValue(purchaseInItems, ErpPurchaseInItemDO::getCount, BigDecimal::add));\n        purchaseIn.setTotalProductPrice(getSumValue(purchaseInItems, ErpPurchaseInItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));\n        purchaseIn.setTotalTaxPrice(getSumValue(purchaseInItems, ErpPurchaseInItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));\n        purchaseIn.setTotalPrice(purchaseIn.getTotalProductPrice().add(purchaseIn.getTotalTaxPrice()));\n        // 计算优惠价格\n        if (purchaseIn.getDiscountPercent() == null) {\n            purchaseIn.setDiscountPercent(BigDecimal.ZERO);\n        }\n        purchaseIn.setDiscountPrice(MoneyUtils.priceMultiplyPercent(purchaseIn.getTotalPrice(), purchaseIn.getDiscountPercent()));\n        purchaseIn.setTotalPrice(purchaseIn.getTotalPrice().subtract(purchaseIn.getDiscountPrice()).add(purchaseIn.getOtherPrice()));\n    }\n\n    private void updatePurchaseOrderInCount(Long orderId) {\n        // 1.1 查询采购订单对应的采购入库单列表\n        List<ErpPurchaseInDO> purchaseIns = purchaseInMapper.selectListByOrderId(orderId);\n        // 1.2 查询对应的采购订单项的入库数量\n        Map<Long, BigDecimal> returnCountMap = purchaseInItemMapper.selectOrderItemCountSumMapByInIds(\n                convertList(purchaseIns, ErpPurchaseInDO::getId));\n        // 2. 更新采购订单的入库数量\n        purchaseOrderService.updatePurchaseOrderInCount(orderId, returnCountMap);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePurchaseInStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpPurchaseInDO purchaseIn = validatePurchaseInExists(id);\n        // 1.2 校验状态\n        if (purchaseIn.getStatus().equals(status)) {\n            throw exception(approve ? PURCHASE_IN_APPROVE_FAIL : PURCHASE_IN_PROCESS_FAIL);\n        }\n        // 1.3 校验已付款\n        if (!approve && purchaseIn.getPaymentPrice().compareTo(BigDecimal.ZERO) > 0) {\n            throw exception(PURCHASE_IN_PROCESS_FAIL_EXISTS_PAYMENT);\n        }\n\n        // 2. 更新状态\n        int updateCount = purchaseInMapper.updateByIdAndStatus(id, purchaseIn.getStatus(),\n                new ErpPurchaseInDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? PURCHASE_IN_APPROVE_FAIL : PURCHASE_IN_PROCESS_FAIL);\n        }\n\n        // 3. 变更库存\n        List<ErpPurchaseInItemDO> purchaseInItems = purchaseInItemMapper.selectListByInId(id);\n        Integer bizType = approve ? ErpStockRecordBizTypeEnum.PURCHASE_IN.getType()\n                : ErpStockRecordBizTypeEnum.PURCHASE_IN_CANCEL.getType();\n        purchaseInItems.forEach(purchaseInItem -> {\n            BigDecimal count = approve ? purchaseInItem.getCount() : purchaseInItem.getCount().negate();\n            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(\n                    purchaseInItem.getProductId(), purchaseInItem.getWarehouseId(), count,\n                    bizType, purchaseInItem.getInId(), purchaseInItem.getId(), purchaseIn.getNo()));\n        });\n    }\n\n    @Override\n    public void updatePurchaseInPaymentPrice(Long id, BigDecimal paymentPrice) {\n        ErpPurchaseInDO purchaseIn = purchaseInMapper.selectById(id);\n        if (purchaseIn.getPaymentPrice().equals(paymentPrice)) {\n            return;\n        }\n        if (paymentPrice.compareTo(purchaseIn.getTotalPrice()) > 0) {\n            throw exception(PURCHASE_IN_FAIL_PAYMENT_PRICE_EXCEED, paymentPrice, purchaseIn.getTotalPrice());\n        }\n        purchaseInMapper.updateById(new ErpPurchaseInDO().setId(id).setPaymentPrice(paymentPrice));\n    }\n\n    private List<ErpPurchaseInItemDO> validatePurchaseInItems(List<ErpPurchaseInSaveReqVO.Item> list) {\n        // 1. 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpPurchaseInSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 2. 转化为 ErpPurchaseInItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseInItemDO.class, item -> {\n            item.setProductUnitId(productMap.get(item.getProductId()).getUnitId());\n            item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()));\n            if (item.getTotalPrice() == null) {\n                return;\n            }\n            if (item.getTaxPercent() != null) {\n                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));\n            }\n        }));\n    }\n\n    private void updatePurchaseInItemList(Long id, List<ErpPurchaseInItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpPurchaseInItemDO> oldList = purchaseInItemMapper.selectListByInId(id);\n        List<List<ErpPurchaseInItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setInId(id));\n            purchaseInItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            purchaseInItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            purchaseInItemMapper.deleteByIds(convertList(diffList.get(2), ErpPurchaseInItemDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deletePurchaseIn(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpPurchaseInDO> purchaseIns = purchaseInMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(purchaseIns)) {\n            return;\n        }\n        purchaseIns.forEach(purchaseIn -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseIn.getStatus())) {\n                throw exception(PURCHASE_IN_DELETE_FAIL_APPROVE, purchaseIn.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        purchaseIns.forEach(purchaseIn -> {\n            // 2.1 删除订单\n            purchaseInMapper.deleteById(purchaseIn.getId());\n            // 2.2 删除订单项\n            purchaseInItemMapper.deleteByInId(purchaseIn.getId());\n\n            // 2.3 更新采购订单的入库数量\n            updatePurchaseOrderInCount(purchaseIn.getOrderId());\n        });\n\n    }\n\n    private ErpPurchaseInDO validatePurchaseInExists(Long id) {\n        ErpPurchaseInDO purchaseIn = purchaseInMapper.selectById(id);\n        if (purchaseIn == null) {\n            throw exception(PURCHASE_IN_NOT_EXISTS);\n        }\n        return purchaseIn;\n    }\n\n    @Override\n    public ErpPurchaseInDO getPurchaseIn(Long id) {\n        return purchaseInMapper.selectById(id);\n    }\n\n    @Override\n    public ErpPurchaseInDO validatePurchaseIn(Long id) {\n        ErpPurchaseInDO purchaseIn = validatePurchaseInExists(id);\n        if (ObjectUtil.notEqual(purchaseIn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {\n            throw exception(PURCHASE_IN_NOT_APPROVE);\n        }\n        return purchaseIn;\n    }\n\n    @Override\n    public PageResult<ErpPurchaseInDO> getPurchaseInPage(ErpPurchaseInPageReqVO pageReqVO) {\n        return purchaseInMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 采购入库项 ====================\n\n    @Override\n    public List<ErpPurchaseInItemDO> getPurchaseInItemListByInId(Long inId) {\n        return purchaseInItemMapper.selectListByInId(inId);\n    }\n\n    @Override\n    public List<ErpPurchaseInItemDO> getPurchaseInItemListByInIds(Collection<Long> inIds) {\n        if (CollUtil.isEmpty(inIds)) {\n            return Collections.emptyList();\n        }\n        return purchaseInItemMapper.selectListByInIds(inIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.purchase;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO;\nimport javax.validation.Valid;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * ERP 采购订单 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpPurchaseOrderService {\n\n    /**\n     * 创建采购订单\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createPurchaseOrder(@Valid ErpPurchaseOrderSaveReqVO createReqVO);\n\n    /**\n     * 更新采购订单\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updatePurchaseOrder(@Valid ErpPurchaseOrderSaveReqVO updateReqVO);\n\n    /**\n     * 更新采购订单的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updatePurchaseOrderStatus(Long id, Integer status);\n\n    /**\n     * 更新采购订单的入库数量\n     *\n     * @param id 编号\n     * @param inCountMap 入库数量 Map：key 采购订单项编号；value 入库数量\n     */\n    void updatePurchaseOrderInCount(Long id, Map<Long, BigDecimal> inCountMap);\n\n    /**\n     * 更新采购订单的退货数量\n     *\n     * @param orderId 编号\n     * @param returnCountMap 退货数量 Map：key 采购订单项编号；value 退货数量\n     */\n    void updatePurchaseOrderReturnCount(Long orderId, Map<Long, BigDecimal> returnCountMap);\n\n    /**\n     * 删除采购订单\n     *\n     * @param ids 编号数组\n     */\n    void deletePurchaseOrder(List<Long> ids);\n\n    /**\n     * 获得采购订单\n     *\n     * @param id 编号\n     * @return 采购订单\n     */\n    ErpPurchaseOrderDO getPurchaseOrder(Long id);\n\n    /**\n     * 校验采购订单，已经审核通过\n     *\n     * @param id 编号\n     * @return 采购订单\n     */\n    ErpPurchaseOrderDO validatePurchaseOrder(Long id);\n\n    /**\n     * 获得采购订单分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 采购订单分页\n     */\n    PageResult<ErpPurchaseOrderDO> getPurchaseOrderPage(ErpPurchaseOrderPageReqVO pageReqVO);\n\n    // ==================== 采购订单项 ====================\n\n    /**\n     * 获得采购订单项列表\n     *\n     * @param orderId 采购订单编号\n     * @return 采购订单项列表\n     */\n    List<ErpPurchaseOrderItemDO> getPurchaseOrderItemListByOrderId(Long orderId);\n\n    /**\n     * 获得采购订单项 List\n     *\n     * @param orderIds 采购订单编号数组\n     * @return 采购订单项 List\n     */\n    List<ErpPurchaseOrderItemDO> getPurchaseOrderItemListByOrderIds(Collection<Long> orderIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.purchase;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseOrderItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseOrderMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 采购订单 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpPurchaseOrderServiceImpl implements ErpPurchaseOrderService {\n\n    @Resource\n    private ErpPurchaseOrderMapper purchaseOrderMapper;\n    @Resource\n    private ErpPurchaseOrderItemMapper purchaseOrderItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpSupplierService supplierService;\n    @Resource\n    private ErpAccountService accountService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createPurchaseOrder(ErpPurchaseOrderSaveReqVO createReqVO) {\n        // 1.1 校验订单项的有效性\n        List<ErpPurchaseOrderItemDO> purchaseOrderItems = validatePurchaseOrderItems(createReqVO.getItems());\n        // 1.2 校验供应商\n        supplierService.validateSupplier(createReqVO.getSupplierId());\n        // 1.3 校验结算账户\n        if (createReqVO.getAccountId() != null) {\n            accountService.validateAccount(createReqVO.getAccountId());\n        }\n        // 1.4 生成订单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_ORDER_NO_PREFIX);\n        if (purchaseOrderMapper.selectByNo(no) != null) {\n            throw exception(PURCHASE_ORDER_NO_EXISTS);\n        }\n\n        // 2.1 插入订单\n        ErpPurchaseOrderDO purchaseOrder = BeanUtils.toBean(createReqVO, ErpPurchaseOrderDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()));\n        calculateTotalPrice(purchaseOrder, purchaseOrderItems);\n        purchaseOrderMapper.insert(purchaseOrder);\n        // 2.2 插入订单项\n        purchaseOrderItems.forEach(o -> o.setOrderId(purchaseOrder.getId()));\n        purchaseOrderItemMapper.insertBatch(purchaseOrderItems);\n        return purchaseOrder.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePurchaseOrder(ErpPurchaseOrderSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseOrder.getStatus())) {\n            throw exception(PURCHASE_ORDER_UPDATE_FAIL_APPROVE, purchaseOrder.getNo());\n        }\n        // 1.2 校验供应商\n        supplierService.validateSupplier(updateReqVO.getSupplierId());\n        // 1.3 校验结算账户\n        if (updateReqVO.getAccountId() != null) {\n            accountService.validateAccount(updateReqVO.getAccountId());\n        }\n        // 1.4 校验订单项的有效性\n        List<ErpPurchaseOrderItemDO> purchaseOrderItems = validatePurchaseOrderItems(updateReqVO.getItems());\n\n        // 2.1 更新订单\n        ErpPurchaseOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseOrderDO.class);\n        calculateTotalPrice(updateObj, purchaseOrderItems);\n        purchaseOrderMapper.updateById(updateObj);\n        // 2.2 更新订单项\n        updatePurchaseOrderItemList(updateReqVO.getId(), purchaseOrderItems);\n    }\n\n    private void calculateTotalPrice(ErpPurchaseOrderDO purchaseOrder, List<ErpPurchaseOrderItemDO> purchaseOrderItems) {\n        purchaseOrder.setTotalCount(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getCount, BigDecimal::add));\n        purchaseOrder.setTotalProductPrice(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));\n        purchaseOrder.setTotalTaxPrice(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));\n        purchaseOrder.setTotalPrice(purchaseOrder.getTotalProductPrice().add(purchaseOrder.getTotalTaxPrice()));\n        // 计算优惠价格\n        if (purchaseOrder.getDiscountPercent() == null) {\n            purchaseOrder.setDiscountPercent(BigDecimal.ZERO);\n        }\n        purchaseOrder.setDiscountPrice(MoneyUtils.priceMultiplyPercent(purchaseOrder.getTotalPrice(), purchaseOrder.getDiscountPercent()));\n        purchaseOrder.setTotalPrice(purchaseOrder.getTotalPrice().subtract(purchaseOrder.getDiscountPrice()));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePurchaseOrderStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);\n        // 1.2 校验状态\n        if (purchaseOrder.getStatus().equals(status)) {\n            throw exception(approve ? PURCHASE_ORDER_APPROVE_FAIL : PURCHASE_ORDER_PROCESS_FAIL);\n        }\n        // 1.3 存在采购入单，无法反审核\n        if (!approve && purchaseOrder.getInCount().compareTo(BigDecimal.ZERO) > 0) {\n            throw exception(PURCHASE_ORDER_PROCESS_FAIL_EXISTS_IN);\n        }\n        // 1.4 存在采购退货单，无法反审核\n        if (!approve && purchaseOrder.getReturnCount().compareTo(BigDecimal.ZERO) > 0) {\n            throw exception(PURCHASE_ORDER_PROCESS_FAIL_EXISTS_RETURN);\n        }\n\n        // 2. 更新状态\n        int updateCount = purchaseOrderMapper.updateByIdAndStatus(id, purchaseOrder.getStatus(),\n                new ErpPurchaseOrderDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? PURCHASE_ORDER_APPROVE_FAIL : PURCHASE_ORDER_PROCESS_FAIL);\n        }\n    }\n\n    private List<ErpPurchaseOrderItemDO> validatePurchaseOrderItems(List<ErpPurchaseOrderSaveReqVO.Item> list) {\n        // 1. 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpPurchaseOrderSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 2. 转化为 ErpPurchaseOrderItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseOrderItemDO.class, item -> {\n            item.setProductUnitId(productMap.get(item.getProductId()).getUnitId());\n            item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()));\n            if (item.getTotalPrice() == null) {\n                return;\n            }\n            if (item.getTaxPercent() != null) {\n                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));\n            }\n        }));\n    }\n\n    private void updatePurchaseOrderItemList(Long id, List<ErpPurchaseOrderItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpPurchaseOrderItemDO> oldList = purchaseOrderItemMapper.selectListByOrderId(id);\n        List<List<ErpPurchaseOrderItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setOrderId(id));\n            purchaseOrderItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            purchaseOrderItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            purchaseOrderItemMapper.deleteByIds(convertList(diffList.get(2), ErpPurchaseOrderItemDO::getId));\n        }\n    }\n\n    @Override\n    public void updatePurchaseOrderInCount(Long id, Map<Long, BigDecimal> inCountMap) {\n        List<ErpPurchaseOrderItemDO> orderItems = purchaseOrderItemMapper.selectListByOrderId(id);\n        // 1. 更新每个采购订单项\n        orderItems.forEach(item -> {\n            BigDecimal inCount = inCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);\n            if (item.getInCount().equals(inCount)) {\n                return;\n            }\n            if (inCount.compareTo(item.getCount()) > 0) {\n                throw exception(PURCHASE_ORDER_ITEM_IN_FAIL_PRODUCT_EXCEED,\n                        productService.getProduct(item.getProductId()).getName(), item.getCount());\n            }\n            purchaseOrderItemMapper.updateById(new ErpPurchaseOrderItemDO().setId(item.getId()).setInCount(inCount));\n        });\n        // 2. 更新采购订单\n        BigDecimal totalInCount = getSumValue(inCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);\n        purchaseOrderMapper.updateById(new ErpPurchaseOrderDO().setId(id).setInCount(totalInCount));\n    }\n\n    @Override\n    public void updatePurchaseOrderReturnCount(Long orderId, Map<Long, BigDecimal> returnCountMap) {\n        List<ErpPurchaseOrderItemDO> orderItems = purchaseOrderItemMapper.selectListByOrderId(orderId);\n        // 1. 更新每个采购订单项\n        orderItems.forEach(item -> {\n            BigDecimal returnCount = returnCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);\n            if (item.getReturnCount().equals(returnCount)) {\n                return;\n            }\n            if (returnCount.compareTo(item.getInCount()) > 0) {\n                throw exception(PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED,\n                        productService.getProduct(item.getProductId()).getName(), item.getInCount());\n            }\n            purchaseOrderItemMapper.updateById(new ErpPurchaseOrderItemDO().setId(item.getId()).setReturnCount(returnCount));\n        });\n        // 2. 更新采购订单\n        BigDecimal totalReturnCount = getSumValue(returnCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);\n        purchaseOrderMapper.updateById(new ErpPurchaseOrderDO().setId(orderId).setReturnCount(totalReturnCount));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deletePurchaseOrder(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpPurchaseOrderDO> purchaseOrders = purchaseOrderMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(purchaseOrders)) {\n            return;\n        }\n        purchaseOrders.forEach(purchaseOrder -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseOrder.getStatus())) {\n                throw exception(PURCHASE_ORDER_DELETE_FAIL_APPROVE, purchaseOrder.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        purchaseOrders.forEach(purchaseOrder -> {\n            // 2.1 删除订单\n            purchaseOrderMapper.deleteById(purchaseOrder.getId());\n            // 2.2 删除订单项\n            purchaseOrderItemMapper.deleteByOrderId(purchaseOrder.getId());\n        });\n    }\n\n    private ErpPurchaseOrderDO validatePurchaseOrderExists(Long id) {\n        ErpPurchaseOrderDO purchaseOrder = purchaseOrderMapper.selectById(id);\n        if (purchaseOrder == null) {\n            throw exception(PURCHASE_ORDER_NOT_EXISTS);\n        }\n        return purchaseOrder;\n    }\n\n    @Override\n    public ErpPurchaseOrderDO getPurchaseOrder(Long id) {\n        return purchaseOrderMapper.selectById(id);\n    }\n\n    @Override\n    public ErpPurchaseOrderDO validatePurchaseOrder(Long id) {\n        ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);\n        if (ObjectUtil.notEqual(purchaseOrder.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {\n            throw exception(PURCHASE_ORDER_NOT_APPROVE);\n        }\n        return purchaseOrder;\n    }\n\n    @Override\n    public PageResult<ErpPurchaseOrderDO> getPurchaseOrderPage(ErpPurchaseOrderPageReqVO pageReqVO) {\n        return purchaseOrderMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 订单项 ====================\n\n    @Override\n    public List<ErpPurchaseOrderItemDO> getPurchaseOrderItemListByOrderId(Long orderId) {\n        return purchaseOrderItemMapper.selectListByOrderId(orderId);\n    }\n\n    @Override\n    public List<ErpPurchaseOrderItemDO> getPurchaseOrderItemListByOrderIds(Collection<Long> orderIds) {\n        if (CollUtil.isEmpty(orderIds)) {\n            return Collections.emptyList();\n        }\n        return purchaseOrderItemMapper.selectListByOrderIds(orderIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.purchase;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO;\nimport javax.validation.Valid;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 采购退货 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpPurchaseReturnService {\n\n    /**\n     * 创建采购退货\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createPurchaseReturn(@Valid ErpPurchaseReturnSaveReqVO createReqVO);\n\n    /**\n     * 更新采购退货\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updatePurchaseReturn(@Valid ErpPurchaseReturnSaveReqVO updateReqVO);\n\n    /**\n     * 更新采购退货的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updatePurchaseReturnStatus(Long id, Integer status);\n\n    /**\n     * 更新采购退货的退款金额\n     *\n     * @param id 编号\n     * @param refundPrice 退款金额\n     */\n    void updatePurchaseReturnRefundPrice(Long id, BigDecimal refundPrice);\n\n    /**\n     * 删除采购退货\n     *\n     * @param ids 编号数组\n     */\n    void deletePurchaseReturn(List<Long> ids);\n\n    /**\n     * 获得采购退货\n     *\n     * @param id 编号\n     * @return 采购退货\n     */\n    ErpPurchaseReturnDO getPurchaseReturn(Long id);\n\n    /**\n     * 校验采购退货，已经审核通过\n     *\n     * @param id 编号\n     * @return 采购退货\n     */\n    ErpPurchaseReturnDO validatePurchaseReturn(Long id);\n\n    /**\n     * 获得采购退货分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 采购退货分页\n     */\n    PageResult<ErpPurchaseReturnDO> getPurchaseReturnPage(ErpPurchaseReturnPageReqVO pageReqVO);\n\n    // ==================== 采购退货项 ====================\n\n    /**\n     * 获得采购退货项列表\n     *\n     * @param returnId 采购退货编号\n     * @return 采购退货项列表\n     */\n    List<ErpPurchaseReturnItemDO> getPurchaseReturnItemListByReturnId(Long returnId);\n\n    /**\n     * 获得采购退货项 List\n     *\n     * @param returnIds 采购退货编号数组\n     * @return 采购退货项 List\n     */\n    List<ErpPurchaseReturnItemDO> getPurchaseReturnItemListByReturnIds(Collection<Long> returnIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.purchase;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseReturnItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseReturnMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 采购退货 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpPurchaseReturnServiceImpl implements ErpPurchaseReturnService {\n\n    @Resource\n    private ErpPurchaseReturnMapper purchaseReturnMapper;\n    @Resource\n    private ErpPurchaseReturnItemMapper purchaseReturnItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private ErpPurchaseOrderService purchaseOrderService;\n    @Resource\n    private ErpAccountService accountService;\n    @Resource\n    private ErpStockRecordService stockRecordService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createPurchaseReturn(ErpPurchaseReturnSaveReqVO createReqVO) {\n        // 1.1 校验采购订单已审核\n        ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(createReqVO.getOrderId());\n        // 1.2 校验退货项的有效性\n        List<ErpPurchaseReturnItemDO> purchaseReturnItems = validatePurchaseReturnItems(createReqVO.getItems());\n        // 1.3 校验结算账户\n        accountService.validateAccount(createReqVO.getAccountId());\n        // 1.4 生成退货单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_RETURN_NO_PREFIX);\n        if (purchaseReturnMapper.selectByNo(no) != null) {\n            throw exception(PURCHASE_RETURN_NO_EXISTS);\n        }\n\n        // 2.1 插入退货\n        ErpPurchaseReturnDO purchaseReturn = BeanUtils.toBean(createReqVO, ErpPurchaseReturnDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()))\n                .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId());\n        calculateTotalPrice(purchaseReturn, purchaseReturnItems);\n        purchaseReturnMapper.insert(purchaseReturn);\n        // 2.2 插入退货项\n        purchaseReturnItems.forEach(o -> o.setReturnId(purchaseReturn.getId()));\n        purchaseReturnItemMapper.insertBatch(purchaseReturnItems);\n\n        // 3. 更新采购订单的退货数量\n        updatePurchaseOrderReturnCount(createReqVO.getOrderId());\n        return purchaseReturn.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePurchaseReturn(ErpPurchaseReturnSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpPurchaseReturnDO purchaseReturn = validatePurchaseReturnExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseReturn.getStatus())) {\n            throw exception(PURCHASE_RETURN_UPDATE_FAIL_APPROVE, purchaseReturn.getNo());\n        }\n        // 1.2 校验采购订单已审核\n        ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(updateReqVO.getOrderId());\n        // 1.3 校验结算账户\n        accountService.validateAccount(updateReqVO.getAccountId());\n        // 1.4 校验订单项的有效性\n        List<ErpPurchaseReturnItemDO> purchaseReturnItems = validatePurchaseReturnItems(updateReqVO.getItems());\n\n        // 2.1 更新退货\n        ErpPurchaseReturnDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseReturnDO.class)\n                .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId());\n        calculateTotalPrice(updateObj, purchaseReturnItems);\n        purchaseReturnMapper.updateById(updateObj);\n        // 2.2 更新退货项\n        updatePurchaseReturnItemList(updateReqVO.getId(), purchaseReturnItems);\n\n        // 3.1 更新采购订单的出库数量\n        updatePurchaseOrderReturnCount(updateObj.getOrderId());\n        // 3.2 注意：如果采购订单编号变更了，需要更新“老”采购订单的出库数量\n        if (ObjectUtil.notEqual(purchaseReturn.getOrderId(), updateObj.getOrderId())) {\n            updatePurchaseOrderReturnCount(purchaseReturn.getOrderId());\n        }\n    }\n\n    private void calculateTotalPrice(ErpPurchaseReturnDO purchaseReturn, List<ErpPurchaseReturnItemDO> purchaseReturnItems) {\n        purchaseReturn.setTotalCount(getSumValue(purchaseReturnItems, ErpPurchaseReturnItemDO::getCount, BigDecimal::add));\n        purchaseReturn.setTotalProductPrice(getSumValue(purchaseReturnItems, ErpPurchaseReturnItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));\n        purchaseReturn.setTotalTaxPrice(getSumValue(purchaseReturnItems, ErpPurchaseReturnItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));\n        purchaseReturn.setTotalPrice(purchaseReturn.getTotalProductPrice().add(purchaseReturn.getTotalTaxPrice()));\n        // 计算优惠价格\n        if (purchaseReturn.getDiscountPercent() == null) {\n            purchaseReturn.setDiscountPercent(BigDecimal.ZERO);\n        }\n        purchaseReturn.setDiscountPrice(MoneyUtils.priceMultiplyPercent(purchaseReturn.getTotalPrice(), purchaseReturn.getDiscountPercent()));\n        purchaseReturn.setTotalPrice(purchaseReturn.getTotalPrice().subtract(purchaseReturn.getDiscountPrice()).add(purchaseReturn.getOtherPrice()));\n    }\n\n    private void updatePurchaseOrderReturnCount(Long orderId) {\n        // 1.1 查询采购订单对应的采购出库单列表\n        List<ErpPurchaseReturnDO> purchaseReturns = purchaseReturnMapper.selectListByOrderId(orderId);\n        // 1.2 查询对应的采购订单项的退货数量\n        Map<Long, BigDecimal> returnCountMap = purchaseReturnItemMapper.selectOrderItemCountSumMapByReturnIds(\n                convertList(purchaseReturns, ErpPurchaseReturnDO::getId));\n        // 2. 更新采购订单的出库数量\n        purchaseOrderService.updatePurchaseOrderReturnCount(orderId, returnCountMap);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePurchaseReturnStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpPurchaseReturnDO purchaseReturn = validatePurchaseReturnExists(id);\n        // 1.2 校验状态\n        if (purchaseReturn.getStatus().equals(status)) {\n            throw exception(approve ? PURCHASE_RETURN_APPROVE_FAIL : PURCHASE_RETURN_PROCESS_FAIL);\n        }\n        // 1.3 校验已退款\n        if (!approve && purchaseReturn.getRefundPrice().compareTo(BigDecimal.ZERO) > 0) {\n            throw exception(PURCHASE_RETURN_PROCESS_FAIL_EXISTS_REFUND);\n        }\n\n        // 2. 更新状态\n        int updateCount = purchaseReturnMapper.updateByIdAndStatus(id, purchaseReturn.getStatus(),\n                new ErpPurchaseReturnDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? PURCHASE_RETURN_APPROVE_FAIL : PURCHASE_RETURN_PROCESS_FAIL);\n        }\n\n        // 3. 变更库存\n        List<ErpPurchaseReturnItemDO> purchaseReturnItems = purchaseReturnItemMapper.selectListByReturnId(id);\n        Integer bizType = approve ? ErpStockRecordBizTypeEnum.PURCHASE_RETURN.getType()\n                : ErpStockRecordBizTypeEnum.PURCHASE_RETURN_CANCEL.getType();\n        purchaseReturnItems.forEach(purchaseReturnItem -> {\n            BigDecimal count = approve ? purchaseReturnItem.getCount().negate() : purchaseReturnItem.getCount();\n            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(\n                    purchaseReturnItem.getProductId(), purchaseReturnItem.getWarehouseId(), count,\n                    bizType, purchaseReturnItem.getReturnId(), purchaseReturnItem.getId(), purchaseReturn.getNo()));\n        });\n    }\n\n    @Override\n    public void updatePurchaseReturnRefundPrice(Long id, BigDecimal refundPrice) {\n        ErpPurchaseReturnDO purchaseReturn = purchaseReturnMapper.selectById(id);\n        if (purchaseReturn.getRefundPrice().equals(refundPrice)) {\n            return;\n        }\n        if (refundPrice.compareTo(purchaseReturn.getTotalPrice()) > 0) {\n            throw exception(PURCHASE_RETURN_FAIL_REFUND_PRICE_EXCEED, refundPrice, purchaseReturn.getTotalPrice());\n        }\n        purchaseReturnMapper.updateById(new ErpPurchaseReturnDO().setId(id).setRefundPrice(refundPrice));\n    }\n\n    private List<ErpPurchaseReturnItemDO> validatePurchaseReturnItems(List<ErpPurchaseReturnSaveReqVO.Item> list) {\n        // 1. 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpPurchaseReturnSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 2. 转化为 ErpPurchaseReturnItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseReturnItemDO.class, item -> {\n            item.setProductUnitId(productMap.get(item.getProductId()).getUnitId());\n            item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()));\n            if (item.getTotalPrice() == null) {\n                return;\n            }\n            if (item.getTaxPercent() != null) {\n                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));\n            }\n        }));\n    }\n\n    private void updatePurchaseReturnItemList(Long id, List<ErpPurchaseReturnItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpPurchaseReturnItemDO> oldList = purchaseReturnItemMapper.selectListByReturnId(id);\n        List<List<ErpPurchaseReturnItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setReturnId(id));\n            purchaseReturnItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            purchaseReturnItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            purchaseReturnItemMapper.deleteByIds(convertList(diffList.get(2), ErpPurchaseReturnItemDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deletePurchaseReturn(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpPurchaseReturnDO> purchaseReturns = purchaseReturnMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(purchaseReturns)) {\n            return;\n        }\n        purchaseReturns.forEach(purchaseReturn -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseReturn.getStatus())) {\n                throw exception(PURCHASE_RETURN_DELETE_FAIL_APPROVE, purchaseReturn.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        purchaseReturns.forEach(purchaseReturn -> {\n            // 2.1 删除订单\n            purchaseReturnMapper.deleteById(purchaseReturn.getId());\n            // 2.2 删除订单项\n            purchaseReturnItemMapper.deleteByReturnId(purchaseReturn.getId());\n\n            // 2.3 更新采购订单的出库数量\n            updatePurchaseOrderReturnCount(purchaseReturn.getOrderId());\n        });\n\n    }\n\n    private ErpPurchaseReturnDO validatePurchaseReturnExists(Long id) {\n        ErpPurchaseReturnDO purchaseReturn = purchaseReturnMapper.selectById(id);\n        if (purchaseReturn == null) {\n            throw exception(PURCHASE_RETURN_NOT_EXISTS);\n        }\n        return purchaseReturn;\n    }\n\n    @Override\n    public ErpPurchaseReturnDO getPurchaseReturn(Long id) {\n        return purchaseReturnMapper.selectById(id);\n    }\n\n    @Override\n    public ErpPurchaseReturnDO validatePurchaseReturn(Long id) {\n        ErpPurchaseReturnDO purchaseReturn = getPurchaseReturn(id);\n        if (ObjectUtil.notEqual(purchaseReturn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {\n            throw exception(PURCHASE_RETURN_NOT_APPROVE);\n        }\n        return purchaseReturn;\n    }\n\n    @Override\n    public PageResult<ErpPurchaseReturnDO> getPurchaseReturnPage(ErpPurchaseReturnPageReqVO pageReqVO) {\n        return purchaseReturnMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 采购退货项 ====================\n\n    @Override\n    public List<ErpPurchaseReturnItemDO> getPurchaseReturnItemListByReturnId(Long returnId) {\n        return purchaseReturnItemMapper.selectListByReturnId(returnId);\n    }\n\n    @Override\n    public List<ErpPurchaseReturnItemDO> getPurchaseReturnItemListByReturnIds(Collection<Long> returnIds) {\n        if (CollUtil.isEmpty(returnIds)) {\n            return Collections.emptyList();\n        }\n        return purchaseReturnItemMapper.selectListByReturnIds(returnIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.purchase;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 供应商 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpSupplierService {\n\n    /**\n     * 创建供应商\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createSupplier(@Valid ErpSupplierSaveReqVO createReqVO);\n\n    /**\n     * 更新供应商\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateSupplier(@Valid ErpSupplierSaveReqVO updateReqVO);\n\n    /**\n     * 删除供应商\n     *\n     * @param id 编号\n     */\n    void deleteSupplier(Long id);\n\n    /**\n     * 获得供应商\n     *\n     * @param id 编号\n     * @return 供应商\n     */\n    ErpSupplierDO getSupplier(Long id);\n\n    /**\n     * 校验供应商\n     *\n     * @param id 编号\n     * @return 供应商\n     */\n    ErpSupplierDO validateSupplier(Long id);\n\n    /**\n     * 获得供应商列表\n     *\n     * @param ids 编号列表\n     * @return 供应商列表\n     */\n    List<ErpSupplierDO> getSupplierList(Collection<Long> ids);\n\n    /**\n     * 获得供应商 Map\n     *\n     * @param ids 编号列表\n     * @return 供应商 Map\n     */\n    default Map<Long, ErpSupplierDO> getSupplierMap(Collection<Long> ids) {\n        return convertMap(getSupplierList(ids), ErpSupplierDO::getId);\n    }\n\n    /**\n     * 获得供应商分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 供应商分页\n     */\n    PageResult<ErpSupplierDO> getSupplierPage(ErpSupplierPageReqVO pageReqVO);\n\n    /**\n     * 获得指定状态的供应商列表\n     *\n     * @param status 状态\n     * @return 供应商列表\n     */\n    List<ErpSupplierDO> getSupplierListByStatus(Integer status);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.purchase;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpSupplierMapper;\nimport javax.annotation.Resource;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n/**\n * ERP 供应商 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpSupplierServiceImpl implements ErpSupplierService {\n\n    @Resource\n    private ErpSupplierMapper supplierMapper;\n\n    @Override\n    public Long createSupplier(ErpSupplierSaveReqVO createReqVO) {\n        ErpSupplierDO supplier = BeanUtils.toBean(createReqVO, ErpSupplierDO.class);\n        supplierMapper.insert(supplier);\n        return supplier.getId();\n    }\n\n    @Override\n    public void updateSupplier(ErpSupplierSaveReqVO updateReqVO) {\n        // 校验存在\n        validateSupplierExists(updateReqVO.getId());\n        // 更新\n        ErpSupplierDO updateObj = BeanUtils.toBean(updateReqVO, ErpSupplierDO.class);\n        supplierMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteSupplier(Long id) {\n        // 校验存在\n        validateSupplierExists(id);\n        // 删除\n        supplierMapper.deleteById(id);\n    }\n\n    private void validateSupplierExists(Long id) {\n        if (supplierMapper.selectById(id) == null) {\n            throw exception(SUPPLIER_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ErpSupplierDO getSupplier(Long id) {\n        return supplierMapper.selectById(id);\n    }\n\n    @Override\n    public ErpSupplierDO validateSupplier(Long id) {\n        ErpSupplierDO supplier = supplierMapper.selectById(id);\n        if (supplier == null) {\n            throw exception(SUPPLIER_NOT_EXISTS);\n        }\n        if (CommonStatusEnum.isDisable(supplier.getStatus())) {\n            throw exception(SUPPLIER_NOT_ENABLE, supplier.getName());\n        }\n        return supplier;\n    }\n\n    @Override\n    public List<ErpSupplierDO> getSupplierList(Collection<Long> ids) {\n        return supplierMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<ErpSupplierDO> getSupplierPage(ErpSupplierPageReqVO pageReqVO) {\n        return supplierMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<ErpSupplierDO> getSupplierListByStatus(Integer status) {\n        return supplierMapper.selectListByStatus(status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.sale;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 客户 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpCustomerService {\n\n    /**\n     * 创建客户\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createCustomer(@Valid ErpCustomerSaveReqVO createReqVO);\n\n    /**\n     * 更新客户\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCustomer(@Valid ErpCustomerSaveReqVO updateReqVO);\n\n    /**\n     * 删除客户\n     *\n     * @param id 编号\n     */\n    void deleteCustomer(Long id);\n\n    /**\n     * 获得客户\n     *\n     * @param id 编号\n     * @return 客户\n     */\n    ErpCustomerDO getCustomer(Long id);\n\n    /**\n     * 校验客户\n     *\n     * @param id 编号\n     * @return 客户\n     */\n    ErpCustomerDO validateCustomer(Long id);\n\n    /**\n     * 获得客户列表\n     *\n     * @param ids 编号列表\n     * @return 客户列表\n     */\n    List<ErpCustomerDO> getCustomerList(Collection<Long> ids);\n\n    /**\n     * 获得客户 Map\n     *\n     * @param ids 编号列表\n     * @return 客户 Map\n     */\n    default Map<Long, ErpCustomerDO> getCustomerMap(Collection<Long> ids) {\n        return convertMap(getCustomerList(ids), ErpCustomerDO::getId);\n    }\n\n    /**\n     * 获得客户分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 客户分页\n     */\n    PageResult<ErpCustomerDO> getCustomerPage(ErpCustomerPageReqVO pageReqVO);\n\n    /**\n     * 获得指定状态的客户列表\n     *\n     * @param status 状态\n     * @return 客户列表\n     */\n    List<ErpCustomerDO> getCustomerListByStatus(Integer status);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.sale;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpCustomerMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.CUSTOMER_NOT_ENABLE;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS;\n\n/**\n * ERP 客户 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpCustomerServiceImpl implements ErpCustomerService {\n\n    @Resource\n    private ErpCustomerMapper customerMapper;\n\n    @Override\n    public Long createCustomer(ErpCustomerSaveReqVO createReqVO) {\n        // 插入\n        ErpCustomerDO customer = BeanUtils.toBean(createReqVO, ErpCustomerDO.class);\n        customerMapper.insert(customer);\n        // 返回\n        return customer.getId();\n    }\n\n    @Override\n    public void updateCustomer(ErpCustomerSaveReqVO updateReqVO) {\n        // 校验存在\n        validateCustomerExists(updateReqVO.getId());\n        // 更新\n        ErpCustomerDO updateObj = BeanUtils.toBean(updateReqVO, ErpCustomerDO.class);\n        customerMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteCustomer(Long id) {\n        // 校验存在\n        validateCustomerExists(id);\n        // 删除\n        customerMapper.deleteById(id);\n    }\n\n    private void validateCustomerExists(Long id) {\n        if (customerMapper.selectById(id) == null) {\n            throw exception(CUSTOMER_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ErpCustomerDO getCustomer(Long id) {\n        return customerMapper.selectById(id);\n    }\n\n    @Override\n    public ErpCustomerDO validateCustomer(Long id) {\n        ErpCustomerDO customer = customerMapper.selectById(id);\n        if (customer == null) {\n            throw exception(CUSTOMER_NOT_EXISTS);\n        }\n        if (CommonStatusEnum.isDisable(customer.getStatus())) {\n            throw exception(CUSTOMER_NOT_ENABLE, customer.getName());\n        }\n        return customer;\n    }\n\n    @Override\n    public List<ErpCustomerDO> getCustomerList(Collection<Long> ids) {\n        return customerMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<ErpCustomerDO> getCustomerPage(ErpCustomerPageReqVO pageReqVO) {\n        return customerMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<ErpCustomerDO> getCustomerListByStatus(Integer status) {\n        return customerMapper.selectListByStatus(status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.sale;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;\nimport javax.validation.Valid;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * ERP 销售订单 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpSaleOrderService {\n\n    /**\n     * 创建销售订单\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createSaleOrder(@Valid ErpSaleOrderSaveReqVO createReqVO);\n\n    /**\n     * 更新销售订单\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateSaleOrder(@Valid ErpSaleOrderSaveReqVO updateReqVO);\n\n    /**\n     * 更新销售订单的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateSaleOrderStatus(Long id, Integer status);\n\n    /**\n     * 更新销售订单的出库数量\n     *\n     * @param id 编号\n     * @param outCountMap 出库数量 Map：key 销售订单项编号；value 出库数量\n     */\n    void updateSaleOrderOutCount(Long id, Map<Long, BigDecimal> outCountMap);\n\n    /**\n     * 更新销售订单的退货数量\n     *\n     * @param orderId 编号\n     * @param returnCountMap 退货数量 Map：key 销售订单项编号；value 退货数量\n     */\n    void updateSaleOrderReturnCount(Long orderId, Map<Long, BigDecimal> returnCountMap);\n\n    /**\n     * 删除销售订单\n     *\n     * @param ids 编号数组\n     */\n    void deleteSaleOrder(List<Long> ids);\n\n    /**\n     * 获得销售订单\n     *\n     * @param id 编号\n     * @return 销售订单\n     */\n    ErpSaleOrderDO getSaleOrder(Long id);\n\n    /**\n     * 校验销售订单，已经审核通过\n     *\n     * @param id 编号\n     * @return 销售订单\n     */\n    ErpSaleOrderDO validateSaleOrder(Long id);\n\n    /**\n     * 获得销售订单分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 销售订单分页\n     */\n    PageResult<ErpSaleOrderDO> getSaleOrderPage(ErpSaleOrderPageReqVO pageReqVO);\n\n    // ==================== 销售订单项 ====================\n\n    /**\n     * 获得销售订单项列表\n     *\n     * @param orderId 销售订单编号\n     * @return 销售订单项列表\n     */\n    List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderId(Long orderId);\n\n    /**\n     * 获得销售订单项 List\n     *\n     * @param orderIds 销售订单编号数组\n     * @return 销售订单项 List\n     */\n    List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderIds(Collection<Long> orderIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.sale;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 销售订单 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpSaleOrderServiceImpl implements ErpSaleOrderService {\n\n    @Resource\n    private ErpSaleOrderMapper saleOrderMapper;\n    @Resource\n    private ErpSaleOrderItemMapper saleOrderItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpCustomerService customerService;\n    @Resource\n    private ErpAccountService accountService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createSaleOrder(ErpSaleOrderSaveReqVO createReqVO) {\n        // 1.1 校验订单项的有效性\n        List<ErpSaleOrderItemDO> saleOrderItems = validateSaleOrderItems(createReqVO.getItems());\n        // 1.2 校验客户\n        customerService.validateCustomer(createReqVO.getCustomerId());\n        // 1.3 校验结算账户\n        if (createReqVO.getAccountId() != null) {\n            accountService.validateAccount(createReqVO.getAccountId());\n        }\n        // 1.4 校验销售人员\n        if (createReqVO.getSaleUserId() != null) {\n            adminUserApi.validateUser(createReqVO.getSaleUserId());\n        }\n        // 1.5 生成订单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_ORDER_NO_PREFIX);\n        if (saleOrderMapper.selectByNo(no) != null) {\n            throw exception(SALE_ORDER_NO_EXISTS);\n        }\n\n        // 2.1 插入订单\n        ErpSaleOrderDO saleOrder = BeanUtils.toBean(createReqVO, ErpSaleOrderDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()));\n        calculateTotalPrice(saleOrder, saleOrderItems);\n        saleOrderMapper.insert(saleOrder);\n        // 2.2 插入订单项\n        saleOrderItems.forEach(o -> o.setOrderId(saleOrder.getId()));\n        saleOrderItemMapper.insertBatch(saleOrderItems);\n        return saleOrder.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSaleOrder(ErpSaleOrderSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpSaleOrderDO saleOrder = validateSaleOrderExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) {\n            throw exception(SALE_ORDER_UPDATE_FAIL_APPROVE, saleOrder.getNo());\n        }\n        // 1.2 校验客户\n        customerService.validateCustomer(updateReqVO.getCustomerId());\n        // 1.3 校验结算账户\n        if (updateReqVO.getAccountId() != null) {\n            accountService.validateAccount(updateReqVO.getAccountId());\n        }\n        // 1.4 校验销售人员\n        if (updateReqVO.getSaleUserId() != null) {\n            adminUserApi.validateUser(updateReqVO.getSaleUserId());\n        }\n        // 1.5 校验订单项的有效性\n        List<ErpSaleOrderItemDO> saleOrderItems = validateSaleOrderItems(updateReqVO.getItems());\n\n        // 2.1 更新订单\n        ErpSaleOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleOrderDO.class);\n        calculateTotalPrice(updateObj, saleOrderItems);\n        saleOrderMapper.updateById(updateObj);\n        // 2.2 更新订单项\n        updateSaleOrderItemList(updateReqVO.getId(), saleOrderItems);\n    }\n\n    private void calculateTotalPrice(ErpSaleOrderDO saleOrder, List<ErpSaleOrderItemDO> saleOrderItems) {\n        saleOrder.setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, BigDecimal::add));\n        saleOrder.setTotalProductPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));\n        saleOrder.setTotalTaxPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));\n        saleOrder.setTotalPrice(saleOrder.getTotalProductPrice().add(saleOrder.getTotalTaxPrice()));\n        // 计算优惠价格\n        if (saleOrder.getDiscountPercent() == null) {\n            saleOrder.setDiscountPercent(BigDecimal.ZERO);\n        }\n        saleOrder.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleOrder.getTotalPrice(), saleOrder.getDiscountPercent()));\n        saleOrder.setTotalPrice(saleOrder.getTotalPrice().subtract(saleOrder.getDiscountPrice()));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSaleOrderStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);\n        // 1.2 校验状态\n        if (saleOrder.getStatus().equals(status)) {\n            throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL);\n        }\n        // 1.3 存在销售出库单，无法反审核\n        if (!approve && saleOrder.getOutCount().compareTo(BigDecimal.ZERO) > 0) {\n            throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_OUT);\n        }\n        // 1.4 存在销售退货单，无法反审核\n        if (!approve && saleOrder.getReturnCount().compareTo(BigDecimal.ZERO) > 0) {\n            throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN);\n        }\n\n        // 2. 更新状态\n        int updateCount = saleOrderMapper.updateByIdAndStatus(id, saleOrder.getStatus(),\n                new ErpSaleOrderDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL);\n        }\n    }\n\n    private List<ErpSaleOrderItemDO> validateSaleOrderItems(List<ErpSaleOrderSaveReqVO.Item> list) {\n        // 1. 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpSaleOrderSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 2. 转化为 ErpSaleOrderItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpSaleOrderItemDO.class, item -> {\n            item.setProductUnitId(productMap.get(item.getProductId()).getUnitId());\n            item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()));\n            if (item.getTotalPrice() == null) {\n                return;\n            }\n            if (item.getTaxPercent() != null) {\n                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));\n            }\n        }));\n    }\n\n    private void updateSaleOrderItemList(Long id, List<ErpSaleOrderItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpSaleOrderItemDO> oldList = saleOrderItemMapper.selectListByOrderId(id);\n        List<List<ErpSaleOrderItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setOrderId(id));\n            saleOrderItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            saleOrderItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            saleOrderItemMapper.deleteByIds(convertList(diffList.get(2), ErpSaleOrderItemDO::getId));\n        }\n    }\n\n    @Override\n    public void updateSaleOrderOutCount(Long id, Map<Long, BigDecimal> outCountMap) {\n        List<ErpSaleOrderItemDO> orderItems = saleOrderItemMapper.selectListByOrderId(id);\n        // 1. 更新每个销售订单项\n        orderItems.forEach(item -> {\n            BigDecimal outCount = outCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);\n            if (item.getOutCount().equals(outCount)) {\n                return;\n            }\n            if (outCount.compareTo(item.getCount()) > 0) {\n                throw exception(SALE_ORDER_ITEM_OUT_FAIL_PRODUCT_EXCEED,\n                        productService.getProduct(item.getProductId()).getName(), item.getCount());\n            }\n            saleOrderItemMapper.updateById(new ErpSaleOrderItemDO().setId(item.getId()).setOutCount(outCount));\n        });\n        // 2. 更新销售订单\n        BigDecimal totalOutCount = getSumValue(outCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);\n        saleOrderMapper.updateById(new ErpSaleOrderDO().setId(id).setOutCount(totalOutCount));\n    }\n\n    @Override\n    public void updateSaleOrderReturnCount(Long orderId, Map<Long, BigDecimal> returnCountMap) {\n        List<ErpSaleOrderItemDO> orderItems = saleOrderItemMapper.selectListByOrderId(orderId);\n        // 1. 更新每个销售订单项\n        orderItems.forEach(item -> {\n            BigDecimal returnCount = returnCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);\n            if (item.getReturnCount().equals(returnCount)) {\n                return;\n            }\n            if (returnCount.compareTo(item.getOutCount()) > 0) {\n                throw exception(SALE_ORDER_ITEM_RETURN_FAIL_OUT_EXCEED,\n                        productService.getProduct(item.getProductId()).getName(), item.getOutCount());\n            }\n            saleOrderItemMapper.updateById(new ErpSaleOrderItemDO().setId(item.getId()).setReturnCount(returnCount));\n        });\n        // 2. 更新销售订单\n        BigDecimal totalReturnCount = getSumValue(returnCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);\n        saleOrderMapper.updateById(new ErpSaleOrderDO().setId(orderId).setReturnCount(totalReturnCount));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteSaleOrder(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpSaleOrderDO> saleOrders = saleOrderMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(saleOrders)) {\n            return;\n        }\n        saleOrders.forEach(saleOrder -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) {\n                throw exception(SALE_ORDER_DELETE_FAIL_APPROVE, saleOrder.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        saleOrders.forEach(saleOrder -> {\n            // 2.1 删除订单\n            saleOrderMapper.deleteById(saleOrder.getId());\n            // 2.2 删除订单项\n            saleOrderItemMapper.deleteByOrderId(saleOrder.getId());\n        });\n    }\n\n    private ErpSaleOrderDO validateSaleOrderExists(Long id) {\n        ErpSaleOrderDO saleOrder = saleOrderMapper.selectById(id);\n        if (saleOrder == null) {\n            throw exception(SALE_ORDER_NOT_EXISTS);\n        }\n        return saleOrder;\n    }\n\n    @Override\n    public ErpSaleOrderDO getSaleOrder(Long id) {\n        return saleOrderMapper.selectById(id);\n    }\n\n    @Override\n    public ErpSaleOrderDO validateSaleOrder(Long id) {\n        ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);\n        if (ObjectUtil.notEqual(saleOrder.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {\n            throw exception(SALE_ORDER_NOT_APPROVE);\n        }\n        return saleOrder;\n    }\n\n    @Override\n    public PageResult<ErpSaleOrderDO> getSaleOrderPage(ErpSaleOrderPageReqVO pageReqVO) {\n        return saleOrderMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 订单项 ====================\n\n    @Override\n    public List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderId(Long orderId) {\n        return saleOrderItemMapper.selectListByOrderId(orderId);\n    }\n\n    @Override\n    public List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderIds(Collection<Long> orderIds) {\n        if (CollUtil.isEmpty(orderIds)) {\n            return Collections.emptyList();\n        }\n        return saleOrderItemMapper.selectListByOrderIds(orderIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.sale;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO;\nimport javax.validation.Valid;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 销售出库 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpSaleOutService {\n\n    /**\n     * 创建销售出库\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createSaleOut(@Valid ErpSaleOutSaveReqVO createReqVO);\n\n    /**\n     * 更新销售出库\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateSaleOut(@Valid ErpSaleOutSaveReqVO updateReqVO);\n\n    /**\n     * 更新销售出库的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateSaleOutStatus(Long id, Integer status);\n\n    /**\n     * 更新销售出库的收款金额\n     *\n     * @param id 编号\n     * @param receiptPrice 收款金额\n     */\n    void updateSaleInReceiptPrice(Long id, BigDecimal receiptPrice);\n\n    /**\n     * 删除销售出库\n     *\n     * @param ids 编号数组\n     */\n    void deleteSaleOut(List<Long> ids);\n\n    /**\n     * 获得销售出库\n     *\n     * @param id 编号\n     * @return 销售出库\n     */\n    ErpSaleOutDO getSaleOut(Long id);\n\n    /**\n     * 校验销售出库，已经审核通过\n     *\n     * @param id 编号\n     * @return 销售出库\n     */\n    ErpSaleOutDO validateSaleOut(Long id);\n\n    /**\n     * 获得销售出库分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 销售出库分页\n     */\n    PageResult<ErpSaleOutDO> getSaleOutPage(ErpSaleOutPageReqVO pageReqVO);\n\n    // ==================== 销售出库项 ====================\n\n    /**\n     * 获得销售出库项列表\n     *\n     * @param outId 销售出库编号\n     * @return 销售出库项列表\n     */\n    List<ErpSaleOutItemDO> getSaleOutItemListByOutId(Long outId);\n\n    /**\n     * 获得销售出库项 List\n     *\n     * @param outIds 销售出库编号数组\n     * @return 销售出库项 List\n     */\n    List<ErpSaleOutItemDO> getSaleOutItemListByOutIds(Collection<Long> outIds);\n\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.sale;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOutItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOutMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 销售出库 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpSaleOutServiceImpl implements ErpSaleOutService {\n\n    @Resource\n    private ErpSaleOutMapper saleOutMapper;\n    @Resource\n    private ErpSaleOutItemMapper saleOutItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private ErpSaleOrderService saleOrderService;\n    @Resource\n    private ErpAccountService accountService;\n    @Resource\n    private ErpStockRecordService stockRecordService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createSaleOut(ErpSaleOutSaveReqVO createReqVO) {\n        // 1.1 校验销售订单已审核\n        ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(createReqVO.getOrderId());\n        // 1.2 校验出库项的有效性\n        List<ErpSaleOutItemDO> saleOutItems = validateSaleOutItems(createReqVO.getItems());\n        // 1.3 校验结算账户\n        accountService.validateAccount(createReqVO.getAccountId());\n        // 1.4 校验销售人员\n        if (createReqVO.getSaleUserId() != null) {\n            adminUserApi.validateUser(createReqVO.getSaleUserId());\n        }\n        // 1.5 生成出库单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_OUT_NO_PREFIX);\n        if (saleOutMapper.selectByNo(no) != null) {\n            throw exception(SALE_OUT_NO_EXISTS);\n        }\n\n        // 2.1 插入出库\n        ErpSaleOutDO saleOut = BeanUtils.toBean(createReqVO, ErpSaleOutDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()))\n                .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId());\n        calculateTotalPrice(saleOut, saleOutItems);\n        saleOutMapper.insert(saleOut);\n        // 2.2 插入出库项\n        saleOutItems.forEach(o -> o.setOutId(saleOut.getId()));\n        saleOutItemMapper.insertBatch(saleOutItems);\n\n        // 3. 更新销售订单的出库数量\n        updateSaleOrderOutCount(createReqVO.getOrderId());\n        return saleOut.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSaleOut(ErpSaleOutSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpSaleOutDO saleOut = validateSaleOutExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(saleOut.getStatus())) {\n            throw exception(SALE_OUT_UPDATE_FAIL_APPROVE, saleOut.getNo());\n        }\n        // 1.2 校验销售订单已审核\n        ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(updateReqVO.getOrderId());\n        // 1.3 校验结算账户\n        accountService.validateAccount(updateReqVO.getAccountId());\n        // 1.4 校验销售人员\n        if (updateReqVO.getSaleUserId() != null) {\n            adminUserApi.validateUser(updateReqVO.getSaleUserId());\n        }\n        // 1.5 校验订单项的有效性\n        List<ErpSaleOutItemDO> saleOutItems = validateSaleOutItems(updateReqVO.getItems());\n\n        // 2.1 更新出库\n        ErpSaleOutDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleOutDO.class)\n                .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId());\n        calculateTotalPrice(updateObj, saleOutItems);\n        saleOutMapper.updateById(updateObj);\n        // 2.2 更新出库项\n        updateSaleOutItemList(updateReqVO.getId(), saleOutItems);\n\n        // 3.1 更新销售订单的出库数量\n        updateSaleOrderOutCount(updateObj.getOrderId());\n        // 3.2 注意：如果销售订单编号变更了，需要更新“老”销售订单的出库数量\n        if (ObjectUtil.notEqual(saleOut.getOrderId(), updateObj.getOrderId())) {\n            updateSaleOrderOutCount(saleOut.getOrderId());\n        }\n    }\n\n    private void calculateTotalPrice(ErpSaleOutDO saleOut, List<ErpSaleOutItemDO> saleOutItems) {\n        saleOut.setTotalCount(getSumValue(saleOutItems, ErpSaleOutItemDO::getCount, BigDecimal::add));\n        saleOut.setTotalProductPrice(getSumValue(saleOutItems, ErpSaleOutItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));\n        saleOut.setTotalTaxPrice(getSumValue(saleOutItems, ErpSaleOutItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));\n        saleOut.setTotalPrice(saleOut.getTotalProductPrice().add(saleOut.getTotalTaxPrice()));\n        // 计算优惠价格\n        if (saleOut.getDiscountPercent() == null) {\n            saleOut.setDiscountPercent(BigDecimal.ZERO);\n        }\n        saleOut.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleOut.getTotalPrice(), saleOut.getDiscountPercent()));\n        saleOut.setTotalPrice(saleOut.getTotalPrice().subtract(saleOut.getDiscountPrice().add(saleOut.getOtherPrice())));\n    }\n\n    private void updateSaleOrderOutCount(Long orderId) {\n        // 1.1 查询销售订单对应的销售出库单列表\n        List<ErpSaleOutDO> saleOuts = saleOutMapper.selectListByOrderId(orderId);\n        // 1.2 查询对应的销售订单项的出库数量\n        Map<Long, BigDecimal> returnCountMap = saleOutItemMapper.selectOrderItemCountSumMapByOutIds(\n                convertList(saleOuts, ErpSaleOutDO::getId));\n        // 2. 更新销售订单的出库数量\n        saleOrderService.updateSaleOrderOutCount(orderId, returnCountMap);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSaleOutStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpSaleOutDO saleOut = validateSaleOutExists(id);\n        // 1.2 校验状态\n        if (saleOut.getStatus().equals(status)) {\n            throw exception(approve ? SALE_OUT_APPROVE_FAIL : SALE_OUT_PROCESS_FAIL);\n        }\n        // 1.3 校验已退款\n        if (!approve && saleOut.getReceiptPrice().compareTo(BigDecimal.ZERO) > 0) {\n            throw exception(SALE_OUT_PROCESS_FAIL_EXISTS_RECEIPT);\n        }\n\n        // 2. 更新状态\n        int updateCount = saleOutMapper.updateByIdAndStatus(id, saleOut.getStatus(),\n                new ErpSaleOutDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? SALE_OUT_APPROVE_FAIL : SALE_OUT_PROCESS_FAIL);\n        }\n\n        // 3. 变更库存\n        List<ErpSaleOutItemDO> saleOutItems = saleOutItemMapper.selectListByOutId(id);\n        Integer bizType = approve ? ErpStockRecordBizTypeEnum.SALE_OUT.getType()\n                : ErpStockRecordBizTypeEnum.SALE_OUT_CANCEL.getType();\n        saleOutItems.forEach(saleOutItem -> {\n            BigDecimal count = approve ? saleOutItem.getCount().negate() : saleOutItem.getCount();\n            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(\n                    saleOutItem.getProductId(), saleOutItem.getWarehouseId(), count,\n                    bizType, saleOutItem.getOutId(), saleOutItem.getId(), saleOut.getNo()));\n        });\n    }\n\n    @Override\n    public void updateSaleInReceiptPrice(Long id, BigDecimal receiptPrice) {\n        ErpSaleOutDO saleOut = saleOutMapper.selectById(id);\n        if (saleOut.getReceiptPrice().equals(receiptPrice)) {\n            return;\n        }\n        if (receiptPrice.compareTo(saleOut.getTotalPrice()) > 0) {\n            throw exception(SALE_OUT_FAIL_RECEIPT_PRICE_EXCEED, receiptPrice,  saleOut.getTotalPrice());\n        }\n        saleOutMapper.updateById(new ErpSaleOutDO().setId(id).setReceiptPrice(receiptPrice));\n    }\n\n    private List<ErpSaleOutItemDO> validateSaleOutItems(List<ErpSaleOutSaveReqVO.Item> list) {\n        // 1. 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpSaleOutSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 2. 转化为 ErpSaleOutItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpSaleOutItemDO.class, item -> {\n            item.setProductUnitId(productMap.get(item.getProductId()).getUnitId());\n            item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()));\n            if (item.getTotalPrice() == null) {\n                return;\n            }\n            if (item.getTaxPercent() != null) {\n                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));\n            }\n        }));\n    }\n\n    private void updateSaleOutItemList(Long id, List<ErpSaleOutItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpSaleOutItemDO> oldList = saleOutItemMapper.selectListByOutId(id);\n        List<List<ErpSaleOutItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setOutId(id));\n            saleOutItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            saleOutItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            saleOutItemMapper.deleteByIds(convertList(diffList.get(2), ErpSaleOutItemDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteSaleOut(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpSaleOutDO> saleOuts = saleOutMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(saleOuts)) {\n            return;\n        }\n        saleOuts.forEach(saleOut -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(saleOut.getStatus())) {\n                throw exception(SALE_OUT_DELETE_FAIL_APPROVE, saleOut.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        saleOuts.forEach(saleOut -> {\n            // 2.1 删除订单\n            saleOutMapper.deleteById(saleOut.getId());\n            // 2.2 删除订单项\n            saleOutItemMapper.deleteByOutId(saleOut.getId());\n\n            // 2.3 更新销售订单的出库数量\n            updateSaleOrderOutCount(saleOut.getOrderId());\n        });\n\n    }\n\n    private ErpSaleOutDO validateSaleOutExists(Long id) {\n        ErpSaleOutDO saleOut = saleOutMapper.selectById(id);\n        if (saleOut == null) {\n            throw exception(SALE_OUT_NOT_EXISTS);\n        }\n        return saleOut;\n    }\n\n    @Override\n    public ErpSaleOutDO getSaleOut(Long id) {\n        return saleOutMapper.selectById(id);\n    }\n\n    @Override\n    public ErpSaleOutDO validateSaleOut(Long id) {\n        ErpSaleOutDO saleOut = validateSaleOutExists(id);\n        if (ObjectUtil.notEqual(saleOut.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {\n            throw exception(SALE_OUT_NOT_APPROVE);\n        }\n        return saleOut;\n    }\n\n    @Override\n    public PageResult<ErpSaleOutDO> getSaleOutPage(ErpSaleOutPageReqVO pageReqVO) {\n        return saleOutMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 销售出库项 ====================\n\n    @Override\n    public List<ErpSaleOutItemDO> getSaleOutItemListByOutId(Long outId) {\n        return saleOutItemMapper.selectListByOutId(outId);\n    }\n\n    @Override\n    public List<ErpSaleOutItemDO> getSaleOutItemListByOutIds(Collection<Long> outIds) {\n        if (CollUtil.isEmpty(outIds)) {\n            return Collections.emptyList();\n        }\n        return saleOutItemMapper.selectListByOutIds(outIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.sale;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO;\nimport javax.validation.Valid;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 销售退货 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpSaleReturnService {\n\n    /**\n     * 创建销售退货\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createSaleReturn(@Valid ErpSaleReturnSaveReqVO createReqVO);\n\n    /**\n     * 更新销售退货\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateSaleReturn(@Valid ErpSaleReturnSaveReqVO updateReqVO);\n\n    /**\n     * 更新销售退货的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateSaleReturnStatus(Long id, Integer status);\n\n    /**\n     * 更新销售退货的退款金额\n     *\n     * @param id 编号\n     * @param refundPrice 退款金额\n     */\n    void updateSaleReturnRefundPrice(Long id, BigDecimal refundPrice);\n\n    /**\n     * 删除销售退货\n     *\n     * @param ids 编号数组\n     */\n    void deleteSaleReturn(List<Long> ids);\n\n    /**\n     * 获得销售退货\n     *\n     * @param id 编号\n     * @return 销售退货\n     */\n    ErpSaleReturnDO getSaleReturn(Long id);\n\n    /**\n     * 校验销售退货，已经审核通过\n     *\n     * @param id 编号\n     * @return 销售退货\n     */\n    ErpSaleReturnDO validateSaleReturn(Long id);\n\n    /**\n     * 获得销售退货分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 销售退货分页\n     */\n    PageResult<ErpSaleReturnDO> getSaleReturnPage(ErpSaleReturnPageReqVO pageReqVO);\n\n    // ==================== 销售退货项 ====================\n\n    /**\n     * 获得销售退货项列表\n     *\n     * @param returnId 销售退货编号\n     * @return 销售退货项列表\n     */\n    List<ErpSaleReturnItemDO> getSaleReturnItemListByReturnId(Long returnId);\n\n    /**\n     * 获得销售退货项 List\n     *\n     * @param returnIds 销售退货编号数组\n     * @return 销售退货项 List\n     */\n    List<ErpSaleReturnItemDO> getSaleReturnItemListByReturnIds(Collection<Long> returnIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.sale;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleReturnItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleReturnMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 销售退货 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpSaleReturnServiceImpl implements ErpSaleReturnService {\n\n    @Resource\n    private ErpSaleReturnMapper saleReturnMapper;\n    @Resource\n    private ErpSaleReturnItemMapper saleReturnItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private ErpSaleOrderService saleOrderService;\n    @Resource\n    private ErpAccountService accountService;\n    @Resource\n    private ErpStockRecordService stockRecordService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createSaleReturn(ErpSaleReturnSaveReqVO createReqVO) {\n        // 1.1 校验销售订单已审核\n        ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(createReqVO.getOrderId());\n        // 1.2 校验退货项的有效性\n        List<ErpSaleReturnItemDO> saleReturnItems = validateSaleReturnItems(createReqVO.getItems());\n        // 1.3 校验结算账户\n        accountService.validateAccount(createReqVO.getAccountId());\n        // 1.4 校验销售人员\n        if (createReqVO.getSaleUserId() != null) {\n            adminUserApi.validateUser(createReqVO.getSaleUserId());\n        }\n        // 1.5 生成退货单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_RETURN_NO_PREFIX);\n        if (saleReturnMapper.selectByNo(no) != null) {\n            throw exception(SALE_RETURN_NO_EXISTS);\n        }\n\n        // 2.1 插入退货\n        ErpSaleReturnDO saleReturn = BeanUtils.toBean(createReqVO, ErpSaleReturnDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()))\n                .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId());\n        calculateTotalPrice(saleReturn, saleReturnItems);\n        saleReturnMapper.insert(saleReturn);\n        // 2.2 插入退货项\n        saleReturnItems.forEach(o -> o.setReturnId(saleReturn.getId()));\n        saleReturnItemMapper.insertBatch(saleReturnItems);\n\n        // 3. 更新销售订单的退货数量\n        updateSaleOrderReturnCount(createReqVO.getOrderId());\n        return saleReturn.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSaleReturn(ErpSaleReturnSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpSaleReturnDO saleReturn = validateSaleReturnExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(saleReturn.getStatus())) {\n            throw exception(SALE_RETURN_UPDATE_FAIL_APPROVE, saleReturn.getNo());\n        }\n        // 1.2 校验销售订单已审核\n        ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(updateReqVO.getOrderId());\n        // 1.3 校验结算账户\n        accountService.validateAccount(updateReqVO.getAccountId());\n        // 1.4 校验销售人员\n        if (updateReqVO.getSaleUserId() != null) {\n            adminUserApi.validateUser(updateReqVO.getSaleUserId());\n        }\n        // 1.5 校验订单项的有效性\n        List<ErpSaleReturnItemDO> saleReturnItems = validateSaleReturnItems(updateReqVO.getItems());\n\n        // 2.1 更新退货\n        ErpSaleReturnDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleReturnDO.class)\n                .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId());\n        calculateTotalPrice(updateObj, saleReturnItems);\n        saleReturnMapper.updateById(updateObj);\n        // 2.2 更新退货项\n        updateSaleReturnItemList(updateReqVO.getId(), saleReturnItems);\n\n        // 3.1 更新销售订单的出库数量\n        updateSaleOrderReturnCount(updateObj.getOrderId());\n        // 3.2 注意：如果销售订单编号变更了，需要更新“老”销售订单的出库数量\n        if (ObjectUtil.notEqual(saleReturn.getOrderId(), updateObj.getOrderId())) {\n            updateSaleOrderReturnCount(saleReturn.getOrderId());\n        }\n    }\n\n    private void calculateTotalPrice(ErpSaleReturnDO saleReturn, List<ErpSaleReturnItemDO> saleReturnItems) {\n        saleReturn.setTotalCount(getSumValue(saleReturnItems, ErpSaleReturnItemDO::getCount, BigDecimal::add));\n        saleReturn.setTotalProductPrice(getSumValue(saleReturnItems, ErpSaleReturnItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));\n        saleReturn.setTotalTaxPrice(getSumValue(saleReturnItems, ErpSaleReturnItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));\n        saleReturn.setTotalPrice(saleReturn.getTotalProductPrice().add(saleReturn.getTotalTaxPrice()));\n        // 计算优惠价格\n        if (saleReturn.getDiscountPercent() == null) {\n            saleReturn.setDiscountPercent(BigDecimal.ZERO);\n        }\n        saleReturn.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleReturn.getTotalPrice(), saleReturn.getDiscountPercent()));\n        saleReturn.setTotalPrice(saleReturn.getTotalPrice().subtract(saleReturn.getDiscountPrice().add(saleReturn.getOtherPrice())));\n    }\n\n    private void updateSaleOrderReturnCount(Long orderId) {\n        // 1.1 查询销售订单对应的销售出库单列表\n        List<ErpSaleReturnDO> saleReturns = saleReturnMapper.selectListByOrderId(orderId);\n        // 1.2 查询对应的销售订单项的退货数量\n        Map<Long, BigDecimal> returnCountMap = saleReturnItemMapper.selectOrderItemCountSumMapByReturnIds(\n                convertList(saleReturns, ErpSaleReturnDO::getId));\n        // 2. 更新销售订单的出库数量\n        saleOrderService.updateSaleOrderReturnCount(orderId, returnCountMap);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSaleReturnStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpSaleReturnDO saleReturn = validateSaleReturnExists(id);\n        // 1.2 校验状态\n        if (saleReturn.getStatus().equals(status)) {\n            throw exception(approve ? SALE_RETURN_APPROVE_FAIL : SALE_RETURN_PROCESS_FAIL);\n        }\n        // 1.3 校验已退款\n        if (!approve && saleReturn.getRefundPrice().compareTo(BigDecimal.ZERO) > 0) {\n            throw exception(SALE_RETURN_PROCESS_FAIL_EXISTS_REFUND);\n        }\n\n        // 2. 更新状态\n        int updateCount = saleReturnMapper.updateByIdAndStatus(id, saleReturn.getStatus(),\n                new ErpSaleReturnDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? SALE_RETURN_APPROVE_FAIL : SALE_RETURN_PROCESS_FAIL);\n        }\n\n        // 3. 变更库存\n        List<ErpSaleReturnItemDO> saleReturnItems = saleReturnItemMapper.selectListByReturnId(id);\n        Integer bizType = approve ? ErpStockRecordBizTypeEnum.SALE_RETURN.getType()\n                : ErpStockRecordBizTypeEnum.SALE_RETURN_CANCEL.getType();\n        saleReturnItems.forEach(saleReturnItem -> {\n            BigDecimal count = approve ? saleReturnItem.getCount() : saleReturnItem.getCount().negate();\n            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(\n                    saleReturnItem.getProductId(), saleReturnItem.getWarehouseId(), count,\n                    bizType, saleReturnItem.getReturnId(), saleReturnItem.getId(), saleReturn.getNo()));\n        });\n    }\n\n    @Override\n    public void updateSaleReturnRefundPrice(Long id, BigDecimal refundPrice) {\n        ErpSaleReturnDO saleReturn = saleReturnMapper.selectById(id);\n        if (saleReturn.getRefundPrice().equals(refundPrice)) {\n            return;\n        }\n        if (refundPrice.compareTo(saleReturn.getTotalPrice()) > 0) {\n            throw exception(SALE_RETURN_FAIL_REFUND_PRICE_EXCEED, refundPrice, saleReturn.getTotalPrice());\n        }\n        saleReturnMapper.updateById(new ErpSaleReturnDO().setId(id).setRefundPrice(refundPrice));\n    }\n\n    private List<ErpSaleReturnItemDO> validateSaleReturnItems(List<ErpSaleReturnSaveReqVO.Item> list) {\n        // 1. 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpSaleReturnSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 2. 转化为 ErpSaleReturnItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpSaleReturnItemDO.class, item -> {\n            item.setProductUnitId(productMap.get(item.getProductId()).getUnitId());\n            item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()));\n            if (item.getTotalPrice() == null) {\n                return;\n            }\n            if (item.getTaxPercent() != null) {\n                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));\n            }\n        }));\n    }\n\n    private void updateSaleReturnItemList(Long id, List<ErpSaleReturnItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpSaleReturnItemDO> oldList = saleReturnItemMapper.selectListByReturnId(id);\n        List<List<ErpSaleReturnItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setReturnId(id));\n            saleReturnItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            saleReturnItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            saleReturnItemMapper.deleteByIds(convertList(diffList.get(2), ErpSaleReturnItemDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteSaleReturn(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpSaleReturnDO> saleReturns = saleReturnMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(saleReturns)) {\n            return;\n        }\n        saleReturns.forEach(saleReturn -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(saleReturn.getStatus())) {\n                throw exception(SALE_RETURN_DELETE_FAIL_APPROVE, saleReturn.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        saleReturns.forEach(saleReturn -> {\n            // 2.1 删除订单\n            saleReturnMapper.deleteById(saleReturn.getId());\n            // 2.2 删除订单项\n            saleReturnItemMapper.deleteByReturnId(saleReturn.getId());\n\n            // 2.3 更新销售订单的出库数量\n            updateSaleOrderReturnCount(saleReturn.getOrderId());\n        });\n\n    }\n\n    private ErpSaleReturnDO validateSaleReturnExists(Long id) {\n        ErpSaleReturnDO saleReturn = saleReturnMapper.selectById(id);\n        if (saleReturn == null) {\n            throw exception(SALE_RETURN_NOT_EXISTS);\n        }\n        return saleReturn;\n    }\n\n    @Override\n    public ErpSaleReturnDO getSaleReturn(Long id) {\n        return saleReturnMapper.selectById(id);\n    }\n\n    @Override\n    public ErpSaleReturnDO validateSaleReturn(Long id) {\n        ErpSaleReturnDO saleReturn = validateSaleReturnExists(id);\n        if (ObjectUtil.notEqual(saleReturn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {\n            throw exception(SALE_RETURN_NOT_APPROVE);\n        }\n        return saleReturn;\n    }\n\n    @Override\n    public PageResult<ErpSaleReturnDO> getSaleReturnPage(ErpSaleReturnPageReqVO pageReqVO) {\n        return saleReturnMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 销售退货项 ====================\n\n    @Override\n    public List<ErpSaleReturnItemDO> getSaleReturnItemListByReturnId(Long returnId) {\n        return saleReturnItemMapper.selectListByReturnId(returnId);\n    }\n\n    @Override\n    public List<ErpSaleReturnItemDO> getSaleReturnItemListByReturnIds(Collection<Long> returnIds) {\n        if (CollUtil.isEmpty(returnIds)) {\n            return Collections.emptyList();\n        }\n        return saleReturnItemMapper.selectListByReturnIds(returnIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/statistics/ErpPurchaseStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.statistics;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 采购统计 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpPurchaseStatisticsService {\n\n    /**\n     * 获得采购金额\n     *\n     * 计算逻辑：采购出库的金额 - 采购退货的金额\n     *\n     * @param beginTime >= 开始时间\n     * @param endTime < 结束时间\n     * @return 采购金额\n     */\n    BigDecimal getPurchasePrice(LocalDateTime beginTime, LocalDateTime endTime);\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/statistics/ErpPurchaseStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.statistics;\n\nimport cn.iocoder.yudao.module.erp.dal.mysql.statistics.ErpPurchaseStatisticsMapper;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 采购统计 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\npublic class ErpPurchaseStatisticsServiceImpl implements ErpPurchaseStatisticsService {\n\n    @Resource\n    private ErpPurchaseStatisticsMapper purchaseStatisticsMapper;\n\n    @Override\n    public BigDecimal getPurchasePrice(LocalDateTime beginTime, LocalDateTime endTime) {\n        return purchaseStatisticsMapper.getPurchasePrice(beginTime, endTime);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/statistics/ErpSaleStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.statistics;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 销售统计 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpSaleStatisticsService {\n\n    /**\n     * 获得销售金额\n     *\n     * 计算逻辑：销售出库的金额 - 销售退货的金额\n     *\n     * @param beginTime >= 开始时间\n     * @param endTime < 结束时间\n     * @return 销售金额\n     */\n    BigDecimal getSalePrice(LocalDateTime beginTime, LocalDateTime endTime);\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/statistics/ErpSaleStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.statistics;\n\nimport cn.iocoder.yudao.module.erp.dal.mysql.statistics.ErpSaleStatisticsMapper;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n/**\n * ERP 销售统计 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\npublic class ErpSaleStatisticsServiceImpl implements ErpSaleStatisticsService {\n\n    @Resource\n    private ErpSaleStatisticsMapper saleStatisticsMapper;\n\n    @Override\n    public BigDecimal getSalePrice(LocalDateTime beginTime, LocalDateTime endTime) {\n        return saleStatisticsMapper.getSalePrice(beginTime, endTime);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 库存盘点单 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpStockCheckService {\n\n    /**\n     * 创建库存盘点单\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStockCheck(@Valid ErpStockCheckSaveReqVO createReqVO);\n\n    /**\n     * 更新库存盘点单\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStockCheck(@Valid ErpStockCheckSaveReqVO updateReqVO);\n\n    /**\n     * 更新库存盘点单的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateStockCheckStatus(Long id, Integer status);\n\n    /**\n     * 删除库存盘点单\n     *\n     * @param ids 编号数组\n     */\n    void deleteStockCheck(List<Long> ids);\n\n    /**\n     * 获得库存盘点单\n     *\n     * @param id 编号\n     * @return 库存盘点单\n     */\n    ErpStockCheckDO getStockCheck(Long id);\n\n    /**\n     * 获得库存盘点单分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 库存盘点单分页\n     */\n    PageResult<ErpStockCheckDO> getStockCheckPage(ErpStockCheckPageReqVO pageReqVO);\n\n    // ==================== 盘点项 ====================\n\n    /**\n     * 获得库存盘点单项列表\n     *\n     * @param checkId 盘点编号\n     * @return 库存盘点单项列表\n     */\n    List<ErpStockCheckItemDO> getStockCheckItemListByCheckId(Long checkId);\n\n    /**\n     * 获得库存盘点单项 List\n     *\n     * @param checkIds 盘点编号数组\n     * @return 库存盘点单项 List\n     */\n    List<ErpStockCheckItemDO> getStockCheckItemListByCheckIds(Collection<Long> checkIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockCheckItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockCheckMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport javax.annotation.Resource;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 库存盘点单 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpStockCheckServiceImpl implements ErpStockCheckService {\n\n    @Resource\n    private ErpStockCheckMapper stockCheckMapper;\n    @Resource\n    private ErpStockCheckItemMapper stockCheckItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpWarehouseService warehouseService;\n    @Resource\n    private ErpStockRecordService stockRecordService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createStockCheck(ErpStockCheckSaveReqVO createReqVO) {\n        // 1.1 校验盘点项的有效性\n        List<ErpStockCheckItemDO> stockCheckItems = validateStockCheckItems(createReqVO.getItems());\n        // 1.2 生成盘点单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_CHECK_NO_PREFIX);\n        if (stockCheckMapper.selectByNo(no) != null) {\n            throw exception(STOCK_CHECK_NO_EXISTS);\n        }\n\n        // 2.1 插入盘点单\n        ErpStockCheckDO stockCheck = BeanUtils.toBean(createReqVO, ErpStockCheckDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())\n                .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add))\n                .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)));\n        stockCheckMapper.insert(stockCheck);\n        // 2.2 插入盘点单项\n        stockCheckItems.forEach(o -> o.setCheckId(stockCheck.getId()));\n        stockCheckItemMapper.insertBatch(stockCheckItems);\n        return stockCheck.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStockCheck(ErpStockCheckSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpStockCheckDO stockCheck = validateStockCheckExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) {\n            throw exception(STOCK_CHECK_UPDATE_FAIL_APPROVE, stockCheck.getNo());\n        }\n        // 1.2 校验盘点项的有效性\n        List<ErpStockCheckItemDO> stockCheckItems = validateStockCheckItems(updateReqVO.getItems());\n\n        // 2.1 更新盘点单\n        ErpStockCheckDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockCheckDO.class, in -> in\n                .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add))\n                .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add)));\n        stockCheckMapper.updateById(updateObj);\n        // 2.2 更新盘点单项\n        updateStockCheckItemList(updateReqVO.getId(), stockCheckItems);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStockCheckStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpStockCheckDO stockCheck = validateStockCheckExists(id);\n        // 1.2 校验状态\n        if (stockCheck.getStatus().equals(status)) {\n            throw exception(approve ? STOCK_CHECK_APPROVE_FAIL : STOCK_CHECK_PROCESS_FAIL);\n        }\n\n        // 2. 更新状态\n        int updateCount = stockCheckMapper.updateByIdAndStatus(id, stockCheck.getStatus(),\n                new ErpStockCheckDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? STOCK_CHECK_APPROVE_FAIL : STOCK_CHECK_PROCESS_FAIL);\n        }\n\n        // 3. 变更库存\n        List<ErpStockCheckItemDO> stockCheckItems = stockCheckItemMapper.selectListByCheckId(id);\n        stockCheckItems.forEach(stockCheckItem -> {\n            // 没有盈亏，不用出入库\n            if (stockCheckItem.getCount().compareTo(BigDecimal.ZERO) == 0) {\n                return;\n            }\n            // 10；12；-2（）\n            BigDecimal count = approve ? stockCheckItem.getCount(): stockCheckItem.getCount().negate();\n            Integer bizType;\n            if (approve) {\n                bizType = count.compareTo(BigDecimal.ZERO) > 0 ? ErpStockRecordBizTypeEnum.CHECK_MORE_IN.getType()\n                        : ErpStockRecordBizTypeEnum.CHECK_LESS_OUT.getType();\n            } else {\n                bizType = count.compareTo(BigDecimal.ZERO) > 0 ? ErpStockRecordBizTypeEnum.CHECK_MORE_IN_CANCEL.getType()\n                        : ErpStockRecordBizTypeEnum.CHECK_LESS_OUT_CANCEL.getType();\n            }\n            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(\n                    stockCheckItem.getProductId(), stockCheckItem.getWarehouseId(), count,\n                    bizType, stockCheckItem.getCheckId(), stockCheckItem.getId(), stockCheck.getNo()));\n        });\n    }\n\n    private List<ErpStockCheckItemDO> validateStockCheckItems(List<ErpStockCheckSaveReqVO.Item> list) {\n        // 1.1 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpStockCheckSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 1.2 校验仓库存在\n        warehouseService.validWarehouseList(convertSet(list, ErpStockCheckSaveReqVO.Item::getWarehouseId));\n        // 2. 转化为 ErpStockCheckItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpStockCheckItemDO.class, item -> item\n                .setProductUnitId(productMap.get(item.getProductId()).getUnitId())\n                .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()))));\n    }\n\n    private void updateStockCheckItemList(Long id, List<ErpStockCheckItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpStockCheckItemDO> oldList = stockCheckItemMapper.selectListByCheckId(id);\n        List<List<ErpStockCheckItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setCheckId(id));\n            stockCheckItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            stockCheckItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            stockCheckItemMapper.deleteByIds(convertList(diffList.get(2), ErpStockCheckItemDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStockCheck(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpStockCheckDO> stockChecks = stockCheckMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(stockChecks)) {\n            return;\n        }\n        stockChecks.forEach(stockCheck -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) {\n                throw exception(STOCK_CHECK_DELETE_FAIL_APPROVE, stockCheck.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        stockChecks.forEach(stockCheck -> {\n            // 2.1 删除盘点单\n            stockCheckMapper.deleteById(stockCheck.getId());\n            // 2.2 删除盘点单项\n            stockCheckItemMapper.deleteByCheckId(stockCheck.getId());\n        });\n    }\n\n    private ErpStockCheckDO validateStockCheckExists(Long id) {\n        ErpStockCheckDO stockCheck = stockCheckMapper.selectById(id);\n        if (stockCheck == null) {\n            throw exception(STOCK_CHECK_NOT_EXISTS);\n        }\n        return stockCheck;\n    }\n\n    @Override\n    public ErpStockCheckDO getStockCheck(Long id) {\n        return stockCheckMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ErpStockCheckDO> getStockCheckPage(ErpStockCheckPageReqVO pageReqVO) {\n        return stockCheckMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 盘点项 ====================\n\n    @Override\n    public List<ErpStockCheckItemDO> getStockCheckItemListByCheckId(Long checkId) {\n        return stockCheckItemMapper.selectListByCheckId(checkId);\n    }\n\n    @Override\n    public List<ErpStockCheckItemDO> getStockCheckItemListByCheckIds(Collection<Long> checkIds) {\n        if (CollUtil.isEmpty(checkIds)) {\n            return Collections.emptyList();\n        }\n        return stockCheckItemMapper.selectListByCheckIds(checkIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 其它入库单 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpStockInService {\n\n    /**\n     * 创建其它入库单\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStockIn(@Valid ErpStockInSaveReqVO createReqVO);\n\n    /**\n     * 更新其它入库单\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStockIn(@Valid ErpStockInSaveReqVO updateReqVO);\n\n    /**\n     * 更新其它入库单的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateStockInStatus(Long id, Integer status);\n\n    /**\n     * 删除其它入库单\n     *\n     * @param ids 编号数组\n     */\n    void deleteStockIn(List<Long> ids);\n\n    /**\n     * 获得其它入库单\n     *\n     * @param id 编号\n     * @return 其它入库单\n     */\n    ErpStockInDO getStockIn(Long id);\n\n    /**\n     * 获得其它入库单分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 其它入库单分页\n     */\n    PageResult<ErpStockInDO> getStockInPage(ErpStockInPageReqVO pageReqVO);\n\n    // ==================== 入库项 ====================\n\n    /**\n     * 获得其它入库单项列表\n     *\n     * @param inId 入库编号\n     * @return 其它入库单项列表\n     */\n    List<ErpStockInItemDO> getStockInItemListByInId(Long inId);\n\n    /**\n     * 获得其它入库单项 List\n     *\n     * @param inIds 入库编号数组\n     * @return 其它入库单项 List\n     */\n    List<ErpStockInItemDO> getStockInItemListByInIds(Collection<Long> inIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport javax.annotation.Resource;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n/**\n * ERP 其它入库单 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpStockInServiceImpl implements ErpStockInService {\n\n    @Resource\n    private ErpStockInMapper stockInMapper;\n    @Resource\n    private ErpStockInItemMapper stockInItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpWarehouseService warehouseService;\n    @Resource\n    private ErpSupplierService supplierService;\n    @Resource\n    private ErpStockRecordService stockRecordService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createStockIn(ErpStockInSaveReqVO createReqVO) {\n        // 1.1 校验入库项的有效性\n        List<ErpStockInItemDO> stockInItems = validateStockInItems(createReqVO.getItems());\n        // 1.2 校验供应商\n        supplierService.validateSupplier(createReqVO.getSupplierId());\n        // 1.3 生成入库单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_IN_NO_PREFIX);\n        if (stockInMapper.selectByNo(no) != null) {\n            throw exception(STOCK_IN_NO_EXISTS);\n        }\n\n        // 2.1 插入入库单\n        ErpStockInDO stockIn = BeanUtils.toBean(createReqVO, ErpStockInDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())\n                .setTotalCount(getSumValue(stockInItems, ErpStockInItemDO::getCount, BigDecimal::add))\n                .setTotalPrice(getSumValue(stockInItems, ErpStockInItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)));\n        stockInMapper.insert(stockIn);\n        // 2.2 插入入库单项\n        stockInItems.forEach(o -> o.setInId(stockIn.getId()));\n        stockInItemMapper.insertBatch(stockInItems);\n        return stockIn.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStockIn(ErpStockInSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpStockInDO stockIn = validateStockInExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(stockIn.getStatus())) {\n            throw exception(STOCK_IN_UPDATE_FAIL_APPROVE, stockIn.getNo());\n        }\n        // 1.2 校验供应商\n        supplierService.validateSupplier(updateReqVO.getSupplierId());\n        // 1.3 校验入库项的有效性\n        List<ErpStockInItemDO> stockInItems = validateStockInItems(updateReqVO.getItems());\n\n        // 2.1 更新入库单\n        ErpStockInDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockInDO.class, in -> in\n                .setTotalCount(getSumValue(stockInItems, ErpStockInItemDO::getCount, BigDecimal::add))\n                .setTotalPrice(getSumValue(stockInItems, ErpStockInItemDO::getTotalPrice, BigDecimal::add)));\n        stockInMapper.updateById(updateObj);\n        // 2.2 更新入库单项\n        updateStockInItemList(updateReqVO.getId(), stockInItems);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStockInStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpStockInDO stockIn = validateStockInExists(id);\n        // 1.2 校验状态\n        if (stockIn.getStatus().equals(status)) {\n            throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL);\n        }\n\n        // 2. 更新状态\n        int updateCount = stockInMapper.updateByIdAndStatus(id, stockIn.getStatus(),\n                new ErpStockInDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL);\n        }\n\n        // 3. 变更库存\n        List<ErpStockInItemDO> stockInItems = stockInItemMapper.selectListByInId(id);\n        Integer bizType = approve ? ErpStockRecordBizTypeEnum.OTHER_IN.getType()\n                : ErpStockRecordBizTypeEnum.OTHER_IN_CANCEL.getType();\n        stockInItems.forEach(stockInItem -> {\n            BigDecimal count = approve ? stockInItem.getCount() : stockInItem.getCount().negate();\n            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(\n                    stockInItem.getProductId(), stockInItem.getWarehouseId(), count,\n                    bizType, stockInItem.getInId(), stockInItem.getId(), stockIn.getNo()));\n        });\n    }\n\n    private List<ErpStockInItemDO> validateStockInItems(List<ErpStockInSaveReqVO.Item> list) {\n        // 1.1 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpStockInSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 1.2 校验仓库存在\n        warehouseService.validWarehouseList(convertSet(\n                list, ErpStockInSaveReqVO.Item::getWarehouseId));\n        // 2. 转化为 ErpStockInItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpStockInItemDO.class, item -> item\n                .setProductUnitId(productMap.get(item.getProductId()).getUnitId())\n                .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()))));\n    }\n\n    private void updateStockInItemList(Long id, List<ErpStockInItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpStockInItemDO> oldList = stockInItemMapper.selectListByInId(id);\n        List<List<ErpStockInItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setInId(id));\n            stockInItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            stockInItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            stockInItemMapper.deleteByIds(convertList(diffList.get(2), ErpStockInItemDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStockIn(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpStockInDO> stockIns = stockInMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(stockIns)) {\n            return;\n        }\n        stockIns.forEach(stockIn -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(stockIn.getStatus())) {\n                throw exception(STOCK_IN_DELETE_FAIL_APPROVE, stockIn.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        stockIns.forEach(stockIn -> {\n            // 2.1 删除入库单\n            stockInMapper.deleteById(stockIn.getId());\n            // 2.2 删除入库单项\n            stockInItemMapper.deleteByInId(stockIn.getId());\n        });\n    }\n\n    private ErpStockInDO validateStockInExists(Long id) {\n        ErpStockInDO stockIn = stockInMapper.selectById(id);\n        if (stockIn == null) {\n            throw exception(STOCK_IN_NOT_EXISTS);\n        }\n        return stockIn;\n    }\n\n    @Override\n    public ErpStockInDO getStockIn(Long id) {\n        return stockInMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ErpStockInDO> getStockInPage(ErpStockInPageReqVO pageReqVO) {\n        return stockInMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 入库项 ====================\n\n    @Override\n    public List<ErpStockInItemDO> getStockInItemListByInId(Long inId) {\n        return stockInItemMapper.selectListByInId(inId);\n    }\n\n    @Override\n    public List<ErpStockInItemDO> getStockInItemListByInIds(Collection<Long> inIds) {\n        if (CollUtil.isEmpty(inIds)) {\n            return Collections.emptyList();\n        }\n        return stockInItemMapper.selectListByInIds(inIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMovePageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMoveSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 库存调拨单 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpStockMoveService {\n\n    /**\n     * 创建库存调拨单\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStockMove(@Valid ErpStockMoveSaveReqVO createReqVO);\n\n    /**\n     * 更新库存调拨单\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStockMove(@Valid ErpStockMoveSaveReqVO updateReqVO);\n\n    /**\n     * 更新库存调拨单的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateStockMoveStatus(Long id, Integer status);\n\n    /**\n     * 删除库存调拨单\n     *\n     * @param ids 编号数组\n     */\n    void deleteStockMove(List<Long> ids);\n\n    /**\n     * 获得库存调拨单\n     *\n     * @param id 编号\n     * @return 库存调拨单\n     */\n    ErpStockMoveDO getStockMove(Long id);\n\n    /**\n     * 获得库存调拨单分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 库存调拨单分页\n     */\n    PageResult<ErpStockMoveDO> getStockMovePage(ErpStockMovePageReqVO pageReqVO);\n\n    // ==================== 调拨项 ====================\n\n    /**\n     * 获得库存调拨单项列表\n     *\n     * @param moveId 调拨编号\n     * @return 库存调拨单项列表\n     */\n    List<ErpStockMoveItemDO> getStockMoveItemListByMoveId(Long moveId);\n\n    /**\n     * 获得库存调拨单项 List\n     *\n     * @param moveIds 调拨编号数组\n     * @return 库存调拨单项 List\n     */\n    List<ErpStockMoveItemDO> getStockMoveItemListByMoveIds(Collection<Long> moveIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMovePageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMoveSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMoveItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMoveMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 库存调拨单 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpStockMoveServiceImpl implements ErpStockMoveService {\n\n    @Resource\n    private ErpStockMoveMapper stockMoveMapper;\n    @Resource\n    private ErpStockMoveItemMapper stockMoveItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpWarehouseService warehouseService;\n    @Resource\n    private ErpStockRecordService stockRecordService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createStockMove(ErpStockMoveSaveReqVO createReqVO) {\n        // 1.1 校验出库项的有效性\n        List<ErpStockMoveItemDO> stockMoveItems = validateStockMoveItems(createReqVO.getItems());\n        // 1.2 生成调拨单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_MOVE_NO_PREFIX);\n        if (stockMoveMapper.selectByNo(no) != null) {\n            throw exception(STOCK_MOVE_NO_EXISTS);\n        }\n\n        // 2.1 插入出库单\n        ErpStockMoveDO stockMove = BeanUtils.toBean(createReqVO, ErpStockMoveDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())\n                .setTotalCount(getSumValue(stockMoveItems, ErpStockMoveItemDO::getCount, BigDecimal::add))\n                .setTotalPrice(getSumValue(stockMoveItems, ErpStockMoveItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)));\n        stockMoveMapper.insert(stockMove);\n        // 2.2 插入出库单项\n        stockMoveItems.forEach(o -> o.setMoveId(stockMove.getId()));\n        stockMoveItemMapper.insertBatch(stockMoveItems);\n        return stockMove.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStockMove(ErpStockMoveSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpStockMoveDO stockMove = validateStockMoveExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(stockMove.getStatus())) {\n            throw exception(STOCK_MOVE_UPDATE_FAIL_APPROVE, stockMove.getNo());\n        }\n        // 1.2 校验出库项的有效性\n        List<ErpStockMoveItemDO> stockMoveItems = validateStockMoveItems(updateReqVO.getItems());\n\n        // 2.1 更新出库单\n        ErpStockMoveDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockMoveDO.class, in -> in\n                .setTotalCount(getSumValue(stockMoveItems, ErpStockMoveItemDO::getCount, BigDecimal::add))\n                .setTotalPrice(getSumValue(stockMoveItems, ErpStockMoveItemDO::getTotalPrice, BigDecimal::add)));\n        stockMoveMapper.updateById(updateObj);\n        // 2.2 更新出库单项\n        updateStockMoveItemList(updateReqVO.getId(), stockMoveItems);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStockMoveStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpStockMoveDO stockMove = validateStockMoveExists(id);\n        // 1.2 校验状态\n        if (stockMove.getStatus().equals(status)) {\n            throw exception(approve ? STOCK_MOVE_APPROVE_FAIL : STOCK_MOVE_PROCESS_FAIL);\n        }\n\n        // 2. 更新状态\n        int updateCount = stockMoveMapper.updateByIdAndStatus(id, stockMove.getStatus(),\n                new ErpStockMoveDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? STOCK_MOVE_APPROVE_FAIL : STOCK_MOVE_PROCESS_FAIL);\n        }\n\n        // 3. 变更库存\n        List<ErpStockMoveItemDO> stockMoveItems = stockMoveItemMapper.selectListByMoveId(id);\n        Integer fromBizType = approve ? ErpStockRecordBizTypeEnum.MOVE_OUT.getType()\n                : ErpStockRecordBizTypeEnum.MOVE_OUT_CANCEL.getType();\n        Integer toBizType = approve ? ErpStockRecordBizTypeEnum.MOVE_IN.getType()\n                : ErpStockRecordBizTypeEnum.MOVE_IN_CANCEL.getType();\n        stockMoveItems.forEach(stockMoveItem -> {\n            BigDecimal fromCount = approve ? stockMoveItem.getCount().negate() : stockMoveItem.getCount();\n            BigDecimal toCount = approve ? stockMoveItem.getCount() : stockMoveItem.getCount().negate();\n            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(\n                    stockMoveItem.getProductId(), stockMoveItem.getFromWarehouseId(), fromCount,\n                    fromBizType, stockMoveItem.getMoveId(), stockMoveItem.getId(), stockMove.getNo()));\n            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(\n                    stockMoveItem.getProductId(), stockMoveItem.getToWarehouseId(), toCount,\n                    toBizType, stockMoveItem.getMoveId(), stockMoveItem.getId(), stockMove.getNo()));\n        });\n    }\n\n    private List<ErpStockMoveItemDO> validateStockMoveItems(List<ErpStockMoveSaveReqVO.Item> list) {\n        // 1.1 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpStockMoveSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 1.2 校验仓库存在\n        warehouseService.validWarehouseList(convertSetByFlatMap(list,\n                item -> Stream.of(item.getFromWarehouseId(),  item.getToWarehouseId())));\n        // 2. 转化为 ErpStockMoveItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpStockMoveItemDO.class, item -> item\n                .setProductUnitId(productMap.get(item.getProductId()).getUnitId())\n                .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()))));\n    }\n\n    private void updateStockMoveItemList(Long id, List<ErpStockMoveItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpStockMoveItemDO> oldList = stockMoveItemMapper.selectListByMoveId(id);\n        List<List<ErpStockMoveItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setMoveId(id));\n            stockMoveItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            stockMoveItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            stockMoveItemMapper.deleteByIds(convertList(diffList.get(2), ErpStockMoveItemDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStockMove(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpStockMoveDO> stockMoves = stockMoveMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(stockMoves)) {\n            return;\n        }\n        stockMoves.forEach(stockMove -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(stockMove.getStatus())) {\n                throw exception(STOCK_MOVE_DELETE_FAIL_APPROVE, stockMove.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        stockMoves.forEach(stockMove -> {\n            // 2.1 删除出库单\n            stockMoveMapper.deleteById(stockMove.getId());\n            // 2.2 删除出库单项\n            stockMoveItemMapper.deleteByMoveId(stockMove.getId());\n        });\n    }\n\n    private ErpStockMoveDO validateStockMoveExists(Long id) {\n        ErpStockMoveDO stockMove = stockMoveMapper.selectById(id);\n        if (stockMove == null) {\n            throw exception(STOCK_MOVE_NOT_EXISTS);\n        }\n        return stockMove;\n    }\n\n    @Override\n    public ErpStockMoveDO getStockMove(Long id) {\n        return stockMoveMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ErpStockMoveDO> getStockMovePage(ErpStockMovePageReqVO pageReqVO) {\n        return stockMoveMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 出库项 ====================\n\n    @Override\n    public List<ErpStockMoveItemDO> getStockMoveItemListByMoveId(Long moveId) {\n        return stockMoveItemMapper.selectListByMoveId(moveId);\n    }\n\n    @Override\n    public List<ErpStockMoveItemDO> getStockMoveItemListByMoveIds(Collection<Long> moveIds) {\n        if (CollUtil.isEmpty(moveIds)) {\n            return Collections.emptyList();\n        }\n        return stockMoveItemMapper.selectListByMoveIds(moveIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * ERP 其它出库单 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpStockOutService {\n\n    /**\n     * 创建其它出库单\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStockOut(@Valid ErpStockOutSaveReqVO createReqVO);\n\n    /**\n     * 更新其它出库单\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStockOut(@Valid ErpStockOutSaveReqVO updateReqVO);\n\n    /**\n     * 更新其它出库单的状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateStockOutStatus(Long id, Integer status);\n\n    /**\n     * 删除其它出库单\n     *\n     * @param ids 编号数组\n     */\n    void deleteStockOut(List<Long> ids);\n\n    /**\n     * 获得其它出库单\n     *\n     * @param id 编号\n     * @return 其它出库单\n     */\n    ErpStockOutDO getStockOut(Long id);\n\n    /**\n     * 获得其它出库单分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 其它出库单分页\n     */\n    PageResult<ErpStockOutDO> getStockOutPage(ErpStockOutPageReqVO pageReqVO);\n\n    // ==================== 出库项 ====================\n\n    /**\n     * 获得其它出库单项列表\n     *\n     * @param outId 出库编号\n     * @return 其它出库单项列表\n     */\n    List<ErpStockOutItemDO> getStockOutItemListByOutId(Long outId);\n\n    /**\n     * 获得其它出库单项 List\n     *\n     * @param outIds 出库编号数组\n     * @return 其它出库单项 List\n     */\n    List<ErpStockOutItemDO> getStockOutItemListByOutIds(Collection<Long> outIds);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutPageReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutSaveReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutItemMapper;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutMapper;\nimport cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;\nimport cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;\nimport cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport javax.annotation.Resource;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n// TODO 芋艿：记录操作日志\n\n/**\n * ERP 其它出库单 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpStockOutServiceImpl implements ErpStockOutService {\n\n    @Resource\n    private ErpStockOutMapper stockOutMapper;\n    @Resource\n    private ErpStockOutItemMapper stockOutItemMapper;\n\n    @Resource\n    private ErpNoRedisDAO noRedisDAO;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpWarehouseService warehouseService;\n    @Resource\n    private ErpCustomerService customerService;\n    @Resource\n    private ErpStockRecordService stockRecordService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createStockOut(ErpStockOutSaveReqVO createReqVO) {\n        // 1.1 校验出库项的有效性\n        List<ErpStockOutItemDO> stockOutItems = validateStockOutItems(createReqVO.getItems());\n        // 1.2 校验客户\n        customerService.validateCustomer(createReqVO.getCustomerId());\n        // 1.3 生成出库单号，并校验唯一性\n        String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_OUT_NO_PREFIX);\n        if (stockOutMapper.selectByNo(no) != null) {\n            throw exception(STOCK_OUT_NO_EXISTS);\n        }\n\n        // 2.1 插入出库单\n        ErpStockOutDO stockOut = BeanUtils.toBean(createReqVO, ErpStockOutDO.class, in -> in\n                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())\n                .setTotalCount(getSumValue(stockOutItems, ErpStockOutItemDO::getCount, BigDecimal::add))\n                .setTotalPrice(getSumValue(stockOutItems, ErpStockOutItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)));\n        stockOutMapper.insert(stockOut);\n        // 2.2 插入出库单项\n        stockOutItems.forEach(o -> o.setOutId(stockOut.getId()));\n        stockOutItemMapper.insertBatch(stockOutItems);\n        return stockOut.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStockOut(ErpStockOutSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        ErpStockOutDO stockOut = validateStockOutExists(updateReqVO.getId());\n        if (ErpAuditStatus.APPROVE.getStatus().equals(stockOut.getStatus())) {\n            throw exception(STOCK_OUT_UPDATE_FAIL_APPROVE, stockOut.getNo());\n        }\n        // 1.2 校验客户\n        customerService.validateCustomer(updateReqVO.getCustomerId());\n        // 1.3 校验出库项的有效性\n        List<ErpStockOutItemDO> stockOutItems = validateStockOutItems(updateReqVO.getItems());\n\n        // 2.1 更新出库单\n        ErpStockOutDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockOutDO.class, in -> in\n                .setTotalCount(getSumValue(stockOutItems, ErpStockOutItemDO::getCount, BigDecimal::add))\n                .setTotalPrice(getSumValue(stockOutItems, ErpStockOutItemDO::getTotalPrice, BigDecimal::add)));\n        stockOutMapper.updateById(updateObj);\n        // 2.2 更新出库单项\n        updateStockOutItemList(updateReqVO.getId(), stockOutItems);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStockOutStatus(Long id, Integer status) {\n        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);\n        // 1.1 校验存在\n        ErpStockOutDO stockOut = validateStockOutExists(id);\n        // 1.2 校验状态\n        if (stockOut.getStatus().equals(status)) {\n            throw exception(approve ? STOCK_OUT_APPROVE_FAIL : STOCK_OUT_PROCESS_FAIL);\n        }\n\n        // 2. 更新状态\n        int updateCount = stockOutMapper.updateByIdAndStatus(id, stockOut.getStatus(),\n                new ErpStockOutDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(approve ? STOCK_OUT_APPROVE_FAIL : STOCK_OUT_PROCESS_FAIL);\n        }\n\n        // 3. 变更库存\n        List<ErpStockOutItemDO> stockOutItems = stockOutItemMapper.selectListByOutId(id);\n        Integer bizType = approve ? ErpStockRecordBizTypeEnum.OTHER_OUT.getType()\n                : ErpStockRecordBizTypeEnum.OTHER_OUT_CANCEL.getType();\n        stockOutItems.forEach(stockOutItem -> {\n            BigDecimal count = approve ? stockOutItem.getCount().negate() : stockOutItem.getCount();\n            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(\n                    stockOutItem.getProductId(), stockOutItem.getWarehouseId(), count,\n                    bizType, stockOutItem.getOutId(), stockOutItem.getId(), stockOut.getNo()));\n        });\n    }\n\n    private List<ErpStockOutItemDO> validateStockOutItems(List<ErpStockOutSaveReqVO.Item> list) {\n        // 1.1 校验产品存在\n        List<ErpProductDO> productList = productService.validProductList(\n                convertSet(list, ErpStockOutSaveReqVO.Item::getProductId));\n        Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);\n        // 1.2 校验仓库存在\n        warehouseService.validWarehouseList(convertSet(list, ErpStockOutSaveReqVO.Item::getWarehouseId));\n        // 2. 转化为 ErpStockOutItemDO 列表\n        return convertList(list, o -> BeanUtils.toBean(o, ErpStockOutItemDO.class, item -> item\n                .setProductUnitId(productMap.get(item.getProductId()).getUnitId())\n                .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()))));\n    }\n\n    private void updateStockOutItemList(Long id, List<ErpStockOutItemDO> newList) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<ErpStockOutItemDO> oldList = stockOutItemMapper.selectListByOutId(id);\n        List<List<ErpStockOutItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录\n                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            diffList.get(0).forEach(o -> o.setOutId(id));\n            stockOutItemMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            stockOutItemMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            stockOutItemMapper.deleteByIds(convertList(diffList.get(2), ErpStockOutItemDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStockOut(List<Long> ids) {\n        // 1. 校验不处于已审批\n        List<ErpStockOutDO> stockOuts = stockOutMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(stockOuts)) {\n            return;\n        }\n        stockOuts.forEach(stockOut -> {\n            if (ErpAuditStatus.APPROVE.getStatus().equals(stockOut.getStatus())) {\n                throw exception(STOCK_OUT_DELETE_FAIL_APPROVE, stockOut.getNo());\n            }\n        });\n\n        // 2. 遍历删除，并记录操作日志\n        stockOuts.forEach(stockOut -> {\n            // 2.1 删除出库单\n            stockOutMapper.deleteById(stockOut.getId());\n            // 2.2 删除出库单项\n            stockOutItemMapper.deleteByOutId(stockOut.getId());\n        });\n    }\n\n    private ErpStockOutDO validateStockOutExists(Long id) {\n        ErpStockOutDO stockOut = stockOutMapper.selectById(id);\n        if (stockOut == null) {\n            throw exception(STOCK_OUT_NOT_EXISTS);\n        }\n        return stockOut;\n    }\n\n    @Override\n    public ErpStockOutDO getStockOut(Long id) {\n        return stockOutMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ErpStockOutDO> getStockOutPage(ErpStockOutPageReqVO pageReqVO) {\n        return stockOutMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 出库项 ====================\n\n    @Override\n    public List<ErpStockOutItemDO> getStockOutItemListByOutId(Long outId) {\n        return stockOutItemMapper.selectListByOutId(outId);\n    }\n\n    @Override\n    public List<ErpStockOutItemDO> getStockOutItemListByOutIds(Collection<Long> outIds) {\n        if (CollUtil.isEmpty(outIds)) {\n            return Collections.emptyList();\n        }\n        return stockOutItemMapper.selectListByOutIds(outIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport javax.validation.Valid;\n\n/**\n * ERP 产品库存明细 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpStockRecordService {\n\n    /**\n     * 获得产品库存明细\n     *\n     * @param id 编号\n     * @return 产品库存明细\n     */\n    ErpStockRecordDO getStockRecord(Long id);\n\n    /**\n     * 获得产品库存明细分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 产品库存明细分页\n     */\n    PageResult<ErpStockRecordDO> getStockRecordPage(ErpStockRecordPageReqVO pageReqVO);\n\n    /**\n     * 创建库存明细\n     *\n     * @param createReqBO 创建库存明细 BO\n     */\n    void createStockRecord(@Valid ErpStockRecordCreateReqBO createReqBO);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockRecordMapper;\nimport cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\n\n/**\n * ERP 产品库存明细 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpStockRecordServiceImpl implements ErpStockRecordService {\n\n    @Resource\n    private ErpStockRecordMapper stockRecordMapper;\n\n    @Resource\n    private ErpStockService stockService;\n\n    @Override\n    public ErpStockRecordDO getStockRecord(Long id) {\n        return stockRecordMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ErpStockRecordDO> getStockRecordPage(ErpStockRecordPageReqVO pageReqVO) {\n        return stockRecordMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void createStockRecord(ErpStockRecordCreateReqBO createReqBO) {\n        // 1. 更新库存\n        BigDecimal totalCount = stockService.updateStockCountIncrement(\n                createReqBO.getProductId(), createReqBO.getWarehouseId(), createReqBO.getCount());\n        // 2. 创建库存明细\n        ErpStockRecordDO stockRecord = BeanUtils.toBean(createReqBO, ErpStockRecordDO.class)\n                .setTotalCount(totalCount);\n        stockRecordMapper.insert(stockRecord);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\n\nimport java.math.BigDecimal;\n\n/**\n * ERP 产品库存 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpStockService {\n\n    /**\n     * 获得产品库存\n     *\n     * @param id 编号\n     * @return 库存\n     */\n    ErpStockDO getStock(Long id);\n\n    /**\n     * 基于产品 + 仓库，获得产品库存\n     *\n     * @param productId 产品编号\n     * @param warehouseId 仓库编号\n     * @return 产品库存\n     */\n    ErpStockDO getStock(Long productId, Long warehouseId);\n\n    /**\n     * 获得产品库存数量\n     *\n     * 如果不存在库存记录，则返回 0\n     *\n     * @param productId 产品编号\n     * @return 产品库存数量\n     */\n    BigDecimal getStockCount(Long productId);\n\n    /**\n     * 获得产品库存分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 库存分页\n     */\n    PageResult<ErpStockDO> getStockPage(ErpStockPageReqVO pageReqVO);\n\n    /**\n     * 增量更新产品库存数量\n     *\n     * @param productId 产品编号\n     * @param warehouseId 仓库编号\n     * @param count 增量数量：正数，表示增加；负数，表示减少\n     * @return 更新后的库存\n     */\n    BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper;\nimport cn.iocoder.yudao.module.erp.service.product.ErpProductService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE2;\n\n/**\n * ERP 产品库存 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpStockServiceImpl implements ErpStockService {\n\n    /**\n     * 允许库存为负数\n     *\n     * TODO 芋艿：后续做成 db 配置\n     */\n    private static final Boolean NEGATIVE_STOCK_COUNT_ENABLE = false;\n\n    @Resource\n    private ErpProductService productService;\n    @Resource\n    private ErpWarehouseService warehouseService;\n\n    @Resource\n    private ErpStockMapper stockMapper;\n\n    @Override\n    public ErpStockDO getStock(Long id) {\n        return stockMapper.selectById(id);\n    }\n\n    @Override\n    public ErpStockDO getStock(Long productId, Long warehouseId) {\n        return stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId);\n    }\n\n    @Override\n    public BigDecimal getStockCount(Long productId) {\n        BigDecimal count = stockMapper.selectSumByProductId(productId);\n        return count != null ? count : BigDecimal.ZERO;\n    }\n\n    @Override\n    public PageResult<ErpStockDO> getStockPage(ErpStockPageReqVO pageReqVO) {\n        return stockMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count) {\n        // 1.1 查询当前库存\n        ErpStockDO stock = stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId);\n        if (stock == null) {\n            stock = new ErpStockDO().setProductId(productId).setWarehouseId(warehouseId).setCount(BigDecimal.ZERO);\n            stockMapper.insert(stock);\n        }\n        // 1.2 校验库存是否充足\n        if (!NEGATIVE_STOCK_COUNT_ENABLE && stock.getCount().add(count).compareTo(BigDecimal.ZERO) < 0) {\n            throw exception(STOCK_COUNT_NEGATIVE, productService.getProduct(productId).getName(),\n                    warehouseService.getWarehouse(warehouseId).getName(), stock.getCount(), count);\n        }\n\n        // 2. 库存变更\n        int updateCount = stockMapper.updateCountIncrement(stock.getId(), count, NEGATIVE_STOCK_COUNT_ENABLE);\n        if (updateCount == 0) {\n            // 此时不好去查询最新库存，所以直接抛出该提示，不提供具体库存数字\n            throw exception(STOCK_COUNT_NEGATIVE2, productService.getProduct(productId).getName(),\n                    warehouseService.getWarehouse(warehouseId).getName());\n        }\n\n        // 3. 返回最新库存\n        return stock.getCount().add(count);\n    }\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseService.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSaveReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport javax.validation.Valid;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * ERP 仓库 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ErpWarehouseService {\n\n    /**\n     * 创建仓库\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createWarehouse(@Valid ErpWarehouseSaveReqVO createReqVO);\n\n    /**\n     * 更新ERP 仓库\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateWarehouse(@Valid ErpWarehouseSaveReqVO updateReqVO);\n\n    /**\n     * 更新仓库默认状态\n     *\n     * @param id     编号\n     * @param defaultStatus 默认状态\n     */\n    void updateWarehouseDefaultStatus(Long id, Boolean defaultStatus);\n\n    /**\n     * 删除仓库\n     *\n     * @param id 编号\n     */\n    void deleteWarehouse(Long id);\n\n    /**\n     * 获得仓库\n     *\n     * @param id 编号\n     * @return 仓库\n     */\n    ErpWarehouseDO getWarehouse(Long id);\n\n    /**\n     * 校验仓库列表的有效性\n     *\n     * @param ids 编号数组\n     * @return 仓库列表\n     */\n    List<ErpWarehouseDO> validWarehouseList(Collection<Long> ids);\n\n    /**\n     * 获得指定状态的仓库列表\n     *\n     * @param status 状态\n     * @return 仓库列表\n     */\n    List<ErpWarehouseDO> getWarehouseListByStatus(Integer status);\n\n    /**\n     * 获得仓库列表\n     *\n     * @param ids 编号数组\n     * @return 仓库列表\n     */\n    List<ErpWarehouseDO> getWarehouseList(Collection<Long> ids);\n\n    /**\n     * 获得仓库 Map\n     *\n     * @param ids 编号数组\n     * @return 仓库 Map\n     */\n    default Map<Long, ErpWarehouseDO> getWarehouseMap(Collection<Long> ids) {\n        return convertMap(getWarehouseList(ids), ErpWarehouseDO::getId);\n    }\n\n    /**\n     * 获得仓库分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 仓库分页\n     */\n    PageResult<ErpWarehouseDO> getWarehousePage(ErpWarehousePageReqVO pageReqVO);\n\n}"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSaveReqVO;\nimport cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO;\nimport cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;\nimport cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpWarehouseMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;\n\n/**\n * ERP 仓库 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ErpWarehouseServiceImpl implements ErpWarehouseService {\n\n    @Resource\n    private ErpWarehouseMapper warehouseMapper;\n\n    @Override\n    public Long createWarehouse(ErpWarehouseSaveReqVO createReqVO) {\n        // 插入\n        ErpWarehouseDO warehouse = BeanUtils.toBean(createReqVO, ErpWarehouseDO.class);\n        warehouseMapper.insert(warehouse);\n        // 返回\n        return warehouse.getId();\n    }\n\n    @Override\n    public void updateWarehouse(ErpWarehouseSaveReqVO updateReqVO) {\n        // 校验存在\n        validateWarehouseExists(updateReqVO.getId());\n        // 更新\n        ErpWarehouseDO updateObj = BeanUtils.toBean(updateReqVO, ErpWarehouseDO.class);\n        warehouseMapper.updateById(updateObj);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateWarehouseDefaultStatus(Long id, Boolean defaultStatus) {\n        // 1. 校验存在\n        validateWarehouseExists(id);\n\n        // 2.1 如果开启，则需要关闭所有其它的默认\n        if (defaultStatus) {\n            ErpWarehouseDO warehouse = warehouseMapper.selectByDefaultStatus();\n            if (warehouse != null) {\n                warehouseMapper.updateById(new ErpWarehouseDO().setId(warehouse.getId()).setDefaultStatus(false));\n            }\n        }\n        // 2.2 更新对应的默认状态\n        warehouseMapper.updateById(new ErpWarehouseDO().setId(id).setDefaultStatus(defaultStatus));\n    }\n\n    @Override\n    public void deleteWarehouse(Long id) {\n        // 校验存在\n        validateWarehouseExists(id);\n        // 删除\n        warehouseMapper.deleteById(id);\n    }\n\n    private void validateWarehouseExists(Long id) {\n        if (warehouseMapper.selectById(id) == null) {\n            throw exception(WAREHOUSE_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ErpWarehouseDO getWarehouse(Long id) {\n        return warehouseMapper.selectById(id);\n    }\n\n    @Override\n    public List<ErpWarehouseDO> validWarehouseList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        List<ErpWarehouseDO> list = warehouseMapper.selectByIds(ids);\n        Map<Long, ErpWarehouseDO> warehouseMap = convertMap(list, ErpWarehouseDO::getId);\n        for (Long id : ids) {\n            ErpWarehouseDO warehouse = warehouseMap.get(id);\n            if (warehouseMap.get(id) == null) {\n                throw exception(WAREHOUSE_NOT_EXISTS);\n            }\n            if (CommonStatusEnum.isDisable(warehouse.getStatus())) {\n                throw exception(WAREHOUSE_NOT_ENABLE, warehouse.getName());\n            }\n        }\n        return list;\n    }\n\n    @Override\n    public List<ErpWarehouseDO> getWarehouseListByStatus(Integer status) {\n        return warehouseMapper.selectListByStatus(status);\n    }\n\n    @Override\n    public List<ErpWarehouseDO> getWarehouseList(Collection<Long> ids) {\n        return warehouseMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<ErpWarehouseDO> getWarehousePage(ErpWarehousePageReqVO pageReqVO) {\n        return warehouseMapper.selectPage(pageReqVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockRecordCreateReqBO.java",
    "content": "package cn.iocoder.yudao.module.erp.service.stock.bo;\n\nimport javax.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.math.BigDecimal;\n\n/**\n * 库存明细的创建 Request BO\n *\n * @author 芋道源码\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ErpStockRecordCreateReqBO {\n\n    /**\n     * 产品编号\n     */\n    @NotNull(message = \"产品编号不能为空\")\n    private Long productId;\n    /**\n     * 仓库编号\n     */\n    @NotNull(message = \"仓库编号不能为空\")\n    private Long warehouseId;\n    /**\n     * 出入库数量\n     *\n     * 正数，表示入库；负数，表示出库\n     */\n    @NotNull(message = \"出入库数量不能为空\")\n    private BigDecimal count;\n\n    /**\n     * 业务类型\n     */\n    @NotNull(message = \"业务类型不能为空\")\n    private Integer bizType;\n    /**\n     * 业务编号\n     */\n    @NotNull(message = \"业务编号不能为空\")\n    private Long bizId;\n    /**\n     * 业务项编号\n     */\n    @NotNull(message = \"业务项编号不能为空\")\n    private Long bizItemId;\n    /**\n     * 业务单号\n     */\n    @NotNull(message = \"业务单号不能为空\")\n    private String bizNo;\n\n}\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 5 # 初始连接数\n        min-idle: 10 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n        slave: # 模拟从库，可根据自己需要修改 # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 400-infra.server.iocoder.cn # 地址\n    port: 6379 # 端口\n    database: 1 # 数据库索引\n#      password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  access-log: # 访问日志的配置项\n    enable: false\n  demo: false # 关闭演示模式\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 1 # 初始连接数\n        min-idle: 1 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例\n          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例\n          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例\n          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例\n          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例\n          username: root\n          password: 123456\n        #          username: sa # SQL Server 连接的示例\n        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例\n        #          username: SYSDBA # DM 连接的示例\n        #          password: SYSDBA # DM 连接的示例\n        slave: # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 6379 # 端口\n    database: 0 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n# 日志文件配置\nlogging:\n  level:\n    # 配置自己写的 MyBatis Mapper 打印日志\n    cn.iocoder.yudao.module.erp.dal.mysql: debug\n    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿：先禁用，Spring Boot 3.X 存在部分错误的 WARN 提示\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  env: # 多环境的配置项\n    tag: ${HOSTNAME}\n  security:\n    mock-enable: true\n  access-log: # 访问日志的配置项\n    enable: false"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: erp-server\n\n  profiles:\n    active: local\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n    allow-bean-definition-overriding: true # 允许 Bean 覆盖，例如说 Feign 等会存在重复定义的服务\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  # Servlet 配置\n  servlet:\n    # 文件上传相关配置项\n    multipart:\n      max-file-size: 16MB # 单个文件大小\n      max-request-size: 32MB # 设置总上传的文件大小\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  # Cache 配置项\n  cache:\n    type: REDIS\n    redis:\n      time-to-live: 1h # 设置过期时间为 1 小时\n\nserver:\n  port: 48088\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\n--- #################### 接口文档配置 ####################\n\nspringdoc:\n  api-docs:\n    enabled: true # 1. 是否开启 Swagger 接文档的元数据\n    path: /v3/api-docs\n  swagger-ui:\n    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面\n    path: /swagger-ui\n  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档\n\nknife4j:\n  enable: true\n  setting:\n    language: zh_cn\n\n# MyBatis Plus 的配置项\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: NONE # “智能”模式，基于 IdTypeEnvironmentPostProcessor + 数据源的类型，自动适配成 AUTO、INPUT 模式。\n      #      id-type: AUTO # 自增 ID，适合 MySQL 等直接自增的数据库\n      #      id-type: INPUT # 用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n      #      id-type: ASSIGN_ID # 分配 ID，默认使用雪花算法。注意，Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时，需要去除实体类上的 @KeySequence 注解\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n    banner: false # 关闭控制台的 Banner 打印\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  encryptor:\n    password: XDV71a+xqStEA3WH # 加解密的秘钥，可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成\n\nmybatis-plus-join:\n  banner: false # 关闭控制台的 Banner 打印\n\n# Spring Data Redis 配置\nspring:\n  data:\n    redis:\n      repositories:\n        enabled: false # 项目未使用到 Spring Data Redis 的 Repository，所以直接禁用，保证启动速度\n\n# VO 转换（数据翻译）相关\neasy-trans:\n  is-enable-global: false # 【默认禁用，对性能确认压力大】启用全局翻译（拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置，或通过 @IgnoreTrans 忽略某个接口\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    executor:\n      appname: ${spring.application.name} # 执行器 AppName\n      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径\n    accessToken: default_token # 执行器通讯TOKEN\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0\n    base-package: cn.iocoder.yudao.module.erp\n  web:\n    admin-ui:\n      url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址\n  xss:\n    enable: false\n    exclude-urls: # 如下 url，仅仅是为了演示，去掉配置也没关系\n      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求\n  swagger:\n    title: 管理后台\n    description: 提供管理员管理的所有功能\n    version: ${yudao.info.version}\n  tenant: # 多租户相关配置项\n    enable: true\n    ignore-urls:\n\ndebug: false\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/resources/mapper/statistics/ErpPurchaseStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.erp.dal.mysql.statistics.ErpPurchaseStatisticsMapper\">\n\n    <select id=\"getPurchasePrice\" resultType=\"java.math.BigDecimal\">\n        SELECT\n            (SELECT IFNULL(SUM(total_price), 0)\n             FROM erp_purchase_in\n             WHERE in_time >= #{beginTime}\n               <if test=\"endTime != null\">\n                   AND in_time &lt; #{endTime}\n               </if>\n               <if test=\"@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId() != null\">\n                   AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId()}\n               </if>\n               AND deleted = 0) -\n            (SELECT IFNULL(SUM(total_price), 0)\n                FROM erp_purchase_return\n                WHERE return_time >= #{beginTime}\n                <if test=\"endTime != null\">\n                    AND return_time &lt; #{endTime}\n                </if>\n                <if test=\"@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId() != null\">\n                    AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId()}\n                </if>\n                AND deleted = 0)\n    </select>\n\n</mapper>"
  },
  {
    "path": "yudao-module-erp/yudao-module-erp-server/src/main/resources/mapper/statistics/ErpSaleStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.erp.dal.mysql.statistics.ErpSaleStatisticsMapper\">\n\n    <select id=\"getSalePrice\" resultType=\"java.math.BigDecimal\">\n        SELECT\n            (SELECT IFNULL(SUM(total_price), 0)\n             FROM erp_sale_out\n             WHERE out_time >= #{beginTime}\n               <if test=\"endTime != null\">\n                   AND out_time &lt; #{endTime}\n               </if>\n               <if test=\"@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId() != null\">\n                   AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId()}\n               </if>\n               AND deleted = 0) -\n            (SELECT IFNULL(SUM(total_price), 0)\n                FROM erp_sale_return\n                WHERE return_time >= #{beginTime}\n                <if test=\"endTime != null\">\n                    AND return_time &lt; #{endTime}\n                </if>\n                <if test=\"@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId() != null\">\n                    AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId()}\n                </if>\n                AND deleted = 0)\n    </select>\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <modules>\n        <module>yudao-module-infra-api</module>\n        <module>yudao-module-infra-server</module>\n    </modules>\n    <artifactId>yudao-module-infra</artifactId>\n    <packaging>pom</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        infra 模块，主要提供两块能力：\n        1. 我们放基础设施的运维与管理，支撑上层的通用与核心业务。 例如说：定时任务的管理、服务器的信息等等\n        2. 研发工具，提升研发效率与质量。 例如说：代码生成器、接口文档等等\n    </description>\n\n</project>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-infra</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-infra-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        infra 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springdoc</groupId>\n            <artifactId>springdoc-openapi-ui</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApi.java",
    "content": "package cn.iocoder.yudao.module.infra.api.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.infra.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 参数配置\")\npublic interface ConfigApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/config\";\n\n    @GetMapping(PREFIX + \"/get-value-by-key\")\n    @Operation(summary = \"根据参数键查询参数值\")\n    CommonResult<String> getConfigValueByKey(@RequestParam(\"key\") String key);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApi.java",
    "content": "package cn.iocoder.yudao.module.infra.api.file;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.infra.api.file.dto.FileCreateReqDTO;\nimport cn.iocoder.yudao.module.infra.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 文件\")\npublic interface FileApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/file\";\n\n    /**\n     * 保存文件，并返回文件的访问路径\n     *\n     * @param content 文件内容\n     * @return 文件路径\n     */\n    default String createFile(byte[] content) {\n        return createFile(content, null, null, null);\n    }\n\n    /**\n     * 保存文件，并返回文件的访问路径\n     *\n     * @param content 文件内容\n     * @param name 文件名称，允许空\n     * @return 文件路径\n     */\n    default String createFile(byte[] content, String name) {\n        return createFile(content, name, null, null);\n    }\n\n    /**\n     * 保存文件，并返回文件的访问路径\n     *\n     * @param content 文件内容\n     * @param name 文件名称，允许空\n     * @param directory 目录，允许空\n     * @param type 文件的 MIME 类型，允许空\n     * @return 文件路径\n     */\n    default String createFile(@NotEmpty(message = \"文件内容不能为空\") byte[] content,\n                              String name, String directory, String type) {\n        return createFile(new FileCreateReqDTO().setName(name).setDirectory(directory).setType(type).setContent(content)).getCheckedData();\n    }\n\n    @PostMapping(PREFIX + \"/create\")\n    @Operation(summary = \"保存文件，并返回文件的访问路径\")\n    CommonResult<String> createFile(@Valid @RequestBody FileCreateReqDTO createReqDTO);\n\n    /**\n     * 生成文件预签名地址，用于读取\n     *\n     * @param url 完整的文件访问地址\n     * @param expirationSeconds 访问有效期，单位秒\n     * @return 文件预签名地址\n     */\n    @GetMapping(PREFIX + \"/presigned-url\")\n    @Operation(summary = \"生成文件预签名地址，用于读取\")\n    CommonResult<String> presignGetUrl(@NotEmpty(message = \"URL 不能为空\") @RequestParam(\"url\") String url,\n                                       Integer expirationSeconds);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/file/dto/FileCreateReqDTO.java",
    "content": "package cn.iocoder.yudao.module.infra.api.file.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Schema(description = \"RPC 服务 - 文件创建 Request DTO\")\n@Data\npublic class FileCreateReqDTO {\n\n    @Schema(description = \"原文件名称\", example = \"xxx.png\")\n    private String name;\n\n    @Schema(description = \"文件目录\", example = \"xxx\")\n    private String directory;\n\n    @Schema(description = \"文件的 MIME 类型\", example = \"image/png\")\n    private String type;\n\n    @Schema(description = \"文件内容\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"文件内容不能为空\")\n    private byte[] content;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java",
    "content": "/**\n * infra API 包，定义暴露给其它模块的 API\n */\npackage cn.iocoder.yudao.module.infra.api;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApi.java",
    "content": "package cn.iocoder.yudao.module.infra.api.websocket;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.infra.api.websocket.dto.WebSocketSendReqDTO;\nimport cn.iocoder.yudao.module.infra.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\n\nimport javax.validation.Valid;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - WebSocket 发送器的\") // 对 WebSocketMessageSender 进行封装，提供给其它模块使用\npublic interface WebSocketSenderApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/websocket\";\n\n    @PostMapping(PREFIX + \"/send\")\n    @Operation(summary = \"发送 WebSocket 消息\")\n    CommonResult<Boolean> send(@Valid @RequestBody WebSocketSendReqDTO message);\n\n    /**\n     * 发送消息给指定用户\n     *\n     * @param userType 用户类型\n     * @param userId 用户编号\n     * @param messageType 消息类型\n     * @param messageContent 消息内容，JSON 格式\n     */\n    default void send(Integer userType, Long userId, String messageType, String messageContent) {\n        send(new WebSocketSendReqDTO().setUserType(userType).setUserId(userId)\n                .setMessageType(messageType).setMessageContent(messageContent)).checkError();\n    }\n\n    /**\n     * 发送消息给指定用户类型\n     *\n     * @param userType 用户类型\n     * @param messageType 消息类型\n     * @param messageContent 消息内容，JSON 格式\n     */\n    default void send(Integer userType, String messageType, String messageContent) {\n        send(new WebSocketSendReqDTO().setUserType(userType)\n                .setMessageType(messageType).setMessageContent(messageContent)).checkError();\n    }\n\n    /**\n     * 发送消息给指定 Session\n     *\n     * @param sessionId Session 编号\n     * @param messageType 消息类型\n     * @param messageContent 消息内容，JSON 格式\n     */\n    default void send(String sessionId, String messageType, String messageContent) {\n        send(new WebSocketSendReqDTO().setSessionId(sessionId)\n                .setMessageType(messageType).setMessageContent(messageContent)).checkError();\n    }\n\n    default void sendObject(Integer userType, Long userId, String messageType, Object messageContent) {\n        send(userType, userId, messageType, JsonUtils.toJsonString(messageContent));\n    }\n\n    default void sendObject(Integer userType, String messageType, Object messageContent) {\n        send(userType, messageType, JsonUtils.toJsonString(messageContent));\n    }\n\n    default void sendObject(String sessionId, String messageType, Object messageContent) {\n        send(sessionId, messageType, JsonUtils.toJsonString(messageContent));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/dto/WebSocketSendReqDTO.java",
    "content": "package cn.iocoder.yudao.module.infra.api.websocket.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Schema(description = \"RPC 服务 - WebSocket 消息发送 Request DTO\")\n@Data\npublic class WebSocketSendReqDTO {\n\n    @Schema(description = \"Session 编号\", example = \"abc\")\n    private String sessionId;\n    @Schema(description = \"用户编号\", example = \"1024\")\n    private Long userId;\n    @Schema(description = \"用户类型\", example = \"1\")\n    private Integer userType;\n\n    @Schema(description = \"消息类型\", example = \"demo-message\")\n    @NotEmpty(message = \"消息类型不能为空\")\n    private String messageType;\n    @Schema(description = \"消息内容\", example = \"{\\\"name\\\":\\\"李四\\\"}}\")\n    @NotEmpty(message = \"消息内容不能为空\")\n    private String messageContent;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ApiConstants.java",
    "content": "package cn.iocoder.yudao.module.infra.enums;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\n\n/**\n * API 相关的枚举\n *\n * @author 芋道源码\n */\npublic class ApiConstants {\n\n    /**\n     * 服务名\n     *\n     * 注意，需要保证和 spring.application.name 保持一致\n     */\n    public static final String NAME = \"infra-server\";\n\n    public static final String PREFIX = RpcConstants.RPC_API_PREFIX + \"/infra\";\n\n    public static final String VERSION = \"1.0.0\";\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/DictTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.infra.enums;\n\n/**\n * Infra 字典类型的枚举类\n *\n * @author 芋道源码\n */\npublic interface DictTypeConstants {\n\n    String USER_TYPE = \"user_type\"; // 用户类型\n\n    String JOB_STATUS = \"infra_job_status\"; // 定时任务状态的枚举\n    String JOB_LOG_STATUS = \"infra_job_log_status\"; // 定时任务日志状态的枚举\n\n    String API_ERROR_LOG_PROCESS_STATUS = \"infra_api_error_log_process_status\"; // API 错误日志的处理状态的枚举\n\n    String CONFIG_TYPE = \"infra_config_type\"; // 参数配置类型\n    String BOOLEAN_STRING = \"infra_boolean_string\"; // Boolean 是否类型\n\n    String OPERATE_TYPE = \"infra_operate_type\"; // 操作类型\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.infra.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * Infra 错误码枚举类\n *\n * infra 系统，使用 1-001-000-000 段\n */\npublic interface ErrorCodeConstants {\n\n    // ========== 参数配置 1-001-000-000 ==========\n    ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1_001_000_001, \"参数配置不存在\");\n    ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1_001_000_002, \"参数配置 key 重复\");\n    ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1_001_000_003, \"不能删除类型为系统内置的参数配置\");\n    ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1_001_000_004, \"获取参数配置失败，原因：不允许获取不可见配置\");\n\n    // ========== 定时任务 1-001-001-000 ==========\n    ErrorCode JOB_NOT_EXISTS = new ErrorCode(1_001_001_000, \"定时任务不存在\");\n    ErrorCode JOB_HANDLER_EXISTS = new ErrorCode(1_001_001_001, \"定时任务的处理器已经存在\");\n    ErrorCode JOB_CHANGE_STATUS_INVALID = new ErrorCode(1_001_001_002, \"只允许修改为开启或者关闭状态\");\n    ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, \"定时任务已经处于该状态，无需修改\");\n    ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, \"只有开启状态的任务，才可以修改\");\n    ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, \"CRON 表达式不正确\");\n    ErrorCode JOB_HANDLER_BEAN_NOT_EXISTS = new ErrorCode(1_001_001_006, \"定时任务的处理器 Bean 不存在，注意 Bean 默认首字母小写\");\n    ErrorCode JOB_HANDLER_BEAN_TYPE_ERROR = new ErrorCode(1_001_001_007, \"定时任务的处理器 Bean 类型不正确，未实现 JobHandler 接口\");\n\n    // ========== API 错误日志 1-001-002-000 ==========\n    ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1_001_002_000, \"API 错误日志不存在\");\n    ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1_001_002_001, \"API 错误日志已处理\");\n\n    // ========= 文件相关 1-001-003-000 =================\n    ErrorCode FILE_PATH_EXISTS = new ErrorCode(1_001_003_000, \"文件路径已存在\");\n    ErrorCode FILE_NOT_EXISTS = new ErrorCode(1_001_003_001, \"文件不存在\");\n    ErrorCode FILE_IS_EMPTY = new ErrorCode(1_001_003_002, \"文件为空\");\n\n    // ========== 代码生成器 1-001-004-000 ==========\n    ErrorCode CODEGEN_TABLE_EXISTS = new ErrorCode(1_001_004_002, \"表定义已经存在\");\n    ErrorCode CODEGEN_IMPORT_TABLE_NULL = new ErrorCode(1_001_004_001, \"导入的表不存在\");\n    ErrorCode CODEGEN_IMPORT_COLUMNS_NULL = new ErrorCode(1_001_004_002, \"导入的字段不存在\");\n    ErrorCode CODEGEN_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_004, \"表定义不存在\");\n    ErrorCode CODEGEN_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_005, \"字段义不存在\");\n    ErrorCode CODEGEN_SYNC_COLUMNS_NULL = new ErrorCode(1_001_004_006, \"同步的字段不存在\");\n    ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1_001_004_007, \"同步失败，不存在改变\");\n    ErrorCode CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL = new ErrorCode(1_001_004_008, \"数据库的表注释未填写\");\n    ErrorCode CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL = new ErrorCode(1_001_004_009, \"数据库的表字段({})注释未填写\");\n    ErrorCode CODEGEN_MASTER_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_010, \"主表(id={})定义不存在，请检查\");\n    ErrorCode CODEGEN_SUB_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_011, \"子表的字段(id={})不存在，请检查\");\n    ErrorCode CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE = new ErrorCode(1_001_004_012, \"主表生成代码失败，原因：它没有子表\");\n\n    // ========== 文件配置 1-001-006-000 ==========\n    ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, \"文件配置不存在\");\n    ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1_001_006_001, \"该文件配置不允许删除，原因：它是主配置，删除会导致无法上传文件\");\n\n    // ========== 数据源配置 1-001-007-000 ==========\n    ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_007_000, \"数据源配置不存在\");\n    ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1_001_007_001, \"数据源配置不正确，无法进行连接\");\n\n    // ========== 学生 1-001-201-000 ==========\n    ErrorCode DEMO01_CONTACT_NOT_EXISTS = new ErrorCode(1_001_201_000, \"示例联系人不存在\");\n    ErrorCode DEMO02_CATEGORY_NOT_EXISTS = new ErrorCode(1_001_201_001, \"示例分类不存在\");\n    ErrorCode DEMO02_CATEGORY_EXITS_CHILDREN = new ErrorCode(1_001_201_002, \"存在存在子示例分类，无法删除\");\n    ErrorCode DEMO02_CATEGORY_PARENT_NOT_EXITS = new ErrorCode(1_001_201_003,\"父级示例分类不存在\");\n    ErrorCode DEMO02_CATEGORY_PARENT_ERROR = new ErrorCode(1_001_201_004, \"不能设置自己为父示例分类\");\n    ErrorCode DEMO02_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_001_201_005, \"已经存在该名字的示例分类\");\n    ErrorCode DEMO02_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_001_201_006, \"不能设置自己的子示例分类为父示例分类\");\n    ErrorCode DEMO03_STUDENT_NOT_EXISTS = new ErrorCode(1_001_201_007, \"学生不存在\");\n    ErrorCode DEMO03_COURSE_NOT_EXISTS = new ErrorCode(1_001_201_008, \"学生课程不存在\");\n    ErrorCode DEMO03_GRADE_NOT_EXISTS = new ErrorCode(1_001_201_009, \"学生班级不存在\");\n    ErrorCode DEMO03_GRADE_EXISTS = new ErrorCode(1_001_201_010, \"学生班级已存在\");\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnHtmlTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.infra.enums.codegen;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 代码生成器的字段 HTML 展示枚举\n */\n@AllArgsConstructor\n@Getter\npublic enum CodegenColumnHtmlTypeEnum {\n\n    INPUT(\"input\"), // 文本框\n    TEXTAREA(\"textarea\"), // 文本域\n    SELECT(\"select\"), // 下拉框\n    RADIO(\"radio\"), // 单选框\n    CHECKBOX(\"checkbox\"), // 复选框\n    DATETIME(\"datetime\"), // 日期控件\n    IMAGE_UPLOAD(\"imageUpload\"), // 上传图片\n    FILE_UPLOAD(\"fileUpload\"), // 上传文件\n    EDITOR(\"editor\"), // 富文本控件\n    ;\n\n    /**\n     * 条件\n     */\n    private final String type;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnListConditionEnum.java",
    "content": "package cn.iocoder.yudao.module.infra.enums.codegen;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 代码生成器的字段过滤条件枚举\n */\n@AllArgsConstructor\n@Getter\npublic enum CodegenColumnListConditionEnum {\n\n    EQ(\"=\"),\n    NE(\"!=\"),\n    GT(\">\"),\n    GTE(\">=\"),\n    LT(\"<\"),\n    LTE(\"<=\"),\n    LIKE(\"LIKE\"),\n    BETWEEN(\"BETWEEN\");\n\n    /**\n     * 条件\n     */\n    private final String condition;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.infra.enums.codegen;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 代码生成的前端类型枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum CodegenFrontTypeEnum {\n\n    VUE2_ELEMENT_UI(10), // Vue2 Element UI 标准模版\n\n    VUE3_ELEMENT_PLUS(20), // Vue3 Element Plus 标准模版\n\n    VUE3_VBEN2_ANTD_SCHEMA(30), // Vue3 VBEN2 + ANTD + Schema 模版\n\n    VUE3_VBEN5_ANTD_SCHEMA(40), // Vue3 VBEN5 + ANTD + schema 模版\n    VUE3_VBEN5_ANTD_GENERAL(41), // Vue3 VBEN5 + ANTD 标准模版\n\n    VUE3_VBEN5_EP_SCHEMA(50), // Vue3 VBEN5 + EP + schema 模版\n    VUE3_VBEN5_EP_GENERAL(51), // Vue3 VBEN5 + EP 标准模版\n\n    VUE3_ADMIN_UNIAPP_WOT(60), // Vue3 Admin + Uniapp + WOT 标准模版\n    ;\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenSceneEnum.java",
    "content": "package cn.iocoder.yudao.module.infra.enums.codegen;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport static cn.hutool.core.util.ArrayUtil.firstMatch;\n\n/**\n * 代码生成的场景枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum CodegenSceneEnum {\n\n    ADMIN(1, \"管理后台\", \"admin\", \"\"),\n    APP(2, \"用户 APP\", \"app\", \"App\");\n\n    /**\n     * 场景\n     */\n    private final Integer scene;\n    /**\n     * 场景名\n     */\n    private final String name;\n    /**\n     * 基础包名\n     */\n    private final String basePackage;\n    /**\n     * Controller 和 VO 类的前缀\n     */\n    private final String prefixClass;\n\n    public static CodegenSceneEnum valueOf(Integer scene) {\n        return firstMatch(sceneEnum -> sceneEnum.getScene().equals(scene), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.infra.enums.codegen;\n\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Objects;\n\n/**\n * 代码生成模板类型\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum CodegenTemplateTypeEnum {\n\n    ONE(1), // 单表（增删改查）\n    TREE(2), // 树表（增删改查）\n\n    MASTER_NORMAL(10), // 主子表 - 主表 - 普通模式\n    MASTER_ERP(11), // 主子表 - 主表 - ERP 模式\n    MASTER_INNER(12), // 主子表 - 主表 - 内嵌模式\n    SUB(15), // 主子表 - 子表\n    ;\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n\n    /**\n     * 是否为主表\n     *\n     * @param type 类型\n     * @return 是否主表\n     */\n    public static boolean isMaster(Integer type) {\n        return ObjectUtils.equalsAny(type,\n                MASTER_NORMAL.type, MASTER_ERP.type, MASTER_INNER.type);\n    }\n\n    /**\n     * 是否为树表\n     *\n     * @param type 类型\n     * @return 是否树表\n     */\n    public static boolean isTree(Integer type) {\n        return Objects.equals(type, TREE.type);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenVOTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.infra.enums.codegen;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 代码生成的 VO 类型枚举\n *\n * 目前的作用：Controller 新增、修改、响应时，使用 VO 还是 DO\n * 注意：不包括 Controller 的分页参数！\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum CodegenVOTypeEnum {\n\n    VO(10, \"VO\"),\n    DO(20, \"DO\");\n\n    /**\n     * 场景\n     */\n    private final Integer type;\n    /**\n     * 场景名\n     */\n    private final String name;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/config/ConfigTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.infra.enums.config;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n@Getter\n@AllArgsConstructor\npublic enum ConfigTypeEnum {\n\n    /**\n     * 系统配置\n     */\n    SYSTEM(1),\n    /**\n     * 自定义配置\n     */\n    CUSTOM(2);\n\n    private final Integer type;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/logger/ApiErrorLogProcessStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.infra.enums.logger;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * API 异常数据的处理状态\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum ApiErrorLogProcessStatusEnum {\n\n    INIT(0, \"未处理\"),\n    DONE(1, \"已处理\"),\n    IGNORE(2, \"已忽略\");\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n    /**\n     * 资源类型名\n     */\n    private final String name;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.infra.enums;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:8-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-module-infra-server\nWORKDIR /yudao-module-infra-server\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-module-infra-server.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48080 端口\nEXPOSE 48082\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-infra</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-infra-server</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        infra 模块，主要提供两块能力：\n            1. 我们放基础设施的运维与管理，支撑上层的通用与核心业务。 例如说：定时任务的管理、服务器的信息等等\n            2. 研发工具，提升研发效率与质量。 例如说：代码生成器、接口文档等等\n    </description>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-infra-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-websocket</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器，使用它解析表结构 -->\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- Job 定时任务相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-job</artifactId>\n        </dependency>\n\n        <!-- 消息队列相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mq</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.velocity</groupId>\n            <artifactId>velocity-engine-core</artifactId> <!-- 实现代码生成 -->\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n\n<!--        <dependency>-->\n<!--            <groupId>de.codecentric</groupId>-->\n<!--            <artifactId>spring-boot-admin-starter-server</artifactId> &lt;!&ndash; 实现 Spring Boot Admin Server 服务端 &ndash;&gt;-->\n<!--        </dependency>-->\n\n        <!-- 三方云服务相关 -->\n        <dependency>\n            <groupId>commons-net</groupId>\n            <artifactId>commons-net</artifactId> <!-- 文件客户端：解决 ftp 连接 -->\n        </dependency>\n        <dependency>\n            <groupId>com.github.mwiede</groupId>\n            <artifactId>jsch</artifactId> <!-- 文件客户端：解决 sftp 连接 -->\n        </dependency>\n        <dependency>\n            <groupId>software.amazon.awssdk</groupId> <!-- 文件客户端：解决阿里云、腾讯云、minio 等 S3 连接 -->\n            <artifactId>s3</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.tika</groupId>\n            <artifactId>tika-core</artifactId> <!-- 文件客户端：文件类型的识别 -->\n        </dependency>\n\n    </dependencies>\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/InfraServerApplication.java",
    "content": "package cn.iocoder.yudao.module.infra;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n * <p>\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication\npublic class InfraServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(InfraServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApiImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.api.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;\nimport cn.iocoder.yudao.module.infra.service.config.ConfigService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class ConfigApiImpl implements ConfigApi {\n\n    @Resource\n    private ConfigService configService;\n\n    @Override\n    public CommonResult<String> getConfigValueByKey(String key) {\n        ConfigDO config = configService.getConfigByKey(key);\n        return success(config != null ? config.getValue() : null);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApiImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.api.file;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.infra.api.file.dto.FileCreateReqDTO;\nimport cn.iocoder.yudao.module.infra.service.file.FileService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class FileApiImpl implements FileApi {\n\n    @Resource\n    private FileService fileService;\n\n    @Override\n    public CommonResult<String> createFile(FileCreateReqDTO createReqDTO) {\n        return success(fileService.createFile(createReqDTO.getContent(), createReqDTO.getName(),\n                createReqDTO.getDirectory(), createReqDTO.getType()));\n    }\n\n    @Override\n    public CommonResult<String> presignGetUrl(String url, Integer expirationSeconds) {\n        return success(fileService.presignGetUrl(url, expirationSeconds));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiAccessLogApiImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.api.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.ApiAccessLogCommonApi;\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.infra.service.logger.ApiAccessLogService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class ApiAccessLogApiImpl implements ApiAccessLogCommonApi {\n\n    @Resource\n    private ApiAccessLogService apiAccessLogService;\n\n    @Override\n    public CommonResult<Boolean> createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {\n        apiAccessLogService.createApiAccessLog(createDTO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiErrorLogApiImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.api.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi;\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.infra.service.logger.ApiErrorLogService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class ApiErrorLogApiImpl implements ApiErrorLogCommonApi {\n\n    @Resource\n    private ApiErrorLogService apiErrorLogService;\n\n    @Override\n    public CommonResult<Boolean> createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) {\n        apiErrorLogService.createApiErrorLog(createDTO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java",
    "content": "package cn.iocoder.yudao.module.infra.api;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApiImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.api.websocket;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;\nimport cn.iocoder.yudao.module.infra.api.websocket.dto.WebSocketSendReqDTO;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class WebSocketSenderApiImpl implements WebSocketSenderApi {\n\n    @Resource\n    private WebSocketMessageSender webSocketMessageSender;\n\n    @Override\n    public CommonResult<Boolean> send(WebSocketSendReqDTO message) {\n        if (StrUtil.isNotEmpty(message.getSessionId())) {\n            webSocketMessageSender.send(message.getSessionId(),\n                    message.getMessageType(), message.getMessageContent());\n        } else if (message.getUserType() != null && message.getUserId() != null) {\n            webSocketMessageSender.send(message.getUserType(), message.getUserId(),\n                    message.getMessageType(), message.getMessageContent());\n        } else if (message.getUserType() != null) {\n            webSocketMessageSender.send(message.getUserType(),\n                    message.getMessageType(), message.getMessageContent());\n        }\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.ZipUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;\nimport cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport cn.iocoder.yudao.module.infra.service.codegen.CodegenService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserNickname;\nimport static cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;\n\n@Tag(name = \"管理后台 - 代码生成器\")\n@RestController\n@RequestMapping(\"/infra/codegen\")\n@Validated\npublic class CodegenController {\n\n    @Resource\n    private CodegenService codegenService;\n\n    @GetMapping(\"/db/table/list\")\n    @Operation(summary = \"获得数据库自带的表定义列表\", description = \"会过滤掉已经导入 Codegen 的表\")\n    @Parameters({\n            @Parameter(name = \"dataSourceConfigId\", description = \"数据源配置的编号\", required = true, example = \"1\"),\n            @Parameter(name = \"name\", description = \"表名，模糊匹配\", example = \"yudao\"),\n            @Parameter(name = \"comment\", description = \"描述，模糊匹配\", example = \"芋道\")\n    })\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:query')\")\n    public CommonResult<List<DatabaseTableRespVO>> getDatabaseTableList(\n            @RequestParam(value = \"dataSourceConfigId\") Long dataSourceConfigId,\n            @RequestParam(value = \"name\", required = false) String name,\n            @RequestParam(value = \"comment\", required = false) String comment) {\n        return success(codegenService.getDatabaseTableList(dataSourceConfigId, name, comment));\n    }\n\n    @GetMapping(\"/table/list\")\n    @Operation(summary = \"获得表定义列表\")\n    @Parameter(name = \"dataSourceConfigId\", description = \"数据源配置的编号\", required = true, example = \"1\")\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:query')\")\n    public CommonResult<List<CodegenTableRespVO>> getCodegenTableList(@RequestParam(value = \"dataSourceConfigId\") Long dataSourceConfigId) {\n        List<CodegenTableDO> list = codegenService.getCodegenTableList(dataSourceConfigId);\n        return success(BeanUtils.toBean(list, CodegenTableRespVO.class));\n    }\n\n    @GetMapping(\"/table/page\")\n    @Operation(summary = \"获得表定义分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:query')\")\n    public CommonResult<PageResult<CodegenTableRespVO>> getCodegenTablePage(@Valid CodegenTablePageReqVO pageReqVO) {\n        PageResult<CodegenTableDO> pageResult = codegenService.getCodegenTablePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, CodegenTableRespVO.class));\n    }\n\n    @GetMapping(\"/detail\")\n    @Operation(summary = \"获得表和字段的明细\")\n    @Parameter(name = \"tableId\", description = \"表编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:query')\")\n    public CommonResult<CodegenDetailRespVO> getCodegenDetail(@RequestParam(\"tableId\") Long tableId) {\n        CodegenTableDO table = codegenService.getCodegenTable(tableId);\n        List<CodegenColumnDO> columns = codegenService.getCodegenColumnListByTableId(tableId);\n        // 拼装返回\n        return success(CodegenConvert.INSTANCE.convert(table, columns));\n    }\n\n    @Operation(summary = \"基于数据库的表结构，创建代码生成器的表和字段定义\")\n    @PostMapping(\"/create-list\")\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:create')\")\n    public CommonResult<List<Long>> createCodegenList(@Valid @RequestBody CodegenCreateListReqVO reqVO) {\n        return success(codegenService.createCodegenList(getLoginUserNickname(), reqVO));\n    }\n\n    @Operation(summary = \"更新数据库的表和字段定义\")\n    @PutMapping(\"/update\")\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:update')\")\n    public CommonResult<Boolean> updateCodegen(@Valid @RequestBody CodegenUpdateReqVO updateReqVO) {\n        codegenService.updateCodegen(updateReqVO);\n        return success(true);\n    }\n\n    @Operation(summary = \"基于数据库的表结构，同步数据库的表和字段定义\")\n    @PutMapping(\"/sync-from-db\")\n    @Parameter(name = \"tableId\", description = \"表编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:update')\")\n    public CommonResult<Boolean> syncCodegenFromDB(@RequestParam(\"tableId\") Long tableId) {\n        codegenService.syncCodegenFromDB(tableId);\n        return success(true);\n    }\n\n    @Operation(summary = \"删除数据库的表和字段定义\")\n    @DeleteMapping(\"/delete\")\n    @Parameter(name = \"tableId\", description = \"表编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:delete')\")\n    public CommonResult<Boolean> deleteCodegen(@RequestParam(\"tableId\") Long tableId) {\n        codegenService.deleteCodegen(tableId);\n        return success(true);\n    }\n\n    @Operation(summary = \"批量删除数据库的表和字段定义\")\n    @DeleteMapping(\"/delete-list\")\n    @Parameter(name = \"tableIds\", description = \"表编号列表\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:delete')\")\n    public CommonResult<Boolean> deleteCodegenList(@RequestParam(\"tableIds\") List<Long> tableIds) {\n        codegenService.deleteCodegenList(tableIds);\n        return success(true);\n    }\n\n    @Operation(summary = \"预览生成代码\")\n    @GetMapping(\"/preview\")\n    @Parameter(name = \"tableId\", description = \"表编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:preview')\")\n    public CommonResult<List<CodegenPreviewRespVO>> previewCodegen(@RequestParam(\"tableId\") Long tableId) {\n        Map<String, String> codes = codegenService.generationCodes(tableId);\n        return success(CodegenConvert.INSTANCE.convert(codes));\n    }\n\n    @Operation(summary = \"下载生成代码\")\n    @GetMapping(\"/download\")\n    @Parameter(name = \"tableId\", description = \"表编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:codegen:download')\")\n    public void downloadCodegen(@RequestParam(\"tableId\") Long tableId,\n                                HttpServletResponse response) throws IOException {\n        // 生成代码\n        Map<String, String> codes = codegenService.generationCodes(tableId);\n        // 构建 zip 包\n        String[] paths = codes.keySet().toArray(new String[0]);\n        ByteArrayInputStream[] ins = codes.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);\n        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n        ZipUtil.zip(outputStream, paths, ins);\n        // 输出\n        writeAttachment(response, \"codegen.zip\", outputStream.toByteArray());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenCreateListReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 基于数据库的表结构，创建代码生成器的表和字段定义 Request VO\")\n@Data\npublic class CodegenCreateListReqVO {\n\n    @Schema(description = \"数据源配置的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"数据源配置的编号不能为空\")\n    private Long dataSourceConfigId;\n\n    @Schema(description = \"表名数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1, 2, 3]\")\n    @NotNull(message = \"表名数组不能为空\")\n    private List<String> tableNames;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 代码生成表和字段的明细 Response VO\")\n@Data\npublic class CodegenDetailRespVO {\n\n    @Schema(description = \"表定义\")\n    private CodegenTableRespVO table;\n\n    @Schema(description = \"字段定义\")\n    private List<CodegenColumnRespVO> columns;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenPreviewRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 代码生成预览 Response VO，注意，每个文件都是一个该对象\")\n@Data\npublic class CodegenPreviewRespVO {\n\n    @Schema(description = \"文件路径\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"java/cn/iocoder/yudao/adminserver/modules/system/controller/test/SysTestDemoController.java\")\n    private String filePath;\n\n    @Schema(description = \"代码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Hello World\")\n    private String code;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnSaveReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableSaveReqVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 代码生成表和字段的修改 Request VO\")\n@Data\npublic class CodegenUpdateReqVO {\n\n    @Valid // 校验内嵌的字段\n    @NotNull(message = \"表定义不能为空\")\n    private CodegenTableSaveReqVO table;\n\n    @Valid // 校验内嵌的字段\n    @NotNull(message = \"字段定义不能为空\")\n    private List<CodegenColumnSaveReqVO> columns;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 代码生成字段定义 Response VO\")\n@Data\npublic class CodegenColumnRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"表编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long tableId;\n\n    @Schema(description = \"字段名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"user_age\")\n    private String columnName;\n\n    @Schema(description = \"字段类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"int(11)\")\n    private String dataType;\n\n    @Schema(description = \"字段描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"年龄\")\n    private String columnComment;\n\n    @Schema(description = \"是否允许为空\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean nullable;\n\n    @Schema(description = \"是否主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    private Boolean primaryKey;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer ordinalPosition;\n\n    @Schema(description = \"Java 属性类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"userAge\")\n    private String javaType;\n\n    @Schema(description = \"Java 属性名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Integer\")\n    private String javaField;\n\n    @Schema(description = \"字典类型\", example = \"sys_gender\")\n    private String dictType;\n\n    @Schema(description = \"数据示例\", example = \"1024\")\n    private String example;\n\n    @Schema(description = \"是否为 Create 创建操作的字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean createOperation;\n\n    @Schema(description = \"是否为 Update 更新操作的字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    private Boolean updateOperation;\n\n    @Schema(description = \"是否为 List 查询操作的字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean listOperation;\n\n    @Schema(description = \"List 查询操作的条件类型，参见 CodegenColumnListConditionEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"LIKE\")\n    private String listOperationCondition;\n\n    @Schema(description = \"是否为 List 查询操作的返回字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean listOperationResult;\n\n    @Schema(description = \"显示类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"input\")\n    private String htmlType;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 代码生成字段定义创建/修改 Request VO\")\n@Data\npublic class CodegenColumnSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"表编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"表编号不能为空\")\n    private Long tableId;\n\n    @Schema(description = \"字段名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"user_age\")\n    @NotNull(message = \"字段名不能为空\")\n    private String columnName;\n\n    @Schema(description = \"字段类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"int(11)\")\n    @NotNull(message = \"字段类型不能为空\")\n    private String dataType;\n\n    @Schema(description = \"字段描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"年龄\")\n    @NotNull(message = \"字段描述不能为空\")\n    private String columnComment;\n\n    @Schema(description = \"是否允许为空\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否允许为空不能为空\")\n    private Boolean nullable;\n\n    @Schema(description = \"是否主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    @NotNull(message = \"是否主键不能为空\")\n    private Boolean primaryKey;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"排序不能为空\")\n    private Integer ordinalPosition;\n\n    @Schema(description = \"Java 属性类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"userAge\")\n    @NotNull(message = \"Java 属性类型不能为空\")\n    private String javaType;\n\n    @Schema(description = \"Java 属性名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Integer\")\n    @NotNull(message = \"Java 属性名不能为空\")\n    private String javaField;\n\n    @Schema(description = \"字典类型\", example = \"sys_gender\")\n    private String dictType;\n\n    @Schema(description = \"数据示例\", example = \"1024\")\n    private String example;\n\n    @Schema(description = \"是否为 Create 创建操作的字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否为 Create 创建操作的字段不能为空\")\n    private Boolean createOperation;\n\n    @Schema(description = \"是否为 Update 更新操作的字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    @NotNull(message = \"是否为 Update 更新操作的字段不能为空\")\n    private Boolean updateOperation;\n\n    @Schema(description = \"是否为 List 查询操作的字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否为 List 查询操作的字段不能为空\")\n    private Boolean listOperation;\n\n    @Schema(description = \"List 查询操作的条件类型，参见 CodegenColumnListConditionEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"LIKE\")\n    @NotNull(message = \"List 查询操作的条件类型不能为空\")\n    private String listOperationCondition;\n\n    @Schema(description = \"是否为 List 查询操作的返回字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否为 List 查询操作的返回字段不能为空\")\n    private Boolean listOperationResult;\n\n    @Schema(description = \"显示类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"input\")\n    @NotNull(message = \"显示类型不能为空\")\n    private String htmlType;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 表定义分页 Request VO\")\n@Data\npublic class CodegenTablePageReqVO extends PageParam {\n\n    @Schema(description = \"表名称，模糊匹配\", example = \"yudao\")\n    private String tableName;\n\n    @Schema(description = \"表描述，模糊匹配\", example = \"芋道\")\n    private String tableComment;\n\n    @Schema(description = \"实体，模糊匹配\", example = \"Yudao\")\n    private String className;\n\n    @Schema(description = \"创建时间\", example = \"[2022-07-01 00:00:00,2022-07-01 23:59:59]\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 代码生成表定义 Response VO\")\n@Data\npublic class CodegenTableRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"生成场景，参见 CodegenSceneEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer scene;\n\n    @Schema(description = \"表名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yudao\")\n    private String tableName;\n\n    @Schema(description = \"表描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String tableComment;\n\n    @Schema(description = \"备注\", example = \"我是备注\")\n    private String remark;\n\n    @Schema(description = \"模块名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"system\")\n    private String moduleName;\n\n    @Schema(description = \"业务名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"codegen\")\n    private String businessName;\n\n    @Schema(description = \"类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"CodegenTable\")\n    private String className;\n\n    @Schema(description = \"类描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"代码生成器的表定义\")\n    private String classComment;\n\n    @Schema(description = \"作者\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String author;\n\n    @Schema(description = \"模板类型，参见 CodegenTemplateTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer templateType;\n\n    @Schema(description = \"前端类型，参见 CodegenFrontTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Integer frontType;\n\n    @Schema(description = \"父菜单编号\", example = \"1024\")\n    private Long parentMenuId;\n\n    @Schema(description = \"主表的编号\", example = \"2048\")\n    private Long masterTableId;\n    @Schema(description = \"子表关联主表的字段编号\", example = \"4096\")\n    private Long subJoinColumnId;\n    @Schema(description = \"主表与子表是否一对多\", example = \"4096\")\n    private Boolean subJoinMany;\n\n    @Schema(description = \"树表的父字段编号\", example = \"8192\")\n    private Long treeParentColumnId;\n    @Schema(description = \"树表的名字字段编号\", example = \"16384\")\n    private Long treeNameColumnId;\n\n    @Schema(description = \"主键编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer dataSourceConfigId;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime updateTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 代码生成表定义创建/修改 Response VO\")\n@Data\npublic class CodegenTableSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"生成场景，参见 CodegenSceneEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"导入类型不能为空\")\n    private Integer scene;\n\n    @Schema(description = \"表名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yudao\")\n    @NotNull(message = \"表名称不能为空\")\n    private String tableName;\n\n    @Schema(description = \"表描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    @NotNull(message = \"表描述不能为空\")\n    private String tableComment;\n\n    @Schema(description = \"备注\", example = \"我是备注\")\n    private String remark;\n\n    @Schema(description = \"模块名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"system\")\n    @NotNull(message = \"模块名不能为空\")\n    private String moduleName;\n\n    @Schema(description = \"业务名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"codegen\")\n    @NotNull(message = \"业务名不能为空\")\n    private String businessName;\n\n    @Schema(description = \"类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"CodegenTable\")\n    @NotNull(message = \"类名称不能为空\")\n    private String className;\n\n    @Schema(description = \"类描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"代码生成器的表定义\")\n    @NotNull(message = \"类描述不能为空\")\n    private String classComment;\n\n    @Schema(description = \"作者\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    @NotNull(message = \"作者不能为空\")\n    private String author;\n\n    @Schema(description = \"模板类型，参见 CodegenTemplateTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"模板类型不能为空\")\n    private Integer templateType;\n\n    @Schema(description = \"前端类型，参见 CodegenFrontTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    @NotNull(message = \"前端类型不能为空\")\n    private Integer frontType;\n\n    @Schema(description = \"父菜单编号\", example = \"1024\")\n    private Long parentMenuId;\n\n    @Schema(description = \"主表的编号\", example = \"2048\")\n    private Long masterTableId;\n    @Schema(description = \"子表关联主表的字段编号\", example = \"4096\")\n    private Long subJoinColumnId;\n    @Schema(description = \"主表与子表是否一对多\", example = \"4096\")\n    private Boolean subJoinMany;\n\n    @Schema(description = \"树表的父字段编号\", example = \"8192\")\n    private Long treeParentColumnId;\n    @Schema(description = \"树表的名字字段编号\", example = \"16384\")\n    private Long treeNameColumnId;\n\n    @AssertTrue(message = \"上级菜单不能为空，请前往 [修改生成配置 -> 生成信息] 界面，设置“上级菜单”字段\")\n    @JsonIgnore\n    public boolean isParentMenuIdValid() {\n        // 生成场景为管理后台时，必须设置上级菜单，不然生成的菜单 SQL 是无父级菜单的\n        return ObjectUtil.notEqual(getScene(), CodegenSceneEnum.ADMIN.getScene())\n                || getParentMenuId() != null;\n    }\n\n    @AssertTrue(message = \"关联的父表信息不全\")\n    @JsonIgnore\n    public boolean isSubValid() {\n        return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.SUB)\n                || (ObjectUtil.isAllNotEmpty(masterTableId, subJoinColumnId, subJoinMany));\n    }\n\n    @AssertTrue(message = \"关联的树表信息不全\")\n    @JsonIgnore\n    public boolean isTreeValid() {\n        return ObjectUtil.notEqual(templateType, CodegenTemplateTypeEnum.TREE)\n                || (ObjectUtil.isAllNotEmpty(treeParentColumnId, treeNameColumnId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/DatabaseTableRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 数据库的表定义 Response VO\")\n@Data\npublic class DatabaseTableRespVO {\n\n    @Schema(description = \"表名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yuanma\")\n    private String name;\n\n    @Schema(description = \"表描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String comment;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.config;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.convert.config.ConfigConvert;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;\nimport cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants;\nimport cn.iocoder.yudao.module.infra.service.config.ConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 参数配置\")\n@RestController\n@RequestMapping(\"/infra/config\")\n@Validated\npublic class ConfigController {\n\n    @Resource\n    private ConfigService configService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建参数配置\")\n    @PreAuthorize(\"@ss.hasPermission('infra:config:create')\")\n    public CommonResult<Long> createConfig(@Valid @RequestBody ConfigSaveReqVO createReqVO) {\n        return success(configService.createConfig(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"修改参数配置\")\n    @PreAuthorize(\"@ss.hasPermission('infra:config:update')\")\n    public CommonResult<Boolean> updateConfig(@Valid @RequestBody ConfigSaveReqVO updateReqVO) {\n        configService.updateConfig(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除参数配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:config:delete')\")\n    public CommonResult<Boolean> deleteConfig(@RequestParam(\"id\") Long id) {\n        configService.deleteConfig(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-list\")\n    @Operation(summary = \"批量删除参数配置\")\n    @Parameter(name = \"ids\", description = \"编号列表\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:config:delete')\")\n    public CommonResult<Boolean> deleteConfigList(@RequestParam(\"ids\") List<Long> ids) {\n        configService.deleteConfigList(ids);\n        return success(true);\n    }\n\n    @GetMapping(value = \"/get\")\n    @Operation(summary = \"获得参数配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:config:query')\")\n    public CommonResult<ConfigRespVO> getConfig(@RequestParam(\"id\") Long id) {\n        return success(ConfigConvert.INSTANCE.convert(configService.getConfig(id)));\n    }\n\n    @GetMapping(value = \"/get-value-by-key\")\n    @Operation(summary = \"根据参数键名查询参数值\", description = \"不可见的配置，不允许返回给前端\")\n    @Parameter(name = \"key\", description = \"参数键\", required = true, example = \"yunai.biz.username\")\n    public CommonResult<String> getConfigKey(@RequestParam(\"key\") String key) {\n        ConfigDO config = configService.getConfigByKey(key);\n        if (config == null) {\n            return success(null);\n        }\n        if (!config.getVisible()) {\n            throw exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_VISIBLE);\n        }\n        return success(config.getValue());\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获取参数配置分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:config:query')\")\n    public CommonResult<PageResult<ConfigRespVO>> getConfigPage(@Valid ConfigPageReqVO pageReqVO) {\n        PageResult<ConfigDO> page = configService.getConfigPage(pageReqVO);\n        return success(ConfigConvert.INSTANCE.convertPage(page));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出参数配置\")\n    @PreAuthorize(\"@ss.hasPermission('infra:config:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportConfig(ConfigPageReqVO exportReqVO,\n                             HttpServletResponse response) throws IOException {\n        exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ConfigDO> list = configService.getConfigPage(exportReqVO).getList();\n        // 输出\n        ExcelUtils.write(response, \"参数配置.xls\", \"数据\", ConfigRespVO.class,\n                ConfigConvert.INSTANCE.convertList(list));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.config.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 参数配置分页 Request VO\")\n@Data\npublic class ConfigPageReqVO extends PageParam {\n\n    @Schema(description = \"数据源名称，模糊匹配\", example = \"名称\")\n    private String name;\n\n    @Schema(description = \"参数键名，模糊匹配\", example = \"yunai.db.username\")\n    private String key;\n\n    @Schema(description = \"参数类型，参见 SysConfigTypeEnum 枚举\", example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"创建时间\", example = \"[2022-07-01 00:00:00,2022-07-01 23:59:59]\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.config.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.infra.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 参数配置信息 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ConfigRespVO {\n\n    @Schema(description = \"参数配置序号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"参数配置序号\")\n    private Long id;\n\n    @Schema(description = \"参数分类\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"biz\")\n    @ExcelProperty(\"参数分类\")\n    private String category;\n\n    @Schema(description = \"参数名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"数据库名\")\n    @ExcelProperty(\"参数名称\")\n    private String name;\n\n    @Schema(description = \"参数键名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yunai.db.username\")\n    @ExcelProperty(\"参数键名\")\n    private String key;\n\n    @Schema(description = \"参数键值\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"参数键值\")\n    private String value;\n\n    @Schema(description = \"参数类型，参见 SysConfigTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"参数类型\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.CONFIG_TYPE)\n    private Integer type;\n\n    @Schema(description = \"是否可见\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"是否可见\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.BOOLEAN_STRING)\n    private Boolean visible;\n\n    @Schema(description = \"备注\", example = \"备注一下很帅气！\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"时间戳格式\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.config.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\n\n@Schema(description = \"管理后台 - 参数配置创建/修改 Request VO\")\n@Data\npublic class ConfigSaveReqVO {\n\n    @Schema(description = \"参数配置序号\", example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"参数分组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"biz\")\n    @NotEmpty(message = \"参数分组不能为空\")\n    @Size(max = 50, message = \"参数名称不能超过 50 个字符\")\n    private String category;\n\n    @Schema(description = \"参数名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"数据库名\")\n    @NotBlank(message = \"参数名称不能为空\")\n    @Size(max = 100, message = \"参数名称不能超过 100 个字符\")\n    private String name;\n\n    @Schema(description = \"参数键名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yunai.db.username\")\n    @NotBlank(message = \"参数键名长度不能为空\")\n    @Size(max = 100, message = \"参数键名长度不能超过 100 个字符\")\n    private String key;\n\n    @Schema(description = \"参数键值\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotBlank(message = \"参数键值不能为空\")\n    @Size(max = 500, message = \"参数键值长度不能超过 500 个字符\")\n    private String value;\n\n    @Schema(description = \"是否可见\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否可见不能为空\")\n    private Boolean visible;\n\n    @Schema(description = \"备注\", example = \"备注一下很帅气！\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DataSourceConfigController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.db;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\nimport cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 数据源配置\")\n@RestController\n@RequestMapping(\"/infra/data-source-config\")\n@Validated\npublic class DataSourceConfigController {\n\n    @Resource\n    private DataSourceConfigService dataSourceConfigService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建数据源配置\")\n    @PreAuthorize(\"@ss.hasPermission('infra:data-source-config:create')\")\n    public CommonResult<Long> createDataSourceConfig(@Valid @RequestBody DataSourceConfigSaveReqVO createReqVO) {\n        return success(dataSourceConfigService.createDataSourceConfig(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新数据源配置\")\n    @PreAuthorize(\"@ss.hasPermission('infra:data-source-config:update')\")\n    public CommonResult<Boolean> updateDataSourceConfig(@Valid @RequestBody DataSourceConfigSaveReqVO updateReqVO) {\n        dataSourceConfigService.updateDataSourceConfig(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除数据源配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:data-source-config:delete')\")\n    public CommonResult<Boolean> deleteDataSourceConfig(@RequestParam(\"id\") Long id) {\n        dataSourceConfigService.deleteDataSourceConfig(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-list\")\n    @Operation(summary = \"批量删除数据源配置\")\n    @Parameter(name = \"ids\", description = \"编号列表\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:data-source-config:delete')\")\n    public CommonResult<Boolean> deleteDataSourceConfigList(@RequestParam(\"ids\") List<Long> ids) {\n        dataSourceConfigService.deleteDataSourceConfigList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得数据源配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:data-source-config:query')\")\n    public CommonResult<DataSourceConfigRespVO> getDataSourceConfig(@RequestParam(\"id\") Long id) {\n        DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(id);\n        return success(BeanUtils.toBean(config, DataSourceConfigRespVO.class));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得数据源配置列表\")\n    @PreAuthorize(\"@ss.hasPermission('infra:data-source-config:query')\")\n    public CommonResult<List<DataSourceConfigRespVO>> getDataSourceConfigList() {\n        List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();\n        return success(BeanUtils.toBean(list, DataSourceConfigRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.db.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 数据源配置 Response VO\")\n@Data\npublic class DataSourceConfigRespVO {\n\n    @Schema(description = \"主键编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"数据源名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"test\")\n    private String name;\n\n    @Schema(description = \"数据源连接\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro\")\n    private String url;\n\n    @Schema(description = \"用户名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"root\")\n    private String username;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.db.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 数据源配置创建/修改 Request VO\")\n@Data\npublic class DataSourceConfigSaveReqVO {\n\n    @Schema(description = \"主键编号\", example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"数据源名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"test\")\n    @NotNull(message = \"数据源名称不能为空\")\n    private String name;\n\n    @Schema(description = \"数据源连接\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro\")\n    @NotNull(message = \"数据源连接不能为空\")\n    private String url;\n\n    @Schema(description = \"用户名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"root\")\n    @NotNull(message = \"用户名不能为空\")\n    private String username;\n\n    @Schema(description = \"密码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123456\")\n    @NotNull(message = \"密码不能为空\")\n    private String password;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01.Demo01ContactDO;\nimport cn.iocoder.yudao.module.infra.service.demo.demo01.Demo01ContactService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 示例联系人\")\n@RestController\n@RequestMapping(\"/infra/demo01-contact\")\n@Validated\npublic class Demo01ContactController {\n\n    @Resource\n    private Demo01ContactService demo01ContactService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建示例联系人\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo01-contact:create')\")\n    public CommonResult<Long> createDemo01Contact(@Valid @RequestBody Demo01ContactSaveReqVO createReqVO) {\n        return success(demo01ContactService.createDemo01Contact(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新示例联系人\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo01-contact:update')\")\n    public CommonResult<Boolean> updateDemo01Contact(@Valid @RequestBody Demo01ContactSaveReqVO updateReqVO) {\n        demo01ContactService.updateDemo01Contact(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除示例联系人\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:demo01-contact:delete')\")\n    public CommonResult<Boolean> deleteDemo01Contact(@RequestParam(\"id\") Long id) {\n        demo01ContactService.deleteDemo01Contact(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-list\")\n    @Parameter(name = \"ids\", description = \"编号\", required = true)\n    @Operation(summary = \"批量删除示例联系人\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo01-contact:delete')\")\n    public CommonResult<Boolean> deleteDemo0iContactList(@RequestParam(\"ids\") List<Long> ids) {\n        demo01ContactService.deleteDemo0iContactList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得示例联系人\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo01-contact:query')\")\n    public CommonResult<Demo01ContactRespVO> getDemo01Contact(@RequestParam(\"id\") Long id) {\n        Demo01ContactDO demo01Contact = demo01ContactService.getDemo01Contact(id);\n        return success(BeanUtils.toBean(demo01Contact, Demo01ContactRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得示例联系人分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo01-contact:query')\")\n    public CommonResult<PageResult<Demo01ContactRespVO>> getDemo01ContactPage(@Valid Demo01ContactPageReqVO pageReqVO) {\n        PageResult<Demo01ContactDO> pageResult = demo01ContactService.getDemo01ContactPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, Demo01ContactRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出示例联系人 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo01-contact:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportDemo01ContactExcel(@Valid Demo01ContactPageReqVO pageReqVO,\n                                         HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<Demo01ContactDO> list = demo01ContactService.getDemo01ContactPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"示例联系人.xls\", \"数据\", Demo01ContactRespVO.class,\n                BeanUtils.toBean(list, Demo01ContactRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 示例联系人分页 Request VO\")\n@Data\npublic class Demo01ContactPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"张三\")\n    private String name;\n\n    @Schema(description = \"性别\", example = \"1\")\n    private Integer sex;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 示例联系人 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class Demo01ContactRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"21555\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"出生年\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生年\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你说的对\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"头像\")\n    @ExcelProperty(\"头像\")\n    private String avatar;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 示例联系人新增/修改 Request VO\")\n@Data\npublic class Demo01ContactSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"21555\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"出生年\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生年不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你说的对\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"头像\")\n    private String avatar;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;\nimport cn.iocoder.yudao.module.infra.service.demo.demo02.Demo02CategoryService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 示例分类\")\n@RestController\n@RequestMapping(\"/infra/demo02-category\")\n@Validated\npublic class Demo02CategoryController {\n\n    @Resource\n    private Demo02CategoryService demo02CategoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建示例分类\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo02-category:create')\")\n    public CommonResult<Long> createDemo02Category(@Valid @RequestBody Demo02CategorySaveReqVO createReqVO) {\n        return success(demo02CategoryService.createDemo02Category(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新示例分类\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo02-category:update')\")\n    public CommonResult<Boolean> updateDemo02Category(@Valid @RequestBody Demo02CategorySaveReqVO updateReqVO) {\n        demo02CategoryService.updateDemo02Category(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除示例分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:demo02-category:delete')\")\n    public CommonResult<Boolean> deleteDemo02Category(@RequestParam(\"id\") Long id) {\n        demo02CategoryService.deleteDemo02Category(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得示例分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo02-category:query')\")\n    public CommonResult<Demo02CategoryRespVO> getDemo02Category(@RequestParam(\"id\") Long id) {\n        Demo02CategoryDO demo02Category = demo02CategoryService.getDemo02Category(id);\n        return success(BeanUtils.toBean(demo02Category, Demo02CategoryRespVO.class));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得示例分类列表\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo02-category:query')\")\n    public CommonResult<List<Demo02CategoryRespVO>> getDemo02CategoryList(@Valid Demo02CategoryListReqVO listReqVO) {\n        List<Demo02CategoryDO> list = demo02CategoryService.getDemo02CategoryList(listReqVO);\n        return success(BeanUtils.toBean(list, Demo02CategoryRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出示例分类 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo02-category:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportDemo02CategoryExcel(@Valid Demo02CategoryListReqVO listReqVO,\n                                          HttpServletResponse response) throws IOException {\n        List<Demo02CategoryDO> list = demo02CategoryService.getDemo02CategoryList(listReqVO);\n        // 导出 Excel\n        ExcelUtils.write(response, \"示例分类.xls\", \"数据\", Demo02CategoryRespVO.class,\n                BeanUtils.toBean(list, Demo02CategoryRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 示例分类列表 Request VO\")\n@Data\npublic class Demo02CategoryListReqVO {\n\n    @Schema(description = \"名字\", example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"父级编号\", example = \"6080\")\n    private Long parentId;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 示例分类 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class Demo02CategoryRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10304\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"父级编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6080\")\n    @ExcelProperty(\"父级编号\")\n    private Long parentId;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 示例分类新增/修改 Request VO\")\n@Data\npublic class Demo02CategorySaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10304\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"父级编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6080\")\n    @NotNull(message = \"父级编号不能为空\")\n    private Long parentId;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/erp/Demo03StudentErpController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport cn.iocoder.yudao.module.infra.service.demo.demo03.erp.Demo03StudentErpService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/demo03-student-erp\")\n@Validated\npublic class Demo03StudentErpController {\n\n    @Resource\n    private Demo03StudentErpService demo03StudentErpService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:create')\")\n    public CommonResult<Long> createDemo03Student(@Valid @RequestBody Demo03StudentErpSaveReqVO createReqVO) {\n        return success(demo03StudentErpService.createDemo03Student(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:update')\")\n    public CommonResult<Boolean> updateDemo03Student(@Valid @RequestBody Demo03StudentErpSaveReqVO updateReqVO) {\n        demo03StudentErpService.updateDemo03Student(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03Student(@RequestParam(\"id\") Long id) {\n        demo03StudentErpService.deleteDemo03Student(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-list\")\n    @Parameter(name = \"ids\", description = \"编号\", required = true)\n    @Operation(summary = \"批量删除学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03StudentList(@RequestParam(\"ids\") List<Long> ids) {\n        demo03StudentErpService.deleteDemo03StudentList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<Demo03StudentErpRespVO> getDemo03Student(@RequestParam(\"id\") Long id) {\n        Demo03StudentDO demo03Student = demo03StudentErpService.getDemo03Student(id);\n        return success(BeanUtils.toBean(demo03Student, Demo03StudentErpRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<PageResult<Demo03StudentErpRespVO>> getDemo03StudentPage(@Valid Demo03StudentErpPageReqVO pageReqVO) {\n        PageResult<Demo03StudentDO> pageResult = demo03StudentErpService.getDemo03StudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, Demo03StudentErpRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportDemo03StudentExcel(@Valid Demo03StudentErpPageReqVO pageReqVO,\n                                         HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<Demo03StudentDO> list = demo03StudentErpService.getDemo03StudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", Demo03StudentErpRespVO.class,\n                BeanUtils.toBean(list, Demo03StudentErpRespVO.class));\n    }\n\n    // ==================== 子表（学生课程） ====================\n\n    @GetMapping(\"/demo03-course/page\")\n    @Operation(summary = \"获得学生课程分页\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<PageResult<Demo03CourseDO>> getDemo03CoursePage(PageParam pageReqVO,\n                                                                        @RequestParam(\"studentId\") Long studentId) {\n        return success(demo03StudentErpService.getDemo03CoursePage(pageReqVO, studentId));\n    }\n\n    @PostMapping(\"/demo03-course/create\")\n    @Operation(summary = \"创建学生课程\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:create')\")\n    public CommonResult<Long> createDemo03Course(@Valid @RequestBody Demo03CourseDO demo03Course) {\n        return success(demo03StudentErpService.createDemo03Course(demo03Course));\n    }\n\n    @PutMapping(\"/demo03-course/update\")\n    @Operation(summary = \"更新学生课程\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:update')\")\n    public CommonResult<Boolean> updateDemo03Course(@Valid @RequestBody Demo03CourseDO demo03Course) {\n        demo03StudentErpService.updateDemo03Course(demo03Course);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/demo03-course/delete\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @Operation(summary = \"删除学生课程\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03Course(@RequestParam(\"id\") Long id) {\n        demo03StudentErpService.deleteDemo03Course(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/demo03-course/delete-list\")\n    @Parameter(name = \"ids\", description = \"编号\", required = true)\n    @Operation(summary = \"批量删除学生课程\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03CourseList(@RequestParam(\"ids\") List<Long> ids) {\n        demo03StudentErpService.deleteDemo03CourseList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/demo03-course/get\")\n    @Operation(summary = \"获得学生课程\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<Demo03CourseDO> getDemo03Course(@RequestParam(\"id\") Long id) {\n        return success(demo03StudentErpService.getDemo03Course(id));\n    }\n\n    // ==================== 子表（学生班级） ====================\n\n    @GetMapping(\"/demo03-grade/page\")\n    @Operation(summary = \"获得学生班级分页\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<PageResult<Demo03GradeDO>> getDemo03GradePage(PageParam pageReqVO,\n                                                                      @RequestParam(\"studentId\") Long studentId) {\n        return success(demo03StudentErpService.getDemo03GradePage(pageReqVO, studentId));\n    }\n\n    @PostMapping(\"/demo03-grade/create\")\n    @Operation(summary = \"创建学生班级\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:create')\")\n    public CommonResult<Long> createDemo03Grade(@Valid @RequestBody Demo03GradeDO demo03Grade) {\n        return success(demo03StudentErpService.createDemo03Grade(demo03Grade));\n    }\n\n    @PutMapping(\"/demo03-grade/update\")\n    @Operation(summary = \"更新学生班级\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:update')\")\n    public CommonResult<Boolean> updateDemo03Grade(@Valid @RequestBody Demo03GradeDO demo03Grade) {\n        demo03StudentErpService.updateDemo03Grade(demo03Grade);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/demo03-grade/delete\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @Operation(summary = \"删除学生班级\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03Grade(@RequestParam(\"id\") Long id) {\n        demo03StudentErpService.deleteDemo03Grade(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/demo03-grade/delete-list\")\n    @Parameter(name = \"ids\", description = \"编号\", required = true)\n    @Operation(summary = \"批量删除学生班级\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03GradeList(@RequestParam(\"ids\") List<Long> ids) {\n        demo03StudentErpService.deleteDemo03GradeList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/demo03-grade/get\")\n    @Operation(summary = \"获得学生班级\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<Demo03GradeDO> getDemo03Grade(@RequestParam(\"id\") Long id) {\n        return success(demo03StudentErpService.getDemo03Grade(id));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/erp/vo/Demo03StudentErpPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\npublic class Demo03StudentErpPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"性别\")\n    private Integer sex;\n\n    @Schema(description = \"简介\", example = \"随便\")\n    private String description;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/erp/vo/Demo03StudentErpRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class Demo03StudentErpRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8525\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"随便\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/erp/vo/Demo03StudentErpSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class Demo03StudentErpSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8525\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"随便\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/inner/Demo03StudentInnerController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport cn.iocoder.yudao.module.infra.service.demo.demo03.inner.Demo03StudentInnerService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/demo03-student-inner\")\n@Validated\npublic class Demo03StudentInnerController {\n\n    @Resource\n    private Demo03StudentInnerService demo03StudentInnerService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:create')\")\n    public CommonResult<Long> createDemo03Student(@Valid @RequestBody Demo03StudentInnerSaveReqVO createReqVO) {\n        return success(demo03StudentInnerService.createDemo03Student(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:update')\")\n    public CommonResult<Boolean> updateDemo03Student(@Valid @RequestBody Demo03StudentInnerSaveReqVO updateReqVO) {\n        demo03StudentInnerService.updateDemo03Student(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03Student(@RequestParam(\"id\") Long id) {\n        demo03StudentInnerService.deleteDemo03Student(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-list\")\n    @Parameter(name = \"ids\", description = \"编号\", required = true)\n    @Operation(summary = \"批量删除学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03StudentList(@RequestParam(\"ids\") List<Long> ids) {\n        demo03StudentInnerService.deleteDemo03StudentList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<Demo03StudentInnerRespVO> getDemo03Student(@RequestParam(\"id\") Long id) {\n        Demo03StudentDO demo03Student = demo03StudentInnerService.getDemo03Student(id);\n        return success(BeanUtils.toBean(demo03Student, Demo03StudentInnerRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<PageResult<Demo03StudentInnerRespVO>> getDemo03StudentPage(@Valid Demo03StudentInnerPageReqVO pageReqVO) {\n        PageResult<Demo03StudentDO> pageResult = demo03StudentInnerService.getDemo03StudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, Demo03StudentInnerRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportDemo03StudentExcel(@Valid Demo03StudentInnerPageReqVO pageReqVO,\n                                         HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<Demo03StudentDO> list = demo03StudentInnerService.getDemo03StudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", Demo03StudentInnerRespVO.class,\n                BeanUtils.toBean(list, Demo03StudentInnerRespVO.class));\n    }\n\n    // ==================== 子表（学生课程） ====================\n\n    @GetMapping(\"/demo03-course/list-by-student-id\")\n    @Operation(summary = \"获得学生课程列表\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<List<Demo03CourseDO>> getDemo03CourseListByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(demo03StudentInnerService.getDemo03CourseListByStudentId(studentId));\n    }\n\n    // ==================== 子表（学生班级） ====================\n\n    @GetMapping(\"/demo03-grade/get-by-student-id\")\n    @Operation(summary = \"获得学生班级\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<Demo03GradeDO> getDemo03GradeByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(demo03StudentInnerService.getDemo03GradeByStudentId(studentId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/inner/vo/Demo03StudentInnerPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\npublic class Demo03StudentInnerPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"性别\")\n    private Integer sex;\n\n    @Schema(description = \"简介\", example = \"随便\")\n    private String description;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/inner/vo/Demo03StudentInnerRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class Demo03StudentInnerRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8525\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"随便\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/inner/vo/Demo03StudentInnerSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo;\n\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class Demo03StudentInnerSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8525\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"随便\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"学生课程列表\")\n    private List<Demo03CourseDO> demo03Courses;\n\n    @Schema(description = \"学生班级\")\n    private Demo03GradeDO demo03Grade;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/Demo03StudentNormalController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport cn.iocoder.yudao.module.infra.service.demo.demo03.normal.Demo03StudentNormalService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/demo03-student-normal\")\n@Validated\npublic class Demo03StudentNormalController {\n\n    @Resource\n    private Demo03StudentNormalService demo03StudentNormalService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:create')\")\n    public CommonResult<Long> createDemo03Student(@Valid @RequestBody Demo03StudentNormalSaveReqVO createReqVO) {\n        return success(demo03StudentNormalService.createDemo03Student(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:update')\")\n    public CommonResult<Boolean> updateDemo03Student(@Valid @RequestBody Demo03StudentNormalSaveReqVO updateReqVO) {\n        demo03StudentNormalService.updateDemo03Student(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03Student(@RequestParam(\"id\") Long id) {\n        demo03StudentNormalService.deleteDemo03Student(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-list\")\n    @Parameter(name = \"ids\", description = \"编号\", required = true)\n    @Operation(summary = \"批量删除学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:delete')\")\n    public CommonResult<Boolean> deleteDemo03StudentList(@RequestParam(\"ids\") List<Long> ids) {\n        demo03StudentNormalService.deleteDemo03StudentList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<Demo03StudentNormalRespVO> getDemo03Student(@RequestParam(\"id\") Long id) {\n        Demo03StudentDO demo03Student = demo03StudentNormalService.getDemo03Student(id);\n        return success(BeanUtils.toBean(demo03Student, Demo03StudentNormalRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<PageResult<Demo03StudentNormalRespVO>> getDemo03StudentPage(@Valid Demo03StudentNormalPageReqVO pageReqVO) {\n        PageResult<Demo03StudentDO> pageResult = demo03StudentNormalService.getDemo03StudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, Demo03StudentNormalRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportDemo03StudentExcel(@Valid Demo03StudentNormalPageReqVO pageReqVO,\n                                         HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<Demo03StudentDO> list = demo03StudentNormalService.getDemo03StudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", Demo03StudentNormalRespVO.class,\n                BeanUtils.toBean(list, Demo03StudentNormalRespVO.class));\n    }\n\n    // ==================== 子表（学生课程） ====================\n\n    @GetMapping(\"/demo03-course/list-by-student-id\")\n    @Operation(summary = \"获得学生课程列表\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<List<Demo03CourseDO>> getDemo03CourseListByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(demo03StudentNormalService.getDemo03CourseListByStudentId(studentId));\n    }\n\n    // ==================== 子表（学生班级） ====================\n\n    @GetMapping(\"/demo03-grade/get-by-student-id\")\n    @Operation(summary = \"获得学生班级\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:demo03-student:query')\")\n    public CommonResult<Demo03GradeDO> getDemo03GradeByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(demo03StudentNormalService.getDemo03GradeByStudentId(studentId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentNormalPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\npublic class Demo03StudentNormalPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"性别\")\n    private Integer sex;\n\n    @Schema(description = \"简介\", example = \"随便\")\n    private String description;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentNormalRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class Demo03StudentNormalRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8525\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"随便\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentNormalSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo;\n\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class Demo03StudentNormalSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8525\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"随便\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"学生课程列表\")\n    private List<Demo03CourseDO> demo03Courses;\n\n    @Schema(description = \"学生班级\")\n    private Demo03GradeDO demo03Grade;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/package-info.java",
    "content": "/**\n * 代码生成示例\n *\n * 1. demo01：单表（增删改查）\n * 2. demo02：单表（树形结构）\n * 3. demo03：主子表（标准模式）+ 主子表（ERP 模式）+ 主子表（内嵌模式）\n */\npackage cn.iocoder.yudao.module.infra.controller.admin.demo;"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.http",
    "content": "### 请求 /infra/file-config/create 接口 => 成功\nPOST {{baseUrl}}/infra/file-config/create\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"name\": \"S3 - 七牛云\",\n  \"remark\": \"\",\n  \"storage\": 20,\n  \"config\": {\n    \"accessKey\": \"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\n    \"accessSecret\": \"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\",\n    \"bucket\": \"ruoyi-vue-pro\",\n    \"endpoint\": \"s3-cn-south-1.qiniucs.com\",\n    \"domain\": \"http://test.yudao.iocoder.cn\",\n    \"region\": \"oss-cn-beijing\"\n  }\n}\n\n### 请求 /infra/file-config/update 接口 => 成功\nPUT {{baseUrl}}/infra/file-config/update\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"id\": 2,\n  \"name\": \"S3 - 七牛云\",\n  \"remark\": \"\",\n  \"config\": {\n    \"accessKey\": \"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\n    \"accessSecret\": \"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\",\n    \"bucket\": \"ruoyi-vue-pro\",\n    \"endpoint\": \"s3-cn-south-1.qiniucs.com\",\n    \"domain\": \"http://test.yudao.iocoder.cn\",\n    \"region\": \"oss-cn-beijing\"\n  }\n}\n\n### 请求 /infra/file-config/test 接口 => 成功\nGET {{baseUrl}}/infra/file-config/test?id=2\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;\nimport cn.iocoder.yudao.module.infra.service.file.FileConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 文件配置\")\n@RestController\n@RequestMapping(\"/infra/file-config\")\n@Validated\npublic class FileConfigController {\n\n    @Resource\n    private FileConfigService fileConfigService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建文件配置\")\n    @PreAuthorize(\"@ss.hasPermission('infra:file-config:create')\")\n    public CommonResult<Long> createFileConfig(@Valid @RequestBody FileConfigSaveReqVO createReqVO) {\n        return success(fileConfigService.createFileConfig(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新文件配置\")\n    @PreAuthorize(\"@ss.hasPermission('infra:file-config:update')\")\n    public CommonResult<Boolean> updateFileConfig(@Valid @RequestBody FileConfigSaveReqVO updateReqVO) {\n        fileConfigService.updateFileConfig(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-master\")\n    @Operation(summary = \"更新文件配置为 Master\")\n    @PreAuthorize(\"@ss.hasPermission('infra:file-config:update')\")\n    public CommonResult<Boolean> updateFileConfigMaster(@RequestParam(\"id\") Long id) {\n        fileConfigService.updateFileConfigMaster(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除文件配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:file-config:delete')\")\n    public CommonResult<Boolean> deleteFileConfig(@RequestParam(\"id\") Long id) {\n        fileConfigService.deleteFileConfig(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-list\")\n    @Operation(summary = \"批量删除文件配置\")\n    @Parameter(name = \"ids\", description = \"编号列表\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:file-config:delete')\")\n    public CommonResult<Boolean> deleteFileConfigList(@RequestParam(\"ids\") List<Long> ids) {\n        fileConfigService.deleteFileConfigList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得文件配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:file-config:query')\")\n    public CommonResult<FileConfigRespVO> getFileConfig(@RequestParam(\"id\") Long id) {\n        FileConfigDO config = fileConfigService.getFileConfig(id);\n        return success(BeanUtils.toBean(config, FileConfigRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得文件配置分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:file-config:query')\")\n    public CommonResult<PageResult<FileConfigRespVO>> getFileConfigPage(@Valid FileConfigPageReqVO pageVO) {\n        PageResult<FileConfigDO> pageResult = fileConfigService.getFileConfigPage(pageVO);\n        return success(BeanUtils.toBean(pageResult, FileConfigRespVO.class));\n    }\n\n    @GetMapping(\"/test\")\n    @Operation(summary = \"测试文件配置是否正确\")\n    @PreAuthorize(\"@ss.hasPermission('infra:file-config:query')\")\n    public CommonResult<String> testFileConfig(@RequestParam(\"id\") Long id) throws Exception {\n        String url = fileConfigService.testFileConfig(id);\n        return success(url);\n    }\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.core.util.URLUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;\nimport cn.iocoder.yudao.module.infra.service.file.FileService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;\n\n@Tag(name = \"管理后台 - 文件存储\")\n@RestController\n@RequestMapping(\"/infra/file\")\n@Validated\n@Slf4j\npublic class FileController {\n\n    @Resource\n    private FileService fileService;\n\n    @PostMapping(\"/upload\")\n    @Operation(summary = \"上传文件\", description = \"模式一：后端上传文件\")\n    @Parameter(name = \"file\", description = \"文件附件\", required = true,\n            schema = @Schema(type = \"string\", format = \"binary\"))\n    public CommonResult<String> uploadFile(@Valid FileUploadReqVO uploadReqVO) throws Exception {\n        MultipartFile file = uploadReqVO.getFile();\n        byte[] content = IoUtil.readBytes(file.getInputStream());\n        return success(fileService.createFile(content, file.getOriginalFilename(),\n                uploadReqVO.getDirectory(), file.getContentType()));\n    }\n\n    @GetMapping(\"/presigned-url\")\n    @Operation(summary = \"获取文件预签名地址（上传）\", description = \"模式二：前端上传文件：用于前端直接上传七牛、阿里云 OSS 等文件存储器\")\n    @Parameters({\n            @Parameter(name = \"name\", description = \"文件名称\", required = true),\n            @Parameter(name = \"directory\", description = \"文件目录\")\n    })\n    public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(\n            @RequestParam(\"name\") String name,\n            @RequestParam(value = \"directory\", required = false) String directory) {\n        return success(fileService.presignPutUrl(name, directory));\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建文件\", description = \"模式二：前端上传文件：配合 presigned-url 接口，记录上传了上传的文件\")\n    public CommonResult<Long> createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {\n        return success(fileService.createFile(createReqVO));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得文件\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:file:query')\")\n    public CommonResult<FileRespVO> getFile(@RequestParam(\"id\") Long id) {\n        return success(BeanUtils.toBean(fileService.getFile(id), FileRespVO.class));\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除文件\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:file:delete')\")\n    public CommonResult<Boolean> deleteFile(@RequestParam(\"id\") Long id) throws Exception {\n        fileService.deleteFile(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-list\")\n    @Operation(summary = \"批量删除文件\")\n    @Parameter(name = \"ids\", description = \"编号列表\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:file:delete')\")\n    public CommonResult<Boolean> deleteFileList(@RequestParam(\"ids\") List<Long> ids) throws Exception {\n        fileService.deleteFileList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/{configId}/get/**\")\n    @PermitAll\n    @TenantIgnore\n    @Operation(summary = \"下载文件\")\n    @Parameter(name = \"configId\", description = \"配置编号\", required = true)\n    public void getFileContent(HttpServletRequest request,\n                               HttpServletResponse response,\n                               @PathVariable(\"configId\") Long configId) throws Exception {\n        // 获取请求的路径\n        String path = StrUtil.subAfter(request.getRequestURI(), \"/get/\", false);\n        if (StrUtil.isEmpty(path)) {\n            throw new IllegalArgumentException(\"结尾的 path 路径必须传递\");\n        }\n        // 解码，解决中文路径的问题\n        // https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/807/\n        // https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1432/\n        path = URLUtil.decode(path, StandardCharsets.UTF_8, false);\n\n        // 读取内容\n        byte[] content = fileService.getFileContent(configId, path);\n        if (content == null) {\n            log.warn(\"[getFileContent][configId({}) path({}) 文件不存在]\", configId, path);\n            response.setStatus(HttpStatus.NOT_FOUND.value());\n            return;\n        }\n        writeAttachment(response, path, content);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得文件分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:file:query')\")\n    public CommonResult<PageResult<FileRespVO>> getFilePage(@Valid FilePageReqVO pageVO) {\n        PageResult<FileDO> pageResult = fileService.getFilePage(pageVO);\n        return success(BeanUtils.toBean(pageResult, FileRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file.vo.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 文件配置分页 Request VO\")\n@Data\npublic class FileConfigPageReqVO extends PageParam {\n\n    @Schema(description = \"配置名\", example = \"S3 - 阿里云\")\n    private String name;\n\n    @Schema(description = \"存储器\", example = \"1\")\n    private Integer storage;\n\n    @Schema(description = \"创建时间\", example = \"[2022-07-01 00:00:00, 2022-07-01 23:59:59]\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file.vo.config;\n\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 文件配置 Response VO\")\n@Data\npublic class FileConfigRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"配置名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"S3 - 阿里云\")\n    private String name;\n\n    @Schema(description = \"存储器，参见 FileStorageEnum 枚举类\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer storage;\n\n    @Schema(description = \"是否为主配置\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean master;\n\n    @Schema(description = \"存储配置\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private FileClientConfig config;\n\n    @Schema(description = \"备注\", example = \"我是备注\")\n    private String remark;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file.vo.config;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - 文件配置创建/修改 Request VO\")\n@Data\npublic class FileConfigSaveReqVO {\n\n    @Schema(description = \"编号\", example = \"1\")\n    private Long id;\n\n    @Schema(description = \"配置名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"S3 - 阿里云\")\n    @NotNull(message = \"配置名不能为空\")\n    private String name;\n\n    @Schema(description = \"存储器，参见 FileStorageEnum 枚举类\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"存储器不能为空\")\n    private Integer storage;\n\n    @Schema(description = \"存储配置,配置是动态参数，所以使用 Map 接收\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"存储配置不能为空\")\n    private Map<String, Object> config;\n\n    @Schema(description = \"备注\", example = \"我是备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 文件创建 Request VO\")\n@Data\npublic class FileCreateReqVO {\n\n    @NotNull(message = \"文件配置编号不能为空\")\n    @Schema(description = \"文件配置编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11\")\n    private Long configId;\n\n    @NotNull(message = \"文件路径不能为空\")\n    @Schema(description = \"文件路径\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yudao.jpg\")\n    private String path;\n\n    @NotNull(message = \"原文件名不能为空\")\n    @Schema(description = \"原文件名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yudao.jpg\")\n    private String name;\n\n    @NotNull(message = \"文件 URL不能为空\")\n    @Schema(description = \"文件 URL\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/yudao.jpg\")\n    private String url;\n\n    @Schema(description = \"文件 MIME 类型\", example = \"application/octet-stream\")\n    private String type;\n\n    @Schema(description = \"文件大小\", example = \"2048\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Long size;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 文件分页 Request VO\")\n@Data\npublic class FilePageReqVO extends PageParam {\n\n    @Schema(description = \"文件路径，模糊匹配\", example = \"yudao\")\n    private String path;\n\n    @Schema(description = \"文件类型，模糊匹配\", example = \"jpg\")\n    private String type;\n\n    @Schema(description = \"创建时间\", example = \"[2022-07-01 00:00:00, 2022-07-01 23:59:59]\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@AllArgsConstructor\n@NoArgsConstructor\n@Schema(description = \"管理后台 - 文件预签名地址 Response VO\")\n@Data\npublic class FilePresignedUrlRespVO {\n\n    @Schema(description = \"配置编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11\")\n    private Long configId;\n\n    @Schema(description = \"文件上传 URL\", requiredMode = Schema.RequiredMode.REQUIRED,\n            example = \"https://s3.cn-south-1.qiniucs.com/ruoyi-vue-pro/758d3a5387507358c7236de4c8f96de1c7f5097ff6a7722b34772fb7b76b140f.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS%2F20240217%2Fcn-south-1%2Fs3%2Faws4_request&X-Amz-Date=20240217T123222Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=a29f33770ab79bf523ccd4034d0752ac545f3c2a3b17baa1eb4e280cfdccfda5\")\n    private String uploadUrl;\n\n    /**\n     * 为什么要返回 url 字段？\n     *\n     * 前端上传完文件后，需要使用该 URL 进行访问\n     */\n    @Schema(description = \"文件访问 URL\", requiredMode = Schema.RequiredMode.REQUIRED,\n            example = \"https://test.yudao.iocoder.cn/758d3a5387507358c7236de4c8f96de1c7f5097ff6a7722b34772fb7b76b140f.png\")\n    private String url;\n\n    /**\n     * 为什么要返回 path 字段？\n     *\n     * 前端上传完文件后，需要调用 createFile 记录下 path 路径\n     */\n    @Schema(description = \"文件路径\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"xxx.png\")\n    private String path;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 文件 Response VO,不返回 content 字段，太大\")\n@Data\npublic class FileRespVO {\n\n    @Schema(description = \"文件编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"配置编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11\")\n    private Long configId;\n\n    @Schema(description = \"文件路径\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yudao.jpg\")\n    private String path;\n\n    @Schema(description = \"原文件名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"yudao.jpg\")\n    private String name;\n\n    @Schema(description = \"文件 URL\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/yudao.jpg\")\n    private String url;\n\n    @Schema(description = \"文件MIME类型\", example = \"application/octet-stream\")\n    private String type;\n\n    @Schema(description = \"文件大小\", example = \"2048\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Long size;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileUploadReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 上传文件 Request VO\")\n@Data\npublic class FileUploadReqVO {\n\n    @Schema(description = \"文件附件\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"文件附件不能为空\")\n    private MultipartFile file;\n\n    @Schema(description = \"文件目录\", example = \"XXX/YYY\")\n    private String directory;\n\n    @AssertTrue(message = \"文件目录不正确\")\n    @JsonIgnore\n    public boolean isDirectoryValid() {\n        return isDirectoryValid(directory);\n    }\n\n    public static boolean isDirectoryValid(String directory) {\n        // 1. 不能包含 .. 防止目录穿越\n        // 2. 不能以 / 或 \\ 开头，防止上传到根目录\n        return !StrUtil.contains(directory, \"..\")\n                && !StrUtil.startWithAny(directory, \"/\", \"\\\\\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiAccessLogController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.logger;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogRespVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO;\nimport cn.iocoder.yudao.module.infra.service.logger.ApiAccessLogService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - API 访问日志\")\n@RestController\n@RequestMapping(\"/infra/api-access-log\")\n@Validated\npublic class ApiAccessLogController {\n\n    @Resource\n    private ApiAccessLogService apiAccessLogService;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得 API 访问日志\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:api-access-log:query')\")\n    public CommonResult<ApiAccessLogRespVO> getApiAccessLog(@RequestParam(\"id\") Long id) {\n        ApiAccessLogDO apiAccessLog = apiAccessLogService.getApiAccessLog(id);\n        return success(BeanUtils.toBean(apiAccessLog, ApiAccessLogRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得API 访问日志分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:api-access-log:query')\")\n    public CommonResult<PageResult<ApiAccessLogRespVO>> getApiAccessLogPage(@Valid ApiAccessLogPageReqVO pageReqVO) {\n        PageResult<ApiAccessLogDO> pageResult = apiAccessLogService.getApiAccessLogPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, ApiAccessLogRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出API 访问日志 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:api-access-log:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportApiAccessLogExcel(@Valid ApiAccessLogPageReqVO exportReqVO,\n                                        HttpServletResponse response) throws IOException {\n        exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ApiAccessLogDO> list = apiAccessLogService.getApiAccessLogPage(exportReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"API 访问日志.xls\", \"数据\", ApiAccessLogRespVO.class,\n                BeanUtils.toBean(list, ApiAccessLogRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiErrorLogController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.logger;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogRespVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO;\nimport cn.iocoder.yudao.module.infra.service.logger.ApiErrorLogService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - API 错误日志\")\n@RestController\n@RequestMapping(\"/infra/api-error-log\")\n@Validated\npublic class ApiErrorLogController {\n\n    @Resource\n    private ApiErrorLogService apiErrorLogService;\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新 API 错误日志的状态\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\"),\n            @Parameter(name = \"processStatus\", description = \"处理状态\", required = true, example = \"1\")\n    })\n    @PreAuthorize(\"@ss.hasPermission('infra:api-error-log:update-status')\")\n    public CommonResult<Boolean> updateApiErrorLogProcess(@RequestParam(\"id\") Long id,\n                                                          @RequestParam(\"processStatus\") Integer processStatus) {\n        apiErrorLogService.updateApiErrorLogProcess(id, processStatus, getLoginUserId());\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得 API 错误日志\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:api-error-log:query')\")\n    public CommonResult<ApiErrorLogRespVO> getApiErrorLog(@RequestParam(\"id\") Long id) {\n        ApiErrorLogDO apiErrorLog = apiErrorLogService.getApiErrorLog(id);\n        return success(BeanUtils.toBean(apiErrorLog, ApiErrorLogRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得 API 错误日志分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:api-error-log:query')\")\n    public CommonResult<PageResult<ApiErrorLogRespVO>> getApiErrorLogPage(@Valid ApiErrorLogPageReqVO pageReqVO) {\n        PageResult<ApiErrorLogDO> pageResult = apiErrorLogService.getApiErrorLogPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, ApiErrorLogRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出 API 错误日志 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:api-error-log:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportApiErrorLogExcel(@Valid ApiErrorLogPageReqVO exportReqVO,\n                                       HttpServletResponse response) throws IOException {\n        exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<ApiErrorLogDO> list = apiErrorLogService.getApiErrorLogPage(exportReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"API 错误日志.xls\", \"数据\", ApiErrorLogRespVO.class,\n                BeanUtils.toBean(list, ApiErrorLogRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - API 访问日志分页 Request VO\")\n@Data\npublic class ApiAccessLogPageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"666\")\n    private Long userId;\n\n    @Schema(description = \"用户类型\", example = \"2\")\n    private Integer userType;\n\n    @Schema(description = \"应用名\", example = \"dashboard\")\n    private String applicationName;\n\n    @Schema(description = \"请求地址，模糊匹配\", example = \"/xxx/yyy\")\n    private String requestUrl;\n\n    @Schema(description = \"开始时间\", example = \"[2022-07-01 00:00:00, 2022-07-01 23:59:59]\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] beginTime;\n\n    @Schema(description = \"执行时长,大于等于，单位：毫秒\", example = \"100\")\n    private Integer duration;\n\n    @Schema(description = \"结果码\", example = \"0\")\n    private Integer resultCode;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.infra.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - API 访问日志 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ApiAccessLogRespVO {\n\n    @Schema(description = \"日志主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"日志主键\")\n    private Long id;\n\n    @Schema(description = \"链路追踪编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"66600cb6-7852-11eb-9439-0242ac130002\")\n    @ExcelProperty(\"链路追踪编号\")\n    private String traceId;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"666\")\n    @ExcelProperty(\"用户编号\")\n    private Long userId;\n\n    @Schema(description = \"用户类型，参见 UserTypeEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(value = \"用户类型\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.USER_TYPE)\n    private Integer userType;\n\n    @Schema(description = \"应用名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"dashboard\")\n    @ExcelProperty(\"应用名\")\n    private String applicationName;\n\n    @Schema(description = \"请求方法名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"GET\")\n    @ExcelProperty(\"请求方法名\")\n    private String requestMethod;\n\n    @Schema(description = \"请求地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"/xxx/yyy\")\n    @ExcelProperty(\"请求地址\")\n    private String requestUrl;\n\n    @Schema(description = \"请求参数\")\n    @ExcelProperty(\"请求参数\")\n    private String requestParams;\n\n    @Schema(description = \"响应结果\")\n    @ExcelProperty(\"响应结果\")\n    private String responseBody;\n\n    @Schema(description = \"用户 IP\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"127.0.0.1\")\n    @ExcelProperty(\"用户 IP\")\n    private String userIp;\n\n    @Schema(description = \"浏览器 UA\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Mozilla/5.0\")\n    @ExcelProperty(\"浏览器 UA\")\n    private String userAgent;\n\n    @Schema(description = \"操作模块\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"商品模块\")\n    @ExcelProperty(\"操作模块\")\n    private String operateModule;\n\n    @Schema(description = \"操作名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"创建商品\")\n    @ExcelProperty(\"操作名\")\n    private String operateName;\n\n    @Schema(description = \"操作分类\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"操作分类\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.OPERATE_TYPE)\n    private Integer operateType;\n\n    @Schema(description = \"开始请求时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"开始请求时间\")\n    private LocalDateTime beginTime;\n\n    @Schema(description = \"结束请求时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"结束请求时间\")\n    private LocalDateTime endTime;\n\n    @Schema(description = \"执行时长\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    @ExcelProperty(\"执行时长\")\n    private Integer duration;\n\n    @Schema(description = \"结果码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @ExcelProperty(\"结果码\")\n    private Integer resultCode;\n\n    @Schema(description = \"结果提示\", example = \"芋道源码，牛逼！\")\n    @ExcelProperty(\"结果提示\")\n    private String resultMsg;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - API 错误日志分页 Request VO\")\n@Data\npublic class ApiErrorLogPageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"666\")\n    private Long userId;\n\n    @Schema(description = \"用户类型\", example = \"1\")\n    private Integer userType;\n\n    @Schema(description = \"应用名\", example = \"dashboard\")\n    private String applicationName;\n\n    @Schema(description = \"请求地址\", example = \"/xx/yy\")\n    private String requestUrl;\n\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"异常发生时间\")\n    private LocalDateTime[] exceptionTime;\n\n    @Schema(description = \"处理状态\", example = \"0\")\n    private Integer processStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.infra.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - API 错误日志 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ApiErrorLogRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"链路追踪编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"66600cb6-7852-11eb-9439-0242ac130002\")\n    @ExcelProperty(\"链路追踪编号\")\n    private String traceId;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"666\")\n    @ExcelProperty(\"用户编号\")\n    private Long userId;\n\n    @Schema(description = \"用户类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"用户类型\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.USER_TYPE)\n    private Integer userType;\n\n    @Schema(description = \"应用名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"dashboard\")\n    @ExcelProperty(\"应用名\")\n    private String applicationName;\n\n    @Schema(description = \"请求方法名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"GET\")\n    @ExcelProperty(\"请求方法名\")\n    private String requestMethod;\n\n    @Schema(description = \"请求地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"/xx/yy\")\n    @ExcelProperty(\"请求地址\")\n    private String requestUrl;\n\n    @Schema(description = \"请求参数\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"请求参数\")\n    private String requestParams;\n\n    @Schema(description = \"用户 IP\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"127.0.0.1\")\n    @ExcelProperty(\"用户 IP\")\n    private String userIp;\n\n    @Schema(description = \"浏览器 UA\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Mozilla/5.0\")\n    @ExcelProperty(\"浏览器 UA\")\n    private String userAgent;\n\n    @Schema(description = \"异常发生时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"异常发生时间\")\n    private LocalDateTime exceptionTime;\n\n    @Schema(description = \"异常名\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"异常名\")\n    private String exceptionName;\n\n    @Schema(description = \"异常导致的消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"异常导致的消息\")\n    private String exceptionMessage;\n\n    @Schema(description = \"异常导致的根消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"异常导致的根消息\")\n    private String exceptionRootCauseMessage;\n\n    @Schema(description = \"异常的栈轨迹\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"异常的栈轨迹\")\n    private String exceptionStackTrace;\n\n    @Schema(description = \"异常发生的类全名\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"异常发生的类全名\")\n    private String exceptionClassName;\n\n    @Schema(description = \"异常发生的类文件\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"异常发生的类文件\")\n    private String exceptionFileName;\n\n    @Schema(description = \"异常发生的方法名\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"异常发生的方法名\")\n    private String exceptionMethodName;\n\n    @Schema(description = \"异常发生的方法所在行\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"异常发生的方法所在行\")\n    private Integer exceptionLineNumber;\n\n    @Schema(description = \"处理状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @ExcelProperty(value = \"处理状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.API_ERROR_LOG_PROCESS_STATUS)\n    private Integer processStatus;\n\n    @Schema(description = \"处理时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"处理时间\")\n    private LocalDateTime processTime;\n\n    @Schema(description = \"处理用户编号\", example = \"233\")\n    @ExcelProperty(\"处理用户编号\")\n    private Integer processUserId;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http",
    "content": "### 请求 /infra/redis/get-monitor-info 接口 => 成功\nGET {{baseUrl}}/infra/redis/get-monitor-info\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.redis;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;\nimport cn.iocoder.yudao.module.infra.convert.redis.RedisConvert;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.data.redis.connection.RedisServerCommands;\nimport org.springframework.data.redis.core.RedisCallback;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.Properties;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - Redis 监控\")\n@RestController\n@RequestMapping(\"/infra/redis\")\npublic class RedisController {\n\n    @Resource\n    private StringRedisTemplate stringRedisTemplate;\n\n    @GetMapping(\"/get-monitor-info\")\n    @Operation(summary = \"获得 Redis 监控信息\")\n    @PreAuthorize(\"@ss.hasPermission('infra:redis:get-monitor-info')\")\n    public CommonResult<RedisMonitorRespVO> getRedisMonitorInfo() {\n        // 获得 Redis 统计信息\n        Properties info = stringRedisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info);\n        Long dbSize = stringRedisTemplate.execute(RedisServerCommands::dbSize);\n        Properties commandStats = stringRedisTemplate.execute((\n                RedisCallback<Properties>) connection -> connection.info(\"commandstats\"));\n        assert commandStats != null; // 断言，避免警告\n        // 拼接结果返回\n        return success(RedisConvert.INSTANCE.build(info, dbSize, commandStats));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisMonitorRespVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.redis.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\n\nimport java.util.List;\nimport java.util.Properties;\n\n@Schema(description = \"管理后台 - Redis 监控信息 Response VO\")\n@Data\n@Builder\n@AllArgsConstructor\npublic class RedisMonitorRespVO {\n\n    @Schema(description = \"Redis info 指令结果,具体字段，查看 Redis 文档\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Properties info;\n\n    @Schema(description = \"Redis key 数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long dbSize;\n\n    @Schema(description = \"CommandStat 数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<CommandStat> commandStats;\n\n    @Schema(description = \"Redis 命令统计结果\")\n    @Data\n    @Builder\n    @AllArgsConstructor\n    public static class CommandStat {\n\n        @Schema(description = \"Redis 命令\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"get\")\n        private String command;\n\n        @Schema(description = \"调用次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private Long calls;\n\n        @Schema(description = \"消耗 CPU 秒数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"666\")\n        private Long usec;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/AppFileController.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.app.file;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;\nimport cn.iocoder.yudao.module.infra.controller.app.file.vo.AppFileUploadReqVO;\nimport cn.iocoder.yudao.module.infra.service.file.FileService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 App - 文件存储\")\n@RestController\n@RequestMapping(\"/infra/file\")\n@Validated\n@Slf4j\npublic class AppFileController {\n\n    @Resource\n    private FileService fileService;\n\n    @PostMapping(\"/upload\")\n    @Operation(summary = \"上传文件\")\n    @Parameter(name = \"file\", description = \"文件附件\", required = true,\n            schema = @Schema(type = \"string\", format = \"binary\"))\n    @PermitAll\n    public CommonResult<String> uploadFile(AppFileUploadReqVO uploadReqVO) throws Exception {\n        MultipartFile file = uploadReqVO.getFile();\n        byte[] content = IoUtil.readBytes(file.getInputStream());\n        return success(fileService.createFile(content, file.getOriginalFilename(),\n                uploadReqVO.getDirectory(), file.getContentType()));\n    }\n\n    @GetMapping(\"/presigned-url\")\n    @Operation(summary = \"获取文件预签名地址（上传）\", description = \"模式二：前端上传文件：用于前端直接上传七牛、阿里云 OSS 等文件存储器\")\n    @Parameters({\n            @Parameter(name = \"name\", description = \"文件名称\", required = true),\n            @Parameter(name = \"directory\", description = \"文件目录\")\n    })\n    public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(\n            @RequestParam(\"name\") String name,\n            @RequestParam(value = \"directory\", required = false) String directory) {\n        return success(fileService.presignPutUrl(name, directory));\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建文件\", description = \"模式二：前端上传文件：配合 presigned-url 接口，记录上传了上传的文件\")\n    @PermitAll\n    public CommonResult<Long> createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {\n        return success(fileService.createFile(createReqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/vo/AppFileUploadReqVO.java",
    "content": "package cn.iocoder.yudao.module.infra.controller.app.file.vo;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileUploadReqVO;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 上传文件 Request VO\")\n@Data\npublic class AppFileUploadReqVO {\n\n    @Schema(description = \"文件附件\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"文件附件不能为空\")\n    private MultipartFile file;\n\n    @Schema(description = \"文件目录\", example = \"XXX/YYY\")\n    private String directory;\n\n    @AssertTrue(message = \"文件目录不正确\")\n    @JsonIgnore\n    public boolean isDirectoryValid() {\n        return FileUploadReqVO.isDirectoryValid(directory);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/app/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.infra.controller.app;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/controller/package-info.java",
    "content": "/**\n * 提供 RESTful API 给前端：\n * 1. admin 包：提供给管理后台 yudao-ui-admin 前端项目\n * 2. app 包：提供给用户 APP yudao-ui-app 前端项目，它的 Controller 和 VO 都要添加 App 前缀，用于和管理后台进行区分\n */\npackage cn.iocoder.yudao.module.infra.controller;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java",
    "content": "package cn.iocoder.yudao.module.infra.convert.codegen;\n\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\nimport org.mapstruct.Named;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\n\n@Mapper\npublic interface CodegenConvert {\n\n    CodegenConvert INSTANCE = Mappers.getMapper(CodegenConvert.class);\n\n    // ========== TableInfo 相关 ==========\n\n    @Mappings({\n            @Mapping(source = \"name\", target = \"tableName\"),\n            @Mapping(source = \"comment\", target = \"tableComment\"),\n    })\n    CodegenTableDO convert(TableInfo bean);\n\n    List<CodegenColumnDO> convertList(List<TableField> list);\n\n    @Mappings({\n            @Mapping(source = \"name\", target = \"columnName\"),\n            @Mapping(source = \"metaInfo.jdbcType\", target = \"dataType\", qualifiedByName = \"getDataType\"),\n            @Mapping(source = \"comment\", target = \"columnComment\"),\n            @Mapping(source = \"metaInfo.nullable\", target = \"nullable\"),\n            @Mapping(source = \"keyFlag\", target = \"primaryKey\"),\n            @Mapping(source = \"columnType.type\", target = \"javaType\"),\n            @Mapping(source = \"propertyName\", target = \"javaField\"),\n    })\n    CodegenColumnDO convert(TableField bean);\n\n    @Named(\"getDataType\")\n    default String getDataType(JdbcType jdbcType) {\n        return jdbcType.name();\n    }\n\n    // ========== 其它 ==========\n\n    default CodegenDetailRespVO convert(CodegenTableDO table, List<CodegenColumnDO> columns) {\n        CodegenDetailRespVO respVO = new CodegenDetailRespVO();\n        respVO.setTable(BeanUtils.toBean(table, CodegenTableRespVO.class));\n        respVO.setColumns(BeanUtils.toBean(columns, CodegenColumnRespVO.class));\n        return respVO;\n    }\n\n    default List<CodegenPreviewRespVO> convert(Map<String, String> codes) {\n        return CollectionUtils.convertList(codes.entrySet(),\n                entry -> new CodegenPreviewRespVO().setFilePath(entry.getKey()).setCode(entry.getValue()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/convert/config/ConfigConvert.java",
    "content": "package cn.iocoder.yudao.module.infra.convert.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigRespVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n@Mapper\npublic interface ConfigConvert {\n\n    ConfigConvert INSTANCE = Mappers.getMapper(ConfigConvert.class);\n\n    PageResult<ConfigRespVO> convertPage(PageResult<ConfigDO> page);\n\n    List<ConfigRespVO> convertList(List<ConfigDO> list);\n\n    @Mapping(source = \"configKey\", target = \"key\")\n    ConfigRespVO convert(ConfigDO bean);\n\n    @Mapping(source = \"key\", target = \"configKey\")\n    ConfigDO convert(ConfigSaveReqVO bean);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/convert/file/FileConfigConvert.java",
    "content": "package cn.iocoder.yudao.module.infra.convert.file;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.factory.Mappers;\n\n/**\n * 文件配置 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface FileConfigConvert {\n\n    FileConfigConvert INSTANCE = Mappers.getMapper(FileConfigConvert.class);\n\n    @Mapping(target = \"config\", ignore = true)\n    FileConfigDO convert(FileConfigSaveReqVO bean);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/convert/package-info.java",
    "content": "/**\n * 提供 POJO 类的实体转换\n *\n * 目前使用 MapStruct 框架\n */\npackage cn.iocoder.yudao.module.infra.convert;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java",
    "content": "package cn.iocoder.yudao.module.infra.convert.redis;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.ArrayList;\nimport java.util.Properties;\n\n@Mapper\npublic interface RedisConvert {\n\n    RedisConvert INSTANCE = Mappers.getMapper(RedisConvert.class);\n\n    default RedisMonitorRespVO build(Properties info, Long dbSize, Properties commandStats) {\n        RedisMonitorRespVO respVO = RedisMonitorRespVO.builder().info(info).dbSize(dbSize)\n                .commandStats(new ArrayList<>(commandStats.size())).build();\n        commandStats.forEach((key, value) -> {\n            respVO.getCommandStats().add(RedisMonitorRespVO.CommandStat.builder()\n                    .command(StrUtil.subAfter((String) key, \"cmdstat_\", false))\n                    .calls(Long.valueOf(StrUtil.subBetween((String) value, \"calls=\", \",\")))\n                    .usec(Long.valueOf(StrUtil.subBetween((String) value, \"usec=\", \",\")))\n                    .build());\n        });\n        return respVO;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenColumnDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport lombok.Data;\n\n/**\n * 代码生成 column 字段定义\n *\n * @author 芋道源码\n */\n@TableName(value = \"infra_codegen_column\", autoResultMap = true)\n@KeySequence(\"infra_codegen_column_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@TenantIgnore\npublic class CodegenColumnDO extends BaseDO {\n\n    /**\n     * ID 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 表编号\n     * <p>\n     * 关联 {@link CodegenTableDO#getId()}\n     */\n    private Long tableId;\n\n    // ========== 表相关字段 ==========\n\n    /**\n     * 字段名\n     *\n     * 关联 {@link TableField#getName()}\n     */\n    private String columnName;\n    /**\n     * 数据库字段类型\n     *\n     * 关联 {@link TableField.MetaInfo#getJdbcType()}\n     */\n    private String dataType;\n    /**\n     * 字段描述\n     *\n     * 关联 {@link TableField#getComment()}\n     */\n    private String columnComment;\n    /**\n     * 是否允许为空\n     *\n     * 关联 {@link TableField.MetaInfo#isNullable()}\n     */\n    private Boolean nullable;\n    /**\n     * 是否主键\n     *\n     * 关联 {@link TableField#isKeyFlag()}\n     */\n    private Boolean primaryKey;\n    /**\n     * 排序\n     */\n    private Integer ordinalPosition;\n\n    // ========== Java 相关字段 ==========\n\n    /**\n     * Java 属性类型\n     *\n     * 例如说 String、Boolean 等等\n     *\n     * 关联 {@link TableField#getColumnType()}\n     */\n    private String javaType;\n    /**\n     * Java 属性名\n     *\n     * 关联 {@link TableField#getPropertyName()}\n     */\n    private String javaField;\n    /**\n     * 字典类型\n     * <p>\n     * 关联 DictTypeDO 的 type 属性\n     */\n    private String dictType;\n    /**\n     * 数据示例，主要用于生成 Swagger 注解的 example 字段\n     */\n    private String example;\n\n    // ========== CRUD 相关字段 ==========\n\n    /**\n     * 是否为 Create 创建操作的字段\n     */\n    private Boolean createOperation;\n    /**\n     * 是否为 Update 更新操作的字段\n     */\n    private Boolean updateOperation;\n    /**\n     * 是否为 List 查询操作的字段\n     */\n    private Boolean listOperation;\n    /**\n     * List 查询操作的条件类型\n     * <p>\n     * 枚举 {@link CodegenColumnListConditionEnum}\n     */\n    private String listOperationCondition;\n    /**\n     * 是否为 List 查询操作的返回字段\n     */\n    private Boolean listOperationResult;\n\n    // ========== UI 相关字段 ==========\n\n    /**\n     * 显示类型\n     * <p>\n     * 枚举 {@link CodegenColumnHtmlTypeEnum}\n     */\n    private String htmlType;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport lombok.Data;\n\n/**\n * 代码生成 table 表定义\n *\n * @author 芋道源码\n */\n@TableName(value = \"infra_codegen_table\", autoResultMap = true)\n@KeySequence(\"infra_codegen_table_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@TenantIgnore\npublic class CodegenTableDO extends BaseDO {\n\n    /**\n     * ID 编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 数据源编号\n     *\n     * 关联 {@link DataSourceConfigDO#getId()}\n     */\n    private Long dataSourceConfigId;\n    /**\n     * 生成场景\n     *\n     * 枚举 {@link CodegenSceneEnum}\n     */\n    private Integer scene;\n\n    // ========== 表相关字段 ==========\n\n    /**\n     * 表名称\n     *\n     * 关联 {@link TableInfo#getName()}\n     */\n    private String tableName;\n    /**\n     * 表描述\n     *\n     * 关联 {@link TableInfo#getComment()}\n     */\n    private String tableComment;\n    /**\n     * 备注\n     */\n    private String remark;\n\n    // ========== 类相关字段 ==========\n\n    /**\n     * 模块名，即一级目录\n     *\n     * 例如说，system、infra、tool 等等\n     */\n    private String moduleName;\n    /**\n     * 业务名，即二级目录\n     *\n     * 例如说，user、permission、dict 等等\n     */\n    private String businessName;\n    /**\n     * 类名称（首字母大写）\n     *\n     * 例如说，SysUser、SysMenu、SysDictData 等等\n     */\n    private String className;\n    /**\n     * 类描述\n     */\n    private String classComment;\n    /**\n     * 作者\n     */\n    private String author;\n\n    // ========== 生成相关字段 ==========\n\n    /**\n     * 模板类型\n     *\n     * 枚举 {@link CodegenTemplateTypeEnum}\n     */\n    private Integer templateType;\n    /**\n     * 代码生成的前端类型\n     *\n     * 枚举 {@link CodegenFrontTypeEnum}\n     */\n    private Integer frontType;\n\n    // ========== 菜单相关字段 ==========\n\n    /**\n     * 父菜单编号\n     *\n     * 关联 MenuDO 的 id 属性\n     */\n    private Long parentMenuId;\n\n    // ========== 主子表相关字段 ==========\n\n    /**\n     * 主表的编号\n     *\n     * 关联 {@link CodegenTableDO#getId()}\n     */\n    private Long masterTableId;\n    /**\n     * 【自己】子表关联主表的字段编号\n     *\n     * 关联 {@link CodegenColumnDO#getId()}\n     */\n    private Long subJoinColumnId;\n    /**\n     * 主表与子表是否一对多\n     *\n     * true：一对多\n     * false：一对一\n     */\n    private Boolean subJoinMany;\n\n    // ========== 树表相关字段 ==========\n\n    /**\n     * 树表的父字段编号\n     *\n     * 关联 {@link CodegenColumnDO#getId()}\n     */\n    private Long treeParentColumnId;\n    /**\n     * 树表的名字字段编号\n     *\n     * 名字的用途：新增或修改时，select 框展示的字段\n     *\n     * 关联 {@link CodegenColumnDO#getId()}\n     */\n    private Long treeNameColumnId;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/config/ConfigDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.config;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.infra.enums.config.ConfigTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n/**\n * 参数配置表\n *\n * @author 芋道源码\n */\n@TableName(\"infra_config\")\n@KeySequence(\"infra_config_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@TenantIgnore\npublic class ConfigDO extends BaseDO {\n\n    /**\n     * 参数主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 参数分类\n     */\n    private String category;\n    /**\n     * 参数名称\n     */\n    private String name;\n    /**\n     * 参数键名\n     *\n     * 支持多 DB 类型时，无法直接使用 key + @TableField(\"config_key\") 来实现转换，原因是 \"config_key\" AS key 而存在报错\n     */\n    private String configKey;\n    /**\n     * 参数键值\n     */\n    private String value;\n    /**\n     * 参数类型\n     *\n     * 枚举 {@link ConfigTypeEnum}\n     */\n    private Integer type;\n    /**\n     * 是否可见\n     *\n     * 不可见的参数，一般是敏感参数，前端不可获取\n     */\n    private Boolean visible;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.db;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.EncryptTypeHandler;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * 数据源配置\n *\n * @author 芋道源码\n */\n@TableName(value = \"infra_data_source_config\", autoResultMap = true)\n@KeySequence(\"infra_data_source_config_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@TenantIgnore\npublic class DataSourceConfigDO extends BaseDO {\n\n    /**\n     * 主键编号 - Master 数据源\n     */\n    public static final Long ID_MASTER = 0L;\n\n    /**\n     * 主键编号\n     */\n    private Long id;\n    /**\n     * 连接名\n     */\n    private String name;\n\n    /**\n     * 数据源连接\n     */\n    private String url;\n    /**\n     * 用户名\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    @TableField(typeHandler = EncryptTypeHandler.class)\n    private String password;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * 示例联系人 DO\n *\n * @author 芋道源码\n */\n@TableName(\"yudao_demo01_contact\")\n@KeySequence(\"yudao_demo01_contact_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Demo01ContactDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 出生年\n     */\n    private LocalDateTime birthday;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 头像\n     */\n    private String avatar;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 示例分类 DO\n *\n * @author 芋道源码\n */\n@TableName(\"yudao_demo02_category\")\n@KeySequence(\"yudao_demo02_category_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Demo02CategoryDO extends BaseDO {\n\n    public static final Long PARENT_ID_ROOT = 0L;\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 父级编号\n     */\n    private Long parentId;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 学生课程 DO\n *\n * @author 芋道源码\n */\n@TableName(\"yudao_demo03_course\")\n@KeySequence(\"yudao_demo03_course_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Demo03CourseDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 分数\n     */\n    private Integer score;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 学生班级 DO\n *\n * @author 芋道源码\n */\n@TableName(\"yudao_demo03_grade\")\n@KeySequence(\"yudao_demo03_grade_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Demo03GradeDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 班主任\n     */\n    private String teacher;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * 学生 DO\n *\n * @author 芋道源码\n */\n@TableName(\"yudao_demo03_student\")\n@KeySequence(\"yudao_demo03_student_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Demo03StudentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 简介\n     */\n    private String description;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.file;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport lombok.*;\n\nimport java.lang.reflect.Field;\n\n/**\n * 文件配置表\n *\n * @author 芋道源码\n */\n@TableName(value = \"infra_file_config\", autoResultMap = true)\n@KeySequence(\"infra_file_config_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@TenantIgnore\npublic class FileConfigDO extends BaseDO {\n\n    /**\n     * 配置编号，数据库自增\n     */\n    private Long id;\n    /**\n     * 配置名\n     */\n    private String name;\n    /**\n     * 存储器\n     *\n     * 枚举 {@link FileStorageEnum}\n     */\n    private Integer storage;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 是否为主配置\n     *\n     * 由于我们可以配置多个文件配置，默认情况下，使用主配置进行文件的上传\n     */\n    private Boolean master;\n\n    /**\n     * 支付渠道配置\n     */\n    @TableField(typeHandler = FileClientConfigTypeHandler.class)\n    private FileClientConfig config;\n\n    public static class FileClientConfigTypeHandler extends AbstractJsonTypeHandler<Object> {\n\n        public FileClientConfigTypeHandler(Class<?> type) {\n            super(type);\n        }\n\n        public FileClientConfigTypeHandler(Class<?> type, Field field) {\n            super(type, field);\n        }\n\n        @Override\n        public Object parse(String json) {\n            FileClientConfig config = JsonUtils.parseObjectQuietly(json, new TypeReference<FileClientConfig>() {});\n            if (config != null) {\n                return config;\n            }\n\n            // 兼容老版本的包路径\n            String className = JsonUtils.parseObject(json, \"@class\", String.class);\n            className = StrUtil.subAfter(className, \".\", true);\n            switch (className) {\n                case \"DBFileClientConfig\":\n                    return JsonUtils.parseObject2(json, DBFileClientConfig.class);\n                case \"FtpFileClientConfig\":\n                    return JsonUtils.parseObject2(json, FtpFileClientConfig.class);\n                case \"LocalFileClientConfig\":\n                    return JsonUtils.parseObject2(json, LocalFileClientConfig.class);\n                case \"SftpFileClientConfig\":\n                    return JsonUtils.parseObject2(json, SftpFileClientConfig.class);\n                case \"S3FileClientConfig\":\n                    return JsonUtils.parseObject2(json, S3FileClientConfig.class);\n                default:\n                    throw new IllegalArgumentException(\"未知的 FileClientConfig 类型：\" + json);\n            }\n        }\n\n        @Override\n        public String toJson(Object obj) {\n            return JsonUtils.toJsonString(obj);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.file;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClient;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 文件内容表\n *\n * 专门用于存储 {@link DBFileClient} 的文件内容\n *\n * @author 芋道源码\n */\n@TableName(\"infra_file_content\")\n@KeySequence(\"infra_file_content_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@TenantIgnore\npublic class FileContentDO extends BaseDO {\n\n    /**\n     * 编号，数据库自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * 配置编号\n     *\n     * 关联 {@link FileConfigDO#getId()}\n     */\n    private Long configId;\n    /**\n     * 路径，即文件名\n     */\n    private String path;\n    /**\n     * 文件内容\n     */\n    private byte[] content;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.file;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 文件表\n * 每次文件上传，都会记录一条记录到该表中\n *\n * @author 芋道源码\n */\n@TableName(\"infra_file\")\n@KeySequence(\"infra_file_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@TenantIgnore\npublic class FileDO extends BaseDO {\n\n    /**\n     * 编号，数据库自增\n     */\n    private Long id;\n    /**\n     * 配置编号\n     *\n     * 关联 {@link FileConfigDO#getId()}\n     */\n    private Long configId;\n    /**\n     * 原文件名\n     */\n    private String name;\n    /**\n     * 路径，即文件名\n     */\n    private String path;\n    /**\n     * 访问地址\n     */\n    private String url;\n    /**\n     * 文件的 MIME 类型，例如 \"application/octet-stream\"\n     */\n    private String type;\n    /**\n     * 文件大小\n     */\n    private Long size;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.logger;\n\nimport cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * API 访问日志\n *\n * @author 芋道源码\n */\n@TableName(\"infra_api_access_log\")\n@KeySequence(value = \"infra_api_access_log_seq\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ApiAccessLogDO extends BaseDO {\n\n    /**\n     * {@link #requestParams} 的最大长度\n     */\n    public static final Integer REQUEST_PARAMS_MAX_LENGTH = 8000;\n\n    /**\n     * {@link #resultMsg} 的最大长度\n     */\n    public static final Integer RESULT_MSG_MAX_LENGTH = 512;\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 链路追踪编号\n     *\n     * 一般来说，通过链路追踪编号，可以将访问日志，错误日志，链路追踪日志，logger 打印日志等，结合在一起，从而进行排错。\n     */\n    private String traceId;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n    /**\n     * 用户类型\n     *\n     * 枚举 {@link UserTypeEnum}\n     */\n    private Integer userType;\n    /**\n     * 应用名\n     *\n     * 目前读取 `spring.application.name` 配置项\n     */\n    private String applicationName;\n\n    // ========== 请求相关字段 ==========\n\n    /**\n     * 请求方法名\n     */\n    private String requestMethod;\n    /**\n     * 访问地址\n     */\n    private String requestUrl;\n    /**\n     * 请求参数\n     *\n     * query: Query String\n     * body: Quest Body\n     */\n    private String requestParams;\n    /**\n     * 响应结果\n     */\n    private String responseBody;\n    /**\n     * 用户 IP\n     */\n    private String userIp;\n    /**\n     * 浏览器 UA\n     */\n    private String userAgent;\n\n    // ========== 执行相关字段 ==========\n\n    /**\n     * 操作模块\n     */\n    private String operateModule;\n    /**\n     * 操作名\n     */\n    private String operateName;\n    /**\n     * 操作分类\n     *\n     * 枚举 {@link OperateTypeEnum}\n     */\n    private Integer operateType;\n\n    /**\n     * 开始请求时间\n     */\n    private LocalDateTime beginTime;\n    /**\n     * 结束请求时间\n     */\n    private LocalDateTime endTime;\n    /**\n     * 执行时长，单位：毫秒\n     */\n    private Integer duration;\n\n    /**\n     * 结果码\n     *\n     * 目前使用的 {@link CommonResult#getCode()} 属性\n     */\n    private Integer resultCode;\n    /**\n     * 结果提示\n     *\n     * 目前使用的 {@link CommonResult#getMsg()} 属性\n     */\n    private String resultMsg;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.logger;\n\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * API 异常数据\n *\n * @author 芋道源码\n */\n@TableName(\"infra_api_error_log\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@KeySequence(value = \"infra_api_error_log_seq\")\npublic class ApiErrorLogDO extends BaseDO {\n\n    /**\n     * {@link #requestParams} 的最大长度\n     */\n    public static final Integer REQUEST_PARAMS_MAX_LENGTH = 8000;\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n    /**\n     * 链路追踪编号\n     *\n     * 一般来说，通过链路追踪编号，可以将访问日志，错误日志，链路追踪日志，logger 打印日志等，结合在一起，从而进行排错。\n     */\n    private String traceId;\n    /**\n     * 用户类型\n     *\n     * 枚举 {@link UserTypeEnum}\n     */\n    private Integer userType;\n    /**\n     * 应用名\n     *\n     * 目前读取 spring.application.name\n     */\n    private String applicationName;\n\n    // ========== 请求相关字段 ==========\n\n    /**\n     * 请求方法名\n     */\n    private String requestMethod;\n    /**\n     * 访问地址\n     */\n    private String requestUrl;\n    /**\n     * 请求参数\n     *\n     * query: Query String\n     * body: Quest Body\n     */\n    private String requestParams;\n    /**\n     * 用户 IP\n     */\n    private String userIp;\n    /**\n     * 浏览器 UA\n     */\n    private String userAgent;\n\n    // ========== 异常相关字段 ==========\n\n    /**\n     * 异常发生时间\n     */\n    private LocalDateTime exceptionTime;\n    /**\n     * 异常名\n     *\n     * {@link Throwable#getClass()} 的类全名\n     */\n    private String exceptionName;\n    /**\n     * 异常导致的消息\n     *\n     * {@link cn.hutool.core.exceptions.ExceptionUtil#getMessage(Throwable)}\n     */\n    private String exceptionMessage;\n    /**\n     * 异常导致的根消息\n     *\n     * {@link cn.hutool.core.exceptions.ExceptionUtil#getRootCauseMessage(Throwable)}\n     */\n    private String exceptionRootCauseMessage;\n    /**\n     * 异常的栈轨迹\n     *\n     * {@link org.apache.commons.lang3.exception.ExceptionUtils#getStackTrace(Throwable)}\n     */\n    private String exceptionStackTrace;\n    /**\n     * 异常发生的类全名\n     *\n     * {@link StackTraceElement#getClassName()}\n     */\n    private String exceptionClassName;\n    /**\n     * 异常发生的类文件\n     *\n     * {@link StackTraceElement#getFileName()}\n     */\n    private String exceptionFileName;\n    /**\n     * 异常发生的方法名\n     *\n     * {@link StackTraceElement#getMethodName()}\n     */\n    private String exceptionMethodName;\n    /**\n     * 异常发生的方法所在行\n     *\n     * {@link StackTraceElement#getLineNumber()}\n     */\n    private Integer exceptionLineNumber;\n\n    // ========== 处理相关字段 ==========\n\n    /**\n     * 处理状态\n     *\n     * 枚举 {@link ApiErrorLogProcessStatusEnum}\n     */\n    private Integer processStatus;\n    /**\n     * 处理时间\n     */\n    private LocalDateTime processTime;\n    /**\n     * 处理用户编号\n     *\n     * 关联 cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserDO.SysUserDO#getId()\n     */\n    private Long processUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenColumnMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.codegen;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n@Mapper\npublic interface CodegenColumnMapper extends BaseMapperX<CodegenColumnDO> {\n\n    default List<CodegenColumnDO> selectListByTableId(Long tableId) {\n        return selectList(new LambdaQueryWrapperX<CodegenColumnDO>()\n                .eq(CodegenColumnDO::getTableId, tableId)\n                .orderByAsc(CodegenColumnDO::getOrdinalPosition));\n    }\n\n    default void deleteListByTableId(Long tableId) {\n        delete(CodegenColumnDO::getTableId, tableId);\n    }\n\n    default void deleteListByTableId(Collection<Long> tableIds) {\n        delete(new LambdaQueryWrapperX<CodegenColumnDO>()\n               .in(CodegenColumnDO::getTableId, tableIds));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenTableMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.codegen;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n@Mapper\npublic interface CodegenTableMapper extends BaseMapperX<CodegenTableDO> {\n\n    default CodegenTableDO selectByTableNameAndDataSourceConfigId(String tableName, Long dataSourceConfigId) {\n        return selectOne(CodegenTableDO::getTableName, tableName,\n                CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);\n    }\n\n    default PageResult<CodegenTableDO> selectPage(CodegenTablePageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<CodegenTableDO>()\n                .likeIfPresent(CodegenTableDO::getTableName, pageReqVO.getTableName())\n                .likeIfPresent(CodegenTableDO::getTableComment, pageReqVO.getTableComment())\n                .likeIfPresent(CodegenTableDO::getClassName, pageReqVO.getClassName())\n                .betweenIfPresent(CodegenTableDO::getCreateTime, pageReqVO.getCreateTime())\n                .orderByDesc(CodegenTableDO::getUpdateTime)\n        );\n    }\n\n    default List<CodegenTableDO> selectListByDataSourceConfigId(Long dataSourceConfigId) {\n        return selectList(CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);\n    }\n\n    default List<CodegenTableDO> selectListByTemplateTypeAndMasterTableId(Integer templateType, Long masterTableId) {\n        return selectList(CodegenTableDO::getTemplateType, templateType,\n                CodegenTableDO::getMasterTableId, masterTableId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/config/ConfigMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ConfigMapper extends BaseMapperX<ConfigDO> {\n\n    default ConfigDO selectByKey(String key) {\n        return selectOne(ConfigDO::getConfigKey, key);\n    }\n\n    default PageResult<ConfigDO> selectPage(ConfigPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ConfigDO>()\n                .likeIfPresent(ConfigDO::getName, reqVO.getName())\n                .likeIfPresent(ConfigDO::getConfigKey, reqVO.getKey())\n                .eqIfPresent(ConfigDO::getType, reqVO.getType())\n                .betweenIfPresent(ConfigDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(ConfigDO::getId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/db/DataSourceConfigMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.db;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 数据源配置 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface DataSourceConfigMapper extends BaseMapperX<DataSourceConfigDO> {\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo01/Demo01ContactMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo01;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01.Demo01ContactDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 示例联系人 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo01ContactMapper extends BaseMapperX<Demo01ContactDO> {\n\n    default PageResult<Demo01ContactDO> selectPage(Demo01ContactPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<Demo01ContactDO>()\n                .likeIfPresent(Demo01ContactDO::getName, reqVO.getName())\n                .eqIfPresent(Demo01ContactDO::getSex, reqVO.getSex())\n                .betweenIfPresent(Demo01ContactDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(Demo01ContactDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo02;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 示例分类 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo02CategoryMapper extends BaseMapperX<Demo02CategoryDO> {\n\n    default List<Demo02CategoryDO> selectList(Demo02CategoryListReqVO reqVO) {\n        return selectList(new LambdaQueryWrapperX<Demo02CategoryDO>()\n                .likeIfPresent(Demo02CategoryDO::getName, reqVO.getName())\n                .eqIfPresent(Demo02CategoryDO::getParentId, reqVO.getParentId())\n                .betweenIfPresent(Demo02CategoryDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(Demo02CategoryDO::getId));\n    }\n\n    default Demo02CategoryDO selectByParentIdAndName(Long parentId, String name) {\n        return selectOne(Demo02CategoryDO::getParentId, parentId, Demo02CategoryDO::getName, name);\n    }\n\n    default Long selectCountByParentId(Long parentId) {\n        return selectCount(Demo02CategoryDO::getParentId, parentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/erp/Demo03CourseErpMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.erp;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 学生课程 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo03CourseErpMapper extends BaseMapperX<Demo03CourseDO> {\n\n    default PageResult<Demo03CourseDO> selectPage(PageParam reqVO, Long studentId) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<Demo03CourseDO>()\n                .eq(Demo03CourseDO::getStudentId, studentId)\n                .orderByDesc(Demo03CourseDO::getId));\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(Demo03CourseDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentIds(List<Long> studentIds) {\n        return deleteBatch(Demo03CourseDO::getStudentId, studentIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/erp/Demo03GradeErpMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.erp;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 学生班级 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo03GradeErpMapper extends BaseMapperX<Demo03GradeDO> {\n\n    default PageResult<Demo03GradeDO> selectPage(PageParam reqVO, Long studentId) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<Demo03GradeDO>()\n                .eq(Demo03GradeDO::getStudentId, studentId)\n                .orderByDesc(Demo03GradeDO::getId));\n    }\n\n    default Demo03GradeDO selectByStudentId(Long studentId) {\n        return selectOne(Demo03GradeDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(Demo03GradeDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentIds(List<Long> studentIds) {\n        return deleteBatch(Demo03GradeDO::getStudentId, studentIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/erp/Demo03StudentErpMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.erp;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo03StudentErpMapper extends BaseMapperX<Demo03StudentDO> {\n\n    default PageResult<Demo03StudentDO> selectPage(Demo03StudentErpPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<Demo03StudentDO>()\n                .likeIfPresent(Demo03StudentDO::getName, reqVO.getName())\n                .eqIfPresent(Demo03StudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(Demo03StudentDO::getDescription, reqVO.getDescription())\n                .betweenIfPresent(Demo03StudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(Demo03StudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/inner/Demo03CourseInnerMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.inner;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 学生课程 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo03CourseInnerMapper extends BaseMapperX<Demo03CourseDO> {\n\n    default List<Demo03CourseDO> selectListByStudentId(Long studentId) {\n        return selectList(Demo03CourseDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(Demo03CourseDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentIds(List<Long> studentIds) {\n        return deleteBatch(Demo03CourseDO::getStudentId, studentIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/inner/Demo03GradeInnerMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.inner;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 学生班级 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo03GradeInnerMapper extends BaseMapperX<Demo03GradeDO> {\n\n    default Demo03GradeDO selectByStudentId(Long studentId) {\n        return selectOne(Demo03GradeDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(Demo03GradeDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentIds(List<Long> studentIds) {\n        return deleteBatch(Demo03GradeDO::getStudentId, studentIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/inner/Demo03StudentInnerMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.inner;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo03StudentInnerMapper extends BaseMapperX<Demo03StudentDO> {\n\n    default PageResult<Demo03StudentDO> selectPage(Demo03StudentInnerPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<Demo03StudentDO>()\n                .likeIfPresent(Demo03StudentDO::getName, reqVO.getName())\n                .eqIfPresent(Demo03StudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(Demo03StudentDO::getDescription, reqVO.getDescription())\n                .betweenIfPresent(Demo03StudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(Demo03StudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/normal/Demo03CourseNormalMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.normal;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 学生课程 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo03CourseNormalMapper extends BaseMapperX<Demo03CourseDO> {\n\n    default List<Demo03CourseDO> selectListByStudentId(Long studentId) {\n        return selectList(Demo03CourseDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(Demo03CourseDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentIds(List<Long> studentIds) {\n        return deleteBatch(Demo03CourseDO::getStudentId, studentIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/normal/Demo03GradeNormalMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.normal;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 学生班级 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo03GradeNormalMapper extends BaseMapperX<Demo03GradeDO> {\n\n    default Demo03GradeDO selectByStudentId(Long studentId) {\n        return selectOne(Demo03GradeDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(Demo03GradeDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentIds(List<Long> studentIds) {\n        return deleteBatch(Demo03GradeDO::getStudentId, studentIds);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/normal/Demo03StudentNormalMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.normal;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface Demo03StudentNormalMapper extends BaseMapperX<Demo03StudentDO> {\n\n    default PageResult<Demo03StudentDO> selectPage(Demo03StudentNormalPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<Demo03StudentDO>()\n                .likeIfPresent(Demo03StudentDO::getName, reqVO.getName())\n                .eqIfPresent(Demo03StudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(Demo03StudentDO::getDescription, reqVO.getDescription())\n                .betweenIfPresent(Demo03StudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(Demo03StudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.file;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface FileConfigMapper extends BaseMapperX<FileConfigDO> {\n\n    default PageResult<FileConfigDO> selectPage(FileConfigPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<FileConfigDO>()\n                .likeIfPresent(FileConfigDO::getName, reqVO.getName())\n                .eqIfPresent(FileConfigDO::getStorage, reqVO.getStorage())\n                .betweenIfPresent(FileConfigDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(FileConfigDO::getId));\n    }\n\n    default FileConfigDO selectByMaster() {\n        return selectOne(FileConfigDO::getMaster, true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.file;\n\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileContentDO;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n@Mapper\npublic interface FileContentMapper extends BaseMapper<FileContentDO> {\n\n    default void deleteByConfigIdAndPath(Long configId, String path) {\n        this.delete(new LambdaQueryWrapper<FileContentDO>()\n                .eq(FileContentDO::getConfigId, configId)\n                .eq(FileContentDO::getPath, path));\n    }\n\n    default List<FileContentDO> selectListByConfigIdAndPath(Long configId, String path) {\n        return selectList(new LambdaQueryWrapper<FileContentDO>()\n                .eq(FileContentDO::getConfigId, configId)\n                .eq(FileContentDO::getPath, path));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.file;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 文件操作 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface FileMapper extends BaseMapperX<FileDO> {\n\n    default PageResult<FileDO> selectPage(FilePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<FileDO>()\n                .likeIfPresent(FileDO::getPath, reqVO.getPath())\n                .likeIfPresent(FileDO::getType, reqVO.getType())\n                .betweenIfPresent(FileDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(FileDO::getId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiAccessLogMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.logger;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO;\nimport org.apache.ibatis.annotations.Delete;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\n\n/**\n * API 访问日志 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ApiAccessLogMapper extends BaseMapperX<ApiAccessLogDO> {\n\n    default PageResult<ApiAccessLogDO> selectPage(ApiAccessLogPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ApiAccessLogDO>()\n                .eqIfPresent(ApiAccessLogDO::getUserId, reqVO.getUserId())\n                .eqIfPresent(ApiAccessLogDO::getUserType, reqVO.getUserType())\n                .eqIfPresent(ApiAccessLogDO::getApplicationName, reqVO.getApplicationName())\n                .likeIfPresent(ApiAccessLogDO::getRequestUrl, reqVO.getRequestUrl())\n                .betweenIfPresent(ApiAccessLogDO::getBeginTime, reqVO.getBeginTime())\n                .geIfPresent(ApiAccessLogDO::getDuration, reqVO.getDuration())\n                .eqIfPresent(ApiAccessLogDO::getResultCode, reqVO.getResultCode())\n                .orderByDesc(ApiAccessLogDO::getId)\n        );\n    }\n\n    /**\n     * 物理删除指定时间之前的日志\n     *\n     * @param createTime 最大时间\n     * @param limit      删除条数，防止一次删除太多\n     * @return 删除条数\n     */\n    @Delete(\"DELETE FROM infra_api_access_log WHERE create_time < #{createTime} LIMIT #{limit}\")\n    Integer deleteByCreateTimeLt(@Param(\"createTime\") LocalDateTime createTime, @Param(\"limit\") Integer limit);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiErrorLogMapper.java",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.logger;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO;\nimport org.apache.ibatis.annotations.Delete;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\n\n/**\n * API 错误日志 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ApiErrorLogMapper extends BaseMapperX<ApiErrorLogDO> {\n\n    default PageResult<ApiErrorLogDO> selectPage(ApiErrorLogPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ApiErrorLogDO>()\n                .eqIfPresent(ApiErrorLogDO::getUserId, reqVO.getUserId())\n                .eqIfPresent(ApiErrorLogDO::getUserType, reqVO.getUserType())\n                .eqIfPresent(ApiErrorLogDO::getApplicationName, reqVO.getApplicationName())\n                .likeIfPresent(ApiErrorLogDO::getRequestUrl, reqVO.getRequestUrl())\n                .betweenIfPresent(ApiErrorLogDO::getExceptionTime, reqVO.getExceptionTime())\n                .eqIfPresent(ApiErrorLogDO::getProcessStatus, reqVO.getProcessStatus())\n                .orderByDesc(ApiErrorLogDO::getId)\n        );\n    }\n\n    /**\n     * 物理删除指定时间之前的日志\n     *\n     * @param createTime 最大时间\n     * @param limit      删除条数，防止一次删除太多\n     * @return 删除条数\n     */\n    @Delete(\"DELETE FROM infra_api_error_log WHERE create_time < #{createTime} LIMIT #{limit}\")\n    Integer deleteByCreateTimeLt(@Param(\"createTime\") LocalDateTime createTime, @Param(\"limit\") Integer limit);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenConfiguration.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.codegen.config;\n\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(proxyBeanMethods = false)\n@EnableConfigurationProperties(CodegenProperties.class)\npublic class CodegenConfiguration {\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenProperties.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.codegen.config;\n\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenVOTypeEnum;\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Collection;\n\n@ConfigurationProperties(prefix = \"yudao.codegen\")\n@Validated\n@Data\npublic class CodegenProperties {\n\n    /**\n     * 生成的 Java 代码的基础包\n     */\n    @NotNull(message = \"Java 代码的基础包不能为空\")\n    private String basePackage;\n\n    /**\n     * 数据库名数组\n     */\n    @NotEmpty(message = \"数据库不能为空\")\n    private Collection<String> dbSchemas;\n\n    /**\n     * 代码生成的前端类型（默认）\n     *\n     * 枚举 {@link CodegenFrontTypeEnum#getType()}\n     */\n    @NotNull(message = \"代码生成的前端类型不能为空\")\n    private Integer frontType;\n\n    /**\n     * 代码生成的 VO 类型\n     *\n     * 枚举 {@link CodegenVOTypeEnum#getType()}\n     */\n    @NotNull(message = \"代码生成的 VO 类型不能为空\")\n    private Integer voType;\n\n    /**\n     * 是否生成批量删除接口\n     */\n    @NotNull(message = \"是否生成批量删除接口不能为空\")\n    private Boolean deleteBatchEnable;\n\n    /**\n     * 是否生成单元测试\n     */\n    @NotNull(message = \"是否生成单元测试不能为空\")\n    private Boolean unitTestEnable;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/package-info.java",
    "content": "/**\n * 代码生成器\n */\npackage cn.iocoder.yudao.module.infra.framework.codegen;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.config;\n\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactoryImpl;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * 文件配置类\n *\n * @author 芋道源码\n */\n@Configuration(proxyBeanMethods = false)\npublic class YudaoFileAutoConfiguration {\n\n    @Bean\n    public FileClientFactory fileClientFactory() {\n        return new FileClientFactoryImpl();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client;\n\nimport cn.hutool.core.util.StrUtil;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * 文件客户端的抽象类，提供模板方法，减少子类的冗余代码\n *\n * @author 芋道源码\n */\n@Slf4j\npublic abstract class AbstractFileClient<Config extends FileClientConfig> implements FileClient {\n\n    /**\n     * 配置编号\n     */\n    private final Long id;\n    /**\n     * 文件配置\n     */\n    protected Config config;\n    /**\n     * 原始的文件配置\n     *\n     * 原因：{@link #config} 可能被子类所修改，无法用于判断配置是否变更\n     * @link <a href=\"https://t.zsxq.com/29wkW\">相关案例</a>\n     */\n    private Config originalConfig;\n\n    public AbstractFileClient(Long id, Config config) {\n        this.id = id;\n        this.config = config;\n        this.originalConfig = config;\n    }\n\n    /**\n     * 初始化\n     */\n    public final void init() {\n        doInit();\n        log.debug(\"[init][配置({}) 初始化完成]\", config);\n    }\n\n    /**\n     * 自定义初始化\n     */\n    protected abstract void doInit();\n\n    public final void refresh(Config config) {\n        // 判断是否更新\n        if (config.equals(this.originalConfig)) {\n            return;\n        }\n        log.info(\"[refresh][配置({})发生变化，重新初始化]\", config);\n        this.config = config;\n        this.originalConfig = config;\n        // 初始化\n        this.init();\n    }\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    /**\n     * 格式化文件的 URL 访问地址\n     * 使用场景：local、ftp、db，通过 FileController 的 getFile 来获取文件内容\n     *\n     * @param domain 自定义域名\n     * @param path 文件路径\n     * @return URL 访问地址\n     */\n    protected String formatFileUrl(String domain, String path) {\n        return StrUtil.format(\"{}/admin-api/infra/file/{}/get/{}\", domain, getId(), path);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client;\n\n/**\n * 文件客户端\n *\n * @author 芋道源码\n */\npublic interface FileClient {\n\n    /**\n     * 获得客户端编号\n     *\n     * @return 客户端编号\n     */\n    Long getId();\n\n    /**\n     * 上传文件\n     *\n     * @param content 文件流\n     * @param path    相对路径\n     * @return 完整路径，即 HTTP 访问地址\n     * @throws Exception 上传文件时，抛出 Exception 异常\n     */\n    String upload(byte[] content, String path, String type) throws Exception;\n\n    /**\n     * 删除文件\n     *\n     * @param path 相对路径\n     * @throws Exception 删除文件时，抛出 Exception 异常\n     */\n    void delete(String path) throws Exception;\n\n    /**\n     * 获得文件的内容\n     *\n     * @param path 相对路径\n     * @return 文件的内容\n     */\n    byte[] getContent(String path) throws Exception;\n\n    // ========== 文件签名，目前仅 S3 支持 ==========\n\n    /**\n     * 获得文件预签名地址，用于上传\n     *\n     * @param path 相对路径\n     * @return 文件预签名地址\n     */\n    default String presignPutUrl(String path) {\n        throw new UnsupportedOperationException(\"不支持的操作\");\n    }\n\n    /**\n     * 生成文件预签名地址，用于读取\n     *\n     * @param url 完整的文件访问地址\n     * @param expirationSeconds 访问有效期，单位秒\n     * @return 文件预签名地址\n     */\n    default String presignGetUrl(String url, Integer expirationSeconds) {\n        throw new UnsupportedOperationException(\"不支持的操作\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client;\n\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * 文件客户端的配置\n * 不同实现的客户端，需要不同的配置，通过子类来定义\n *\n * @author 芋道源码\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n// @JsonTypeInfo 注解的作用，Jackson 多态\n// 1. 序列化到时数据库时，增加 @class 属性。\n// 2. 反序列化到内存对象时，通过 @class 属性，可以创建出正确的类型\npublic interface FileClientConfig {\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client;\n\nimport cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;\n\npublic interface FileClientFactory {\n\n    /**\n     * 获得文件客户端\n     *\n     * @param configId 配置编号\n     * @return 文件客户端\n     */\n    FileClient getFileClient(Long configId);\n\n    /**\n     * 创建文件客户端\n     *\n     * @param configId 配置编号\n     * @param storage 存储器的枚举 {@link FileStorageEnum}\n     * @param config 文件配置\n     */\n    <Config extends FileClientConfig> void createOrUpdateFileClient(Long configId, Integer storage, Config config);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * 文件客户端的工厂实现类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class FileClientFactoryImpl implements FileClientFactory {\n\n    /**\n     * 文件客户端 Map\n     * key：配置编号\n     */\n    private final ConcurrentMap<Long, AbstractFileClient<?>> clients = new ConcurrentHashMap<>();\n\n    @Override\n    public FileClient getFileClient(Long configId) {\n        AbstractFileClient<?> client = clients.get(configId);\n        if (client == null) {\n            log.error(\"[getFileClient][配置编号({}) 找不到客户端]\", configId);\n        }\n        return client;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <Config extends FileClientConfig> void createOrUpdateFileClient(Long configId, Integer storage, Config config) {\n        AbstractFileClient<Config> client = (AbstractFileClient<Config>) clients.get(configId);\n        if (client == null) {\n            client = this.createFileClient(configId, storage, config);\n            client.init();\n            clients.put(client.getId(), client);\n        } else {\n            client.refresh(config);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private <Config extends FileClientConfig> AbstractFileClient<Config> createFileClient(\n            Long configId, Integer storage, Config config) {\n        FileStorageEnum storageEnum = FileStorageEnum.getByStorage(storage);\n        Assert.notNull(storageEnum, String.format(\"文件配置(%s) 为空\", storageEnum));\n        // 创建客户端\n        return (AbstractFileClient<Config>) ReflectUtil.newInstance(storageEnum.getClientClass(), configId, config);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.db;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileContentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.file.FileContentMapper;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;\n\nimport java.util.Comparator;\nimport java.util.List;\n\n/**\n * 基于 DB 存储的文件客户端的配置类\n *\n * @author 芋道源码\n */\npublic class DBFileClient extends AbstractFileClient<DBFileClientConfig> {\n\n    private FileContentMapper fileContentMapper;\n\n    public DBFileClient(Long id, DBFileClientConfig config) {\n        super(id, config);\n    }\n\n    @Override\n    protected void doInit() {\n        fileContentMapper = SpringUtil.getBean(FileContentMapper.class);\n    }\n\n    @Override\n    public String upload(byte[] content, String path, String type) {\n        FileContentDO contentDO = new FileContentDO().setConfigId(getId())\n                .setPath(path).setContent(content);\n        fileContentMapper.insert(contentDO);\n        // 拼接返回路径\n        return super.formatFileUrl(config.getDomain(), path);\n    }\n\n    @Override\n    public void delete(String path) {\n        fileContentMapper.deleteByConfigIdAndPath(getId(), path);\n    }\n\n    @Override\n    public byte[] getContent(String path) {\n        List<FileContentDO> list = fileContentMapper.selectListByConfigIdAndPath(getId(), path);\n        if (CollUtil.isEmpty(list)) {\n            return null;\n        }\n        // 排序后，拿 id 最大的，即最后上传的\n        list.sort(Comparator.comparing(FileContentDO::getId));\n        return CollUtil.getLast(list).getContent();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.db;\n\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * 基于 DB 存储的文件客户端的配置类\n *\n * @author 芋道源码\n */\n@Data\npublic class DBFileClientConfig implements FileClientConfig {\n\n    /**\n     * 自定义域名\n     */\n    @NotEmpty(message = \"domain 不能为空\")\n    @URL(message = \"domain 必须是 URL 格式\")\n    private String domain;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.ftp;\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.util.CharsetUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.ftp.Ftp;\nimport cn.hutool.extra.ftp.FtpConfig;\nimport cn.hutool.extra.ftp.FtpException;\nimport cn.hutool.extra.ftp.FtpMode;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\n\n/**\n * Ftp 文件客户端\n *\n * @author 芋道源码\n */\npublic class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> {\n\n    /**\n     * 连接超时时间，单位：毫秒\n     */\n    private static final Long CONNECTION_TIMEOUT = 3000L;\n    /**\n     * 读写超时时间，单位：毫秒\n     */\n    private static final Long SO_TIMEOUT = 10000L;\n\n    private Ftp ftp;\n\n    public FtpFileClient(Long id, FtpFileClientConfig config) {\n        super(id, config);\n    }\n\n    @Override\n    protected void doInit() {\n        // 初始化 Ftp 对象：https://gitee.com/zhijiantianya/yudao-cloud/pulls/207/\n        FtpConfig ftpConfig = new FtpConfig(config.getHost(), config.getPort(), config.getUsername(), config.getPassword(),\n                CharsetUtil.CHARSET_UTF_8, null, null);\n        ftpConfig.setConnectionTimeout(CONNECTION_TIMEOUT);\n        ftpConfig.setSoTimeout(SO_TIMEOUT);\n        this.ftp = new Ftp(ftpConfig, FtpMode.valueOf(config.getMode()));\n    }\n\n    @Override\n    public String upload(byte[] content, String path, String type) {\n        // 执行写入\n        String filePath = getFilePath(path);\n        String fileName = FileUtil.getName(filePath);\n        String dir = StrUtil.removeSuffix(filePath, fileName);\n        reconnectIfTimeout();\n        boolean success = ftp.upload(dir, fileName, new ByteArrayInputStream(content)); // 不需要主动创建目录，ftp 内部已经处理（见源码）\n        if (!success) {\n            throw new FtpException(StrUtil.format(\"上传文件到目标目录 ({}) 失败\", filePath));\n        }\n        // 拼接返回路径\n        return super.formatFileUrl(config.getDomain(), path);\n    }\n\n    @Override\n    public void delete(String path) {\n        String filePath = getFilePath(path);\n        reconnectIfTimeout();\n        ftp.delFile(filePath);\n    }\n\n    @Override\n    public byte[] getContent(String path) {\n        String filePath = getFilePath(path);\n        String fileName = FileUtil.getName(filePath);\n        String dir = StrUtil.removeSuffix(filePath, fileName);\n        ByteArrayOutputStream out = new ByteArrayOutputStream();\n        reconnectIfTimeout();\n        ftp.download(dir, fileName, out);\n        return out.toByteArray();\n    }\n\n    private String getFilePath(String path) {\n        return config.getBasePath() + StrUtil.SLASH + path;\n    }\n\n    private synchronized void reconnectIfTimeout() {\n        ftp.reconnectIfTimeout();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.ftp;\n\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * Ftp 文件客户端的配置类\n *\n * @author 芋道源码\n */\n@Data\npublic class FtpFileClientConfig implements FileClientConfig {\n\n    /**\n     * 基础路径\n     */\n    @NotEmpty(message = \"基础路径不能为空\")\n    private String basePath;\n\n    /**\n     * 自定义域名\n     */\n    @NotEmpty(message = \"domain 不能为空\")\n    @URL(message = \"domain 必须是 URL 格式\")\n    private String domain;\n\n    /**\n     * 主机地址\n     */\n    @NotEmpty(message = \"host 不能为空\")\n    private String host;\n    /**\n     * 主机端口\n     */\n    @NotNull(message = \"port 不能为空\")\n    private Integer port;\n    /**\n     * 用户名\n     */\n    @NotEmpty(message = \"用户名不能为空\")\n    private String username;\n    /**\n     * 密码\n     */\n    @NotEmpty(message = \"密码不能为空\")\n    private String password;\n    /**\n     * 连接模式\n     *\n     * 使用 {@link  cn.hutool.extra.ftp.FtpMode} 对应的字符串\n     */\n    @NotEmpty(message = \"连接模式不能为空\")\n    private String mode;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.local;\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.io.IORuntimeException;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;\n\nimport java.io.File;\n\n/**\n * 本地文件客户端\n *\n * @author 芋道源码\n */\npublic class LocalFileClient extends AbstractFileClient<LocalFileClientConfig> {\n\n    public LocalFileClient(Long id, LocalFileClientConfig config) {\n        super(id, config);\n    }\n\n    @Override\n    protected void doInit() {\n    }\n\n    @Override\n    public String upload(byte[] content, String path, String type) {\n        // 执行写入\n        String filePath = getFilePath(path);\n        FileUtil.writeBytes(content, filePath);\n        // 拼接返回路径\n        return super.formatFileUrl(config.getDomain(), path);\n    }\n\n    @Override\n    public void delete(String path) {\n        String filePath = getFilePath(path);\n        FileUtil.del(filePath);\n    }\n\n    @Override\n    public byte[] getContent(String path) {\n        String filePath = getFilePath(path);\n        try {\n            return FileUtil.readBytes(filePath);\n        } catch (IORuntimeException ex) {\n            if (ex.getMessage().startsWith(\"File not exist:\")) {\n                return null;\n            }\n            throw ex;\n        }\n    }\n\n    private String getFilePath(String path) {\n        return config.getBasePath() + File.separator + path;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.local;\n\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * 本地文件客户端的配置类\n *\n * @author 芋道源码\n */\n@Data\npublic class LocalFileClientConfig implements FileClientConfig {\n\n    /**\n     * 基础路径\n     */\n    @NotEmpty(message = \"基础路径不能为空\")\n    private String basePath;\n\n    /**\n     * 自定义域名\n     */\n    @NotEmpty(message = \"domain 不能为空\")\n    @URL(message = \"domain 必须是 URL 格式\")\n    private String domain;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.s3;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.http.HttpUtil;\nimport cn.iocoder.yudao.framework.common.util.http.HttpUtils;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;\nimport software.amazon.awssdk.auth.credentials.AwsBasicCredentials;\nimport software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;\nimport software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;\nimport software.amazon.awssdk.core.sync.RequestBody;\nimport software.amazon.awssdk.regions.Region;\nimport software.amazon.awssdk.services.s3.S3Client;\nimport software.amazon.awssdk.services.s3.S3Configuration;\nimport software.amazon.awssdk.services.s3.model.DeleteObjectRequest;\nimport software.amazon.awssdk.services.s3.model.GetObjectRequest;\nimport software.amazon.awssdk.services.s3.model.PutObjectRequest;\nimport software.amazon.awssdk.services.s3.presigner.S3Presigner;\nimport software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;\nimport software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;\n\nimport java.net.URI;\nimport java.net.URL;\nimport java.time.Duration;\n\n/**\n * 基于 S3 协议的文件客户端，实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务\n *\n * @author 芋道源码\n */\npublic class S3FileClient extends AbstractFileClient<S3FileClientConfig> {\n\n    private static final Duration EXPIRATION_DEFAULT = Duration.ofHours(24);\n\n    private S3Client client;\n    private S3Presigner presigner;\n\n    public S3FileClient(Long id, S3FileClientConfig config) {\n        super(id, config);\n    }\n\n    @Override\n    protected void doInit() {\n        // 补全 domain\n        if (StrUtil.isEmpty(config.getDomain())) {\n            config.setDomain(buildDomain());\n        }\n        // 初始化 S3 客户端\n        // 优先级：配置的 region > 从 endpoint 解析的 region > 默认值 us-east-1\n        String regionStr = resolveRegion();\n        Region region = Region.of(regionStr);\n        AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(\n                AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret()));\n        URI endpoint = URI.create(buildEndpoint());\n        S3Configuration serviceConfiguration = S3Configuration.builder() // Path-style 访问\n                .pathStyleAccessEnabled(Boolean.TRUE.equals(config.getEnablePathStyleAccess()))\n                .chunkedEncodingEnabled(false) // 禁用分块编码，参见 https://t.zsxq.com/kBy57\n                .build();\n        client = S3Client.builder()\n                .credentialsProvider(credentialsProvider)\n                .region(region)\n                .endpointOverride(endpoint)\n                .serviceConfiguration(serviceConfiguration)\n                .build();\n        presigner = S3Presigner.builder()\n                .credentialsProvider(credentialsProvider)\n                .region(region)\n                .endpointOverride(endpoint)\n                .serviceConfiguration(serviceConfiguration)\n                .build();\n    }\n\n    @Override\n    public String upload(byte[] content, String path, String type) {\n        // 构造 PutObjectRequest\n        PutObjectRequest putRequest = PutObjectRequest.builder()\n                .bucket(config.getBucket())\n                .key(path)\n                .contentType(type)\n                .contentLength((long) content.length)\n                .build();\n        // 上传文件\n        client.putObject(putRequest, RequestBody.fromBytes(content));\n        // 拼接返回路径\n        return presignGetUrl(path, null);\n    }\n\n    @Override\n    public void delete(String path) {\n        DeleteObjectRequest deleteRequest = DeleteObjectRequest.builder()\n                .bucket(config.getBucket())\n                .key(path)\n                .build();\n        client.deleteObject(deleteRequest);\n    }\n\n    @Override\n    public byte[] getContent(String path) {\n        GetObjectRequest getRequest = GetObjectRequest.builder()\n                .bucket(config.getBucket())\n                .key(path)\n                .build();\n        return IoUtil.readBytes(client.getObject(getRequest));\n    }\n\n    @Override\n    public String presignPutUrl(String path) {\n        return presigner.presignPutObject(PutObjectPresignRequest.builder()\n                .signatureDuration(EXPIRATION_DEFAULT)\n                .putObjectRequest(b -> b.bucket(config.getBucket()).key(path)).build())\n                .url().toString();\n    }\n\n    @Override\n    public String presignGetUrl(String url, Integer expirationSeconds) {\n        // 1. 将 url 转换为 path\n        String path = StrUtil.removePrefix(url, config.getDomain() + \"/\");\n        path = HttpUtils.decodeUrlPath(HttpUtils.removeUrlQuery(path));\n\n        // 2.1 情况一：公开访问：无需签名\n        // 考虑到老版本的兼容，所以必须是 config.getEnablePublicAccess() 为 false 时，才进行签名\n        if (!BooleanUtil.isFalse(config.getEnablePublicAccess())) {\n            return config.getDomain() + \"/\" + path;\n        }\n\n        // 2.2 情况二：私有访问：生成 GET 预签名 URL\n        String finalPath = path;\n        Duration expiration = expirationSeconds != null ? Duration.ofSeconds(expirationSeconds) : EXPIRATION_DEFAULT;\n        URL signedUrl = presigner.presignGetObject(GetObjectPresignRequest.builder()\n                .signatureDuration(expiration)\n                .getObjectRequest(b -> b.bucket(config.getBucket()).key(finalPath)).build())\n                .url();\n        return signedUrl.toString();\n    }\n\n    /**\n     * 基于 bucket + endpoint 构建访问的 Domain 地址\n     *\n     * @return Domain 地址\n     */\n    private String buildDomain() {\n        // 如果已经是 http 或者 https，则不进行拼接.主要适配 MinIO\n        if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {\n            return StrUtil.format(\"{}/{}\", config.getEndpoint(), config.getBucket());\n        }\n        // 阿里云、腾讯云、华为云都适合。七牛云比较特殊，必须有自定义域名\n        return StrUtil.format(\"https://{}.{}\", config.getBucket(), config.getEndpoint());\n    }\n\n    /**\n     * 节点地址补全协议头\n     *\n     * @return 节点地址\n     */\n    private String buildEndpoint() {\n        // 如果已经是 http 或者 https，则不进行拼接\n        if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {\n            return config.getEndpoint();\n        }\n        return StrUtil.format(\"https://{}\", config.getEndpoint());\n    }\n\n    /**\n     * 解析 AWS 区域\n     * 优先级：配置的 region > 从 endpoint 解析的 region > 默认值 us-east-1\n     *\n     * @return 区域字符串\n     */\n    private String resolveRegion() {\n        // 1. 如果配置了 region，直接使用\n        if (StrUtil.isNotEmpty(config.getRegion())) {\n            return config.getRegion();\n        }\n\n        // 2.1 尝试从 endpoint 中解析 region\n        String endpoint = config.getEndpoint();\n        if (StrUtil.isEmpty(endpoint)) {\n            return \"us-east-1\";\n        }\n\n        // 2.2 移除协议头（http:// 或 https://）\n        String host = endpoint;\n        if (HttpUtil.isHttp(endpoint) || HttpUtil.isHttps(endpoint)) {\n            try {\n                host = URI.create(endpoint).getHost();\n            } catch (Exception e) {\n                // 解析失败，使用默认值\n                return \"us-east-1\";\n            }\n        }\n        if (StrUtil.isEmpty(host)) {\n            return \"us-east-1\";\n        }\n\n        // 3.1 AWS S3 格式：s3.us-west-2.amazonaws.com 或 s3.amazonaws.com\n        if (host.contains(\"amazonaws.com\")) {\n            // 匹配 s3.{region}.amazonaws.com 格式\n            if (host.startsWith(\"s3.\") && host.contains(\".amazonaws.com\")) {\n                String regionPart = host.substring(3, host.indexOf(\".amazonaws.com\"));\n                if (StrUtil.isNotEmpty(regionPart) && !regionPart.equals(\"accelerate\")) {\n                    return regionPart;\n                }\n            }\n            // s3.amazonaws.com 或 s3-accelerate.amazonaws.com 使用默认值\n            return \"us-east-1\";\n        }\n        // 3.2 阿里云 OSS 格式：oss-cn-beijing.aliyuncs.com\n        if (host.contains(S3FileClientConfig.ENDPOINT_ALIYUN)) {\n            // 匹配 oss-{region}.aliyuncs.com 格式\n            if (host.startsWith(\"oss-\") && host.contains(\".\" + S3FileClientConfig.ENDPOINT_ALIYUN)) {\n                String regionPart = host.substring(4, host.indexOf(\".\" + S3FileClientConfig.ENDPOINT_ALIYUN));\n                if (StrUtil.isNotEmpty(regionPart)) {\n                    return regionPart;\n                }\n            }\n        }\n        // 3.3 腾讯云 COS 格式：cos.ap-shanghai.myqcloud.com\n        if (host.contains(S3FileClientConfig.ENDPOINT_TENCENT)) {\n            // 匹配 cos.{region}.myqcloud.com 格式\n            if (host.startsWith(\"cos.\") && host.contains(\".\" + S3FileClientConfig.ENDPOINT_TENCENT)) {\n                String regionPart = host.substring(4, host.indexOf(\".\" + S3FileClientConfig.ENDPOINT_TENCENT));\n                if (StrUtil.isNotEmpty(regionPart)) {\n                    return regionPart;\n                }\n            }\n        }\n\n        // 3.4 其他情况（MinIO、七牛云等）使用默认值\n        return \"us-east-1\";\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.s3;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.NotNull;\n\n/**\n * S3 文件客户端的配置类\n *\n * @author 芋道源码\n */\n@Data\npublic class S3FileClientConfig implements FileClientConfig {\n\n    public static final String ENDPOINT_QINIU = \"qiniucs.com\";\n    public static final String ENDPOINT_ALIYUN = \"aliyuncs.com\";\n    public static final String ENDPOINT_TENCENT = \"myqcloud.com\";\n    public static final String ENDPOINT_VOLCES = \"volces.com\"; // 火山云（字节）\n\n    /**\n     * 节点地址\n     * 1. MinIO：https://www.iocoder.cn/Spring-Boot/MinIO 。例如说，http://127.0.0.1:9000\n     * 2. 阿里云：https://help.aliyun.com/document_detail/31837.html\n     * 3. 腾讯云：https://cloud.tencent.com/document/product/436/6224\n     * 4. 七牛云：https://developer.qiniu.com/kodo/4088/s3-access-domainname\n     * 5. 华为云：https://console.huaweicloud.com/apiexplorer/#/endpoint/OBS\n     * 6. 火山云：https://www.volcengine.com/docs/6349/107356\n     */\n    @NotNull(message = \"endpoint 不能为空\")\n    private String endpoint;\n    /**\n     * 自定义域名\n     * 1. MinIO：通过 Nginx 配置\n     * 2. 阿里云：https://help.aliyun.com/document_detail/31836.html\n     * 3. 腾讯云：https://cloud.tencent.com/document/product/436/11142\n     * 4. 七牛云：https://developer.qiniu.com/kodo/8556/set-the-custom-source-domain-name\n     * 5. 华为云：https://support.huaweicloud.com/usermanual-obs/obs_03_0032.html\n     * 6. 火山云：https://www.volcengine.com/docs/6349/128983\n     */\n    @URL(message = \"domain 必须是 URL 格式\")\n    private String domain;\n    /**\n     * 存储 Bucket\n     */\n    @NotNull(message = \"bucket 不能为空\")\n    private String bucket;\n\n    /**\n     * 访问 Key\n     * 1. MinIO：https://www.iocoder.cn/Spring-Boot/MinIO\n     * 2. 阿里云：https://ram.console.aliyun.com/manage/ak\n     * 3. 腾讯云：https://console.cloud.tencent.com/cam/capi\n     * 4. 七牛云：https://portal.qiniu.com/user/key\n     * 5. 华为云：https://support.huaweicloud.com/qs-obs/obs_qs_0005.html\n     * 6. 火山云：https://console.volcengine.com/iam/keymanage/\n     */\n    @NotNull(message = \"accessKey 不能为空\")\n    private String accessKey;\n    /**\n     * 访问 Secret\n     */\n    @NotNull(message = \"accessSecret 不能为空\")\n    private String accessSecret;\n\n    /**\n     * 是否启用 PathStyle 访问\n     */\n    @NotNull(message = \"enablePathStyleAccess 不能为空\")\n    private Boolean enablePathStyleAccess;\n\n    /**\n     * 是否公开访问\n     *\n     * true：公开访问，所有人都可以访问\n     * false：私有访问，只有配置的 accessKey 才可以访问\n     */\n    @NotNull(message = \"是否公开访问不能为空\")\n    private Boolean enablePublicAccess;\n\n    /**\n     * 区域\n     * 1. AWS S3：https://docs.aws.amazon.com/general/latest/gr/s3.html 例如说，us-east-1、us-west-2\n     * 2. MinIO：可以填任意值，通常使用 us-east-1\n     * 3. 阿里云：不需要填写，会自动识别\n     * 4. 腾讯云：不需要填写，会自动识别\n     * 5. 七牛云：不需要填写，会自动识别\n     * 6. 华为云：不需要填写，会自动识别\n     * 7. 火山云：不需要填写，会自动识别\n     */\n    private String region;\n\n    @SuppressWarnings(\"RedundantIfStatement\")\n    @AssertTrue(message = \"domain 不能为空\")\n    @JsonIgnore\n    public boolean isDomainValid() {\n        // 如果是七牛，必须带有 domain\n        if (StrUtil.contains(endpoint, ENDPOINT_QINIU) && StrUtil.isEmpty(domain)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.sftp;\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.util.CharsetUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.ftp.FtpConfig;\nimport cn.hutool.extra.ssh.JschRuntimeException;\nimport cn.hutool.extra.ssh.Sftp;\nimport cn.iocoder.yudao.framework.common.util.io.FileUtils;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;\nimport com.jcraft.jsch.JSch;\n\nimport java.io.File;\n\n/**\n * Sftp 文件客户端\n *\n * @author 芋道源码\n */\npublic class SftpFileClient extends AbstractFileClient<SftpFileClientConfig> {\n\n    /**\n     * 连接超时时间，单位：毫秒\n     */\n    private static final Long CONNECTION_TIMEOUT = 3000L;\n    /**\n     * 读写超时时间，单位：毫秒\n     */\n    private static final Long SO_TIMEOUT = 10000L;\n\n    static {\n        // 某些旧的 sftp 服务器仅支持 ssh-dss 协议，该协议并不安全，默认不支持该协议，按需添加\n        JSch.setConfig(\"server_host_key\", JSch.getConfig(\"server_host_key\") + \",ssh-dss\");\n    }\n\n    private Sftp sftp;\n\n    public SftpFileClient(Long id, SftpFileClientConfig config) {\n        super(id, config);\n    }\n\n    @Override\n    protected void doInit() {\n        // 初始化 Sftp 对象\n        FtpConfig ftpConfig = new FtpConfig(config.getHost(), config.getPort(), config.getUsername(), config.getPassword(),\n                CharsetUtil.CHARSET_UTF_8, null, null);\n        ftpConfig.setConnectionTimeout(CONNECTION_TIMEOUT);\n        ftpConfig.setSoTimeout(SO_TIMEOUT);\n        this.sftp = new Sftp(ftpConfig);\n    }\n\n    @Override\n    public String upload(byte[] content, String path, String type) {\n        // 执行写入\n        String filePath = getFilePath(path);\n        String fileName = FileUtil.getName(filePath);\n        String dir = StrUtil.removeSuffix(filePath, fileName);\n        File file = FileUtils.createTempFile(content);\n        reconnectIfTimeout();\n        sftp.mkDirs(dir); // 需要创建父目录，不然会报错\n        boolean success = sftp.upload(filePath, file);\n        if (!success) {\n            throw new JschRuntimeException(StrUtil.format(\"上传文件到目标目录 ({}) 失败\", filePath));\n        }\n        // 拼接返回路径\n        return super.formatFileUrl(config.getDomain(), path);\n    }\n\n    @Override\n    public void delete(String path) {\n        String filePath = getFilePath(path);\n        reconnectIfTimeout();\n        sftp.delFile(filePath);\n    }\n\n    @Override\n    public byte[] getContent(String path) {\n        String filePath = getFilePath(path);\n        File destFile = FileUtils.createTempFile();\n        reconnectIfTimeout();\n        sftp.download(filePath, destFile);\n        return FileUtil.readBytes(destFile);\n    }\n\n    private String getFilePath(String path) {\n        return config.getBasePath() + File.separator + path;\n    }\n\n    private synchronized void reconnectIfTimeout() {\n        sftp.reconnectIfTimeout();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.client.sftp;\n\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * Sftp 文件客户端的配置类\n *\n * @author 芋道源码\n */\n@Data\npublic class SftpFileClientConfig implements FileClientConfig {\n\n    /**\n     * 基础路径\n     */\n    @NotEmpty(message = \"基础路径不能为空\")\n    private String basePath;\n\n    /**\n     * 自定义域名\n     */\n    @NotEmpty(message = \"domain 不能为空\")\n    @URL(message = \"domain 必须是 URL 格式\")\n    private String domain;\n\n    /**\n     * 主机地址\n     */\n    @NotEmpty(message = \"host 不能为空\")\n    private String host;\n    /**\n     * 主机端口\n     */\n    @NotNull(message = \"port 不能为空\")\n    private Integer port;\n    /**\n     * 用户名\n     */\n    @NotEmpty(message = \"用户名不能为空\")\n    private String username;\n    /**\n     * 密码\n     */\n    @NotEmpty(message = \"密码不能为空\")\n    private String password;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.enums;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 文件存储器枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum FileStorageEnum {\n\n    DB(1, DBFileClientConfig.class, DBFileClient.class),\n\n    LOCAL(10, LocalFileClientConfig.class, LocalFileClient.class),\n    FTP(11, FtpFileClientConfig.class, FtpFileClient.class),\n    SFTP(12, SftpFileClientConfig.class, SftpFileClient.class),\n\n    S3(20, S3FileClientConfig.class, S3FileClient.class),\n    ;\n\n    /**\n     * 存储器\n     */\n    private final Integer storage;\n\n    /**\n     * 配置类\n     */\n    private final Class<? extends FileClientConfig> configClass;\n    /**\n     * 客户端类\n     */\n    private final Class<? extends FileClient> clientClass;\n\n    public static FileStorageEnum getByStorage(Integer storage) {\n        return ArrayUtil.firstMatch(o -> o.getStorage().equals(storage), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.utils;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.http.HttpUtils;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.tika.Tika;\nimport org.apache.tika.mime.MimeTypeException;\nimport org.apache.tika.mime.MimeTypes;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * 文件类型 Utils\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class FileTypeUtils {\n\n    private static final Tika TIKA = new Tika();\n\n    /**\n     * 获得文件的 mineType，对于 doc，jar 等文件会有误差\n     *\n     * @param data 文件内容\n     * @return mineType 无法识别时会返回“application/octet-stream”\n     */\n    @SneakyThrows\n    public static String getMineType(byte[] data) {\n        return TIKA.detect(data);\n    }\n\n    /**\n     * 已知文件名，获取文件类型，在某些情况下比通过字节数组准确，例如使用 jar 文件时，通过名字更为准确\n     *\n     * @param name 文件名\n     * @return mineType 无法识别时会返回“application/octet-stream”\n     */\n    public static String getMineType(String name) {\n        return TIKA.detect(name);\n    }\n\n    /**\n     * 在拥有文件和数据的情况下，最好使用此方法，最为准确\n     *\n     * @param data 文件内容\n     * @param name 文件名\n     * @return mineType 无法识别时会返回“application/octet-stream”\n     */\n    public static String getMineType(byte[] data, String name) {\n        return TIKA.detect(data, name);\n    }\n\n    /**\n     * 根据 mineType 获得文件后缀\n     *\n     * 注意：如果获取不到，或者发生异常，都返回 null\n     *\n     * @param mineType 类型\n     * @return 后缀，例如说 .pdf\n     */\n    public static String getExtension(String mineType) {\n        try {\n            return MimeTypes.getDefaultMimeTypes().forName(mineType).getExtension();\n        } catch (MimeTypeException e) {\n            log.warn(\"[getExtension][获取文件后缀({}) 失败]\", mineType, e);\n            return null;\n        }\n    }\n\n    /**\n     * 返回附件\n     *\n     * @param response 响应\n     * @param filename 文件名\n     * @param content  附件内容\n     */\n    public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {\n        // 设置 header 和 contentType\n        String mineType = getMineType(content, filename);\n        response.setContentType(mineType);\n        // 设置内容显示、下载文件名：https://www.cnblogs.com/wq-9/articles/12165056.html\n        if (isImage(mineType)) {\n            // 参见 https://github.com/YunaiV/ruoyi-vue-pro/issues/692 讨论\n            response.setHeader(\"Content-Disposition\", \"inline;filename=\" + HttpUtils.encodeUtf8(filename));\n        } else {\n            response.setHeader(\"Content-Disposition\", \"attachment;filename=\" + HttpUtils.encodeUtf8(filename));\n        }\n        // 针对 video 的特殊处理，解决视频地址在移动端播放的兼容性问题\n        if (StrUtil.containsIgnoreCase(mineType, \"video\")) {\n            response.setHeader(\"Accept-Ranges\", \"bytes\");\n            response.setHeader(\"Content-Length\", String.valueOf(content.length));\n        }\n        // 输出附件\n        IoUtil.write(response.getOutputStream(), false, content);\n    }\n\n    /**\n     * 判断是否是图片\n     *\n     * @param mineType 类型\n     * @return 是否是图片\n     */\n    public static boolean isImage(String mineType) {\n        return StrUtil.startWith(mineType, \"image/\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java",
    "content": "/**\n * 文件客户端，支持多种存储器\n *\n * 1. local：本地磁盘\n * 2. ftp：FTP 服务器\n * 3. sftp：SFTP 服务器\n * 4. db：数据库\n * 5. s3：支持 S3 协议的云存储服务，例如说 MinIO、阿里云、华为云、腾讯云、七牛云等等\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.infra.framework.file;"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/config/AdminServerConfiguration.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.monitor.config;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.csrf.CookieCsrfTokenRepository;\n\nimport javax.servlet.DispatcherType;\n\n/**\n * Spring Boot Admin Server 配置\n *\n * 包含 Admin Server 的启用配置和安全配置\n * 安全配置独立于 {@link cn.iocoder.yudao.framework.security.config.YudaoWebSecurityConfigurerAdapter}，\n * 使用 HTTP Basic 认证保护 Admin Server 端点，不影响现有的 Token 认证机制\n *\n * @author 芋道源码\n */\n@Configuration(proxyBeanMethods = false)\n//@EnableAdminServer\n@ConditionalOnClass(name = \"de.codecentric.boot.admin.server.config.AdminServerProperties\") // 目的：按需启动 spring boot admin 监控服务\npublic class AdminServerConfiguration {\n\n    @Value(\"${spring.boot.admin.context-path:''}\")\n    private String adminSeverContextPath;\n\n    @Value(\"${spring.boot.admin.client.username:admin}\")\n    private String username;\n\n    @Value(\"${spring.boot.admin.client.password:admin}\")\n    private String password;\n\n    /**\n     * Spring Boot Admin 专用的 InMemoryUserDetailsManager\n     * 使用内存存储，与系统用户隔离\n     */\n    @Bean(\"adminUserDetailsManager\")\n    public InMemoryUserDetailsManager adminUserDetailsManager(PasswordEncoder passwordEncoder) {\n        UserDetails adminUser = User.builder()\n                .username(username)\n                .password(passwordEncoder.encode(password))\n                .roles(\"ADMIN_SERVER\")\n                .build();\n        return new InMemoryUserDetailsManager(adminUser);\n    }\n\n    /**\n     * Spring Boot Admin Server 的 SecurityFilterChain\n     * 使用 @Order(1) 确保优先于默认的 SecurityFilterChain 匹配\n     */\n    @Bean(\"adminServerSecurityFilterChain\")\n    @Order(1)\n    public SecurityFilterChain adminServerSecurityFilterChain(HttpSecurity httpSecurity,\n                                                               InMemoryUserDetailsManager adminUserDetailsManager) throws Exception {\n        // 登录成功后的处理器\n        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();\n        successHandler.setTargetUrlParameter(\"redirectTo\");\n        successHandler.setDefaultTargetUrl(adminSeverContextPath + \"/\");\n\n        // 配置 HttpSecurity 对象\n        httpSecurity\n                // 仅匹配 Admin Server 的路径\n                .securityMatcher(adminSeverContextPath + \"/**\")\n                // 使用独立的 UserDetailsManager\n                .userDetailsService(adminUserDetailsManager)\n                // 授权配置\n                .authorizeHttpRequests(auth -> auth\n                        .requestMatchers(adminSeverContextPath + \"/assets/**\").permitAll() // 静态资源允许匿名访问\n                        .requestMatchers(adminSeverContextPath + \"/login\").permitAll() // 登录页面允许匿名访问\n                        .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll() // 异步请求允许\n                        .anyRequest().authenticated() // 其他请求需要认证\n                )\n                // 表单登录配置（用于 Admin UI 访问）\n                .formLogin(form -> form\n                        .loginPage(adminSeverContextPath + \"/login\")\n                        .successHandler(successHandler)\n                        .permitAll()\n                )\n                // 登出配置\n                .logout(logout -> logout\n                        .logoutUrl(adminSeverContextPath + \"/logout\")\n                        .logoutSuccessUrl(adminSeverContextPath + \"/login\")\n                )\n                // HTTP Basic 认证（用于 Admin Client 注册）\n                .httpBasic(Customizer.withDefaults())\n                // CSRF 配置\n                .csrf(csrf -> csrf\n                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())\n                        .ignoringRequestMatchers(\n                                adminSeverContextPath + \"/instances\", // Admin Client 注册端点忽略 CSRF\n                                adminSeverContextPath + \"/actuator/**\" // Actuator 端点忽略 CSRF\n                        )\n                );\n        return httpSecurity.build();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/package-info.java",
    "content": "/**\n * 使用 Spring Boot Admin 实现简单的监控平台\n */\npackage cn.iocoder.yudao.module.infra.framework.monitor;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/《芋道 Spring Boot 监控工具 Admin 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Admin/?yudao>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/package-info.java",
    "content": "/**\n * 属于 infra 模块的 framework 封装\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.infra.framework;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/rpc/config/RpcConfiguration.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.rpc.config;\n\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(value = \"infraRpcConfiguration\", proxyBeanMethods = false)\n@EnableFeignClients()\npublic class RpcConfiguration {\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/rpc/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.infra.framework.rpc;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/security/config/SecurityConfiguration.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.security.config;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.module.infra.enums.ApiConstants;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\n/**\n * Infra 模块的 Security 配置\n */\n@Configuration(proxyBeanMethods = false, value = \"infraSecurityConfiguration\")\npublic class SecurityConfiguration {\n\n    @Bean(\"infraAuthorizeRequestsCustomizer\")\n    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {\n        return new AuthorizeRequestsCustomizer() {\n\n            @Override\n            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n                // Swagger 接口文档\n                registry.requestMatchers(\"/v3/api-docs/**\").permitAll()\n                        .requestMatchers(\"/webjars/**\").permitAll()\n                        .requestMatchers(\"/swagger-ui\").permitAll()\n                        .requestMatchers(\"/swagger-ui/**\").permitAll();\n                // Spring Boot Actuator 的安全配置\n                registry.requestMatchers(\"/actuator\").permitAll()\n                        .requestMatchers(\"/actuator/**\").permitAll();\n                // Druid 监控\n                registry.requestMatchers(\"/druid/**\").permitAll();\n                // 文件读取\n                registry.requestMatchers(buildAdminApi(\"/infra/file/*/get/**\")).permitAll();\n\n                // TODO 芋艿：这个每个项目都需要重复配置，得捉摸有没通用的方案\n                // RPC 服务的安全配置\n                registry.requestMatchers(ApiConstants.PREFIX + \"/**\").permitAll();\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/security/core/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.infra.framework.security.core;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/job/logger/AccessLogCleanJob.java",
    "content": "package cn.iocoder.yudao.module.infra.job.logger;\n\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.infra.service.logger.ApiAccessLogService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * 物理删除 N 天前的访问日志的 Job\n *\n * @author j-sentinel\n */\n@Component\n@Slf4j\npublic class AccessLogCleanJob {\n\n    @Resource\n    private ApiAccessLogService apiAccessLogService;\n\n    /**\n     * 清理超过（14）天的日志\n     */\n    private static final Integer JOB_CLEAN_RETAIN_DAY = 14;\n\n    /**\n     * 每次删除间隔的条数，如果值太高可能会造成数据库的压力过大\n     */\n    private static final Integer DELETE_LIMIT = 100;\n\n    @XxlJob(\"accessLogCleanJob\")\n    @TenantIgnore\n    public void execute() {\n        Integer count = apiAccessLogService.cleanAccessLog(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT);\n        log.info(\"[execute][定时执行清理访问日志数量 ({}) 个]\", count);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/job/logger/ErrorLogCleanJob.java",
    "content": "package cn.iocoder.yudao.module.infra.job.logger;\n\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.infra.service.logger.ApiErrorLogService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * 物理删除 N 天前的错误日志的 Job\n *\n * @author j-sentinel\n */\n@Slf4j\n@Component\npublic class ErrorLogCleanJob {\n\n    @Resource\n    private ApiErrorLogService apiErrorLogService;\n\n    /**\n     * 清理超过（14）天的日志\n     */\n    private static final Integer JOB_CLEAN_RETAIN_DAY = 14;\n\n    /**\n     * 每次删除间隔的条数，如果值太高可能会造成数据库的压力过大\n     */\n    private static final Integer DELETE_LIMIT = 100;\n\n    @XxlJob(\"errorLogCleanJob\")\n    @TenantIgnore\n    public void execute() {\n        Integer count = apiErrorLogService.cleanErrorLog(JOB_CLEAN_RETAIN_DAY,DELETE_LIMIT);\n        log.info(\"[execute][定时执行清理错误日志数量 ({}) 个]\", count);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/job/package-info.java",
    "content": "/**\n * 占位，无特殊含义\n */\npackage cn.iocoder.yudao.module.infra.job;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java",
    "content": "/**\n * 消息队列的消费者\n */\npackage cn.iocoder.yudao.module.infra.mq.consumer;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java",
    "content": "/**\n * 消息队列的消息\n */\npackage cn.iocoder.yudao.module.infra.mq.message;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java",
    "content": "/**\n * 消息队列的生产者\n */\npackage cn.iocoder.yudao.module.infra.mq.producer;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/package-info.java",
    "content": "/**\n * infra 模块，主要提供两块能力：\n * 1. 我们放基础设施的运维与管理，支撑上层的通用与核心业务。 例如说：定时任务的管理、服务器的信息等等\n * 2. 研发工具，提升研发效率与质量。 例如说：代码生成器、接口文档等等\n *\n * 1. Controller URL：以 /infra/ 开头，避免和其它 Module 冲突\n * 2. DataObject 表名：以 infra_ 开头，方便在数据库中区分\n */\npackage cn.iocoder.yudao.module.infra;\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.codegen;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 代码生成 Service 接口\n *\n * @author 芋道源码\n */\npublic interface CodegenService {\n\n    /**\n     * 基于数据库的表结构，创建代码生成器的表定义\n     *\n     * @param author 作者\n     * @param reqVO  表信息\n     * @return 创建的表定义的编号数组\n     */\n    List<Long> createCodegenList(String author, CodegenCreateListReqVO reqVO);\n\n    /**\n     * 更新数据库的表和字段定义\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCodegen(CodegenUpdateReqVO updateReqVO);\n\n    /**\n     * 基于数据库的表结构，同步数据库的表和字段定义\n     *\n     * @param tableId 表编号\n     */\n    void syncCodegenFromDB(Long tableId);\n\n    /**\n     * 删除数据库的表和字段定义\n     *\n     * @param tableId 数据编号\n     */\n    void deleteCodegen(Long tableId);\n\n    /**\n     * 批量删除数据库的表和字段定义\n     *\n     * @param tableIds 数据编号列表\n     */\n    void deleteCodegenList(List<Long> tableIds);\n\n    /**\n     * 获得表定义列表\n     *\n     * @param dataSourceConfigId 数据源配置的编号\n     * @return 表定义列表\n     */\n    List<CodegenTableDO> getCodegenTableList(Long dataSourceConfigId);\n\n    /**\n     * 获得表定义分页\n     *\n     * @param pageReqVO 分页条件\n     * @return 表定义分页\n     */\n    PageResult<CodegenTableDO> getCodegenTablePage(CodegenTablePageReqVO pageReqVO);\n\n    /**\n     * 获得表定义\n     *\n     * @param id 表编号\n     * @return 表定义\n     */\n    CodegenTableDO getCodegenTable(Long id);\n\n    /**\n     * 获得指定表的字段定义数组\n     *\n     * @param tableId 表编号\n     * @return 字段定义数组\n     */\n    List<CodegenColumnDO> getCodegenColumnListByTableId(Long tableId);\n\n    /**\n     * 执行指定表的代码生成\n     *\n     * @param tableId 表编号\n     * @return 生成结果。key 为文件路径，value 为对应的代码内容\n     */\n    Map<String, String> generationCodes(Long tableId);\n\n    /**\n     * 获得数据库自带的表定义列表\n     *\n     * @param dataSourceConfigId 数据源的配置编号\n     * @param name               表名称\n     * @param comment            表描述\n     * @return 表定义列表\n     */\n    List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.codegen;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;\nimport cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;\nimport cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;\nimport cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;\nimport cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;\nimport cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.google.common.annotations.VisibleForTesting;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport javax.annotation.Resource;\nimport java.util.*;\nimport java.util.function.BiPredicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 代码生成 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\npublic class CodegenServiceImpl implements CodegenService {\n\n    @Resource\n    private DatabaseTableService databaseTableService;\n    @Resource\n    private DataSourceConfigService dataSourceConfigService;\n\n    @Resource\n    private CodegenTableMapper codegenTableMapper;\n    @Resource\n    private CodegenColumnMapper codegenColumnMapper;\n\n    @Resource\n    private CodegenBuilder codegenBuilder;\n    @Resource\n    private CodegenEngine codegenEngine;\n\n    @Resource\n    private CodegenProperties codegenProperties;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public List<Long> createCodegenList(String author, CodegenCreateListReqVO reqVO) {\n        List<Long> ids = new ArrayList<>(reqVO.getTableNames().size());\n        // 遍历添加。虽然效率会低一点，但是没必要做成完全批量，因为不会这么大量\n        reqVO.getTableNames().forEach(tableName -> ids.add(createCodegen(author, reqVO.getDataSourceConfigId(), tableName)));\n        return ids;\n    }\n\n    private Long createCodegen(String author, Long dataSourceConfigId, String tableName) {\n        // 从数据库中，获得数据库表结构\n        TableInfo tableInfo = databaseTableService.getTable(dataSourceConfigId, tableName);\n        // 导入\n        return createCodegen0(author, dataSourceConfigId, tableInfo);\n    }\n\n    private Long createCodegen0(String author, Long dataSourceConfigId, TableInfo tableInfo) {\n        // 校验导入的表和字段非空\n        validateTableInfo(tableInfo);\n        // 校验是否已经存在\n        if (codegenTableMapper.selectByTableNameAndDataSourceConfigId(tableInfo.getName(),\n                dataSourceConfigId) != null) {\n            throw exception(CODEGEN_TABLE_EXISTS);\n        }\n\n        // 构建 CodegenTableDO 对象，插入到 DB 中\n        CodegenTableDO table = codegenBuilder.buildTable(tableInfo);\n        table.setDataSourceConfigId(dataSourceConfigId);\n        table.setScene(CodegenSceneEnum.ADMIN.getScene()); // 默认配置下，使用管理后台的模板\n        table.setFrontType(codegenProperties.getFrontType());\n        table.setAuthor(author);\n        codegenTableMapper.insert(table);\n\n        // 构建 CodegenColumnDO 数组，插入到 DB 中\n        List<CodegenColumnDO> columns = codegenBuilder.buildColumns(table.getId(), tableInfo.getFields());\n        // 如果没有主键，则使用第一个字段作为主键\n        if (!tableInfo.isHavePrimaryKey()) {\n            columns.get(0).setPrimaryKey(true);\n        }\n        codegenColumnMapper.insertBatch(columns);\n        return table.getId();\n    }\n\n    @VisibleForTesting\n    void validateTableInfo(TableInfo tableInfo) {\n        if (tableInfo == null) {\n            throw exception(CODEGEN_IMPORT_TABLE_NULL);\n        }\n        if (StrUtil.isEmpty(tableInfo.getComment())) {\n            throw exception(CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL);\n        }\n        if (CollUtil.isEmpty(tableInfo.getFields())) {\n            throw exception(CODEGEN_IMPORT_COLUMNS_NULL);\n        }\n        tableInfo.getFields().forEach(field -> {\n            if (StrUtil.isEmpty(field.getComment())) {\n                throw exception(CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL, field.getName());\n            }\n        });\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateCodegen(CodegenUpdateReqVO updateReqVO) {\n        // 校验是否已经存在\n        if (codegenTableMapper.selectById(updateReqVO.getTable().getId()) == null) {\n            throw exception(CODEGEN_TABLE_NOT_EXISTS);\n        }\n        // 校验主表字段存在\n        if (Objects.equals(updateReqVO.getTable().getTemplateType(), CodegenTemplateTypeEnum.SUB.getType())) {\n            if (codegenTableMapper.selectById(updateReqVO.getTable().getMasterTableId()) == null) {\n                throw exception(CODEGEN_MASTER_TABLE_NOT_EXISTS, updateReqVO.getTable().getMasterTableId());\n            }\n            if (CollUtil.findOne(updateReqVO.getColumns(),  // 关联主表的字段不存在\n                    column -> column.getId().equals(updateReqVO.getTable().getSubJoinColumnId())) == null) {\n                throw exception(CODEGEN_SUB_COLUMN_NOT_EXISTS, updateReqVO.getTable().getSubJoinColumnId());\n            }\n        }\n\n        // 更新 table 表定义\n        CodegenTableDO updateTableObj = BeanUtils.toBean(updateReqVO.getTable(), CodegenTableDO.class);\n        codegenTableMapper.updateById(updateTableObj);\n        // 更新 column 字段定义\n        List<CodegenColumnDO> updateColumnObjs = BeanUtils.toBean(updateReqVO.getColumns(), CodegenColumnDO.class);\n        updateColumnObjs.forEach(updateColumnObj -> codegenColumnMapper.updateById(updateColumnObj));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void syncCodegenFromDB(Long tableId) {\n        // 校验是否已经存在\n        CodegenTableDO table = codegenTableMapper.selectById(tableId);\n        if (table == null) {\n            throw exception(CODEGEN_TABLE_NOT_EXISTS);\n        }\n        // 从数据库中，获得数据库表结构\n        TableInfo tableInfo = databaseTableService.getTable(table.getDataSourceConfigId(), table.getTableName());\n        // 执行同步\n        syncCodegen0(tableId, tableInfo);\n    }\n\n    private void syncCodegen0(Long tableId, TableInfo tableInfo) {\n        // 1. 校验导入的表和字段非空\n        validateTableInfo(tableInfo);\n        List<TableField> tableFields = tableInfo.getFields();\n\n        // 2. 构建 CodegenColumnDO 数组，只同步新增的字段\n        List<CodegenColumnDO> codegenColumns = codegenColumnMapper.selectListByTableId(tableId);\n        Set<String> codegenColumnNames = convertSet(codegenColumns, CodegenColumnDO::getColumnName);\n\n        // 3.1 计算需要【修改】的字段，插入时重新插入，删除时将原来的删除\n        Map<String, CodegenColumnDO> codegenColumnDOMap = convertMap(codegenColumns, CodegenColumnDO::getColumnName);\n        BiPredicate<TableField, CodegenColumnDO> primaryKeyPredicate =\n                (tableField, codegenColumn) -> tableField.getMetaInfo().getJdbcType().name().equals(codegenColumn.getDataType())\n                        && tableField.getMetaInfo().isNullable() == codegenColumn.getNullable()\n                        && tableField.isKeyFlag() == codegenColumn.getPrimaryKey()\n                        && tableField.getComment().equals(codegenColumn.getColumnComment());\n        Set<String> modifyFieldNames = IntStream.range(0, tableFields.size()).mapToObj(index -> {\n            TableField tableField = tableFields.get(index);\n            String columnName = tableField.getColumnName();\n            CodegenColumnDO codegenColumn = codegenColumnDOMap.get(columnName);\n            if (codegenColumn == null) {\n                return null;\n            }\n            if (!primaryKeyPredicate.test(tableField, codegenColumn) || codegenColumn.getOrdinalPosition() != index) {\n                return columnName;\n            }\n            return null;\n        }).filter(Objects::nonNull).collect(Collectors.toSet());\n        // 3.2 计算需要【删除】的字段\n        Set<String> tableFieldNames = convertSet(tableFields, TableField::getName);\n        Set<Long> deleteColumnIds = codegenColumns.stream()\n                .filter(column -> (!tableFieldNames.contains(column.getColumnName())) || modifyFieldNames.contains(column.getColumnName()))\n                .map(CodegenColumnDO::getId).collect(Collectors.toSet());\n        // 移除已经存在的字段\n        tableFields.removeIf(column -> codegenColumnNames.contains(column.getColumnName()) && (!modifyFieldNames.contains(column.getColumnName())));\n        if (CollUtil.isEmpty(tableFields) && CollUtil.isEmpty(deleteColumnIds)) {\n            throw exception(CODEGEN_SYNC_NONE_CHANGE);\n        }\n\n        // 4.1 插入新增的字段\n        List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, tableFields);\n        codegenColumnMapper.insertBatch(columns);\n        // 4.2 删除不存在的字段\n        if (CollUtil.isNotEmpty(deleteColumnIds)) {\n            codegenColumnMapper.deleteByIds(deleteColumnIds);\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteCodegen(Long tableId) {\n        // 校验是否已经存在\n        if (codegenTableMapper.selectById(tableId) == null) {\n            throw exception(CODEGEN_TABLE_NOT_EXISTS);\n        }\n\n        // 删除 table 表定义\n        codegenTableMapper.deleteById(tableId);\n        // 删除 column 字段定义\n        codegenColumnMapper.deleteListByTableId(tableId);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteCodegenList(List<Long> tableIds) {\n        // 批量删除 table 表定义\n        codegenTableMapper.deleteByIds(tableIds);\n        // 批量删除 column 字段定义\n        codegenColumnMapper.deleteListByTableId(tableIds);\n    }\n\n    @Override\n    public List<CodegenTableDO> getCodegenTableList(Long dataSourceConfigId) {\n        return codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId);\n    }\n\n    @Override\n    public PageResult<CodegenTableDO> getCodegenTablePage(CodegenTablePageReqVO pageReqVO) {\n        return codegenTableMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public CodegenTableDO getCodegenTable(Long id) {\n        return codegenTableMapper.selectById(id);\n    }\n\n    @Override\n    public List<CodegenColumnDO> getCodegenColumnListByTableId(Long tableId) {\n        return codegenColumnMapper.selectListByTableId(tableId);\n    }\n\n    @Override\n    public Map<String, String> generationCodes(Long tableId) {\n        // 校验是否已经存在\n        CodegenTableDO table = codegenTableMapper.selectById(tableId);\n        if (table == null) {\n            throw exception(CODEGEN_TABLE_NOT_EXISTS);\n        }\n        List<CodegenColumnDO> columns = codegenColumnMapper.selectListByTableId(tableId);\n        if (CollUtil.isEmpty(columns)) {\n            throw exception(CODEGEN_COLUMN_NOT_EXISTS);\n        }\n\n        // 如果是主子表，则加载对应的子表信息\n        List<CodegenTableDO> subTables = null;\n        List<List<CodegenColumnDO>> subColumnsList = null;\n        if (CodegenTemplateTypeEnum.isMaster(table.getTemplateType())) {\n            // 校验子表存在\n            subTables = codegenTableMapper.selectListByTemplateTypeAndMasterTableId(\n                    CodegenTemplateTypeEnum.SUB.getType(), tableId);\n            if (CollUtil.isEmpty(subTables)) {\n                throw exception(CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE);\n            }\n            // 校验子表的关联字段存在\n            subColumnsList = new ArrayList<>();\n            for (CodegenTableDO subTable : subTables) {\n                List<CodegenColumnDO> subColumns = codegenColumnMapper.selectListByTableId(subTable.getId());\n                if (CollUtil.findOne(subColumns, column -> column.getId().equals(subTable.getSubJoinColumnId())) == null) {\n                    throw exception(CODEGEN_SUB_COLUMN_NOT_EXISTS, subTable.getId());\n                }\n                subColumnsList.add(subColumns);\n            }\n        }\n\n        // 获取数据源对应的数据库类型\n        DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(table.getDataSourceConfigId());\n        DbType dbType = JdbcUtils.getDbType(dataSourceConfig.getUrl());\n        // 执行生成\n        return codegenEngine.execute(dbType, table, columns, subTables, subColumnsList);\n    }\n\n    @Override\n    public List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {\n        List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId, name, comment);\n        // 移除在 Codegen 中，已经存在的\n        Set<String> existsTables = convertSet(\n                codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId), CodegenTableDO::getTableName);\n        tables.removeIf(table -> existsTables.contains(table.getName()));\n        return BeanUtils.toBean(tables, DatabaseTableRespVO.class);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java",
    "content": "package cn.iocoder.yudao.module.infra.service.codegen.inner;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.google.common.collect.Sets;\nimport org.springframework.stereotype.Component;\n\nimport java.time.LocalDateTime;\nimport java.util.*;\n\nimport static cn.hutool.core.text.CharSequenceUtil.*;\nimport static cn.hutool.core.util.RandomUtil.randomEle;\nimport static cn.hutool.core.util.RandomUtil.randomInt;\n\n/**\n * 代码生成器的 Builder，负责：\n * 1. 将数据库的表 {@link TableInfo} 定义，构建成 {@link CodegenTableDO}\n * 2. 将数据库的列 {@link TableField} 构定义，建成 {@link CodegenColumnDO}\n */\n@Component\npublic class CodegenBuilder {\n\n    /**\n     * 字段名与 {@link CodegenColumnListConditionEnum} 的默认映射\n     * 注意，字段的匹配以后缀的方式\n     */\n    private static final Map<String, CodegenColumnListConditionEnum> COLUMN_LIST_OPERATION_CONDITION_MAPPINGS =\n            MapUtil.<String, CodegenColumnListConditionEnum>builder()\n                    .put(\"name\", CodegenColumnListConditionEnum.LIKE)\n                    .put(\"time\", CodegenColumnListConditionEnum.BETWEEN)\n                    .put(\"date\", CodegenColumnListConditionEnum.BETWEEN)\n                    .build();\n\n    /**\n     * 字段名与 {@link CodegenColumnHtmlTypeEnum} 的默认映射\n     * 注意，字段的匹配以后缀的方式\n     */\n    private static final Map<String, CodegenColumnHtmlTypeEnum> COLUMN_HTML_TYPE_MAPPINGS =\n            MapUtil.<String, CodegenColumnHtmlTypeEnum>builder()\n                    .put(\"status\", CodegenColumnHtmlTypeEnum.RADIO)\n                    .put(\"sex\", CodegenColumnHtmlTypeEnum.RADIO)\n                    .put(\"type\", CodegenColumnHtmlTypeEnum.SELECT)\n                    .put(\"image\", CodegenColumnHtmlTypeEnum.IMAGE_UPLOAD)\n                    .put(\"file\", CodegenColumnHtmlTypeEnum.FILE_UPLOAD)\n                    .put(\"content\", CodegenColumnHtmlTypeEnum.EDITOR)\n                    .put(\"description\", CodegenColumnHtmlTypeEnum.EDITOR)\n                    .put(\"demo\", CodegenColumnHtmlTypeEnum.EDITOR)\n                    .put(\"time\", CodegenColumnHtmlTypeEnum.DATETIME)\n                    .put(\"date\", CodegenColumnHtmlTypeEnum.DATETIME)\n                    .build();\n\n    /**\n     * 多租户编号的字段名\n     */\n    public static final String TENANT_ID_FIELD = \"tenantId\";\n    /**\n     * {@link cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO} 的字段\n     */\n    public static final Set<String> BASE_DO_FIELDS = new HashSet<>();\n    /**\n     * 新增操作，不需要传递的字段\n     */\n    private static final Set<String> CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet(\"id\");\n    /**\n     * 修改操作，不需要传递的字段\n     */\n    private static final Set<String> UPDATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet();\n    /**\n     * 列表操作的条件，不需要传递的字段\n     */\n    private static final Set<String> LIST_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet(\"id\");\n    /**\n     * 列表操作的结果，不需要返回的字段\n     */\n    private static final Set<String> LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet();\n\n    static {\n        Arrays.stream(ReflectUtil.getFields(BaseDO.class)).forEach(field -> BASE_DO_FIELDS.add(field.getName()));\n        BASE_DO_FIELDS.add(TENANT_ID_FIELD);\n        // 处理 OPERATION 相关的字段\n        CREATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);\n        UPDATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);\n        LIST_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);\n        LIST_OPERATION_EXCLUDE_COLUMN.remove(\"createTime\"); // 创建时间，还是可能需要传递的\n        LIST_OPERATION_RESULT_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);\n        LIST_OPERATION_RESULT_EXCLUDE_COLUMN.remove(\"createTime\"); // 创建时间，还是需要返回的\n    }\n\n    public CodegenTableDO buildTable(TableInfo tableInfo) {\n        CodegenTableDO table = CodegenConvert.INSTANCE.convert(tableInfo);\n        initTableDefault(table);\n        return table;\n    }\n\n    /**\n     * 初始化 Table 表的默认字段\n     *\n     * @param table 表定义\n     */\n    private void initTableDefault(CodegenTableDO table) {\n        // 以 system_dept 举例子。moduleName 为 system、businessName 为 dept、className 为 Dept\n        // 如果希望以 System 前缀，则可以手动在【代码生成 - 修改生成配置 - 基本信息】，将实体类名称改为 SystemDept 即可\n        String tableName = table.getTableName().toLowerCase();\n        // 第一步，_ 前缀的前面，作为 module 名字；第二步，moduleName 必须小写；\n        table.setModuleName(subBefore(tableName, '_', false).toLowerCase());\n        // 第一步，第一个 _ 前缀的后面，作为 module 名字; 第二步，可能存在多个 _ 的情况，转换成驼峰; 第三步，businessName 必须小写；\n        table.setBusinessName(toCamelCase(subAfter(tableName, '_', false)).toLowerCase());\n        // 驼峰 + 首字母大写；第一步，第一个 _ 前缀的后面，作为 class 名字；第二步，驼峰命名\n        table.setClassName(upperFirst(toCamelCase(subAfter(tableName, '_', false))));\n        // 去除结尾的表，作为类描述\n        table.setClassComment(StrUtil.removeSuffixIgnoreCase(table.getTableComment(), \"表\"));\n        table.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());\n    }\n\n    public List<CodegenColumnDO> buildColumns(Long tableId, List<TableField> tableFields) {\n        List<CodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(tableFields);\n        int index = 1;\n        for (CodegenColumnDO column : columns) {\n            column.setTableId(tableId);\n            column.setOrdinalPosition(index++);\n            // 特殊处理：Byte => Integer\n            if (Byte.class.getSimpleName().equals(column.getJavaType())) {\n                column.setJavaType(Integer.class.getSimpleName());\n            }\n            // 初始化 Column 列的默认字段\n            processColumnOperation(column); // 处理 CRUD 相关的字段的默认值\n            processColumnUI(column); // 处理 UI 相关的字段的默认值\n            processColumnExample(column); // 处理字段的 swagger example 示例\n        }\n        return columns;\n    }\n\n    private void processColumnOperation(CodegenColumnDO column) {\n        // 处理 createOperation 字段\n        column.setCreateOperation(!CREATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())\n                && !column.getPrimaryKey()); // 对于主键，创建时无需传递\n        // 处理 updateOperation 字段\n        column.setUpdateOperation(!UPDATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())\n                || column.getPrimaryKey()); // 对于主键，更新时需要传递\n        // 处理 listOperation 字段\n        column.setListOperation(!LIST_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())\n                && !column.getPrimaryKey()); // 对于主键，列表过滤不需要传递\n        // 处理 listOperationCondition 字段\n        COLUMN_LIST_OPERATION_CONDITION_MAPPINGS.entrySet().stream()\n                .filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))\n                .findFirst().ifPresent(entry -> column.setListOperationCondition(entry.getValue().getCondition()));\n        if (column.getListOperationCondition() == null) {\n            column.setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition());\n        }\n        // 处理 listOperationResult 字段\n        column.setListOperationResult(!LIST_OPERATION_RESULT_EXCLUDE_COLUMN.contains(column.getJavaField()));\n    }\n\n    private void processColumnUI(CodegenColumnDO column) {\n        // 基于后缀进行匹配\n        COLUMN_HTML_TYPE_MAPPINGS.entrySet().stream()\n                .filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))\n                .findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType()));\n        // 如果是 Boolean 类型时，设置为 radio 类型.\n        if (Boolean.class.getSimpleName().equals(column.getJavaType())) {\n            column.setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType());\n        }\n        // 如果是 LocalDateTime 类型，则设置为 datetime 类型\n        if (LocalDateTime.class.getSimpleName().equals(column.getJavaType())) {\n            column.setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());\n        }\n        // 兜底，设置默认为 input 类型\n        if (column.getHtmlType() == null) {\n            column.setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType());\n        }\n    }\n\n    /**\n     * 处理字段的 swagger example 示例\n     *\n     * @param column 字段\n     */\n    private void processColumnExample(CodegenColumnDO column) {\n        // id、price、count 等可能是整数的后缀\n        if (StrUtil.endWithAnyIgnoreCase(column.getJavaField(), \"id\", \"price\", \"count\")) {\n            column.setExample(String.valueOf(randomInt(1, Short.MAX_VALUE)));\n            return;\n        }\n        // name\n        if (StrUtil.endWithIgnoreCase(column.getJavaField(), \"name\")) {\n            column.setExample(randomEle(new String[]{\"张三\", \"李四\", \"王五\", \"赵六\", \"芋艿\"}));\n            return;\n        }\n        // status\n        if (StrUtil.endWithAnyIgnoreCase(column.getJavaField(), \"status\", \"type\")) {\n            column.setExample(randomEle(new String[]{\"1\", \"2\"}));\n            return;\n        }\n        // url\n        if (StrUtil.endWithIgnoreCase(column.getColumnName(), \"url\")) {\n            column.setExample(\"https://www.iocoder.cn\");\n            return;\n        }\n        // reason\n        if (StrUtil.endWithIgnoreCase(column.getColumnName(), \"reason\")) {\n            column.setExample(randomEle(new String[]{\"不喜欢\", \"不对\", \"不好\", \"不香\"}));\n            return;\n        }\n        // description、memo、remark\n        if (StrUtil.endWithAnyIgnoreCase(column.getColumnName(), \"description\", \"memo\", \"remark\")) {\n            column.setExample(randomEle(new String[]{\"你猜\", \"随便\", \"你说的对\"}));\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java",
    "content": "package cn.iocoder.yudao.module.infra.service.codegen.inner;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.template.TemplateConfig;\nimport cn.hutool.extra.template.TemplateEngine;\nimport cn.hutool.extra.template.engine.velocity.VelocityEngine;\nimport cn.hutool.system.SystemUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;\nimport cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.date.DateUtils;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenVOTypeEnum;\nimport cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.google.common.annotations.VisibleForTesting;\nimport com.google.common.collect.ImmutableTable;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Table;\nimport lombok.Setter;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.ClassUtils;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.Resource;\nimport java.util.*;\n\nimport static cn.hutool.core.map.MapUtil.getStr;\nimport static cn.hutool.core.text.CharSequenceUtil.*;\n\n/**\n * 代码生成的引擎，用于具体生成代码\n * 目前基于 {@link org.apache.velocity.app.Velocity} 模板引擎实现\n *\n * 考虑到 Java 模板引擎的框架非常多，Freemarker、Velocity、Thymeleaf 等等，所以我们采用 hutool 封装的 {@link cn.hutool.extra.template.Template} 抽象\n *\n * @author 芋道源码\n */\n@Component\npublic class CodegenEngine {\n\n    /**\n     * 后端的模板配置\n     *\n     * key：模板在 resources 的地址\n     * value：生成的路径\n     */\n    private static final Map<String, String> SERVER_TEMPLATES = MapUtil.<String, String>builder(new LinkedHashMap<>()) // 有序\n            // Java module-biz(server) Main\n            .put(javaTemplatePath(\"controller/vo/pageReqVO\"), javaModuleImplVOFilePath(\"PageReqVO\"))\n            .put(javaTemplatePath(\"controller/vo/listReqVO\"), javaModuleImplVOFilePath(\"ListReqVO\"))\n            .put(javaTemplatePath(\"controller/vo/respVO\"), javaModuleImplVOFilePath(\"RespVO\"))\n            .put(javaTemplatePath(\"controller/vo/saveReqVO\"), javaModuleImplVOFilePath(\"SaveReqVO\"))\n            .put(javaTemplatePath(\"controller/controller\"), javaModuleImplControllerFilePath())\n            .put(javaTemplatePath(\"dal/do\"),\n                    javaModuleImplMainFilePath(\"dal/dataobject/${table.businessName}/${table.className}DO\"))\n            .put(javaTemplatePath(\"dal/do_sub\"), // 特殊：主子表专属逻辑\n                    javaModuleImplMainFilePath(\"dal/dataobject/${table.businessName}/${subTable.className}DO\"))\n            .put(javaTemplatePath(\"dal/mapper\"),\n                    javaModuleImplMainFilePath(\"dal/mysql/${table.businessName}/${table.className}Mapper\"))\n            .put(javaTemplatePath(\"dal/mapper_sub\"), // 特殊：主子表专属逻辑\n                    javaModuleImplMainFilePath(\"dal/mysql/${table.businessName}/${subTable.className}Mapper\"))\n            .put(javaTemplatePath(\"dal/mapper.xml\"), mapperXmlFilePath())\n            .put(javaTemplatePath(\"service/serviceImpl\"),\n                    javaModuleImplMainFilePath(\"service/${table.businessName}/${table.className}ServiceImpl\"))\n            .put(javaTemplatePath(\"service/service\"),\n                    javaModuleImplMainFilePath(\"service/${table.businessName}/${table.className}Service\"))\n            // Java module-biz(server) Test\n            .put(javaTemplatePath(\"test/serviceTest\"),\n                    javaModuleImplTestFilePath(\"service/${table.businessName}/${table.className}ServiceImplTest\"))\n            // Java module-api Main\n            .put(javaTemplatePath(\"enums/errorcode\"), javaModuleApiMainFilePath(\"enums/ErrorCodeConstants_手动操作\"))\n            // SQL\n            .put(\"codegen/sql/sql.vm\", \"sql/sql.sql\")\n            .put(\"codegen/sql/h2.vm\", \"sql/h2.sql\")\n            .build();\n\n    /**\n     * 前端的配置模版\n     *\n     * key1：UI 模版的类型 {@link CodegenFrontTypeEnum#getType()}\n     * key2：模板在 resources 的地址\n     * value：生成的路径\n     */\n    private static final Table<Integer, String, String> FRONT_TEMPLATES = ImmutableTable.<Integer, String, String>builder()\n            // VUE2_ELEMENT_UI\n            .put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath(\"views/index.vue\"),\n                    vueFilePath(\"views/${table.moduleName}/${table.businessName}/index.vue\"))\n            .put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath(\"api/api.js\"),\n                    vueFilePath(\"api/${table.moduleName}/${table.businessName}/index.js\"))\n            .put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath(\"views/form.vue\"),\n                    vueFilePath(\"views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath(\"views/components/form_sub_normal.vue\"),  // 特殊：主子表专属逻辑\n                    vueFilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath(\"views/components/form_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vueFilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath(\"views/components/form_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vueFilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath(\"views/components/list_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vueFilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue\"))\n            .put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath(\"views/components/list_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vueFilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue\"))\n            // VUE3_ELEMENT_PLUS\n            .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath(\"views/index.vue\"),\n                    vue3FilePath(\"views/${table.moduleName}/${table.businessName}/index.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath(\"views/form.vue\"),\n                    vue3FilePath(\"views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath(\"views/components/form_sub_normal.vue\"),  // 特殊：主子表专属逻辑\n                    vue3FilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath(\"views/components/form_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3FilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath(\"views/components/form_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3FilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath(\"views/components/list_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3FilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath(\"views/components/list_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3FilePath(\"views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath(\"api/api.ts\"),\n                    vue3FilePath(\"api/${table.moduleName}/${table.businessName}/index.ts\"))\n            .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath(\"api/api.ts\"),\n                    vue3UniappFilePath(\"api/${table.moduleName}/${table.businessName}/index.ts\"))\n            .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath(\"views/index.vue\"),\n                    vue3UniappFilePath(\"pages-${table.moduleName}/${table.businessName}/index.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath(\"components/search-form.vue\"),\n                    vue3UniappFilePath(\"pages-${table.moduleName}/${table.businessName}/components/search-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath(\"views/form/index.vue\"),\n                    vue3UniappFilePath(\"pages-${table.moduleName}/${table.businessName}/form/index.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath(\"views/detail/index.vue\"),\n                    vue3UniappFilePath(\"pages-${table.moduleName}/${table.businessName}/detail/index.vue\"))\n            // VUE3_VBEN2_ANTD_SCHEMA\n            .put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath(\"views/data.ts\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath(\"views/index.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/index.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath(\"views/form.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/${simpleClassName}Modal.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath(\"api/api.ts\"),\n                    vue3VbenFilePath(\"api/${table.moduleName}/${table.businessName}/index.ts\"))\n            // VUE3_VBEN5_ANTD_SCHEMA\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath(\"views/data.ts\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/data.ts\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath(\"views/index.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/index.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath(\"views/form.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath(\"api/api.ts\"),\n                    vue3VbenFilePath(\"api/${table.moduleName}/${table.businessName}/index.ts\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath(\"views/modules/form_sub_normal.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath(\"views/modules/form_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath(\"views/modules/form_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath(\"views/modules/list_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath(\"views/modules/list_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue\"))\n            // VUE3_VBEN5_ANTD\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath(\"views/index.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/index.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath(\"views/form.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath(\"api/api.ts\"),\n                    vue3VbenFilePath(\"api/${table.moduleName}/${table.businessName}/index.ts\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath(\"views/modules/form_sub_normal.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath(\"views/modules/form_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath(\"views/modules/form_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath(\"views/modules/list_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath(\"views/modules/list_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue\"))\n            // VUE3_VBEN5_EP_SCHEMA\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath(\"views/data.ts\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/data.ts\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath(\"views/index.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/index.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath(\"views/form.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath(\"api/api.ts\"),\n                    vue3VbenFilePath(\"api/${table.moduleName}/${table.businessName}/index.ts\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath(\"views/modules/form_sub_normal.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath(\"views/modules/form_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath(\"views/modules/form_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath(\"views/modules/list_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath(\"views/modules/list_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue\"))\n            // VUE3_VBEN5_EP\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath(\"views/index.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/index.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath(\"views/form.vue\"),\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath(\"api/api.ts\"),\n                    vue3VbenFilePath(\"api/${table.moduleName}/${table.businessName}/index.ts\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath(\"views/modules/form_sub_normal.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath(\"views/modules/form_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath(\"views/modules/form_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath(\"views/modules/list_sub_inner.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue\"))\n            .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath(\"views/modules/list_sub_erp.vue\"),  // 特殊：主子表专属逻辑\n                    vue3VbenFilePath(\"views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue\"))\n            .build();\n\n    @Resource\n    private CodegenProperties codegenProperties;\n\n    /**\n     * 是否使用 jakarta 包，用于解决 Spring Boot 2.X 和 3.X 的兼容性问题\n     *\n     * true  - 使用 jakarta.validation.constraints.*\n     * false - 使用 javax.validation.constraints.*\n     */\n    @Setter // 允许设置的原因，是因为单测需要手动改变\n    private Boolean jakartaEnable;\n\n    /**\n     * 是否为 yudao-cloud 项目，用于解决 Boot 和 Cloud 的 api 模块兼容性问题\n     *\n     * true  - 需要有 yudao-module-xxx-api 模块\n     * false - 不需要有，使用 api、enum 包即可\n     */\n    @Setter\n    private Boolean cloudEnable;\n\n    /**\n     * 模板引擎，由 hutool 实现\n     */\n    private final TemplateEngine templateEngine;\n    /**\n     * 全局通用变量映射\n     */\n    private final Map<String, Object> globalBindingMap = new HashMap<>();\n\n    public CodegenEngine() {\n        // 初始化 TemplateEngine 属性\n        TemplateConfig config = new TemplateConfig();\n        config.setResourceMode(TemplateConfig.ResourceMode.CLASSPATH);\n        this.templateEngine = new VelocityEngine(config);\n        // 设置 javaxEnable，按照是否使用 JDK17 来判断\n        this.jakartaEnable = SystemUtil.getJavaInfo().isJavaVersionAtLeast(1700) // 17.00 * 100\n                && ClassUtils.isPresent(\"jakarta.annotation.Resource\", ClassUtils.getDefaultClassLoader());\n        // 设置 cloudEnable，按照是否使用 Spring Cloud 来判断\n        this.cloudEnable = ClassUtils.isPresent(\"cn.iocoder.yudao.module.infra.framework.rpc.config.RpcConfiguration\",\n                ClassUtils.getDefaultClassLoader());\n    }\n\n    @PostConstruct\n    @VisibleForTesting\n    void initGlobalBindingMap() {\n        // 全局配置\n        globalBindingMap.put(\"basePackage\", codegenProperties.getBasePackage());\n        globalBindingMap.put(\"baseFrameworkPackage\", codegenProperties.getBasePackage()\n                + '.' + \"framework\"); // 用于后续获取测试类的 package 地址\n        globalBindingMap.put(\"jakartaPackage\", jakartaEnable ? \"jakarta\" : \"javax\");\n        globalBindingMap.put(\"voType\", codegenProperties.getVoType());\n        globalBindingMap.put(\"deleteBatchEnable\", codegenProperties.getDeleteBatchEnable());\n        // 全局 Java Bean\n        globalBindingMap.put(\"CommonResultClassName\", CommonResult.class.getName());\n        globalBindingMap.put(\"PageResultClassName\", PageResult.class.getName());\n        // VO 类，独有字段\n        globalBindingMap.put(\"PageParamClassName\", PageParam.class.getName());\n        globalBindingMap.put(\"DictFormatClassName\", DictFormat.class.getName());\n        // DO 类，独有字段\n        globalBindingMap.put(\"BaseDOClassName\", BaseDO.class.getName());\n        globalBindingMap.put(\"baseDOFields\", CodegenBuilder.BASE_DO_FIELDS);\n        globalBindingMap.put(\"QueryWrapperClassName\", LambdaQueryWrapperX.class.getName());\n        globalBindingMap.put(\"BaseMapperClassName\", BaseMapperX.class.getName());\n        // Util 工具类\n        globalBindingMap.put(\"ServiceExceptionUtilClassName\", ServiceExceptionUtil.class.getName());\n        globalBindingMap.put(\"DateUtilsClassName\", DateUtils.class.getName());\n        globalBindingMap.put(\"ExcelUtilsClassName\", ExcelUtils.class.getName());\n        globalBindingMap.put(\"LocalDateTimeUtilsClassName\", LocalDateTimeUtils.class.getName());\n        globalBindingMap.put(\"ObjectUtilsClassName\", ObjectUtils.class.getName());\n        globalBindingMap.put(\"DictConvertClassName\", DictConvert.class.getName());\n        globalBindingMap.put(\"ApiAccessLogClassName\", ApiAccessLog.class.getName());\n        globalBindingMap.put(\"OperateTypeEnumClassName\", OperateTypeEnum.class.getName());\n        globalBindingMap.put(\"BeanUtils\", BeanUtils.class.getName());\n        globalBindingMap.put(\"CollectionUtilsClassName\", CollectionUtils.class.getName());\n    }\n\n    /**\n     * 生成代码\n     *\n     * @param dbType         数据库类型\n     * @param table          表定义\n     * @param columns        table 的字段定义数组\n     * @param subTables      子表数组，当且仅当主子表时使用\n     * @param subColumnsList subTables 的字段定义数组\n     * @return 生成的代码，key 是路径，value 是对应代码\n     */\n    public Map<String, String> execute(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns,\n                                       List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) {\n        // 1.1 初始化 bindMap 上下文\n        Map<String, Object> bindingMap = initBindingMap(dbType, table, columns, subTables, subColumnsList);\n        // 1.2 获得模版\n        Map<String, String> templates = getTemplates(table.getFrontType());\n\n        // 2. 执行生成\n        Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(templates.size()); // 有序\n        templates.forEach((vmPath, filePath) -> {\n            // 2.1 特殊：主子表专属逻辑\n            if (isSubTemplate(vmPath)) {\n                generateSubCode(table, subTables, result, vmPath, filePath, bindingMap);\n                return;\n                // 2.2 特殊：树表专属逻辑\n            } else if (isPageReqVOTemplate(vmPath)) {\n                // 减少多余的类生成，例如说 PageVO.java 类\n                if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {\n                    return;\n                }\n            } else if (isListReqVOTemplate(vmPath)) {\n                // 减少多余的类生成，例如说 ListVO.java 类\n                if (!CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {\n                    return;\n                }\n            }\n            // 2.3 默认生成\n            generateCode(result, vmPath, filePath, bindingMap);\n        });\n        return result;\n    }\n\n    private void generateCode(Map<String, String> result, String vmPath,\n                              String filePath, Map<String, Object> bindingMap) {\n        filePath = formatFilePath(filePath, bindingMap);\n        String content = templateEngine.getTemplate(vmPath).render(bindingMap);\n        // 格式化代码\n        content = prettyCode(content, vmPath);\n        result.put(filePath, content);\n    }\n\n    private void generateSubCode(CodegenTableDO table, List<CodegenTableDO> subTables,\n                                 Map<String, String> result, String vmPath,\n                                 String filePath, Map<String, Object> bindingMap) {\n        // 没有子表，所以不生成\n        if (CollUtil.isEmpty(subTables)) {\n            return;\n        }\n        // 主子表的模式匹配。目的：过滤掉个性化的模版\n        if (vmPath.contains(\"_normal\")\n                && ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_NORMAL.getType())) {\n            return;\n        }\n        if (vmPath.contains(\"_erp\")\n                && ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_ERP.getType())) {\n            return;\n        }\n        if (vmPath.contains(\"_inner\")\n                && ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_INNER.getType())) {\n            return;\n        }\n\n        // 逐个生成\n        for (int i = 0; i < subTables.size(); i++) {\n            bindingMap.put(\"subIndex\", i);\n            generateCode(result, vmPath, filePath, bindingMap);\n        }\n        bindingMap.remove(\"subIndex\");\n    }\n\n    /**\n     * 格式化生成后的代码\n     *\n     * 因为尽量让 vm 模版简单，所以统一的处理都在这个方法。\n     * 如果不处理，Vue 的 Pretty 格式校验可能会报错\n     *\n     * @param content 格式化前的代码\n     * @param vmPath 模板路径\n     * @return 格式化后的代码\n     */\n    private String prettyCode(String content, String vmPath) {\n        // Vue 界面：去除字段后面多余的 , 逗号，解决前端的 Pretty 代码格式检查的报错（需要排除 vben5、vue3_admin_uniapp）\n        if (!StrUtil.containsAny(vmPath, \"vben5\", \"vue3_admin_uniapp\")) {\n            content = content.replaceAll(\",\\n}\", \"\\n}\").replaceAll(\",\\n  }\", \"\\n  }\");\n        }\n        // Vue 界面：去除多的 dateFormatter，只有一个的情况下，说明没使用到\n        if (StrUtil.count(content, \"dateFormatter\") == 1) {\n            content = StrUtils.removeLineContains(content, \"dateFormatter\");\n        }\n        // Vue2 界面：修正 $refs\n        if (StrUtil.count(content, \"this.refs\") >= 1) {\n            content = content.replace(\"this.refs\", \"this.$refs\");\n        }\n        // Vue 界面：去除多的 dict 相关，只有一个的情况下，说明没使用到\n        if (StrUtil.count(content, \"getIntDictOptions\") == 1) {\n            content = content.replace(\"getIntDictOptions, \", \"\");\n        }\n        if (StrUtil.count(content, \"getStrDictOptions\") == 1) {\n            content = content.replace(\"getStrDictOptions, \", \"\");\n        }\n        if (StrUtil.count(content, \"getBoolDictOptions\") == 1) {\n            content = content.replace(\"getBoolDictOptions, \", \"\");\n        }\n        if (StrUtil.count(content, \"DICT_TYPE.\") == 0) {\n            content = StrUtils.removeLineContains(content, \"DICT_TYPE\");\n        }\n        return content;\n    }\n\n    private Map<String, Object> initBindingMap(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns,\n                                               List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) {\n        // 创建 bindingMap\n        Map<String, Object> bindingMap = new HashMap<>(globalBindingMap);\n        bindingMap.put(\"dbType\", dbType);\n        bindingMap.put(\"table\", table);\n        bindingMap.put(\"columns\", columns);\n        bindingMap.put(\"primaryColumn\", CollectionUtils.findFirst(columns, CodegenColumnDO::getPrimaryKey)); // 主键字段\n        bindingMap.put(\"sceneEnum\", CodegenSceneEnum.valueOf(table.getScene()));\n        // className 相关\n        // 去掉指定前缀，将 TestDictType 转换成 DictType. 因为在 create 等方法后，不需要带上 Test 前缀\n        String className = table.getClassName();\n        String simpleClassName = equalsAnyIgnoreCase(table.getClassName(), table.getModuleName()) ? table.getClassName()\n                : removePrefix(table.getClassName(), upperFirst(table.getModuleName()));\n        String classNameVar = lowerFirst(simpleClassName);\n        bindingMap.put(\"simpleClassName\", simpleClassName);\n        bindingMap.put(\"simpleClassName_underlineCase\", toUnderlineCase(simpleClassName)); // 将 DictType 转换成 dict_type\n        bindingMap.put(\"classNameVar\", classNameVar); // 将 DictType 转换成 dictType，用于变量\n        // 将 DictType 转换成 dict-type\n        String simpleClassNameStrikeCase = toSymbolCase(simpleClassName, '-');\n        bindingMap.put(\"simpleClassName_strikeCase\", simpleClassNameStrikeCase);\n        // permission 前缀\n        bindingMap.put(\"permissionPrefix\", table.getModuleName() + \":\" + simpleClassNameStrikeCase);\n\n        // 特殊：树表专属逻辑\n        if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {\n            CodegenColumnDO treeParentColumn = CollUtil.findOne(columns,\n                    column -> Objects.equals(column.getId(), table.getTreeParentColumnId()));\n            bindingMap.put(\"treeParentColumn\", treeParentColumn);\n            bindingMap.put(\"treeParentColumn_javaField_underlineCase\", toUnderlineCase(treeParentColumn.getJavaField()));\n            CodegenColumnDO treeNameColumn = CollUtil.findOne(columns,\n                    column -> Objects.equals(column.getId(), table.getTreeNameColumnId()));\n            bindingMap.put(\"treeNameColumn\", treeNameColumn);\n            bindingMap.put(\"treeNameColumn_javaField_underlineCase\", toUnderlineCase(treeNameColumn.getJavaField()));\n        }\n\n        // 特殊：主子表专属逻辑\n        if (CollUtil.isNotEmpty(subTables)) {\n            // 创建 bindingMap\n            bindingMap.put(\"subTables\", subTables);\n            bindingMap.put(\"subColumnsList\", subColumnsList);\n            List<CodegenColumnDO> subPrimaryColumns = new ArrayList<>();\n            List<CodegenColumnDO> subJoinColumns = new ArrayList<>();\n            List<String> subJoinColumnStrikeCases = new ArrayList<>();\n            List<String> subSimpleClassNames = new ArrayList<>();\n            List<String> subClassNameVars = new ArrayList<>();\n            List<String> simpleClassNameUnderlineCases = new ArrayList<>();\n            List<String> subSimpleClassNameStrikeCases = new ArrayList<>();\n            for (int i = 0; i < subTables.size(); i++) {\n                CodegenTableDO subTable = subTables.get(i);\n                List<CodegenColumnDO> subColumns = subColumnsList.get(i);\n                subPrimaryColumns.add(CollectionUtils.findFirst(subColumns, CodegenColumnDO::getPrimaryKey)); //\n                CodegenColumnDO subColumn = CollectionUtils.findFirst(subColumns, // 关联的字段\n                        column -> Objects.equals(column.getId(), subTable.getSubJoinColumnId()));\n                subJoinColumns.add(subColumn);\n                subJoinColumnStrikeCases.add(toSymbolCase(subColumn.getJavaField(), '-')); // 将 DictType 转换成 dict-type\n                // className 相关\n                String subSimpleClassName = removePrefix(subTable.getClassName(), upperFirst(subTable.getModuleName()));\n                subSimpleClassNames.add(subSimpleClassName);\n                simpleClassNameUnderlineCases.add(toUnderlineCase(subSimpleClassName)); // 将 DictType 转换成 dict_type\n                subClassNameVars.add(lowerFirst(subSimpleClassName)); // 将 DictType 转换成 dictType，用于变量\n                subSimpleClassNameStrikeCases.add(toSymbolCase(subSimpleClassName, '-')); // 将 DictType 转换成 dict-type\n            }\n            bindingMap.put(\"subPrimaryColumns\", subPrimaryColumns);\n            bindingMap.put(\"subJoinColumns\", subJoinColumns);\n            bindingMap.put(\"subJoinColumn_strikeCases\", subJoinColumnStrikeCases);\n            bindingMap.put(\"subSimpleClassNames\", subSimpleClassNames);\n            bindingMap.put(\"simpleClassNameUnderlineCases\", simpleClassNameUnderlineCases);\n            bindingMap.put(\"subClassNameVars\", subClassNameVars);\n            bindingMap.put(\"subSimpleClassName_strikeCases\", subSimpleClassNameStrikeCases);\n        }\n\n        // 多个 vm 公用的 VO 变量\n        if (ObjectUtil.equal(codegenProperties.getVoType(), CodegenVOTypeEnum.VO.getType())) {\n            String prefixClass = CodegenSceneEnum.valueOf(table.getScene()).getPrefixClass();\n            bindingMap.put(\"saveReqVOClass\", prefixClass + className + \"SaveReqVO\");\n            bindingMap.put(\"updateReqVOClass\", prefixClass + className + \"SaveReqVO\");\n            bindingMap.put(\"respVOClass\", prefixClass + className + \"RespVO\");\n            bindingMap.put(\"saveReqVOVar\", \"createReqVO\");\n            bindingMap.put(\"updateReqVOVar\", \"updateReqVO\");\n        } else if (ObjectUtil.equal(codegenProperties.getVoType(), CodegenVOTypeEnum.DO.getType())) {\n            bindingMap.put(\"saveReqVOClass\", className + \"DO\");\n            bindingMap.put(\"updateReqVOClass\", className + \"DO\");\n            bindingMap.put(\"respVOClass\", className + \"DO\");\n            bindingMap.put(\"saveReqVOVar\", classNameVar);\n            bindingMap.put(\"updateReqVOVar\", classNameVar);\n        }\n        return bindingMap;\n    }\n\n    private Map<String, String> getTemplates(Integer frontType) {\n        Map<String, String> templates = new LinkedHashMap<>();\n        templates.putAll(SERVER_TEMPLATES);\n        templates.putAll(FRONT_TEMPLATES.row(frontType));\n        // 如果是 Boot 项目，则不使用 api/server 模块\n        if (Boolean.FALSE.equals(cloudEnable)) {\n            SERVER_TEMPLATES.forEach((templatePath, filePath) -> {\n                filePath = StrUtil.replace(filePath, \"/yudao-module-${table.moduleName}-api\", \"\");\n                filePath = StrUtil.replace(filePath, \"/yudao-module-${table.moduleName}-server\", \"\");\n                templates.put(templatePath, filePath);\n            });\n        }\n        // 如果禁用单元测试，则移除对应的模版\n        if (Boolean.FALSE.equals(codegenProperties.getUnitTestEnable())) {\n            templates.remove(javaTemplatePath(\"test/serviceTest\"));\n            templates.remove(\"codegen/sql/h2.vm\");\n        }\n        // 如果禁用 VO 类型，则移除对应的模版\n        if (ObjectUtil.notEqual(codegenProperties.getVoType(), CodegenVOTypeEnum.VO.getType())) {\n            templates.remove(javaTemplatePath(\"controller/vo/respVO\"));\n            templates.remove(javaTemplatePath(\"controller/vo/saveReqVO\"));\n        }\n        return templates;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private String formatFilePath(String filePath, Map<String, Object> bindingMap) {\n        filePath = StrUtil.replace(filePath, \"${basePackage}\",\n                getStr(bindingMap, \"basePackage\").replaceAll(\"\\\\.\", \"/\"));\n        filePath = StrUtil.replace(filePath, \"${classNameVar}\",\n                getStr(bindingMap, \"classNameVar\"));\n        filePath = StrUtil.replace(filePath, \"${simpleClassName}\",\n                getStr(bindingMap, \"simpleClassName\"));\n        // sceneEnum 包含的字段\n        CodegenSceneEnum sceneEnum = (CodegenSceneEnum) bindingMap.get(\"sceneEnum\");\n        filePath = StrUtil.replace(filePath, \"${sceneEnum.prefixClass}\", sceneEnum.getPrefixClass());\n        filePath = StrUtil.replace(filePath, \"${sceneEnum.basePackage}\", sceneEnum.getBasePackage());\n        // table 包含的字段\n        CodegenTableDO table = (CodegenTableDO) bindingMap.get(\"table\");\n        filePath = StrUtil.replace(filePath, \"${table.moduleName}\", table.getModuleName());\n        filePath = StrUtil.replace(filePath, \"${table.businessName}\", table.getBusinessName());\n        filePath = StrUtil.replace(filePath, \"${table.className}\", table.getClassName());\n        // 特殊：主子表专属逻辑\n        Integer subIndex = (Integer) bindingMap.get(\"subIndex\");\n        if (subIndex != null) {\n            CodegenTableDO subTable = ((List<CodegenTableDO>) bindingMap.get(\"subTables\")).get(subIndex);\n            filePath = StrUtil.replace(filePath, \"${subTable.moduleName}\", subTable.getModuleName());\n            filePath = StrUtil.replace(filePath, \"${subTable.businessName}\", subTable.getBusinessName());\n            filePath = StrUtil.replace(filePath, \"${subTable.className}\", subTable.getClassName());\n            filePath = StrUtil.replace(filePath, \"${subSimpleClassName}\",\n                    ((List<String>) bindingMap.get(\"subSimpleClassNames\")).get(subIndex));\n            filePath = StrUtil.replace(filePath, \"${subSimpleClassName_strikeCase}\",\n                    ((List<String>) bindingMap.get(\"subSimpleClassName_strikeCases\")).get(subIndex));\n        }\n        return filePath;\n    }\n\n    private static String javaTemplatePath(String path) {\n        return \"codegen/java/\" + path + \".vm\";\n    }\n\n    private static String javaModuleImplVOFilePath(String path) {\n        return javaModuleFilePath(\"controller/${sceneEnum.basePackage}/${table.businessName}/\" +\n                \"vo/${sceneEnum.prefixClass}${table.className}\" + path, \"server\", \"main\");\n    }\n\n    private static String javaModuleImplControllerFilePath() {\n        return javaModuleFilePath(\"controller/${sceneEnum.basePackage}/${table.businessName}/\" +\n                \"${sceneEnum.prefixClass}${table.className}Controller\", \"server\", \"main\");\n    }\n\n    private static String javaModuleImplMainFilePath(String path) {\n        return javaModuleFilePath(path, \"server\", \"main\");\n    }\n\n    private static String javaModuleApiMainFilePath(String path) {\n        return javaModuleFilePath(path, \"api\", \"main\");\n    }\n\n    private static String javaModuleImplTestFilePath(String path) {\n        return javaModuleFilePath(path, \"server\", \"test\");\n    }\n\n    private static String javaModuleFilePath(String path, String module, String src) {\n        return \"yudao-module-${table.moduleName}/\" + // 顶级模块\n                \"yudao-module-${table.moduleName}-\" + module + \"/\" + // 子模块\n                \"src/\" + src + \"/java/${basePackage}/module/${table.moduleName}/\" + path + \".java\";\n    }\n\n    private static String mapperXmlFilePath() {\n        return \"yudao-module-${table.moduleName}/\" + // 顶级模块\n                \"yudao-module-${table.moduleName}-server/\" + // 子模块\n                \"src/main/resources/mapper/${table.businessName}/${table.className}Mapper.xml\";\n    }\n\n    private static String vueTemplatePath(String path) {\n        return \"codegen/vue/\" + path + \".vm\";\n    }\n\n    private static String vueFilePath(String path) {\n        return \"yudao-ui-${sceneEnum.basePackage}-vue2/\" + // 顶级目录\n                \"src/\" + path;\n    }\n\n    private static String vue3TemplatePath(String path) {\n        return \"codegen/vue3/\" + path + \".vm\";\n    }\n\n    private static String vue3FilePath(String path) {\n        return \"yudao-ui-${sceneEnum.basePackage}-vue3/\" + // 顶级目录\n                \"src/\" + path;\n    }\n\n    private static String vue3AdminUniappTemplatePath(String path) {\n        return \"codegen/vue3_admin_uniapp/\" + path + \".vm\";\n    }\n\n    private static String vue3UniappFilePath(String path) {\n        return \"yudao-ui-${sceneEnum.basePackage}-uniapp/\" + // 顶级目录\n                \"src/\" + path;\n    }\n\n    private static String vue3VbenFilePath(String path) {\n        return \"yudao-ui-${sceneEnum.basePackage}-vben/\" + // 顶级目录\n                \"src/\" + path;\n    }\n\n    private static String vue3VbenTemplatePath(String path) {\n        return \"codegen/vue3_vben/\" + path + \".vm\";\n    }\n\n    private static String vue3Vben5AntdSchemaTemplatePath(String path) {\n        return \"codegen/vue3_vben5_antd/schema/\" + path + \".vm\";\n    }\n\n    private static String vue3Vben5AntdGeneralTemplatePath(String path) {\n        return \"codegen/vue3_vben5_antd/general/\" + path + \".vm\";\n    }\n\n    private static String vue3Vben5EpSchemaTemplatePath(String path) {\n        return \"codegen/vue3_vben5_ele/schema/\" + path + \".vm\";\n    }\n\n    private static String vue3Vben5EpGeneralTemplatePath(String path) {\n        return \"codegen/vue3_vben5_ele/general/\" + path + \".vm\";\n    }\n\n    private static boolean isSubTemplate(String path) {\n        return path.contains(\"_sub\");\n    }\n\n    private static boolean isPageReqVOTemplate(String path) {\n        return path.contains(\"pageReqVO\");\n    }\n\n    private static boolean isListReqVOTemplate(String path) {\n        return path.contains(\"listReqVO\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 参数配置 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ConfigService {\n\n    /**\n     * 创建参数配置\n     *\n     * @param createReqVO 创建信息\n     * @return 配置编号\n     */\n    Long createConfig(@Valid ConfigSaveReqVO createReqVO);\n\n    /**\n     * 更新参数配置\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateConfig(@Valid ConfigSaveReqVO updateReqVO);\n\n    /**\n     * 删除参数配置\n     *\n     * @param id 配置编号\n     */\n    void deleteConfig(Long id);\n\n    /**\n     * 批量删除参数配置\n     *\n     * @param ids 配置编号列表\n     */\n    void deleteConfigList(List<Long> ids);\n\n    /**\n     * 获得参数配置\n     *\n     * @param id 配置编号\n     * @return 参数配置\n     */\n    ConfigDO getConfig(Long id);\n\n    /**\n     * 根据参数键，获得参数配置\n     *\n     * @param key 配置键\n     * @return 参数配置\n     */\n    ConfigDO getConfigByKey(String key);\n\n    /**\n     * 获得参数配置分页列表\n     *\n     * @param reqVO 分页条件\n     * @return 分页列表\n     */\n    PageResult<ConfigDO> getConfigPage(ConfigPageReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.convert.config.ConfigConvert;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.config.ConfigMapper;\nimport cn.iocoder.yudao.module.infra.enums.config.ConfigTypeEnum;\nimport com.google.common.annotations.VisibleForTesting;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 参数配置 Service 实现类\n */\n@Service\n@Slf4j\n@Validated\npublic class ConfigServiceImpl implements ConfigService {\n\n    @Resource\n    private ConfigMapper configMapper;\n\n    @Override\n    public Long createConfig(ConfigSaveReqVO createReqVO) {\n        // 校验参数配置 key 的唯一性\n        validateConfigKeyUnique(null, createReqVO.getKey());\n\n        // 插入参数配置\n        ConfigDO config = ConfigConvert.INSTANCE.convert(createReqVO);\n        config.setType(ConfigTypeEnum.CUSTOM.getType());\n        configMapper.insert(config);\n        return config.getId();\n    }\n\n    @Override\n    public void updateConfig(ConfigSaveReqVO updateReqVO) {\n        // 校验自己存在\n        validateConfigExists(updateReqVO.getId());\n        // 校验参数配置 key 的唯一性\n        validateConfigKeyUnique(updateReqVO.getId(), updateReqVO.getKey());\n\n        // 更新参数配置\n        ConfigDO updateObj = ConfigConvert.INSTANCE.convert(updateReqVO);\n        configMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteConfig(Long id) {\n        // 校验配置存在\n        ConfigDO config = validateConfigExists(id);\n        // 内置配置，不允许删除\n        if (ConfigTypeEnum.SYSTEM.getType().equals(config.getType())) {\n            throw exception(CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE);\n        }\n        // 删除\n        configMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteConfigList(List<Long> ids) {\n        // 校验是否有内置配置\n        List<ConfigDO> configs = configMapper.selectByIds(ids);\n        configs.forEach(config -> {\n            if (ConfigTypeEnum.SYSTEM.getType().equals(config.getType())) {\n                throw exception(CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE);\n            }\n        });\n\n        // 批量删除\n        configMapper.deleteByIds(ids);\n    }\n\n    @Override\n    public ConfigDO getConfig(Long id) {\n        return configMapper.selectById(id);\n    }\n\n    @Override\n    public ConfigDO getConfigByKey(String key) {\n        return configMapper.selectByKey(key);\n    }\n\n    @Override\n    public PageResult<ConfigDO> getConfigPage(ConfigPageReqVO pageReqVO) {\n        return configMapper.selectPage(pageReqVO);\n    }\n\n    @VisibleForTesting\n    public ConfigDO validateConfigExists(Long id) {\n        if (id == null) {\n            return null;\n        }\n        ConfigDO config = configMapper.selectById(id);\n        if (config == null) {\n            throw exception(CONFIG_NOT_EXISTS);\n        }\n        return config;\n    }\n\n    @VisibleForTesting\n    public void validateConfigKeyUnique(Long id, String key) {\n        ConfigDO config = configMapper.selectByKey(key);\n        if (config == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的参数配置\n        if (id == null) {\n            throw exception(CONFIG_KEY_DUPLICATE);\n        }\n        if (!config.getId().equals(id)) {\n            throw exception(CONFIG_KEY_DUPLICATE);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.db;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 数据源配置 Service 接口\n *\n * @author 芋道源码\n */\npublic interface DataSourceConfigService {\n\n    /**\n     * 创建数据源配置\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDataSourceConfig(@Valid DataSourceConfigSaveReqVO createReqVO);\n\n    /**\n     * 更新数据源配置\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDataSourceConfig(@Valid DataSourceConfigSaveReqVO updateReqVO);\n\n    /**\n     * 删除数据源配置\n     *\n     * @param id 编号\n     */\n    void deleteDataSourceConfig(Long id);\n\n    /**\n     * 批量删除数据源配置\n     *\n     * @param ids 编号列表\n     */\n    void deleteDataSourceConfigList(List<Long> ids);\n\n    /**\n     * 获得数据源配置\n     *\n     * @param id 编号\n     * @return 数据源配置\n     */\n    DataSourceConfigDO getDataSourceConfig(Long id);\n\n    /**\n     * 获得数据源配置列表\n     *\n     * @return 数据源配置列表\n     */\n    List<DataSourceConfigDO> getDataSourceConfigList();\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.db;\n\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper;\nimport com.baomidou.dynamic.datasource.creator.DataSourceProperty;\nimport com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_OK;\n\n/**\n * 数据源配置 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class DataSourceConfigServiceImpl implements DataSourceConfigService {\n\n    @Resource\n    private DataSourceConfigMapper dataSourceConfigMapper;\n\n    @Resource\n    private DynamicDataSourceProperties dynamicDataSourceProperties;\n\n    @Override\n    public Long createDataSourceConfig(DataSourceConfigSaveReqVO createReqVO) {\n        DataSourceConfigDO config = BeanUtils.toBean(createReqVO, DataSourceConfigDO.class);\n        validateConnectionOK(config);\n\n        // 插入\n        dataSourceConfigMapper.insert(config);\n        // 返回\n        return config.getId();\n    }\n\n    @Override\n    public void updateDataSourceConfig(DataSourceConfigSaveReqVO updateReqVO) {\n        // 校验存在\n        validateDataSourceConfigExists(updateReqVO.getId());\n        DataSourceConfigDO updateObj = BeanUtils.toBean(updateReqVO, DataSourceConfigDO.class);\n        validateConnectionOK(updateObj);\n\n        // 更新\n        dataSourceConfigMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteDataSourceConfig(Long id) {\n        // 校验存在\n        validateDataSourceConfigExists(id);\n        // 删除\n        dataSourceConfigMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteDataSourceConfigList(List<Long> ids) {\n        dataSourceConfigMapper.deleteByIds(ids);\n    }\n\n    private void validateDataSourceConfigExists(Long id) {\n        if (dataSourceConfigMapper.selectById(id) == null) {\n            throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public DataSourceConfigDO getDataSourceConfig(Long id) {\n        // 如果 id 为 0，默认为 master 的数据源\n        if (Objects.equals(id, DataSourceConfigDO.ID_MASTER)) {\n            return buildMasterDataSourceConfig();\n        }\n        // 从 DB 中读取\n        return dataSourceConfigMapper.selectById(id);\n    }\n\n    @Override\n    public List<DataSourceConfigDO> getDataSourceConfigList() {\n        List<DataSourceConfigDO> result = dataSourceConfigMapper.selectList();\n        // 补充 master 数据源\n        result.add(0, buildMasterDataSourceConfig());\n        return result;\n    }\n\n    private void validateConnectionOK(DataSourceConfigDO config) {\n        boolean success = JdbcUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword());\n        if (!success) {\n            throw exception(DATA_SOURCE_CONFIG_NOT_OK);\n        }\n    }\n\n    private DataSourceConfigDO buildMasterDataSourceConfig() {\n        String primary = dynamicDataSourceProperties.getPrimary();\n        DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(primary);\n        return new DataSourceConfigDO().setId(DataSourceConfigDO.ID_MASTER).setName(primary)\n                .setUrl(dataSourceProperty.getUrl())\n                .setUsername(dataSourceProperty.getUsername())\n                .setPassword(dataSourceProperty.getPassword());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.db;\n\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\n\nimport java.util.List;\n\n/**\n * 数据库表 Service\n *\n * @author 芋道源码\n */\npublic interface DatabaseTableService {\n\n    /**\n     * 获得表列表，基于表名称 + 表描述进行模糊匹配\n     *\n     * @param dataSourceConfigId 数据源配置的编号\n     * @param nameLike           表名称，模糊匹配\n     * @param commentLike        表描述，模糊匹配\n     * @return 表列表\n     */\n    List<TableInfo> getTableList(Long dataSourceConfigId, String nameLike, String commentLike);\n\n    /**\n     * 获得指定表名\n     *\n     * @param dataSourceConfigId 数据源配置的编号\n     * @param tableName          表名称\n     * @return 表\n     */\n    TableInfo getTable(Long dataSourceConfigId, String tableName);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.db;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\nimport com.baomidou.mybatisplus.generator.query.SQLQuery;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * 数据库表 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\npublic class DatabaseTableServiceImpl implements DatabaseTableService {\n\n    @Resource\n    private DataSourceConfigService dataSourceConfigService;\n\n    @Override\n    public List<TableInfo> getTableList(Long dataSourceConfigId, String nameLike, String commentLike) {\n        List<TableInfo> tables = getTableList0(dataSourceConfigId, null);\n        return tables.stream().filter(tableInfo -> (StrUtil.isEmpty(nameLike) || tableInfo.getName().contains(nameLike))\n                        && (StrUtil.isEmpty(commentLike) || tableInfo.getComment().contains(commentLike)))\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public TableInfo getTable(Long dataSourceConfigId, String name) {\n        return CollUtil.getFirst(getTableList0(dataSourceConfigId, name));\n    }\n\n    private List<TableInfo> getTableList0(Long dataSourceConfigId, String name) {\n        // 获得数据源配置\n        DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(dataSourceConfigId);\n        Assert.notNull(config, \"数据源({}) 不存在！\", dataSourceConfigId);\n\n        // 使用 MyBatis Plus Generator 解析表结构\n        DataSourceConfig.Builder dataSourceConfigBuilder = new DataSourceConfig.Builder(config.getUrl(), config.getUsername(),\n                config.getPassword());\n        if (JdbcUtils.isSQLServer(config.getUrl())) { // 特殊：SQLServer jdbc 非标准，参见 https://github.com/baomidou/mybatis-plus/issues/5419\n            dataSourceConfigBuilder.databaseQueryClass(SQLQuery.class);\n        }\n        StrategyConfig.Builder strategyConfig = new StrategyConfig.Builder().enableSkipView(); // 忽略视图，业务上一般用不到\n        if (StrUtil.isNotEmpty(name)) {\n            strategyConfig.addInclude(name);\n        } else {\n            // 移除工作流和定时任务前缀的表名\n            strategyConfig.addExclude(\"ACT_[\\\\S\\\\s]+|QRTZ_[\\\\S\\\\s]+|FLW_[\\\\S\\\\s]+|act_[\\\\S\\\\s]+|qrtz_[\\\\S\\\\s]+|flw_[\\\\S\\\\s]+\");\n            // 移除 ORACLE 相关的系统表\n            strategyConfig.addExclude(\"IMPDP_[\\\\S\\\\s]+|ALL_[\\\\S\\\\s]+|HS_[\\\\S\\\\s]+|impdp_[\\\\S\\\\s]+|all_[\\\\S\\\\s]+|hs_[\\\\S\\\\s]+\");\n            strategyConfig.addExclude(\"[\\\\S\\\\s]+\\\\$[\\\\S\\\\s]+|[\\\\S\\\\s]+\\\\$\"); // 表里不能有 $，一般有都是系统的表\n        }\n\n        GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.TIME_PACK).build(); // 只使用 LocalDateTime 类型，不使用 LocalDate\n        ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfigBuilder.build(), strategyConfig.build(),\n                null, globalConfig, null);\n        // 按照名字排序\n        List<TableInfo> tables = builder.getTableInfoList();\n        tables.sort(Comparator.comparing(TableInfo::getName));\n        return tables;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo01;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01.Demo01ContactDO;\nimport javax.validation.Valid;\n\nimport java.util.List;\n\n/**\n * 示例联系人 Service 接口\n *\n * @author 芋道源码\n */\npublic interface Demo01ContactService {\n\n    /**\n     * 创建示例联系人\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDemo01Contact(@Valid Demo01ContactSaveReqVO createReqVO);\n\n    /**\n     * 更新示例联系人\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDemo01Contact(@Valid Demo01ContactSaveReqVO updateReqVO);\n\n    /**\n     * 删除示例联系人\n     *\n     * @param id 编号\n     */\n    void deleteDemo01Contact(Long id);\n\n    /**\n     * 批量删除示例联系人\n     *\n     * @param ids 编号\n     */\n    void deleteDemo0iContactList(List<Long> ids);\n\n    /**\n     * 获得示例联系人\n     *\n     * @param id 编号\n     * @return 示例联系人\n     */\n    Demo01ContactDO getDemo01Contact(Long id);\n\n    /**\n     * 获得示例联系人分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 示例联系人分页\n     */\n    PageResult<Demo01ContactDO> getDemo01ContactPage(Demo01ContactPageReqVO pageReqVO);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo01;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01.Demo01ContactDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo01.Demo01ContactMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DEMO01_CONTACT_NOT_EXISTS;\n\n/**\n * 示例联系人 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class Demo01ContactServiceImpl implements Demo01ContactService {\n\n    @Resource\n    private Demo01ContactMapper demo01ContactMapper;\n\n    @Override\n    public Long createDemo01Contact(Demo01ContactSaveReqVO createReqVO) {\n        // 插入\n        Demo01ContactDO demo01Contact = BeanUtils.toBean(createReqVO, Demo01ContactDO.class);\n        demo01ContactMapper.insert(demo01Contact);\n        // 返回\n        return demo01Contact.getId();\n    }\n\n    @Override\n    public void updateDemo01Contact(Demo01ContactSaveReqVO updateReqVO) {\n        // 校验存在\n        validateDemo01ContactExists(updateReqVO.getId());\n        // 更新\n        Demo01ContactDO updateObj = BeanUtils.toBean(updateReqVO, Demo01ContactDO.class);\n        demo01ContactMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteDemo01Contact(Long id) {\n        // 校验存在\n        validateDemo01ContactExists(id);\n        // 删除\n        demo01ContactMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteDemo0iContactList(List<Long> ids) {\n        // 校验存在\n        validateDemo01ContactExists(ids);\n        // 删除\n        demo01ContactMapper.deleteByIds(ids);\n    }\n\n    private void validateDemo01ContactExists(List<Long> ids) {\n        List<Demo01ContactDO> list = demo01ContactMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {\n            throw exception(DEMO01_CONTACT_NOT_EXISTS);\n        }\n    }\n\n    private void validateDemo01ContactExists(Long id) {\n        if (demo01ContactMapper.selectById(id) == null) {\n            throw exception(DEMO01_CONTACT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public Demo01ContactDO getDemo01Contact(Long id) {\n        return demo01ContactMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<Demo01ContactDO> getDemo01ContactPage(Demo01ContactPageReqVO pageReqVO) {\n        return demo01ContactMapper.selectPage(pageReqVO);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo02;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;\nimport javax.validation.Valid;\n\nimport java.util.List;\n\n/**\n * 示例分类 Service 接口\n *\n * @author 芋道源码\n */\npublic interface Demo02CategoryService {\n\n    /**\n     * 创建示例分类\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDemo02Category(@Valid Demo02CategorySaveReqVO createReqVO);\n\n    /**\n     * 更新示例分类\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDemo02Category(@Valid Demo02CategorySaveReqVO updateReqVO);\n\n    /**\n     * 删除示例分类\n     *\n     * @param id 编号\n     */\n    void deleteDemo02Category(Long id);\n\n    /**\n     * 获得示例分类\n     *\n     * @param id 编号\n     * @return 示例分类\n     */\n    Demo02CategoryDO getDemo02Category(Long id);\n\n    /**\n     * 获得示例分类列表\n     *\n     * @param listReqVO 查询条件\n     * @return 示例分类列表\n     */\n    List<Demo02CategoryDO> getDemo02CategoryList(Demo02CategoryListReqVO listReqVO);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo02;\n\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo02.Demo02CategoryMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 示例分类 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class Demo02CategoryServiceImpl implements Demo02CategoryService {\n\n    @Resource\n    private Demo02CategoryMapper demo02CategoryMapper;\n\n    @Override\n    public Long createDemo02Category(Demo02CategorySaveReqVO createReqVO) {\n        // 校验父级编号的有效性\n        validateParentDemo02Category(null, createReqVO.getParentId());\n        // 校验名字的唯一性\n        validateDemo02CategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());\n\n        // 插入\n        Demo02CategoryDO demo02Category = BeanUtils.toBean(createReqVO, Demo02CategoryDO.class);\n        demo02CategoryMapper.insert(demo02Category);\n        // 返回\n        return demo02Category.getId();\n    }\n\n    @Override\n    public void updateDemo02Category(Demo02CategorySaveReqVO updateReqVO) {\n        // 校验存在\n        validateDemo02CategoryExists(updateReqVO.getId());\n        // 校验父级编号的有效性\n        validateParentDemo02Category(updateReqVO.getId(), updateReqVO.getParentId());\n        // 校验名字的唯一性\n        validateDemo02CategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());\n\n        // 更新\n        Demo02CategoryDO updateObj = BeanUtils.toBean(updateReqVO, Demo02CategoryDO.class);\n        demo02CategoryMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteDemo02Category(Long id) {\n        // 校验存在\n        validateDemo02CategoryExists(id);\n        // 校验是否有子示例分类\n        if (demo02CategoryMapper.selectCountByParentId(id) > 0) {\n            throw exception(DEMO02_CATEGORY_EXITS_CHILDREN);\n        }\n        // 删除\n        demo02CategoryMapper.deleteById(id);\n    }\n\n    private void validateDemo02CategoryExists(Long id) {\n        if (demo02CategoryMapper.selectById(id) == null) {\n            throw exception(DEMO02_CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    private void validateParentDemo02Category(Long id, Long parentId) {\n        if (parentId == null || Demo02CategoryDO.PARENT_ID_ROOT.equals(parentId)) {\n            return;\n        }\n        // 1. 不能设置自己为父示例分类\n        if (Objects.equals(id, parentId)) {\n            throw exception(DEMO02_CATEGORY_PARENT_ERROR);\n        }\n        // 2. 父示例分类不存在\n        Demo02CategoryDO parentDemo02Category = demo02CategoryMapper.selectById(parentId);\n        if (parentDemo02Category == null) {\n            throw exception(DEMO02_CATEGORY_PARENT_NOT_EXITS);\n        }\n        // 3. 递归校验父示例分类，如果父示例分类是自己的子示例分类，则报错，避免形成环路\n        if (id == null) { // id 为空，说明新增，不需要考虑环路\n            return;\n        }\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            // 3.1 校验环路\n            parentId = parentDemo02Category.getParentId();\n            if (Objects.equals(id, parentId)) {\n                throw exception(DEMO02_CATEGORY_PARENT_IS_CHILD);\n            }\n            // 3.2 继续递归下一级父示例分类\n            if (parentId == null || Demo02CategoryDO.PARENT_ID_ROOT.equals(parentId)) {\n                break;\n            }\n            parentDemo02Category = demo02CategoryMapper.selectById(parentId);\n            if (parentDemo02Category == null) {\n                break;\n            }\n        }\n    }\n\n    private void validateDemo02CategoryNameUnique(Long id, Long parentId, String name) {\n        Demo02CategoryDO demo02Category = demo02CategoryMapper.selectByParentIdAndName(parentId, name);\n        if (demo02Category == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的示例分类\n        if (id == null) {\n            throw exception(DEMO02_CATEGORY_NAME_DUPLICATE);\n        }\n        if (!Objects.equals(demo02Category.getId(), id)) {\n            throw exception(DEMO02_CATEGORY_NAME_DUPLICATE);\n        }\n    }\n\n    @Override\n    public Demo02CategoryDO getDemo02Category(Long id) {\n        return demo02CategoryMapper.selectById(id);\n    }\n\n    @Override\n    public List<Demo02CategoryDO> getDemo02CategoryList(Demo02CategoryListReqVO listReqVO) {\n        return demo02CategoryMapper.selectList(listReqVO);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/erp/Demo03StudentErpService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo03.erp;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport javax.validation.Valid;\n\nimport java.util.List;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface Demo03StudentErpService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDemo03Student(@Valid Demo03StudentErpSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDemo03Student(@Valid Demo03StudentErpSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteDemo03Student(Long id);\n\n    /**\n     * 批量删除学生\n     *\n     * @param ids 编号\n     */\n    void deleteDemo03StudentList(List<Long> ids);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    Demo03StudentDO getDemo03Student(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<Demo03StudentDO> getDemo03StudentPage(Demo03StudentErpPageReqVO pageReqVO);\n\n    // ==================== 子表（学生课程） ====================\n\n    /**\n     * 获得学生课程分页\n     *\n     * @param pageReqVO 分页查询\n     * @param studentId 学生编号\n     * @return 学生课程分页\n     */\n    PageResult<Demo03CourseDO> getDemo03CoursePage(PageParam pageReqVO, Long studentId);\n\n    /**\n     * 创建学生课程\n     *\n     * @param demo03Course 创建信息\n     * @return 编号\n     */\n    Long createDemo03Course(@Valid Demo03CourseDO demo03Course);\n\n    /**\n     * 更新学生课程\n     *\n     * @param demo03Course 更新信息\n     */\n    void updateDemo03Course(@Valid Demo03CourseDO demo03Course);\n\n    /**\n     * 删除学生课程\n     *\n     * @param id 编号\n     */\n    void deleteDemo03Course(Long id);\n\n    /**\n     * 批量删除学生课程\n     *\n     * @param ids 编号\n     */\n    void deleteDemo03CourseList(List<Long> ids);\n\n    /**\n     * 获得学生课程\n     *\n     * @param id 编号\n     * @return 学生课程\n     */\n    Demo03CourseDO getDemo03Course(Long id);\n\n    // ==================== 子表（学生班级） ====================\n\n    /**\n     * 获得学生班级分页\n     *\n     * @param pageReqVO 分页查询\n     * @param studentId 学生编号\n     * @return 学生班级分页\n     */\n    PageResult<Demo03GradeDO> getDemo03GradePage(PageParam pageReqVO, Long studentId);\n\n    /**\n     * 创建学生班级\n     *\n     * @param demo03Grade 创建信息\n     * @return 编号\n     */\n    Long createDemo03Grade(@Valid Demo03GradeDO demo03Grade);\n\n    /**\n     * 更新学生班级\n     *\n     * @param demo03Grade 更新信息\n     */\n    void updateDemo03Grade(@Valid Demo03GradeDO demo03Grade);\n\n    /**\n     * 删除学生班级\n     *\n     * @param id 编号\n     */\n    void deleteDemo03Grade(Long id);\n\n    /**\n     * 批量删除学生班级\n     *\n     * @param ids 编号\n     */\n    void deleteDemo03GradeList(List<Long> ids);\n\n    /**\n     * 获得学生班级\n     *\n     * @param id 编号\n     * @return 学生班级\n     */\n    Demo03GradeDO getDemo03Grade(Long id);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/erp/Demo03StudentErpServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo03.erp;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo.Demo03StudentErpSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.erp.Demo03CourseErpMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.erp.Demo03GradeErpMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.erp.Demo03StudentErpMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class Demo03StudentErpServiceImpl implements Demo03StudentErpService {\n\n    @Resource\n    private Demo03StudentErpMapper demo03StudentErpMapper;\n    @Resource\n    private Demo03CourseErpMapper demo03CourseErpMapper;\n    @Resource\n    private Demo03GradeErpMapper demo03GradeErpMapper;\n\n    @Override\n    public Long createDemo03Student(Demo03StudentErpSaveReqVO createReqVO) {\n        // 插入\n        Demo03StudentDO demo03Student = BeanUtils.toBean(createReqVO, Demo03StudentDO.class);\n        demo03StudentErpMapper.insert(demo03Student);\n        // 返回\n        return demo03Student.getId();\n    }\n\n    @Override\n    public void updateDemo03Student(Demo03StudentErpSaveReqVO updateReqVO) {\n        // 校验存在\n        validateDemo03StudentExists(updateReqVO.getId());\n        // 更新\n        Demo03StudentDO updateObj = BeanUtils.toBean(updateReqVO, Demo03StudentDO.class);\n        demo03StudentErpMapper.updateById(updateObj);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteDemo03Student(Long id) {\n        // 校验存在\n        validateDemo03StudentExists(id);\n        // 删除\n        demo03StudentErpMapper.deleteById(id);\n\n        // 删除子表\n        deleteDemo03CourseByStudentId(id);\n        deleteDemo03GradeByStudentId(id);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteDemo03StudentList(List<Long> ids) {\n        // 校验存在\n        validateDemo03StudentExists(ids);\n        // 删除\n        demo03StudentErpMapper.deleteByIds(ids);\n\n        // 删除子表\n        deleteDemo03CourseByStudentIds(ids);\n        deleteDemo03GradeByStudentIds(ids);\n    }\n\n    private void validateDemo03StudentExists(List<Long> ids) {\n        List<Demo03StudentDO> list = demo03StudentErpMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {\n            throw exception(DEMO03_STUDENT_NOT_EXISTS);\n        }\n    }\n\n    private void validateDemo03StudentExists(Long id) {\n        if (demo03StudentErpMapper.selectById(id) == null) {\n            throw exception(DEMO03_STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public Demo03StudentDO getDemo03Student(Long id) {\n        return demo03StudentErpMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<Demo03StudentDO> getDemo03StudentPage(Demo03StudentErpPageReqVO pageReqVO) {\n        return demo03StudentErpMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 子表（学生课程） ====================\n\n    @Override\n    public PageResult<Demo03CourseDO> getDemo03CoursePage(PageParam pageReqVO, Long studentId) {\n        return demo03CourseErpMapper.selectPage(pageReqVO, studentId);\n    }\n\n    @Override\n    public Long createDemo03Course(Demo03CourseDO demo03Course) {\n        demo03CourseErpMapper.insert(demo03Course);\n        return demo03Course.getId();\n    }\n\n    @Override\n    public void updateDemo03Course(Demo03CourseDO demo03Course) {\n        // 校验存在\n        validateDemo03CourseExists(demo03Course.getId());\n        // 更新\n        demo03Course.clean();\n        demo03CourseErpMapper.updateById(demo03Course);\n    }\n\n    @Override\n    public void deleteDemo03Course(Long id) {\n        // 删除\n        demo03CourseErpMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteDemo03CourseList(List<Long> ids) {\n        // 删除\n        demo03CourseErpMapper.deleteByIds(ids);\n    }\n\n    @Override\n    public Demo03CourseDO getDemo03Course(Long id) {\n        return demo03CourseErpMapper.selectById(id);\n    }\n\n    private void validateDemo03CourseExists(Long id) {\n        if (demo03CourseErpMapper.selectById(id) == null) {\n            throw exception(DEMO03_COURSE_NOT_EXISTS);\n        }\n    }\n\n    private void deleteDemo03CourseByStudentId(Long studentId) {\n        demo03CourseErpMapper.deleteByStudentId(studentId);\n    }\n\n    private void deleteDemo03CourseByStudentIds(List<Long> studentIds) {\n        demo03CourseErpMapper.deleteByStudentIds(studentIds);\n    }\n\n    // ==================== 子表（学生班级） ====================\n\n    @Override\n    public PageResult<Demo03GradeDO> getDemo03GradePage(PageParam pageReqVO, Long studentId) {\n        return demo03GradeErpMapper.selectPage(pageReqVO, studentId);\n    }\n\n    @Override\n    public Long createDemo03Grade(Demo03GradeDO demo03Grade) {\n        // 校验是否已经存在\n        if (demo03GradeErpMapper.selectByStudentId(demo03Grade.getStudentId()) != null) {\n            throw exception(DEMO03_GRADE_EXISTS);\n        }\n        // 插入\n        demo03GradeErpMapper.insert(demo03Grade);\n        return demo03Grade.getId();\n    }\n\n    @Override\n    public void updateDemo03Grade(Demo03GradeDO demo03Grade) {\n        // 校验存在\n        validateDemo03GradeExists(demo03Grade.getId());\n        // 更新\n        demo03Grade.clean();\n        demo03GradeErpMapper.updateById(demo03Grade);\n    }\n\n    @Override\n    public void deleteDemo03Grade(Long id) {\n        // 删除\n        demo03GradeErpMapper.deleteById(id);\n    }\n\n    @Override\n    public void deleteDemo03GradeList(List<Long> ids) {\n        // 删除\n        demo03GradeErpMapper.deleteByIds(ids);\n    }\n\n    @Override\n    public Demo03GradeDO getDemo03Grade(Long id) {\n        return demo03GradeErpMapper.selectById(id);\n    }\n\n    private void validateDemo03GradeExists(Long id) {\n        if (demo03GradeErpMapper.selectById(id) == null) {\n            throw exception(DEMO03_GRADE_NOT_EXISTS);\n        }\n    }\n\n    private void deleteDemo03GradeByStudentId(Long studentId) {\n        demo03GradeErpMapper.deleteByStudentId(studentId);\n    }\n\n    private void deleteDemo03GradeByStudentIds(List<Long> studentIds) {\n        demo03GradeErpMapper.deleteByStudentIds(studentIds);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/inner/Demo03StudentInnerService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo03.inner;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport javax.validation.Valid;\n\nimport java.util.List;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface Demo03StudentInnerService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDemo03Student(@Valid Demo03StudentInnerSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDemo03Student(@Valid Demo03StudentInnerSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteDemo03Student(Long id);\n\n    /**\n     * 批量删除学生\n     *\n     * @param ids 编号\n     */\n    void deleteDemo03StudentList(List<Long> ids);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    Demo03StudentDO getDemo03Student(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<Demo03StudentDO> getDemo03StudentPage(Demo03StudentInnerPageReqVO pageReqVO);\n\n    // ==================== 子表（学生课程） ====================\n\n    /**\n     * 获得学生课程列表\n     *\n     * @param studentId 学生编号\n     * @return 学生课程列表\n     */\n    List<Demo03CourseDO> getDemo03CourseListByStudentId(Long studentId);\n\n    // ==================== 子表（学生班级） ====================\n\n    /**\n     * 获得学生班级\n     *\n     * @param studentId 学生编号\n     * @return 学生班级\n     */\n    Demo03GradeDO getDemo03GradeByStudentId(Long studentId);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/inner/Demo03StudentInnerServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo03.inner;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo.Demo03StudentInnerSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.inner.Demo03CourseInnerMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.inner.Demo03GradeInnerMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.inner.Demo03StudentInnerMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DEMO03_STUDENT_NOT_EXISTS;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class Demo03StudentInnerServiceImpl implements Demo03StudentInnerService {\n\n    @Resource\n    private Demo03StudentInnerMapper demo03StudentInnerMapper;\n    @Resource\n    private Demo03CourseInnerMapper demo03CourseInnerMapper;\n    @Resource\n    private Demo03GradeInnerMapper demo03GradeInnerMapper;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createDemo03Student(Demo03StudentInnerSaveReqVO createReqVO) {\n        // 插入\n        Demo03StudentDO demo03Student = BeanUtils.toBean(createReqVO, Demo03StudentDO.class);\n        demo03StudentInnerMapper.insert(demo03Student);\n\n        // 插入子表\n        createDemo03CourseList(demo03Student.getId(), createReqVO.getDemo03Courses());\n        createDemo03Grade(demo03Student.getId(), createReqVO.getDemo03Grade());\n        // 返回\n        return demo03Student.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateDemo03Student(Demo03StudentInnerSaveReqVO updateReqVO) {\n        // 校验存在\n        validateDemo03StudentExists(updateReqVO.getId());\n        // 更新\n        Demo03StudentDO updateObj = BeanUtils.toBean(updateReqVO, Demo03StudentDO.class);\n        demo03StudentInnerMapper.updateById(updateObj);\n\n        // 更新子表\n        updateDemo03CourseList(updateReqVO.getId(), updateReqVO.getDemo03Courses());\n        updateDemo03Grade(updateReqVO.getId(), updateReqVO.getDemo03Grade());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteDemo03Student(Long id) {\n        // 校验存在\n        validateDemo03StudentExists(id);\n        // 删除\n        demo03StudentInnerMapper.deleteById(id);\n\n        // 删除子表\n        deleteDemo03CourseByStudentId(id);\n        deleteDemo03GradeByStudentId(id);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteDemo03StudentList(List<Long> ids) {\n        // 校验存在\n        validateDemo03StudentExists(ids);\n        // 删除\n        demo03StudentInnerMapper.deleteByIds(ids);\n\n        // 删除子表\n        deleteDemo03CourseByStudentIds(ids);\n        deleteDemo03GradeByStudentIds(ids);\n    }\n\n    private void validateDemo03StudentExists(List<Long> ids) {\n        List<Demo03StudentDO> list = demo03StudentInnerMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {\n            throw exception(DEMO03_STUDENT_NOT_EXISTS);\n        }\n    }\n\n    private void validateDemo03StudentExists(Long id) {\n        if (demo03StudentInnerMapper.selectById(id) == null) {\n            throw exception(DEMO03_STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public Demo03StudentDO getDemo03Student(Long id) {\n        return demo03StudentInnerMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<Demo03StudentDO> getDemo03StudentPage(Demo03StudentInnerPageReqVO pageReqVO) {\n        return demo03StudentInnerMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 子表（学生课程） ====================\n\n    @Override\n    public List<Demo03CourseDO> getDemo03CourseListByStudentId(Long studentId) {\n        return demo03CourseInnerMapper.selectListByStudentId(studentId);\n    }\n\n    private void createDemo03CourseList(Long studentId, List<Demo03CourseDO> list) {\n        list.forEach(o -> o.setStudentId(studentId).clean());\n        demo03CourseInnerMapper.insertBatch(list);\n    }\n\n    private void updateDemo03CourseList(Long studentId, List<Demo03CourseDO> list) {\n        list.forEach(o -> o.setStudentId(studentId).clean());\n        List<Demo03CourseDO> oldList = demo03CourseInnerMapper.selectListByStudentId(studentId);\n        List<List<Demo03CourseDO>> diffList = diffList(oldList, list, (oldVal, newVal) -> {\n            boolean same = ObjectUtil.equal(oldVal.getId(), newVal.getId());\n            if (same) {\n                newVal.setId(oldVal.getId());\n            }\n            return same;\n        });\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            demo03CourseInnerMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            demo03CourseInnerMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            demo03CourseInnerMapper.deleteByIds(convertList(diffList.get(2), Demo03CourseDO::getId));\n        }\n    }\n\n    private void deleteDemo03CourseByStudentId(Long studentId) {\n        demo03CourseInnerMapper.deleteByStudentId(studentId);\n    }\n\n    private void deleteDemo03CourseByStudentIds(List<Long> studentIds) {\n        demo03CourseInnerMapper.deleteByStudentIds(studentIds);\n    }\n\n    // ==================== 子表（学生班级） ====================\n\n    @Override\n    public Demo03GradeDO getDemo03GradeByStudentId(Long studentId) {\n        return demo03GradeInnerMapper.selectByStudentId(studentId);\n    }\n\n    private void createDemo03Grade(Long studentId, Demo03GradeDO demo03Grade) {\n        if (demo03Grade == null) {\n            return;\n        }\n        demo03Grade.setStudentId(studentId);\n        demo03GradeInnerMapper.insert(demo03Grade);\n    }\n\n    private void updateDemo03Grade(Long studentId, Demo03GradeDO demo03Grade) {\n        if (demo03Grade == null) {\n            return;\n        }\n        demo03Grade.setStudentId(studentId).clean();\n        demo03GradeInnerMapper.insertOrUpdate(demo03Grade);\n    }\n\n    private void deleteDemo03GradeByStudentId(Long studentId) {\n        demo03GradeInnerMapper.deleteByStudentId(studentId);\n    }\n\n    private void deleteDemo03GradeByStudentIds(List<Long> studentIds) {\n        demo03GradeInnerMapper.deleteByStudentIds(studentIds);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/normal/Demo03StudentNormalService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo03.normal;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport javax.validation.Valid;\n\nimport java.util.List;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface Demo03StudentNormalService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDemo03Student(@Valid Demo03StudentNormalSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDemo03Student(@Valid Demo03StudentNormalSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteDemo03Student(Long id);\n\n    /**\n     * 批量删除学生\n     *\n     * @param ids 编号\n     */\n    void deleteDemo03StudentList(List<Long> ids);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    Demo03StudentDO getDemo03Student(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<Demo03StudentDO> getDemo03StudentPage(Demo03StudentNormalPageReqVO pageReqVO);\n\n    // ==================== 子表（学生课程） ====================\n\n    /**\n     * 获得学生课程列表\n     *\n     * @param studentId 学生编号\n     * @return 学生课程列表\n     */\n    List<Demo03CourseDO> getDemo03CourseListByStudentId(Long studentId);\n\n    // ==================== 子表（学生班级） ====================\n\n    /**\n     * 获得学生班级\n     *\n     * @param studentId 学生编号\n     * @return 学生班级\n     */\n    Demo03GradeDO getDemo03GradeByStudentId(Long studentId);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/normal/Demo03StudentNormalServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.demo.demo03.normal;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.Demo03StudentNormalSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.normal.Demo03CourseNormalMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.normal.Demo03GradeNormalMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.normal.Demo03StudentNormalMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DEMO03_STUDENT_NOT_EXISTS;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class Demo03StudentNormalServiceImpl implements Demo03StudentNormalService {\n\n    @Resource\n    private Demo03StudentNormalMapper demo03StudentNormalMapper;\n    @Resource\n    private Demo03CourseNormalMapper demo03CourseNormalMapper;\n    @Resource\n    private Demo03GradeNormalMapper demo03GradeNormalMapper;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createDemo03Student(Demo03StudentNormalSaveReqVO createReqVO) {\n        // 插入\n        Demo03StudentDO demo03Student = BeanUtils.toBean(createReqVO, Demo03StudentDO.class);\n        demo03StudentNormalMapper.insert(demo03Student);\n\n        // 插入子表\n        createDemo03CourseList(demo03Student.getId(), createReqVO.getDemo03Courses());\n        createDemo03Grade(demo03Student.getId(), createReqVO.getDemo03Grade());\n        // 返回\n        return demo03Student.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateDemo03Student(Demo03StudentNormalSaveReqVO updateReqVO) {\n        // 校验存在\n        validateDemo03StudentExists(updateReqVO.getId());\n        // 更新\n        Demo03StudentDO updateObj = BeanUtils.toBean(updateReqVO, Demo03StudentDO.class);\n        demo03StudentNormalMapper.updateById(updateObj);\n\n        // 更新子表\n        updateDemo03CourseList(updateReqVO.getId(), updateReqVO.getDemo03Courses());\n        updateDemo03Grade(updateReqVO.getId(), updateReqVO.getDemo03Grade());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteDemo03Student(Long id) {\n        // 校验存在\n        validateDemo03StudentExists(id);\n        // 删除\n        demo03StudentNormalMapper.deleteById(id);\n\n        // 删除子表\n        deleteDemo03CourseByStudentId(id);\n        deleteDemo03GradeByStudentId(id);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteDemo03StudentList(List<Long> ids) {\n        // 校验存在\n        validateDemo03StudentExists(ids);\n        // 删除\n        demo03StudentNormalMapper.deleteByIds(ids);\n\n        // 删除子表\n        deleteDemo03CourseByStudentIds(ids);\n        deleteDemo03GradeByStudentIds(ids);\n    }\n\n    private void validateDemo03StudentExists(List<Long> ids) {\n        List<Demo03StudentDO> list = demo03StudentNormalMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {\n            throw exception(DEMO03_STUDENT_NOT_EXISTS);\n        }\n    }\n\n    private void validateDemo03StudentExists(Long id) {\n        if (demo03StudentNormalMapper.selectById(id) == null) {\n            throw exception(DEMO03_STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public Demo03StudentDO getDemo03Student(Long id) {\n        return demo03StudentNormalMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<Demo03StudentDO> getDemo03StudentPage(Demo03StudentNormalPageReqVO pageReqVO) {\n        return demo03StudentNormalMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 子表（学生课程） ====================\n\n    @Override\n    public List<Demo03CourseDO> getDemo03CourseListByStudentId(Long studentId) {\n        return demo03CourseNormalMapper.selectListByStudentId(studentId);\n    }\n\n    private void createDemo03CourseList(Long studentId, List<Demo03CourseDO> list) {\n        list.forEach(o -> o.setStudentId(studentId).clean());\n        demo03CourseNormalMapper.insertBatch(list);\n    }\n\n    private void updateDemo03CourseList(Long studentId, List<Demo03CourseDO> list) {\n        list.forEach(o -> o.setStudentId(studentId).clean());\n        List<Demo03CourseDO> oldList = demo03CourseNormalMapper.selectListByStudentId(studentId);\n        List<List<Demo03CourseDO>> diffList = diffList(oldList, list, (oldVal, newVal) -> {\n            boolean same = ObjectUtil.equal(oldVal.getId(), newVal.getId());\n            if (same) {\n                newVal.setId(oldVal.getId());\n            }\n            return same;\n        });\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            demo03CourseNormalMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            demo03CourseNormalMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            demo03CourseNormalMapper.deleteByIds(convertList(diffList.get(2), Demo03CourseDO::getId));\n        }\n    }\n\n    private void deleteDemo03CourseByStudentId(Long studentId) {\n        demo03CourseNormalMapper.deleteByStudentId(studentId);\n    }\n\n    private void deleteDemo03CourseByStudentIds(List<Long> studentIds) {\n        demo03CourseNormalMapper.deleteByStudentIds(studentIds);\n    }\n\n    // ==================== 子表（学生班级） ====================\n\n    @Override\n    public Demo03GradeDO getDemo03GradeByStudentId(Long studentId) {\n        return demo03GradeNormalMapper.selectByStudentId(studentId);\n    }\n\n    private void createDemo03Grade(Long studentId, Demo03GradeDO demo03Grade) {\n        if (demo03Grade == null) {\n            return;\n        }\n        demo03Grade.setStudentId(studentId);\n        demo03GradeNormalMapper.insert(demo03Grade);\n    }\n\n    private void updateDemo03Grade(Long studentId, Demo03GradeDO demo03Grade) {\n        if (demo03Grade == null) {\n            return;\n        }\n        demo03Grade.setStudentId(studentId).clean();\n        demo03GradeNormalMapper.insertOrUpdate(demo03Grade);\n    }\n\n    private void deleteDemo03GradeByStudentId(Long studentId) {\n        demo03GradeNormalMapper.deleteByStudentId(studentId);\n    }\n\n    private void deleteDemo03GradeByStudentIds(List<Long> studentIds) {\n        demo03GradeNormalMapper.deleteByStudentIds(studentIds);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.file;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;\n\nimport javax.validation.Valid;\n\nimport java.util.List;\n\n/**\n * 文件配置 Service 接口\n *\n * @author 芋道源码\n */\npublic interface FileConfigService {\n\n    /**\n     * 创建文件配置\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createFileConfig(@Valid FileConfigSaveReqVO createReqVO);\n\n    /**\n     * 更新文件配置\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateFileConfig(@Valid FileConfigSaveReqVO updateReqVO);\n\n    /**\n     * 更新文件配置为 Master\n     *\n     * @param id 编号\n     */\n    void updateFileConfigMaster(Long id);\n\n    /**\n     * 删除文件配置\n     *\n     * @param id 编号\n     */\n    void deleteFileConfig(Long id);\n\n    /**\n     * 批量删除文件配置\n     *\n     * @param ids 编号列表\n     */\n    void deleteFileConfigList(List<Long> ids);\n\n    /**\n     * 获得文件配置\n     *\n     * @param id 编号\n     * @return 文件配置\n     */\n    FileConfigDO getFileConfig(Long id);\n\n    /**\n     * 获得文件配置分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 文件配置分页\n     */\n    PageResult<FileConfigDO> getFileConfigPage(FileConfigPageReqVO pageReqVO);\n\n    /**\n     * 测试文件配置是否正确，通过上传文件\n     *\n     * @param id 编号\n     * @return 文件 URL\n     */\n    String testFileConfig(Long id) throws Exception;\n\n    /**\n     * 获得指定编号的文件客户端\n     *\n     * @param id 配置编号\n     * @return 文件客户端\n     */\n    FileClient getFileClient(Long id);\n\n    /**\n     * 获得 Master 文件客户端\n     *\n     * @return 文件客户端\n     */\n    FileClient getMasterFileClient();\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.file;\n\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory;\nimport cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport javax.validation.Validator;\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_NOT_EXISTS;\n\n/**\n * 文件配置 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class FileConfigServiceImpl implements FileConfigService {\n\n    private static final Long CACHE_MASTER_ID = 0L;\n\n    /**\n     * {@link FileClient} 缓存，通过它异步刷新 fileClientFactory\n     */\n    @Getter\n    private final LoadingCache<Long, FileClient> clientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),\n            new CacheLoader<Long, FileClient>() {\n\n                @Override\n                public FileClient load(Long id) {\n                    FileConfigDO config = Objects.equals(CACHE_MASTER_ID, id) ?\n                            fileConfigMapper.selectByMaster() : fileConfigMapper.selectById(id);\n                    if (config != null) {\n                        fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig());\n                    }\n                    return fileClientFactory.getFileClient(null == config ? id : config.getId());\n                }\n\n            });\n\n    @Resource\n    private FileClientFactory fileClientFactory;\n\n    @Resource\n    private FileConfigMapper fileConfigMapper;\n\n    @Resource\n    private Validator validator;\n\n    @Override\n    public Long createFileConfig(FileConfigSaveReqVO createReqVO) {\n        FileConfigDO fileConfig = FileConfigConvert.INSTANCE.convert(createReqVO)\n                .setConfig(parseClientConfig(createReqVO.getStorage(), createReqVO.getConfig()))\n                .setMaster(false); // 默认非 master\n        fileConfigMapper.insert(fileConfig);\n        return fileConfig.getId();\n    }\n\n    @Override\n    public void updateFileConfig(FileConfigSaveReqVO updateReqVO) {\n        // 校验存在\n        FileConfigDO config = validateFileConfigExists(updateReqVO.getId());\n        // 更新\n        FileConfigDO updateObj = FileConfigConvert.INSTANCE.convert(updateReqVO)\n                .setConfig(parseClientConfig(config.getStorage(), updateReqVO.getConfig()));\n        fileConfigMapper.updateById(updateObj);\n\n        // 清空缓存\n        clearCache(config.getId(), null);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateFileConfigMaster(Long id) {\n        // 校验存在\n        validateFileConfigExists(id);\n        // 更新其它为非 master\n        fileConfigMapper.updateBatch(new FileConfigDO().setMaster(false));\n        // 更新\n        fileConfigMapper.updateById(new FileConfigDO().setId(id).setMaster(true));\n\n        // 清空缓存\n        clearCache(null, true);\n    }\n\n    private FileClientConfig parseClientConfig(Integer storage, Map<String, Object> config) {\n        // 获取配置类\n        Class<? extends FileClientConfig> configClass = FileStorageEnum.getByStorage(storage)\n                .getConfigClass();\n        FileClientConfig clientConfig = JsonUtils.parseObject2(JsonUtils.toJsonString(config), configClass);\n        // 参数校验\n        ValidationUtils.validate(validator, clientConfig);\n        // 设置参数\n        return clientConfig;\n    }\n\n    @Override\n    public void deleteFileConfig(Long id) {\n        // 校验存在\n        FileConfigDO config = validateFileConfigExists(id);\n        if (Boolean.TRUE.equals(config.getMaster())) {\n            throw exception(FILE_CONFIG_DELETE_FAIL_MASTER);\n        }\n        // 删除\n        fileConfigMapper.deleteById(id);\n\n        // 清空缓存\n        clearCache(id, null);\n    }\n\n    @Override\n    public void deleteFileConfigList(List<Long> ids) {\n        // 校验是否有主配置\n        List<FileConfigDO> configs = fileConfigMapper.selectByIds(ids);\n        for (FileConfigDO config : configs) {\n            if (Boolean.TRUE.equals(config.getMaster())) {\n                throw exception(FILE_CONFIG_DELETE_FAIL_MASTER);\n            }\n        }\n\n        // 批量删除\n        fileConfigMapper.deleteByIds(ids);\n\n        // 清空缓存\n        ids.forEach(id -> clearCache(id, null));\n    }\n\n    /**\n     * 清空指定文件配置\n     *\n     * @param id     配置编号\n     * @param master 是否主配置\n     */\n    private void clearCache(Long id, Boolean master) {\n        if (id != null) {\n            clientCache.invalidate(id);\n        }\n        if (Boolean.TRUE.equals(master)) {\n            clientCache.invalidate(CACHE_MASTER_ID);\n        }\n    }\n\n    private FileConfigDO validateFileConfigExists(Long id) {\n        FileConfigDO config = fileConfigMapper.selectById(id);\n        if (config == null) {\n            throw exception(FILE_CONFIG_NOT_EXISTS);\n        }\n        return config;\n    }\n\n    @Override\n    public FileConfigDO getFileConfig(Long id) {\n        return fileConfigMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<FileConfigDO> getFileConfigPage(FileConfigPageReqVO pageReqVO) {\n        return fileConfigMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public String testFileConfig(Long id) throws Exception {\n        // 校验存在\n        validateFileConfigExists(id);\n        // 上传文件\n        byte[] content = ResourceUtil.readBytes(\"file/erweima.jpg\");\n        return getFileClient(id).upload(content, IdUtil.fastSimpleUUID() + \".jpg\", \"image/jpeg\");\n    }\n\n    @Override\n    public FileClient getFileClient(Long id) {\n        return clientCache.getUnchecked(id);\n    }\n\n    @Override\n    public FileClient getMasterFileClient() {\n        return clientCache.getUnchecked(CACHE_MASTER_ID);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.file;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;\n\nimport javax.validation.constraints.NotEmpty;\n\nimport java.util.List;\n\n/**\n * 文件 Service 接口\n *\n * @author 芋道源码\n */\npublic interface FileService {\n\n    /**\n     * 获得文件分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 文件分页\n     */\n    PageResult<FileDO> getFilePage(FilePageReqVO pageReqVO);\n\n    /**\n     * 保存文件，并返回文件的访问路径\n     *\n     * @param content   文件内容\n     * @param name      文件名称，允许空\n     * @param directory 目录，允许空\n     * @param type      文件的 MIME 类型，允许空\n     * @return 文件路径\n     */\n    String createFile(@NotEmpty(message = \"文件内容不能为空\") byte[] content,\n                      String name, String directory, String type);\n\n    /**\n     * 生成文件预签名地址信息，用于上传\n     *\n     * @param name      文件名\n     * @param directory 目录\n     * @return 预签名地址信息\n     */\n    FilePresignedUrlRespVO presignPutUrl(@NotEmpty(message = \"文件名不能为空\") String name,\n                                         String directory);\n    /**\n     * 生成文件预签名地址信息，用于读取\n     *\n     * @param url 完整的文件访问地址\n     * @param expirationSeconds 访问有效期，单位秒\n     * @return 文件预签名地址\n     */\n    String presignGetUrl(String url, Integer expirationSeconds);\n\n    /**\n     * 创建文件\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createFile(FileCreateReqVO createReqVO);\n    FileDO getFile(Long id);\n\n    /**\n     * 删除文件\n     *\n     * @param id 编号\n     */\n    void deleteFile(Long id) throws Exception;\n\n    /**\n     * 批量删除文件\n     *\n     * @param ids 编号列表\n     */\n    void deleteFileList(List<Long> ids) throws Exception;\n\n    /**\n     * 获得文件内容\n     *\n     * @param configId 配置编号\n     * @param path     文件路径\n     * @return 文件内容\n     */\n    byte[] getFileContent(Long configId, String path) throws Exception;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.file;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.http.HttpUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils;\nimport com.google.common.annotations.VisibleForTesting;\nimport javax.annotation.Resource;\nimport lombok.SneakyThrows;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\nimport static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS;\n\n/**\n * 文件 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\npublic class FileServiceImpl implements FileService {\n\n    /**\n     * 上传文件的前缀，是否包含日期（yyyyMMdd）\n     *\n     * 目的：按照日期，进行分目录\n     */\n    static boolean PATH_PREFIX_DATE_ENABLE = true;\n    /**\n     * 上传文件的后缀，是否包含时间戳\n     *\n     * 目的：保证文件的唯一性，避免覆盖\n     * 定制：可按需调整成 UUID、或者其他方式\n     */\n    static boolean PATH_SUFFIX_TIMESTAMP_ENABLE = true;\n\n    @Resource\n    private FileConfigService fileConfigService;\n\n    @Resource\n    private FileMapper fileMapper;\n\n    @Override\n    public PageResult<FileDO> getFilePage(FilePageReqVO pageReqVO) {\n        return fileMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    @SneakyThrows\n    public String createFile(byte[] content, String name, String directory, String type) {\n        // 1.1 处理 type 为空的情况\n        if (StrUtil.isEmpty(type)) {\n            type = FileTypeUtils.getMineType(content, name);\n        }\n        // 1.2 处理 name 为空的情况\n        if (StrUtil.isEmpty(name)) {\n            name = DigestUtil.sha256Hex(content);\n        }\n        if (StrUtil.isEmpty(FileUtil.extName(name))) {\n            // 如果 name 没有后缀 type，则补充后缀\n            String extension = FileTypeUtils.getExtension(type);\n            if (StrUtil.isNotEmpty(extension)) {\n                name = name + extension;\n            }\n        }\n\n        // 2.1 生成上传的 path，需要保证唯一\n        String path = generateUploadPath(name, directory);\n        // 2.2 上传到文件存储器\n        FileClient client = fileConfigService.getMasterFileClient();\n        Assert.notNull(client, \"客户端(master) 不能为空\");\n        String url = client.upload(content, path, type);\n\n        // 3. 保存到数据库\n        fileMapper.insert(new FileDO().setConfigId(client.getId())\n                .setName(name).setPath(path).setUrl(url)\n                .setType(type).setSize((long) content.length));\n        return url;\n    }\n\n    @VisibleForTesting\n    String generateUploadPath(String name, String directory) {\n        // 1. 生成前缀、后缀\n        String prefix = null;\n        if (PATH_PREFIX_DATE_ENABLE) {\n            prefix = LocalDateTimeUtil.format(LocalDateTimeUtil.now(), PURE_DATE_PATTERN);\n        }\n        String suffix = null;\n        if (PATH_SUFFIX_TIMESTAMP_ENABLE) {\n            suffix = String.valueOf(System.currentTimeMillis());\n        }\n\n        // 2.1 先拼接 suffix 后缀\n        if (StrUtil.isNotEmpty(suffix)) {\n            String ext = FileUtil.extName(name);\n            if (StrUtil.isNotEmpty(ext)) {\n                name = FileUtil.mainName(name) + StrUtil.C_UNDERLINE + suffix + StrUtil.DOT + ext;\n            } else {\n                name = name + StrUtil.C_UNDERLINE + suffix;\n            }\n        }\n        // 2.2 再拼接 prefix 前缀\n        if (StrUtil.isNotEmpty(prefix)) {\n            name = prefix + StrUtil.SLASH + name;\n        }\n        // 2.3 最后拼接 directory 目录\n        if (StrUtil.isNotEmpty(directory)) {\n            name = directory + StrUtil.SLASH + name;\n        }\n        return name;\n    }\n\n    @Override\n    @SneakyThrows\n    public FilePresignedUrlRespVO presignPutUrl(String name, String directory) {\n        // 1. 生成上传的 path，需要保证唯一\n        String path = generateUploadPath(name, directory);\n\n        // 2. 获取文件预签名地址\n        FileClient fileClient = fileConfigService.getMasterFileClient();\n        String uploadUrl = fileClient.presignPutUrl(path);\n        String visitUrl = fileClient.presignGetUrl(path, null);\n        return new FilePresignedUrlRespVO().setConfigId(fileClient.getId())\n                .setPath(path).setUploadUrl(uploadUrl).setUrl(visitUrl);\n    }\n\n    @Override\n    public String presignGetUrl(String url, Integer expirationSeconds) {\n        FileClient fileClient = fileConfigService.getMasterFileClient();\n        return fileClient.presignGetUrl(url, expirationSeconds);\n    }\n\n    @Override\n    public Long createFile(FileCreateReqVO createReqVO) {\n        createReqVO.setUrl(HttpUtils.removeUrlQuery(createReqVO.getUrl())); // 目的：移除私有桶情况下，URL 的签名参数\n        FileDO file = BeanUtils.toBean(createReqVO, FileDO.class);\n        fileMapper.insert(file);\n        return file.getId();\n    }\n\n    @Override\n    public FileDO getFile(Long id) {\n        return validateFileExists(id);\n    }\n\n    @Override\n    public void deleteFile(Long id) throws Exception {\n        // 校验存在\n        FileDO file = validateFileExists(id);\n\n        // 从文件存储器中删除\n        FileClient client = fileConfigService.getFileClient(file.getConfigId());\n        Assert.notNull(client, \"客户端({}) 不能为空\", file.getConfigId());\n        client.delete(file.getPath());\n\n        // 删除记录\n        fileMapper.deleteById(id);\n    }\n\n    @Override\n    @SneakyThrows\n    public void deleteFileList(List<Long> ids) {\n        // 删除文件\n        List<FileDO> files = fileMapper.selectByIds(ids);\n        for (FileDO file : files) {\n            // 获取客户端\n            FileClient client = fileConfigService.getFileClient(file.getConfigId());\n            Assert.notNull(client, \"客户端({}) 不能为空\", file.getPath());\n            // 删除文件\n            client.delete(file.getPath());\n        }\n\n        // 删除记录\n        fileMapper.deleteByIds(ids);\n    }\n\n    private FileDO validateFileExists(Long id) {\n        FileDO fileDO = fileMapper.selectById(id);\n        if (fileDO == null) {\n            throw exception(FILE_NOT_EXISTS);\n        }\n        return fileDO;\n    }\n\n    @Override\n    public byte[] getFileContent(Long configId, String path) throws Exception {\n        FileClient client = fileConfigService.getFileClient(configId);\n        Assert.notNull(client, \"客户端({}) 不能为空\", configId);\n        return client.getContent(path);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO;\n\n/**\n * API 访问日志 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ApiAccessLogService {\n\n    /**\n     * 创建 API 访问日志\n     *\n     * @param createReqDTO API 访问日志\n     */\n    void createApiAccessLog(ApiAccessLogCreateReqDTO createReqDTO);\n\n    /**\n     * 获得 API 访问日志\n     *\n     * @param id 编号\n     * @return API 访问日志\n     */\n    ApiAccessLogDO getApiAccessLog(Long id);\n\n    /**\n     * 获得 API 访问日志分页\n     *\n     * @param pageReqVO 分页查询\n     * @return API 访问日志分页\n     */\n    PageResult<ApiAccessLogDO> getApiAccessLogPage(ApiAccessLogPageReqVO pageReqVO);\n\n    /**\n     * 清理 exceedDay 天前的访问日志\n     *\n     * @param exceedDay   超过多少天就进行清理\n     * @param deleteLimit 清理的间隔条数\n     */\n    Integer cleanAccessLog(Integer exceedDay, Integer deleteLimit);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiAccessLogMapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO.REQUEST_PARAMS_MAX_LENGTH;\nimport static cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO.RESULT_MSG_MAX_LENGTH;\n\n/**\n * API 访问日志 Service 实现类\n *\n * @author 芋道源码\n */\n@Slf4j\n@Service\n@Validated\npublic class ApiAccessLogServiceImpl implements ApiAccessLogService {\n\n    @Resource\n    private ApiAccessLogMapper apiAccessLogMapper;\n\n    @Override\n    public void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {\n        ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class);\n        apiAccessLog.setRequestParams(StrUtils.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));\n        apiAccessLog.setResultMsg(StrUtils.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH));\n        if (TenantContextHolder.getTenantId() != null) {\n            apiAccessLogMapper.insert(apiAccessLog);\n        } else {\n            // 极端情况下，上下文中没有租户时，此时忽略租户上下文，避免插入失败！\n            TenantUtils.executeIgnore(() -> apiAccessLogMapper.insert(apiAccessLog));\n        }\n    }\n\n    @Override\n    public ApiAccessLogDO getApiAccessLog(Long id) {\n        return apiAccessLogMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ApiAccessLogDO> getApiAccessLogPage(ApiAccessLogPageReqVO pageReqVO) {\n        return apiAccessLogMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    @SuppressWarnings(\"DuplicatedCode\")\n    public Integer cleanAccessLog(Integer exceedDay, Integer deleteLimit) {\n        int count = 0;\n        LocalDateTime expireDate = LocalDateTime.now().minusDays(exceedDay);\n        // 循环删除，直到没有满足条件的数据\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            int deleteCount = apiAccessLogMapper.deleteByCreateTimeLt(expireDate, deleteLimit);\n            count += deleteCount;\n            // 达到删除预期条数，说明到底了\n            if (deleteCount < deleteLimit) {\n                break;\n            }\n        }\n        return count;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogService.java",
    "content": "package cn.iocoder.yudao.module.infra.service.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO;\n\n/**\n * API 错误日志 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ApiErrorLogService {\n\n    /**\n     * 创建 API 错误日志\n     *\n     * @param createReqDTO API 错误日志\n     */\n    void createApiErrorLog(ApiErrorLogCreateReqDTO createReqDTO);\n\n    /**\n     * 获得 API 错误日志\n     *\n     * @param id 编号\n     * @return API 错误日志\n     */\n    ApiErrorLogDO getApiErrorLog(Long id);\n\n    /**\n     * 获得 API 错误日志分页\n     *\n     * @param pageReqVO 分页查询\n     * @return API 错误日志分页\n     */\n    PageResult<ApiErrorLogDO> getApiErrorLogPage(ApiErrorLogPageReqVO pageReqVO);\n\n    /**\n     * 更新 API 错误日志已处理\n     *\n     * @param id            API 日志编号\n     * @param processStatus 处理结果\n     * @param processUserId 处理人\n     */\n    void updateApiErrorLogProcess(Long id, Integer processStatus, Long processUserId);\n\n    /**\n     * 清理 exceedDay 天前的错误日志\n     *\n     * @param exceedDay   超过多少天就进行清理\n     * @param deleteLimit 清理的间隔条数\n     */\n    Integer cleanErrorLog(Integer exceedDay, Integer deleteLimit);\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.infra.service.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper;\nimport cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO.REQUEST_PARAMS_MAX_LENGTH;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_NOT_FOUND;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_PROCESSED;\n\n/**\n * API 错误日志 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class ApiErrorLogServiceImpl implements ApiErrorLogService {\n\n    @Resource\n    private ApiErrorLogMapper apiErrorLogMapper;\n\n    @Override\n    public void createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) {\n        ApiErrorLogDO apiErrorLog = BeanUtils.toBean(createDTO, ApiErrorLogDO.class)\n                .setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus());\n        apiErrorLog.setRequestParams(StrUtils.maxLength(apiErrorLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));\n        try {\n            if (TenantContextHolder.getTenantId() != null) {\n                apiErrorLogMapper.insert(apiErrorLog);\n            } else {\n                // 极端情况下，上下文中没有租户时，此时忽略租户上下文，避免插入失败！\n                TenantUtils.executeIgnore(() -> apiErrorLogMapper.insert(apiErrorLog));\n            }\n        } catch (Exception ex) {\n            // 兜底处理，目前只有 yudao-cloud 会发生：https://gitee.com/yudaocode/yudao-cloud-mini/issues/IC1O0A\n            log.error(\"[createApiErrorLog][记录时({}) 发生异常]\", createDTO, ex);\n        }\n    }\n\n    @Override\n    public PageResult<ApiErrorLogDO> getApiErrorLogPage(ApiErrorLogPageReqVO pageReqVO) {\n        return apiErrorLogMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public ApiErrorLogDO getApiErrorLog(Long id) {\n        return apiErrorLogMapper.selectById(id);\n    }\n\n    @Override\n    public void updateApiErrorLogProcess(Long id, Integer processStatus, Long processUserId) {\n        ApiErrorLogDO errorLog = apiErrorLogMapper.selectById(id);\n        if (errorLog == null) {\n            throw exception(API_ERROR_LOG_NOT_FOUND);\n        }\n        if (!ApiErrorLogProcessStatusEnum.INIT.getStatus().equals(errorLog.getProcessStatus())) {\n            throw exception(API_ERROR_LOG_PROCESSED);\n        }\n        // 标记处理\n        apiErrorLogMapper.updateById(ApiErrorLogDO.builder().id(id).processStatus(processStatus)\n                .processUserId(processUserId).processTime(LocalDateTime.now()).build());\n    }\n\n    @Override\n    @SuppressWarnings(\"DuplicatedCode\")\n    public Integer cleanErrorLog(Integer exceedDay, Integer deleteLimit) {\n        int count = 0;\n        LocalDateTime expireDate = LocalDateTime.now().minusDays(exceedDay);\n        // 循环删除，直到没有满足条件的数据\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            int deleteCount = apiErrorLogMapper.deleteByCreateTimeLt(expireDate, deleteLimit);\n            count += deleteCount;\n            // 达到删除预期条数，说明到底了\n            if (deleteCount < deleteLimit) {\n                break;\n            }\n        }\n        return count;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/websocket/DemoWebSocketMessageListener.java",
    "content": "package cn.iocoder.yudao.module.infra.websocket;\n\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;\nimport cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;\nimport cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;\nimport cn.iocoder.yudao.module.infra.websocket.message.DemoReceiveMessage;\nimport cn.iocoder.yudao.module.infra.websocket.message.DemoSendMessage;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.socket.WebSocketSession;\n\n/**\n * WebSocket 示例：单发消息\n *\n * @author 芋道源码\n */\n@Component\npublic class DemoWebSocketMessageListener implements WebSocketMessageListener<DemoSendMessage> {\n\n    @SuppressWarnings(\"SpringJavaAutowiredFieldsWarningInspection\")\n    @Autowired(required = false) // 由于 yudao.websocket.enable 配置项，可以关闭 WebSocket 的功能，所以这里只能不强制注入\n    private WebSocketMessageSender webSocketMessageSender;\n\n    @Override\n    public void onMessage(WebSocketSession session, DemoSendMessage message) {\n        Long fromUserId = WebSocketFrameworkUtils.getLoginUserId(session);\n        // 情况一：单发\n        if (message.getToUserId() != null) {\n            DemoReceiveMessage toMessage = new DemoReceiveMessage().setFromUserId(fromUserId)\n                    .setText(message.getText()).setSingle(true);\n            webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), message.getToUserId(), // 给指定用户\n                    \"demo-message-receive\", toMessage);\n            return;\n        }\n        // 情况二：群发\n        DemoReceiveMessage toMessage = new DemoReceiveMessage().setFromUserId(fromUserId)\n                .setText(message.getText()).setSingle(false);\n        webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), // 给所有用户\n                \"demo-message-receive\", toMessage);\n    }\n\n    @Override\n    public String getType() {\n        return \"demo-message-send\";\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoReceiveMessage.java",
    "content": "package cn.iocoder.yudao.module.infra.websocket.message;\n\nimport lombok.Data;\n\n/**\n * 示例：server -> client 同步消息\n *\n * @author 芋道源码\n */\n@Data\npublic class DemoReceiveMessage {\n\n    /**\n     * 接收人的编号\n     */\n    private Long fromUserId;\n    /**\n     * 内容\n     */\n    private String text;\n\n    /**\n     * 是否单聊\n     */\n    private Boolean single;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoSendMessage.java",
    "content": "package cn.iocoder.yudao.module.infra.websocket.message;\n\nimport lombok.Data;\n\n/**\n * 示例：client -> server 发送消息\n *\n * @author 芋道源码\n */\n@Data\npublic class DemoSendMessage {\n\n    /**\n     * 发送给谁\n     *\n     * 如果为空，说明发送给所有人\n     */\n    private Long toUserId;\n    /**\n     * 内容\n     */\n    private String text;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 5 # 初始连接数\n        min-idle: 10 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒（1 分钟）\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n        slave: # 模拟从库，可根据自己需要修改 # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 400-infra.server.iocoder.cn # 地址\n    port: 6379 # 端口\n    database: 1 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n\nspring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n      # Spring Boot Admin Server 服务端的相关配置\n      context-path: /admin # 配置 Spring\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  demo: true # 开启演示模式\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 1 # 初始连接数\n        min-idle: 1 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒（1 分钟）\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例\n          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例\n          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例\n          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例\n          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例\n          username: root\n          password: 123456\n        #          username: sa # SQL Server 连接的示例\n        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例\n        #          username: SYSDBA # DM 连接的示例\n        #          password: SYSDBA # DM 连接的示例\n        slave: # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 6379 # 端口\n    database: 0 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n\nspring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n      # Spring Boot Admin Server 服务端的相关配置\n      context-path: /admin # 配置 Spring\n\n# 日志文件配置\nlogging:\n  level:\n    # 配置自己写的 MyBatis Mapper 打印日志\n    cn.iocoder.yudao.module.infra.dal.mysql: debug\n    cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper: INFO # 配置 ApiErrorLogMapper 的日志级别为 info，避免和 GlobalExceptionHandler 重复打印\n    cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper: INFO # 配置 FileConfigMapper 的日志级别为 info\n    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿：先禁用，Spring Boot 3.X 存在部分错误的 WARN 提示\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  env: # 多环境的配置项\n    tag: ${HOSTNAME}\n  security:\n    mock-enable: true\n  access-log: # 访问日志的配置项\n    enable: true"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: infra-server\n\n  profiles:\n    active: local\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n    allow-bean-definition-overriding: true # 允许 Bean 覆盖，例如说 Feign 等会存在重复定义的服务\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  # Servlet 配置\n  servlet:\n    # 文件上传相关配置项\n    multipart:\n      max-file-size: 16MB # 单个文件大小\n      max-request-size: 32MB # 设置总上传的文件大小\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  # Cache 配置项\n  cache:\n    type: REDIS\n    redis:\n      time-to-live: 1h # 设置过期时间为 1 小时\n\nserver:\n  port: 48082\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\n--- #################### 接口文档配置 ####################\n\nspringdoc:\n  api-docs:\n    enabled: true # 1. 是否开启 Swagger 接文档的元数据\n    path: /v3/api-docs\n  swagger-ui:\n    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面\n    path: /swagger-ui\n  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档\n\nknife4j:\n  enable: true\n  setting:\n    language: zh_cn\n\n# MyBatis Plus 的配置项\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: NONE # “智能”模式，基于 IdTypeEnvironmentPostProcessor + 数据源的类型，自动适配成 AUTO、INPUT 模式。\n      #      id-type: AUTO # 自增 ID，适合 MySQL 等直接自增的数据库\n      #      id-type: INPUT # 用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n      #      id-type: ASSIGN_ID # 分配 ID，默认使用雪花算法。注意，Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时，需要去除实体类上的 @KeySequence 注解\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n    banner: false # 关闭控制台的 Banner 打印\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  encryptor:\n    password: XDV71a+xqStEA3WH # 加解密的秘钥，可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成\n\nmybatis-plus-join:\n  banner: false # 关闭控制台的 Banner 打印\n\n# Spring Data Redis 配置\nspring:\n  data:\n    redis:\n      repositories:\n        enabled: false # 项目未使用到 Spring Data Redis 的 Repository，所以直接禁用，保证启动速度\n\n# VO 转换（数据翻译）相关\neasy-trans:\n  is-enable-global: false # 【默认禁用，对性能确认压力大】启用全局翻译（拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置，或通过 @IgnoreTrans 忽略某个接口\n\n--- #################### RPC 远程调用相关配置 ####################\n\n--- #################### 消息队列相关 ####################\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  # Producer 配置项\n  producer:\n    group: ${spring.application.name}_PRODUCER # 生产者分组\n\nspring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring.json.trusted.packages: '*'\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    executor:\n      appname: ${spring.application.name} # 执行器 AppName\n      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径\n    accessToken: default_token # 执行器通讯TOKEN\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0\n    base-package: cn.iocoder.yudao.module.infra\n  web:\n    admin-ui:\n      url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址\n  xss:\n    enable: false\n    exclude-urls: # 如下两个 url，仅仅是为了演示，去掉配置也没关系\n      - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求\n      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求\n  websocket:\n    enable: true # websocket的开关\n    path: /infra/ws # 路径\n    sender-type: local # 消息发送的类型，可选值为 local、redis、rocketmq、kafka、rabbitmq\n    sender-rocketmq:\n      topic: ${spring.application.name}-websocket # 消息发送的 RocketMQ Topic\n      consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 RocketMQ Consumer Group\n    sender-rabbitmq:\n      exchange: ${spring.application.name}-websocket-exchange # 消息发送的 RabbitMQ Exchange\n      queue: ${spring.application.name}-websocket-queue # 消息发送的 RabbitMQ Queue\n    sender-kafka:\n      topic: ${spring.application.name}-websocket # 消息发送的 Kafka Topic\n      consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 Kafka Consumer Group\n  swagger:\n    title: 管理后台\n    description: 提供管理员管理的所有功能\n    version: ${yudao.info.version}\n  codegen:\n    base-package: cn.iocoder.yudao\n    db-schemas: ${spring.datasource.dynamic.datasource.master.name}\n    front-type: 20 # 前端模版的类型，参见 CodegenFrontTypeEnum 枚举类\n    vo-type: 10 # VO 的类型，参见 CodegenVOTypeEnum 枚举类\n    delete-batch-enable: true # 是否生成批量删除接口\n    unit-test-enable: false # 是否生成单元测试\n  tenant: # 多租户相关配置项\n    enable: true\n    ignore-urls:\n    ignore-tables:\n\ndebug: false\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/controller/controller.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName};\n\nimport org.springframework.web.bind.annotation.*;\nimport ${jakartaPackage}.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\n#if ($sceneEnum.scene == 1)import org.springframework.security.access.prepost.PreAuthorize;#end\n\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport ${jakartaPackage}.validation.constraints.*;\nimport ${jakartaPackage}.validation.*;\nimport ${jakartaPackage}.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport ${PageParamClassName};\nimport ${PageResultClassName};\nimport ${CommonResultClassName};\nimport ${BeanUtils};\nimport static ${CommonResultClassName}.success;\n\nimport ${ExcelUtilsClassName};\n\nimport ${ApiAccessLogClassName};\nimport static ${OperateTypeEnumClassName}.*;\n\nimport ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;\nimport ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\nimport ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;\n#end\nimport ${basePackage}.module.${table.moduleName}.service.${table.businessName}.${table.className}Service;\n\n@Tag(name = \"${sceneEnum.name} - ${table.classComment}\")\n@RestController\n##二级的 businessName 暂时不算在 HTTP 路径上，可以根据需要写\n@RequestMapping(\"/${table.moduleName}/${simpleClassName_strikeCase}\")\n@Validated\npublic class ${sceneEnum.prefixClass}${table.className}Controller {\n\n    @Resource\n    private ${table.className}Service ${classNameVar}Service;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建${table.classComment}\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:create')\")\n#end\n    public CommonResult<${primaryColumn.javaType}> create${simpleClassName}(@Valid @RequestBody ${saveReqVOClass} ${saveReqVOVar}) {\n        return success(${classNameVar}Service.create${simpleClassName}(${saveReqVOVar}));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新${table.classComment}\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:update')\")\n#end\n    public CommonResult<Boolean> update${simpleClassName}(@Valid @RequestBody ${updateReqVOClass} ${updateReqVOVar}) {\n        ${classNameVar}Service.update${simpleClassName}(${updateReqVOVar});\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除${table.classComment}\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:delete')\")\n#end\n    public CommonResult<Boolean> delete${simpleClassName}(@RequestParam(\"id\") ${primaryColumn.javaType} id) {\n        ${classNameVar}Service.delete${simpleClassName}(id);\n        return success(true);\n    }\n\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n    @DeleteMapping(\"/delete-list\")\n    @Parameter(name = \"ids\", description = \"编号\", required = true)\n    @Operation(summary = \"批量删除${table.classComment}\")\n    #if ($sceneEnum.scene == 1)\n                @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:delete')\")\n    #end\n    public CommonResult<Boolean> delete${simpleClassName}List(@RequestParam(\"ids\") List<${primaryColumn.javaType}> ids) {\n        ${classNameVar}Service.delete${simpleClassName}ListByIds(ids);\n        return success(true);\n    }\n#end\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得${table.classComment}\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:query')\")\n#end\n    public CommonResult<${respVOClass}> get${simpleClassName}(@RequestParam(\"id\") ${primaryColumn.javaType} id) {\n        ${table.className}DO ${classNameVar} = ${classNameVar}Service.get${simpleClassName}(id);\n#if ($voType == 10)\n        return success(BeanUtils.toBean(${classNameVar}, ${respVOClass}.class));\n#else\n        return success(${classNameVar});\n#end\n    }\n\n#if ( $table.templateType != 2 )\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得${table.classComment}分页\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:query')\")\n#end\n    public CommonResult<PageResult<${respVOClass}>> get${simpleClassName}Page(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) {\n        PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(pageReqVO);\n#if ($voType == 10)\n        return success(BeanUtils.toBean(pageResult, ${respVOClass}.class));\n#else\n        return success(pageResult);\n#end\n    }\n\n## 特殊：树表专属逻辑（树不需要分页接口）\n#else\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得${table.classComment}列表\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:query')\")\n#end\n    public CommonResult<List<${respVOClass}>> get${simpleClassName}List(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) {\n        List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO);\n#if ($voType == 10)\n        return success(BeanUtils.toBean(list, ${respVOClass}.class));\n#else\n\t    return success(list);\n#end\n    }\n\n#end\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出${table.classComment} Excel\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:export')\")\n#end\n    @ApiAccessLog(operateType = EXPORT)\n#if ( $table.templateType != 2 )\n    public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}Page(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"${table.classComment}.xls\", \"数据\", ${respVOClass}.class,\n                        BeanUtils.toBean(list, ${respVOClass}.class));\n    }\n## 特殊：树表专属逻辑（树不需要分页接口）\n#else\n    public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO,\n              HttpServletResponse response) throws IOException {\n        List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO);\n        // 导出 Excel\n        ExcelUtils.write(response, \"${table.classComment}.xls\", \"数据\", ${table.className}RespVO.class,\n                        BeanUtils.toBean(list, ${table.className}RespVO.class));\n    }\n#end\n\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n#set ($subSimpleClassName = $subSimpleClassNames.get($index))\n#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段\n#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n#set ($subClassNameVar = $subClassNameVars.get($index))\n    // ==================== 子表（$subTable.classComment） ====================\n\n## 情况一：MASTER_ERP 时，需要分查询页子表\n#if ( $table.templateType == 11 )\n    @GetMapping(\"/${subSimpleClassName_strikeCase}/page\")\n    @Operation(summary = \"获得${subTable.classComment}分页\")\n    @Parameter(name = \"${subJoinColumn.javaField}\", description = \"${subJoinColumn.columnComment}\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:query')\")\n#end\n    public CommonResult<PageResult<${subTable.className}DO>> get${subSimpleClassName}Page(PageParam pageReqVO,\n                                                                                        @RequestParam(\"${subJoinColumn.javaField}\") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return success(${classNameVar}Service.get${subSimpleClassName}Page(pageReqVO, ${subJoinColumn.javaField}));\n    }\n\n## 情况二：非 MASTER_ERP 时，需要列表查询子表\n#else\n    #if ( $subTable.subJoinMany )\n    @GetMapping(\"/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}\")\n    @Operation(summary = \"获得${subTable.classComment}列表\")\n    @Parameter(name = \"${subJoinColumn.javaField}\", description = \"${subJoinColumn.columnComment}\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:query')\")\n#end\n    public CommonResult<List<${subTable.className}DO>> get${subSimpleClassName}ListBy${SubJoinColumnName}(@RequestParam(\"${subJoinColumn.javaField}\") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return success(${classNameVar}Service.get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}));\n    }\n\n    #else\n    @GetMapping(\"/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}\")\n    @Operation(summary = \"获得${subTable.classComment}\")\n    @Parameter(name = \"${subJoinColumn.javaField}\", description = \"${subJoinColumn.columnComment}\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:query')\")\n#end\n    public CommonResult<${subTable.className}DO> get${subSimpleClassName}By${SubJoinColumnName}(@RequestParam(\"${subJoinColumn.javaField}\") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return success(${classNameVar}Service.get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}));\n    }\n\n    #end\n#end\n## 特殊：MASTER_ERP 时，支持单个的新增、修改、删除操作\n#if ( $table.templateType == 11 )\n    @PostMapping(\"/${subSimpleClassName_strikeCase}/create\")\n    @Operation(summary = \"创建${subTable.classComment}\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:create')\")\n#end\n    public CommonResult<${subPrimaryColumn.javaType}> create${subSimpleClassName}(@Valid @RequestBody ${subTable.className}DO ${subClassNameVar}) {\n        return success(${classNameVar}Service.create${subSimpleClassName}(${subClassNameVar}));\n    }\n\n    @PutMapping(\"/${subSimpleClassName_strikeCase}/update\")\n    @Operation(summary = \"更新${subTable.classComment}\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:update')\")\n#end\n    public CommonResult<Boolean> update${subSimpleClassName}(@Valid @RequestBody ${subTable.className}DO ${subClassNameVar}) {\n        ${classNameVar}Service.update${subSimpleClassName}(${subClassNameVar});\n        return success(true);\n    }\n\n    @DeleteMapping(\"/${subSimpleClassName_strikeCase}/delete\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @Operation(summary = \"删除${subTable.classComment}\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:delete')\")\n#end\n    public CommonResult<Boolean> delete${subSimpleClassName}(@RequestParam(\"id\") ${subPrimaryColumn.javaType} id) {\n        ${classNameVar}Service.delete${subSimpleClassName}(id);\n        return success(true);\n    }\n\n#if ($deleteBatchEnable)\n    @DeleteMapping(\"/${subSimpleClassName_strikeCase}/delete-list\")\n    @Parameter(name = \"ids\", description = \"编号\", required = true)\n    @Operation(summary = \"批量删除${subTable.classComment}\")\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:delete')\")\n#end\n    public CommonResult<Boolean> delete${subSimpleClassName}List(@RequestParam(\"ids\") List<${subPrimaryColumn.javaType}> ids) {\n        ${classNameVar}Service.delete${subSimpleClassName}ListByIds(ids);\n        return success(true);\n    }\n#end\n\n\t@GetMapping(\"/${subSimpleClassName_strikeCase}/get\")\n\t@Operation(summary = \"获得${subTable.classComment}\")\n\t@Parameter(name = \"id\", description = \"编号\", required = true)\n#if ($sceneEnum.scene == 1)\n    @PreAuthorize(\"@ss.hasPermission('${permissionPrefix}:query')\")\n#end\n\tpublic CommonResult<${subTable.className}DO> get${subSimpleClassName}(@RequestParam(\"id\") ${subPrimaryColumn.javaType} id) {\n\t    return success(${classNameVar}Service.get${subSimpleClassName}(id));\n\t}\n\n#end\n#end\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/controller/vo/listReqVO.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport ${PageParamClassName};\n#foreach ($column in $columns)\n#if (${column.javaType} == \"BigDecimal\")\nimport java.math.BigDecimal;\n#break\n#end\n#end\n## 处理 LocalDateTime 字段的引入\n#foreach ($column in $columns)\n#if (${column.listOperation} && ${column.javaType} == \"LocalDateTime\")\nimport java.time.LocalDateTime;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n#break\n#end\n#end\n## 字段模板\n#macro(columnTpl $prefix $prefixStr)\n    @Schema(description = \"${prefixStr}${column.columnComment}\"#if (\"$!column.example\" != \"\"), example = \"${column.example}\"#end)\n    private ${column.javaType}#if (\"$!prefix\" != \"\") ${prefix}${JavaField}#else ${column.javaField}#end;\n#end\n\n@Schema(description = \"${sceneEnum.name} - ${table.classComment}列表 Request VO\")\n@Data\npublic class ${sceneEnum.prefixClass}${table.className}ListReqVO {\n\n#foreach ($column in $columns)\n#if (${column.listOperation})##查询操作\n#if (${column.listOperationCondition} == \"BETWEEN\")## 情况一，Between 的时候\n    @Schema(description = \"${column.columnComment}\"#if (\"$!column.example\" != \"\"), example = \"${column.example}\"#end)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private ${column.javaType}[] ${column.javaField};\n#else##情况二，非 Between 的时间\n    #columnTpl('', '')\n#end\n\n#end\n#end\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/controller/vo/pageReqVO.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport ${PageParamClassName};\n#foreach ($column in $columns)\n#if (${column.javaType} == \"BigDecimal\")\nimport java.math.BigDecimal;\n#break\n#end\n#end\n## 处理 LocalDateTime 字段的引入\n#foreach ($column in $columns)\n#if (${column.listOperationCondition} && ${column.javaType} == \"LocalDateTime\")\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n#break\n#end\n#end\n## 字段模板\n#macro(columnTpl $prefix $prefixStr)\n    @Schema(description = \"${prefixStr}${column.columnComment}\"#if (\"$!column.example\" != \"\"), example = \"${column.example}\"#end)\n    private ${column.javaType}#if (\"$!prefix\" != \"\") ${prefix}${JavaField}#else ${column.javaField}#end;\n#end\n\n@Schema(description = \"${sceneEnum.name} - ${table.classComment}分页 Request VO\")\n@Data\npublic class ${sceneEnum.prefixClass}${table.className}PageReqVO extends PageParam {\n\n#foreach ($column in $columns)\n#if (${column.listOperation})##查询操作\n#if (${column.listOperationCondition} == \"BETWEEN\")## 情况一，Between 的时候\n    @Schema(description = \"${column.columnComment}\"#if (\"$!column.example\" != \"\"), example = \"${column.example}\"#end)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private ${column.javaType}[] ${column.javaField};\n#else##情况二，非 Between 的时间\n    #columnTpl('', '')\n#end\n\n#end\n#end\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/controller/vo/respVO.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\n## 处理 BigDecimal 字段的引入\n#foreach ($column in $columns)\n#if (${column.javaType} == \"BigDecimal\")\nimport java.math.BigDecimal;\n#break\n#end\n#end\n## 处理 LocalDateTime 字段的引入\n#foreach ($column in $columns)\n#if (${column.listOperationResult} && ${column.javaType} == \"LocalDateTime\")\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n#break\n#end\n#end\n## 处理 Excel 导出\nimport cn.idev.excel.annotation.*;\n#foreach ($column in $columns)\n#if (\"$!column.dictType\" != \"\")## 有设置数据字典\nimport ${DictFormatClassName};\nimport ${DictConvertClassName};\n#break\n#end\n#end\n\n@Schema(description = \"${sceneEnum.name} - ${table.classComment} Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ${sceneEnum.prefixClass}${table.className}RespVO {\n\n## 逐个处理字段\n#foreach ($column in $columns)\n#if (${column.listOperationResult})\n## 1. 处理 Swagger 注解\n    @Schema(description = \"${column.columnComment}\"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if (\"$!column.example\" != \"\"), example = \"${column.example}\"#end)\n## 2. 处理 Excel 导出\n#if (\"$!column.dictType\" != \"\")##处理枚举值\n    @ExcelProperty(value = \"${column.columnComment}\", converter = DictConvert.class)\n    @DictFormat(\"${column.dictType}\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n#else\n    @ExcelProperty(\"${column.columnComment}\")\n#end\n## 3. 处理字段定义\n    private ${column.javaType} ${column.javaField};\n\n#end\n#end\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/controller/vo/saveReqVO.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport ${jakartaPackage}.validation.constraints.*;\n## 处理 BigDecimal 字段的引入\n#foreach ($column in $columns)\n#if (${column.javaType} == \"BigDecimal\")\nimport java.math.BigDecimal;\n#break\n#end\n#end\n## 处理 LocalDateTime 字段的引入\n#foreach ($column in $columns)\n#if ((${column.createOperation} || ${column.updateOperation}) && ${column.javaType} == \"LocalDateTime\")\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n#break\n#end\n#end\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\nimport ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;\n#end\n\n@Schema(description = \"${sceneEnum.name} - ${table.classComment}新增/修改 Request VO\")\n@Data\npublic class ${sceneEnum.prefixClass}${table.className}SaveReqVO {\n\n## 逐个处理字段\n#foreach ($column in $columns)\n#if (${column.createOperation} || ${column.updateOperation})\n## 1. 处理 Swagger 注解\n    @Schema(description = \"${column.columnComment}\"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if (\"$!column.example\" != \"\"), example = \"${column.example}\"#end)\n## 2. 处理 Validator 参数校验\n#if (!${column.nullable} && !${column.primaryKey})\n#if (${column.javaType} == 'String')\n    @NotEmpty(message = \"${column.columnComment}不能为空\")\n#else\n    @NotNull(message = \"${column.columnComment}不能为空\")\n#end\n#end\n## 3. 处理字段定义\n    private ${column.javaType} ${column.javaField};\n\n#end\n#end\n## 特殊：主子表专属逻辑（非 ERP 模式）\n#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n    #if ( $subTable.subJoinMany)\n    @Schema(description = \"${subTable.classComment}列表\")\n    private List<${subTable.className}DO> ${subClassNameVars.get($index)}s;\n\n    #else\n    @Schema(description = \"${subTable.classComment}\")\n    private ${subTable.className}DO ${subClassNameVars.get($index)};\n\n    #end\n#end\n#end\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/dal/do.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName};\n\nimport lombok.*;\nimport java.util.*;\n#foreach ($column in $columns)\n#if (${column.javaType} == \"BigDecimal\")\nimport java.math.BigDecimal;\n#end\n#if (${column.javaType} == \"LocalDateTime\")\nimport java.time.LocalDateTime;\n#end\n#end\nimport com.baomidou.mybatisplus.annotation.*;\nimport ${BaseDOClassName};\n## 处理 Excel 导出 + Schema 注解（仅 DO 模式）\n#if ($voType == 20)\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.idev.excel.annotation.*;\n#foreach ($column in $columns)\n    #if (\"$!column.dictType\" != \"\")## 有设置数据字典\n\t\timport ${DictFormatClassName};\n\t\timport ${DictConvertClassName};\n        #break\n    #end\n#end\n#end\n\n/**\n * ${table.classComment} DO\n *\n * @author ${table.author}\n */\n@TableName(\"${table.tableName.toLowerCase()}\")\n@KeySequence(\"${table.tableName.toLowerCase()}_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n## 处理 Excel 导出 + Schema 注解（仅 DO 模式）\n#if ($voType == 20)\n@Schema(description = \"${sceneEnum.name} - ${table.classComment} Response VO\")\n@ExcelIgnoreUnannotated\n#end\npublic class ${table.className}DO extends BaseDO {\n\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n    public static final Long ${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT = 0L;\n\n#end\n#foreach ($column in $columns)\n#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段\n    /**\n     * ${column.columnComment}\n    #if (\"$!column.dictType\" != \"\")##处理枚举值\n     *\n     * 枚举 {@link TODO ${column.dictType} 对应的类}\n    #end\n     */\n    #if (${column.primaryKey})##处理主键\n    @TableId#if (${column.javaType} == 'String')(type = IdType.INPUT)#end\n    #end\n#if ($voType == 20)\n## 1. 处理 Swagger 注解\n    @Schema(description = \"${column.columnComment}\"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if (\"$!column.example\" != \"\"), example = \"${column.example}\"#end)\n## 2. 处理 Excel 导出\n#if (\"$!column.dictType\" != \"\")##处理枚举值\n    @ExcelProperty(value = \"${column.columnComment}\", converter = DictConvert.class)\n    @DictFormat(\"${column.dictType}\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n#else\n    @ExcelProperty(\"${column.columnComment}\")\n#end\n#end\n## 3. 处理字段定义\n    private ${column.javaType} ${column.javaField};\n#end\n#end\n\n## 特殊：主子表专属逻辑（非 ERP 模式）\n#if ( $voType == 20 && $subTables && $subTables.size() > 0 && $table.templateType != 11 )\n    #foreach ($subTable in $subTables)\n        #set ($index = $foreach.count - 1)\n        #if ( $subTable.subJoinMany)\n    /**\n    * ${subTable.classComment}列表\n    */\n    @Schema(description = \"${subTable.classComment}列表\")\n    @TableField(exist = false)\n    private List<${subTable.className}DO> ${subClassNameVars.get($index)}s;\n        #else\n    /**\n    * ${subTable.classComment}\n    */\n    @Schema(description = \"${subTable.classComment}\")\n\t@TableField(exist = false)\n    private ${subTable.className}DO ${subClassNameVars.get($index)};\n        #end\n    #end\n#end\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/dal/do_sub.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\npackage ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName};\n\nimport lombok.*;\nimport java.util.*;\n#foreach ($column in $subColumns)\n#if (${column.javaType} == \"BigDecimal\")\nimport java.math.BigDecimal;\n#end\n#if (${column.javaType} == \"LocalDateTime\")\nimport java.time.LocalDateTime;\n#end\n#end\nimport com.baomidou.mybatisplus.annotation.*;\nimport ${BaseDOClassName};\n## 处理 Schema 注解（仅 DO 模式）\n#if ($voType == 20)\nimport io.swagger.v3.oas.annotations.media.Schema;\n#foreach ($column in $columns)\n    #if (\"$!column.dictType\" != \"\")## 有设置数据字典\n        import ${DictFormatClassName};\n        import ${DictConvertClassName};\n        #break\n    #end\n#end\n#end\n\n/**\n * ${subTable.classComment} DO\n *\n * @author ${subTable.author}\n */\n@TableName(\"${subTable.tableName.toLowerCase()}\")\n@KeySequence(\"${subTable.tableName.toLowerCase()}_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n## 处理 Schema 注解（仅 DO 模式）\n#if ($voType == 20)\n@Schema(description = \"${sceneEnum.name} - ${table.classComment} Response VO\")\n#end\npublic class ${subTable.className}DO extends BaseDO {\n\n#foreach ($column in $subColumns)\n#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段\n    /**\n     * ${column.columnComment}\n    #if (\"$!column.dictType\" != \"\")##处理枚举值\n     *\n     * 枚举 {@link TODO ${column.dictType} 对应的类}\n    #end\n     */\n    #if (${column.primaryKey})##处理主键\n    @TableId#if (${column.javaType} == 'String')(type = IdType.INPUT)#end\n    #end\n#if ($voType == 20)\n## 1. 处理 Swagger 注解\n    @Schema(description = \"${column.columnComment}\"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if (\"$!column.example\" != \"\"), example = \"${column.example}\"#end)\n#end\n## 2. 处理字段定义\n    private ${column.javaType} ${column.javaField};\n#end\n#end\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/dal/mapper.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName};\n\nimport java.util.*;\n\nimport ${PageResultClassName};\nimport ${QueryWrapperClassName};\nimport ${BaseMapperClassName};\nimport ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;\nimport org.apache.ibatis.annotations.Mapper;\nimport ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;\n\n## 字段模板\n#macro(listCondition)\n#foreach ($column in $columns)\n#if (${column.listOperation})\n#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写\n#if (${column.listOperationCondition} == \"=\")##情况一，= 的时候\n                .eqIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())\n#end\n#if (${column.listOperationCondition} == \"!=\")##情况二，!= 的时候\n                .neIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())\n#end\n#if (${column.listOperationCondition} == \">\")##情况三，> 的时候\n                .gtIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())\n#end\n#if (${column.listOperationCondition} == \">=\")##情况四，>= 的时候\n                .geIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())\n#end\n#if (${column.listOperationCondition} == \"<\")##情况五，< 的时候\n                .ltIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())\n#end\n#if (${column.listOperationCondition} == \"<=\")##情况五，<= 的时候\n                .leIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())\n#end\n#if (${column.listOperationCondition} == \"LIKE\")##情况七，Like 的时候\n                .likeIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())\n#end\n#if (${column.listOperationCondition} == \"BETWEEN\")##情况八，Between 的时候\n                .betweenIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())\n#end\n#end\n#end\n#end\n/**\n * ${table.classComment} Mapper\n *\n * @author ${table.author}\n */\n@Mapper\npublic interface ${table.className}Mapper extends BaseMapperX<${table.className}DO> {\n\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\n    default PageResult<${table.className}DO> selectPage(${sceneEnum.prefixClass}${table.className}PageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<${table.className}DO>()\n\t\t\t#listCondition()\n                .orderByDesc(${table.className}DO::getId));## 大多数情况下，id 倒序\n\n    }\n#else\n    default List<${table.className}DO> selectList(${sceneEnum.prefixClass}${table.className}ListReqVO reqVO) {\n        return selectList(new LambdaQueryWrapperX<${table.className}DO>()\n\t\t\t#listCondition()\n                .orderByDesc(${table.className}DO::getId));## 大多数情况下，id 倒序\n\n    }\n#end\n\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写\n#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写\n\tdefault ${table.className}DO selectBy${TreeParentJavaField}And${TreeNameJavaField}(Long ${treeParentColumn.javaField}, String ${treeNameColumn.javaField}) {\n\t    return selectOne(${table.className}DO::get${TreeParentJavaField}, ${treeParentColumn.javaField}, ${table.className}DO::get${TreeNameJavaField}, ${treeNameColumn.javaField});\n\t}\n\n    default Long selectCountBy${TreeParentJavaField}(${treeParentColumn.javaType} ${treeParentColumn.javaField}) {\n        return selectCount(${table.className}DO::get${TreeParentJavaField}, ${treeParentColumn.javaField});\n    }\n\n#end\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/dal/mapper.xml.vm",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/dal/mapper_sub.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subJoinColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\npackage ${basePackage}.module.${subTable.moduleName}.dal.mysql.${subTable.businessName};\n\nimport java.util.*;\n\nimport ${PageResultClassName};\nimport ${PageParamClassName};\nimport ${QueryWrapperClassName};\nimport ${BaseMapperClassName};\nimport ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * ${subTable.classComment} Mapper\n *\n * @author ${subTable.author}\n */\n@Mapper\npublic interface ${subTable.className}Mapper extends BaseMapperX<${subTable.className}DO> {\n\n## 情况一：MASTER_ERP 时，需要分查询页子表\n#if ( $table.templateType == 11 )\n    default PageResult<${subTable.className}DO> selectPage(PageParam reqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<${subTable.className}DO>()\n            .eq(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField})\n            .orderByDesc(${subTable.className}DO::getId));## 大多数情况下，id 倒序\n\n    }\n## 主表与子表是一对一时 \n    #if (!$subTable.subJoinMany)\n        default ${subTable.className}DO selectBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return selectOne(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});\n        }\n    #end\n\n## 情况二：非 MASTER_ERP 时，需要列表查询子表\n#else\n    #if ( $subTable.subJoinMany)\n    default List<${subTable.className}DO> selectListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return selectList(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});\n    }\n\n    #else\n    default ${subTable.className}DO selectBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return selectOne(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});\n    }\n\n    #end\n    #end\n    default int deleteBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return delete(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});\n    }\n\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n\tdefault int deleteBy${SubJoinColumnName}s(List<${subJoinColumn.javaType}> ${subJoinColumn.javaField}s) {\n\t    return deleteBatch(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField}s);\n\t}\n#end\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/enums/errorcode.vm",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-${table.moduleName} 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\n// ========== ${table.classComment} TODO 补充编号 ==========\nErrorCode ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"${table.classComment}不存在\");\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\nErrorCode ${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, \"存在存在子${table.classComment}，无法删除\");\nErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,\"父级${table.classComment}不存在\");\nErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_ERROR = new ErrorCode(TODO 补充编号, \"不能设置自己为父${table.classComment}\");\nErrorCode ${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE = new ErrorCode(TODO 补充编号, \"已经存在该${treeNameColumn.columnComment}的${table.classComment}\");\nErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, \"不能设置自己的子${table.className}为父${table.className}\");\n#end\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 11 )## 特殊：ERP 情况\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n#set ($simpleClassNameUnderlineCase = $simpleClassNameUnderlineCases.get($index))\nErrorCode ${simpleClassNameUnderlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"${subTable.classComment}不存在\");\n#if ( !$subTable.subJoinMany )\nErrorCode ${simpleClassNameUnderlineCase.toUpperCase()}_EXISTS = new ErrorCode(TODO 补充编号, \"${subTable.classComment}已存在\");\n#end\n#end\n#end"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/service/service.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.service.${table.businessName};\n\nimport java.util.*;\nimport ${jakartaPackage}.validation.*;\nimport ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;\nimport ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\nimport ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;\n#end\nimport ${PageResultClassName};\nimport ${PageParamClassName};\n\n/**\n * ${table.classComment} Service 接口\n *\n * @author ${table.author}\n */\npublic interface ${table.className}Service {\n\n    /**\n     * 创建${table.classComment}\n     *\n     * @param ${saveReqVOVar} 创建信息\n     * @return 编号\n     */\n    ${primaryColumn.javaType} create${simpleClassName}(@Valid ${saveReqVOClass} ${saveReqVOVar});\n\n    /**\n     * 更新${table.classComment}\n     *\n     * @param ${updateReqVOVar} 更新信息\n     */\n    void update${simpleClassName}(@Valid ${updateReqVOClass} ${updateReqVOVar});\n\n    /**\n     * 删除${table.classComment}\n     *\n     * @param id 编号\n     */\n    void delete${simpleClassName}(${primaryColumn.javaType} id);\n\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n    /**\n    * 批量删除${table.classComment}\n    *\n    * @param ids 编号\n    */\n    void delete${simpleClassName}ListByIds(List<${primaryColumn.javaType}> ids);\n#end\n\n    /**\n     * 获得${table.classComment}\n     *\n     * @param id 编号\n     * @return ${table.classComment}\n     */\n    ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id);\n\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\n    /**\n     * 获得${table.classComment}分页\n     *\n     * @param pageReqVO 分页查询\n     * @return ${table.classComment}分页\n     */\n    PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO);\n#else\n    /**\n     * 获得${table.classComment}列表\n     *\n     * @param listReqVO 查询条件\n     * @return ${table.classComment}列表\n     */\n    List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO);\n#end\n\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n#set ($subSimpleClassName = $subSimpleClassNames.get($index))\n#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段\n#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n#set ($subClassNameVar = $subClassNameVars.get($index))\n    // ==================== 子表（$subTable.classComment） ====================\n\n## 情况一：MASTER_ERP 时，需要分查询页子表\n#if ( $table.templateType == 11 )\n    /**\n     * 获得${subTable.classComment}分页\n     *\n     * @param pageReqVO 分页查询\n     * @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment}\n     * @return ${subTable.classComment}分页\n     */\n    PageResult<${subTable.className}DO> get${subSimpleClassName}Page(PageParam pageReqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField});\n\n## 情况二：非 MASTER_ERP 时，需要列表查询子表\n#else\n    #if ( $subTable.subJoinMany )\n    /**\n     * 获得${subTable.classComment}列表\n     *\n     * @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment}\n     * @return ${subTable.classComment}列表\n     */\n    List<${subTable.className}DO> get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField});\n\n    #else\n    /**\n     * 获得${subTable.classComment}\n     *\n     * @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment}\n     * @return ${subTable.classComment}\n     */\n    ${subTable.className}DO get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField});\n\n    #end\n#end\n## 特殊：MASTER_ERP 时，支持单个的新增、修改、删除操作\n#if ( $table.templateType == 11 )\n    /**\n     * 创建${subTable.classComment}\n     *\n     * @param ${subClassNameVar} 创建信息\n     * @return 编号\n     */\n    ${subPrimaryColumn.javaType} create${subSimpleClassName}(@Valid ${subTable.className}DO ${subClassNameVar});\n\n    /**\n     * 更新${subTable.classComment}\n     *\n     * @param ${subClassNameVar} 更新信息\n     */\n    void update${subSimpleClassName}(@Valid ${subTable.className}DO ${subClassNameVar});\n\n    /**\n     * 删除${subTable.classComment}\n     *\n     * @param id 编号\n     */\n    void delete${subSimpleClassName}(${subPrimaryColumn.javaType} id);\n\n#if ($deleteBatchEnable)\n    /**\n    * 批量删除${subTable.classComment}\n    *\n    * @param ids 编号\n    */\n    void delete${subSimpleClassName}ListByIds(List<${subPrimaryColumn.javaType}> ids);\n#end\n\n\t/**\n\t * 获得${subTable.classComment}\n\t *\n\t * @param id 编号\n     * @return ${subTable.classComment}\n\t */\n    ${subTable.className}DO get${subSimpleClassName}(${subPrimaryColumn.javaType} id);\n\n#end\n#end\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/service/serviceImpl.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.service.${table.businessName};\n\nimport cn.hutool.core.collection.CollUtil;\nimport org.springframework.stereotype.Service;\nimport ${jakartaPackage}.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;\nimport ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\nimport ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;\n#end\nimport ${PageResultClassName};\nimport ${PageParamClassName};\nimport ${BeanUtils};\n\nimport ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper;\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\nimport ${basePackage}.module.${subTable.moduleName}.dal.mysql.${subTable.businessName}.${subTable.className}Mapper;\n#end\n\nimport static ${ServiceExceptionUtilClassName}.exception;\nimport static ${CollectionUtilsClassName}.convertList;\nimport static ${CollectionUtilsClassName}.diffList;\nimport static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;\n\n/**\n * ${table.classComment} Service 实现类\n *\n * @author ${table.author}\n */\n@Service\n@Validated\npublic class ${table.className}ServiceImpl implements ${table.className}Service {\n\n    @Resource\n    private ${table.className}Mapper ${classNameVar}Mapper;\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n    @Resource\n    private ${subTable.className}Mapper ${subClassNameVars.get($index)}Mapper;\n#end\n\n    @Override\n## 特殊：主子表专属逻辑（非 ERP 模式）\n#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )\n    @Transactional(rollbackFor = Exception.class)\n#end\n    public ${primaryColumn.javaType} create${simpleClassName}(${saveReqVOClass} ${saveReqVOVar}) {\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写\n#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写\n        // 校验${treeParentColumn.columnComment}的有效性\n        validateParent${simpleClassName}(null, ${saveReqVOVar}.get${TreeParentJavaField}());\n        // 校验${treeNameColumn.columnComment}的唯一性\n        validate${simpleClassName}${TreeNameJavaField}Unique(null, ${saveReqVOVar}.get${TreeParentJavaField}(), ${saveReqVOVar}.get${TreeNameJavaField}());\n\n#end\n        // 插入\n#if ($voType == 10)\n        ${table.className}DO ${classNameVar} = BeanUtils.toBean(createReqVO, ${table.className}DO.class);\n        ${classNameVar}Mapper.insert(${classNameVar});\n#else\n        ${saveReqVOVar}.clean() // 清理掉创建、更新时间等相关属性值\n        ${classNameVar}Mapper.insert(${saveReqVOVar});\n#end\n\n## 特殊：主子表专属逻辑（非 ERP 模式）\n#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )\n\n        // 插入子表\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n#set ($subSimpleClassName = $subSimpleClassNames.get($index))\n#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n    #if ( $subTable.subJoinMany)\n        create${subSimpleClassName}List(${classNameVar}.getId(), ${saveReqVOVar}.get${subSimpleClassNames.get($index)}s());\n    #else\n        create${subSimpleClassName}(${classNameVar}.getId(), ${saveReqVOVar}.get${subSimpleClassNames.get($index)}());\n    #end\n#end\n#end\n        // 返回\n        return ${classNameVar}.getId();\n    }\n\n    @Override\n## 特殊：主子表专属逻辑（非 ERP 模式）\n#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )\n    @Transactional(rollbackFor = Exception.class)\n#end\n    public void update${simpleClassName}(${updateReqVOClass} ${updateReqVOVar}) {\n        // 校验存在\n        validate${simpleClassName}Exists(${updateReqVOVar}.getId());\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写\n#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写\n        // 校验${treeParentColumn.columnComment}的有效性\n        validateParent${simpleClassName}(${updateReqVOVar}.getId(), ${updateReqVOVar}.get${TreeParentJavaField}());\n        // 校验${treeNameColumn.columnComment}的唯一性\n        validate${simpleClassName}${TreeNameJavaField}Unique(${updateReqVOVar}.getId(), ${updateReqVOVar}.get${TreeParentJavaField}(), ${updateReqVOVar}.get${TreeNameJavaField}());\n\n#end\n        // 更新\n#if ($voType == 10)\n        ${table.className}DO updateObj = BeanUtils.toBean(updateReqVO, ${table.className}DO.class);\n        ${classNameVar}Mapper.updateById(updateObj);\n#else\n        ${updateReqVOVar}.clean() // 清理掉创建、更新时间等相关属性值\n        ${classNameVar}Mapper.updateById(${updateReqVOVar});\n#end\n## 特殊：主子表专属逻辑（非 ERP 模式）\n#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11)\n\n        // 更新子表\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n#set ($subSimpleClassName = $subSimpleClassNames.get($index))\n#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n    #if ( $subTable.subJoinMany)\n        update${subSimpleClassName}List(${updateReqVOVar}.getId(), ${updateReqVOVar}.get${subSimpleClassNames.get($index)}s());\n    #else\n        update${subSimpleClassName}(${updateReqVOVar}.getId(), ${updateReqVOVar}.get${subSimpleClassNames.get($index)}());\n    #end\n#end\n#end\n    }\n\n    @Override\n## 特殊：主子表专属逻辑\n#if ( $subTables && $subTables.size() > 0)\n    @Transactional(rollbackFor = Exception.class)\n#end\n    public void delete${simpleClassName}(${primaryColumn.javaType} id) {\n        // 校验存在\n        validate${simpleClassName}Exists(id);\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n#set ($ParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写\n        // 校验是否有子${table.classComment}\n        if (${classNameVar}Mapper.selectCountBy${ParentJavaField}(id) > 0) {\n            throw exception(${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN);\n        }\n#end\n        // 删除\n        ${classNameVar}Mapper.deleteById(id);\n## 特殊：主子表专属逻辑\n#if ( $subTables && $subTables.size() > 0)\n\n        // 删除子表\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n#set ($subSimpleClassName = $subSimpleClassNames.get($index))\n#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n        delete${subSimpleClassName}By${SubJoinColumnName}(id);\n#end\n#end\n    }\n\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n    @Override\n    ## 特殊：主子表专属逻辑\n    #if ( $subTables && $subTables.size() > 0)\n    @Transactional(rollbackFor = Exception.class)\n    #end\n    public void delete${simpleClassName}ListByIds(List<${primaryColumn.javaType}> ids) {\n        // 删除\n        ${classNameVar}Mapper.deleteByIds(ids);\n    ## 特殊：主子表专属逻辑\n    #if ( $subTables && $subTables.size() > 0)\n\n    // 删除子表\n        #foreach ($subTable in $subTables)\n            #set ($index = $foreach.count - 1)\n            #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n            #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n            #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n            delete${subSimpleClassName}By${SubJoinColumnName}s(ids);\n        #end\n    #end\n    }\n\n#end\n\n    private void validate${simpleClassName}Exists(${primaryColumn.javaType} id) {\n        if (${classNameVar}Mapper.selectById(id) == null) {\n            throw exception(${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);\n        }\n    }\n\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写\n#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写\n    private void validateParent${simpleClassName}(Long id, Long ${treeParentColumn.javaField}) {\n        if (${treeParentColumn.javaField} == null || ${simpleClassName}DO.${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT.equals(${treeParentColumn.javaField})) {\n            return;\n        }\n        // 1. 不能设置自己为父${table.classComment}\n        if (Objects.equals(id, ${treeParentColumn.javaField})) {\n            throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_ERROR);\n        }\n        // 2. 父${table.classComment}不存在\n        ${simpleClassName}DO parent${simpleClassName} = ${classNameVar}Mapper.selectById(${treeParentColumn.javaField});\n        if (parent${simpleClassName} == null) {\n            throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_NOT_EXITS);\n        }\n        // 3. 递归校验父${table.classComment}，如果父${table.classComment}是自己的子${table.classComment}，则报错，避免形成环路\n        if (id == null) { // id 为空，说明新增，不需要考虑环路\n            return;\n        }\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            // 3.1 校验环路\n            ${treeParentColumn.javaField} = parent${simpleClassName}.get${TreeParentJavaField}();\n            if (Objects.equals(id, ${treeParentColumn.javaField})) {\n                throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_IS_CHILD);\n            }\n            // 3.2 继续递归下一级父${table.classComment}\n            if (${treeParentColumn.javaField} == null || ${simpleClassName}DO.${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT.equals(${treeParentColumn.javaField})) {\n                break;\n            }\n            parent${simpleClassName} = ${classNameVar}Mapper.selectById(${treeParentColumn.javaField});\n            if (parent${simpleClassName} == null) {\n                break;\n            }\n        }\n    }\n\n    private void validate${simpleClassName}${TreeNameJavaField}Unique(Long id, Long ${treeParentColumn.javaField}, String ${treeNameColumn.javaField}) {\n        ${simpleClassName}DO ${classNameVar} = ${classNameVar}Mapper.selectBy${TreeParentJavaField}And${TreeNameJavaField}(${treeParentColumn.javaField}, ${treeNameColumn.javaField});\n        if (${classNameVar} == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的${table.classComment}\n        if (id == null) {\n            throw exception(${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE);\n        }\n        if (!Objects.equals(${classNameVar}.getId(), id)) {\n            throw exception(${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE);\n        }\n    }\n\n#end\n    @Override\n    public ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id) {\n        return ${classNameVar}Mapper.selectById(id);\n    }\n\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\n    @Override\n    public PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) {\n        return ${classNameVar}Mapper.selectPage(pageReqVO);\n    }\n#else\n    @Override\n    public List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) {\n        return ${classNameVar}Mapper.selectList(listReqVO);\n    }\n#end\n\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n#set ($subSimpleClassName = $subSimpleClassNames.get($index))\n#set ($simpleClassNameUnderlineCase = $simpleClassNameUnderlineCases.get($index))\n#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段\n#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n#set ($subClassNameVar = $subClassNameVars.get($index))\n    // ==================== 子表（$subTable.classComment） ====================\n\n## 情况一：MASTER_ERP 时，需要分查询页子表\n#if ( $table.templateType == 11 )\n    @Override\n    public PageResult<${subTable.className}DO> get${subSimpleClassName}Page(PageParam pageReqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return ${subClassNameVars.get($index)}Mapper.selectPage(pageReqVO, ${subJoinColumn.javaField});\n    }\n\n## 情况二：非 MASTER_ERP 时，需要列表查询子表\n#else\n    #if ( $subTable.subJoinMany )\n    @Override\n    public List<${subTable.className}DO> get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return ${subClassNameVars.get($index)}Mapper.selectListBy${SubJoinColumnName}(${subJoinColumn.javaField});\n    }\n\n    #else\n    @Override\n    public ${subTable.className}DO get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {\n        return ${subClassNameVars.get($index)}Mapper.selectBy${SubJoinColumnName}(${subJoinColumn.javaField});\n    }\n\n    #end\n#end\n## 情况一：MASTER_ERP 时，支持单个的新增、修改、删除操作\n#if ( $table.templateType == 11 )\n    @Override\n    public ${subPrimaryColumn.javaType} create${subSimpleClassName}(${subTable.className}DO ${subClassNameVar}) {\n## 特殊：一对一时，需要保证只有一条，不能重复插入\n#if ( !$subTable.subJoinMany)\n        // 校验是否已经存在\n        if (${subClassNameVars.get($index)}Mapper.selectBy${SubJoinColumnName}(${subClassNameVar}.get${SubJoinColumnName}()) != null) {\n            throw exception(${simpleClassNameUnderlineCase.toUpperCase()}_EXISTS);\n        }\n        // 插入\n#end\n        ${subClassNameVar}.clean(); // 清理掉创建、更新时间等相关属性值\n        ${subClassNameVars.get($index)}Mapper.insert(${subClassNameVar});\n        return ${subClassNameVar}.getId();\n    }\n\n    @Override\n    public void update${subSimpleClassName}(${subTable.className}DO ${subClassNameVar}) {\n        // 校验存在\n        validate${subSimpleClassName}Exists(${subClassNameVar}.getId());\n        // 更新\n        ${subClassNameVar}.clean(); // 解决更新情况下：updateTime 不更新\n        ${subClassNameVars.get($index)}Mapper.updateById(${subClassNameVar});\n    }\n\n    @Override\n    public void delete${subSimpleClassName}(${subPrimaryColumn.javaType} id) {\n        // 删除\n        ${subClassNameVars.get($index)}Mapper.deleteById(id);\n    }\n\n#if ($deleteBatchEnable)\n\t@Override\n\tpublic void delete${subSimpleClassName}ListByIds(List<${subPrimaryColumn.javaType}> ids) {\n        // 删除\n        ${subClassNameVars.get($index)}Mapper.deleteByIds(ids);\n\t}\n#end\n\n    @Override\n    public ${subTable.className}DO get${subSimpleClassName}(${subPrimaryColumn.javaType} id) {\n        return ${subClassNameVars.get($index)}Mapper.selectById(id);\n    }\n\n    private void validate${subSimpleClassName}Exists(${subPrimaryColumn.javaType} id) {\n        if (${subClassNameVar}Mapper.selectById(id) == null) {\n            throw exception(${simpleClassNameUnderlineCase.toUpperCase()}_NOT_EXISTS);\n        }\n    }\n\n## 情况二：非 MASTER_ERP 时，支持批量的新增、修改操作\n#else\n    #if ( $subTable.subJoinMany)\n    private void create${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) {\n        list.forEach(o -> o.set${SubJoinColumnName}(${subJoinColumn.javaField}).clean());\n        ${subClassNameVars.get($index)}Mapper.insertBatch(list);\n    }\n\n    private void update${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) {\n\t    list.forEach(o -> o.set${SubJoinColumnName}(${subJoinColumn.javaField}).clean());\n\t    List<${subTable.className}DO> oldList = ${subClassNameVar}Mapper.selectListBy${SubJoinColumnName}(${subJoinColumn.javaField});\n\t    List<List<${subTable.className}DO>> diffList = diffList(oldList, list, (oldVal, newVal) -> {\n            boolean same = ObjectUtil.equal(oldVal.getId(), newVal.getId());\n            if (same) {\n                newVal.setId(oldVal.getId()).clean(); // 解决更新情况下：updateTime 不更新\n            }\n            return same;\n\t    });\n\n\t    // 第二步，批量添加、修改、删除\n\t    if (CollUtil.isNotEmpty(diffList.get(0))) {\n\t        ${subClassNameVar}Mapper.insertBatch(diffList.get(0));\n\t    }\n\t    if (CollUtil.isNotEmpty(diffList.get(1))) {\n\t        ${subClassNameVar}Mapper.updateBatch(diffList.get(1));\n\t    }\n\t    if (CollUtil.isNotEmpty(diffList.get(2))) {\n\t        ${subClassNameVar}Mapper.deleteByIds(convertList(diffList.get(2), ${subTable.className}DO::getId));\n\t    }\n    }\n\n    #else\n    private void create${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) {\n        if (${subClassNameVar} == null) {\n            return;\n        }\n        ${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField});\n        ${subClassNameVars.get($index)}Mapper.insert(${subClassNameVar});\n    }\n\n    private void update${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) {\n        if (${subClassNameVar} == null) {\n\t\t\treturn;\n        }\n        ${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField}).clean();// 解决更新情况下：updateTime 不更新\n        ${subClassNameVars.get($index)}Mapper.insertOrUpdate(${subClassNameVar});\n    }\n\n    #end\n#end\n    private void delete${subSimpleClassName}By${SubJoinColumnName}(${primaryColumn.javaType} ${subJoinColumn.javaField}) {\n        ${subClassNameVars.get($index)}Mapper.deleteBy${SubJoinColumnName}(${subJoinColumn.javaField});\n    }\n\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n\tprivate void delete${subSimpleClassName}By${SubJoinColumnName}s(List<${primaryColumn.javaType}> ${subJoinColumn.javaField}s) {\n        ${subClassNameVars.get($index)}Mapper.deleteBy${SubJoinColumnName}s(${subJoinColumn.javaField}s);\n\t}\n#end\n\n#end\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/test/serviceTest.vm",
    "content": "package ${basePackage}.module.${table.moduleName}.service.${table.businessName};\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport ${jakartaPackage}.annotation.Resource;\n\nimport ${baseFrameworkPackage}.test.core.ut.BaseDbUnitTest;\n\nimport ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;\nimport ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;\nimport ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper;\nimport ${PageResultClassName};\n\nimport ${jakartaPackage}.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;\nimport static ${baseFrameworkPackage}.test.core.util.AssertUtils.*;\nimport static ${baseFrameworkPackage}.test.core.util.RandomUtils.*;\nimport static ${LocalDateTimeUtilsClassName}.*;\nimport static ${ObjectUtilsClassName}.*;\nimport static ${DateUtilsClassName}.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n## 字段模板\n#macro(getPageCondition $VO)\n       // mock 数据\n       ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class, o -> { // 等会查询到\n       #foreach ($column in $columns)\n       #if (${column.listOperation})\n       #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写\n           o.set$JavaField(null);\n       #end\n       #end\n       });\n       ${classNameVar}Mapper.insert(db${simpleClassName});\n       #foreach ($column in $columns)\n       #if (${column.listOperation})\n       #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写\n       // 测试 ${column.javaField} 不匹配\n       ${classNameVar}Mapper.insert(cloneIgnoreId(db${simpleClassName}, o -> o.set$JavaField(null)));\n       #end\n       #end\n       // 准备参数\n       ${sceneEnum.prefixClass}${table.className}${VO} reqVO = new ${sceneEnum.prefixClass}${table.className}${VO}();\n       #foreach ($column in $columns)\n       #if (${column.listOperation})\n       #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写\n       #if (${column.listOperationCondition} == \"BETWEEN\")## BETWEEN 的情况\n       reqVO.set${JavaField}(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n       #else\n       reqVO.set$JavaField(null);\n       #end\n       #end\n       #end\n#end\n/**\n * {@link ${table.className}ServiceImpl} 的单元测试类\n *\n * @author ${table.author}\n */\n@Import(${table.className}ServiceImpl.class)\npublic class ${table.className}ServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private ${table.className}ServiceImpl ${classNameVar}Service;\n\n    @Resource\n    private ${table.className}Mapper ${classNameVar}Mapper;\n\n    @Test\n    public void testCreate${simpleClassName}_success() {\n        // 准备参数\n        ${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class).setId(null);\n\n        // 调用\n        ${primaryColumn.javaType} ${classNameVar}Id = ${classNameVar}Service.create${simpleClassName}(createReqVO);\n        // 断言\n        assertNotNull(${classNameVar}Id);\n        // 校验记录的属性是否正确\n        ${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(${classNameVar}Id);\n        assertPojoEquals(createReqVO, ${classNameVar}, \"id\");\n    }\n\n    @Test\n    public void testUpdate${simpleClassName}_success() {\n        // mock 数据\n        ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class);\n        ${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class, o -> {\n            o.setId(db${simpleClassName}.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        ${classNameVar}Service.update${simpleClassName}(updateReqVO);\n        // 校验是否更新正确\n        ${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, ${classNameVar});\n    }\n\n    @Test\n    public void testUpdate${simpleClassName}_notExists() {\n        // 准备参数\n        ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> ${classNameVar}Service.update${simpleClassName}(updateReqVO), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDelete${simpleClassName}_success() {\n        // mock 数据\n        ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class);\n        ${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        ${primaryColumn.javaType} id = db${simpleClassName}.getId();\n\n        // 调用\n        ${classNameVar}Service.delete${simpleClassName}(id);\n       // 校验数据不存在了\n       assertNull(${classNameVar}Mapper.selectById(id));\n    }\n\n    @Test\n    public void testDelete${simpleClassName}_notExists() {\n        // 准备参数\n        ${primaryColumn.javaType} id = random${primaryColumn.javaType}Id();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);\n    }\n\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGet${simpleClassName}Page() {\n       #getPageCondition(\"PageReqVO\")\n\n       // 调用\n       PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(reqVO);\n       // 断言\n       assertEquals(1, pageResult.getTotal());\n       assertEquals(1, pageResult.getList().size());\n       assertPojoEquals(db${simpleClassName}, pageResult.getList().get(0));\n    }\n#else\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGet${simpleClassName}List() {\n       #getPageCondition(\"ListReqVO\")\n\n       // 调用\n       List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(reqVO);\n       // 断言\n       assertEquals(1, list.size());\n       assertPojoEquals(db${simpleClassName}, list.get(0));\n    }\n#end\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/sql/h2.vm",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"${table.tableName.toLowerCase()}\" (\n#foreach ($column in $columns)\n#if (${column.javaType} == 'Long')\n    #set ($dataType='bigint')\n#elseif (${column.javaType} == 'Integer')\n    #set ($dataType='int')\n#elseif (${column.javaType} == 'Boolean')\n    #set ($dataType='bit')\n#elseif (${column.javaType} == 'Date')\n    #set ($dataType='datetime')\n#else\n    #set ($dataType='varchar')\n#end\n    #if (${column.primaryKey})##处理主键\n    \"${column.javaField}\"#if (${column.javaType} == 'String') ${dataType} NOT NULL#else ${dataType} NOT NULL GENERATED BY DEFAULT AS IDENTITY#end,\n    #else\n    #if (${column.columnName} == 'create_time')\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    #elseif (${column.columnName} == 'update_time')\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    #elseif (${column.columnName} == 'creator' || ${column.columnName} == 'updater')\n    \"${column.columnName}\" ${dataType} DEFAULT '',\n    #elseif (${column.columnName} == 'deleted')\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    #elseif (${column.columnName} == 'tenant_id')\n    \"tenant_id\" bigint NOT NULL DEFAULT 0,\n    #else\n    \"${column.columnName.toLowerCase()}\" ${dataType}#if (${column.nullable} == false) NOT NULL#end,\n    #end\n    #end\n#end\n    PRIMARY KEY (\"${primaryColumn.columnName.toLowerCase()}\")\n) COMMENT '${table.tableComment}';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"${table.tableName}\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/sql/sql.vm",
    "content": "## 通用变量定义\n#set ($functionNames = ['查询', '创建', '更新', '删除', '导出'])\n#set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])\n##\n## 宏定义：生成按钮 SQL（通用部分）\n#macro(insertButtonSql $parentIdVar)\n#foreach ($functionName in $functionNames)\n#set ($index = $foreach.count - 1)\n    INSERT INTO system_menu(\n        name, permission, type, sort, parent_id,\n        path, icon, component, status\n    )\n    VALUES (\n        '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, ${parentIdVar},\n        '', '', '', 0\n    );\n#end\n#end\n##\n## ======================= MySQL / OceanBase =======================\n#if ($dbType.name() == 'MYSQL' || $dbType.name() == 'OCEAN_BASE')\n-- 菜单 SQL\nINSERT INTO system_menu(\n    name, permission, type, sort, parent_id,\n    path, icon, component, status, component_name\n)\nVALUES (\n    '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},\n    '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'\n);\n\n-- 按钮父菜单ID\nSELECT @parentId := LAST_INSERT_ID();\n\n-- 按钮 SQL\n#foreach ($functionName in $functionNames)\n#set ($index = $foreach.count - 1)\nINSERT INTO system_menu(\n    name, permission, type, sort, parent_id,\n    path, icon, component, status\n)\nVALUES (\n    '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,\n    '', '', '', 0\n);\n#end\n##\n## ======================= Oracle / 达梦 DM =======================\n#elseif ($dbType.name() == 'ORACLE' || $dbType.name() == 'DM')\n-- 菜单 SQL\nINSERT INTO system_menu(\n    name, permission, type, sort, parent_id,\n    path, icon, component, status, component_name\n)\nVALUES (\n    '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},\n    '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'\n);\n\n-- 按钮父菜单ID\n-- 说明：Oracle/达梦 使用序列获取上一个插入的 ID\nDECLARE\n    v_parent_id NUMBER;\nBEGIN\n    SELECT system_menu_seq.CURRVAL INTO v_parent_id FROM DUAL;\n    -- 按钮 SQL\n#foreach ($functionName in $functionNames)\n#set ($index = $foreach.count - 1)\n    INSERT INTO system_menu(\n        name, permission, type, sort, parent_id,\n        path, icon, component, status\n    )\n    VALUES (\n        '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, v_parent_id,\n        '', '', '', 0\n    );\n#end\nEND;\n/\n##\n## ======================= PostgreSQL / 人大金仓 KingbaseES =======================\n#elseif ($dbType.name() == 'POSTGRE_SQL' || $dbType.name() == 'KINGBASE_ES')\n-- 菜单 SQL\nINSERT INTO system_menu(\n    name, permission, type, sort, parent_id,\n    path, icon, component, status, component_name\n)\nVALUES (\n    '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},\n    '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'\n);\n\n-- 按钮父菜单ID\n-- 说明：PostgreSQL/KingbaseES 使用 lastval() 获取上一个插入的序列值\nDO $$\nDECLARE\n    v_parent_id BIGINT;\nBEGIN\n    v_parent_id := lastval();\n    -- 按钮 SQL\n#foreach ($functionName in $functionNames)\n#set ($index = $foreach.count - 1)\n    INSERT INTO system_menu(\n        name, permission, type, sort, parent_id,\n        path, icon, component, status\n    )\n    VALUES (\n        '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, v_parent_id,\n        '', '', '', 0\n    );\n#end\nEND $$;\n##\n## ======================= SQL Server =======================\n#elseif ($dbType.name() == 'SQL_SERVER' || $dbType.name() == 'SQL_SERVER2005')\n-- 菜单 SQL\nINSERT INTO system_menu(\n    name, permission, type, sort, parent_id,\n    path, icon, component, status, component_name\n)\nVALUES (\n    '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},\n    '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'\n);\n\n-- 按钮父菜单ID\n-- 说明：SQL Server 使用 SCOPE_IDENTITY() 获取上一个插入的自增 ID\nDECLARE @parentId BIGINT;\nSET @parentId = SCOPE_IDENTITY();\n\n-- 按钮 SQL\n#foreach ($functionName in $functionNames)\n#set ($index = $foreach.count - 1)\nINSERT INTO system_menu(\n    name, permission, type, sort, parent_id,\n    path, icon, component, status\n)\nVALUES (\n    '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,\n    '', '', '', 0\n);\n#end\n##\n## ======================= 不支持的数据库类型 =======================\n#else\n-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n-- 注意：当前数据库类型 ${dbType.name()} 暂不支持自动生成菜单 SQL\n-- 请参考以下 MySQL 语法，手动修改为您的数据库对应的语法\n-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n-- 菜单 SQL\nINSERT INTO system_menu(\n    name, permission, type, sort, parent_id,\n    path, icon, component, status, component_name\n)\nVALUES (\n    '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},\n    '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'\n);\n\n-- 按钮父菜单ID\n-- TODO: 请根据您的数据库类型，修改获取上一个插入 ID 的方式\n-- MySQL: SELECT @parentId := LAST_INSERT_ID();\n-- Oracle: SELECT system_menu_seq.CURRVAL INTO v_parent_id FROM DUAL;\n-- PostgreSQL: SELECT lastval() INTO v_parent_id;\n-- SQL Server: SET @parentId = SCOPE_IDENTITY();\nSELECT @parentId := LAST_INSERT_ID();\n\n-- 按钮 SQL\n#foreach ($functionName in $functionNames)\n#set ($index = $foreach.count - 1)\nINSERT INTO system_menu(\n    name, permission, type, sort, parent_id,\n    path, icon, component, status\n)\nVALUES (\n    '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,\n    '', '', '', 0\n);\n#end\n#end\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/api/api.js.vm",
    "content": "import request from '@/utils/request'\n#set ($baseURL = \"/${table.moduleName}/${simpleClassName_strikeCase}\")\n\n// 创建${table.classComment}\nexport function create${simpleClassName}(data) {\n  return request({\n    url: '${baseURL}/create',\n    method: 'post',\n    data: data\n  })\n}\n\n// 更新${table.classComment}\nexport function update${simpleClassName}(data) {\n  return request({\n    url: '${baseURL}/update',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除${table.classComment}\nexport function delete${simpleClassName}(id) {\n  return request({\n    url: '${baseURL}/delete?id=' + id,\n    method: 'delete'\n  })\n}\n\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n/** 批量删除${table.classComment} */\nexport function delete${simpleClassName}List(ids) {\n  return request({\n    url: `${baseURL}/delete-list?ids=${ids.join(',')}`,\n    method: 'delete'\n  })\n}\n#end\n\n// 获得${table.classComment}\nexport function get${simpleClassName}(id) {\n  return request({\n    url: '${baseURL}/get?id=' + id,\n    method: 'get'\n  })\n}\n\n#if ( $table.templateType != 2 )\n// 获得${table.classComment}分页\nexport function get${simpleClassName}Page(params) {\n  return request({\n    url: '${baseURL}/page',\n    method: 'get',\n    params\n  })\n}\n#else\n// 获得${table.classComment}列表\nexport function get${simpleClassName}List(params) {\n  return request({\n    url: '${baseURL}/list',\n    method: 'get',\n    params\n  })\n}\n#end\n// 导出${table.classComment} Excel\nexport function export${simpleClassName}Excel(params) {\n  return request({\n    url: '${baseURL}/export-excel',\n    method: 'get',\n    params,\n    responseType: 'blob'\n  })\n}\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n  #set ($index = $foreach.count - 1)\n  #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n  #set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段\n  #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n  #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n  #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n  #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n  #set ($subClassNameVar = $subClassNameVars.get($index))\n\n// ==================== 子表（$subTable.classComment） ====================\n  ## 情况一：MASTER_ERP 时，需要分查询页子表\n  #if ($table.templateType == 11)\n  // 获得${subTable.classComment}分页\n  export function get${subSimpleClassName}Page(params) {\n    return request({\n      url: '${baseURL}/${subSimpleClassName_strikeCase}/page',\n      method: 'get',\n      params\n    })\n  }\n    ## 情况二：非 MASTER_ERP 时，需要列表查询子表\n  #else\n    #if ($subTable.subJoinMany)\n    // 获得${subTable.classComment}列表\n    export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}) {\n      return request({\n        url: '${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=' + ${subJoinColumn.javaField},\n        method: 'get'\n      })\n    }\n    #else\n    // 获得${subTable.classComment}\n    export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}) {\n      return request({\n        url: '${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=' + ${subJoinColumn.javaField},\n        method: 'get'\n      })\n    }\n    #end\n  #end\n  ## 特殊：MASTER_ERP 时，支持单个的新增、修改、删除操作\n  #if ($table.templateType == 11)\n  // 新增${subTable.classComment}\n  export function create${subSimpleClassName}(data) {\n    return request({\n      url: '${baseURL}/${subSimpleClassName_strikeCase}/create',\n      method: 'post',\n      data\n    })\n  }\n  // 修改${subTable.classComment}\n  export function update${subSimpleClassName}(data) {\n    return request({\n      url: '${baseURL}/${subSimpleClassName_strikeCase}/update',\n      method: 'post',\n      data\n    })\n  }\n  // 删除${subTable.classComment}\n  export function delete${subSimpleClassName}(id) {\n    return request({\n      url: '${baseURL}/${subSimpleClassName_strikeCase}/delete?id=' + id,\n      method: 'delete'\n    })\n  }\n  #if ($deleteBatchEnable)\n  /** 批量删除${subTable.classComment} */\n  export function delete${subSimpleClassName}List(ids) {\n    return request({\n      url: `${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}`,\n      method: 'delete'\n    })\n  }\n  #end\n  // 获得${subTable.classComment}\n  export function get${subSimpleClassName}(id) {\n    return request({\n      url: '${baseURL}/${subSimpleClassName_strikeCase}/get?id=' + id,\n      method: 'get'\n    })\n  }\n  #end\n#end"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n<template>\n  <div class=\"app-container\">\n    <!-- 对话框(添加 / 修改) -->\n    <el-dialog :title=\"dialogTitle\" :visible.sync=\"dialogVisible\" width=\"45%\" v-dialogDrag append-to-body>\n      <el-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\" v-loading=\"formLoading\" label-width=\"100px\">\n          #foreach($column in $subColumns)\n              #if ($column.createOperation || $column.updateOperation)\n                  #set ($dictType = $column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($javaType = $column.javaType)\n                  #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n                  #set ($comment = $column.columnComment)\n                  #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                  #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-input v-model=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n                    </el-form-item>\n                  #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                      #set ($hasImageUploadColumn = true)\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <ImageUpload v-model=\"formData.${javaField}\"/>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                      #set ($hasFileUploadColumn = true)\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <FileUpload v-model=\"formData.${javaField}\"/>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"editor\")## 文本编辑器\n                      #set ($hasEditorColumn = true)\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <editor v-model=\"formData.${javaField}\" :min-height=\"192\"/>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"select\")## 下拉框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-select v-model=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                          #if (\"\" != $dictType)## 有数据字典\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                       :key=\"dict.value\" :label=\"dict.label\" #if ($column.javaType == \"Integer\" || $column.javaType == \"Long\"):value=\"parseInt(dict.value)\"#else:value=\"dict.value\"#end />\n                          #else##没数据字典\n                            <el-option label=\"请选择字典生成\" value=\"\" />\n                          #end\n                      </el-select>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"checkbox\")## 多选框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-checkbox-group v-model=\"formData.${javaField}\">\n                          #if (\"\" != $dictType)## 有数据字典\n                            <el-checkbox v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                         :key=\"dict.value\" #if($column.javaType == \"Integer\" || $column.javaType == \"Long\"):label=\"parseInt(dict.value)\"#else:label=\"dict.value\"#end>{{dict.label}}</el-checkbox>\n                          #else##没数据字典\n                            <el-checkbox>请选择字典生成</el-checkbox>\n                          #end\n                      </el-checkbox-group>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"radio\")## 单选框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-radio-group v-model=\"formData.${javaField}\">\n                          #if (\"\" != $dictType)## 有数据字典\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                      :key=\"dict.value\" #if($column.javaType == \"Integer\" || $column.javaType == \"Long\"):label=\"parseInt(dict.value)\"\n                                      #else:label=\"dict.value\"#end>{{dict.label}}</el-radio>\n                          #else##没数据字典\n                            <el-radio label=\"1\">请选择字典生成</el-radio>\n                          #end\n                      </el-radio-group>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"datetime\")## 时间框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-date-picker clearable v-model=\"formData.${javaField}\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择${comment}\" />\n                    </el-form-item>\n                  #elseif($column.htmlType == \"textarea\")## 文本框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-input v-model=\"formData.${javaField}\" type=\"textarea\" placeholder=\"请输入内容\" />\n                    </el-form-item>\n                  #end\n              #end\n          #end\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\" :disabled=\"formLoading\">确 定</el-button>\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\n  import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';\n      #if ($hasImageUploadColumn)\n      import ImageUpload from '@/components/ImageUpload';\n      #end\n      #if ($hasFileUploadColumn)\n      import FileUpload from '@/components/FileUpload';\n      #end\n      #if ($hasEditorColumn)\n      import Editor from '@/components/Editor';\n      #end\n  export default {\n    name: \"${subSimpleClassName}Form\",\n    components: {\n        #if ($hasImageUploadColumn)\n          ImageUpload,\n        #end\n        #if ($hasFileUploadColumn)\n          FileUpload,\n        #end\n        #if ($hasEditorColumn)\n          Editor,\n        #end\n    },\n    data() {\n      return {\n        // 弹出层标题\n        dialogTitle: \"\",\n        // 是否显示弹出层\n        dialogVisible: false,\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: {\n            #foreach ($column in $subColumns)\n                #if ($column.createOperation || $column.updateOperation)\n                    #if ($column.htmlType == \"checkbox\")\n                            $column.javaField: [],\n                    #else\n                            $column.javaField: undefined,\n                    #end\n                #end\n            #end\n        },\n        // 表单校验\n        formRules: {\n            #foreach ($column in $subColumns)\n                #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n                    #set($comment=$column.columnComment)\n                        $column.javaField: [{ required: true, message: \"${comment}不能为空\", trigger: #if($column.htmlType == \"select\")\"change\"#else\"blur\"#end }],\n                #end\n            #end\n        },\n      };\n    },\n    methods: {\n      /** 打开弹窗 */\n      async open(id, ${subJoinColumn.javaField}) {\n        this.dialogVisible = true;\n        this.reset();\n        this.formData.${subJoinColumn.javaField} = ${subJoinColumn.javaField};\n        // 修改时，设置数据\n        if (id) {\n          this.formLoading = true;\n          try {\n            const res = await ${simpleClassName}Api.get${subSimpleClassName}(id);\n            this.formData = res.data;\n            this.dialogTitle = \"修改${subTable.classComment}\";\n          } finally {\n            this.formLoading = false;\n          }\n        }\n        this.dialogTitle = \"新增${subTable.classComment}\";\n      },\n      /** 提交按钮 */\n      async submitForm() {\n        await this.#[[$]]#refs[\"formRef\"].validate();\n        this.formLoading = true;\n        try {\n            const data = this.formData;\n            // 修改的提交\n            if (data.${primaryColumn.javaField}) {\n            await  ${simpleClassName}Api.update${subSimpleClassName}(data);\n            this.#[[$modal]]#.msgSuccess(\"修改成功\");\n            this.dialogVisible = false;\n            this.#[[$]]#emit('success');\n              return;\n            }\n            // 添加的提交\n              await ${simpleClassName}Api.create${subSimpleClassName}(data);\n              this.#[[$modal]]#.msgSuccess(\"新增成功\");\n              this.dialogVisible = false;\n              this.#[[$]]#emit('success');\n        }finally {\n          this.formLoading = false;\n        }\n      },\n      /** 表单重置 */\n      reset() {\n        this.formData = {\n            #foreach ($column in $subColumns)\n                #if ($column.createOperation || $column.updateOperation)\n                    #if ($column.htmlType == \"checkbox\")\n                            $column.javaField: [],\n                    #else\n                            $column.javaField: undefined,\n                    #end\n                #end\n            #end\n        };\n        this.resetForm(\"formRef\");\n      },\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm",
    "content": "## 主表的 normal 和 inner 使用相同的 form 表单\n#parse(\"codegen/vue/views/components/form_sub_normal.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n<template>\n  <div class=\"app-container\">\n    #if ( $subTable.subJoinMany )## 情况一：一对多，table + form\n      <el-form\n        ref=\"formRef\"\n        :model=\"formData\"\n        :rules=\"formRules\"\n        v-loading=\"formLoading\"\n        label-width=\"0px\"\n        :inline-message=\"true\"\n      >\n        <el-table :data=\"formData\" class=\"-mt-10px\">\n          <el-table-column label=\"序号\" type=\"index\" width=\"100\" />\n            #foreach($column in $subColumns)\n                #if ($column.createOperation || $column.updateOperation)\n                    #set ($dictType = $column.dictType)\n                    #set ($javaField = $column.javaField)\n                    #set ($javaType = $column.javaType)\n                    #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n                    #set ($comment = $column.columnComment)\n                    #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                    #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n                      <el-table-column label=\"${comment}\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n                            <el-input v-model=\"row.${javaField}\" placeholder=\"请输入${comment}\" />\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                    #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                        #set ($hasImageUploadColumn = true)\n                      <el-table-column label=\"${comment}\" min-width=\"200\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n                            <ImageUpload v-model=\"row.${javaField}\"/>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                    #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                        #set ($hasFileUploadColumn = true)\n                      <el-table-column label=\"${comment}\" min-width=\"200\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n                            <FileUpload v-model=\"row.${javaField}\"/>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                    #elseif($column.htmlType == \"editor\")## 文本编辑器\n                        #set ($hasEditorColumn = true)\n                      <el-table-column label=\"${comment}\" min-width=\"400\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n                            <Editor v-model=\"row.${javaField}\" :min-height=\"192\"/>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                    #elseif($column.htmlType == \"select\")## 下拉框\n                      <el-table-column label=\"${comment}\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n                            <el-select v-model=\"row.${javaField}\" placeholder=\"请选择${comment}\">\n                                #if (\"\" != $dictType)## 有数据字典\n                                  <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                             :key=\"dict.value\" :label=\"dict.label\" #if ($column.javaType == \"Integer\" || $column.javaType == \"Long\"):value=\"parseInt(dict.value)\"#else:value=\"dict.value\"#end />\n                                #else##没数据字典\n                                  <el-option label=\"请选择字典生成\" value=\"\" />\n                                #end\n                            </el-select>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                    #elseif($column.htmlType == \"checkbox\")## 多选框\n                      <el-table-column label=\"${comment}\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n                            <el-checkbox-group v-model=\"row.${javaField}\">\n                                #if (\"\" != $dictType)## 有数据字典\n                                  <el-checkbox v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                               :key=\"dict.value\" #if($column.javaType == \"Integer\" || $column.javaType == \"Long\"):label=\"parseInt(dict.value)\"#else:label=\"dict.value\"#end>{{dict.label}}</el-checkbox>\n                                #else##没数据字典\n                                  <el-checkbox>请选择字典生成</el-checkbox>\n                                #end\n                            </el-checkbox-group>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                    #elseif($column.htmlType == \"radio\")## 单选框\n                      <el-table-column label=\"${comment}\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n                            <el-radio-group v-model=\"row.${javaField}\">\n                                #if (\"\" != $dictType)## 有数据字典\n                                  <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                            :key=\"dict.value\" #if($column.javaType == \"Integer\" || $column.javaType == \"Long\"):label=\"parseInt(dict.value)\"\n                                            #else:label=\"dict.value\"#end>{{dict.label}}</el-radio>\n                                #else##没数据字典\n                                  <el-radio label=\"1\">请选择字典生成</el-radio>\n                                #end\n                            </el-radio-group>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                    #elseif($column.htmlType == \"datetime\")## 时间框\n                      <el-table-column label=\"${comment}\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n                            <el-date-picker clearable v-model=\"row.${javaField}\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择${comment}\" />\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                    #elseif($column.htmlType == \"textarea\")## 文本框\n                      <el-table-column label=\"${comment}\" min-width=\"200\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n                            <el-input v-model=\"row.${javaField}\" type=\"textarea\" placeholder=\"请输入${comment}\" />\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                    #end\n                #end\n            #end\n          <el-table-column align=\"center\" fixed=\"right\" label=\"操作\" width=\"60\">\n            <template v-slot=\"{ $index }\">\n              <el-link @click=\"handleDelete($index)\">—</el-link>\n            </template>\n          </el-table-column>\n        </el-table>\n      </el-form>\n      <el-row justify=\"center\" class=\"mt-3\">\n        <el-button @click=\"handleAdd\" round>+ 添加${subTable.classComment}</el-button>\n      </el-row>\n    #else## 情况二：一对一，form\n      <el-form\n        ref=\"formRef\"\n        :model=\"formData\"\n        :rules=\"formRules\"\n        label-width=\"100px\"\n        v-loading=\"formLoading\"\n      >\n          #foreach($column in $subColumns)\n              #if ($column.createOperation || $column.updateOperation)\n                  #set ($dictType = $column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($javaType = $column.javaType)\n                  #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n                  #set ($comment = $column.columnComment)\n                  #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                  #elseif ($column.htmlType == \"input\" && !$column.primaryKey)\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-input v-model=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n                    </el-form-item>\n                  #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                      #set ($hasImageUploadColumn = true)\n                    <el-form-item label=\"${comment}\">\n                      <ImageUpload v-model=\"formData.${javaField}\"/>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                      #set ($hasFileUploadColumn = true)\n                    <el-form-item label=\"${comment}\">\n                      <FileUpload v-model=\"formData.${javaField}\"/>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"editor\")## 文本编辑器\n                      #set ($hasEditorColumn = true)\n                    <el-form-item label=\"${comment}\">\n                      <Editor v-model=\"formData.${javaField}\" :min-height=\"192\"/>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"select\")## 下拉框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-select v-model=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                          #if (\"\" != $dictType)## 有数据字典\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                       :key=\"dict.value\" :label=\"dict.label\" #if ($column.javaType == \"Integer\" || $column.javaType == \"Long\"):value=\"parseInt(dict.value)\"#else:value=\"dict.value\"#end />\n                          #else##没数据字典\n                            <el-option label=\"请选择字典生成\" value=\"\" />\n                          #end\n                      </el-select>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"checkbox\")## 多选框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-checkbox-group v-model=\"formData.${javaField}\">\n                          #if (\"\" != $dictType)## 有数据字典\n                            <el-checkbox v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                         :key=\"dict.value\" #if($column.javaType == \"Integer\" || $column.javaType == \"Long\"):label=\"parseInt(dict.value)\"#else:label=\"dict.value\"#end>{{dict.label}}</el-checkbox>\n                          #else##没数据字典\n                            <el-checkbox>请选择字典生成</el-checkbox>\n                          #end\n                      </el-checkbox-group>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"radio\")## 单选框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-radio-group v-model=\"formData.${javaField}\">\n                          #if (\"\" != $dictType)## 有数据字典\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                      :key=\"dict.value\" #if($column.javaType == \"Integer\" || $column.javaType == \"Long\"):label=\"parseInt(dict.value)\"\n                                      #else:label=\"dict.value\"#end>{{dict.label}}</el-radio>\n                          #else##没数据字典\n                            <el-radio label=\"1\">请选择字典生成</el-radio>\n                          #end\n                      </el-radio-group>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"datetime\")## 时间框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-date-picker clearable v-model=\"formData.${javaField}\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择${comment}\" />\n                    </el-form-item>\n                  #elseif($column.htmlType == \"textarea\")## 文本框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-input v-model=\"formData.${javaField}\" type=\"textarea\" placeholder=\"请输入${comment}\" />\n                    </el-form-item>\n                  #end\n              #end\n          #end\n      </el-form>\n    #end\n  </div>\n</template>\n\n<script>\n  import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';\n      #if ($hasImageUploadColumn)\n      import ImageUpload from '@/components/ImageUpload';\n      #end\n      #if ($hasFileUploadColumn)\n      import FileUpload from '@/components/FileUpload';\n      #end\n      #if ($hasEditorColumn)\n      import Editor from '@/components/Editor';\n      #end\n  export default {\n    name: \"${subSimpleClassName}Form\",\n    components: {\n        #if ($hasImageUploadColumn)\n          ImageUpload,\n        #end\n        #if ($hasFileUploadColumn)\n          FileUpload,\n        #end\n        #if ($hasEditorColumn)\n          Editor,\n        #end\n    },\n    props:[\n      '${subJoinColumn.javaField}'\n    ],// ${subJoinColumn.columnComment}（主表的关联字段）\n    data() {\n      return {\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: [],\n        // 表单校验\n        formRules: {\n            #foreach ($column in $subColumns)\n                #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n                    #set($comment=$column.columnComment)\n                        $column.javaField: [{ required: true, message: \"${comment}不能为空\", trigger: #if($column.htmlType == \"select\")\"change\"#else\"blur\"#end }],\n                #end\n            #end\n        },\n      };\n    },\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n      ${subJoinColumn.javaField}:{\n        handler(val) {\n          // 1. 重置表单\n            #if ( $subTable.subJoinMany )\n              this.formData = []\n            #else\n              this.formData = {\n                  #foreach ($column in $subColumns)\n                      #if ($column.createOperation || $column.updateOperation)\n                          #if ($column.htmlType == \"checkbox\")\n                                  $column.javaField: [],\n                          #else\n                                  $column.javaField: undefined,\n                          #end\n                      #end\n                  #end\n              }\n            #end\n          // 2. val 非空，则加载数据\n          if (!val) {\n            return;\n          }\n          try {\n            this.formLoading = true;\n            // 这里还是需要获取一下 this 的不然取不到 formData\n            const that = this;\n            #if ( $subTable.subJoinMany )\n            ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(val).then(function (res){\n              that.formData = res.data;\n            })\n            #else\n            ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(val).then(function (res){\n              const data = res.data;\n              if (!data) {\n                return\n              }\n              that.formData = data;\n            })\n            #end\n          } finally {\n            this.formLoading = false;\n          }\n        },\n        immediate: true\n      }\n    },\n    methods: {\n        #if ( $subTable.subJoinMany )\n          /** 新增按钮操作 */\n          handleAdd() {\n            const row = {\n                #foreach ($column in $subColumns)\n                    #if ($column.createOperation || $column.updateOperation)\n                        #if ($column.htmlType == \"checkbox\")\n                                $column.javaField: [],\n                        #else\n                                $column.javaField: undefined,\n                        #end\n                    #end\n                #end\n            }\n            row.${subJoinColumn.javaField} = this.${subJoinColumn.javaField};\n            this.formData.push(row);\n          },\n          /** 删除按钮操作 */\n          handleDelete(index) {\n            this.formData.splice(index, 1);\n          },\n        #end\n      /** 表单校验 */\n      validate(){\n        return this.#[[$]]#refs[\"formRef\"].validate();\n      },\n      /** 表单值 */\n      getData(){\n        return this.formData;\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n<template>\n  <div class=\"app-container\">\n#if ($table.templateType == 11)\n    <!-- 操作工具栏 -->\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"openForm(undefined)\"\n                   v-hasPermi=\"['${permissionPrefix}:create']\">新增</el-button>\n      </el-col>\n    #if ($deleteBatchEnable)\n      <el-col :span=\"1.5\">\n        <el-button\n            type=\"danger\"\n            plain\n            icon=\"el-icon-delete\"\n            size=\"mini\"\n            :disabled=\"isEmpty(checkedIds)\"\n            @click=\"handleDeleteBatch\"\n            v-hasPermi=\"['${permissionPrefix}:delete']\"\n        >\n          批量删除\n        </el-button>\n      </el-col>\n    #end\n    </el-row>\n#end\n      ## 列表\n      <el-table\n          v-loading=\"loading\"\n          :data=\"list\"\n          :stripe=\"true\"\n          :show-overflow-tooltip=\"true\"\n          #if ($table.templateType == 11 && $deleteBatchEnable)\n          @selection-change=\"handleRowCheckboxChange\"\n          #end\n      >\n          #if ($table.templateType == 11 && $deleteBatchEnable)\n            <el-table-column type=\"selection\" width=\"55\" />\n          #end\n          #foreach($column in $subColumns)\n              #if ($column.listOperationResult)\n                  #set ($dictType=$column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n                  #set ($comment=$column.columnComment)\n                  #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                  #elseif ($column.javaType == \"LocalDateTime\")## 时间类型\n                <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"180\">\n                  <template v-slot=\"scope\">\n                    <span>{{ parseTime(scope.row.${javaField}) }}</span>\n                  </template>\n                </el-table-column>\n                  #elseif($column.dictType && \"\" != $column.dictType)## 数据字典\n                <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\">\n                  <template v-slot=\"scope\">\n                    <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.${column.javaField}\" />\n                  </template>\n                </el-table-column>\n              #else\n                <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" />\n              #end\n          #end\n      #end\n    <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n      <template v-slot=\"scope\">\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.${primaryColumn.javaField})\"\n                   v-hasPermi=\"['${permissionPrefix}:update']\">修改</el-button>\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                   v-hasPermi=\"['${permissionPrefix}:delete']\">删除</el-button>\n      </template>\n    </el-table-column>\n  </el-table>\n#if ($table.templateType == 11)\n    <!-- 分页组件 -->\n    <pagination v-show=\"total > 0\" :total=\"total\" :page.sync=\"queryParams.pageNo\" :limit.sync=\"queryParams.pageSize\"\n                @pagination=\"getList\"/>\n  <!-- 对话框(添加 / 修改) -->\n  <${subSimpleClassName}Form ref=\"formRef\" @success=\"getList\" />\n#end\n  </div>\n</template>\n\n<script>\n  import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';\n  #if ($table.templateType == 11)\n  import ${subSimpleClassName}Form from './${subSimpleClassName}Form.vue';\n  #end\n  export default {\n    name: \"${subSimpleClassName}List\",\n#if ($table.templateType == 11)\n    components: {\n       ${subSimpleClassName}Form\n    },\n#end\n    props:[\n      '${subJoinColumn.javaField}'\n    ],// ${subJoinColumn.columnComment}（主表的关联字段）\n    data() {\n      return {\n        // 遮罩层\n        loading: true,\n        // 列表的数据\n        list: [],\n#if ($table.templateType == 11)\n        #if ($deleteBatchEnable)\n        checkedIds: [],\n        #end\n        // 列表的总页数\n        total: 0,\n        // 查询参数\n        queryParams: {\n          pageNo: 1,\n          pageSize: 10,\n          ${subJoinColumn.javaField}: undefined\n        }\n#end\n      };\n    },\n#if ($table.templateType != 11)\n    created() {\n      this.getList();\n    },\n#end\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n        ${subJoinColumn.javaField}:{\n            handler(val) {\n              this.queryParams.${subJoinColumn.javaField} = val;\n              if (val){\n                this.handleQuery();\n              }\n            },\n            immediate: true\n      }\n    },\n    methods: {\n      /** 查询列表 */\n      async getList() {\n        try {\n          this.loading = true;\n          #if ($table.templateType == 11)\n            const res = await ${simpleClassName}Api.get${subSimpleClassName}Page(this.queryParams);\n            this.list = res.data.list;\n            this.total = res.data.total;\n          #else\n              #if ( $subTable.subJoinMany )\n                const res = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(this.${subJoinColumn.javaField});\n                this.list = res.data;\n              #else\n                const res = await  ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(this.${subJoinColumn.javaField});\n                const data = res.data;\n                if (!data) {\n                  return;\n                }\n                this.list.push(data);\n              #end\n          #end\n        } finally {\n          this.loading = false;\n        }\n      },\n      #if ($table.templateType == 11 && $deleteBatchEnable)\n        /** 批量删除${table.classComment} */\n        async handleDeleteBatch() {\n          await this.#[[$modal]]#.confirm('是否确认删除?')\n          try {\n            await ${simpleClassName}Api.delete${subSimpleClassName}List(this.checkedIds);\n            this.checkedIds = [];\n            await this.getList();\n            this.#[[$modal]]#.msgSuccess(\"删除成功\");\n          } catch {}\n        },\n        handleRowCheckboxChange(records) {\n          this.checkedIds = records.map((item) => item.id);\n        },\n        #end\n\n#if ($table.templateType == 11)\n        /** 搜索按钮操作 */\n        handleQuery() {\n          this.queryParams.pageNo = 1;\n          this.getList();\n        },\n      /** 添加/修改操作 */\n      openForm(id) {\n        if (!this.${subJoinColumn.javaField}) {\n          this.#[[$modal]]#.msgError('请选择一个${table.classComment}');\n          return;\n        }\n        this.#[[$]]#refs[\"formRef\"].open(id, this.${subJoinColumn.javaField});\n      },\n      /** 删除按钮操作 */\n      async handleDelete(row) {\n        const ${primaryColumn.javaField} = row.${primaryColumn.javaField};\n        await this.#[[$modal]]#.confirm('是否确认删除${table.classComment}编号为\"' + ${primaryColumn.javaField} + '\"的数据项?');\n        try {\n          await ${simpleClassName}Api.delete${subSimpleClassName}(${primaryColumn.javaField});\n          await this.getList();\n          this.#[[$modal]]#.msgSuccess(\"删除成功\");\n        } catch {}\n      },\n#end\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm",
    "content": "## 子表的 erp 和 inner 使用相似的 list 列表，差异主要两点：\n## 1）inner 使用 list 不分页，erp 使用 page 分页\n## 2）erp 支持单个子表的新增、修改、删除，inner 不支持\n#parse(\"codegen/vue/views/components/list_sub_erp.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/views/form.vue.vm",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 对话框(添加 / 修改) -->\n    <el-dialog :title=\"dialogTitle\" :visible.sync=\"dialogVisible\" width=\"45%\" v-dialogDrag append-to-body>\n      <el-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\" v-loading=\"formLoading\" label-width=\"100px\">\n          #foreach($column in $columns)\n              #if ($column.createOperation || $column.updateOperation)\n                  #set ($dictType = $column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n                  #set ($comment = $column.columnComment)\n                  #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <TreeSelect\n                        v-model=\"formData.${javaField}\"\n                        :options=\"${classNameVar}Tree\"\n                        :normalizer=\"normalizer\"\n                        placeholder=\"请选择${comment}\"\n                      />\n                    </el-form-item>\n                  #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-input v-model=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n                    </el-form-item>\n                  #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                      #set ($hasImageUploadColumn = true)\n                    <el-form-item label=\"${comment}\">\n                      <ImageUpload v-model=\"formData.${javaField}\"/>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                      #set ($hasFileUploadColumn = true)\n                    <el-form-item label=\"${comment}\">\n                      <FileUpload v-model=\"formData.${javaField}\"/>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"editor\")## 文本编辑器\n                      #set ($hasEditorColumn = true)\n                    <el-form-item label=\"${comment}\">\n                      <Editor v-model=\"formData.${javaField}\" :min-height=\"192\"/>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"select\")## 下拉框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-select v-model=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                          #if (\"\" != $dictType)## 有数据字典\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                       :key=\"dict.value\" :label=\"dict.label\" #if ($column.javaType == \"Integer\" || $column.javaType == \"Long\"):value=\"parseInt(dict.value)\"#else:value=\"dict.value\"#end />\n                          #else##没数据字典\n                            <el-option label=\"请选择字典生成\" value=\"\" />\n                          #end\n                      </el-select>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"checkbox\")## 多选框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-checkbox-group v-model=\"formData.${javaField}\">\n                          #if (\"\" != $dictType)## 有数据字典\n                            <el-checkbox v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                         :key=\"dict.value\" #if($column.javaType == \"Integer\" || $column.javaType == \"Long\"):label=\"parseInt(dict.value)\"#else:label=\"dict.value\"#end>{{dict.label}}</el-checkbox>\n                          #else##没数据字典\n                            <el-checkbox>请选择字典生成</el-checkbox>\n                          #end\n                      </el-checkbox-group>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"radio\")## 单选框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-radio-group v-model=\"formData.${javaField}\">\n                          #if (\"\" != $dictType)## 有数据字典\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                                      :key=\"dict.value\" #if($column.javaType == \"Integer\" || $column.javaType == \"Long\"):label=\"parseInt(dict.value)\"\n                                      #else:label=\"dict.value\"#end>{{dict.label}}</el-radio>\n                          #else##没数据字典\n                            <el-radio label=\"1\">请选择字典生成</el-radio>\n                          #end\n                      </el-radio-group>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"datetime\")## 时间框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-date-picker clearable v-model=\"formData.${javaField}\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择${comment}\" />\n                    </el-form-item>\n                  #elseif($column.htmlType == \"textarea\")## 文本框\n                    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                      <el-input v-model=\"formData.${javaField}\" type=\"textarea\" placeholder=\"请输入内容\" />\n                    </el-form-item>\n                  #end\n              #end\n          #end\n      </el-form>\n        ## 特殊：主子表专属逻辑\n        #if ( $table.templateType == 10 || $table.templateType == 12 )\n          <!-- 子表的表单 -->\n          <el-tabs v-model=\"subTabsName\">\n              #foreach ($subTable in $subTables)\n                  #set ($index = $foreach.count - 1)\n                  #set ($subClassNameVar = $subClassNameVars.get($index))\n                  #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n                  #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n                <el-tab-pane label=\"${subTable.classComment}\" name=\"$subClassNameVar\">\n                  <${subSimpleClassName}Form ref=\"${subClassNameVar}FormRef\" :${subJoinColumn_strikeCase}=\"formData.id\" />\n                </el-tab-pane>\n              #end\n          </el-tabs>\n        #end\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\" :disabled=\"formLoading\">确 定</el-button>\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\n  import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';\n  #if ($hasImageUploadColumn)\n  import ImageUpload from '@/components/ImageUpload';\n  #end\n  #if ($hasFileUploadColumn)\n  import FileUpload from '@/components/FileUpload';\n  #end\n  #if ($hasEditorColumn)\n  import Editor from '@/components/Editor';\n  #end\n  ## 特殊：树表专属逻辑\n  #if ( $table.templateType == 2 )\n  import TreeSelect from \"@riophae/vue-treeselect\";\n  import \"@riophae/vue-treeselect/dist/vue-treeselect.css\";\n  #end\n  ## 特殊：主子表专属逻辑\n  #if ( $table.templateType == 10 || $table.templateType == 12 )\n      #foreach ($subSimpleClassName in $subSimpleClassNames)\n      import ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue'\n      #end\n  #end\n  export default {\n    name: \"${simpleClassName}Form\",\n    components: {\n        #if ($hasImageUploadColumn)\n          ImageUpload,\n        #end\n        #if ($hasFileUploadColumn)\n          FileUpload,\n        #end\n        #if ($hasEditorColumn)\n          Editor,\n        #end\n        ## 特殊：树表专属逻辑\n        #if ( $table.templateType == 2 )\n          TreeSelect,\n        #end\n        ## 特殊：主子表专属逻辑\n        #if ( $table.templateType == 10 || $table.templateType == 12 )\n            #foreach ($subSimpleClassName in $subSimpleClassNames)\n               ${subSimpleClassName}Form,\n            #end\n        #end\n    },\n    data() {\n      return {\n        // 弹出层标题\n        dialogTitle: \"\",\n        // 是否显示弹出层\n        dialogVisible: false,\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: {\n            #foreach ($column in $columns)\n                #if ($column.createOperation || $column.updateOperation)\n                    #if ($column.htmlType == \"checkbox\")\n                            $column.javaField: [],\n                    #else\n                            $column.javaField: undefined,\n                    #end\n                #end\n            #end\n        },\n        // 表单校验\n        formRules: {\n            #foreach ($column in $columns)\n                #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n                    #set($comment=$column.columnComment)\n                        $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n                #end\n            #end\n        },\n          ## 特殊：树表专属逻辑\n          #if ( $table.templateType == 2 )\n             ${classNameVar}Tree: [], // 树形结构\n          #end\n        ## 特殊：主子表专属逻辑\n        #if ( $table.templateType == 10 || $table.templateType == 12 )\n        #if ( $subTables && $subTables.size() > 0 )\n            /** 子表的表单 */\n             subTabsName: '$subClassNameVars.get(0)'\n        #end\n        #end\n      };\n    },\n    methods: {\n      /** 打开弹窗 */\n     async open(id) {\n        this.dialogVisible = true;\n        this.reset();\n        // 修改时，设置数据\n        if (id) {\n          this.formLoading = true;\n          try {\n            const res = await ${simpleClassName}Api.get${simpleClassName}(id);\n            this.formData = res.data;\n            this.title = \"修改${table.classComment}\";\n          } finally {\n            this.formLoading = false;\n          }\n        }\n        this.title = \"新增${table.classComment}\";\n        ## 特殊：树表专属逻辑\n        #if ( $table.templateType == 2 )\n        await this.get${simpleClassName}Tree();\n        #end\n      },\n      /** 提交按钮 */\n      async submitForm() {\n        // 校验主表\n        await this.$refs[\"formRef\"].validate();\n          ## 特殊：主子表专属逻辑\n          #if ( $table.templateType == 10 || $table.templateType == 12 )\n              #if ( $subTables && $subTables.size() > 0 )\n                // 校验子表\n                  #foreach ($subTable in $subTables)\n                      #set ($index = $foreach.count - 1)\n                      #set ($subClassNameVar = $subClassNameVars.get($index))\n                    try {\n                      ## 代码生成后会替换为正确的 refs\n                      await this.refs['${subClassNameVar}FormRef'].validate();\n                    } catch (e) {\n                      this.subTabsName = '${subClassNameVar}';\n                      return;\n                    }\n                  #end\n              #end\n          #end\n        this.formLoading = true;\n        try {\n          const data = this.formData;\n        ## 特殊：主子表专属逻辑\n        #if ( $table.templateType == 10 || $table.templateType == 12 )\n        #if ( $subTables && $subTables.size() > 0 )\n            // 拼接子表的数据\n            #foreach ($subTable in $subTables)\n                #set ($index = $foreach.count - 1)\n                #set ($subClassNameVar = $subClassNameVars.get($index))\n              data.${subClassNameVar}#if ( $subTable.subJoinMany)s#end = this.refs['${subClassNameVar}FormRef'].getData();\n            #end\n        #end\n        #end\n          // 修改的提交\n          if (data.${primaryColumn.javaField}) {\n            await ${simpleClassName}Api.update${simpleClassName}(data);\n            this.#[[$modal]]#.msgSuccess(\"修改成功\");\n            this.dialogVisible = false;\n            this.#[[$]]#emit('success');\n            return;\n          }\n          // 添加的提交\n          await ${simpleClassName}Api.create${simpleClassName}(data);\n          this.#[[$modal]]#.msgSuccess(\"新增成功\");\n          this.dialogVisible = false;\n          this.#[[$]]#emit('success');\n        } finally {\n          this.formLoading = false;\n        }\n      },\n        ## 特殊：树表专属逻辑\n        #if ( $table.templateType == 2 )\n          /** 获得${table.classComment}树 */\n         async get${simpleClassName}Tree() {\n            this.${classNameVar}Tree = [];\n            const res = await ${simpleClassName}Api.get${simpleClassName}List();\n            const root = { id: 0, name: '顶级${table.classComment}', children: [] };\n            root.children = this.handleTree(res.data, 'id', '${treeParentColumn.javaField}')\n            this.${classNameVar}Tree.push(root)\n          },\n        #end\n        ## 特殊：树表专属逻辑\n        #if ( $table.templateType == 2 )\n          /** 转换${table.classComment}数据结构 */\n          normalizer(node) {\n            if (node.children && !node.children.length) {\n              delete node.children;\n            }\n              #if ($treeNameColumn.javaField == \"name\")\n                return {\n                  id: node.id,\n                  label: node.name,\n                  children: node.children\n                };\n              #else\n                return {\n                  id: node.id,\n                  label: node['$treeNameColumn.javaField'],\n                  children: node.children\n                };\n              #end\n          },\n        #end\n      /** 表单重置 */\n      reset() {\n        this.formData = {\n            #foreach ($column in $columns)\n                #if ($column.createOperation || $column.updateOperation)\n                    #if ($column.htmlType == \"checkbox\")\n                            $column.javaField: [],\n                    #else\n                            $column.javaField: undefined,\n                    #end\n                #end\n            #end\n        };\n        this.resetForm(\"formRef\");\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/views/index.vue.vm",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 搜索工作栏 -->\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n#foreach($column in $columns)\n#if ($column.listOperation)\n    #set ($dictType=$column.dictType)\n    #set ($javaField = $column.javaField)\n    #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n    #set ($comment=$column.columnComment)\n#if ($column.htmlType == \"input\")\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-input v-model=\"queryParams.${javaField}\" placeholder=\"请输入${comment}\" clearable @keyup.enter.native=\"handleQuery\"/>\n      </el-form-item>\n#elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\")\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-select v-model=\"queryParams.${javaField}\" placeholder=\"请选择${comment}\" clearable size=\"small\">\n    #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n          <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())\"\n                       :key=\"dict.value\" :label=\"dict.label\" :value=\"dict.value\"/>\n    #else## 未设置 dictType 数据字典的情况\n          <el-option label=\"请选择字典生成\" value=\"\" />\n    #end\n        </el-select>\n      </el-form-item>\n#elseif($column.htmlType == \"datetime\")\n    #if ($column.listOperationCondition != \"BETWEEN\")## 非范围\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-date-picker clearable v-model=\"queryParams.${javaField}\" type=\"date\" value-format=\"yyyy-MM-dd\" placeholder=\"选择${comment}\" />\n      </el-form-item>\n    #else## 范围\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-date-picker v-model=\"queryParams.${javaField}\" style=\"width: 240px\" value-format=\"yyyy-MM-dd HH:mm:ss\" type=\"daterange\"\n                        range-separator=\"-\" start-placeholder=\"开始日期\" end-placeholder=\"结束日期\" :default-time=\"['00:00:00', '23:59:59']\" />\n      </el-form-item>\n    #end\n#end\n#end\n#end\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <!-- 操作工具栏 -->\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"openForm(undefined)\"\n                   v-hasPermi=\"['${permissionPrefix}:create']\">新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button type=\"warning\" plain icon=\"el-icon-download\" size=\"mini\" @click=\"handleExport\" :loading=\"exportLoading\"\n                   v-hasPermi=\"['${permissionPrefix}:export']\">导出</el-button>\n      </el-col>\n    ## 特殊：树表专属逻辑\n    #if ( $table.templateType == 2 )\n      <el-col :span=\"1.5\">\n        <el-button type=\"danger\" plain icon=\"el-icon-sort\" size=\"mini\" @click=\"toggleExpandAll\">\n          展开/折叠\n        </el-button>\n      </el-col>\n    #end\n    #if ($table.templateType != 2 && $deleteBatchEnable)\n      <el-col :span=\"1.5\">\n        <el-button\n            type=\"danger\"\n            plain\n            icon=\"el-icon-delete\"\n            size=\"mini\"\n            :disabled=\"isEmpty(checkedIds)\"\n            @click=\"handleDeleteBatch\"\n            v-hasPermi=\"['${permissionPrefix}:delete']\"\n        >\n          批量删除\n        </el-button>\n      </el-col>\n    #end\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n      ## 特殊：主子表专属逻辑\n      #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )\n      <el-table\n        row-key=\"id\"\n        v-loading=\"loading\"\n        :data=\"list\"\n        :stripe=\"true\"\n        :highlight-current-row=\"true\"\n        :show-overflow-tooltip=\"true\"\n        @current-change=\"handleCurrentChange\"\n        #if ($deleteBatchEnable)\n        @selection-change=\"handleRowCheckboxChange\"\n        #end\n      >\n          ## 特殊：树表专属逻辑\n      #elseif ( $table.templateType == 2 )\n      <el-table\n        v-loading=\"loading\"\n        :data=\"list\"\n        :stripe=\"true\"\n        :show-overflow-tooltip=\"true\"\n        v-if=\"refreshTable\"\n        row-key=\"id\"\n        :default-expand-all=\"isExpandAll\"\n        :tree-props=\"{children: 'children', hasChildren: 'hasChildren'}\"\n      >\n      #else\n      <el-table\n          v-loading=\"loading\"\n          :data=\"list\"\n          :stripe=\"true\"\n          :show-overflow-tooltip=\"true\"\n          #if ($deleteBatchEnable)\n          @selection-change=\"handleRowCheckboxChange\"\n          #end\n      >\n      #end\n      #if ($table.templateType != 2 && $deleteBatchEnable)\n        <el-table-column type=\"selection\" width=\"55\" />\n      #end\n      ## 特殊：主子表专属逻辑\n      #if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )\n        <!-- 子表的列表 -->\n        <el-table-column type=\"expand\">\n          <template #default=\"scope\">\n            <el-tabs value=\"$subClassNameVars.get(0)\">\n                #foreach ($subTable in $subTables)\n                    #set ($index = $foreach.count - 1)\n                    #set ($subClassNameVar = $subClassNameVars.get($index))\n                    #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n                    #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n                  <el-tab-pane label=\"${subTable.classComment}\" name=\"$subClassNameVar\">\n                    <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"scope.row.id\" />\n                  </el-tab-pane>\n                #end\n            </el-tabs>\n          </template>\n        </el-table-column>\n      #end\n#foreach($column in $columns)\n#if ($column.listOperationResult)\n    #set ($dictType=$column.dictType)\n    #set ($javaField = $column.javaField)\n    #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n    #set ($comment=$column.columnComment)\n#if ($column.javaType == \"LocalDateTime\")## 时间类型\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" width=\"180\">\n        <template v-slot=\"scope\">\n          <span>{{ parseTime(scope.row.${javaField}) }}</span>\n        </template>\n      </el-table-column>\n#elseif(\"\" != $column.dictType)## 数据字典\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.${column.javaField}\" />\n        </template>\n      </el-table-column>\n#else\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" />\n#end\n#end\n#end\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template v-slot=\"scope\">\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.${primaryColumn.javaField})\"\n                     v-hasPermi=\"['${permissionPrefix}:update']\">修改</el-button>\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                     v-hasPermi=\"['${permissionPrefix}:delete']\">删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n## 特殊：树表专属逻辑（树不需要分页）\n#if ( $table.templateType != 2 )\n    <!-- 分页组件 -->\n    <pagination v-show=\"total > 0\" :total=\"total\" :page.sync=\"queryParams.pageNo\" :limit.sync=\"queryParams.pageSize\"\n                @pagination=\"getList\"/>\n#end\n    <!-- 对话框(添加 / 修改) -->\n    <${simpleClassName}Form ref=\"formRef\" @success=\"getList\" />\n  ## 特殊：主子表专属逻辑\n  #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )\n    <!-- 子表的列表 -->\n      <el-tabs v-model=\"subTabsName\">\n          #foreach ($subTable in $subTables)\n              #set ($index = $foreach.count - 1)\n              #set ($subClassNameVar = $subClassNameVars.get($index))\n              #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n              #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n            <el-tab-pane label=\"${subTable.classComment}\" name=\"$subClassNameVar\">\n              <${subSimpleClassName}List v-if=\"currentRow.id\" :${subJoinColumn_strikeCase}=\"currentRow.id\" />\n            </el-tab-pane>\n          #end\n      </el-tabs>\n  #end\n  </div>\n</template>\n\n<script>\nimport * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';\nimport ${simpleClassName}Form from './${simpleClassName}Form.vue';\n#if ($hasImageUploadColumn)\nimport ImageUpload from '@/components/ImageUpload';\n#end\n#if ($hasFileUploadColumn)\nimport FileUpload from '@/components/FileUpload';\n#end\n#if ($hasEditorColumn)\nimport Editor from '@/components/Editor';\n#end\n## 特殊：主子表专属逻辑\n#if ( $table.templateType != 10 )\n#if ( $subTables && $subTables.size() > 0 )\n    #foreach ($subSimpleClassName in $subSimpleClassNames)\n    import ${subSimpleClassName}List from './components/${subSimpleClassName}List.vue';\n    #end\n#end\n#end\nexport default {\n  name: \"${simpleClassName}\",\n  components: {\n          ${simpleClassName}Form,\n## 特殊：主子表专属逻辑\n#if ( $table.templateType != 10 )\n#if ( $subTables && $subTables.size() > 0 )\n      #foreach ($subSimpleClassName in $subSimpleClassNames)\n          ${subSimpleClassName}List,\n      #end\n#end\n#end\n#if ($hasImageUploadColumn)\n    ImageUpload,\n#end\n#if ($hasFileUploadColumn)\n    FileUpload,\n#end\n#if ($hasEditorColumn)\n    Editor,\n#end\n  },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 导出遮罩层\n      exportLoading: false,\n      // 显示搜索条件\n      showSearch: true,\n      ## 特殊：树表专属逻辑（树不需要分页接口）\n      #if ( $table.templateType != 2 )\n        // 总条数\n        total: 0,\n      #end\n      // ${table.classComment}列表\n      list: [],\n      // 是否展开，默认全部展开\n      isExpandAll: true,\n      // 重新渲染表格状态\n      refreshTable: true,\n      // 选中行\n      currentRow: {},\n      checkedIds: [],\n      // 查询参数\n      queryParams: {\n        ## 特殊：树表专属逻辑（树不需要分页接口）\n        #if ( $table.templateType != 2 )\n            pageNo: 1,\n            pageSize: 10,\n        #end\n        #foreach ($column in $columns)\n        #if ($column.listOperation)\n        #if ($column.listOperationCondition != 'BETWEEN')\n        $column.javaField: null,\n        #end\n        #if ($column.htmlType == \"datetime\" && $column.listOperationCondition == \"BETWEEN\")\n        $column.javaField: [],\n        #end\n        #end\n        #end\n      },\n        ## 特殊：主子表专属逻辑-erp\n        #if ( $table.templateType == 11)\n            #if ( $subTables && $subTables.size() > 0 )\n              /** 子表的列表 */\n              subTabsName: '$subClassNameVars.get(0)'\n            #end\n        #end\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询列表 */\n    async getList() {\n      try {\n      this.loading = true;\n      ## 特殊：树表专属逻辑（树不需要分页接口）\n      #if ( $table.templateType == 2 )\n       const res = await ${simpleClassName}Api.get${simpleClassName}List(this.queryParams);\n       this.list = this.handleTree(res.data, 'id', '${treeParentColumn.javaField}');\n      #else\n        const res = await ${simpleClassName}Api.get${simpleClassName}Page(this.queryParams);\n        this.list = res.data.list;\n        this.total = res.data.total;\n      #end\n      } finally {\n        this.loading = false;\n      }\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNo = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 添加/修改操作 */\n    openForm(id) {\n      this.#[[$]]#refs[\"formRef\"].open(id);\n    },\n    /** 删除按钮操作 */\n    async handleDelete(row) {\n      const ${primaryColumn.javaField} = row.${primaryColumn.javaField};\n      await this.#[[$modal]]#.confirm('是否确认删除${table.classComment}编号为\"' + ${primaryColumn.javaField} + '\"的数据项?')\n      try {\n       await ${simpleClassName}Api.delete${simpleClassName}(${primaryColumn.javaField});\n       await this.getList();\n       this.#[[$modal]]#.msgSuccess(\"删除成功\");\n      } catch {}\n    },\n    #if ($table.templateType != 2 && $deleteBatchEnable)\n    /** 批量删除${table.classComment} */\n    async handleDeleteBatch() {\n      await this.#[[$modal]]#.confirm('是否确认删除?')\n      try {\n        await ${simpleClassName}Api.delete${simpleClassName}List(this.checkedIds);\n        this.checkedIds = [];\n        await this.getList();\n        this.#[[$modal]]#.msgSuccess(\"删除成功\");\n      } catch {}\n    },\n    handleRowCheckboxChange(records) {\n      this.checkedIds = records.map((item) => item.id);\n    },\n    #end\n    /** 导出按钮操作 */\n    async handleExport() {\n      await this.#[[$modal]]#.confirm('是否确认导出所有${table.classComment}数据项?');\n      try {\n        this.exportLoading = true;\n        const data = await ${simpleClassName}Api.export${simpleClassName}Excel(this.queryParams);\n        this.#[[$]]#download.excel(data, '${table.classComment}.xls');\n      } catch {\n      } finally {\n        this.exportLoading = false;\n      }\n    },\n      ## 特殊：主子表专属逻辑\n      #if ( $table.templateType == 11 )\n        /** 选中行操作 */\n        handleCurrentChange(row) {\n         this.currentRow = row;\n        #if ( $subTables && $subTables.size() > 0 )\n          /** 子表的列表 */\n          this.subTabsName = '$subClassNameVars.get(0)';\n        #end\n        },\n      #end\n      ## 特殊：树表专属逻辑\n      #if ( $table.templateType == 2 )\n        /** 展开/折叠操作 */\n        toggleExpandAll() {\n          this.refreshTable = false\n          this.isExpandAll = !this.isExpandAll\n          this.$nextTick(function () {\n            this.refreshTable = true\n          })\n        }\n      #end\n  }\n};\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/api/api.ts.vm",
    "content": "import request from '@/config/axios'\nimport type { Dayjs } from 'dayjs';\n#set ($baseURL = \"/${table.moduleName}/${simpleClassName_strikeCase}\")\n\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n  #set ($index = $foreach.count - 1)\n  #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n  #set ($subColumns = $subColumnsList.get($index))##当前字段数组\n/** ${subTable.classComment}信息 */\nexport interface ${subSimpleClassName} {\n  #foreach ($column in $subColumns)\n    #if ($column.createOperation || $column.updateOperation)\n      #if(${column.javaType.toLowerCase()} == \"long\" || ${column.javaType.toLowerCase()} == \"integer\" || ${column.javaType.toLowerCase()} == \"short\" || ${column.javaType.toLowerCase()} == \"double\" || ${column.javaType.toLowerCase()} == \"bigdecimal\")\n          ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}\n      #elseif(${column.javaType.toLowerCase()} == \"date\" || ${column.javaType.toLowerCase()} == \"localdate\" || ${column.javaType.toLowerCase()} == \"localdatetime\")\n          ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}\n      #else\n          ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}\n      #end\n    #end\n  #end\n}\n\n#end\n/** ${table.classComment}信息 */\nexport interface ${simpleClassName} {\n  #foreach ($column in $columns)\n    #if ($column.createOperation || $column.updateOperation)\n      #if(${column.javaType.toLowerCase()} == \"long\" || ${column.javaType.toLowerCase()} == \"integer\" || ${column.javaType.toLowerCase()} == \"short\" || ${column.javaType.toLowerCase()} == \"double\" || ${column.javaType.toLowerCase()} == \"bigdecimal\")\n          ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}\n      #elseif(${column.javaType.toLowerCase()} == \"date\" || ${column.javaType.toLowerCase()} == \"localdate\" || ${column.javaType.toLowerCase()} == \"localdatetime\")\n          ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}\n      #else\n          ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}\n      #end\n    #end\n  #end\n  #if ( $table.templateType == 2 )\n    children?: ${simpleClassName}[];\n  #end\n  ## 特殊：主子表专属逻辑\n  #if ( $table.templateType == 10 || $table.templateType == 12 )\n    #foreach ($subTable in $subTables)\n      #set ($index = $foreach.count - 1)\n      #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n      #if ( $subTable.subJoinMany )\n          ${subSimpleClassName.toLowerCase()}s?: ${subSimpleClassName}[]\n      #else\n          ${subSimpleClassName.toLowerCase()}?: ${subSimpleClassName}\n      #end\n    #end\n  #end\n}\n\n// ${table.classComment} API\nexport const ${simpleClassName}Api = {\n#if ( $table.templateType != 2 )\n  // 查询${table.classComment}分页\n  get${simpleClassName}Page: async (params: any) => {\n    return await request.get({ url: `${baseURL}/page`, params })\n  },\n#else\n  // 查询${table.classComment}列表\n  get${simpleClassName}List: async (params) => {\n    return await request.get({ url: `${baseURL}/list`, params })\n  },\n#end\n\n  // 查询${table.classComment}详情\n  get${simpleClassName}: async (id: number) => {\n    return await request.get({ url: `${baseURL}/get?id=` + id })\n  },\n\n  // 新增${table.classComment}\n  create${simpleClassName}: async (data: ${simpleClassName}) => {\n    return await request.post({ url: `${baseURL}/create`, data })\n  },\n\n  // 修改${table.classComment}\n  update${simpleClassName}: async (data: ${simpleClassName}) => {\n    return await request.put({ url: `${baseURL}/update`, data })\n  },\n\n  // 删除${table.classComment}\n  delete${simpleClassName}: async (id: number) => {\n    return await request.delete({ url: `${baseURL}/delete?id=` + id })\n  },\n\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n  /** 批量删除${table.classComment} */\n  delete${simpleClassName}List: async (ids: number[]) => {\n    return await request.delete({ url: `${baseURL}/delete-list?ids=${ids.join(',')}` })\n  },\n#end\n\n  // 导出${table.classComment} Excel\n  export${simpleClassName}: async (params) => {\n    return await request.download({ url: `${baseURL}/export-excel`, params })\n  },\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n#set ($subSimpleClassName = $subSimpleClassNames.get($index))\n#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段\n#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n#set ($subClassNameVar = $subClassNameVars.get($index))\n\n// ==================== 子表（$subTable.classComment） ====================\n## 情况一：MASTER_ERP 时，需要分查询页子表\n#if ( $table.templateType == 11 )\n\n  // 获得${subTable.classComment}分页\n  get${subSimpleClassName}Page: async (params) => {\n    return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/page`, params })\n  },\n## 情况二：非 MASTER_ERP 时，需要列表查询子表\n#else\n  #if ( $subTable.subJoinMany )\n\n  // 获得${subTable.classComment}列表\n  get${subSimpleClassName}ListBy${SubJoinColumnName}: async (${subJoinColumn.javaField}) => {\n    return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField} })\n  },\n  #else\n\n  // 获得${subTable.classComment}\n  get${subSimpleClassName}By${SubJoinColumnName}: async (${subJoinColumn.javaField}) => {\n    return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField} })\n  },\n  #end\n#end\n## 特殊：MASTER_ERP 时，支持单个的新增、修改、删除操作\n#if ( $table.templateType == 11 )\n  // 新增${subTable.classComment}\n  create${subSimpleClassName}: async (data: ${subSimpleClassName}) => {\n    return await request.post({ url: `${baseURL}/${subSimpleClassName_strikeCase}/create`, data })\n  },\n\n  // 修改${subTable.classComment}\n  update${subSimpleClassName}: async (data: ${subSimpleClassName}) => {\n    return await request.put({ url: `${baseURL}/${subSimpleClassName_strikeCase}/update`, data })\n  },\n\n  // 删除${subTable.classComment}\n  delete${subSimpleClassName}: async (id: number) => {\n    return await request.delete({ url: `${baseURL}/${subSimpleClassName_strikeCase}/delete?id=` + id })\n  },\n\n  #if ($deleteBatchEnable)\n  /** 批量删除${subTable.classComment} */\n  delete${subSimpleClassName}List: async (ids: number[]) => {\n    return await request.delete({ url: `${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}` })\n  },\n  #end\n\n  // 获得${subTable.classComment}\n  get${subSimpleClassName}: async (id: number) => {\n    return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get?id=` + id })\n  },\n#end\n#end\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm",
    "content": "#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n<template>\n  <Dialog :title=\"dialogTitle\" v-model=\"dialogVisible\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"formRules\"\n      label-width=\"100px\"\n      v-loading=\"formLoading\"\n    >\n#foreach($column in $subColumns)\n    #if ($column.createOperation || $column.updateOperation)\n        #set ($dictType = $column.dictType)\n        #set ($javaField = $column.javaField)\n        #set ($javaType = $column.javaType)\n        #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n        #set ($comment = $column.columnComment)\n        #set ($dictMethod = \"getDictOptions\")## 计算使用哪个 dict 字典方法\n        #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n            #set ($dictMethod = \"getIntDictOptions\")\n        #elseif ($javaType == \"String\")\n            #set ($dictMethod = \"getStrDictOptions\")\n        #elseif ($javaType == \"Boolean\")\n            #set ($dictMethod = \"getBoolDictOptions\")\n        #end\n        #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n        #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-input v-model=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n      </el-form-item>\n        #elseif($column.htmlType == \"imageUpload\")## 图片上传\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <UploadImg v-model=\"formData.${javaField}\" />\n      </el-form-item>\n        #elseif($column.htmlType == \"fileUpload\")## 文件上传\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <UploadFile v-model=\"formData.${javaField}\" />\n      </el-form-item>\n        #elseif($column.htmlType == \"editor\")## 文本编辑器\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <Editor v-model=\"formData.${javaField}\" height=\"150px\" />\n      </el-form-item>\n        #elseif($column.htmlType == \"select\")## 下拉框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-select v-model=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                #if (\"\" != $dictType)## 有数据字典\n          <el-option\n            v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n                #else##没数据字典\n          <el-option label=\"请选择字典生成\" value=\"\" />\n                #end\n        </el-select>\n      </el-form-item>\n        #elseif($column.htmlType == \"checkbox\")## 多选框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-checkbox-group v-model=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n          <el-checkbox\n            v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n                #else##没数据字典\n          <el-checkbox label=\"请选择字典生成\" />\n                #end\n        </el-checkbox-group>\n      </el-form-item>\n        #elseif($column.htmlType == \"radio\")## 单选框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-radio-group v-model=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n          <el-radio\n            v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n            :key=\"dict.value\"\n            :label=\"dict.value\"\n          >\n            {{ dict.label }}\n          </el-radio>\n                #else##没数据字典\n          <el-radio value=\"1\">请选择字典生成</el-radio>\n                #end\n        </el-radio-group>\n      </el-form-item>\n        #elseif($column.htmlType == \"datetime\")## 时间框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-date-picker\n          v-model=\"formData.${javaField}\"\n          type=\"date\"\n          value-format=\"x\"\n          placeholder=\"选择${comment}\"\n        />\n      </el-form-item>\n        #elseif($column.htmlType == \"textarea\")## 文本框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-input v-model=\"formData.${javaField}\" type=\"textarea\" placeholder=\"请输入${comment}\" />\n      </el-form-item>\n        #end\n    #end\n#end\n    </el-form>\n    <template #footer>\n      <el-button @click=\"submitForm\" type=\"primary\" :disabled=\"formLoading\">确 定</el-button>\n      <el-button @click=\"dialogVisible = false\">取 消</el-button>\n    </template>\n  </Dialog>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport { ${simpleClassName}Api, ${subSimpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst dialogVisible = ref(false) // 弹窗的是否展示\nconst dialogTitle = ref('') // 弹窗的标题\nconst formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\nconst formType = ref('') // 表单的类型：create - 新增；update - 修改\nconst formData = ref({\n#foreach ($column in $subColumns)\n    #if ($column.createOperation || $column.updateOperation)\n      #if ($column.htmlType == \"checkbox\")\n  $column.javaField: [],\n      #else\n  $column.javaField: undefined,\n      #end\n    #end\n#end\n})\nconst formRules = reactive({\n#foreach ($column in $subColumns)\n    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n        #set($comment=$column.columnComment)\n  $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n    #end\n#end\n})\nconst formRef = ref() // 表单 Ref\n\n/** 打开弹窗 */\nconst open = async (type: string, id?: number, ${subJoinColumn.javaField}?: number) => {\n  dialogVisible.value = true\n  dialogTitle.value = t('action.' + type)\n  formType.value = type\n  resetForm()\n  formData.value.${subJoinColumn.javaField} = ${subJoinColumn.javaField}  as any\n  // 修改时，设置数据\n  if (id) {\n    formLoading.value = true\n    try {\n      formData.value = await ${simpleClassName}Api.get${subSimpleClassName}(id)\n    } finally {\n      formLoading.value = false\n    }\n  }\n}\ndefineExpose({ open }) // 提供 open 方法，用于打开弹窗\n\n/** 提交表单 */\nconst emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调\nconst submitForm = async () => {\n  // 校验表单\n  await formRef.value.validate()\n  // 提交请求\n  formLoading.value = true\n  try {\n    const data = formData.value as unknown as ${subSimpleClassName}\n    if (formType.value === 'create') {\n      await ${simpleClassName}Api.create${subSimpleClassName}(data)\n      message.success(t('common.createSuccess'))\n    } else {\n      await ${simpleClassName}Api.update${subSimpleClassName}(data)\n      message.success(t('common.updateSuccess'))\n    }\n    dialogVisible.value = false\n    // 发送操作成功的事件\n    emit('success')\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n#foreach ($column in $subColumns)\n  #if ($column.createOperation || $column.updateOperation)\n      #if ($column.htmlType == \"checkbox\")\n    $column.javaField: [],\n      #else\n    $column.javaField: undefined,\n      #end\n  #end\n#end\n  }\n  formRef.value?.resetFields()\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm",
    "content": "## 主表的 normal 和 inner 使用相同的 form 表单\n#parse(\"codegen/vue3/views/components/form_sub_normal.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n<template>\n#if ( $subTable.subJoinMany )## 情况一：一对多，table + form\n  <el-form\n    ref=\"formRef\"\n    :model=\"formData\"\n    :rules=\"formRules\"\n    v-loading=\"formLoading\"\n    label-width=\"0px\"\n    :inline-message=\"true\"\n  >\n    <el-table :data=\"formData\" class=\"-mt-10px\">\n      <el-table-column label=\"序号\" type=\"index\" width=\"100\" />\n#foreach($column in $subColumns)\n    #if ($column.createOperation || $column.updateOperation)\n        #set ($dictType = $column.dictType)\n        #set ($javaField = $column.javaField)\n        #set ($javaType = $column.javaType)\n        #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n        #set ($comment = $column.columnComment)\n        #set ($dictMethod = \"getDictOptions\")## 计算使用哪个 dict 字典方法\n        #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n            #set ($dictMethod = \"getIntDictOptions\")\n        #elseif ($javaType == \"String\")\n            #set ($dictMethod = \"getStrDictOptions\")\n        #elseif ($javaType == \"Boolean\")\n            #set ($dictMethod = \"getBoolDictOptions\")\n        #end\n        #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n        #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n      <el-table-column label=\"${comment}\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n            <el-input v-model=\"row.${javaField}\" placeholder=\"请输入${comment}\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n        #elseif($column.htmlType == \"imageUpload\")## 图片上传\n      <el-table-column label=\"${comment}\" min-width=\"200\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n            <UploadImg v-model=\"row.${javaField}\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n        #elseif($column.htmlType == \"fileUpload\")## 文件上传\n      <el-table-column label=\"${comment}\" min-width=\"200\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n            <UploadFile v-model=\"row.${javaField}\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n        #elseif($column.htmlType == \"editor\")## 文本编辑器\n      <el-table-column label=\"${comment}\" min-width=\"400\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n            <Editor v-model=\"row.${javaField}\" height=\"150px\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n        #elseif($column.htmlType == \"select\")## 下拉框\n      <el-table-column label=\"${comment}\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n            <el-select v-model=\"row.${javaField}\" placeholder=\"请选择${comment}\">\n              #if (\"\" != $dictType)## 有数据字典\n                <el-option\n                  v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n                  :key=\"dict.value\"\n                  :label=\"dict.label\"\n                  :value=\"dict.value\"\n                />\n              #else##没数据字典\n                <el-option label=\"请选择字典生成\" value=\"\" />\n              #end\n            </el-select>\n          </el-form-item>\n        </template>\n      </el-table-column>\n        #elseif($column.htmlType == \"checkbox\")## 多选框\n      <el-table-column label=\"${comment}\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n            <el-checkbox-group v-model=\"row.${javaField}\">\n              #if (\"\" != $dictType)## 有数据字典\n                <el-checkbox\n                  v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n                  :key=\"dict.value\"\n                  :label=\"dict.label\"\n                  :value=\"dict.value\"\n                />\n              #else##没数据字典\n                <el-checkbox label=\"请选择字典生成\" />\n              #end\n            </el-checkbox-group>\n          </el-form-item>\n        </template>\n      </el-table-column>\n        #elseif($column.htmlType == \"radio\")## 单选框\n      <el-table-column label=\"${comment}\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n            <el-radio-group v-model=\"row.${javaField}\">\n              #if (\"\" != $dictType)## 有数据字典\n                <el-radio\n                  v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n                  :key=\"dict.value\"\n                  :label=\"dict.value\"\n                >\n                  {{ dict.label }}\n                </el-radio>\n              #else##没数据字典\n                <el-radio value=\"1\">请选择字典生成</el-radio>\n              #end\n            </el-radio-group>\n          </el-form-item>\n        </template>\n      </el-table-column>\n        #elseif($column.htmlType == \"datetime\")## 时间框\n      <el-table-column label=\"${comment}\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n            <el-date-picker\n              v-model=\"row.${javaField}\"\n              type=\"date\"\n              value-format=\"x\"\n              placeholder=\"选择${comment}\"\n            />\n          </el-form-item>\n        </template>\n      </el-table-column>\n        #elseif($column.htmlType == \"textarea\")## 文本框\n      <el-table-column label=\"${comment}\" min-width=\"200\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.${javaField}`\" :rules=\"formRules.${javaField}\" class=\"mb-0px!\">\n            <el-input v-model=\"row.${javaField}\" type=\"textarea\" placeholder=\"请输入${comment}\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n        #end\n    #end\n#end\n      <el-table-column align=\"center\" fixed=\"right\" label=\"操作\" width=\"60\">\n        <template #default=\"{ $index }\">\n          <el-button @click=\"handleDelete($index)\" link>—</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n  </el-form>\n  <el-row justify=\"center\" class=\"mt-3\">\n    <el-button @click=\"handleAdd\" round>+ 添加${subTable.classComment}</el-button>\n  </el-row>\n#else## 情况二：一对一，form\n  <el-form\n    ref=\"formRef\"\n    :model=\"formData\"\n    :rules=\"formRules\"\n    label-width=\"100px\"\n    v-loading=\"formLoading\"\n  >\n#foreach($column in $subColumns)\n  #if ($column.createOperation || $column.updateOperation)\n  #set ($dictType = $column.dictType)\n      #set ($javaField = $column.javaField)\n      #set ($javaType = $column.javaType)\n      #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n      #set ($comment = $column.columnComment)\n      #set ($dictMethod = \"getDictOptions\")## 计算使用哪个 dict 字典方法\n      #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n        #set ($dictMethod = \"getIntDictOptions\")\n      #elseif ($javaType == \"String\")\n          #set ($dictMethod = \"getStrDictOptions\")\n      #elseif ($javaType == \"Boolean\")\n          #set ($dictMethod = \"getBoolDictOptions\")\n      #end\n      #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n      #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n      <el-input v-model=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n    </el-form-item>\n      #elseif($column.htmlType == \"imageUpload\")## 图片上传\n    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n      <UploadImg v-model=\"formData.${javaField}\" />\n    </el-form-item>\n      #elseif($column.htmlType == \"fileUpload\")## 文件上传\n    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n      <UploadFile v-model=\"formData.${javaField}\" />\n    </el-form-item>\n      #elseif($column.htmlType == \"editor\")## 文本编辑器\n    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n      <Editor v-model=\"formData.${javaField}\" height=\"150px\" />\n    </el-form-item>\n      #elseif($column.htmlType == \"select\")## 下拉框\n    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n      <el-select v-model=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n              #if (\"\" != $dictType)## 有数据字典\n        <el-option\n          v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n          :key=\"dict.value\"\n          :label=\"dict.label\"\n          :value=\"dict.value\"\n        />\n              #else##没数据字典\n        <el-option label=\"请选择字典生成\" value=\"\" />\n              #end\n      </el-select>\n    </el-form-item>\n      #elseif($column.htmlType == \"checkbox\")## 多选框\n    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n      <el-checkbox-group v-model=\"formData.${javaField}\">\n              #if (\"\" != $dictType)## 有数据字典\n        <el-checkbox\n          v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n          :key=\"dict.value\"\n          :label=\"dict.label\"\n          :value=\"dict.value\"\n        />\n              #else##没数据字典\n        <el-checkbox label=\"请选择字典生成\" />\n              #end\n      </el-checkbox-group>\n    </el-form-item>\n      #elseif($column.htmlType == \"radio\")## 单选框\n    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n      <el-radio-group v-model=\"formData.${javaField}\">\n              #if (\"\" != $dictType)## 有数据字典\n        <el-radio\n          v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n          :key=\"dict.value\"\n          :label=\"dict.value\"\n          >\n          {{ dict.label }}\n        </el-radio>\n              #else##没数据字典\n        <el-radio value=\"1\">请选择字典生成</el-radio>\n              #end\n      </el-radio-group>\n    </el-form-item>\n      #elseif($column.htmlType == \"datetime\")## 时间框\n    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n      <el-date-picker\n        v-model=\"formData.${javaField}\"\n        type=\"date\"\n        value-format=\"x\"\n        placeholder=\"选择${comment}\"\n      />\n    </el-form-item>\n      #elseif($column.htmlType == \"textarea\")## 文本框\n    <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n      <el-input v-model=\"formData.${javaField}\" type=\"textarea\" placeholder=\"请输入${comment}\" />\n    </el-form-item>\n      #end\n  #end\n#end\n  </el-form>\n#end\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport { ${simpleClassName}Api } from '@/api/${table.moduleName}/${table.businessName}'\n\nconst props = defineProps<{\n  ${subJoinColumn.javaField}: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\nconst formLoading = ref(false) // 表单的加载中\nconst formData = ref<any#if ( $subTable.subJoinMany )[]#end>(#if ( $subTable.subJoinMany )[]#else{}#end)\nconst formRules = reactive({\n#foreach ($column in $subColumns)\n    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n        #set($comment=$column.columnComment)\n  $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n    #end\n#end\n})\nconst formRef = ref() // 表单 Ref\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.${subJoinColumn.javaField},\n  async (val) => {\n    // 1. 重置表单\n#if ( $subTable.subJoinMany )\n    formData.value = []\n#else\n    formData.value = {\n    #foreach ($column in $subColumns)\n      #if ($column.createOperation || $column.updateOperation)\n        #if ($column.htmlType == \"checkbox\")\n      $column.javaField: [],\n        #else\n      $column.javaField: undefined,\n        #end\n      #end\n    #end\n    }\n#end\n    // 2. val 非空，则加载数据\n    if (!val) {\n      return;\n    }\n    try {\n      formLoading.value = true\n#if ( $subTable.subJoinMany )\n      formData.value = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(val)\n#else\n      const data = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(val)\n      if (!data) {\n        return\n      }\n      formData.value = data\n#end\n    } finally {\n      formLoading.value = false\n    }\n  },\n  { immediate: true }\n)\n#if ( $subTable.subJoinMany )\n\n/** 新增按钮操作 */\nconst handleAdd = () => {\n  const row = {\n#foreach ($column in $subColumns)\n    #if ($column.createOperation || $column.updateOperation)\n      #if ($column.htmlType == \"checkbox\")\n    $column.javaField: [],\n      #else\n    $column.javaField: undefined,\n      #end\n  #end\n#end\n  }\n  row.${subJoinColumn.javaField} = props.${subJoinColumn.javaField} as any\n  formData.value.push(row)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = (index) => {\n  formData.value.splice(index, 1)\n}\n#end\n\n/** 表单校验 */\nconst validate = () => {\n  return formRef.value.validate()\n}\n\n/** 表单值 */\nconst getData = () => {\n  return formData.value\n}\n\ndefineExpose({ validate, getData })\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n<template>\n  <!-- 列表 -->\n  <ContentWrap>\n#if ($table.templateType == 11)\n    <el-button\n      type=\"primary\"\n      plain\n      @click=\"openForm('create')\"\n      v-hasPermi=\"['${permissionPrefix}:create']\"\n    >\n      <Icon icon=\"ep:plus\" class=\"mr-5px\" /> 新增\n    </el-button>\n    #if ($deleteBatchEnable)\n      <el-button\n          type=\"danger\"\n          plain\n          :disabled=\"isEmpty(checkedIds)\"\n          @click=\"handleDeleteBatch\"\n          v-hasPermi=\"['${permissionPrefix}:delete']\"\n      >\n        <Icon icon=\"ep:delete\" class=\"mr-5px\" /> 批量删除\n      </el-button>\n    #end\n#end\n    <el-table\n        row-key=\"id\"\n        v-loading=\"loading\"\n        :data=\"list\"\n        :stripe=\"true\"\n        :show-overflow-tooltip=\"true\"\n        #if ($table.templateType == 11 && $deleteBatchEnable)\n        @selection-change=\"handleRowCheckboxChange\"\n        #end\n    >\n        #if ($table.templateType == 11 && $deleteBatchEnable)\n          <el-table-column type=\"selection\" width=\"55\" />\n        #end\n      #foreach($column in $subColumns)\n      #if ($column.listOperationResult)\n        #set ($dictType=$column.dictType)\n        #set ($javaField = $column.javaField)\n        #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n        #set ($comment=$column.columnComment)\n        #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n        #elseif ($column.javaType == \"LocalDateTime\")## 时间类型\n      <el-table-column\n        label=\"${comment}\"\n        align=\"center\"\n        prop=\"${javaField}\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n        #elseif($column.dictType && \"\" != $column.dictType)## 数据字典\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.${column.javaField}\" />\n        </template>\n      </el-table-column>\n        #else\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" />\n        #end\n      #end\n    #end\n    #if ($table.templateType == 11)\n      <el-table-column label=\"操作\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button\n            link\n            type=\"primary\"\n            @click=\"openForm('update', scope.row.id)\"\n            v-hasPermi=\"['${permissionPrefix}:update']\"\n          >\n            编辑\n          </el-button>\n          <el-button\n            link\n            type=\"danger\"\n            @click=\"handleDelete(scope.row.id)\"\n            v-hasPermi=\"['${permissionPrefix}:delete']\"\n          >\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n    #end\n    </el-table>\n    #if ($table.templateType == 11)\n    <!-- 分页 -->\n    <Pagination\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNo\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n    #end\n  </ContentWrap>\n#if ($table.templateType == 11)\n    <!-- 表单弹窗：添加/修改 -->\n    <${subSimpleClassName}Form ref=\"formRef\" @success=\"getList\" />\n#end\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport { dateFormatter } from '@/utils/formatTime'\n#if ($deleteBatchEnable)\nimport { isEmpty } from '@/utils/is'\n#end\nimport { ${simpleClassName}Api, ${subSimpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\n#if ($table.templateType == 11)\nimport ${subSimpleClassName}Form from './${subSimpleClassName}Form.vue'\n#end\n\n#if ($table.templateType == 11)\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n#end\nconst props = defineProps<{\n  ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\nconst loading = ref(false) // 列表的加载中\nconst list = ref([]) // 列表的数据\n#if ($table.templateType == 11)\nconst total = ref(0) // 列表的总页数\nconst queryParams = reactive({\n  pageNo: 1,\n  pageSize: 10,\n  ${subJoinColumn.javaField}: undefined as unknown\n})\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.${subJoinColumn.javaField},\n  (val: number) => {\n    if (!val) {\n      list.value = [] // 清空列表\n      return\n    }\n    queryParams.${subJoinColumn.javaField} = val\n    handleQuery()\n  },\n    { immediate: true, deep: true }\n)\n#end\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n#if ($table.templateType == 11)\n    const data = await ${simpleClassName}Api.get${subSimpleClassName}Page(queryParams)\n    list.value = data.list\n    total.value = data.total\n#else\n  #if ( $subTable.subJoinMany )\n    list.value = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField})\n  #else\n    const data = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField})\n    if (!data) {\n      return\n    }\n    list.value.push(data)\n  #end\n#end\n  } finally {\n    loading.value = false\n  }\n}\n\n#if ($table.templateType == 11)\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 添加/修改操作 */\nconst formRef = ref()\nconst openForm = (type: string, id?: number) => {\n  if (!props.${subJoinColumn.javaField}) {\n    message.error('请选择一个${table.classComment}')\n    return\n  }\n  formRef.value.open(type, id, props.${subJoinColumn.javaField})\n}\n\n/** 删除按钮操作 */\nconst handleDelete = async (id: number) => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    // 发起删除\n    await ${simpleClassName}Api.delete${subSimpleClassName}(id)\n    message.success(t('common.delSuccess'))\n    // 刷新列表\n    await getList()\n  } catch {}\n}\n\n#if ($table.templateType == 11 && $deleteBatchEnable)\n/** 批量删除${subTable.classComment} */\nconst handleDeleteBatch = async () => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    await ${simpleClassName}Api.delete${subSimpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    message.success(t('common.delSuccess'))\n    await getList();\n  } catch {}\n}\n\nconst checkedIds = ref<number[]>([])\nconst handleRowCheckboxChange = (records: ${subSimpleClassName}[]) => {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n#end\n#if ($table.templateType != 11)\n\n/** 初始化 **/\nonMounted(() => {\n  getList()\n})\n#end\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm",
    "content": "## 子表的 erp 和 inner 使用相似的 list 列表，差异主要两点：\n## 1）inner 使用 list 不分页，erp 使用 page 分页\n## 2）erp 支持单个子表的新增、修改、删除，inner 不支持\n#parse(\"codegen/vue3/views/components/list_sub_erp.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/form.vue.vm",
    "content": "<template>\n  <Dialog :title=\"dialogTitle\" v-model=\"dialogVisible\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"formRules\"\n      label-width=\"100px\"\n      v-loading=\"formLoading\"\n    >\n#foreach($column in $columns)\n    #if ($column.createOperation || $column.updateOperation)\n        #set ($dictType = $column.dictType)\n        #set ($javaField = $column.javaField)\n        #set ($javaType = $column.javaType)\n        #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n        #set ($comment = $column.columnComment)\n        #set ($dictMethod = \"getDictOptions\")## 计算使用哪个 dict 字典方法\n        #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n            #set ($dictMethod = \"getIntDictOptions\")\n        #elseif ($javaType == \"String\")\n            #set ($dictMethod = \"getStrDictOptions\")\n        #elseif ($javaType == \"Boolean\")\n            #set ($dictMethod = \"getBoolDictOptions\")\n        #end\n        #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-tree-select\n          v-model=\"formData.${javaField}\"\n          :data=\"${classNameVar}Tree\"\n          #if ($treeNameColumn.javaField == \"name\")\n          :props=\"defaultProps\"\n          #else\n          :props=\"{...defaultProps, label: '$treeNameColumn.javaField'}\"\n          #end\n          check-strictly\n          default-expand-all\n          placeholder=\"请选择${comment}\"\n        />\n      </el-form-item>\n        #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-input v-model=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n      </el-form-item>\n        #elseif($column.htmlType == \"imageUpload\")## 图片上传\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <UploadImg v-model=\"formData.${javaField}\" />\n      </el-form-item>\n        #elseif($column.htmlType == \"fileUpload\")## 文件上传\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <UploadFile v-model=\"formData.${javaField}\" />\n      </el-form-item>\n        #elseif($column.htmlType == \"editor\")## 文本编辑器\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <Editor v-model=\"formData.${javaField}\" height=\"150px\" />\n      </el-form-item>\n        #elseif($column.htmlType == \"select\")## 下拉框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-select v-model=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                #if (\"\" != $dictType)## 有数据字典\n          <el-option\n            v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n                #else##没数据字典\n          <el-option label=\"请选择字典生成\" value=\"\" />\n                #end\n        </el-select>\n      </el-form-item>\n        #elseif($column.htmlType == \"checkbox\")## 多选框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-checkbox-group v-model=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n          <el-checkbox\n            v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n                #else##没数据字典\n          <el-checkbox label=\"请选择字典生成\" />\n                #end\n        </el-checkbox-group>\n      </el-form-item>\n        #elseif($column.htmlType == \"radio\")## 单选框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-radio-group v-model=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n          <el-radio\n            v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n            :key=\"dict.value\"\n            :label=\"dict.value\"\n          >\n            {{ dict.label }}\n          </el-radio>\n                #else##没数据字典\n          <el-radio value=\"1\">请选择字典生成</el-radio>\n                #end\n        </el-radio-group>\n      </el-form-item>\n        #elseif($column.htmlType == \"datetime\")## 时间框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-date-picker\n          v-model=\"formData.${javaField}\"\n          type=\"date\"\n          value-format=\"x\"\n          placeholder=\"选择${comment}\"\n        />\n      </el-form-item>\n        #elseif($column.htmlType == \"textarea\")## 文本框\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-input v-model=\"formData.${javaField}\" type=\"textarea\" placeholder=\"请输入${comment}\" />\n      </el-form-item>\n        #end\n    #end\n#end\n    </el-form>\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n    <!-- 子表的表单 -->\n    <el-tabs v-model=\"subTabsName\">\n    #foreach ($subTable in $subTables)\n      #set ($index = $foreach.count - 1)\n      #set ($subClassNameVar = $subClassNameVars.get($index))\n      #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n      #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n      <el-tab-pane label=\"${subTable.classComment}\" name=\"$subClassNameVar\">\n        <${subSimpleClassName}Form ref=\"${subClassNameVar}FormRef\" :${subJoinColumn_strikeCase}=\"formData.id\" />\n      </el-tab-pane>\n    #end\n    </el-tabs>\n#end\n    <template #footer>\n      <el-button @click=\"submitForm\" type=\"primary\" :disabled=\"formLoading\">确 定</el-button>\n      <el-button @click=\"dialogVisible = false\">取 消</el-button>\n    </template>\n  </Dialog>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport { ${simpleClassName}Api, ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\nimport { defaultProps, handleTree } from '@/utils/tree'\n#end\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n#foreach ($subSimpleClassName in $subSimpleClassNames)\nimport ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue'\n#end\n#end\n\n/** ${table.classComment} 表单 */\ndefineOptions({ name: '${simpleClassName}Form' })\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst dialogVisible = ref(false) // 弹窗的是否展示\nconst dialogTitle = ref('') // 弹窗的标题\nconst formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\nconst formType = ref('') // 表单的类型：create - 新增；update - 修改\nconst formData = ref({\n#foreach ($column in $columns)\n    #if ($column.createOperation || $column.updateOperation)\n      #if ($column.htmlType == \"checkbox\")\n  $column.javaField: [],\n      #else\n  $column.javaField: undefined,\n      #end\n    #end\n#end\n})\nconst formRules = reactive({\n#foreach ($column in $columns)\n    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n        #set($comment=$column.columnComment)\n  $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n    #end\n#end\n})\nconst formRef = ref() // 表单 Ref\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\nconst ${classNameVar}Tree = ref() // 树形结构\n#end\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n#if ( $subTables && $subTables.size() > 0 )\n\n/** 子表的表单 */\nconst subTabsName = ref('$subClassNameVars.get(0)')\n#foreach ($subClassNameVar in $subClassNameVars)\nconst ${subClassNameVar}FormRef = ref()\n#end\n#end\n#end\n\n/** 打开弹窗 */\nconst open = async (type: string, id?: number) => {\n  dialogVisible.value = true\n  dialogTitle.value = t('action.' + type)\n  formType.value = type\n  resetForm()\n  // 修改时，设置数据\n  if (id) {\n    formLoading.value = true\n    try {\n      formData.value = await ${simpleClassName}Api.get${simpleClassName}(id)\n    } finally {\n      formLoading.value = false\n    }\n  }\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n  await get${simpleClassName}Tree()\n#end\n}\ndefineExpose({ open }) // 提供 open 方法，用于打开弹窗\n\n/** 提交表单 */\nconst emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调\nconst submitForm = async () => {\n  // 校验表单\n  await formRef.value.validate()\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n#if ( $subTables && $subTables.size() > 0 )\n  // 校验子表单\n  #foreach ($subTable in $subTables)\n  #set ($index = $foreach.count - 1)\n  #set ($subClassNameVar = $subClassNameVars.get($index))\n  try {\n    await ${subClassNameVar}FormRef.value.validate()\n  } catch (e) {\n    subTabsName.value = '${subClassNameVar}'\n    return\n  }\n  #end\n#end\n#end\n  // 提交请求\n  formLoading.value = true\n  try {\n    const data = formData.value as unknown as ${simpleClassName}\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n#if ( $subTables && $subTables.size() > 0 )\n    // 拼接子表的数据\n  #foreach ($subTable in $subTables)\n  #set ($index = $foreach.count - 1)\n  #set ($subClassNameVar = $subClassNameVars.get($index))\n    data.${subClassNameVar}#if ( $subTable.subJoinMany)s#end = ${subClassNameVar}FormRef.value.getData()\n  #end\n#end\n#end\n    if (formType.value === 'create') {\n      await ${simpleClassName}Api.create${simpleClassName}(data)\n      message.success(t('common.createSuccess'))\n    } else {\n      await ${simpleClassName}Api.update${simpleClassName}(data)\n      message.success(t('common.updateSuccess'))\n    }\n    dialogVisible.value = false\n    // 发送操作成功的事件\n    emit('success')\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n#foreach ($column in $columns)\n  #if ($column.createOperation || $column.updateOperation)\n      #if ($column.htmlType == \"checkbox\")\n    $column.javaField: [],\n      #else\n    $column.javaField: undefined,\n      #end\n  #end\n#end\n  }\n  formRef.value?.resetFields()\n}\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n\n/** 获得${table.classComment}树 */\nconst get${simpleClassName}Tree = async () => {\n  ${classNameVar}Tree.value = []\n  const data = await ${simpleClassName}Api.get${simpleClassName}List()\n  const root: Tree = { id: 0, name: '顶级${table.classComment}', children: [] }\n  root.children = handleTree(data, 'id', '${treeParentColumn.javaField}')\n  ${classNameVar}Tree.value.push(root)\n}\n#end\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/index.vue.vm",
    "content": "<template>\n  <ContentWrap>\n    <!-- 搜索工作栏 -->\n    <el-form\n      class=\"-mb-15px\"\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n    #foreach($column in $columns)\n        #if ($column.listOperation)\n            #set ($dictType = $column.dictType)\n            #set ($javaField = $column.javaField)\n            #set ($javaType = $column.javaType)\n            #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n            #set ($comment = $column.columnComment)\n            #set ($dictMethod = \"getDictOptions\")## 计算使用哪个 dict 字典方法\n            #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                #set ($dictMethod = \"getIntDictOptions\")\n            #elseif ($javaType == \"String\")\n                #set ($dictMethod = \"getStrDictOptions\")\n            #elseif ($javaType == \"Boolean\")\n                #set ($dictMethod = \"getBoolDictOptions\")\n            #end\n            #if ($column.htmlType == \"input\")\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-input\n          v-model=\"queryParams.${javaField}\"\n          placeholder=\"请输入${comment}\"\n          clearable\n          @keyup.enter=\"handleQuery\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n            #elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\")\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-select\n          v-model=\"queryParams.${javaField}\"\n          placeholder=\"请选择${comment}\"\n          clearable\n          class=\"!w-240px\"\n        >\n                #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n          <el-option\n            v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n                #else## 未设置 dictType 数据字典的情况\n          <el-option label=\"请选择字典生成\" value=\"\" />\n                #end\n        </el-select>\n      </el-form-item>\n    #elseif($column.htmlType == \"datetime\")\n      #if ($column.listOperationCondition != \"BETWEEN\")## 非范围\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-date-picker\n          v-model=\"queryParams.${javaField}\"\n          value-format=\"YYYY-MM-DD\"\n          type=\"date\"\n          placeholder=\"选择${comment}\"\n          clearable\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      #else## 范围\n      <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n        <el-date-picker\n          v-model=\"queryParams.${javaField}\"\n          value-format=\"YYYY-MM-DD HH:mm:ss\"\n          type=\"daterange\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"[new Date('1 00:00:00'), new Date('1 23:59:59')]\"\n          class=\"!w-220px\"\n        />\n      </el-form-item>\n      #end\n    #end\n    #end\n    #end\n      <el-form-item>\n        <el-button @click=\"handleQuery\"><Icon icon=\"ep:search\" class=\"mr-5px\" /> 搜索</el-button>\n        <el-button @click=\"resetQuery\"><Icon icon=\"ep:refresh\" class=\"mr-5px\" /> 重置</el-button>\n        <el-button\n          type=\"primary\"\n          plain\n          @click=\"openForm('create')\"\n          v-hasPermi=\"['${permissionPrefix}:create']\"\n        >\n          <Icon icon=\"ep:plus\" class=\"mr-5px\" /> 新增\n        </el-button>\n        <el-button\n          type=\"success\"\n          plain\n          @click=\"handleExport\"\n          :loading=\"exportLoading\"\n          v-hasPermi=\"['${permissionPrefix}:export']\"\n        >\n          <Icon icon=\"ep:download\" class=\"mr-5px\" /> 导出\n        </el-button>\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n        <el-button type=\"danger\" plain @click=\"toggleExpandAll\">\n          <Icon icon=\"ep:sort\" class=\"mr-5px\" /> 展开/折叠\n        </el-button>\n#end\n      #if ($table.templateType != 2 && $deleteBatchEnable)\n        <el-button\n            type=\"danger\"\n            plain\n            :disabled=\"isEmpty(checkedIds)\"\n            @click=\"handleDeleteBatch\"\n            v-hasPermi=\"['${table.moduleName}:${simpleClassName_strikeCase}:delete']\"\n        >\n          <Icon icon=\"ep:delete\" class=\"mr-5px\" /> 批量删除\n        </el-button>\n      #end\n      </el-form-item>\n    </el-form>\n  </ContentWrap>\n\n  <!-- 列表 -->\n  <ContentWrap>\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )\n    <el-table\n      row-key=\"id\"\n      v-loading=\"loading\"\n      :data=\"list\"\n      :stripe=\"true\"\n      :show-overflow-tooltip=\"true\"\n      highlight-current-row\n      @current-change=\"handleCurrentChange\"\n      #if ($deleteBatchEnable)\n      @selection-change=\"handleRowCheckboxChange\"\n      #end\n    >\n## 特殊：树表专属逻辑\n#elseif ( $table.templateType == 2 )\n    <el-table\n      v-loading=\"loading\"\n      :data=\"list\"\n      :stripe=\"true\"\n      :show-overflow-tooltip=\"true\"\n      row-key=\"id\"\n      :default-expand-all=\"isExpandAll\"\n      v-if=\"refreshTable\"\n    >\n#else\n    <el-table\n        row-key=\"id\"\n        v-loading=\"loading\"\n        :data=\"list\"\n        :stripe=\"true\"\n        :show-overflow-tooltip=\"true\"\n        #if ($deleteBatchEnable)\n        @selection-change=\"handleRowCheckboxChange\"\n        #end\n    >\n#end\n#if ($table.templateType != 2 && $deleteBatchEnable)\n    <el-table-column type=\"selection\" width=\"55\" />\n#end\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )\n      <!-- 子表的列表 -->\n      <el-table-column type=\"expand\">\n        <template #default=\"scope\">\n          <el-tabs model-value=\"$subClassNameVars.get(0)\">\n            #foreach ($subTable in $subTables)\n              #set ($index = $foreach.count - 1)\n              #set ($subClassNameVar = $subClassNameVars.get($index))\n              #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n              #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n            <el-tab-pane label=\"${subTable.classComment}\" name=\"$subClassNameVar\">\n              <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"scope.row.id\" />\n            </el-tab-pane>\n            #end\n          </el-tabs>\n        </template>\n      </el-table-column>\n#end\n      #foreach($column in $columns)\n      #if ($column.listOperationResult)\n        #set ($dictType=$column.dictType)\n        #set ($javaField = $column.javaField)\n        #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n        #set ($comment=$column.columnComment)\n        #if ($column.javaType == \"LocalDateTime\")## 时间类型\n      <el-table-column\n        label=\"${comment}\"\n        align=\"center\"\n        prop=\"${javaField}\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n        #elseif($column.dictType && \"\" != $column.dictType)## 数据字典\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.${column.javaField}\" />\n        </template>\n      </el-table-column>\n        #else\n      <el-table-column label=\"${comment}\" align=\"center\" prop=\"${javaField}\" />\n        #end\n      #end\n    #end\n      <el-table-column label=\"操作\" align=\"center\" min-width=\"120px\">\n        <template #default=\"scope\">\n          <el-button\n            link\n            type=\"primary\"\n            @click=\"openForm('update', scope.row.id)\"\n            v-hasPermi=\"['${permissionPrefix}:update']\"\n          >\n            编辑\n          </el-button>\n          <el-button\n            link\n            type=\"danger\"\n            @click=\"handleDelete(scope.row.id)\"\n            v-hasPermi=\"['${permissionPrefix}:delete']\"\n          >\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页 -->\n    <Pagination\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNo\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n  </ContentWrap>\n\n  <!-- 表单弹窗：添加/修改 -->\n  <${simpleClassName}Form ref=\"formRef\" @success=\"getList\" />\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )\n  <!-- 子表的列表 -->\n  <ContentWrap>\n    <el-tabs model-value=\"$subClassNameVars.get(0)\">\n      #foreach ($subTable in $subTables)\n        #set ($index = $foreach.count - 1)\n        #set ($subClassNameVar = $subClassNameVars.get($index))\n        #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n        #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n      <el-tab-pane label=\"${subTable.classComment}\" name=\"$subClassNameVar\">\n        <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"currentRow.id\" />\n      </el-tab-pane>\n      #end\n    </el-tabs>\n  </ContentWrap>\n#end\n</template>\n\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport { isEmpty } from '@/utils/is'\nimport { dateFormatter } from '@/utils/formatTime'\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\nimport { handleTree } from '@/utils/tree'\n#end\nimport download from '@/utils/download'\nimport { ${simpleClassName}Api, ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\nimport ${simpleClassName}Form from './${simpleClassName}Form.vue'\n## 特殊：主子表专属逻辑\n#if ( $table.templateType != 10 )\n#foreach ($subSimpleClassName in $subSimpleClassNames)\nimport ${subSimpleClassName}List from './components/${subSimpleClassName}List.vue'\n#end\n#end\n\n/** ${table.classComment} 列表 */\ndefineOptions({ name: '${table.className}' })\n\nconst message = useMessage() // 消息弹窗\nconst { t } = useI18n() // 国际化\n\nconst loading = ref(true) // 列表的加载中\nconst list = ref<${simpleClassName}[]>([]) // 列表的数据\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\nconst total = ref(0) // 列表的总页数\n#end\nconst queryParams = reactive({\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\n  pageNo: 1,\n  pageSize: 10,\n#end\n  #foreach ($column in $columns)\n    #if ($column.listOperation)\n      #if ($column.listOperationCondition != 'BETWEEN')\n  $column.javaField: undefined,\n  #end\n      #if ($column.htmlType == \"datetime\" || $column.listOperationCondition == \"BETWEEN\")\n  $column.javaField: [],\n      #end\n    #end\n  #end\n})\nconst queryFormRef = ref() // 搜索的表单\nconst exportLoading = ref(false) // 导出的加载中\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n## 特殊：树表专属逻辑（树不需要分页接口）\n  #if ( $table.templateType == 2 )\n    const data = await ${simpleClassName}Api.get${simpleClassName}List(queryParams)\n    list.value = handleTree(data, 'id', '${treeParentColumn.javaField}')\n  #else\n    const data = await ${simpleClassName}Api.get${simpleClassName}Page(queryParams)\n    list.value = data.list\n    total.value = data.total\n  #end\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 重置按钮操作 */\nconst resetQuery = () => {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n\n/** 添加/修改操作 */\nconst formRef = ref()\nconst openForm = (type: string, id?: number) => {\n  formRef.value.open(type, id)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = async (id: number) => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    // 发起删除\n    await ${simpleClassName}Api.delete${simpleClassName}(id)\n    message.success(t('common.delSuccess'))\n#if ( $table.templateType == 11 )\n    currentRow.value = {}\n#end\n    // 刷新列表\n    await getList()\n  } catch {}\n}\n\n#if ($table.templateType != 2 && $deleteBatchEnable)\n/** 批量删除${table.classComment} */\nconst handleDeleteBatch = async () => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    await ${simpleClassName}Api.delete${simpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    message.success(t('common.delSuccess'))\n    await getList();\n  } catch {}\n}\n\nconst checkedIds = ref<number[]>([])\nconst handleRowCheckboxChange = (records: ${simpleClassName}[]) => {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n\n/** 导出按钮操作 */\nconst handleExport = async () => {\n  try {\n    // 导出的二次确认\n    await message.exportConfirm()\n    // 发起导出\n    exportLoading.value = true\n    const data = await ${simpleClassName}Api.export${simpleClassName}(queryParams)\n    download.excel(data, '${table.classComment}.xls')\n  } catch {\n  } finally {\n    exportLoading.value = false\n  }\n}\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 11 )\n\n/** 选中行操作 */\nconst currentRow = ref({}) // 选中行\nconst handleCurrentChange = (row) => {\n  currentRow.value = row\n}\n#end\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n\n/** 展开/折叠操作 */\nconst isExpandAll = ref(true) // 是否展开，默认全部展开\nconst refreshTable = ref(true) // 重新渲染表格状态\nconst toggleExpandAll = async () => {\n  refreshTable.value = false\n  isExpandAll.value = !isExpandAll.value\n  await nextTick()\n  refreshTable.value = true\n}\n#end\n\n/** 初始化 **/\nonMounted(() => {\n  getList()\n})\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_admin_uniapp/components/search-form.vue.vm",
    "content": "<template>\n  <!-- 搜索框入口 -->\n  <view @click=\"visible = true\">\n    <wd-search :placeholder=\"placeholder\" hide-cancel disabled />\n  </view>\n\n  <!-- 搜索弹窗 -->\n  <wd-popup v-model=\"visible\" position=\"top\" @close=\"visible = false\">\n    <view class=\"yd-search-form-container\" :style=\"{ paddingTop: `#[[${]]#getNavbarHeight()#[[}]]#px` }\">\n#set ($hasDict = 0)\n#foreach ($column in $columns)\n  #if ($hasDict == 0 && $column.listOperation && $column.dictType && \"\" != $column.dictType)\n    #set ($hasDict = 1)\n  #end\n#end\n#foreach($column in $columns)\n  #if ($column.listOperation)\n    #set ($dictType = $column.dictType)\n    #set ($javaField = $column.javaField)\n    #set ($javaType = $column.javaType)\n    #set ($listOperationCondition = $column.listOperationCondition)\n    #set ($comment = $column.columnComment)\n    #set ($dictMethod = \"getDictOptions\")\n    #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n      #set ($dictMethod = \"getIntDictOptions\")\n    #elseif ($javaType == \"String\")\n      #set ($dictMethod = \"getStrDictOptions\")\n    #elseif ($javaType == \"Boolean\")\n      #set ($dictMethod = \"getBoolDictOptions\")\n    #end\n    #if ($column.htmlType == \"input\")\n      <view class=\"yd-search-form-item\">\n        <view class=\"yd-search-form-label\">\n          ${comment}\n        </view>\n        <wd-input\n          v-model=\"formData.${javaField}\"\n          placeholder=\"请输入${comment}\"\n          clearable\n        />\n      </view>\n    #elseif ($column.htmlType == \"datetime\" && $listOperationCondition == \"BETWEEN\")\n      #set ($AttrName = $javaField.substring(0,1).toUpperCase() + ${javaField.substring(1)})\n      <view class=\"yd-search-form-item\">\n        <view class=\"yd-search-form-label\">\n          ${comment}\n        </view>\n        <view class=\"yd-search-form-date-range-container\">\n          <view @click=\"visible${AttrName}[0] = true\">\n            <view class=\"yd-search-form-date-range-picker\">\n              {{ formatDate(formData.${javaField}?.[0]) || '开始日期' }}\n            </view>\n          </view>\n          -\n          <view @click=\"visible${AttrName}[1] = true\">\n            <view class=\"yd-search-form-date-range-picker\">\n              {{ formatDate(formData.${javaField}?.[1]) || '结束日期' }}\n            </view>\n          </view>\n        </view>\n        <wd-datetime-picker-view v-if=\"visible${AttrName}[0]\" v-model=\"temp${AttrName}[0]\" type=\"date\" />\n        <view v-if=\"visible${AttrName}[0]\" class=\"yd-search-form-date-range-actions\">\n          <wd-button size=\"small\" plain @click=\"visible${AttrName}[0] = false\">\n            取消\n          </wd-button>\n          <wd-button size=\"small\" type=\"primary\" @click=\"handle${AttrName}0Confirm\">\n            确定\n          </wd-button>\n        </view>\n        <wd-datetime-picker-view v-if=\"visible${AttrName}[1]\" v-model=\"temp${AttrName}[1]\" type=\"date\" />\n        <view v-if=\"visible${AttrName}[1]\" class=\"yd-search-form-date-range-actions\">\n          <wd-button size=\"small\" plain @click=\"visible${AttrName}[1] = false\">\n            取消\n          </wd-button>\n          <wd-button size=\"small\" type=\"primary\" @click=\"handle${AttrName}1Confirm\">\n            确定\n          </wd-button>\n        </view>\n      </view>\n    #elseif (($column.htmlType == \"select\" || $column.htmlType == \"radio\") && $dictType && \"\" != $dictType)\n      <view class=\"yd-search-form-item\">\n        <view class=\"yd-search-form-label\">\n          ${comment}\n        </view>\n        <wd-radio-group v-model=\"formData.${javaField}\" shape=\"button\">\n          <wd-radio :value=\"-1\">\n            全部\n          </wd-radio>\n          <wd-radio\n            v-for=\"dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())\"\n            :key=\"dict.value\"\n            :value=\"dict.value\"\n          >\n            {{ dict.label }}\n          </wd-radio>\n        </wd-radio-group>\n      </view>\n    #else\n      <view class=\"yd-search-form-item\">\n        <view class=\"yd-search-form-label\">\n          ${comment}\n        </view>\n        <wd-input\n          v-model=\"formData.${javaField}\"\n          placeholder=\"请输入${comment}\"\n          clearable\n        />\n      </view>\n    #end\n  #end\n#end\n      <view class=\"yd-search-form-actions\">\n        <wd-button class=\"flex-1\" plain @click=\"handleReset\">\n          重置\n        </wd-button>\n        <wd-button class=\"flex-1\" type=\"primary\" @click=\"handleSearch\">\n          搜索\n        </wd-button>\n      </view>\n    </view>\n  </wd-popup>\n</template>\n\n<script lang=\"ts\" setup>\n#set ($hasDict = 0)\n#set ($hasGetDictOptions = 0)\n#set ($hasGetIntDictOptions = 0)\n#set ($hasGetStrDictOptions = 0)\n#set ($hasGetBoolDictOptions = 0)\n#set ($hasDateTimeBetween = 0)\n#foreach($column in $columns)\n  #if ($column.listOperation && $column.dictType && \"\" != $column.dictType)\n    #set ($hasDict = 1)\n    #if ($column.htmlType == \"select\" || $column.htmlType == \"radio\")\n      #if ($column.javaType == \"Integer\" || $column.javaType == \"Long\" || $column.javaType == \"Byte\" || $column.javaType == \"Short\")\n        #set ($hasGetIntDictOptions = 1)\n      #elseif ($column.javaType == \"String\")\n        #set ($hasGetStrDictOptions = 1)\n      #elseif ($column.javaType == \"Boolean\")\n        #set ($hasGetBoolDictOptions = 1)\n      #else\n        #set ($hasGetDictOptions = 1)\n      #end\n    #end\n  #end\n  #if ($hasDateTimeBetween == 0 && $column.listOperation && $column.htmlType == \"datetime\" && $column.listOperationCondition == \"BETWEEN\")\n    #set ($hasDateTimeBetween = 1)\n  #end\n#end\nimport { computed, reactive, ref } from 'vue'\nimport { getNavbarHeight } from '@/utils'\n#if ($hasDateTimeBetween == 1)\nimport { formatDate, formatDateRange } from '@/utils/date'\n#end\n#if ($hasDict == 1)\n  #set ($dictImportNames = \"getDictLabel, \")\n  #if ($hasGetDictOptions == 1)\n    #set ($dictImportNames = \"${dictImportNames}getDictOptions, \")\n  #end\n  #if ($hasGetIntDictOptions == 1)\n    #set ($dictImportNames = \"${dictImportNames}getIntDictOptions, \")\n  #end\n  #if ($hasGetStrDictOptions == 1)\n    #set ($dictImportNames = \"${dictImportNames}getStrDictOptions, \")\n  #end\n  #if ($hasGetBoolDictOptions == 1)\n    #set ($dictImportNames = \"${dictImportNames}getBoolDictOptions, \")\n  #end\n  #set ($dictImportNames = $dictImportNames.trim())\n  #set ($dictImportNames = $dictImportNames.substring(0, $dictImportNames.length() - 1))\nimport { $dictImportNames } from '@/hooks/useDict'\nimport { DICT_TYPE } from '@/utils/constants'\n#end\n\nconst emit = defineEmits<{\n  search: [data: Record<string, any>]\n  reset: []\n}>()\n\nconst visible = ref(false)\nconst formData = reactive({\n#foreach($column in $columns)\n  #if ($column.listOperation)\n    #set ($javaType = $column.javaType.toLowerCase())\n    #if ($column.htmlType == \"datetime\" && $column.listOperationCondition == \"BETWEEN\")\n  ${column.javaField}: [undefined, undefined] as [number | undefined, number | undefined],\n    #elseif ($column.dictType && \"\" != $column.dictType)\n      #if(${javaType} == \"string\")\n  ${column.javaField}: -1 as -1 | string, // -1 表示全部\n      #elseif(${javaType} == \"boolean\")\n  ${column.javaField}: -1 as -1 | boolean, // -1 表示全部\n      #else\n  ${column.javaField}: -1, // -1 表示全部\n      #end\n    #elseif(${javaType} == \"long\" || ${javaType} == \"integer\" || ${javaType} == \"short\" || ${javaType} == \"double\" || ${javaType} == \"bigdecimal\" || ${javaType} == \"byte\")\n  ${column.javaField}: undefined as number | undefined,\n    #elseif(${javaType} == \"boolean\")\n  ${column.javaField}: undefined as boolean | undefined,\n    #else\n  ${column.javaField}: undefined as string | undefined,\n    #end\n  #end\n#end\n})\n\n/** 搜索条件 placeholder 拼接 */\nconst placeholder = computed(() => {\n  const conditions: string[] = []\n#foreach($column in $columns)\n  #if ($column.listOperation)\n    #set ($dictType = $column.dictType)\n    #set ($javaField = $column.javaField)\n    #set ($javaType = $column.javaType.toLowerCase())\n    #set ($comment = $column.columnComment)\n    #if ($column.htmlType == \"datetime\" && $column.listOperationCondition == \"BETWEEN\")\n  if (formData.${javaField}?.[0] && formData.${javaField}?.[1]) {\n    conditions.push(`${comment}:#[[${]]#formatDate(formData.${javaField}[0])#[[}]]#~#[[${]]#formatDate(formData.${javaField}[1])#[[}]]#`)\n  }\n    #elseif ($dictType && \"\" != $dictType)\n  if (formData.${javaField} !== -1) {\n    conditions.push(`${comment}:#[[${]]#getDictLabel(DICT_TYPE.${dictType.toUpperCase()}, formData.${javaField})#[[}]]#`)\n  }\n    #else\n      #if(${javaType} == \"long\" || ${javaType} == \"integer\" || ${javaType} == \"short\" || ${javaType} == \"double\" || ${javaType} == \"bigdecimal\" || ${javaType} == \"byte\" || ${javaType} == \"boolean\")\n  if (formData.${javaField} !== undefined) {\n    conditions.push(`${comment}:#[[${]]#formData.${javaField}#[[}]]#`)\n  }\n      #else\n  if (formData.${javaField}) {\n    conditions.push(`${comment}:#[[${]]#formData.${javaField}#[[}]]#`)\n  }\n      #end\n    #end\n  #end\n#end\n  return conditions.length > 0 ? conditions.join(' | ') : '搜索${table.classComment}'\n})\n\n#if ($hasDateTimeBetween == 1)\n// 时间范围选择器状态\n#foreach($column in $columns)\n  #if ($column.listOperation && $column.htmlType == \"datetime\" && $column.listOperationCondition == \"BETWEEN\")\n    #set ($javaField = $column.javaField)\n    #set ($AttrName = $javaField.substring(0,1).toUpperCase() + ${javaField.substring(1)})\nconst visible${AttrName} = ref<[boolean, boolean]>([false, false])\nconst temp${AttrName} = ref<[number, number]>([Date.now(), Date.now()])\n\n/** ${column.columnComment}[0]确认 */\nfunction handle${AttrName}0Confirm() {\n  formData.${javaField} = [temp${AttrName}.value[0], formData.${javaField}?.[1]]\n  visible${AttrName}.value[0] = false\n}\n\n/** ${column.columnComment}[1]确认 */\nfunction handle${AttrName}1Confirm() {\n  formData.${javaField} = [formData.${javaField}?.[0], temp${AttrName}.value[1]]\n  visible${AttrName}.value[1] = false\n}\n  #end\n#end\n#end\n\n/** 搜索 */\nfunction handleSearch() {\n  visible.value = false\n  emit('search', {\n    ...formData,\n#foreach($column in $columns)\n  #if ($column.listOperation)\n    #if ($column.dictType && \"\" != $column.dictType)\n    ${column.javaField}: formData.${column.javaField} === -1 ? undefined : formData.${column.javaField},\n    #elseif ($column.htmlType == \"datetime\" && $column.listOperationCondition == \"BETWEEN\")\n    ${column.javaField}: formatDateRange(formData.${column.javaField}),\n    #end\n  #end\n#end\n  })\n}\n\n/** 重置 */\nfunction handleReset() {\n#foreach($column in $columns)\n  #if ($column.listOperation)\n    #if ($column.htmlType == \"datetime\" && $column.listOperationCondition == \"BETWEEN\")\n  formData.${column.javaField} = [undefined, undefined]\n    #elseif ($column.dictType && \"\" != $column.dictType)\n  formData.${column.javaField} = -1\n    #else\n  formData.${column.javaField} = undefined\n    #end\n  #end\n#end\n  visible.value = false\n  emit('reset')\n}\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_admin_uniapp/views/detail/index.vue.vm",
    "content": "<template>\n  <view class=\"yd-page-container\">\n    <!-- 顶部导航栏 -->\n    <wd-navbar\n      title=\"${table.classComment}详情\"\n      left-arrow placeholder safe-area-inset-top fixed\n      @click-left=\"handleBack\"\n    />\n\n    <!-- 详情内容 -->\n    <view>\n      <wd-cell-group border>\n#foreach($column in $columns)\n  #if ($column.primaryKey || $column.listOperationResult || $column.createOperation || $column.updateOperation)\n    #set ($javaField = $column.javaField)\n    #set ($comment = $column.columnComment)\n    #if ($column.dictType && \"\" != $column.dictType)\n        <wd-cell title=\"${comment}\">\n          <dict-tag :type=\"DICT_TYPE.${column.dictType.toUpperCase()}\" :value=\"formData?.${javaField}\" />\n        </wd-cell>\n    #elseif ($column.javaType == \"LocalDateTime\")\n        <wd-cell title=\"${comment}\" :value=\"formatDateTime(formData?.${javaField}) || '-'\" />\n    #else\n        <wd-cell title=\"${comment}\" :value=\"formData?.${javaField} ?? '-'\" />\n    #end\n  #end\n#end\n      </wd-cell-group>\n    </view>\n\n    <!-- 底部操作按钮 -->\n    <view class=\"yd-detail-footer\">\n      <view class=\"yd-detail-footer-actions\">\n        <wd-button\n          v-if=\"hasAccessByCodes(['${permissionPrefix}:update'])\"\n          class=\"flex-1\" type=\"warning\" @click=\"handleEdit\"\n        >\n          编辑\n        </wd-button>\n        <wd-button\n          v-if=\"hasAccessByCodes(['${permissionPrefix}:delete'])\"\n          class=\"flex-1\" type=\"error\" :loading=\"deleting\" @click=\"handleDelete\"\n        >\n          删除\n        </wd-button>\n      </view>\n    </view>\n  </view>\n</template>\n\n<script lang=\"ts\" setup>\n#set ($hasDict = 0)\n#foreach($column in $columns)\n  #if ($hasDict == 0 && $column.dictType && \"\" != $column.dictType)\n    #set ($hasDict = 1)\n  #end\n#end\n#set ($hasDateTime = 0)\n#foreach($column in $columns)\n  #if ($hasDateTime == 0 && $column.javaType == \"LocalDateTime\")\n    #set ($hasDateTime = 1)\n  #end\n#end\nimport type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\nimport { onMounted, ref } from 'vue'\nimport { useToast } from 'wot-design-uni'\nimport { delete${simpleClassName}, get${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\nimport { useAccess } from '@/hooks/useAccess'\nimport { navigateBackPlus } from '@/utils'\n#if ($hasDict == 1)\nimport { DICT_TYPE } from '@/utils/constants'\n#end\n#if ($hasDateTime == 1)\nimport { formatDateTime } from '@/utils/date'\n#end\n\nconst props = defineProps<{\n  id?: number | any\n}>()\n\ndefinePage({\n  style: {\n    navigationBarTitleText: '',\n    navigationStyle: 'custom',\n  },\n})\n\nconst { hasAccessByCodes } = useAccess()\nconst toast = useToast()\nconst formData = ref<${simpleClassName}>()\nconst deleting = ref(false)\n\n/** 返回上一页 */\nfunction handleBack() {\n  navigateBackPlus('/pages-${table.moduleName}/${table.businessName}/index')\n}\n\n/** 加载${table.classComment}详情 */\nasync function getDetail() {\n  if (!props.id) {\n    return\n  }\n  try {\n    toast.loading('加载中...')\n    formData.value = await get${simpleClassName}(props.id)\n  } finally {\n    toast.close()\n  }\n}\n\n/** 编辑${table.classComment} */\nfunction handleEdit() {\n  uni.navigateTo({\n    url: `/pages-${table.moduleName}/${table.businessName}/form/index?id=#[[${]]#props.id#[[}]]#`,\n  })\n}\n\n/** 删除${table.classComment} */\nfunction handleDelete() {\n  if (!props.id) {\n    return\n  }\n  uni.showModal({\n    title: '提示',\n    content: '确定要删除该${table.classComment}吗？',\n    success: async (res) => {\n      if (!res.confirm) {\n        return\n      }\n      deleting.value = true\n      try {\n        await delete${simpleClassName}(props.id)\n        toast.success('删除成功')\n        setTimeout(() => {\n          handleBack()\n        }, 500)\n      } finally {\n        deleting.value = false\n      }\n    },\n  })\n}\n\n/** 初始化 */\nonMounted(() => {\n  getDetail()\n})\n</script>\n\n<style lang=\"scss\" scoped>\n</style>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_admin_uniapp/views/form/index.vue.vm",
    "content": "<template>\n  <view class=\"yd-page-container\">\n    <!-- 顶部导航栏 -->\n    <wd-navbar\n      :title=\"getTitle\"\n      left-arrow placeholder safe-area-inset-top fixed\n      @click-left=\"handleBack\"\n    />\n\n    <!-- 表单区域 -->\n    <view>\n      <wd-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\">\n        <wd-cell-group border>\n#foreach($column in $columns)\n  #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey)\n    #set ($dictType = $column.dictType)\n    #set ($javaField = $column.javaField)\n    #set ($javaType = $column.javaType)\n    #set ($comment = $column.columnComment)\n    #set ($dictMethod = \"getDictOptions\")\n    #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n      #set ($dictMethod = \"getIntDictOptions\")\n    #elseif ($javaType == \"String\")\n      #set ($dictMethod = \"getStrDictOptions\")\n    #elseif ($javaType == \"Boolean\")\n      #set ($dictMethod = \"getBoolDictOptions\")\n    #end\n    ## 优先判断是否有字典，有字典则使用 radio-group\n    #if (($column.htmlType == \"select\" || $column.htmlType == \"radio\") && $dictType && \"\" != $dictType)\n          <wd-cell title=\"${comment}\" title-width=\"180rpx\" prop=\"${javaField}\" center>\n            <wd-radio-group v-model=\"formData.${javaField}\" shape=\"button\">\n              <wd-radio\n                v-for=\"dict in $dictMethod(DICT_TYPE.${dictType.toUpperCase()})\"\n                :key=\"dict.value\"\n                :value=\"dict.value\"\n              >\n                {{ dict.label }}\n              </wd-radio>\n            </wd-radio-group>\n          </wd-cell>\n    ## 数字类型（无字典）\n    #elseif (${javaType.toLowerCase()} == \"long\" || ${javaType.toLowerCase()} == \"integer\" || ${javaType.toLowerCase()} == \"short\" || ${javaType.toLowerCase()} == \"double\" || ${javaType.toLowerCase()} == \"bigdecimal\" || ${javaType.toLowerCase()} == \"byte\")\n          <wd-cell title=\"${comment}\" title-width=\"180rpx\" prop=\"${javaField}\" center>\n            <wd-input-number\n              v-model=\"formData.${javaField}\"\n              :min=\"0\"\n            />\n          </wd-cell>\n    ## 布尔类型\n    #elseif (${javaType.toLowerCase()} == \"boolean\")\n          <wd-cell title=\"${comment}\" title-width=\"180rpx\" prop=\"${javaField}\" center>\n            <wd-switch v-model=\"formData.${javaField}\" />\n          </wd-cell>\n    ## 日期时间类型\n    #elseif (${javaType.toLowerCase()} == \"date\" || ${javaType.toLowerCase()} == \"localdate\" || ${javaType.toLowerCase()} == \"localdatetime\")\n      #set ($pickerType = \"date\")\n      #if (${javaType.toLowerCase()} == \"localdatetime\")\n        #set ($pickerType = \"datetime\")\n      #end\n          <wd-datetime-picker\n            v-model=\"formData.${javaField}\"\n            type=\"${pickerType}\"\n            label=\"${comment}\"\n            label-width=\"180rpx\"\n            prop=\"${javaField}\"\n          />\n    ## 文本域\n    #elseif ($column.htmlType == \"textarea\")\n          <wd-textarea\n            v-model=\"formData.${javaField}\"\n            label=\"${comment}\"\n            label-width=\"180rpx\"\n            placeholder=\"请输入${comment}\"\n            :maxlength=\"200\"\n            show-word-limit\n            clearable\n          />\n    ## 默认：文本输入\n    #else\n          <wd-input\n            v-model=\"formData.${javaField}\"\n            label=\"${comment}\"\n            label-width=\"180rpx\"\n            prop=\"${javaField}\"\n            clearable\n            placeholder=\"请输入${comment}\"\n          />\n    #end\n  #end\n#end\n        </wd-cell-group>\n      </wd-form>\n    </view>\n\n    <!-- 底部保存按钮 -->\n    <view class=\"yd-detail-footer\">\n      <wd-button\n        type=\"primary\"\n        block\n        :loading=\"formLoading\"\n        @click=\"handleSubmit\"\n      >\n        保存\n      </wd-button>\n    </view>\n  </view>\n</template>\n\n<script lang=\"ts\" setup>\nimport type { FormInstance } from 'wot-design-uni/components/wd-form/types'\n#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())\n#if(${primaryJavaType} == \"long\" || ${primaryJavaType} == \"integer\" || ${primaryJavaType} == \"short\" || ${primaryJavaType} == \"double\" || ${primaryJavaType} == \"bigdecimal\" || ${primaryJavaType} == \"byte\")\n#set ($primaryTsType = \"number\")\n#else\n#set ($primaryTsType = \"string\")\n#end\n#set ($hasDict = 0)\n#set ($hasGetDictOptions = 0)\n#set ($hasGetIntDictOptions = 0)\n#set ($hasGetStrDictOptions = 0)\n#set ($hasGetBoolDictOptions = 0)\n#foreach ($column in $columns)\n  #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey\n      && ($column.htmlType == \"select\" || $column.htmlType == \"radio\")\n      && $column.dictType && \"\" != $column.dictType)\n    #set ($hasDict = 1)\n    #if ($column.javaType == \"Integer\" || $column.javaType == \"Long\" || $column.javaType == \"Byte\" || $column.javaType == \"Short\")\n      #set ($hasGetIntDictOptions = 1)\n    #elseif ($column.javaType == \"String\")\n      #set ($hasGetStrDictOptions = 1)\n    #elseif ($column.javaType == \"Boolean\")\n      #set ($hasGetBoolDictOptions = 1)\n    #else\n      #set ($hasGetDictOptions = 1)\n    #end\n  #end\n#end\nimport type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\nimport { computed, onMounted, ref } from 'vue'\nimport { useToast } from 'wot-design-uni'\nimport { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\n#if ($hasDict == 1)\n  #set ($dictImportNames = \"\")\n  #if ($hasGetDictOptions == 1)\n    #set ($dictImportNames = \"${dictImportNames}getDictOptions, \")\n  #end\n  #if ($hasGetIntDictOptions == 1)\n    #set ($dictImportNames = \"${dictImportNames}getIntDictOptions, \")\n  #end\n  #if ($hasGetStrDictOptions == 1)\n    #set ($dictImportNames = \"${dictImportNames}getStrDictOptions, \")\n  #end\n  #if ($hasGetBoolDictOptions == 1)\n    #set ($dictImportNames = \"${dictImportNames}getBoolDictOptions, \")\n  #end\n  #set ($dictImportNames = $dictImportNames.trim())\n  #set ($dictImportNames = $dictImportNames.substring(0, $dictImportNames.length() - 1))\nimport { $dictImportNames } from '@/hooks/useDict'\n#end\nimport { navigateBackPlus } from '@/utils'\n#if ($hasDict == 1)\nimport { DICT_TYPE } from '@/utils/constants'\n#end\n\nconst props = defineProps<{\n  id?: ${primaryTsType} | any\n}>()\n\ndefinePage({\n  style: {\n    navigationBarTitleText: '',\n    navigationStyle: 'custom',\n  },\n})\n\nconst toast = useToast()\nconst getTitle = computed(() => props.id ? '编辑${table.classComment}' : '新增${table.classComment}')\nconst formLoading = ref(false)\nconst formData = ref<${simpleClassName}>({\n#foreach($column in $columns)\n  #if (($column.createOperation || $column.updateOperation) || $column.primaryKey)\n    #set ($javaType = $column.javaType.toLowerCase())\n    #set ($javaFieldLower = $column.javaField.toLowerCase())\n    #set ($optional = $column.nullable || $column.primaryKey || $javaFieldLower == \"createtime\" || $javaFieldLower == \"updatetime\")\n    #if ($column.primaryKey)\n  ${column.javaField}: undefined,\n    #elseif(${javaType} == \"long\" || ${javaType} == \"integer\" || ${javaType} == \"short\" || ${javaType} == \"double\" || ${javaType} == \"bigdecimal\" || ${javaType} == \"byte\")\n  ${column.javaField}: 0,\n    #elseif(${javaType} == \"boolean\")\n  ${column.javaField}: false,\n    #elseif(${javaType} == \"date\" || ${javaType} == \"localdate\" || ${javaType} == \"localdatetime\")\n  ${column.javaField}: undefined,\n    #else\n  ${column.javaField}: '',\n    #end\n  #end\n#end\n})\nconst formRules = {\n#foreach($column in $columns)\n  #set ($javaFieldLower = $column.javaField.toLowerCase())\n  #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey\n        && $javaFieldLower != \"createtime\" && $javaFieldLower != \"updatetime\")\n  ${column.javaField}: [{ required: true, message: '${column.columnComment}不能为空' }],\n  #end\n#end\n}\nconst formRef = ref<FormInstance>()\n\n/** 返回上一页 */\nfunction handleBack() {\n  navigateBackPlus('/pages-${table.moduleName}/${table.businessName}/index')\n}\n\n/** 加载${table.classComment}详情 */\nasync function getDetail() {\n  if (!props.id) {\n    return\n  }\n  formData.value = await get${simpleClassName}(props.id)\n}\n\n/** 提交表单 */\nasync function handleSubmit() {\n  const { valid } = await formRef.value.validate()\n  if (!valid) {\n    return\n  }\n\n  formLoading.value = true\n  try {\n    if (props.id) {\n      await update${simpleClassName}(formData.value)\n      toast.success('修改成功')\n    } else {\n      await create${simpleClassName}(formData.value)\n      toast.success('新增成功')\n    }\n    setTimeout(() => {\n      handleBack()\n    }, 500)\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 初始化 */\nonMounted(() => {\n  getDetail()\n})\n</script>\n\n<style lang=\"scss\" scoped>\n</style>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_admin_uniapp/views/index.vue.vm",
    "content": "<template>\n  <view class=\"yd-page-container\">\n    <!-- 顶部导航栏 -->\n    <wd-navbar\n      title=\"${table.classComment}管理\"\n      left-arrow placeholder safe-area-inset-top fixed\n      @click-left=\"handleBack\"\n    />\n\n    <!-- 搜索组件 -->\n    <SearchForm @search=\"handleQuery\" @reset=\"handleReset\" />\n\n    <!-- ${table.classComment}列表 -->\n    <view class=\"p-24rpx\">\n      <view\n        v-for=\"item in list\"\n        :key=\"item.${primaryColumn.javaField}\"\n        class=\"mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm\"\n        @click=\"handleDetail(item)\"\n      >\n        <view class=\"p-24rpx\">\n#set ($titleField = \"\")\n#set ($statusField = \"\")\n#set ($statusDictType = \"\")\n#foreach($column in $columns)\n  #if ($titleField == \"\" && !$column.primaryKey && $column.listOperationResult)\n    #set ($titleField = $column.javaField)\n    #set ($titleComment = $column.columnComment)\n  #end\n  #if ($statusField == \"\" && $column.listOperationResult && $column.dictType && \"\" != $column.dictType)\n    #set ($statusField = $column.javaField)\n    #set ($statusDictType = $column.dictType)\n  #end\n#end\n #if ($titleField == \"\")\n   #set ($titleField = $primaryColumn.javaField)\n #end\n          <view class=\"mb-16rpx flex items-center justify-between\">\n            <view class=\"text-32rpx text-[#333] font-semibold\">\n              {{ item.${titleField} }}\n            </view>\n#if($statusField != \"\")\n            <dict-tag :type=\"DICT_TYPE.${statusDictType.toUpperCase()}\" :value=\"item.${statusField}\" />\n#end\n          </view>\n#foreach($column in $columns)\n  #if ($column.listOperationResult && !$column.primaryKey && $column.javaField != $titleField && $column.javaField != $statusField)\n    #set ($javaField = $column.javaField)\n    #set ($comment = $column.columnComment)\n    #set ($dictType = $column.dictType)\n    #set ($javaType = $column.javaType)\n    #if ($dictType && \"\" != $dictType)\n          <view class=\"mb-12rpx flex items-center text-28rpx text-[#666]\">\n            <text class=\"mr-8rpx text-[#999]\">${comment}：</text>\n            <dict-tag :type=\"DICT_TYPE.${dictType.toUpperCase()}\" :value=\"item.${javaField}\" />\n          </view>\n    #elseif ($javaType == \"LocalDateTime\")\n          <view class=\"mb-12rpx flex items-center text-28rpx text-[#666]\">\n            <text class=\"mr-8rpx text-[#999]\">${comment}：</text>\n            <text class=\"line-clamp-1\">{{ formatDateTime(item.${javaField}) || '-' }}</text>\n          </view>\n    #else\n          <view class=\"mb-12rpx flex items-center text-28rpx text-[#666]\">\n            <text class=\"mr-8rpx text-[#999]\">${comment}：</text>\n            <text class=\"line-clamp-1\">{{ item.${javaField} }}</text>\n          </view>\n    #end\n  #end\n#end\n        </view>\n      </view>\n\n      <!-- 加载更多 -->\n      <view v-if=\"loadMoreState !== 'loading' && list.length === 0\" class=\"py-100rpx text-center\">\n        <wd-status-tip image=\"content\" tip=\"暂无${table.classComment}数据\" />\n      </view>\n      <wd-loadmore\n        v-if=\"list.length > 0\"\n        :state=\"loadMoreState\"\n        @reload=\"loadMore\"\n      />\n    </view>\n\n    <!-- 新增按钮 -->\n    <wd-fab\n      v-if=\"hasAccessByCodes(['${permissionPrefix}:create'])\"\n      position=\"right-bottom\"\n      type=\"primary\"\n      :expandable=\"false\"\n      @click=\"handleAdd\"\n    />\n  </view>\n</template>\n\n<script lang=\"ts\" setup>\n#set ($hasDict = 0)\n#foreach($column in $columns)\n  #if ($hasDict == 0 && $column.listOperationResult && $column.dictType && \"\" != $column.dictType)\n    #set ($hasDict = 1)\n  #end\n#end\n#set ($hasDateTime = 0)\n#foreach($column in $columns)\n  #if ($column.listOperationResult)\n    #if ($hasDateTime == 0 && $column.javaType == \"LocalDateTime\")\n      #set ($hasDateTime = 1)\n    #end\n  #end\n#end\nimport type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\nimport type { LoadMoreState } from '@/http/types'\nimport { onReachBottom } from '@dcloudio/uni-app'\nimport { onMounted, ref } from 'vue'\nimport { get${simpleClassName}Page } from '@/api/${table.moduleName}/${table.businessName}'\nimport { useAccess } from '@/hooks/useAccess'\nimport { navigateBackPlus } from '@/utils'\n#if ($hasDict == 1)\nimport { DICT_TYPE } from '@/utils/constants'\n#end\n#if ($hasDateTime == 1)\nimport { formatDateTime } from '@/utils/date'\n#end\nimport SearchForm from './components/search-form.vue'\n\ndefinePage({\n  style: {\n    navigationBarTitleText: '',\n    navigationStyle: 'custom',\n  },\n})\n\nconst { hasAccessByCodes } = useAccess()\nconst total = ref(0)\nconst list = ref<${simpleClassName}[]>([])\nconst loadMoreState = ref<LoadMoreState>('loading')\nconst queryParams = ref({\n  pageNo: 1,\n  pageSize: 10,\n})\n\n/** 返回上一页 */\nfunction handleBack() {\n  navigateBackPlus()\n}\n\n/** 查询${table.classComment}列表 */\nasync function getList() {\n  loadMoreState.value = 'loading'\n  try {\n    const data = await get${simpleClassName}Page(queryParams.value)\n    list.value = [...list.value, ...data.list]\n    total.value = data.total\n    loadMoreState.value = list.value.length >= total.value ? 'finished' : 'loading'\n  } catch {\n    queryParams.value.pageNo = queryParams.value.pageNo > 1 ? queryParams.value.pageNo - 1 : 1\n    loadMoreState.value = 'error'\n  }\n}\n\n/** 搜索按钮操作 */\nfunction handleQuery(data?: Record<string, any>) {\n  queryParams.value = {\n    ...data,\n    pageNo: 1,\n    pageSize: queryParams.value.pageSize,\n  }\n  list.value = []\n  getList()\n}\n\n/** 重置按钮操作 */\nfunction handleReset() {\n  handleQuery()\n}\n\n/** 加载更多 */\nfunction loadMore() {\n  if (loadMoreState.value === 'finished') {\n    return\n  }\n  queryParams.value.pageNo++\n  getList()\n}\n\n/** 新增${table.classComment} */\nfunction handleAdd() {\n  uni.navigateTo({\n    url: '/pages-${table.moduleName}/${table.businessName}/form/index',\n  })\n}\n\n/** 查看详情 */\nfunction handleDetail(item: ${simpleClassName}) {\n  uni.navigateTo({\n    url: `/pages-${table.moduleName}/${table.businessName}/detail/index?id=#[[${]]#item.${primaryColumn.javaField}#[[}]]#`,\n  })\n}\n\n/** 触底加载更多 */\nonReachBottom(() => {\n  loadMore()\n})\n\n/** 初始化 */\nonMounted(() => {\n  getList()\n})\n</script>\n\n<style lang=\"scss\" scoped>\n</style>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben/api/api.ts.vm",
    "content": "import { defHttp } from '@/utils/http/axios'\n#set ($baseURL = \"/${table.moduleName}/${simpleClassName_strikeCase}\")\n\n// 查询${table.classComment}列表\nexport function get${simpleClassName}Page(params) {\n  return defHttp.get({ url: '${baseURL}/page', params })\n}\n\n// 查询${table.classComment}详情\nexport function get${simpleClassName}(id: number) {\n  return defHttp.get({ url: `${baseURL}/get?id=${id}` })\n}\n\n// 新增${table.classComment}\nexport function create${simpleClassName}(data) {\n  return defHttp.post({ url: '${baseURL}/create', data })\n}\n\n// 修改${table.classComment}\nexport function update${simpleClassName}(data) {\n  return defHttp.put({ url: '${baseURL}/update', data })\n}\n\n// 删除${table.classComment}\nexport function delete${simpleClassName}(id: number) {\n  return defHttp.delete({ url: `${baseURL}/delete?id=${id}` })\n}\n\n// 导出${table.classComment} Excel\nexport function export${simpleClassName}(params) {\n  return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls')\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben/views/data.ts.vm",
    "content": "import type {BasicColumn, FormSchema} from '@/components/Table'\nimport {useRender} from '@/components/Table'\nimport {DICT_TYPE, getDictOptions} from '@/utils/dict'\n\nexport const columns: BasicColumn[] = [\n#foreach($column in $columns)\n#if ($column.listOperationResult)\n  #set ($dictType=$column.dictType)\n  #set ($javaField = $column.javaField)\n  #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n  #set ($comment=$column.columnComment)\n#if ($column.javaType == \"LocalDateTime\")## 时间类型\n  {\n    title: '${comment}',\n    dataIndex: '${javaField}',\n    width: 180,\n    customRender: ({ text }) => {\n      return useRender.renderDate(text)\n    },\n  },\n#elseif(\"\" != $column.dictType)## 数据字典\n  {\n    title: '${comment}',\n    dataIndex: '${javaField}',\n    width: 180,\n    customRender: ({ text }) => {\n      return useRender.renderDict(text, DICT_TYPE.$dictType.toUpperCase())\n    },\n  },\n#else\n  {\n    title: '${comment}',\n    dataIndex: '${javaField}',\n    width: 160,\n  },\n#end\n#end\n#end\n]\n\nexport const searchFormSchema: FormSchema[] = [\n#foreach($column in $columns)\n#if ($column.listOperation)\n  #set ($dictType=$column.dictType)\n  #set ($javaType = $column.javaType)\n  #set ($javaField = $column.javaField)\n  #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n  #set ($comment=$column.columnComment)\n  #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n    #set ($dictMethod = \"number\")\n  #elseif ($javaType == \"String\")\n    #set ($dictMethod = \"string\")\n  #elseif ($javaType == \"Boolean\")\n    #set ($dictMethod = \"boolean\")\n  #end\n  {\n    label: '${comment}',\n    field: '${javaField}',\n  #if ($column.htmlType == \"input\")\n    component: 'Input',\n  #elseif ($column.htmlType == \"select\")\n    component: 'Select',\n    componentProps: {\n      #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n      #else## 未设置 dictType 数据字典的情况\n        options: [],\n      #end\n    },\n  #elseif ($column.htmlType == \"radio\")\n    component: 'RadioButtonGroup',\n    componentProps: {\n      #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n      #else## 未设置 dictType 数据字典的情况\n        options: [],\n      #end\n    },\n  #elseif($column.htmlType == \"datetime\")\n    component: 'RangePicker',\n    #end\n    colProps: { span: 8 },\n  },\n#end\n#end\n]\n\nexport const createFormSchema: FormSchema[] = [\n  {\n    label: '编号',\n    field: 'id',\n    show: false,\n    component: 'Input',\n  },\n#foreach($column in $columns)\n#if ($column.createOperation)\n  #set ($dictType = $column.dictType)\n  #set ($javaType = $column.javaType)\n  #set ($javaField = $column.javaField)\n  #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n  #set ($comment = $column.columnComment)\n  #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n    #set ($dictMethod = \"number\")\n  #elseif ($javaType == \"String\")\n    #set ($dictMethod = \"string\")\n  #elseif ($javaType == \"Boolean\")\n    #set ($dictMethod = \"boolean\")\n  #end\n#if (!$column.primaryKey)## 忽略主键，不用在表单里\n  {\n    label: '${comment}',\n    field: '${javaField}',\n  #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n    required: true,\n    #end\n  #if ($column.htmlType == \"input\")\n    component: 'Input',\n  #elseif($column.htmlType == \"imageUpload\")## 图片上传\n    component: 'FileUpload',\n    componentProps: {\n      fileType: 'image',\n      maxCount: 1,\n    },\n  #elseif($column.htmlType == \"fileUpload\")## 文件上传\n    component: 'FileUpload',\n    componentProps: {\n      fileType: 'file',\n      maxCount: 1,\n    },\n  #elseif($column.htmlType == \"editor\")## 文本编辑器\n    component: 'Editor',\n  #elseif($column.htmlType == \"select\")## 下拉框\n    component: 'Select',\n    componentProps: {\n      #if (\"\" != $dictType)## 有数据字典\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n      #else##没数据字典\n        options:[],\n      #end\n    },\n  #elseif($column.htmlType == \"checkbox\")## 多选框\n    component: 'Checkbox',\n    componentProps: {\n      #if (\"\" != $dictType)## 有数据字典\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n      #else##没数据字典\n        options:[],\n      #end\n    },\n  #elseif($column.htmlType == \"radio\")## 单选框\n    component: 'RadioButtonGroup',\n    componentProps: {\n      #if (\"\" != $dictType)## 有数据字典\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n      #else##没数据字典\n        options:[],\n      #end\n    },\n  #elseif($column.htmlType == \"datetime\")## 时间框\n    component: 'DatePicker',\n    componentProps: {\n      showTime: true,\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'x',\n    },\n  #elseif($column.htmlType == \"textarea\")## 文本域\n    component: 'InputTextArea',\n  #end\n  },\n#end\n#end\n#end\n]\n\nexport const updateFormSchema: FormSchema[] = [\n  {\n    label: '编号',\n    field: 'id',\n    show: false,\n    component: 'Input',\n  },\n#foreach($column in $columns)\n#if ($column.updateOperation)\n#set ($dictType = $column.dictType)\n#set ($javaType = $column.javaType)\n#set ($javaField = $column.javaField)\n#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n#set ($comment = $column.columnComment)\n#if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n  #set ($dictMethod = \"number\")\n#elseif ($javaType == \"String\")\n  #set ($dictMethod = \"string\")\n#elseif ($javaType == \"Boolean\")\n  #set ($dictMethod = \"boolean\")\n#end\n  #if (!$column.primaryKey)## 忽略主键，不用在表单里\n  {\n    label: '${comment}',\n    field: '${javaField}',\n    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n    required: true,\n    #end\n    #if ($column.htmlType == \"input\")\n    component: 'Input',\n    #elseif($column.htmlType == \"imageUpload\")## 图片上传\n    component: 'FileUpload',\n    componentProps: {\n      fileType: 'image',\n      maxCount: 1,\n    },\n    #elseif($column.htmlType == \"fileUpload\")## 文件上传\n    component: 'FileUpload',\n    componentProps: {\n      fileType: 'file',\n      maxCount: 1,\n    },\n    #elseif($column.htmlType == \"editor\")## 文本编辑器\n    component: 'Editor',\n    #elseif($column.htmlType == \"select\")## 下拉框\n    component: 'Select',\n    componentProps: {\n      #if (\"\" != $dictType)## 有数据字典\n      options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n      #else##没数据字典\n      options:[],\n      #end\n    },\n    #elseif($column.htmlType == \"checkbox\")## 多选框\n    component: 'Checkbox',\n    componentProps: {\n      #if (\"\" != $dictType)## 有数据字典\n      options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n      #else##没数据字典\n      options:[],\n      #end\n    },\n    #elseif($column.htmlType == \"radio\")## 单选框\n    component: 'RadioButtonGroup',\n    componentProps: {\n      #if (\"\" != $dictType)## 有数据字典\n      options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n      #else##没数据字典\n      options:[],\n      #end\n    },\n    #elseif($column.htmlType == \"datetime\")## 时间框\n    component: 'DatePicker',\n    componentProps: {\n      showTime: true,\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'x',\n    },\n    #elseif($column.htmlType == \"textarea\")## 文本域\n    component: 'InputTextArea',\n    #end\n  },\n  #end\n#end\n#end\n]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben/views/form.vue.vm",
    "content": "<script lang=\"ts\" setup>\nimport { ref, unref } from 'vue'\nimport { createFormSchema, updateFormSchema } from './${classNameVar}.data'\nimport { useI18n } from '@/hooks/web/useI18n'\nimport { useMessage } from '@/hooks/web/useMessage'\nimport { BasicForm, useForm } from '@/components/Form'\nimport { BasicModal, useModalInner } from '@/components/Modal'\nimport { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'\n\ndefineOptions({ name: '${table.className}Modal' })\n\nconst emit = defineEmits(['success', 'register'])\n\nconst { t } = useI18n()\nconst { createMessage } = useMessage()\nconst isUpdate = ref(true)\n\nconst [registerForm, { setFieldsValue, resetFields, resetSchema, validate }] = useForm({\n  labelWidth: 120,\n  baseColProps: { span: 24 },\n  schemas: createFormSchema,\n  showActionButtonGroup: false,\n  actionColOptions: { span: 23 },\n})\n\nconst [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {\n  resetFields()\n  setModalProps({ confirmLoading: false })\n  isUpdate.value = !!data?.isUpdate\n  if (unref(isUpdate)) {\n    resetSchema(updateFormSchema)\n    const res = await get${simpleClassName}(data.record.id)\n    setFieldsValue({ ...res })\n  }\n})\n\nasync function handleSubmit() {\n  try {\n    const values = await validate()\n    setModalProps({ confirmLoading: true })\n    if (unref(isUpdate))\n      await update${simpleClassName}(values)\n    else\n      await create${simpleClassName}(values)\n\n    closeModal()\n    emit('success')\n    createMessage.success(t('common.saveSuccessText'))\n  } finally {\n    setModalProps({ confirmLoading: false })\n  }\n}\n</script>\n<template>\n  <BasicModal v-bind=\"$attrs\" :title=\"isUpdate ? t('action.edit') : t('action.create')\" @register=\"registerModal\" @ok=\"handleSubmit\">\n    <BasicForm @register=\"registerForm\" />\n  </BasicModal>\n</template>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben/views/index.vue.vm",
    "content": "<script lang=\"ts\" setup>\nimport ${simpleClassName}Modal from './${simpleClassName}Modal.vue'\nimport { columns, searchFormSchema } from './${classNameVar}.data'\nimport { useI18n } from '@/hooks/web/useI18n'\nimport { useMessage } from '@/hooks/web/useMessage'\nimport { useModal } from '@/components/Modal'\nimport { IconEnum } from '@/enums/appEnum'\nimport { BasicTable, TableAction, useTable } from '@/components/Table'\nimport { delete${simpleClassName}, export${simpleClassName}, get${simpleClassName}Page } from '@/api/${table.moduleName}/${table.businessName}'\n\ndefineOptions({ name: '${table.className}' })\n\nconst { t } = useI18n()\nconst { createConfirm, createMessage } = useMessage()\nconst [registerModal, { openModal }] = useModal()\n\nconst [registerTable, { getForm, reload }] = useTable({\n  title: '${table.classComment}列表',\n  api: get${simpleClassName}Page,\n    columns,\n    formConfig: { labelWidth: 120, schemas: searchFormSchema },\n    useSearchForm: true,\n    showTableSetting: true,\n    actionColumn: {\n      width: 140,\n      title: t('common.action'),\n      dataIndex: 'action',\n      fixed: 'right',\n    },\n})\n\nfunction handleCreate() {\n  openModal(true, { isUpdate: false })\n}\n\nfunction handleEdit(record: Recordable) {\n  openModal(true, { record, isUpdate: true })\n}\n\nasync function handleExport() {\n  createConfirm({\n    title: t('common.exportTitle'),\n    iconType: 'warning',\n    content: t('common.exportMessage'),\n    async onOk() {\n      await export${simpleClassName}(getForm().getFieldsValue())\n      createMessage.success(t('common.exportSuccessText'))\n    },\n  })\n}\n\nasync function handleDelete(record: Recordable) {\n  await delete${simpleClassName}(record.id)\n  createMessage.success(t('common.delSuccessText'))\n  reload()\n}\n</script>\n<template>\n  <div>\n    <BasicTable @register=\"registerTable\">\n      <template #toolbar>\n        <a-button type=\"primary\" v-auth=\"['${permissionPrefix}:create']\" :preIcon=\"IconEnum.ADD\" @click=\"handleCreate\">\n          {{ t('action.create') }}\n        </a-button>\n        <a-button v-auth=\"['${permissionPrefix}:export']\" :preIcon=\"IconEnum.EXPORT\" @click=\"handleExport\">\n          {{ t('action.export') }}\n        </a-button>\n      </template>\n      <template #bodyCell=\"{ column, record }\">\n        <template v-if=\"column.key === 'action'\">\n          <TableAction\n            :actions=\"[\n              { icon: IconEnum.EDIT, label: t('action.edit'), auth: '${permissionPrefix}:update', onClick: handleEdit.bind(null, record) },\n              {\n                icon: IconEnum.DELETE,\n                danger: true,\n                label: t('action.delete'),\n                auth: '${permissionPrefix}:delete',\n                popConfirm: {\n                  title: t('common.delMessage'),\n                  placement: 'left',\n                  confirm: handleDelete.bind(null, record),\n                },\n              },\n            ]\"\n          />\n        </template>\n      </template>\n    </BasicTable>\n    <${simpleClassName}Modal @register=\"registerModal\" @success=\"reload()\" />\n  </div>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm",
    "content": "import type { PageParam, PageResult } from '@vben/request';\nimport type { Dayjs } from 'dayjs';\n\nimport { requestClient } from '#/api/request';\n#set ($baseURL = \"/${table.moduleName}/${simpleClassName_strikeCase}\")\n\nexport namespace ${simpleClassName}Api {\n  ## 特殊：主子表专属逻辑\n  #foreach ($subTable in $subTables)\n    #set ($index = $foreach.count - 1)\n    #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n    #set ($subColumns = $subColumnsList.get($index))##当前字段数组\n    /** ${subTable.classComment}信息 */\n    export interface ${subSimpleClassName} {\n      #foreach ($column in $subColumns)\n        #if ($column.createOperation || $column.updateOperation)\n          #if(${column.javaType.toLowerCase()} == \"long\" || ${column.javaType.toLowerCase()} == \"integer\" || ${column.javaType.toLowerCase()} == \"short\" || ${column.javaType.toLowerCase()} == \"double\" || ${column.javaType.toLowerCase()} == \"bigdecimal\")\n              ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}\n          #elseif(${column.javaType.toLowerCase()} == \"date\" || ${column.javaType.toLowerCase()} == \"localdate\" || ${column.javaType.toLowerCase()} == \"localdatetime\")\n              ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}\n          #else\n              ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}\n          #end\n        #end\n      #end\n    }\n\n  #end\n  /** ${table.classComment}信息 */\n  export interface ${simpleClassName} {\n    #foreach ($column in $columns)\n      #if ($column.createOperation || $column.updateOperation)\n        #if(${column.javaType.toLowerCase()} == \"long\" || ${column.javaType.toLowerCase()} == \"integer\" || ${column.javaType.toLowerCase()} == \"short\" || ${column.javaType.toLowerCase()} == \"double\" || ${column.javaType.toLowerCase()} == \"bigdecimal\")\n            ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}\n        #elseif(${column.javaType.toLowerCase()} == \"date\" || ${column.javaType.toLowerCase()} == \"localdate\" || ${column.javaType.toLowerCase()} == \"localdatetime\")\n            ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}\n        #else\n            ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}\n        #end\n      #end\n    #end\n    #if ( $table.templateType == 2 )\n      children?: ${simpleClassName}[];\n    #end\n    ## 特殊：主子表专属逻辑\n    #if ( $table.templateType == 10 || $table.templateType == 12 )\n      #foreach ($subTable in $subTables)\n        #set ($index = $foreach.count - 1)\n        #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n        #if ( $subTable.subJoinMany )\n            ${subSimpleClassName.toLowerCase()}s?: ${subSimpleClassName}[]\n        #else\n            ${subSimpleClassName.toLowerCase()}?: ${subSimpleClassName}\n        #end\n      #end\n    #end\n  }\n}\n\n#if ( $table.templateType != 2 )\n/** 查询${table.classComment}分页 */\nexport function get${simpleClassName}Page(params: PageParam) {\n  return requestClient.get<PageResult<${simpleClassName}Api.${simpleClassName}>>('${baseURL}/page', { params });\n}\n#else\n/** 查询${table.classComment}列表 */\nexport function get${simpleClassName}List(params: any) {\n  return requestClient.get<${simpleClassName}Api.${simpleClassName}[]>('${baseURL}/list', { params });\n}\n#end\n\n/** 查询${table.classComment}详情 */\nexport function get${simpleClassName}(id: number) {\n  return requestClient.get<${simpleClassName}Api.${simpleClassName}>(`${baseURL}/get?id=${id}`);\n}\n\n/** 新增${table.classComment} */\nexport function create${simpleClassName}(data: ${simpleClassName}Api.${simpleClassName}) {\n  return requestClient.post('${baseURL}/create', data);\n}\n\n/** 修改${table.classComment} */\nexport function update${simpleClassName}(data: ${simpleClassName}Api.${simpleClassName}) {\n  return requestClient.put('${baseURL}/update', data);\n}\n\n/** 删除${table.classComment} */\nexport function delete${simpleClassName}(id: number) {\n  return requestClient.delete(`${baseURL}/delete?id=${id}`);\n}\n\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n/** 批量删除${table.classComment} */\nexport function delete${simpleClassName}List(ids: number[]) {\n  return requestClient.delete(`${baseURL}/delete-list?ids=${ids.join(',')}`)\n}\n#end\n\n/** 导出${table.classComment} */\nexport function export${simpleClassName}(params: any) {\n  return requestClient.download('${baseURL}/export-excel', { params });\n}\n\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n  #set ($index = $foreach.count - 1)\n  #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n  #set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段\n  #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n  #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n  #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n  #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n  #set ($subClassNameVar = $subClassNameVars.get($index))\n\n// ==================== 子表（$subTable.classComment） ====================\n\n## 情况一：MASTER_ERP 时，需要分查询页子表\n#if ( $table.templateType == 11 )\n/** 获得${subTable.classComment}分页 */\nexport function get${subSimpleClassName}Page(params: PageParam) {\n  return requestClient.get<PageResult<${simpleClassName}Api.${subSimpleClassName}>>(`${baseURL}/${subSimpleClassName_strikeCase}/page`, { params });\n}\n  ## 情况二：非 MASTER_ERP 时，需要列表查询子表\n#else\n#if ( $subTable.subJoinMany )\n/** 获得${subTable.classComment}列表 */\nexport function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}: number) {\n  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}[]>(`${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);\n}\n#else\n/** 获得${subTable.classComment} */\nexport function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}: number) {\n  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);\n}\n#end\n#end\n## 特殊：MASTER_ERP 时，支持单个的新增、修改、删除操作\n#if ( $table.templateType == 11 )\n/** 新增${subTable.classComment} */\nexport function create${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {\n  return requestClient.post(`${baseURL}/${subSimpleClassName_strikeCase}/create`, data);\n}\n\n/** 修改${subTable.classComment} */\nexport function update${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {\n  return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data);\n}\n\n/** 删除${subTable.classComment} */\nexport function delete${subSimpleClassName}(id: number) {\n  return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete?id=${id}`);\n}\n\n#if ($deleteBatchEnable)\n/** 批量删除${subTable.classComment} */\nexport function delete${subSimpleClassName}List(ids: number[]) {\n  return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}`)\n}\n#end\n\n/** 获得${subTable.classComment} */\nexport function get${subSimpleClassName}(id: number) {\n  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`);\n}\n#end\n#end\n\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm",
    "content": "<script lang=\"ts\" setup>\nimport type { Rule } from 'ant-design-vue/es/form';\nimport type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { computed, ref } from 'vue';\n\nimport { useVbenModal } from '@vben/common-ui';\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\nimport { Tinymce as RichTextarea } from '#/components/tinymce';\nimport { ImageUpload, FileUpload } from \"#/components/upload\";\nimport { message, Tabs, Form, Input, Textarea, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, DatePicker, TreeSelect } from 'ant-design-vue';\n#if($table.templateType == 2)## 树表需要导入这些\nimport { get${simpleClassName}List } from '#/api/${table.moduleName}/${table.businessName}';\nimport { handleTree } from '@vben/utils'\n#end\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n  #foreach ($subSimpleClassName in $subSimpleClassNames)\n  #set ($index = $foreach.count - 1)\n  #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n  import ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'\n  #end\n#end\n\nimport { $t } from '#/locales';\nimport { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';\n\nconst emit = defineEmits(['success']);\n\nconst formRef = ref();\nconst formData = ref<Partial<${simpleClassName}Api.${simpleClassName}>>({\n#foreach ($column in $columns)\n  #if ($column.createOperation || $column.updateOperation)\n    #if ($column.htmlType == \"checkbox\")\n        $column.javaField: [],\n    #else\n        $column.javaField: undefined,\n    #end\n  #end\n#end\n});\nconst rules: Record<string, Rule[]> = {\n  #foreach ($column in $columns)\n    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n      #set($comment=$column.columnComment)\n        $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n    #end\n  #end\n};\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\nconst ${classNameVar}Tree = ref<any[]>([]) // 树形结构\n#end\nconst getTitle = computed(() => {\n  return formData.value?.id\n    ? $t('ui.actionTitle.edit', ['${table.classComment}'])\n    : $t('ui.actionTitle.create', ['${table.classComment}']);\n});\n\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n  #if ( $subTables && $subTables.size() > 0 )\n  /** 子表的表单 */\n  const subTabsName = ref('$subClassNameVars.get(0)')\n    #foreach ($subClassNameVar in $subClassNameVars)\n      #set ($index = $foreach.count - 1)\n      #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n      const ${subClassNameVar}FormRef = ref<InstanceType<typeof ${subSimpleClassName}Form>>()\n    #end\n  #end\n#end\n\n/** 重置表单 */\nfunction resetForm() {\n  formData.value = {\n    #foreach ($column in $columns)\n      #if ($column.createOperation || $column.updateOperation)\n        #if ($column.htmlType == \"checkbox\")\n            $column.javaField: [],\n        #else\n            $column.javaField: undefined,\n        #end\n      #end\n    #end\n  };\n  formRef.value?.resetFields();\n}\n\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n/** 获得${table.classComment}树 */\nasync function get${simpleClassName}Tree() {\n  ${classNameVar}Tree.value = []\n  const data = await get${simpleClassName}List({});\n  data.unshift({\n    id: 0,\n    name: '顶级${table.classComment}',\n  });\n    ${classNameVar}Tree.value = handleTree(data);\n}\n#end\n\nconst [Modal, modalApi] = useVbenModal({\n  async onConfirm() {\n    await formRef.value?.validate();\n    ## 特殊：主子表专属逻辑\n    #if ( $table.templateType == 10 || $table.templateType == 12 )\n      #if ( $subTables && $subTables.size() > 0 )\n        // 校验子表单\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #if ($subTable.subJoinMany) ## 一对多\n            ## TODO 列表值校验？\n          #else\n            try {\n              await ${subClassNameVar}FormRef.value?.validate()\n            } catch (e) {\n              subTabsName.value = '${subClassNameVar}'\n              return\n            }\n          #end\n        #end\n      #end\n    #end\n    modalApi.lock();\n    // 提交表单\n    const data = formData.value as ${simpleClassName}Api.${simpleClassName};\n    ## 特殊：主子表专属逻辑\n    #if ( $table.templateType == 10 || $table.templateType == 12 )\n      #if ( $subTables && $subTables.size() > 0 )\n        // 拼接子表的数据\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #if ($subTable.subJoinMany)\n            data.${subClassNameVar}s = ${subClassNameVar}FormRef.value?.getData();\n          #else\n            data.${subClassNameVar} = ${subClassNameVar}FormRef.value?.getValues();\n          #end\n        #end\n      #end\n    #end\n    try {\n      await (formData.value?.id ? update${simpleClassName}(data) : create${simpleClassName}(data));\n      // 关闭并提示\n      await modalApi.close();\n      emit('success');\n      message.success({\n        content: $t('ui.actionMessage.operationSuccess'),\n      });\n    } finally {\n      modalApi.unlock();\n    }\n  },\n  async onOpenChange(isOpen: boolean) {\n    if (!isOpen) {\n      resetForm()\n      return;\n    }\n    // 加载数据\n    let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();\n    if (!data) {\n      return;\n    }\n    if (data.id) {\n      modalApi.lock();\n      try {\n        data = await get${simpleClassName}(data.id);\n      } finally {\n        modalApi.unlock();\n      }\n    }\n    formData.value = data;\n#if ( $table.templateType == 2 )\n    // 加载树数据\n    await get${simpleClassName}Tree()\n#end\n  },\n});\n</script>\n\n<template>\n  <Modal :title=\"getTitle\">\n    <Form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"rules\"\n      :label-col=\"{ span: 5 }\"\n      :wrapper-col=\"{ span: 18 }\"\n    >\n      #foreach($column in $columns)\n        #if ($column.createOperation || $column.updateOperation)\n          #set ($dictType = $column.dictType)\n          #set ($javaField = $column.javaField)\n          #set ($javaType = $column.javaType)\n          #set ($comment = $column.columnComment)\n          #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n            #set ($dictMethod = \"number\")\n          #elseif ($javaType == \"String\")\n            #set ($dictMethod = \"string\")\n          #elseif ($javaType == \"Boolean\")\n            #set ($dictMethod = \"boolean\")\n          #end\n          #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <TreeSelect\n                      v-model:value=\"formData.${javaField}\"\n                      :treeData=\"${classNameVar}Tree\"\n                #if ($treeNameColumn.javaField == \"name\")\n                      :fieldNames=\"{\n            label: 'name',\n            value: 'id',\n            children: 'children',\n          }\"\n                #else\n                      :fieldNames=\"{\n                        label: '$treeNameColumn.javaField',\n                        value: 'id',\n                        children: 'children',\n                        }\"\n                #end\n                      checkable\n                      treeDefaultExpandAll\n                      placeholder=\"请选择${comment}\"\n              />\n            </Form.Item>\n          #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <Input v-model:value=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n            </Form.Item>\n          #elseif($column.htmlType == \"imageUpload\")## 图片上传\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <ImageUpload v-model:value=\"formData.${javaField}\" />\n            </Form.Item>\n          #elseif($column.htmlType == \"fileUpload\")## 文件上传\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <FileUpload v-model:value=\"formData.${javaField}\" />\n            </Form.Item>\n          #elseif($column.htmlType == \"editor\")## 文本编辑器\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <RichTextarea v-model=\"formData.${javaField}\" height=\"500px\" />\n            </Form.Item>\n          #elseif($column.htmlType == \"select\")## 下拉框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <Select v-model:value=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <Select.Option\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :value=\"dict.value\"\n                  >\n                    {{ dict.label }}\n                  </Select.Option>\n                #else##没数据字典\n                  <Select.Option label=\"请选择字典生成\" value=\"\" />\n                #end\n              </Select>\n            </Form.Item>\n          #elseif($column.htmlType == \"checkbox\")## 多选框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <CheckboxGroup v-model:value=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <Checkbox\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :value=\"dict.value\"\n                 >\n                    {{ dict.label }}\n                  </Checkbox>\n                #else##没数据字典\n                  <Checkbox label=\"请选择字典生成\" />\n                #end\n              </CheckboxGroup>\n            </Form.Item>\n          #elseif($column.htmlType == \"radio\")## 单选框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <RadioGroup v-model:value=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <Radio\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :value=\"dict.value\"\n                  >\n                    {{ dict.label }}\n                  </Radio>\n                #else##没数据字典\n                  <Radio value=\"1\">请选择字典生成</Radio>\n                #end\n              </RadioGroup>\n            </Form.Item>\n          #elseif($column.htmlType == \"datetime\")## 时间框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <DatePicker\n                      v-model:value=\"formData.${javaField}\"\n                      valueFormat=\"x\"\n                      placeholder=\"选择${comment}\"\n              />\n            </Form.Item>\n          #elseif($column.htmlType == \"textarea\")## 文本框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <Textarea v-model:value=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n            </Form.Item>\n          #end\n        #end\n      #end\n    </Form>\n    ## 特殊：主子表专属逻辑\n    #if ( $table.templateType == 10 || $table.templateType == 12 )\n      <!-- 子表的表单 -->\n      <Tabs v-model:active-key=\"subTabsName\">\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n          #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n          <Tabs.TabPane key=\"$subClassNameVar\" tab=\"${subTable.classComment}\" force-render>\n            <${subSimpleClassName}Form ref=\"${subClassNameVar}FormRef\" :${subJoinColumn_strikeCase}=\"formData?.id\" />\n          </Tabs.TabPane>\n        #end\n      </Tabs>\n    #end\n  </Modal>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm",
    "content": "<script lang=\"ts\" setup>\nimport type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { ref, h, reactive, onMounted, nextTick } from 'vue';\n\nimport { Page, useVbenModal } from '@vben/common-ui';\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\nimport { useTableToolbar, VbenVxeTableToolbar } from '@vben/plugins/vxe-table';\nimport { cloneDeep, downloadFileFromBlobPart, formatDateTime, isEmpty } from '@vben/utils';\nimport { Button, Card, message, Tabs, Pagination, Form, RangePicker, DatePicker, Select, Input } from 'ant-design-vue';\nimport ${simpleClassName}Form from './modules/form.vue';\nimport { Download, Plus, RefreshCw, Search, Trash2 } from '@vben/icons';\nimport { DictTag } from '#/components/dict-tag';\nimport { VxeColumn, VxeTable } from '#/adapter/vxe-table';\nimport { getRangePickerDefaultProps } from '#/utils/rangePickerProps';\n\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 11 || $table.templateType == 12 )\n    #foreach ($subSimpleClassName in $subSimpleClassNames)\n    #set ($index = $foreach.count - 1)\n    #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n    import ${subSimpleClassName}List from './modules/${subSimpleClassName_strikeCase}-list.vue'\n    #end\n#end\n\nimport { $t } from '#/locales';\n#if (${table.templateType} == 2)## 树表接口\nimport { handleTree } from '@vben/utils'\nimport { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';\n#else## 标准表接口\nimport { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';\n#end\n\n#if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况\n/** 子表的列表 */\nconst subTabsName = ref('$subClassNameVars.get(0)')\n#if ($table.templateType == 11)\nconst select${simpleClassName} = ref<${simpleClassName}Api.${simpleClassName}>();\nasync function onCellClick({ row }: { row: ${simpleClassName}Api.${simpleClassName} }) {\n  select${simpleClassName}.value = row\n}\n#end\n#end\n\nconst loading = ref(true) // 列表的加载中\n#if ( $table.templateType == 2 )\nconst list = ref<any[]>([]) // 树列表的数据\n#else\nconst list = ref<${simpleClassName}Api.${simpleClassName}[]>([]) // 列表的数据\n#end\n\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\nconst total = ref(0) // 列表的总页数\n#end\nconst queryParams = reactive({\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\n  pageNo: 1,\n  pageSize: 10,\n#end\n#foreach ($column in $columns)\n    #if ($column.listOperation)\n        #if ($column.listOperationCondition != 'BETWEEN')\n                $column.javaField: undefined,\n        #end\n        #if ($column.htmlType == \"datetime\" || $column.listOperationCondition == \"BETWEEN\")\n                $column.javaField: undefined,\n        #end\n    #end\n#end\n})\nconst queryFormRef = ref() // 搜索的表单\nconst exportLoading = ref(false) // 导出的加载中\n\n/** 查询列表 */\nasync function getList() {\n  loading.value = true\n  try {\n    const params = cloneDeep(queryParams) as any;\n      #foreach ($column in $columns)\n          #if ($column.listOperation)\n              #if ($column.htmlType == \"datetime\" || $column.listOperationCondition == \"BETWEEN\")\n                if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {\n                  params.${column.javaField} = (params.${column.javaField} as string[]).join(',');\n                }\n              #end\n          #end\n      #end\n      ## 特殊：树表专属逻辑（树不需要分页接口）\n      #if ( $table.templateType == 2 )\n        list.value = await get${simpleClassName}List(params);\n      #else\n        const data = await get${simpleClassName}Page(params)\n        list.value = data.list\n        total.value = data.total\n      #end\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nfunction handleQuery() {\n#if ( $table.templateType != 2 )\n  queryParams.pageNo = 1\n#end\n  getList()\n}\n\n/** 重置按钮操作 */\nfunction resetQuery() {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n\nconst [FormModal, formModalApi] = useVbenModal({\n  connectedComponent: ${simpleClassName}Form,\n  destroyOnClose: true,\n});\n\n/** 创建${table.classComment} */\nfunction handleCreate() {\n  formModalApi.setData(null).open();\n}\n\n/** 编辑${table.classComment} */\nfunction handleEdit(row: ${simpleClassName}Api.${simpleClassName}) {\n  formModalApi.setData(row).open();\n}\n\n#if (${table.templateType} == 2)## 树表特有：新增下级\n/** 新增下级${table.classComment} */\nfunction handleAppend(row: ${simpleClassName}Api.${simpleClassName}) {\n  formModalApi.setData({ ${treeParentColumn.javaField}: row.id }).open();\n}\n#end\n\n/** 删除${table.classComment} */\nasync function handleDelete(row: ${simpleClassName}Api.${simpleClassName}) {\n  const hideLoading = message.loading({\n    content: $t('ui.actionMessage.deleting', [row.id]),\n    duration: 0,\n  });\n  try {\n    await delete${simpleClassName}(row.id!);\n    message.success($t('ui.actionMessage.deleteSuccess', [row.id]));\n    await getList();\n  } finally {\n    hideLoading();\n  }\n}\n\n#if ($table.templateType != 2 && $deleteBatchEnable)\n/** 批量删除${table.classComment} */\nasync function handleDeleteBatch() {\n  const hideLoading = message.loading({\n    content: $t('ui.actionMessage.deleting'),\n    duration: 0,\n  });\n  try {\n    await delete${simpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    message.success($t('ui.actionMessage.deleteSuccess'));\n    await getList();\n  } finally {\n    hideLoading();\n  }\n}\n\nconst checkedIds = ref<number[]>([])\nfunction handleRowCheckboxChange({\n  records,\n}: {\n  records: ${simpleClassName}Api.${simpleClassName}[];\n}) {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n\n/** 导出表格 */\nasync function handleExport() {\ntry {\n  exportLoading.value = true;\n  const data = await export${simpleClassName}(queryParams);\n  downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });\n}finally {\n  exportLoading.value = false;\n}\n}\n\n#if (${table.templateType} == 2)\n/** 切换树形展开/收缩状态 */\nconst isExpanded = ref(true);\nfunction handleExpand() {\n  isExpanded.value = !isExpanded.value;\n  tableRef.value?.setAllTreeExpand(isExpanded.value);\n}\n#end\n\n/** 初始化 */\nconst { hiddenSearchBar, tableToolbarRef, tableRef } = useTableToolbar();\nonMounted(() => {\n  getList();\n});\n</script>\n\n<template>\n  <Page auto-content-height>\n    <FormModal @success=\"getList\" />\n\n    <Card v-if=\"!hiddenSearchBar\" class=\"mb-4\">\n      <!-- 搜索工作栏 -->\n      <Form\n          :model=\"queryParams\"\n          ref=\"queryFormRef\"\n          layout=\"inline\"\n      >\n          #foreach($column in $columns)\n              #if ($column.listOperation)\n                  #set ($dictType = $column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($javaType = $column.javaType)\n                  #set ($comment = $column.columnComment)\n                  #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                      #set ($dictMethod = \"number\")\n                  #elseif ($javaType == \"String\")\n                      #set ($dictMethod = \"string\")\n                  #elseif ($javaType == \"Boolean\")\n                      #set ($dictMethod = \"boolean\")\n                  #end\n                  #if ($column.htmlType == \"input\")\n                    <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                      <Input\n                          v-model:value=\"queryParams.${javaField}\"\n                          placeholder=\"请输入${comment}\"\n                          allowClear\n                          @pressEnter=\"handleQuery\"\n                           class=\"w-full\"\n                      />\n                    </Form.Item>\n                  #elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\" || $column.htmlType == \"checkbox\")\n                    <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                      <Select\n                          v-model:value=\"queryParams.${javaField}\"\n                          placeholder=\"请选择${comment}\"\n                          allowClear\n                           class=\"w-full\"\n                      >\n                          #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n                            <Select.Option\n                                v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                                :key=\"dict.value\"\n                                :value=\"dict.value\"\n                            >\n                              {{ dict.label }}\n                            </Select.Option>\n                          #else## 未设置 dictType 数据字典的情况\n                            <Select.Option label=\"请选择字典生成\" value=\"\" />\n                          #end\n                      </Select>\n                    </Form.Item>\n                  #elseif($column.htmlType == \"datetime\")\n                      #if ($column.listOperationCondition != \"BETWEEN\")## 非范围\n                        <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                          <DatePicker\n                              v-model:value=\"queryParams.${javaField}\"\n                              valueFormat=\"YYYY-MM-DD\"\n                              placeholder=\"选择${comment}\"\n                              allowClear\n                               class=\"w-full\"\n                          />\n                        </Form.Item>\n                      #else## 范围\n                        <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                          <RangePicker\n                              v-model:value=\"queryParams.${javaField}\"\n                              v-bind=\"getRangePickerDefaultProps()\"\n                              class=\"w-full\"\n                          />\n                        </Form.Item>\n                      #end\n                  #end\n              #end\n          #end\n        <Form.Item>\n          <Button class=\"ml-2\" @click=\"resetQuery\"> 重置 </Button>\n          <Button class=\"ml-2\" @click=\"handleQuery\" type=\"primary\">\n            搜索\n          </Button>\n        </Form.Item>\n      </Form>\n    </Card>\n\n    <!-- 列表 -->\n    <Card title=\"${table.classComment}\">\n      <template #extra>\n        <VbenVxeTableToolbar\n            ref=\"tableToolbarRef\"\n            v-model:hidden-search=\"hiddenSearchBar\"\n        >\n        #if (${table.templateType} == 2)\n          <Button @click=\"handleExpand\" class=\"mr-2\">\n            {{ isExpanded ? '收缩' : '展开' }}\n          </Button>\n        #end\n          <Button\n              class=\"ml-2\"\n              :icon=\"h(Plus)\"\n              type=\"primary\"\n              @click=\"handleCreate\"\n              v-access:code=\"['${permissionPrefix}:create']\"\n          >\n            {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}\n          </Button>\n          <Button\n              :icon=\"h(Download)\"\n              type=\"primary\"\n              class=\"ml-2\"\n              :loading=\"exportLoading\"\n              @click=\"handleExport\"\n              v-access:code=\"['${permissionPrefix}:export']\"\n          >\n            {{ $t('ui.actionTitle.export') }}\n          </Button>\n        #if ($table.templateType != 2 && $deleteBatchEnable)\n          <Button\n              :icon=\"h(Trash2)\"\n              type=\"primary\"\n              danger\n              class=\"ml-2\"\n              :disabled=\"isEmpty(checkedIds)\"\n              @click=\"handleDeleteBatch\"\n              v-access:code=\"['${table.moduleName}:${simpleClassName_strikeCase}:delete']\"\n          >\n            批量删除\n          </Button>\n        #end\n        </VbenVxeTableToolbar>\n      </template>\n      <VxeTable\n          ref=\"tableRef\"\n          :data=\"list\"\n        #if ( $table.templateType == 2 )\n          :tree-config=\"{\n          parentField: '${treeParentColumn.javaField}',\n          rowField: 'id',\n          transform: true,\n          expandAll: true,\n          reserve: true,\n        }\"\n        #end\n#if ($table.templateType == 11) ## erp情况\n          @cell-click=\"onCellClick\"\n          :row-config=\"{\n            keyField: 'id',\n            isHover: true,\n            isCurrent: true,\n          }\"\n#end\n          show-overflow\n          :loading=\"loading\"\n#if ($table.templateType != 2 && $deleteBatchEnable)\n          @checkboxAll=\"handleRowCheckboxChange\"\n          @checkboxChange=\"handleRowCheckboxChange\"\n#end\n      >\n#if ($table.templateType != 2 && $deleteBatchEnable)\n        <VxeColumn type=\"checkbox\" width=\"40\" />\n#end\n          ## 特殊：主子表专属逻辑\n          #if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )\n            <!-- 子表的列表 -->\n            <VxeColumn type=\"expand\" width=\"60\">\n              <template #content=\"{ row }\">\n                <!-- 子表的表单 -->\n                <Tabs v-model:active-key=\"subTabsName\" class=\"mx-8\">\n                    #foreach ($subTable in $subTables)\n                        #set ($index = $foreach.count - 1)\n                        #set ($subClassNameVar = $subClassNameVars.get($index))\n                        #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n                        #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n                      <Tabs.TabPane key=\"$subClassNameVar\" tab=\"${subTable.classComment}\" force-render>\n                        <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"row?.id\" />\n                      </Tabs.TabPane>\n                    #end\n                </Tabs>\n              </template>\n            </VxeColumn>\n          #end\n          #foreach($column in $columns)\n              #if ($column.listOperationResult)\n                  #set ($dictType=$column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n                  #set ($comment=$column.columnComment)\n                  #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                    <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                      <template #default=\"{row}\">\n                        {{formatDateTime(row.${javaField})}}\n                      </template>\n                    </VxeColumn>\n                  #elseif($column.dictType && \"\" != $column.dictType)## 数据字典\n                    <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                      <template #default=\"{row}\">\n                        <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"row.${javaField}\" />\n                      </template>\n                    </VxeColumn>\n                  #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)\n                    <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\"  tree-node/>\n                  #else\n                    <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\" />\n                  #end\n              #end\n          #end\n        <VxeColumn field=\"operation\" title=\"操作\" align=\"center\">\n          <template #default=\"{row}\">\n#if ( $table.templateType == 2 )\n  <Button\n      size=\"small\"\n      type=\"link\"\n      @click=\"handleAppend(row)\"\n      v-access:code=\"['${permissionPrefix}:create']\"\n  >\n    新增下级\n  </Button>\n#end\n            <Button\n                size=\"small\"\n                type=\"link\"\n                @click=\"handleEdit(row)\"\n                v-access:code=\"['${permissionPrefix}:update']\"\n            >\n              {{ $t('ui.actionTitle.edit') }}\n            </Button>\n            <Button\n                size=\"small\"\n                type=\"link\"\n                danger\n                class=\"ml-2\"\n                #if ( $table.templateType == 2 )\n                :disabled=\"!isEmpty(row?.children)\"\n                #end\n                @click=\"handleDelete(row)\"\n                v-access:code=\"['${permissionPrefix}:delete']\"\n            >\n              {{ $t('ui.actionTitle.delete') }}\n            </Button>\n          </template>\n        </VxeColumn>\n      </VxeTable>\n#if ( $table.templateType != 2 )\n      <!-- 分页 -->\n      <div class=\"mt-2 flex justify-end\">\n        <Pagination\n            :total=\"total\"\n            v-model:current=\"queryParams.pageNo\"\n            v-model:page-size=\"queryParams.pageSize\"\n            show-size-changer\n            @change=\"getList\"\n        />\n      </div>\n#end\n    </Card>\n#if ($table.templateType == 11) ## erp情况\n  <Card>\n    <!-- 子表的表单 -->\n    <Tabs v-model:active-key=\"subTabsName\">\n        #foreach ($subTable in $subTables)\n            #set ($index = $foreach.count - 1)\n            #set ($subClassNameVar = $subClassNameVars.get($index))\n            #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n            #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n          <Tabs.TabPane key=\"$subClassNameVar\" tab=\"${subTable.classComment}\" force-render>\n            <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"select${simpleClassName}?.id\" />\n          </Tabs.TabPane>\n        #end\n    </Tabs>\n  </Card>\n#end\n  </Page>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n<script lang=\"ts\" setup>\nimport type { Rule } from 'ant-design-vue/es/form';\nimport type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { computed, ref } from 'vue';\n\nimport { useVbenModal } from '@vben/common-ui';\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\nimport { Tinymce as RichTextarea } from '#/components/tinymce';\nimport { ImageUpload, FileUpload } from \"#/components/upload\";\nimport { message, Tabs, Form, Input, Textarea, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, DatePicker, TreeSelect } from 'ant-design-vue';\n\nimport { $t } from '#/locales';\n\nimport { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';\n\nconst emit = defineEmits(['success']);\nconst getTitle = computed(() => {\n  return formData.value?.id\n      ? $t('ui.actionTitle.edit', ['${subTable.classComment}'])\n      : $t('ui.actionTitle.create', ['${subTable.classComment}']);\n});\n\nconst formRef = ref();\nconst formData = ref<Partial<${simpleClassName}Api.${subSimpleClassName}>>({\n  #foreach ($column in $subColumns)\n    #if ($column.createOperation || $column.updateOperation)\n      #if ($column.htmlType == \"checkbox\")\n          $column.javaField: [],\n      #else\n          $column.javaField: undefined,\n      #end\n    #end\n  #end\n});\nconst rules: Record<string, Rule[]> = {\n  #foreach ($column in $subColumns)\n    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n      #set($comment=$column.columnComment)\n        $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n    #end\n  #end\n};\n\nconst [Modal, modalApi] = useVbenModal({\n  async onConfirm() {\n    await formRef.value?.validate();\n\n    modalApi.lock();\n    // 提交表单\n    const data = formData.value as ${simpleClassName}Api.${subSimpleClassName};\n    try {\n      await (formData.value?.id ? update${subSimpleClassName}(data) : create${subSimpleClassName}(data));\n      // 关闭并提示\n      await modalApi.close();\n      emit('success');\n      message.success({\n        content: $t('ui.actionMessage.operationSuccess'),\n      });\n    } finally {\n      modalApi.unlock();\n    }\n  },\n  async onOpenChange(isOpen: boolean) {\n    if (!isOpen) {\n      resetForm()\n      return;\n    }\n\n    // 加载数据\n    let data = modalApi.getData<${simpleClassName}Api.${subSimpleClassName}>();\n    if (!data) {\n      return;\n    }\n    if (data.id) {\n      modalApi.lock();\n      try {\n        data = await get${subSimpleClassName}(data.id);\n      } finally {\n        modalApi.unlock();\n      }\n    }\n    // 设置到 values\n    formData.value = data;\n  },\n});\n\n/** 重置表单 */\nfunction resetForm(){\n  formData.value = {\n    #foreach ($column in $subColumns)\n      #if ($column.createOperation || $column.updateOperation)\n        #if ($column.htmlType == \"checkbox\")\n            $column.javaField: [],\n        #else\n            $column.javaField: undefined,\n        #end\n      #end\n    #end\n  };\n  formRef.value?.resetFields();\n}\n</script>\n\n<template>\n  <Modal :title=\"getTitle\">\n    <Form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"rules\"\n      :label-col=\"{ span: 5 }\"\n      :wrapper-col=\"{ span: 18 }\"\n    >\n      #foreach($column in $subColumns)\n        #if ($column.createOperation || $column.updateOperation)\n          #set ($dictType = $column.dictType)\n          #set ($javaField = $column.javaField)\n          #set ($javaType = $column.javaType)\n          #set ($comment = $column.columnComment)\n          #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n            #set ($dictMethod = \"number\")\n          #elseif ($javaType == \"String\")\n            #set ($dictMethod = \"string\")\n          #elseif ($javaType == \"Boolean\")\n            #set ($dictMethod = \"boolean\")\n          #end\n          #if ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <Input v-model:value=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n            </Form.Item>\n          #elseif($column.htmlType == \"imageUpload\")## 图片上传\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <ImageUpload v-model:value=\"formData.${javaField}\" />\n            </Form.Item>\n          #elseif($column.htmlType == \"fileUpload\")## 文件上传\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <FileUpload v-model:value=\"formData.${javaField}\" />\n            </Form.Item>\n          #elseif($column.htmlType == \"editor\")## 文本编辑器\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <RichTextarea v-model=\"formData.${javaField}\" height=\"500px\" />\n            </Form.Item>\n          #elseif($column.htmlType == \"select\")## 下拉框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <Select v-model:value=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <Select.Option\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :value=\"dict.value\"\n                  >\n                    {{ dict.label }}\n                  </Select.Option>\n                #else##没数据字典\n                  <Select.Option label=\"请选择字典生成\" value=\"\" />\n                #end\n              </Select>\n            </Form.Item>\n          #elseif($column.htmlType == \"checkbox\")## 多选框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <CheckboxGroup v-model:value=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <Checkbox\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :value=\"dict.value\"\n                  >\n                    {{ dict.label }}\n                  </Checkbox>\n                #else##没数据字典\n                  <Checkbox label=\"请选择字典生成\" />\n                #end\n              </CheckboxGroup>\n            </Form.Item>\n          #elseif($column.htmlType == \"radio\")## 单选框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <RadioGroup v-model:value=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <Radio\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :value=\"dict.value\"\n                  >\n                    {{ dict.label }}\n                  </Radio>\n                #else##没数据字典\n                  <Radio value=\"1\">请选择字典生成</Radio>\n                #end\n              </RadioGroup>\n            </Form.Item>\n          #elseif($column.htmlType == \"datetime\")## 时间框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <DatePicker\n                      v-model:value=\"formData.${javaField}\"\n                      valueFormat=\"x\"\n                      placeholder=\"选择${comment}\"\n              />\n            </Form.Item>\n          #elseif($column.htmlType == \"textarea\")## 文本框\n            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n              <Textarea v-model:value=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n            </Form.Item>\n          #end\n        #end\n      #end\n    </Form>\n  </Modal>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm",
    "content": "## 主表的 normal 和 inner 使用相同的 form 表单\n#parse(\"codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subClassNameVar = $subClassNameVars.get($subIndex))\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n<script lang=\"ts\" setup>\nimport type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { computed, ref, h, onMounted, watch, nextTick } from 'vue';\n\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\n\nimport { message, Tabs, Form, Input, Textarea, Button, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, DatePicker } from 'ant-design-vue';\nimport { $t } from '#/locales';\n\n#if ($subTable.subJoinMany) ## 一对多\nimport type { VxeTableInstance } from '#/adapter/vxe-table';\nimport { Plus } from \"@vben/icons\";\nimport { VxeColumn, VxeTable } from '#/adapter/vxe-table';\nimport { ImageUpload, FileUpload } from \"#/components/upload\";\nimport { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#else\nimport type { Rule } from 'ant-design-vue/es/form';\nimport { Tinymce as RichTextarea } from '#/components/tinymce';\nimport { ImageUpload, FileUpload } from \"#/components/upload\";\nimport { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#end\n\nconst props = defineProps<{\n   ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\n\n#if ($subTable.subJoinMany) ## 一对多\nconst list = ref<${simpleClassName}Api.${subSimpleClassName}[]>([]) // 列表的数据\nconst tableRef = ref<VxeTableInstance>();\n\n/** 添加${subTable.classComment} */\nasync function handleAdd() {\n  await tableRef.value?.insertAt({} as ${simpleClassName}Api.${subSimpleClassName}, -1);\n}\n\n/** 删除${subTable.classComment} */\nasync function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {\n  await tableRef.value?.remove(row);\n}\n\n/** 提供获取表格数据的方法供父组件调用 */\ndefineExpose({\n  getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {\n    const data = list.value as ${simpleClassName}Api.${subSimpleClassName}[];\n    const removeRecords = tableRef.value?.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];\n    const insertRecords = tableRef.value?.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];\n    return [\n      ...data.filter(\n        (row) => !removeRecords.some((removed) => removed.id === row.id),\n      ),\n      ...insertRecords.map((row: any) => ({ ...row, id: undefined })),\n    ];\n  },\n});\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n    () => props.${subJoinColumn.javaField},\n    async (val) => {\n      if (!val) {\n        return;\n      }\n      list.value = await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!);\n    },\n    { immediate: true },\n);\n#else\nconst formRef = ref();\nconst formData = ref<Partial<${simpleClassName}Api.${subSimpleClassName}>>({\n    #foreach ($column in $subColumns)\n        #if ($column.createOperation || $column.updateOperation)\n            #if ($column.htmlType == \"checkbox\")\n                    $column.javaField: [],\n            #else\n                    $column.javaField: undefined,\n            #end\n        #end\n    #end\n});\nconst rules: Record<string, Rule[]> = {\n    #foreach ($column in $subColumns)\n        #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n            #set($comment=$column.columnComment)\n                $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n        #end\n    #end\n};\n/** 暴露出表单校验方法和表单值获取方法 */\ndefineExpose({\n  validate: async () => await formRef.value?.validate(),\n  getValues: ()=> formData.value,\n});\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n    () => props.${subJoinColumn.javaField},\n    async (val) => {\n      if (!val) {\n        return;\n      }\n      await nextTick();\n      formData.value = await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!);\n    },\n    { immediate: true },\n);\n#end\n</script>\n\n<template>\n#if ($subTable.subJoinMany) ## 一对多\n  <VxeTable ref=\"tableRef\" :data=\"list\" show-overflow class=\"mx-4\">\n      #foreach($column in $subColumns)\n          #if ($column.createOperation || $column.updateOperation)\n              #set ($comment = $column.columnComment)\n              #set ($javaField = $column.javaField)\n              #set ($javaType = $column.javaType)\n              #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                  #set ($dictMethod = \"number\")\n              #elseif ($javaType == \"String\")\n                  #set ($dictMethod = \"string\")\n              #elseif ($javaType == \"Boolean\")\n                  #set ($dictMethod = \"boolean\")\n              #end\n              #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n              #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n                <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{ row }\">\n                    <Input v-model:value=\"row.${javaField}\" />\n                  </template>\n                </VxeColumn>\n              #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{ row }\">\n                    <ImageUpload v-model:value=\"row.${javaField}\" />\n                  </template>\n                </VxeColumn>\n              #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{ row }\">\n                    <FileUpload v-model:value=\"row.${javaField}\" />\n                  </template>\n                </VxeColumn>\n              #elseif($column.htmlType == \"select\")## 下拉框\n                <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{ row }\">\n                    <Select v-model:value=\"row.${javaField}\" placeholder=\"请选择${comment}\">\n                        #if (\"\" != $dictType)## 有数据字典\n                          <Select.Option\n                              v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                              :key=\"dict.value\"\n                              :value=\"dict.value\"\n                          >\n                            {{ dict.label }}\n                          </Select.Option>\n                        #else##没数据字典\n                          <Select.Option label=\"请选择字典生成\" value=\"\" />\n                        #end\n                    </Select>\n                  </template>\n                </VxeColumn>\n              #elseif($column.htmlType == \"checkbox\")## 多选框\n                <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{ row }\">\n                    <CheckboxGroup v-model:value=\"row.${javaField}\">\n                        #if (\"\" != $dictType)## 有数据字典\n                          <Checkbox\n                              v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                              :key=\"dict.value\"\n                              :value=\"dict.value\"\n                          >\n                            {{ dict.label }}\n                          </Checkbox>\n                        #else##没数据字典\n                          <Checkbox label=\"请选择字典生成\" />\n                        #end\n                    </CheckboxGroup>\n                  </template>\n                </VxeColumn>\n              #elseif($column.htmlType == \"radio\")## 单选框\n                <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{ row }\">\n                    <RadioGroup v-model:value=\"row.${javaField}\">\n                        #if (\"\" != $dictType)## 有数据字典\n                          <Radio\n                              v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                              :key=\"dict.value\"\n                              :value=\"dict.value\"\n                          >\n                            {{ dict.label }}\n                          </Radio>\n                        #else##没数据字典\n                          <Radio value=\"1\">请选择字典生成</Radio>\n                        #end\n                    </RadioGroup>\n                  </template>\n                </VxeColumn>\n              #elseif($column.htmlType == \"datetime\")## 时间框\n                <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{ row }\">\n                    <DatePicker\n                        v-model:value=\"row.${javaField}\"\n                        :showTime=\"true\"\n                        format=\"YYYY-MM-DD HH:mm:ss\"\n                        valueFormat='x'\n                    />\n                  </template>\n                </VxeColumn>\n              #elseif($column.htmlType == \"textarea\" || $column.htmlType == \"editor\")## 文本框\n                <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{ row }\">\n                    <Textarea v-model:value=\"row.${javaField}\" />\n                  </template>\n                </VxeColumn>\n              #end\n          #end\n      #end\n    <VxeColumn field=\"operation\" title=\"操作\" align=\"center\">\n      <template #default=\"{ row }\">\n        <Button\n            size=\"small\"\n            type=\"link\"\n            danger\n            @click=\"handleDelete(row)\"\n            v-access:code=\"['${permissionPrefix}:delete']\"\n        >\n          {{ $t('ui.actionTitle.delete') }}\n        </Button>\n      </template>\n    </VxeColumn>\n  </VxeTable>\n  <div class=\"flex justify-center mt-4\">\n    <Button :icon=\"h(Plus)\" type=\"primary\" ghost @click=\"handleAdd\" v-access:code=\"['${permissionPrefix}:create']\">\n      {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}\n    </Button>\n  </div>\n#else\n  <Form\n      ref=\"formRef\"\n      class=\"mx-4\"\n      :model=\"formData\"\n      :rules=\"rules\"\n      :label-col=\"{ span: 5 }\"\n      :wrapper-col=\"{ span: 18 }\"\n  >\n      #foreach($column in $subColumns)\n          #if ($column.createOperation || $column.updateOperation)\n              #set ($dictType = $column.dictType)\n              #set ($javaField = $column.javaField)\n              #set ($javaType = $column.javaType)\n              #set ($comment = $column.columnComment)\n              #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                  #set ($dictMethod = \"number\")\n              #elseif ($javaType == \"String\")\n                  #set ($dictMethod = \"string\")\n              #elseif ($javaType == \"Boolean\")\n                  #set ($dictMethod = \"boolean\")\n              #end\n              #if ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n                <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                  <Input v-model:value=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n                </Form.Item>\n              #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                  <ImageUpload v-model:value=\"formData.${javaField}\" />\n                </Form.Item>\n              #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                  <FileUpload v-model:value=\"formData.${javaField}\" />\n                </Form.Item>\n              #elseif($column.htmlType == \"editor\")## 文本编辑器\n                <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                  <RichTextarea v-model=\"formData.${javaField}\" height=\"500px\" />\n                </Form.Item>\n              #elseif($column.htmlType == \"select\")## 下拉框\n                <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                  <Select v-model:value=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                      #if (\"\" != $dictType)## 有数据字典\n                        <Select.Option\n                            v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                            :key=\"dict.value\"\n                            :value=\"dict.value\"\n                        >\n                          {{ dict.label }}\n                        </Select.Option>\n                      #else##没数据字典\n                        <Select.Option label=\"请选择字典生成\" value=\"\" />\n                      #end\n                  </Select>\n                </Form.Item>\n              #elseif($column.htmlType == \"checkbox\")## 多选框\n                <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                  <CheckboxGroup v-model:value=\"formData.${javaField}\">\n                      #if (\"\" != $dictType)## 有数据字典\n                        <Checkbox\n                            v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                            :key=\"dict.value\"\n                            :value=\"dict.value\"\n                        >\n                          {{ dict.label }}\n                        </Checkbox>\n                      #else##没数据字典\n                        <Checkbox label=\"请选择字典生成\" />\n                      #end\n                  </CheckboxGroup>\n                </Form.Item>\n              #elseif($column.htmlType == \"radio\")## 单选框\n                <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                  <RadioGroup v-model:value=\"formData.${javaField}\">\n                      #if (\"\" != $dictType)## 有数据字典\n                        <Radio\n                            v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                            :key=\"dict.value\"\n                            :value=\"dict.value\"\n                        >\n                          {{ dict.label }}\n                        </Radio>\n                      #else##没数据字典\n                        <Radio value=\"1\">请选择字典生成</Radio>\n                      #end\n                  </RadioGroup>\n                </Form.Item>\n              #elseif($column.htmlType == \"datetime\")## 时间框\n                <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                  <DatePicker\n                      v-model:value=\"formData.${javaField}\"\n                      valueFormat=\"x\"\n                      placeholder=\"选择${comment}\"\n                  />\n                </Form.Item>\n              #elseif($column.htmlType == \"textarea\")## 文本框\n                <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                  <Textarea v-model:value=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n                </Form.Item>\n              #end\n          #end\n      #end\n  </Form>\n#end\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($subIndex))\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n<script lang=\"ts\" setup>\nimport type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';\nimport type { VxeTableInstance } from '#/adapter/vxe-table';\n\nimport { reactive, ref, h, nextTick, watch, onMounted } from 'vue';\n\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\n\nimport { DictTag } from '#/components/dict-tag';\nimport { getRangePickerDefaultProps } from '#/utils/rangePickerProps';\nimport { VxeColumn, VxeTable } from '#/adapter/vxe-table';\nimport { formatDateTime } from '@vben/utils';\n\n#if ($table.templateType == 11) ## erp\nimport { useVbenModal } from '@vben/common-ui';\nimport { useTableToolbar, VbenVxeTableToolbar } from '@vben/plugins/vxe-table';\nimport ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'\nimport { Tinymce as RichTextarea } from '#/components/tinymce';\nimport { ImageUpload, FileUpload } from \"#/components/upload\";\nimport { message, Button, Card, Tabs, Pagination, Form, Input, Textarea, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, RangePicker, DatePicker, TreeSelect } from 'ant-design-vue';\nimport { Plus, Trash2 } from '@vben/icons';\nimport { $t } from '#/locales';\n#end\n\n#if ($table.templateType == 11) ## erp\nimport { delete${subSimpleClassName},#if ($deleteBatchEnable) delete${subSimpleClassName}List,#end get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${table.businessName}';\nimport { isEmpty, cloneDeep } from '@vben/utils';\n#else\n#if ($subTable.subJoinMany) ## 一对多\nimport { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#else\nimport { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#end\n#end\n\nconst props = defineProps<{\n  ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\n\n#if ($table.templateType == 11) ## erp\nconst [FormModal, formModalApi] = useVbenModal({\n  connectedComponent: ${subSimpleClassName}Form,\n  destroyOnClose: true,\n});\n\n/** 创建${subTable.classComment} */\nfunction handleCreate() {\n  if (!props.${subJoinColumn.javaField}){\n    message.warning(\"请先选择一个${table.classComment}!\")\n    return\n  }\n  formModalApi.setData({${subJoinColumn.javaField}: props.${subJoinColumn.javaField}}).open();\n}\n\n/** 编辑${subTable.classComment} */\nfunction handleEdit(row: ${simpleClassName}Api.${subSimpleClassName}) {\n  formModalApi.setData(row).open();\n}\n\n/** 删除${subTable.classComment} */\nasync function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {\n  const hideLoading = message.loading({\n    content: $t('ui.actionMessage.deleting', [row.id]),\n    duration: 0,\n  });\n  try {\n    await delete${subSimpleClassName}(row.id!);\n    message.success($t('ui.actionMessage.deleteSuccess', [row.id]));\n    await getList();\n  } finally {\n    hideLoading();\n  }\n}\n\n#if ($deleteBatchEnable)\n/** 批量删除${subTable.classComment} */\nasync function handleDeleteBatch() {\n  const hideLoading = message.loading({\n    content: $t('ui.actionMessage.deleting'),\n    duration: 0,\n  });\n  try {\n    await delete${subSimpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    message.success($t('ui.actionMessage.deleteSuccess'));\n    await getList();\n  } finally {\n    hideLoading();\n  }\n}\n\nconst checkedIds = ref<number[]>([])\nfunction handleRowCheckboxChange({\n  records,\n}: {\n  records: ${simpleClassName}Api.${subSimpleClassName}[];\n}) {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n#end\n\nconst loading = ref(true) // 列表的加载中\nconst list = ref<${simpleClassName}Api.${subSimpleClassName}[]>([]) // 列表的数据\n#if ($table.templateType == 11) ## erp\nconst total = ref(0) // 列表的总页数\n#end\n#if ($table.templateType == 11) ## erp\nconst queryFormRef = ref() // 搜索的表单\nconst queryParams = reactive({\n  pageNo: 1,\n  pageSize: 10,\n  #foreach ($column in $subColumns)\n    #if ($column.listOperation)\n      #if ($column.listOperationCondition != 'BETWEEN')\n            $column.javaField: undefined,\n      #end\n      #if ($column.htmlType == \"datetime\" || $column.listOperationCondition == \"BETWEEN\")\n            $column.javaField: undefined,\n      #end\n    #end\n  #end\n})\n\n/** 搜索按钮操作 */\nfunction handleQuery() {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 重置按钮操作 */\nfunction resetQuery() {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n#end\n  /** 查询列表 */\nasync function getList() {\n  loading.value = true\n  try {\n    if (!props.${subJoinColumn.javaField}){\n      return []\n    }\n      ## 特殊：树表专属逻辑（树不需要分页接口）\n      #if ($table.templateType == 11) ## erp\n        const params = cloneDeep(queryParams) as any;\n          #foreach ($column in $columns)\n              #if ($column.listOperation)\n                  #if ($column.htmlType == \"datetime\" || $column.listOperationCondition == \"BETWEEN\")\n                    if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {\n                      params.${column.javaField} = (params.${column.javaField} as string[]).join(',');\n                    }\n                  #end\n              #end\n          #end\n        params.${subJoinColumn.javaField} = props.${subJoinColumn.javaField};\n        const data = await get${subSimpleClassName}Page(params)\n        list.value = data.list\n        total.value = data.total\n      #else\n          #if ($subTable.subJoinMany) ## 一对多\n           list.value = await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!);\n          #else\n           list.value = [await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!)];\n          #end\n      #end\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.${subJoinColumn.javaField},\n  async (val) => {\n    if (!val) {\n      return;\n    }\n    await nextTick();\n    await getList()\n  },\n  { immediate: true },\n);\n\n#if ($table.templateType == 11) ## erp\n/** 初始化 */\nconst { hiddenSearchBar, tableToolbarRef, tableRef } = useTableToolbar();\nonMounted(() => {\n  getList();\n});\n#end\n</script>\n\n<template>\n    #if ($table.templateType == 11) ## erp\n      <FormModal @success=\"getList\" />\n      <div class=\"h-[600px]\">\n        <Card v-if=\"!hiddenSearchBar\" class=\"mb-4\">\n          <!-- 搜索工作栏 -->\n          <Form\n              :model=\"queryParams\"\n              ref=\"queryFormRef\"\n              layout=\"inline\"\n          >\n              #foreach($column in $subColumns)\n                  #if ($column.listOperation)\n                      #set ($dictType = $column.dictType)\n                      #set ($javaField = $column.javaField)\n                      #set ($javaType = $column.javaType)\n                      #set ($comment = $column.columnComment)\n                      #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                          #set ($dictMethod = \"number\")\n                      #elseif ($javaType == \"String\")\n                          #set ($dictMethod = \"string\")\n                      #elseif ($javaType == \"Boolean\")\n                          #set ($dictMethod = \"boolean\")\n                      #end\n                      #if ($column.htmlType == \"input\")\n                        <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                          <Input\n                              v-model:value=\"queryParams.${javaField}\"\n                              placeholder=\"请输入${comment}\"\n                              allowClear\n                              @pressEnter=\"handleQuery\"\n                              class=\"w-full\"\n                          />\n                        </Form.Item>\n                      #elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\" || $column.htmlType == \"checkbox\")\n                        <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                          <Select\n                              v-model:value=\"queryParams.${javaField}\"\n                              placeholder=\"请选择${comment}\"\n                              allowClear\n                              class=\"w-full\"\n                          >\n                              #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n                                <Select.Option\n                                    v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                                    :key=\"dict.value\"\n                                    :value=\"dict.value\"\n                                >\n                                  {{ dict.label }}\n                                </Select.Option>\n                              #else## 未设置 dictType 数据字典的情况\n                                <Select.Option label=\"请选择字典生成\" value=\"\" />\n                              #end\n                          </Select>\n                        </Form.Item>\n                      #elseif($column.htmlType == \"datetime\")\n                          #if ($column.listOperationCondition != \"BETWEEN\")## 非范围\n                            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                              <DatePicker\n                                  v-model:value=\"queryParams.${javaField}\"\n                                  valueFormat=\"YYYY-MM-DD\"\n                                  placeholder=\"选择${comment}\"\n                                  allowClear\n                                  class=\"w-full\"\n                              />\n                            </Form.Item>\n                          #else## 范围\n                            <Form.Item label=\"${comment}\" name=\"${javaField}\">\n                              <RangePicker\n                                  v-model:value=\"queryParams.${javaField}\"\n                                  v-bind=\"getRangePickerDefaultProps()\"\n                                  class=\"w-full\"\n                              />\n                            </Form.Item>\n                          #end\n                      #end\n                  #end\n              #end\n            <Form.Item>\n              <Button class=\"ml-2\" @click=\"resetQuery\"> 重置 </Button>\n              <Button class=\"ml-2\" @click=\"handleQuery\" type=\"primary\">\n                搜索\n              </Button>\n            </Form.Item>\n          </Form>\n        </Card>\n\n        <!-- 列表 -->\n        <Card title=\"${table.classComment}\">\n          <template #extra>\n            <VbenVxeTableToolbar\n                ref=\"tableToolbarRef\"\n                v-model:hidden-search=\"hiddenSearchBar\"\n            >\n              <Button\n                  class=\"ml-2\"\n                  :icon=\"h(Plus)\"\n                  type=\"primary\"\n                  @click=\"handleCreate\"\n                  v-access:code=\"['${permissionPrefix}:create']\"\n              >\n                {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}\n              </Button>\n                #if ($deleteBatchEnable)\n                  <Button\n                      :icon=\"h(Trash2)\"\n                      type=\"primary\"\n                      danger\n                      class=\"ml-2\"\n                      :disabled=\"isEmpty(checkedIds)\"\n                      @click=\"handleDeleteBatch\"\n                      v-access:code=\"['${table.moduleName}:${simpleClassName_strikeCase}:delete']\"\n                  >\n                    批量删除\n                  </Button>\n                #end\n            </VbenVxeTableToolbar>\n          </template>\n          <VxeTable\n              ref=\"tableRef\"\n              :data=\"list\"\n              show-overflow\n              :loading=\"loading\"\n              #if ($deleteBatchEnable)\n              @checkboxAll=\"handleRowCheckboxChange\"\n              @checkboxChange=\"handleRowCheckboxChange\"\n              #end\n          >\n              #if ($deleteBatchEnable)\n                <VxeColumn type=\"checkbox\" width=\"40\" />\n              #end\n              #foreach($column in $subColumns)\n                  #if ($column.listOperationResult)\n                      #set ($dictType=$column.dictType)\n                      #set ($javaField = $column.javaField)\n                      #set ($comment=$column.columnComment)\n                      #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                        <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                          <template #default=\"{row}\">\n                            {{formatDateTime(row.${javaField})}}\n                          </template>\n                        </VxeColumn>\n                      #elseif($column.dictType && \"\" != $column.dictType)## 数据字典\n                        <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                          <template #default=\"{row}\">\n                            <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"row.${javaField}\" />\n                          </template>\n                        </VxeColumn>\n                      #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)\n                        <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\"  tree-node/>\n                      #else\n                        <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\" />\n                      #end\n                  #end\n              #end\n            <VxeColumn field=\"operation\" title=\"操作\" align=\"center\">\n              <template #default=\"{row}\">\n                <Button\n                    size=\"small\"\n                    type=\"link\"\n                    @click=\"handleEdit(row)\"\n                    v-access:code=\"['${permissionPrefix}:update']\"\n                >\n                  {{ $t('ui.actionTitle.edit') }}\n                </Button>\n                <Button\n                    size=\"small\"\n                    type=\"link\"\n                    danger\n                    class=\"ml-2\"\n                    @click=\"handleDelete(row)\"\n                    v-access:code=\"['${permissionPrefix}:delete']\"\n                >\n                  {{ $t('ui.actionTitle.delete') }}\n                </Button>\n              </template>\n            </VxeColumn>\n          </VxeTable>\n          <!-- 分页 -->\n          <div class=\"mt-2 flex justify-end\">\n            <Pagination\n                :total=\"total\"\n                v-model:current=\"queryParams.pageNo\"\n                v-model:page-size=\"queryParams.pageSize\"\n                show-size-changer\n                @change=\"getList\"\n            />\n          </div>\n        </Card>\n      </div>\n    #else\n    <Card title=\"${subTable.classComment}列表\">\n      <VxeTable\n          :data=\"list\"\n          show-overflow\n          :loading=\"loading\"\n      >\n          #foreach($column in $subColumns)\n              #if ($column.listOperationResult)\n                  #set ($dictType=$column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($comment=$column.columnComment)\n                  #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                    <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                      <template #default=\"{row}\">\n                        {{formatDateTime(row.${javaField})}}\n                      </template>\n                    </VxeColumn>\n                  #elseif($column.dictType && \"\" != $column.dictType)## 数据字典\n                    <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                      <template #default=\"{row}\">\n                        <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"row.${javaField}\" />\n                      </template>\n                    </VxeColumn>\n                  #else\n                    <VxeColumn field=\"${javaField}\" title=\"${comment}\" align=\"center\" />\n                  #end\n              #end\n          #end\n      </VxeTable>\n    </Card>\n    #end\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm",
    "content": "## 子表的 erp 和 inner 使用相似的 list 列表，差异主要两点：\n## 1）inner 使用 list 不分页，erp 使用 page 分页\n## 2）erp 支持单个子表的新增、修改、删除，inner 不支持\n#parse(\"codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm",
    "content": "#parse(\"codegen/vue3_vben5_ele/schema/api/api.ts.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm",
    "content": "#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\nimport type { VbenFormSchema } from '#/adapter/form';\nimport type { VxeTableGridOptions } from '#/adapter/vxe-table';\nimport type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\n#if(${table.templateType} == 2)## 树表需要导入这些\nimport { get${simpleClassName}List } from '#/api/${table.moduleName}/${table.businessName}';\nimport { handleTree } from '@vben/utils';\n#end\n\nimport { getRangePickerDefaultProps } from '#/utils';\n\n/** 新增/修改的表单 */\nexport function useFormSchema(): VbenFormSchema[] {\n  return [\n    {\n      fieldName: 'id',\n      component: 'Input',\n      dependencies: {\n        triggerFields: [''],\n        show: () => false,\n      },\n    },\n#if(${table.templateType} == 2)## 树表特有字段：上级\n    {\n      fieldName: '${treeParentColumn.javaField}',\n      label: '上级${table.classComment}',\n      component: 'ApiTreeSelect',\n      componentProps: {\n        allowClear: true,\n        api: async () => {\n          const data = await get${simpleClassName}List({});\n          data.unshift({\n            id: 0,\n            ${treeNameColumn.javaField}: '顶级${table.classComment}',\n          });\n          return handleTree(data);\n        },\n        labelField: '${treeNameColumn.javaField}',\n        valueField: 'id',\n        childrenField: 'children',\n        placeholder: '请选择上级${table.classComment}',\n        treeDefaultExpandAll: true,\n      },\n      rules: 'selectRequired',\n    },\n#end\n#foreach($column in $columns)\n#if ($column.createOperation || $column.updateOperation)\n#if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段，这里排除\n  #set ($dictType = $column.dictType)\n  #set ($javaType = $column.javaType)\n  #set ($javaField = $column.javaField)\n  #set ($comment = $column.columnComment)\n  #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n    #set ($dictMethod = \"number\")\n  #elseif ($javaType == \"String\")\n    #set ($dictMethod = \"string\")\n  #elseif ($javaType == \"Boolean\")\n    #set ($dictMethod = \"boolean\")\n  #end\n    {\n      fieldName: '${javaField}',\n      label: '${comment}',\n  #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n      rules: 'required',\n  #end\n  #if ($column.htmlType == \"input\")\n      component: 'Input',\n      componentProps: {\n        placeholder: '请输入${comment}',\n      },\n  #elseif($column.htmlType == \"imageUpload\")## 图片上传\n      component: 'ImageUpload',\n  #elseif($column.htmlType == \"fileUpload\")## 文件上传\n      component: 'FileUpload',\n  #elseif($column.htmlType == \"editor\")## 文本编辑器\n      component: 'RichTextarea',\n  #elseif($column.htmlType == \"select\")## 下拉框\n      component: 'Select',\n      componentProps: {\n        #if (\"\" != $dictType)## 有数据字典\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n        #else##没数据字典\n        options: [],\n        #end\n        placeholder: '请选择${comment}',\n      },\n  #elseif($column.htmlType == \"checkbox\")## 多选框\n      component: 'Checkbox',\n      componentProps: {\n        #if (\"\" != $dictType)## 有数据字典\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n        #else##没数据字典\n        options: [],\n        #end\n      },\n  #elseif($column.htmlType == \"radio\")## 单选框\n      component: 'RadioGroup',\n      componentProps: {\n        #if (\"\" != $dictType)## 有数据字典\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n        #else##没数据字典\n        options: [],\n        #end\n        buttonStyle: 'solid',\n        optionType: 'button',\n      },\n  #elseif($column.htmlType == \"datetime\")## 时间框\n      component: 'DatePicker',\n      componentProps: {\n        showTime: true,\n        format: 'YYYY-MM-DD HH:mm:ss',\n        valueFormat: 'x',\n      },\n  #elseif($column.htmlType == \"textarea\")## 文本域\n      component: 'Textarea',\n      componentProps: {\n        placeholder: '请输入${comment}',\n      },\n  #elseif($column.htmlType == \"inputNumber\")## 数字输入框\n      component: 'InputNumber',\n      componentProps: {\n        min: 0,\n        placeholder: '请输入${comment}',\n      },\n  #end\n    },\n#end\n#end\n#end\n  ];\n}\n\n/** 列表的搜索表单 */\nexport function useGridFormSchema(): VbenFormSchema[] {\n  return [\n#foreach($column in $columns)\n#if ($column.listOperation)\n  #set ($dictType = $column.dictType)\n  #set ($javaType = $column.javaType)\n  #set ($javaField = $column.javaField)\n  #set ($comment = $column.columnComment)\n  #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n    #set ($dictMethod = \"number\")\n  #elseif ($javaType == \"String\")\n    #set ($dictMethod = \"string\")\n  #elseif ($javaType == \"Boolean\")\n    #set ($dictMethod = \"boolean\")\n  #end\n    {\n      fieldName: '${javaField}',\n      label: '${comment}',\n  #if ($column.htmlType == \"input\" || $column.htmlType == \"textarea\" || $column.htmlType == \"editor\")\n      component: 'Input',\n      componentProps: {\n        allowClear: true,\n        placeholder: '请输入${comment}',\n      },\n  #elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\")\n      component: 'Select',\n      componentProps: {\n        allowClear: true,\n        #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n        #else## 未设置 dictType 数据字典的情况\n        options: [],\n        #end\n        placeholder: '请选择${comment}',\n      },\n  #elseif($column.htmlType == \"datetime\")\n      component: 'RangePicker',\n      componentProps: {\n        ...getRangePickerDefaultProps(),\n        allowClear: true,\n      },\n  #end\n    },\n#end\n#end\n  ];\n}\n\n/** 列表的字段 */\nexport function useGridColumns(): VxeTableGridOptions<${apiName}.${simpleClassName}>['columns'] {\n  return [\n#if ($table.templateType != 2 && $deleteBatchEnable)\n  { type: 'checkbox', width: 40 },\n#end\n#if ($table.templateType == 12) ## 内嵌情况\n      { type: 'expand', width: 80, slots: { content: 'expand_content' } },\n#end\n#foreach($column in $columns)\n#if ($column.listOperationResult)\n  #set ($dictType = $column.dictType)\n  #set ($javaField = $column.javaField)\n  #set ($comment = $column.columnComment)\n    {\n      field: '${javaField}',\n      title: '${comment}',\n      minWidth: 120,\n  #if ($column.javaType == \"LocalDateTime\")## 时间类型\n      formatter: 'formatDateTime',\n  #elseif(\"\" != $dictType)## 数据字典\n      cellRender: {\n        name: 'CellDict',\n        props: { type: DICT_TYPE.$dictType.toUpperCase() },\n      },\n  #end\n  #if (${table.templateType} == 2 && $column.id == $treeNameColumn.id)## 树表特有:标记树节点列\n      treeNode: true,\n  #end\n    },\n#end\n#end\n    {\n      title: '操作',\n      width: 200,\n      fixed: 'right',\n      slots: { default: 'actions' },\n    },\n  ];\n}\n\n## 标准模式和内嵌模式时，主子关系一对一则生成表单schema,一对多则生成列表schema（内嵌模式时表单schema也要生成）。erp 模式时都生成\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n    #set ($index = $foreach.count - 1)\n    #set ($subColumns = $subColumnsList.get($index))##当前字段数组\n    #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n    #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n    #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n// ==================== 子表（$subTable.classComment） ====================\n\n#if ($table.templateType == 11) ## erp 情况\n/** 新增/修改的表单 */\nexport function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {\n    return [\n        {\n            fieldName: 'id',\n            component: 'Input',\n            dependencies: {\n                triggerFields: [''],\n                show: () => false,\n            },\n        },\n        #foreach($column in $subColumns)\n            #if ($column.createOperation || $column.updateOperation)\n                #if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段，这里排除\n                    #set ($dictType = $column.dictType)\n                    #set ($javaType = $column.javaType)\n                    #set ($javaField = $column.javaField)\n                    #set ($comment = $column.columnComment)\n                    #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                        #set ($dictMethod = \"number\")\n                    #elseif ($javaType == \"String\")\n                        #set ($dictMethod = \"string\")\n                    #elseif ($javaType == \"Boolean\")\n                        #set ($dictMethod = \"boolean\")\n                    #end\n                    #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                    #else\n                        {\n                            fieldName: '${javaField}',\n                            label: '${comment}',\n                            #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n                                rules: 'required',\n                            #end\n                            #if ($column.htmlType == \"input\")\n                                component: 'Input',\n                                componentProps: {\n                                    placeholder: '请输入${comment}',\n                                },\n                            #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                                component: 'ImageUpload',\n                            #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                                component: 'FileUpload',\n                            #elseif($column.htmlType == \"editor\")## 文本编辑器\n                                component: 'RichTextarea',\n                            #elseif($column.htmlType == \"select\")## 下拉框\n                                component: 'Select',\n                                componentProps: {\n                                    #if (\"\" != $dictType)## 有数据字典\n                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                    #else##没数据字典\n                                        options: [],\n                                    #end\n                                    placeholder: '请选择${comment}',\n                                },\n                            #elseif($column.htmlType == \"checkbox\")## 多选框\n                                component: 'Checkbox',\n                                componentProps: {\n                                    #if (\"\" != $dictType)## 有数据字典\n                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                    #else##没数据字典\n                                        options: [],\n                                    #end\n                                },\n                            #elseif($column.htmlType == \"radio\")## 单选框\n                                component: 'RadioGroup',\n                                componentProps: {\n                                    #if (\"\" != $dictType)## 有数据字典\n                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                    #else##没数据字典\n                                        options: [],\n                                    #end\n                                    buttonStyle: 'solid',\n                                    optionType: 'button',\n                                },\n                            #elseif($column.htmlType == \"datetime\")## 时间框\n                                component: 'DatePicker',\n                                componentProps: {\n                                    showTime: true,\n                                    format: 'YYYY-MM-DD HH:mm:ss',\n                                    valueFormat: 'x',\n                                },\n                            #elseif($column.htmlType == \"textarea\")## 文本域\n                                component: 'Textarea',\n                                componentProps: {\n                                    placeholder: '请输入${comment}',\n                                },\n                            #elseif($column.htmlType == \"inputNumber\")## 数字输入框\n                                component: 'InputNumber',\n                                componentProps: {\n                                    min: 0,\n                                    placeholder: '请输入${comment}',\n                                },\n                            #end\n                        },\n                    #end\n                #end\n            #end\n        #end\n    ];\n}\n\n/** 列表的搜索表单 */\nexport function use${subSimpleClassName}GridFormSchema(): VbenFormSchema[] {\n    return [\n        #foreach($column in $subColumns)\n            #if ($column.listOperation)\n                #set ($dictType = $column.dictType)\n                #set ($javaType = $column.javaType)\n                #set ($javaField = $column.javaField)\n                #set ($comment = $column.columnComment)\n                #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                    #set ($dictMethod = \"number\")\n                #elseif ($javaType == \"String\")\n                    #set ($dictMethod = \"string\")\n                #elseif ($javaType == \"Boolean\")\n                    #set ($dictMethod = \"boolean\")\n                #end\n                {\n                    fieldName: '${javaField}',\n                    label: '${comment}',\n                    #if ($column.htmlType == \"input\" || $column.htmlType == \"textarea\" || $column.htmlType == \"editor\")\n                        component: 'Input',\n                        componentProps: {\n                            allowClear: true,\n                            placeholder: '请输入${comment}',\n                        },\n                    #elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\")\n                        component: 'Select',\n                        componentProps: {\n                            allowClear: true,\n                            #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n                                options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                            #else## 未设置 dictType 数据字典的情况\n                                options: [],\n                            #end\n                            placeholder: '请选择${comment}',\n                        },\n                    #elseif($column.htmlType == \"datetime\")\n                        component: 'RangePicker',\n                        componentProps: {\n                            ...getRangePickerDefaultProps(),\n                            allowClear: true,\n                        },\n                    #end\n                },\n            #end\n        #end\n    ];\n}\n\n/** 列表的字段 */\nexport function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {\n    return [\n        #if ($table.templateType != 2 && $deleteBatchEnable)\n            { type: 'checkbox', width: 40 },\n        #end\n        #foreach($column in $subColumns)\n            #if ($column.listOperationResult)\n                #set ($dictType = $column.dictType)\n                #set ($javaField = $column.javaField)\n                #set ($comment = $column.columnComment)\n                {\n                    field: '${javaField}',\n                    title: '${comment}',\n                    minWidth: 120,\n                    #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                        formatter: 'formatDateTime',\n                    #elseif(\"\" != $dictType)## 数据字典\n                        cellRender: {\n                            name: 'CellDict',\n                            props: { type: DICT_TYPE.$dictType.toUpperCase() },\n                        },\n                    #end\n                },\n            #end\n        #end\n        {\n            title: '操作',\n            width: 200,\n            fixed: 'right',\n            slots: { default: 'actions' },\n        },\n    ];\n}\n\n#else\n    #if ($subTable.subJoinMany) ## 一对多\n    /** 新增/修改列表的字段 */\n    export function use${subSimpleClassName}GridEditColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {\n        return [\n            #foreach($column in $subColumns)\n                #if ($column.createOperation || $column.updateOperation)\n                    #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                        #set ($dictType = $column.dictType)\n                        #set ($javaField = $column.javaField)\n                        #set ($comment = $column.columnComment)\n                        #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                            #set ($dictMethod = \"number\")\n                        #elseif ($javaType == \"String\")\n                            #set ($dictMethod = \"string\")\n                        #elseif ($javaType == \"Boolean\")\n                            #set ($dictMethod = \"boolean\")\n                        #end\n                        {\n                            field: '${javaField}',\n                            title: '${comment}',\n                            minWidth: 120,\n                            slots: { default: '${javaField}' },\n                            #if ($column.htmlType == \"select\" || $column.htmlType == \"checkbox\" || $column.htmlType == \"radio\")\n                                #if (\"\" != $dictType)## 有数据字典\n                                    params: {\n                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                    },\n                                #else\n                                    params: {\n                                        options: [],\n                                    },\n                                #end\n                            #end\n                        },\n                    #end\n                #end\n            #end\n            {\n                title: '操作',\n                width: 200,\n                fixed: 'right',\n                slots: { default: 'actions' },\n            },\n        ];\n    }\n\n    #else\n    /** 新增/修改的表单 */\n    export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {\n        return [\n            {\n                fieldName: 'id',\n                component: 'Input',\n                dependencies: {\n                    triggerFields: [''],\n                    show: () => false,\n                },\n            },\n            #foreach($column in $subColumns)\n                #if ($column.createOperation || $column.updateOperation)\n                    #if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段，这里排除\n                        #set ($dictType = $column.dictType)\n                        #set ($javaType = $column.javaType)\n                        #set ($javaField = $column.javaField)\n                        #set ($comment = $column.columnComment)\n                        #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                            #set ($dictMethod = \"number\")\n                        #elseif ($javaType == \"String\")\n                            #set ($dictMethod = \"string\")\n                        #elseif ($javaType == \"Boolean\")\n                            #set ($dictMethod = \"boolean\")\n                        #end\n                        #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                        #else\n                            {\n                                fieldName: '${javaField}',\n                                label: '${comment}',\n                                #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n                                    rules: 'required',\n                                #end\n                                #if ($column.htmlType == \"input\")\n                                    component: 'Input',\n                                    componentProps: {\n                                        placeholder: '请输入${comment}',\n                                    },\n                                #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                                    component: 'ImageUpload',\n                                #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                                    component: 'FileUpload',\n                                #elseif($column.htmlType == \"editor\")## 文本编辑器\n                                    component: 'RichTextarea',\n                                #elseif($column.htmlType == \"select\")## 下拉框\n                                    component: 'Select',\n                                    componentProps: {\n                                        #if (\"\" != $dictType)## 有数据字典\n                                            options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                        #else##没数据字典\n                                            options: [],\n                                        #end\n                                        placeholder: '请选择${comment}',\n                                    },\n                                #elseif($column.htmlType == \"checkbox\")## 多选框\n                                    component: 'Checkbox',\n                                    componentProps: {\n                                        #if (\"\" != $dictType)## 有数据字典\n                                            options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                        #else##没数据字典\n                                            options: [],\n                                        #end\n                                    },\n                                #elseif($column.htmlType == \"radio\")## 单选框\n                                    component: 'RadioGroup',\n                                    componentProps: {\n                                        #if (\"\" != $dictType)## 有数据字典\n                                            options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                        #else##没数据字典\n                                            options: [],\n                                        #end\n                                        buttonStyle: 'solid',\n                                        optionType: 'button',\n                                    },\n                                #elseif($column.htmlType == \"datetime\")## 时间框\n                                    component: 'DatePicker',\n                                    componentProps: {\n                                        showTime: true,\n                                        format: 'YYYY-MM-DD HH:mm:ss',\n                                        valueFormat: 'x',\n                                    },\n                                #elseif($column.htmlType == \"textarea\")## 文本域\n                                    component: 'Textarea',\n                                    componentProps: {\n                                        placeholder: '请输入${comment}',\n                                    },\n                                #elseif($column.htmlType == \"inputNumber\")## 数字输入框\n                                    component: 'InputNumber',\n                                    componentProps: {\n                                        min: 0,\n                                        placeholder: '请输入${comment}',\n                                    },\n                                #end\n                            },\n                        #end\n                    #end\n                #end\n            #end\n        ];\n    }\n\n    #end\n    #if ($table.templateType == 12) ## 内嵌情况\n    /** 列表的字段 */\n    export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {\n        return [\n            #foreach($column in $subColumns)\n                #if ($column.listOperationResult)\n                    #set ($dictType = $column.dictType)\n                    #set ($javaField = $column.javaField)\n                    #set ($comment = $column.columnComment)\n                    {\n                        field: '${javaField}',\n                        title: '${comment}',\n                        minWidth: 120,\n                        #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                            formatter: 'formatDateTime',\n                        #elseif(\"\" != $dictType)## 数据字典\n                            cellRender: {\n                                name: 'CellDict',\n                                props: { type: DICT_TYPE.$dictType.toUpperCase() },\n                            },\n                        #end\n                    },\n                #end\n            #end\n        ];\n    }\n    #end\n#end\n#end"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm",
    "content": "#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\nimport type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { computed, ref } from 'vue';\n\nimport { useVbenModal } from '@vben/common-ui';\n\nimport { message#if ($table.templateType == 11), Tabs#end } from 'ant-design-vue';\n\nimport { useVbenForm } from '#/adapter/form';\nimport { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';\nimport { $t } from '#/locales';\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n  #foreach ($subSimpleClassName in $subSimpleClassNames)\n  #set ($index = $foreach.count - 1)\n  #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n  import ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'\n  #end\n#end\n\nimport { useFormSchema } from '../data';\n\nconst emit = defineEmits(['success']);\nconst formData = ref<${apiName}.${simpleClassName}>();\nconst getTitle = computed(() => {\n  return formData.value?.id\n    ? $t('ui.actionTitle.edit', ['${table.classComment}'])\n    : $t('ui.actionTitle.create', ['${table.classComment}']);\n});\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n  #if ( $subTables && $subTables.size() > 0 )\n\n  /** 子表的表单 */\n  const subTabsName = ref('$subClassNameVars.get(0)')\n    #foreach ($subClassNameVar in $subClassNameVars)\n      #set ($index = $foreach.count - 1)\n      #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n      const ${subClassNameVar}FormRef = ref<InstanceType<typeof ${subSimpleClassName}Form>>()\n    #end\n  #end\n#end\n\nconst [Form, formApi] = useVbenForm({\n  commonConfig: {\n    componentProps: {\n      class: 'w-full',\n    },\n    formItemClass: 'col-span-2',\n    labelWidth: 80,\n  },\n  layout: 'horizontal',\n  schema: useFormSchema(),\n  showDefaultActions: false,\n});\n\nconst [Modal, modalApi] = useVbenModal({\n  async onConfirm() {\n    const { valid } = await formApi.validate();\n    if (!valid) {\n      return;\n    }\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n      #if ( $subTables && $subTables.size() > 0 )\n        // 校验子表单\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #if ($subTable.subJoinMany) ## 一对多\n            ## TODO 列表值校验？\n          #else\n            const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();\n            if (!${subClassNameVar}Valid) {\n              subTabsName.value = '${subClassNameVar}';\n              return;\n            }\n          #end\n        #end\n      #end\n#end\n    modalApi.lock();\n    // 提交表单\n    const data = (await formApi.getValues()) as ${apiName}.${simpleClassName};\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n      #if ( $subTables && $subTables.size() > 0 )\n        // 拼接子表的数据\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #if ($subTable.subJoinMany)\n            data.${subClassNameVar}s = ${subClassNameVar}FormRef.value?.getData();\n          #else\n            data.${subClassNameVar} = await ${subClassNameVar}FormRef.value?.getValues();\n          #end\n        #end\n      #end\n#end\n    try {\n      await (formData.value?.id ? update${simpleClassName}(data) : create${simpleClassName}(data));\n      // 关闭并提示\n      await modalApi.close();\n      emit('success');\n      message.success($t('ui.actionMessage.operationSuccess'));\n    } finally {\n      modalApi.unlock();\n    }\n  },\n  async onOpenChange(isOpen: boolean) {\n    if (!isOpen) {\n      formData.value = undefined;\n      return;\n    }\n    // 加载数据\n    const data = modalApi.getData<${apiName}.${simpleClassName}>();\n    if (!data || !data.id) {\n#if (${table.templateType} == 2)## 树表特有\n      // 设置上级\n      await formApi.setValues(data);\n#end\n      return;\n    }\n    modalApi.lock();\n    try {\n      formData.value = await get${simpleClassName}(data.id);\n      // 设置到 values\n      await formApi.setValues(formData.value);\n    } finally {\n      modalApi.unlock();\n    }\n  },\n});\n</script>\n\n<template>\n  <Modal :title=\"getTitle\">\n    <Form class=\"mx-4\" />\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n      <!-- 子表的表单 -->\n      <Tabs v-model:active-key=\"subTabsName\">\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n          #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n          <Tabs.TabPane key=\"$subClassNameVar\" tab=\"${subTable.classComment}\" force-render>\n            <${subSimpleClassName}Form ref=\"${subClassNameVar}FormRef\" :${subJoinColumn_strikeCase}=\"formData?.id\" />\n          </Tabs.TabPane>\n        #end\n      </Tabs>\n#end\n  </Modal>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm",
    "content": "#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\nimport type { VxeTableGridOptions } from '#/adapter/vxe-table';\nimport type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { ref } from 'vue';\n\nimport {#if ($table.templateType != 2 && $deleteBatchEnable) confirm,#end Page, useVbenModal } from '@vben/common-ui';\nimport { downloadFileFromBlobPart#if ($table.templateType != 2 && $deleteBatchEnable), isEmpty#end } from '@vben/utils';\n\nimport { message#if ($table.templateType == 11), Tabs#end } from 'ant-design-vue';\n\nimport { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';\n#if (${table.templateType} == 2)## 树表接口\nimport {\n  delete${simpleClassName},\n  export${simpleClassName},\n  get${simpleClassName}List,\n} from '#/api/${table.moduleName}/${table.businessName}';\n#else## 标准表接口\nimport {\n  delete${simpleClassName},#if ($deleteBatchEnable)\n\n  delete${simpleClassName}List,#end\n  export${simpleClassName},\n  get${simpleClassName}Page,\n} from '#/api/${table.moduleName}/${table.businessName}';\n#end\nimport { $t } from '#/locales';\n\nimport { useGridColumns, useGridFormSchema } from './data';\nimport Form from './modules/form.vue';\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 11 || $table.templateType == 12 )\n    #foreach ($subSimpleClassName in $subSimpleClassNames)\n    #set ($index = $foreach.count - 1)\n    #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\nimport ${subSimpleClassName}List from './modules/${subSimpleClassName_strikeCase}-list.vue';\n    #end\n#end\n\n#if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况\n/** 子表的列表 */\nconst subTabsName = ref('$subClassNameVars.get(0)')\n#if ($table.templateType == 11)\nconst select${simpleClassName} = ref<${apiName}.${simpleClassName}>();\n#end\n#end\nconst [FormModal, formModalApi] = useVbenModal({\n  connectedComponent: Form,\n  destroyOnClose: true,\n});\n#if (${table.templateType} == 2)## 树表特有：控制表格展开收缩\n\n/** 切换树形展开/收缩状态 */\nconst isExpanded = ref(true);\nfunction handleExpand() {\n  isExpanded.value = !isExpanded.value;\n  gridApi.grid.setAllTreeExpand(isExpanded.value);\n}\n#end\n\n/** 刷新表格 */\nfunction handleRefresh() {\n#if ($table.templateType == 12) ## 内嵌情况\n  gridApi.reload();\n#else\n  gridApi.query();\n#end\n}\n\n/** 创建${table.classComment} */\nfunction handleCreate() {\n  formModalApi.setData(null).open();\n}\n#if (${table.templateType} == 2)## 树表特有：新增下级\n\n/** 添加下级${table.classComment} */\nfunction handleAppend(row: ${apiName}.${simpleClassName}) {\n  formModalApi.setData({ ${treeParentColumn.javaField}: row.id }).open();\n}\n#end\n\n/** 编辑${table.classComment} */\nfunction handleEdit(row: ${apiName}.${simpleClassName}) {\n  formModalApi.setData(row).open();\n}\n\n/** 删除${table.classComment} */\nasync function handleDelete(row: ${apiName}.${simpleClassName}) {\n  const hideLoading = message.loading({\n    content: $t('ui.actionMessage.deleting', [row.id]),\n    duration: 0,\n  });\n  try {\n    await delete${simpleClassName}(row.id!);\n    message.success($t('ui.actionMessage.deleteSuccess', [row.id]));\n    handleRefresh();\n  } finally {\n    hideLoading();\n  }\n}\n\n#if ($table.templateType != 2 && $deleteBatchEnable)\n/** 批量删除${table.classComment} */\nasync function handleDeleteBatch() {\n  await confirm($t('ui.actionMessage.deleteBatchConfirm'));\n  const hideLoading = message.loading({\n    content: $t('ui.actionMessage.deletingBatch'),\n    duration: 0,\n  });\n  try {\n    await delete${simpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    message.success($t('ui.actionMessage.deleteSuccess'));\n    handleRefresh();\n  } finally {\n    hideLoading();\n  }\n}\n\nconst checkedIds = ref<number[]>([]);\nfunction handleRowCheckboxChange({\n  records,\n}: {\n  records: ${apiName}.${simpleClassName}[];\n}) {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n\n/** 导出表格 */\nasync function handleExport() {\n  const data = await export${simpleClassName}(await gridApi.formApi.getValues());\n  downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });\n}\n\nconst [Grid, gridApi] = useVbenVxeGrid({\n  formOptions: {\n    schema: useGridFormSchema(),\n  },\n  gridOptions: {\n    columns: useGridColumns(),\n#if (${table.templateType} == 11)\n    height: '600px',\n#else\n    height: 'auto',\n#end\n#if (${table.templateType} == 2)## 树表设置\n    pagerConfig: {\n      enabled: false,\n    },\n#else## 标准表设置\n    keepSource: true,\n#end\n    proxyConfig: {\n      ajax: {\n#if (${table.templateType} == 2)## 树表数据加载\n        query: async (_, formValues) => {\n          return await get${simpleClassName}List(formValues);\n        },\n#else## 标准表数据加载\n        query: async ({ page }, formValues) => {\n          return await get${simpleClassName}Page({\n            pageNo: page.currentPage,\n            pageSize: page.pageSize,\n            ...formValues,\n          });\n        },\n#end\n      },\n    },\n    rowConfig: {\n      keyField: 'id',\n      isHover: true,\n#if (${table.templateType} == 11)\n      isCurrent: true,\n#end\n    },\n    toolbarConfig: {\n      refresh: true,\n      search: true,\n    },\n#if (${table.templateType} == 2)## 树表设置\n    treeConfig: {\n      parentField: '${treeParentColumn.javaField}',\n      rowField: 'id',\n      transform: true,\n      expandAll: true,\n      reserve: true,\n    },\n#end\n  } as VxeTableGridOptions<${apiName}.${simpleClassName}>,\n#if (${table.templateType} == 11 || (${table.templateType} != 2 && $deleteBatchEnable))\n  gridEvents: {\n  #if(${table.templateType} == 11)\n    cellClick: ({ row }: { row: ${apiName}.${simpleClassName}}) => {\n      select${simpleClassName}.value = row;\n    },\n  #end\n  #if (${table.templateType} != 2 && $deleteBatchEnable)\n    checkboxAll: handleRowCheckboxChange,\n    checkboxChange: handleRowCheckboxChange,\n  #end\n  },\n#end\n});\n</script>\n\n<template>\n  <Page auto-content-height>\n    <FormModal @success=\"handleRefresh\" />\n#if ($table.templateType == 11) ## erp情况\n  <div>\n#end\n    <Grid table-title=\"${table.classComment}列表\">\n        #if ($table.templateType == 12) ## 内嵌情况\n          <template #expand_content=\"{ row }\">\n            <!-- 子表的表单 -->\n            <Tabs v-model:active-key=\"subTabsName\" class=\"mx-8\">\n                #foreach ($subTable in $subTables)\n                    #set ($index = $foreach.count - 1)\n                    #set ($subClassNameVar = $subClassNameVars.get($index))\n                    #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n                    #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n                  <Tabs.TabPane key=\"$subClassNameVar\" tab=\"${subTable.classComment}\" force-render>\n                    <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"row?.id\" />\n                  </Tabs.TabPane>\n                #end\n            </Tabs>\n          </template>\n        #end\n      <template #toolbar-tools>\n        <TableAction\n          :actions=\"[\n            {\n              label: $t('ui.actionTitle.create', ['${table.classComment}']),\n              type: 'primary',\n              icon: ACTION_ICON.ADD,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:create'],\n              onClick: handleCreate,\n            },\n            #if (${table.templateType} == 2)## 树表特有：展开/收缩按钮\n            {\n              label: isExpanded ? '收缩' : '展开',\n              type: 'primary',\n              onClick: handleExpand,\n            },\n            #end\n            {\n              label: $t('ui.actionTitle.export'),\n              type: 'primary',\n              icon: ACTION_ICON.DOWNLOAD,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:export'],\n              onClick: handleExport,\n            },\n            #if ($table.templateType != 2 && $deleteBatchEnable)\n            {\n              label: $t('ui.actionTitle.deleteBatch'),\n              type: 'primary',\n              danger: true,\n              icon: ACTION_ICON.DELETE,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:delete'],\n              disabled: isEmpty(checkedIds),\n              onClick: handleDeleteBatch,\n            },\n            #end\n          ]\"\n        />\n      </template>\n      <template #actions=\"{ row }\">\n        <TableAction\n          :actions=\"[\n            #if (${table.templateType} == 2)## 树表特有：新增下级\n            {\n              label: '新增下级',\n              type: 'link',\n              icon: ACTION_ICON.ADD,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:create'],\n              onClick: handleAppend.bind(null, row),\n            },\n            #end\n            {\n              label: $t('common.edit'),\n              type: 'link',\n              icon: ACTION_ICON.EDIT,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:update'],\n              onClick: handleEdit.bind(null, row),\n            },\n            {\n              label: $t('common.delete'),\n              type: 'link',\n              danger: true,\n              icon: ACTION_ICON.DELETE,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:delete'],\n              popConfirm: {\n                title: $t('ui.actionMessage.deleteConfirm', [row.id]),\n                confirm: handleDelete.bind(null, row),\n              },\n            },\n          ]\"\n        />\n      </template>\n    </Grid>\n#if ($table.templateType == 11) ## erp情况\n    <!-- 子表的表单 -->\n    <Tabs v-model:active-key=\"subTabsName\" class=\"mt-2\">\n        #foreach ($subTable in $subTables)\n            #set ($index = $foreach.count - 1)\n            #set ($subClassNameVar = $subClassNameVars.get($index))\n            #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n            #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n          <Tabs.TabPane key=\"$subClassNameVar\" tab=\"${subTable.classComment}\" force-render>\n            <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"select${simpleClassName}?.id\" />\n          </Tabs.TabPane>\n        #end\n    </Tabs>\n    </div>\n#end\n  </Page>\n</template>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\n  import type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\n  import { useVbenModal } from '@vben/common-ui';\n  import { message } from 'ant-design-vue';\n\n  import { computed, ref } from 'vue';\n  import { $t } from '#/locales';\n  import { useVbenForm } from '#/adapter/form';\n  import { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';\n\n  import { use${subSimpleClassName}FormSchema } from '../data';\n\n  const emit = defineEmits(['success']);\n  const formData = ref<${apiName}.${subSimpleClassName}>();\n  const getTitle = computed(() => {\n    return formData.value?.id\n        ? $t('ui.actionTitle.edit', ['${subTable.classComment}'])\n        : $t('ui.actionTitle.create', ['${subTable.classComment}']);\n  });\n\n  const [Form, formApi] = useVbenForm({\n    commonConfig: {\n      componentProps: {\n        class: 'w-full',\n      },\n      formItemClass: 'col-span-2',\n      labelWidth: 80,\n    },\n    layout: 'horizontal',\n    schema: use${subSimpleClassName}FormSchema(),\n    showDefaultActions: false\n  });\n\n  const [Modal, modalApi] = useVbenModal({\n    async onConfirm() {\n      const { valid } = await formApi.validate();\n      if (!valid) {\n        return;\n      }\n      modalApi.lock();\n      // 提交表单\n      const data = (await formApi.getValues()) as ${apiName}.${subSimpleClassName};\n      data.${subJoinColumn.javaField} = formData.value?.${subJoinColumn.javaField};\n      try {\n        await (formData.value?.id ? update${subSimpleClassName}(data) : create${subSimpleClassName}(data));\n        // 关闭并提示\n        await modalApi.close();\n        emit('success');\n        message.success( $t('ui.actionMessage.operationSuccess') );\n      } finally {\n        modalApi.unlock();\n      }\n    },\n    async onOpenChange(isOpen: boolean) {\n      if (!isOpen) {\n        formData.value = undefined;\n        return;\n      }\n      // 加载数据\n      let data = modalApi.getData<${apiName}.${subSimpleClassName}>();\n      if (!data) {\n        return;\n      }\n      if (data.id) {\n        modalApi.lock();\n        try {\n          data = await get${subSimpleClassName}(data.id);\n        } finally {\n          modalApi.unlock();\n        }\n      }\n      // 设置到 values\n      formData.value = data;\n      await formApi.setValues(formData.value);\n    },\n  });\n</script>\n\n<template>\n  <Modal :title=\"getTitle\">\n    <Form class=\"mx-4\" />\n  </Modal>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm",
    "content": "## 主表的 normal 和 inner 使用相同的 form 表单\n#parse(\"codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subClassNameVar = $subClassNameVars.get($subIndex))\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\n  import type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\n  import { computed, ref, h, onMounted, watch, nextTick } from 'vue';\n\n  import { $t } from '#/locales';\n\n#if ($subTable.subJoinMany) ## 一对多\nimport { Plus } from \"@vben/icons\";\nimport { Button, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue';\nimport { ImageUpload, FileUpload } from \"#/components/upload\";\nimport { useVbenVxeGrid } from '#/adapter/vxe-table';\nimport { use${subSimpleClassName}GridEditColumns } from '../data';\nimport { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#else\nimport { useVbenForm } from '#/adapter/form';\nimport { use${subSimpleClassName}FormSchema } from '../data';\nimport { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#end\n\nconst props = defineProps<{\n   ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\n\n#if ($subTable.subJoinMany) ## 一对多\nconst [Grid, gridApi] = useVbenVxeGrid({\ngridOptions: {\n  columns: use${subSimpleClassName}GridEditColumns(),\n  border: true,\n  showOverflow: true,\n  autoResize: true,\n  keepSource: true,\n  rowConfig: {\n    keyField: 'id',\n  },\n  pagerConfig: {\n    enabled: false,\n  },\n  toolbarConfig: {\n    enabled: false,\n  },\n},\n});\n\n/** 添加${subTable.classComment} */\nconst handleAdd = async () => {\n  await gridApi.grid.insertAt({} as ${apiName}.${subSimpleClassName}, -1);\n}\n\n/** 删除${subTable.classComment} */\nconst handleDelete =  async (row: ${apiName}.${subSimpleClassName}) => {\n  await gridApi.grid.remove(row);\n}\n\n/** 提供获取表格数据的方法供父组件调用 */\ndefineExpose({\n  getData: (): ${apiName}.${subSimpleClassName}[] => {\n    const data = gridApi.grid.getData() as ${apiName}.${subSimpleClassName}[];\n    const removeRecords = gridApi.grid.getRemoveRecords() as ${apiName}.${subSimpleClassName}[];\n    const insertRecords = gridApi.grid.getInsertRecords() as ${apiName}.${subSimpleClassName}[];\n    return data\n        .filter((row) => !removeRecords.some((removed) => removed.id === row.id))\n        .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));\n  },\n});\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n    () => props.${subJoinColumn.javaField},\n    async (val) => {\n      if (!val) {\n        return;\n      }\n      await nextTick();\n      await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));\n    },\n    { immediate: true },\n);\n#else\nconst [Form, formApi] = useVbenForm({\n  commonConfig: {\n    componentProps: {\n      class: 'w-full',\n    },\n    formItemClass: 'col-span-2',\n    labelWidth: 80,\n  },\n  layout: 'horizontal',\n  schema: use${subSimpleClassName}FormSchema(),\n  showDefaultActions: false\n});\n\n/** 暴露出表单校验方法和表单值获取方法 */\ndefineExpose({\n  validate: async () => {\n    const { valid } = await formApi.validate();\n    return valid;\n  },\n  getValues: formApi.getValues,\n});\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n    () => props.${subJoinColumn.javaField},\n    async (val) => {\n      if (!val) {\n        return;\n      }\n      await nextTick();\n      await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));\n    },\n    { immediate: true },\n);\n#end\n</script>\n\n<template>\n#if ($subTable.subJoinMany) ## 一对多\n  <Grid class=\"mx-4\">\n      #foreach($column in $subColumns)\n          #if ($column.createOperation || $column.updateOperation)\n              #set ($javaField = $column.javaField)\n              #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n              #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n                <template #${javaField}=\"{ row }\">\n                  <Input v-model:value=\"row.${javaField}\" />\n                </template>\n              #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                <template #${javaField}=\"{ row }\">\n                  <ImageUpload v-model:value=\"row.${javaField}\" />\n                </template>\n              #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                <template #${javaField}=\"{ row }\">\n                  <FileUpload v-model:value=\"row.${javaField}\" />\n                </template>\n              #elseif($column.htmlType == \"select\")## 下拉框\n                <template #${javaField}=\"{ row, column }\">\n                  <Select v-model:value=\"row.${javaField}\" class=\"w-full\">\n                    <Select.Option v-for=\"option in column.params.options\" :key=\"option.value\" :value=\"option.value\">\n                      {{ option.label }}\n                    </Select.Option>\n                  </Select>\n                </template>\n              #elseif($column.htmlType == \"checkbox\")## 多选框\n                <template #${javaField}=\"{ row, column }\">\n                  <CheckboxGroup v-model:value=\"row.${javaField}\" :options=\"column.params.options\" />\n                </template>\n              #elseif($column.htmlType == \"radio\")## 单选框\n                <template #${javaField}=\"{ row, column }\">\n                  <RadioGroup v-model:value=\"row.${javaField}\" :options=\"column.params.options\" />\n                </template>\n              #elseif($column.htmlType == \"datetime\")## 时间框\n                <template #${javaField}=\"{ row }\">\n                  <DatePicker\n                      v-model:value=\"row.${javaField}\"\n                      :showTime=\"true\"\n                      format=\"YYYY-MM-DD HH:mm:ss\"\n                      valueFormat='x'\n                  />\n                </template>\n              #elseif($column.htmlType == \"textarea\" || $column.htmlType == \"editor\")## 文本框\n                <template #${javaField}=\"{ row }\">\n                  <Textarea v-model:value=\"row.${javaField}\" />\n                </template>\n              #end\n          #end\n      #end\n    <template #actions=\"{ row }\">\n      <Button\n          size=\"small\"\n          type=\"link\"\n          danger\n          @click=\"handleDelete(row)\"\n          v-access:code=\"['${subTable.moduleName}:${simpleClassName_strikeCase}:delete']\"\n      >\n        {{ $t('ui.actionTitle.delete') }}\n      </Button>\n    </template>\n  </Grid>\n  <div class=\"flex justify-center -mt-4\">\n    <Button :icon=\"h(Plus)\" type=\"primary\" ghost @click=\"handleAdd\" v-access:code=\"['${subTable.moduleName}:${simpleClassName_strikeCase}:create']\">\n      {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}\n    </Button>\n  </div>\n#else\n  <Form class=\"mx-4\" />\n#end\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($subIndex))\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\nimport type { VxeTableGridOptions } from '#/adapter/vxe-table';\nimport type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\n#if ($table.templateType == 11) ## erp\nimport ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'\n#end\nimport { confirm, useVbenModal } from '@vben/common-ui';\nimport { message } from 'ant-design-vue';\nimport { ref, computed, nextTick,watch } from 'vue';\nimport { $t } from '#/locales';\nimport { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';\n\n#if ($table.templateType == 11) ## erp\nimport { delete${subSimpleClassName},#if ($deleteBatchEnable) delete${subSimpleClassName}List,#end get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${table.businessName}';\nimport { use${subSimpleClassName}GridFormSchema, use${subSimpleClassName}GridColumns } from '../data';\nimport { isEmpty } from '@vben/utils';\n#else\n#if ($subTable.subJoinMany) ## 一对多\nimport { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#else\nimport { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#end\nimport { use${subSimpleClassName}GridColumns } from '../data';\n#end\n\nconst props = defineProps<{\n  ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\n\n#if ($table.templateType == 11) ## erp\nconst [FormModal, formModalApi] = useVbenModal({\n  connectedComponent: ${subSimpleClassName}Form,\n  destroyOnClose: true,\n});\n\n/** 创建${subTable.classComment} */\nfunction handleCreate() {\n  if (!props.${subJoinColumn.javaField}){\n    message.warning(\"请先选择一个${table.classComment}!\")\n    return\n  }\n  formModalApi.setData({${subJoinColumn.javaField}: props.${subJoinColumn.javaField}}).open();\n}\n\n/** 编辑${subTable.classComment} */\nfunction handleEdit(row: ${apiName}.${subSimpleClassName}) {\n  formModalApi.setData(row).open();\n}\n\n/** 删除${subTable.classComment} */\nasync function handleDelete(row: ${apiName}.${subSimpleClassName}) {\n  const hideLoading = message.loading({\n    content: $t('ui.actionMessage.deleting', [row.id]),\n    duration: 0,\n  });\n  try {\n    await delete${subSimpleClassName}(row.id!);\n    message.success($t('ui.actionMessage.deleteSuccess', [row.id]));\n    handleRefresh();\n  } finally {\n    hideLoading();\n  }\n}\n\n#if ($deleteBatchEnable)\n/** 批量删除${subTable.classComment} */\nasync function handleDeleteBatch() {\n  await confirm($t('ui.actionMessage.deleteBatchConfirm'));\n  const hideLoading = message.loading({\n    content: $t('ui.actionMessage.deletingBatch'),\n    duration: 0,\n  });\n  try {\n    await delete${subSimpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    message.success($t('ui.actionMessage.deleteSuccess'));\n    handleRefresh();\n  } finally {\n    hideLoading();\n  }\n}\n\nconst checkedIds = ref<number[]>([])\nfunction handleRowCheckboxChange({\n  records,\n}: {\n  records: ${apiName}.${subSimpleClassName}[];\n}) {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n\n#end\nconst [Grid, gridApi] = useVbenVxeGrid({\n#if ($table.templateType == 11)\n  formOptions: {\n    schema: use${subSimpleClassName}GridFormSchema(),\n  },\n#end\n  gridOptions: {\n#if ($table.templateType == 11)\n  columns: use${subSimpleClassName}GridColumns(),\n    proxyConfig: {\n      ajax: {\n        query: async ({ page }, formValues) => {\n          if (!props.${subJoinColumn.javaField}){\n              return []\n          }\n          return await get${subSimpleClassName}Page({\n            pageNo: page.currentPage,\n            pageSize: page.pageSize,\n            ${subJoinColumn.javaField}: props.${subJoinColumn.javaField},\n            ...formValues,\n          });\n        },\n      },\n    },\n  pagerConfig: {\n    enabled: true,\n  },\n  toolbarConfig: {\n    refresh: true,\n    search: true,\n  },\n#else\n  columns: use${subSimpleClassName}GridColumns(),\n  pagerConfig: {\n    enabled: false,\n  },\n  toolbarConfig: {\n    enabled: false,\n  },\n#end\n  height: '600px',\n  rowConfig: {\n    keyField: 'id',\n    isHover: true,\n  },\n  } as VxeTableGridOptions<${apiName}.${subSimpleClassName}>,\n  #if (${table.templateType} == 11 && $deleteBatchEnable)\n  gridEvents:{\n    checkboxAll: handleRowCheckboxChange,\n    checkboxChange: handleRowCheckboxChange,\n  }\n  #end\n});\n\n/** 刷新表格 */\nasync function handleRefresh() {\n#if ($table.templateType == 11) ## erp\n  await gridApi.query();\n#else\n  #if ($subTable.subJoinMany) ## 一对多\n  await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));\n  #else\n  await gridApi.grid.loadData([await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!)]);\n  #end\n#end\n}\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.${subJoinColumn.javaField},\n  async (val) => {\n    if (!val) {\n      return;\n    }\n    await nextTick();\n    await handleRefresh()\n  },\n  { immediate: true },\n);\n</script>\n\n<template>\n    #if ($table.templateType == 11) ## erp\n      <FormModal @success=\"handleRefresh\" />\n      <Grid table-title=\"${subTable.classComment}列表\">\n        <template #toolbar-tools>\n          <TableAction\n            :actions=\"[\n              {\n                label: $t('ui.actionTitle.create', ['${table.classComment}']),\n                type: 'primary',\n                icon: ACTION_ICON.ADD,\n                auth: ['${table.moduleName}:${simpleClassName_strikeCase}:create'],\n                onClick: handleCreate,\n              },\n              #if ($table.templateType == 11 && $deleteBatchEnable)\n              {\n                label: $t('ui.actionTitle.deleteBatch'),\n                type: 'primary',\n                danger: true,\n                icon: ACTION_ICON.DELETE,\n                disabled: isEmpty(checkedIds),\n                auth: ['${table.moduleName}:${simpleClassName_strikeCase}:delete'],\n                onClick: handleDeleteBatch,\n              },\n              #end\n            ]\"\n          />\n        </template>\n        <template #actions=\"{ row }\">\n          <TableAction\n            :actions=\"[\n              {\n                label: $t('common.edit'),\n                type: 'link',\n                icon: ACTION_ICON.EDIT,\n                auth: ['${table.moduleName}:${simpleClassName_strikeCase}:update'],\n                onClick: handleEdit.bind(null, row),\n              },\n              {\n                label: $t('common.delete'),\n                type: 'link',\n                danger: true,\n                icon: ACTION_ICON.DELETE,\n                auth: ['${table.moduleName}:${simpleClassName_strikeCase}:delete'],\n                popConfirm: {\n                  title: $t('ui.actionMessage.deleteConfirm', [row.id]),\n                  confirm: handleDelete.bind(null, row),\n                },\n              },\n            ]\"\n          />\n        </template>\n      </Grid>\n    #else\n      <Grid table-title=\"${subTable.classComment}列表\" />\n    #end\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm",
    "content": "## 子表的 erp 和 inner 使用相似的 list 列表，差异主要两点：\n## 1）inner 使用 list 不分页，erp 使用 page 分页\n## 2）erp 支持单个子表的新增、修改、删除，inner 不支持\n#parse(\"codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/general/api/api.ts.vm",
    "content": "import type { PageParam, PageResult } from '@vben/request';\nimport type { Dayjs } from 'dayjs';\n\nimport { requestClient } from '#/api/request';\n#set ($baseURL = \"/${table.moduleName}/${simpleClassName_strikeCase}\")\n\nexport namespace ${simpleClassName}Api {\n  ## 特殊：主子表专属逻辑\n  #foreach ($subTable in $subTables)\n    #set ($index = $foreach.count - 1)\n    #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n    #set ($subColumns = $subColumnsList.get($index))##当前字段数组\n    /** ${subTable.classComment}信息 */\n    export interface ${subSimpleClassName} {\n      #foreach ($column in $subColumns)\n        #if ($column.createOperation || $column.updateOperation)\n          #if(${column.javaType.toLowerCase()} == \"long\" || ${column.javaType.toLowerCase()} == \"integer\" || ${column.javaType.toLowerCase()} == \"short\" || ${column.javaType.toLowerCase()} == \"double\" || ${column.javaType.toLowerCase()} == \"bigdecimal\")\n              ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}\n          #elseif(${column.javaType.toLowerCase()} == \"date\" || ${column.javaType.toLowerCase()} == \"localdate\" || ${column.javaType.toLowerCase()} == \"localdatetime\")\n              ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}\n          #else\n              ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}\n          #end\n        #end\n      #end\n    }\n\n  #end\n  /** ${table.classComment}信息 */\n  export interface ${simpleClassName} {\n    #foreach ($column in $columns)\n      #if ($column.createOperation || $column.updateOperation)\n        #if(${column.javaType.toLowerCase()} == \"long\" || ${column.javaType.toLowerCase()} == \"integer\" || ${column.javaType.toLowerCase()} == \"short\" || ${column.javaType.toLowerCase()} == \"double\" || ${column.javaType.toLowerCase()} == \"bigdecimal\")\n            ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}\n        #elseif(${column.javaType.toLowerCase()} == \"date\" || ${column.javaType.toLowerCase()} == \"localdate\" || ${column.javaType.toLowerCase()} == \"localdatetime\")\n            ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}\n        #else\n            ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}\n        #end\n      #end\n    #end\n    #if ( $table.templateType == 2 )\n      children?: ${simpleClassName}[];\n    #end\n    ## 特殊：主子表专属逻辑\n    #if ( $table.templateType == 10 || $table.templateType == 12 )\n      #foreach ($subTable in $subTables)\n        #set ($index = $foreach.count - 1)\n        #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n        #if ( $subTable.subJoinMany )\n            ${subSimpleClassName.toLowerCase()}s?: ${subSimpleClassName}[]\n        #else\n            ${subSimpleClassName.toLowerCase()}?: ${subSimpleClassName}\n        #end\n      #end\n    #end\n  }\n}\n\n#if ( $table.templateType != 2 )\n/** 查询${table.classComment}分页 */\nexport function get${simpleClassName}Page(params: PageParam) {\n  return requestClient.get<PageResult<${simpleClassName}Api.${simpleClassName}>>('${baseURL}/page', { params });\n}\n#else\n/** 查询${table.classComment}列表 */\nexport function get${simpleClassName}List(params: any) {\n  return requestClient.get<${simpleClassName}Api.${simpleClassName}[]>('${baseURL}/list', { params });\n}\n#end\n\n/** 查询${table.classComment}详情 */\nexport function get${simpleClassName}(id: number) {\n  return requestClient.get<${simpleClassName}Api.${simpleClassName}>(`${baseURL}/get?id=${id}`);\n}\n\n/** 新增${table.classComment} */\nexport function create${simpleClassName}(data: ${simpleClassName}Api.${simpleClassName}) {\n  return requestClient.post('${baseURL}/create', data);\n}\n\n/** 修改${table.classComment} */\nexport function update${simpleClassName}(data: ${simpleClassName}Api.${simpleClassName}) {\n  return requestClient.put('${baseURL}/update', data);\n}\n\n/** 删除${table.classComment} */\nexport function delete${simpleClassName}(id: number) {\n  return requestClient.delete(`${baseURL}/delete?id=${id}`);\n}\n\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n/** 批量删除${table.classComment} */\nexport function delete${simpleClassName}List(ids: number[]) {\n  return requestClient.delete(`${baseURL}/delete-list?ids=${ids.join(',')}`)\n}\n#end\n\n/** 导出${table.classComment} */\nexport function export${simpleClassName}(params: any) {\n  return requestClient.download('${baseURL}/export-excel', { params });\n}\n\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n  #set ($index = $foreach.count - 1)\n  #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n  #set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段\n  #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n  #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n  #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n  #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n  #set ($subClassNameVar = $subClassNameVars.get($index))\n\n// ==================== 子表（$subTable.classComment） ====================\n\n## 情况一：MASTER_ERP 时，需要分查询页子表\n#if ( $table.templateType == 11 )\n/** 获得${subTable.classComment}分页 */\nexport function get${subSimpleClassName}Page(params: PageParam) {\n  return requestClient.get<PageResult<${simpleClassName}Api.${subSimpleClassName}>>(`${baseURL}/${subSimpleClassName_strikeCase}/page`, { params });\n}\n  ## 情况二：非 MASTER_ERP 时，需要列表查询子表\n#else\n#if ( $subTable.subJoinMany )\n/** 获得${subTable.classComment}列表 */\nexport function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}: number) {\n  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}[]>(`${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);\n}\n#else\n/** 获得${subTable.classComment} */\nexport function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}: number) {\n  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);\n}\n#end\n#end\n## 特殊：MASTER_ERP 时，支持单个的新增、修改、删除操作\n#if ( $table.templateType == 11 )\n/** 新增${subTable.classComment} */\nexport function create${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {\n  return requestClient.post(`${baseURL}/${subSimpleClassName_strikeCase}/create`, data);\n}\n\n/** 修改${subTable.classComment} */\nexport function update${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {\n  return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data);\n}\n\n/** 删除${subTable.classComment} */\nexport function delete${subSimpleClassName}(id: number) {\n  return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete?id=${id}`);\n}\n\n#if ($deleteBatchEnable)\n/** 批量删除${subTable.classComment} */\nexport function delete${subSimpleClassName}List(ids: number[]) {\n  return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}`)\n}\n#end\n\n/** 获得${subTable.classComment} */\nexport function get${subSimpleClassName}(id: number) {\n  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`);\n}\n#end\n#end\n\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/general/views/form.vue.vm",
    "content": "<script lang=\"ts\" setup>\nimport type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\nimport type { FormRules } from 'element-plus';\n\nimport { useVbenModal } from '@vben/common-ui';\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\nimport { Tinymce as RichTextarea } from '#/components/tinymce';\nimport { ImageUpload, FileUpload } from \"#/components/upload\";\nimport { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker, ElTreeSelect } from 'element-plus';\n#if($table.templateType == 2)## 树表需要导入这些\nimport { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\nimport { handleTree } from '@vben/utils'\n#end\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n  #foreach ($subSimpleClassName in $subSimpleClassNames)\n  #set ($index = $foreach.count - 1)\n  #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n  import ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'\n  #end\n#end\n\nimport { computed, ref, reactive } from 'vue';\nimport { $t } from '#/locales';\nimport { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n\nconst emit = defineEmits(['success']);\n\nconst formRef = ref();\nconst formData = ref<Partial<${simpleClassName}Api.${simpleClassName}>>({\n#foreach ($column in $columns)\n  #if ($column.createOperation || $column.updateOperation)\n    #if ($column.htmlType == \"checkbox\")\n        $column.javaField: [],\n    #else\n        $column.javaField: undefined,\n    #end\n  #end\n#end\n});\nconst rules = reactive<FormRules>({\n  #foreach ($column in $columns)\n    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n      #set($comment=$column.columnComment)\n        $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n    #end\n  #end\n});\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\nconst ${classNameVar}Tree = ref<any[]>([]) // 树形结构\n#end\nconst getTitle = computed(() => {\n  return formData.value?.id\n    ? $t('ui.actionTitle.edit', ['${table.classComment}'])\n    : $t('ui.actionTitle.create', ['${table.classComment}']);\n});\n\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n  #if ( $subTables && $subTables.size() > 0 )\n  /** 子表的表单 */\n  const subTabsName = ref('$subClassNameVars.get(0)')\n    #foreach ($subClassNameVar in $subClassNameVars)\n      #set ($index = $foreach.count - 1)\n      #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n      const ${subClassNameVar}FormRef = ref<InstanceType<typeof ${subSimpleClassName}Form>>()\n    #end\n  #end\n#end\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n    #foreach ($column in $columns)\n      #if ($column.createOperation || $column.updateOperation)\n        #if ($column.htmlType == \"checkbox\")\n            $column.javaField: [],\n        #else\n            $column.javaField: undefined,\n        #end\n      #end\n    #end\n  };\n  formRef.value?.resetFields();\n}\n\n## 特殊：树表专属逻辑\n#if ( $table.templateType == 2 )\n/** 获得${table.classComment}树 */\nconst get${simpleClassName}Tree = async () => {\n  ${classNameVar}Tree.value = []\n  const data = await get${simpleClassName}List({});\n  data.unshift({\n    id: 0,\n    name: '顶级${table.classComment}',\n  });\n    ${classNameVar}Tree.value = handleTree(data);\n}\n#end\n\nconst [Modal, modalApi] = useVbenModal({\n  async onConfirm() {\n    await formRef.value?.validate();\n    ## 特殊：主子表专属逻辑\n    #if ( $table.templateType == 10 || $table.templateType == 12 )\n      #if ( $subTables && $subTables.size() > 0 )\n        // 校验子表单\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #if ($subTable.subJoinMany) ## 一对多\n            ## TODO 列表值校验？\n          #else\n            try {\n              await ${subClassNameVar}FormRef.value?.validate()\n            } catch (e) {\n              subTabsName.value = '${subClassNameVar}'\n              return\n            }\n          #end\n        #end\n      #end\n    #end\n    modalApi.lock();\n    // 提交表单\n    const data = formData.value as ${simpleClassName}Api.${simpleClassName};\n    ## 特殊：主子表专属逻辑\n    #if ( $table.templateType == 10 || $table.templateType == 12 )\n      #if ( $subTables && $subTables.size() > 0 )\n        // 拼接子表的数据\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #if ($subTable.subJoinMany)\n            data.${subClassNameVar}s = ${subClassNameVar}FormRef.value?.getData();\n          #else\n            data.${subClassNameVar} = ${subClassNameVar}FormRef.value?.getValues();\n          #end\n        #end\n      #end\n    #end\n    try {\n      await (formData.value?.id ? update${simpleClassName}(data) : create${simpleClassName}(data));\n      // 关闭并提示\n      await modalApi.close();\n      emit('success');\n      ElMessage.success($t('ui.actionMessage.operationSuccess'));\n    } finally {\n      modalApi.unlock();\n    }\n  },\n  async onOpenChange(isOpen: boolean) {\n    if (!isOpen) {\n      resetForm()\n      return;\n    }\n    // 加载数据\n    let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();\n    if (!data) {\n      return;\n    }\n    if (data.id) {\n      modalApi.lock();\n      try {\n        data = await get${simpleClassName}(data.id);\n      } finally {\n        modalApi.unlock();\n      }\n    }\n    formData.value = data;\n#if ( $table.templateType == 2 )\n    // 加载树数据\n    await get${simpleClassName}Tree()\n#end\n  },\n});\n</script>\n\n<template>\n  <Modal :title=\"getTitle\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"rules\"\n      label-width=\"120px\"\n      label-position=\"right\"\n    >\n      #foreach($column in $columns)\n        #if ($column.createOperation || $column.updateOperation)\n          #set ($dictType = $column.dictType)\n          #set ($javaField = $column.javaField)\n          #set ($javaType = $column.javaType)\n          #set ($comment = $column.columnComment)\n          #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n            #set ($dictMethod = \"number\")\n          #elseif ($javaType == \"String\")\n            #set ($dictMethod = \"string\")\n          #elseif ($javaType == \"Boolean\")\n            #set ($dictMethod = \"boolean\")\n          #end\n          #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-tree-select\n                      v-model=\"formData.${javaField}\"\n                      :data=\"${classNameVar}Tree\"\n                #if ($treeNameColumn.javaField == \"name\")\n                      :props=\"{\n            label: 'name',\n            value: 'id',\n            children: 'children',\n          }\"\n                #else\n                      :props=\"{\n                        label: '$treeNameColumn.javaField',\n                        value: 'id',\n                        children: 'children',\n                        }\"\n                #end\n                      check-strictly\n                      default-expand-all\n                      placeholder=\"请选择${comment}\"\n              />\n            </el-form-item>\n          #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-input v-model=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n            </el-form-item>\n          #elseif($column.htmlType == \"imageUpload\")## 图片上传\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <ImageUpload v-model=\"formData.${javaField}\" />\n            </el-form-item>\n          #elseif($column.htmlType == \"fileUpload\")## 文件上传\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <FileUpload v-model=\"formData.${javaField}\" />\n            </el-form-item>\n          #elseif($column.htmlType == \"editor\")## 文本编辑器\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <RichTextarea v-model=\"formData.${javaField}\" height=\"500px\" />\n            </el-form-item>\n          #elseif($column.htmlType == \"select\")## 下拉框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-select v-model=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <el-option\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :value=\"dict.value\"\n                          :label=\"dict.label\"\n                  />\n                #else##没数据字典\n                  <el-option label=\"请选择字典生成\" value=\"\" />\n                #end\n              </el-select>\n            </el-form-item>\n          #elseif($column.htmlType == \"checkbox\")## 多选框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-checkbox-group v-model=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <el-checkbox\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :label=\"dict.value\"\n                 >\n                    {{ dict.label }}\n                  </el-checkbox>\n                #else##没数据字典\n                  <el-checkbox label=\"请选择字典生成\" />\n                #end\n              </el-checkbox-group>\n            </el-form-item>\n          #elseif($column.htmlType == \"radio\")## 单选框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-radio-group v-model=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <el-radio\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :label=\"dict.value\"\n                  >\n                    {{ dict.label }}\n                  </el-radio>\n                #else##没数据字典\n                  <el-radio :label=\"1\">请选择字典生成</el-radio>\n                #end\n              </el-radio-group>\n            </el-form-item>\n          #elseif($column.htmlType == \"datetime\")## 时间框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-date-picker\n                      v-model=\"formData.${javaField}\"\n                      value-format=\"x\"\n                      placeholder=\"选择${comment}\"\n              />\n            </el-form-item>\n          #elseif($column.htmlType == \"textarea\")## 文本框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-input v-model=\"formData.${javaField}\" type=\"textarea\" placeholder=\"请输入${comment}\" />\n            </el-form-item>\n          #end\n        #end\n      #end\n    </el-form>\n    ## 特殊：主子表专属逻辑\n    #if ( $table.templateType == 10 || $table.templateType == 12 )\n      <!-- 子表的表单 -->\n      <el-tabs v-model=\"subTabsName\">\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n          #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n          <el-tab-pane name=\"$subClassNameVar\" label=\"${subTable.classComment}\">\n            <${subSimpleClassName}Form ref=\"${subClassNameVar}FormRef\" :${subJoinColumn_strikeCase}=\"formData?.id\" />\n          </el-tab-pane>\n        #end\n      </el-tabs>\n    #end\n  </Modal>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/general/views/index.vue.vm",
    "content": "<script lang=\"ts\" setup>\nimport type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n\nimport { ref, h, reactive, onMounted, nextTick } from 'vue';\n\nimport { Page, useVbenModal } from '@vben/common-ui';\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\nimport { useTableToolbar, VbenVxeTableToolbar } from '@vben/plugins/vxe-table';\nimport { cloneDeep, downloadFileFromBlobPart, formatDateTime } from '@vben/utils';\nimport { ElButton, ElMessage, ElLoading, ElTabs, ElTabPane, ElPagination, ElForm, ElFormItem, ElDatePicker, ElSelect, ElOption, ElInput } from 'element-plus';\nimport ${simpleClassName}Form from './modules/form.vue';\nimport { Download, Plus, RefreshCw, Search, Trash2 } from '@vben/icons';\nimport { ContentWrap } from '#/components/content-wrap';\nimport { VxeColumn, VxeTable } from '#/adapter/vxe-table';\n\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 11 || $table.templateType == 12 )\n    #foreach ($subSimpleClassName in $subSimpleClassNames)\n    #set ($index = $foreach.count - 1)\n    #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n    import ${subSimpleClassName}List from './modules/${subSimpleClassName_strikeCase}-list.vue'\n    #end\n#end\n\nimport { $t } from '#/locales';\n#if (${table.templateType} == 2)## 树表接口\nimport { handleTree,isEmpty } from '@vben/utils'\nimport { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n#else## 标准表接口\nimport { isEmpty } from '@vben/utils';\nimport { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n#end\nimport { downloadFileFromBlobPart } from '@vben/utils';\n\n#if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况\n/** 子表的列表 */\nconst subTabsName = ref('$subClassNameVars.get(0)')\n#if ($table.templateType == 11)\nconst select${simpleClassName} = ref<${simpleClassName}Api.${simpleClassName}>();\nasync function onCellClick({ row }: { row: ${simpleClassName}Api.${simpleClassName} }) {\n  select${simpleClassName}.value = row\n}\n#end\n#end\n\nconst loading = ref(true) // 列表的加载中\n#if ( $table.templateType == 2 )\nconst list = ref<any[]>([]) // 树列表的数据\n#else\nconst list = ref<${simpleClassName}Api.${simpleClassName}[]>([]) // 列表的数据\n#end\n\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\nconst total = ref(0) // 列表的总页数\n#end\nconst queryParams = reactive({\n## 特殊：树表专属逻辑（树不需要分页接口）\n#if ( $table.templateType != 2 )\n  pageNo: 1,\n  pageSize: 10,\n#end\n#foreach ($column in $columns)\n    #if ($column.listOperation)\n        #if ($column.listOperationCondition != 'BETWEEN')\n                $column.javaField: undefined,\n        #end\n        #if ($column.htmlType == \"datetime\" || $column.listOperationCondition == \"BETWEEN\")\n                $column.javaField: undefined,\n        #end\n    #end\n#end\n})\nconst queryFormRef = ref() // 搜索的表单\nconst exportLoading = ref(false) // 导出的加载中\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    const params = cloneDeep(queryParams) as any;\n      #foreach ($column in $columns)\n          #if ($column.listOperation)\n              #if ($column.htmlType == \"datetime\" || $column.listOperationCondition == \"BETWEEN\")\n                if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {\n                  params.${column.javaField} = (params.${column.javaField} as string[]).join(',');\n                }\n              #end\n          #end\n      #end\n      ## 特殊：树表专属逻辑（树不需要分页接口）\n      #if ( $table.templateType == 2 )\n        list.value = await get${simpleClassName}List(params);\n      #else\n        const data = await get${simpleClassName}Page(params)\n        list.value = data.list\n        total.value = data.total\n      #end\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nfunction handleQuery() {\n#if ( $table.templateType != 2 )\n  queryParams.pageNo = 1\n#end\n  getList()\n}\n\n/** 重置按钮操作 */\nfunction resetQuery() {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n\nconst [FormModal, formModalApi] = useVbenModal({\n  connectedComponent: ${simpleClassName}Form,\n  destroyOnClose: true,\n});\n\n/** 创建${table.classComment} */\nfunction handleCreate() {\n  formModalApi.setData(null).open();\n}\n\n/** 编辑${table.classComment} */\nfunction handleEdit(row: ${simpleClassName}Api.${simpleClassName}) {\n  formModalApi.setData(row).open();\n}\n\n#if (${table.templateType} == 2)## 树表特有：新增下级\n/** 新增下级${table.classComment} */\nfunction handleAppend(row: ${simpleClassName}Api.${simpleClassName}) {\n  formModalApi.setData({ ${treeParentColumn.javaField}: row.id }).open();\n}\n#end\n\n/** 删除${table.classComment} */\nasync function handleDelete(row: ${simpleClassName}Api.${simpleClassName}) {\n  const loadingInstance = ElLoading.service({\n    text: $t('ui.actionMessage.deleting', [row.id]),\n  });\n  try {\n    await delete${simpleClassName}(row.id as number);\n    ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.id]));\n    await getList();\n  } finally {\n    loadingInstance.close();\n  }\n}\n\n#if ($table.templateType != 2 && $deleteBatchEnable)\n/** 批量删除${table.classComment} */\nasync function handleDeleteBatch() {\n  const loadingInstance = ElLoading.service({\n    text: $t('ui.actionMessage.deleting'),\n  });\n  try {\n    await delete${simpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    ElMessage.success($t('ui.actionMessage.deleteSuccess'));\n    await getList();\n  } finally {\n    loadingInstance.close();\n  }\n}\n\nconst checkedIds = ref<number[]>([])\nfunction handleRowCheckboxChange({\n  records,\n}: {\n  records: ${simpleClassName}Api.${simpleClassName}[];\n}) {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n\n/** 导出表格 */\nasync function handleExport() {\ntry {\n  exportLoading.value = true;\n  const data = await export${simpleClassName}(queryParams);\n  downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });\n}finally {\n  exportLoading.value = false;\n}\n}\n\n#if (${table.templateType} == 2)\n/** 切换树形展开/收缩状态 */\nconst isExpanded = ref(true);\nfunction handleExpand() {\n  isExpanded.value = !isExpanded.value;\n  tableRef.value?.setAllTreeExpand(isExpanded.value);\n}\n#end\n\n/** 初始化 */\nconst { hiddenSearchBar, tableToolbarRef, tableRef } = useTableToolbar();\nonMounted(() => {\n  getList();\n});\n</script>\n\n<template>\n  <Page auto-content-height>\n    <FormModal @success=\"getList\" />\n\n    <ContentWrap v-if=\"!hiddenSearchBar\">\n      <!-- 搜索工作栏 -->\n      <el-form\n          :model=\"queryParams\"\n          ref=\"queryFormRef\"\n          inline\n      >\n          #foreach($column in $columns)\n              #if ($column.listOperation)\n                  #set ($dictType = $column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($javaType = $column.javaType)\n                  #set ($comment = $column.columnComment)\n                  #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                      #set ($dictMethod = \"number\")\n                  #elseif ($javaType == \"String\")\n                      #set ($dictMethod = \"string\")\n                  #elseif ($javaType == \"Boolean\")\n                      #set ($dictMethod = \"boolean\")\n                  #end\n                  #if ($column.htmlType == \"input\")\n                    <el-form-item label=\"${comment}\">\n                      <el-input\n                          v-model=\"queryParams.${javaField}\"\n                          placeholder=\"请输入${comment}\"\n                          clearable\n                          @keyup.enter=\"handleQuery\"\n                           class=\"!w-[240px]\"\n                      />\n                    </el-form-item>\n                  #elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\" || $column.htmlType == \"checkbox\")\n                    <el-form-item label=\"${comment}\">\n                      <el-select\n                          v-model=\"queryParams.${javaField}\"\n                          placeholder=\"请选择${comment}\"\n                          clearable\n                           class=\"!w-[240px]\"\n                      >\n                          #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n                            <el-option\n                                v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                                :key=\"dict.value\"\n                                :value=\"dict.value\"\n                                :label=\"dict.label\"\n                            />\n                          #else## 未设置 dictType 数据字典的情况\n                            <el-option label=\"请选择字典生成\" value=\"\" />\n                          #end\n                      </el-select>\n                    </el-form-item>\n                  #elseif($column.htmlType == \"datetime\")\n                      #if ($column.listOperationCondition != \"BETWEEN\")## 非范围\n                        <el-form-item label=\"${comment}\">\n                          <el-date-picker\n                              v-model=\"queryParams.${javaField}\"\n                              value-format=\"YYYY-MM-DD\"\n                              placeholder=\"选择${comment}\"\n                              clearable\n                               class=\"!w-[240px]\"\n                          />\n                        </el-form-item>\n                      #else## 范围\n                        <el-form-item label=\"${comment}\">\n                          <el-date-picker\n                              v-model=\"queryParams.${javaField}\"\n                              type=\"daterange\"\n                              value-format=\"YYYY-MM-DD\"\n                              range-separator=\"至\"\n                              start-placeholder=\"开始日期\"\n                              end-placeholder=\"结束日期\"\n                              class=\"!w-[240px]\"\n                          />\n                        </el-form-item>\n                      #end\n                  #end\n              #end\n          #end\n        <el-form-item>\n          <el-button class=\"ml-2\" @click=\"resetQuery\"> 重置 </el-button>\n          <el-button class=\"ml-2\" @click=\"handleQuery\" type=\"primary\">\n            搜索\n          </el-button>\n        </el-form-item>\n      </el-form>\n    </ContentWrap>\n\n    <!-- 列表 -->\n    <ContentWrap title=\"${table.classComment}\">\n      <template #extra>\n        <TableToolbar\n            ref=\"tableToolbarRef\"\n            v-model:hidden-search=\"hiddenSearchBar\"\n        >\n        #if (${table.templateType} == 2)\n          <el-button @click=\"handleExpand\" class=\"mr-2\">\n            {{ isExpanded ? '收缩' : '展开' }}\n          </el-button>\n        #end\n          <el-button\n              class=\"ml-2\"\n              :icon=\"h(Plus)\"\n              type=\"primary\"\n              @click=\"handleCreate\"\n              v-access:code=\"['${permissionPrefix}:create']\"\n          >\n            {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}\n          </el-button>\n          <el-button\n              :icon=\"h(Download)\"\n              type=\"primary\"\n              class=\"ml-2\"\n              :loading=\"exportLoading\"\n              @click=\"handleExport\"\n              v-access:code=\"['${permissionPrefix}:export']\"\n          >\n            {{ $t('ui.actionTitle.export') }}\n          </el-button>\n        #if ($table.templateType != 2 && $deleteBatchEnable)\n          <el-button\n              :icon=\"h(Trash2)\"\n              type=\"danger\"\n              class=\"ml-2\"\n              :disabled=\"isEmpty(checkedIds)\"\n              @click=\"handleDeleteBatch\"\n              v-access:code=\"['${table.moduleName}:${simpleClassName_strikeCase}:delete']\"\n          >\n            批量删除\n          </el-button>\n        #end\n        </TableToolbar>\n      </template>\n      <vxe-table\n          ref=\"tableRef\"\n          :data=\"list\"\n        #if ( $table.templateType == 2 )\n          :tree-config=\"{\n          parentField: '${treeParentColumn.javaField}',\n          rowField: 'id',\n          transform: true,\n          expandAll: true,\n          reserve: true,\n        }\"\n        #end\n#if ($table.templateType == 11) ## erp情况\n          @cell-click=\"onCellClick\"\n          :row-config=\"{\n            keyField: 'id',\n            isHover: true,\n            isCurrent: true,\n          }\"\n#end\n          show-overflow\n          :loading=\"loading\"\n#if ($table.templateType != 2 && $deleteBatchEnable)\n          @checkboxAll=\"handleRowCheckboxChange\"\n          @checkboxChange=\"handleRowCheckboxChange\"\n#end\n      >\n#if ($table.templateType != 2 && $deleteBatchEnable)\n        <vxe-column type=\"checkbox\" width=\"40\"></vxe-column>\n#end\n          ## 特殊：主子表专属逻辑\n          #if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )\n            <!-- 子表的列表 -->\n            <vxe-column type=\"expand\" width=\"60\">\n              <template #content=\"{ row }\">\n                <!-- 子表的表单 -->\n                <el-tabs v-model=\"subTabsName\" class=\"mx-8\">\n                    #foreach ($subTable in $subTables)\n                        #set ($index = $foreach.count - 1)\n                        #set ($subClassNameVar = $subClassNameVars.get($index))\n                        #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n                        #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n                      <el-tab-pane name=\"$subClassNameVar\" label=\"${subTable.classComment}\">\n                        <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"row?.id\" />\n                      </el-tab-pane>\n                    #end\n                </el-tabs>\n              </template>\n            </vxe-column>\n          #end\n          #foreach($column in $columns)\n              #if ($column.listOperationResult)\n                  #set ($dictType=$column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})\n                  #set ($comment=$column.columnComment)\n                  #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                    <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                      <template #default=\"{row}\">\n                        {{formatDateTime(row.${javaField})}}\n                      </template>\n                    </vxe-column>\n                  #elseif($column.dictType && \"\" != $column.dictType)## 数据字典\n                    <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                      <template #default=\"{row}\">\n                        <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"row.${javaField}\" />\n                      </template>\n                    </vxe-column>\n                  #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)\n                    <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\"  tree-node/>\n                  #else\n                    <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\" />\n                  #end\n              #end\n          #end\n        <vxe-column field=\"operation\" title=\"操作\" align=\"center\">\n          <template #default=\"{row}\">\n#if ( $table.templateType == 2 )\n  <el-button\n      size=\"small\"\n      type=\"primary\"\n      link\n      @click=\"handleAppend(row as any)\"\n      v-access:code=\"['${permissionPrefix}:create']\"\n  >\n    新增下级\n  </el-button>\n#end\n            <el-button\n                size=\"small\"\n                type=\"primary\"\n                link\n                @click=\"handleEdit(row as any)\"\n                v-access:code=\"['${permissionPrefix}:update']\"\n            >\n              {{ $t('ui.actionTitle.edit') }}\n            </el-button>\n            <el-button\n                size=\"small\"\n                type=\"danger\"\n                link\n                class=\"ml-2\"\n                #if ( $table.templateType == 2 )\n                :disabled=\"!isEmpty(row?.children)\"\n                #end\n                @click=\"handleDelete(row as any)\"\n                v-access:code=\"['${permissionPrefix}:delete']\"\n            >\n              {{ $t('ui.actionTitle.delete') }}\n            </el-button>\n          </template>\n        </vxe-column>\n      </vxe-table>\n#if ( $table.templateType != 2 )\n      <!-- 分页 -->\n      <div class=\"mt-2 flex justify-end\">\n        <el-pagination\n            :total=\"total\"\n            v-model:current-page=\"queryParams.pageNo\"\n            v-model:page-size=\"queryParams.pageSize\"\n            :page-sizes=\"[10, 20, 50, 100]\"\n            layout=\"total, sizes, prev, pager, next, jumper\"\n            @size-change=\"getList\"\n            @current-change=\"getList\"\n        />\n      </div>\n#end\n    </ContentWrap>\n#if ($table.templateType == 11) ## erp情况\n  <ContentWrap>\n    <!-- 子表的表单 -->\n    <el-tabs v-model=\"subTabsName\">\n        #foreach ($subTable in $subTables)\n            #set ($index = $foreach.count - 1)\n            #set ($subClassNameVar = $subClassNameVars.get($index))\n            #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n            #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n          <el-tab-pane name=\"$subClassNameVar\" label=\"${subTable.classComment}\">\n            <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"select${simpleClassName}?.id\" />\n          </el-tab-pane>\n        #end\n    </el-tabs>\n  </ContentWrap>\n#end\n  </Page>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/general/views/modules/form_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n<script lang=\"ts\" setup>\n  import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n  import type { FormRules } from 'element-plus';\n\n  import { computed, ref, reactive } from 'vue';\n\n  import { useVbenModal } from '@vben/common-ui';\n  import { DICT_TYPE } from '@vben/constants';\n  import { getDictOptions } from '@vben/hooks';\n  import { Tinymce as RichTextarea } from '#/components/tinymce';\n  import { ImageUpload, FileUpload } from \"#/components/upload\";\n  import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker, ElTreeSelect } from 'element-plus';\n\n  import { $t } from '#/locales';\n\n  import { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n\n  const emit = defineEmits(['success']);\n  const getTitle = computed(() => {\n    return formData.value?.id\n        ? $t('ui.actionTitle.edit', ['${subTable.classComment}'])\n        : $t('ui.actionTitle.create', ['${subTable.classComment}']);\n  });\n\n  const formRef = ref();\n  const formData = ref<Partial<${simpleClassName}Api.${subSimpleClassName}>>({\n    #foreach ($column in $subColumns)\n      #if ($column.createOperation || $column.updateOperation)\n        #if ($column.htmlType == \"checkbox\")\n            $column.javaField: [],\n        #else\n            $column.javaField: undefined,\n        #end\n      #end\n    #end\n  });\n  const rules = reactive<FormRules>({\n    #foreach ($column in $subColumns)\n      #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n        #set($comment=$column.columnComment)\n          $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n      #end\n    #end\n  });\n\n  const [Modal, modalApi] = useVbenModal({\n    async onConfirm() {\n      await formRef.value?.validate();\n\n      modalApi.lock();\n      // 提交表单\n      const data = formData.value as ${simpleClassName}Api.${subSimpleClassName};\n      try {\n        await (formData.value?.id ? update${subSimpleClassName}(data) : create${subSimpleClassName}(data));\n        // 关闭并提示\n        await modalApi.close();\n        emit('success');\n        ElMessage.success($t('ui.actionMessage.operationSuccess'));\n      } finally {\n        modalApi.unlock();\n      }\n    },\n    async onOpenChange(isOpen: boolean) {\n      if (!isOpen) {\n        resetForm()\n        return;\n      }\n\n      // 加载数据\n      let data = modalApi.getData<${simpleClassName}Api.${subSimpleClassName}>();\n      if (!data) {\n        return;\n      }\n      if (data.id) {\n        modalApi.lock();\n        try {\n          data = await get${subSimpleClassName}(data.id);\n        } finally {\n          modalApi.unlock();\n        }\n      }\n      // 设置到 values\n      formData.value = data;\n    },\n  });\n\n  /** 重置表单 */\n  const resetForm = () => {\n    formData.value = {\n      #foreach ($column in $subColumns)\n        #if ($column.createOperation || $column.updateOperation)\n          #if ($column.htmlType == \"checkbox\")\n              $column.javaField: [],\n          #else\n              $column.javaField: undefined,\n          #end\n        #end\n      #end\n    };\n    formRef.value?.resetFields();\n  }\n</script>\n\n<template>\n  <Modal :title=\"getTitle\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"rules\"\n      label-width=\"120px\"\n      label-position=\"right\"\n    >\n      #foreach($column in $subColumns)\n        #if ($column.createOperation || $column.updateOperation)\n          #set ($dictType = $column.dictType)\n          #set ($javaField = $column.javaField)\n          #set ($javaType = $column.javaType)\n          #set ($comment = $column.columnComment)\n          #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n            #set ($dictMethod = \"number\")\n          #elseif ($javaType == \"String\")\n            #set ($dictMethod = \"string\")\n          #elseif ($javaType == \"Boolean\")\n            #set ($dictMethod = \"boolean\")\n          #end\n          #if ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-input v-model=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n            </el-form-item>\n          #elseif($column.htmlType == \"imageUpload\")## 图片上传\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <ImageUpload v-model=\"formData.${javaField}\" />\n            </el-form-item>\n          #elseif($column.htmlType == \"fileUpload\")## 文件上传\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <FileUpload v-model=\"formData.${javaField}\" />\n            </el-form-item>\n          #elseif($column.htmlType == \"editor\")## 文本编辑器\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <RichTextarea v-model=\"formData.${javaField}\" height=\"500px\" />\n            </el-form-item>\n          #elseif($column.htmlType == \"select\")## 下拉框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-select v-model=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <el-option\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :value=\"dict.value\"\n                          :label=\"dict.label\"\n                  />\n                #else##没数据字典\n                  <el-option label=\"请选择字典生成\" value=\"\" />\n                #end\n              </el-select>\n            </el-form-item>\n          #elseif($column.htmlType == \"checkbox\")## 多选框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-checkbox-group v-model=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <el-checkbox\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :label=\"dict.value\"\n                  >\n                    {{ dict.label }}\n                  </el-checkbox>\n                #else##没数据字典\n                  <el-checkbox label=\"请选择字典生成\" />\n                #end\n              </el-checkbox-group>\n            </el-form-item>\n          #elseif($column.htmlType == \"radio\")## 单选框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-radio-group v-model=\"formData.${javaField}\">\n                #if (\"\" != $dictType)## 有数据字典\n                  <el-radio\n                          v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                          :key=\"dict.value\"\n                          :label=\"dict.value\"\n                  >\n                    {{ dict.label }}\n                  </el-radio>\n                #else##没数据字典\n                  <el-radio :label=\"1\">请选择字典生成</el-radio>\n                #end\n              </el-radio-group>\n            </el-form-item>\n          #elseif($column.htmlType == \"datetime\")## 时间框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-date-picker\n                      v-model=\"formData.${javaField}\"\n                      value-format=\"x\"\n                      placeholder=\"选择${comment}\"\n              />\n            </el-form-item>\n          #elseif($column.htmlType == \"textarea\")## 文本框\n            <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n              <el-input v-model=\"formData.${javaField}\" type=\"textarea\" placeholder=\"请输入${comment}\" />\n            </el-form-item>\n          #end\n        #end\n      #end\n    </el-form>\n  </Modal>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/general/views/modules/form_sub_inner.vue.vm",
    "content": "## 主表的 normal 和 inner 使用相同的 form 表单\n#parse(\"codegen/vue3_vben5_ele/general/views/modules/form_sub_normal.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/general/views/modules/form_sub_normal.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subClassNameVar = $subClassNameVars.get($subIndex))\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n<script lang=\"ts\" setup>\nimport type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n\nimport { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue';\n\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\n\nimport { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';\nimport { $t } from '#/locales';\n\n#if ($subTable.subJoinMany) ## 一对多\nimport type { VxeTableInstance } from '#/adapter/vxe-table';\nimport { Plus } from \"@vben/icons\";\nimport { VxeColumn, VxeTable } from '#/adapter/vxe-table';\nimport { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n#else\nimport type { FormRules } from 'element-plus';\nimport { Tinymce as RichTextarea } from '#/components/tinymce';\nimport { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n#end\n\nconst props = defineProps<{\n   ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\n\n#if ($subTable.subJoinMany) ## 一对多\nconst list = ref<${simpleClassName}Api.${subSimpleClassName}[]>([]) // 列表的数据\nconst tableRef = ref<VxeTableInstance>();\n/** 添加${subTable.classComment} */\nasync function handleAdd() {\n  await tableRef.value?.insertAt({} as ${simpleClassName}Api.${subSimpleClassName}, -1);\n}\n\n/** 删除${subTable.classComment} */\nasync function onDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {\n  await tableRef.value?.remove(row);\n}\n\n/** 提供获取表格数据的方法供父组件调用 */\ndefineExpose({\n  getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {\n    const data = list.value as ${simpleClassName}Api.${subSimpleClassName}[];\n    const removeRecords = tableRef.value?.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];\n    const insertRecords = tableRef.value?.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];\n    return [\n      ...data.filter(\n        (row) => !removeRecords.some((removed) => removed.id === row.id),\n      ),\n      ...insertRecords.map((row: any) => ({ ...row, id: undefined })),\n  },\n});\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n    () => props.${subJoinColumn.javaField},\n    async (val) => {\n      if (!val) {\n        return;\n      }\n      list.value = await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!);\n    },\n    { immediate: true },\n);\n#else\nconst formRef = ref();\nconst formData = ref<Partial<${simpleClassName}Api.${subSimpleClassName}>>({\n    #foreach ($column in $subColumns)\n        #if ($column.createOperation || $column.updateOperation)\n            #if ($column.htmlType == \"checkbox\")\n                    $column.javaField: [],\n            #else\n                    $column.javaField: undefined,\n            #end\n        #end\n    #end\n});\nconst rules = reactive<FormRules>({\n    #foreach ($column in $subColumns)\n        #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n            #set($comment=$column.columnComment)\n                $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],\n        #end\n    #end\n});\n/** 暴露出表单校验方法和表单值获取方法 */\ndefineExpose({\n  validate: async () => await formRef.value?.validate(),\n  getValues: ()=> formData.value,\n});\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n    () => props.${subJoinColumn.javaField},\n    async (val) => {\n      if (!val) {\n        return;\n      }\n      await nextTick();\n      formData.value = await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!);\n    },\n    { immediate: true },\n);\n#end\n</script>\n\n<template>\n#if ($subTable.subJoinMany) ## 一对多\n  <vxe-table ref=\"tableRef\" :data=\"list\" show-overflow class=\"mx-4\">\n      #foreach($column in $subColumns)\n          #if ($column.createOperation || $column.updateOperation)\n              #set ($comment = $column.columnComment)\n              #set ($javaField = $column.javaField)\n              #set ($javaType = $column.javaType)\n              #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                  #set ($dictMethod = \"number\")\n              #elseif ($javaType == \"String\")\n                  #set ($dictMethod = \"string\")\n              #elseif ($javaType == \"Boolean\")\n                  #set ($dictMethod = \"boolean\")\n              #end\n              #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n              #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n                <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{row}\">\n                    <el-input v-model=\"row.${javaField}\" />\n                  </template>\n                </vxe-column>\n              #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{row}\">\n                    <ImageUpload v-model=\"row.${javaField}\" />\n                  </template>\n                </vxe-column>\n              #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{row}\">\n                    <FileUpload v-model=\"row.${javaField}\" />\n                  </template>\n                </vxe-column>\n              #elseif($column.htmlType == \"select\")## 下拉框\n                <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{row}\">\n                    <el-select v-model=\"row.${javaField}\" placeholder=\"请选择${comment}\">\n                        #if (\"\" != $dictType)## 有数据字典\n                          <el-option\n                              v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                              :key=\"dict.value\"\n                              :value=\"dict.value\"\n                              :label=\"dict.label\"\n                          />\n                        #else##没数据字典\n                          <el-option label=\"请选择字典生成\" value=\"\" />\n                        #end\n                    </el-select>\n                  </template>\n                </vxe-column>\n              #elseif($column.htmlType == \"checkbox\")## 多选框\n                <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{row}\">\n                    <el-checkbox-group v-model=\"row.${javaField}\">\n                        #if (\"\" != $dictType)## 有数据字典\n                          <el-checkbox\n                              v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                              :key=\"dict.value\"\n                              :label=\"dict.value\"\n                          >\n                            {{ dict.label }}\n                          </el-checkbox>\n                        #else##没数据字典\n                          <el-checkbox label=\"请选择字典生成\" />\n                        #end\n                    </el-checkbox-group>\n                  </template>\n                </vxe-column>\n              #elseif($column.htmlType == \"radio\")## 单选框\n                <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{row}\">\n                    <el-radio-group v-model=\"row.${javaField}\">\n                        #if (\"\" != $dictType)## 有数据字典\n                          <el-radio\n                              v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                              :key=\"dict.value\"\n                              :label=\"dict.value\"\n                          >\n                            {{ dict.label }}\n                          </el-radio>\n                        #else##没数据字典\n                          <el-radio :label=\"1\">请选择字典生成</el-radio>\n                        #end\n                    </el-radio-group>\n                  </template>\n                </vxe-column>\n              #elseif($column.htmlType == \"datetime\")## 时间框\n                <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{row}\">\n                    <el-date-picker\n                        v-model=\"row.${javaField}\"\n                        type=\"datetime\"\n                        value-format=\"x\"\n                        format=\"YYYY-MM-DD HH:mm:ss\"\n                    />\n                  </template>\n                </vxe-column>\n              #elseif($column.htmlType == \"textarea\" || $column.htmlType == \"editor\")## 文本框\n                <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                  <template #default=\"{row}\">\n                    <el-input v-model=\"row.${javaField}\" type=\"textarea\" />\n                  </template>\n                </vxe-column>\n              #end\n          #end\n      #end\n    <vxe-column field=\"operation\" title=\"操作\" align=\"center\">\n      <template #default=\"{row}\">\n        <el-button\n            size=\"small\"\n            type=\"danger\"\n            link\n            @click=\"onDelete(row as any)\"\n            v-access:code=\"['${permissionPrefix}:delete']\"\n        >\n          {{ $t('ui.actionTitle.delete') }}\n        </el-button>\n      </template>\n    </vxe-column>\n  </vxe-table>\n  <div class=\"flex justify-center mt-4\">\n    <el-button :icon=\"h(Plus)\" type=\"primary\" plain @click=\"handleAdd\" v-access:code=\"['${permissionPrefix}:create']\">\n      {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}\n    </el-button>\n  </div>\n#else\n  <el-form\n      ref=\"formRef\"\n      class=\"mx-4\"\n      :model=\"formData\"\n      :rules=\"rules\"\n      label-width=\"120px\"\n      label-position=\"right\"\n  >\n      #foreach($column in $subColumns)\n          #if ($column.createOperation || $column.updateOperation)\n              #set ($dictType = $column.dictType)\n              #set ($javaField = $column.javaField)\n              #set ($javaType = $column.javaType)\n              #set ($comment = $column.columnComment)\n              #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                  #set ($dictMethod = \"number\")\n              #elseif ($javaType == \"String\")\n                  #set ($dictMethod = \"string\")\n              #elseif ($javaType == \"Boolean\")\n                  #set ($dictMethod = \"boolean\")\n              #end\n              #if ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n                <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                  <el-input v-model=\"formData.${javaField}\" placeholder=\"请输入${comment}\" />\n                </el-form-item>\n              #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                  <ImageUpload v-model=\"formData.${javaField}\" />\n                </el-form-item>\n              #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                  <FileUpload v-model=\"formData.${javaField}\" />\n                </el-form-item>\n              #elseif($column.htmlType == \"editor\")## 文本编辑器\n                <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                  <RichTextarea v-model=\"formData.${javaField}\" height=\"500px\" />\n                </el-form-item>\n              #elseif($column.htmlType == \"select\")## 下拉框\n                <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                  <el-select v-model=\"formData.${javaField}\" placeholder=\"请选择${comment}\">\n                      #if (\"\" != $dictType)## 有数据字典\n                        <el-option\n                            v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                            :key=\"dict.value\"\n                            :value=\"dict.value\"\n                            :label=\"dict.label\"\n                        />\n                      #else##没数据字典\n                        <el-option label=\"请选择字典生成\" value=\"\" />\n                      #end\n                  </el-select>\n                </el-form-item>\n              #elseif($column.htmlType == \"checkbox\")## 多选框\n                <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                  <el-checkbox-group v-model=\"formData.${javaField}\">\n                      #if (\"\" != $dictType)## 有数据字典\n                        <el-checkbox\n                            v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                            :key=\"dict.value\"\n                            :label=\"dict.value\"\n                        >\n                          {{ dict.label }}\n                        </el-checkbox>\n                      #else##没数据字典\n                        <el-checkbox label=\"请选择字典生成\" />\n                      #end\n                  </el-checkbox-group>\n                </el-form-item>\n              #elseif($column.htmlType == \"radio\")## 单选框\n                <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                  <el-radio-group v-model=\"formData.${javaField}\">\n                      #if (\"\" != $dictType)## 有数据字典\n                        <el-radio\n                            v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                            :key=\"dict.value\"\n                            :label=\"dict.value\"\n                        >\n                          {{ dict.label }}\n                        </el-radio>\n                      #else##没数据字典\n                        <el-radio :label=\"1\">请选择字典生成</el-radio>\n                      #end\n                  </el-radio-group>\n                </el-form-item>\n              #elseif($column.htmlType == \"datetime\")## 时间框\n                <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                  <el-date-picker\n                      v-model=\"formData.${javaField}\"\n                      value-format=\"x\"\n                      placeholder=\"选择${comment}\"\n                  />\n                </el-form-item>\n              #elseif($column.htmlType == \"textarea\")## 文本框\n                <el-form-item label=\"${comment}\" prop=\"${javaField}\">\n                  <el-input v-model=\"formData.${javaField}\" type=\"textarea\" placeholder=\"请输入${comment}\" />\n                </el-form-item>\n              #end\n          #end\n      #end\n  </el-form>\n#end\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/general/views/modules/list_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($subIndex))\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n<script lang=\"ts\" setup>\n  import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n  import type { VxeTableInstance } from '#/adapter/vxe-table';\n\n  import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';\n\n  import { DICT_TYPE } from '@vben/constants';\n  import { getDictOptions } from '@vben/hooks';\n\n  import { DictTag } from '#/components/dict-tag';\n  import { getRangePickerDefaultProps } from '#/utils';\n  import { VxeColumn, VxeTable } from '#/adapter/vxe-table';\n  import { ContentWrap } from '#/components/content-wrap';\n\n#if ($table.templateType == 11) ## erp\n    import { useVbenModal } from '@vben/common-ui';\n    import { useTableToolbar, VbenVxeTableToolbar } from '@vben/plugins/vxe-table';\n    import ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'\n    import { Tinymce as RichTextarea } from '#/components/tinymce';\n    import { ImageUpload, FileUpload } from \"#/components/upload\";\n    import { ElMessage, ElLoading, ElButton, ElTabs, ElTabPane, ElPagination, ElForm, ElFormItem, ElDatePicker, ElSelect, ElOption, ElInput } from 'element-plus';\n    import { Plus, Trash2 } from '@vben/icons';\n    import { $t } from '#/locales';\n#end\n\n#if ($table.templateType == 11) ## erp\n    import { delete${subSimpleClassName},#if ($deleteBatchEnable) delete${subSimpleClassName}List,#end get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n    import { isEmpty } from '@vben/utils';\n  #else\n  #if ($subTable.subJoinMany) ## 一对多\n  import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n  #else\n  import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';\n  #end\n#end\n\nconst props = defineProps<{\n      ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\n\n#if ($table.templateType == 11) ## erp\n  const [FormModal, formModalApi] = useVbenModal({\n    connectedComponent: ${subSimpleClassName}Form,\n    destroyOnClose: true,\n  });\n\n/** 创建${subTable.classComment} */\nfunction handleCreate() {\n  if (!props.${subJoinColumn.javaField}){\n    ElMessage.warning(\"请先选择一个${table.classComment}!\")\n    return\n  }\n  formModalApi.setData({${subJoinColumn.javaField}: props.${subJoinColumn.javaField}}).open();\n}\n\n/** 编辑${subTable.classComment} */\nfunction handleEdit(row: ${simpleClassName}Api.${subSimpleClassName}) {\n  formModalApi.setData(row).open();\n}\n\n/** 删除${subTable.classComment} */\nasync function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {\n  const loadingInstance = ElLoading.service({\n    text: $t('ui.actionMessage.deleting', [row.id]),\n  });\n  try {\n    await delete${subSimpleClassName}(row.id as number);\n    ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.id]));\n    await getList();\n  } finally {\n    loadingInstance.close();\n  }\n}\n\n#if ($deleteBatchEnable)\n/** 批量删除${subTable.classComment} */\nasync function handleDeleteBatch() {\n  const loadingInstance = ElLoading.service({\n    text: $t('ui.actionMessage.deleting'),\n  });\n  try {\n    await delete${subSimpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    ElMessage.success($t('ui.actionMessage.deleteSuccess'));\n    await getList();\n  } finally {\n    loadingInstance.close();\n  }\n}\n\nconst checkedIds = ref<number[]>([])\nfunction handleRowCheckboxChange({\n  records,\n}: {\n  records: ${simpleClassName}Api.${subSimpleClassName}[];\n}) {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n#end\n\n  const loading = ref(true) // 列表的加载中\n  const list = ref<${simpleClassName}Api.${subSimpleClassName}[]>([]) // 列表的数据\n#if ($table.templateType == 11) ## erp\n  const total = ref(0) // 列表的总页数\n#end\n#if ($table.templateType == 11) ## erp\n  const queryFormRef = ref() // 搜索的表单\n  const queryParams = reactive({\n      pageNo: 1,\n      pageSize: 10,\n      #foreach ($column in $subColumns)\n          #if ($column.listOperation)\n              #if ($column.listOperationCondition != 'BETWEEN')\n                      $column.javaField: undefined,\n              #end\n              #if ($column.htmlType == \"datetime\" || $column.listOperationCondition == \"BETWEEN\")\n                      $column.javaField: undefined,\n              #end\n          #end\n      #end\n  })\n\n/** 搜索按钮操作 */\nfunction handleQuery() {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 重置按钮操作 */\nfunction resetQuery() {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n#end\n/** 查询列表 */\nasync function getList() {\n  loading.value = true\n  try {\n    if (!props.${subJoinColumn.javaField}){\n      return []\n    }\n      ## 特殊：树表专属逻辑（树不需要分页接口）\n      #if ($table.templateType == 11) ## erp\n        const params = cloneDeep(queryParams) as any;\n          #foreach ($column in $columns)\n              #if ($column.listOperation)\n                  #if ($column.htmlType == \"datetime\" || $column.listOperationCondition == \"BETWEEN\")\n                    if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {\n                      params.${column.javaField} = (params.${column.javaField} as string[]).join(',');\n                    }\n                  #end\n              #end\n          #end\n        params.${subJoinColumn.javaField} = props.${subJoinColumn.javaField};\n        const data = await get${subSimpleClassName}Page(params)\n        list.value = data.list\n        total.value = data.total\n      #else\n          #if ($subTable.subJoinMany) ## 一对多\n           list.value = await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!);\n          #else\n           list.value = [await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!)];\n          #end\n      #end\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n    () => props.${subJoinColumn.javaField},\n    async (val) => {\n      if (!val) {\n        return;\n      }\n      await nextTick();\n      await getList()\n    },\n    { immediate: true },\n);\n\n#if ($table.templateType == 11) ## erp\n/** 初始化 */\nconst { hiddenSearchBar, tableToolbarRef, tableRef } = useTableToolbar();\nonMounted(() => {\n  getList();\n});\n#end\n</script>\n\n<template>\n    #if ($table.templateType == 11) ## erp\n      <FormModal @success=\"getList\" />\n      <div class=\"h-[600px]\">\n        <ContentWrap v-if=\"!hiddenSearchBar\">\n          <!-- 搜索工作栏 -->\n          <el-form\n              :model=\"queryParams\"\n              ref=\"queryFormRef\"\n              inline\n          >\n              #foreach($column in $subColumns)\n                  #if ($column.listOperation)\n                      #set ($dictType = $column.dictType)\n                      #set ($javaField = $column.javaField)\n                      #set ($javaType = $column.javaType)\n                      #set ($comment = $column.columnComment)\n                      #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                          #set ($dictMethod = \"number\")\n                      #elseif ($javaType == \"String\")\n                          #set ($dictMethod = \"string\")\n                      #elseif ($javaType == \"Boolean\")\n                          #set ($dictMethod = \"boolean\")\n                      #end\n                      #if ($column.htmlType == \"input\")\n                        <el-form-item label=\"${comment}\">\n                          <el-input\n                              v-model=\"queryParams.${javaField}\"\n                              placeholder=\"请输入${comment}\"\n                              clearable\n                              @keyup.enter=\"handleQuery\"\n                              class=\"!w-[240px]\"\n                          />\n                        </el-form-item>\n                      #elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\" || $column.htmlType == \"checkbox\")\n                        <el-form-item label=\"${comment}\">\n                          <el-select\n                              v-model=\"queryParams.${javaField}\"\n                              placeholder=\"请选择${comment}\"\n                              clearable\n                              class=\"!w-[240px]\"\n                          >\n                              #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n                                <el-option\n                                    v-for=\"dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')\"\n                                    :key=\"dict.value\"\n                                    :value=\"dict.value\"\n                                    :label=\"dict.label\"\n                                />\n                              #else## 未设置 dictType 数据字典的情况\n                                <el-option label=\"请选择字典生成\" value=\"\" />\n                              #end\n                          </el-select>\n                        </el-form-item>\n                      #elseif($column.htmlType == \"datetime\")\n                          #if ($column.listOperationCondition != \"BETWEEN\")## 非范围\n                            <el-form-item label=\"${comment}\">\n                              <el-date-picker\n                                  v-model=\"queryParams.${javaField}\"\n                                  value-format=\"YYYY-MM-DD\"\n                                  placeholder=\"选择${comment}\"\n                                  clearable\n                                  class=\"!w-[240px]\"\n                              />\n                            </el-form-item>\n                          #else## 范围\n                            <el-form-item label=\"${comment}\">\n                              <el-date-picker\n                                  v-model=\"queryParams.${javaField}\"\n                                  type=\"daterange\"\n                                  value-format=\"YYYY-MM-DD\"\n                                  range-separator=\"至\"\n                                  start-placeholder=\"开始日期\"\n                                  end-placeholder=\"结束日期\"\n                                  class=\"!w-[240px]\"\n                              />\n                            </el-form-item>\n                          #end\n                      #end\n                  #end\n              #end\n            <el-form-item>\n              <el-button class=\"ml-2\" @click=\"resetQuery\"> 重置 </el-button>\n              <el-button class=\"ml-2\" @click=\"handleQuery\" type=\"primary\">\n                搜索\n              </el-button>\n            </el-form-item>\n          </el-form>\n        </ContentWrap>\n\n        <!-- 列表 -->\n        <ContentWrap title=\"${table.classComment}\">\n          <template #extra>\n            <VbenVxeTableToolbar\n                ref=\"tableToolbarRef\"\n                v-model:hidden-search=\"hiddenSearchBar\"\n            >\n              <el-button\n                  class=\"ml-2\"\n                  :icon=\"h(Plus)\"\n                  type=\"primary\"\n                  @click=\"handleCreate\"\n                  v-access:code=\"['${permissionPrefix}:create']\"\n              >\n                {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}\n              </el-button>\n                #if ($deleteBatchEnable)\n                  <el-button\n                      :icon=\"h(Trash2)\"\n                      type=\"danger\"\n                      class=\"ml-2\"\n                      :disabled=\"isEmpty(checkedIds)\"\n                      @click=\"handleDeleteBatch\"\n                      v-access:code=\"['${table.moduleName}:${simpleClassName_strikeCase}:delete']\"\n                  >\n                    批量删除\n                  </el-button>\n                #end\n            </TableToolbar>\n          </template>\n          <vxe-table\n              ref=\"tableRef\"\n              :data=\"list\"\n              show-overflow\n              :loading=\"loading\"\n              #if ($deleteBatchEnable)\n              @checkboxAll=\"handleRowCheckboxChange\"\n              @checkboxChange=\"handleRowCheckboxChange\"\n              #end\n          >\n              #if ($deleteBatchEnable)\n                <vxe-column type=\"checkbox\" width=\"40\"></vxe-column>\n              #end\n              #foreach($column in $subColumns)\n                  #if ($column.listOperationResult)\n                      #set ($dictType=$column.dictType)\n                      #set ($javaField = $column.javaField)\n                      #set ($comment=$column.columnComment)\n                      #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                        <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                          <template #default=\"{row}\">\n                            {{formatDateTime(row.${javaField})}}\n                          </template>\n                        </vxe-column>\n                      #elseif($column.dictType && \"\" != $column.dictType)## 数据字典\n                        <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                          <template #default=\"{row}\">\n                            <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"row.${javaField}\" />\n                          </template>\n                        </vxe-column>\n                      #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)\n                        <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\"  tree-node/>\n                      #else\n                        <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\" />\n                      #end\n                  #end\n              #end\n            <vxe-column field=\"operation\" title=\"操作\" align=\"center\">\n              <template #default=\"{row}\">\n                <el-button\n                    size=\"small\"\n                    type=\"primary\"\n                    link\n                    @click=\"handleEdit(row as any)\"\n                    v-access:code=\"['${permissionPrefix}:update']\"\n                >\n                  {{ $t('ui.actionTitle.edit') }}\n                </el-button>\n                <el-button\n                    size=\"small\"\n                    type=\"danger\"\n                    link\n                    class=\"ml-2\"\n                    @click=\"handleDelete(row as any)\"\n                    v-access:code=\"['${permissionPrefix}:delete']\"\n                >\n                  {{ $t('ui.actionTitle.delete') }}\n                </el-button>\n              </template>\n            </vxe-column>\n          </vxe-table>\n          <!-- 分页 -->\n          <div class=\"mt-2 flex justify-end\">\n            <el-pagination\n                :total=\"total\"\n                v-model:current-page=\"queryParams.pageNo\"\n                v-model:page-size=\"queryParams.pageSize\"\n                :page-sizes=\"[10, 20, 50, 100]\"\n                layout=\"total, sizes, prev, pager, next, jumper\"\n                @size-change=\"getList\"\n                @current-change=\"getList\"\n            />\n          </div>\n        </ContentWrap>\n      </div>\n    #else\n    <ContentWrap title=\"${subTable.classComment}列表\">\n      <vxe-table\n          :data=\"list\"\n          show-overflow\n          :loading=\"loading\"\n      >\n          #foreach($column in $subColumns)\n              #if ($column.listOperationResult)\n                  #set ($dictType=$column.dictType)\n                  #set ($javaField = $column.javaField)\n                  #set ($comment=$column.columnComment)\n                  #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                    <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                      <template #default=\"{row}\">\n                        {{formatDateTime(row.${javaField})}}\n                      </template>\n                    </vxe-column>\n                  #elseif($column.dictType && \"\" != $column.dictType)## 数据字典\n                    <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\">\n                      <template #default=\"{row}\">\n                        <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"row.${javaField}\" />\n                      </template>\n                    </vxe-column>\n                  #else\n                    <vxe-column field=\"${javaField}\" title=\"${comment}\" align=\"center\" />\n                  #end\n              #end\n          #end\n      </vxe-table>\n    </ContentWrap>\n    #end\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/general/views/modules/list_sub_inner.vue.vm",
    "content": "## 子表的 erp 和 inner 使用相似的 list 列表，差异主要两点：\n## 1）inner 使用 list 不分页，erp 使用 page 分页\n## 2）erp 支持单个子表的新增、修改、删除，inner 不支持\n#parse(\"codegen/vue3_vben5_ele/general/views/modules/list_sub_erp.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/api/api.ts.vm",
    "content": "#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n#if ( $table.templateType != 2 )\nimport type { PageParam, PageResult } from '@vben/request';\n#end\nimport type { Dayjs } from 'dayjs';\n\nimport { requestClient } from '#/api/request';\n#set ($baseURL = \"/${table.moduleName}/${simpleClassName_strikeCase}\")\n\nexport namespace ${apiName} {\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n  #set ($index = $foreach.count - 1)\n  #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n  #set ($subColumns = $subColumnsList.get($index))##当前字段数组\n  /** ${subTable.classComment}信息 */\n  export interface ${subSimpleClassName} {\n    #foreach ($column in $subColumns)\n      #if ($column.createOperation || $column.updateOperation)\n        #if(${column.javaType.toLowerCase()} == \"long\" || ${column.javaType.toLowerCase()} == \"integer\" || ${column.javaType.toLowerCase()} == \"short\" || ${column.javaType.toLowerCase()} == \"double\" || ${column.javaType.toLowerCase()} == \"bigdecimal\")\n            ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}\n        #elseif(${column.javaType.toLowerCase()} == \"date\" || ${column.javaType.toLowerCase()} == \"localdate\" || ${column.javaType.toLowerCase()} == \"localdatetime\")\n            ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}\n        #else\n            ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}\n        #end\n      #end\n    #end\n  }\n\n#end\n  /** ${table.classComment}信息 */\n  export interface ${simpleClassName} {\n#foreach ($column in $columns)\n#if ($column.createOperation || $column.updateOperation)\n#if(${column.javaType.toLowerCase()} == \"long\" || ${column.javaType.toLowerCase()} == \"integer\" || ${column.javaType.toLowerCase()} == \"short\" || ${column.javaType.toLowerCase()} == \"double\" || ${column.javaType.toLowerCase()} == \"bigdecimal\")\n    ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}\n#elseif(${column.javaType.toLowerCase()} == \"date\" || ${column.javaType.toLowerCase()} == \"localdate\" || ${column.javaType.toLowerCase()} == \"localdatetime\")\n    ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}\n#else\n    ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}\n#end\n#end\n#end\n#if ( $table.templateType == 2 )\n  children?: ${simpleClassName}[];\n#end\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n  #foreach ($subTable in $subTables)\n    #set ($index = $foreach.count - 1)\n    #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n    #if ( $subTable.subJoinMany )\n        ${subSimpleClassName.toLowerCase()}s?: ${subSimpleClassName}[]\n    #else\n        ${subSimpleClassName.toLowerCase()}?: ${subSimpleClassName}\n    #end\n  #end\n#end\n  }\n}\n\n#if ( $table.templateType != 2 )\n/** 查询${table.classComment}分页 */\nexport function get${simpleClassName}Page(params: PageParam) {\n  return requestClient.get<PageResult<${apiName}.${simpleClassName}>>(\n    '${baseURL}/page',\n    { params },\n  );\n}\n#else\n/** 查询${table.classComment}列表 */\nexport function get${simpleClassName}List(params: any) {\n  return requestClient.get<${apiName}.${simpleClassName}[]>(\n    '${baseURL}/list',\n    { params },\n  );\n}\n#end\n\n/** 查询${table.classComment}详情 */\nexport function get${simpleClassName}(id: number) {\n  return requestClient.get<${apiName}.${simpleClassName}>(\n    `${baseURL}/get?id=${id}`,\n  );\n}\n\n/** 新增${table.classComment} */\nexport function create${simpleClassName}(data: ${apiName}.${simpleClassName}) {\n  return requestClient.post('${baseURL}/create', data);\n}\n\n/** 修改${table.classComment} */\nexport function update${simpleClassName}(data: ${apiName}.${simpleClassName}) {\n  return requestClient.put('${baseURL}/update', data);\n}\n\n/** 删除${table.classComment} */\nexport function delete${simpleClassName}(id: number) {\n  return requestClient.delete(`${baseURL}/delete?id=${id}`);\n}\n#if ( $table.templateType != 2 && $deleteBatchEnable)\n\n/** 批量删除${table.classComment} */\nexport function delete${simpleClassName}List(ids: number[]) {\n  return requestClient.delete(\n    `${baseURL}/delete-list?ids=${ids.join(',')}`,\n  );\n}\n#end\n\n/** 导出${table.classComment} */\nexport function export${simpleClassName}(params: any) {\n  return requestClient.download('${baseURL}/export-excel', { params });\n}\n\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n#set ($index = $foreach.count - 1)\n#set ($subSimpleClassName = $subSimpleClassNames.get($index))\n#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段\n#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n#set ($subClassNameVar = $subClassNameVars.get($index))\n\n// ==================== 子表（$subTable.classComment） ====================\n\n## 情况一：MASTER_ERP 时，需要分查询页子表\n#if ( $table.templateType == 11 )\n/** 获得${subTable.classComment}分页 */\nexport function get${subSimpleClassName}Page(params: PageParam) {\n  return requestClient.get<PageResult<${apiName}.${subSimpleClassName}>>(\n    `${baseURL}/${subSimpleClassName_strikeCase}/page`,\n    { params },\n  );\n}\n## 情况二：非 MASTER_ERP 时，需要列表查询子表\n#else\n  #if ( $subTable.subJoinMany )\n/** 获得${subTable.classComment}列表 */\nexport function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}: number) {\n  return requestClient.get<${apiName}.${subSimpleClassName}[]>(\n    `${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`,\n  );\n}\n  #else\n/** 获得${subTable.classComment} */\nexport function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}: number) {\n  return requestClient.get<${apiName}.${subSimpleClassName}>(\n    `${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`,\n  );\n}\n  #end\n#end\n## 特殊：MASTER_ERP 时，支持单个的新增、修改、删除操作\n#if ( $table.templateType == 11 )\n/** 新增${subTable.classComment} */\nexport function create${subSimpleClassName}(data: ${apiName}.${subSimpleClassName}) {\n  return requestClient.post(`${baseURL}/${subSimpleClassName_strikeCase}/create`, data);\n}\n\n/** 修改${subTable.classComment} */\nexport function update${subSimpleClassName}(data: ${apiName}.${subSimpleClassName}) {\n  return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data);\n}\n\n/** 删除${subTable.classComment} */\nexport function delete${subSimpleClassName}(id: number) {\n  return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete?id=${id}`);\n}\n\n#if ($deleteBatchEnable)\n/** 批量删除${subTable.classComment} */\nexport function delete${subSimpleClassName}List(ids: number[]) {\n  return requestClient.delete(\n    `${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}`,\n  );\n}\n#end\n\n/** 获得${subTable.classComment} */\nexport function get${subSimpleClassName}(id: number) {\n  return requestClient.get<${apiName}.${subSimpleClassName}>(\n    `${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`,\n  );\n}\n#end\n#end\n\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/data.ts.vm",
    "content": "#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\nimport type { VbenFormSchema } from '#/adapter/form';\nimport type { VxeTableGridOptions } from '#/adapter/vxe-table';\nimport type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { DICT_TYPE } from '@vben/constants';\nimport { getDictOptions } from '@vben/hooks';\n#if(${table.templateType} == 2)## 树表需要导入这些\nimport { get${simpleClassName}List } from '#/api/${table.moduleName}/${table.businessName}';\nimport { handleTree } from '@vben/utils';\n#end\n\nimport { getRangePickerDefaultProps } from '#/utils';\n\n/** 新增/修改的表单 */\nexport function useFormSchema(): VbenFormSchema[] {\n  return [\n    {\n      fieldName: 'id',\n      component: 'Input',\n      dependencies: {\n        triggerFields: [''],\n        show: () => false,\n      },\n    },\n#if(${table.templateType} == 2)## 树表特有字段：上级\n    {\n      fieldName: '${treeParentColumn.javaField}',\n      label: '上级${table.classComment}',\n      component: 'ApiTreeSelect',\n      componentProps: {\n        clearable: true,\n        api: async () => {\n          const data = await get${simpleClassName}List({});\n          data.unshift({\n            id: 0,\n            ${treeNameColumn.javaField}: '顶级${table.classComment}',\n          });\n          return handleTree(data);\n        },\n        labelField: '${treeNameColumn.javaField}',\n        valueField: 'id',\n        childrenField: 'children',\n        placeholder: '请选择上级${table.classComment}',\n        treeDefaultExpandAll: true,\n      },\n      rules: 'selectRequired',\n    },\n#end\n#foreach($column in $columns)\n#if ($column.createOperation || $column.updateOperation)\n#if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段，这里排除\n  #set ($dictType = $column.dictType)\n  #set ($javaType = $column.javaType)\n  #set ($javaField = $column.javaField)\n  #set ($comment = $column.columnComment)\n  #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n    #set ($dictMethod = \"number\")\n  #elseif ($javaType == \"String\")\n    #set ($dictMethod = \"string\")\n  #elseif ($javaType == \"Boolean\")\n    #set ($dictMethod = \"boolean\")\n  #end\n    {\n      fieldName: '${javaField}',\n      label: '${comment}',\n  #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n      rules: 'required',\n  #end\n  #if ($column.htmlType == \"input\")\n      component: 'Input',\n      componentProps: {\n        placeholder: '请输入${comment}',\n      },\n  #elseif($column.htmlType == \"imageUpload\")## 图片上传\n      component: 'ImageUpload',\n  #elseif($column.htmlType == \"fileUpload\")## 文件上传\n      component: 'FileUpload',\n  #elseif($column.htmlType == \"editor\")## 文本编辑器\n      component: 'RichTextarea',\n  #elseif($column.htmlType == \"select\")## 下拉框\n      component: 'Select',\n      componentProps: {\n        #if (\"\" != $dictType)## 有数据字典\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n        #else##没数据字典\n        options: [],\n        #end\n        placeholder: '请选择${comment}',\n      },\n  #elseif($column.htmlType == \"checkbox\")## 多选框\n      component: 'Checkbox',\n      componentProps: {\n        #if (\"\" != $dictType)## 有数据字典\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n        #else##没数据字典\n        options: [],\n        #end\n      },\n  #elseif($column.htmlType == \"radio\")## 单选框\n      component: 'RadioGroup',\n      componentProps: {\n        #if (\"\" != $dictType)## 有数据字典\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n        #else##没数据字典\n        options: [],\n        #end\n        buttonStyle: 'solid',\n        optionType: 'button',\n      },\n  #elseif($column.htmlType == \"datetime\")## 时间框\n      component: 'DatePicker',\n      componentProps: {\n        showTime: true,\n        format: 'YYYY-MM-DD HH:mm:ss',\n        valueFormat: 'x',\n        class: '!w-full',\n      },\n  #elseif($column.htmlType == \"textarea\")## 文本域\n      component: 'Textarea',\n      componentProps: {\n        placeholder: '请输入${comment}',\n      },\n  #elseif($column.htmlType == \"inputNumber\")## 数字输入框\n      component: 'InputNumber',\n      componentProps: {\n        min: 0,\n        placeholder: '请输入${comment}',\n        controlsPosition: 'right',\n        class: '!w-full',\n      },\n  #end\n    },\n#end\n#end\n#end\n  ];\n}\n\n/** 列表的搜索表单 */\nexport function useGridFormSchema(): VbenFormSchema[] {\n  return [\n#foreach($column in $columns)\n#if ($column.listOperation)\n  #set ($dictType = $column.dictType)\n  #set ($javaType = $column.javaType)\n  #set ($javaField = $column.javaField)\n  #set ($comment = $column.columnComment)\n  #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n    #set ($dictMethod = \"number\")\n  #elseif ($javaType == \"String\")\n    #set ($dictMethod = \"string\")\n  #elseif ($javaType == \"Boolean\")\n    #set ($dictMethod = \"boolean\")\n  #end\n    {\n      fieldName: '${javaField}',\n      label: '${comment}',\n  #if ($column.htmlType == \"input\" || $column.htmlType == \"textarea\" || $column.htmlType == \"editor\")\n      component: 'Input',\n      componentProps: {\n        clearable: true,\n        placeholder: '请输入${comment}',\n      },\n  #elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\")\n      component: 'Select',\n      componentProps: {\n        clearable: true,\n        #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n        #else## 未设置 dictType 数据字典的情况\n        options: [],\n        #end\n        placeholder: '请选择${comment}',\n      },\n  #elseif($column.htmlType == \"datetime\")\n      component: 'RangePicker',\n      componentProps: {\n        ...getRangePickerDefaultProps(),\n        clearable: true,\n      },\n  #end\n    },\n#end\n#end\n  ];\n}\n\n/** 列表的字段 */\nexport function useGridColumns(): VxeTableGridOptions<${apiName}.${simpleClassName}>['columns'] {\n  return [\n#if ($table.templateType != 2 && $deleteBatchEnable)\n  { type: 'checkbox', width: 40 },\n#end\n#if ($table.templateType == 12) ## 内嵌情况\n      { type: 'expand', width: 80, slots: { content: 'expand_content' } },\n#end\n#foreach($column in $columns)\n#if ($column.listOperationResult)\n  #set ($dictType = $column.dictType)\n  #set ($javaField = $column.javaField)\n  #set ($comment = $column.columnComment)\n    {\n      field: '${javaField}',\n      title: '${comment}',\n      minWidth: 120,\n  #if ($column.javaType == \"LocalDateTime\")## 时间类型\n      formatter: 'formatDateTime',\n  #elseif(\"\" != $dictType)## 数据字典\n      cellRender: {\n        name: 'CellDict',\n        props: { type: DICT_TYPE.$dictType.toUpperCase() },\n      },\n  #end\n  #if (${table.templateType} == 2 && $column.id == $treeNameColumn.id)## 树表特有:标记树节点列\n      treeNode: true,\n  #end\n    },\n#end\n#end\n    {\n      title: '操作',\n      width: 200,\n      fixed: 'right',\n      slots: { default: 'actions' },\n    },\n  ];\n}\n\n## 标准模式和内嵌模式时，主子关系一对一则生成表单schema,一对多则生成列表schema（内嵌模式时表单schema也要生成）。erp 模式时都生成\n## 特殊：主子表专属逻辑\n#foreach ($subTable in $subTables)\n    #set ($index = $foreach.count - 1)\n    #set ($subColumns = $subColumnsList.get($index))##当前字段数组\n    #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n    #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段\n    #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n// ==================== 子表（$subTable.classComment） ====================\n\n#if ($table.templateType == 11) ## erp 情况\n/** 新增/修改的表单 */\nexport function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {\n    return [\n        {\n            fieldName: 'id',\n            component: 'Input',\n            dependencies: {\n                triggerFields: [''],\n                show: () => false,\n            },\n        },\n        #foreach($column in $subColumns)\n            #if ($column.createOperation || $column.updateOperation)\n                #if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段，这里排除\n                    #set ($dictType = $column.dictType)\n                    #set ($javaType = $column.javaType)\n                    #set ($javaField = $column.javaField)\n                    #set ($comment = $column.columnComment)\n                    #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                        #set ($dictMethod = \"number\")\n                    #elseif ($javaType == \"String\")\n                        #set ($dictMethod = \"string\")\n                    #elseif ($javaType == \"Boolean\")\n                        #set ($dictMethod = \"boolean\")\n                    #end\n                    #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                    #else\n                        {\n                            fieldName: '${javaField}',\n                            label: '${comment}',\n                            #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n                                rules: 'required',\n                            #end\n                            #if ($column.htmlType == \"input\")\n                                component: 'Input',\n                                componentProps: {\n                                    placeholder: '请输入${comment}',\n                                },\n                            #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                                component: 'ImageUpload',\n                            #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                                component: 'FileUpload',\n                            #elseif($column.htmlType == \"editor\")## 文本编辑器\n                                component: 'RichTextarea',\n                            #elseif($column.htmlType == \"select\")## 下拉框\n                                component: 'Select',\n                                componentProps: {\n                                    #if (\"\" != $dictType)## 有数据字典\n                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                    #else##没数据字典\n                                        options: [],\n                                    #end\n                                    placeholder: '请选择${comment}',\n                                },\n                            #elseif($column.htmlType == \"checkbox\")## 多选框\n                                component: 'Checkbox',\n                                componentProps: {\n                                    #if (\"\" != $dictType)## 有数据字典\n                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                    #else##没数据字典\n                                        options: [],\n                                    #end\n                                },\n                            #elseif($column.htmlType == \"radio\")## 单选框\n                                component: 'RadioGroup',\n                                componentProps: {\n                                    #if (\"\" != $dictType)## 有数据字典\n                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                    #else##没数据字典\n                                        options: [],\n                                    #end\n                                    buttonStyle: 'solid',\n                                    optionType: 'button',\n                                },\n                            #elseif($column.htmlType == \"datetime\")## 时间框\n                                component: 'DatePicker',\n                                componentProps: {\n                                    showTime: true,\n                                    format: 'YYYY-MM-DD HH:mm:ss',\n                                    valueFormat: 'x',\n                                    class: '!w-full',\n                                },\n                            #elseif($column.htmlType == \"textarea\")## 文本域\n                                component: 'Textarea',\n                                componentProps: {\n                                    placeholder: '请输入${comment}',\n                                },\n                            #elseif($column.htmlType == \"inputNumber\")## 数字输入框\n                                component: 'InputNumber',\n                                componentProps: {\n                                    min: 0,\n                                    placeholder: '请输入${comment}',\n                                    controlsPosition: 'right',\n                                    class: '!w-full',\n                                },\n                            #end\n                        },\n                    #end\n                #end\n            #end\n        #end\n    ];\n}\n\n/** 列表的搜索表单 */\nexport function use${subSimpleClassName}GridFormSchema(): VbenFormSchema[] {\n    return [\n        #foreach($column in $subColumns)\n            #if ($column.listOperation)\n                #set ($dictType = $column.dictType)\n                #set ($javaType = $column.javaType)\n                #set ($javaField = $column.javaField)\n                #set ($comment = $column.columnComment)\n                #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                    #set ($dictMethod = \"number\")\n                #elseif ($javaType == \"String\")\n                    #set ($dictMethod = \"string\")\n                #elseif ($javaType == \"Boolean\")\n                    #set ($dictMethod = \"boolean\")\n                #end\n                {\n                    fieldName: '${javaField}',\n                    label: '${comment}',\n                    #if ($column.htmlType == \"input\" || $column.htmlType == \"textarea\" || $column.htmlType == \"editor\")\n                        component: 'Input',\n                        componentProps: {\n                            clearable: true,\n                            placeholder: '请输入${comment}',\n                        },\n                    #elseif ($column.htmlType == \"select\" || $column.htmlType == \"radio\")\n                        component: 'Select',\n                        componentProps: {\n                            clearable: true,\n                            #if (\"\" != $dictType)## 设置了 dictType 数据字典的情况\n                                options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                            #else## 未设置 dictType 数据字典的情况\n                                options: [],\n                            #end\n                            placeholder: '请选择${comment}',\n                        },\n                    #elseif($column.htmlType == \"datetime\")\n                        component: 'RangePicker',\n                        componentProps: {\n                            ...getRangePickerDefaultProps(),\n                            clearable: true,\n                        },\n                    #end\n                },\n            #end\n        #end\n    ];\n}\n\n/** 列表的字段 */\nexport function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {\n    return [\n        #if ($table.templateType != 2 && $deleteBatchEnable)\n            { type: 'checkbox', width: 40 },\n        #end\n        #foreach($column in $subColumns)\n            #if ($column.listOperationResult)\n                #set ($dictType = $column.dictType)\n                #set ($javaField = $column.javaField)\n                #set ($comment = $column.columnComment)\n                {\n                    field: '${javaField}',\n                    title: '${comment}',\n                    minWidth: 120,\n                    #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                        formatter: 'formatDateTime',\n                    #elseif(\"\" != $dictType)## 数据字典\n                        cellRender: {\n                            name: 'CellDict',\n                            props: { type: DICT_TYPE.$dictType.toUpperCase() },\n                        },\n                    #end\n                },\n            #end\n        #end\n        {\n            title: '操作',\n            width: 200,\n            fixed: 'right',\n            slots: { default: 'actions' },\n        },\n    ];\n}\n\n#else\n    #if ($subTable.subJoinMany) ## 一对多\n    /** 新增/修改列表的字段 */\n    export function use${subSimpleClassName}GridEditColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {\n        return [\n            #foreach($column in $subColumns)\n                #if ($column.createOperation || $column.updateOperation)\n                    #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                        #set ($dictType = $column.dictType)\n                        #set ($javaField = $column.javaField)\n                        #set ($comment = $column.columnComment)\n                        #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                            #set ($dictMethod = \"number\")\n                        #elseif ($javaType == \"String\")\n                            #set ($dictMethod = \"string\")\n                        #elseif ($javaType == \"Boolean\")\n                            #set ($dictMethod = \"boolean\")\n                        #end\n                        {\n                            field: '${javaField}',\n                            title: '${comment}',\n                            minWidth: 120,\n                            slots: { default: '${javaField}' },\n                            #if ($column.htmlType == \"select\" || $column.htmlType == \"checkbox\" || $column.htmlType == \"radio\")\n                                #if (\"\" != $dictType)## 有数据字典\n                                    params: {\n                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                    },\n                                #else\n                                    params: {\n                                        options: [],\n                                    },\n                                #end\n                            #end\n                        },\n                    #end\n                #end\n            #end\n            {\n                title: '操作',\n                width: 200,\n                fixed: 'right',\n                slots: { default: 'actions' },\n            },\n        ];\n    }\n\n    #else\n    /** 新增/修改的表单 */\n    export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {\n        return [\n            {\n                fieldName: 'id',\n                component: 'Input',\n                dependencies: {\n                    triggerFields: [''],\n                    show: () => false,\n                },\n            },\n            #foreach($column in $subColumns)\n                #if ($column.createOperation || $column.updateOperation)\n                    #if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段，这里排除\n                        #set ($dictType = $column.dictType)\n                        #set ($javaType = $column.javaType)\n                        #set ($javaField = $column.javaField)\n                        #set ($comment = $column.columnComment)\n                        #if ($javaType == \"Integer\" || $javaType == \"Long\" || $javaType == \"Byte\" || $javaType == \"Short\")\n                            #set ($dictMethod = \"number\")\n                        #elseif ($javaType == \"String\")\n                            #set ($dictMethod = \"string\")\n                        #elseif ($javaType == \"Boolean\")\n                            #set ($dictMethod = \"boolean\")\n                        #end\n                        #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n                        #else\n                            {\n                                fieldName: '${javaField}',\n                                label: '${comment}',\n                                #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键\n                                    rules: 'required',\n                                #end\n                                #if ($column.htmlType == \"input\")\n                                    component: 'Input',\n                                    componentProps: {\n                                        placeholder: '请输入${comment}',\n                                    },\n                                #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                                    component: 'ImageUpload',\n                                #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                                    component: 'FileUpload',\n                                #elseif($column.htmlType == \"editor\")## 文本编辑器\n                                    component: 'RichTextarea',\n                                #elseif($column.htmlType == \"select\")## 下拉框\n                                    component: 'Select',\n                                    componentProps: {\n                                        #if (\"\" != $dictType)## 有数据字典\n                                            options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                        #else##没数据字典\n                                            options: [],\n                                        #end\n                                        placeholder: '请选择${comment}',\n                                    },\n                                #elseif($column.htmlType == \"checkbox\")## 多选框\n                                    component: 'Checkbox',\n                                    componentProps: {\n                                        #if (\"\" != $dictType)## 有数据字典\n                                            options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                        #else##没数据字典\n                                            options: [],\n                                        #end\n                                    },\n                                #elseif($column.htmlType == \"radio\")## 单选框\n                                    component: 'RadioGroup',\n                                    componentProps: {\n                                        #if (\"\" != $dictType)## 有数据字典\n                                            options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),\n                                        #else##没数据字典\n                                            options: [],\n                                        #end\n                                        buttonStyle: 'solid',\n                                        optionType: 'button',\n                                    },\n                                #elseif($column.htmlType == \"datetime\")## 时间框\n                                    component: 'DatePicker',\n                                    componentProps: {\n                                        showTime: true,\n                                        format: 'YYYY-MM-DD HH:mm:ss',\n                                        valueFormat: 'x',\n                                        class: '!w-full',\n                                    },\n                                #elseif($column.htmlType == \"textarea\")## 文本域\n                                    component: 'Textarea',\n                                    componentProps: {\n                                        placeholder: '请输入${comment}',\n                                    },\n                                #elseif($column.htmlType == \"inputNumber\")## 数字输入框\n                                    component: 'InputNumber',\n                                    componentProps: {\n                                        min: 0,\n                                        placeholder: '请输入${comment}',\n                                        controlsPosition: 'right',\n                                        class: '!w-full',\n                                    },\n                                #end\n                            },\n                        #end\n                    #end\n                #end\n            #end\n        ];\n    }\n\n    #end\n    #if ($table.templateType == 12) ## 内嵌情况\n    /** 列表的字段 */\n    export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {\n        return [\n            #foreach($column in $subColumns)\n                #if ($column.listOperationResult)\n                    #set ($dictType = $column.dictType)\n                    #set ($javaField = $column.javaField)\n                    #set ($comment = $column.columnComment)\n                    {\n                        field: '${javaField}',\n                        title: '${comment}',\n                        minWidth: 120,\n                        #if ($column.javaType == \"LocalDateTime\")## 时间类型\n                            formatter: 'formatDateTime',\n                        #elseif(\"\" != $dictType)## 数据字典\n                            cellRender: {\n                                name: 'CellDict',\n                                props: { type: DICT_TYPE.$dictType.toUpperCase() },\n                            },\n                        #end\n                    },\n                #end\n            #end\n        ];\n    }\n    #end\n#end\n#end"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/form.vue.vm",
    "content": "#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\nimport type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { computed, ref } from 'vue';\n\nimport { useVbenModal } from '@vben/common-ui';\n\nimport { ElMessage#if ($table.templateType == 11), ElTabs, ElTabPane#end } from 'element-plus';\n\nimport { useVbenForm } from '#/adapter/form';\nimport { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';\nimport { $t } from '#/locales';\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n  #foreach ($subSimpleClassName in $subSimpleClassNames)\n  #set ($index = $foreach.count - 1)\n  #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\n  import ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'\n  #end\n#end\n\nimport { useFormSchema } from '../data';\n\nconst emit = defineEmits(['success']);\nconst formData = ref<${apiName}.${simpleClassName}>();\nconst getTitle = computed(() => {\n  return formData.value?.id\n    ? $t('ui.actionTitle.edit', ['${table.classComment}'])\n    : $t('ui.actionTitle.create', ['${table.classComment}']);\n});\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n  #if ( $subTables && $subTables.size() > 0 )\n\n  /** 子表的表单 */\n  const subTabsName = ref('$subClassNameVars.get(0)')\n    #foreach ($subClassNameVar in $subClassNameVars)\n      #set ($index = $foreach.count - 1)\n      #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n      const ${subClassNameVar}FormRef = ref<InstanceType<typeof ${subSimpleClassName}Form>>()\n    #end\n  #end\n#end\n\nconst [Form, formApi] = useVbenForm({\n  commonConfig: {\n    componentProps: {\n      class: 'w-full',\n    },\n    formItemClass: 'col-span-2',\n    labelWidth: 80,\n  },\n  layout: 'horizontal',\n  schema: useFormSchema(),\n  showDefaultActions: false,\n});\n\nconst [Modal, modalApi] = useVbenModal({\n  async onConfirm() {\n    const { valid } = await formApi.validate();\n    if (!valid) {\n      return;\n    }\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n      #if ( $subTables && $subTables.size() > 0 )\n        // 校验子表单\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #if ($subTable.subJoinMany) ## 一对多\n            ## TODO 列表值校验？\n          #else\n            const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();\n            if (!${subClassNameVar}Valid) {\n              subTabsName.value = '${subClassNameVar}';\n              return;\n            }\n          #end\n        #end\n      #end\n#end\n    modalApi.lock();\n    // 提交表单\n    const data = (await formApi.getValues()) as ${apiName}.${simpleClassName};\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n      #if ( $subTables && $subTables.size() > 0 )\n        // 拼接子表的数据\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #if ($subTable.subJoinMany)\n            data.${subClassNameVar}s = ${subClassNameVar}FormRef.value?.getData();\n          #else\n            data.${subClassNameVar} = await ${subClassNameVar}FormRef.value?.getValues();\n          #end\n        #end\n      #end\n#end\n    try {\n      await (formData.value?.id ? update${simpleClassName}(data) : create${simpleClassName}(data));\n      // 关闭并提示\n      await modalApi.close();\n      emit('success');\n      ElMessage.success($t('ui.actionMessage.operationSuccess'));\n    } finally {\n      modalApi.unlock();\n    }\n  },\n  async onOpenChange(isOpen: boolean) {\n    if (!isOpen) {\n      formData.value = undefined;\n      return;\n    }\n    // 加载数据\n    const data = modalApi.getData<${apiName}.${simpleClassName}>();\n    if (!data || !data.id) {\n#if (${table.templateType} == 2)## 树表特有\n      // 设置上级\n      await formApi.setValues(data);\n#end\n      return;\n    }\n    modalApi.lock();\n    try {\n      formData.value = await get${simpleClassName}(data.id);\n      // 设置到 values\n      await formApi.setValues(formData.value);\n    } finally {\n      modalApi.unlock();\n    }\n  },\n});\n</script>\n\n<template>\n  <Modal :title=\"getTitle\">\n    <Form class=\"mx-4\" />\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 10 || $table.templateType == 12 )\n      <!-- 子表的表单 -->\n      <ElTabs v-model=\"subTabsName\">\n        #foreach ($subTable in $subTables)\n          #set ($index = $foreach.count - 1)\n          #set ($subClassNameVar = $subClassNameVars.get($index))\n          #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n          #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n          <ElTabPane name=\"$subClassNameVar\" label=\"${subTable.classComment}\">\n            <${subSimpleClassName}Form ref=\"${subClassNameVar}FormRef\" :${subJoinColumn_strikeCase}=\"formData?.id\" />\n          </ElTabPane>\n        #end\n      </ElTabs>\n#end\n  </Modal>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/index.vue.vm",
    "content": "#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\nimport type { VxeTableGridOptions } from '#/adapter/vxe-table';\nimport type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\nimport { ref } from 'vue';\n\nimport {#if ($table.templateType != 2 && $deleteBatchEnable) confirm,#end Page, useVbenModal } from '@vben/common-ui';\nimport { downloadFileFromBlobPart#if ($table.templateType != 2 && $deleteBatchEnable), isEmpty#end } from '@vben/utils';\n\nimport { ElLoading, ElMessage#if ($table.templateType == 11), ElTabs, ElTabPane#end } from 'element-plus';\n\nimport { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';\n#if (${table.templateType} == 2)## 树表接口\nimport {\n  delete${simpleClassName},\n  export${simpleClassName},\n  get${simpleClassName}List,\n} from '#/api/${table.moduleName}/${table.businessName}';\n#else## 标准表接口\nimport {\n  delete${simpleClassName},#if ($deleteBatchEnable)\n\n  delete${simpleClassName}List,#end\n  export${simpleClassName},\n  get${simpleClassName}Page,\n} from '#/api/${table.moduleName}/${table.businessName}';\n#end\nimport { $t } from '#/locales';\n\nimport { useGridColumns, useGridFormSchema } from './data';\nimport Form from './modules/form.vue';\n## 特殊：主子表专属逻辑\n#if ( $table.templateType == 11 || $table.templateType == 12 )\n    #foreach ($subSimpleClassName in $subSimpleClassNames)\n    #set ($index = $foreach.count - 1)\n    #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))\nimport ${subSimpleClassName}List from './modules/${subSimpleClassName_strikeCase}-list.vue';\n    #end\n#end\n\n#if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况\n/** 子表的列表 */\nconst subTabsName = ref('$subClassNameVars.get(0)')\n#if ($table.templateType == 11)\nconst select${simpleClassName} = ref<${apiName}.${simpleClassName}>();\n#end\n#end\nconst [FormModal, formModalApi] = useVbenModal({\n  connectedComponent: Form,\n  destroyOnClose: true,\n});\n#if (${table.templateType} == 2)## 树表特有：控制表格展开收缩\n\n/** 切换树形展开/收缩状态 */\nconst isExpanded = ref(true);\nfunction handleExpand() {\n  isExpanded.value = !isExpanded.value;\n  gridApi.grid.setAllTreeExpand(isExpanded.value);\n}\n#end\n\n/** 刷新表格 */\nfunction handleRefresh() {\n#if ($table.templateType == 12) ## 内嵌情况\n  gridApi.reload();\n#else\n  gridApi.query();\n#end\n}\n\n/** 创建${table.classComment} */\nfunction handleCreate() {\n  formModalApi.setData(null).open();\n}\n#if (${table.templateType} == 2)## 树表特有：新增下级\n\n/** 添加下级${table.classComment} */\nfunction handleAppend(row: ${apiName}.${simpleClassName}) {\n  formModalApi.setData({ ${treeParentColumn.javaField}: row.id }).open();\n}\n#end\n\n/** 编辑${table.classComment} */\nfunction handleEdit(row: ${apiName}.${simpleClassName}) {\n  formModalApi.setData(row).open();\n}\n\n/** 删除${table.classComment} */\nasync function handleDelete(row: ${apiName}.${simpleClassName}) {\n  const loadingInstance = ElLoading.service({\n    text: $t('ui.actionMessage.deleting', [row.id]),\n  });\n  try {\n    await delete${simpleClassName}(row.id!);\n    ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.id]));\n    handleRefresh();\n  } finally {\n    loadingInstance.close();\n  }\n}\n\n#if ($table.templateType != 2 && $deleteBatchEnable)\n/** 批量删除${table.classComment} */\nasync function handleDeleteBatch() {\n  await confirm($t('ui.actionMessage.deleteBatchConfirm'));\n  const loadingInstance = ElLoading.service({\n    text: $t('ui.actionMessage.deletingBatch'),\n  });\n  try {\n    await delete${simpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    ElMessage.success($t('ui.actionMessage.deleteSuccess'));\n    handleRefresh();\n  } finally {\n    loadingInstance.close();\n  }\n}\n\nconst checkedIds = ref<number[]>([]);\nfunction handleRowCheckboxChange({\n  records,\n}: {\n  records: ${apiName}.${simpleClassName}[];\n}) {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n\n/** 导出表格 */\nasync function handleExport() {\n  const data = await export${simpleClassName}(await gridApi.formApi.getValues());\n  downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });\n}\n\nconst [Grid, gridApi] = useVbenVxeGrid({\n  formOptions: {\n    schema: useGridFormSchema(),\n  },\n  gridOptions: {\n    columns: useGridColumns(),\n#if (${table.templateType} == 11)\n    height: '600px',\n#else\n    height: 'auto',\n#end\n#if (${table.templateType} == 2)## 树表设置\n    pagerConfig: {\n      enabled: false,\n    },\n#else## 标准表设置\n    keepSource: true,\n#end\n    proxyConfig: {\n      ajax: {\n#if (${table.templateType} == 2)## 树表数据加载\n        query: async (_, formValues) => {\n          return await get${simpleClassName}List(formValues);\n        },\n#else## 标准表数据加载\n        query: async ({ page }, formValues) => {\n          return await get${simpleClassName}Page({\n            pageNo: page.currentPage,\n            pageSize: page.pageSize,\n            ...formValues,\n          });\n        },\n#end\n      },\n    },\n    rowConfig: {\n      keyField: 'id',\n      isHover: true,\n#if (${table.templateType} == 11)\n      isCurrent: true,\n#end\n    },\n    toolbarConfig: {\n      refresh: true,\n      search: true,\n    },\n#if (${table.templateType} == 2)## 树表设置\n    treeConfig: {\n      parentField: '${treeParentColumn.javaField}',\n      rowField: 'id',\n      transform: true,\n      expandAll: true,\n      reserve: true,\n    },\n#end\n  } as VxeTableGridOptions<${apiName}.${simpleClassName}>,\n#if (${table.templateType} == 11 || (${table.templateType} != 2 && $deleteBatchEnable))\n  gridEvents: {\n  #if(${table.templateType} == 11)\n    cellClick: ({ row }: { row: ${apiName}.${simpleClassName}}) => {\n      select${simpleClassName}.value = row;\n    },\n  #end\n  #if (${table.templateType} != 2 && $deleteBatchEnable)\n    checkboxAll: handleRowCheckboxChange,\n    checkboxChange: handleRowCheckboxChange,\n  #end\n  },\n#end\n});\n</script>\n\n<template>\n  <Page auto-content-height>\n    <FormModal @success=\"handleRefresh\" />\n#if ($table.templateType == 11) ## erp情况\n  <div>\n#end\n    <Grid table-title=\"${table.classComment}列表\">\n        #if ($table.templateType == 12) ## 内嵌情况\n          <template #expand_content=\"{ row }\">\n            <!-- 子表的表单 -->\n            <ElTabs v-model=\"subTabsName\" class=\"mx-8\">\n                #foreach ($subTable in $subTables)\n                    #set ($index = $foreach.count - 1)\n                    #set ($subClassNameVar = $subClassNameVars.get($index))\n                    #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n                    #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n                  <ElTabPane name=\"$subClassNameVar\" label=\"${subTable.classComment}\">\n                    <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"row?.id\" />\n                  </ElTabPane>\n                #end\n            </ElTabs>\n          </template>\n        #end\n      <template #toolbar-tools>\n        <TableAction\n          :actions=\"[\n            {\n              label: $t('ui.actionTitle.create', ['${table.classComment}']),\n              type: 'primary',\n              icon: ACTION_ICON.ADD,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:create'],\n              onClick: handleCreate,\n            },\n            #if (${table.templateType} == 2)## 树表特有：展开/收缩按钮\n            {\n              label: isExpanded ? '收缩' : '展开',\n              type: 'primary',\n              onClick: handleExpand,\n            },\n            #end\n            {\n              label: $t('ui.actionTitle.export'),\n              type: 'primary',\n              icon: ACTION_ICON.DOWNLOAD,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:export'],\n              onClick: handleExport,\n            },\n            #if ($table.templateType != 2 && $deleteBatchEnable)\n            {\n              label: $t('ui.actionTitle.deleteBatch'),\n              type: 'danger',\n              icon: ACTION_ICON.DELETE,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:delete'],\n              disabled: isEmpty(checkedIds),\n              onClick: handleDeleteBatch,\n            },\n            #end\n          ]\"\n        />\n      </template>\n      <template #actions=\"{ row }\">\n        <TableAction\n          :actions=\"[\n            #if (${table.templateType} == 2)## 树表特有：新增下级\n            {\n              label: '新增下级',\n              type: 'primary',\n              link: true,\n              icon: ACTION_ICON.ADD,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:create'],\n              onClick: handleAppend.bind(null, row),\n            },\n            #end\n            {\n              label: $t('common.edit'),\n              type: 'primary',\n              link: true,\n              icon: ACTION_ICON.EDIT,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:update'],\n              onClick: handleEdit.bind(null, row),\n            },\n            {\n              label: $t('common.delete'),\n              type: 'danger',\n              link: true,\n              icon: ACTION_ICON.DELETE,\n              auth: ['${table.moduleName}:${simpleClassName_strikeCase}:delete'],\n              popConfirm: {\n                title: $t('ui.actionMessage.deleteConfirm', [row.id]),\n                confirm: handleDelete.bind(null, row),\n              },\n            },\n          ]\"\n        />\n      </template>\n    </Grid>\n#if ($table.templateType == 11) ## erp情况\n    <!-- 子表的表单 -->\n    <ElTabs v-model=\"subTabsName\" class=\"mt-2\">\n        #foreach ($subTable in $subTables)\n            #set ($index = $foreach.count - 1)\n            #set ($subClassNameVar = $subClassNameVars.get($index))\n            #set ($subSimpleClassName = $subSimpleClassNames.get($index))\n            #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))\n          <ElTabPane name=\"$subClassNameVar\" label=\"${subTable.classComment}\">\n            <${subSimpleClassName}List :${subJoinColumn_strikeCase}=\"select${simpleClassName}?.id\" />\n          </ElTabPane>\n        #end\n    </ElTabs>\n    </div>\n#end\n  </Page>\n</template>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/modules/form_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\n  import type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\n  import { computed, ref } from 'vue';\n\n  import { useVbenModal } from '@vben/common-ui';\n\n  import { ElMessage } from 'element-plus';\n\n  import { useVbenForm } from '#/adapter/form';\n  import { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';\n  import { $t } from '#/locales';\n\n  import { use${subSimpleClassName}FormSchema } from '../data';\n\n  const emit = defineEmits(['success']);\n  const formData = ref<${apiName}.${subSimpleClassName}>();\n  const getTitle = computed(() => {\n    return formData.value?.id\n        ? $t('ui.actionTitle.edit', ['${subTable.classComment}'])\n        : $t('ui.actionTitle.create', ['${subTable.classComment}']);\n  });\n\n  const [Form, formApi] = useVbenForm({\n    commonConfig: {\n      componentProps: {\n        class: 'w-full',\n      },\n      formItemClass: 'col-span-2',\n      labelWidth: 80,\n    },\n    layout: 'horizontal',\n    schema: use${subSimpleClassName}FormSchema(),\n    showDefaultActions: false\n  });\n\n  const [Modal, modalApi] = useVbenModal({\n    async onConfirm() {\n      const { valid } = await formApi.validate();\n      if (!valid) {\n        return;\n      }\n\n      modalApi.lock();\n      // 提交表单\n      const data = (await formApi.getValues()) as ${apiName}.${subSimpleClassName};\n      data.${subJoinColumn.javaField} = formData.value?.${subJoinColumn.javaField};\n      try {\n        await (formData.value?.id ? update${subSimpleClassName}(data) : create${subSimpleClassName}(data));\n        // 关闭并提示\n        await modalApi.close();\n        emit('success');\n        ElMessage.success($t('ui.actionMessage.operationSuccess'));\n      } finally {\n        modalApi.unlock();\n      }\n    },\n    async onOpenChange(isOpen: boolean) {\n      if (!isOpen) {\n        formData.value = undefined;\n        return;\n      }\n\n      // 加载数据\n      let data = modalApi.getData<${apiName}.${subSimpleClassName}>();\n      if (!data) {\n        return;\n      }\n      if (data.id) {\n        modalApi.lock();\n        try {\n          data = await get${subSimpleClassName}(data.id);\n        } finally {\n          modalApi.unlock();\n        }\n      }\n      // 设置到 values\n      formData.value = data;\n      await formApi.setValues(formData.value);\n    },\n  });\n</script>\n\n<template>\n  <Modal :title=\"getTitle\">\n    <Form class=\"mx-4\" />\n  </Modal>\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/modules/form_sub_inner.vue.vm",
    "content": "## 主表的 normal 和 inner 使用相同的 form 表单\n#parse(\"codegen/vue3_vben5_ele/schema/views/modules/form_sub_normal.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/modules/form_sub_normal.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subClassNameVar = $subClassNameVars.get($subIndex))\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\n  import type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\n  import { computed, ref, h, onMounted,watch,nextTick } from 'vue';\n\n  import { $t } from '#/locales';\n\n#if ($subTable.subJoinMany) ## 一对多\nimport { Plus } from \"@vben/icons\";\nimport { ElButton, ElTabs, ElTabPane, ElCheckbox, ElInput, ElSelect, ElOption, ElRadioGroup, ElCheckboxGroup, ElDatePicker } from 'element-plus';\nimport { ImageUpload, FileUpload } from \"#/components/upload\";\nimport type { OnActionClickParams } from '#/adapter/vxe-table';\nimport { useVbenVxeGrid } from '#/adapter/vxe-table';\nimport { use${subSimpleClassName}GridEditColumns } from '../data';\nimport { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#else\nimport { useVbenForm } from '#/adapter/form';\nimport { use${subSimpleClassName}FormSchema } from '../data';\nimport { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#end\n\nconst props = defineProps<{\n   ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\n\n#if ($subTable.subJoinMany) ## 一对多\n/** 表格操作按钮的回调函数 */\nfunction onActionClick({\n code,\n row,\n}: OnActionClickParams<${apiName}.${subSimpleClassName}>) {\n  switch (code) {\n    case 'delete': {\n      onDelete(row);\n      break;\n    }\n  }\n}\n\nconst [Grid, gridApi] = useVbenVxeGrid({\ngridOptions: {\n  columns: use${subSimpleClassName}GridEditColumns(onActionClick),\n  border: true,\n  showOverflow: true,\n  autoResize: true,\n  keepSource: true,\n  rowConfig: {\n    keyField: 'id',\n  },\n  pagerConfig: {\n    enabled: false,\n  },\n  toolbarConfig: {\n    enabled: false,\n  },\n},\n});\n\n/** 添加${subTable.classComment} */\nconst onAdd = async () => {\n  await gridApi.grid.insertAt({} as ${simpleClassName}Api.${subSimpleClassName}, -1);\n}\n\n/** 删除${subTable.classComment} */\nconst onDelete =  async (row: ${apiName}.${subSimpleClassName}) => {\n  await gridApi.grid.remove(row);\n}\n\n/** 提供获取表格数据的方法供父组件调用 */\ndefineExpose({\n  getData: (): ${apiName}.${subSimpleClassName}[] => {\n    const data = gridApi.grid.getData() as ${apiName}.${subSimpleClassName}[];\n    const removeRecords = gridApi.grid.getRemoveRecords() as ${apiName}.${subSimpleClassName}[];\n    const insertRecords = gridApi.grid.getInsertRecords() as ${apiName}.${subSimpleClassName}[];\n    return data\n        .filter((row) => !removeRecords.some((removed) => removed.id === row.id))\n        .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));\n  },\n});\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n    () => props.${subJoinColumn.javaField},\n    async (val) => {\n      if (!val) {\n        return;\n      }\n      await nextTick();\n      await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));\n    },\n    { immediate: true },\n);\n#else\nconst [Form, formApi] = useVbenForm({\n  commonConfig: {\n    componentProps: {\n      class: 'w-full',\n    },\n    formItemClass: 'col-span-2',\n    labelWidth: 80,\n  },\n  layout: 'horizontal',\n  schema: use${subSimpleClassName}FormSchema(),\n  showDefaultActions: false\n});\n\n/** 暴露出表单校验方法和表单值获取方法 */\ndefineExpose({\n  validate: async () => {\n    const { valid } = await formApi.validate();\n    return valid;\n  },\n  getValues: formApi.getValues,\n});\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n    () => props.${subJoinColumn.javaField},\n    async (val) => {\n      if (!val) {\n        return;\n      }\n      await nextTick();\n      await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));\n    },\n    { immediate: true },\n);\n#end\n</script>\n\n<template>\n#if ($subTable.subJoinMany) ## 一对多\n  <Grid class=\"mx-4\">\n      #foreach($column in $subColumns)\n          #if ($column.createOperation || $column.updateOperation)\n              #set ($javaField = $column.javaField)\n              #if ( $column.id == $subJoinColumn.id) ## 特殊：忽略主子表的 join 字段，不用填写\n              #elseif ($column.htmlType == \"input\" && !$column.primaryKey)## 忽略主键，不用在表单里\n                <template #${javaField}=\"{ row }\">\n                  <el-input v-model=\"row.${javaField}\" />\n                </template>\n              #elseif($column.htmlType == \"imageUpload\")## 图片上传\n                <template #${javaField}=\"{ row }\">\n                  <ImageUpload v-model=\"row.${javaField}\" />\n                </template>\n              #elseif($column.htmlType == \"fileUpload\")## 文件上传\n                <template #${javaField}=\"{ row }\">\n                  <FileUpload v-model=\"row.${javaField}\" />\n                </template>\n              #elseif($column.htmlType == \"select\")## 下拉框\n                <template #${javaField}=\"{ row, column }\">\n                  <el-select v-model=\"row.${javaField}\" class=\"w-full\">\n                    <el-option v-for=\"option in column.params.options\" :key=\"option.value\" :value=\"option.value\" :label=\"option.label\" />\n                  </el-select>\n                </template>\n              #elseif($column.htmlType == \"checkbox\")## 多选框\n                <template #${javaField}=\"{ row, column }\">\n                  <el-checkbox-group v-model=\"row.${javaField}\">\n                    <el-checkbox v-for=\"option in column.params.options\" :key=\"option.value\" :label=\"option.value\">\n                      {{ option.label }}\n                    </el-checkbox>\n                  </el-checkbox-group>\n                </template>\n              #elseif($column.htmlType == \"radio\")## 单选框\n                <template #${javaField}=\"{ row, column }\">\n                  <el-radio-group v-model=\"row.${javaField}\">\n                    <el-radio v-for=\"option in column.params.options\" :key=\"option.value\" :label=\"option.value\">\n                      {{ option.label }}\n                    </el-radio>\n                  </el-radio-group>\n                </template>\n              #elseif($column.htmlType == \"datetime\")## 时间框\n                <template #${javaField}=\"{ row }\">\n                  <el-date-picker\n                      v-model=\"row.${javaField}\"\n                      type=\"datetime\"\n                      value-format=\"x\"\n                      format=\"YYYY-MM-DD HH:mm:ss\"\n                  />\n                </template>\n              #elseif($column.htmlType == \"textarea\" || $column.htmlType == \"editor\")## 文本框\n                <template #${javaField}=\"{ row }\">\n                  <el-input v-model=\"row.${javaField}\" type=\"textarea\" />\n                </template>\n              #end\n          #end\n      #end\n  </Grid>\n  <div class=\"flex justify-center -mt-4\">\n    <el-button :icon=\"Plus\" type=\"primary\" plain @click=\"onAdd\" v-access:code=\"['${subTable.moduleName}:${simpleClassName_strikeCase}:create']\">\n      {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}\n    </el-button>\n  </div>\n#else\n  <Form class=\"mx-4\" />\n#end\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/modules/list_sub_erp.vue.vm",
    "content": "#set ($subTable = $subTables.get($subIndex))##当前表\n#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))\n#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段\n#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($subIndex))\n#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写\n#set ($apiName = \"${table.moduleName.substring(0,1).toUpperCase()}${table.moduleName.substring(1)}${simpleClassName}Api\")\n<script lang=\"ts\" setup>\nimport type { VxeTableGridOptions } from '#/adapter/vxe-table';\nimport type { ${apiName} } from '#/api/${table.moduleName}/${table.businessName}';\n\n#if ($table.templateType == 11) ## erp\nimport ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'\n#end\nimport { useVbenModal } from '@vben/common-ui';\nimport { ElMessage, ElLoading } from 'element-plus';\nimport { ref, computed, nextTick,watch } from 'vue';\nimport { $t } from '#/locales';\nimport { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';\n\n#if ($table.templateType == 11) ## erp\nimport { delete${subSimpleClassName},#if ($deleteBatchEnable) delete${subSimpleClassName}List,#end get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${table.businessName}';\nimport { use${subSimpleClassName}GridFormSchema, use${subSimpleClassName}GridColumns } from '../data';\nimport { isEmpty } from '@vben/utils';\n#else\n#if ($subTable.subJoinMany) ## 一对多\nimport { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#else\nimport { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';\n#end\nimport { use${subSimpleClassName}GridColumns } from '../data';\n#end\n\nconst props = defineProps<{\n  ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}（主表的关联字段）\n}>()\n\n#if ($table.templateType == 11) ## erp\nconst [FormModal, formModalApi] = useVbenModal({\n  connectedComponent: ${subSimpleClassName}Form,\n  destroyOnClose: true,\n});\n\n/** 刷新表格 */\nasync function handleRefresh() {\n#if ($table.templateType == 11) ## erp\n  await gridApi.query();\n#else\n  #if ($subTable.subJoinMany) ## 一对多\n  await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));\n  #else\n  await gridApi.grid.loadData([await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!)]);\n  #end\n#end\n}\n\n/** 创建${subTable.classComment} */\nfunction handleCreate() {\n  if (!props.${subJoinColumn.javaField}){\n    ElMessage.warning(\"请先选择一个${table.classComment}!\")\n    return\n  }\n  formModalApi.setData({${subJoinColumn.javaField}: props.${subJoinColumn.javaField}}).open();\n}\n\n/** 编辑${subTable.classComment} */\nfunction handleEdit(row: ${apiName}.${subSimpleClassName}) {\n  formModalApi.setData(row).open();\n}\n\n/** 删除${subTable.classComment} */\nasync function handleDelete(row: ${apiName}.${subSimpleClassName}) {\n  const loadingInstance = ElLoading.service({\n    text: $t('ui.actionMessage.deleting', [row.id]),\n  });\n  try {\n    await delete${subSimpleClassName}(row.id!);\n    ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.id]));\n    handleRefresh();\n  } finally {\n    loadingInstance.close();\n  }\n}\n\n#if ($deleteBatchEnable)\n/** 批量删除${subTable.classComment} */\nasync function handleDeleteBatch() {\n  const loadingInstance = ElLoading.service({\n    text: $t('ui.actionMessage.deleting'),\n  });\n  try {\n    await delete${subSimpleClassName}List(checkedIds.value);\n    checkedIds.value = [];\n    ElMessage.success($t('ui.actionMessage.deleteSuccess'));\n    handleRefresh();\n  } finally {\n    loadingInstance.close();\n  }\n}\n\nconst checkedIds = ref<number[]>([])\nfunction handleRowCheckboxChange({\n  records,\n}: {\n  records: ${apiName}.${subSimpleClassName}[];\n}) {\n  checkedIds.value = records.map((item) => item.id!);\n}\n#end\n\n#end\nconst [Grid, gridApi] = useVbenVxeGrid({\n#if ($table.templateType == 11)\n  formOptions: {\n    schema: use${subSimpleClassName}GridFormSchema(),\n  },\n#end\n  gridOptions: {\n#if ($table.templateType == 11)\n  columns: use${subSimpleClassName}GridColumns(),\n    proxyConfig: {\n      ajax: {\n        query: async ({ page }, formValues) => {\n          if (!props.${subJoinColumn.javaField}){\n              return []\n          }\n          return await get${subSimpleClassName}Page({\n            pageNo: page.currentPage,\n            pageSize: page.pageSize,\n            ${subJoinColumn.javaField}: props.${subJoinColumn.javaField},\n            ...formValues,\n          });\n        },\n      },\n    },\n  pagerConfig: {\n    enabled: true,\n  },\n  toolbarConfig: {\n    refresh: true,\n    search: true,\n  },\n#else\n  columns: use${subSimpleClassName}GridColumns(),\n  pagerConfig: {\n    nabled: false,\n  },\n  toolbarConfig: {\n    enabled: false,\n  },\n#end\n  height: '600px',\n  rowConfig: {\n    keyField: 'id',\n    isHover: true,\n  },\n  } as VxeTableGridOptions<${apiName}.${subSimpleClassName}>,\n  #if (${table.templateType} == 11 && $deleteBatchEnable)\n  gridEvents:{\n    checkboxAll: handleRowCheckboxChange,\n    checkboxChange: handleRowCheckboxChange,\n  }\n  #end\n});\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.${subJoinColumn.javaField},\n  async (val) => {\n    if (!val) {\n      return;\n    }\n    await nextTick();\n    await handleRefresh()\n  },\n  { immediate: true },\n);\n</script>\n\n<template>\n    #if ($table.templateType == 11) ## erp\n      <FormModal @success=\"handleRefresh\" />\n      <Grid table-title=\"${subTable.classComment}列表\">\n        <template #toolbar-tools>\n          <TableAction\n            :actions=\"[\n              {\n                label: $t('ui.actionTitle.create', ['${table.classComment}']),\n                type: 'primary',\n                icon: ACTION_ICON.ADD,\n                auth: ['${table.moduleName}:${simpleClassName_strikeCase}:create'],\n                onClick: handleCreate,\n              },\n              #if ($table.templateType == 11 && $deleteBatchEnable)\n              {\n                label: $t('ui.actionTitle.deleteBatch'),\n                type: 'danger',\n                icon: ACTION_ICON.DELETE,\n                disabled: isEmpty(checkedIds),\n                auth: ['${table.moduleName}:${simpleClassName_strikeCase}:delete'],\n                onClick: handleDeleteBatch,\n              },\n              #end\n            ]\"\n          />\n        </template>\n        <template #actions=\"{ row }\">\n          <TableAction\n            :actions=\"[\n              {\n                label: $t('common.edit'),\n                type: 'primary',\n                link: true,\n                icon: ACTION_ICON.EDIT,\n                auth: ['${table.moduleName}:${simpleClassName_strikeCase}:update'],\n                onClick: handleEdit.bind(null, row),\n              },\n              {\n                label: $t('common.delete'),\n                type: 'danger',\n                link: true,\n                icon: ACTION_ICON.DELETE,\n                auth: ['${table.moduleName}:${simpleClassName_strikeCase}:delete'],\n                popConfirm: {\n                  title: $t('ui.actionMessage.deleteConfirm', [row.id]),\n                  confirm: handleDelete.bind(null, row),\n                },\n              },\n            ]\"\n          />\n        </template>\n      </Grid>\n    #else\n      <Grid table-title=\"${subTable.classComment}列表\" />\n    #end\n</template>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/modules/list_sub_inner.vue.vm",
    "content": "## 子表的 erp 和 inner 使用相似的 list 列表，差异主要两点：\n## 1）inner 使用 list 不分页，erp 使用 page 分页\n## 2）erp 支持单个子表的新增、修改、删除，inner 不支持\n#parse(\"codegen/vue3_vben5_ele/schema/views/modules/list_sub_erp.vue.vm\")"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.ftp;\n\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.extra.ftp.FtpMode;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * {@link FtpFileClient} 集成测试\n *\n * @author 芋道源码\n */\npublic class FtpFileClientTest {\n\n//    docker run -d \\\n//            -p 2121:21 -p 30000-30009:30000-30009 \\\n//            -e FTP_USER=foo \\\n//            -e FTP_PASS=pass \\\n//            -e PASV_ADDRESS=127.0.0.1 \\\n//            -e PASV_MIN_PORT=30000 \\\n//            -e PASV_MAX_PORT=30009 \\\n//            -v $(pwd)/ftp-data:/home/vsftpd \\\n//    fauria/vsftpd\n\n    @Test\n    @Disabled\n    public void test() {\n        // 创建客户端\n        FtpFileClientConfig config = new FtpFileClientConfig();\n        config.setDomain(\"http://127.0.0.1:48080\");\n        config.setBasePath(\"/home/ftp\");\n        config.setHost(\"127.0.0.1\");\n        config.setPort(2121);\n        config.setUsername(\"foo\");\n        config.setPassword(\"pass\");\n        config.setMode(FtpMode.Passive.name());\n        FtpFileClient client = new FtpFileClient(0L, config);\n        client.init();\n        // 上传文件\n        String path = IdUtil.fastSimpleUUID() + \".jpg\";\n        byte[] content = ResourceUtil.readBytes(\"file/erweima.jpg\");\n        String fullPath = client.upload(content, path, \"image/jpeg\");\n        System.out.println(\"访问地址：\" + fullPath);\n        if (false) {\n            byte[] bytes = client.getContent(path);\n            System.out.println(\"文件内容：\" + bytes);\n        }\n        if (false) {\n            client.delete(path);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.local;\n\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\n\npublic class LocalFileClientTest {\n\n    @Test\n    @Disabled\n    public void test() {\n        // 创建客户端\n        LocalFileClientConfig config = new LocalFileClientConfig();\n        config.setDomain(\"http://127.0.0.1:48080\");\n        config.setBasePath(\"/Users/yunai/file_test\");\n        LocalFileClient client = new LocalFileClient(0L, config);\n        client.init();\n        // 上传文件\n        String path = IdUtil.fastSimpleUUID() + \".jpg\";\n        byte[] content = ResourceUtil.readBytes(\"file/erweima.jpg\");\n        String fullPath = client.upload(content, path, \"image/jpeg\");\n        System.out.println(\"访问地址：\" + fullPath);\n        client.delete(path);\n    }\n\n    @Test\n    @Disabled\n    public void testGetContent_notFound() {\n        // 创建客户端\n        LocalFileClientConfig config = new LocalFileClientConfig();\n        config.setDomain(\"http://127.0.0.1:48080\");\n        config.setBasePath(\"/Users/yunai/file_test\");\n        LocalFileClient client = new LocalFileClient(0L, config);\n        client.init();\n        // 上传文件\n        byte[] content = client.getContent(randomString());\n        System.out.println();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.s3;\n\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.validation.Validation;\n\n@SuppressWarnings(\"resource\")\npublic class S3FileClientTest {\n\n    @Test\n    @Disabled // MinIO，如果要集成测试，可以注释本行\n    public void testMinIO() throws Exception {\n        S3FileClientConfig config = new S3FileClientConfig();\n        // 配置成你自己的\n        config.setAccessKey(\"admin\");\n        config.setAccessSecret(\"password\");\n        config.setBucket(\"yudaoyuanma\");\n        config.setDomain(null);\n        // 默认 9000 endpoint\n        config.setEndpoint(\"http://127.0.0.1:9000\");\n\n        // 执行上传\n        testExecuteUpload(config);\n    }\n\n    @Test\n    @Disabled // 阿里云 OSS，如果要集成测试，可以注释本行\n    public void testAliyun() throws Exception {\n        S3FileClientConfig config = new S3FileClientConfig();\n        // 配置成你自己的\n        config.setAccessKey(System.getenv(\"ALIYUN_ACCESS_KEY\"));\n        config.setAccessSecret(System.getenv(\"ALIYUN_SECRET_KEY\"));\n        config.setBucket(\"yunai-aoteman\");\n        config.setDomain(null); // 如果有自定义域名，则可以设置。http://ali-oss.iocoder.cn\n        // 默认北京的 endpoint\n        config.setEndpoint(\"oss-cn-beijing.aliyuncs.com\");\n\n        // 执行上传\n        testExecuteUpload(config);\n    }\n\n    @Test\n    @Disabled // 腾讯云 COS，如果要集成测试，可以注释本行\n    public void testQCloud() throws Exception {\n        S3FileClientConfig config = new S3FileClientConfig();\n        // 配置成你自己的\n        config.setAccessKey(System.getenv(\"QCLOUD_ACCESS_KEY\"));\n        config.setAccessSecret(System.getenv(\"QCLOUD_SECRET_KEY\"));\n        config.setBucket(\"aoteman-1255880240\");\n        config.setDomain(null); // 如果有自定义域名，则可以设置。http://tengxun-oss.iocoder.cn\n        // 默认上海的 endpoint\n        config.setEndpoint(\"cos.ap-shanghai.myqcloud.com\");\n\n        // 执行上传\n        testExecuteUpload(config);\n    }\n\n    @Test\n    @Disabled // 七牛云存储，如果要集成测试，可以注释本行\n    public void testQiniu() throws Exception {\n        S3FileClientConfig config = new S3FileClientConfig();\n        // 配置成你自己的\n//        config.setAccessKey(System.getenv(\"QINIU_ACCESS_KEY\"));\n//        config.setAccessSecret(System.getenv(\"QINIU_SECRET_KEY\"));\n        config.setAccessKey(\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\");\n        config.setAccessSecret(\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\");\n        config.setBucket(\"ruoyi-vue-pro\");\n        config.setDomain(\"http://test.yudao.iocoder.cn\"); // 如果有自定义域名，则可以设置。http://static.yudao.iocoder.cn\n        config.setEnablePathStyleAccess(false);\n        // 默认上海的 endpoint\n        config.setEndpoint(\"s3-cn-south-1.qiniucs.com\");\n\n        // 执行上传\n        testExecuteUpload(config);\n    }\n\n    @Test\n    @Disabled // 七牛云存储（读私有桶），如果要集成测试，可以注释本行\n    public void testQiniu_privateGet() {\n        S3FileClientConfig config = new S3FileClientConfig();\n        // 配置成你自己的\n//        config.setAccessKey(System.getenv(\"QINIU_ACCESS_KEY\"));\n//        config.setAccessSecret(System.getenv(\"QINIU_SECRET_KEY\"));\n        config.setAccessKey(\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\");\n        config.setAccessSecret(\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\");\n        config.setBucket(\"ruoyi-vue-pro-private\");\n        config.setDomain(\"http://t151glocd.hn-bkt.clouddn.com\"); // 如果有自定义域名，则可以设置。http://static.yudao.iocoder.cn\n        config.setEnablePathStyleAccess(false);\n        // 默认上海的 endpoint\n        config.setEndpoint(\"s3-cn-south-1.qiniucs.com\");\n\n        // 校验配置\n        ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config);\n        // 创建 Client\n        S3FileClient client = new S3FileClient(0L, config);\n        client.init();\n        // 执行生成 URL 签名\n        String path = \"output.png\";\n        String presignedUrl = client.presignGetUrl(path, 300);\n        System.out.println(presignedUrl);\n    }\n\n    @Test\n    @Disabled // 华为云存储，如果要集成测试，可以注释本行\n    public void testHuaweiCloud() throws Exception {\n        S3FileClientConfig config = new S3FileClientConfig();\n        // 配置成你自己的\n//        config.setAccessKey(System.getenv(\"HUAWEI_CLOUD_ACCESS_KEY\"));\n//        config.setAccessSecret(System.getenv(\"HUAWEI_CLOUD_SECRET_KEY\"));\n        config.setBucket(\"yudao\");\n        config.setDomain(null); // 如果有自定义域名，则可以设置。\n        // 默认上海的 endpoint\n        config.setEndpoint(\"obs.cn-east-3.myhuaweicloud.com\");\n\n        // 执行上传\n        testExecuteUpload(config);\n    }\n\n    private void testExecuteUpload(S3FileClientConfig config) {\n        // 校验配置\n        ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config);\n        // 创建 Client\n        S3FileClient client = new S3FileClient(0L, config);\n        client.init();\n        // 上传文件\n        String path = IdUtil.fastSimpleUUID() + \".jpg\";\n        byte[] content = ResourceUtil.readBytes(\"file/erweima.jpg\");\n        String fullPath = client.upload(content, path, \"image/jpeg\");\n        System.out.println(\"访问地址：\" + fullPath);\n        // 读取文件\n        if (true) {\n            byte[] bytes = client.getContent(path);\n            System.out.println(\"文件内容：\" + bytes.length);\n        }\n        // 删除文件\n        if (false) {\n            client.delete(path);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java",
    "content": "package cn.iocoder.yudao.module.infra.framework.file.core.sftp;\n\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * {@link SftpFileClient} 集成测试\n *\n * @author 芋道源码\n */\npublic class SftpFileClientTest {\n\n//    docker run -p 2222:22 -d \\\n//            -v $(pwd)/sftp-data:/home/foo/upload \\\n//    atmoz/sftp \\\n//    foo:pass:1001\n\n    @Test\n    @Disabled\n    public void test() {\n        // 创建客户端\n        SftpFileClientConfig config = new SftpFileClientConfig();\n        config.setDomain(\"http://127.0.0.1:48080\");\n        config.setBasePath(\"/upload\"); // 注意，这个是相对路径，不是实际 linux 上的路径！！！\n        config.setHost(\"127.0.0.1\");\n        config.setPort(2222);\n        config.setUsername(\"foo\");\n        config.setPassword(\"pass\");\n        SftpFileClient client = new SftpFileClient(0L, config);\n        client.init();\n        // 上传文件\n        String path = IdUtil.fastSimpleUUID() + \".jpg\";\n        byte[] content = ResourceUtil.readBytes(\"file/erweima.jpg\");\n        String fullPath = client.upload(content, path, \"image/jpeg\");\n        System.out.println(\"访问地址：\" + fullPath);\n        if (false) {\n            byte[] bytes = client.getContent(path);\n            System.out.println(\"文件内容：\" + bytes);\n        }\n        if (false) {\n            client.delete(path);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.baomidou.mybatisplus.generator.query.DefaultQuery;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\n\nimport java.util.List;\n\npublic class DefaultDatabaseQueryTest {\n\n    public static void main(String[] args) {\n//        DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(\"jdbc:oracle:thin:@127.0.0.1:1521:xe\",\n//                \"root\", \"123456\").build();\n        DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(\"jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro\",\n                \"root\", \"123456\").build();\n//        StrategyConfig strategyConfig = new StrategyConfig.Builder().build();\n\n        ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null);\n\n        DefaultQuery query = new DefaultQuery(builder);\n\n        long time = System.currentTimeMillis();\n        List<TableInfo> tableInfos = query.queryTables();\n        for (TableInfo tableInfo : tableInfos) {\n            if (StrUtil.startWithAny(tableInfo.getName().toLowerCase(), \"act_\", \"flw_\", \"qrtz_\")) {\n                continue;\n            }\n            System.out.println(String.format(\"CREATE SEQUENCE %s_seq MINVALUE 1;\", tableInfo.getName()));\n//            System.out.println(String.format(\"DELETE FROM %s WHERE deleted = '1';\", tableInfo.getName()));\n        }\n        System.out.println(tableInfos.size());\n        System.out.println(System.currentTimeMillis() - time);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImplTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.codegen;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnSaveReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;\nimport cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;\nimport cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;\nimport cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;\nimport cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;\nimport cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\n/**\n * {@link CodegenServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(CodegenServiceImpl.class)\npublic class CodegenServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private CodegenServiceImpl codegenService;\n\n    @Resource\n    private CodegenTableMapper codegenTableMapper;\n    @Resource\n    private CodegenColumnMapper codegenColumnMapper;\n\n    @MockBean\n    private DatabaseTableService databaseTableService;\n    @MockBean\n    private DataSourceConfigService dataSourceConfigService;\n\n    @MockBean\n    private CodegenBuilder codegenBuilder;\n    @MockBean\n    private CodegenEngine codegenEngine;\n\n    @MockBean\n    private CodegenProperties codegenProperties;\n\n    @Test\n    public void testCreateCodegenList() {\n        // 准备参数\n        String author = randomString();\n        CodegenCreateListReqVO reqVO = randomPojo(CodegenCreateListReqVO.class,\n                o -> o.setDataSourceConfigId(1L).setTableNames(Collections.singletonList(\"t_yunai\")));\n        // mock 方法（TableInfo）\n        TableInfo tableInfo = mock(TableInfo.class);\n        when(databaseTableService.getTable(eq(1L), eq(\"t_yunai\")))\n                .thenReturn(tableInfo);\n        when(tableInfo.getComment()).thenReturn(\"芋艿\");\n        // mock 方法（TableInfo fields）\n        TableField field01 = mock(TableField.class);\n        when(field01.getComment()).thenReturn(\"主键\");\n        TableField field02 = mock(TableField.class);\n        when(field02.getComment()).thenReturn(\"名字\");\n        List<TableField> fields = Arrays.asList(field01, field02);\n        when(tableInfo.getFields()).thenReturn(fields);\n        // mock 方法（CodegenTableDO）\n        CodegenTableDO table = randomPojo(CodegenTableDO.class);\n        when(codegenBuilder.buildTable(same(tableInfo))).thenReturn(table);\n        // mock 方法（CodegenColumnDO）\n        List<CodegenColumnDO> columns = randomPojoList(CodegenColumnDO.class);\n        when(codegenBuilder.buildColumns(eq(table.getId()), same(fields)))\n                .thenReturn(columns);\n        // mock 方法（CodegenProperties）\n        when(codegenProperties.getFrontType()).thenReturn(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType());\n\n        // 调用\n        List<Long> result = codegenService.createCodegenList(author, reqVO);\n        // 断言\n        assertEquals(1, result.size());\n        // 断言（CodegenTableDO）\n        CodegenTableDO dbTable = codegenTableMapper.selectList().get(0);\n        assertPojoEquals(table, dbTable);\n        assertEquals(1L, dbTable.getDataSourceConfigId());\n        assertEquals(CodegenSceneEnum.ADMIN.getScene(), dbTable.getScene());\n        assertEquals(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), dbTable.getFrontType());\n        assertEquals(author, dbTable.getAuthor());\n        // 断言（CodegenColumnDO）\n        List<CodegenColumnDO> dbColumns = codegenColumnMapper.selectList();\n        assertEquals(columns.size(), dbColumns.size());\n        assertTrue(dbColumns.get(0).getPrimaryKey());\n        for (int i = 0; i < dbColumns.size(); i++) {\n            assertPojoEquals(columns.get(i), dbColumns.get(i));\n        }\n    }\n\n    @Test\n    public void testValidateTableInfo() {\n        // 情况一\n        assertServiceException(() -> codegenService.validateTableInfo(null),\n                CODEGEN_IMPORT_TABLE_NULL);\n        // 情况二\n        TableInfo tableInfo = mock(TableInfo.class);\n        assertServiceException(() -> codegenService.validateTableInfo(tableInfo),\n                CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL);\n        // 情况三\n        when(tableInfo.getComment()).thenReturn(\"芋艿\");\n        assertServiceException(() -> codegenService.validateTableInfo(tableInfo),\n                CODEGEN_IMPORT_COLUMNS_NULL);\n        // 情况四\n        TableField field = mock(TableField.class);\n        when(field.getName()).thenReturn(\"name\");\n        when(tableInfo.getFields()).thenReturn(Collections.singletonList(field));\n        assertServiceException(() -> codegenService.validateTableInfo(tableInfo),\n                CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL, field.getName());\n    }\n\n    @Test\n    public void testUpdateCodegen_notExists() {\n        // 准备参数\n        CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class);\n        // mock 方法\n\n        // 调用，并断言\n        assertServiceException(() -> codegenService.updateCodegen(updateReqVO),\n                CODEGEN_TABLE_NOT_EXISTS);\n    }\n\n    @Test\n    public void testUpdateCodegen_sub_masterNotExists() {\n        // mock 数据\n        CodegenTableDO table = randomPojo(CodegenTableDO.class,\n                o -> o.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())\n                        .setScene(CodegenSceneEnum.ADMIN.getScene()));\n        codegenTableMapper.insert(table);\n        // 准备参数\n        CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class,\n                o -> o.getTable().setId(table.getId())\n                        .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()));\n\n        // 调用，并断言\n        assertServiceException(() -> codegenService.updateCodegen(updateReqVO),\n                CODEGEN_MASTER_TABLE_NOT_EXISTS, updateReqVO.getTable().getMasterTableId());\n    }\n\n    @Test\n    public void testUpdateCodegen_sub_columnNotExists() {\n        // mock 数据\n        CodegenTableDO subTable = randomPojo(CodegenTableDO.class,\n                o -> o.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())\n                        .setScene(CodegenSceneEnum.ADMIN.getScene()));\n        codegenTableMapper.insert(subTable);\n        // mock 数据（master）\n        CodegenTableDO masterTable = randomPojo(CodegenTableDO.class,\n                o -> o.setTemplateType(CodegenTemplateTypeEnum.MASTER_ERP.getType())\n                        .setScene(CodegenSceneEnum.ADMIN.getScene()));\n        codegenTableMapper.insert(masterTable);\n        // 准备参数\n        CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class,\n                o -> o.getTable().setId(subTable.getId())\n                        .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())\n                        .setMasterTableId(masterTable.getId()));\n\n        // 调用，并断言\n        assertServiceException(() -> codegenService.updateCodegen(updateReqVO),\n                CODEGEN_SUB_COLUMN_NOT_EXISTS, updateReqVO.getTable().getSubJoinColumnId());\n    }\n\n    @Test\n    public void testUpdateCodegen_success() {\n        // mock 数据\n        CodegenTableDO table = randomPojo(CodegenTableDO.class,\n                o -> o.setTemplateType(CodegenTemplateTypeEnum.ONE.getType())\n                        .setScene(CodegenSceneEnum.ADMIN.getScene()));\n        codegenTableMapper.insert(table);\n        CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));\n        codegenColumnMapper.insert(column01);\n        CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));\n        codegenColumnMapper.insert(column02);\n        // 准备参数\n        CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class,\n                o -> o.getTable().setId(table.getId())\n                        .setTemplateType(CodegenTemplateTypeEnum.ONE.getType())\n                        .setScene(CodegenSceneEnum.ADMIN.getScene()));\n        CodegenColumnSaveReqVO columnVO01 = randomPojo(CodegenColumnSaveReqVO.class,\n                o -> o.setId(column01.getId()).setTableId(table.getId()));\n        CodegenColumnSaveReqVO columnVO02 = randomPojo(CodegenColumnSaveReqVO.class,\n                o -> o.setId(column02.getId()).setTableId(table.getId()));\n        updateReqVO.setColumns(Arrays.asList(columnVO01, columnVO02));\n\n        // 调用\n        codegenService.updateCodegen(updateReqVO);\n        // 断言\n        CodegenTableDO dbTable = codegenTableMapper.selectById(table.getId());\n        assertPojoEquals(updateReqVO.getTable(), dbTable);\n        List<CodegenColumnDO> dbColumns = codegenColumnMapper.selectList();\n        assertEquals(2, dbColumns.size());\n        assertPojoEquals(columnVO01, dbColumns.get(0));\n        assertPojoEquals(columnVO02, dbColumns.get(1));\n    }\n\n    @Test\n    public void testSyncCodegenFromDB() {\n        // mock 数据（CodegenTableDO）\n        CodegenTableDO table = randomPojo(CodegenTableDO.class, o -> o.setTableName(\"t_yunai\")\n                .setDataSourceConfigId(1L).setScene(CodegenSceneEnum.ADMIN.getScene()));\n        codegenTableMapper.insert(table);\n        CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())\n                .setColumnName(\"id\").setPrimaryKey(true).setOrdinalPosition(0));\n        codegenColumnMapper.insert(column01);\n        CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())\n                .setColumnName(\"name\").setOrdinalPosition(1));\n        codegenColumnMapper.insert(column02);\n        // 准备参数\n        Long tableId = table.getId();\n        // mock 方法（TableInfo）\n        TableInfo tableInfo = mock(TableInfo.class);\n        when(databaseTableService.getTable(eq(1L), eq(\"t_yunai\")))\n                .thenReturn(tableInfo);\n        when(tableInfo.getComment()).thenReturn(\"芋艿\");\n        // mock 方法（TableInfo fields）\n        TableField field01 = mock(TableField.class);\n        when(field01.getComment()).thenReturn(\"主键\");\n        TableField field03 = mock(TableField.class);\n        when(field03.getComment()).thenReturn(\"分类\");\n        List<TableField> fields = Arrays.asList(field01, field03);\n        when(tableInfo.getFields()).thenReturn(fields);\n        when(databaseTableService.getTable(eq(1L), eq(\"t_yunai\")))\n                .thenReturn(tableInfo);\n        // mock 方法（CodegenTableDO）\n        List<CodegenColumnDO> newColumns = randomPojoList(CodegenColumnDO.class, 2);\n        when(codegenBuilder.buildColumns(eq(table.getId()), argThat(tableFields -> {\n            assertEquals(2, tableFields.size());\n            assertSame(tableInfo.getFields(), tableFields);\n            return true;\n        }))).thenReturn(newColumns);\n\n        // 调用\n        codegenService.syncCodegenFromDB(tableId);\n        // 断言\n        List<CodegenColumnDO> dbColumns = codegenColumnMapper.selectList();\n        assertEquals(newColumns.size(), dbColumns.size());\n        assertPojoEquals(newColumns.get(0), dbColumns.get(0));\n        assertPojoEquals(newColumns.get(1), dbColumns.get(1));\n    }\n\n    @Test\n    public void testDeleteCodegen_notExists() {\n        assertServiceException(() -> codegenService.deleteCodegen(randomLongId()),\n                CODEGEN_TABLE_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteCodegen_success() {\n        // mock 数据\n        CodegenTableDO table = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()));\n        codegenTableMapper.insert(table);\n        CodegenColumnDO column = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));\n        codegenColumnMapper.insert(column);\n        // 准备参数\n        Long tableId = table.getId();\n\n        // 调用\n        codegenService.deleteCodegen(tableId);\n        // 断言\n        assertNull(codegenTableMapper.selectById(tableId));\n        assertEquals(0, codegenColumnMapper.selectList().size());\n    }\n\n    @Test\n    public void testGetCodegenTableList() {\n        // mock 数据\n        CodegenTableDO table01 = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()));\n        codegenTableMapper.insert(table01);\n        CodegenTableDO table02 = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()));\n        codegenTableMapper.insert(table02);\n        // 准备参数\n        Long dataSourceConfigId = table01.getDataSourceConfigId();\n\n        // 调用\n        List<CodegenTableDO> result = codegenService.getCodegenTableList(dataSourceConfigId);\n        // 断言\n        assertEquals(1, result.size());\n        assertPojoEquals(table01, result.get(0));\n    }\n\n    @Test\n    public void testGetCodegenTablePage() {\n        // mock 数据\n        CodegenTableDO tableDO = randomPojo(CodegenTableDO.class, o -> {\n            o.setTableName(\"t_yunai\");\n            o.setTableComment(\"芋艿\");\n            o.setClassName(\"SystemYunai\");\n            o.setCreateTime(buildTime(2021, 3, 10));\n        }).setScene(CodegenSceneEnum.ADMIN.getScene());\n        codegenTableMapper.insert(tableDO);\n        // 测试 tableName 不匹配\n        codegenTableMapper.insert(cloneIgnoreId(tableDO, o -> o.setTableName(randomString())));\n        // 测试 tableComment 不匹配\n        codegenTableMapper.insert(cloneIgnoreId(tableDO, o -> o.setTableComment(randomString())));\n        // 测试 className 不匹配\n        codegenTableMapper.insert(cloneIgnoreId(tableDO, o -> o.setClassName(randomString())));\n        // 测试 createTime 不匹配\n        codegenTableMapper.insert(cloneIgnoreId(tableDO, logDO -> logDO.setCreateTime(buildTime(2021, 4, 10))));\n        // 准备参数\n        CodegenTablePageReqVO reqVO = new CodegenTablePageReqVO();\n        reqVO.setTableName(\"yunai\");\n        reqVO.setTableComment(\"芋\");\n        reqVO.setClassName(\"Yunai\");\n        reqVO.setCreateTime(buildBetweenTime(2021, 3, 1, 2021, 3, 31));\n\n        // 调用\n        PageResult<CodegenTableDO> pageResult = codegenService.getCodegenTablePage(reqVO);\n        // 断言，只查到了一条符合条件的\n        assertEquals(1, pageResult.getTotal());\n        assertEquals(1, pageResult.getList().size());\n        assertPojoEquals(tableDO, pageResult.getList().get(0));\n    }\n\n    @Test\n    public void testGetCodegenTable() {\n        // mock 数据\n        CodegenTableDO tableDO = randomPojo(CodegenTableDO.class, o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()));\n        codegenTableMapper.insert(tableDO);\n        // 准备参数\n        Long id = tableDO.getId();\n\n        // 调用\n        CodegenTableDO result = codegenService.getCodegenTable(id);\n        // 断言\n        assertPojoEquals(tableDO, result);\n    }\n\n    @Test\n    public void testGetCodegenColumnListByTableId() {\n        // mock 数据\n        CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class);\n        codegenColumnMapper.insert(column01);\n        CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class);\n        codegenColumnMapper.insert(column02);\n        // 准备参数\n        Long tableId = column01.getTableId();\n\n        // 调用\n        List<CodegenColumnDO> result = codegenService.getCodegenColumnListByTableId(tableId);\n        // 断言\n        assertEquals(1, result.size());\n        assertPojoEquals(column01, result.get(0));\n    }\n\n    @Test\n    public void testGenerationCodes_tableNotExists() {\n        assertServiceException(() -> codegenService.generationCodes(randomLongId()),\n                CODEGEN_TABLE_NOT_EXISTS);\n    }\n\n    @Test\n    public void testGenerationCodes_columnNotExists() {\n        // mock 数据（CodegenTableDO）\n        CodegenTableDO table = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())\n                        .setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType()));\n        codegenTableMapper.insert(table);\n        // 准备参数\n        Long tableId = table.getId();\n\n        // 调用，并断言\n        assertServiceException(() -> codegenService.generationCodes(tableId),\n                CODEGEN_COLUMN_NOT_EXISTS);\n    }\n\n    @Test\n    public void testGenerationCodes_sub_tableNotExists() {\n        // mock 数据（CodegenTableDO）\n        CodegenTableDO table = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())\n                        .setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType()));\n        codegenTableMapper.insert(table);\n        // mock 数据（CodegenColumnDO）\n        CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));\n        codegenColumnMapper.insert(column01);\n        // 准备参数\n        Long tableId = table.getId();\n\n        // 调用，并断言\n        assertServiceException(() -> codegenService.generationCodes(tableId),\n                CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE);\n    }\n\n    @Test\n    public void testGenerationCodes_sub_columnNotExists() {\n        // mock 数据（CodegenTableDO）\n        CodegenTableDO table = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())\n                        .setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType()));\n        codegenTableMapper.insert(table);\n        // mock 数据（CodegenColumnDO）\n        CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));\n        codegenColumnMapper.insert(column01);\n        // mock 数据（sub CodegenTableDO）\n        CodegenTableDO subTable = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())\n                        .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())\n                        .setMasterTableId(table.getId()));\n        codegenTableMapper.insert(subTable);\n        // 准备参数\n        Long tableId = table.getId();\n\n        // 调用，并断言\n        assertServiceException(() -> codegenService.generationCodes(tableId),\n                CODEGEN_SUB_COLUMN_NOT_EXISTS, subTable.getId());\n    }\n\n    @Test\n    public void testGenerationCodes_one_success() {\n        // mock 数据（CodegenTableDO）\n        CodegenTableDO table = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())\n                        .setTemplateType(CodegenTemplateTypeEnum.ONE.getType()));\n        codegenTableMapper.insert(table);\n        // mock 数据（CodegenColumnDO）\n        CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())\n                .setOrdinalPosition(1));\n        codegenColumnMapper.insert(column01);\n        CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())\n                .setOrdinalPosition(2));\n        codegenColumnMapper.insert(column02);\n        // mock 数据（DataSourceConfigDO）\n        when(dataSourceConfigService.getDataSourceConfig(eq(table.getDataSourceConfigId())))\n                .thenReturn(randomPojo(DataSourceConfigDO.class, o -> o.setUrl(\"jdbc:mysql://\")));\n        // mock 执行生成\n        Map<String, String> codes = MapUtil.of(randomString(), randomString());\n        when(codegenEngine.execute(eq(DbType.MYSQL), eq(table), argThat(columns -> {\n            assertEquals(2, columns.size());\n            assertEquals(column01, columns.get(0));\n            assertEquals(column02, columns.get(1));\n            return true;\n        }), isNull(), isNull())).thenReturn(codes);\n        // 准备参数\n        Long tableId = table.getId();\n\n        // 调用\n        Map<String, String> result = codegenService.generationCodes(tableId);\n        // 断言\n        assertSame(codes, result);\n    }\n\n    @Test\n    public void testGenerationCodes_master_success() {\n        // mock 数据（CodegenTableDO）\n        CodegenTableDO table = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())\n                        .setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType()));\n        codegenTableMapper.insert(table);\n        // mock 数据（CodegenColumnDO）\n        CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())\n                .setOrdinalPosition(1));\n        codegenColumnMapper.insert(column01);\n        CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())\n                .setOrdinalPosition(2));\n        codegenColumnMapper.insert(column02);\n        // mock 数据（sub CodegenTableDO）\n        CodegenTableDO subTable = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())\n                        .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())\n                        .setMasterTableId(table.getId())\n                        .setSubJoinColumnId(1024L));\n        codegenTableMapper.insert(subTable);\n        // mock 数据（sub CodegenColumnDO）\n        CodegenColumnDO subColumn01 = randomPojo(CodegenColumnDO.class, o -> o.setId(1024L).setTableId(subTable.getId()));\n        codegenColumnMapper.insert(subColumn01);\n        // mock 数据（DataSourceConfigDO）\n        when(dataSourceConfigService.getDataSourceConfig(eq(table.getDataSourceConfigId())))\n                .thenReturn(randomPojo(DataSourceConfigDO.class, o -> o.setUrl(\"jdbc:mysql://\")));\n        // mock 执行生成\n        Map<String, String> codes = MapUtil.of(randomString(), randomString());\n        when(codegenEngine.execute(eq(DbType.MYSQL), eq(table), argThat(columns -> {\n            assertEquals(2, columns.size());\n            assertEquals(column01, columns.get(0));\n            assertEquals(column02, columns.get(1));\n            return true;\n        }), argThat(tables -> {\n            assertEquals(1, tables.size());\n            assertPojoEquals(subTable, tables.get(0));\n            return true;\n        }), argThat(columns -> {\n            assertEquals(1, columns.size());\n            assertPojoEquals(subColumn01, columns.size());\n            return true;\n        }))).thenReturn(codes);\n        // 准备参数\n        Long tableId = table.getId();\n\n        // 调用\n        Map<String, String> result = codegenService.generationCodes(tableId);\n        // 断言\n        assertSame(codes, result);\n    }\n\n    @Test\n    public void testGetDatabaseTableList() {\n        // 准备参数\n        Long dataSourceConfigId = randomLongId();\n        String name = randomString();\n        String comment = randomString();\n        // mock 方法\n        TableInfo tableInfo01 = mock(TableInfo.class);\n        when(tableInfo01.getName()).thenReturn(\"t_yunai\");\n        when(tableInfo01.getComment()).thenReturn(\"芋艿\");\n        TableInfo tableInfo02 = mock(TableInfo.class);\n        when(tableInfo02.getName()).thenReturn(\"t_yunai_02\");\n        when(tableInfo02.getComment()).thenReturn(\"芋艿_02\");\n        when(databaseTableService.getTableList(eq(dataSourceConfigId), eq(name), eq(comment)))\n                .thenReturn(ListUtil.toList(tableInfo01, tableInfo02));\n        // mock 数据\n        CodegenTableDO tableDO = randomPojo(CodegenTableDO.class,\n                o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())\n                        .setTableName(\"t_yunai_02\")\n                        .setDataSourceConfigId(dataSourceConfigId));\n        codegenTableMapper.insert(tableDO);\n\n        // 调用\n        List<DatabaseTableRespVO> result = codegenService.getDatabaseTableList(dataSourceConfigId, name, comment);\n        // 断言\n        assertEquals(1, result.size());\n        assertEquals(\"t_yunai\", result.get(0).getName());\n        assertEquals(\"芋艿\", result.get(0).getComment());\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.codegen.inner;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class CodegenBuilderTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private CodegenBuilder codegenBuilder;\n\n    @Test\n    public void testBuildTable() {\n        // 准备参数\n        TableInfo tableInfo = mock(TableInfo.class);\n        // mock 方法\n        when(tableInfo.getName()).thenReturn(\"system_user\");\n        when(tableInfo.getComment()).thenReturn(\"用户\");\n\n        // 调用\n        CodegenTableDO table = codegenBuilder.buildTable(tableInfo);\n        // 断言\n        assertEquals(\"system_user\", table.getTableName());\n        assertEquals(\"用户\", table.getTableComment());\n        assertEquals(\"system\", table.getModuleName());\n        assertEquals(\"user\", table.getBusinessName());\n        assertEquals(\"User\", table.getClassName());\n        assertEquals(\"用户\", table.getClassComment());\n    }\n\n    @Test\n    public void testBuildColumns() {\n        // 准备参数\n        Long tableId = randomLongId();\n        TableField tableField = mock(TableField.class);\n        List<TableField> tableFields = Collections.singletonList(tableField);\n        // mock 方法\n        TableField.MetaInfo metaInfo = mock(TableField.MetaInfo.class);\n        when(tableField.getMetaInfo()).thenReturn(metaInfo);\n        when(metaInfo.getJdbcType()).thenReturn(JdbcType.BIGINT);\n        when(tableField.getComment()).thenReturn(\"编号\");\n        when(tableField.isKeyFlag()).thenReturn(true);\n        IColumnType columnType = mock(IColumnType.class);\n        when(tableField.getColumnType()).thenReturn(columnType);\n        when(columnType.getType()).thenReturn(\"Long\");\n        when(tableField.getName()).thenReturn(\"id2\");\n        when(tableField.getPropertyName()).thenReturn(\"id\");\n\n        // 调用\n        List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, tableFields);\n        // 断言\n        assertEquals(1, columns.size());\n        CodegenColumnDO column = columns.get(0);\n        assertEquals(tableId, column.getTableId());\n        assertEquals(\"id2\", column.getColumnName());\n        assertEquals(\"BIGINT\", column.getDataType());\n        assertEquals(\"编号\", column.getColumnComment());\n        assertFalse(column.getNullable());\n        assertTrue(column.getPrimaryKey());\n        assertEquals(1, column.getOrdinalPosition());\n        assertEquals(\"Long\", column.getJavaType());\n        assertEquals(\"id\", column.getJavaField());\n        assertNull(column.getDictType());\n        assertNotNull(column.getExample());\n        assertFalse(column.getCreateOperation());\n        assertTrue(column.getUpdateOperation());\n        assertFalse(column.getListOperation());\n        assertEquals(\"=\", column.getListOperationCondition());\n        assertTrue(column.getListOperationResult());\n        assertEquals(\"input\", column.getHtmlType());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.codegen.inner;\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.core.util.ZipUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.mockito.InjectMocks;\nimport org.mockito.Spy;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * {@link CodegenEngine} 的单元测试抽象基类\n *\n * @author 芋道源码\n */\npublic abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {\n\n    /**\n     * 测试文件资源目录\n     */\n    private String resourcesPath = \"\";\n\n    @InjectMocks\n    protected CodegenEngine codegenEngine;\n\n    @Spy\n    protected CodegenProperties codegenProperties = new CodegenProperties()\n            .setBasePackage(\"cn.iocoder.yudao\");\n\n    @BeforeEach\n    public void setUp() {\n        codegenEngine.setJakartaEnable(true); // 强制使用 jakarta，保证单测可以基于 jakarta 断言\n        codegenEngine.initGlobalBindingMap();\n        // 单测强制使用\n        // 获取测试文件 resources 路径\n        String absolutePath = FileUtil.getAbsolutePath(\"application-unit-test.yaml\");\n        // 系统不一样生成的文件也有差异，那就各自生成各自的\n        resourcesPath = absolutePath.split(\"/target\")[0] + \"/src/test/resources/codegen/\";\n    }\n\n    protected static CodegenTableDO getTable(String name) {\n        String content = ResourceUtil.readUtf8Str(\"codegen/table/\" + name + \".json\");\n        return JsonUtils.parseObject(content, \"table\", CodegenTableDO.class);\n    }\n\n    protected static List<CodegenColumnDO> getColumnList(String name) {\n        String content = ResourceUtil.readUtf8Str(\"codegen/table/\" + name + \".json\");\n        List<CodegenColumnDO> list = JsonUtils.parseArray(content, \"columns\", CodegenColumnDO.class);\n        list.forEach(column -> {\n            if (column.getNullable() == null) {\n                column.setNullable(false);\n            }\n            if (column.getCreateOperation() == null) {\n                column.setCreateOperation(false);\n            }\n            if (column.getUpdateOperation() == null) {\n                column.setUpdateOperation(false);\n            }\n            if (column.getListOperation() == null) {\n                column.setListOperation(false);\n            }\n            if (column.getListOperationResult() == null) {\n                column.setListOperationResult(false);\n            }\n        });\n        return list;\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    protected static void assertResult(Map<String, String> result, String path) {\n        String assertContent = ResourceUtil.readUtf8Str(\"codegen/\" + path + \"/assert.json\");\n        List<HashMap> asserts = JsonUtils.parseArray(assertContent, HashMap.class);\n        assertEquals(asserts.size(), result.size());\n        // 校验每个文件\n        asserts.forEach(assertMap -> {\n            String contentPath = (String) assertMap.get(\"contentPath\");\n            String filePath = (String) assertMap.get(\"filePath\");\n            String content = ResourceUtil.readUtf8Str(\"codegen/\" + path + \"/\" + contentPath);\n            assertEquals(content, result.get(filePath), filePath + \"：不匹配\");\n        });\n    }\n\n    // ==================== 调试专用 ====================\n\n    /**\n     * 【调试使用】将生成的代码，写入到文件\n     *\n     * @param result 生成的代码\n     * @param path   写入文件的路径\n     */\n    protected void writeFile(Map<String, String> result, String path) {\n        // 生成压缩包\n        String[] paths = result.keySet().toArray(new String[0]);\n        ByteArrayInputStream[] ins = result.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);\n        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n        ZipUtil.zip(outputStream, paths, ins);\n        // 写入文件\n        FileUtil.writeBytes(outputStream.toByteArray(), path);\n    }\n\n    /**\n     * 【调试使用】将生成的结果，写入到文件\n     *\n     * @param result   生成的代码\n     * @param basePath 写入文件的路径（绝对路径）\n     */\n    protected void writeResult(Map<String, String> result, String basePath) {\n        // 写入文件内容\n        List<Map<String, String>> asserts = new ArrayList<>();\n        result.forEach((filePath, fileContent) -> {\n            String lastFilePath = StrUtil.subAfter(filePath, '/', true);\n            String contentPath = StrUtil.subAfter(lastFilePath, '.', true)\n                    + '/' + StrUtil.subBefore(lastFilePath, '.', true);\n            asserts.add(MapUtil.<String, String>builder().put(\"filePath\", filePath)\n                    .put(\"contentPath\", contentPath).build());\n            FileUtil.writeUtf8String(fileContent, basePath + \"/\" + contentPath);\n        });\n        // 写入 assert.json 文件\n        FileUtil.writeUtf8String(JsonUtils.toJsonPrettyString(asserts), basePath + \"/assert.json\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java",
    "content": "package cn.iocoder.yudao.module.infra.service.codegen.inner;\n\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * {@link CodegenEngine} 的 Vue2 + Element UI 单元测试\n *\n * @author 芋道源码\n */\n@Disabled\npublic class CodegenEngineVue2Test extends CodegenEngineAbstractTest {\n\n    @Test\n    public void testExecute_vue2_one() {\n        // 准备参数\n        CodegenTableDO table = getTable(\"student\")\n                .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType())\n                .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());\n        List<CodegenColumnDO> columns = getColumnList(\"student\");\n\n        // 调用\n        Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);\n        // 生成测试文件\n        //writeResult(result, resourcesPath + \"/vue2_one\");\n        // 断言\n        assertResult(result, \"/vue2_one\");\n    }\n\n    @Test\n    public void testExecute_vue2_tree() {\n        // 准备参数\n        CodegenTableDO table = getTable(\"category\")\n                .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType())\n                .setTemplateType(CodegenTemplateTypeEnum.TREE.getType());\n        List<CodegenColumnDO> columns = getColumnList(\"category\");\n\n        // 调用\n        Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);\n        // 生成测试文件\n        //writeResult(result, resourcesPath + \"/vue2_tree\");\n        // 断言\n        assertResult(result, \"/vue2_tree\");\n//        writeFile(result, \"/Users/yunai/test/demo66.zip\");\n    }\n\n    @Test\n    public void testExecute_vue2_master_normal() {\n        testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_NORMAL, \"/vue2_master_normal\");\n    }\n\n    @Test\n    public void testExecute_vue2_master_erp() {\n        testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_ERP, \"/vue2_master_erp\");\n    }\n\n    @Test\n    public void testExecute_vue2_master_inner() {\n        testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_INNER, \"/vue2_master_inner\");\n    }\n\n    private void testExecute_vue2_master(CodegenTemplateTypeEnum templateType,\n                                         String path) {\n        // 准备参数\n        CodegenTableDO table = getTable(\"student\")\n                .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType())\n                .setTemplateType(templateType.getType());\n        List<CodegenColumnDO> columns = getColumnList(\"student\");\n        // 准备参数（子表）\n        CodegenTableDO contactTable = getTable(\"contact\")\n                .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())\n                .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType())\n                .setSubJoinColumnId(100L).setSubJoinMany(true);\n        List<CodegenColumnDO> contactColumns = getColumnList(\"contact\");\n        // 准备参数（班主任）\n        CodegenTableDO teacherTable = getTable(\"teacher\")\n                .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())\n                .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType())\n                .setSubJoinColumnId(200L).setSubJoinMany(false);\n        List<CodegenColumnDO> teacherColumns = getColumnList(\"teacher\");\n\n        // 调用\n        Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,\n                Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));\n        // 生成测试文件\n        //writeResult(result, resourcesPath + path);\n        // 断言\n        assertResult(result, path);\n//        writeFile(result, \"/Users/yunai/test/demo11.zip\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue3Test.java",
    "content": "package cn.iocoder.yudao.module.infra.service.codegen.inner;\n\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;\nimport cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * {@link CodegenEngine} 的 Vue2 + Element Plus 单元测试\n *\n * @author 芋道源码\n */\n@Disabled\npublic class CodegenEngineVue3Test extends CodegenEngineAbstractTest {\n\n    @Test\n    public void testExecute_vue3_one() {\n        // 准备参数\n        CodegenTableDO table = getTable(\"student\")\n                .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())\n                .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());\n        List<CodegenColumnDO> columns = getColumnList(\"student\");\n\n        // 调用\n        Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);\n        // 生成测试文件\n        //writeResult(result, resourcesPath + \"/vue3_one\");\n        // 断言\n        assertResult(result, \"/vue3_one\");\n    }\n\n    @Test\n    public void testExecute_vue3_tree() {\n        // 准备参数\n        CodegenTableDO table = getTable(\"category\")\n                .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())\n                .setTemplateType(CodegenTemplateTypeEnum.TREE.getType());\n        List<CodegenColumnDO> columns = getColumnList(\"category\");\n\n        // 调用\n        Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);\n        // 生成测试文件\n        //writeResult(result, resourcesPath + \"/vue3_tree\");\n        // 断言\n        assertResult(result, \"/vue3_tree\");\n//        writeFile(result, \"/Users/yunai/test/demo66.zip\");\n    }\n\n    @Test\n    public void testExecute_vue3_master_normal() {\n        testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_NORMAL, \"/vue3_master_normal\");\n    }\n\n    @Test\n    public void testExecute_vue3_master_erp() {\n        testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_ERP, \"/vue3_master_erp\");\n    }\n\n    @Test\n    public void testExecute_vue3_master_inner() {\n        testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_INNER, \"/vue3_master_inner\");\n    }\n\n    private void testExecute_vue3_master(CodegenTemplateTypeEnum templateType,\n                                         String path) {\n        // 准备参数\n        CodegenTableDO table = getTable(\"student\")\n                .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())\n                .setTemplateType(templateType.getType());\n        List<CodegenColumnDO> columns = getColumnList(\"student\");\n        // 准备参数（子表）\n        CodegenTableDO contactTable = getTable(\"contact\")\n                .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())\n                .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())\n                .setSubJoinColumnId(100L).setSubJoinMany(true);\n        List<CodegenColumnDO> contactColumns = getColumnList(\"contact\");\n        // 准备参数（班主任）\n        CodegenTableDO teacherTable = getTable(\"teacher\")\n                .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())\n                .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())\n                .setSubJoinColumnId(200L).setSubJoinMany(false);\n        List<CodegenColumnDO> teacherColumns = getColumnList(\"teacher\");\n\n        // 调用\n        Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,\n                Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));\n        // 生成测试文件\n        //writeResult(result, resourcesPath + path);\n        // 断言\n        assertResult(result, path);\n        // writeFile(result, \"/Users/yunai/test/demo11.zip\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImplTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.framework.test.core.util.RandomUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.config.ConfigMapper;\nimport cn.iocoder.yudao.module.infra.enums.config.ConfigTypeEnum;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport java.util.function.Consumer;\n\nimport static cn.hutool.core.util.RandomUtil.randomEle;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static org.junit.jupiter.api.Assertions.*;\n\n@Import(ConfigServiceImpl.class)\npublic class ConfigServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private ConfigServiceImpl configService;\n\n    @Resource\n    private ConfigMapper configMapper;\n\n    @Test\n    public void testCreateConfig_success() {\n        // 准备参数\n        ConfigSaveReqVO reqVO = randomPojo(ConfigSaveReqVO.class)\n                .setId(null); // 防止 id 被赋值，导致唯一性校验失败\n\n        // 调用\n        Long configId = configService.createConfig(reqVO);\n        // 断言\n        assertNotNull(configId);\n        // 校验记录的属性是否正确\n        ConfigDO config = configMapper.selectById(configId);\n        assertPojoEquals(reqVO, config, \"id\");\n        assertEquals(ConfigTypeEnum.CUSTOM.getType(), config.getType());\n    }\n\n    @Test\n    public void testUpdateConfig_success() {\n        // mock 数据\n        ConfigDO dbConfig = randomConfigDO();\n        configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        ConfigSaveReqVO reqVO = randomPojo(ConfigSaveReqVO.class, o -> {\n            o.setId(dbConfig.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        configService.updateConfig(reqVO);\n        // 校验是否更新正确\n        ConfigDO config = configMapper.selectById(reqVO.getId()); // 获取最新的\n        assertPojoEquals(reqVO, config);\n    }\n\n    @Test\n    public void testDeleteConfig_success() {\n        // mock 数据\n        ConfigDO dbConfig = randomConfigDO(o -> {\n            o.setType(ConfigTypeEnum.CUSTOM.getType()); // 只能删除 CUSTOM 类型\n        });\n        configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbConfig.getId();\n\n        // 调用\n        configService.deleteConfig(id);\n        // 校验数据不存在了\n        assertNull(configMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteConfig_canNotDeleteSystemType() {\n        // mock 数据\n        ConfigDO dbConfig = randomConfigDO(o -> {\n            o.setType(ConfigTypeEnum.SYSTEM.getType()); // SYSTEM 不允许删除\n        });\n        configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbConfig.getId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> configService.deleteConfig(id), CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE);\n    }\n\n    @Test\n    public void testValidateConfigExists_success() {\n        // mock 数据\n        ConfigDO dbConfigDO = randomConfigDO();\n        configMapper.insert(dbConfigDO);// @Sql: 先插入出一条存在的数据\n\n        // 调用成功\n        configService.validateConfigExists(dbConfigDO.getId());\n    }\n\n    @Test\n    public void testValidateConfigExist_notExists() {\n        assertServiceException(() -> configService.validateConfigExists(randomLongId()), CONFIG_NOT_EXISTS);\n    }\n\n    @Test\n    public void testValidateConfigKeyUnique_success() {\n        // 调用，成功\n        configService.validateConfigKeyUnique(randomLongId(), randomString());\n    }\n\n    @Test\n    public void testValidateConfigKeyUnique_keyDuplicateForCreate() {\n        // 准备参数\n        String key = randomString();\n        // mock 数据\n        configMapper.insert(randomConfigDO(o -> o.setConfigKey(key)));\n\n        // 调用，校验异常\n        assertServiceException(() -> configService.validateConfigKeyUnique(null, key),\n                CONFIG_KEY_DUPLICATE);\n    }\n\n    @Test\n    public void testValidateConfigKeyUnique_keyDuplicateForUpdate() {\n        // 准备参数\n        Long id = randomLongId();\n        String key = randomString();\n        // mock 数据\n        configMapper.insert(randomConfigDO(o -> o.setConfigKey(key)));\n\n        // 调用，校验异常\n        assertServiceException(() -> configService.validateConfigKeyUnique(id, key),\n                CONFIG_KEY_DUPLICATE);\n    }\n\n    @Test\n    public void testGetConfigPage() {\n        // mock 数据\n        ConfigDO dbConfig = randomConfigDO(o -> { // 等会查询到\n            o.setName(\"芋艿\");\n            o.setConfigKey(\"yunai\");\n            o.setType(ConfigTypeEnum.SYSTEM.getType());\n            o.setCreateTime(buildTime(2021, 2, 1));\n        });\n        configMapper.insert(dbConfig);\n        // 测试 name 不匹配\n        configMapper.insert(cloneIgnoreId(dbConfig, o -> o.setName(\"土豆\")));\n        // 测试 key 不匹配\n        configMapper.insert(cloneIgnoreId(dbConfig, o -> o.setConfigKey(\"tudou\")));\n        // 测试 type 不匹配\n        configMapper.insert(cloneIgnoreId(dbConfig, o -> o.setType(ConfigTypeEnum.CUSTOM.getType())));\n        // 测试 createTime 不匹配\n        configMapper.insert(cloneIgnoreId(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1))));\n        // 准备参数\n        ConfigPageReqVO reqVO = new ConfigPageReqVO();\n        reqVO.setName(\"艿\");\n        reqVO.setKey(\"nai\");\n        reqVO.setType(ConfigTypeEnum.SYSTEM.getType());\n        reqVO.setCreateTime(buildBetweenTime(2021, 1, 15, 2021, 2, 15));\n\n        // 调用\n        PageResult<ConfigDO> pageResult = configService.getConfigPage(reqVO);\n        // 断言\n        assertEquals(1, pageResult.getTotal());\n        assertEquals(1, pageResult.getList().size());\n        assertPojoEquals(dbConfig, pageResult.getList().get(0));\n    }\n\n    @Test\n    public void testGetConfig() {\n        // mock 数据\n        ConfigDO dbConfig = randomConfigDO();\n        configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbConfig.getId();\n\n        // 调用\n        ConfigDO config = configService.getConfig(id);\n        // 断言\n        assertNotNull(config);\n        assertPojoEquals(dbConfig, config);\n    }\n\n    @Test\n    public void testGetConfigByKey() {\n        // mock 数据\n        ConfigDO dbConfig = randomConfigDO();\n        configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        String key = dbConfig.getConfigKey();\n\n        // 调用\n        ConfigDO config = configService.getConfigByKey(key);\n        // 断言\n        assertNotNull(config);\n        assertPojoEquals(dbConfig, config);\n    }\n\n    // ========== 随机对象 ==========\n\n    @SafeVarargs\n    private static ConfigDO randomConfigDO(Consumer<ConfigDO>... consumers) {\n        Consumer<ConfigDO> consumer = (o) -> {\n            o.setType(randomEle(ConfigTypeEnum.values()).getType()); // 保证 key 的范围\n        };\n        return RandomUtils.randomPojo(ConfigDO.class, ArrayUtils.append(consumer, consumers));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.db;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.hutool.crypto.symmetric.AES;\nimport cn.iocoder.yudao.framework.mybatis.core.type.EncryptTypeHandler;\nimport cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper;\nimport com.baomidou.dynamic.datasource.creator.DataSourceProperty;\nimport com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.stubbing.Answer;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mockStatic;\nimport static org.mockito.Mockito.when;\n\n/**\n * {@link DataSourceConfigServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(DataSourceConfigServiceImpl.class)\npublic class DataSourceConfigServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private DataSourceConfigServiceImpl dataSourceConfigService;\n\n    @Resource\n    private DataSourceConfigMapper dataSourceConfigMapper;\n\n    @MockBean\n    private AES aes;\n\n    @MockBean\n    private DynamicDataSourceProperties dynamicDataSourceProperties;\n\n    @BeforeEach\n    public void setUp() {\n        // mock 一个空实现的 StringEncryptor，避免 EncryptTypeHandler 报错\n        ReflectUtil.setFieldValue(EncryptTypeHandler.class, \"aes\", aes);\n        when(aes.encryptBase64(anyString())).then((Answer<String>) invocation -> invocation.getArgument(0));\n        when(aes.decryptStr(anyString())).then((Answer<String>) invocation -> invocation.getArgument(0));\n\n        // mock DynamicDataSourceProperties\n        when(dynamicDataSourceProperties.getPrimary()).thenReturn(\"primary\");\n        DataSourceProperty dataSourceProperty = new DataSourceProperty();\n        dataSourceProperty.setUrl(\"http://localhost:3306\");\n        dataSourceProperty.setUsername(\"yunai\");\n        dataSourceProperty.setPassword(\"tudou\");\n        when(dynamicDataSourceProperties.getDatasource()).thenReturn(MapUtil.of(\"primary\", dataSourceProperty));\n    }\n\n    @Test\n    public void testCreateDataSourceConfig_success() {\n        try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) {\n            // 准备参数\n            DataSourceConfigSaveReqVO reqVO = randomPojo(DataSourceConfigSaveReqVO.class)\n                    .setId(null); // 避免 id 被设置\n            // mock 方法\n            databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()),\n                    eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);\n\n            // 调用\n            Long dataSourceConfigId = dataSourceConfigService.createDataSourceConfig(reqVO);\n            // 断言\n            assertNotNull(dataSourceConfigId);\n            // 校验记录的属性是否正确\n            DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(dataSourceConfigId);\n            assertPojoEquals(reqVO, dataSourceConfig, \"id\");\n        }\n    }\n\n    @Test\n    public void testUpdateDataSourceConfig_success() {\n        try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) {\n            // mock 数据\n            DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);\n            dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据\n            // 准备参数\n            DataSourceConfigSaveReqVO reqVO = randomPojo(DataSourceConfigSaveReqVO.class, o -> {\n                o.setId(dbDataSourceConfig.getId()); // 设置更新的 ID\n            });\n            // mock 方法\n            databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()),\n                    eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);\n\n            // 调用\n            dataSourceConfigService.updateDataSourceConfig(reqVO);\n            // 校验是否更新正确\n            DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(reqVO.getId()); // 获取最新的\n            assertPojoEquals(reqVO, dataSourceConfig);\n        }\n    }\n\n    @Test\n    public void testUpdateDataSourceConfig_notExists() {\n        // 准备参数\n        DataSourceConfigSaveReqVO reqVO = randomPojo(DataSourceConfigSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> dataSourceConfigService.updateDataSourceConfig(reqVO), DATA_SOURCE_CONFIG_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteDataSourceConfig_success() {\n        // mock 数据\n        DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);\n        dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbDataSourceConfig.getId();\n\n        // 调用\n        dataSourceConfigService.deleteDataSourceConfig(id);\n        // 校验数据不存在了\n        assertNull(dataSourceConfigMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteDataSourceConfig_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> dataSourceConfigService.deleteDataSourceConfig(id), DATA_SOURCE_CONFIG_NOT_EXISTS);\n    }\n\n    @Test // 测试使用 password 查询，可以查询到数据\n    public void testSelectPassword() {\n        // mock 数据\n        DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);\n        dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据\n\n        // 调用\n        DataSourceConfigDO result = dataSourceConfigMapper.selectOne(DataSourceConfigDO::getPassword,\n                EncryptTypeHandler.encrypt(dbDataSourceConfig.getPassword()));\n        assertPojoEquals(dbDataSourceConfig, result);\n    }\n\n    @Test\n    public void testGetDataSourceConfig_master() {\n        // 准备参数\n        Long id = 0L;\n        // mock 方法\n\n        // 调用\n        DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(id);\n        // 断言\n        assertEquals(id, dataSourceConfig.getId());\n        assertEquals(\"primary\", dataSourceConfig.getName());\n        assertEquals(\"http://localhost:3306\", dataSourceConfig.getUrl());\n        assertEquals(\"yunai\", dataSourceConfig.getUsername());\n        assertEquals(\"tudou\", dataSourceConfig.getPassword());\n    }\n\n    @Test\n    public void testGetDataSourceConfig_normal() {\n        // mock 数据\n        DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);\n        dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbDataSourceConfig.getId();\n\n        // 调用\n        DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(id);\n        // 断言\n        assertPojoEquals(dbDataSourceConfig, dataSourceConfig);\n    }\n\n    @Test\n    public void testGetDataSourceConfigList() {\n        // mock 数据\n        DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);\n        dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n\n        // 调用\n        List<DataSourceConfigDO> dataSourceConfigList = dataSourceConfigService.getDataSourceConfigList();\n        // 断言\n        assertEquals(2, dataSourceConfigList.size());\n        // master\n        assertEquals(0L, dataSourceConfigList.get(0).getId());\n        assertEquals(\"primary\", dataSourceConfigList.get(0).getName());\n        assertEquals(\"http://localhost:3306\", dataSourceConfigList.get(0).getUrl());\n        assertEquals(\"yunai\", dataSourceConfigList.get(0).getUsername());\n        assertEquals(\"tudou\", dataSourceConfigList.get(0).getPassword());\n        // normal\n        assertPojoEquals(dbDataSourceConfig, dataSourceConfigList.get(1));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImplTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.db;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\n\n@Import(DatabaseTableServiceImpl.class)\npublic class DatabaseTableServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private DatabaseTableServiceImpl databaseTableService;\n\n    @MockBean\n    private DataSourceConfigService dataSourceConfigService;\n\n    @Test\n    public void testGetTableList() {\n        // 准备参数\n        Long dataSourceConfigId = randomLongId();\n        // mock 方法\n        DataSourceConfigDO dataSourceConfig = new DataSourceConfigDO().setUsername(\"sa\").setPassword(\"\")\n                .setUrl(\"jdbc:h2:mem:testdb\");\n        when(dataSourceConfigService.getDataSourceConfig(eq(dataSourceConfigId)))\n                .thenReturn(dataSourceConfig);\n\n        // 调用\n        List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId,\n                \"config\", \"参数\");\n        // 断言\n        assertEquals(1, tables.size());\n        assertTableInfo(tables.get(0));\n    }\n\n    @Test\n    public void testGetTable() {\n        // 准备参数\n        Long dataSourceConfigId = randomLongId();\n        // mock 方法\n        DataSourceConfigDO dataSourceConfig = new DataSourceConfigDO().setUsername(\"sa\").setPassword(\"\")\n                .setUrl(\"jdbc:h2:mem:testdb\");\n        when(dataSourceConfigService.getDataSourceConfig(eq(dataSourceConfigId)))\n                .thenReturn(dataSourceConfig);\n\n        // 调用\n        TableInfo tableInfo = databaseTableService.getTable(dataSourceConfigId, \"infra_config\");\n        // 断言\n        assertTableInfo(tableInfo);\n    }\n\n    private void assertTableInfo(TableInfo tableInfo) {\n        assertEquals(\"infra_config\", tableInfo.getName());\n        assertEquals(\"参数配置表\", tableInfo.getComment());\n        assertEquals(13, tableInfo.getFields().size());\n        // id 字段\n        TableField idField = tableInfo.getFields().get(0);\n        assertEquals(\"id\", idField.getName());\n        assertEquals(JdbcType.BIGINT, idField.getMetaInfo().getJdbcType());\n        assertEquals(\"编号\", idField.getComment());\n        assertFalse(idField.getMetaInfo().isNullable());\n        assertTrue(idField.isKeyFlag());\n        assertTrue(idField.isKeyIdentityFlag());\n        assertEquals(DbColumnType.LONG, idField.getColumnType());\n        assertEquals(\"id\", idField.getPropertyName());\n        // name 字段\n        TableField nameField = tableInfo.getFields().get(3);\n        assertEquals(\"name\", nameField.getName());\n        assertEquals(JdbcType.VARCHAR, nameField.getMetaInfo().getJdbcType());\n        assertEquals(\"名字\", nameField.getComment());\n        assertFalse(nameField.getMetaInfo().isNullable());\n        assertFalse(nameField.isKeyFlag());\n        assertFalse(nameField.isKeyIdentityFlag());\n        assertEquals(DbColumnType.STRING, nameField.getColumnType());\n        assertEquals(\"name\", nameField.getPropertyName());\n    }\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.file;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig;\nimport cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;\nimport lombok.Data;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport javax.validation.Validator;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport java.util.Map;\n\nimport static cn.hutool.core.util.RandomUtil.randomEle;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_NOT_EXISTS;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link FileConfigServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(FileConfigServiceImpl.class)\npublic class FileConfigServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private FileConfigServiceImpl fileConfigService;\n\n    @Resource\n    private FileConfigMapper fileConfigMapper;\n\n    @MockBean\n    private Validator validator;\n    @MockBean\n    private FileClientFactory fileClientFactory;\n\n    @Test\n    public void testCreateFileConfig_success() {\n        // 准备参数\n        Map<String, Object> config = MapUtil.<String, Object>builder().put(\"basePath\", \"/yunai\")\n                .put(\"domain\", \"https://www.iocoder.cn\").build();\n        FileConfigSaveReqVO reqVO = randomPojo(FileConfigSaveReqVO.class,\n                o -> o.setStorage(FileStorageEnum.LOCAL.getStorage()).setConfig(config))\n                .setId(null); // 避免 id 被赋值\n\n        // 调用\n        Long fileConfigId = fileConfigService.createFileConfig(reqVO);\n        // 断言\n        assertNotNull(fileConfigId);\n        // 校验记录的属性是否正确\n        FileConfigDO fileConfig = fileConfigMapper.selectById(fileConfigId);\n        assertPojoEquals(reqVO, fileConfig, \"id\", \"config\");\n        assertFalse(fileConfig.getMaster());\n        assertEquals(\"/yunai\", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());\n        assertEquals(\"https://www.iocoder.cn\", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());\n        // 验证 cache\n        assertNull(fileConfigService.getClientCache().getIfPresent(fileConfigId));\n    }\n\n    @Test\n    public void testUpdateFileConfig_success() {\n        // mock 数据\n        FileConfigDO dbFileConfig = randomPojo(FileConfigDO.class, o -> o.setStorage(FileStorageEnum.LOCAL.getStorage())\n                .setConfig(new LocalFileClientConfig().setBasePath(\"/yunai\").setDomain(\"https://www.iocoder.cn\")));\n        fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        FileConfigSaveReqVO reqVO = randomPojo(FileConfigSaveReqVO.class, o -> {\n            o.setId(dbFileConfig.getId()); // 设置更新的 ID\n            o.setStorage(FileStorageEnum.LOCAL.getStorage());\n            Map<String, Object> config = MapUtil.<String, Object>builder().put(\"basePath\", \"/yunai2\")\n                    .put(\"domain\", \"https://doc.iocoder.cn\").build();\n            o.setConfig(config);\n        });\n\n        // 调用\n        fileConfigService.updateFileConfig(reqVO);\n        // 校验是否更新正确\n        FileConfigDO fileConfig = fileConfigMapper.selectById(reqVO.getId()); // 获取最新的\n        assertPojoEquals(reqVO, fileConfig, \"config\");\n        assertEquals(\"/yunai2\", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());\n        assertEquals(\"https://doc.iocoder.cn\", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());\n        // 验证 cache\n        assertNull(fileConfigService.getClientCache().getIfPresent(fileConfig.getId()));\n    }\n\n    @Test\n    public void testUpdateFileConfig_notExists() {\n        // 准备参数\n        FileConfigSaveReqVO reqVO = randomPojo(FileConfigSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> fileConfigService.updateFileConfig(reqVO), FILE_CONFIG_NOT_EXISTS);\n    }\n\n    @Test\n    public void testUpdateFileConfigMaster_success() {\n        // mock 数据\n        FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false);\n        fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据\n        FileConfigDO masterFileConfig = randomFileConfigDO().setMaster(true);\n        fileConfigMapper.insert(masterFileConfig);// @Sql: 先插入出一条存在的数据\n\n        // 调用\n        fileConfigService.updateFileConfigMaster(dbFileConfig.getId());\n        // 断言数据\n        assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster());\n        assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster());\n        // 验证 cache\n        assertNull(fileConfigService.getClientCache().getIfPresent(0L));\n    }\n\n    @Test\n    public void testUpdateFileConfigMaster_notExists() {\n        // 调用, 并断言异常\n        assertServiceException(() -> fileConfigService.updateFileConfigMaster(randomLongId()), FILE_CONFIG_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteFileConfig_success() {\n        // mock 数据\n        FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false);\n        fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbFileConfig.getId();\n\n        // 调用\n        fileConfigService.deleteFileConfig(id);\n        // 校验数据不存在了\n        assertNull(fileConfigMapper.selectById(id));\n        // 验证 cache\n        assertNull(fileConfigService.getClientCache().getIfPresent(id));\n    }\n\n    @Test\n    public void testDeleteFileConfig_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> fileConfigService.deleteFileConfig(id), FILE_CONFIG_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteFileConfig_master() {\n        // mock 数据\n        FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(true);\n        fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbFileConfig.getId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> fileConfigService.deleteFileConfig(id), FILE_CONFIG_DELETE_FAIL_MASTER);\n    }\n\n    @Test\n    public void testGetFileConfigPage() {\n        // mock 数据\n        FileConfigDO dbFileConfig = randomFileConfigDO().setName(\"芋道源码\")\n                .setStorage(FileStorageEnum.LOCAL.getStorage());\n        dbFileConfig.setCreateTime(LocalDateTimeUtil.parse(\"2020-01-23\", DatePattern.NORM_DATE_PATTERN));// 等会查询到\n        fileConfigMapper.insert(dbFileConfig);\n        // 测试 name 不匹配\n        fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setName(\"源码\")));\n        // 测试 storage 不匹配\n        fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setStorage(FileStorageEnum.DB.getStorage())));\n        // 测试 createTime 不匹配\n        fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setCreateTime(LocalDateTimeUtil.parse(\"2020-11-23\", DatePattern.NORM_DATE_PATTERN))));\n        // 准备参数\n        FileConfigPageReqVO reqVO = new FileConfigPageReqVO();\n        reqVO.setName(\"芋道\");\n        reqVO.setStorage(FileStorageEnum.LOCAL.getStorage());\n        reqVO.setCreateTime((new LocalDateTime[]{buildTime(2020, 1, 1),\n                buildTime(2020, 1, 24)}));\n\n        // 调用\n        PageResult<FileConfigDO> pageResult = fileConfigService.getFileConfigPage(reqVO);\n        // 断言\n        assertEquals(1, pageResult.getTotal());\n        assertEquals(1, pageResult.getList().size());\n        assertPojoEquals(dbFileConfig, pageResult.getList().get(0));\n    }\n\n    @Test\n    public void testFileConfig() throws Exception {\n        // mock 数据\n        FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false);\n        fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbFileConfig.getId();\n        // mock 获得 Client\n        FileClient fileClient = mock(FileClient.class);\n        when(fileClientFactory.getFileClient(eq(id))).thenReturn(fileClient);\n        when(fileClient.upload(any(), any(), any())).thenReturn(\"https://www.iocoder.cn\");\n\n        // 调用，并断言\n        assertEquals(\"https://www.iocoder.cn\", fileConfigService.testFileConfig(id));\n    }\n\n    @Test\n    public void testGetFileConfig() {\n        // mock 数据\n        FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false);\n        fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbFileConfig.getId();\n\n        // 调用，并断言\n        assertPojoEquals(dbFileConfig, fileConfigService.getFileConfig(id));\n    }\n\n    @Test\n    public void testGetFileClient() {\n        // mock 数据\n        FileConfigDO fileConfig = randomFileConfigDO().setMaster(false);\n        fileConfigMapper.insert(fileConfig);\n        // 准备参数\n        Long id = fileConfig.getId();\n        // mock 获得 Client\n        FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());\n        when(fileClientFactory.getFileClient(eq(id))).thenReturn(fileClient);\n\n        // 调用，并断言\n        assertSame(fileClient, fileConfigService.getFileClient(id));\n        // 断言缓存\n        verify(fileClientFactory).createOrUpdateFileClient(eq(id), eq(fileConfig.getStorage()),\n                eq(fileConfig.getConfig()));\n    }\n\n    @Test\n    public void testGetMasterFileClient() {\n        // mock 数据\n        FileConfigDO fileConfig = randomFileConfigDO().setMaster(true);\n        fileConfigMapper.insert(fileConfig);\n        // 准备参数\n        Long id = fileConfig.getId();\n        // mock 获得 Client\n        FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());\n        when(fileClientFactory.getFileClient(eq(fileConfig.getId()))).thenReturn(fileClient);\n\n        // 调用，并断言\n        assertSame(fileClient, fileConfigService.getMasterFileClient());\n        // 断言缓存\n        verify(fileClientFactory).createOrUpdateFileClient(eq(fileConfig.getId()), eq(fileConfig.getStorage()),\n                eq(fileConfig.getConfig()));\n    }\n\n    private FileConfigDO randomFileConfigDO() {\n        return randomPojo(FileConfigDO.class).setStorage(randomEle(FileStorageEnum.values()).getStorage())\n                .setConfig(new EmptyFileClientConfig());\n    }\n\n    @Data\n    public static class EmptyFileClientConfig implements FileClientConfig, Serializable {\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.file;\n\nimport cn.hutool.core.io.resource.ResourceUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.framework.test.core.util.AssertUtils;\nimport cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper;\nimport cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.Mockito.*;\n\n@Import({FileServiceImpl.class})\npublic class FileServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private FileServiceImpl fileService;\n\n    @Resource\n    private FileMapper fileMapper;\n\n    @MockBean\n    private FileConfigService fileConfigService;\n\n    @BeforeEach\n    public void setUp() {\n        FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true;\n        FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true;\n    }\n\n    @Test\n    public void testGetFilePage() {\n        // mock 数据\n        FileDO dbFile = randomPojo(FileDO.class, o -> { // 等会查询到\n            o.setPath(\"yunai\");\n            o.setType(\"image/jpg\");\n            o.setCreateTime(buildTime(2021, 1, 15));\n        });\n        fileMapper.insert(dbFile);\n        // 测试 path 不匹配\n        fileMapper.insert(ObjectUtils.cloneIgnoreId(dbFile, o -> o.setPath(\"tudou\")));\n        // 测试 type 不匹配\n        fileMapper.insert(ObjectUtils.cloneIgnoreId(dbFile, o -> {\n            o.setType(\"image/png\");\n        }));\n        // 测试 createTime 不匹配\n        fileMapper.insert(ObjectUtils.cloneIgnoreId(dbFile, o -> {\n            o.setCreateTime(buildTime(2020, 1, 15));\n        }));\n        // 准备参数\n        FilePageReqVO reqVO = new FilePageReqVO();\n        reqVO.setPath(\"yunai\");\n        reqVO.setType(\"jp\");\n        reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021, 1, 10), buildTime(2021, 1, 20)}));\n\n        // 调用\n        PageResult<FileDO> pageResult = fileService.getFilePage(reqVO);\n        // 断言\n        assertEquals(1, pageResult.getTotal());\n        assertEquals(1, pageResult.getList().size());\n        AssertUtils.assertPojoEquals(dbFile, pageResult.getList().get(0));\n    }\n\n    /**\n     * content、name、directory、type 都非空\n     */\n    @Test\n    public void testCreateFile_success_01() throws Exception {\n        // 准备参数\n        byte[] content = ResourceUtil.readBytes(\"file/erweima.jpg\");\n        String name = \"单测文件名\";\n        String directory = randomString();\n        String type = \"image/jpeg\";\n        // mock Master 文件客户端\n        FileClient client = mock(FileClient.class);\n        when(fileConfigService.getMasterFileClient()).thenReturn(client);\n        String url = randomString();\n        AtomicReference<String> pathRef = new AtomicReference<>();\n        when(client.upload(same(content), argThat(path -> {\n            assertTrue(path.matches(directory + \"/\\\\d{8}/\" + name + \"_\\\\d+.jpg\"));\n            pathRef.set(path);\n            return true;\n        }), eq(type))).thenReturn(url);\n        when(client.getId()).thenReturn(10L);\n        // 调用\n        String result = fileService.createFile(content, name, directory, type);\n        // 断言\n        assertEquals(result, url);\n        // 校验数据\n        FileDO file = fileMapper.selectOne(FileDO::getUrl, url);\n        assertEquals(10L, file.getConfigId());\n        assertEquals(pathRef.get(), file.getPath());\n        assertEquals(url, file.getUrl());\n        assertEquals(type, file.getType());\n        assertEquals(content.length, file.getSize());\n    }\n\n    /**\n     * content 非空，其它都空\n     */\n    @Test\n    public void testCreateFile_success_02() throws Exception {\n        // 准备参数\n        byte[] content = ResourceUtil.readBytes(\"file/erweima.jpg\");\n        // mock Master 文件客户端\n        String type = \"image/jpeg\";\n        FileClient client = mock(FileClient.class);\n        when(fileConfigService.getMasterFileClient()).thenReturn(client);\n        String url = randomString();\n        AtomicReference<String> pathRef = new AtomicReference<>();\n        when(client.upload(same(content), argThat(path -> {\n            assertTrue(path.matches(\"\\\\d{8}/6318848e882d8a7e7e82789d87608f684ee52d41966bfc8cad3ce15aad2b970e_\\\\d+\\\\.jpg\"));\n            pathRef.set(path);\n            return true;\n        }), eq(type))).thenReturn(url);\n        when(client.getId()).thenReturn(10L);\n        // 调用\n        String result = fileService.createFile(content, null, null, null);\n        // 断言\n        assertEquals(result, url);\n        // 校验数据\n        FileDO file = fileMapper.selectOne(FileDO::getUrl, url);\n        assertEquals(10L, file.getConfigId());\n        assertEquals(pathRef.get(), file.getPath());\n        assertEquals(url, file.getUrl());\n        assertEquals(type, file.getType());\n        assertEquals(content.length, file.getSize());\n    }\n\n    @Test\n    public void testDeleteFile_success() throws Exception {\n        // mock 数据\n        FileDO dbFile = randomPojo(FileDO.class, o -> o.setConfigId(10L).setPath(\"tudou.jpg\"));\n        fileMapper.insert(dbFile);// @Sql: 先插入出一条存在的数据\n        // mock Master 文件客户端\n        FileClient client = mock(FileClient.class);\n        when(fileConfigService.getFileClient(eq(10L))).thenReturn(client);\n        // 准备参数\n        Long id = dbFile.getId();\n\n        // 调用\n        fileService.deleteFile(id);\n        // 校验数据不存在了\n        assertNull(fileMapper.selectById(id));\n        // 校验调用\n        verify(client).delete(eq(\"tudou.jpg\"));\n    }\n\n    @Test\n    public void testDeleteFile_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> fileService.deleteFile(id), FILE_NOT_EXISTS);\n    }\n\n    @Test\n    public void testGetFileContent() throws Exception {\n        // 准备参数\n        Long configId = 10L;\n        String path = \"tudou.jpg\";\n        // mock 方法\n        FileClient client = mock(FileClient.class);\n        when(fileConfigService.getFileClient(eq(10L))).thenReturn(client);\n        byte[] content = new byte[]{};\n        when(client.getContent(eq(\"tudou.jpg\"))).thenReturn(content);\n\n        // 调用\n        byte[] result = fileService.getFileContent(configId, path);\n        // 断言\n        assertSame(result, content);\n    }\n\n    @Test\n    public void testGenerateUploadPath_AllEnabled() {\n        // 准备参数\n        String name = \"test.jpg\";\n        String directory = \"avatar\";\n        FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true;\n        FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true;\n\n        // 调用\n        String path = fileService.generateUploadPath(name, directory);\n\n        // 断言\n        // 格式为：avatar/yyyyMMdd/test_timestamp.jpg\n        assertTrue(path.startsWith(directory + \"/\"));\n        // 包含日期格式：8 位数字，如 20240517\n        assertTrue(path.matches(directory + \"/\\\\d{8}/test_\\\\d+\\\\.jpg\"));\n    }\n\n    @Test\n    public void testGenerateUploadPath_PrefixEnabled_SuffixDisabled() {\n        // 准备参数\n        String name = \"test.jpg\";\n        String directory = \"avatar\";\n        FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true;\n        FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = false;\n\n        // 调用\n        String path = fileService.generateUploadPath(name, directory);\n\n        // 断言\n        // 格式为：avatar/yyyyMMdd/test.jpg\n        assertTrue(path.startsWith(directory + \"/\"));\n        // 包含日期格式：8 位数字，如 20240517\n        assertTrue(path.matches(directory + \"/\\\\d{8}/test\\\\.jpg\"));\n    }\n\n    @Test\n    public void testGenerateUploadPath_PrefixDisabled_SuffixEnabled() {\n        // 准备参数\n        String name = \"test.jpg\";\n        String directory = \"avatar\";\n        FileServiceImpl.PATH_PREFIX_DATE_ENABLE = false;\n        FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true;\n\n        // 调用\n        String path = fileService.generateUploadPath(name, directory);\n\n        // 断言\n        // 格式为：avatar/test_timestamp.jpg\n        assertTrue(path.startsWith(directory + \"/\"));\n        assertTrue(path.matches(directory + \"/test_\\\\d+\\\\.jpg\"));\n    }\n\n    @Test\n    public void testGenerateUploadPath_AllDisabled() {\n        // 准备参数\n        String name = \"test.jpg\";\n        String directory = \"avatar\";\n        FileServiceImpl.PATH_PREFIX_DATE_ENABLE = false;\n        FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = false;\n\n        // 调用\n        String path = fileService.generateUploadPath(name, directory);\n\n        // 断言\n        // 格式为：avatar/test.jpg\n        assertEquals(directory + \"/\" + name, path);\n    }\n\n    @Test\n    public void testGenerateUploadPath_NoExtension() {\n        // 准备参数\n        String name = \"test\";\n        String directory = \"avatar\";\n        FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true;\n        FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true;\n\n        // 调用\n        String path = fileService.generateUploadPath(name, directory);\n\n        // 断言\n        // 格式为：avatar/yyyyMMdd/test_timestamp\n        assertTrue(path.startsWith(directory + \"/\"));\n        assertTrue(path.matches(directory + \"/\\\\d{8}/test_\\\\d+\"));\n    }\n\n    @Test\n    public void testGenerateUploadPath_DirectoryNull() {\n        // 准备参数\n        String name = \"test.jpg\";\n        String directory = null;\n        FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true;\n        FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true;\n\n        // 调用\n        String path = fileService.generateUploadPath(name, directory);\n\n        // 断言\n        // 格式为：yyyyMMdd/test_timestamp.jpg\n        assertTrue(path.matches(\"\\\\d{8}/test_\\\\d+\\\\.jpg\"));\n    }\n\n    @Test\n    public void testGenerateUploadPath_DirectoryEmpty() {\n        // 准备参数\n        String name = \"test.jpg\";\n        String directory = \"\";\n        FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true;\n        FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true;\n\n        // 调用\n        String path = fileService.generateUploadPath(name, directory);\n\n        // 断言\n        // 格式为：yyyyMMdd/test_timestamp.jpg\n        assertTrue(path.matches(\"\\\\d{8}/test_\\\\d+\\\\.jpg\"));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImplTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiAccessLogMapper;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n@Import(ApiAccessLogServiceImpl.class)\npublic class ApiAccessLogServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private ApiAccessLogServiceImpl apiAccessLogService;\n\n    @Resource\n    private ApiAccessLogMapper apiAccessLogMapper;\n\n    @Test\n    public void testGetApiAccessLogPage() {\n        ApiAccessLogDO apiAccessLogDO = randomPojo(ApiAccessLogDO.class, o -> {\n            o.setUserId(2233L);\n            o.setUserType(UserTypeEnum.ADMIN.getValue());\n            o.setApplicationName(\"yudao-test\");\n            o.setRequestUrl(\"foo\");\n            o.setBeginTime(buildTime(2021, 3, 13));\n            o.setDuration(1000);\n            o.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode());\n        });\n        apiAccessLogMapper.insert(apiAccessLogDO);\n        // 测试 userId 不匹配\n        apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setUserId(3344L)));\n        // 测试 userType 不匹配\n        apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));\n        // 测试 applicationName 不匹配\n        apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setApplicationName(\"test\")));\n        // 测试 requestUrl 不匹配\n        apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setRequestUrl(\"bar\")));\n        // 测试 beginTime 不匹配：构造一个早期时间 2021-02-06 00:00:00\n        apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setBeginTime(buildTime(2021, 2, 6))));\n        // 测试 duration 不匹配\n        apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setDuration(100)));\n        // 测试 resultCode 不匹配\n        apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setResultCode(2)));\n        // 准备参数\n        ApiAccessLogPageReqVO reqVO = new ApiAccessLogPageReqVO();\n        reqVO.setUserId(2233L);\n        reqVO.setUserType(UserTypeEnum.ADMIN.getValue());\n        reqVO.setApplicationName(\"yudao-test\");\n        reqVO.setRequestUrl(\"foo\");\n        reqVO.setBeginTime(buildBetweenTime(2021, 3, 13, 2021, 3, 13));\n        reqVO.setDuration(1000);\n        reqVO.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode());\n\n        // 调用\n        PageResult<ApiAccessLogDO> pageResult = apiAccessLogService.getApiAccessLogPage(reqVO);\n        // 断言，只查到了一条符合条件的\n        assertEquals(1, pageResult.getTotal());\n        assertEquals(1, pageResult.getList().size());\n        assertPojoEquals(apiAccessLogDO, pageResult.getList().get(0));\n    }\n\n    @Test\n    public void testCleanJobLog() {\n        // mock 数据\n        ApiAccessLogDO log01 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3))));\n        apiAccessLogMapper.insert(log01);\n        ApiAccessLogDO log02 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1))));\n        apiAccessLogMapper.insert(log02);\n        // 准备参数\n        Integer exceedDay = 2;\n        Integer deleteLimit = 1;\n\n        // 调用\n        Integer count = apiAccessLogService.cleanAccessLog(exceedDay, deleteLimit);\n        // 断言\n        assertEquals(1, count);\n        List<ApiAccessLogDO> logs = apiAccessLogMapper.selectList();\n        assertEquals(1, logs.size());\n        // TODO @芋艿：createTime updateTime 被屏蔽，仅 win11 会复现，建议后续修复。\n        assertPojoEquals(log02, logs.get(0), \"createTime\", \"updateTime\");\n    }\n\n    @Test\n    public void testCreateApiAccessLog() {\n        // 准备参数\n        ApiAccessLogCreateReqDTO createDTO = randomPojo(ApiAccessLogCreateReqDTO.class);\n\n        // 调用\n        apiAccessLogService.createApiAccessLog(createDTO);\n        // 断言\n        ApiAccessLogDO apiAccessLogDO = apiAccessLogMapper.selectOne(null);\n        assertPojoEquals(createDTO, apiAccessLogDO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImplTest.java",
    "content": "package cn.iocoder.yudao.module.infra.service.logger;\n\nimport cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\nimport cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper;\nimport cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.context.annotation.Import;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\nimport java.util.List;\n\nimport static cn.hutool.core.util.RandomUtil.randomEle;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_NOT_FOUND;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_PROCESSED;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\n@Import(ApiErrorLogServiceImpl.class)\npublic class ApiErrorLogServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private ApiErrorLogServiceImpl apiErrorLogService;\n\n    @Resource\n    private ApiErrorLogMapper apiErrorLogMapper;\n\n    @Test\n    public void testGetApiErrorLogPage() {\n        // mock 数据\n        ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, o -> {\n            o.setUserId(2233L);\n            o.setUserType(UserTypeEnum.ADMIN.getValue());\n            o.setApplicationName(\"yudao-test\");\n            o.setRequestUrl(\"foo\");\n            o.setExceptionTime(buildTime(2021, 3, 13));\n            o.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus());\n        });\n        apiErrorLogMapper.insert(apiErrorLogDO);\n        // 测试 userId 不匹配\n        apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setUserId(3344L)));\n        // 测试 userType 不匹配\n        apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));\n        // 测试 applicationName 不匹配\n        apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setApplicationName(\"test\")));\n        // 测试 requestUrl 不匹配\n        apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setRequestUrl(\"bar\")));\n        // 测试 exceptionTime 不匹配：构造一个早期时间 2021-02-06 00:00:00\n        apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setExceptionTime(buildTime(2021, 2, 6))));\n        // 测试 progressStatus 不匹配\n        apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, logDO -> logDO.setProcessStatus(ApiErrorLogProcessStatusEnum.DONE.getStatus())));\n        // 准备参数\n        ApiErrorLogPageReqVO reqVO = new ApiErrorLogPageReqVO();\n        reqVO.setUserId(2233L);\n        reqVO.setUserType(UserTypeEnum.ADMIN.getValue());\n        reqVO.setApplicationName(\"yudao-test\");\n        reqVO.setRequestUrl(\"foo\");\n        reqVO.setExceptionTime(buildBetweenTime(2021, 3, 1, 2021, 3, 31));\n        reqVO.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus());\n\n        // 调用\n        PageResult<ApiErrorLogDO> pageResult = apiErrorLogService.getApiErrorLogPage(reqVO);\n        // 断言，只查到了一条符合条件的\n        assertEquals(1, pageResult.getTotal());\n        assertEquals(1, pageResult.getList().size());\n        assertPojoEquals(apiErrorLogDO, pageResult.getList().get(0));\n    }\n\n    @Test\n    public void testCreateApiErrorLog() {\n        // 准备参数\n        ApiErrorLogCreateReqDTO createDTO = randomPojo(ApiErrorLogCreateReqDTO.class);\n\n        // 调用\n        apiErrorLogService.createApiErrorLog(createDTO);\n        // 断言\n        ApiErrorLogDO apiErrorLogDO = apiErrorLogMapper.selectOne(null);\n        assertPojoEquals(createDTO, apiErrorLogDO);\n        assertEquals(ApiErrorLogProcessStatusEnum.INIT.getStatus(), apiErrorLogDO.getProcessStatus());\n    }\n\n    @Test\n    public void testUpdateApiErrorLogProcess_success() {\n        // 准备参数\n        ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class,\n                o -> o.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()));\n        apiErrorLogMapper.insert(apiErrorLogDO);\n        // 准备参数\n        Long id = apiErrorLogDO.getId();\n        Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus();\n        Long processUserId = randomLongId();\n\n        // 调用\n        apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId);\n        // 断言\n        ApiErrorLogDO dbApiErrorLogDO = apiErrorLogMapper.selectById(apiErrorLogDO.getId());\n        assertEquals(processStatus, dbApiErrorLogDO.getProcessStatus());\n        assertEquals(processUserId, dbApiErrorLogDO.getProcessUserId());\n        assertNotNull(dbApiErrorLogDO.getProcessTime());\n    }\n\n    @Test\n    public void testUpdateApiErrorLogProcess_processed() {\n        // 准备参数\n        ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class,\n                o -> o.setProcessStatus(ApiErrorLogProcessStatusEnum.DONE.getStatus()));\n        apiErrorLogMapper.insert(apiErrorLogDO);\n        // 准备参数\n        Long id = apiErrorLogDO.getId();\n        Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus();\n        Long processUserId = randomLongId();\n\n        // 调用，并断言异常\n        assertServiceException(() ->\n                        apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId),\n                API_ERROR_LOG_PROCESSED);\n    }\n\n    @Test\n    public void testUpdateApiErrorLogProcess_notFound() {\n        // 准备参数\n        Long id = randomLongId();\n        Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus();\n        Long processUserId = randomLongId();\n\n        // 调用，并断言异常\n        assertServiceException(() ->\n                        apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId),\n                API_ERROR_LOG_NOT_FOUND);\n    }\n\n    @Test\n    public void testCleanJobLog() {\n        // mock 数据\n        ApiErrorLogDO log01 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3))));\n        apiErrorLogMapper.insert(log01);\n        ApiErrorLogDO log02 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1))));\n        apiErrorLogMapper.insert(log02);\n        // 准备参数\n        Integer exceedDay = 2;\n        Integer deleteLimit = 1;\n\n        // 调用\n        Integer count = apiErrorLogService.cleanErrorLog(exceedDay, deleteLimit);\n        // 断言\n        assertEquals(1, count);\n        List<ApiErrorLogDO> logs = apiErrorLogMapper.selectList();\n        assertEquals(1, logs.size());\n        // TODO @芋艿：createTime updateTime 被屏蔽，仅 win11 会复现，建议后续修复。\n        assertPojoEquals(log02, logs.get(0), \"createTime\", \"updateTime\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/application-unit-test.yaml",
    "content": "spring:\n  main:\n    lazy-initialization: true # 开启懒加载，加快速度\n    banner-mode: off # 单元测试，禁用 Banner\n\n--- #################### 数据库相关配置 ####################\n\nspring:\n  # 数据源配置项\n  datasource:\n    name: ruoyi-vue-pro\n    url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式；DATABASE_TO_UPPER 配置表和字段使用小写\n    driver-class-name: org.h2.Driver\n    username: sa\n    password:\n    druid:\n      async-init: true # 单元测试，异步初始化 Druid 连接池，提升启动速度\n      initial-size: 1 # 单元测试，配置为 1，提升启动速度\n  sql:\n    init:\n      schema-locations: classpath:/sql/create_tables.sql\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 16379 # 端口（单元测试，使用 16379 端口）\n    database: 0 # 数据库索引\n\nmybatis-plus:\n  lazy-initialization: true # 单元测试，设置 MyBatis Mapper 延迟加载，加速每个单元测试\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  global-config:\n    db-config:\n      id-type: AUTO # H2 主键递增\n\n--- #################### 定时任务相关配置 ####################\n\n--- #################### 配置中心相关配置 ####################\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项（单元测试，禁用 Lock4j）\n\n--- #################### 监控相关配置 ####################\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  info:\n    base-package: cn.iocoder.yudao.module.infra\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/table/category.json",
    "content": "{\n  \"table\": {\n    \"id\": 10,\n    \"scene\" : 1,\n    \"parentMenuId\" : 888,\n    \"tableName\" : \"infra_category\",\n    \"tableComment\" : \"分类表\",\n    \"moduleName\" : \"infra\",\n    \"businessName\" : \"demo\",\n    \"className\" : \"InfraCategory\",\n    \"classComment\" : \"分类\",\n    \"author\" : \"芋道源码\",\n    \"treeParentColumnId\" : 22,\n    \"treeNameColumnId\" : 11\n  },\n  \"columns\": [ {\n    \"columnName\" : \"id\",\n    \"dataType\" : \"BIGINT\",\n    \"columnComment\" : \"编号\",\n    \"primaryKey\" : true,\n    \"javaType\" : \"Long\",\n    \"javaField\" : \"id\",\n    \"example\" : \"1024\",\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true\n  }, {\n    \"id\" : 11,\n    \"columnName\" : \"name\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"名字\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"name\",\n    \"example\" : \"芋头\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"LIKE\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"input\"\n  }, {\n    \"id\" : 22,\n    \"columnName\" : \"description\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"父编号\",\n    \"javaType\" : \"Long\",\n    \"javaField\" : \"parentId\",\n    \"example\" : \"2048\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true\n  } ]\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/table/contact.json",
    "content": "{\n  \"table\": {\n    \"scene\" : 1,\n    \"tableName\" : \"infra_student_contact\",\n    \"tableComment\" : \"学生联系人表\",\n    \"moduleName\" : \"infra\",\n    \"businessName\" : \"demo\",\n    \"className\" : \"InfraStudentContact\",\n    \"classComment\" : \"学生联系人\",\n    \"author\" : \"芋道源码\"\n  },\n  \"columns\": [ {\n    \"columnName\" : \"id\",\n    \"dataType\" : \"BIGINT\",\n    \"columnComment\" : \"编号\",\n    \"primaryKey\" : true,\n    \"javaType\" : \"Long\",\n    \"javaField\" : \"id\",\n    \"example\" : \"1024\",\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true\n  }, {\n    \"id\" : 100,\n    \"columnName\" : \"student_id\",\n    \"dataType\" : \"BIGINT\",\n    \"columnComment\" : \"学生编号\",\n    \"javaType\" : \"Long\",\n    \"javaField\" : \"studentId\",\n    \"example\" : \"2048\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true\n  }, {\n    \"columnName\" : \"name\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"名字\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"name\",\n    \"example\" : \"芋头\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"LIKE\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"input\"\n  }, {\n    \"columnName\" : \"description\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"简介\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"description\",\n    \"example\" : \"我是介绍\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"textarea\"\n  }, {\n    \"columnName\" : \"birthday\",\n    \"dataType\" : \"DATE\",\n    \"columnComment\" : \"出生日期\",\n    \"javaType\" : \"LocalDateTime\",\n    \"javaField\" : \"birthday\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"=\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"datetime\"\n  }, {\n    \"columnName\" : \"sex\",\n    \"dataType\" : \"INTEGER\",\n    \"columnComment\" : \"性别\",\n    \"javaType\" : \"Integer\",\n    \"javaField\" : \"sex\",\n    \"dictType\" : \"system_user_sex\",\n    \"example\" : \"1\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"=\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"select\"\n  }, {\n    \"columnName\" : \"enabled\",\n    \"dataType\" : \"BOOLEAN\",\n    \"columnComment\" : \"是否有效\",\n    \"javaType\" : \"Boolean\",\n    \"javaField\" : \"enabled\",\n    \"dictType\" : \"infra_boolean_string\",\n    \"example\" : \"true\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"=\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"radio\"\n  }, {\n    \"columnName\" : \"avatar\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"头像\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"avatar\",\n    \"example\" : \"https://www.iocoder.cn/1.png\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"imageUpload\"\n  }, {\n    \"columnName\" : \"video\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"附件\",\n    \"nullable\" : true,\n    \"javaType\" : \"String\",\n    \"javaField\" : \"video\",\n    \"example\" : \"https://www.iocoder.cn/1.mp4\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"fileUpload\"\n  }, {\n    \"columnName\" : \"memo\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"备注\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"memo\",\n    \"example\" : \"我是备注\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"editor\"\n  }, {\n    \"columnName\" : \"create_time\",\n    \"dataType\" : \"DATE\",\n    \"columnComment\" : \"创建时间\",\n    \"nullable\" : true,\n    \"javaType\" : \"LocalDateTime\",\n    \"javaField\" : \"createTime\",\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"BETWEEN\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"datetime\"\n  } ]\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/table/student.json",
    "content": "{\n  \"table\": {\n    \"id\": 1,\n    \"scene\" : 1,\n    \"parentMenuId\" : 888,\n    \"tableName\" : \"infra_student\",\n    \"tableComment\" : \"学生表\",\n    \"moduleName\" : \"infra\",\n    \"businessName\" : \"demo\",\n    \"className\" : \"InfraStudent\",\n    \"classComment\" : \"学生\",\n    \"author\" : \"芋道源码\"\n  },\n  \"columns\": [ {\n    \"id\" : 100,\n    \"columnName\" : \"id\",\n    \"dataType\" : \"BIGINT\",\n    \"columnComment\" : \"编号\",\n    \"primaryKey\" : true,\n    \"javaType\" : \"Long\",\n    \"javaField\" : \"id\",\n    \"example\" : \"1024\",\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true\n  }, {\n    \"columnName\" : \"name\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"名字\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"name\",\n    \"example\" : \"芋头\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"LIKE\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"input\"\n  }, {\n    \"columnName\" : \"description\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"简介\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"description\",\n    \"example\" : \"我是介绍\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"textarea\"\n  }, {\n    \"columnName\" : \"birthday\",\n    \"dataType\" : \"DATE\",\n    \"columnComment\" : \"出生日期\",\n    \"javaType\" : \"LocalDateTime\",\n    \"javaField\" : \"birthday\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"=\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"datetime\"\n  }, {\n    \"columnName\" : \"sex\",\n    \"dataType\" : \"INTEGER\",\n    \"columnComment\" : \"性别\",\n    \"javaType\" : \"Integer\",\n    \"javaField\" : \"sex\",\n    \"dictType\" : \"system_user_sex\",\n    \"example\" : \"1\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"=\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"select\"\n  }, {\n    \"columnName\" : \"enabled\",\n    \"dataType\" : \"BOOLEAN\",\n    \"columnComment\" : \"是否有效\",\n    \"javaType\" : \"Boolean\",\n    \"javaField\" : \"enabled\",\n    \"dictType\" : \"infra_boolean_string\",\n    \"example\" : \"true\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"=\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"radio\"\n  }, {\n    \"columnName\" : \"avatar\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"头像\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"avatar\",\n    \"example\" : \"https://www.iocoder.cn/1.png\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"imageUpload\"\n  }, {\n    \"columnName\" : \"video\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"附件\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"video\",\n    \"example\" : \"https://www.iocoder.cn/1.mp4\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"fileUpload\"\n  }, {\n    \"columnName\" : \"memo\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"备注\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"memo\",\n    \"example\" : \"我是备注\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"editor\"\n  }, {\n    \"columnName\" : \"create_time\",\n    \"dataType\" : \"DATE\",\n    \"columnComment\" : \"创建时间\",\n    \"nullable\" : true,\n    \"javaType\" : \"LocalDateTime\",\n    \"javaField\" : \"createTime\",\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"BETWEEN\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"datetime\"\n  } ]\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/table/teacher.json",
    "content": "{\n  \"table\": {\n    \"scene\" : 1,\n    \"tableName\" : \"infra_student_teacher\",\n    \"tableComment\" : \"学生班主任表\",\n    \"moduleName\" : \"infra\",\n    \"businessName\" : \"demo\",\n    \"className\" : \"InfraStudentTeacher\",\n    \"classComment\" : \"学生班主任\",\n    \"author\" : \"芋道源码\"\n  },\n  \"columns\": [ {\n    \"columnName\" : \"id\",\n    \"dataType\" : \"BIGINT\",\n    \"columnComment\" : \"编号\",\n    \"primaryKey\" : true,\n    \"javaType\" : \"Long\",\n    \"javaField\" : \"id\",\n    \"example\" : \"1024\",\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true\n  }, {\n    \"id\" : 200,\n    \"columnName\" : \"student_id\",\n    \"dataType\" : \"BIGINT\",\n    \"columnComment\" : \"学生编号\",\n    \"javaType\" : \"Long\",\n    \"javaField\" : \"studentId\",\n    \"example\" : \"2048\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true\n  }, {\n    \"columnName\" : \"name\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"名字\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"name\",\n    \"example\" : \"芋头\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"LIKE\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"input\"\n  }, {\n    \"columnName\" : \"description\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"简介\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"description\",\n    \"example\" : \"我是介绍\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"textarea\"\n  }, {\n    \"columnName\" : \"birthday\",\n    \"dataType\" : \"DATE\",\n    \"columnComment\" : \"出生日期\",\n    \"javaType\" : \"LocalDateTime\",\n    \"javaField\" : \"birthday\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"=\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"datetime\"\n  }, {\n    \"columnName\" : \"sex\",\n    \"dataType\" : \"INTEGER\",\n    \"columnComment\" : \"性别\",\n    \"javaType\" : \"Integer\",\n    \"javaField\" : \"sex\",\n    \"dictType\" : \"system_user_sex\",\n    \"example\" : \"1\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"=\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"select\"\n  }, {\n    \"columnName\" : \"enabled\",\n    \"dataType\" : \"BOOLEAN\",\n    \"columnComment\" : \"是否有效\",\n    \"javaType\" : \"Boolean\",\n    \"javaField\" : \"enabled\",\n    \"dictType\" : \"infra_boolean_string\",\n    \"example\" : \"true\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"=\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"radio\"\n  }, {\n    \"columnName\" : \"avatar\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"头像\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"avatar\",\n    \"example\" : \"https://www.iocoder.cn/1.png\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"imageUpload\"\n  }, {\n    \"columnName\" : \"video\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"附件\",\n    \"nullable\" : true,\n    \"javaType\" : \"String\",\n    \"javaField\" : \"video\",\n    \"example\" : \"https://www.iocoder.cn/1.mp4\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"fileUpload\"\n  }, {\n    \"columnName\" : \"memo\",\n    \"dataType\" : \"VARCHAR\",\n    \"columnComment\" : \"备注\",\n    \"javaType\" : \"String\",\n    \"javaField\" : \"memo\",\n    \"example\" : \"我是备注\",\n    \"createOperation\" : true,\n    \"updateOperation\" : true,\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"editor\"\n  }, {\n    \"columnName\" : \"create_time\",\n    \"dataType\" : \"DATE\",\n    \"columnComment\" : \"创建时间\",\n    \"nullable\" : true,\n    \"javaType\" : \"LocalDateTime\",\n    \"javaField\" : \"createTime\",\n    \"listOperation\" : true,\n    \"listOperationCondition\" : \"BETWEEN\",\n    \"listOperationResult\" : true,\n    \"htmlType\" : \"datetime\"\n  } ]\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraStudentPageReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentSaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\": \"js/index\",\n  \"filePath\": \"yudao-ui-admin-vue2/src/api/infra/demo/index.js\"\n}, {\n  \"contentPath\" : \"vue/StudentForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactList\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherList\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 学生 TODO 补充编号 ==========\r\nErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生不存在\");\r\nErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生联系人不存在\");\r\nErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生班主任不存在\");\r\nErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, \"学生班主任已存在\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生联系人 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_contact\")\n@KeySequence(\"infra_student_contact_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentContactDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生联系人 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {\n\n    default PageResult<InfraStudentContactDO> selectPage(PageParam reqVO, Long studentId) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentContactDO>()\n            .eq(InfraStudentContactDO::getStudentId, studentId)\n            .orderByDesc(InfraStudentContactDO::getId));\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/student\")\n@Validated\npublic class InfraStudentController {\n\n    @Resource\n    private InfraStudentService studentService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {\n        return success(studentService.createStudent(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {\n        studentService.updateStudent(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudent(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudent(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentRespVO> getStudent(@RequestParam(\"id\") Long id) {\n        InfraStudentDO student = studentService.getStudent(id);\n        return success(BeanUtils.toBean(student, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {\n        PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", InfraStudentRespVO.class,\n                        BeanUtils.toBean(list, InfraStudentRespVO.class));\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @GetMapping(\"/student-contact/page\")\n    @Operation(summary = \"获得学生联系人分页\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentContactDO>> getStudentContactPage(PageParam pageReqVO,\n                                                                                        @RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentContactPage(pageReqVO, studentId));\n    }\n\n    @PostMapping(\"/student-contact/create\")\n    @Operation(summary = \"创建学生联系人\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {\n        return success(studentService.createStudentContact(studentContact));\n    }\n\n    @PutMapping(\"/student-contact/update\")\n    @Operation(summary = \"更新学生联系人\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {\n        studentService.updateStudentContact(studentContact);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/student-contact/delete\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @Operation(summary = \"删除学生联系人\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudentContact(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudentContact(id);\n        return success(true);\n    }\n\n\t@GetMapping(\"/student-contact/get\")\n\t@Operation(summary = \"获得学生联系人\")\n\t@Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n\tpublic CommonResult<InfraStudentContactDO> getStudentContact(@RequestParam(\"id\") Long id) {\n\t    return success(studentService.getStudentContact(id));\n\t}\n\n    // ==================== 子表（学生班主任） ====================\n\n    @GetMapping(\"/student-teacher/page\")\n    @Operation(summary = \"获得学生班主任分页\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentTeacherDO>> getStudentTeacherPage(PageParam pageReqVO,\n                                                                                        @RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentTeacherPage(pageReqVO, studentId));\n    }\n\n    @PostMapping(\"/student-teacher/create\")\n    @Operation(summary = \"创建学生班主任\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {\n        return success(studentService.createStudentTeacher(studentTeacher));\n    }\n\n    @PutMapping(\"/student-teacher/update\")\n    @Operation(summary = \"更新学生班主任\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {\n        studentService.updateStudentTeacher(studentTeacher);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/student-teacher/delete\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @Operation(summary = \"删除学生班主任\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudentTeacher(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudentTeacher(id);\n        return success(true);\n    }\n\n\t@GetMapping(\"/student-teacher/get\")\n\t@Operation(summary = \"获得学生班主任\")\n\t@Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n\tpublic CommonResult<InfraStudentTeacherDO> getStudentTeacher(@RequestParam(\"id\") Long id) {\n\t    return success(studentService.getStudentTeacher(id));\n\t}\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student\")\n@KeySequence(\"infra_student_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {\n\n    default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()\n                .likeIfPresent(InfraStudentDO::getName, reqVO.getName())\n                .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())\n                .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())\n                .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(InfraStudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentPageReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class InfraStudentPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n    @Schema(description = \"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", example = \"1\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", example = \"true\")\n    private Boolean enabled;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.idev.excel.annotation.*;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraStudentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"是否有效\", converter = DictConvert.class)\n    @DictFormat(\"infra_boolean_string\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @ExcelProperty(\"头像\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @ExcelProperty(\"附件\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @ExcelProperty(\"备注\")\n    private String memo;\n\n    @Schema(description = \"创建时间\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentSaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class InfraStudentSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否有效不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"头像不能为空\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @NotEmpty(message = \"附件不能为空\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @NotEmpty(message = \"备注不能为空\")\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraStudentService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteStudent(Long id);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    InfraStudentDO getStudent(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);\n\n    // ==================== 子表（学生联系人） ====================\n\n    /**\n     * 获得学生联系人分页\n     *\n     * @param pageReqVO 分页查询\n     * @param studentId 学生编号\n     * @return 学生联系人分页\n     */\n    PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId);\n\n    /**\n     * 创建学生联系人\n     *\n     * @param studentContact 创建信息\n     * @return 编号\n     */\n    Long createStudentContact(@Valid InfraStudentContactDO studentContact);\n\n    /**\n     * 更新学生联系人\n     *\n     * @param studentContact 更新信息\n     */\n    void updateStudentContact(@Valid InfraStudentContactDO studentContact);\n\n    /**\n     * 删除学生联系人\n     *\n     * @param id 编号\n     */\n    void deleteStudentContact(Long id);\n\n\t/**\n\t * 获得学生联系人\n\t *\n\t * @param id 编号\n     * @return 学生联系人\n\t */\n    InfraStudentContactDO getStudentContact(Long id);\n\n    // ==================== 子表（学生班主任） ====================\n\n    /**\n     * 获得学生班主任分页\n     *\n     * @param pageReqVO 分页查询\n     * @param studentId 学生编号\n     * @return 学生班主任分页\n     */\n    PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId);\n\n    /**\n     * 创建学生班主任\n     *\n     * @param studentTeacher 创建信息\n     * @return 编号\n     */\n    Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);\n\n    /**\n     * 更新学生班主任\n     *\n     * @param studentTeacher 更新信息\n     */\n    void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);\n\n    /**\n     * 删除学生班主任\n     *\n     * @param id 编号\n     */\n    void deleteStudentTeacher(Long id);\n\n\t/**\n\t * 获得学生班主任\n\t *\n\t * @param id 编号\n     * @return 学生班主任\n\t */\n    InfraStudentTeacherDO getStudentTeacher(Long id);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraStudentServiceImpl implements InfraStudentService {\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n    @Resource\n    private InfraStudentContactMapper studentContactMapper;\n    @Resource\n    private InfraStudentTeacherMapper studentTeacherMapper;\n\n    @Override\n    public Long createStudent(InfraStudentSaveReqVO createReqVO) {\n        // 插入\n        InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);\n        studentMapper.insert(student);\n        // 返回\n        return student.getId();\n    }\n\n    @Override\n    public void updateStudent(InfraStudentSaveReqVO updateReqVO) {\n        // 校验存在\n        validateStudentExists(updateReqVO.getId());\n        // 更新\n        InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);\n        studentMapper.updateById(updateObj);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStudent(Long id) {\n        // 校验存在\n        validateStudentExists(id);\n        // 删除\n        studentMapper.deleteById(id);\n\n        // 删除子表\n        deleteStudentContactByStudentId(id);\n        deleteStudentTeacherByStudentId(id);\n    }\n\n    private void validateStudentExists(Long id) {\n        if (studentMapper.selectById(id) == null) {\n            throw exception(STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public InfraStudentDO getStudent(Long id) {\n        return studentMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {\n        return studentMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @Override\n    public PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId) {\n        return studentContactMapper.selectPage(pageReqVO, studentId);\n    }\n\n    @Override\n    public Long createStudentContact(InfraStudentContactDO studentContact) {\n        studentContactMapper.insert(studentContact);\n        return studentContact.getId();\n    }\n\n    @Override\n    public void updateStudentContact(InfraStudentContactDO studentContact) {\n        // 校验存在\n        validateStudentContactExists(studentContact.getId());\n        // 更新\n        studentContactMapper.updateById(studentContact);\n    }\n\n    @Override\n    public void deleteStudentContact(Long id) {\n        // 校验存在\n        validateStudentContactExists(id);\n        // 删除\n        studentContactMapper.deleteById(id);\n    }\n\n    @Override\n    public InfraStudentContactDO getStudentContact(Long id) {\n        return studentContactMapper.selectById(id);\n    }\n\n    private void validateStudentContactExists(Long id) {\n        if (studentContactMapper.selectById(id) == null) {\n            throw exception(STUDENT_CONTACT_NOT_EXISTS);\n        }\n    }\n\n    private void deleteStudentContactByStudentId(Long studentId) {\n        studentContactMapper.deleteByStudentId(studentId);\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @Override\n    public PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId) {\n        return studentTeacherMapper.selectPage(pageReqVO, studentId);\n    }\n\n    @Override\n    public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) {\n        // 校验是否已经存在\n        if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) {\n            throw exception(STUDENT_TEACHER_EXISTS);\n        }\n        // 插入\n        studentTeacherMapper.insert(studentTeacher);\n        return studentTeacher.getId();\n    }\n\n    @Override\n    public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) {\n        // 校验存在\n        validateStudentTeacherExists(studentTeacher.getId());\n        // 更新\n        studentTeacherMapper.updateById(studentTeacher);\n    }\n\n    @Override\n    public void deleteStudentTeacher(Long id) {\n        // 校验存在\n        validateStudentTeacherExists(id);\n        // 删除\n        studentTeacherMapper.deleteById(id);\n    }\n\n    @Override\n    public InfraStudentTeacherDO getStudentTeacher(Long id) {\n        return studentTeacherMapper.selectById(id);\n    }\n\n    private void validateStudentTeacherExists(Long id) {\n        if (studentTeacherMapper.selectById(id) == null) {\n            throw exception(STUDENT_TEACHER_NOT_EXISTS);\n        }\n    }\n\n    private void deleteStudentTeacherByStudentId(Long studentId) {\n        studentTeacherMapper.deleteByStudentId(studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraStudentServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraStudentServiceImpl.class)\npublic class InfraStudentServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraStudentServiceImpl studentService;\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Test\n    public void testCreateStudent_success() {\n        // 准备参数\n        InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);\n\n        // 调用\n        Long studentId = studentService.createStudent(createReqVO);\n        // 断言\n        assertNotNull(studentId);\n        // 校验记录的属性是否正确\n        InfraStudentDO student = studentMapper.selectById(studentId);\n        assertPojoEquals(createReqVO, student, \"id\");\n    }\n\n    @Test\n    public void testUpdateStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {\n            o.setId(dbStudent.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        studentService.updateStudent(updateReqVO);\n        // 校验是否更新正确\n        InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, student);\n    }\n\n    @Test\n    public void testUpdateStudent_notExists() {\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbStudent.getId();\n\n        // 调用\n        studentService.deleteStudent(id);\n       // 校验数据不存在了\n       assertNull(studentMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteStudent_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetStudentPage() {\n       // mock 数据\n       InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到\n           o.setName(null);\n           o.setBirthday(null);\n           o.setSex(null);\n           o.setEnabled(null);\n           o.setCreateTime(null);\n       });\n       studentMapper.insert(dbStudent);\n       // 测试 name 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));\n       // 测试 birthday 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));\n       // 测试 sex 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));\n       // 测试 enabled 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));\n       // 测试 createTime 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));\n       // 准备参数\n       InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();\n       reqVO.setName(null);\n       reqVO.setBirthday(null);\n       reqVO.setSex(null);\n       reqVO.setEnabled(null);\n       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n\n       // 调用\n       PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);\n       // 断言\n       assertEquals(1, pageResult.getTotal());\n       assertEquals(1, pageResult.getList().size());\n       assertPojoEquals(dbStudent, pageResult.getList().get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生班主任 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_teacher\")\n@KeySequence(\"infra_student_teacher_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentTeacherDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生班主任 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {\n\n    default PageResult<InfraStudentTeacherDO> selectPage(PageParam reqVO, Long studentId) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentTeacherDO>()\n            .eq(InfraStudentTeacherDO::getStudentId, studentId)\n            .orderByDesc(InfraStudentTeacherDO::getId));\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/js/index",
    "content": "import request from '@/utils/request'\r\n\r\n// 创建学生\r\nexport function createStudent(data) {\r\n  return request({\r\n    url: '/infra/student/create',\r\n    method: 'post',\r\n    data: data\r\n  })\r\n}\r\n\r\n// 更新学生\r\nexport function updateStudent(data) {\r\n  return request({\r\n    url: '/infra/student/update',\r\n    method: 'put',\r\n    data: data\r\n  })\r\n}\r\n\r\n// 删除学生\r\nexport function deleteStudent(id) {\r\n  return request({\r\n    url: '/infra/student/delete?id=' + id,\r\n    method: 'delete'\r\n  })\r\n}\r\n\r\n// 获得学生\r\nexport function getStudent(id) {\r\n  return request({\r\n    url: '/infra/student/get?id=' + id,\r\n    method: 'get'\r\n  })\r\n}\r\n\r\n// 获得学生分页\r\nexport function getStudentPage(params) {\r\n  return request({\r\n    url: '/infra/student/page',\r\n    method: 'get',\r\n    params\r\n  })\r\n}\r\n// 导出学生 Excel\r\nexport function exportStudentExcel(params) {\r\n  return request({\r\n    url: '/infra/student/export-excel',\r\n    method: 'get',\r\n    params,\r\n    responseType: 'blob'\r\n  })\r\n}\r\n\r\n// ==================== 子表（学生联系人） ====================\r\n  \r\n  // 获得学生联系人分页\r\n  export function getStudentContactPage(params) {\r\n    return request({\r\n      url: '/infra/student/student-contact/page',\r\n      method: 'get',\r\n      params\r\n    })\r\n  }\r\n        // 新增学生联系人\r\n  export function createStudentContact(data) {\r\n    return request({\r\n      url: `/infra/student/student-contact/create`,\r\n      method: 'post',\r\n      data\r\n    })\r\n  }\r\n\r\n  // 修改学生联系人\r\n  export function updateStudentContact(data) {\r\n    return request({\r\n      url: `/infra/student/student-contact/update`,\r\n      method: 'post',\r\n      data\r\n    })\r\n  }\r\n\r\n  // 删除学生联系人\r\n  export function deleteStudentContact(id) {\r\n    return request({\r\n      url: `/infra/student/student-contact/delete?id=` + id,\r\n      method: 'delete'\r\n    })\r\n  }\r\n\r\n  // 获得学生联系人\r\n  export function getStudentContact(id) {\r\n    return request({\r\n      url: `/infra/student/student-contact/get?id=` + id,\r\n      method: 'get'\r\n    })\r\n  }\r\n\r\n// ==================== 子表（学生班主任） ====================\r\n  \r\n  // 获得学生班主任分页\r\n  export function getStudentTeacherPage(params) {\r\n    return request({\r\n      url: '/infra/student/student-teacher/page',\r\n      method: 'get',\r\n      params\r\n    })\r\n  }\r\n        // 新增学生班主任\r\n  export function createStudentTeacher(data) {\r\n    return request({\r\n      url: `/infra/student/student-teacher/create`,\r\n      method: 'post',\r\n      data\r\n    })\r\n  }\r\n\r\n  // 修改学生班主任\r\n  export function updateStudentTeacher(data) {\r\n    return request({\r\n      url: `/infra/student/student-teacher/update`,\r\n      method: 'post',\r\n      data\r\n    })\r\n  }\r\n\r\n  // 删除学生班主任\r\n  export function deleteStudentTeacher(id) {\r\n    return request({\r\n      url: `/infra/student/student-teacher/delete?id=` + id,\r\n      method: 'delete'\r\n    })\r\n  }\r\n\r\n  // 获得学生班主任\r\n  export function getStudentTeacher(id) {\r\n    return request({\r\n      url: `/infra/student/student-teacher/get?id=` + id,\r\n      method: 'get'\r\n    })\r\n  }\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_student\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" varchar NOT NULL,\n    \"birthday\" varchar NOT NULL,\n    \"sex\" int NOT NULL,\n    \"enabled\" bit NOT NULL,\n    \"avatar\" varchar NOT NULL,\n    \"video\" varchar NOT NULL,\n    \"memo\" varchar NOT NULL,\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (\"id\")\n) COMMENT '学生表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_student\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '学生管理', '', 2, 0, 888,\r\n    'student', '', 'infra/demo/index', 0, 'InfraStudent'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生查询', 'infra:student:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生创建', 'infra:student:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生更新', 'infra:student:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生删除', 'infra:student:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生导出', 'infra:student:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactForm",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 对话框(添加 / 修改) -->\n    <el-dialog :title=\"dialogTitle\" :visible.sync=\"dialogVisible\" width=\"45%\" v-dialogDrag append-to-body>\n      <el-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\" v-loading=\"formLoading\" label-width=\"100px\">\n                     <el-form-item label=\"名字\" prop=\"name\">\n                      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n                    </el-form-item>\n                    <el-form-item label=\"简介\" prop=\"description\">\n                      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入内容\" />\n                    </el-form-item>\n                    <el-form-item label=\"出生日期\" prop=\"birthday\">\n                      <el-date-picker clearable v-model=\"formData.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                    </el-form-item>\n                    <el-form-item label=\"性别\" prop=\"sex\">\n                      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                       :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                      </el-select>\n                    </el-form-item>\n                    <el-form-item label=\"是否有效\" prop=\"enabled\">\n                      <el-radio-group v-model=\"formData.enabled\">\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                      :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                      </el-radio-group>\n                    </el-form-item>\n                    <el-form-item label=\"头像\" prop=\"avatar\">\n                      <ImageUpload v-model=\"formData.avatar\"/>\n                    </el-form-item>\n                    <el-form-item label=\"附件\" prop=\"video\">\n                      <FileUpload v-model=\"formData.video\"/>\n                    </el-form-item>\n                    <el-form-item label=\"备注\" prop=\"memo\">\n                      <editor v-model=\"formData.memo\" :min-height=\"192\"/>\n                    </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\" :disabled=\"formLoading\">确 定</el-button>\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n      import ImageUpload from '@/components/ImageUpload';\n      import FileUpload from '@/components/FileUpload';\n      import Editor from '@/components/Editor';\n  export default {\n    name: \"StudentContactForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n    },\n    data() {\n      return {\n        // 弹出层标题\n        dialogTitle: \"\",\n        // 是否显示弹出层\n        dialogVisible: false,\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: {\n                            id: undefined,\n                            studentId: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        },\n        // 表单校验\n        formRules: {\n                        studentId: [{ required: true, message: \"学生编号不能为空\", trigger: \"blur\" }],\n                        name: [{ required: true, message: \"名字不能为空\", trigger: \"blur\" }],\n                        description: [{ required: true, message: \"简介不能为空\", trigger: \"blur\" }],\n                        birthday: [{ required: true, message: \"出生日期不能为空\", trigger: \"blur\" }],\n                        sex: [{ required: true, message: \"性别不能为空\", trigger: \"change\" }],\n                        enabled: [{ required: true, message: \"是否有效不能为空\", trigger: \"blur\" }],\n                        avatar: [{ required: true, message: \"头像不能为空\", trigger: \"blur\" }],\n                        memo: [{ required: true, message: \"备注不能为空\", trigger: \"blur\" }],\n        },\n      };\n    },\n    methods: {\n      /** 打开弹窗 */\n      async open(id, studentId) {\n        this.dialogVisible = true;\n        this.reset();\n        this.formData.studentId = studentId;\n        // 修改时，设置数据\n        if (id) {\n          this.formLoading = true;\n          try {\n            const res = await StudentApi.getStudentContact(id);\n            this.formData = res.data;\n            this.dialogTitle = \"修改学生联系人\";\n          } finally {\n            this.formLoading = false;\n          }\n        }\n        this.dialogTitle = \"新增学生联系人\";\n      },\n      /** 提交按钮 */\n      async submitForm() {\n        await this.$refs[\"formRef\"].validate();\n        this.formLoading = true;\n        try {\n            const data = this.formData;\n            // 修改的提交\n            if (data.id) {\n            await  StudentApi.updateStudentContact(data);\n            this.$modal.msgSuccess(\"修改成功\");\n            this.dialogVisible = false;\n            this.$emit('success');\n              return;\n            }\n            // 添加的提交\n              await StudentApi.createStudentContact(data);\n              this.$modal.msgSuccess(\"新增成功\");\n              this.dialogVisible = false;\n              this.$emit('success');\n        }finally {\n          this.formLoading = false;\n        }\n      },\n      /** 表单重置 */\n      reset() {\n        this.formData = {\n                            id: undefined,\n                            studentId: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        };\n        this.resetForm(\"formRef\");\n      },\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactList",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 操作工具栏 -->\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"openForm(undefined)\"\n                   v-hasPermi=\"['infra:student:create']\">新增</el-button>\n      </el-col>\n    </el-row>\n            <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n                <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n                 <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n                <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n                <el-table-column label=\"出生日期\" align=\"center\" prop=\"birthday\" width=\"180\">\n                  <template v-slot=\"scope\">\n                    <span>{{ parseTime(scope.row.birthday) }}</span>\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n                  <template v-slot=\"scope\">\n                    <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n                  <template v-slot=\"scope\">\n                    <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n                <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n                <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n                <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n                  <template v-slot=\"scope\">\n                    <span>{{ parseTime(scope.row.createTime) }}</span>\n                  </template>\n                </el-table-column>\n    <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n      <template v-slot=\"scope\">\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.id)\"\n                   v-hasPermi=\"['infra:student:update']\">修改</el-button>\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                   v-hasPermi=\"['infra:student:delete']\">删除</el-button>\n      </template>\n    </el-table-column>\n  </el-table>\n    <!-- 分页组件 -->\n    <pagination v-show=\"total > 0\" :total=\"total\" :page.sync=\"queryParams.pageNo\" :limit.sync=\"queryParams.pageSize\"\n                @pagination=\"getList\"/>\n  <!-- 对话框(添加 / 修改) -->\n  <StudentContactForm ref=\"formRef\" @success=\"getList\" />\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n  import StudentContactForm from './StudentContactForm.vue';\n  export default {\n    name: \"StudentContactList\",\n    components: {\n       StudentContactForm\n    },\n    props:[\n      'studentId'\n    ],// 学生编号（主表的关联字段）\n    data() {\n      return {\n        // 遮罩层\n        loading: true,\n        // 列表的数据\n        list: [],\n        // 列表的总页数\n        total: 0,\n        // 查询参数\n        queryParams: {\n          pageNo: 1,\n          pageSize: 10,\n          studentId: undefined\n        }\n      };\n    },\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n        studentId:{\n            handler(val) {\n              this.queryParams.studentId = val;\n              if (val){\n                this.handleQuery();\n              }\n            },\n            immediate: true\n      }\n    },\n    methods: {\n      /** 查询列表 */\n      async getList() {\n        try {\n          this.loading = true;\n            const res = await StudentApi.getStudentContactPage(this.queryParams);\n            this.list = res.data.list;\n            this.total = res.data.total;\n        } finally {\n          this.loading = false;\n        }\n      },\n      /** 搜索按钮操作 */\n      handleQuery() {\n        this.queryParams.pageNo = 1;\n        this.getList();\n      },\n      /** 添加/修改操作 */\n      openForm(id) {\n        if (!this.studentId) {\n          this.$modal.msgError('请选择一个学生');\n          return;\n        }\n        this.$refs[\"formRef\"].open(id, this.studentId);\n      },\n      /** 删除按钮操作 */\n      async handleDelete(row) {\n        const id = row.id;\n        await this.$modal.confirm('是否确认删除学生编号为\"' + id + '\"的数据项?');\n        try {\n          await StudentApi.deleteStudentContact(id);\n          await this.getList();\n          this.$modal.msgSuccess(\"删除成功\");\n        } catch {}\n      },\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentForm",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 对话框(添加 / 修改) -->\n    <el-dialog :title=\"dialogTitle\" :visible.sync=\"dialogVisible\" width=\"45%\" v-dialogDrag append-to-body>\n      <el-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\" v-loading=\"formLoading\" label-width=\"100px\">\n                    <el-form-item label=\"名字\" prop=\"name\">\n                      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n                    </el-form-item>\n                    <el-form-item label=\"简介\" prop=\"description\">\n                      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入内容\" />\n                    </el-form-item>\n                    <el-form-item label=\"出生日期\" prop=\"birthday\">\n                      <el-date-picker clearable v-model=\"formData.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                    </el-form-item>\n                    <el-form-item label=\"性别\" prop=\"sex\">\n                      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                       :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                      </el-select>\n                    </el-form-item>\n                    <el-form-item label=\"是否有效\" prop=\"enabled\">\n                      <el-radio-group v-model=\"formData.enabled\">\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                      :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                      </el-radio-group>\n                    </el-form-item>\n                    <el-form-item label=\"头像\">\n                      <ImageUpload v-model=\"formData.avatar\"/>\n                    </el-form-item>\n                    <el-form-item label=\"附件\">\n                      <FileUpload v-model=\"formData.video\"/>\n                    </el-form-item>\n                    <el-form-item label=\"备注\">\n                      <Editor v-model=\"formData.memo\" :min-height=\"192\"/>\n                    </el-form-item>\n      </el-form>\n              <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\" :disabled=\"formLoading\">确 定</el-button>\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n  import ImageUpload from '@/components/ImageUpload';\n  import FileUpload from '@/components/FileUpload';\n  import Editor from '@/components/Editor';\n      export default {\n    name: \"StudentForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n                    },\n    data() {\n      return {\n        // 弹出层标题\n        dialogTitle: \"\",\n        // 是否显示弹出层\n        dialogVisible: false,\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: {\n                            id: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        },\n        // 表单校验\n        formRules: {\n                        name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n                        description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n                        birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n                        sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n                        enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n                        avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n                        video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],\n                        memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n        },\n                        };\n    },\n    methods: {\n      /** 打开弹窗 */\n     async open(id) {\n        this.dialogVisible = true;\n        this.reset();\n        // 修改时，设置数据\n        if (id) {\n          this.formLoading = true;\n          try {\n            const res = await StudentApi.getStudent(id);\n            this.formData = res.data;\n            this.title = \"修改学生\";\n          } finally {\n            this.formLoading = false;\n          }\n        }\n        this.title = \"新增学生\";\n              },\n      /** 提交按钮 */\n      async submitForm() {\n        // 校验主表\n        await this.$refs[\"formRef\"].validate();\n                  this.formLoading = true;\n        try {\n          const data = this.formData;\n                  // 修改的提交\n          if (data.id) {\n            await StudentApi.updateStudent(data);\n            this.$modal.msgSuccess(\"修改成功\");\n            this.dialogVisible = false;\n            this.$emit('success');\n            return;\n          }\n          // 添加的提交\n          await StudentApi.createStudent(data);\n          this.$modal.msgSuccess(\"新增成功\");\n          this.dialogVisible = false;\n          this.$emit('success');\n        } finally {\n          this.formLoading = false;\n        }\n      },\n                      /** 表单重置 */\n      reset() {\n        this.formData = {\n                            id: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        };\n        this.resetForm(\"formRef\");\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherForm",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 对话框(添加 / 修改) -->\n    <el-dialog :title=\"dialogTitle\" :visible.sync=\"dialogVisible\" width=\"45%\" v-dialogDrag append-to-body>\n      <el-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\" v-loading=\"formLoading\" label-width=\"100px\">\n                     <el-form-item label=\"名字\" prop=\"name\">\n                      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n                    </el-form-item>\n                    <el-form-item label=\"简介\" prop=\"description\">\n                      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入内容\" />\n                    </el-form-item>\n                    <el-form-item label=\"出生日期\" prop=\"birthday\">\n                      <el-date-picker clearable v-model=\"formData.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                    </el-form-item>\n                    <el-form-item label=\"性别\" prop=\"sex\">\n                      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                       :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                      </el-select>\n                    </el-form-item>\n                    <el-form-item label=\"是否有效\" prop=\"enabled\">\n                      <el-radio-group v-model=\"formData.enabled\">\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                      :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                      </el-radio-group>\n                    </el-form-item>\n                    <el-form-item label=\"头像\" prop=\"avatar\">\n                      <ImageUpload v-model=\"formData.avatar\"/>\n                    </el-form-item>\n                    <el-form-item label=\"附件\" prop=\"video\">\n                      <FileUpload v-model=\"formData.video\"/>\n                    </el-form-item>\n                    <el-form-item label=\"备注\" prop=\"memo\">\n                      <editor v-model=\"formData.memo\" :min-height=\"192\"/>\n                    </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\" :disabled=\"formLoading\">确 定</el-button>\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n      import ImageUpload from '@/components/ImageUpload';\n      import FileUpload from '@/components/FileUpload';\n      import Editor from '@/components/Editor';\n  export default {\n    name: \"StudentTeacherForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n    },\n    data() {\n      return {\n        // 弹出层标题\n        dialogTitle: \"\",\n        // 是否显示弹出层\n        dialogVisible: false,\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: {\n                            id: undefined,\n                            studentId: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        },\n        // 表单校验\n        formRules: {\n                        studentId: [{ required: true, message: \"学生编号不能为空\", trigger: \"blur\" }],\n                        name: [{ required: true, message: \"名字不能为空\", trigger: \"blur\" }],\n                        description: [{ required: true, message: \"简介不能为空\", trigger: \"blur\" }],\n                        birthday: [{ required: true, message: \"出生日期不能为空\", trigger: \"blur\" }],\n                        sex: [{ required: true, message: \"性别不能为空\", trigger: \"change\" }],\n                        enabled: [{ required: true, message: \"是否有效不能为空\", trigger: \"blur\" }],\n                        avatar: [{ required: true, message: \"头像不能为空\", trigger: \"blur\" }],\n                        memo: [{ required: true, message: \"备注不能为空\", trigger: \"blur\" }],\n        },\n      };\n    },\n    methods: {\n      /** 打开弹窗 */\n      async open(id, studentId) {\n        this.dialogVisible = true;\n        this.reset();\n        this.formData.studentId = studentId;\n        // 修改时，设置数据\n        if (id) {\n          this.formLoading = true;\n          try {\n            const res = await StudentApi.getStudentTeacher(id);\n            this.formData = res.data;\n            this.dialogTitle = \"修改学生班主任\";\n          } finally {\n            this.formLoading = false;\n          }\n        }\n        this.dialogTitle = \"新增学生班主任\";\n      },\n      /** 提交按钮 */\n      async submitForm() {\n        await this.$refs[\"formRef\"].validate();\n        this.formLoading = true;\n        try {\n            const data = this.formData;\n            // 修改的提交\n            if (data.id) {\n            await  StudentApi.updateStudentTeacher(data);\n            this.$modal.msgSuccess(\"修改成功\");\n            this.dialogVisible = false;\n            this.$emit('success');\n              return;\n            }\n            // 添加的提交\n              await StudentApi.createStudentTeacher(data);\n              this.$modal.msgSuccess(\"新增成功\");\n              this.dialogVisible = false;\n              this.$emit('success');\n        }finally {\n          this.formLoading = false;\n        }\n      },\n      /** 表单重置 */\n      reset() {\n        this.formData = {\n                            id: undefined,\n                            studentId: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        };\n        this.resetForm(\"formRef\");\n      },\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherList",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 操作工具栏 -->\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"openForm(undefined)\"\n                   v-hasPermi=\"['infra:student:create']\">新增</el-button>\n      </el-col>\n    </el-row>\n            <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n                <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n                 <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n                <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n                <el-table-column label=\"出生日期\" align=\"center\" prop=\"birthday\" width=\"180\">\n                  <template v-slot=\"scope\">\n                    <span>{{ parseTime(scope.row.birthday) }}</span>\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n                  <template v-slot=\"scope\">\n                    <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n                  <template v-slot=\"scope\">\n                    <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n                <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n                <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n                <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n                  <template v-slot=\"scope\">\n                    <span>{{ parseTime(scope.row.createTime) }}</span>\n                  </template>\n                </el-table-column>\n    <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n      <template v-slot=\"scope\">\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.id)\"\n                   v-hasPermi=\"['infra:student:update']\">修改</el-button>\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                   v-hasPermi=\"['infra:student:delete']\">删除</el-button>\n      </template>\n    </el-table-column>\n  </el-table>\n    <!-- 分页组件 -->\n    <pagination v-show=\"total > 0\" :total=\"total\" :page.sync=\"queryParams.pageNo\" :limit.sync=\"queryParams.pageSize\"\n                @pagination=\"getList\"/>\n  <!-- 对话框(添加 / 修改) -->\n  <StudentTeacherForm ref=\"formRef\" @success=\"getList\" />\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n  import StudentTeacherForm from './StudentTeacherForm.vue';\n  export default {\n    name: \"StudentTeacherList\",\n    components: {\n       StudentTeacherForm\n    },\n    props:[\n      'studentId'\n    ],// 学生编号（主表的关联字段）\n    data() {\n      return {\n        // 遮罩层\n        loading: true,\n        // 列表的数据\n        list: [],\n        // 列表的总页数\n        total: 0,\n        // 查询参数\n        queryParams: {\n          pageNo: 1,\n          pageSize: 10,\n          studentId: undefined\n        }\n      };\n    },\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n        studentId:{\n            handler(val) {\n              this.queryParams.studentId = val;\n              if (val){\n                this.handleQuery();\n              }\n            },\n            immediate: true\n      }\n    },\n    methods: {\n      /** 查询列表 */\n      async getList() {\n        try {\n          this.loading = true;\n            const res = await StudentApi.getStudentTeacherPage(this.queryParams);\n            this.list = res.data.list;\n            this.total = res.data.total;\n        } finally {\n          this.loading = false;\n        }\n      },\n      /** 搜索按钮操作 */\n      handleQuery() {\n        this.queryParams.pageNo = 1;\n        this.getList();\n      },\n      /** 添加/修改操作 */\n      openForm(id) {\n        if (!this.studentId) {\n          this.$modal.msgError('请选择一个学生');\n          return;\n        }\n        this.$refs[\"formRef\"].open(id, this.studentId);\n      },\n      /** 删除按钮操作 */\n      async handleDelete(row) {\n        const id = row.id;\n        await this.$modal.confirm('是否确认删除学生编号为\"' + id + '\"的数据项?');\n        try {\n          await StudentApi.deleteStudentTeacher(id);\n          await this.getList();\n          this.$modal.msgSuccess(\"删除成功\");\n        } catch {}\n      },\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/vue/index",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 搜索工作栏 -->\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"queryParams.name\" placeholder=\"请输入名字\" clearable @keyup.enter.native=\"handleQuery\"/>\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker clearable v-model=\"queryParams.birthday\" type=\"date\" value-format=\"yyyy-MM-dd\" placeholder=\"选择出生日期\" />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"queryParams.sex\" placeholder=\"请选择性别\" clearable size=\"small\">\n          <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                       :key=\"dict.value\" :label=\"dict.label\" :value=\"dict.value\"/>\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-select v-model=\"queryParams.enabled\" placeholder=\"请选择是否有效\" clearable size=\"small\">\n          <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                       :key=\"dict.value\" :label=\"dict.label\" :value=\"dict.value\"/>\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker v-model=\"queryParams.createTime\" style=\"width: 240px\" value-format=\"yyyy-MM-dd HH:mm:ss\" type=\"daterange\"\n                        range-separator=\"-\" start-placeholder=\"开始日期\" end-placeholder=\"结束日期\" :default-time=\"['00:00:00', '23:59:59']\" />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <!-- 操作工具栏 -->\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"openForm(undefined)\"\n                   v-hasPermi=\"['infra:student:create']\">新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button type=\"warning\" plain icon=\"el-icon-download\" size=\"mini\" @click=\"handleExport\" :loading=\"exportLoading\"\n                   v-hasPermi=\"['infra:student:export']\">导出</el-button>\n      </el-col>\n              <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n            <el-table\n        v-loading=\"loading\"\n        :data=\"list\"\n        :stripe=\"true\"\n        :highlight-current-row=\"true\"\n        :show-overflow-tooltip=\"true\"\n        @current-change=\"handleCurrentChange\"\n      >\n                      <el-table-column label=\"编号\" align=\"center\" prop=\"id\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.id\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.name\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.description\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"出生日期\" align=\"center\" prop=\"birthday\" width=\"180\">\n        <template v-slot=\"scope\">\n          <span>{{ parseTime(scope.row.birthday) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.avatar\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.video\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.memo\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template v-slot=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template v-slot=\"scope\">\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.id)\"\n                     v-hasPermi=\"['infra:student:update']\">修改</el-button>\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                     v-hasPermi=\"['infra:student:delete']\">删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页组件 -->\n    <pagination v-show=\"total > 0\" :total=\"total\" :page.sync=\"queryParams.pageNo\" :limit.sync=\"queryParams.pageSize\"\n                @pagination=\"getList\"/>\n    <!-- 对话框(添加 / 修改) -->\n    <StudentForm ref=\"formRef\" @success=\"getList\" />\n      <!-- 子表的列表 -->\n      <el-tabs v-model=\"subTabsName\">\n            <el-tab-pane label=\"学生联系人\" name=\"studentContact\">\n              <StudentContactList v-if=\"currentRow.id\" :student-id=\"currentRow.id\" />\n            </el-tab-pane>\n            <el-tab-pane label=\"学生班主任\" name=\"studentTeacher\">\n              <StudentTeacherList v-if=\"currentRow.id\" :student-id=\"currentRow.id\" />\n            </el-tab-pane>\n      </el-tabs>\n  </div>\n</template>\n\n<script>\nimport * as StudentApi from '@/api/infra/demo';\nimport StudentForm from './StudentForm.vue';\n    import StudentContactList from './components/StudentContactList.vue';\n    import StudentTeacherList from './components/StudentTeacherList.vue';\nexport default {\n  name: \"Student\",\n  components: {\n          StudentForm,\n          StudentContactList,\n          StudentTeacherList,\n  },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 导出遮罩层\n      exportLoading: false,\n      // 显示搜索条件\n      showSearch: true,\n              // 总条数\n        total: 0,\n      // 学生列表\n      list: [],\n      // 是否展开，默认全部展开\n      isExpandAll: true,\n      // 重新渲染表格状态\n      refreshTable: true,\n      // 选中行\n      currentRow: {},\n      // 查询参数\n      queryParams: {\n                    pageNo: 1,\n            pageSize: 10,\n        name: null,\n        birthday: null,\n        sex: null,\n        enabled: null,\n        createTime: [],\n      },\n                      /** 子表的列表 */\n              subTabsName: 'studentContact'\n    };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询列表 */\n    async getList() {\n      try {\n      this.loading = true;\n              const res = await StudentApi.getStudentPage(this.queryParams);\n        this.list = res.data.list;\n        this.total = res.data.total;\n      } finally {\n        this.loading = false;\n      }\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNo = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 添加/修改操作 */\n    openForm(id) {\n      this.$refs[\"formRef\"].open(id);\n    },\n    /** 删除按钮操作 */\n    async handleDelete(row) {\n      const id = row.id;\n      await this.$modal.confirm('是否确认删除学生编号为\"' + id + '\"的数据项?')\n      try {\n       await StudentApi.deleteStudent(id);\n       await this.getList();\n       this.$modal.msgSuccess(\"删除成功\");\n      } catch {}\n    },\n    /** 导出按钮操作 */\n    async handleExport() {\n      await this.$modal.confirm('是否确认导出所有学生数据项?');\n      try {\n        this.exportLoading = true;\n        const res = await StudentApi.exportStudentExcel(this.queryParams);\n        this.$download.excel(res.data, '学生.xls');\n      } catch {\n      } finally {\n        this.exportLoading = false;\n      }\n    },\n              /** 选中行操作 */\n        handleCurrentChange(row) {\n         this.currentRow = row;\n          /** 子表的列表 */\n          this.subTabsName = 'studentContact';\n        },\n        }\n};\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_erp/xml/InfraStudentMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraStudentPageReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentSaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\": \"js/index\",\n  \"filePath\": \"yudao-ui-admin-vue2/src/api/infra/demo/index.js\"\n}, {\n  \"contentPath\" : \"vue/StudentForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactList\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherList\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 学生 TODO 补充编号 ==========\r\nErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生不存在\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生联系人 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_contact\")\n@KeySequence(\"infra_student_contact_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentContactDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生联系人 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {\n\n    default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {\n        return selectList(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/student\")\n@Validated\npublic class InfraStudentController {\n\n    @Resource\n    private InfraStudentService studentService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {\n        return success(studentService.createStudent(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {\n        studentService.updateStudent(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudent(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudent(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentRespVO> getStudent(@RequestParam(\"id\") Long id) {\n        InfraStudentDO student = studentService.getStudent(id);\n        return success(BeanUtils.toBean(student, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {\n        PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", InfraStudentRespVO.class,\n                        BeanUtils.toBean(list, InfraStudentRespVO.class));\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @GetMapping(\"/student-contact/list-by-student-id\")\n    @Operation(summary = \"获得学生联系人列表\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentContactListByStudentId(studentId));\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @GetMapping(\"/student-teacher/get-by-student-id\")\n    @Operation(summary = \"获得学生班主任\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentTeacherByStudentId(studentId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student\")\n@KeySequence(\"infra_student_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {\n\n    default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()\n                .likeIfPresent(InfraStudentDO::getName, reqVO.getName())\n                .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())\n                .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())\n                .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(InfraStudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentPageReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class InfraStudentPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n    @Schema(description = \"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", example = \"1\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", example = \"true\")\n    private Boolean enabled;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.idev.excel.annotation.*;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraStudentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"是否有效\", converter = DictConvert.class)\n    @DictFormat(\"infra_boolean_string\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @ExcelProperty(\"头像\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @ExcelProperty(\"附件\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @ExcelProperty(\"备注\")\n    private String memo;\n\n    @Schema(description = \"创建时间\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentSaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class InfraStudentSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否有效不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"头像不能为空\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @NotEmpty(message = \"附件不能为空\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @NotEmpty(message = \"备注不能为空\")\n    private String memo;\n\n    @Schema(description = \"学生联系人列表\")\n    private List<InfraStudentContactDO> studentContacts;\n\n    @Schema(description = \"学生班主任\")\n    private InfraStudentTeacherDO studentTeacher;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraStudentService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteStudent(Long id);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    InfraStudentDO getStudent(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);\n\n    // ==================== 子表（学生联系人） ====================\n\n    /**\n     * 获得学生联系人列表\n     *\n     * @param studentId 学生编号\n     * @return 学生联系人列表\n     */\n    List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId);\n\n    // ==================== 子表（学生班主任） ====================\n\n    /**\n     * 获得学生班主任\n     *\n     * @param studentId 学生编号\n     * @return 学生班主任\n     */\n    InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraStudentServiceImpl implements InfraStudentService {\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n    @Resource\n    private InfraStudentContactMapper studentContactMapper;\n    @Resource\n    private InfraStudentTeacherMapper studentTeacherMapper;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createStudent(InfraStudentSaveReqVO createReqVO) {\n        // 插入\n        InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);\n        studentMapper.insert(student);\n\n        // 插入子表\n        createStudentContactList(student.getId(), createReqVO.getStudentContacts());\n        createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());\n        // 返回\n        return student.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStudent(InfraStudentSaveReqVO updateReqVO) {\n        // 校验存在\n        validateStudentExists(updateReqVO.getId());\n        // 更新\n        InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);\n        studentMapper.updateById(updateObj);\n\n        // 更新子表\n        updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());\n        updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStudent(Long id) {\n        // 校验存在\n        validateStudentExists(id);\n        // 删除\n        studentMapper.deleteById(id);\n\n        // 删除子表\n        deleteStudentContactByStudentId(id);\n        deleteStudentTeacherByStudentId(id);\n    }\n\n    private void validateStudentExists(Long id) {\n        if (studentMapper.selectById(id) == null) {\n            throw exception(STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public InfraStudentDO getStudent(Long id) {\n        return studentMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {\n        return studentMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @Override\n    public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) {\n        return studentContactMapper.selectListByStudentId(studentId);\n    }\n\n    private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) {\n        list.forEach(o -> o.setStudentId(studentId));\n        studentContactMapper.insertBatch(list);\n    }\n\n    private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) {\n        deleteStudentContactByStudentId(studentId);\n\t\tlist.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新\n        createStudentContactList(studentId, list);\n    }\n\n    private void deleteStudentContactByStudentId(Long studentId) {\n        studentContactMapper.deleteByStudentId(studentId);\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @Override\n    public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {\n        return studentTeacherMapper.selectByStudentId(studentId);\n    }\n\n    private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {\n        if (studentTeacher == null) {\n            return;\n        }\n        studentTeacher.setStudentId(studentId);\n        studentTeacherMapper.insert(studentTeacher);\n    }\n\n    private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {\n        if (studentTeacher == null) {\n\t\t\treturn;\n        }\n        studentTeacher.setStudentId(studentId);\n        studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下：updateTime 不更新\n        studentTeacherMapper.insertOrUpdate(studentTeacher);\n    }\n\n    private void deleteStudentTeacherByStudentId(Long studentId) {\n        studentTeacherMapper.deleteByStudentId(studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraStudentServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraStudentServiceImpl.class)\npublic class InfraStudentServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraStudentServiceImpl studentService;\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Test\n    public void testCreateStudent_success() {\n        // 准备参数\n        InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);\n\n        // 调用\n        Long studentId = studentService.createStudent(createReqVO);\n        // 断言\n        assertNotNull(studentId);\n        // 校验记录的属性是否正确\n        InfraStudentDO student = studentMapper.selectById(studentId);\n        assertPojoEquals(createReqVO, student, \"id\");\n    }\n\n    @Test\n    public void testUpdateStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {\n            o.setId(dbStudent.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        studentService.updateStudent(updateReqVO);\n        // 校验是否更新正确\n        InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, student);\n    }\n\n    @Test\n    public void testUpdateStudent_notExists() {\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbStudent.getId();\n\n        // 调用\n        studentService.deleteStudent(id);\n       // 校验数据不存在了\n       assertNull(studentMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteStudent_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetStudentPage() {\n       // mock 数据\n       InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到\n           o.setName(null);\n           o.setBirthday(null);\n           o.setSex(null);\n           o.setEnabled(null);\n           o.setCreateTime(null);\n       });\n       studentMapper.insert(dbStudent);\n       // 测试 name 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));\n       // 测试 birthday 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));\n       // 测试 sex 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));\n       // 测试 enabled 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));\n       // 测试 createTime 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));\n       // 准备参数\n       InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();\n       reqVO.setName(null);\n       reqVO.setBirthday(null);\n       reqVO.setSex(null);\n       reqVO.setEnabled(null);\n       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n\n       // 调用\n       PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);\n       // 断言\n       assertEquals(1, pageResult.getTotal());\n       assertEquals(1, pageResult.getList().size());\n       assertPojoEquals(dbStudent, pageResult.getList().get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生班主任 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_teacher\")\n@KeySequence(\"infra_student_teacher_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentTeacherDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生班主任 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {\n\n    default InfraStudentTeacherDO selectByStudentId(Long studentId) {\n        return selectOne(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/js/index",
    "content": "import request from '@/utils/request'\n\n// 创建学生\nexport function createStudent(data) {\n  return request({\n    url: '/infra/student/create',\n    method: 'post',\n    data: data\n  })\n}\n\n// 更新学生\nexport function updateStudent(data) {\n  return request({\n    url: '/infra/student/update',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除学生\nexport function deleteStudent(id) {\n  return request({\n    url: '/infra/student/delete?id=' + id,\n    method: 'delete'\n  })\n}\n\n// 获得学生\nexport function getStudent(id) {\n  return request({\n    url: '/infra/student/get?id=' + id,\n    method: 'get'\n  })\n}\n\n// 获得学生分页\nexport function getStudentPage(params) {\n  return request({\n    url: '/infra/student/page',\n    method: 'get',\n    params\n  })\n}\n// 导出学生 Excel\nexport function exportStudentExcel(params) {\n  return request({\n    url: '/infra/student/export-excel',\n    method: 'get',\n    params,\n    responseType: 'blob'\n  })\n}\n\n// ==================== 子表（学生联系人） ====================\n  \n    // 获得学生联系人列表\n    export function getStudentContactListByStudentId(studentId) {\n      return request({\n        url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId,\n        method: 'get'\n      })\n    }\n  \n// ==================== 子表（学生班主任） ====================\n  \n    // 获得学生班主任\n    export function getStudentTeacherByStudentId(studentId) {\n      return request({\n        url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId,\n        method: 'get'\n      })\n    }\n  "
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_student\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" varchar NOT NULL,\n    \"birthday\" varchar NOT NULL,\n    \"sex\" int NOT NULL,\n    \"enabled\" bit NOT NULL,\n    \"avatar\" varchar NOT NULL,\n    \"video\" varchar NOT NULL,\n    \"memo\" varchar NOT NULL,\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (\"id\")\n) COMMENT '学生表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_student\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '学生管理', '', 2, 0, 888,\r\n    'student', '', 'infra/demo/index', 0, 'InfraStudent'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生查询', 'infra:student:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生创建', 'infra:student:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生更新', 'infra:student:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生删除', 'infra:student:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生导出', 'infra:student:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm",
    "content": "<template>\n  <div class=\"app-container\">\n      <el-form\n        ref=\"formRef\"\n        :model=\"formData\"\n        :rules=\"formRules\"\n        v-loading=\"formLoading\"\n        label-width=\"0px\"\n        :inline-message=\"true\"\n      >\n        <el-table :data=\"formData\" class=\"-mt-10px\">\n          <el-table-column label=\"序号\" type=\"index\" width=\"100\" />\n                       <el-table-column label=\"名字\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.name`\" :rules=\"formRules.name\" class=\"mb-0px!\">\n                            <el-input v-model=\"row.name\" placeholder=\"请输入名字\" />\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"简介\" min-width=\"200\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.description`\" :rules=\"formRules.description\" class=\"mb-0px!\">\n                            <el-input v-model=\"row.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"出生日期\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.birthday`\" :rules=\"formRules.birthday\" class=\"mb-0px!\">\n                            <el-date-picker clearable v-model=\"row.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"性别\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.sex`\" :rules=\"formRules.sex\" class=\"mb-0px!\">\n                            <el-select v-model=\"row.sex\" placeholder=\"请选择性别\">\n                                  <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                             :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                            </el-select>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"是否有效\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.enabled`\" :rules=\"formRules.enabled\" class=\"mb-0px!\">\n                            <el-radio-group v-model=\"row.enabled\">\n                                  <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                            :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                            </el-radio-group>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"头像\" min-width=\"200\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.avatar`\" :rules=\"formRules.avatar\" class=\"mb-0px!\">\n                            <ImageUpload v-model=\"row.avatar\"/>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"附件\" min-width=\"200\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.video`\" :rules=\"formRules.video\" class=\"mb-0px!\">\n                            <FileUpload v-model=\"row.video\"/>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"备注\" min-width=\"400\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.memo`\" :rules=\"formRules.memo\" class=\"mb-0px!\">\n                            <Editor v-model=\"row.memo\" :min-height=\"192\"/>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n          <el-table-column align=\"center\" fixed=\"right\" label=\"操作\" width=\"60\">\n            <template v-slot=\"{ $index }\">\n              <el-link @click=\"handleDelete($index)\">—</el-link>\n            </template>\n          </el-table-column>\n        </el-table>\n      </el-form>\n      <el-row justify=\"center\" class=\"mt-3\">\n        <el-button @click=\"handleAdd\" round>+ 添加学生联系人</el-button>\n      </el-row>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n      import ImageUpload from '@/components/ImageUpload';\n      import FileUpload from '@/components/FileUpload';\n      import Editor from '@/components/Editor';\n  export default {\n    name: \"StudentContactForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n    },\n    props:[\n      'studentId'\n    ],// 学生编号（主表的关联字段）\n    data() {\n      return {\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: [],\n        // 表单校验\n        formRules: {\n                        studentId: [{ required: true, message: \"学生编号不能为空\", trigger: \"blur\" }],\n                        name: [{ required: true, message: \"名字不能为空\", trigger: \"blur\" }],\n                        description: [{ required: true, message: \"简介不能为空\", trigger: \"blur\" }],\n                        birthday: [{ required: true, message: \"出生日期不能为空\", trigger: \"blur\" }],\n                        sex: [{ required: true, message: \"性别不能为空\", trigger: \"change\" }],\n                        enabled: [{ required: true, message: \"是否有效不能为空\", trigger: \"blur\" }],\n                        avatar: [{ required: true, message: \"头像不能为空\", trigger: \"blur\" }],\n                        memo: [{ required: true, message: \"备注不能为空\", trigger: \"blur\" }],\n        },\n      };\n    },\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n      studentId:{\n        handler(val) {\n          // 1. 重置表单\n              this.formData = []\n          // 2. val 非空，则加载数据\n          if (!val) {\n            return;\n          }\n          try {\n            this.formLoading = true;\n            // 这里还是需要获取一下 this 的不然取不到 formData\n            const that = this;\n            StudentApi.getStudentContactListByStudentId(val).then(function (res){\n              that.formData = res.data;\n            })\n          } finally {\n            this.formLoading = false;\n          }\n        },\n        immediate: true\n      }\n    },\n    methods: {\n          /** 新增按钮操作 */\n          handleAdd() {\n            const row = {\n                                id: undefined,\n                                studentId: undefined,\n                                name: undefined,\n                                description: undefined,\n                                birthday: undefined,\n                                sex: undefined,\n                                enabled: undefined,\n                                avatar: undefined,\n                                video: undefined,\n                                memo: undefined,\n            }\n            row.studentId = this.studentId;\n            this.formData.push(row);\n          },\n          /** 删除按钮操作 */\n          handleDelete(index) {\n            this.formData.splice(index, 1);\n          },\n      /** 表单校验 */\n      validate(){\n        return this.$refs[\"formRef\"].validate();\n      },\n      /** 表单值 */\n      getData(){\n        return this.formData;\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList",
    "content": "<template>\n  <div class=\"app-container\">\n            <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n                <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n                 <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n                <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n                <el-table-column label=\"出生日期\" align=\"center\" prop=\"birthday\" width=\"180\">\n                  <template v-slot=\"scope\">\n                    <span>{{ parseTime(scope.row.birthday) }}</span>\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n                  <template v-slot=\"scope\">\n                    <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n                  <template v-slot=\"scope\">\n                    <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n                <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n                <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n                <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n                  <template v-slot=\"scope\">\n                    <span>{{ parseTime(scope.row.createTime) }}</span>\n                  </template>\n                </el-table-column>\n    <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n      <template v-slot=\"scope\">\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.id)\"\n                   v-hasPermi=\"['infra:student:update']\">修改</el-button>\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                   v-hasPermi=\"['infra:student:delete']\">删除</el-button>\n      </template>\n    </el-table-column>\n  </el-table>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n  export default {\n    name: \"StudentContactList\",\n    props:[\n      'studentId'\n    ],// 学生编号（主表的关联字段）\n    data() {\n      return {\n        // 遮罩层\n        loading: true,\n        // 列表的数据\n        list: [],\n      };\n    },\n    created() {\n      this.getList();\n    },\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n        studentId:{\n            handler(val) {\n              this.queryParams.studentId = val;\n              if (val){\n                this.handleQuery();\n              }\n            },\n            immediate: true\n      }\n    },\n    methods: {\n      /** 查询列表 */\n      async getList() {\n        try {\n          this.loading = true;\n                const res = await StudentApi.getStudentContactListByStudentId(this.studentId);\n                this.list = res.data;\n        } finally {\n          this.loading = false;\n        }\n      },\n      /** 搜索按钮操作 */\n      handleQuery() {\n        this.queryParams.pageNo = 1;\n        this.getList();\n      },\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 对话框(添加 / 修改) -->\n    <el-dialog :title=\"dialogTitle\" :visible.sync=\"dialogVisible\" width=\"45%\" v-dialogDrag append-to-body>\n      <el-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\" v-loading=\"formLoading\" label-width=\"100px\">\n                    <el-form-item label=\"名字\" prop=\"name\">\n                      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n                    </el-form-item>\n                    <el-form-item label=\"简介\" prop=\"description\">\n                      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入内容\" />\n                    </el-form-item>\n                    <el-form-item label=\"出生日期\" prop=\"birthday\">\n                      <el-date-picker clearable v-model=\"formData.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                    </el-form-item>\n                    <el-form-item label=\"性别\" prop=\"sex\">\n                      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                       :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                      </el-select>\n                    </el-form-item>\n                    <el-form-item label=\"是否有效\" prop=\"enabled\">\n                      <el-radio-group v-model=\"formData.enabled\">\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                      :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                      </el-radio-group>\n                    </el-form-item>\n                    <el-form-item label=\"头像\">\n                      <ImageUpload v-model=\"formData.avatar\"/>\n                    </el-form-item>\n                    <el-form-item label=\"附件\">\n                      <FileUpload v-model=\"formData.video\"/>\n                    </el-form-item>\n                    <el-form-item label=\"备注\">\n                      <Editor v-model=\"formData.memo\" :min-height=\"192\"/>\n                    </el-form-item>\n      </el-form>\n                  <!-- 子表的表单 -->\n          <el-tabs v-model=\"subTabsName\">\n                <el-tab-pane label=\"学生联系人\" name=\"studentContact\">\n                  <StudentContactForm ref=\"studentContactFormRef\" :student-id=\"formData.id\" />\n                </el-tab-pane>\n                <el-tab-pane label=\"学生班主任\" name=\"studentTeacher\">\n                  <StudentTeacherForm ref=\"studentTeacherFormRef\" :student-id=\"formData.id\" />\n                </el-tab-pane>\n          </el-tabs>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\" :disabled=\"formLoading\">确 定</el-button>\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n  import ImageUpload from '@/components/ImageUpload';\n  import FileUpload from '@/components/FileUpload';\n  import Editor from '@/components/Editor';\n          import StudentContactForm from './components/StudentContactForm.vue'\n      import StudentTeacherForm from './components/StudentTeacherForm.vue'\n  export default {\n    name: \"StudentForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n                               StudentContactForm,\n               StudentTeacherForm,\n    },\n    data() {\n      return {\n        // 弹出层标题\n        dialogTitle: \"\",\n        // 是否显示弹出层\n        dialogVisible: false,\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: {\n                            id: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        },\n        // 表单校验\n        formRules: {\n                        name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n                        description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n                        birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n                        sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n                        enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n                        avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n                        video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],\n                        memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n        },\n                              /** 子表的表单 */\n             subTabsName: 'studentContact'\n      };\n    },\n    methods: {\n      /** 打开弹窗 */\n     async open(id) {\n        this.dialogVisible = true;\n        this.reset();\n        // 修改时，设置数据\n        if (id) {\n          this.formLoading = true;\n          try {\n            const res = await StudentApi.getStudent(id);\n            this.formData = res.data;\n            this.title = \"修改学生\";\n          } finally {\n            this.formLoading = false;\n          }\n        }\n        this.title = \"新增学生\";\n              },\n      /** 提交按钮 */\n      async submitForm() {\n        // 校验主表\n        await this.$refs[\"formRef\"].validate();\n                          // 校验子表\n                    try {\n                                            await this.$refs['studentContactFormRef'].validate();\n                    } catch (e) {\n                      this.subTabsName = 'studentContact';\n                      return;\n                    }\n                    try {\n                                            await this.$refs['studentTeacherFormRef'].validate();\n                    } catch (e) {\n                      this.subTabsName = 'studentTeacher';\n                      return;\n                    }\n        this.formLoading = true;\n        try {\n          const data = this.formData;\n                    // 拼接子表的数据\n              data.studentContacts = this.$refs['studentContactFormRef'].getData();\n              data.studentTeacher = this.$refs['studentTeacherFormRef'].getData();\n          // 修改的提交\n          if (data.id) {\n            await StudentApi.updateStudent(data);\n            this.$modal.msgSuccess(\"修改成功\");\n            this.dialogVisible = false;\n            this.$emit('success');\n            return;\n          }\n          // 添加的提交\n          await StudentApi.createStudent(data);\n          this.$modal.msgSuccess(\"新增成功\");\n          this.dialogVisible = false;\n          this.$emit('success');\n        } finally {\n          this.formLoading = false;\n        }\n      },\n                      /** 表单重置 */\n      reset() {\n        this.formData = {\n                            id: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        };\n        this.resetForm(\"formRef\");\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm",
    "content": "<template>\n  <div class=\"app-container\">\n      <el-form\n        ref=\"formRef\"\n        :model=\"formData\"\n        :rules=\"formRules\"\n        label-width=\"100px\"\n        v-loading=\"formLoading\"\n      >\n                     <el-form-item label=\"名字\" prop=\"name\">\n                      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n                    </el-form-item>\n                    <el-form-item label=\"简介\" prop=\"description\">\n                      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n                    </el-form-item>\n                    <el-form-item label=\"出生日期\" prop=\"birthday\">\n                      <el-date-picker clearable v-model=\"formData.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                    </el-form-item>\n                    <el-form-item label=\"性别\" prop=\"sex\">\n                      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                       :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                      </el-select>\n                    </el-form-item>\n                    <el-form-item label=\"是否有效\" prop=\"enabled\">\n                      <el-radio-group v-model=\"formData.enabled\">\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                      :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                      </el-radio-group>\n                    </el-form-item>\n                    <el-form-item label=\"头像\">\n                      <ImageUpload v-model=\"formData.avatar\"/>\n                    </el-form-item>\n                    <el-form-item label=\"附件\">\n                      <FileUpload v-model=\"formData.video\"/>\n                    </el-form-item>\n                    <el-form-item label=\"备注\">\n                      <Editor v-model=\"formData.memo\" :min-height=\"192\"/>\n                    </el-form-item>\n      </el-form>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n      import ImageUpload from '@/components/ImageUpload';\n      import FileUpload from '@/components/FileUpload';\n      import Editor from '@/components/Editor';\n  export default {\n    name: \"StudentTeacherForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n    },\n    props:[\n      'studentId'\n    ],// 学生编号（主表的关联字段）\n    data() {\n      return {\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: [],\n        // 表单校验\n        formRules: {\n                        studentId: [{ required: true, message: \"学生编号不能为空\", trigger: \"blur\" }],\n                        name: [{ required: true, message: \"名字不能为空\", trigger: \"blur\" }],\n                        description: [{ required: true, message: \"简介不能为空\", trigger: \"blur\" }],\n                        birthday: [{ required: true, message: \"出生日期不能为空\", trigger: \"blur\" }],\n                        sex: [{ required: true, message: \"性别不能为空\", trigger: \"change\" }],\n                        enabled: [{ required: true, message: \"是否有效不能为空\", trigger: \"blur\" }],\n                        avatar: [{ required: true, message: \"头像不能为空\", trigger: \"blur\" }],\n                        memo: [{ required: true, message: \"备注不能为空\", trigger: \"blur\" }],\n        },\n      };\n    },\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n      studentId:{\n        handler(val) {\n          // 1. 重置表单\n              this.formData = {\n                                  id: undefined,\n                                  studentId: undefined,\n                                  name: undefined,\n                                  description: undefined,\n                                  birthday: undefined,\n                                  sex: undefined,\n                                  enabled: undefined,\n                                  avatar: undefined,\n                                  video: undefined,\n                                  memo: undefined,\n              }\n          // 2. val 非空，则加载数据\n          if (!val) {\n            return;\n          }\n          try {\n            this.formLoading = true;\n            // 这里还是需要获取一下 this 的不然取不到 formData\n            const that = this;\n            StudentApi.getStudentTeacherByStudentId(val).then(function (res){\n              const data = res.data;\n              if (!data) {\n                return\n              }\n              that.formData = data;\n            })\n          } finally {\n            this.formLoading = false;\n          }\n        },\n        immediate: true\n      }\n    },\n    methods: {\n      /** 表单校验 */\n      validate(){\n        return this.$refs[\"formRef\"].validate();\n      },\n      /** 表单值 */\n      getData(){\n        return this.formData;\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList",
    "content": "<template>\n  <div class=\"app-container\">\n            <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n                <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n                 <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n                <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n                <el-table-column label=\"出生日期\" align=\"center\" prop=\"birthday\" width=\"180\">\n                  <template v-slot=\"scope\">\n                    <span>{{ parseTime(scope.row.birthday) }}</span>\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n                  <template v-slot=\"scope\">\n                    <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n                  <template v-slot=\"scope\">\n                    <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n                  </template>\n                </el-table-column>\n                <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n                <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n                <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n                <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n                  <template v-slot=\"scope\">\n                    <span>{{ parseTime(scope.row.createTime) }}</span>\n                  </template>\n                </el-table-column>\n    <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n      <template v-slot=\"scope\">\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.id)\"\n                   v-hasPermi=\"['infra:student:update']\">修改</el-button>\n        <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                   v-hasPermi=\"['infra:student:delete']\">删除</el-button>\n      </template>\n    </el-table-column>\n  </el-table>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n  export default {\n    name: \"StudentTeacherList\",\n    props:[\n      'studentId'\n    ],// 学生编号（主表的关联字段）\n    data() {\n      return {\n        // 遮罩层\n        loading: true,\n        // 列表的数据\n        list: [],\n      };\n    },\n    created() {\n      this.getList();\n    },\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n        studentId:{\n            handler(val) {\n              this.queryParams.studentId = val;\n              if (val){\n                this.handleQuery();\n              }\n            },\n            immediate: true\n      }\n    },\n    methods: {\n      /** 查询列表 */\n      async getList() {\n        try {\n          this.loading = true;\n                const res = await  StudentApi.getStudentTeacherByStudentId(this.studentId);\n                const data = res.data;\n                if (!data) {\n                  return;\n                }\n                this.list.push(data);\n        } finally {\n          this.loading = false;\n        }\n      },\n      /** 搜索按钮操作 */\n      handleQuery() {\n        this.queryParams.pageNo = 1;\n        this.getList();\n      },\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/vue/index",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 搜索工作栏 -->\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"queryParams.name\" placeholder=\"请输入名字\" clearable @keyup.enter.native=\"handleQuery\"/>\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker clearable v-model=\"queryParams.birthday\" type=\"date\" value-format=\"yyyy-MM-dd\" placeholder=\"选择出生日期\" />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"queryParams.sex\" placeholder=\"请选择性别\" clearable size=\"small\">\n          <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                       :key=\"dict.value\" :label=\"dict.label\" :value=\"dict.value\"/>\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-select v-model=\"queryParams.enabled\" placeholder=\"请选择是否有效\" clearable size=\"small\">\n          <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                       :key=\"dict.value\" :label=\"dict.label\" :value=\"dict.value\"/>\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker v-model=\"queryParams.createTime\" style=\"width: 240px\" value-format=\"yyyy-MM-dd HH:mm:ss\" type=\"daterange\"\n                        range-separator=\"-\" start-placeholder=\"开始日期\" end-placeholder=\"结束日期\" :default-time=\"['00:00:00', '23:59:59']\" />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <!-- 操作工具栏 -->\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"openForm(undefined)\"\n                   v-hasPermi=\"['infra:student:create']\">新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button type=\"warning\" plain icon=\"el-icon-download\" size=\"mini\" @click=\"handleExport\" :loading=\"exportLoading\"\n                   v-hasPermi=\"['infra:student:export']\">导出</el-button>\n      </el-col>\n              <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n            <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n              <!-- 子表的列表 -->\n        <el-table-column type=\"expand\">\n          <template #default=\"scope\">\n            <el-tabs value=\"studentContact\">\n                  <el-tab-pane label=\"学生联系人\" name=\"studentContact\">\n                    <StudentContactList :student-id=\"scope.row.id\" />\n                  </el-tab-pane>\n                  <el-tab-pane label=\"学生班主任\" name=\"studentTeacher\">\n                    <StudentTeacherList :student-id=\"scope.row.id\" />\n                  </el-tab-pane>\n            </el-tabs>\n          </template>\n        </el-table-column>\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.id\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.name\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.description\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"出生日期\" align=\"center\" prop=\"birthday\" width=\"180\">\n        <template v-slot=\"scope\">\n          <span>{{ parseTime(scope.row.birthday) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.avatar\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.video\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.memo\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template v-slot=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template v-slot=\"scope\">\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.id)\"\n                     v-hasPermi=\"['infra:student:update']\">修改</el-button>\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                     v-hasPermi=\"['infra:student:delete']\">删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页组件 -->\n    <pagination v-show=\"total > 0\" :total=\"total\" :page.sync=\"queryParams.pageNo\" :limit.sync=\"queryParams.pageSize\"\n                @pagination=\"getList\"/>\n    <!-- 对话框(添加 / 修改) -->\n    <StudentForm ref=\"formRef\" @success=\"getList\" />\n    </div>\n</template>\n\n<script>\nimport * as StudentApi from '@/api/infra/demo';\nimport StudentForm from './StudentForm.vue';\n    import StudentContactList from './components/StudentContactList.vue';\n    import StudentTeacherList from './components/StudentTeacherList.vue';\nexport default {\n  name: \"Student\",\n  components: {\n          StudentForm,\n          StudentContactList,\n          StudentTeacherList,\n  },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 导出遮罩层\n      exportLoading: false,\n      // 显示搜索条件\n      showSearch: true,\n              // 总条数\n        total: 0,\n      // 学生列表\n      list: [],\n      // 是否展开，默认全部展开\n      isExpandAll: true,\n      // 重新渲染表格状态\n      refreshTable: true,\n      // 选中行\n      currentRow: {},\n      // 查询参数\n      queryParams: {\n                    pageNo: 1,\n            pageSize: 10,\n        name: null,\n        birthday: null,\n        sex: null,\n        enabled: null,\n        createTime: [],\n      },\n            };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询列表 */\n    async getList() {\n      try {\n      this.loading = true;\n              const res = await StudentApi.getStudentPage(this.queryParams);\n        this.list = res.data.list;\n        this.total = res.data.total;\n      } finally {\n        this.loading = false;\n      }\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNo = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 添加/修改操作 */\n    openForm(id) {\n      this.$refs[\"formRef\"].open(id);\n    },\n    /** 删除按钮操作 */\n    async handleDelete(row) {\n      const id = row.id;\n      await this.$modal.confirm('是否确认删除学生编号为\"' + id + '\"的数据项?')\n      try {\n       await StudentApi.deleteStudent(id);\n       await this.getList();\n       this.$modal.msgSuccess(\"删除成功\");\n      } catch {}\n    },\n    /** 导出按钮操作 */\n    async handleExport() {\n      await this.$modal.confirm('是否确认导出所有学生数据项?');\n      try {\n        this.exportLoading = true;\n        const res = await StudentApi.exportStudentExcel(this.queryParams);\n        this.$download.excel(res.data, '学生.xls');\n      } catch {\n      } finally {\n        this.exportLoading = false;\n      }\n    },\n              }\n};\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraStudentPageReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentSaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\": \"js/index\",\n  \"filePath\": \"yudao-ui-admin-vue2/src/api/infra/demo/index.js\"\n}, {\n  \"contentPath\" : \"vue/StudentForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 学生 TODO 补充编号 ==========\r\nErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生不存在\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生联系人 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_contact\")\n@KeySequence(\"infra_student_contact_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentContactDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生联系人 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {\n\n    default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {\n        return selectList(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/student\")\n@Validated\npublic class InfraStudentController {\n\n    @Resource\n    private InfraStudentService studentService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {\n        return success(studentService.createStudent(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {\n        studentService.updateStudent(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudent(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudent(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentRespVO> getStudent(@RequestParam(\"id\") Long id) {\n        InfraStudentDO student = studentService.getStudent(id);\n        return success(BeanUtils.toBean(student, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {\n        PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", InfraStudentRespVO.class,\n                        BeanUtils.toBean(list, InfraStudentRespVO.class));\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @GetMapping(\"/student-contact/list-by-student-id\")\n    @Operation(summary = \"获得学生联系人列表\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentContactListByStudentId(studentId));\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @GetMapping(\"/student-teacher/get-by-student-id\")\n    @Operation(summary = \"获得学生班主任\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentTeacherByStudentId(studentId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student\")\n@KeySequence(\"infra_student_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {\n\n    default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()\n                .likeIfPresent(InfraStudentDO::getName, reqVO.getName())\n                .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())\n                .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())\n                .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(InfraStudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class InfraStudentPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n    @Schema(description = \"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", example = \"1\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", example = \"true\")\n    private Boolean enabled;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.idev.excel.annotation.*;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraStudentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"是否有效\", converter = DictConvert.class)\n    @DictFormat(\"infra_boolean_string\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @ExcelProperty(\"头像\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @ExcelProperty(\"附件\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @ExcelProperty(\"备注\")\n    private String memo;\n\n    @Schema(description = \"创建时间\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class InfraStudentSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否有效不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"头像不能为空\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @NotEmpty(message = \"附件不能为空\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @NotEmpty(message = \"备注不能为空\")\n    private String memo;\n\n    @Schema(description = \"学生联系人列表\")\n    private List<InfraStudentContactDO> studentContacts;\n\n    @Schema(description = \"学生班主任\")\n    private InfraStudentTeacherDO studentTeacher;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraStudentService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteStudent(Long id);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    InfraStudentDO getStudent(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);\n\n    // ==================== 子表（学生联系人） ====================\n\n    /**\n     * 获得学生联系人列表\n     *\n     * @param studentId 学生编号\n     * @return 学生联系人列表\n     */\n    List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId);\n\n    // ==================== 子表（学生班主任） ====================\n\n    /**\n     * 获得学生班主任\n     *\n     * @param studentId 学生编号\n     * @return 学生班主任\n     */\n    InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraStudentServiceImpl implements InfraStudentService {\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n    @Resource\n    private InfraStudentContactMapper studentContactMapper;\n    @Resource\n    private InfraStudentTeacherMapper studentTeacherMapper;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createStudent(InfraStudentSaveReqVO createReqVO) {\n        // 插入\n        InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);\n        studentMapper.insert(student);\n\n        // 插入子表\n        createStudentContactList(student.getId(), createReqVO.getStudentContacts());\n        createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());\n        // 返回\n        return student.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStudent(InfraStudentSaveReqVO updateReqVO) {\n        // 校验存在\n        validateStudentExists(updateReqVO.getId());\n        // 更新\n        InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);\n        studentMapper.updateById(updateObj);\n\n        // 更新子表\n        updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());\n        updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStudent(Long id) {\n        // 校验存在\n        validateStudentExists(id);\n        // 删除\n        studentMapper.deleteById(id);\n\n        // 删除子表\n        deleteStudentContactByStudentId(id);\n        deleteStudentTeacherByStudentId(id);\n    }\n\n    private void validateStudentExists(Long id) {\n        if (studentMapper.selectById(id) == null) {\n            throw exception(STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public InfraStudentDO getStudent(Long id) {\n        return studentMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {\n        return studentMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @Override\n    public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) {\n        return studentContactMapper.selectListByStudentId(studentId);\n    }\n\n    private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) {\n        list.forEach(o -> o.setStudentId(studentId));\n        studentContactMapper.insertBatch(list);\n    }\n\n    private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) {\n        deleteStudentContactByStudentId(studentId);\n\t\tlist.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新\n        createStudentContactList(studentId, list);\n    }\n\n    private void deleteStudentContactByStudentId(Long studentId) {\n        studentContactMapper.deleteByStudentId(studentId);\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @Override\n    public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {\n        return studentTeacherMapper.selectByStudentId(studentId);\n    }\n\n    private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {\n        if (studentTeacher == null) {\n            return;\n        }\n        studentTeacher.setStudentId(studentId);\n        studentTeacherMapper.insert(studentTeacher);\n    }\n\n    private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {\n        if (studentTeacher == null) {\n\t\t\treturn;\n        }\n        studentTeacher.setStudentId(studentId);\n        studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下：updateTime 不更新\n        studentTeacherMapper.insertOrUpdate(studentTeacher);\n    }\n\n    private void deleteStudentTeacherByStudentId(Long studentId) {\n        studentTeacherMapper.deleteByStudentId(studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraStudentServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraStudentServiceImpl.class)\npublic class InfraStudentServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraStudentServiceImpl studentService;\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Test\n    public void testCreateStudent_success() {\n        // 准备参数\n        InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);\n\n        // 调用\n        Long studentId = studentService.createStudent(createReqVO);\n        // 断言\n        assertNotNull(studentId);\n        // 校验记录的属性是否正确\n        InfraStudentDO student = studentMapper.selectById(studentId);\n        assertPojoEquals(createReqVO, student, \"id\");\n    }\n\n    @Test\n    public void testUpdateStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {\n            o.setId(dbStudent.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        studentService.updateStudent(updateReqVO);\n        // 校验是否更新正确\n        InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, student);\n    }\n\n    @Test\n    public void testUpdateStudent_notExists() {\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbStudent.getId();\n\n        // 调用\n        studentService.deleteStudent(id);\n       // 校验数据不存在了\n       assertNull(studentMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteStudent_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetStudentPage() {\n       // mock 数据\n       InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到\n           o.setName(null);\n           o.setBirthday(null);\n           o.setSex(null);\n           o.setEnabled(null);\n           o.setCreateTime(null);\n       });\n       studentMapper.insert(dbStudent);\n       // 测试 name 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));\n       // 测试 birthday 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));\n       // 测试 sex 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));\n       // 测试 enabled 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));\n       // 测试 createTime 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));\n       // 准备参数\n       InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();\n       reqVO.setName(null);\n       reqVO.setBirthday(null);\n       reqVO.setSex(null);\n       reqVO.setEnabled(null);\n       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n\n       // 调用\n       PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);\n       // 断言\n       assertEquals(1, pageResult.getTotal());\n       assertEquals(1, pageResult.getList().size());\n       assertPojoEquals(dbStudent, pageResult.getList().get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生班主任 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_teacher\")\n@KeySequence(\"infra_student_teacher_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentTeacherDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生班主任 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {\n\n    default InfraStudentTeacherDO selectByStudentId(Long studentId) {\n        return selectOne(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/js/index",
    "content": "import request from '@/utils/request'\n\n// 创建学生\nexport function createStudent(data) {\n  return request({\n    url: '/infra/student/create',\n    method: 'post',\n    data: data\n  })\n}\n\n// 更新学生\nexport function updateStudent(data) {\n  return request({\n    url: '/infra/student/update',\n    method: 'put',\n    data: data\n  })\n}\n\n// 删除学生\nexport function deleteStudent(id) {\n  return request({\n    url: '/infra/student/delete?id=' + id,\n    method: 'delete'\n  })\n}\n\n// 获得学生\nexport function getStudent(id) {\n  return request({\n    url: '/infra/student/get?id=' + id,\n    method: 'get'\n  })\n}\n\n// 获得学生分页\nexport function getStudentPage(params) {\n  return request({\n    url: '/infra/student/page',\n    method: 'get',\n    params\n  })\n}\n// 导出学生 Excel\nexport function exportStudentExcel(params) {\n  return request({\n    url: '/infra/student/export-excel',\n    method: 'get',\n    params,\n    responseType: 'blob'\n  })\n}\n\n// ==================== 子表（学生联系人） ====================\n  \n    // 获得学生联系人列表\n    export function getStudentContactListByStudentId(studentId) {\n      return request({\n        url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId,\n        method: 'get'\n      })\n    }\n  \n// ==================== 子表（学生班主任） ====================\n  \n    // 获得学生班主任\n    export function getStudentTeacherByStudentId(studentId) {\n      return request({\n        url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId,\n        method: 'get'\n      })\n    }\n  "
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_student\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" varchar NOT NULL,\n    \"birthday\" varchar NOT NULL,\n    \"sex\" int NOT NULL,\n    \"enabled\" bit NOT NULL,\n    \"avatar\" varchar NOT NULL,\n    \"video\" varchar NOT NULL,\n    \"memo\" varchar NOT NULL,\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (\"id\")\n) COMMENT '学生表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_student\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '学生管理', '', 2, 0, 888,\r\n    'student', '', 'infra/demo/index', 0, 'InfraStudent'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生查询', 'infra:student:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生创建', 'infra:student:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生更新', 'infra:student:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生删除', 'infra:student:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生导出', 'infra:student:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm",
    "content": "<template>\n  <div class=\"app-container\">\n      <el-form\n        ref=\"formRef\"\n        :model=\"formData\"\n        :rules=\"formRules\"\n        v-loading=\"formLoading\"\n        label-width=\"0px\"\n        :inline-message=\"true\"\n      >\n        <el-table :data=\"formData\" class=\"-mt-10px\">\n          <el-table-column label=\"序号\" type=\"index\" width=\"100\" />\n                       <el-table-column label=\"名字\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.name`\" :rules=\"formRules.name\" class=\"mb-0px!\">\n                            <el-input v-model=\"row.name\" placeholder=\"请输入名字\" />\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"简介\" min-width=\"200\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.description`\" :rules=\"formRules.description\" class=\"mb-0px!\">\n                            <el-input v-model=\"row.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"出生日期\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.birthday`\" :rules=\"formRules.birthday\" class=\"mb-0px!\">\n                            <el-date-picker clearable v-model=\"row.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"性别\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.sex`\" :rules=\"formRules.sex\" class=\"mb-0px!\">\n                            <el-select v-model=\"row.sex\" placeholder=\"请选择性别\">\n                                  <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                             :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                            </el-select>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"是否有效\" min-width=\"150\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.enabled`\" :rules=\"formRules.enabled\" class=\"mb-0px!\">\n                            <el-radio-group v-model=\"row.enabled\">\n                                  <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                            :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                            </el-radio-group>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"头像\" min-width=\"200\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.avatar`\" :rules=\"formRules.avatar\" class=\"mb-0px!\">\n                            <ImageUpload v-model=\"row.avatar\"/>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"附件\" min-width=\"200\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.video`\" :rules=\"formRules.video\" class=\"mb-0px!\">\n                            <FileUpload v-model=\"row.video\"/>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n                      <el-table-column label=\"备注\" min-width=\"400\">\n                        <template v-slot=\"{ row, $index }\">\n                          <el-form-item :prop=\"`${$index}.memo`\" :rules=\"formRules.memo\" class=\"mb-0px!\">\n                            <Editor v-model=\"row.memo\" :min-height=\"192\"/>\n                          </el-form-item>\n                        </template>\n                      </el-table-column>\n          <el-table-column align=\"center\" fixed=\"right\" label=\"操作\" width=\"60\">\n            <template v-slot=\"{ $index }\">\n              <el-link @click=\"handleDelete($index)\">—</el-link>\n            </template>\n          </el-table-column>\n        </el-table>\n      </el-form>\n      <el-row justify=\"center\" class=\"mt-3\">\n        <el-button @click=\"handleAdd\" round>+ 添加学生联系人</el-button>\n      </el-row>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n      import ImageUpload from '@/components/ImageUpload';\n      import FileUpload from '@/components/FileUpload';\n      import Editor from '@/components/Editor';\n  export default {\n    name: \"StudentContactForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n    },\n    props:[\n      'studentId'\n    ],// 学生编号（主表的关联字段）\n    data() {\n      return {\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: [],\n        // 表单校验\n        formRules: {\n                        studentId: [{ required: true, message: \"学生编号不能为空\", trigger: \"blur\" }],\n                        name: [{ required: true, message: \"名字不能为空\", trigger: \"blur\" }],\n                        description: [{ required: true, message: \"简介不能为空\", trigger: \"blur\" }],\n                        birthday: [{ required: true, message: \"出生日期不能为空\", trigger: \"blur\" }],\n                        sex: [{ required: true, message: \"性别不能为空\", trigger: \"change\" }],\n                        enabled: [{ required: true, message: \"是否有效不能为空\", trigger: \"blur\" }],\n                        avatar: [{ required: true, message: \"头像不能为空\", trigger: \"blur\" }],\n                        memo: [{ required: true, message: \"备注不能为空\", trigger: \"blur\" }],\n        },\n      };\n    },\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n      studentId:{\n        handler(val) {\n          // 1. 重置表单\n              this.formData = []\n          // 2. val 非空，则加载数据\n          if (!val) {\n            return;\n          }\n          try {\n            this.formLoading = true;\n            // 这里还是需要获取一下 this 的不然取不到 formData\n            const that = this;\n            StudentApi.getStudentContactListByStudentId(val).then(function (res){\n              that.formData = res.data;\n            })\n          } finally {\n            this.formLoading = false;\n          }\n        },\n        immediate: true\n      }\n    },\n    methods: {\n          /** 新增按钮操作 */\n          handleAdd() {\n            const row = {\n                                id: undefined,\n                                studentId: undefined,\n                                name: undefined,\n                                description: undefined,\n                                birthday: undefined,\n                                sex: undefined,\n                                enabled: undefined,\n                                avatar: undefined,\n                                video: undefined,\n                                memo: undefined,\n            }\n            row.studentId = this.studentId;\n            this.formData.push(row);\n          },\n          /** 删除按钮操作 */\n          handleDelete(index) {\n            this.formData.splice(index, 1);\n          },\n      /** 表单校验 */\n      validate(){\n        return this.$refs[\"formRef\"].validate();\n      },\n      /** 表单值 */\n      getData(){\n        return this.formData;\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 对话框(添加 / 修改) -->\n    <el-dialog :title=\"dialogTitle\" :visible.sync=\"dialogVisible\" width=\"45%\" v-dialogDrag append-to-body>\n      <el-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\" v-loading=\"formLoading\" label-width=\"100px\">\n                    <el-form-item label=\"名字\" prop=\"name\">\n                      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n                    </el-form-item>\n                    <el-form-item label=\"简介\" prop=\"description\">\n                      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入内容\" />\n                    </el-form-item>\n                    <el-form-item label=\"出生日期\" prop=\"birthday\">\n                      <el-date-picker clearable v-model=\"formData.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                    </el-form-item>\n                    <el-form-item label=\"性别\" prop=\"sex\">\n                      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                       :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                      </el-select>\n                    </el-form-item>\n                    <el-form-item label=\"是否有效\" prop=\"enabled\">\n                      <el-radio-group v-model=\"formData.enabled\">\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                      :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                      </el-radio-group>\n                    </el-form-item>\n                    <el-form-item label=\"头像\">\n                      <ImageUpload v-model=\"formData.avatar\"/>\n                    </el-form-item>\n                    <el-form-item label=\"附件\">\n                      <FileUpload v-model=\"formData.video\"/>\n                    </el-form-item>\n                    <el-form-item label=\"备注\">\n                      <Editor v-model=\"formData.memo\" :min-height=\"192\"/>\n                    </el-form-item>\n      </el-form>\n                  <!-- 子表的表单 -->\n          <el-tabs v-model=\"subTabsName\">\n                <el-tab-pane label=\"学生联系人\" name=\"studentContact\">\n                  <StudentContactForm ref=\"studentContactFormRef\" :student-id=\"formData.id\" />\n                </el-tab-pane>\n                <el-tab-pane label=\"学生班主任\" name=\"studentTeacher\">\n                  <StudentTeacherForm ref=\"studentTeacherFormRef\" :student-id=\"formData.id\" />\n                </el-tab-pane>\n          </el-tabs>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\" :disabled=\"formLoading\">确 定</el-button>\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n  import ImageUpload from '@/components/ImageUpload';\n  import FileUpload from '@/components/FileUpload';\n  import Editor from '@/components/Editor';\n          import StudentContactForm from './components/StudentContactForm.vue'\n      import StudentTeacherForm from './components/StudentTeacherForm.vue'\n  export default {\n    name: \"StudentForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n                               StudentContactForm,\n               StudentTeacherForm,\n    },\n    data() {\n      return {\n        // 弹出层标题\n        dialogTitle: \"\",\n        // 是否显示弹出层\n        dialogVisible: false,\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: {\n                            id: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        },\n        // 表单校验\n        formRules: {\n                        name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n                        description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n                        birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n                        sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n                        enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n                        avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n                        video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],\n                        memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n        },\n                              /** 子表的表单 */\n             subTabsName: 'studentContact'\n      };\n    },\n    methods: {\n      /** 打开弹窗 */\n     async open(id) {\n        this.dialogVisible = true;\n        this.reset();\n        // 修改时，设置数据\n        if (id) {\n          this.formLoading = true;\n          try {\n            const res = await StudentApi.getStudent(id);\n            this.formData = res.data;\n            this.title = \"修改学生\";\n          } finally {\n            this.formLoading = false;\n          }\n        }\n        this.title = \"新增学生\";\n              },\n      /** 提交按钮 */\n      async submitForm() {\n        // 校验主表\n        await this.$refs[\"formRef\"].validate();\n                          // 校验子表\n                    try {\n                                            await this.$refs['studentContactFormRef'].validate();\n                    } catch (e) {\n                      this.subTabsName = 'studentContact';\n                      return;\n                    }\n                    try {\n                                            await this.$refs['studentTeacherFormRef'].validate();\n                    } catch (e) {\n                      this.subTabsName = 'studentTeacher';\n                      return;\n                    }\n        this.formLoading = true;\n        try {\n          const data = this.formData;\n                    // 拼接子表的数据\n              data.studentContacts = this.$refs['studentContactFormRef'].getData();\n              data.studentTeacher = this.$refs['studentTeacherFormRef'].getData();\n          // 修改的提交\n          if (data.id) {\n            await StudentApi.updateStudent(data);\n            this.$modal.msgSuccess(\"修改成功\");\n            this.dialogVisible = false;\n            this.$emit('success');\n            return;\n          }\n          // 添加的提交\n          await StudentApi.createStudent(data);\n          this.$modal.msgSuccess(\"新增成功\");\n          this.dialogVisible = false;\n          this.$emit('success');\n        } finally {\n          this.formLoading = false;\n        }\n      },\n                      /** 表单重置 */\n      reset() {\n        this.formData = {\n                            id: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        };\n        this.resetForm(\"formRef\");\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm",
    "content": "<template>\n  <div class=\"app-container\">\n      <el-form\n        ref=\"formRef\"\n        :model=\"formData\"\n        :rules=\"formRules\"\n        label-width=\"100px\"\n        v-loading=\"formLoading\"\n      >\n                     <el-form-item label=\"名字\" prop=\"name\">\n                      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n                    </el-form-item>\n                    <el-form-item label=\"简介\" prop=\"description\">\n                      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n                    </el-form-item>\n                    <el-form-item label=\"出生日期\" prop=\"birthday\">\n                      <el-date-picker clearable v-model=\"formData.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                    </el-form-item>\n                    <el-form-item label=\"性别\" prop=\"sex\">\n                      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                       :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                      </el-select>\n                    </el-form-item>\n                    <el-form-item label=\"是否有效\" prop=\"enabled\">\n                      <el-radio-group v-model=\"formData.enabled\">\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                      :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                      </el-radio-group>\n                    </el-form-item>\n                    <el-form-item label=\"头像\">\n                      <ImageUpload v-model=\"formData.avatar\"/>\n                    </el-form-item>\n                    <el-form-item label=\"附件\">\n                      <FileUpload v-model=\"formData.video\"/>\n                    </el-form-item>\n                    <el-form-item label=\"备注\">\n                      <Editor v-model=\"formData.memo\" :min-height=\"192\"/>\n                    </el-form-item>\n      </el-form>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n      import ImageUpload from '@/components/ImageUpload';\n      import FileUpload from '@/components/FileUpload';\n      import Editor from '@/components/Editor';\n  export default {\n    name: \"StudentTeacherForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n    },\n    props:[\n      'studentId'\n    ],// 学生编号（主表的关联字段）\n    data() {\n      return {\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: [],\n        // 表单校验\n        formRules: {\n                        studentId: [{ required: true, message: \"学生编号不能为空\", trigger: \"blur\" }],\n                        name: [{ required: true, message: \"名字不能为空\", trigger: \"blur\" }],\n                        description: [{ required: true, message: \"简介不能为空\", trigger: \"blur\" }],\n                        birthday: [{ required: true, message: \"出生日期不能为空\", trigger: \"blur\" }],\n                        sex: [{ required: true, message: \"性别不能为空\", trigger: \"change\" }],\n                        enabled: [{ required: true, message: \"是否有效不能为空\", trigger: \"blur\" }],\n                        avatar: [{ required: true, message: \"头像不能为空\", trigger: \"blur\" }],\n                        memo: [{ required: true, message: \"备注不能为空\", trigger: \"blur\" }],\n        },\n      };\n    },\n    watch:{/** 监听主表的关联字段的变化，加载对应的子表数据 */\n      studentId:{\n        handler(val) {\n          // 1. 重置表单\n              this.formData = {\n                                  id: undefined,\n                                  studentId: undefined,\n                                  name: undefined,\n                                  description: undefined,\n                                  birthday: undefined,\n                                  sex: undefined,\n                                  enabled: undefined,\n                                  avatar: undefined,\n                                  video: undefined,\n                                  memo: undefined,\n              }\n          // 2. val 非空，则加载数据\n          if (!val) {\n            return;\n          }\n          try {\n            this.formLoading = true;\n            // 这里还是需要获取一下 this 的不然取不到 formData\n            const that = this;\n            StudentApi.getStudentTeacherByStudentId(val).then(function (res){\n              const data = res.data;\n              if (!data) {\n                return\n              }\n              that.formData = data;\n            })\n          } finally {\n            this.formLoading = false;\n          }\n        },\n        immediate: true\n      }\n    },\n    methods: {\n      /** 表单校验 */\n      validate(){\n        return this.$refs[\"formRef\"].validate();\n      },\n      /** 表单值 */\n      getData(){\n        return this.formData;\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/vue/index",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 搜索工作栏 -->\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"queryParams.name\" placeholder=\"请输入名字\" clearable @keyup.enter.native=\"handleQuery\"/>\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker clearable v-model=\"queryParams.birthday\" type=\"date\" value-format=\"yyyy-MM-dd\" placeholder=\"选择出生日期\" />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"queryParams.sex\" placeholder=\"请选择性别\" clearable size=\"small\">\n          <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                       :key=\"dict.value\" :label=\"dict.label\" :value=\"dict.value\"/>\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-select v-model=\"queryParams.enabled\" placeholder=\"请选择是否有效\" clearable size=\"small\">\n          <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                       :key=\"dict.value\" :label=\"dict.label\" :value=\"dict.value\"/>\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker v-model=\"queryParams.createTime\" style=\"width: 240px\" value-format=\"yyyy-MM-dd HH:mm:ss\" type=\"daterange\"\n                        range-separator=\"-\" start-placeholder=\"开始日期\" end-placeholder=\"结束日期\" :default-time=\"['00:00:00', '23:59:59']\" />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <!-- 操作工具栏 -->\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"openForm(undefined)\"\n                   v-hasPermi=\"['infra:student:create']\">新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button type=\"warning\" plain icon=\"el-icon-download\" size=\"mini\" @click=\"handleExport\" :loading=\"exportLoading\"\n                   v-hasPermi=\"['infra:student:export']\">导出</el-button>\n      </el-col>\n              <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n            <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n            <el-table-column label=\"编号\" align=\"center\" prop=\"id\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.id\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.name\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.description\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"出生日期\" align=\"center\" prop=\"birthday\" width=\"180\">\n        <template v-slot=\"scope\">\n          <span>{{ parseTime(scope.row.birthday) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.avatar\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.video\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.memo\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template v-slot=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template v-slot=\"scope\">\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.id)\"\n                     v-hasPermi=\"['infra:student:update']\">修改</el-button>\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                     v-hasPermi=\"['infra:student:delete']\">删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页组件 -->\n    <pagination v-show=\"total > 0\" :total=\"total\" :page.sync=\"queryParams.pageNo\" :limit.sync=\"queryParams.pageSize\"\n                @pagination=\"getList\"/>\n    <!-- 对话框(添加 / 修改) -->\n    <StudentForm ref=\"formRef\" @success=\"getList\" />\n    </div>\n</template>\n\n<script>\nimport * as StudentApi from '@/api/infra/demo';\nimport StudentForm from './StudentForm.vue';\nexport default {\n  name: \"Student\",\n  components: {\n          StudentForm,\n  },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 导出遮罩层\n      exportLoading: false,\n      // 显示搜索条件\n      showSearch: true,\n              // 总条数\n        total: 0,\n      // 学生列表\n      list: [],\n      // 是否展开，默认全部展开\n      isExpandAll: true,\n      // 重新渲染表格状态\n      refreshTable: true,\n      // 选中行\n      currentRow: {},\n      // 查询参数\n      queryParams: {\n                    pageNo: 1,\n            pageSize: 10,\n        name: null,\n        birthday: null,\n        sex: null,\n        enabled: null,\n        createTime: [],\n      },\n            };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询列表 */\n    async getList() {\n      try {\n      this.loading = true;\n              const res = await StudentApi.getStudentPage(this.queryParams);\n        this.list = res.data.list;\n        this.total = res.data.total;\n      } finally {\n        this.loading = false;\n      }\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNo = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 添加/修改操作 */\n    openForm(id) {\n      this.$refs[\"formRef\"].open(id);\n    },\n    /** 删除按钮操作 */\n    async handleDelete(row) {\n      const id = row.id;\n      await this.$modal.confirm('是否确认删除学生编号为\"' + id + '\"的数据项?')\n      try {\n       await StudentApi.deleteStudent(id);\n       await this.getList();\n       this.$modal.msgSuccess(\"删除成功\");\n      } catch {}\n    },\n    /** 导出按钮操作 */\n    async handleExport() {\n      await this.$modal.confirm('是否确认导出所有学生数据项?');\n      try {\n        this.exportLoading = true;\n        const res = await StudentApi.exportStudentExcel(this.queryParams);\n        this.$download.excel(res.data, '学生.xls');\n      } catch {\n      } finally {\n        this.exportLoading = false;\n      }\n    },\n              }\n};\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraStudentPageReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentSaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\": \"js/index\",\n  \"filePath\": \"yudao-ui-admin-vue2/src/api/infra/demo/index.js\"\n}, {\n  \"contentPath\" : \"vue/StudentForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 学生 TODO 补充编号 ==========\r\nErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生不存在\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/student\")\n@Validated\npublic class InfraStudentController {\n\n    @Resource\n    private InfraStudentService studentService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {\n        return success(studentService.createStudent(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {\n        studentService.updateStudent(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudent(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudent(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentRespVO> getStudent(@RequestParam(\"id\") Long id) {\n        InfraStudentDO student = studentService.getStudent(id);\n        return success(BeanUtils.toBean(student, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {\n        PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", InfraStudentRespVO.class,\n                        BeanUtils.toBean(list, InfraStudentRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student\")\n@KeySequence(\"infra_student_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {\n\n    default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()\n                .likeIfPresent(InfraStudentDO::getName, reqVO.getName())\n                .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())\n                .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())\n                .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(InfraStudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class InfraStudentPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n    @Schema(description = \"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", example = \"1\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", example = \"true\")\n    private Boolean enabled;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.idev.excel.annotation.*;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraStudentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"是否有效\", converter = DictConvert.class)\n    @DictFormat(\"infra_boolean_string\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @ExcelProperty(\"头像\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @ExcelProperty(\"附件\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @ExcelProperty(\"备注\")\n    private String memo;\n\n    @Schema(description = \"创建时间\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class InfraStudentSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否有效不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"头像不能为空\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @NotEmpty(message = \"附件不能为空\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @NotEmpty(message = \"备注不能为空\")\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraStudentService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteStudent(Long id);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    InfraStudentDO getStudent(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraStudentServiceImpl implements InfraStudentService {\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Override\n    public Long createStudent(InfraStudentSaveReqVO createReqVO) {\n        // 插入\n        InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);\n        studentMapper.insert(student);\n        // 返回\n        return student.getId();\n    }\n\n    @Override\n    public void updateStudent(InfraStudentSaveReqVO updateReqVO) {\n        // 校验存在\n        validateStudentExists(updateReqVO.getId());\n        // 更新\n        InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);\n        studentMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteStudent(Long id) {\n        // 校验存在\n        validateStudentExists(id);\n        // 删除\n        studentMapper.deleteById(id);\n    }\n\n    private void validateStudentExists(Long id) {\n        if (studentMapper.selectById(id) == null) {\n            throw exception(STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public InfraStudentDO getStudent(Long id) {\n        return studentMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {\n        return studentMapper.selectPage(pageReqVO);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraStudentServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraStudentServiceImpl.class)\npublic class InfraStudentServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraStudentServiceImpl studentService;\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Test\n    public void testCreateStudent_success() {\n        // 准备参数\n        InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);\n\n        // 调用\n        Long studentId = studentService.createStudent(createReqVO);\n        // 断言\n        assertNotNull(studentId);\n        // 校验记录的属性是否正确\n        InfraStudentDO student = studentMapper.selectById(studentId);\n        assertPojoEquals(createReqVO, student, \"id\");\n    }\n\n    @Test\n    public void testUpdateStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {\n            o.setId(dbStudent.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        studentService.updateStudent(updateReqVO);\n        // 校验是否更新正确\n        InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, student);\n    }\n\n    @Test\n    public void testUpdateStudent_notExists() {\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbStudent.getId();\n\n        // 调用\n        studentService.deleteStudent(id);\n       // 校验数据不存在了\n       assertNull(studentMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteStudent_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetStudentPage() {\n       // mock 数据\n       InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到\n           o.setName(null);\n           o.setBirthday(null);\n           o.setSex(null);\n           o.setEnabled(null);\n           o.setCreateTime(null);\n       });\n       studentMapper.insert(dbStudent);\n       // 测试 name 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));\n       // 测试 birthday 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));\n       // 测试 sex 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));\n       // 测试 enabled 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));\n       // 测试 createTime 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));\n       // 准备参数\n       InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();\n       reqVO.setName(null);\n       reqVO.setBirthday(null);\n       reqVO.setSex(null);\n       reqVO.setEnabled(null);\n       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n\n       // 调用\n       PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);\n       // 断言\n       assertEquals(1, pageResult.getTotal());\n       assertEquals(1, pageResult.getList().size());\n       assertPojoEquals(dbStudent, pageResult.getList().get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/js/index",
    "content": "import request from '@/utils/request'\r\n\r\n// 创建学生\r\nexport function createStudent(data) {\r\n  return request({\r\n    url: '/infra/student/create',\r\n    method: 'post',\r\n    data: data\r\n  })\r\n}\r\n\r\n// 更新学生\r\nexport function updateStudent(data) {\r\n  return request({\r\n    url: '/infra/student/update',\r\n    method: 'put',\r\n    data: data\r\n  })\r\n}\r\n\r\n// 删除学生\r\nexport function deleteStudent(id) {\r\n  return request({\r\n    url: '/infra/student/delete?id=' + id,\r\n    method: 'delete'\r\n  })\r\n}\r\n\r\n// 获得学生\r\nexport function getStudent(id) {\r\n  return request({\r\n    url: '/infra/student/get?id=' + id,\r\n    method: 'get'\r\n  })\r\n}\r\n\r\n// 获得学生分页\r\nexport function getStudentPage(params) {\r\n  return request({\r\n    url: '/infra/student/page',\r\n    method: 'get',\r\n    params\r\n  })\r\n}\r\n// 导出学生 Excel\r\nexport function exportStudentExcel(params) {\r\n  return request({\r\n    url: '/infra/student/export-excel',\r\n    method: 'get',\r\n    params,\r\n    responseType: 'blob'\r\n  })\r\n}\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_student\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" varchar NOT NULL,\n    \"birthday\" varchar NOT NULL,\n    \"sex\" int NOT NULL,\n    \"enabled\" bit NOT NULL,\n    \"avatar\" varchar NOT NULL,\n    \"video\" varchar NOT NULL,\n    \"memo\" varchar NOT NULL,\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (\"id\")\n) COMMENT '学生表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_student\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '学生管理', '', 2, 0, 888,\r\n    'student', '', 'infra/demo/index', 0, 'InfraStudent'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生查询', 'infra:student:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生创建', 'infra:student:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生更新', 'infra:student:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生删除', 'infra:student:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生导出', 'infra:student:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 对话框(添加 / 修改) -->\n    <el-dialog :title=\"dialogTitle\" :visible.sync=\"dialogVisible\" width=\"45%\" v-dialogDrag append-to-body>\n      <el-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\" v-loading=\"formLoading\" label-width=\"100px\">\n                    <el-form-item label=\"名字\" prop=\"name\">\n                      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n                    </el-form-item>\n                    <el-form-item label=\"简介\" prop=\"description\">\n                      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入内容\" />\n                    </el-form-item>\n                    <el-form-item label=\"出生日期\" prop=\"birthday\">\n                      <el-date-picker clearable v-model=\"formData.birthday\" type=\"date\" value-format=\"timestamp\" placeholder=\"选择出生日期\" />\n                    </el-form-item>\n                    <el-form-item label=\"性别\" prop=\"sex\">\n                      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n                            <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                                       :key=\"dict.value\" :label=\"dict.label\" :value=\"parseInt(dict.value)\" />\n                      </el-select>\n                    </el-form-item>\n                    <el-form-item label=\"是否有效\" prop=\"enabled\">\n                      <el-radio-group v-model=\"formData.enabled\">\n                            <el-radio v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                                      :key=\"dict.value\" :label=\"dict.value\">{{dict.label}}</el-radio>\n                      </el-radio-group>\n                    </el-form-item>\n                    <el-form-item label=\"头像\">\n                      <ImageUpload v-model=\"formData.avatar\"/>\n                    </el-form-item>\n                    <el-form-item label=\"附件\">\n                      <FileUpload v-model=\"formData.video\"/>\n                    </el-form-item>\n                    <el-form-item label=\"备注\">\n                      <Editor v-model=\"formData.memo\" :min-height=\"192\"/>\n                    </el-form-item>\n      </el-form>\n              <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button type=\"primary\" @click=\"submitForm\" :disabled=\"formLoading\">确 定</el-button>\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\n  import * as StudentApi from '@/api/infra/demo';\n  import ImageUpload from '@/components/ImageUpload';\n  import FileUpload from '@/components/FileUpload';\n  import Editor from '@/components/Editor';\n      export default {\n    name: \"StudentForm\",\n    components: {\n          ImageUpload,\n          FileUpload,\n          Editor,\n                    },\n    data() {\n      return {\n        // 弹出层标题\n        dialogTitle: \"\",\n        // 是否显示弹出层\n        dialogVisible: false,\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\n        formLoading: false,\n        // 表单参数\n        formData: {\n                            id: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        },\n        // 表单校验\n        formRules: {\n                        name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n                        description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n                        birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n                        sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n                        enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n                        avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n                        video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],\n                        memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n        },\n                        };\n    },\n    methods: {\n      /** 打开弹窗 */\n     async open(id) {\n        this.dialogVisible = true;\n        this.reset();\n        // 修改时，设置数据\n        if (id) {\n          this.formLoading = true;\n          try {\n            const res = await StudentApi.getStudent(id);\n            this.formData = res.data;\n            this.title = \"修改学生\";\n          } finally {\n            this.formLoading = false;\n          }\n        }\n        this.title = \"新增学生\";\n              },\n      /** 提交按钮 */\n      async submitForm() {\n        // 校验主表\n        await this.$refs[\"formRef\"].validate();\n                  this.formLoading = true;\n        try {\n          const data = this.formData;\n                  // 修改的提交\n          if (data.id) {\n            await StudentApi.updateStudent(data);\n            this.$modal.msgSuccess(\"修改成功\");\n            this.dialogVisible = false;\n            this.$emit('success');\n            return;\n          }\n          // 添加的提交\n          await StudentApi.createStudent(data);\n          this.$modal.msgSuccess(\"新增成功\");\n          this.dialogVisible = false;\n          this.$emit('success');\n        } finally {\n          this.formLoading = false;\n        }\n      },\n                      /** 表单重置 */\n      reset() {\n        this.formData = {\n                            id: undefined,\n                            name: undefined,\n                            description: undefined,\n                            birthday: undefined,\n                            sex: undefined,\n                            enabled: undefined,\n                            avatar: undefined,\n                            video: undefined,\n                            memo: undefined,\n        };\n        this.resetForm(\"formRef\");\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/vue/index",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 搜索工作栏 -->\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"queryParams.name\" placeholder=\"请输入名字\" clearable @keyup.enter.native=\"handleQuery\"/>\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker clearable v-model=\"queryParams.birthday\" type=\"date\" value-format=\"yyyy-MM-dd\" placeholder=\"选择出生日期\" />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"queryParams.sex\" placeholder=\"请选择性别\" clearable size=\"small\">\n          <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)\"\n                       :key=\"dict.value\" :label=\"dict.label\" :value=\"dict.value\"/>\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-select v-model=\"queryParams.enabled\" placeholder=\"请选择是否有效\" clearable size=\"small\">\n          <el-option v-for=\"dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                       :key=\"dict.value\" :label=\"dict.label\" :value=\"dict.value\"/>\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker v-model=\"queryParams.createTime\" style=\"width: 240px\" value-format=\"yyyy-MM-dd HH:mm:ss\" type=\"daterange\"\n                        range-separator=\"-\" start-placeholder=\"开始日期\" end-placeholder=\"结束日期\" :default-time=\"['00:00:00', '23:59:59']\" />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <!-- 操作工具栏 -->\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"openForm(undefined)\"\n                   v-hasPermi=\"['infra:student:create']\">新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button type=\"warning\" plain icon=\"el-icon-download\" size=\"mini\" @click=\"handleExport\" :loading=\"exportLoading\"\n                   v-hasPermi=\"['infra:student:export']\">导出</el-button>\n      </el-col>\n              <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n            <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n            <el-table-column label=\"编号\" align=\"center\" prop=\"id\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.id\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.name\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.description\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"出生日期\" align=\"center\" prop=\"birthday\" width=\"180\">\n        <template v-slot=\"scope\">\n          <span>{{ parseTime(scope.row.birthday) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.avatar\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.video\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.memo\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"createTime\" width=\"180\">\n        <template v-slot=\"scope\">\n          <span>{{ parseTime(scope.row.createTime) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template v-slot=\"scope\">\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.id)\"\n                     v-hasPermi=\"['infra:student:update']\">修改</el-button>\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                     v-hasPermi=\"['infra:student:delete']\">删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页组件 -->\n    <pagination v-show=\"total > 0\" :total=\"total\" :page.sync=\"queryParams.pageNo\" :limit.sync=\"queryParams.pageSize\"\n                @pagination=\"getList\"/>\n    <!-- 对话框(添加 / 修改) -->\n    <StudentForm ref=\"formRef\" @success=\"getList\" />\n    </div>\n</template>\n\n<script>\nimport * as StudentApi from '@/api/infra/demo';\nimport StudentForm from './StudentForm.vue';\nexport default {\n  name: \"Student\",\n  components: {\n          StudentForm,\n  },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 导出遮罩层\n      exportLoading: false,\n      // 显示搜索条件\n      showSearch: true,\n              // 总条数\n        total: 0,\n      // 学生列表\n      list: [],\n      // 是否展开，默认全部展开\n      isExpandAll: true,\n      // 重新渲染表格状态\n      refreshTable: true,\n      // 选中行\n      currentRow: {},\n      // 查询参数\n      queryParams: {\n                    pageNo: 1,\n            pageSize: 10,\n        name: null,\n        birthday: null,\n        sex: null,\n        enabled: null,\n        createTime: [],\n      },\n            };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询列表 */\n    async getList() {\n      try {\n      this.loading = true;\n              const res = await StudentApi.getStudentPage(this.queryParams);\n        this.list = res.data.list;\n        this.total = res.data.total;\n      } finally {\n        this.loading = false;\n      }\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNo = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 添加/修改操作 */\n    openForm(id) {\n      this.$refs[\"formRef\"].open(id);\n    },\n    /** 删除按钮操作 */\n    async handleDelete(row) {\n      const id = row.id;\n      await this.$modal.confirm('是否确认删除学生编号为\"' + id + '\"的数据项?')\n      try {\n       await StudentApi.deleteStudent(id);\n       await this.getList();\n       this.$modal.msgSuccess(\"删除成功\");\n      } catch {}\n    },\n    /** 导出按钮操作 */\n    async handleExport() {\n      await this.$modal.confirm('是否确认导出所有学生数据项?');\n      try {\n        this.exportLoading = true;\n        const res = await StudentApi.exportStudentExcel(this.queryParams);\n        this.$download.excel(res.data, '学生.xls');\n      } catch {\n      } finally {\n        this.exportLoading = false;\n      }\n    },\n              }\n};\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraCategoryListReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategorySaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraCategoryMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\": \"js/index\",\n  \"filePath\": \"yudao-ui-admin-vue2/src/api/infra/demo/index.js\"\n}, {\n  \"contentPath\" : \"vue/CategoryForm\",\n  \"filePath\" : \"yudao-ui-admin-vue2/src/views/infra/demo/CategoryForm.vue\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 分类 TODO 补充编号 ==========\r\nErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"分类不存在\");\r\nErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, \"存在存在子分类，无法删除\");\r\nErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,\"父级分类不存在\");\r\nErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, \"不能设置自己为父分类\");\r\nErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, \"已经存在该名字的分类\");\r\nErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, \"不能设置自己的子InfraCategory为父InfraCategory\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService;\n\n@Tag(name = \"管理后台 - 分类\")\n@RestController\n@RequestMapping(\"/infra/category\")\n@Validated\npublic class InfraCategoryController {\n\n    @Resource\n    private InfraCategoryService categoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建分类\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:create')\")\n    public CommonResult<Long> createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) {\n        return success(categoryService.createCategory(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新分类\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:update')\")\n    public CommonResult<Boolean> updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) {\n        categoryService.updateCategory(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:category:delete')\")\n    public CommonResult<Boolean> deleteCategory(@RequestParam(\"id\") Long id) {\n        categoryService.deleteCategory(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:query')\")\n    public CommonResult<InfraCategoryRespVO> getCategory(@RequestParam(\"id\") Long id) {\n        InfraCategoryDO category = categoryService.getCategory(id);\n        return success(BeanUtils.toBean(category, InfraCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得分类列表\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:query')\")\n    public CommonResult<List<InfraCategoryRespVO>> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) {\n        List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO);\n        return success(BeanUtils.toBean(list, InfraCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出分类 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO,\n              HttpServletResponse response) throws IOException {\n        List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO);\n        // 导出 Excel\n        ExcelUtils.write(response, \"分类.xls\", \"数据\", InfraCategoryRespVO.class,\n                        BeanUtils.toBean(list, InfraCategoryRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 分类 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_category\")\n@KeySequence(\"infra_category_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraCategoryDO extends BaseDO {\n\n    public static final Long PARENT_ID_ROOT = 0L;\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 父编号\n     */\n    private Long parentId;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n@Schema(description = \"管理后台 - 分类列表 Request VO\")\n@Data\npublic class InfraCategoryListReqVO {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 分类 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraCategoryMapper extends BaseMapperX<InfraCategoryDO> {\n\n    default List<InfraCategoryDO> selectList(InfraCategoryListReqVO reqVO) {\n        return selectList(new LambdaQueryWrapperX<InfraCategoryDO>()\n                .likeIfPresent(InfraCategoryDO::getName, reqVO.getName())\n                .orderByDesc(InfraCategoryDO::getId));\n    }\n\n\tdefault InfraCategoryDO selectByParentIdAndName(Long parentId, String name) {\n\t    return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name);\n\t}\n\n    default Long selectCountByParentId(Long parentId) {\n        return selectCount(InfraCategoryDO::getParentId, parentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport cn.idev.excel.annotation.*;\n\n@Schema(description = \"管理后台 - 分类 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraCategoryRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"父编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    @ExcelProperty(\"父编号\")\n    private Long parentId;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\n\n@Schema(description = \"管理后台 - 分类新增/修改 Request VO\")\n@Data\npublic class InfraCategorySaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"父编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    @NotNull(message = \"父编号不能为空\")\n    private Long parentId;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 分类 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraCategoryService {\n\n    /**\n     * 创建分类\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createCategory(@Valid InfraCategorySaveReqVO createReqVO);\n\n    /**\n     * 更新分类\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO);\n\n    /**\n     * 删除分类\n     *\n     * @param id 编号\n     */\n    void deleteCategory(Long id);\n\n    /**\n     * 获得分类\n     *\n     * @param id 编号\n     * @return 分类\n     */\n    InfraCategoryDO getCategory(Long id);\n\n    /**\n     * 获得分类列表\n     *\n     * @param listReqVO 查询条件\n     * @return 分类列表\n     */\n    List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 分类 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraCategoryServiceImpl implements InfraCategoryService {\n\n    @Resource\n    private InfraCategoryMapper categoryMapper;\n\n    @Override\n    public Long createCategory(InfraCategorySaveReqVO createReqVO) {\n        // 校验父编号的有效性\n        validateParentCategory(null, createReqVO.getParentId());\n        // 校验名字的唯一性\n        validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());\n\n        // 插入\n        InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class);\n        categoryMapper.insert(category);\n        // 返回\n        return category.getId();\n    }\n\n    @Override\n    public void updateCategory(InfraCategorySaveReqVO updateReqVO) {\n        // 校验存在\n        validateCategoryExists(updateReqVO.getId());\n        // 校验父编号的有效性\n        validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId());\n        // 校验名字的唯一性\n        validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());\n\n        // 更新\n        InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class);\n        categoryMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteCategory(Long id) {\n        // 校验存在\n        validateCategoryExists(id);\n        // 校验是否有子分类\n        if (categoryMapper.selectCountByParentId(id) > 0) {\n            throw exception(CATEGORY_EXITS_CHILDREN);\n        }\n        // 删除\n        categoryMapper.deleteById(id);\n    }\n\n    private void validateCategoryExists(Long id) {\n        if (categoryMapper.selectById(id) == null) {\n            throw exception(CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    private void validateParentCategory(Long id, Long parentId) {\n        if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {\n            return;\n        }\n        // 1. 不能设置自己为父分类\n        if (Objects.equals(id, parentId)) {\n            throw exception(CATEGORY_PARENT_ERROR);\n        }\n        // 2. 父分类不存在\n        CategoryDO parentCategory = categoryMapper.selectById(parentId);\n        if (parentCategory == null) {\n            throw exception(CATEGORY_PARENT_NOT_EXITS);\n        }\n        // 3. 递归校验父分类，如果父分类是自己的子分类，则报错，避免形成环路\n        if (id == null) { // id 为空，说明新增，不需要考虑环路\n            return;\n        }\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            // 3.1 校验环路\n            parentId = parentCategory.getParentId();\n            if (Objects.equals(id, parentId)) {\n                throw exception(CATEGORY_PARENT_IS_CHILD);\n            }\n            // 3.2 继续递归下一级父分类\n            if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {\n                break;\n            }\n            parentCategory = categoryMapper.selectById(parentId);\n            if (parentCategory == null) {\n                break;\n            }\n        }\n    }\n\n    private void validateCategoryNameUnique(Long id, Long parentId, String name) {\n        CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name);\n        if (category == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的分类\n        if (id == null) {\n            throw exception(CATEGORY_NAME_DUPLICATE);\n        }\n        if (!Objects.equals(category.getId(), id)) {\n            throw exception(CATEGORY_NAME_DUPLICATE);\n        }\n    }\n\n    @Override\n    public InfraCategoryDO getCategory(Long id) {\n        return categoryMapper.selectById(id);\n    }\n\n    @Override\n    public List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO) {\n        return categoryMapper.selectList(listReqVO);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraCategoryServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraCategoryServiceImpl.class)\npublic class InfraCategoryServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraCategoryServiceImpl categoryService;\n\n    @Resource\n    private InfraCategoryMapper categoryMapper;\n\n    @Test\n    public void testCreateCategory_success() {\n        // 准备参数\n        InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null);\n\n        // 调用\n        Long categoryId = categoryService.createCategory(createReqVO);\n        // 断言\n        assertNotNull(categoryId);\n        // 校验记录的属性是否正确\n        InfraCategoryDO category = categoryMapper.selectById(categoryId);\n        assertPojoEquals(createReqVO, category, \"id\");\n    }\n\n    @Test\n    public void testUpdateCategory_success() {\n        // mock 数据\n        InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);\n        categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> {\n            o.setId(dbCategory.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        categoryService.updateCategory(updateReqVO);\n        // 校验是否更新正确\n        InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, category);\n    }\n\n    @Test\n    public void testUpdateCategory_notExists() {\n        // 准备参数\n        InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteCategory_success() {\n        // mock 数据\n        InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);\n        categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbCategory.getId();\n\n        // 调用\n        categoryService.deleteCategory(id);\n       // 校验数据不存在了\n       assertNull(categoryMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteCategory_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetCategoryList() {\n       // mock 数据\n       InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到\n           o.setName(null);\n       });\n       categoryMapper.insert(dbCategory);\n       // 测试 name 不匹配\n       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));\n       // 准备参数\n       InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO();\n       reqVO.setName(null);\n\n       // 调用\n       List<InfraCategoryDO> list = categoryService.getCategoryList(reqVO);\n       // 断言\n       assertEquals(1, list.size());\n       assertPojoEquals(dbCategory, list.get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/js/index",
    "content": "import request from '@/utils/request'\r\n\r\n// 创建分类\r\nexport function createCategory(data) {\r\n  return request({\r\n    url: '/infra/category/create',\r\n    method: 'post',\r\n    data: data\r\n  })\r\n}\r\n\r\n// 更新分类\r\nexport function updateCategory(data) {\r\n  return request({\r\n    url: '/infra/category/update',\r\n    method: 'put',\r\n    data: data\r\n  })\r\n}\r\n\r\n// 删除分类\r\nexport function deleteCategory(id) {\r\n  return request({\r\n    url: '/infra/category/delete?id=' + id,\r\n    method: 'delete'\r\n  })\r\n}\r\n\r\n// 获得分类\r\nexport function getCategory(id) {\r\n  return request({\r\n    url: '/infra/category/get?id=' + id,\r\n    method: 'get'\r\n  })\r\n}\r\n\r\n// 获得分类列表\r\nexport function getCategoryList(params) {\r\n  return request({\r\n    url: '/infra/category/list',\r\n    method: 'get',\r\n    params\r\n  })\r\n}\r\n// 导出分类 Excel\r\nexport function exportCategoryExcel(params) {\r\n  return request({\r\n    url: '/infra/category/export-excel',\r\n    method: 'get',\r\n    params,\r\n    responseType: 'blob'\r\n  })\r\n}\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_category\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" bigint NOT NULL,\n    PRIMARY KEY (\"id\")\n) COMMENT '分类表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_category\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '分类管理', '', 2, 0, 888,\r\n    'category', '', 'infra/demo/index', 0, 'InfraCategory'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类查询', 'infra:category:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类创建', 'infra:category:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类更新', 'infra:category:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类删除', 'infra:category:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类导出', 'infra:category:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm",
    "content": "<template>\r\n  <div class=\"app-container\">\r\n    <!-- 对话框(添加 / 修改) -->\r\n    <el-dialog :title=\"dialogTitle\" :visible.sync=\"dialogVisible\" width=\"45%\" v-dialogDrag append-to-body>\r\n      <el-form ref=\"formRef\" :model=\"formData\" :rules=\"formRules\" v-loading=\"formLoading\" label-width=\"100px\">\r\n                    <el-form-item label=\"名字\" prop=\"name\">\r\n                      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\r\n                    </el-form-item>\r\n                    <el-form-item label=\"父编号\" prop=\"parentId\">\r\n                      <TreeSelect\r\n                        v-model=\"formData.parentId\"\r\n                        :options=\"categoryTree\"\r\n                        :normalizer=\"normalizer\"\r\n                        placeholder=\"请选择父编号\"\r\n                      />\r\n                    </el-form-item>\r\n      </el-form>\r\n              <div slot=\"footer\" class=\"dialog-footer\">\r\n        <el-button type=\"primary\" @click=\"submitForm\" :disabled=\"formLoading\">确 定</el-button>\r\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\r\n      </div>\r\n    </el-dialog>\r\n  </div>\r\n</template>\r\n\r\n<script>\r\n  import * as CategoryApi from '@/api/infra/demo';\r\n    import TreeSelect from \"@riophae/vue-treeselect\";\r\n  import \"@riophae/vue-treeselect/dist/vue-treeselect.css\";\r\n    export default {\r\n    name: \"CategoryForm\",\r\n    components: {\r\n                  TreeSelect,\r\n            },\r\n    data() {\r\n      return {\r\n        // 弹出层标题\r\n        dialogTitle: \"\",\r\n        // 是否显示弹出层\r\n        dialogVisible: false,\r\n        // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\r\n        formLoading: false,\r\n        // 表单参数\r\n        formData: {\r\n                            id: undefined,\r\n                            name: undefined,\r\n                            parentId: undefined,\r\n        },\r\n        // 表单校验\r\n        formRules: {\r\n                        name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\r\n                        parentId: [{ required: true, message: '父编号不能为空', trigger: 'blur' }],\r\n        },\r\n                       categoryTree: [], // 树形结构\r\n              };\r\n    },\r\n    methods: {\r\n      /** 打开弹窗 */\r\n     async open(id) {\r\n        this.dialogVisible = true;\r\n        this.reset();\r\n        // 修改时，设置数据\r\n        if (id) {\r\n          this.formLoading = true;\r\n          try {\r\n            const res = await CategoryApi.getCategory(id);\r\n            this.formData = res.data;\r\n            this.title = \"修改分类\";\r\n          } finally {\r\n            this.formLoading = false;\r\n          }\r\n        }\r\n        this.title = \"新增分类\";\r\n                await this.getCategoryTree();\r\n      },\r\n      /** 提交按钮 */\r\n      async submitForm() {\r\n        // 校验主表\r\n        await this.$refs[\"formRef\"].validate();\r\n                  this.formLoading = true;\r\n        try {\r\n          const data = this.formData;\r\n                  // 修改的提交\r\n          if (data.id) {\r\n            await CategoryApi.updateCategory(data);\r\n            this.$modal.msgSuccess(\"修改成功\");\r\n            this.dialogVisible = false;\r\n            this.$emit('success');\r\n            return;\r\n          }\r\n          // 添加的提交\r\n          await CategoryApi.createCategory(data);\r\n          this.$modal.msgSuccess(\"新增成功\");\r\n          this.dialogVisible = false;\r\n          this.$emit('success');\r\n        } finally {\r\n          this.formLoading = false;\r\n        }\r\n      },\r\n                  /** 获得分类树 */\r\n         async getCategoryTree() {\r\n            this.categoryTree = [];\r\n            const res = await CategoryApi.getCategoryList();\r\n            const root = { id: 0, name: '顶级分类', children: [] };\r\n            root.children = this.handleTree(res.data, 'id', 'parentId')\r\n            this.categoryTree.push(root)\r\n          },\r\n                  /** 转换分类数据结构 */\r\n          normalizer(node) {\r\n            if (node.children && !node.children.length) {\r\n              delete node.children;\r\n            }\r\n                return {\r\n                  id: node.id,\r\n                  label: node.name,\r\n                  children: node.children\r\n                };\r\n          },\r\n      /** 表单重置 */\r\n      reset() {\r\n        this.formData = {\r\n                            id: undefined,\r\n                            name: undefined,\r\n                            parentId: undefined,\r\n        };\r\n        this.resetForm(\"formRef\");\r\n      }\r\n    }\r\n  };\r\n</script>\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/vue/index",
    "content": "<template>\n  <div class=\"app-container\">\n    <!-- 搜索工作栏 -->\n    <el-form :model=\"queryParams\" ref=\"queryForm\" size=\"small\" :inline=\"true\" v-show=\"showSearch\" label-width=\"68px\">\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"queryParams.name\" placeholder=\"请输入名字\" clearable @keyup.enter.native=\"handleQuery\"/>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"el-icon-search\" @click=\"handleQuery\">搜索</el-button>\n        <el-button icon=\"el-icon-refresh\" @click=\"resetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <!-- 操作工具栏 -->\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"openForm(undefined)\"\n                   v-hasPermi=\"['infra:category:create']\">新增</el-button>\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button type=\"warning\" plain icon=\"el-icon-download\" size=\"mini\" @click=\"handleExport\" :loading=\"exportLoading\"\n                   v-hasPermi=\"['infra:category:export']\">导出</el-button>\n      </el-col>\n                  <el-col :span=\"1.5\">\n            <el-button type=\"danger\" plain icon=\"el-icon-sort\" size=\"mini\" @click=\"toggleExpandAll\">\n              展开/折叠\n            </el-button>\n          </el-col>\n      <right-toolbar :showSearch.sync=\"showSearch\" @queryTable=\"getList\"></right-toolbar>\n    </el-row>\n\n            <el-table\n        v-loading=\"loading\"\n        :data=\"list\"\n        :stripe=\"true\"\n        :show-overflow-tooltip=\"true\"\n        v-if=\"refreshTable\"\n        row-key=\"id\"\n        :default-expand-all=\"isExpandAll\"\n        :tree-props=\"{children: 'children', hasChildren: 'hasChildren'}\"\n      >\n            <el-table-column label=\"编号\" align=\"center\" prop=\"id\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.id\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.name\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"父编号\" align=\"center\" prop=\"parentId\">\n        <template v-slot=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.$dictType.toUpperCase()\" :value=\"scope.row.parentId\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template v-slot=\"scope\">\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-edit\" @click=\"openForm(scope.row.id)\"\n                     v-hasPermi=\"['infra:category:update']\">修改</el-button>\n          <el-button size=\"mini\" type=\"text\" icon=\"el-icon-delete\" @click=\"handleDelete(scope.row)\"\n                     v-hasPermi=\"['infra:category:delete']\">删除</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 对话框(添加 / 修改) -->\n    <CategoryForm ref=\"formRef\" @success=\"getList\" />\n    </div>\n</template>\n\n<script>\nimport * as CategoryApi from '@/api/infra/demo';\nimport CategoryForm from './CategoryForm.vue';\nexport default {\n  name: \"Category\",\n  components: {\n          CategoryForm,\n  },\n  data() {\n    return {\n      // 遮罩层\n      loading: true,\n      // 导出遮罩层\n      exportLoading: false,\n      // 显示搜索条件\n      showSearch: true,\n            // 分类列表\n      list: [],\n      // 是否展开，默认全部展开\n      isExpandAll: true,\n      // 重新渲染表格状态\n      refreshTable: true,\n      // 选中行\n      currentRow: {},\n      // 查询参数\n      queryParams: {\n                name: null,\n      },\n            };\n  },\n  created() {\n    this.getList();\n  },\n  methods: {\n    /** 查询列表 */\n    async getList() {\n      try {\n      this.loading = true;\n             const res = await CategoryApi.getCategoryList(this.queryParams);\n       this.list = this.handleTree(res.data, 'id', 'parentId');\n      } finally {\n        this.loading = false;\n      }\n    },\n    /** 搜索按钮操作 */\n    handleQuery() {\n      this.queryParams.pageNo = 1;\n      this.getList();\n    },\n    /** 重置按钮操作 */\n    resetQuery() {\n      this.resetForm(\"queryForm\");\n      this.handleQuery();\n    },\n    /** 添加/修改操作 */\n    openForm(id) {\n      this.$refs[\"formRef\"].open(id);\n    },\n    /** 删除按钮操作 */\n    async handleDelete(row) {\n      const id = row.id;\n      await this.$modal.confirm('是否确认删除分类编号为\"' + id + '\"的数据项?')\n      try {\n       await CategoryApi.deleteCategory(id);\n       await this.getList();\n       this.$modal.msgSuccess(\"删除成功\");\n      } catch {}\n    },\n    /** 导出按钮操作 */\n    async handleExport() {\n      await this.$modal.confirm('是否确认导出所有分类数据项?');\n      try {\n        this.exportLoading = true;\n        const res = await CategoryApi.exportCategoryExcel(this.queryParams);\n        this.$download.excel(res.data, '分类.xls');\n      } catch {\n      } finally {\n        this.exportLoading = false;\n      }\n    },\n                    /** 展开/折叠操作 */\n        toggleExpandAll() {\n          this.refreshTable = false\n          this.isExpandAll = !this.isExpandAll\n          this.$nextTick(function () {\n            this.refreshTable = true\n          })\n        }\n  }\n};\n</script>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraStudentPageReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentSaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactList\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactList.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherList\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherList.vue\"\n}, {\n  \"contentPath\" : \"ts/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/api/infra/demo/index.ts\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 学生 TODO 补充编号 ==========\r\nErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生不存在\");\r\nErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生联系人不存在\");\r\nErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生班主任不存在\");\r\nErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, \"学生班主任已存在\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生联系人 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_contact\")\n@KeySequence(\"infra_student_contact_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentContactDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生联系人 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {\n\n    default PageResult<InfraStudentContactDO> selectPage(PageParam reqVO, Long studentId) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentContactDO>()\n            .eq(InfraStudentContactDO::getStudentId, studentId)\n            .orderByDesc(InfraStudentContactDO::getId));\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/student\")\n@Validated\npublic class InfraStudentController {\n\n    @Resource\n    private InfraStudentService studentService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {\n        return success(studentService.createStudent(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {\n        studentService.updateStudent(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudent(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudent(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentRespVO> getStudent(@RequestParam(\"id\") Long id) {\n        InfraStudentDO student = studentService.getStudent(id);\n        return success(BeanUtils.toBean(student, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {\n        PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", InfraStudentRespVO.class,\n                        BeanUtils.toBean(list, InfraStudentRespVO.class));\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @GetMapping(\"/student-contact/page\")\n    @Operation(summary = \"获得学生联系人分页\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentContactDO>> getStudentContactPage(PageParam pageReqVO,\n                                                                                        @RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentContactPage(pageReqVO, studentId));\n    }\n\n    @PostMapping(\"/student-contact/create\")\n    @Operation(summary = \"创建学生联系人\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {\n        return success(studentService.createStudentContact(studentContact));\n    }\n\n    @PutMapping(\"/student-contact/update\")\n    @Operation(summary = \"更新学生联系人\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {\n        studentService.updateStudentContact(studentContact);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/student-contact/delete\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @Operation(summary = \"删除学生联系人\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudentContact(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudentContact(id);\n        return success(true);\n    }\n\n\t@GetMapping(\"/student-contact/get\")\n\t@Operation(summary = \"获得学生联系人\")\n\t@Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n\tpublic CommonResult<InfraStudentContactDO> getStudentContact(@RequestParam(\"id\") Long id) {\n\t    return success(studentService.getStudentContact(id));\n\t}\n\n    // ==================== 子表（学生班主任） ====================\n\n    @GetMapping(\"/student-teacher/page\")\n    @Operation(summary = \"获得学生班主任分页\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentTeacherDO>> getStudentTeacherPage(PageParam pageReqVO,\n                                                                                        @RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentTeacherPage(pageReqVO, studentId));\n    }\n\n    @PostMapping(\"/student-teacher/create\")\n    @Operation(summary = \"创建学生班主任\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {\n        return success(studentService.createStudentTeacher(studentTeacher));\n    }\n\n    @PutMapping(\"/student-teacher/update\")\n    @Operation(summary = \"更新学生班主任\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {\n        studentService.updateStudentTeacher(studentTeacher);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/student-teacher/delete\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @Operation(summary = \"删除学生班主任\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudentTeacher(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudentTeacher(id);\n        return success(true);\n    }\n\n\t@GetMapping(\"/student-teacher/get\")\n\t@Operation(summary = \"获得学生班主任\")\n\t@Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n\tpublic CommonResult<InfraStudentTeacherDO> getStudentTeacher(@RequestParam(\"id\") Long id) {\n\t    return success(studentService.getStudentTeacher(id));\n\t}\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student\")\n@KeySequence(\"infra_student_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {\n\n    default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()\n                .likeIfPresent(InfraStudentDO::getName, reqVO.getName())\n                .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())\n                .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())\n                .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(InfraStudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class InfraStudentPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n    @Schema(description = \"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", example = \"1\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", example = \"true\")\n    private Boolean enabled;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.idev.excel.annotation.*;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraStudentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"是否有效\", converter = DictConvert.class)\n    @DictFormat(\"infra_boolean_string\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @ExcelProperty(\"头像\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @ExcelProperty(\"附件\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @ExcelProperty(\"备注\")\n    private String memo;\n\n    @Schema(description = \"创建时间\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class InfraStudentSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否有效不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"头像不能为空\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @NotEmpty(message = \"附件不能为空\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @NotEmpty(message = \"备注不能为空\")\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraStudentService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteStudent(Long id);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    InfraStudentDO getStudent(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);\n\n    // ==================== 子表（学生联系人） ====================\n\n    /**\n     * 获得学生联系人分页\n     *\n     * @param pageReqVO 分页查询\n     * @param studentId 学生编号\n     * @return 学生联系人分页\n     */\n    PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId);\n\n    /**\n     * 创建学生联系人\n     *\n     * @param studentContact 创建信息\n     * @return 编号\n     */\n    Long createStudentContact(@Valid InfraStudentContactDO studentContact);\n\n    /**\n     * 更新学生联系人\n     *\n     * @param studentContact 更新信息\n     */\n    void updateStudentContact(@Valid InfraStudentContactDO studentContact);\n\n    /**\n     * 删除学生联系人\n     *\n     * @param id 编号\n     */\n    void deleteStudentContact(Long id);\n\n\t/**\n\t * 获得学生联系人\n\t *\n\t * @param id 编号\n     * @return 学生联系人\n\t */\n    InfraStudentContactDO getStudentContact(Long id);\n\n    // ==================== 子表（学生班主任） ====================\n\n    /**\n     * 获得学生班主任分页\n     *\n     * @param pageReqVO 分页查询\n     * @param studentId 学生编号\n     * @return 学生班主任分页\n     */\n    PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId);\n\n    /**\n     * 创建学生班主任\n     *\n     * @param studentTeacher 创建信息\n     * @return 编号\n     */\n    Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);\n\n    /**\n     * 更新学生班主任\n     *\n     * @param studentTeacher 更新信息\n     */\n    void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);\n\n    /**\n     * 删除学生班主任\n     *\n     * @param id 编号\n     */\n    void deleteStudentTeacher(Long id);\n\n\t/**\n\t * 获得学生班主任\n\t *\n\t * @param id 编号\n     * @return 学生班主任\n\t */\n    InfraStudentTeacherDO getStudentTeacher(Long id);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraStudentServiceImpl implements InfraStudentService {\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n    @Resource\n    private InfraStudentContactMapper studentContactMapper;\n    @Resource\n    private InfraStudentTeacherMapper studentTeacherMapper;\n\n    @Override\n    public Long createStudent(InfraStudentSaveReqVO createReqVO) {\n        // 插入\n        InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);\n        studentMapper.insert(student);\n        // 返回\n        return student.getId();\n    }\n\n    @Override\n    public void updateStudent(InfraStudentSaveReqVO updateReqVO) {\n        // 校验存在\n        validateStudentExists(updateReqVO.getId());\n        // 更新\n        InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);\n        studentMapper.updateById(updateObj);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStudent(Long id) {\n        // 校验存在\n        validateStudentExists(id);\n        // 删除\n        studentMapper.deleteById(id);\n\n        // 删除子表\n        deleteStudentContactByStudentId(id);\n        deleteStudentTeacherByStudentId(id);\n    }\n\n    private void validateStudentExists(Long id) {\n        if (studentMapper.selectById(id) == null) {\n            throw exception(STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public InfraStudentDO getStudent(Long id) {\n        return studentMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {\n        return studentMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @Override\n    public PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId) {\n        return studentContactMapper.selectPage(pageReqVO, studentId);\n    }\n\n    @Override\n    public Long createStudentContact(InfraStudentContactDO studentContact) {\n        studentContactMapper.insert(studentContact);\n        return studentContact.getId();\n    }\n\n    @Override\n    public void updateStudentContact(InfraStudentContactDO studentContact) {\n        // 校验存在\n        validateStudentContactExists(studentContact.getId());\n        // 更新\n        studentContactMapper.updateById(studentContact);\n    }\n\n    @Override\n    public void deleteStudentContact(Long id) {\n        // 校验存在\n        validateStudentContactExists(id);\n        // 删除\n        studentContactMapper.deleteById(id);\n    }\n\n    @Override\n    public InfraStudentContactDO getStudentContact(Long id) {\n        return studentContactMapper.selectById(id);\n    }\n\n    private void validateStudentContactExists(Long id) {\n        if (studentContactMapper.selectById(id) == null) {\n            throw exception(STUDENT_CONTACT_NOT_EXISTS);\n        }\n    }\n\n    private void deleteStudentContactByStudentId(Long studentId) {\n        studentContactMapper.deleteByStudentId(studentId);\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @Override\n    public PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId) {\n        return studentTeacherMapper.selectPage(pageReqVO, studentId);\n    }\n\n    @Override\n    public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) {\n        // 校验是否已经存在\n        if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) {\n            throw exception(STUDENT_TEACHER_EXISTS);\n        }\n        // 插入\n        studentTeacherMapper.insert(studentTeacher);\n        return studentTeacher.getId();\n    }\n\n    @Override\n    public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) {\n        // 校验存在\n        validateStudentTeacherExists(studentTeacher.getId());\n        // 更新\n        studentTeacherMapper.updateById(studentTeacher);\n    }\n\n    @Override\n    public void deleteStudentTeacher(Long id) {\n        // 校验存在\n        validateStudentTeacherExists(id);\n        // 删除\n        studentTeacherMapper.deleteById(id);\n    }\n\n    @Override\n    public InfraStudentTeacherDO getStudentTeacher(Long id) {\n        return studentTeacherMapper.selectById(id);\n    }\n\n    private void validateStudentTeacherExists(Long id) {\n        if (studentTeacherMapper.selectById(id) == null) {\n            throw exception(STUDENT_TEACHER_NOT_EXISTS);\n        }\n    }\n\n    private void deleteStudentTeacherByStudentId(Long studentId) {\n        studentTeacherMapper.deleteByStudentId(studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraStudentServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraStudentServiceImpl.class)\npublic class InfraStudentServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraStudentServiceImpl studentService;\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Test\n    public void testCreateStudent_success() {\n        // 准备参数\n        InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);\n\n        // 调用\n        Long studentId = studentService.createStudent(createReqVO);\n        // 断言\n        assertNotNull(studentId);\n        // 校验记录的属性是否正确\n        InfraStudentDO student = studentMapper.selectById(studentId);\n        assertPojoEquals(createReqVO, student, \"id\");\n    }\n\n    @Test\n    public void testUpdateStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {\n            o.setId(dbStudent.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        studentService.updateStudent(updateReqVO);\n        // 校验是否更新正确\n        InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, student);\n    }\n\n    @Test\n    public void testUpdateStudent_notExists() {\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbStudent.getId();\n\n        // 调用\n        studentService.deleteStudent(id);\n       // 校验数据不存在了\n       assertNull(studentMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteStudent_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetStudentPage() {\n       // mock 数据\n       InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到\n           o.setName(null);\n           o.setBirthday(null);\n           o.setSex(null);\n           o.setEnabled(null);\n           o.setCreateTime(null);\n       });\n       studentMapper.insert(dbStudent);\n       // 测试 name 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));\n       // 测试 birthday 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));\n       // 测试 sex 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));\n       // 测试 enabled 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));\n       // 测试 createTime 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));\n       // 准备参数\n       InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();\n       reqVO.setName(null);\n       reqVO.setBirthday(null);\n       reqVO.setSex(null);\n       reqVO.setEnabled(null);\n       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n\n       // 调用\n       PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);\n       // 断言\n       assertEquals(1, pageResult.getTotal());\n       assertEquals(1, pageResult.getList().size());\n       assertPojoEquals(dbStudent, pageResult.getList().get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生班主任 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_teacher\")\n@KeySequence(\"infra_student_teacher_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentTeacherDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生班主任 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {\n\n    default PageResult<InfraStudentTeacherDO> selectPage(PageParam reqVO, Long studentId) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentTeacherDO>()\n            .eq(InfraStudentTeacherDO::getStudentId, studentId)\n            .orderByDesc(InfraStudentTeacherDO::getId));\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_student\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" varchar NOT NULL,\n    \"birthday\" varchar NOT NULL,\n    \"sex\" int NOT NULL,\n    \"enabled\" bit NOT NULL,\n    \"avatar\" varchar NOT NULL,\n    \"video\" varchar NOT NULL,\n    \"memo\" varchar NOT NULL,\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (\"id\")\n) COMMENT '学生表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_student\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '学生管理', '', 2, 0, 888,\r\n    'student', '', 'infra/demo/index', 0, 'InfraStudent'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生查询', 'infra:student:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生创建', 'infra:student:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生更新', 'infra:student:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生删除', 'infra:student:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生导出', 'infra:student:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/ts/index",
    "content": "import request from '@/config/axios'\r\n\r\nexport interface StudentVO {\r\n  id: number\r\n  name: string\r\n  description: string\r\n  birthday: Date\r\n  sex: number\r\n  enabled: boolean\r\n  avatar: string\r\n  video: string\r\n  memo: string\r\n}\r\n\r\n// 查询学生分页\r\nexport const getStudentPage = async (params) => {\r\n  return await request.get({ url: `/infra/student/page`, params })\r\n}\r\n\r\n// 查询学生详情\r\nexport const getStudent = async (id: number) => {\r\n  return await request.get({ url: `/infra/student/get?id=` + id })\r\n}\r\n\r\n// 新增学生\r\nexport const createStudent = async (data: StudentVO) => {\r\n  return await request.post({ url: `/infra/student/create`, data })\r\n}\r\n\r\n// 修改学生\r\nexport const updateStudent = async (data: StudentVO) => {\r\n  return await request.put({ url: `/infra/student/update`, data })\r\n}\r\n\r\n// 删除学生\r\nexport const deleteStudent = async (id: number) => {\r\n  return await request.delete({ url: `/infra/student/delete?id=` + id })\r\n}\r\n\r\n// 导出学生 Excel\r\nexport const exportStudent = async (params) => {\r\n  return await request.download({ url: `/infra/student/export-excel`, params })\r\n}\r\n\r\n// ==================== 子表（学生联系人） ====================\r\n\r\n// 获得学生联系人分页\r\nexport const getStudentContactPage = async (params) => {\r\n  return await request.get({ url: `/infra/student/student-contact/page`, params })\r\n}\r\n// 新增学生联系人\r\nexport const createStudentContact = async (data) => {\r\n  return await request.post({ url: `/infra/student/student-contact/create`, data })\r\n}\r\n\r\n// 修改学生联系人\r\nexport const updateStudentContact = async (data) => {\r\n  return await request.put({ url: `/infra/student/student-contact/update`, data })\r\n}\r\n\r\n// 删除学生联系人\r\nexport const deleteStudentContact = async (id: number) => {\r\n  return await request.delete({ url: `/infra/student/student-contact/delete?id=` + id })\r\n}\r\n\r\n// 获得学生联系人\r\nexport const getStudentContact = async (id: number) => {\r\n  return await request.get({ url: `/infra/student/student-contact/get?id=` + id })\r\n}\r\n\r\n// ==================== 子表（学生班主任） ====================\r\n\r\n// 获得学生班主任分页\r\nexport const getStudentTeacherPage = async (params) => {\r\n  return await request.get({ url: `/infra/student/student-teacher/page`, params })\r\n}\r\n// 新增学生班主任\r\nexport const createStudentTeacher = async (data) => {\r\n  return await request.post({ url: `/infra/student/student-teacher/create`, data })\r\n}\r\n\r\n// 修改学生班主任\r\nexport const updateStudentTeacher = async (data) => {\r\n  return await request.put({ url: `/infra/student/student-teacher/update`, data })\r\n}\r\n\r\n// 删除学生班主任\r\nexport const deleteStudentTeacher = async (id: number) => {\r\n  return await request.delete({ url: `/infra/student/student-teacher/delete?id=` + id })\r\n}\r\n\r\n// 获得学生班主任\r\nexport const getStudentTeacher = async (id: number) => {\r\n  return await request.get({ url: `/infra/student/student-teacher/get?id=` + id })\r\n}\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm",
    "content": "<template>\n  <Dialog :title=\"dialogTitle\" v-model=\"dialogVisible\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"formRules\"\n      label-width=\"100px\"\n      v-loading=\"formLoading\"\n    >\n       <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n      </el-form-item>\n      <el-form-item label=\"简介\" prop=\"description\">\n        <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"formData.birthday\"\n          type=\"date\"\n          value-format=\"x\"\n          placeholder=\"选择出生日期\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-radio-group v-model=\"formData.enabled\">\n          <el-radio\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.value\"\n          >\n            {{ dict.label }}\n          </el-radio>\n        </el-radio-group>\n      </el-form-item>\n      <el-form-item label=\"头像\" prop=\"avatar\">\n        <UploadImg v-model=\"formData.avatar\" />\n      </el-form-item>\n      <el-form-item label=\"附件\" prop=\"video\">\n        <UploadFile v-model=\"formData.video\" />\n      </el-form-item>\n      <el-form-item label=\"备注\" prop=\"memo\">\n        <Editor v-model=\"formData.memo\" height=\"150px\" />\n      </el-form-item>\n    </el-form>\n    <template #footer>\n      <el-button @click=\"submitForm\" type=\"primary\" :disabled=\"formLoading\">确 定</el-button>\n      <el-button @click=\"dialogVisible = false\">取 消</el-button>\n    </template>\n  </Dialog>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst dialogVisible = ref(false) // 弹窗的是否展示\nconst dialogTitle = ref('') // 弹窗的标题\nconst formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\nconst formType = ref('') // 表单的类型：create - 新增；update - 修改\nconst formData = ref({\n  id: undefined,\n  studentId: undefined,\n  name: undefined,\n  description: undefined,\n  birthday: undefined,\n  sex: undefined,\n  enabled: undefined,\n  avatar: undefined,\n  video: undefined,\n  memo: undefined,\n})\nconst formRules = reactive({\n  studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 打开弹窗 */\nconst open = async (type: string, id?: number, studentId: number) => {\n  dialogVisible.value = true\n  dialogTitle.value = t('action.' + type)\n  formType.value = type\n  resetForm()\n  formData.value.studentId = studentId\n  // 修改时，设置数据\n  if (id) {\n    formLoading.value = true\n    try {\n      formData.value = await StudentApi.getStudentContact(id)\n    } finally {\n      formLoading.value = false\n    }\n  }\n}\ndefineExpose({ open }) // 提供 open 方法，用于打开弹窗\n\n/** 提交表单 */\nconst emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调\nconst submitForm = async () => {\n  // 校验表单\n  await formRef.value.validate()\n  // 提交请求\n  formLoading.value = true\n  try {\n    const data = formData.value\n    if (formType.value === 'create') {\n      await StudentApi.createStudentContact(data)\n      message.success(t('common.createSuccess'))\n    } else {\n      await StudentApi.updateStudentContact(data)\n      message.success(t('common.updateSuccess'))\n    }\n    dialogVisible.value = false\n    // 发送操作成功的事件\n    emit('success')\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n    id: undefined,\n    studentId: undefined,\n    name: undefined,\n    description: undefined,\n    birthday: undefined,\n    sex: undefined,\n    enabled: undefined,\n    avatar: undefined,\n    video: undefined,\n    memo: undefined,\n  }\n  formRef.value?.resetFields()\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList",
    "content": "<template>\n  <!-- 列表 -->\n  <ContentWrap>\n    <el-button\n      type=\"primary\"\n      plain\n      @click=\"openForm('create')\"\n      v-hasPermi=\"['infra:student:create']\"\n    >\n      <Icon icon=\"ep:plus\" class=\"mr-5px\" /> 新增\n    </el-button>\n    <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n       <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n      <el-table-column\n        label=\"出生日期\"\n        align=\"center\"\n        prop=\"birthday\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"createTime\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"操作\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button\n            link\n            type=\"primary\"\n            @click=\"openForm('update', scope.row.id)\"\n            v-hasPermi=\"['infra:student:update']\"\n          >\n            编辑\n          </el-button>\n          <el-button\n            link\n            type=\"danger\"\n            @click=\"handleDelete(scope.row.id)\"\n            v-hasPermi=\"['infra:student:delete']\"\n          >\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页 -->\n    <Pagination\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNo\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n  </ContentWrap>\n    <!-- 表单弹窗：添加/修改 -->\n    <StudentContactForm ref=\"formRef\" @success=\"getList\" />\n</template>\n<script setup lang=\"ts\">\nimport { DICT_TYPE } from '@/utils/dict'\nimport { dateFormatter } from '@/utils/formatTime'\nimport * as StudentApi from '@/api/infra/demo'\nimport StudentContactForm from './StudentContactForm.vue'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst props = defineProps<{\n  studentId: undefined // 学生编号（主表的关联字段）\n}>()\nconst loading = ref(false) // 列表的加载中\nconst list = ref([]) // 列表的数据\nconst total = ref(0) // 列表的总页数\nconst queryParams = reactive({\n  pageNo: 1,\n  pageSize: 10,\n  studentId: undefined\n})\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.studentId,\n  (val) => {\n    queryParams.studentId = val\n    handleQuery()\n  },\n  { immediate: false }\n)\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    const data = await StudentApi.getStudentContactPage(queryParams)\n    list.value = data.list\n    total.value = data.total\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 添加/修改操作 */\nconst formRef = ref()\nconst openForm = (type: string, id?: number) => {\n  if (!props.studentId) {\n    message.error('请选择一个学生')\n    return\n  }\n  formRef.value.open(type, id, props.studentId)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = async (id: number) => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    // 发起删除\n    await StudentApi.deleteStudentContact(id)\n    message.success(t('common.delSuccess'))\n    // 刷新列表\n    await getList()\n  } catch {}\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm",
    "content": "<template>\n  <Dialog :title=\"dialogTitle\" v-model=\"dialogVisible\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"formRules\"\n      label-width=\"100px\"\n      v-loading=\"formLoading\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n      </el-form-item>\n      <el-form-item label=\"简介\" prop=\"description\">\n        <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"formData.birthday\"\n          type=\"date\"\n          value-format=\"x\"\n          placeholder=\"选择出生日期\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-radio-group v-model=\"formData.enabled\">\n          <el-radio\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.value\"\n          >\n            {{ dict.label }}\n          </el-radio>\n        </el-radio-group>\n      </el-form-item>\n      <el-form-item label=\"头像\" prop=\"avatar\">\n        <UploadImg v-model=\"formData.avatar\" />\n      </el-form-item>\n      <el-form-item label=\"附件\" prop=\"video\">\n        <UploadFile v-model=\"formData.video\" />\n      </el-form-item>\n      <el-form-item label=\"备注\" prop=\"memo\">\n        <Editor v-model=\"formData.memo\" height=\"150px\" />\n      </el-form-item>\n    </el-form>\n    <template #footer>\n      <el-button @click=\"submitForm\" type=\"primary\" :disabled=\"formLoading\">确 定</el-button>\n      <el-button @click=\"dialogVisible = false\">取 消</el-button>\n    </template>\n  </Dialog>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst dialogVisible = ref(false) // 弹窗的是否展示\nconst dialogTitle = ref('') // 弹窗的标题\nconst formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\nconst formType = ref('') // 表单的类型：create - 新增；update - 修改\nconst formData = ref({\n  id: undefined,\n  name: undefined,\n  description: undefined,\n  birthday: undefined,\n  sex: undefined,\n  enabled: undefined,\n  avatar: undefined,\n  video: undefined,\n  memo: undefined,\n})\nconst formRules = reactive({\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 打开弹窗 */\nconst open = async (type: string, id?: number) => {\n  dialogVisible.value = true\n  dialogTitle.value = t('action.' + type)\n  formType.value = type\n  resetForm()\n  // 修改时，设置数据\n  if (id) {\n    formLoading.value = true\n    try {\n      formData.value = await StudentApi.getStudent(id)\n    } finally {\n      formLoading.value = false\n    }\n  }\n}\ndefineExpose({ open }) // 提供 open 方法，用于打开弹窗\n\n/** 提交表单 */\nconst emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调\nconst submitForm = async () => {\n  // 校验表单\n  await formRef.value.validate()\n  // 提交请求\n  formLoading.value = true\n  try {\n    const data = formData.value as unknown as StudentApi.StudentVO\n    if (formType.value === 'create') {\n      await StudentApi.createStudent(data)\n      message.success(t('common.createSuccess'))\n    } else {\n      await StudentApi.updateStudent(data)\n      message.success(t('common.updateSuccess'))\n    }\n    dialogVisible.value = false\n    // 发送操作成功的事件\n    emit('success')\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n    id: undefined,\n    name: undefined,\n    description: undefined,\n    birthday: undefined,\n    sex: undefined,\n    enabled: undefined,\n    avatar: undefined,\n    video: undefined,\n    memo: undefined,\n  }\n  formRef.value?.resetFields()\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm",
    "content": "<template>\n  <Dialog :title=\"dialogTitle\" v-model=\"dialogVisible\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"formRules\"\n      label-width=\"100px\"\n      v-loading=\"formLoading\"\n    >\n       <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n      </el-form-item>\n      <el-form-item label=\"简介\" prop=\"description\">\n        <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"formData.birthday\"\n          type=\"date\"\n          value-format=\"x\"\n          placeholder=\"选择出生日期\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-radio-group v-model=\"formData.enabled\">\n          <el-radio\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.value\"\n          >\n            {{ dict.label }}\n          </el-radio>\n        </el-radio-group>\n      </el-form-item>\n      <el-form-item label=\"头像\" prop=\"avatar\">\n        <UploadImg v-model=\"formData.avatar\" />\n      </el-form-item>\n      <el-form-item label=\"附件\" prop=\"video\">\n        <UploadFile v-model=\"formData.video\" />\n      </el-form-item>\n      <el-form-item label=\"备注\" prop=\"memo\">\n        <Editor v-model=\"formData.memo\" height=\"150px\" />\n      </el-form-item>\n    </el-form>\n    <template #footer>\n      <el-button @click=\"submitForm\" type=\"primary\" :disabled=\"formLoading\">确 定</el-button>\n      <el-button @click=\"dialogVisible = false\">取 消</el-button>\n    </template>\n  </Dialog>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst dialogVisible = ref(false) // 弹窗的是否展示\nconst dialogTitle = ref('') // 弹窗的标题\nconst formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\nconst formType = ref('') // 表单的类型：create - 新增；update - 修改\nconst formData = ref({\n  id: undefined,\n  studentId: undefined,\n  name: undefined,\n  description: undefined,\n  birthday: undefined,\n  sex: undefined,\n  enabled: undefined,\n  avatar: undefined,\n  video: undefined,\n  memo: undefined,\n})\nconst formRules = reactive({\n  studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 打开弹窗 */\nconst open = async (type: string, id?: number, studentId: number) => {\n  dialogVisible.value = true\n  dialogTitle.value = t('action.' + type)\n  formType.value = type\n  resetForm()\n  formData.value.studentId = studentId\n  // 修改时，设置数据\n  if (id) {\n    formLoading.value = true\n    try {\n      formData.value = await StudentApi.getStudentTeacher(id)\n    } finally {\n      formLoading.value = false\n    }\n  }\n}\ndefineExpose({ open }) // 提供 open 方法，用于打开弹窗\n\n/** 提交表单 */\nconst emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调\nconst submitForm = async () => {\n  // 校验表单\n  await formRef.value.validate()\n  // 提交请求\n  formLoading.value = true\n  try {\n    const data = formData.value\n    if (formType.value === 'create') {\n      await StudentApi.createStudentTeacher(data)\n      message.success(t('common.createSuccess'))\n    } else {\n      await StudentApi.updateStudentTeacher(data)\n      message.success(t('common.updateSuccess'))\n    }\n    dialogVisible.value = false\n    // 发送操作成功的事件\n    emit('success')\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n    id: undefined,\n    studentId: undefined,\n    name: undefined,\n    description: undefined,\n    birthday: undefined,\n    sex: undefined,\n    enabled: undefined,\n    avatar: undefined,\n    video: undefined,\n    memo: undefined,\n  }\n  formRef.value?.resetFields()\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList",
    "content": "<template>\n  <!-- 列表 -->\n  <ContentWrap>\n    <el-button\n      type=\"primary\"\n      plain\n      @click=\"openForm('create')\"\n      v-hasPermi=\"['infra:student:create']\"\n    >\n      <Icon icon=\"ep:plus\" class=\"mr-5px\" /> 新增\n    </el-button>\n    <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n       <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n      <el-table-column\n        label=\"出生日期\"\n        align=\"center\"\n        prop=\"birthday\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"createTime\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"操作\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button\n            link\n            type=\"primary\"\n            @click=\"openForm('update', scope.row.id)\"\n            v-hasPermi=\"['infra:student:update']\"\n          >\n            编辑\n          </el-button>\n          <el-button\n            link\n            type=\"danger\"\n            @click=\"handleDelete(scope.row.id)\"\n            v-hasPermi=\"['infra:student:delete']\"\n          >\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页 -->\n    <Pagination\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNo\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n  </ContentWrap>\n    <!-- 表单弹窗：添加/修改 -->\n    <StudentTeacherForm ref=\"formRef\" @success=\"getList\" />\n</template>\n<script setup lang=\"ts\">\nimport { DICT_TYPE } from '@/utils/dict'\nimport { dateFormatter } from '@/utils/formatTime'\nimport * as StudentApi from '@/api/infra/demo'\nimport StudentTeacherForm from './StudentTeacherForm.vue'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst props = defineProps<{\n  studentId: undefined // 学生编号（主表的关联字段）\n}>()\nconst loading = ref(false) // 列表的加载中\nconst list = ref([]) // 列表的数据\nconst total = ref(0) // 列表的总页数\nconst queryParams = reactive({\n  pageNo: 1,\n  pageSize: 10,\n  studentId: undefined\n})\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.studentId,\n  (val) => {\n    queryParams.studentId = val\n    handleQuery()\n  },\n  { immediate: false }\n)\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    const data = await StudentApi.getStudentTeacherPage(queryParams)\n    list.value = data.list\n    total.value = data.total\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 添加/修改操作 */\nconst formRef = ref()\nconst openForm = (type: string, id?: number) => {\n  if (!props.studentId) {\n    message.error('请选择一个学生')\n    return\n  }\n  formRef.value.open(type, id, props.studentId)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = async (id: number) => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    // 发起删除\n    await StudentApi.deleteStudentTeacher(id)\n    message.success(t('common.delSuccess'))\n    // 刷新列表\n    await getList()\n  } catch {}\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/vue/index",
    "content": "<template>\n  <ContentWrap>\n    <!-- 搜索工作栏 -->\n    <el-form\n      class=\"-mb-15px\"\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入名字\"\n          clearable\n          @keyup.enter=\"handleQuery\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"queryParams.birthday\"\n          value-format=\"YYYY-MM-DD\"\n          type=\"date\"\n          placeholder=\"选择出生日期\"\n          clearable\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select\n          v-model=\"queryParams.sex\"\n          placeholder=\"请选择性别\"\n          clearable\n          class=\"!w-240px\"\n        >\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-select\n          v-model=\"queryParams.enabled\"\n          placeholder=\"请选择是否有效\"\n          clearable\n          class=\"!w-240px\"\n        >\n          <el-option\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker\n          v-model=\"queryParams.createTime\"\n          value-format=\"YYYY-MM-DD HH:mm:ss\"\n          type=\"daterange\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"[new Date('1 00:00:00'), new Date('1 23:59:59')]\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button @click=\"handleQuery\"><Icon icon=\"ep:search\" class=\"mr-5px\" /> 搜索</el-button>\n        <el-button @click=\"resetQuery\"><Icon icon=\"ep:refresh\" class=\"mr-5px\" /> 重置</el-button>\n        <el-button\n          type=\"primary\"\n          plain\n          @click=\"openForm('create')\"\n          v-hasPermi=\"['infra:student:create']\"\n        >\n          <Icon icon=\"ep:plus\" class=\"mr-5px\" /> 新增\n        </el-button>\n        <el-button\n          type=\"success\"\n          plain\n          @click=\"handleExport\"\n          :loading=\"exportLoading\"\n          v-hasPermi=\"['infra:student:export']\"\n        >\n          <Icon icon=\"ep:download\" class=\"mr-5px\" /> 导出\n        </el-button>\n      </el-form-item>\n    </el-form>\n  </ContentWrap>\n\n  <!-- 列表 -->\n  <ContentWrap>\n    <el-table\n      v-loading=\"loading\"\n      :data=\"list\"\n      :stripe=\"true\"\n      :show-overflow-tooltip=\"true\"\n      highlight-current-row\n      @current-change=\"handleCurrentChange\"\n    >\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n      <el-table-column\n        label=\"出生日期\"\n        align=\"center\"\n        prop=\"birthday\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"createTime\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"操作\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button\n            link\n            type=\"primary\"\n            @click=\"openForm('update', scope.row.id)\"\n            v-hasPermi=\"['infra:student:update']\"\n          >\n            编辑\n          </el-button>\n          <el-button\n            link\n            type=\"danger\"\n            @click=\"handleDelete(scope.row.id)\"\n            v-hasPermi=\"['infra:student:delete']\"\n          >\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页 -->\n    <Pagination\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNo\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n  </ContentWrap>\n\n  <!-- 表单弹窗：添加/修改 -->\n  <StudentForm ref=\"formRef\" @success=\"getList\" />\n  <!-- 子表的列表 -->\n  <ContentWrap>\n    <el-tabs model-value=\"studentContact\">\n      <el-tab-pane label=\"学生联系人\" name=\"studentContact\">\n        <StudentContactList :student-id=\"currentRow.id\" />\n      </el-tab-pane>\n      <el-tab-pane label=\"学生班主任\" name=\"studentTeacher\">\n        <StudentTeacherList :student-id=\"currentRow.id\" />\n      </el-tab-pane>\n    </el-tabs>\n  </ContentWrap>\n</template>\n\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport { dateFormatter } from '@/utils/formatTime'\nimport download from '@/utils/download'\nimport * as StudentApi from '@/api/infra/demo'\nimport StudentForm from './StudentForm.vue'\nimport StudentContactList from './components/StudentContactList.vue'\nimport StudentTeacherList from './components/StudentTeacherList.vue'\n\ndefineOptions({ name: 'InfraStudent' })\n\nconst message = useMessage() // 消息弹窗\nconst { t } = useI18n() // 国际化\n\nconst loading = ref(true) // 列表的加载中\nconst list = ref([]) // 列表的数据\nconst total = ref(0) // 列表的总页数\nconst queryParams = reactive({\n  pageNo: 1,\n  pageSize: 10,\n  name: undefined,\n  birthday: undefined,\n  birthday: [],\n  sex: undefined,\n  enabled: undefined,\n  createTime: [],\n})\nconst queryFormRef = ref() // 搜索的表单\nconst exportLoading = ref(false) // 导出的加载中\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    const data = await StudentApi.getStudentPage(queryParams)\n    list.value = data.list\n    total.value = data.total\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 重置按钮操作 */\nconst resetQuery = () => {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n\n/** 添加/修改操作 */\nconst formRef = ref()\nconst openForm = (type: string, id?: number) => {\n  formRef.value.open(type, id)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = async (id: number) => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    // 发起删除\n    await StudentApi.deleteStudent(id)\n    message.success(t('common.delSuccess'))\n    // 刷新列表\n    await getList()\n  } catch {}\n}\n\n/** 导出按钮操作 */\nconst handleExport = async () => {\n  try {\n    // 导出的二次确认\n    await message.exportConfirm()\n    // 发起导出\n    exportLoading.value = true\n    const data = await StudentApi.exportStudent(queryParams)\n    download.excel(data, '学生.xls')\n  } catch {\n  } finally {\n    exportLoading.value = false\n  }\n}\n\n/** 选中行操作 */\nconst currentRow = ref({}) // 选中行\nconst handleCurrentChange = (row) => {\n  currentRow.value = row\n}\n\n/** 初始化 **/\nonMounted(() => {\n  getList()\n})\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraStudentPageReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentSaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactList\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactList.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherList\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherList.vue\"\n}, {\n  \"contentPath\" : \"ts/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/api/infra/demo/index.ts\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 学生 TODO 补充编号 ==========\r\nErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生不存在\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生联系人 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_contact\")\n@KeySequence(\"infra_student_contact_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentContactDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生联系人 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {\n\n    default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {\n        return selectList(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/student\")\n@Validated\npublic class InfraStudentController {\n\n    @Resource\n    private InfraStudentService studentService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {\n        return success(studentService.createStudent(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {\n        studentService.updateStudent(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudent(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudent(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentRespVO> getStudent(@RequestParam(\"id\") Long id) {\n        InfraStudentDO student = studentService.getStudent(id);\n        return success(BeanUtils.toBean(student, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {\n        PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", InfraStudentRespVO.class,\n                        BeanUtils.toBean(list, InfraStudentRespVO.class));\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @GetMapping(\"/student-contact/list-by-student-id\")\n    @Operation(summary = \"获得学生联系人列表\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentContactListByStudentId(studentId));\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @GetMapping(\"/student-teacher/get-by-student-id\")\n    @Operation(summary = \"获得学生班主任\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentTeacherByStudentId(studentId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student\")\n@KeySequence(\"infra_student_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {\n\n    default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()\n                .likeIfPresent(InfraStudentDO::getName, reqVO.getName())\n                .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())\n                .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())\n                .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(InfraStudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class InfraStudentPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n    @Schema(description = \"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", example = \"1\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", example = \"true\")\n    private Boolean enabled;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.idev.excel.annotation.*;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraStudentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"是否有效\", converter = DictConvert.class)\n    @DictFormat(\"infra_boolean_string\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @ExcelProperty(\"头像\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @ExcelProperty(\"附件\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @ExcelProperty(\"备注\")\n    private String memo;\n\n    @Schema(description = \"创建时间\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class InfraStudentSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否有效不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"头像不能为空\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @NotEmpty(message = \"附件不能为空\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @NotEmpty(message = \"备注不能为空\")\n    private String memo;\n\n    @Schema(description = \"学生联系人列表\")\n    private List<InfraStudentContactDO> studentContacts;\n\n    @Schema(description = \"学生班主任\")\n    private InfraStudentTeacherDO studentTeacher;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraStudentService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteStudent(Long id);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    InfraStudentDO getStudent(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);\n\n    // ==================== 子表（学生联系人） ====================\n\n    /**\n     * 获得学生联系人列表\n     *\n     * @param studentId 学生编号\n     * @return 学生联系人列表\n     */\n    List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId);\n\n    // ==================== 子表（学生班主任） ====================\n\n    /**\n     * 获得学生班主任\n     *\n     * @param studentId 学生编号\n     * @return 学生班主任\n     */\n    InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraStudentServiceImpl implements InfraStudentService {\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n    @Resource\n    private InfraStudentContactMapper studentContactMapper;\n    @Resource\n    private InfraStudentTeacherMapper studentTeacherMapper;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createStudent(InfraStudentSaveReqVO createReqVO) {\n        // 插入\n        InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);\n        studentMapper.insert(student);\n\n        // 插入子表\n        createStudentContactList(student.getId(), createReqVO.getStudentContacts());\n        createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());\n        // 返回\n        return student.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStudent(InfraStudentSaveReqVO updateReqVO) {\n        // 校验存在\n        validateStudentExists(updateReqVO.getId());\n        // 更新\n        InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);\n        studentMapper.updateById(updateObj);\n\n        // 更新子表\n        updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());\n        updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStudent(Long id) {\n        // 校验存在\n        validateStudentExists(id);\n        // 删除\n        studentMapper.deleteById(id);\n\n        // 删除子表\n        deleteStudentContactByStudentId(id);\n        deleteStudentTeacherByStudentId(id);\n    }\n\n    private void validateStudentExists(Long id) {\n        if (studentMapper.selectById(id) == null) {\n            throw exception(STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public InfraStudentDO getStudent(Long id) {\n        return studentMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {\n        return studentMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @Override\n    public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) {\n        return studentContactMapper.selectListByStudentId(studentId);\n    }\n\n    private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) {\n        list.forEach(o -> o.setStudentId(studentId));\n        studentContactMapper.insertBatch(list);\n    }\n\n    private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) {\n        deleteStudentContactByStudentId(studentId);\n\t\tlist.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新\n        createStudentContactList(studentId, list);\n    }\n\n    private void deleteStudentContactByStudentId(Long studentId) {\n        studentContactMapper.deleteByStudentId(studentId);\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @Override\n    public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {\n        return studentTeacherMapper.selectByStudentId(studentId);\n    }\n\n    private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {\n        if (studentTeacher == null) {\n            return;\n        }\n        studentTeacher.setStudentId(studentId);\n        studentTeacherMapper.insert(studentTeacher);\n    }\n\n    private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {\n        if (studentTeacher == null) {\n\t\t\treturn;\n        }\n        studentTeacher.setStudentId(studentId);\n        studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下：updateTime 不更新\n        studentTeacherMapper.insertOrUpdate(studentTeacher);\n    }\n\n    private void deleteStudentTeacherByStudentId(Long studentId) {\n        studentTeacherMapper.deleteByStudentId(studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraStudentServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraStudentServiceImpl.class)\npublic class InfraStudentServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraStudentServiceImpl studentService;\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Test\n    public void testCreateStudent_success() {\n        // 准备参数\n        InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);\n\n        // 调用\n        Long studentId = studentService.createStudent(createReqVO);\n        // 断言\n        assertNotNull(studentId);\n        // 校验记录的属性是否正确\n        InfraStudentDO student = studentMapper.selectById(studentId);\n        assertPojoEquals(createReqVO, student, \"id\");\n    }\n\n    @Test\n    public void testUpdateStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {\n            o.setId(dbStudent.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        studentService.updateStudent(updateReqVO);\n        // 校验是否更新正确\n        InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, student);\n    }\n\n    @Test\n    public void testUpdateStudent_notExists() {\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbStudent.getId();\n\n        // 调用\n        studentService.deleteStudent(id);\n       // 校验数据不存在了\n       assertNull(studentMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteStudent_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetStudentPage() {\n       // mock 数据\n       InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到\n           o.setName(null);\n           o.setBirthday(null);\n           o.setSex(null);\n           o.setEnabled(null);\n           o.setCreateTime(null);\n       });\n       studentMapper.insert(dbStudent);\n       // 测试 name 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));\n       // 测试 birthday 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));\n       // 测试 sex 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));\n       // 测试 enabled 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));\n       // 测试 createTime 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));\n       // 准备参数\n       InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();\n       reqVO.setName(null);\n       reqVO.setBirthday(null);\n       reqVO.setSex(null);\n       reqVO.setEnabled(null);\n       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n\n       // 调用\n       PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);\n       // 断言\n       assertEquals(1, pageResult.getTotal());\n       assertEquals(1, pageResult.getList().size());\n       assertPojoEquals(dbStudent, pageResult.getList().get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生班主任 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_teacher\")\n@KeySequence(\"infra_student_teacher_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentTeacherDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生班主任 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {\n\n    default InfraStudentTeacherDO selectByStudentId(Long studentId) {\n        return selectOne(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_student\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" varchar NOT NULL,\n    \"birthday\" varchar NOT NULL,\n    \"sex\" int NOT NULL,\n    \"enabled\" bit NOT NULL,\n    \"avatar\" varchar NOT NULL,\n    \"video\" varchar NOT NULL,\n    \"memo\" varchar NOT NULL,\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (\"id\")\n) COMMENT '学生表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_student\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '学生管理', '', 2, 0, 888,\r\n    'student', '', 'infra/demo/index', 0, 'InfraStudent'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生查询', 'infra:student:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生创建', 'infra:student:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生更新', 'infra:student:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生删除', 'infra:student:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生导出', 'infra:student:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/ts/index",
    "content": "import request from '@/config/axios'\r\n\r\nexport interface StudentVO {\r\n  id: number\r\n  name: string\r\n  description: string\r\n  birthday: Date\r\n  sex: number\r\n  enabled: boolean\r\n  avatar: string\r\n  video: string\r\n  memo: string\r\n}\r\n\r\n// 查询学生分页\r\nexport const getStudentPage = async (params) => {\r\n  return await request.get({ url: `/infra/student/page`, params })\r\n}\r\n\r\n// 查询学生详情\r\nexport const getStudent = async (id: number) => {\r\n  return await request.get({ url: `/infra/student/get?id=` + id })\r\n}\r\n\r\n// 新增学生\r\nexport const createStudent = async (data: StudentVO) => {\r\n  return await request.post({ url: `/infra/student/create`, data })\r\n}\r\n\r\n// 修改学生\r\nexport const updateStudent = async (data: StudentVO) => {\r\n  return await request.put({ url: `/infra/student/update`, data })\r\n}\r\n\r\n// 删除学生\r\nexport const deleteStudent = async (id: number) => {\r\n  return await request.delete({ url: `/infra/student/delete?id=` + id })\r\n}\r\n\r\n// 导出学生 Excel\r\nexport const exportStudent = async (params) => {\r\n  return await request.download({ url: `/infra/student/export-excel`, params })\r\n}\r\n\r\n// ==================== 子表（学生联系人） ====================\r\n\r\n// 获得学生联系人列表\r\nexport const getStudentContactListByStudentId = async (studentId) => {\r\n  return await request.get({ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId })\r\n}\r\n\r\n// ==================== 子表（学生班主任） ====================\r\n\r\n// 获得学生班主任\r\nexport const getStudentTeacherByStudentId = async (studentId) => {\r\n  return await request.get({ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId })\r\n}\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm",
    "content": "<template>\n  <el-form\n    ref=\"formRef\"\n    :model=\"formData\"\n    :rules=\"formRules\"\n    v-loading=\"formLoading\"\n    label-width=\"0px\"\n    :inline-message=\"true\"\n  >\n    <el-table :data=\"formData\" class=\"-mt-10px\">\n      <el-table-column label=\"序号\" type=\"index\" width=\"100\" />\n       <el-table-column label=\"名字\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.name`\" :rules=\"formRules.name\" class=\"mb-0px!\">\n            <el-input v-model=\"row.name\" placeholder=\"请输入名字\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"简介\" min-width=\"200\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.description`\" :rules=\"formRules.description\" class=\"mb-0px!\">\n            <el-input v-model=\"row.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"出生日期\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.birthday`\" :rules=\"formRules.birthday\" class=\"mb-0px!\">\n            <el-date-picker\n              v-model=\"row.birthday\"\n              type=\"date\"\n              value-format=\"x\"\n              placeholder=\"选择出生日期\"\n            />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"性别\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.sex`\" :rules=\"formRules.sex\" class=\"mb-0px!\">\n            <el-select v-model=\"row.sex\" placeholder=\"请选择性别\">\n                <el-option\n                  v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n                  :key=\"dict.value\"\n                  :label=\"dict.label\"\n                  :value=\"dict.value\"\n                />\n            </el-select>\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.enabled`\" :rules=\"formRules.enabled\" class=\"mb-0px!\">\n            <el-radio-group v-model=\"row.enabled\">\n                <el-radio\n                  v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                  :key=\"dict.value\"\n                  :label=\"dict.value\"\n                >\n                  {{ dict.label }}\n                </el-radio>\n            </el-radio-group>\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" min-width=\"200\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.avatar`\" :rules=\"formRules.avatar\" class=\"mb-0px!\">\n            <UploadImg v-model=\"row.avatar\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"附件\" min-width=\"200\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.video`\" :rules=\"formRules.video\" class=\"mb-0px!\">\n            <UploadFile v-model=\"row.video\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" min-width=\"400\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.memo`\" :rules=\"formRules.memo\" class=\"mb-0px!\">\n            <Editor v-model=\"row.memo\" height=\"150px\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column align=\"center\" fixed=\"right\" label=\"操作\" width=\"60\">\n        <template #default=\"{ $index }\">\n          <el-button @click=\"handleDelete($index)\" link>—</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n  </el-form>\n  <el-row justify=\"center\" class=\"mt-3\">\n    <el-button @click=\"handleAdd\" round>+ 添加学生联系人</el-button>\n  </el-row>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst props = defineProps<{\n  studentId: undefined // 学生编号（主表的关联字段）\n}>()\nconst formLoading = ref(false) // 表单的加载中\nconst formData = ref([])\nconst formRules = reactive({\n  studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.studentId,\n  async (val) => {\n    // 1. 重置表单\n    formData.value = []\n    // 2. val 非空，则加载数据\n    if (!val) {\n      return;\n    }\n    try {\n      formLoading.value = true\n      formData.value = await StudentApi.getStudentContactListByStudentId(val)\n    } finally {\n      formLoading.value = false\n    }\n  },\n  { immediate: true }\n)\n\n/** 新增按钮操作 */\nconst handleAdd = () => {\n  const row = {\n    id: undefined,\n    studentId: undefined,\n    name: undefined,\n    description: undefined,\n    birthday: undefined,\n    sex: undefined,\n    enabled: undefined,\n    avatar: undefined,\n    video: undefined,\n    memo: undefined,\n  }\n  row.studentId = props.studentId\n  formData.value.push(row)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = (index) => {\n  formData.value.splice(index, 1)\n}\n\n/** 表单校验 */\nconst validate = () => {\n  return formRef.value.validate()\n}\n\n/** 表单值 */\nconst getData = () => {\n  return formData.value\n}\n\ndefineExpose({ validate, getData })\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList",
    "content": "<template>\n  <!-- 列表 -->\n  <ContentWrap>\n    <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n       <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n      <el-table-column\n        label=\"出生日期\"\n        align=\"center\"\n        prop=\"birthday\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"createTime\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n    </el-table>\n  </ContentWrap>\n</template>\n<script setup lang=\"ts\">\nimport { DICT_TYPE } from '@/utils/dict'\nimport { dateFormatter } from '@/utils/formatTime'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst props = defineProps<{\n  studentId: undefined // 学生编号（主表的关联字段）\n}>()\nconst loading = ref(false) // 列表的加载中\nconst list = ref([]) // 列表的数据\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    list.value = await StudentApi.getStudentContactListByStudentId(props.studentId)\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 初始化 **/\nonMounted(() => {\n  getList()\n})\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm",
    "content": "<template>\n  <Dialog :title=\"dialogTitle\" v-model=\"dialogVisible\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"formRules\"\n      label-width=\"100px\"\n      v-loading=\"formLoading\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n      </el-form-item>\n      <el-form-item label=\"简介\" prop=\"description\">\n        <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"formData.birthday\"\n          type=\"date\"\n          value-format=\"x\"\n          placeholder=\"选择出生日期\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-radio-group v-model=\"formData.enabled\">\n          <el-radio\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.value\"\n          >\n            {{ dict.label }}\n          </el-radio>\n        </el-radio-group>\n      </el-form-item>\n      <el-form-item label=\"头像\" prop=\"avatar\">\n        <UploadImg v-model=\"formData.avatar\" />\n      </el-form-item>\n      <el-form-item label=\"附件\" prop=\"video\">\n        <UploadFile v-model=\"formData.video\" />\n      </el-form-item>\n      <el-form-item label=\"备注\" prop=\"memo\">\n        <Editor v-model=\"formData.memo\" height=\"150px\" />\n      </el-form-item>\n    </el-form>\n    <!-- 子表的表单 -->\n    <el-tabs v-model=\"subTabsName\">\n      <el-tab-pane label=\"学生联系人\" name=\"studentContact\">\n        <StudentContactForm ref=\"studentContactFormRef\" :student-id=\"formData.id\" />\n      </el-tab-pane>\n      <el-tab-pane label=\"学生班主任\" name=\"studentTeacher\">\n        <StudentTeacherForm ref=\"studentTeacherFormRef\" :student-id=\"formData.id\" />\n      </el-tab-pane>\n    </el-tabs>\n    <template #footer>\n      <el-button @click=\"submitForm\" type=\"primary\" :disabled=\"formLoading\">确 定</el-button>\n      <el-button @click=\"dialogVisible = false\">取 消</el-button>\n    </template>\n  </Dialog>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\nimport StudentContactForm from './components/StudentContactForm.vue'\nimport StudentTeacherForm from './components/StudentTeacherForm.vue'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst dialogVisible = ref(false) // 弹窗的是否展示\nconst dialogTitle = ref('') // 弹窗的标题\nconst formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\nconst formType = ref('') // 表单的类型：create - 新增；update - 修改\nconst formData = ref({\n  id: undefined,\n  name: undefined,\n  description: undefined,\n  birthday: undefined,\n  sex: undefined,\n  enabled: undefined,\n  avatar: undefined,\n  video: undefined,\n  memo: undefined,\n})\nconst formRules = reactive({\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 子表的表单 */\nconst subTabsName = ref('studentContact')\nconst studentContactFormRef = ref()\nconst studentTeacherFormRef = ref()\n\n/** 打开弹窗 */\nconst open = async (type: string, id?: number) => {\n  dialogVisible.value = true\n  dialogTitle.value = t('action.' + type)\n  formType.value = type\n  resetForm()\n  // 修改时，设置数据\n  if (id) {\n    formLoading.value = true\n    try {\n      formData.value = await StudentApi.getStudent(id)\n    } finally {\n      formLoading.value = false\n    }\n  }\n}\ndefineExpose({ open }) // 提供 open 方法，用于打开弹窗\n\n/** 提交表单 */\nconst emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调\nconst submitForm = async () => {\n  // 校验表单\n  await formRef.value.validate()\n  // 校验子表单\n  try {\n    await studentContactFormRef.value.validate()\n  } catch (e) {\n    subTabsName.value = 'studentContact'\n    return\n  }\n  try {\n    await studentTeacherFormRef.value.validate()\n  } catch (e) {\n    subTabsName.value = 'studentTeacher'\n    return\n  }\n  // 提交请求\n  formLoading.value = true\n  try {\n    const data = formData.value as unknown as StudentApi.StudentVO\n    // 拼接子表的数据\n    data.studentContacts = studentContactFormRef.value.getData()\n    data.studentTeacher = studentTeacherFormRef.value.getData()\n    if (formType.value === 'create') {\n      await StudentApi.createStudent(data)\n      message.success(t('common.createSuccess'))\n    } else {\n      await StudentApi.updateStudent(data)\n      message.success(t('common.updateSuccess'))\n    }\n    dialogVisible.value = false\n    // 发送操作成功的事件\n    emit('success')\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n    id: undefined,\n    name: undefined,\n    description: undefined,\n    birthday: undefined,\n    sex: undefined,\n    enabled: undefined,\n    avatar: undefined,\n    video: undefined,\n    memo: undefined,\n  }\n  formRef.value?.resetFields()\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm",
    "content": "<template>\n  <el-form\n    ref=\"formRef\"\n    :model=\"formData\"\n    :rules=\"formRules\"\n    label-width=\"100px\"\n    v-loading=\"formLoading\"\n  >\n     <el-form-item label=\"名字\" prop=\"name\">\n      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n    </el-form-item>\n    <el-form-item label=\"简介\" prop=\"description\">\n      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n    </el-form-item>\n    <el-form-item label=\"出生日期\" prop=\"birthday\">\n      <el-date-picker\n        v-model=\"formData.birthday\"\n        type=\"date\"\n        value-format=\"x\"\n        placeholder=\"选择出生日期\"\n      />\n    </el-form-item>\n    <el-form-item label=\"性别\" prop=\"sex\">\n      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n        <el-option\n          v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n          :key=\"dict.value\"\n          :label=\"dict.label\"\n          :value=\"dict.value\"\n        />\n      </el-select>\n    </el-form-item>\n    <el-form-item label=\"是否有效\" prop=\"enabled\">\n      <el-radio-group v-model=\"formData.enabled\">\n        <el-radio\n          v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n          :key=\"dict.value\"\n          :label=\"dict.value\"\n          >\n          {{ dict.label }}\n        </el-radio>\n      </el-radio-group>\n    </el-form-item>\n    <el-form-item label=\"头像\" prop=\"avatar\">\n      <UploadImg v-model=\"formData.avatar\" />\n    </el-form-item>\n    <el-form-item label=\"附件\" prop=\"video\">\n      <UploadFile v-model=\"formData.video\" />\n    </el-form-item>\n    <el-form-item label=\"备注\" prop=\"memo\">\n      <Editor v-model=\"formData.memo\" height=\"150px\" />\n    </el-form-item>\n  </el-form>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst props = defineProps<{\n  studentId: undefined // 学生编号（主表的关联字段）\n}>()\nconst formLoading = ref(false) // 表单的加载中\nconst formData = ref([])\nconst formRules = reactive({\n  studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.studentId,\n  async (val) => {\n    // 1. 重置表单\n    formData.value = {\n      id: undefined,\n      studentId: undefined,\n      name: undefined,\n      description: undefined,\n      birthday: undefined,\n      sex: undefined,\n      enabled: undefined,\n      avatar: undefined,\n      video: undefined,\n      memo: undefined,\n    }\n    // 2. val 非空，则加载数据\n    if (!val) {\n      return;\n    }\n    try {\n      formLoading.value = true\n      const data = await StudentApi.getStudentTeacherByStudentId(val)\n      if (!data) {\n        return\n      }\n      formData.value = data\n    } finally {\n      formLoading.value = false\n    }\n  },\n  { immediate: true }\n)\n\n/** 表单校验 */\nconst validate = () => {\n  return formRef.value.validate()\n}\n\n/** 表单值 */\nconst getData = () => {\n  return formData.value\n}\n\ndefineExpose({ validate, getData })\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList",
    "content": "<template>\n  <!-- 列表 -->\n  <ContentWrap>\n    <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n       <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n      <el-table-column\n        label=\"出生日期\"\n        align=\"center\"\n        prop=\"birthday\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"createTime\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n    </el-table>\n  </ContentWrap>\n</template>\n<script setup lang=\"ts\">\nimport { DICT_TYPE } from '@/utils/dict'\nimport { dateFormatter } from '@/utils/formatTime'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst props = defineProps<{\n  studentId: undefined // 学生编号（主表的关联字段）\n}>()\nconst loading = ref(false) // 列表的加载中\nconst list = ref([]) // 列表的数据\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    const data = await StudentApi.getStudentTeacherByStudentId(props.studentId)\n    if (!data) {\n      return\n    }\n    list.value.push(data)\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 初始化 **/\nonMounted(() => {\n  getList()\n})\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/vue/index",
    "content": "<template>\n  <ContentWrap>\n    <!-- 搜索工作栏 -->\n    <el-form\n      class=\"-mb-15px\"\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入名字\"\n          clearable\n          @keyup.enter=\"handleQuery\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"queryParams.birthday\"\n          value-format=\"YYYY-MM-DD\"\n          type=\"date\"\n          placeholder=\"选择出生日期\"\n          clearable\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select\n          v-model=\"queryParams.sex\"\n          placeholder=\"请选择性别\"\n          clearable\n          class=\"!w-240px\"\n        >\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-select\n          v-model=\"queryParams.enabled\"\n          placeholder=\"请选择是否有效\"\n          clearable\n          class=\"!w-240px\"\n        >\n          <el-option\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker\n          v-model=\"queryParams.createTime\"\n          value-format=\"YYYY-MM-DD HH:mm:ss\"\n          type=\"daterange\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"[new Date('1 00:00:00'), new Date('1 23:59:59')]\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button @click=\"handleQuery\"><Icon icon=\"ep:search\" class=\"mr-5px\" /> 搜索</el-button>\n        <el-button @click=\"resetQuery\"><Icon icon=\"ep:refresh\" class=\"mr-5px\" /> 重置</el-button>\n        <el-button\n          type=\"primary\"\n          plain\n          @click=\"openForm('create')\"\n          v-hasPermi=\"['infra:student:create']\"\n        >\n          <Icon icon=\"ep:plus\" class=\"mr-5px\" /> 新增\n        </el-button>\n        <el-button\n          type=\"success\"\n          plain\n          @click=\"handleExport\"\n          :loading=\"exportLoading\"\n          v-hasPermi=\"['infra:student:export']\"\n        >\n          <Icon icon=\"ep:download\" class=\"mr-5px\" /> 导出\n        </el-button>\n      </el-form-item>\n    </el-form>\n  </ContentWrap>\n\n  <!-- 列表 -->\n  <ContentWrap>\n    <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n      <!-- 子表的列表 -->\n      <el-table-column type=\"expand\">\n        <template #default=\"scope\">\n          <el-tabs model-value=\"studentContact\">\n            <el-tab-pane label=\"学生联系人\" name=\"studentContact\">\n              <StudentContactList :student-id=\"scope.row.id\" />\n            </el-tab-pane>\n            <el-tab-pane label=\"学生班主任\" name=\"studentTeacher\">\n              <StudentTeacherList :student-id=\"scope.row.id\" />\n            </el-tab-pane>\n          </el-tabs>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n      <el-table-column\n        label=\"出生日期\"\n        align=\"center\"\n        prop=\"birthday\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"createTime\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"操作\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button\n            link\n            type=\"primary\"\n            @click=\"openForm('update', scope.row.id)\"\n            v-hasPermi=\"['infra:student:update']\"\n          >\n            编辑\n          </el-button>\n          <el-button\n            link\n            type=\"danger\"\n            @click=\"handleDelete(scope.row.id)\"\n            v-hasPermi=\"['infra:student:delete']\"\n          >\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页 -->\n    <Pagination\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNo\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n  </ContentWrap>\n\n  <!-- 表单弹窗：添加/修改 -->\n  <StudentForm ref=\"formRef\" @success=\"getList\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport { dateFormatter } from '@/utils/formatTime'\nimport download from '@/utils/download'\nimport * as StudentApi from '@/api/infra/demo'\nimport StudentForm from './StudentForm.vue'\nimport StudentContactList from './components/StudentContactList.vue'\nimport StudentTeacherList from './components/StudentTeacherList.vue'\n\ndefineOptions({ name: 'InfraStudent' })\n\nconst message = useMessage() // 消息弹窗\nconst { t } = useI18n() // 国际化\n\nconst loading = ref(true) // 列表的加载中\nconst list = ref([]) // 列表的数据\nconst total = ref(0) // 列表的总页数\nconst queryParams = reactive({\n  pageNo: 1,\n  pageSize: 10,\n  name: undefined,\n  birthday: undefined,\n  birthday: [],\n  sex: undefined,\n  enabled: undefined,\n  createTime: [],\n})\nconst queryFormRef = ref() // 搜索的表单\nconst exportLoading = ref(false) // 导出的加载中\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    const data = await StudentApi.getStudentPage(queryParams)\n    list.value = data.list\n    total.value = data.total\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 重置按钮操作 */\nconst resetQuery = () => {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n\n/** 添加/修改操作 */\nconst formRef = ref()\nconst openForm = (type: string, id?: number) => {\n  formRef.value.open(type, id)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = async (id: number) => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    // 发起删除\n    await StudentApi.deleteStudent(id)\n    message.success(t('common.delSuccess'))\n    // 刷新列表\n    await getList()\n  } catch {}\n}\n\n/** 导出按钮操作 */\nconst handleExport = async () => {\n  try {\n    // 导出的二次确认\n    await message.exportConfirm()\n    // 发起导出\n    exportLoading.value = true\n    const data = await StudentApi.exportStudent(queryParams)\n    download.excel(data, '学生.xls')\n  } catch {\n  } finally {\n    exportLoading.value = false\n  }\n}\n\n/** 初始化 **/\nonMounted(() => {\n  getList()\n})\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraStudentPageReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentSaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentContactMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentTeacherMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentContactForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentTeacherForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue\"\n}, {\n  \"contentPath\" : \"ts/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/api/infra/demo/index.ts\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 学生 TODO 补充编号 ==========\r\nErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生不存在\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生联系人 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_contact\")\n@KeySequence(\"infra_student_contact_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentContactDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生联系人 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {\n\n    default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {\n        return selectList(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentContactDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/student\")\n@Validated\npublic class InfraStudentController {\n\n    @Resource\n    private InfraStudentService studentService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {\n        return success(studentService.createStudent(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {\n        studentService.updateStudent(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudent(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudent(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentRespVO> getStudent(@RequestParam(\"id\") Long id) {\n        InfraStudentDO student = studentService.getStudent(id);\n        return success(BeanUtils.toBean(student, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {\n        PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", InfraStudentRespVO.class,\n                        BeanUtils.toBean(list, InfraStudentRespVO.class));\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @GetMapping(\"/student-contact/list-by-student-id\")\n    @Operation(summary = \"获得学生联系人列表\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentContactListByStudentId(studentId));\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @GetMapping(\"/student-teacher/get-by-student-id\")\n    @Operation(summary = \"获得学生班主任\")\n    @Parameter(name = \"studentId\", description = \"学生编号\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam(\"studentId\") Long studentId) {\n        return success(studentService.getStudentTeacherByStudentId(studentId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student\")\n@KeySequence(\"infra_student_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {\n\n    default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()\n                .likeIfPresent(InfraStudentDO::getName, reqVO.getName())\n                .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())\n                .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())\n                .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(InfraStudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class InfraStudentPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n    @Schema(description = \"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", example = \"1\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", example = \"true\")\n    private Boolean enabled;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.idev.excel.annotation.*;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraStudentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"是否有效\", converter = DictConvert.class)\n    @DictFormat(\"infra_boolean_string\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @ExcelProperty(\"头像\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @ExcelProperty(\"附件\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @ExcelProperty(\"备注\")\n    private String memo;\n\n    @Schema(description = \"创建时间\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class InfraStudentSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否有效不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"头像不能为空\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @NotEmpty(message = \"附件不能为空\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @NotEmpty(message = \"备注不能为空\")\n    private String memo;\n\n    @Schema(description = \"学生联系人列表\")\n    private List<InfraStudentContactDO> studentContacts;\n\n    @Schema(description = \"学生班主任\")\n    private InfraStudentTeacherDO studentTeacher;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraStudentService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteStudent(Long id);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    InfraStudentDO getStudent(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);\n\n    // ==================== 子表（学生联系人） ====================\n\n    /**\n     * 获得学生联系人列表\n     *\n     * @param studentId 学生编号\n     * @return 学生联系人列表\n     */\n    List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId);\n\n    // ==================== 子表（学生班主任） ====================\n\n    /**\n     * 获得学生班主任\n     *\n     * @param studentId 学生编号\n     * @return 学生班主任\n     */\n    InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraStudentServiceImpl implements InfraStudentService {\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n    @Resource\n    private InfraStudentContactMapper studentContactMapper;\n    @Resource\n    private InfraStudentTeacherMapper studentTeacherMapper;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createStudent(InfraStudentSaveReqVO createReqVO) {\n        // 插入\n        InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);\n        studentMapper.insert(student);\n\n        // 插入子表\n        createStudentContactList(student.getId(), createReqVO.getStudentContacts());\n        createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());\n        // 返回\n        return student.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateStudent(InfraStudentSaveReqVO updateReqVO) {\n        // 校验存在\n        validateStudentExists(updateReqVO.getId());\n        // 更新\n        InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);\n        studentMapper.updateById(updateObj);\n\n        // 更新子表\n        updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());\n        updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteStudent(Long id) {\n        // 校验存在\n        validateStudentExists(id);\n        // 删除\n        studentMapper.deleteById(id);\n\n        // 删除子表\n        deleteStudentContactByStudentId(id);\n        deleteStudentTeacherByStudentId(id);\n    }\n\n    private void validateStudentExists(Long id) {\n        if (studentMapper.selectById(id) == null) {\n            throw exception(STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public InfraStudentDO getStudent(Long id) {\n        return studentMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {\n        return studentMapper.selectPage(pageReqVO);\n    }\n\n    // ==================== 子表（学生联系人） ====================\n\n    @Override\n    public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) {\n        return studentContactMapper.selectListByStudentId(studentId);\n    }\n\n    private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) {\n        list.forEach(o -> o.setStudentId(studentId));\n        studentContactMapper.insertBatch(list);\n    }\n\n    private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) {\n        deleteStudentContactByStudentId(studentId);\n\t\tlist.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新\n        createStudentContactList(studentId, list);\n    }\n\n    private void deleteStudentContactByStudentId(Long studentId) {\n        studentContactMapper.deleteByStudentId(studentId);\n    }\n\n    // ==================== 子表（学生班主任） ====================\n\n    @Override\n    public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {\n        return studentTeacherMapper.selectByStudentId(studentId);\n    }\n\n    private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {\n        if (studentTeacher == null) {\n            return;\n        }\n        studentTeacher.setStudentId(studentId);\n        studentTeacherMapper.insert(studentTeacher);\n    }\n\n    private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {\n        if (studentTeacher == null) {\n\t\t\treturn;\n        }\n        studentTeacher.setStudentId(studentId);\n        studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下：updateTime 不更新\n        studentTeacherMapper.insertOrUpdate(studentTeacher);\n    }\n\n    private void deleteStudentTeacherByStudentId(Long studentId) {\n        studentTeacherMapper.deleteByStudentId(studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraStudentServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraStudentServiceImpl.class)\npublic class InfraStudentServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraStudentServiceImpl studentService;\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Test\n    public void testCreateStudent_success() {\n        // 准备参数\n        InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);\n\n        // 调用\n        Long studentId = studentService.createStudent(createReqVO);\n        // 断言\n        assertNotNull(studentId);\n        // 校验记录的属性是否正确\n        InfraStudentDO student = studentMapper.selectById(studentId);\n        assertPojoEquals(createReqVO, student, \"id\");\n    }\n\n    @Test\n    public void testUpdateStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {\n            o.setId(dbStudent.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        studentService.updateStudent(updateReqVO);\n        // 校验是否更新正确\n        InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, student);\n    }\n\n    @Test\n    public void testUpdateStudent_notExists() {\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbStudent.getId();\n\n        // 调用\n        studentService.deleteStudent(id);\n       // 校验数据不存在了\n       assertNull(studentMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteStudent_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetStudentPage() {\n       // mock 数据\n       InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到\n           o.setName(null);\n           o.setBirthday(null);\n           o.setSex(null);\n           o.setEnabled(null);\n           o.setCreateTime(null);\n       });\n       studentMapper.insert(dbStudent);\n       // 测试 name 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));\n       // 测试 birthday 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));\n       // 测试 sex 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));\n       // 测试 enabled 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));\n       // 测试 createTime 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));\n       // 准备参数\n       InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();\n       reqVO.setName(null);\n       reqVO.setBirthday(null);\n       reqVO.setSex(null);\n       reqVO.setEnabled(null);\n       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n\n       // 调用\n       PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);\n       // 断言\n       assertEquals(1, pageResult.getTotal());\n       assertEquals(1, pageResult.getList().size());\n       assertPojoEquals(dbStudent, pageResult.getList().get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生班主任 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student_teacher\")\n@KeySequence(\"infra_student_teacher_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentTeacherDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 学生编号\n     */\n    private Long studentId;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 学生班主任 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {\n\n    default InfraStudentTeacherDO selectByStudentId(Long studentId) {\n        return selectOne(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n    default int deleteByStudentId(Long studentId) {\n        return delete(InfraStudentTeacherDO::getStudentId, studentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_student\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" varchar NOT NULL,\n    \"birthday\" varchar NOT NULL,\n    \"sex\" int NOT NULL,\n    \"enabled\" bit NOT NULL,\n    \"avatar\" varchar NOT NULL,\n    \"video\" varchar NOT NULL,\n    \"memo\" varchar NOT NULL,\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (\"id\")\n) COMMENT '学生表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_student\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '学生管理', '', 2, 0, 888,\r\n    'student', '', 'infra/demo/index', 0, 'InfraStudent'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生查询', 'infra:student:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生创建', 'infra:student:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生更新', 'infra:student:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生删除', 'infra:student:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生导出', 'infra:student:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/ts/index",
    "content": "import request from '@/config/axios'\r\n\r\nexport interface StudentVO {\r\n  id: number\r\n  name: string\r\n  description: string\r\n  birthday: Date\r\n  sex: number\r\n  enabled: boolean\r\n  avatar: string\r\n  video: string\r\n  memo: string\r\n}\r\n\r\n// 查询学生分页\r\nexport const getStudentPage = async (params) => {\r\n  return await request.get({ url: `/infra/student/page`, params })\r\n}\r\n\r\n// 查询学生详情\r\nexport const getStudent = async (id: number) => {\r\n  return await request.get({ url: `/infra/student/get?id=` + id })\r\n}\r\n\r\n// 新增学生\r\nexport const createStudent = async (data: StudentVO) => {\r\n  return await request.post({ url: `/infra/student/create`, data })\r\n}\r\n\r\n// 修改学生\r\nexport const updateStudent = async (data: StudentVO) => {\r\n  return await request.put({ url: `/infra/student/update`, data })\r\n}\r\n\r\n// 删除学生\r\nexport const deleteStudent = async (id: number) => {\r\n  return await request.delete({ url: `/infra/student/delete?id=` + id })\r\n}\r\n\r\n// 导出学生 Excel\r\nexport const exportStudent = async (params) => {\r\n  return await request.download({ url: `/infra/student/export-excel`, params })\r\n}\r\n\r\n// ==================== 子表（学生联系人） ====================\r\n\r\n// 获得学生联系人列表\r\nexport const getStudentContactListByStudentId = async (studentId) => {\r\n  return await request.get({ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId })\r\n}\r\n\r\n// ==================== 子表（学生班主任） ====================\r\n\r\n// 获得学生班主任\r\nexport const getStudentTeacherByStudentId = async (studentId) => {\r\n  return await request.get({ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId })\r\n}\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm",
    "content": "<template>\n  <el-form\n    ref=\"formRef\"\n    :model=\"formData\"\n    :rules=\"formRules\"\n    v-loading=\"formLoading\"\n    label-width=\"0px\"\n    :inline-message=\"true\"\n  >\n    <el-table :data=\"formData\" class=\"-mt-10px\">\n      <el-table-column label=\"序号\" type=\"index\" width=\"100\" />\n       <el-table-column label=\"名字\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.name`\" :rules=\"formRules.name\" class=\"mb-0px!\">\n            <el-input v-model=\"row.name\" placeholder=\"请输入名字\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"简介\" min-width=\"200\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.description`\" :rules=\"formRules.description\" class=\"mb-0px!\">\n            <el-input v-model=\"row.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"出生日期\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.birthday`\" :rules=\"formRules.birthday\" class=\"mb-0px!\">\n            <el-date-picker\n              v-model=\"row.birthday\"\n              type=\"date\"\n              value-format=\"x\"\n              placeholder=\"选择出生日期\"\n            />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"性别\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.sex`\" :rules=\"formRules.sex\" class=\"mb-0px!\">\n            <el-select v-model=\"row.sex\" placeholder=\"请选择性别\">\n                <el-option\n                  v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n                  :key=\"dict.value\"\n                  :label=\"dict.label\"\n                  :value=\"dict.value\"\n                />\n            </el-select>\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" min-width=\"150\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.enabled`\" :rules=\"formRules.enabled\" class=\"mb-0px!\">\n            <el-radio-group v-model=\"row.enabled\">\n                <el-radio\n                  v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n                  :key=\"dict.value\"\n                  :label=\"dict.value\"\n                >\n                  {{ dict.label }}\n                </el-radio>\n            </el-radio-group>\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" min-width=\"200\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.avatar`\" :rules=\"formRules.avatar\" class=\"mb-0px!\">\n            <UploadImg v-model=\"row.avatar\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"附件\" min-width=\"200\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.video`\" :rules=\"formRules.video\" class=\"mb-0px!\">\n            <UploadFile v-model=\"row.video\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" min-width=\"400\">\n        <template #default=\"{ row, $index }\">\n          <el-form-item :prop=\"`${$index}.memo`\" :rules=\"formRules.memo\" class=\"mb-0px!\">\n            <Editor v-model=\"row.memo\" height=\"150px\" />\n          </el-form-item>\n        </template>\n      </el-table-column>\n      <el-table-column align=\"center\" fixed=\"right\" label=\"操作\" width=\"60\">\n        <template #default=\"{ $index }\">\n          <el-button @click=\"handleDelete($index)\" link>—</el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n  </el-form>\n  <el-row justify=\"center\" class=\"mt-3\">\n    <el-button @click=\"handleAdd\" round>+ 添加学生联系人</el-button>\n  </el-row>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst props = defineProps<{\n  studentId: undefined // 学生编号（主表的关联字段）\n}>()\nconst formLoading = ref(false) // 表单的加载中\nconst formData = ref([])\nconst formRules = reactive({\n  studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.studentId,\n  async (val) => {\n    // 1. 重置表单\n    formData.value = []\n    // 2. val 非空，则加载数据\n    if (!val) {\n      return;\n    }\n    try {\n      formLoading.value = true\n      formData.value = await StudentApi.getStudentContactListByStudentId(val)\n    } finally {\n      formLoading.value = false\n    }\n  },\n  { immediate: true }\n)\n\n/** 新增按钮操作 */\nconst handleAdd = () => {\n  const row = {\n    id: undefined,\n    studentId: undefined,\n    name: undefined,\n    description: undefined,\n    birthday: undefined,\n    sex: undefined,\n    enabled: undefined,\n    avatar: undefined,\n    video: undefined,\n    memo: undefined,\n  }\n  row.studentId = props.studentId\n  formData.value.push(row)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = (index) => {\n  formData.value.splice(index, 1)\n}\n\n/** 表单校验 */\nconst validate = () => {\n  return formRef.value.validate()\n}\n\n/** 表单值 */\nconst getData = () => {\n  return formData.value\n}\n\ndefineExpose({ validate, getData })\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm",
    "content": "<template>\n  <Dialog :title=\"dialogTitle\" v-model=\"dialogVisible\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"formRules\"\n      label-width=\"100px\"\n      v-loading=\"formLoading\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n      </el-form-item>\n      <el-form-item label=\"简介\" prop=\"description\">\n        <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"formData.birthday\"\n          type=\"date\"\n          value-format=\"x\"\n          placeholder=\"选择出生日期\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-radio-group v-model=\"formData.enabled\">\n          <el-radio\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.value\"\n          >\n            {{ dict.label }}\n          </el-radio>\n        </el-radio-group>\n      </el-form-item>\n      <el-form-item label=\"头像\" prop=\"avatar\">\n        <UploadImg v-model=\"formData.avatar\" />\n      </el-form-item>\n      <el-form-item label=\"附件\" prop=\"video\">\n        <UploadFile v-model=\"formData.video\" />\n      </el-form-item>\n      <el-form-item label=\"备注\" prop=\"memo\">\n        <Editor v-model=\"formData.memo\" height=\"150px\" />\n      </el-form-item>\n    </el-form>\n    <!-- 子表的表单 -->\n    <el-tabs v-model=\"subTabsName\">\n      <el-tab-pane label=\"学生联系人\" name=\"studentContact\">\n        <StudentContactForm ref=\"studentContactFormRef\" :student-id=\"formData.id\" />\n      </el-tab-pane>\n      <el-tab-pane label=\"学生班主任\" name=\"studentTeacher\">\n        <StudentTeacherForm ref=\"studentTeacherFormRef\" :student-id=\"formData.id\" />\n      </el-tab-pane>\n    </el-tabs>\n    <template #footer>\n      <el-button @click=\"submitForm\" type=\"primary\" :disabled=\"formLoading\">确 定</el-button>\n      <el-button @click=\"dialogVisible = false\">取 消</el-button>\n    </template>\n  </Dialog>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\nimport StudentContactForm from './components/StudentContactForm.vue'\nimport StudentTeacherForm from './components/StudentTeacherForm.vue'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst dialogVisible = ref(false) // 弹窗的是否展示\nconst dialogTitle = ref('') // 弹窗的标题\nconst formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\nconst formType = ref('') // 表单的类型：create - 新增；update - 修改\nconst formData = ref({\n  id: undefined,\n  name: undefined,\n  description: undefined,\n  birthday: undefined,\n  sex: undefined,\n  enabled: undefined,\n  avatar: undefined,\n  video: undefined,\n  memo: undefined,\n})\nconst formRules = reactive({\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 子表的表单 */\nconst subTabsName = ref('studentContact')\nconst studentContactFormRef = ref()\nconst studentTeacherFormRef = ref()\n\n/** 打开弹窗 */\nconst open = async (type: string, id?: number) => {\n  dialogVisible.value = true\n  dialogTitle.value = t('action.' + type)\n  formType.value = type\n  resetForm()\n  // 修改时，设置数据\n  if (id) {\n    formLoading.value = true\n    try {\n      formData.value = await StudentApi.getStudent(id)\n    } finally {\n      formLoading.value = false\n    }\n  }\n}\ndefineExpose({ open }) // 提供 open 方法，用于打开弹窗\n\n/** 提交表单 */\nconst emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调\nconst submitForm = async () => {\n  // 校验表单\n  await formRef.value.validate()\n  // 校验子表单\n  try {\n    await studentContactFormRef.value.validate()\n  } catch (e) {\n    subTabsName.value = 'studentContact'\n    return\n  }\n  try {\n    await studentTeacherFormRef.value.validate()\n  } catch (e) {\n    subTabsName.value = 'studentTeacher'\n    return\n  }\n  // 提交请求\n  formLoading.value = true\n  try {\n    const data = formData.value as unknown as StudentApi.StudentVO\n    // 拼接子表的数据\n    data.studentContacts = studentContactFormRef.value.getData()\n    data.studentTeacher = studentTeacherFormRef.value.getData()\n    if (formType.value === 'create') {\n      await StudentApi.createStudent(data)\n      message.success(t('common.createSuccess'))\n    } else {\n      await StudentApi.updateStudent(data)\n      message.success(t('common.updateSuccess'))\n    }\n    dialogVisible.value = false\n    // 发送操作成功的事件\n    emit('success')\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n    id: undefined,\n    name: undefined,\n    description: undefined,\n    birthday: undefined,\n    sex: undefined,\n    enabled: undefined,\n    avatar: undefined,\n    video: undefined,\n    memo: undefined,\n  }\n  formRef.value?.resetFields()\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm",
    "content": "<template>\n  <el-form\n    ref=\"formRef\"\n    :model=\"formData\"\n    :rules=\"formRules\"\n    label-width=\"100px\"\n    v-loading=\"formLoading\"\n  >\n     <el-form-item label=\"名字\" prop=\"name\">\n      <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n    </el-form-item>\n    <el-form-item label=\"简介\" prop=\"description\">\n      <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n    </el-form-item>\n    <el-form-item label=\"出生日期\" prop=\"birthday\">\n      <el-date-picker\n        v-model=\"formData.birthday\"\n        type=\"date\"\n        value-format=\"x\"\n        placeholder=\"选择出生日期\"\n      />\n    </el-form-item>\n    <el-form-item label=\"性别\" prop=\"sex\">\n      <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n        <el-option\n          v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n          :key=\"dict.value\"\n          :label=\"dict.label\"\n          :value=\"dict.value\"\n        />\n      </el-select>\n    </el-form-item>\n    <el-form-item label=\"是否有效\" prop=\"enabled\">\n      <el-radio-group v-model=\"formData.enabled\">\n        <el-radio\n          v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n          :key=\"dict.value\"\n          :label=\"dict.value\"\n          >\n          {{ dict.label }}\n        </el-radio>\n      </el-radio-group>\n    </el-form-item>\n    <el-form-item label=\"头像\" prop=\"avatar\">\n      <UploadImg v-model=\"formData.avatar\" />\n    </el-form-item>\n    <el-form-item label=\"附件\" prop=\"video\">\n      <UploadFile v-model=\"formData.video\" />\n    </el-form-item>\n    <el-form-item label=\"备注\" prop=\"memo\">\n      <Editor v-model=\"formData.memo\" height=\"150px\" />\n    </el-form-item>\n  </el-form>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst props = defineProps<{\n  studentId: undefined // 学生编号（主表的关联字段）\n}>()\nconst formLoading = ref(false) // 表单的加载中\nconst formData = ref([])\nconst formRules = reactive({\n  studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 监听主表的关联字段的变化，加载对应的子表数据 */\nwatch(\n  () => props.studentId,\n  async (val) => {\n    // 1. 重置表单\n    formData.value = {\n      id: undefined,\n      studentId: undefined,\n      name: undefined,\n      description: undefined,\n      birthday: undefined,\n      sex: undefined,\n      enabled: undefined,\n      avatar: undefined,\n      video: undefined,\n      memo: undefined,\n    }\n    // 2. val 非空，则加载数据\n    if (!val) {\n      return;\n    }\n    try {\n      formLoading.value = true\n      const data = await StudentApi.getStudentTeacherByStudentId(val)\n      if (!data) {\n        return\n      }\n      formData.value = data\n    } finally {\n      formLoading.value = false\n    }\n  },\n  { immediate: true }\n)\n\n/** 表单校验 */\nconst validate = () => {\n  return formRef.value.validate()\n}\n\n/** 表单值 */\nconst getData = () => {\n  return formData.value\n}\n\ndefineExpose({ validate, getData })\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/vue/index",
    "content": "<template>\n  <ContentWrap>\n    <!-- 搜索工作栏 -->\n    <el-form\n      class=\"-mb-15px\"\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入名字\"\n          clearable\n          @keyup.enter=\"handleQuery\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"queryParams.birthday\"\n          value-format=\"YYYY-MM-DD\"\n          type=\"date\"\n          placeholder=\"选择出生日期\"\n          clearable\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select\n          v-model=\"queryParams.sex\"\n          placeholder=\"请选择性别\"\n          clearable\n          class=\"!w-240px\"\n        >\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-select\n          v-model=\"queryParams.enabled\"\n          placeholder=\"请选择是否有效\"\n          clearable\n          class=\"!w-240px\"\n        >\n          <el-option\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker\n          v-model=\"queryParams.createTime\"\n          value-format=\"YYYY-MM-DD HH:mm:ss\"\n          type=\"daterange\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"[new Date('1 00:00:00'), new Date('1 23:59:59')]\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button @click=\"handleQuery\"><Icon icon=\"ep:search\" class=\"mr-5px\" /> 搜索</el-button>\n        <el-button @click=\"resetQuery\"><Icon icon=\"ep:refresh\" class=\"mr-5px\" /> 重置</el-button>\n        <el-button\n          type=\"primary\"\n          plain\n          @click=\"openForm('create')\"\n          v-hasPermi=\"['infra:student:create']\"\n        >\n          <Icon icon=\"ep:plus\" class=\"mr-5px\" /> 新增\n        </el-button>\n        <el-button\n          type=\"success\"\n          plain\n          @click=\"handleExport\"\n          :loading=\"exportLoading\"\n          v-hasPermi=\"['infra:student:export']\"\n        >\n          <Icon icon=\"ep:download\" class=\"mr-5px\" /> 导出\n        </el-button>\n      </el-form-item>\n    </el-form>\n  </ContentWrap>\n\n  <!-- 列表 -->\n  <ContentWrap>\n    <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n      <el-table-column\n        label=\"出生日期\"\n        align=\"center\"\n        prop=\"birthday\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"createTime\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"操作\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button\n            link\n            type=\"primary\"\n            @click=\"openForm('update', scope.row.id)\"\n            v-hasPermi=\"['infra:student:update']\"\n          >\n            编辑\n          </el-button>\n          <el-button\n            link\n            type=\"danger\"\n            @click=\"handleDelete(scope.row.id)\"\n            v-hasPermi=\"['infra:student:delete']\"\n          >\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页 -->\n    <Pagination\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNo\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n  </ContentWrap>\n\n  <!-- 表单弹窗：添加/修改 -->\n  <StudentForm ref=\"formRef\" @success=\"getList\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport { dateFormatter } from '@/utils/formatTime'\nimport download from '@/utils/download'\nimport * as StudentApi from '@/api/infra/demo'\nimport StudentForm from './StudentForm.vue'\n\ndefineOptions({ name: 'InfraStudent' })\n\nconst message = useMessage() // 消息弹窗\nconst { t } = useI18n() // 国际化\n\nconst loading = ref(true) // 列表的加载中\nconst list = ref([]) // 列表的数据\nconst total = ref(0) // 列表的总页数\nconst queryParams = reactive({\n  pageNo: 1,\n  pageSize: 10,\n  name: undefined,\n  birthday: undefined,\n  birthday: [],\n  sex: undefined,\n  enabled: undefined,\n  createTime: [],\n})\nconst queryFormRef = ref() // 搜索的表单\nconst exportLoading = ref(false) // 导出的加载中\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    const data = await StudentApi.getStudentPage(queryParams)\n    list.value = data.list\n    total.value = data.total\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 重置按钮操作 */\nconst resetQuery = () => {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n\n/** 添加/修改操作 */\nconst formRef = ref()\nconst openForm = (type: string, id?: number) => {\n  formRef.value.open(type, id)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = async (id: number) => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    // 发起删除\n    await StudentApi.deleteStudent(id)\n    message.success(t('common.delSuccess'))\n    // 刷新列表\n    await getList()\n  } catch {}\n}\n\n/** 导出按钮操作 */\nconst handleExport = async () => {\n  try {\n    // 导出的二次确认\n    await message.exportConfirm()\n    // 发起导出\n    exportLoading.value = true\n    const data = await StudentApi.exportStudent(queryParams)\n    download.excel(data, '学生.xls')\n  } catch {\n  } finally {\n    exportLoading.value = false\n  }\n}\n\n/** 初始化 **/\nonMounted(() => {\n  getList()\n})\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraStudentPageReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentSaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraStudentMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java\"\n}, {\n  \"contentPath\" : \"java/InfraStudentServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\" : \"vue/StudentForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue\"\n}, {\n  \"contentPath\" : \"ts/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/api/infra/demo/index.ts\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 学生 TODO 补充编号 ==========\r\nErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"学生不存在\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;\n\n@Tag(name = \"管理后台 - 学生\")\n@RestController\n@RequestMapping(\"/infra/student\")\n@Validated\npublic class InfraStudentController {\n\n    @Resource\n    private InfraStudentService studentService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:create')\")\n    public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {\n        return success(studentService.createStudent(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新学生\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:update')\")\n    public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {\n        studentService.updateStudent(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:student:delete')\")\n    public CommonResult<Boolean> deleteStudent(@RequestParam(\"id\") Long id) {\n        studentService.deleteStudent(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得学生\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<InfraStudentRespVO> getStudent(@RequestParam(\"id\") Long id) {\n        InfraStudentDO student = studentService.getStudent(id);\n        return success(BeanUtils.toBean(student, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得学生分页\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:query')\")\n    public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {\n        PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出学生 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:student:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,\n              HttpServletResponse response) throws IOException {\n        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"学生.xls\", \"数据\", InfraStudentRespVO.class,\n                        BeanUtils.toBean(list, InfraStudentRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport java.time.LocalDateTime;\nimport java.time.LocalDateTime;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 学生 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_student\")\n@KeySequence(\"infra_student_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraStudentDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 简介\n     */\n    private String description;\n    /**\n     * 出生日期\n     */\n    private LocalDateTime birthday;\n    /**\n     * 性别\n     *\n     * 枚举 {@link TODO system_user_sex 对应的类}\n     */\n    private Integer sex;\n    /**\n     * 是否有效\n     *\n     * 枚举 {@link TODO infra_boolean_string 对应的类}\n     */\n    private Boolean enabled;\n    /**\n     * 头像\n     */\n    private String avatar;\n    /**\n     * 附件\n     */\n    private String video;\n    /**\n     * 备注\n     */\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 学生 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {\n\n    default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()\n                .likeIfPresent(InfraStudentDO::getName, reqVO.getName())\n                .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())\n                .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())\n                .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())\n                .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(InfraStudentDO::getId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 学生分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class InfraStudentPageReqVO extends PageParam {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n    @Schema(description = \"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", example = \"1\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", example = \"true\")\n    private Boolean enabled;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\nimport cn.idev.excel.annotation.*;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\n\n@Schema(description = \"管理后台 - 学生 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraStudentRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @ExcelProperty(\"简介\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"出生日期\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"性别\", converter = DictConvert.class)\n    @DictFormat(\"system_user_sex\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(value = \"是否有效\", converter = DictConvert.class)\n    @DictFormat(\"infra_boolean_string\") // TODO 代码优化：建议设置到对应的 DictTypeConstants 枚举类中\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @ExcelProperty(\"头像\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @ExcelProperty(\"附件\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @ExcelProperty(\"备注\")\n    private String memo;\n\n    @Schema(description = \"创建时间\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 学生新增/修改 Request VO\")\n@Data\npublic class InfraStudentSaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是介绍\")\n    @NotEmpty(message = \"简介不能为空\")\n    private String description;\n\n    @Schema(description = \"出生日期\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"出生日期不能为空\")\n    private LocalDateTime birthday;\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"性别不能为空\")\n    private Integer sex;\n\n    @Schema(description = \"是否有效\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否有效不能为空\")\n    private Boolean enabled;\n\n    @Schema(description = \"头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotEmpty(message = \"头像不能为空\")\n    private String avatar;\n\n    @Schema(description = \"附件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.mp4\")\n    @NotEmpty(message = \"附件不能为空\")\n    private String video;\n\n    @Schema(description = \"备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是备注\")\n    @NotEmpty(message = \"备注不能为空\")\n    private String memo;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 学生 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraStudentService {\n\n    /**\n     * 创建学生\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);\n\n    /**\n     * 更新学生\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);\n\n    /**\n     * 删除学生\n     *\n     * @param id 编号\n     */\n    void deleteStudent(Long id);\n\n    /**\n     * 获得学生\n     *\n     * @param id 编号\n     * @return 学生\n     */\n    InfraStudentDO getStudent(Long id);\n\n    /**\n     * 获得学生分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 学生分页\n     */\n    PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 学生 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraStudentServiceImpl implements InfraStudentService {\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Override\n    public Long createStudent(InfraStudentSaveReqVO createReqVO) {\n        // 插入\n        InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);\n        studentMapper.insert(student);\n        // 返回\n        return student.getId();\n    }\n\n    @Override\n    public void updateStudent(InfraStudentSaveReqVO updateReqVO) {\n        // 校验存在\n        validateStudentExists(updateReqVO.getId());\n        // 更新\n        InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);\n        studentMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteStudent(Long id) {\n        // 校验存在\n        validateStudentExists(id);\n        // 删除\n        studentMapper.deleteById(id);\n    }\n\n    private void validateStudentExists(Long id) {\n        if (studentMapper.selectById(id) == null) {\n            throw exception(STUDENT_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public InfraStudentDO getStudent(Long id) {\n        return studentMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {\n        return studentMapper.selectPage(pageReqVO);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraStudentServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraStudentServiceImpl.class)\npublic class InfraStudentServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraStudentServiceImpl studentService;\n\n    @Resource\n    private InfraStudentMapper studentMapper;\n\n    @Test\n    public void testCreateStudent_success() {\n        // 准备参数\n        InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);\n\n        // 调用\n        Long studentId = studentService.createStudent(createReqVO);\n        // 断言\n        assertNotNull(studentId);\n        // 校验记录的属性是否正确\n        InfraStudentDO student = studentMapper.selectById(studentId);\n        assertPojoEquals(createReqVO, student, \"id\");\n    }\n\n    @Test\n    public void testUpdateStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {\n            o.setId(dbStudent.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        studentService.updateStudent(updateReqVO);\n        // 校验是否更新正确\n        InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, student);\n    }\n\n    @Test\n    public void testUpdateStudent_notExists() {\n        // 准备参数\n        InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteStudent_success() {\n        // mock 数据\n        InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);\n        studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbStudent.getId();\n\n        // 调用\n        studentService.deleteStudent(id);\n       // 校验数据不存在了\n       assertNull(studentMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteStudent_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetStudentPage() {\n       // mock 数据\n       InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到\n           o.setName(null);\n           o.setBirthday(null);\n           o.setSex(null);\n           o.setEnabled(null);\n           o.setCreateTime(null);\n       });\n       studentMapper.insert(dbStudent);\n       // 测试 name 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));\n       // 测试 birthday 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));\n       // 测试 sex 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));\n       // 测试 enabled 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));\n       // 测试 createTime 不匹配\n       studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));\n       // 准备参数\n       InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();\n       reqVO.setName(null);\n       reqVO.setBirthday(null);\n       reqVO.setSex(null);\n       reqVO.setEnabled(null);\n       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));\n\n       // 调用\n       PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);\n       // 断言\n       assertEquals(1, pageResult.getTotal());\n       assertEquals(1, pageResult.getList().size());\n       assertPojoEquals(dbStudent, pageResult.getList().get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_student\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" varchar NOT NULL,\n    \"birthday\" varchar NOT NULL,\n    \"sex\" int NOT NULL,\n    \"enabled\" bit NOT NULL,\n    \"avatar\" varchar NOT NULL,\n    \"video\" varchar NOT NULL,\n    \"memo\" varchar NOT NULL,\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (\"id\")\n) COMMENT '学生表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_student\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '学生管理', '', 2, 0, 888,\r\n    'student', '', 'infra/demo/index', 0, 'InfraStudent'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生查询', 'infra:student:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生创建', 'infra:student:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生更新', 'infra:student:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生删除', 'infra:student:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '学生导出', 'infra:student:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/ts/index",
    "content": "import request from '@/config/axios'\r\n\r\nexport interface StudentVO {\r\n  id: number\r\n  name: string\r\n  description: string\r\n  birthday: Date\r\n  sex: number\r\n  enabled: boolean\r\n  avatar: string\r\n  video: string\r\n  memo: string\r\n}\r\n\r\n// 查询学生分页\r\nexport const getStudentPage = async (params) => {\r\n  return await request.get({ url: `/infra/student/page`, params })\r\n}\r\n\r\n// 查询学生详情\r\nexport const getStudent = async (id: number) => {\r\n  return await request.get({ url: `/infra/student/get?id=` + id })\r\n}\r\n\r\n// 新增学生\r\nexport const createStudent = async (data: StudentVO) => {\r\n  return await request.post({ url: `/infra/student/create`, data })\r\n}\r\n\r\n// 修改学生\r\nexport const updateStudent = async (data: StudentVO) => {\r\n  return await request.put({ url: `/infra/student/update`, data })\r\n}\r\n\r\n// 删除学生\r\nexport const deleteStudent = async (id: number) => {\r\n  return await request.delete({ url: `/infra/student/delete?id=` + id })\r\n}\r\n\r\n// 导出学生 Excel\r\nexport const exportStudent = async (params) => {\r\n  return await request.download({ url: `/infra/student/export-excel`, params })\r\n}\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm",
    "content": "<template>\n  <Dialog :title=\"dialogTitle\" v-model=\"dialogVisible\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"formRules\"\n      label-width=\"100px\"\n      v-loading=\"formLoading\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n      </el-form-item>\n      <el-form-item label=\"简介\" prop=\"description\">\n        <el-input v-model=\"formData.description\" type=\"textarea\" placeholder=\"请输入简介\" />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"formData.birthday\"\n          type=\"date\"\n          value-format=\"x\"\n          placeholder=\"选择出生日期\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select v-model=\"formData.sex\" placeholder=\"请选择性别\">\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-radio-group v-model=\"formData.enabled\">\n          <el-radio\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.value\"\n          >\n            {{ dict.label }}\n          </el-radio>\n        </el-radio-group>\n      </el-form-item>\n      <el-form-item label=\"头像\" prop=\"avatar\">\n        <UploadImg v-model=\"formData.avatar\" />\n      </el-form-item>\n      <el-form-item label=\"附件\" prop=\"video\">\n        <UploadFile v-model=\"formData.video\" />\n      </el-form-item>\n      <el-form-item label=\"备注\" prop=\"memo\">\n        <Editor v-model=\"formData.memo\" height=\"150px\" />\n      </el-form-item>\n    </el-form>\n    <template #footer>\n      <el-button @click=\"submitForm\" type=\"primary\" :disabled=\"formLoading\">确 定</el-button>\n      <el-button @click=\"dialogVisible = false\">取 消</el-button>\n    </template>\n  </Dialog>\n</template>\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport * as StudentApi from '@/api/infra/demo'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst dialogVisible = ref(false) // 弹窗的是否展示\nconst dialogTitle = ref('') // 弹窗的标题\nconst formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\nconst formType = ref('') // 表单的类型：create - 新增；update - 修改\nconst formData = ref({\n  id: undefined,\n  name: undefined,\n  description: undefined,\n  birthday: undefined,\n  sex: undefined,\n  enabled: undefined,\n  avatar: undefined,\n  video: undefined,\n  memo: undefined,\n})\nconst formRules = reactive({\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],\n  birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],\n  sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],\n  enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],\n  avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],\n  video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],\n  memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\n\n/** 打开弹窗 */\nconst open = async (type: string, id?: number) => {\n  dialogVisible.value = true\n  dialogTitle.value = t('action.' + type)\n  formType.value = type\n  resetForm()\n  // 修改时，设置数据\n  if (id) {\n    formLoading.value = true\n    try {\n      formData.value = await StudentApi.getStudent(id)\n    } finally {\n      formLoading.value = false\n    }\n  }\n}\ndefineExpose({ open }) // 提供 open 方法，用于打开弹窗\n\n/** 提交表单 */\nconst emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调\nconst submitForm = async () => {\n  // 校验表单\n  await formRef.value.validate()\n  // 提交请求\n  formLoading.value = true\n  try {\n    const data = formData.value as unknown as StudentApi.StudentVO\n    if (formType.value === 'create') {\n      await StudentApi.createStudent(data)\n      message.success(t('common.createSuccess'))\n    } else {\n      await StudentApi.updateStudent(data)\n      message.success(t('common.updateSuccess'))\n    }\n    dialogVisible.value = false\n    // 发送操作成功的事件\n    emit('success')\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n    id: undefined,\n    name: undefined,\n    description: undefined,\n    birthday: undefined,\n    sex: undefined,\n    enabled: undefined,\n    avatar: undefined,\n    video: undefined,\n    memo: undefined,\n  }\n  formRef.value?.resetFields()\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/vue/index",
    "content": "<template>\n  <ContentWrap>\n    <!-- 搜索工作栏 -->\n    <el-form\n      class=\"-mb-15px\"\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入名字\"\n          clearable\n          @keyup.enter=\"handleQuery\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item label=\"出生日期\" prop=\"birthday\">\n        <el-date-picker\n          v-model=\"queryParams.birthday\"\n          value-format=\"YYYY-MM-DD\"\n          type=\"date\"\n          placeholder=\"选择出生日期\"\n          clearable\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item label=\"性别\" prop=\"sex\">\n        <el-select\n          v-model=\"queryParams.sex\"\n          placeholder=\"请选择性别\"\n          clearable\n          class=\"!w-240px\"\n        >\n          <el-option\n            v-for=\"dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"是否有效\" prop=\"enabled\">\n        <el-select\n          v-model=\"queryParams.enabled\"\n          placeholder=\"请选择是否有效\"\n          clearable\n          class=\"!w-240px\"\n        >\n          <el-option\n            v-for=\"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)\"\n            :key=\"dict.value\"\n            :label=\"dict.label\"\n            :value=\"dict.value\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\" prop=\"createTime\">\n        <el-date-picker\n          v-model=\"queryParams.createTime\"\n          value-format=\"YYYY-MM-DD HH:mm:ss\"\n          type=\"daterange\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n          :default-time=\"[new Date('1 00:00:00'), new Date('1 23:59:59')]\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button @click=\"handleQuery\"><Icon icon=\"ep:search\" class=\"mr-5px\" /> 搜索</el-button>\n        <el-button @click=\"resetQuery\"><Icon icon=\"ep:refresh\" class=\"mr-5px\" /> 重置</el-button>\n        <el-button\n          type=\"primary\"\n          plain\n          @click=\"openForm('create')\"\n          v-hasPermi=\"['infra:student:create']\"\n        >\n          <Icon icon=\"ep:plus\" class=\"mr-5px\" /> 新增\n        </el-button>\n        <el-button\n          type=\"success\"\n          plain\n          @click=\"handleExport\"\n          :loading=\"exportLoading\"\n          v-hasPermi=\"['infra:student:export']\"\n        >\n          <Icon icon=\"ep:download\" class=\"mr-5px\" /> 导出\n        </el-button>\n      </el-form-item>\n    </el-form>\n  </ContentWrap>\n\n  <!-- 列表 -->\n  <ContentWrap>\n    <el-table v-loading=\"loading\" :data=\"list\" :stripe=\"true\" :show-overflow-tooltip=\"true\">\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"简介\" align=\"center\" prop=\"description\" />\n      <el-table-column\n        label=\"出生日期\"\n        align=\"center\"\n        prop=\"birthday\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"性别\" align=\"center\" prop=\"sex\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.SYSTEM_USER_SEX\" :value=\"scope.row.sex\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"是否有效\" align=\"center\" prop=\"enabled\">\n        <template #default=\"scope\">\n          <dict-tag :type=\"DICT_TYPE.INFRA_BOOLEAN_STRING\" :value=\"scope.row.enabled\" />\n        </template>\n      </el-table-column>\n      <el-table-column label=\"头像\" align=\"center\" prop=\"avatar\" />\n      <el-table-column label=\"附件\" align=\"center\" prop=\"video\" />\n      <el-table-column label=\"备注\" align=\"center\" prop=\"memo\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"createTime\"\n        :formatter=\"dateFormatter\"\n        width=\"180px\"\n      />\n      <el-table-column label=\"操作\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button\n            link\n            type=\"primary\"\n            @click=\"openForm('update', scope.row.id)\"\n            v-hasPermi=\"['infra:student:update']\"\n          >\n            编辑\n          </el-button>\n          <el-button\n            link\n            type=\"danger\"\n            @click=\"handleDelete(scope.row.id)\"\n            v-hasPermi=\"['infra:student:delete']\"\n          >\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页 -->\n    <Pagination\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNo\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n  </ContentWrap>\n\n  <!-- 表单弹窗：添加/修改 -->\n  <StudentForm ref=\"formRef\" @success=\"getList\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'\nimport { dateFormatter } from '@/utils/formatTime'\nimport download from '@/utils/download'\nimport * as StudentApi from '@/api/infra/demo'\nimport StudentForm from './StudentForm.vue'\n\ndefineOptions({ name: 'InfraStudent' })\n\nconst message = useMessage() // 消息弹窗\nconst { t } = useI18n() // 国际化\n\nconst loading = ref(true) // 列表的加载中\nconst list = ref([]) // 列表的数据\nconst total = ref(0) // 列表的总页数\nconst queryParams = reactive({\n  pageNo: 1,\n  pageSize: 10,\n  name: undefined,\n  birthday: undefined,\n  birthday: [],\n  sex: undefined,\n  enabled: undefined,\n  createTime: [],\n})\nconst queryFormRef = ref() // 搜索的表单\nconst exportLoading = ref(false) // 导出的加载中\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    const data = await StudentApi.getStudentPage(queryParams)\n    list.value = data.list\n    total.value = data.total\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 重置按钮操作 */\nconst resetQuery = () => {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n\n/** 添加/修改操作 */\nconst formRef = ref()\nconst openForm = (type: string, id?: number) => {\n  formRef.value.open(type, id)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = async (id: number) => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    // 发起删除\n    await StudentApi.deleteStudent(id)\n    message.success(t('common.delSuccess'))\n    // 刷新列表\n    await getList()\n  } catch {}\n}\n\n/** 导出按钮操作 */\nconst handleExport = async () => {\n  try {\n    // 导出的二次确认\n    await message.exportConfirm()\n    // 发起导出\n    exportLoading.value = true\n    const data = await StudentApi.exportStudent(queryParams)\n    download.excel(data, '学生.xls')\n  } catch {\n  } finally {\n    exportLoading.value = false\n  }\n}\n\n/** 初始化 **/\nonMounted(() => {\n  getList()\n})\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/assert.json",
    "content": "[ {\n  \"contentPath\" : \"java/InfraCategoryListReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryRespVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategorySaveReqVO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryController\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryDO\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java\"\n}, {\n  \"contentPath\" : \"xml/InfraCategoryMapper\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryServiceImpl\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryService\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java\"\n}, {\n  \"contentPath\" : \"java/InfraCategoryServiceImplTest\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java\"\n}, {\n  \"contentPath\" : \"java/ErrorCodeConstants_手动操作\",\n  \"filePath\" : \"yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java\"\n}, {\n  \"contentPath\" : \"sql/sql\",\n  \"filePath\" : \"sql/sql.sql\"\n}, {\n  \"contentPath\" : \"sql/h2\",\n  \"filePath\" : \"sql/h2.sql\"\n}, {\n  \"contentPath\" : \"vue/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/index.vue\"\n}, {\n  \"contentPath\" : \"vue/CategoryForm\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/views/infra/demo/CategoryForm.vue\"\n}, {\n  \"contentPath\" : \"ts/index\",\n  \"filePath\" : \"yudao-ui-admin-vue3/src/api/infra/demo/index.ts\"\n} ]"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作",
    "content": "// TODO 待办：请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意，请给“TODO 补充编号”设置一个错误码编号！！！\r\n// ========== 分类 TODO 补充编号 ==========\r\nErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, \"分类不存在\");\r\nErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, \"存在存在子分类，无法删除\");\r\nErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,\"父级分类不存在\");\r\nErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, \"不能设置自己为父分类\");\r\nErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, \"已经存在该名字的分类\");\r\nErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, \"不能设置自己的子InfraCategory为父InfraCategory\");\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo;\n\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Operation;\n\nimport javax.validation.constraints.*;\nimport javax.validation.*;\nimport javax.servlet.http.*;\nimport java.util.*;\nimport java.io.IOException;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\n\nimport cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;\nimport static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService;\n\n@Tag(name = \"管理后台 - 分类\")\n@RestController\n@RequestMapping(\"/infra/category\")\n@Validated\npublic class InfraCategoryController {\n\n    @Resource\n    private InfraCategoryService categoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建分类\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:create')\")\n    public CommonResult<Long> createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) {\n        return success(categoryService.createCategory(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新分类\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:update')\")\n    public CommonResult<Boolean> updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) {\n        categoryService.updateCategory(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('infra:category:delete')\")\n    public CommonResult<Boolean> deleteCategory(@RequestParam(\"id\") Long id) {\n        categoryService.deleteCategory(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:query')\")\n    public CommonResult<InfraCategoryRespVO> getCategory(@RequestParam(\"id\") Long id) {\n        InfraCategoryDO category = categoryService.getCategory(id);\n        return success(BeanUtils.toBean(category, InfraCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得分类列表\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:query')\")\n    public CommonResult<List<InfraCategoryRespVO>> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) {\n        List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO);\n        return success(BeanUtils.toBean(list, InfraCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出分类 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('infra:category:export')\")\n    @OperateLog(type = EXPORT)\n    public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO,\n              HttpServletResponse response) throws IOException {\n        List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO);\n        // 导出 Excel\n        ExcelUtils.write(response, \"分类.xls\", \"数据\", InfraCategoryRespVO.class,\n                        BeanUtils.toBean(list, InfraCategoryRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO",
    "content": "package cn.iocoder.yudao.module.infra.dal.dataobject.demo;\n\nimport lombok.*;\nimport java.util.*;\nimport com.baomidou.mybatisplus.annotation.*;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\n\n/**\n * 分类 DO\n *\n * @author 芋道源码\n */\n@TableName(\"infra_category\")\n@KeySequence(\"infra_category_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfraCategoryDO extends BaseDO {\n\n    public static final Long PARENT_ID_ROOT = 0L;\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名字\n     */\n    private String name;\n    /**\n     * 父编号\n     */\n    private Long parentId;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n@Schema(description = \"管理后台 - 分类列表 Request VO\")\n@Data\npublic class InfraCategoryListReqVO {\n\n    @Schema(description = \"名字\", example = \"芋头\")\n    private String name;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper",
    "content": "package cn.iocoder.yudao.module.infra.dal.mysql.demo;\n\nimport java.util.*;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\n\n/**\n * 分类 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface InfraCategoryMapper extends BaseMapperX<InfraCategoryDO> {\n\n    default List<InfraCategoryDO> selectList(InfraCategoryListReqVO reqVO) {\n        return selectList(new LambdaQueryWrapperX<InfraCategoryDO>()\n                .likeIfPresent(InfraCategoryDO::getName, reqVO.getName())\n                .orderByDesc(InfraCategoryDO::getId));\n    }\n\n\tdefault InfraCategoryDO selectByParentIdAndName(Long parentId, String name) {\n\t    return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name);\n\t}\n\n    default Long selectCountByParentId(Long parentId) {\n        return selectCount(InfraCategoryDO::getParentId, parentId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport java.util.*;\nimport cn.idev.excel.annotation.*;\n\n@Schema(description = \"管理后台 - 分类 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class InfraCategoryRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @ExcelProperty(\"名字\")\n    private String name;\n\n    @Schema(description = \"父编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    @ExcelProperty(\"父编号\")\n    private Long parentId;\n\n}\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO",
    "content": "package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.*;\nimport java.util.*;\nimport javax.validation.constraints.*;\nimport java.util.*;\n\n@Schema(description = \"管理后台 - 分类新增/修改 Request VO\")\n@Data\npublic class InfraCategorySaveReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋头\")\n    @NotEmpty(message = \"名字不能为空\")\n    private String name;\n\n    @Schema(description = \"父编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    @NotNull(message = \"父编号不能为空\")\n    private Long parentId;\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport java.util.*;\nimport javax.validation.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\n\n/**\n * 分类 Service 接口\n *\n * @author 芋道源码\n */\npublic interface InfraCategoryService {\n\n    /**\n     * 创建分类\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createCategory(@Valid InfraCategorySaveReqVO createReqVO);\n\n    /**\n     * 更新分类\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO);\n\n    /**\n     * 删除分类\n     *\n     * @param id 编号\n     */\n    void deleteCategory(Long id);\n\n    /**\n     * 获得分类\n     *\n     * @param id 编号\n     * @return 分类\n     */\n    InfraCategoryDO getCategory(Long id);\n\n    /**\n     * 获得分类列表\n     *\n     * @param listReqVO 查询条件\n     * @return 分类列表\n     */\n    List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO);\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.springframework.stereotype.Service;\nimport javax.annotation.Resource;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.*;\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\n\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\n\n/**\n * 分类 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class InfraCategoryServiceImpl implements InfraCategoryService {\n\n    @Resource\n    private InfraCategoryMapper categoryMapper;\n\n    @Override\n    public Long createCategory(InfraCategorySaveReqVO createReqVO) {\n        // 校验父编号的有效性\n        validateParentCategory(null, createReqVO.getParentId());\n        // 校验名字的唯一性\n        validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());\n\n        // 插入\n        InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class);\n        categoryMapper.insert(category);\n        // 返回\n        return category.getId();\n    }\n\n    @Override\n    public void updateCategory(InfraCategorySaveReqVO updateReqVO) {\n        // 校验存在\n        validateCategoryExists(updateReqVO.getId());\n        // 校验父编号的有效性\n        validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId());\n        // 校验名字的唯一性\n        validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());\n\n        // 更新\n        InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class);\n        categoryMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteCategory(Long id) {\n        // 校验存在\n        validateCategoryExists(id);\n        // 校验是否有子分类\n        if (categoryMapper.selectCountByParentId(id) > 0) {\n            throw exception(CATEGORY_EXITS_CHILDREN);\n        }\n        // 删除\n        categoryMapper.deleteById(id);\n    }\n\n    private void validateCategoryExists(Long id) {\n        if (categoryMapper.selectById(id) == null) {\n            throw exception(CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    private void validateParentCategory(Long id, Long parentId) {\n        if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {\n            return;\n        }\n        // 1. 不能设置自己为父分类\n        if (Objects.equals(id, parentId)) {\n            throw exception(CATEGORY_PARENT_ERROR);\n        }\n        // 2. 父分类不存在\n        CategoryDO parentCategory = categoryMapper.selectById(parentId);\n        if (parentCategory == null) {\n            throw exception(CATEGORY_PARENT_NOT_EXITS);\n        }\n        // 3. 递归校验父分类，如果父分类是自己的子分类，则报错，避免形成环路\n        if (id == null) { // id 为空，说明新增，不需要考虑环路\n            return;\n        }\n        for (int i = 0; i < Short.MAX_VALUE; i++) {\n            // 3.1 校验环路\n            parentId = parentCategory.getParentId();\n            if (Objects.equals(id, parentId)) {\n                throw exception(CATEGORY_PARENT_IS_CHILD);\n            }\n            // 3.2 继续递归下一级父分类\n            if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {\n                break;\n            }\n            parentCategory = categoryMapper.selectById(parentId);\n            if (parentCategory == null) {\n                break;\n            }\n        }\n    }\n\n    private void validateCategoryNameUnique(Long id, Long parentId, String name) {\n        CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name);\n        if (category == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的分类\n        if (id == null) {\n            throw exception(CATEGORY_NAME_DUPLICATE);\n        }\n        if (!Objects.equals(category.getId(), id)) {\n            throw exception(CATEGORY_NAME_DUPLICATE);\n        }\n    }\n\n    @Override\n    public InfraCategoryDO getCategory(Long id) {\n        return categoryMapper.selectById(id);\n    }\n\n    @Override\n    public List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO) {\n        return categoryMapper.selectList(listReqVO);\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest",
    "content": "package cn.iocoder.yudao.module.infra.service.demo;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport javax.annotation.Resource;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;\n\nimport cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;\nimport cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;\nimport cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\n\nimport javax.annotation.Resource;\nimport org.springframework.context.annotation.Import;\nimport java.util.*;\nimport java.time.LocalDateTime;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link InfraCategoryServiceImpl} 的单元测试类\n *\n * @author 芋道源码\n */\n@Import(InfraCategoryServiceImpl.class)\npublic class InfraCategoryServiceImplTest extends BaseDbUnitTest {\n\n    @Resource\n    private InfraCategoryServiceImpl categoryService;\n\n    @Resource\n    private InfraCategoryMapper categoryMapper;\n\n    @Test\n    public void testCreateCategory_success() {\n        // 准备参数\n        InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null);\n\n        // 调用\n        Long categoryId = categoryService.createCategory(createReqVO);\n        // 断言\n        assertNotNull(categoryId);\n        // 校验记录的属性是否正确\n        InfraCategoryDO category = categoryMapper.selectById(categoryId);\n        assertPojoEquals(createReqVO, category, \"id\");\n    }\n\n    @Test\n    public void testUpdateCategory_success() {\n        // mock 数据\n        InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);\n        categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> {\n            o.setId(dbCategory.getId()); // 设置更新的 ID\n        });\n\n        // 调用\n        categoryService.updateCategory(updateReqVO);\n        // 校验是否更新正确\n        InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的\n        assertPojoEquals(updateReqVO, category);\n    }\n\n    @Test\n    public void testUpdateCategory_notExists() {\n        // 准备参数\n        InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class);\n\n        // 调用, 并断言异常\n        assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS);\n    }\n\n    @Test\n    public void testDeleteCategory_success() {\n        // mock 数据\n        InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);\n        categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据\n        // 准备参数\n        Long id = dbCategory.getId();\n\n        // 调用\n        categoryService.deleteCategory(id);\n       // 校验数据不存在了\n       assertNull(categoryMapper.selectById(id));\n    }\n\n    @Test\n    public void testDeleteCategory_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // 调用, 并断言异常\n        assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);\n    }\n\n    @Test\n    @Disabled  // TODO 请修改 null 为需要的值，然后删除 @Disabled 注解\n    public void testGetCategoryList() {\n       // mock 数据\n       InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到\n           o.setName(null);\n       });\n       categoryMapper.insert(dbCategory);\n       // 测试 name 不匹配\n       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));\n       // 准备参数\n       InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO();\n       reqVO.setName(null);\n\n       // 调用\n       List<InfraCategoryDO> list = categoryService.getCategoryList(reqVO);\n       // 断言\n       assertEquals(1, list.size());\n       assertPojoEquals(dbCategory, list.get(0));\n    }\n\n}"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/sql/h2",
    "content": "-- 将该建表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里\nCREATE TABLE IF NOT EXISTS \"infra_category\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar NOT NULL,\n    \"description\" bigint NOT NULL,\n    PRIMARY KEY (\"id\")\n) COMMENT '分类表';\n\n-- 将该删表 SQL 语句，添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里\nDELETE FROM \"infra_category\";"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/sql/sql",
    "content": "-- 菜单 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status, component_name\r\n)\r\nVALUES (\r\n    '分类管理', '', 2, 0, 888,\r\n    'category', '', 'infra/demo/index', 0, 'InfraCategory'\r\n);\r\n\r\n-- 按钮父菜单ID\r\n-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话，需要手动修改 @parentId 的部分的代码\r\nSELECT @parentId := LAST_INSERT_ID();\r\n\r\n-- 按钮 SQL\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类查询', 'infra:category:query', 3, 1, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类创建', 'infra:category:create', 3, 2, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类更新', 'infra:category:update', 3, 3, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类删除', 'infra:category:delete', 3, 4, @parentId,\r\n    '', '', '', 0\r\n);\r\nINSERT INTO system_menu(\r\n    name, permission, type, sort, parent_id,\r\n    path, icon, component, status\r\n)\r\nVALUES (\r\n    '分类导出', 'infra:category:export', 3, 5, @parentId,\r\n    '', '', '', 0\r\n);\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/ts/index",
    "content": "import request from '@/config/axios'\r\n\r\nexport interface CategoryVO {\r\n  id: number\r\n  name: string\r\n  parentId: number\r\n}\r\n\r\n// 查询分类列表\r\nexport const getCategoryList = async (params) => {\r\n  return await request.get({ url: `/infra/category/list`, params })\r\n}\r\n\r\n// 查询分类详情\r\nexport const getCategory = async (id: number) => {\r\n  return await request.get({ url: `/infra/category/get?id=` + id })\r\n}\r\n\r\n// 新增分类\r\nexport const createCategory = async (data: CategoryVO) => {\r\n  return await request.post({ url: `/infra/category/create`, data })\r\n}\r\n\r\n// 修改分类\r\nexport const updateCategory = async (data: CategoryVO) => {\r\n  return await request.put({ url: `/infra/category/update`, data })\r\n}\r\n\r\n// 删除分类\r\nexport const deleteCategory = async (id: number) => {\r\n  return await request.delete({ url: `/infra/category/delete?id=` + id })\r\n}\r\n\r\n// 导出分类 Excel\r\nexport const exportCategory = async (params) => {\r\n  return await request.download({ url: `/infra/category/export-excel`, params })\r\n}\r"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm",
    "content": "<template>\n  <Dialog :title=\"dialogTitle\" v-model=\"dialogVisible\">\n    <el-form\n      ref=\"formRef\"\n      :model=\"formData\"\n      :rules=\"formRules\"\n      label-width=\"100px\"\n      v-loading=\"formLoading\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input v-model=\"formData.name\" placeholder=\"请输入名字\" />\n      </el-form-item>\n      <el-form-item label=\"父编号\" prop=\"parentId\">\n        <el-tree-select\n          v-model=\"formData.parentId\"\n          :data=\"categoryTree\"\n          :props=\"defaultProps\"\n          check-strictly\n          default-expand-all\n          placeholder=\"请选择父编号\"\n        />\n      </el-form-item>\n    </el-form>\n    <template #footer>\n      <el-button @click=\"submitForm\" type=\"primary\" :disabled=\"formLoading\">确 定</el-button>\n      <el-button @click=\"dialogVisible = false\">取 消</el-button>\n    </template>\n  </Dialog>\n</template>\n<script setup lang=\"ts\">\nimport * as CategoryApi from '@/api/infra/demo'\nimport { defaultProps, handleTree } from '@/utils/tree'\n\nconst { t } = useI18n() // 国际化\nconst message = useMessage() // 消息弹窗\n\nconst dialogVisible = ref(false) // 弹窗的是否展示\nconst dialogTitle = ref('') // 弹窗的标题\nconst formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用\nconst formType = ref('') // 表单的类型：create - 新增；update - 修改\nconst formData = ref({\n  id: undefined,\n  name: undefined,\n  parentId: undefined,\n})\nconst formRules = reactive({\n  name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],\n  parentId: [{ required: true, message: '父编号不能为空', trigger: 'blur' }],\n})\nconst formRef = ref() // 表单 Ref\nconst categoryTree = ref() // 树形结构\n\n/** 打开弹窗 */\nconst open = async (type: string, id?: number) => {\n  dialogVisible.value = true\n  dialogTitle.value = t('action.' + type)\n  formType.value = type\n  resetForm()\n  // 修改时，设置数据\n  if (id) {\n    formLoading.value = true\n    try {\n      formData.value = await CategoryApi.getCategory(id)\n    } finally {\n      formLoading.value = false\n    }\n  }\n  await getCategoryTree()\n}\ndefineExpose({ open }) // 提供 open 方法，用于打开弹窗\n\n/** 提交表单 */\nconst emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调\nconst submitForm = async () => {\n  // 校验表单\n  await formRef.value.validate()\n  // 提交请求\n  formLoading.value = true\n  try {\n    const data = formData.value as unknown as CategoryApi.CategoryVO\n    if (formType.value === 'create') {\n      await CategoryApi.createCategory(data)\n      message.success(t('common.createSuccess'))\n    } else {\n      await CategoryApi.updateCategory(data)\n      message.success(t('common.updateSuccess'))\n    }\n    dialogVisible.value = false\n    // 发送操作成功的事件\n    emit('success')\n  } finally {\n    formLoading.value = false\n  }\n}\n\n/** 重置表单 */\nconst resetForm = () => {\n  formData.value = {\n    id: undefined,\n    name: undefined,\n    parentId: undefined,\n  }\n  formRef.value?.resetFields()\n}\n\n/** 获得分类树 */\nconst getCategoryTree = async () => {\n  categoryTree.value = []\n  const data = await CategoryApi.getCategoryList()\n  const root: Tree = { id: 0, name: '顶级分类', children: [] }\n  root.children = handleTree(data, 'id', 'parentId')\n  categoryTree.value.push(root)\n}\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/vue/index",
    "content": "<template>\n  <ContentWrap>\n    <!-- 搜索工作栏 -->\n    <el-form\n      class=\"-mb-15px\"\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"名字\" prop=\"name\">\n        <el-input\n          v-model=\"queryParams.name\"\n          placeholder=\"请输入名字\"\n          clearable\n          @keyup.enter=\"handleQuery\"\n          class=\"!w-240px\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button @click=\"handleQuery\"><Icon icon=\"ep:search\" class=\"mr-5px\" /> 搜索</el-button>\n        <el-button @click=\"resetQuery\"><Icon icon=\"ep:refresh\" class=\"mr-5px\" /> 重置</el-button>\n        <el-button\n          type=\"primary\"\n          plain\n          @click=\"openForm('create')\"\n          v-hasPermi=\"['infra:category:create']\"\n        >\n          <Icon icon=\"ep:plus\" class=\"mr-5px\" /> 新增\n        </el-button>\n        <el-button\n          type=\"success\"\n          plain\n          @click=\"handleExport\"\n          :loading=\"exportLoading\"\n          v-hasPermi=\"['infra:category:export']\"\n        >\n          <Icon icon=\"ep:download\" class=\"mr-5px\" /> 导出\n        </el-button>\n        <el-button type=\"danger\" plain @click=\"toggleExpandAll\">\n          <Icon icon=\"ep:sort\" class=\"mr-5px\" /> 展开/折叠\n        </el-button>\n      </el-form-item>\n    </el-form>\n  </ContentWrap>\n\n  <!-- 列表 -->\n  <ContentWrap>\n    <el-table\n      v-loading=\"loading\"\n      :data=\"list\"\n      :stripe=\"true\"\n      :show-overflow-tooltip=\"true\"\n      row-key=\"id\"\n      :default-expand-all=\"isExpandAll\"\n      v-if=\"refreshTable\"\n    >\n      <el-table-column label=\"编号\" align=\"center\" prop=\"id\" />\n      <el-table-column label=\"名字\" align=\"center\" prop=\"name\" />\n      <el-table-column label=\"父编号\" align=\"center\" prop=\"parentId\" />\n      <el-table-column label=\"操作\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button\n            link\n            type=\"primary\"\n            @click=\"openForm('update', scope.row.id)\"\n            v-hasPermi=\"['infra:category:update']\"\n          >\n            编辑\n          </el-button>\n          <el-button\n            link\n            type=\"danger\"\n            @click=\"handleDelete(scope.row.id)\"\n            v-hasPermi=\"['infra:category:delete']\"\n          >\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n    </el-table>\n    <!-- 分页 -->\n    <Pagination\n      :total=\"total\"\n      v-model:page=\"queryParams.pageNo\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getList\"\n    />\n  </ContentWrap>\n\n  <!-- 表单弹窗：添加/修改 -->\n  <CategoryForm ref=\"formRef\" @success=\"getList\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { handleTree } from '@/utils/tree'\nimport download from '@/utils/download'\nimport * as CategoryApi from '@/api/infra/demo'\nimport CategoryForm from './CategoryForm.vue'\n\ndefineOptions({ name: 'InfraCategory' })\n\nconst message = useMessage() // 消息弹窗\nconst { t } = useI18n() // 国际化\n\nconst loading = ref(true) // 列表的加载中\nconst list = ref([]) // 列表的数据\nconst queryParams = reactive({\n  name: undefined,\n})\nconst queryFormRef = ref() // 搜索的表单\nconst exportLoading = ref(false) // 导出的加载中\n\n/** 查询列表 */\nconst getList = async () => {\n  loading.value = true\n  try {\n    const data = await CategoryApi.getCategoryList(queryParams)\n    list.value = handleTree(data, 'id', 'parentId')\n  } finally {\n    loading.value = false\n  }\n}\n\n/** 搜索按钮操作 */\nconst handleQuery = () => {\n  queryParams.pageNo = 1\n  getList()\n}\n\n/** 重置按钮操作 */\nconst resetQuery = () => {\n  queryFormRef.value.resetFields()\n  handleQuery()\n}\n\n/** 添加/修改操作 */\nconst formRef = ref()\nconst openForm = (type: string, id?: number) => {\n  formRef.value.open(type, id)\n}\n\n/** 删除按钮操作 */\nconst handleDelete = async (id: number) => {\n  try {\n    // 删除的二次确认\n    await message.delConfirm()\n    // 发起删除\n    await CategoryApi.deleteCategory(id)\n    message.success(t('common.delSuccess'))\n    // 刷新列表\n    await getList()\n  } catch {}\n}\n\n/** 导出按钮操作 */\nconst handleExport = async () => {\n  try {\n    // 导出的二次确认\n    await message.exportConfirm()\n    // 发起导出\n    exportLoading.value = true\n    const data = await CategoryApi.exportCategory(queryParams)\n    download.excel(data, '分类.xls')\n  } catch {\n  } finally {\n    exportLoading.value = false\n  }\n}\n\n/** 展开/折叠操作 */\nconst isExpandAll = ref(true) // 是否展开，默认全部展开\nconst refreshTable = ref(true) // 重新渲染表格状态\nconst toggleExpandAll = async () => {\n  refreshTable.value = false\n  isExpandAll.value = !isExpandAll.value\n  await nextTick()\n  refreshTable.value = true\n}\n\n/** 初始化 **/\nonMounted(() => {\n  getList()\n})\n</script>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper\">\n\n    <!--\n        一般情况下，尽可能使用 Mapper 进行 CRUD 增删改查即可。\n        无法满足的场景，例如说多表关联查询，才使用 XML 编写 SQL。\n        代码生成器暂时只生成 Mapper XML 文件本身，更多推荐 MybatisX 快速开发插件来生成查询。\n        文档可见：https://www.iocoder.cn/MyBatis/x-plugins/\n     -->\n\n</mapper>"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/logback.xml",
    "content": "<configuration>\n    <!-- 引用 Spring Boot 的 logback 基础配置 -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n</configuration>\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/sql/clean.sql",
    "content": "DELETE FROM \"infra_config\";\nDELETE FROM \"infra_file_config\";\nDELETE FROM \"infra_file\";\nDELETE FROM \"infra_job\";\nDELETE FROM \"infra_job_log\";\nDELETE FROM \"infra_api_access_log\";\nDELETE FROM \"infra_api_error_log\";\nDELETE FROM \"infra_file_config\";\nDELETE FROM \"infra_data_source_config\";\nDELETE FROM \"infra_codegen_table\";\nDELETE FROM \"infra_codegen_column\";\n"
  },
  {
    "path": "yudao-module-infra/yudao-module-infra-server/src/test/resources/sql/create_tables.sql",
    "content": "\nCREATE TABLE IF NOT EXISTS \"infra_config\" (\n    \"id\" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',\n    \"category\" varchar(50) NOT NULL,\n    \"type\" tinyint NOT NULL,\n    \"name\" varchar(100) NOT NULL DEFAULT '' COMMENT '名字',\n    \"config_key\" varchar(100) NOT NULL DEFAULT '',\n    \"value\" varchar(500) NOT NULL DEFAULT '',\n    \"visible\" bit NOT NULL,\n    \"remark\" varchar(500) DEFAULT NULL,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '参数配置表';\n\nCREATE TABLE IF NOT EXISTS \"infra_file_config\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(63) NOT NULL,\n    \"storage\" tinyint NOT NULL,\n    \"remark\" varchar(255),\n    \"master\" bit(1) NOT NULL,\n    \"config\" varchar(4096) NOT NULL,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '文件配置表';\n\nCREATE TABLE IF NOT EXISTS \"infra_file\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"config_id\" bigint NOT NULL,\n    \"name\" varchar(256),\n    \"path\" varchar(512),\n    \"url\" varchar(1024),\n    \"type\" varchar(63) DEFAULT NULL,\n    \"size\" bigint NOT NULL,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint not null default  '0',\n    PRIMARY KEY (\"id\")\n) COMMENT '文件表';\n\nCREATE TABLE IF NOT EXISTS \"infra_job\" (\n    \"id\" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '任务编号',\n    \"name\" varchar(32) NOT NULL COMMENT '任务名称',\n    \"status\" tinyint(4) NOT NULL COMMENT '任务状态',\n    \"handler_name\" varchar(64) NOT NULL COMMENT '处理器的名字',\n    \"handler_param\" varchar(255) DEFAULT NULL COMMENT '处理器的参数',\n    \"cron_expression\" varchar(32) NOT NULL COMMENT 'CRON 表达式',\n    \"retry_count\" int(11) NOT NULL DEFAULT '0' COMMENT '重试次数',\n    \"retry_interval\" int(11) NOT NULL DEFAULT '0' COMMENT '重试间隔',\n    \"monitor_timeout\" int(11) NOT NULL DEFAULT '0' COMMENT '监控超时时间',\n    \"creator\" varchar(64) DEFAULT '' COMMENT '创建者',\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    \"updater\" varchar(64) DEFAULT '' COMMENT '更新者',\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    \"deleted\" bit NOT NULL DEFAULT FALSE COMMENT '是否删除',\n    PRIMARY KEY (\"id\")\n) COMMENT='定时任务表';\n\nCREATE TABLE IF NOT EXISTS \"infra_job_log\" (\n    \"id\" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '日志编号',\n    \"job_id\" bigint(20) NOT NULL COMMENT '任务编号',\n    \"handler_name\" varchar(64) NOT NULL COMMENT '处理器的名字',\n    \"handler_param\" varchar(255) DEFAULT NULL COMMENT '处理器的参数',\n    \"execute_index\" tinyint(4) NOT NULL DEFAULT '1' COMMENT '第几次执行',\n    \"begin_time\" datetime NOT NULL COMMENT '开始执行时间',\n    \"end_time\" datetime DEFAULT NULL COMMENT '结束执行时间',\n    \"duration\" int(11) DEFAULT NULL COMMENT '执行时长',\n    \"status\" tinyint(4) NOT NULL COMMENT '任务状态',\n    \"result\" varchar(4000) DEFAULT '' COMMENT '结果数据',\n    \"creator\" varchar(64) DEFAULT '' COMMENT '创建者',\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    \"updater\" varchar(64) DEFAULT '' COMMENT '更新者',\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    \"deleted\" bit(1) NOT NULL DEFAULT FALSE COMMENT '是否删除',\n    PRIMARY KEY (\"id\")\n)COMMENT='定时任务日志表';\n\nCREATE TABLE IF NOT EXISTS \"infra_api_access_log\" (\n    \"id\" bigint not null GENERATED BY DEFAULT AS IDENTITY,\n    \"trace_id\" varchar(64) not null default '',\n    \"user_id\" bigint not null default '0',\n    \"user_type\" tinyint not null default '0',\n    \"application_name\" varchar(50) not null,\n    \"request_method\" varchar(16) not null default '',\n    \"request_url\" varchar(255) not null default '',\n    \"request_params\" varchar(8000) not null default '',\n    \"response_body\" varchar(8000) not null default '',\n    \"user_ip\" varchar(50) not null,\n    \"user_agent\" varchar(512) not null,\n    `operate_module`           varchar(50)   NOT NULL,\n    `operate_name`             varchar(50)   NOT NULL,\n    `operate_type`     bigint(4)     NOT NULL DEFAULT '0',\n    \"begin_time\" timestamp not null,\n    \"end_time\" timestamp not null,\n    \"duration\" integer not null,\n    \"result_code\" integer not null default '0',\n    \"result_msg\" varchar(512) default '',\n    \"creator\" varchar(64) default '',\n    \"create_time\" timestamp not null default current_timestamp,\n    \"updater\" varchar(64) default '',\n    \"update_time\" timestamp not null default current_timestamp,\n    \"deleted\" bit not null default false,\n    \"tenant_id\" bigint not null default  '0',\n    primary key (\"id\")\n) COMMENT 'API 访问日志表';\n\nCREATE TABLE IF NOT EXISTS \"infra_api_error_log\" (\n    \"id\" bigint not null GENERATED BY DEFAULT AS IDENTITY,\n    \"trace_id\" varchar(64) not null,\n    \"user_id\" bigint not null default '0',\n    \"user_type\" tinyint not null default '0',\n    \"application_name\" varchar(50) not null,\n    \"request_method\" varchar(16) not null,\n    \"request_url\" varchar(255) not null,\n    \"request_params\" varchar(8000) not null,\n    \"user_ip\" varchar(50) not null,\n    \"user_agent\" varchar(512) not null,\n    \"exception_time\" timestamp not null,\n    \"exception_name\" varchar(128) not null default '',\n    \"exception_message\" clob not null,\n    \"exception_root_cause_message\" clob not null,\n    \"exception_stack_trace\" clob not null,\n    \"exception_class_name\" varchar(512) not null,\n    \"exception_file_name\" varchar(512) not null,\n    \"exception_method_name\" varchar(512) not null,\n    \"exception_line_number\" integer not null,\n    \"process_status\" tinyint not null,\n    \"process_time\" timestamp default null,\n    \"process_user_id\" bigint default '0',\n    \"creator\" varchar(64) default '',\n    \"create_time\" timestamp not null default current_timestamp,\n    \"updater\" varchar(64) default '',\n    \"update_time\" timestamp not null default current_timestamp,\n    \"deleted\" bit not null default false,\n    \"tenant_id\" bigint not null default  '0',\n    primary key (\"id\")\n) COMMENT '系统异常日志';\n\nCREATE TABLE IF NOT EXISTS \"infra_data_source_config\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(100) NOT NULL,\n    \"url\" varchar(1024) NOT NULL,\n    \"username\" varchar(255) NOT NULL,\n    \"password\" varchar(255) NOT NULL,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '数据源配置表';\n\nCREATE TABLE IF NOT EXISTS \"infra_codegen_table\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"data_source_config_id\" bigint not null,\n    \"scene\" tinyint not null DEFAULT 1,\n    \"table_name\" varchar(200) NOT NULL,\n    \"table_comment\" varchar(500) NOT NULL,\n    \"remark\" varchar(500) NOT NULL,\n    \"module_name\" varchar(30) NOT NULL,\n    \"business_name\" varchar(30) NOT NULL,\n    \"class_name\" varchar(100) NOT NULL,\n    \"class_comment\" varchar(50) NOT NULL,\n    \"author\" varchar(50) NOT NULL,\n    \"template_type\" tinyint not null DEFAULT 1,\n    \"front_type\" tinyint not null,\n    \"parent_menu_id\" bigint not null,\n    \"master_table_id\" bigint not null,\n    \"sub_join_column_id\" bigint not null,\n    \"sub_join_many\" bit not null,\n    \"tree_parent_column_id\" bigint not null,\n    \"tree_name_column_id\" bigint not null,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '代码生成表定义表';\n\nCREATE TABLE IF NOT EXISTS \"infra_codegen_column\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"table_id\" bigint not null,\n    \"column_name\" varchar(200) NOT NULL,\n    \"data_type\" varchar(100) NOT NULL,\n    \"column_comment\" varchar(500) NOT NULL,\n    \"nullable\" tinyint not null,\n    \"primary_key\" tinyint not null,\n    \"ordinal_position\" int not null,\n    \"java_type\" varchar(32) NOT NULL,\n    \"java_field\" varchar(64) NOT NULL,\n    \"dict_type\" varchar(200) NOT NULL,\n    \"example\" varchar(64) NOT NULL,\n    \"create_operation\" bit not null,\n    \"update_operation\" bit not null,\n    \"list_operation\" bit not null,\n    \"list_operation_condition\" varchar(32) not null,\n    \"list_operation_result\" bit not null,\n    \"html_type\" varchar(32) NOT NULL,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '代码生成表字段定义表';"
  },
  {
    "path": "yudao-module-iot/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modules>\n        <module>yudao-module-iot-api</module>\n        <module>yudao-module-iot-core</module>\n        <module>yudao-module-iot-server</module>\n        <module>yudao-module-iot-gateway</module>\n    </modules>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>yudao-module-iot</artifactId>\n    <packaging>pom</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        物联网模块\n        <!-- TODO 芋艿：需要补充下说明！ -->\n    </description>\n\n</project>"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao-module-iot</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-iot-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <!-- TODO 芋艿：需要在整理下，特别是 PF4J -->\n    <description>\n        物联网 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n            <scope>provided</scope> <!-- 设置为 provided，只有工具类需要使用到 -->\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java",
    "content": "/**\n * iot API 包，定义暴露给其它模块的 API\n */\npackage cn.iocoder.yudao.module.iot.api;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java",
    "content": "package cn.iocoder.yudao.module.iot.enums;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\n\n/**\n * API 相关的枚举\n *\n * @author 芋道源码\n */\npublic class ApiConstants {\n\n    public static final String PREFIX = RpcConstants.RPC_API_PREFIX + \"/iot\";\n\n    public static final String VERSION = \"1.0.0\";\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.iot.enums;\n\n/**\n * IoT 字典类型的枚举类\n *\n * @author 芋道源码\n */\npublic class DictTypeConstants {\n\n    public static final String NET_TYPE = \"iot_net_type\";\n    public static final String PROTOCOL_TYPE = \"iot_protocol_type\";\n    public static final String SERIALIZE_TYPE = \"iot_serialize_type\";\n\n    public static final String PRODUCT_STATUS = \"iot_product_status\";\n    public static final String PRODUCT_DEVICE_TYPE = \"iot_product_device_type\";\n\n    public static final String DEVICE_STATE = \"iot_device_state\";\n\n    public static final String ALERT_LEVEL = \"iot_alert_level\";\n\n    public static final String OTA_TASK_DEVICE_SCOPE = \"iot_ota_task_device_scope\";\n    public static final String OTA_TASK_STATUS = \"iot_ota_task_status\";\n    public static final String OTA_TASK_RECORD_STATUS = \"iot_ota_task_record_status\";\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.iot.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * iot 错误码枚举类\n * <p>\n * iot 系统，使用 1-050-000-000 段\n */\npublic interface ErrorCodeConstants {\n\n    // ========== 产品相关 1-050-001-000 ============\n    ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_050_001_000, \"产品不存在\");\n    ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, \"产品标识已经存在\");\n    ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, \"产品状是发布状态，不允许删除\");\n    ErrorCode PRODUCT_STATUS_NOT_ALLOW_THING_MODEL = new ErrorCode(1_050_001_003, \"产品状是发布状态，不允许操作物模型\");\n    ErrorCode PRODUCT_DELETE_FAIL_HAS_DEVICE = new ErrorCode(1_050_001_004, \"产品下存在设备，不允许删除\");\n\n    // ========== 产品物模型 1-050-002-000 ============\n    ErrorCode THING_MODEL_NOT_EXISTS = new ErrorCode(1_050_002_000, \"产品物模型不存在\");\n    ErrorCode THING_MODEL_EXISTS_BY_PRODUCT_KEY = new ErrorCode(1_050_002_001, \"ProductKey 对应的产品物模型已存在\");\n    ErrorCode THING_MODEL_IDENTIFIER_EXISTS = new ErrorCode(1_050_002_002, \"存在重复的功能标识符。\");\n    ErrorCode THING_MODEL_NAME_EXISTS = new ErrorCode(1_050_002_003, \"存在重复的功能名称。\");\n    ErrorCode THING_MODEL_IDENTIFIER_INVALID = new ErrorCode(1_050_002_003, \"产品物模型标识无效\");\n\n    // ========== 设备 1-050-003-000 ============\n    ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, \"设备不存在\");\n    ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, \"设备名称在同一产品下必须唯一\");\n    ErrorCode DEVICE_GATEWAY_HAS_SUB = new ErrorCode(1_050_003_002, \"网关设备存在已绑定的子设备，不允许删除\");\n    ErrorCode DEVICE_KEY_EXISTS = new ErrorCode(1_050_003_003, \"设备标识已经存在\");\n    ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, \"网关设备不存在\");\n    ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, \"设备不是网关设备\");\n    ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, \"导入设备数据不能为空！\");\n    ErrorCode DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL = new ErrorCode(1_050_003_007, \"下行设备消息失败，原因：设备未连接网关\");\n    ErrorCode DEVICE_SERIAL_NUMBER_EXISTS = new ErrorCode(1_050_003_008, \"设备序列号已存在，序列号必须全局唯一\");\n    ErrorCode DEVICE_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_009, \"设备【{}/{}】不是网关子设备类型，无法绑定到网关\");\n    ErrorCode DEVICE_GATEWAY_BINDTO_EXISTS = new ErrorCode(1_050_003_010, \"设备【{}/{}】已绑定到其他网关，请先解绑\");\n    // 拓扑管理相关错误码 1-050-003-100\n    ErrorCode DEVICE_TOPO_PARAMS_INVALID = new ErrorCode(1_050_003_100, \"拓扑管理参数无效\");\n    ErrorCode DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID = new ErrorCode(1_050_003_101, \"子设备用户名格式无效\");\n    ErrorCode DEVICE_TOPO_SUB_DEVICE_AUTH_FAILED = new ErrorCode(1_050_003_102, \"子设备认证失败\");\n    ErrorCode DEVICE_TOPO_SUB_NOT_BINDTO_GATEWAY = new ErrorCode(1_050_003_103, \"子设备【{}/{}】未绑定到该网关\");\n    // 设备注册相关错误码 1-050-003-200\n    ErrorCode DEVICE_SUB_REGISTER_PARAMS_INVALID = new ErrorCode(1_050_003_200, \"子设备注册参数无效\");\n    ErrorCode DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_201, \"产品【{}】不是网关子设备类型\");\n    ErrorCode DEVICE_REGISTER_DISABLED = new ErrorCode(1_050_003_210, \"该产品未开启动态注册功能\");\n    ErrorCode DEVICE_REGISTER_SECRET_INVALID = new ErrorCode(1_050_003_211, \"产品密钥验证失败\");\n    ErrorCode DEVICE_REGISTER_ALREADY_EXISTS = new ErrorCode(1_050_003_212, \"设备已存在，不允许重复注册\");\n\n    // ========== 产品分类 1-050-004-000 ==========\n    ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, \"产品分类不存在\");\n\n    // ========== 设备分组 1-050-005-000 ==========\n    ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, \"设备分组不存在\");\n    ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, \"设备分组下存在设备，不允许删除\");\n\n    // ========== 设备 Modbus 配置 1-050-006-000 ==========\n    ErrorCode DEVICE_MODBUS_CONFIG_NOT_EXISTS = new ErrorCode(1_050_006_000, \"设备 Modbus 连接配置不存在\");\n    ErrorCode DEVICE_MODBUS_CONFIG_EXISTS = new ErrorCode(1_050_006_001, \"设备 Modbus 连接配置已存在\");\n\n    // ========== 设备 Modbus 点位 1-050-007-000 ==========\n    ErrorCode DEVICE_MODBUS_POINT_NOT_EXISTS = new ErrorCode(1_050_007_000, \"设备 Modbus 点位配置不存在\");\n    ErrorCode DEVICE_MODBUS_POINT_EXISTS = new ErrorCode(1_050_007_001, \"设备 Modbus 点位配置已存在\");\n\n    // ========== OTA 固件相关 1-050-008-000 ==========\n\n    ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, \"固件信息不存在\");\n    ErrorCode OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE = new ErrorCode(1_050_008_001, \"产品版本号重复\");\n\n    // ========== OTA 升级任务相关 1-050-008-100 ==========\n\n    ErrorCode OTA_TASK_NOT_EXISTS = new ErrorCode(1_050_008_100, \"升级任务不存在\");\n    ErrorCode OTA_TASK_CREATE_FAIL_NAME_DUPLICATE = new ErrorCode(1_050_008_101, \"创建 OTA 任务失败，原因：任务名称重复\");\n    ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_FIRMWARE_EXISTS = new ErrorCode(1_050_008_102,\n            \"创建 OTA 任务失败，原因：设备({})已经是该固件版本\");\n    ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_OTA_IN_PROCESS = new ErrorCode(1_050_008_102,\n            \"创建 OTA 任务失败，原因：设备({})已经在升级中...\");\n    ErrorCode OTA_TASK_CREATE_FAIL_DEVICE_EMPTY = new ErrorCode(1_050_008_103, \"创建 OTA 任务失败，原因：没有可升级的设备\");\n    ErrorCode OTA_TASK_CANCEL_FAIL_STATUS_END = new ErrorCode(1_050_008_104, \"取消 OTA 任务失败，原因：任务状态不是进行中\");\n\n    // ========== OTA 升级任务记录相关 1-050-008-200 ==========\n\n    ErrorCode OTA_TASK_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, \"升级记录不存在\");\n    ErrorCode OTA_TASK_RECORD_CANCEL_FAIL_STATUS_ERROR = new ErrorCode(1_050_008_201, \"取消 OTA 升级记录失败，原因：记录状态不是进行中\");\n    ErrorCode OTA_TASK_RECORD_UPDATE_PROGRESS_FAIL_NO_EXISTS = new ErrorCode(1_050_008_202, \"更新 OTA 升级记录进度失败，原因：该设备没有进行中的升级记录\");\n\n    // ========== IoT 数据流转规则 1-050-010-000 ==========\n    ErrorCode DATA_RULE_NOT_EXISTS = new ErrorCode(1_050_010_000, \"数据流转规则不存在\");\n    ErrorCode DATA_RULE_NAME_EXISTS = new ErrorCode(1_050_010_001, \"数据流转规则名称已存在\");\n\n    // ========== IoT 数据流转目的 1-050-011-000 ==========\n    ErrorCode DATA_SINK_NOT_EXISTS = new ErrorCode(1_050_011_000, \"数据桥梁不存在\");\n    ErrorCode DATA_SINK_DELETE_FAIL_USED_BY_RULE = new ErrorCode(1_050_011_001, \"数据流转目的正在被数据流转规则使用，无法删除\");\n    ErrorCode DATA_SINK_NAME_EXISTS = new ErrorCode(1_050_011_002, \"数据流转目的名称已存在\");\n\n    // ========== IoT 场景联动 1-050-012-000 ==========\n    ErrorCode RULE_SCENE_NOT_EXISTS = new ErrorCode(1_050_012_000, \"场景联动不存在\");\n\n    // ========== IoT 告警配置 1-050-013-000 ==========\n    ErrorCode ALERT_CONFIG_NOT_EXISTS = new ErrorCode(1_050_013_000, \"IoT 告警配置不存在\");\n\n    // ========== IoT 告警记录 1-050-014-000 ==========\n    ErrorCode ALERT_RECORD_NOT_EXISTS = new ErrorCode(1_050_014_000, \"IoT 告警记录不存在\");\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/alert/IotAlertReceiveTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.alert;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT 告警的接收方式枚举\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotAlertReceiveTypeEnum implements ArrayValuable<Integer> {\n\n    SMS(1), // 短信\n    MAIL(2), // 邮箱\n    NOTIFY(3); // 站内信\n    // TODO 待实现（欢迎 pull request）：webhook 4\n\n    private final Integer type;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotAlertReceiveTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaTaskDeviceScopeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.ota;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT OTA 升级任务的设备范围枚举\n *\n * @author haohao\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotOtaTaskDeviceScopeEnum implements ArrayValuable<Integer> {\n\n    ALL(1), // 全部设备：只包括当前产品下的设备，不包括未来创建的设备\n    SELECT(2); // 指定设备\n\n    public static final Integer[] ARRAYS = Arrays.stream(values())\n            .map(IotOtaTaskDeviceScopeEnum::getScope).toArray(Integer[]::new);\n\n    /**\n     * 范围\n     */\n    private final Integer scope;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaTaskRecordStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.ota;\n\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * IoT OTA 升级任务记录的状态枚举\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotOtaTaskRecordStatusEnum implements ArrayValuable<Integer> {\n\n    PENDING(0), // 待推送\n    PUSHED(10), // 已推送\n    UPGRADING(20), // 升级中\n    SUCCESS(30), // 升级成功\n    FAILURE(40), // 升级失败\n    CANCELED(50),; // 升级取消\n\n    public static final Integer[] ARRAYS = Arrays.stream(values())\n            .map(IotOtaTaskRecordStatusEnum::getStatus).toArray(Integer[]::new);\n\n    public static final Set<Integer> IN_PROCESS_STATUSES = SetUtils.asSet(\n            PENDING.getStatus(),\n            PUSHED.getStatus(),\n            UPGRADING.getStatus());\n\n    public static final List<Integer> PRIORITY_STATUSES = Arrays.asList(\n            SUCCESS.getStatus(),\n            PENDING.getStatus(), PUSHED.getStatus(), UPGRADING.getStatus(),\n            FAILURE.getStatus(), CANCELED.getStatus());\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static IotOtaTaskRecordStatusEnum of(Integer status) {\n        return ArrayUtil.firstMatch(o -> o.getStatus().equals(status), values());\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaTaskStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.ota;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT OTA 升级任务的状态\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotOtaTaskStatusEnum implements ArrayValuable<Integer> {\n\n    IN_PROGRESS(10), // 进行中（升级中）\n    END(20), // 已结束（包括全部成功、部分成功）\n    CANCELED(30),; // 已取消（一般是主动取消任务）\n\n    public static final Integer[] ARRAYS = Arrays.stream(values())\n            .map(IotOtaTaskStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotNetTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.product;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * IoT 联网方式枚举类\n *\n * @author ahh\n */\n@AllArgsConstructor\n@Getter\npublic enum IotNetTypeEnum implements ArrayValuable<Integer> {\n\n    WIFI(0, \"Wi-Fi\"),\n    CELLULAR(1, \"Cellular\"),\n    ETHERNET(2, \"Ethernet\"),\n    OTHER(3, \"其他\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotNetTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 描述\n     */\n    private final String description;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.product;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * IoT 产品的设备类型\n *\n * @author ahh\n */\n@AllArgsConstructor\n@Getter\npublic enum IotProductDeviceTypeEnum implements ArrayValuable<Integer> {\n\n    DIRECT(0, \"直连设备\"),\n    GATEWAY_SUB(1, \"网关子设备\"),\n    GATEWAY(2, \"网关设备\");\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n\n    /**\n     * 描述\n     */\n    private final String description;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotProductDeviceTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    /**\n     * 判断是否是网关\n     *\n     * @param type 类型\n     * @return 是否是网关\n     */\n    public static boolean isGateway(Integer type) {\n        return GATEWAY.getType().equals(type);\n    }\n\n    /**\n     * 判断是否是网关子设备\n     *\n     * @param type 类型\n     * @return 是否是网关子设备\n     */\n    public static boolean isGatewaySub(Integer type) {\n        return GATEWAY_SUB.getType().equals(type);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.product;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * IoT 产品的状态枚举类\n *\n * @author ahh\n */\n@AllArgsConstructor\n@Getter\npublic enum IotProductStatusEnum implements ArrayValuable<Integer> {\n\n    UNPUBLISHED(0, \"开发中\"),\n    PUBLISHED(1, \"已发布\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotProductStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer status;\n    /**\n     * 描述\n     */\n    private final String description;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.rule;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT 数据目的的类型枚举\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotDataSinkTypeEnum implements ArrayValuable<Integer> {\n\n    HTTP(1, \"HTTP\"),\n    TCP(2, \"TCP\"),\n    WEBSOCKET(3, \"WebSocket\"),\n\n    MQTT(10, \"MQTT\"), // TODO @puhui999：待实现；\n\n    DATABASE(20, \"Database\"), // TODO @puhui999：待实现；\n    REDIS(21, \"Redis\"),\n\n    ROCKETMQ(30, \"RocketMQ\"),\n    RABBITMQ(31, \"RabbitMQ\"),\n    KAFKA(32, \"Kafka\");\n\n    private final Integer type;\n\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataSinkTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRedisDataStructureEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.rule;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT Redis 数据结构类型枚举\n *\n * @author HUIHUI\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotRedisDataStructureEnum implements ArrayValuable<Integer> {\n\n    STREAM(1, \"Stream\"),\n    HASH(2, \"Hash\"),\n    LIST(3, \"List\"),\n    SET(4, \"Set\"),\n    ZSET(5, \"ZSet\"),\n    STRING(6, \"String\");\n\n    private final Integer type;\n\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRedisDataStructureEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleActionTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.rule;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT 规则场景的触发类型枚举\n *\n * 设备触发，定时触发\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotSceneRuleActionTypeEnum implements ArrayValuable<Integer> {\n\n    /**\n     * 设备属性设置\n     *\n     * 对应 {@link cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum#PROPERTY_SET}\n     */\n    DEVICE_PROPERTY_SET(1),\n    /**\n     * 设备服务调用\n     *\n     * 对应 {@link cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum#SERVICE_INVOKE}\n     */\n    DEVICE_SERVICE_INVOKE(2),\n\n    /**\n     * 告警触发\n     */\n    ALERT_TRIGGER(100),\n    /**\n     * 告警恢复\n     */\n    ALERT_RECOVER(101),\n\n    ;\n\n    private final Integer type;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotSceneRuleActionTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleConditionOperatorEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.rule;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT 场景触发条件的操作符枚举\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotSceneRuleConditionOperatorEnum implements ArrayValuable<String> {\n\n    EQUALS(\"=\", \"#source == #value\"),\n    NOT_EQUALS(\"!=\", \"!(#source == #value)\"),\n\n    GREATER_THAN(\">\", \"#source > #value\"),\n    GREATER_THAN_OR_EQUALS(\">=\", \"#source >= #value\"),\n\n    LESS_THAN(\"<\", \"#source < #value\"),\n    LESS_THAN_OR_EQUALS(\"<=\", \"#source <= #value\"),\n\n    IN(\"in\", \"#values.contains(#source)\"),\n    NOT_IN(\"not in\", \"!(#values.contains(#source))\"),\n\n    BETWEEN(\"between\", \"(#source >= #values.get(0)) && (#source <= #values.get(1))\"),\n    NOT_BETWEEN(\"not between\", \"(#source < #values.get(0)) || (#source > #values.get(1))\"),\n\n    LIKE(\"like\", \"#source.contains(#value)\"), // 字符串匹配\n    NOT_NULL(\"not null\", \"#source != null && #source.length() > 0\"), // 非空\n\n    // ========== 特殊：不放在字典里 ==========\n\n    // TODO @puhui999：@芋艿：需要测试下\n    DATE_TIME_GREATER_THAN(\"date_time_>\", \"#source > #value\"), // 在时间之后：时间戳\n    DATE_TIME_LESS_THAN(\"date_time_<\", \"#source < #value\"), // 在时间之前：时间戳\n    DATE_TIME_BETWEEN(\"date_time_between\", // 在时间之间：时间戳\n            \"(#source >= #values.get(0)) && (#source <= #values.get(1))\"),\n\n    // TODO @puhui999：@芋艿：需要测试下\n    TIME_GREATER_THAN(\"time_>\", \"#source.isAfter(#value)\"), // 在当日时间之后：HH:mm:ss\n    TIME_LESS_THAN(\"time_<\", \"#source.isBefore(#value)\"), // 在当日时间之前：HH:mm:ss\n    TIME_BETWEEN(\"time_between\", // 在当日时间之间：HH:mm:ss\n            \"(#source >= #values.get(0)) && (#source <= #values.get(1))\"),\n\n    ;\n\n    private final String operator;\n    private final String springExpression;\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(IotSceneRuleConditionOperatorEnum::getOperator).toArray(String[]::new);\n\n    /**\n     * Spring 表达式 - 原始值\n     */\n    public static final String SPRING_EXPRESSION_SOURCE = \"source\";\n    /**\n     * Spring 表达式 - 目标值\n     */\n    public static final String SPRING_EXPRESSION_VALUE = \"value\";\n    /**\n     * Spring 表达式 - 目标值数组\n     */\n    public static final String SPRING_EXPRESSION_VALUE_LIST = \"values\";\n\n    public static IotSceneRuleConditionOperatorEnum operatorOf(String operator) {\n        return ArrayUtil.firstMatch(item -> item.getOperator().equals(operator), values());\n    }\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleConditionTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.rule;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT 条件类型枚举\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotSceneRuleConditionTypeEnum implements ArrayValuable<Integer> {\n\n    DEVICE_STATE(1, \"设备状态\"),\n    DEVICE_PROPERTY(2, \"设备属性\"),\n\n    CURRENT_TIME(100, \"当前时间\"),\n\n    ;\n\n    private final Integer type;\n    private final String name;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotSceneRuleConditionTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static IotSceneRuleConditionTypeEnum typeOf(Integer type) {\n        return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleTriggerTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.rule;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT 场景流转的触发类型枚举\n *\n * 为什么不直接使用 IotDeviceMessageMethodEnum 呢？\n * 原因是，物模型属性上报，存在批量上报的情况，不只对应一个 method！！！\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotSceneRuleTriggerTypeEnum implements ArrayValuable<Integer> {\n\n    /**\n     * 设备上下线变更\n     *\n     * 对应 IotDeviceMessageMethodEnum.STATE_UPDATE\n     */\n    DEVICE_STATE_UPDATE(1),\n    /**\n     * 物模型属性上报\n     *\n     * 对应 IotDeviceMessageMethodEnum.DEVICE_PROPERTY_POST\n     */\n    DEVICE_PROPERTY_POST(2),\n    /**\n     * 设备事件上报\n     *\n     * 对应 IotDeviceMessageMethodEnum.DEVICE_EVENT_POST\n     */\n    DEVICE_EVENT_POST(3),\n    /**\n     * 设备服务调用\n     *\n     * 对应 IotDeviceMessageMethodEnum.DEVICE_SERVICE_INVOKE\n     */\n    DEVICE_SERVICE_INVOKE(4),\n\n    /**\n     * 定时触发\n     */\n    TIMER(100)\n\n    ;\n\n    private final Integer type;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotSceneRuleTriggerTypeEnum::getType).toArray(Integer[]::new);\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static IotSceneRuleTriggerTypeEnum typeOf(Integer type) {\n        return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.thingmodel;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * IoT 数据定义的数据类型枚举类\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum IotDataSpecsDataTypeEnum implements ArrayValuable<String> {\n\n    INT(\"int\"),\n    FLOAT(\"float\"),\n    DOUBLE(\"double\"),\n    ENUM(\"enum\"),\n    BOOL(\"bool\"),\n    TEXT(\"text\"),\n    DATE(\"date\"),\n    STRUCT(\"struct\"),\n    ARRAY(\"array\");\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(IotDataSpecsDataTypeEnum::getDataType).toArray(String[]::new);\n\n    private final String dataType;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.thingmodel;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * IoT 产品物模型属性读取类型枚举\n *\n * @author ahh\n */\n@AllArgsConstructor\n@Getter\npublic enum IotThingModelAccessModeEnum implements ArrayValuable<String> {\n\n    READ_ONLY(\"r\"),\n    READ_WRITE(\"rw\");\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(IotThingModelAccessModeEnum::getMode).toArray(String[]::new);\n\n    private final String mode;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.thingmodel;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n\n/**\n * IoT 产品物模型参数是输入参数还是输出参数枚举\n *\n * @author HUIHUI\n */\n@AllArgsConstructor\n@Getter\npublic enum IotThingModelParamDirectionEnum implements ArrayValuable<String> {\n\n    INPUT(\"input\"), // 输入参数\n    OUTPUT(\"output\"); // 输出参数\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(IotThingModelParamDirectionEnum::getDirection).toArray(String[]::new);\n\n    private final String direction;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.thingmodel;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * IoT 产品物模型服务调用方式枚举\n *\n * @author HUIHUI\n */\n@AllArgsConstructor\n@Getter\npublic enum IotThingModelServiceCallTypeEnum implements ArrayValuable<String> {\n\n    ASYNC(\"async\"), // 异步调用\n    SYNC(\"sync\"); // 同步调用\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(IotThingModelServiceCallTypeEnum::getType).toArray(String[]::new);\n\n    private final String type;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.thingmodel;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * IoT 产品物模型事件类型枚举\n *\n * @author HUIHUI\n */\n@AllArgsConstructor\n@Getter\npublic enum IotThingModelServiceEventTypeEnum implements ArrayValuable<String> {\n\n    INFO(\"info\"), // 信息\n    ALERT(\"alert\"), // 告警\n    ERROR(\"error\"); // 故障\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(IotThingModelServiceEventTypeEnum::getType).toArray(String[]::new);\n\n    private final String type;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.enums.thingmodel;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * IoT 产品功能（物模型）类型枚举类\n *\n * @author ahh\n */\n@AllArgsConstructor\n@Getter\npublic enum IotThingModelTypeEnum implements ArrayValuable<Integer> {\n\n    PROPERTY(1, \"属性\"),\n    SERVICE(2, \"服务\"),\n    EVENT(3, \"事件\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotThingModelTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 描述\n     */\n    private final String description;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao-module-iot</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>yudao-module-iot-core</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        iot 模块下，提供 iot-biz 和 iot-gateway 模块的核心功能。例如说：\n        1. 消息总线：跨 iot-biz 和 iot-gateway 的设备消息。可选择使用 spring event、redis stream、rocketmq、kafka、rabbitmq 等。\n        2. 查询设备信息的通用 API\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Spring 核心 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 消息中间件相关（可选依赖） -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mq</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.data</groupId>\n            <artifactId>spring-data-redis</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.rocketmq</groupId>\n            <artifactId>rocketmq-spring-boot-starter</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.amqp</groupId>\n            <artifactId>spring-rabbit</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- 测试依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java",
    "content": "package cn.iocoder.yudao.module.iot.core.biz;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.*;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;\n\nimport java.util.List;\n\n/**\n * IoT 设备通用 API\n *\n * @author haohao\n */\npublic interface IotDeviceCommonApi {\n\n    /**\n     * 设备认证\n     *\n     * @param authReqDTO 认证请求\n     * @return 认证结果\n     */\n    CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO);\n\n    /**\n     * 获取设备信息\n     *\n     * @param infoReqDTO 设备信息请求\n     * @return 设备信息\n     */\n    CommonResult<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO infoReqDTO);\n\n    /**\n     * 直连/网关设备动态注册（一型一密）\n     *\n     * @param reqDTO 动态注册请求\n     * @return 注册结果（包含 DeviceSecret）\n     */\n    CommonResult<IotDeviceRegisterRespDTO> registerDevice(IotDeviceRegisterReqDTO reqDTO);\n\n    /**\n     * 网关子设备动态注册（网关代理转发）\n     *\n     * @param reqDTO 子设备注册请求（包含网关标识和子设备列表）\n     * @return 注册结果列表\n     */\n    CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO);\n\n    /**\n     * 获取 Modbus 设备配置列表\n     *\n     * @param listReqDTO 查询参数\n     * @return Modbus 设备配置列表\n     */\n    CommonResult<List<IotModbusDeviceConfigRespDTO>> getModbusDeviceConfigList(IotModbusDeviceConfigListReqDTO listReqDTO);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceAuthReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.biz.dto;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * IoT 设备认证 Request DTO\n *\n * @author 芋道源码\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceAuthReqDTO {\n\n    /**\n     * 客户端 ID\n     */\n    @NotEmpty(message = \"客户端 ID 不能为空\")\n    private String clientId;\n\n    /**\n     * 用户名\n     */\n    @NotEmpty(message = \"用户名不能为空\")\n    private String username;\n\n    /**\n     * 密码\n     */\n    @NotEmpty(message = \"密码不能为空\")\n    private String password;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceGetReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.biz.dto;\n\nimport lombok.Data;\n\n/**\n * IoT 设备信息查询 Request DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class IotDeviceGetReqDTO {\n\n    /**\n     * 设备编号\n     */\n    private Long id;\n\n    /**\n     * 产品标识\n     */\n    private String productKey;\n    /**\n     * 设备名称\n     */\n    private String deviceName;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceRespDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.biz.dto;\n\nimport lombok.Data;\n\n/**\n * IoT 设备信息 Response DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class IotDeviceRespDTO {\n\n    /**\n     * 设备编号\n     */\n    private Long id;\n    /**\n     * 产品标识\n     */\n    private String productKey;\n    /**\n     * 设备名称\n     */\n    private String deviceName;\n    /**\n     * 租户编号\n     */\n    private Long tenantId;\n\n    // ========== 产品相关字段 ==========\n\n    /**\n     * 产品编号\n     */\n    private Long productId;\n    /**\n     * 协议类型\n     */\n    private String protocolType;\n    /**\n     * 序列化类型\n     */\n    private String serializeType;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigListReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.biz.dto;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\nimport java.util.Set;\n\n/**\n * IoT Modbus 设备配置列表查询 Request DTO\n *\n * @author 芋道源码\n */\n@Data\n@Accessors(chain = true)\npublic class IotModbusDeviceConfigListReqDTO {\n\n    /**\n     * 状态\n     */\n    private Integer status;\n\n    /**\n     * 模式\n     */\n    private Integer mode;\n\n    /**\n     * 协议类型\n     */\n    private String protocolType;\n\n    /**\n     * 设备 ID 集合\n     */\n    private Set<Long> deviceIds;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigRespDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.biz.dto;\n\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * IoT Modbus 设备配置 Response DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class IotModbusDeviceConfigRespDTO {\n\n    /**\n     * 设备编号\n     */\n    private Long deviceId;\n    /**\n     * 产品标识\n     */\n    private String productKey;\n    /**\n     * 设备名称\n     */\n    private String deviceName;\n\n    // ========== Modbus 连接配置 ==========\n\n    /**\n     * Modbus 服务器 IP 地址\n     */\n    private String ip;\n    /**\n     * Modbus 服务器端口\n     */\n    private Integer port;\n    /**\n     * 从站地址\n     */\n    private Integer slaveId;\n    /**\n     * 连接超时时间，单位：毫秒\n     */\n    private Integer timeout;\n    /**\n     * 重试间隔，单位：毫秒\n     */\n    private Integer retryInterval;\n    /**\n     * 模式\n     */\n    private Integer mode;\n    /**\n     * 数据帧格式\n     */\n    private Integer frameFormat;\n\n    // ========== Modbus 点位配置 ==========\n\n    /**\n     * 点位列表\n     */\n    private List<IotModbusPointRespDTO> points;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusPointRespDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.biz.dto;\n\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusByteOrderEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusRawDataTypeEnum;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\n\n/**\n * IoT Modbus 点位配置 Response DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class IotModbusPointRespDTO {\n\n    /**\n     * 点位编号\n     */\n    private Long id;\n    /**\n     * 属性标识符（物模型的 identifier）\n     */\n    private String identifier;\n    /**\n     * 属性名称（物模型的 name）\n     */\n    private String name;\n\n    // ========== Modbus 协议配置 ==========\n\n    /**\n     * Modbus 功能码\n     *\n     * 取值范围：FC01-04（读线圈、读离散输入、读保持寄存器、读输入寄存器）\n     */\n    private Integer functionCode;\n    /**\n     * 寄存器起始地址\n     */\n    private Integer registerAddress;\n    /**\n     * 寄存器数量\n     */\n    private Integer registerCount;\n    /**\n     * 字节序\n     *\n     * 枚举 {@link IotModbusByteOrderEnum}\n     */\n    private String byteOrder;\n    /**\n     * 原始数据类型\n     *\n     * 枚举 {@link IotModbusRawDataTypeEnum}\n     */\n    private String rawDataType;\n    /**\n     * 缩放因子\n     */\n    private BigDecimal scale;\n    /**\n     * 轮询间隔（毫秒）\n     */\n    private Integer pollInterval;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotSubDeviceRegisterFullReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.biz.dto;\n\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n/**\n * IoT 子设备动态注册 Request DTO\n * <p>\n * 额外包含了网关设备的标识信息\n *\n * @author 芋道源码\n */\n@Data\npublic class IotSubDeviceRegisterFullReqDTO {\n\n    /**\n     * 网关设备 ProductKey\n     */\n    @NotEmpty(message = \"网关产品标识不能为空\")\n    private String gatewayProductKey;\n\n    /**\n     * 网关设备 DeviceName\n     */\n    @NotEmpty(message = \"网关设备名称不能为空\")\n    private String gatewayDeviceName;\n\n    /**\n     * 子设备注册列表\n     */\n    @NotNull(message = \"子设备注册列表不能为空\")\n    private List<IotSubDeviceRegisterReqDTO> subDevices;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.core.enums;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport cn.iocoder.yudao.framework.common.util.collection.SetUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\nimport java.util.Set;\n\n/**\n * IoT 设备消息的方法枚举\n *\n * @author haohao\n */\n@Getter\n@AllArgsConstructor\npublic enum IotDeviceMessageMethodEnum implements ArrayValuable<String> {\n\n    // ========== 设备状态 ==========\n\n    STATE_UPDATE(\"thing.state.update\", \"设备状态更新\", true),\n\n    // TODO 芋艿：要不要加个 ping 消息；\n\n    // ========== 拓扑管理 ==========\n    // 可参考：https://help.aliyun.com/zh/iot/user-guide/manage-topological-relationships\n\n    TOPO_ADD(\"thing.topo.add\", \"添加拓扑关系\", true),\n    TOPO_DELETE(\"thing.topo.delete\", \"删除拓扑关系\", true),\n    TOPO_GET(\"thing.topo.get\", \"获取拓扑关系\", true),\n    TOPO_CHANGE(\"thing.topo.change\", \"拓扑关系变更通知\", false),\n\n    // ========== 设备注册 ==========\n    // 可参考：https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification\n\n    DEVICE_REGISTER(\"thing.auth.register\", \"设备动态注册\", true),\n    SUB_DEVICE_REGISTER(\"thing.auth.register.sub\", \"子设备动态注册\", true),\n\n    // ========== 设备属性 ==========\n    // 可参考：https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services\n\n    PROPERTY_POST(\"thing.property.post\", \"属性上报\", true),\n    PROPERTY_SET(\"thing.property.set\", \"属性设置\", false),\n\n    PROPERTY_PACK_POST(\"thing.event.property.pack.post\", \"批量上报（属性 + 事件 + 子设备）\", true), // 网关独有\n\n    // ========== 设备事件 ==========\n    // 可参考：https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services\n\n    EVENT_POST(\"thing.event.post\", \"事件上报\", true),\n\n    // ========== 设备服务调用 ==========\n    // 可参考：https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services\n\n    SERVICE_INVOKE(\"thing.service.invoke\", \"服务调用\", false),\n\n    // ========== 设备配置 ==========\n    // 可参考：https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1\n\n    CONFIG_PUSH(\"thing.config.push\", \"配置推送\", false),\n\n    // ========== OTA 固件 ==========\n    // 可参考：https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates\n\n    OTA_UPGRADE(\"thing.ota.upgrade\", \"OTA 固件信息推送\", false),\n    OTA_PROGRESS(\"thing.ota.progress\", \"OTA 升级进度上报\", true),\n\n    ;\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod)\n            .toArray(String[]::new);\n\n    /**\n     * 不进行 reply 回复的方法集合\n     */\n    public static final Set<String> REPLY_DISABLED = SetUtils.asSet(\n            STATE_UPDATE.getMethod(),\n            OTA_PROGRESS.getMethod() // 参考阿里云，OTA 升级进度上报，不进行回复\n    );\n\n    private final String method;\n\n    private final String name;\n\n    private final Boolean upstream;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n    public static IotDeviceMessageMethodEnum of(String method) {\n        return ArrayUtil.firstMatch(item -> item.getMethod().equals(method),\n                IotDeviceMessageMethodEnum.values());\n    }\n\n    public static boolean isReplyDisabled(String method) {\n        return REPLY_DISABLED.contains(method);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotProtocolTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.core.enums;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT 协议类型枚举\n *\n * 用于定义传输层协议类型\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotProtocolTypeEnum implements ArrayValuable<String> {\n\n    TCP(\"tcp\"),\n    UDP(\"udp\"),\n    WEBSOCKET(\"websocket\"),\n    HTTP(\"http\"),\n    MQTT(\"mqtt\"),\n    EMQX(\"emqx\"),\n    COAP(\"coap\"),\n    MODBUS_TCP_CLIENT(\"modbus_tcp_client\"),\n    MODBUS_TCP_SERVER(\"modbus_tcp_server\");\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(IotProtocolTypeEnum::getType).toArray(String[]::new);\n\n    /**\n     * 类型\n     */\n    private final String type;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n    public static IotProtocolTypeEnum of(String type) {\n        return ArrayUtil.firstMatch(e -> e.getType().equals(type), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotSerializeTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.core.enums;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT 序列化类型枚举\n *\n * 用于定义设备消息的序列化格式\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotSerializeTypeEnum implements ArrayValuable<String> {\n\n    JSON(\"json\"),\n    BINARY(\"binary\");\n\n    public static final String[] ARRAYS = Arrays.stream(values()).map(IotSerializeTypeEnum::getType).toArray(String[]::new);\n\n    /**\n     * 类型\n     */\n    private final String type;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n    public static IotSerializeTypeEnum of(String type) {\n        return ArrayUtil.firstMatch(e -> e.getType().equals(type), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/device/IotDeviceStateEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.core.enums.device;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT 设备状态枚举\n *\n * @author haohao\n */\n@RequiredArgsConstructor\n@Getter\npublic enum IotDeviceStateEnum implements ArrayValuable<Integer> {\n\n    INACTIVE(0, \"未激活\"),\n    ONLINE(1, \"在线\"),\n    OFFLINE(2, \"离线\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDeviceStateEnum::getState).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer state;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isOnline(Integer state) {\n        return ONLINE.getState().equals(state);\n    }\n\n    public static boolean isNotOnline(Integer state) {\n        return !isOnline(state);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/modbus/IotModbusByteOrderEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.core.enums.modbus;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT Modbus 字节序枚举\n *\n * @author 芋道源码\n */\n@Getter\n@RequiredArgsConstructor\npublic enum IotModbusByteOrderEnum implements ArrayValuable<String> {\n\n    AB(\"AB\", \"大端序（16位）\", 2),\n    BA(\"BA\", \"小端序（16位）\", 2),\n    ABCD(\"ABCD\", \"大端序（32位）\", 4),\n    CDAB(\"CDAB\", \"大端字交换（32位）\", 4),\n    DCBA(\"DCBA\", \"小端序（32位）\", 4),\n    BADC(\"BADC\", \"小端字交换（32位）\", 4);\n\n    public static final String[] ARRAYS = Arrays.stream(values())\n            .map(IotModbusByteOrderEnum::getOrder)\n            .toArray(String[]::new);\n\n    /**\n     * 字节序\n     */\n    private final String order;\n    /**\n     * 名称\n     */\n    private final String name;\n    /**\n     * 字节数\n     */\n    private final Integer byteCount;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n    public static IotModbusByteOrderEnum getByOrder(String order) {\n        return Arrays.stream(values())\n                .filter(e -> e.getOrder().equals(order))\n                .findFirst()\n                .orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/modbus/IotModbusFrameFormatEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.core.enums.modbus;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT Modbus 数据帧格式枚举\n *\n * @author 芋道源码\n */\n@Getter\n@RequiredArgsConstructor\npublic enum IotModbusFrameFormatEnum implements ArrayValuable<Integer> {\n\n    MODBUS_TCP(1),\n    MODBUS_RTU(2);\n\n    public static final Integer[] ARRAYS = Arrays.stream(values())\n            .map(IotModbusFrameFormatEnum::getFormat)\n            .toArray(Integer[]::new);\n\n    /**\n     * 格式\n     */\n    private final Integer format;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/modbus/IotModbusModeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.core.enums.modbus;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT Modbus 工作模式枚举\n *\n * @author 芋道源码\n */\n@Getter\n@RequiredArgsConstructor\npublic enum IotModbusModeEnum implements ArrayValuable<Integer> {\n\n    POLLING(1, \"云端轮询\"),\n    ACTIVE_REPORT(2, \"边缘采集\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values())\n            .map(IotModbusModeEnum::getMode)\n            .toArray(Integer[]::new);\n\n    /**\n     * 工作模式\n     */\n    private final Integer mode;\n    /**\n     * 模式名称\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/modbus/IotModbusRawDataTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.core.enums.modbus;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * IoT Modbus 原始数据类型枚举\n *\n * @author 芋道源码\n */\n@Getter\n@RequiredArgsConstructor\npublic enum IotModbusRawDataTypeEnum implements ArrayValuable<String> {\n\n    INT16(\"INT16\", \"有符号 16 位整数\", 1),\n    UINT16(\"UINT16\", \"无符号 16 位整数\", 1),\n    INT32(\"INT32\", \"有符号 32 位整数\", 2),\n    UINT32(\"UINT32\", \"无符号 32 位整数\", 2),\n    FLOAT(\"FLOAT\", \"32 位浮点数\", 2),\n    DOUBLE(\"DOUBLE\", \"64 位浮点数\", 4),\n    BOOLEAN(\"BOOLEAN\", \"布尔值（用于线圈）\", 1),\n    STRING(\"STRING\", \"字符串\", null); // null 表示可变长度\n\n    public static final String[] ARRAYS = Arrays.stream(values())\n            .map(IotModbusRawDataTypeEnum::getType)\n            .toArray(String[]::new);\n\n    /**\n     * 数据类型\n     */\n    private final String type;\n    /**\n     * 名称\n     */\n    private final String name;\n    /**\n     * 寄存器数量（null 表示可变）\n     */\n    private final Integer registerCount;\n\n    @Override\n    public String[] array() {\n        return ARRAYS;\n    }\n\n    public static IotModbusRawDataTypeEnum getByType(String type) {\n        return Arrays.stream(values())\n                .filter(e -> e.getType().equals(type))\n                .findFirst()\n                .orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusAutoConfiguration.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.config;\n\nimport cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;\nimport cn.iocoder.yudao.framework.mq.redis.core.job.RedisPendingMessageResendJob;\nimport cn.iocoder.yudao.framework.mq.redis.core.job.RedisStreamMessageCleanupJob;\nimport cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessage;\nimport cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.local.IotLocalMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.redis.IotRedisMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.rocketmq.IotRocketMQMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.rocketmq.spring.autoconfigure.RocketMQProperties;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.redisson.api.RedissonClient;\nimport org.springframework.boot.autoconfigure.AutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.StringRedisTemplate;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * IoT 消息总线自动配置\n *\n * @author 芋道源码\n */\n@AutoConfiguration\n@EnableConfigurationProperties(IotMessageBusProperties.class)\n@Slf4j\npublic class IotMessageBusAutoConfiguration {\n\n    @Bean\n    public IotDeviceMessageProducer deviceMessageProducer(IotMessageBus messageBus) {\n        return new IotDeviceMessageProducer(messageBus);\n    }\n\n    // ==================== Local 实现 ====================\n\n    @Configuration\n    @ConditionalOnProperty(prefix = \"yudao.iot.message-bus\", name = \"type\", havingValue = \"local\", matchIfMissing = true)\n    public static class IotLocalMessageBusConfiguration {\n\n        @Bean\n        public IotLocalMessageBus iotLocalMessageBus(ApplicationContext applicationContext) {\n            log.info(\"[iotLocalMessageBus][创建 IoT Local 消息总线]\");\n            return new IotLocalMessageBus(applicationContext);\n        }\n\n    }\n\n    // ==================== RocketMQ 实现 ====================\n\n    @Configuration\n    @ConditionalOnProperty(prefix = \"yudao.iot.message-bus\", name = \"type\", havingValue = \"rocketmq\")\n    @ConditionalOnClass(RocketMQTemplate.class)\n    public static class IotRocketMQMessageBusConfiguration {\n\n        @Bean\n        public IotRocketMQMessageBus iotRocketMQMessageBus(RocketMQProperties rocketMQProperties,\n                                                           RocketMQTemplate rocketMQTemplate) {\n            log.info(\"[iotRocketMQMessageBus][创建 IoT RocketMQ 消息总线]\");\n            return new IotRocketMQMessageBus(rocketMQProperties, rocketMQTemplate);\n        }\n\n    }\n\n    // ==================== Redis 实现 ====================\n\n    /**\n     * 特殊：由于 YudaoRedisMQConsumerAutoConfiguration 关于 Redis stream 的消费是动态注册，所以这里只能拷贝相关的逻辑！！！\n     *\n     * @see cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration\n     */\n    @Configuration\n    @ConditionalOnProperty(prefix = \"yudao.iot.message-bus\", name = \"type\", havingValue = \"redis\")\n    @ConditionalOnClass(RedisTemplate.class)\n    public static class IotRedisMessageBusConfiguration {\n\n        @Bean\n        public IotRedisMessageBus iotRedisMessageBus(StringRedisTemplate redisTemplate) {\n            log.info(\"[iotRedisMessageBus][创建 IoT Redis 消息总线]\");\n            return new IotRedisMessageBus(redisTemplate);\n        }\n\n        /**\n         * 创建 Redis Stream 重新消费的任务\n         */\n        @Bean\n        public RedisPendingMessageResendJob iotRedisPendingMessageResendJob(IotRedisMessageBus messageBus,\n                                                                            RedisMQTemplate redisTemplate,\n                                                                            RedissonClient redissonClient) {\n            List<AbstractRedisStreamMessageListener<?>> listeners = getListeners(messageBus);\n            return new RedisPendingMessageResendJob(listeners, redisTemplate, redissonClient);\n        }\n\n        /**\n         * 创建 Redis Stream 消息清理任务\n         */\n        @Bean\n        public RedisStreamMessageCleanupJob iotRedisStreamMessageCleanupJob(IotRedisMessageBus messageBus,\n                                                                            RedisMQTemplate redisTemplate,\n                                                                            RedissonClient redissonClient) {\n            List<AbstractRedisStreamMessageListener<?>> listeners = getListeners(messageBus);\n            return new RedisStreamMessageCleanupJob(listeners, redisTemplate, redissonClient);\n        }\n\n        private List<AbstractRedisStreamMessageListener<?>> getListeners(IotRedisMessageBus messageBus) {\n            return convertList(messageBus.getSubscribers(), subscriber ->\n                    new AbstractRedisStreamMessageListener<AbstractRedisStreamMessage>(subscriber.getTopic(), subscriber.getGroup()) {\n\n                        @Override\n                        public void onMessage(AbstractRedisStreamMessage message) {\n                            throw new UnsupportedOperationException(\"不应该调用！！！\");\n                        }\n                    });\n        }\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusProperties.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * IoT 消息总线配置属性\n *\n * @author 芋道源码\n */\n@ConfigurationProperties(\"yudao.iot.message-bus\")\n@Data\n@Validated\npublic class IotMessageBusProperties {\n\n    /**\n     * 消息总线类型\n     *\n     * 可选值：local、redis、rocketmq、rabbitmq\n     */\n    @NotNull(message = \"IoT 消息总线类型不能为空\")\n    private String type = \"local\";\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/IotMessageBus.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.core;\n\n/**\n * IoT 消息总线接口\n *\n * 用于在 IoT 系统中发布和订阅消息，支持多种消息中间件实现\n *\n * @author 芋道源码\n */\npublic interface IotMessageBus {\n\n    /**\n     * 发布消息到消息总线\n     *\n     * @param topic 主题\n     * @param message 消息内容\n     */\n    void post(String topic, Object message);\n\n    /**\n     * 注册消息订阅者\n     *\n     * @param subscriber 订阅者\n     */\n    void register(IotMessageSubscriber<?> subscriber);\n\n    /**\n     * 取消注册消息订阅者\n     *\n     * @param subscriber 订阅者\n     */\n    default void unregister(IotMessageSubscriber<?> subscriber) {\n        // TODO 芋艿：暂时不实现，需求量不大，但是\n        // throw new UnsupportedOperationException(\"取消注册消息订阅者功能，尚未实现\");\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/IotMessageSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.core;\n\n/**\n * IoT 消息总线订阅者接口\n *\n * 用于处理从消息总线接收到的消息\n *\n * @author 芋道源码\n */\npublic interface IotMessageSubscriber<T> {\n\n    /**\n     * @return 主题\n     */\n    String getTopic();\n\n    /**\n     * @return 分组\n     */\n    String getGroup();\n\n    /**\n     * 处理接收到的消息\n     *\n     * @param message 消息内容\n     */\n    void onMessage(T message);\n\n    /**\n     * 启动订阅\n     */\n    default void start() {\n    }\n\n    /**\n     * 停止订阅\n     */\n    default void stop() {\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/local/IotLocalMessage.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.core.local;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\n@Data\n@AllArgsConstructor\npublic class IotLocalMessage {\n\n    private String topic;\n\n    private Object message;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/local/IotLocalMessageBus.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.core.local;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.event.EventListener;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 本地的 {@link IotMessageBus} 实现类\n *\n * 注意：仅适用于单机场景！！！\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class IotLocalMessageBus implements IotMessageBus {\n\n    private final ApplicationContext applicationContext;\n\n    /**\n     * 订阅者映射表\n     * Key: topic\n     */\n    private final Map<String, List<IotMessageSubscriber<?>>> subscribers = new HashMap<>();\n\n    @Override\n    public void post(String topic, Object message) {\n        applicationContext.publishEvent(new IotLocalMessage(topic, message));\n    }\n\n    @Override\n    public void register(IotMessageSubscriber<?> subscriber) {\n        String topic = subscriber.getTopic();\n        List<IotMessageSubscriber<?>> topicSubscribers = subscribers.computeIfAbsent(topic, k -> new ArrayList<>());\n        topicSubscribers.add(subscriber);\n        log.info(\"[register][topic({}/{}) 注册消费者({})成功]\",\n                topic, subscriber.getGroup(), subscriber.getClass().getName());\n    }\n\n    @EventListener\n    @SuppressWarnings({\"unchecked\", \"rawtypes\"})\n    public void onMessage(IotLocalMessage message) {\n        String topic = message.getTopic();\n        List<IotMessageSubscriber<?>> topicSubscribers = subscribers.get(topic);\n        if (CollUtil.isEmpty(topicSubscribers)) {\n            return;\n        }\n        for (IotMessageSubscriber subscriber : topicSubscribers) {\n            try {\n                subscriber.onMessage(message.getMessage());\n            } catch (Exception ex) {\n                log.error(\"[onMessage][topic({}/{}) message({}) 消费者({}) 处理异常]\",\n                        subscriber.getTopic(), subscriber.getGroup(), message.getMessage(), subscriber.getClass().getName(), ex);\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/redis/IotRedisMessageBus.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.core.redis;\n\nimport cn.hutool.core.util.TypeUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.data.redis.connection.stream.*;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.stream.StreamMessageListenerContainer;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.PreDestroy;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration.buildConsumerName;\nimport static cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration.checkRedisVersion;\n\n/**\n * Redis 的 {@link IotMessageBus} 实现类\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotRedisMessageBus implements IotMessageBus {\n\n    private final RedisTemplate<String, ?> redisTemplate;\n\n    private final StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer;\n\n    @Getter\n    private final List<IotMessageSubscriber<?>> subscribers = new ArrayList<>();\n\n    public IotRedisMessageBus(RedisTemplate<String, ?> redisTemplate) {\n        this.redisTemplate = redisTemplate;\n        checkRedisVersion(redisTemplate);\n        // 创建 options 配置\n        StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions =\n                StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()\n                        .batchSize(10) // 一次性最多拉取多少条消息\n                        .targetType(String.class) // 目标类型。统一使用 String，通过自己封装的 AbstractStreamMessageListener 去反序列化\n                        .build();\n        // 创建 container 对象\n        this.redisStreamMessageListenerContainer =\n                StreamMessageListenerContainer.create(redisTemplate.getRequiredConnectionFactory(), containerOptions);\n    }\n\n    @PostConstruct\n    public void init() {\n        this.redisStreamMessageListenerContainer.start();\n    }\n\n    @PreDestroy\n    public void destroy() {\n        this.redisStreamMessageListenerContainer.stop();\n    }\n\n    @Override\n    public void post(String topic, Object message) {\n        redisTemplate.opsForStream().add(StreamRecords.newRecord()\n                .ofObject(JsonUtils.toJsonString(message)) // 设置内容\n                .withStreamKey(topic)); // 设置 stream key\n    }\n\n    @Override\n    public void register(IotMessageSubscriber<?> subscriber) {\n        Type type = TypeUtil.getTypeArgument(subscriber.getClass(), 0);\n        if (type == null) {\n            throw new IllegalStateException(String.format(\"类型(%s) 需要设置消息类型\", getClass().getName()));\n        }\n\n        // 创建 listener 对应的消费者分组\n        try {\n            redisTemplate.opsForStream().createGroup(subscriber.getTopic(), subscriber.getGroup());\n        } catch (Exception ignore) {\n        }\n        // 创建 Consumer 对象\n        String consumerName = buildConsumerName();\n        Consumer consumer = Consumer.from(subscriber.getGroup(), consumerName);\n        // 设置 Consumer 消费进度，以最小消费进度为准\n        StreamOffset<String> streamOffset = StreamOffset.create(subscriber.getTopic(), ReadOffset.lastConsumed());\n        // 设置 Consumer 监听\n        StreamMessageListenerContainer.StreamReadRequestBuilder<String> builder = StreamMessageListenerContainer.StreamReadRequest\n                .builder(streamOffset).consumer(consumer)\n                .autoAcknowledge(false) // 不自动 ack\n                .cancelOnError(throwable -> false); // 默认配置，发生异常就取消消费，显然不符合预期；因此，我们设置为 false\n        redisStreamMessageListenerContainer.register(builder.build(), message -> {\n            // 消费消息\n            subscriber.onMessage(JsonUtils.parseObject(message.getValue(), type));\n            // ack 消息消费完成\n            redisTemplate.opsForStream().acknowledge(subscriber.getGroup(), message);\n        });\n        this.subscribers.add(subscriber);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/rocketmq/IotRocketMQMessageBus.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.core.rocketmq;\n\nimport cn.hutool.core.util.TypeUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;\nimport lombok.RequiredArgsConstructor;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;\nimport org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;\nimport org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.common.message.MessageExt;\nimport org.apache.rocketmq.spring.autoconfigure.RocketMQProperties;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\n\nimport javax.annotation.PreDestroy;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 基于 RocketMQ 的 {@link IotMessageBus} 实现类\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class IotRocketMQMessageBus implements IotMessageBus {\n\n    private final RocketMQProperties rocketMQProperties;\n\n    private final RocketMQTemplate rocketMQTemplate;\n\n    /**\n     * 主题对应的消费者映射\n     */\n    private final List<DefaultMQPushConsumer> topicConsumers = new ArrayList<>();\n\n    /**\n     * 销毁时关闭所有消费者\n     */\n    @PreDestroy\n    public void destroy() {\n        for (DefaultMQPushConsumer consumer : topicConsumers) {\n            try {\n                consumer.shutdown();\n                log.info(\"[destroy][关闭 group({}) 的消费者成功]\", consumer.getConsumerGroup());\n            } catch (Exception e) {\n                log.error(\"[destroy]关闭 group({}) 的消费者异常]\", consumer.getConsumerGroup(), e);\n            }\n        }\n    }\n\n    @Override\n    public void post(String topic, Object message) {\n        // TODO @芋艿：需要 orderly！\n        SendResult result = rocketMQTemplate.syncSend(topic, JsonUtils.toJsonString(message));\n        log.info(\"[post][topic({}) 发送消息({}) result({})]\", topic, message, result);\n    }\n\n    @Override\n    @SneakyThrows\n    public void register(IotMessageSubscriber<?> subscriber) {\n        Type type = TypeUtil.getTypeArgument(subscriber.getClass(), 0);\n        if (type == null) {\n            throw new IllegalStateException(String.format(\"类型(%s) 需要设置消息类型\", getClass().getName()));\n        }\n\n        // 1.1 创建 DefaultMQPushConsumer\n        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer();\n        consumer.setNamesrvAddr(rocketMQProperties.getNameServer());\n        consumer.setConsumerGroup(subscriber.getGroup());\n        // 1.2 订阅主题\n        consumer.subscribe(subscriber.getTopic(), \"*\");\n        // 1.3 设置消息监听器\n        consumer.setMessageListener((MessageListenerConcurrently) (messages, context) -> {\n            for (MessageExt messageExt : messages) {\n                try {\n                    byte[] body = messageExt.getBody();\n                    subscriber.onMessage(JsonUtils.parseObject(body, type));\n                } catch (Exception ex) {\n                    log.error(\"[onMessage][topic({}/{}) message({}) 消费者({}) 处理异常]\",\n                            subscriber.getTopic(), subscriber.getGroup(), messageExt, subscriber.getClass().getName(), ex);\n                    return ConsumeConcurrentlyStatus.RECONSUME_LATER;\n                }\n            }\n            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;\n        });\n        // 1.4 启动消费者\n        consumer.start();\n\n        // 2. 保存消费者引用\n        topicConsumers.add(consumer);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java",
    "content": "package cn.iocoder.yudao.module.iot.core.mq.message;\n\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.state.IotDeviceStateUpdateReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.time.LocalDateTime;\n\n/**\n * IoT 设备消息\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\npublic class IotDeviceMessage {\n\n    /**\n     * 【消息总线】应用的设备消息 Topic，由 iot-gateway 发给 iot-biz 进行消费\n     */\n    public static final String MESSAGE_BUS_DEVICE_MESSAGE_TOPIC = \"iot_device_message\";\n\n    /**\n     * 【消息总线】设备消息 Topic，由 iot-biz 发送给 iot-gateway 的某个 \"server\"(protocol) 进行消费\n     *\n     * 其中，%s 就是该\"server\"(protocol) 的标识\n     */\n    public static final String MESSAGE_BUS_GATEWAY_DEVICE_MESSAGE_TOPIC = MESSAGE_BUS_DEVICE_MESSAGE_TOPIC + \"_%s\";\n\n    /**\n     * 消息编号\n     *\n     * 由后端生成，通过 {@link IotDeviceMessageUtils#generateMessageId()}\n     */\n    private String id;\n    /**\n     * 上报时间\n     *\n     * 由后端生成，当前时间\n     */\n    private LocalDateTime reportTime;\n\n    /**\n     * 设备编号\n     */\n    private Long deviceId;\n    /**\n     * 租户编号\n     */\n    private Long tenantId;\n\n    /**\n     * 服务编号，该消息由哪个 server 发送\n     */\n    private String serverId;\n\n    // ========== serialize（序列化）相关字段 ==========\n\n    /**\n     * 请求编号\n     *\n     * 由设备生成，对应阿里云 IoT 的 Alink 协议中的 id、华为云 IoTDA 协议的 request_id\n     */\n    private String requestId;\n    /**\n     * 请求方法\n     *\n     * 枚举 {@link IotDeviceMessageMethodEnum}\n     * 例如说：thing.property.post 属性上报\n     */\n    private String method;\n    /**\n     * 请求参数\n     *\n     * 例如说：属性上报的 properties、事件上报的 params\n     */\n    private Object params;\n    /**\n     * 响应结果\n     */\n    private Object data;\n    /**\n     * 响应错误码\n     */\n    private Integer code;\n    /**\n     * 返回结果信息\n     */\n    private String msg;\n\n    // ========== 基础方法：只传递\"serialize（序列化）相关字段\" ==========\n\n    public static IotDeviceMessage requestOf(String method) {\n        return requestOf(null, method, null);\n    }\n\n    public static IotDeviceMessage requestOf(String method, Object params) {\n        return requestOf(null, method, params);\n    }\n\n    public static IotDeviceMessage requestOf(String requestId, String method, Object params) {\n        return of(requestId, method, params, null, null, null);\n    }\n\n    /**\n     * 创建设备请求消息（包含设备信息）\n     *\n     * @param deviceId 设备编号\n     * @param tenantId 租户编号\n     * @param serverId 服务标识\n     * @param method   消息方法\n     * @param params   消息参数\n     * @return 消息对象\n     */\n    public static IotDeviceMessage requestOf(Long deviceId, Long tenantId, String serverId,\n                                             String method, Object params) {\n        IotDeviceMessage message = of(null, method, params, null, null, null);\n        return message.setId(IotDeviceMessageUtils.generateMessageId())\n                .setDeviceId(deviceId).setTenantId(tenantId).setServerId(serverId);\n    }\n\n    public static IotDeviceMessage replyOf(String requestId, String method,\n                                           Object data, Integer code, String msg) {\n        if (code == null) {\n            code = GlobalErrorCodeConstants.SUCCESS.getCode();\n            msg = GlobalErrorCodeConstants.SUCCESS.getMsg();\n        }\n        return of(requestId, method, null, data, code, msg);\n    }\n\n    public static IotDeviceMessage of(String requestId, String method,\n                                      Object params, Object data, Integer code, String msg) {\n        // 通用参数\n        IotDeviceMessage message = new IotDeviceMessage()\n                .setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now());\n        // 当前参数\n        message.setRequestId(requestId).setMethod(method).setParams(params)\n                .setData(data).setCode(code).setMsg(msg);\n        return message;\n    }\n\n    // ========== 核心方法：在 of 基础方法之上，添加对应 method ==========\n\n    public static IotDeviceMessage buildStateUpdateOnline() {\n        return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(),\n                new IotDeviceStateUpdateReqDTO(IotDeviceStateEnum.ONLINE.getState()));\n    }\n\n    public static IotDeviceMessage buildStateOffline() {\n        return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(),\n                new IotDeviceStateUpdateReqDTO(IotDeviceStateEnum.OFFLINE.getState()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/producer/IotDeviceMessageProducer.java",
    "content": "package cn.iocoder.yudao.module.iot.core.mq.producer;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport lombok.RequiredArgsConstructor;\n\n/**\n * IoT 设备消息生产者\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\npublic class IotDeviceMessageProducer {\n\n    private final IotMessageBus messageBus;\n\n    /**\n     * 发送设备消息\n     *\n     * @param message 设备消息\n     */\n    public void sendDeviceMessage(IotDeviceMessage message) {\n        messageBus.post(IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC, message);\n    }\n\n    /**\n     * 发送网关设备消息\n     *\n     * @param serverId 网关的 serverId 标识\n     * @param message 设备消息\n     */\n    public void sendDeviceMessageToGateway(String serverId, IotDeviceMessage message) {\n        messageBus.post(IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(serverId), message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/IotDeviceIdentity.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * IoT 设备标识\n *\n * 用于标识一个设备的基本信息（productKey + deviceName）\n *\n * @author 芋道源码\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceIdentity {\n\n    /**\n     * 产品标识\n     */\n    @NotEmpty(message = \"产品标识不能为空\")\n    private String productKey;\n\n    /**\n     * 设备名称\n     */\n    @NotEmpty(message = \"设备名称不能为空\")\n    private String deviceName;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotDeviceRegisterReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.auth;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * IoT 设备动态注册 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#DEVICE_REGISTER} 消息的 params 参数\n * <p>\n * 直连设备/网关的一型一密动态注册：使用 productSecret 验证，返回 deviceSecret\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification\">阿里云 - 一型一密</a>\n */\n@Data\npublic class IotDeviceRegisterReqDTO {\n\n    /**\n     * 产品标识\n     */\n    @NotEmpty(message = \"产品标识不能为空\")\n    private String productKey;\n\n    /**\n     * 设备名称\n     */\n    @NotEmpty(message = \"设备名称不能为空\")\n    private String deviceName;\n\n    /**\n     * 注册签名\n     *\n     * @see cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils#buildSign(String, String, String)\n     */\n    @NotEmpty(message = \"签名不能为空\")\n    private String sign;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotDeviceRegisterRespDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.auth;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 设备动态注册 Response DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#DEVICE_REGISTER} 响应的设备信息\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification\">阿里云 - 一型一密</a>\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceRegisterRespDTO {\n\n    /**\n     * 产品标识\n     */\n    private String productKey;\n\n    /**\n     * 设备名称\n     */\n    private String deviceName;\n\n    /**\n     * 设备密钥\n     */\n    private String deviceSecret;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotSubDeviceRegisterReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.auth;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * IoT 子设备动态注册 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#SUB_DEVICE_REGISTER} 消息的 params 数组元素\n * <p>\n * 特殊：网关子设备的动态注册，必须已经创建好该网关子设备（不然哪来的 {@link #deviceName} 字段）。更多的好处，是设备不用提前烧录 deviceSecret 密钥。\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/register-devices\">阿里云 - 动态注册子设备</a>\n */\n@Data\npublic class IotSubDeviceRegisterReqDTO {\n\n    /**\n     * 子设备 ProductKey\n     */\n    @NotEmpty(message = \"产品标识不能为空\")\n    private String productKey;\n\n    /**\n     * 子设备 DeviceName\n     */\n    @NotEmpty(message = \"设备名称不能为空\")\n    private String deviceName;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotSubDeviceRegisterRespDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.auth;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 子设备动态注册 Response DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#SUB_DEVICE_REGISTER} 响应的设备信息\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/register-devices\">阿里云 - 动态注册子设备</a>\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotSubDeviceRegisterRespDTO {\n\n    /**\n     * 子设备 ProductKey\n     */\n    private String productKey;\n\n    /**\n     * 子设备 DeviceName\n     */\n    private String deviceName;\n\n    /**\n     * 分配的 DeviceSecret\n     */\n    private String deviceSecret;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/config/IotDeviceConfigPushReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.config;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 设备配置推送 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#CONFIG_PUSH} 下行消息的 params 参数\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1\">阿里云 - 远程配置</a>\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceConfigPushReqDTO {\n\n    /**\n     * 配置编号\n     */\n    private String configId;\n\n    /**\n     * 配置文件大小（字节）\n     */\n    private Long configSize;\n\n    /**\n     * 签名方法\n     */\n    private String signMethod;\n\n    /**\n     * 签名\n     */\n    private String sign;\n\n    /**\n     * 配置文件下载地址\n     */\n    private String url;\n\n    /**\n     * 获取类型\n     * <p>\n     * file: 文件\n     * content: 内容\n     */\n    private String getType;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.event;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.Data;\n\n/**\n * IoT 设备事件上报 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#EVENT_POST} 消息的 params 参数\n *\n * @author 芋道源码\n * @see <a href=\"http://help.aliyun.com/zh/marketplace/device-reporting-events\">阿里云 - 设备上报事件</a>\n */\n@Data\npublic class IotDeviceEventPostReqDTO {\n\n    /**\n     * 事件标识符\n     */\n    private String identifier;\n\n    /**\n     * 事件输出参数\n     */\n    private Object value;\n\n    /**\n     * 上报时间（毫秒时间戳，可选）\n     */\n    private Long time;\n\n    /**\n     * 创建事件上报 DTO\n     *\n     * @param identifier 事件标识符\n     * @param value   事件值\n     * @return DTO 对象\n     */\n    public static IotDeviceEventPostReqDTO of(String identifier, Object value) {\n        return of(identifier, value, null);\n    }\n\n    /**\n     * 创建事件上报 DTO（带时间）\n     *\n     * @param identifier 事件标识符\n     * @param value   事件值\n     * @param time    上报时间\n     * @return DTO 对象\n     */\n    public static IotDeviceEventPostReqDTO of(String identifier, Object value, Long time) {\n        return new IotDeviceEventPostReqDTO().setIdentifier(identifier).setValue(value).setTime(time);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/ota/IotDeviceOtaProgressReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.ota;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 设备 OTA 升级进度上报 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#OTA_PROGRESS} 上行消息的 params 参数\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates\">阿里云 - OTA 升级</a>\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceOtaProgressReqDTO {\n\n    /**\n     * 固件版本号\n     */\n    private String version;\n\n    /**\n     * 升级状态\n     */\n    private Integer status;\n\n    /**\n     * 描述信息\n     */\n    private String description;\n\n    /**\n     * 升级进度（0-100）\n     */\n    private Integer progress;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/ota/IotDeviceOtaUpgradeReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.ota;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 设备 OTA 固件升级推送 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#OTA_UPGRADE} 下行消息的 params 参数\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates\">阿里云 - OTA 升级</a>\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceOtaUpgradeReqDTO {\n\n    /**\n     * 固件版本号\n     */\n    private String version;\n\n    /**\n     * 固件文件下载地址\n     */\n    private String fileUrl;\n\n    /**\n     * 固件文件大小（字节）\n     */\n    private Long fileSize;\n\n    /**\n     * 固件文件摘要算法\n     */\n    private String fileDigestAlgorithm;\n\n    /**\n     * 固件文件摘要值\n     */\n    private String fileDigestValue;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/package-info.java",
    "content": "/**\n * IoT Topic 消息体 DTO 定义\n * <p>\n * 定义设备与平台通信的消息体结构，遵循（参考）阿里云 Alink 协议规范\n *\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/alink-protocol-1\">阿里云 Alink 协议</a>\n */\npackage cn.iocoder.yudao.module.iot.core.topic;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertyPackPostReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.property;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport lombok.Data;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * IoT 设备属性批量上报 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_PACK_POST} 消息的 params 参数\n *\n * @author 芋道源码\n * @see <a href=\"http://help.aliyun.com/zh/marketplace/gateway-reports-data-in-batches\">阿里云 - 网关批量上报数据</a>\n */\n@Data\npublic class IotDevicePropertyPackPostReqDTO {\n\n    /**\n     * 网关自身属性\n     * <p>\n     * key: 属性标识符\n     * value: 属性值\n     */\n    private Map<String, Object> properties;\n\n    /**\n     * 网关自身事件\n     * <p>\n     * key: 事件标识符\n     * value: 事件值对象（包含 value 和 time）\n     */\n    private Map<String, EventValue> events;\n\n    /**\n     * 子设备数据列表\n     */\n    private List<SubDeviceData> subDevices;\n\n    /**\n     * 事件值对象\n     */\n    @Data\n    public static class EventValue {\n\n        /**\n         * 事件参数\n         */\n        private Object value;\n\n        /**\n         * 上报时间（毫秒时间戳）\n         */\n        private Long time;\n\n    }\n\n    /**\n     * 子设备数据\n     */\n    @Data\n    public static class SubDeviceData {\n\n        /**\n         * 子设备标识\n         */\n        private IotDeviceIdentity identity;\n\n        /**\n         * 子设备属性\n         * <p>\n         * key: 属性标识符\n         * value: 属性值\n         */\n        private Map<String, Object> properties;\n\n        /**\n         * 子设备事件\n         * <p>\n         * key: 事件标识符\n         * value: 事件值对象（包含 value 和 time）\n         */\n        private Map<String, EventValue> events;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertyPostReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.property;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * IoT 设备属性上报 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_POST} 消息的 params 参数\n * <p>\n * 本质是一个 Map，key 为属性标识符，value 为属性值\n *\n * @author 芋道源码\n * @see <a href=\"http://help.aliyun.com/zh/marketplace/device-reporting-attributes\">阿里云 - 设备上报属性</a>\n */\npublic class IotDevicePropertyPostReqDTO extends HashMap<String, Object> {\n\n    public IotDevicePropertyPostReqDTO() {\n        super();\n    }\n\n    public IotDevicePropertyPostReqDTO(Map<String, Object> properties) {\n        super(properties);\n    }\n\n    /**\n     * 创建属性上报 DTO\n     *\n     * @param properties 属性数据\n     * @return DTO 对象\n     */\n    public static IotDevicePropertyPostReqDTO of(Map<String, Object> properties) {\n        return new IotDevicePropertyPostReqDTO(properties);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertySetReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.property;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * IoT 设备属性设置 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_SET} 下行消息的 params 参数\n * <p>\n * 本质是一个 Map，key 为属性标识符，value 为属性值\n *\n * @author 芋道源码\n */\npublic class IotDevicePropertySetReqDTO extends HashMap<String, Object> {\n\n    public IotDevicePropertySetReqDTO() {\n        super();\n    }\n\n    public IotDevicePropertySetReqDTO(Map<String, Object> properties) {\n        super(properties);\n    }\n\n    /**\n     * 创建属性设置 DTO\n     *\n     * @param properties 属性数据\n     * @return DTO 对象\n     */\n    public static IotDevicePropertySetReqDTO of(Map<String, Object> properties) {\n        return new IotDevicePropertySetReqDTO(properties);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/service/IotDeviceServiceInvokeReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.service;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Map;\n\n/**\n * IoT 设备服务调用 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#SERVICE_INVOKE} 下行消息的 params 参数\n *\n * @author 芋道源码\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceServiceInvokeReqDTO {\n\n    /**\n     * 服务标识符\n     */\n    private String identifier;\n\n    /**\n     * 服务输入参数\n     */\n    private Map<String, Object> inputParams;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/state/IotDeviceStateUpdateReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.state;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 设备状态更新 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#STATE_UPDATE} 消息的 params 参数\n *\n * @author 芋道源码\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceStateUpdateReqDTO {\n\n    /**\n     * 设备状态\n     */\n    private Integer state;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoAddReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.topo;\n\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\n\n/**\n * IoT 设备拓扑添加 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#TOPO_ADD} 消息的 params 参数\n *\n * @author 芋道源码\n * @see <a href=\"http://help.aliyun.com/zh/marketplace/add-topological-relationship\">阿里云 - 添加拓扑关系</a>\n */\n@Data\npublic class IotDeviceTopoAddReqDTO {\n\n    /**\n     * 子设备认证信息列表\n     * <p>\n     * 复用 {@link IotDeviceAuthReqDTO}，包含 clientId、username、password\n     */\n    @NotEmpty(message = \"子设备认证信息列表不能为空\")\n    private List<IotDeviceAuthReqDTO> subDevices;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoChangeReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.topo;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n/**\n * IoT 设备拓扑关系变更通知 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#TOPO_CHANGE} 下行消息的 params 参数\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/marketplace/notify-gateway-topology-changes\">阿里云 - 通知网关拓扑关系变化</a>\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceTopoChangeReqDTO {\n\n    public static final Integer STATUS_CREATE = 0;\n    public static final Integer STATUS_DELETE = 1;\n\n    /**\n     * 拓扑关系状态\n     */\n    private Integer status;\n\n    /**\n     * 子设备列表\n     */\n    private List<IotDeviceIdentity> subList;\n\n    public static IotDeviceTopoChangeReqDTO ofCreate(List<IotDeviceIdentity> subList) {\n        return new IotDeviceTopoChangeReqDTO(STATUS_CREATE, subList);\n    }\n\n    public static IotDeviceTopoChangeReqDTO ofDelete(List<IotDeviceIdentity> subList) {\n        return new IotDeviceTopoChangeReqDTO(STATUS_DELETE, subList);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoDeleteReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.topo;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\n\n/**\n * IoT 设备拓扑删除 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#TOPO_DELETE} 消息的 params 参数\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/marketplace/delete-a-topological-relationship\">阿里云 - 删除拓扑关系</a>\n */\n@Data\npublic class IotDeviceTopoDeleteReqDTO {\n\n    /**\n     * 子设备标识列表\n     */\n    @Valid\n    @NotEmpty(message = \"子设备标识列表不能为空\")\n    private List<IotDeviceIdentity> subDevices;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoGetReqDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.topo;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport lombok.Data;\n\n/**\n * IoT 设备拓扑关系获取 Request DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#TOPO_GET} 请求的 params 参数（目前为空，预留扩展）\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/marketplace/obtain-topological-relationship\">阿里云 - 获取拓扑关系</a>\n */\n@Data\npublic class IotDeviceTopoGetReqDTO {\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoGetRespDTO.java",
    "content": "package cn.iocoder.yudao.module.iot.core.topic.topo;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * IoT 设备拓扑关系获取 Response DTO\n * <p>\n * 用于 {@link IotDeviceMessageMethodEnum#TOPO_GET} 响应\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/marketplace/obtain-topological-relationship\">阿里云 - 获取拓扑关系</a>\n */\n@Data\npublic class IotDeviceTopoGetRespDTO {\n\n    /**\n     * 子设备列表\n     */\n    private List<IotDeviceIdentity> subDevices;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java",
    "content": "package cn.iocoder.yudao.module.iot.core.util;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport cn.hutool.crypto.digest.HmacAlgorithm;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\n\n/**\n * IoT 设备【认证】的工具类，参考阿里云\n *\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/how-do-i-obtain-mqtt-parameters-for-authentication\">如何计算 MQTT 签名参数</a>\n */\npublic class IotDeviceAuthUtils {\n\n    public static IotDeviceAuthReqDTO getAuthInfo(String productKey, String deviceName, String deviceSecret) {\n        String clientId = buildClientId(productKey, deviceName);\n        String username = buildUsername(productKey, deviceName);\n        String password = buildPassword(deviceSecret,\n                buildContent(clientId, productKey, deviceName, deviceSecret));\n        return new IotDeviceAuthReqDTO(clientId, username, password);\n    }\n\n    public static String buildClientId(String productKey, String deviceName) {\n        return String.format(\"%s.%s\", productKey, deviceName);\n    }\n\n    public static String buildClientIdFromUsername(String username) {\n        IotDeviceIdentity identity = parseUsername(username);\n        if (identity == null) {\n            return null;\n        }\n        return buildClientId(identity.getProductKey(), identity.getDeviceName());\n    }\n\n    public static String buildUsername(String productKey, String deviceName) {\n        return String.format(\"%s&%s\", deviceName, productKey);\n    }\n\n    public static String buildPassword(String deviceSecret, String content) {\n        return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.utf8Bytes(deviceSecret))\n                .digestHex(content);\n    }\n\n    private static String buildContent(String clientId, String productKey, String deviceName, String deviceSecret) {\n        return \"clientId\" + clientId +\n                \"deviceName\" + deviceName +\n                \"deviceSecret\" + deviceSecret +\n                \"productKey\" + productKey;\n    }\n\n    public static IotDeviceIdentity parseUsername(String username) {\n        String[] usernameParts = username.split(\"&\");\n        if (usernameParts.length != 2) {\n            return null;\n        }\n        return new IotDeviceIdentity(usernameParts[1], usernameParts[0]);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java",
    "content": "package cn.iocoder.yudao.module.iot.core.util;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.system.SystemUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\n\nimport java.util.Map;\n\n/**\n * IoT 设备【消息】的工具类\n *\n * @author 芋道源码\n */\npublic class IotDeviceMessageUtils {\n\n    // ========== Message 相关 ==========\n\n    public static String generateMessageId() {\n        return IdUtil.fastSimpleUUID();\n    }\n\n    /**\n     * 是否是上行消息：由设备发送\n     *\n     * @param message 消息\n     * @return 是否\n     */\n    @SuppressWarnings(\"SimplifiableConditionalExpression\")\n    public static boolean isUpstreamMessage(IotDeviceMessage message) {\n        IotDeviceMessageMethodEnum methodEnum = IotDeviceMessageMethodEnum.of(message.getMethod());\n        Assert.notNull(methodEnum, \"无法识别的消息方法：\" + message.getMethod());\n        // 注意：回复消息时，需要取反\n        return !isReplyMessage(message) ? methodEnum.getUpstream() : !methodEnum.getUpstream();\n    }\n\n    /**\n     * 是否是回复消息，通过 {@link IotDeviceMessage#getCode()} 非空进行识别\n     *\n     * @param message 消息\n     * @return 是否\n     */\n    public static boolean isReplyMessage(IotDeviceMessage message) {\n        return message.getCode() != null;\n    }\n\n    /**\n     * 提取消息中的标识符\n     *\n     * @param message 消息\n     * @return 标识符\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static String getIdentifier(IotDeviceMessage message) {\n        if (message.getParams() == null) {\n            return null;\n        }\n        if (StrUtil.equalsAny(message.getMethod(), IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod())) {\n            Map<String, Object> params = (Map<String, Object>) message.getParams();\n            return MapUtil.getStr(params, \"identifier\");\n        }  else if (StrUtil.equalsAny(message.getMethod(), IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod())) {\n            Map<String, Object> params = (Map<String, Object>) message.getParams();\n            return MapUtil.getStr(params, \"state\");\n        }\n        return null;\n    }\n\n    /**\n     * 判断消息中是否包含指定的标识符\n     * <p>\n     * 对于不同消息类型的处理：\n     * - EVENT_POST/SERVICE_INVOKE：检查 params.identifier 是否匹配\n     * - STATE_UPDATE：检查 params.state 是否匹配\n     * - PROPERTY_POST：检查 params 中是否包含该属性 key\n     *\n     * @param message    消息\n     * @param identifier 要检查的标识符\n     * @return 是否包含\n     */\n    public static boolean containsIdentifier(IotDeviceMessage message, String identifier) {\n        if (message.getParams() == null || StrUtil.isBlank(identifier)) {\n            return false;\n        }\n        // EVENT_POST / SERVICE_INVOKE / STATE_UPDATE：使用原有逻辑\n        String messageIdentifier = getIdentifier(message);\n        if (messageIdentifier != null) {\n            return identifier.equals(messageIdentifier);\n        }\n        // PROPERTY_POST：检查 params 中是否包含该属性 key\n        if (StrUtil.equals(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())) {\n            Map<String, Object> params = parseParamsToMap(message.getParams());\n            return params != null && params.containsKey(identifier);\n        }\n        return false;\n    }\n\n    /**\n     * 判断消息中是否不包含指定的标识符\n     *\n     * @param message    消息\n     * @param identifier 要检查的标识符\n     * @return 是否不包含\n     */\n    public static boolean notContainsIdentifier(IotDeviceMessage message, String identifier) {\n        return !containsIdentifier(message, identifier);\n    }\n\n    /**\n     * 将 params 解析为 Map\n     *\n     * @param params 参数（可能是 Map 或 JSON 字符串）\n     * @return Map，解析失败返回 null\n     */\n    @SuppressWarnings(\"unchecked\")\n    private static Map<String, Object> parseParamsToMap(Object params) {\n        if (params instanceof Map) {\n            return (Map<String, Object>) params;\n        }\n        if (params instanceof String) {\n            try {\n                return JsonUtils.parseObject((String) params, Map.class);\n            } catch (Exception ignored) {\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 从设备消息中提取指定标识符的属性值\n     * - 支持多种消息格式和属性值提取策略\n     * - 兼容现有的消息结构\n     * - 提供统一的属性值提取接口\n     * <p>\n     * 支持的提取策略（按优先级顺序）：\n     * 1. 直接值：如果 params 不是 Map，直接返回该值（适用于简单消息）\n     * 2. 标识符字段：从 params[identifier] 获取\n     * 3. properties 结构：从 params.properties[identifier] 获取（标准属性上报）\n     * 4. data 结构：从 params.data[identifier] 获取\n     * 5. value 字段：从 params.value 获取（单值消息）\n     * 6. 单值 Map：如果 Map 只包含 identifier 和一个值，返回该值\n     *\n     * @param message    设备消息\n     * @param identifier 属性标识符\n     * @return 属性值，如果未找到则返回 null\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static Object extractPropertyValue(IotDeviceMessage message, String identifier) {\n        Object params = message.getParams();\n        if (params == null) {\n            return null;\n        }\n\n        // 策略 1：如果 params 不是 Map，直接返回该值（适用于简单的单属性消息）\n        if (!(params instanceof Map)) {\n            return params;\n        }\n\n        // 策略 2：直接通过标识符获取属性值\n        Map<String, Object> paramsMap = (Map<String, Object>) params;\n        Object directValue = paramsMap.get(identifier);\n        if (directValue != null) {\n            return directValue;\n        }\n\n        // 策略 3：从 properties 字段中获取（适用于标准属性上报消息）\n        Object properties = paramsMap.get(\"properties\");\n        if (properties instanceof Map) {\n            Map<String, Object> propertiesMap = (Map<String, Object>) properties;\n            Object propertyValue = propertiesMap.get(identifier);\n            if (propertyValue != null) {\n                return propertyValue;\n            }\n        }\n\n        // 策略 4：从 data 字段中获取（适用于某些消息格式）\n        Object data = paramsMap.get(\"data\");\n        if (data instanceof Map) {\n            Map<String, Object> dataMap = (Map<String, Object>) data;\n            Object dataValue = dataMap.get(identifier);\n            if (dataValue != null) {\n                return dataValue;\n            }\n        }\n\n        // 策略 5：从 value 字段中获取（适用于单值消息）\n        Object value = paramsMap.get(\"value\");\n        if (value != null) {\n            return value;\n        }\n\n        // 策略 6：如果 Map 只有两个字段且包含 identifier，返回另一个字段的值\n        if (paramsMap.size() == 2 && paramsMap.containsKey(\"identifier\")) {\n            for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {\n                if (!\"identifier\".equals(entry.getKey())) {\n                    return entry.getValue();\n                }\n            }\n        }\n\n        // 未找到对应的属性值\n        return null;\n    }\n\n    /**\n     * 从服务调用消息中提取输入参数\n     * <p>\n     * 服务调用消息的 params 结构通常为：\n     * {\n     *     \"identifier\": \"serviceIdentifier\",\n     *     \"inputData\": { ... } 或 \"inputParams\": { ... }\n     * }\n     *\n     * @param message 设备消息\n     * @return 输入参数 Map，如果未找到则返回 null\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static Map<String, Object> extractServiceInputParams(IotDeviceMessage message) {\n        // 1. 参数校验\n        Object params = message.getParams();\n        if (params == null) {\n            return null;\n        }\n        if (!(params instanceof Map)) {\n            return null;\n        }\n        Map<String, Object> paramsMap = (Map<String, Object>) params;\n\n        // 尝试从 inputData 字段获取\n        Object inputData = paramsMap.get(\"inputData\");\n        if (inputData instanceof Map) {\n            return (Map<String, Object>) inputData;\n        }\n        // 尝试从 inputParams 字段获取\n        Object inputParams = paramsMap.get(\"inputParams\");\n        if (inputParams instanceof Map) {\n            return (Map<String, Object>) inputParams;\n        }\n        return null;\n    }\n\n    // ========== Topic 相关 ==========\n\n    public static String buildMessageBusGatewayDeviceMessageTopic(String serverId) {\n        return String.format(IotDeviceMessage.MESSAGE_BUS_GATEWAY_DEVICE_MESSAGE_TOPIC, serverId);\n    }\n\n    /**\n     * 生成服务器编号\n     *\n     * @param serverPort 服务器端口\n     * @return 服务器编号\n     */\n    public static String generateServerId(Integer serverPort) {\n        String serverId = String.format(\"%s.%d\", SystemUtil.getHostInfo().getAddress(), serverPort);\n        // 避免一些场景无法使用 . 符号，例如说 RocketMQ Topic\n        return serverId.replaceAll(\"\\\\.\", \"_\");\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotProductAuthUtils.java",
    "content": "package cn.iocoder.yudao.module.iot.core.util;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport cn.hutool.crypto.digest.HmacAlgorithm;\n\n/**\n * IoT 产品【动态注册】认证工具类\n * <p>\n * 用于一型一密场景，使用 productSecret 生成签名\n *\n * @author 芋道源码\n */\npublic class IotProductAuthUtils {\n\n    /**\n     * 生成设备动态注册签名\n     *\n     * @param productKey    产品标识\n     * @param deviceName    设备名称\n     * @param productSecret 产品密钥\n     * @return 签名\n     */\n    public static String buildSign(String productKey, String deviceName, String productSecret) {\n        String content = buildContent(productKey, deviceName);\n        return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.utf8Bytes(productSecret))\n                .digestHex(content);\n    }\n\n    /**\n     * 验证设备动态注册签名\n     *\n     * @param productKey    产品标识\n     * @param deviceName    设备名称\n     * @param productSecret 产品密钥\n     * @param sign          待验证的签名\n     * @return 是否验证通过\n     */\n    public static boolean verifySign(String productKey, String deviceName, String productSecret, String sign) {\n        String expectedSign = buildSign(productKey, deviceName, productSecret);\n        return expectedSign.equals(sign);\n    }\n\n    /**\n     * 构建签名内容\n     *\n     * @param productKey 产品标识\n     * @param deviceName 设备名称\n     * @return 签名内容\n     */\n    private static String buildContent(String productKey, String deviceName) {\n        return \"deviceName\" + deviceName + \"productKey\" + productKey;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "cn.iocoder.yudao.module.iot.core.messagebus.config.IotMessageBusAutoConfiguration"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/messagebus/core/TestMessage.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.core;\n\nimport lombok.Data;\n\n@Data\npublic class TestMessage {\n\n    private String nickname;\n\n    private Integer age;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/messagebus/core/local/LocalIotMessageBusIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.core.local;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.config.IotMessageBusAutoConfiguration;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.TestPropertySource;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * {@link IotLocalMessageBus} 集成测试\n *\n * @author 芋道源码\n */\n@SpringBootTest(classes = LocalIotMessageBusIntegrationTest.class)\n@Import(IotMessageBusAutoConfiguration.class)\n@TestPropertySource(properties = {\n    \"yudao.iot.message-bus.type=local\"\n})\n@Slf4j\npublic class LocalIotMessageBusIntegrationTest {\n\n    @Resource\n    private IotMessageBus messageBus;\n\n    /**\n     * 1 topic 2 subscriber\n     */\n    @Test\n    public void testSendMessageWithTwoSubscribers() throws InterruptedException {\n        // 准备\n        String topic = \"test-topic\";\n        String testMessage = \"Hello IoT Message Bus!\";\n        // 用于等待消息处理完成\n        CountDownLatch latch = new CountDownLatch(2);\n        // 用于记录接收到的消息\n        AtomicInteger subscriber1Count = new AtomicInteger(0);\n        AtomicInteger subscriber2Count = new AtomicInteger(0);\n\n        // 创建第一个订阅者\n        IotMessageSubscriber<String> subscriber1 = new IotMessageSubscriber<String>() {\n\n            @Override\n            public String getTopic() {\n                return topic;\n            }\n\n            @Override\n            public String getGroup() {\n                return \"group1\";\n            }\n\n            @Override\n            public void onMessage(String message) {\n                log.info(\"[订阅者1] 收到消息 - Topic: {}, Message: {}\", getTopic(), message);\n                subscriber1Count.incrementAndGet();\n                assertEquals(testMessage, message);\n                latch.countDown();\n            }\n\n        };\n        // 创建第二个订阅者\n        IotMessageSubscriber<String> subscriber2 = new IotMessageSubscriber<String>() {\n\n            @Override\n            public String getTopic() {\n                return topic;\n            }\n\n            @Override\n            public String getGroup() {\n                return \"group2\";\n            }\n\n            @Override\n            public void onMessage(String message) {\n                log.info(\"[订阅者2] 收到消息 - Topic: {}, Message: {}\", getTopic(), message);\n                subscriber2Count.incrementAndGet();\n                assertEquals(testMessage, message);\n                latch.countDown();\n            }\n\n        };\n        // 注册订阅者\n        messageBus.register(subscriber1);\n        messageBus.register(subscriber2);\n\n        // 发送消息\n        log.info(\"[测试] 发送消息 - Topic: {}, Message: {}\", topic, testMessage);\n        messageBus.post(topic, testMessage);\n        // 等待消息处理完成（最多等待 10 秒）\n        boolean completed = latch.await(10, TimeUnit.SECONDS);\n\n        // 验证结果\n        assertTrue(completed, \"消息处理超时\");\n        assertEquals(1, subscriber1Count.get(), \"订阅者 1 应该收到 1 条消息\");\n        assertEquals(1, subscriber2Count.get(), \"订阅者 2 应该收到 1 条消息\");\n        log.info(\"[测试] 测试完成 - 订阅者 1 收到{}条消息，订阅者 2 收到{}条消息\", subscriber1Count.get(), subscriber2Count.get());\n    }\n\n    /**\n     * 2 topic 2 subscriber\n     */\n    @Test\n    public void testMultipleTopics() throws InterruptedException {\n        // 准备\n        String topic1 = \"device-status\";\n        String topic2 = \"device-data\";\n        String message1 = \"设备在线\";\n        String message2 = \"温度:25°C\";\n        CountDownLatch latch = new CountDownLatch(2);\n\n        // 创建订阅者 1 - 只订阅设备状态\n        IotMessageSubscriber<String> statusSubscriber = new IotMessageSubscriber<String>() {\n\n            @Override\n            public String getTopic() {\n                return topic1;\n            }\n\n            @Override\n            public String getGroup() {\n                return \"status-group\";\n            }\n\n            @Override\n            public void onMessage(String message) {\n                log.info(\"[状态订阅者] 收到消息 - Topic: {}, Message: {}\", getTopic(), message);\n                assertEquals(message1, message);\n                latch.countDown();\n            }\n\n        };\n        // 创建订阅者 2 - 只订阅设备数据\n        IotMessageSubscriber<String> dataSubscriber = new IotMessageSubscriber<String>() {\n\n            @Override\n            public String getTopic() {\n                return topic2;\n            }\n\n            @Override\n            public String getGroup() {\n                return \"data-group\";\n            }\n\n            @Override\n            public void onMessage(String message) {\n                log.info(\"[数据订阅者] 收到消息 - Topic: {}, Message: {}\", getTopic(), message);\n                assertEquals(message2, message);\n                latch.countDown();\n            }\n\n        };\n        // 注册订阅者到不同主题\n        messageBus.register(statusSubscriber);\n        messageBus.register(dataSubscriber);\n\n        // 发送消息到不同主题\n        messageBus.post(topic1, message1);\n        messageBus.post(topic2, message2);\n        // 等待消息处理完成\n        boolean completed = latch.await(10, TimeUnit.SECONDS);\n        assertTrue(completed, \"消息处理超时\");\n        log.info(\"[测试] 多主题测试完成\");\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/messagebus/core/rocketmq/RocketMQIotMessageBusTest.java",
    "content": "package cn.iocoder.yudao.module.iot.core.messagebus.core.rocketmq;\n\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.module.iot.core.messagebus.config.IotMessageBusAutoConfiguration;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.TestMessage;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.TestPropertySource;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * {@link IotRocketMQMessageBus} 集成测试\n *\n * @author 芋道源码\n */\n@SpringBootTest(classes = RocketMQIotMessageBusTest.class)\n@Import({RocketMQAutoConfiguration.class, IotMessageBusAutoConfiguration.class})\n@TestPropertySource(properties = {\n    \"yudao.iot.message-bus.type=rocketmq\",\n    \"rocketmq.name-server=127.0.0.1:9876\",\n    \"rocketmq.producer.group=test-rocketmq-group\",\n    \"rocketmq.producer.send-message-timeout=10000\"\n})\n@Slf4j\n@Disabled\npublic class RocketMQIotMessageBusTest {\n\n    @Resource\n    private IotMessageBus messageBus;\n\n    /**\n     * 1 topic 1 subscriber（string）\n     */\n    @Test\n    public void testSendMessageWithOneSubscriber() throws InterruptedException {\n        // 准备\n        String topic = \"test-topic-\" + IdUtil.simpleUUID();\n//        String topic = \"test-topic-pojo\";\n        String testMessage = \"Hello IoT Message Bus!\";\n        // 用于等待消息处理完成\n        CountDownLatch latch = new CountDownLatch(1);\n        // 用于记录接收到的消息\n        AtomicInteger subscriberCount = new AtomicInteger(0);\n        AtomicReference<String> subscriberMessageRef = new AtomicReference<>();\n\n        // 发送消息（需要提前发，保证 RocketMQ 路由的创建）\n        log.info(\"[测试] 发送消息 - Topic: {}, Message: {}\", topic, testMessage);\n        messageBus.post(topic, testMessage);\n\n        // 创建订阅者\n        IotMessageSubscriber<String> subscriber1 = new IotMessageSubscriber<String>() {\n\n            @Override\n            public String getTopic() {\n                return topic;\n            }\n\n            @Override\n            public String getGroup() {\n                return \"test-topic-\" + IdUtil.simpleUUID() + \"-consumer\";\n//                return \"test-topic-consumer-01\";\n            }\n\n            @Override\n            public void onMessage(String message) {\n                log.info(\"[订阅者] 收到消息 - Topic: {}, Message: {}\", getTopic(), message);\n                subscriberCount.incrementAndGet();\n                subscriberMessageRef.set(message);\n                assertEquals(testMessage, message);\n                latch.countDown();\n            }\n\n        };\n        // 注册订阅者\n        messageBus.register(subscriber1);\n\n        // 等待消息处理完成（最多等待 5 秒）\n        boolean completed = latch.await(10, TimeUnit.SECONDS);\n\n        // 验证结果\n        assertTrue(completed, \"消息处理超时\");\n        assertEquals(1, subscriberCount.get(), \"订阅者应该收到 1 条消息\");\n        log.info(\"[测试] 测试完成 - 订阅者收到{}条消息\", subscriberCount.get());\n        assertEquals(testMessage, subscriberMessageRef.get(), \"接收到的消息内容不匹配\");\n    }\n\n    /**\n     * 1 topic 2 subscriber（pojo）\n     */\n    @Test\n    public void testSendMessageWithTwoSubscribers() throws InterruptedException {\n        // 准备\n        String topic = \"test-topic-\" + IdUtil.simpleUUID();\n//        String topic = \"test-topic-pojo\";\n        TestMessage testMessage = new TestMessage().setNickname(\"yunai\").setAge(18);\n        // 用于等待消息处理完成\n        CountDownLatch latch = new CountDownLatch(2);\n        // 用于记录接收到的消息\n        AtomicInteger subscriber1Count = new AtomicInteger(0);\n        AtomicReference<TestMessage> subscriber1MessageRef = new AtomicReference<>();\n        AtomicInteger subscriber2Count = new AtomicInteger(0);\n        AtomicReference<TestMessage> subscriber2MessageRef = new AtomicReference<>();\n\n        // 发送消息（需要提前发，保证 RocketMQ 路由的创建）\n        log.info(\"[测试] 发送消息 - Topic: {}, Message: {}\", topic, testMessage);\n        messageBus.post(topic, testMessage);\n\n        // 创建第一个订阅者\n        IotMessageSubscriber<TestMessage> subscriber1 = new IotMessageSubscriber<TestMessage>() {\n\n            @Override\n            public String getTopic() {\n                return topic;\n            }\n\n            @Override\n            public String getGroup() {\n                return \"test-topic-\" + IdUtil.simpleUUID() + \"-consumer\";\n//                return \"test-topic-consumer-01\";\n            }\n\n            @Override\n            public void onMessage(TestMessage message) {\n                log.info(\"[订阅者1] 收到消息 - Topic: {}, Message: {}\", getTopic(), message);\n                subscriber1Count.incrementAndGet();\n                subscriber1MessageRef.set(message);\n                assertEquals(testMessage, message);\n                latch.countDown();\n            }\n\n        };\n        // 创建第二个订阅者\n        IotMessageSubscriber<TestMessage> subscriber2 = new IotMessageSubscriber<TestMessage>() {\n\n            @Override\n            public String getTopic() {\n                return topic;\n            }\n\n            @Override\n            public String getGroup() {\n                return \"test-topic-\" + IdUtil.simpleUUID() + \"-consumer\";\n//                return \"test-topic-consumer-02\";\n            }\n\n            @Override\n            public void onMessage(TestMessage message) {\n                log.info(\"[订阅者2] 收到消息 - Topic: {}, Message: {}\", getTopic(), message);\n                subscriber2Count.incrementAndGet();\n                subscriber2MessageRef.set(message);\n                assertEquals(testMessage, message);\n                latch.countDown();\n            }\n\n        };\n        // 注册订阅者\n        messageBus.register(subscriber1);\n        messageBus.register(subscriber2);\n\n        // 等待消息处理完成（最多等待 5 秒）\n        boolean completed = latch.await(10, TimeUnit.SECONDS);\n\n        // 验证结果\n        assertTrue(completed, \"消息处理超时\");\n        assertEquals(1, subscriber1Count.get(), \"订阅者 1 应该收到 1 条消息\");\n        assertEquals(1, subscriber2Count.get(), \"订阅者 2 应该收到 1 条消息\");\n        log.info(\"[测试] 测试完成 - 订阅者 1 收到{}条消息，订阅者2收到{}条消息\", subscriber1Count.get(), subscriber2Count.get());\n        assertEquals(testMessage, subscriber1MessageRef.get(), \"接收到的消息内容不匹配\");\n        assertEquals(testMessage, subscriber2MessageRef.get(), \"接收到的消息内容不匹配\");\n    }\n\n    /**\n     * 2 topic 2 subscriber\n     */\n    @Test\n    public void testMultipleTopics() throws InterruptedException {\n        // 准备\n        String topic1 = \"device-status-\" + IdUtil.simpleUUID();\n        String topic2 = \"device-data-\" + IdUtil.simpleUUID();\n        String message1 = \"设备在线\";\n        String message2 = \"温度:25°C\";\n        CountDownLatch latch = new CountDownLatch(2);\n        AtomicInteger subscriber1Count = new AtomicInteger(0);\n        AtomicReference<String> subscriber1MessageRef = new AtomicReference<>();\n        AtomicInteger subscriber2Count = new AtomicInteger(0);\n        AtomicReference<String> subscriber2MessageRef = new AtomicReference<>();\n\n\n        // 发送消息到不同主题（需要提前发，保证 RocketMQ 路由的创建）\n        log.info(\"[测试] 发送消息 - Topic1: {}, Message1: {}\", topic1, message1);\n        messageBus.post(topic1, message1);\n        log.info(\"[测试] 发送消息 - Topic2: {}, Message2: {}\", topic2, message2);\n        messageBus.post(topic2, message2);\n\n        // 创建订阅者 1 - 只订阅设备状态\n        IotMessageSubscriber<String> statusSubscriber = new IotMessageSubscriber<String>() {\n\n            @Override\n            public String getTopic() {\n                return topic1;\n            }\n\n            @Override\n            public String getGroup() {\n                return \"status-group-\" + IdUtil.simpleUUID();\n            }\n\n            @Override\n            public void onMessage(String message) {\n                log.info(\"[状态订阅者] 收到消息 - Topic: {}, Message: {}\", getTopic(), message);\n                subscriber1Count.incrementAndGet();\n                subscriber1MessageRef.set(message);\n                assertEquals(message1, message);\n                latch.countDown();\n            }\n\n        };\n        // 创建订阅者 2 - 只订阅设备数据\n        IotMessageSubscriber<String> dataSubscriber = new IotMessageSubscriber<String>() {\n\n            @Override\n            public String getTopic() {\n                return topic2;\n            }\n\n            @Override\n            public String getGroup() {\n                return \"data-group-\" + IdUtil.simpleUUID();\n            }\n\n            @Override\n            public void onMessage(String message) {\n                log.info(\"[数据订阅者] 收到消息 - Topic: {}, Message: {}\", getTopic(), message);\n                subscriber2Count.incrementAndGet();\n                subscriber2MessageRef.set(message);\n                assertEquals(message2, message);\n                latch.countDown();\n            }\n\n        };\n        // 注册订阅者到不同主题\n        messageBus.register(statusSubscriber);\n        messageBus.register(dataSubscriber);\n\n        // 等待消息处理完成\n        boolean completed = latch.await(10, TimeUnit.SECONDS);\n\n        // 验证结果\n        assertTrue(completed, \"消息处理超时\");\n        assertEquals(1, subscriber1Count.get(), \"状态订阅者应该收到 1 条消息\");\n        assertEquals(message1, subscriber1MessageRef.get(), \"状态订阅者接收到的消息内容不匹配\");\n        assertEquals(1, subscriber2Count.get(), \"数据订阅者应该收到 1 条消息\");\n        assertEquals(message2, subscriber2MessageRef.get(), \"数据订阅者接收到的消息内容不匹配\");\n        log.info(\"[测试] 多主题测试完成 - 状态订阅者收到{}条消息，数据订阅者收到{}条消息\", subscriber1Count.get(), subscriber2Count.get());\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtilsTest.java",
    "content": "package cn.iocoder.yudao.module.iot.core.util;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotDeviceMessageUtils} 的单元测试\n *\n * @author HUIHUI\n */\npublic class IotDeviceMessageUtilsTest {\n\n    @Test\n    public void testExtractPropertyValue_directValue() {\n        // 测试直接值（非 Map）\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setParams(25.5);\n\n        Object result = IotDeviceMessageUtils.extractPropertyValue(message, \"temperature\");\n        assertEquals(25.5, result);\n    }\n\n    @Test\n    public void testExtractPropertyValue_directIdentifier() {\n        // 测试直接通过标识符获取\n        IotDeviceMessage message = new IotDeviceMessage();\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"temperature\", 25.5);\n        message.setParams(params);\n\n        Object result = IotDeviceMessageUtils.extractPropertyValue(message, \"temperature\");\n        assertEquals(25.5, result);\n    }\n\n    @Test\n    public void testExtractPropertyValue_propertiesStructure() {\n        // 测试 properties 结构\n        IotDeviceMessage message = new IotDeviceMessage();\n        Map<String, Object> properties = new HashMap<>();\n        properties.put(\"temperature\", 25.5);\n        properties.put(\"humidity\", 60);\n\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"properties\", properties);\n        message.setParams(params);\n\n        Object result = IotDeviceMessageUtils.extractPropertyValue(message, \"temperature\");\n        assertEquals(25.5, result);\n    }\n\n    @Test\n    public void testExtractPropertyValue_dataStructure() {\n        // 测试 data 结构\n        IotDeviceMessage message = new IotDeviceMessage();\n        Map<String, Object> data = new HashMap<>();\n        data.put(\"temperature\", 25.5);\n\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"data\", data);\n        message.setParams(params);\n\n        Object result = IotDeviceMessageUtils.extractPropertyValue(message, \"temperature\");\n        assertEquals(25.5, result);\n    }\n\n    @Test\n    public void testExtractPropertyValue_valueField() {\n        // 测试 value 字段\n        IotDeviceMessage message = new IotDeviceMessage();\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"identifier\", \"temperature\");\n        params.put(\"value\", 25.5);\n        message.setParams(params);\n\n        Object result = IotDeviceMessageUtils.extractPropertyValue(message, \"temperature\");\n        assertEquals(25.5, result);\n    }\n\n    @Test\n    public void testExtractPropertyValue_singleValueMap() {\n        // 测试单值 Map（包含 identifier 和一个值）\n        IotDeviceMessage message = new IotDeviceMessage();\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"identifier\", \"temperature\");\n        params.put(\"actualValue\", 25.5);\n        message.setParams(params);\n\n        Object result = IotDeviceMessageUtils.extractPropertyValue(message, \"temperature\");\n        assertEquals(25.5, result);\n    }\n\n    @Test\n    public void testExtractPropertyValue_notFound() {\n        // 测试未找到属性值\n        IotDeviceMessage message = new IotDeviceMessage();\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"humidity\", 60);\n        message.setParams(params);\n\n        Object result = IotDeviceMessageUtils.extractPropertyValue(message, \"temperature\");\n        assertNull(result);\n    }\n\n    @Test\n    public void testExtractPropertyValue_nullParams() {\n        // 测试 params 为 null\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setParams(null);\n\n        Object result = IotDeviceMessageUtils.extractPropertyValue(message, \"temperature\");\n        assertNull(result);\n    }\n\n    @Test\n    public void testExtractPropertyValue_priorityOrder() {\n        // 测试优先级顺序：直接标识符 > properties > data > value\n        IotDeviceMessage message = new IotDeviceMessage();\n\n        Map<String, Object> properties = new HashMap<>();\n        properties.put(\"temperature\", 20.0);\n\n        Map<String, Object> data = new HashMap<>();\n        data.put(\"temperature\", 30.0);\n\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"temperature\", 25.5); // 最高优先级\n        params.put(\"properties\", properties);\n        params.put(\"data\", data);\n        params.put(\"value\", 40.0);\n        message.setParams(params);\n\n        Object result = IotDeviceMessageUtils.extractPropertyValue(message, \"temperature\");\n        assertEquals(25.5, result); // 应该返回直接标识符的值\n    }\n\n    // ========== notContainsIdentifier 测试 ==========\n\n    /**\n     * 测试 notContainsIdentifier 与 containsIdentifier 的互补性\n     * **Property 2: notContainsIdentifier 与 containsIdentifier 互补性**\n     * **Validates: Requirements 4.1**\n     */\n    @Test\n    public void testNotContainsIdentifier_complementary_whenContains() {\n        // 准备参数：消息包含指定标识符\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"temperature\", 25);\n        message.setParams(params);\n        String identifier = \"temperature\";\n\n        // 调用 & 断言：验证互补性\n        boolean containsResult = IotDeviceMessageUtils.containsIdentifier(message, identifier);\n        boolean notContainsResult = IotDeviceMessageUtils.notContainsIdentifier(message, identifier);\n        assertTrue(containsResult);\n        assertFalse(notContainsResult);\n        assertEquals(!containsResult, notContainsResult);\n    }\n\n    /**\n     * 测试 notContainsIdentifier 与 containsIdentifier 的互补性\n     * **Property 2: notContainsIdentifier 与 containsIdentifier 互补性**\n     * **Validates: Requirements 4.1**\n     */\n    @Test\n    public void testNotContainsIdentifier_complementary_whenNotContains() {\n        // 准备参数：消息不包含指定标识符\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"temperature\", 25);\n        message.setParams(params);\n        String identifier = \"humidity\";\n\n        // 调用 & 断言：验证互补性\n        boolean containsResult = IotDeviceMessageUtils.containsIdentifier(message, identifier);\n        boolean notContainsResult = IotDeviceMessageUtils.notContainsIdentifier(message, identifier);\n        assertFalse(containsResult);\n        assertTrue(notContainsResult);\n        assertEquals(!containsResult, notContainsResult);\n    }\n\n    /**\n     * 测试 notContainsIdentifier 与 containsIdentifier 的互补性 - 空参数场景\n     * **Property 2: notContainsIdentifier 与 containsIdentifier 互补性**\n     * **Validates: Requirements 4.1**\n     */\n    @Test\n    public void testNotContainsIdentifier_complementary_nullParams() {\n        // 准备参数：params 为 null\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setParams(null);\n        String identifier = \"temperature\";\n\n        // 调用 & 断言：验证互补性\n        boolean containsResult = IotDeviceMessageUtils.containsIdentifier(message, identifier);\n        boolean notContainsResult = IotDeviceMessageUtils.notContainsIdentifier(message, identifier);\n        assertFalse(containsResult);\n        assertTrue(notContainsResult);\n        assertEquals(!containsResult, notContainsResult);\n    }\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao-module-iot</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>jar</packaging>\n    <artifactId>yudao-module-iot-gateway</artifactId>\n\n    <name>${project.artifactId}</name>\n    <description>\n        iot 模块下，设备网关：\n        ① 功能一：接收来自设备的消息，并进行解码（decode）后，发送到消息网关，提供给 iot-biz 进行处理\n        ② 功能二：接收来自消息网关的消息（由 iot-biz 发送），并进行编码（encode）后，发送给设备\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-iot-core</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n\n        <!-- 消息队列相关 -->\n        <dependency>\n            <groupId>org.apache.rocketmq</groupId>\n            <artifactId>rocketmq-spring-boot-starter</artifactId>\n            <!-- TODO @芋艿：消息队列，后续可能去掉，默认不使用 rocketmq -->\n            <optional>true</optional>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>io.vertx</groupId>\n            <artifactId>vertx-web</artifactId>\n        </dependency>\n\n        <!-- MQTT 相关 -->\n        <dependency>\n            <groupId>io.vertx</groupId>\n            <artifactId>vertx-mqtt</artifactId>\n        </dependency>\n\n        <!-- Modbus 相关 -->\n        <dependency>\n            <groupId>com.ghgande</groupId>\n            <artifactId>j2mod</artifactId>\n        </dependency>\n\n        <!-- CoAP 相关 - Eclipse Californium -->\n        <dependency>\n            <groupId>org.eclipse.californium</groupId>\n            <artifactId>californium-core</artifactId>\n        </dependency>\n\n        <!-- 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/IotGatewayServerApplication.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class IotGatewayServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(IotGatewayServerApplication.class, args);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.config;\n\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * IoT 网关配置类\n *\n * @author 芋道源码\n */\n@Configuration\n@EnableConfigurationProperties(IotGatewayProperties.class)\npublic class IotGatewayConfiguration {\n\n    @Bean\n    public IotMessageSerializerManager iotMessageSerializerManager() {\n        return new IotMessageSerializerManager();\n    }\n\n    @Bean\n    public IotProtocolManager iotProtocolManager(IotGatewayProperties gatewayProperties) {\n        return new IotProtocolManager(gatewayProperties);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.config;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.IotModbusTcpClientConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.IotModbusTcpServerConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketConfig;\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.time.Duration;\nimport java.util.List;\n\n@ConfigurationProperties(prefix = \"yudao.iot.gateway\")\n@Validated\n@Data\npublic class IotGatewayProperties {\n\n    /**\n     * 设备 RPC 服务配置\n     */\n    private RpcProperties rpc;\n    /**\n     * Token 配置\n     */\n    private TokenProperties token;\n\n    /**\n     * 协议实例列表\n     */\n    private List<ProtocolProperties> protocols;\n\n    @Data\n    public static class RpcProperties {\n\n        /**\n         * 主程序 API 地址\n         */\n        @NotEmpty(message = \"主程序 API 地址不能为空\")\n        private String url;\n        /**\n         * 连接超时时间\n         */\n        @NotNull(message = \"连接超时时间不能为空\")\n        private Duration connectTimeout;\n        /**\n         * 读取超时时间\n         */\n        @NotNull(message = \"读取超时时间不能为空\")\n        private Duration readTimeout;\n\n    }\n\n    @Data\n    public static class TokenProperties {\n\n        /**\n         * 密钥\n         */\n        @NotEmpty(message = \"密钥不能为空\")\n        private String secret;\n        /**\n         * 令牌有效期\n         */\n        @NotNull(message = \"令牌有效期不能为空\")\n        private Duration expiration;\n\n    }\n\n    /**\n     * 协议实例配置\n     */\n    @Data\n    public static class ProtocolProperties {\n\n        /**\n         * 协议实例 ID，如 \"http-alink\"、\"tcp-binary\"\n         */\n        @NotEmpty(message = \"协议实例 ID 不能为空\")\n        private String id;\n        /**\n         * 是否启用\n         */\n        @NotNull(message = \"是否启用不能为空\")\n        private Boolean enabled = true;\n        /**\n         * 协议类型\n         *\n         * @see cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum\n         */\n        @NotEmpty(message = \"协议类型不能为空\")\n        private String protocol;\n        /**\n         * 服务端口\n         * <p>\n         * 不同协议含义不同：\n         * 1. TCP/UDP/HTTP/WebSocket/MQTT/CoAP：对应网关自身监听的服务端口\n         * 2. EMQX：对应网关提供给 EMQX 回调的 HTTP Hook 端口（/mqtt/auth、/mqtt/acl、/mqtt/event）\n         */\n        @NotNull(message = \"服务端口不能为空\")\n        private Integer port;\n        /**\n         * 序列化类型（可选）\n         *\n         * @see cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum\n         *\n         * 为什么是可选的呢？\n         * 1. {@link IotProtocolTypeEnum#HTTP}、{@link IotProtocolTypeEnum#COAP} 协议，目前强制是 JSON 格式\n         * 2. {@link IotProtocolTypeEnum#EMQX} 协议，目前支持根据产品（设备）配置的序列化类型来解析\n         */\n        private String serialize;\n\n        // ========== SSL 配置 ==========\n\n        /**\n         * SSL 配置（可选，配置文件中不配置则为 null）\n         */\n        @Valid\n        private SslConfig ssl;\n\n        // ========== 各协议配置 ==========\n\n        /**\n         * HTTP 协议配置\n         */\n        @Valid\n        private IotHttpConfig http;\n        /**\n         * WebSocket 协议配置\n         */\n        @Valid\n        private IotWebSocketConfig websocket;\n\n        /**\n         * TCP 协议配置\n         */\n        @Valid\n        private IotTcpConfig tcp;\n        /**\n         * UDP 协议配置\n         */\n        @Valid\n        private IotUdpConfig udp;\n\n        /**\n         * CoAP 协议配置\n         */\n        @Valid\n        private IotCoapConfig coap;\n\n        /**\n         * MQTT 协议配置\n         */\n        @Valid\n        private IotMqttConfig mqtt;\n        /**\n         * EMQX 协议配置\n         */\n        @Valid\n        private IotEmqxConfig emqx;\n\n        /**\n         * Modbus TCP Client 协议配置\n         */\n        @Valid\n        private IotModbusTcpClientConfig modbusTcpClient;\n\n        /**\n         * Modbus TCP Server 协议配置\n         */\n        @Valid\n        private IotModbusTcpServerConfig modbusTcpServer;\n\n    }\n\n    /**\n     * SSL 配置\n     */\n    @Data\n    public static class SslConfig {\n\n        /**\n         * 是否启用 SSL\n         */\n        @NotNull(message = \"是否启用 SSL 不能为空\")\n        private Boolean ssl = false;\n\n        /**\n         * SSL 证书路径\n         */\n        @NotEmpty(message = \"SSL 证书路径不能为空\")\n        private String sslCertPath;\n\n        /**\n         * SSL 私钥路径\n         */\n        @NotEmpty(message = \"SSL 私钥路径不能为空\")\n        private String sslKeyPath;\n\n        /**\n         * 密钥库（KeyStore）路径\n         * <p>\n         * 包含客户端自己的证书和私钥，用于向服务端证明身份（双向认证）\n         */\n        private String keyStorePath;\n        /**\n         * 密钥库密码\n         */\n        private String keyStorePassword;\n\n        /**\n         * 信任库（TrustStore）路径\n         * <p>\n         * 包含服务端信任的 CA 证书，用于验证服务端的身份\n         */\n        private String trustStorePath;\n        /**\n         * 信任库密码\n         */\n        private String trustStorePassword;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * iot gateway 错误码枚举类\n * <p>\n * iot 系统，使用 1-051-000-000 段\n */\npublic interface ErrorCodeConstants {\n\n    // ========== 设备认证 1-050-001-000 ============\n    ErrorCode DEVICE_AUTH_FAIL = new ErrorCode(1_051_001_000, \"设备鉴权失败\"); // 对应阿里云 20000\n    ErrorCode DEVICE_TOKEN_EXPIRED = new ErrorCode(1_051_001_002, \"token 失效。需重新调用 auth 进行鉴权，获取token\"); // 对应阿里云 20001\n\n    // ========== 设备信息 1-050-002-000 ============\n    ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_051_002_001, \"设备({}/{}) 不存在\");\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/AbstractIotProtocolDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 协议下行消息订阅者抽象类\n *\n * 负责接收来自消息总线的下行消息，并委托给子类进行业务处理\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Slf4j\npublic abstract class AbstractIotProtocolDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {\n\n    private final IotProtocol protocol;\n\n    private final IotMessageBus messageBus;\n\n    @Override\n    public String getTopic() {\n        return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());\n    }\n\n    /**\n     * 保证点对点消费，需要保证独立的 Group，所以使用 Topic 作为 Group\n     */\n    @Override\n    public String getGroup() {\n        return getTopic();\n    }\n\n    @Override\n    public void start() {\n        messageBus.register(this);\n        log.info(\"[start][{} 下行消息订阅成功，Topic：{}]\", protocol.getType().name(), getTopic());\n    }\n\n    @Override\n    public void stop() {\n        messageBus.unregister(this);\n        log.info(\"[stop][{} 下行消息订阅已停止，Topic：{}]\", protocol.getType().name(), getTopic());\n    }\n\n    @Override\n    public void onMessage(IotDeviceMessage message) {\n        log.debug(\"[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]\",\n                message.getId(), message.getMethod(), message.getDeviceId());\n        try {\n            // 1. 校验\n            String method = message.getMethod();\n            if (StrUtil.isBlank(method)) {\n                log.warn(\"[onMessage][消息方法为空, messageId: {}, deviceId: {}]\",\n                        message.getId(), message.getDeviceId());\n                return;\n            }\n\n            // 2. 处理下行消息\n            handleMessage(message);\n        } catch (Exception e) {\n            log.error(\"[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]\",\n                    message.getId(), message.getMethod(), message.getDeviceId(), e);\n        }\n    }\n\n    /**\n     * 处理下行消息\n     *\n     * @param message 下行消息\n     */\n    protected abstract void handleMessage(IotDeviceMessage message);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\n\n/**\n * IoT 协议接口\n *\n * 定义传输层协议的生命周期管理\n *\n * @author 芋道源码\n */\npublic interface IotProtocol {\n\n    /**\n     * 获取协议实例 ID\n     *\n     * @return 协议实例 ID，如 \"http-alink\"、\"tcp-binary\"\n     */\n    String getId();\n\n    /**\n     * 获取服务器 ID（用于消息追踪，全局唯一）\n     *\n     * @return 服务器 ID\n     */\n    String getServerId();\n\n    /**\n     * 获取协议类型\n     *\n     * @return 协议类型枚举\n     */\n    IotProtocolTypeEnum getType();\n\n    /**\n     * 启动协议服务\n     */\n    void start();\n\n    /**\n     * 停止协议服务\n     */\n    void stop();\n\n    /**\n     * 检查协议服务是否正在运行\n     *\n     * @return 是否正在运行\n     */\n    boolean isRunning();\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.IotModbusTcpClientProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.IotModbusTcpServerProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketProtocol;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.SmartLifecycle;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * IoT 协议管理器：负责根据配置创建和管理协议实例\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotProtocolManager implements SmartLifecycle {\n\n    private final IotGatewayProperties gatewayProperties;\n\n    /**\n     * 协议实例列表\n     */\n    private final List<IotProtocol> protocols = new ArrayList<>();\n\n    @Getter\n    private volatile boolean running = false;\n\n    public IotProtocolManager(IotGatewayProperties gatewayProperties) {\n        this.gatewayProperties = gatewayProperties;\n    }\n\n    @Override\n    public void start() {\n        if (running) {\n            return;\n        }\n        List<IotGatewayProperties.ProtocolProperties> protocolConfigs = gatewayProperties.getProtocols();\n        if (CollUtil.isEmpty(protocolConfigs)) {\n            log.info(\"[start][没有配置协议实例，跳过启动]\");\n            return;\n        }\n\n        for (IotGatewayProperties.ProtocolProperties config : protocolConfigs) {\n            if (BooleanUtil.isFalse(config.getEnabled())) {\n                log.info(\"[start][协议实例 {} 未启用，跳过]\", config.getId());\n                continue;\n            }\n            IotProtocol protocol = createProtocol(config);\n            if (protocol == null) {\n                continue;\n            }\n            protocol.start();\n            protocols.add(protocol);\n        }\n        running = true;\n        log.info(\"[start][协议管理器启动完成，共启动 {} 个协议实例]\", protocols.size());\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        for (IotProtocol protocol : protocols) {\n            try {\n                protocol.stop();\n            } catch (Exception e) {\n                log.error(\"[stop][协议实例 {} 停止失败]\", protocol.getId(), e);\n            }\n        }\n        protocols.clear();\n        running = false;\n        log.info(\"[stop][协议管理器已停止]\");\n    }\n\n    /**\n     * 创建协议实例\n     *\n     * @param config 协议实例配置\n     * @return 协议实例\n     */\n    @SuppressWarnings({\"EnhancedSwitchMigration\"})\n    private IotProtocol createProtocol(IotGatewayProperties.ProtocolProperties config) {\n        IotProtocolTypeEnum protocolType = IotProtocolTypeEnum.of(config.getProtocol());\n        if (protocolType == null) {\n            log.error(\"[createProtocol][协议实例 {} 的协议类型 {} 不存在]\", config.getId(), config.getProtocol());\n            return null;\n        }\n        switch (protocolType) {\n            case HTTP:\n                return createHttpProtocol(config);\n            case TCP:\n                return createTcpProtocol(config);\n            case UDP:\n                return createUdpProtocol(config);\n            case COAP:\n                return createCoapProtocol(config);\n            case WEBSOCKET:\n                return createWebSocketProtocol(config);\n            case MQTT:\n                return createMqttProtocol(config);\n            case EMQX:\n                return createEmqxProtocol(config);\n            case MODBUS_TCP_CLIENT:\n                return createModbusTcpClientProtocol(config);\n            case MODBUS_TCP_SERVER:\n                return createModbusTcpServerProtocol(config);\n            default:\n                throw new IllegalArgumentException(String.format(\n                        \"[createProtocol][协议实例 %s 的协议类型 %s 暂不支持]\", config.getId(), protocolType));\n        }\n    }\n\n    /**\n     * 创建 HTTP 协议实例\n     *\n     * @param config 协议实例配置\n     * @return HTTP 协议实例\n     */\n    private IotHttpProtocol createHttpProtocol(IotGatewayProperties.ProtocolProperties config) {\n        return new IotHttpProtocol(config);\n    }\n\n    /**\n     * 创建 TCP 协议实例\n     *\n     * @param config 协议实例配置\n     * @return TCP 协议实例\n     */\n    private IotTcpProtocol createTcpProtocol(IotGatewayProperties.ProtocolProperties config) {\n        return new IotTcpProtocol(config);\n    }\n\n    /**\n     * 创建 UDP 协议实例\n     *\n     * @param config 协议实例配置\n     * @return UDP 协议实例\n     */\n    private IotUdpProtocol createUdpProtocol(IotGatewayProperties.ProtocolProperties config) {\n        return new IotUdpProtocol(config);\n    }\n\n    /**\n     * 创建 CoAP 协议实例\n     *\n     * @param config 协议实例配置\n     * @return CoAP 协议实例\n     */\n    private IotCoapProtocol createCoapProtocol(IotGatewayProperties.ProtocolProperties config) {\n        return new IotCoapProtocol(config);\n    }\n\n    /**\n     * 创建 WebSocket 协议实例\n     *\n     * @param config 协议实例配置\n     * @return WebSocket 协议实例\n     */\n    private IotWebSocketProtocol createWebSocketProtocol(IotGatewayProperties.ProtocolProperties config) {\n        return new IotWebSocketProtocol(config);\n    }\n\n    /**\n     * 创建 MQTT 协议实例\n     *\n     * @param config 协议实例配置\n     * @return MQTT 协议实例\n     */\n    private IotMqttProtocol createMqttProtocol(IotGatewayProperties.ProtocolProperties config) {\n        return new IotMqttProtocol(config);\n    }\n\n    /**\n     * 创建 EMQX 协议实例\n     *\n     * @param config 协议实例配置\n     * @return EMQX 协议实例\n     */\n    private IotEmqxProtocol createEmqxProtocol(IotGatewayProperties.ProtocolProperties config) {\n        return new IotEmqxProtocol(config);\n    }\n\n    /**\n     * 创建 Modbus TCP Client 协议实例\n     *\n     * @param config 协议实例配置\n     * @return Modbus TCP Client 协议实例\n     */\n    private IotModbusTcpClientProtocol createModbusTcpClientProtocol(IotGatewayProperties.ProtocolProperties config) {\n        return new IotModbusTcpClientProtocol(config);\n    }\n\n    /**\n     * 创建 Modbus TCP Server 协议实例\n     *\n     * @param config 协议实例配置\n     * @return Modbus TCP Server 协议实例\n     */\n    private IotModbusTcpServerProtocol createModbusTcpServerProtocol(IotGatewayProperties.ProtocolProperties config) {\n        return new IotModbusTcpServerProtocol(config);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\n\n/**\n * IoT CoAP 协议配置\n *\n * @author 芋道源码\n */\n@Data\npublic class IotCoapConfig {\n\n    /**\n     * 最大消息大小（字节）\n     */\n    @NotNull(message = \"最大消息大小不能为空\")\n    @Min(value = 64, message = \"最大消息大小必须大于 64 字节\")\n    private Integer maxMessageSize = 1024;\n\n    /**\n     * ACK 超时时间（毫秒）\n     */\n    @NotNull(message = \"ACK 超时时间不能为空\")\n    @Min(value = 100, message = \"ACK 超时时间必须大于 100 毫秒\")\n    private Integer ackTimeoutMs = 2000;\n\n    /**\n     * 最大重传次数\n     */\n    @NotNull(message = \"最大重传次数不能为空\")\n    @Min(value = 0, message = \"最大重传次数必须大于等于 0\")\n    private Integer maxRetransmit = 4;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.downstream.IotCoapDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.*;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.CoapResource;\nimport org.eclipse.californium.core.CoapServer;\nimport org.eclipse.californium.core.config.CoapConfig;\nimport org.eclipse.californium.elements.config.Configuration;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * IoT CoAP 协议实现\n * <p>\n * 基于 Eclipse Californium 实现，支持：\n * 1. 认证：POST /auth\n * 2. 设备动态注册：POST /auth/register/device\n * 3. 子设备动态注册：POST /auth/register/sub-device/{productKey}/{deviceName}\n * 4. 属性上报：POST /topic/sys/{productKey}/{deviceName}/thing/property/post\n * 5. 事件上报：POST /topic/sys/{productKey}/{deviceName}/thing/event/post\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotCoapProtocol implements IotProtocol {\n\n    /**\n     * 协议配置\n     */\n    private final ProtocolProperties properties;\n    /**\n     * 服务器 ID（用于消息追踪，全局唯一）\n     */\n    @Getter\n    private final String serverId;\n\n    /**\n     * 运行状态\n     */\n    @Getter\n    private volatile boolean running = false;\n\n    /**\n     * CoAP 服务器\n     */\n    private CoapServer coapServer;\n\n    /**\n     * 下行消息订阅者\n     */\n    private IotCoapDownstreamSubscriber downstreamSubscriber;\n\n    public IotCoapProtocol(ProtocolProperties properties) {\n        IotCoapConfig coapConfig = properties.getCoap();\n        Assert.notNull(coapConfig, \"CoAP 协议配置（coap）不能为空\");\n        this.properties = properties;\n        this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());\n    }\n\n    @Override\n    public String getId() {\n        return properties.getId();\n    }\n\n    @Override\n    public IotProtocolTypeEnum getType() {\n        return IotProtocolTypeEnum.COAP;\n    }\n\n    @Override\n    public void start() {\n        if (running) {\n            log.warn(\"[start][IoT CoAP 协议 {} 已经在运行中]\", getId());\n            return;\n        }\n\n        try {\n            // 1.1 创建 CoAP 配置\n            IotCoapConfig coapConfig = properties.getCoap();\n            Configuration config = Configuration.createStandardWithoutFile();\n            config.set(CoapConfig.COAP_PORT, properties.getPort());\n            config.set(CoapConfig.MAX_MESSAGE_SIZE, coapConfig.getMaxMessageSize());\n            config.set(CoapConfig.ACK_TIMEOUT, coapConfig.getAckTimeoutMs(), TimeUnit.MILLISECONDS);\n            config.set(CoapConfig.MAX_RETRANSMIT, coapConfig.getMaxRetransmit());\n            // 1.2 创建 CoAP 服务器\n            coapServer = new CoapServer(config);\n\n            // 2.1 添加 /auth 认证资源\n            IotCoapAuthHandler authHandler = new IotCoapAuthHandler(serverId);\n            IotCoapAuthResource authResource = new IotCoapAuthResource(authHandler);\n            coapServer.add(authResource);\n            // 2.2 添加 /auth/register/device 设备动态注册资源（一型一密）\n            IotCoapRegisterHandler registerHandler = new IotCoapRegisterHandler();\n            IotCoapRegisterResource registerResource = new IotCoapRegisterResource(registerHandler);\n            // 2.3 添加 /auth/register/sub-device/{productKey}/{deviceName} 子设备动态注册资源\n            IotCoapRegisterSubHandler registerSubHandler = new IotCoapRegisterSubHandler();\n            IotCoapRegisterSubResource registerSubResource = new IotCoapRegisterSubResource(registerSubHandler);\n            authResource.add(new CoapResource(\"register\") {{\n                add(registerResource);\n                add(registerSubResource);\n            }});\n            // 2.4 添加 /topic 根资源（用于上行消息）\n            IotCoapUpstreamHandler upstreamHandler = new IotCoapUpstreamHandler(serverId);\n            IotCoapUpstreamTopicResource topicResource = new IotCoapUpstreamTopicResource(serverId, upstreamHandler);\n            coapServer.add(topicResource);\n\n            // 3. 启动服务器\n            coapServer.start();\n            running = true;\n            log.info(\"[start][IoT CoAP 协议 {} 启动成功，端口：{}，serverId：{}]\",\n                    getId(), properties.getPort(), serverId);\n\n            // 4. 启动下行消息订阅者\n            IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);\n            this.downstreamSubscriber = new IotCoapDownstreamSubscriber(this, messageBus);\n            this.downstreamSubscriber.start();\n        } catch (Exception e) {\n            log.error(\"[start][IoT CoAP 协议 {} 启动失败]\", getId(), e);\n            stop0();\n            throw e;\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        stop0();\n    }\n\n    private void stop0() {\n        // 1. 停止下行消息订阅者\n        if (downstreamSubscriber != null) {\n            try {\n                downstreamSubscriber.stop();\n                log.info(\"[stop][IoT CoAP 协议 {} 下行消息订阅者已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT CoAP 协议 {} 下行消息订阅者停止失败]\", getId(), e);\n            }\n            downstreamSubscriber = null;\n        }\n\n        // 2. 关闭 CoAP 服务器\n        if (coapServer != null) {\n            try {\n                coapServer.stop();\n                coapServer.destroy();\n                coapServer = null;\n                log.info(\"[stop][IoT CoAP 协议 {} 服务器已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT CoAP 协议 {} 服务器停止失败]\", getId(), e);\n            }\n        }\n        running = false;\n        log.info(\"[stop][IoT CoAP 协议 {} 已停止]\", getId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/downstream/IotCoapDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapProtocol;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 CoAP 订阅者：接收下行给设备的消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotCoapDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber {\n\n    public IotCoapDownstreamSubscriber(IotCoapProtocol protocol, IotMessageBus messageBus) {\n        super(protocol, messageBus);\n    }\n\n    @Override\n    protected void handleMessage(IotDeviceMessage message) {\n        // 如需支持，可通过 CoAP Observe 模式实现（设备订阅资源，服务器推送变更）\n        log.warn(\"[handleMessage][IoT 网关 CoAP 协议暂不支持下行消息，忽略消息：{}]\", message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapAbstractHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.coap.CoAP;\nimport org.eclipse.californium.core.coap.MediaTypeRegistry;\nimport org.eclipse.californium.core.coap.Option;\nimport org.eclipse.californium.core.server.resources.CoapExchange;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\n\n/**\n * IoT 网关 CoAP 协议的处理器抽象基类：提供通用的前置处理（认证）、请求解析、响应处理、全局的异常捕获等\n *\n * @author 芋道源码\n */\n@Slf4j\npublic abstract class IotCoapAbstractHandler {\n\n    /**\n     * 自定义 CoAP Option 编号，用于携带 Token\n     * <p>\n     * CoAP Option 范围 2048-65535 属于实验/自定义范围\n     */\n    public static final int OPTION_TOKEN = 2088;\n\n    private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);\n\n    /**\n     * 处理 CoAP 请求（模板方法）\n     *\n     * @param exchange CoAP 交换对象\n     */\n    public final void handle(CoapExchange exchange) {\n        try {\n            // 1. 前置处理\n            beforeHandle(exchange);\n\n            // 2. 执行业务逻辑\n            CommonResult<Object> result = handle0(exchange);\n            writeResponse(exchange, result);\n        } catch (ServiceException e) {\n            // 业务异常，返回对应的错误码和消息\n            writeResponse(exchange, CommonResult.error(e.getCode(), e.getMessage()));\n        } catch (IllegalArgumentException e) {\n            // 参数校验异常（hutool Assert 抛出），返回 BAD_REQUEST\n            writeResponse(exchange, CommonResult.error(BAD_REQUEST.getCode(), e.getMessage()));\n        } catch (Exception e) {\n            // 其他未知异常，返回 INTERNAL_SERVER_ERROR\n            log.error(\"[handle][CoAP 请求处理异常]\", e);\n            writeResponse(exchange, CommonResult.error(INTERNAL_SERVER_ERROR));\n        }\n    }\n\n    /**\n     * 处理 CoAP 请求（子类实现）\n     *\n     * @param exchange CoAP 交换对象\n     * @return 处理结果\n     */\n    protected abstract CommonResult<Object> handle0(CoapExchange exchange);\n\n    /**\n     * 前置处理：认证等\n     *\n     * @param exchange CoAP 交换对象\n     */\n    private void beforeHandle(CoapExchange exchange) {\n        // 1.1 如果不需要认证，则不走前置处理\n        if (!requiresAuthentication()) {\n            return;\n        }\n        // 1.2 从自定义 Option 获取 token\n        String token = getTokenFromOption(exchange);\n        if (StrUtil.isEmpty(token)) {\n            throw exception(UNAUTHORIZED);\n        }\n        // 1.3 校验 token\n        IotDeviceIdentity deviceInfo = deviceTokenService.verifyToken(token);\n        if (deviceInfo == null) {\n            throw exception(UNAUTHORIZED);\n        }\n\n        // 2.1 解析 productKey 和 deviceName\n        List<String> uriPath = exchange.getRequestOptions().getUriPath();\n        String productKey = getProductKey(uriPath);\n        String deviceName = getDeviceName(uriPath);\n        if (StrUtil.isEmpty(productKey) || StrUtil.isEmpty(deviceName)) {\n            throw exception(BAD_REQUEST);\n        }\n        // 2.2 校验设备信息是否匹配\n        if (ObjUtil.notEqual(productKey, deviceInfo.getProductKey())\n                || ObjUtil.notEqual(deviceName, deviceInfo.getDeviceName())) {\n            throw exception(FORBIDDEN);\n        }\n    }\n\n    // ========== Token 相关方法 ==========\n\n    /**\n     * 是否需要认证（子类可覆盖）\n     * <p>\n     * 默认不需要认证\n     *\n     * @return 是否需要认证\n     */\n    protected boolean requiresAuthentication() {\n        return false;\n    }\n\n    /**\n     * 从 URI 路径中获取 productKey（子类实现）\n     * <p>\n     * 默认抛出异常，需要认证的子类必须实现此方法\n     *\n     * @param uriPath URI 路径\n     * @return productKey\n     */\n    protected String getProductKey(List<String> uriPath) {\n        throw new UnsupportedOperationException(\"子类需要实现 getProductKey 方法\");\n    }\n\n    /**\n     * 从 URI 路径中获取 deviceName（子类实现）\n     * <p>\n     * 默认抛出异常，需要认证的子类必须实现此方法\n     *\n     * @param uriPath URI 路径\n     * @return deviceName\n     */\n    protected String getDeviceName(List<String> uriPath) {\n        throw new UnsupportedOperationException(\"子类需要实现 getDeviceName 方法\");\n    }\n\n    /**\n     * 从自定义 CoAP Option 中获取 Token\n     *\n     * @param exchange CoAP 交换对象\n     * @return Token 值，如果不存在则返回 null\n     */\n    protected String getTokenFromOption(CoapExchange exchange) {\n        Option option = CollUtil.findOne(exchange.getRequestOptions().getOthers(),\n                o -> o.getNumber() == OPTION_TOKEN);\n        return option != null ? new String(option.getValue()) : null;\n    }\n\n    // ========== 序列化相关方法 ==========\n\n    /**\n     * 解析请求体为指定类型\n     *\n     * @param exchange CoAP 交换对象\n     * @param clazz    目标类型\n     * @param <T>      目标类型泛型\n     * @return 解析后的对象，解析失败返回 null\n     */\n    protected <T> T deserializeRequest(CoapExchange exchange, Class<T> clazz) {\n        byte[] payload = exchange.getRequestPayload();\n        if (ArrayUtil.isEmpty(payload)) {\n            return null;\n        }\n        return JsonUtils.parseObject(payload, clazz);\n    }\n\n    private static String serializeResponse(Object data) {\n        return JsonUtils.toJsonString(data);\n    }\n\n    protected void writeResponse(CoapExchange exchange, CommonResult<?> data) {\n        String json = serializeResponse(data);\n        exchange.respond(CoAP.ResponseCode.CONTENT, json, MediaTypeRegistry.APPLICATION_JSON);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapAuthHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.server.resources.CoapExchange;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_AUTH_FAIL;\n\n/**\n * IoT 网关 CoAP 协议的【认证】处理器\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotCoapAuthHandler extends IotCoapAbstractHandler {\n\n    private final String serverId;\n\n    private final IotDeviceTokenService deviceTokenService;\n    private final IotDeviceCommonApi deviceApi;\n    private final IotDeviceMessageService deviceMessageService;\n\n    public IotCoapAuthHandler(String serverId) {\n        this.serverId = serverId;\n        this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n    }\n\n    @Override\n    @SuppressWarnings(\"DuplicatedCode\")\n    protected CommonResult<Object> handle0(CoapExchange exchange) {\n        // 1. 解析参数\n        IotDeviceAuthReqDTO request = deserializeRequest(exchange, IotDeviceAuthReqDTO.class);\n        Assert.notNull(request, \"请求体不能为空\");\n        Assert.notBlank(request.getClientId(), \"clientId 不能为空\");\n        Assert.notBlank(request.getUsername(), \"username 不能为空\");\n        Assert.notBlank(request.getPassword(), \"password 不能为空\");\n\n        // 2.1 执行认证\n        CommonResult<Boolean> result = deviceApi.authDevice(request);\n        result.checkError();\n        if (BooleanUtil.isFalse(result.getData())) {\n            throw exception(DEVICE_AUTH_FAIL);\n        }\n        // 2.2 生成 Token\n        IotDeviceIdentity deviceInfo = deviceTokenService.parseUsername(request.getUsername());\n        Assert.notNull(deviceInfo, \"设备信息不能为空\");\n        String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName());\n        Assert.notBlank(token, \"生成 token 不能为空\");\n\n        // 3. 执行上线\n        IotDeviceMessage message = IotDeviceMessage.buildStateUpdateOnline();\n        deviceMessageService.sendDeviceMessage(message,\n                deviceInfo.getProductKey(), deviceInfo.getDeviceName(), serverId);\n\n        // 4. 构建响应数据\n        return CommonResult.success(MapUtil.of(\"token\", token));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapAuthResource.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.CoapResource;\nimport org.eclipse.californium.core.server.resources.CoapExchange;\n\n/**\n * IoT 网关 CoAP 协议的认证资源（/auth）\n *\n * 设备通过此资源进行认证，获取 Token\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotCoapAuthResource extends CoapResource {\n\n    public static final String PATH = \"auth\";\n\n    private final IotCoapAuthHandler authHandler;\n\n    public IotCoapAuthResource(IotCoapAuthHandler authHandler) {\n        super(PATH);\n        this.authHandler = authHandler;\n        log.info(\"[IotCoapAuthResource][创建 CoAP 认证资源: /{}]\", PATH);\n    }\n\n    @Override\n    public void handlePOST(CoapExchange exchange) {\n        log.debug(\"[handlePOST][收到 /auth POST 请求]\");\n        authHandler.handle(exchange);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapRegisterHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.server.resources.CoapExchange;\n\n/**\n * IoT 网关 CoAP 协议的【设备动态注册】处理器\n * <p>\n * 用于直连设备/网关的一型一密动态注册，不需要认证\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification\">阿里云 - 一型一密</a>\n */\n@Slf4j\npublic class IotCoapRegisterHandler extends IotCoapAbstractHandler {\n\n    private final IotDeviceCommonApi deviceApi;\n\n    public IotCoapRegisterHandler() {\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n    }\n\n    @Override\n    protected CommonResult<Object> handle0(CoapExchange exchange) {\n        // 1. 解析参数\n        IotDeviceRegisterReqDTO request = deserializeRequest(exchange, IotDeviceRegisterReqDTO.class);\n        Assert.notNull(request, \"请求体不能为空\");\n        Assert.notBlank(request.getProductKey(), \"productKey 不能为空\");\n        Assert.notBlank(request.getDeviceName(), \"deviceName 不能为空\");\n        Assert.notBlank(request.getSign(), \"sign 不能为空\");\n\n        // 2. 调用动态注册\n        CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(request);\n        result.checkError();\n\n        // 3. 构建响应数据\n        return CommonResult.success(result.getData());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapRegisterResource.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.CoapResource;\nimport org.eclipse.californium.core.server.resources.CoapExchange;\n\n/**\n * IoT 网关 CoAP 协议的设备动态注册资源（/auth/register/device）\n * <p>\n * 用于直连设备/网关的一型一密动态注册，不需要认证\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotCoapRegisterResource extends CoapResource {\n\n    public static final String PATH = \"device\";\n\n    private final IotCoapRegisterHandler registerHandler;\n\n    public IotCoapRegisterResource(IotCoapRegisterHandler registerHandler) {\n        super(PATH);\n        this.registerHandler = registerHandler;\n        log.info(\"[IotCoapRegisterResource][创建 CoAP 设备动态注册资源: /auth/register/{}]\", PATH);\n    }\n\n    @Override\n    public void handlePOST(CoapExchange exchange) {\n        log.debug(\"[handlePOST][收到设备动态注册请求]\");\n        registerHandler.handle(exchange);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapRegisterSubHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.server.resources.CoapExchange;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * IoT 网关 CoAP 协议的【子设备动态注册】处理器\n * <p>\n * 用于子设备的动态注册，需要网关认证\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/register-devices\">阿里云 - 动态注册子设备</a>\n */\n@Slf4j\npublic class IotCoapRegisterSubHandler extends IotCoapAbstractHandler {\n\n    private final IotDeviceCommonApi deviceApi;\n\n    public IotCoapRegisterSubHandler() {\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n    }\n\n    @Override\n    @SuppressWarnings(\"DuplicatedCode\")\n    protected CommonResult<Object> handle0(CoapExchange exchange) {\n        // 1.1 解析通用参数（从 URI 路径获取网关设备信息）\n        List<String> uriPath = exchange.getRequestOptions().getUriPath();\n        String productKey = getProductKey(uriPath);\n        String deviceName = getDeviceName(uriPath);\n        // 1.2 解析子设备列表\n        SubDeviceRegisterRequest request = deserializeRequest(exchange, SubDeviceRegisterRequest.class);\n        Assert.notNull(request, \"请求参数不能为空\");\n        Assert.notEmpty(request.getParams(), \"params 不能为空\");\n\n        // 2. 调用子设备动态注册\n        IotSubDeviceRegisterFullReqDTO reqDTO = new IotSubDeviceRegisterFullReqDTO()\n                .setGatewayProductKey(productKey)\n                .setGatewayDeviceName(deviceName)\n                .setSubDevices(request.getParams());\n        CommonResult<List<IotSubDeviceRegisterRespDTO>> result = deviceApi.registerSubDevices(reqDTO);\n        result.checkError();\n\n        // 3. 返回结果\n        return success(result.getData());\n    }\n\n    @Override\n    protected boolean requiresAuthentication() {\n        return true;\n    }\n\n    @Override\n    protected String getProductKey(List<String> uriPath) {\n        // 路径格式：/auth/register/sub-device/{productKey}/{deviceName}\n        return CollUtil.get(uriPath, 3);\n    }\n\n    @Override\n    protected String getDeviceName(List<String> uriPath) {\n        // 路径格式：/auth/register/sub-device/{productKey}/{deviceName}\n        return CollUtil.get(uriPath, 4);\n    }\n\n    @Data\n    public static class SubDeviceRegisterRequest {\n\n        private List<IotSubDeviceRegisterReqDTO> params;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapRegisterSubResource.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.CoapResource;\nimport org.eclipse.californium.core.server.resources.CoapExchange;\nimport org.eclipse.californium.core.server.resources.Resource;\n\n/**\n * IoT 网关 CoAP 协议的子设备动态注册资源（/auth/register/sub-device/{productKey}/{deviceName}）\n * <p>\n * 用于子设备的动态注册，需要网关认证\n * <p>\n * 支持动态路径匹配：productKey 和 deviceName 是网关设备的标识\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotCoapRegisterSubResource extends CoapResource {\n\n    public static final String PATH = \"sub-device\";\n\n    private final IotCoapRegisterSubHandler registerSubHandler;\n\n    /**\n     * 创建根资源（/auth/register/sub-device）\n     */\n    public IotCoapRegisterSubResource(IotCoapRegisterSubHandler registerSubHandler) {\n        this(PATH, registerSubHandler);\n        log.info(\"[IotCoapRegisterSubResource][创建 CoAP 子设备动态注册资源: /auth/register/{}]\", PATH);\n    }\n\n    /**\n     * 创建子资源（动态路径）\n     */\n    private IotCoapRegisterSubResource(String name, IotCoapRegisterSubHandler registerSubHandler) {\n        super(name);\n        this.registerSubHandler = registerSubHandler;\n    }\n\n    @Override\n    public Resource getChild(String name) {\n        // 递归创建动态子资源，支持 /sub-device/{productKey}/{deviceName} 路径\n        return new IotCoapRegisterSubResource(name, registerSubHandler);\n    }\n\n    @Override\n    public void handlePOST(CoapExchange exchange) {\n        log.debug(\"[handlePOST][收到子设备动态注册请求]\");\n        registerSubHandler.handle(exchange);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapUpstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.text.StrPool;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.server.resources.CoapExchange;\n\nimport java.util.List;\n\n/**\n * IoT 网关 CoAP 协议的【上行】处理器\n *\n * 处理设备通过 CoAP 协议发送的上行消息，包括：\n * 1. 属性上报：POST /topic/sys/{productKey}/{deviceName}/thing/property/post\n * 2. 事件上报：POST /topic/sys/{productKey}/{deviceName}/thing/event/post\n *\n * Token 通过自定义 CoAP Option 2088 携带\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotCoapUpstreamHandler extends IotCoapAbstractHandler {\n\n    private final String serverId;\n\n    private final IotDeviceMessageService deviceMessageService;\n\n    public IotCoapUpstreamHandler(String serverId) {\n        this.serverId = serverId;\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n    }\n\n    @Override\n    @SuppressWarnings(\"DuplicatedCode\")\n    protected CommonResult<Object> handle0(CoapExchange exchange) {\n        // 1.1 解析通用参数\n        List<String> uriPath = exchange.getRequestOptions().getUriPath();\n        String productKey = getProductKey(uriPath);\n        String deviceName = getDeviceName(uriPath);\n        String method = String.join(StrPool.DOT, uriPath.subList(4, uriPath.size()));\n        // 1.2 解析消息\n        IotDeviceMessage message = deserializeRequest(exchange, IotDeviceMessage.class);\n        Assert.notNull(message, \"请求参数不能为空\");\n        Assert.equals(method, message.getMethod(), \"method 不匹配\");\n\n        // 2. 发送消息\n        deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId);\n\n        // 3. 返回结果\n        return CommonResult.success(MapUtil.of(\"messageId\", message.getId()));\n    }\n\n    @Override\n    protected boolean requiresAuthentication() {\n        return true;\n    }\n\n    @Override\n    protected String getProductKey(List<String> uriPath) {\n        // 路径格式：/topic/sys/{productKey}/{deviceName}/...\n        return CollUtil.get(uriPath, 2);\n    }\n\n    @Override\n    protected String getDeviceName(List<String> uriPath) {\n        // 路径格式：/topic/sys/{productKey}/{deviceName}/...\n        return CollUtil.get(uriPath, 3);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapUpstreamTopicResource.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.CoapResource;\nimport org.eclipse.californium.core.server.resources.CoapExchange;\nimport org.eclipse.californium.core.server.resources.Resource;\n\n/**\n * IoT 网关 CoAP 协议的【上行】Topic 资源\n *\n * 支持任意深度的路径匹配：\n * - /topic/sys/{productKey}/{deviceName}/thing/property/post\n * - /topic/sys/{productKey}/{deviceName}/thing/event/{eventId}/post\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotCoapUpstreamTopicResource extends CoapResource {\n\n    public static final String PATH = \"topic\";\n\n    private final String serverId;\n    private final IotCoapUpstreamHandler upstreamHandler;\n\n    /**\n     * 创建根资源（/topic）\n     */\n    public IotCoapUpstreamTopicResource(String serverId,\n                                         IotCoapUpstreamHandler upstreamHandler) {\n        this(PATH, serverId, upstreamHandler);\n        log.info(\"[IotCoapUpstreamTopicResource][创建 CoAP 上行 Topic 资源: /{}]\", PATH);\n    }\n\n    /**\n     * 创建子资源（动态路径）\n     */\n    private IotCoapUpstreamTopicResource(String name,\n                                          String serverId,\n                                          IotCoapUpstreamHandler upstreamHandler) {\n        super(name);\n        this.serverId = serverId;\n        this.upstreamHandler = upstreamHandler;\n    }\n\n    @Override\n    public Resource getChild(String name) {\n        // 递归创建动态子资源，支持任意深度路径\n        return new IotCoapUpstreamTopicResource(name, serverId, upstreamHandler);\n    }\n\n    @Override\n    public void handleGET(CoapExchange exchange) {\n        upstreamHandler.handle(exchange);\n    }\n\n    @Override\n    public void handlePOST(CoapExchange exchange) {\n        upstreamHandler.handle(exchange);\n    }\n\n    @Override\n    public void handlePUT(CoapExchange exchange) {\n        upstreamHandler.handle(exchange);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/package-info.java",
    "content": "/**\n * CoAP 协议实现包\n * <p>\n * 提供基于 Eclipse Californium 的 IoT 设备连接和消息处理功能\n */\npackage cn.iocoder.yudao.module.iot.gateway.protocol.coap;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.emqx;\n\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n/**\n * IoT EMQX 协议配置\n *\n * @author 芋道源码\n */\n@Data\npublic class IotEmqxConfig {\n\n    // ========== MQTT Client 配置（连接 EMQX Broker） ==========\n\n    /**\n     * MQTT 服务器地址\n     */\n    @NotEmpty(message = \"MQTT 服务器地址不能为空\")\n    private String mqttHost;\n\n    /**\n     * MQTT 服务器端口（默认：1883）\n     */\n    @NotNull(message = \"MQTT 服务器端口不能为空\")\n    private Integer mqttPort = 1883;\n\n    /**\n     * MQTT 用户名\n     */\n    @NotEmpty(message = \"MQTT 用户名不能为空\")\n    private String mqttUsername;\n\n    /**\n     * MQTT 密码\n     */\n    @NotEmpty(message = \"MQTT 密码不能为空\")\n    private String mqttPassword;\n\n    /**\n     * MQTT 客户端的 SSL 开关\n     */\n    @NotNull(message = \"MQTT 是否开启 SSL 不能为空\")\n    private Boolean mqttSsl = false;\n\n    /**\n     * MQTT 客户端 ID\n     */\n    @NotEmpty(message = \"MQTT 客户端 ID 不能为空\")\n    private String mqttClientId;\n\n    /**\n     * MQTT 订阅的主题\n     */\n    @NotEmpty(message = \"MQTT 主题不能为空\")\n    private List<@NotEmpty(message = \"MQTT 主题不能为空\") String> mqttTopics;\n\n    /**\n     * 默认 QoS 级别\n     * <p>\n     * 0 - 最多一次\n     * 1 - 至少一次\n     * 2 - 刚好一次\n     */\n    @NotNull(message = \"MQTT QoS 不能为空\")\n    @Min(value = 0, message = \"MQTT QoS 不能小于 0\")\n    @Max(value = 2, message = \"MQTT QoS 不能大于 2\")\n    private Integer mqttQos = 1;\n\n    /**\n     * 连接超时时间（秒）\n     */\n    @NotNull(message = \"连接超时时间不能为空\")\n    @Min(value = 1, message = \"连接超时时间不能小于 1 秒\")\n    private Integer connectTimeoutSeconds = 10;\n\n    /**\n     * 重连延迟时间（毫秒）\n     */\n    @NotNull(message = \"重连延迟时间不能为空\")\n    @Min(value = 0, message = \"重连延迟时间不能小于 0 毫秒\")\n    private Long reconnectDelayMs = 5000L;\n\n    /**\n     * 是否启用 Clean Session (清理会话)\n     * true: 每次连接都是新会话，Broker 不保留离线消息和订阅关系。\n     * 对于网关这类“永远在线”且会主动重新订阅的应用，建议为 true。\n     */\n    @NotNull(message = \"是否启用 Clean Session 不能为空\")\n    private Boolean cleanSession = true;\n\n    /**\n     * 心跳间隔（秒）\n     * 用于保持连接活性，及时发现网络中断。\n     */\n    @NotNull(message = \"心跳间隔不能为空\")\n    @Min(value = 1, message = \"心跳间隔不能小于 1 秒\")\n    private Integer keepAliveIntervalSeconds = 60;\n\n    /**\n     * 最大未确认消息队列大小\n     * 限制已发送但未收到 Broker 确认的 QoS 1/2 消息数量，用于流量控制。\n     */\n    @NotNull(message = \"最大未确认消息队列大小不能为空\")\n    @Min(value = 1, message = \"最大未确认消息队列大小不能小于 1\")\n    private Integer maxInflightQueue = 10000;\n\n    /**\n     * 是否信任所有 SSL 证书\n     * 警告：此配置会绕过证书验证，仅建议在开发和测试环境中使用！\n     * 在生产环境中，应设置为 false，并配置正确的信任库。\n     */\n    @NotNull(message = \"是否信任所有 SSL 证书不能为空\")\n    private Boolean trustAll = false;\n\n    // ========== MQTT Will / SSL 高级配置 ==========\n\n    /**\n     * 遗嘱消息配置 (用于网关异常下线时通知其他系统)\n     */\n    @Valid\n    private Will will = new Will();\n\n    /**\n     * 高级 SSL/TLS 配置 (用于生产环境)\n     */\n    @Valid\n    private Ssl sslOptions = new Ssl();\n\n    // ========== HTTP Hook 配置（网关提供给 EMQX 调用） ==========\n\n    /**\n     * HTTP Hook 服务配置（用于 /mqtt/auth、/mqtt/event）\n     */\n    @Valid\n    private Http http = new Http();\n\n    /**\n     * 遗嘱消息 (Last Will and Testament)\n     */\n    @Data\n    public static class Will {\n\n        /**\n         * 是否启用遗嘱消息\n         */\n        private boolean enabled = false;\n        /**\n         * 遗嘱消息主题\n         */\n        private String topic;\n        /**\n         * 遗嘱消息内容\n         */\n        private String payload;\n        /**\n         * 遗嘱消息 QoS 等级\n         */\n        @Min(value = 0, message = \"遗嘱消息 QoS 不能小于 0\")\n        @Max(value = 2, message = \"遗嘱消息 QoS 不能大于 2\")\n        private Integer qos = 1;\n        /**\n         * 遗嘱消息是否作为保留消息发布\n         */\n        private boolean retain = true;\n\n    }\n\n    /**\n     * 高级 SSL/TLS 配置\n     */\n    @Data\n    public static class Ssl {\n\n        /**\n         * 密钥库（KeyStore）路径，例如：classpath:certs/client.jks\n         * 包含客户端自己的证书和私钥，用于向服务端证明身份（双向认证）。\n         */\n        private String keyStorePath;\n        /**\n         * 密钥库密码\n         */\n        private String keyStorePassword;\n        /**\n         * 信任库（TrustStore）路径，例如：classpath:certs/trust.jks\n         * 包含服务端信任的 CA 证书，用于验证服务端的身份，防止中间人攻击。\n         */\n        private String trustStorePath;\n        /**\n         * 信任库密码\n         */\n        private String trustStorePassword;\n\n    }\n\n    /**\n     * HTTP Hook 服务 SSL 配置\n     */\n    @Data\n    public static class Http {\n\n        /**\n         * 是否启用 SSL\n         */\n        private Boolean sslEnabled = false;\n\n        /**\n         * SSL 证书路径\n         */\n        private String sslCertPath;\n\n        /**\n         * SSL 私钥路径\n         */\n        private String sslKeyPath;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.emqx;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.downstream.IotEmqxDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream.IotEmqxAuthEventHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream.IotEmqxUpstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;\nimport io.netty.handler.codec.mqtt.MqttQoS;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.http.HttpServer;\nimport io.vertx.core.http.HttpServerOptions;\nimport io.vertx.core.net.JksOptions;\nimport io.vertx.core.net.PemKeyCertOptions;\nimport io.vertx.ext.web.Router;\nimport io.vertx.ext.web.handler.BodyHandler;\nimport io.vertx.mqtt.MqttClient;\nimport io.vertx.mqtt.MqttClientOptions;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * IoT 网关 EMQX 协议实现：\n * <p>\n * 1. 提供 HTTP Hook 服务（/mqtt/auth、/mqtt/acl、/mqtt/event）给 EMQX 调用\n * 2. 通过 MQTT Client 订阅设备上行消息，并发布下行消息到 Broker\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotEmqxProtocol implements IotProtocol {\n\n    /**\n     * 协议配置\n     */\n    private final ProtocolProperties properties;\n    /**\n     * EMQX 配置\n     */\n    private final IotEmqxConfig emqxConfig;\n    /**\n     * 服务器 ID\n     */\n    @Getter\n    private final String serverId;\n\n    /**\n     * 运行状态\n     */\n    @Getter\n    private volatile boolean running = false;\n\n    /**\n     * Vert.x 实例\n     */\n    private Vertx vertx;\n    /**\n     * HTTP Hook 服务器\n     */\n    private HttpServer httpServer;\n\n    /**\n     * MQTT Client\n     */\n    private volatile MqttClient mqttClient;\n    /**\n     * MQTT 重连定时器 ID\n     */\n    private volatile Long reconnectTimerId;\n\n    /**\n     * 上行消息处理器\n     */\n    private final IotEmqxUpstreamHandler upstreamHandler;\n\n    /**\n     * 下行消息订阅者\n     */\n    private IotEmqxDownstreamSubscriber downstreamSubscriber;\n\n    public IotEmqxProtocol(ProtocolProperties properties) {\n        Assert.notNull(properties, \"协议实例配置不能为空\");\n        Assert.notNull(properties.getEmqx(), \"EMQX 协议配置（emqx）不能为空\");\n        this.properties = properties;\n        this.emqxConfig = properties.getEmqx();\n        Assert.notNull(emqxConfig.getConnectTimeoutSeconds(),\n                \"MQTT 连接超时时间(emqx.connect-timeout-seconds)不能为空\");\n        this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());\n        this.upstreamHandler = new IotEmqxUpstreamHandler(serverId);\n    }\n\n    @Override\n    public String getId() {\n        return properties.getId();\n    }\n\n    @Override\n    public IotProtocolTypeEnum getType() {\n        return IotProtocolTypeEnum.EMQX;\n    }\n\n    @Override\n    public void start() {\n        if (running) {\n            log.warn(\"[start][IoT EMQX 协议 {} 已经在运行中]\", getId());\n            return;\n        }\n\n        // 1.1 创建 Vertx 实例 和 下行消息订阅者\n        this.vertx = Vertx.vertx();\n\n        try {\n            // 1.2 启动 HTTP Hook 服务\n            startHttpServer();\n\n            // 1.3 启动 MQTT Client\n            startMqttClient();\n            running = true;\n            log.info(\"[start][IoT EMQX 协议 {} 启动成功，hookPort：{}，serverId：{}]\",\n                    getId(), properties.getPort(), serverId);\n\n            // 2. 启动下行消息订阅者\n            IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);\n            this.downstreamSubscriber = new IotEmqxDownstreamSubscriber(this, messageBus);\n            this.downstreamSubscriber.start();\n        } catch (Exception e) {\n            log.error(\"[start][IoT EMQX 协议 {} 启动失败]\", getId(), e);\n            // 启动失败时，关闭资源\n            stop0();\n            throw e;\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        stop0();\n    }\n\n    private void stop0() {\n        // 1. 停止下行消息订阅者\n        if (downstreamSubscriber != null) {\n            try {\n                downstreamSubscriber.stop();\n                log.info(\"[stop][IoT EMQX 协议 {} 下行消息订阅者已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT EMQX 协议 {} 下行消息订阅者停止失败]\", getId(), e);\n            }\n            downstreamSubscriber = null;\n        }\n\n        // 2.1 先置为 false：避免 closeHandler 触发重连\n        running = false;\n        stopMqttClientReconnectChecker();\n        // 2.2 停止 MQTT Client\n        stopMqttClient();\n\n        // 2.3 停止 HTTP Hook 服务\n        stopHttpServer();\n\n        // 2.4 关闭 Vertx\n        if (vertx != null) {\n            try {\n                vertx.close().toCompletionStage().toCompletableFuture()\n                        .get(10, TimeUnit.SECONDS);\n                log.info(\"[stop][IoT EMQX 协议 {} Vertx 已关闭]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT EMQX 协议 {} Vertx 关闭失败]\", getId(), e);\n            }\n            vertx = null;\n        }\n\n        log.info(\"[stop][IoT EMQX 协议 {} 已停止]\", getId());\n    }\n\n    // ======================================= HTTP Hook Server =======================================\n\n    /**\n     * 启动 HTTP Hook 服务（/mqtt/auth、/mqtt/acl、/mqtt/event）\n     */\n    private void startHttpServer() {\n        // 1. 创建路由\n        Router router = Router.router(vertx);\n        router.route().handler(BodyHandler.create().setBodyLimit(1024 * 1024)); // 限制 body 大小为 1MB，防止大包攻击\n\n        // 2. 创建处理器\n        IotEmqxAuthEventHandler handler = new IotEmqxAuthEventHandler(serverId, this);\n        router.post(IotMqttTopicUtils.MQTT_AUTH_PATH).handler(handler::handleAuth);\n        router.post(IotMqttTopicUtils.MQTT_ACL_PATH).handler(handler::handleAcl);\n        router.post(IotMqttTopicUtils.MQTT_EVENT_PATH).handler(handler::handleEvent);\n\n        // 3. 启动 HTTP Server（支持 HTTPS）\n        IotEmqxConfig.Http httpConfig = emqxConfig.getHttp();\n        HttpServerOptions options = new HttpServerOptions().setPort(properties.getPort());\n        if (httpConfig != null && Boolean.TRUE.equals(httpConfig.getSslEnabled())) {\n            Assert.notBlank(httpConfig.getSslCertPath(), \"EMQX HTTP SSL 证书路径(emqx.http.ssl-cert-path)不能为空\");\n            Assert.notBlank(httpConfig.getSslKeyPath(), \"EMQX HTTP SSL 私钥路径(emqx.http.ssl-key-path)不能为空\");\n            PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions()\n                    .setKeyPath(httpConfig.getSslKeyPath())\n                    .setCertPath(httpConfig.getSslCertPath());\n            options.setSsl(true).setKeyCertOptions(pemKeyCertOptions);\n        }\n        try {\n            httpServer = vertx.createHttpServer(options)\n                    .requestHandler(router)\n                    .listen()\n                    .toCompletionStage().toCompletableFuture()\n                    .get(10, TimeUnit.SECONDS);\n            log.info(\"[startHttpServer][IoT EMQX 协议 {} HTTP Hook 服务启动成功, port: {}, ssl: {}]\",\n                    getId(), properties.getPort(), httpConfig != null && Boolean.TRUE.equals(httpConfig.getSslEnabled()));\n        } catch (Exception e) {\n            log.error(\"[startHttpServer][IoT EMQX 协议 {} HTTP Hook 服务启动失败, port: {}]\", getId(), properties.getPort(), e);\n            throw new RuntimeException(\"HTTP Hook 服务启动失败\", e);\n        }\n    }\n\n    private void stopHttpServer() {\n        if (httpServer == null) {\n            return;\n        }\n        try {\n            httpServer.close().toCompletionStage().toCompletableFuture()\n                    .get(5, TimeUnit.SECONDS);\n            log.info(\"[stopHttpServer][IoT EMQX 协议 {} HTTP Hook 服务已停止]\", getId());\n        } catch (Exception e) {\n            log.error(\"[stopHttpServer][IoT EMQX 协议 {} HTTP Hook 服务停止失败]\", getId(), e);\n        } finally {\n            httpServer = null;\n        }\n    }\n\n    // ======================================= MQTT Client ======================================\n\n    private void startMqttClient() {\n        // 1.1 创建 MQTT Client\n        MqttClient client = createMqttClient();\n        this.mqttClient = client;\n        // 1.2 连接 MQTT Broker\n        if (!connectMqttClient(client)) {\n            throw new RuntimeException(\"MQTT Client 启动失败: 连接 Broker 失败\");\n        }\n\n        // 2. 启动定时重连检查\n        startMqttClientReconnectChecker();\n    }\n\n    private void stopMqttClient() {\n        MqttClient client = this.mqttClient;\n        this.mqttClient = null;  // 先清理引用\n        if (client == null) {\n            return;\n        }\n\n        // 1. 批量取消订阅（仅在连接时）\n        if (client.isConnected()) {\n            List<String> topicList = emqxConfig.getMqttTopics();\n            if (CollUtil.isNotEmpty(topicList)) {\n                try {\n                    client.unsubscribe(topicList).toCompletionStage().toCompletableFuture()\n                            .get(5, TimeUnit.SECONDS);\n                } catch (Exception e) {\n                    log.warn(\"[stopMqttClient][IoT EMQX 协议 {} 取消订阅异常]\", getId(), e);\n                }\n            }\n        }\n\n        // 2. 断开 MQTT 连接\n        try {\n            client.disconnect().toCompletionStage().toCompletableFuture()\n                    .get(5, TimeUnit.SECONDS);\n        } catch (Exception e) {\n            log.warn(\"[stopMqttClient][IoT EMQX 协议 {} 断开连接异常]\", getId(), e);\n        }\n    }\n\n    // ======================================= MQTT 基础方法 ======================================\n\n    /**\n     * 创建 MQTT 客户端\n     *\n     * @return 新创建的 MqttClient\n     */\n    private MqttClient createMqttClient() {\n        // 1.1 基础配置\n        MqttClientOptions options = new MqttClientOptions()\n                .setClientId(emqxConfig.getMqttClientId())\n                .setUsername(emqxConfig.getMqttUsername())\n                .setPassword(emqxConfig.getMqttPassword())\n                .setSsl(Boolean.TRUE.equals(emqxConfig.getMqttSsl()))\n                .setCleanSession(Boolean.TRUE.equals(emqxConfig.getCleanSession()))\n                .setKeepAliveInterval(emqxConfig.getKeepAliveIntervalSeconds())\n                .setMaxInflightQueue(emqxConfig.getMaxInflightQueue());\n        options.setConnectTimeout(emqxConfig.getConnectTimeoutSeconds() * 1000); // Vert.x 需要毫秒\n        options.setTrustAll(Boolean.TRUE.equals(emqxConfig.getTrustAll()));\n        // 1.2 配置遗嘱消息\n        IotEmqxConfig.Will will = emqxConfig.getWill();\n        if (will != null && will.isEnabled()) {\n            Assert.notBlank(will.getTopic(), \"遗嘱消息主题(emqx.will.topic)不能为空\");\n            Assert.notNull(will.getPayload(), \"遗嘱消息内容(emqx.will.payload)不能为空\");\n            options.setWillFlag(true)\n                    .setWillTopic(will.getTopic())\n                    .setWillMessageBytes(Buffer.buffer(will.getPayload()))\n                    .setWillQoS(will.getQos())\n                    .setWillRetain(will.isRetain());\n        }\n        // 1.3 配置高级 SSL/TLS（仅在启用 SSL 且不信任所有证书时生效，且需要 sslOptions 非空）\n        IotEmqxConfig.Ssl sslOptions = emqxConfig.getSslOptions();\n        if (Boolean.TRUE.equals(emqxConfig.getMqttSsl())\n                && Boolean.FALSE.equals(emqxConfig.getTrustAll())\n                && sslOptions != null) {\n            if (StrUtil.isNotBlank(sslOptions.getTrustStorePath())) {\n                options.setTrustStoreOptions(new JksOptions()\n                        .setPath(sslOptions.getTrustStorePath())\n                        .setPassword(sslOptions.getTrustStorePassword()));\n            }\n            if (StrUtil.isNotBlank(sslOptions.getKeyStorePath())) {\n                options.setKeyStoreOptions(new JksOptions()\n                        .setPath(sslOptions.getKeyStorePath())\n                        .setPassword(sslOptions.getKeyStorePassword()));\n            }\n        }\n\n        // 2. 创建客户端\n        return MqttClient.create(vertx, options);\n    }\n\n    /**\n     * 连接 MQTT Broker（同步等待）\n     *\n     * @param client MQTT 客户端\n     * @return 连接成功返回 true，失败返回 false\n     */\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    private synchronized boolean connectMqttClient(MqttClient client) {\n        String host = emqxConfig.getMqttHost();\n        int port = emqxConfig.getMqttPort();\n        int timeoutSeconds = emqxConfig.getConnectTimeoutSeconds();\n        try {\n            // 1. 连接 Broker\n            client.connect(port, host).toCompletionStage().toCompletableFuture()\n                    .get(timeoutSeconds, TimeUnit.SECONDS);\n            log.info(\"[connectMqttClient][IoT EMQX 协议 {} 连接成功, host: {}, port: {}]\",\n                    getId(), host, port);\n\n            // 2. 设置处理器\n            setupMqttClientHandlers(client);\n            subscribeMqttClientTopics(client);\n            return true;\n        } catch (Exception e) {\n            log.error(\"[connectMqttClient][IoT EMQX 协议 {} 连接发生异常]\", getId(), e);\n            return false;\n        }\n    }\n\n    /**\n     * 关闭 MQTT 客户端\n     */\n    private void closeMqttClient() {\n        MqttClient oldClient = this.mqttClient;\n        this.mqttClient = null;  // 先清理引用\n        if (oldClient == null) {\n            return;\n        }\n        // 尽力释放（无论是否连接都尝试 disconnect）\n        try {\n            oldClient.disconnect().toCompletionStage().toCompletableFuture()\n                    .get(5, TimeUnit.SECONDS);\n        } catch (Exception ignored) {\n        }\n    }\n\n    // ======================================= MQTT 重连机制 ======================================\n\n    /**\n     * 启动 MQTT Client 周期性重连检查器\n     */\n    private void startMqttClientReconnectChecker() {\n        long interval = emqxConfig.getReconnectDelayMs();\n        this.reconnectTimerId = vertx.setPeriodic(interval, timerId -> {\n            if (!running) {\n                return;\n            }\n            if (mqttClient != null && mqttClient.isConnected()) {\n                return;\n            }\n            log.info(\"[startMqttClientReconnectChecker][IoT EMQX 协议 {} 检测到断开，尝试重连]\", getId());\n            // 用 executeBlocking 避免阻塞 event-loop（tryReconnectMqttClient 内部有同步等待）\n            vertx.executeBlocking(() -> {\n                tryReconnectMqttClient();\n                return null;\n            });\n        });\n    }\n\n    /**\n     * 停止 MQTT Client 重连检查器\n     */\n    private void stopMqttClientReconnectChecker() {\n        if (reconnectTimerId != null && vertx != null) {\n            try {\n                vertx.cancelTimer(reconnectTimerId);\n            } catch (Exception ignored) {\n            }\n            reconnectTimerId = null;\n        }\n    }\n\n    /**\n     * 尝试重连 MQTT Client\n     */\n    private synchronized void tryReconnectMqttClient() {\n        // 1. 前置检查\n        if (!running) {\n            return;\n        }\n        if (mqttClient != null && mqttClient.isConnected()) {\n            return;\n        }\n\n        log.info(\"[tryReconnectMqttClient][IoT EMQX 协议 {} 开始重连]\", getId());\n        try {\n            // 2. 关闭旧客户端\n            closeMqttClient();\n\n            // 3.1 创建新客户端\n            MqttClient client = createMqttClient();\n            this.mqttClient = client;\n            // 3.2 连接（失败只打印日志，等下次定时）\n            if (!connectMqttClient(client)) {\n                log.warn(\"[tryReconnectMqttClient][IoT EMQX 协议 {} 重连失败，等待下次重试]\", getId());\n            }\n        } catch (Exception e) {\n            log.error(\"[tryReconnectMqttClient][IoT EMQX 协议 {} 重连异常]\", getId(), e);\n        }\n    }\n\n    // ======================================= MQTT Handler ======================================\n\n    /**\n     * 设置 MQTT Client 事件处理器\n     */\n    private void setupMqttClientHandlers(MqttClient client) {\n        // 1. 断开重连监听\n        client.closeHandler(closeEvent -> {\n            if (!running) {\n                return;\n            }\n            log.warn(\"[setupMqttClientHandlers][IoT EMQX 协议 {} 连接断开，立即尝试重连]\", getId());\n            // 用 executeBlocking 避免阻塞 event-loop（tryReconnectMqttClient 内部有同步等待）\n            vertx.executeBlocking(() -> {\n                tryReconnectMqttClient();\n                return null;\n            });\n        });\n\n        // 2. 异常处理\n        client.exceptionHandler(exception ->\n                log.error(\"[setupMqttClientHandlers][IoT EMQX 协议 {} MQTT Client 异常]\", getId(), exception));\n\n        // 3. 上行消息处理\n        client.publishHandler(upstreamHandler::handle);\n    }\n\n    /**\n     * 订阅 MQTT Client 主题（同步等待）\n     */\n    private void subscribeMqttClientTopics(MqttClient client) {\n        List<String> topicList = emqxConfig.getMqttTopics();\n        if (!client.isConnected()) {\n            log.warn(\"[subscribeMqttClientTopics][IoT EMQX 协议 {} MQTT Client 未连接, 跳过订阅]\", getId());\n            return;\n        }\n        if (CollUtil.isEmpty(topicList)) {\n            log.warn(\"[subscribeMqttClientTopics][IoT EMQX 协议 {} 未配置订阅主题, 跳过订阅]\", getId());\n            return;\n        }\n        // 执行订阅\n        Map<String, Integer> topics = convertMap(emqxConfig.getMqttTopics(), topic -> topic,\n                topic -> emqxConfig.getMqttQos());\n        try {\n            client.subscribe(topics).toCompletionStage().toCompletableFuture()\n                    .get(10, TimeUnit.SECONDS);\n            log.info(\"[subscribeMqttClientTopics][IoT EMQX 协议 {} 订阅成功, 共 {} 个主题]\", getId(), topicList.size());\n        } catch (Exception e) {\n            log.error(\"[subscribeMqttClientTopics][IoT EMQX 协议 {} 订阅失败]\", getId(), e);\n        }\n    }\n\n    /**\n     * 发布消息到 MQTT Broker\n     *\n     * @param topic   主题\n     * @param payload 消息内容\n     */\n    public void publishMessage(String topic, byte[] payload) {\n        if (mqttClient == null || !mqttClient.isConnected()) {\n            log.warn(\"[publishMessage][IoT EMQX 协议 {} MQTT Client 未连接, 无法发布消息]\", getId());\n            return;\n        }\n        MqttQoS qos = MqttQoS.valueOf(emqxConfig.getMqttQos());\n        mqttClient.publish(topic, Buffer.buffer(payload), qos, false, false)\n                .onFailure(e -> log.error(\"[publishMessage][IoT EMQX 协议 {} 发布失败, topic: {}]\", getId(), topic, e));\n    }\n\n    /**\n     * 延迟发布消息到 MQTT Broker\n     *\n     * @param topic   主题\n     * @param payload 消息内容\n     * @param delayMs 延迟时间（毫秒）\n     */\n    public void publishDelayMessage(String topic, byte[] payload, long delayMs) {\n        vertx.setTimer(delayMs, id -> publishMessage(topic, payload));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/downstream/IotEmqxDownstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.downstream;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 EMQX 下行消息处理器\n * <p>\n * 从消息总线接收到下行消息，然后发布到 MQTT Broker，从而被设备所接收\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotEmqxDownstreamHandler {\n\n    private final IotEmqxProtocol protocol;\n\n    private final IotDeviceService deviceService;\n\n    private final IotDeviceMessageService deviceMessageService;\n\n    public IotEmqxDownstreamHandler(IotEmqxProtocol protocol) {\n        this.protocol = protocol;\n        this.deviceService = SpringUtil.getBean(IotDeviceService.class);\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n    }\n\n    /**\n     * 处理下行消息\n     *\n     * @param message 设备消息\n     */\n    public void handle(IotDeviceMessage message) {\n        // 1. 获取设备信息\n        IotDeviceRespDTO deviceInfo = deviceService.getDeviceFromCache(message.getDeviceId());\n        if (deviceInfo == null) {\n            log.error(\"[handle][设备信息({})不存在]\", message.getDeviceId());\n            return;\n        }\n\n        // 2.1 根据方法构建主题\n        String topic = buildTopicByMethod(message, deviceInfo.getProductKey(), deviceInfo.getDeviceName());\n        if (StrUtil.isBlank(topic)) {\n            log.warn(\"[handle][未知的消息方法: {}]\", message.getMethod());\n            return;\n        }\n        // 2.2 构建载荷\n        byte[] payload = deviceMessageService.serializeDeviceMessage(message, deviceInfo.getProductKey(),\n                deviceInfo.getDeviceName());\n\n        // 3. 发布消息\n        protocol.publishMessage(topic, payload);\n    }\n\n    /**\n     * 根据消息方法和回复状态构建主题\n     *\n     * @param message    设备消息\n     * @param productKey 产品标识\n     * @param deviceName 设备名称\n     * @return 构建的主题，如果方法不支持返回 null\n     */\n    private String buildTopicByMethod(IotDeviceMessage message, String productKey, String deviceName) {\n        // 1. 判断是否为回复消息\n        boolean isReply = IotDeviceMessageUtils.isReplyMessage(message);\n        // 2. 根据消息方法类型构建对应的主题\n        return IotMqttTopicUtils.buildTopicByMethod(message.getMethod(), productKey, deviceName, isReply);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/downstream/IotEmqxDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxProtocol;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 EMQX 订阅者：接收下行给设备的消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotEmqxDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber {\n\n    private final IotEmqxDownstreamHandler downstreamHandler;\n\n    public IotEmqxDownstreamSubscriber(IotEmqxProtocol protocol, IotMessageBus messageBus) {\n        super(protocol, messageBus);\n        this.downstreamHandler = new IotEmqxDownstreamHandler(protocol);\n    }\n\n    @Override\n    protected void handleMessage(IotDeviceMessage message) {\n        downstreamHandler.handle(message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/upstream/IotEmqxAuthEventHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;\nimport io.vertx.core.json.JsonObject;\nimport io.vertx.ext.web.RoutingContext;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Locale;\n\n/**\n * IoT 网关 EMQX 认证事件处理器\n * <p>\n * 为 EMQX 提供 HTTP 接口服务，包括：\n * 1. 设备认证接口 - 对应 EMQX HTTP 认证插件 {@link #handleAuth(RoutingContext)}\n * 2. 设备事件处理接口 - 对应 EMQX Webhook 事件通知 {@link #handleEvent(RoutingContext)}\n * 3. 设备 ACL 权限接口 - 对应 EMQX HTTP ACL 插件 {@link #handleAcl(RoutingContext)}\n * 4. 设备注册接口 - 集成一型一密设备注册 {@link #handleDeviceRegister(RoutingContext, String, String)}\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotEmqxAuthEventHandler {\n\n    /**\n     * HTTP 成功状态码（EMQX 要求固定使用 200）\n     */\n    private static final int SUCCESS_STATUS_CODE = 200;\n\n    /**\n     * 认证允许结果\n     */\n    private static final String RESULT_ALLOW = \"allow\";\n    /**\n     * 认证拒绝结果\n     */\n    private static final String RESULT_DENY = \"deny\";\n    /**\n     * 认证忽略结果\n     */\n    private static final String RESULT_IGNORE = \"ignore\";\n\n    /**\n     * EMQX 事件类型常量 - 客户端连接\n     */\n    private static final String EVENT_CLIENT_CONNECTED = \"client.connected\";\n    /**\n     * EMQX 事件类型常量 - 客户端断开连接\n     */\n    private static final String EVENT_CLIENT_DISCONNECTED = \"client.disconnected\";\n\n    /**\n     * 认证类型标识 - 设备注册\n     */\n    private static final String AUTH_TYPE_REGISTER = \"|authType=register|\";\n\n    private final String serverId;\n\n    private final IotEmqxProtocol protocol;\n\n    private final IotDeviceMessageService deviceMessageService;\n    private final IotDeviceCommonApi deviceApi;\n\n    public IotEmqxAuthEventHandler(String serverId, IotEmqxProtocol protocol) {\n        this.serverId = serverId;\n        this.protocol = protocol;\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n    }\n\n    // ========== 认证处理 ==========\n\n    /**\n     * EMQX 认证接口\n     */\n    public void handleAuth(RoutingContext context) {\n        JsonObject body = null;\n        try {\n            // 1. 参数校验\n            body = parseRequestBody(context);\n            if (body == null) {\n                return;\n            }\n            String clientId = body.getString(\"clientid\");\n            String username = body.getString(\"username\");\n            String password = body.getString(\"password\");\n            log.debug(\"[handleAuth][设备认证请求: clientId={}, username={}]\", clientId, username);\n            if (StrUtil.hasEmpty(clientId, username, password)) {\n                log.info(\"[handleAuth][认证参数不完整: clientId={}, username={}]\", clientId, username);\n                sendAuthResponse(context, RESULT_DENY);\n                return;\n            }\n\n            // 2.1 情况一：判断是否为注册请求\n            if (StrUtil.endWith(clientId, AUTH_TYPE_REGISTER)) {\n                handleDeviceRegister(context, username, password);\n                return;\n            }\n\n            // 2.2 情况二：执行认证\n            boolean authResult = handleDeviceAuth(clientId, username, password);\n            log.info(\"[handleAuth][设备认证结果: {} -> {}]\", username, authResult);\n            if (authResult) {\n                sendAuthResponse(context, RESULT_ALLOW);\n            } else {\n                sendAuthResponse(context, RESULT_DENY);\n            }\n        } catch (Exception e) {\n            log.error(\"[handleAuth][设备认证异常][body={}]\", body, e);\n            sendAuthResponse(context, RESULT_IGNORE);\n        }\n    }\n\n    /**\n     * 解析认证接口请求体\n     * <p>\n     * 认证接口解析失败时返回 JSON 格式响应（包含 result 字段）\n     *\n     * @param context 路由上下文\n     * @return 请求体JSON对象，解析失败时返回null\n     */\n    private JsonObject parseRequestBody(RoutingContext context) {\n        try {\n            JsonObject body = context.body().asJsonObject();\n            if (body == null) {\n                log.info(\"[parseRequestBody][请求体为空]\");\n                sendAuthResponse(context, RESULT_IGNORE);\n                return null;\n            }\n            return body;\n        } catch (Exception e) {\n            log.error(\"[parseRequestBody][body({}) 解析请求体失败]\", context.body().asString(), e);\n            sendAuthResponse(context, RESULT_IGNORE);\n            return null;\n        }\n    }\n\n    /**\n     * 执行设备认证\n     *\n     * @param clientId 客户端ID\n     * @param username 用户名\n     * @param password 密码\n     * @return 认证是否成功\n     */\n    private boolean handleDeviceAuth(String clientId, String username, String password) {\n        try {\n            CommonResult<Boolean> result = deviceApi.authDevice(new IotDeviceAuthReqDTO()\n                    .setClientId(clientId).setUsername(username).setPassword(password));\n            result.checkError();\n            return BooleanUtil.isTrue(result.getData());\n        } catch (Exception e) {\n            log.error(\"[handleDeviceAuth][设备({}) 认证接口调用失败]\", username, e);\n            throw e;\n        }\n    }\n\n    /**\n     * 发送 EMQX 认证响应\n     * 根据 EMQX 官方文档要求，必须返回 JSON 格式响应\n     *\n     * @param context 路由上下文\n     * @param result  认证结果：allow、deny、ignore\n     */\n    private void sendAuthResponse(RoutingContext context, String result) {\n        // 构建符合 EMQX 官方规范的响应\n        JsonObject response = new JsonObject()\n                .put(\"result\", result)\n                .put(\"is_superuser\", false);\n        // 可以根据业务需求添加客户端属性\n        // response.put(\"client_attrs\", new JsonObject().put(\"role\", \"device\"));\n        // 可以添加认证过期时间（可选）\n        // response.put(\"expire_at\", System.currentTimeMillis() / 1000 + 3600);\n\n        // 回复响应\n        context.response()\n                .setStatusCode(SUCCESS_STATUS_CODE)\n                .putHeader(\"Content-Type\", \"application/json; charset=utf-8\")\n                .end(response.encode());\n    }\n\n    // ========== ACL 处理 ==========\n\n    /**\n     * EMQX ACL 接口\n     * <p>\n     * 用于 EMQX 的 HTTP ACL 插件校验设备的 publish/subscribe 权限。\n     * 若请求参数无法识别，则返回 ignore 交给 EMQX 自身 ACL 规则处理。\n     */\n    public void handleAcl(RoutingContext context) {\n        JsonObject body = null;\n        try {\n            // 1.1 解析请求体\n            body = parseRequestBody(context);\n            if (body == null) {\n                return;\n            }\n            String username = body.getString(\"username\");\n            String topic = body.getString(\"topic\");\n            if (StrUtil.hasBlank(username, topic)) {\n                log.info(\"[handleAcl][ACL 参数不完整: username={}, topic={}]\", username, topic);\n                sendAuthResponse(context, RESULT_IGNORE);\n                return;\n            }\n            // 1.2 解析设备身份\n            IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);\n            if (deviceInfo == null) {\n                sendAuthResponse(context, RESULT_IGNORE);\n                return;\n            }\n            // 1.3 解析 ACL 动作（兼容多种 EMQX 版本/插件字段）\n            Boolean subscribe = parseAclSubscribeFlag(body);\n            if (subscribe == null) {\n                sendAuthResponse(context, RESULT_IGNORE);\n                return;\n            }\n\n            // 2. 执行 ACL 校验\n            boolean allowed = subscribe\n                    ? IotMqttTopicUtils.isTopicSubscribeAllowed(topic, deviceInfo.getProductKey(), deviceInfo.getDeviceName())\n                    : IotMqttTopicUtils.isTopicPublishAllowed(topic, deviceInfo.getProductKey(), deviceInfo.getDeviceName());\n            sendAuthResponse(context, allowed ? RESULT_ALLOW : RESULT_DENY);\n        } catch (Exception e) {\n            log.error(\"[handleAcl][ACL 处理失败][body={}]\", body, e);\n            sendAuthResponse(context, RESULT_IGNORE);\n        }\n    }\n\n    /**\n     * 解析 ACL 动作类型：订阅/发布\n     *\n     * @param body ACL 请求体\n     * @return true 订阅；false 发布；null 不识别\n     */\n    private static Boolean parseAclSubscribeFlag(JsonObject body) {\n        // 1. action 字段（常见为 publish/subscribe）\n        String action = body.getString(\"action\");\n        if (StrUtil.isNotBlank(action)) {\n            String lower = action.toLowerCase(Locale.ROOT);\n            if (lower.contains(\"sub\")) {\n                return true;\n            }\n            if (lower.contains(\"pub\")) {\n                return false;\n            }\n        }\n\n        // 2. access 字段：可能是数字或字符串\n        Integer access = body.getInteger(\"access\");\n        if (access != null) {\n            if (access == 1) {\n                return true;\n            }\n            if (access == 2) {\n                return false;\n            }\n        }\n        String accessText = body.getString(\"access\");\n        if (StrUtil.isNotBlank(accessText)) {\n            String lower = accessText.toLowerCase(Locale.ROOT);\n            if (lower.contains(\"sub\")) {\n                return true;\n            }\n            if (lower.contains(\"pub\")) {\n                return false;\n            }\n            if (StrUtil.isNumeric(accessText)) {\n                int value = Integer.parseInt(accessText);\n                if (value == 1) {\n                    return true;\n                }\n                if (value == 2) {\n                    return false;\n                }\n            }\n        }\n        return null;\n    }\n\n    // ========== 事件处理 ==========\n\n    /**\n     * EMQX 统一事件处理接口：根据 EMQX 官方 Webhook 设计，统一处理所有客户端事件\n     * 支持的事件类型：client.connected、client.disconnected 等\n     */\n    public void handleEvent(RoutingContext context) {\n        JsonObject body = null;\n        try {\n            // 1. 解析请求体\n            body = parseEventRequestBody(context);\n            if (body == null) {\n                return;\n            }\n            String event = body.getString(\"event\");\n            String username = body.getString(\"username\");\n            log.debug(\"[handleEvent][收到事件: {} - {}]\", event, username);\n\n            // 2. 根据事件类型进行分发处理\n            switch (event) {\n                case EVENT_CLIENT_CONNECTED:\n                    handleClientConnected(body);\n                    break;\n                case EVENT_CLIENT_DISCONNECTED:\n                    handleClientDisconnected(body);\n                    break;\n                default:\n                    break;\n            }\n\n            // 3. EMQX Webhook 只需要 200 状态码，无需响应体\n            context.response().setStatusCode(SUCCESS_STATUS_CODE).end();\n        } catch (Exception e) {\n            log.error(\"[handleEvent][事件处理失败][body={}]\", body, e);\n            // 即使处理失败，也返回 200 避免 EMQX 重试\n            context.response().setStatusCode(SUCCESS_STATUS_CODE).end();\n        }\n    }\n\n    /**\n     * 解析事件接口请求体\n     * <p>\n     * 事件接口解析失败时仅返回 200 状态码，无响应体（符合 EMQX Webhook 规范）\n     *\n     * @param context 路由上下文\n     * @return 请求体JSON对象，解析失败时返回null\n     */\n    private JsonObject parseEventRequestBody(RoutingContext context) {\n        try {\n            JsonObject body = context.body().asJsonObject();\n            if (body == null) {\n                log.info(\"[parseEventRequestBody][请求体为空]\");\n                context.response().setStatusCode(SUCCESS_STATUS_CODE).end();\n                return null;\n            }\n            return body;\n        } catch (Exception e) {\n            log.error(\"[parseEventRequestBody][body({}) 解析请求体失败]\", context.body().asString(), e);\n            context.response().setStatusCode(SUCCESS_STATUS_CODE).end();\n            return null;\n        }\n    }\n\n    /**\n     * 处理客户端连接事件\n     */\n    private void handleClientConnected(JsonObject body) {\n        String username = body.getString(\"username\");\n        log.info(\"[handleClientConnected][设备上线: {}]\", username);\n        handleDeviceStateChange(username, true);\n    }\n\n    /**\n     * 处理客户端断开连接事件\n     */\n    private void handleClientDisconnected(JsonObject body) {\n        String username = body.getString(\"username\");\n        String reason = body.getString(\"reason\");\n        log.info(\"[handleClientDisconnected][设备下线: {} ({})]\", username, reason);\n        handleDeviceStateChange(username, false);\n    }\n\n    /**\n     * 处理设备状态变化\n     *\n     * @param username 用户名\n     * @param online   是否在线 true 在线 false 离线\n     */\n    private void handleDeviceStateChange(String username, boolean online) {\n        // 1. 解析设备信息\n        IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);\n        if (deviceInfo == null) {\n            log.debug(\"[handleDeviceStateChange][跳过非设备({})连接]\", username);\n            return;\n        }\n\n        try {\n            // 2. 构建设备状态消息\n            IotDeviceMessage message = online ? IotDeviceMessage.buildStateUpdateOnline()\n                    : IotDeviceMessage.buildStateOffline();\n\n            // 3. 发送设备状态消息\n            deviceMessageService.sendDeviceMessage(message,\n                    deviceInfo.getProductKey(), deviceInfo.getDeviceName(), serverId);\n        } catch (Exception e) {\n            log.error(\"[handleDeviceStateChange][发送设备状态消息失败: {}]\", username, e);\n        }\n    }\n\n    // ========= 注册处理 =========\n\n    /**\n     * 处理设备注册请求（一型一密）\n     *\n     * @param context  路由上下文\n     * @param username 用户名\n     * @param password 密码（签名）\n     */\n    private void handleDeviceRegister(RoutingContext context, String username, String password) {\n        try {\n            // 1. 解析设备信息\n            IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);\n            if (deviceInfo == null) {\n                log.warn(\"[handleDeviceRegister][设备注册失败: 无法解析 username={}]\", username);\n                sendAuthResponse(context, RESULT_DENY);\n                return;\n            }\n\n            // 2. 调用注册 API\n            IotDeviceRegisterReqDTO params = new IotDeviceRegisterReqDTO()\n                    .setProductKey(deviceInfo.getProductKey())\n                    .setDeviceName(deviceInfo.getDeviceName())\n                    .setSign(password);\n            CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params);\n            result.checkError();\n\n            // 3. 允许连接\n            log.info(\"[handleDeviceRegister][设备注册成功: {}]\", username);\n            sendAuthResponse(context, RESULT_ALLOW);\n\n            // 4. 延迟 5 秒发送注册结果（等待设备连接成功并完成订阅）\n            sendRegisterResultMessage(username, result.getData());\n        } catch (Exception e) {\n            log.warn(\"[handleDeviceRegister][设备注册失败: {}, 错误: {}]\", username, e.getMessage());\n            sendAuthResponse(context, RESULT_DENY);\n        }\n    }\n\n    /**\n     * 发送注册结果消息给设备\n     * <p>\n     * 注意：延迟 5 秒发送，等待设备连接成功并完成订阅。\n     *\n     * @param username 用户名\n     * @param result   注册结果\n     */\n    @SuppressWarnings(\"DataFlowIssue\")\n    private void sendRegisterResultMessage(String username, IotDeviceRegisterRespDTO result) {\n        IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);\n        Assert.notNull(deviceInfo, \"设备信息不能为空\");\n        try {\n            // 1.1 构建响应消息\n            String method = IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod();\n            IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(null, method, result, 0, null);\n            // 1.2 序列化消息\n            byte[] encodedData = deviceMessageService.serializeDeviceMessage(responseMessage,\n                    cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum.JSON);\n            // 1.3 构建响应主题\n            String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method,\n                    deviceInfo.getProductKey(), deviceInfo.getDeviceName(), true);\n\n            // 2. 构建响应主题，并延迟发布（等待设备连接成功并完成订阅）\n            protocol.publishDelayMessage(replyTopic, encodedData, 5000);\n            log.info(\"[sendRegisterResultMessage][发送注册结果: topic={}]\", replyTopic);\n        } catch (Exception e) {\n            log.error(\"[sendRegisterResultMessage][发送注册结果失败: {}]\", username, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/upstream/IotEmqxUpstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;\nimport io.vertx.mqtt.messages.MqttPublishMessage;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 EMQX 上行消息处理器\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotEmqxUpstreamHandler {\n\n    private final IotDeviceMessageService deviceMessageService;\n\n    private final String serverId;\n\n    public IotEmqxUpstreamHandler(String serverId) {\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n        this.serverId = serverId;\n    }\n\n    /**\n     * 处理 MQTT 发布消息\n     */\n    public void handle(MqttPublishMessage mqttMessage) {\n        log.debug(\"[handle][收到 MQTT 消息, topic: {}, payload: {}]\", mqttMessage.topicName(), mqttMessage.payload());\n        String topic = mqttMessage.topicName();\n        byte[] payload = mqttMessage.payload().getBytes();\n        try {\n            // 1. 解析主题，一次性获取所有信息\n            String[] topicParts = topic.split(\"/\");\n            String productKey = ArrayUtil.get(topicParts, 2);\n            String deviceName = ArrayUtil.get(topicParts, 3);\n            if (topicParts.length < 4 || StrUtil.hasBlank(productKey, deviceName)) {\n                log.warn(\"[handle][topic({}) 格式不正确，无法解析有效的 productKey 和 deviceName]\", topic);\n                return;\n            }\n\n            // 2.1 反序列化消息\n            IotDeviceMessage message = deviceMessageService.deserializeDeviceMessage(payload, productKey, deviceName);\n            if (message == null) {\n                log.warn(\"[handle][topic({}) payload({}) 消息解码失败]\", topic, new String(payload));\n                return;\n            }\n            // 2.2 标准化回复消息的 method（MQTT 协议中，设备回复消息的 method 会携带 _reply 后缀）\n            IotMqttTopicUtils.normalizeReplyMethod(message);\n\n            // 3. 发送消息到队列\n            deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId);\n        } catch (Exception e) {\n            log.error(\"[handle][topic({}) payload({}) 处理异常]\", topic, new String(payload), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/package-info.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.emqx;"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http;\n\nimport lombok.Data;\n\n/**\n * IoT HTTP 协议配置\n *\n * @author 芋道源码\n */\n@Data\npublic class IotHttpConfig {\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.downstream.IotHttpDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpAuthHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpRegisterHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpRegisterSubHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpUpstreamHandler;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.http.HttpServer;\nimport io.vertx.core.http.HttpServerOptions;\nimport io.vertx.core.net.PemKeyCertOptions;\nimport io.vertx.ext.web.Router;\nimport io.vertx.ext.web.handler.BodyHandler;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT HTTP 协议实现\n * <p>\n * 基于 Vert.x 实现 HTTP 服务器，接收设备上行消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotHttpProtocol implements IotProtocol {\n\n    /**\n     * 协议配置\n     */\n    private final ProtocolProperties properties;\n    /**\n     * 服务器 ID（用于消息追踪，全局唯一）\n     */\n    @Getter\n    private final String serverId;\n\n    /**\n     * 运行状态\n     */\n    @Getter\n    private volatile boolean running = false;\n\n    /**\n     * Vert.x 实例\n     */\n    private Vertx vertx;\n    /**\n     * HTTP 服务器\n     */\n    private HttpServer httpServer;\n\n    /**\n     * 下行消息订阅者\n     */\n    private IotHttpDownstreamSubscriber downstreamSubscriber;\n\n    public IotHttpProtocol(ProtocolProperties properties) {\n        this.properties = properties;\n        this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());\n    }\n\n    @Override\n    public String getId() {\n        return properties.getId();\n    }\n\n    @Override\n    public IotProtocolTypeEnum getType() {\n        return IotProtocolTypeEnum.HTTP;\n    }\n\n    @Override\n    public void start() {\n        if (running) {\n            log.warn(\"[start][IoT HTTP 协议 {} 已经在运行中]\", getId());\n            return;\n        }\n\n        // 1.1 创建 Vertx 实例\n        this.vertx = Vertx.vertx();\n\n        // 1.2 创建路由\n        Router router = Router.router(vertx);\n        router.route().handler(BodyHandler.create());\n\n        // 1.3 创建处理器，添加路由处理器\n        IotHttpAuthHandler authHandler = new IotHttpAuthHandler(this);\n        router.post(IotHttpAuthHandler.PATH).handler(authHandler);\n        IotHttpRegisterHandler registerHandler = new IotHttpRegisterHandler();\n        router.post(IotHttpRegisterHandler.PATH).handler(registerHandler);\n        IotHttpRegisterSubHandler registerSubHandler = new IotHttpRegisterSubHandler();\n        router.post(IotHttpRegisterSubHandler.PATH).handler(registerSubHandler);\n        IotHttpUpstreamHandler upstreamHandler = new IotHttpUpstreamHandler(this);\n        router.post(IotHttpUpstreamHandler.PATH).handler(upstreamHandler);\n\n        // 1.4 启动 HTTP 服务器\n        HttpServerOptions options = new HttpServerOptions().setPort(properties.getPort());\n        IotGatewayProperties.SslConfig sslConfig = properties.getSsl();\n        if (sslConfig != null && Boolean.TRUE.equals(sslConfig.getSsl())) {\n            PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions()\n                    .setKeyPath(sslConfig.getSslKeyPath())\n                    .setCertPath(sslConfig.getSslCertPath());\n            options = options.setSsl(true).setKeyCertOptions(pemKeyCertOptions);\n        }\n        try {\n            httpServer = vertx.createHttpServer(options)\n                    .requestHandler(router)\n                    .listen()\n                    .result();\n            running = true;\n            log.info(\"[start][IoT HTTP 协议 {} 启动成功，端口：{}，serverId：{}]\",\n                    getId(), properties.getPort(), serverId);\n\n            // 2. 启动下行消息订阅者\n            IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);\n            this.downstreamSubscriber = new IotHttpDownstreamSubscriber(this, messageBus);\n            this.downstreamSubscriber.start();\n        } catch (Exception e) {\n            log.error(\"[start][IoT HTTP 协议 {} 启动失败]\", getId(), e);\n            stop0();\n            throw e;\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        stop0();\n    }\n\n    private void stop0() {\n        // 1. 停止下行消息订阅者\n        if (downstreamSubscriber != null) {\n            try {\n                downstreamSubscriber.stop();\n                log.info(\"[stop][IoT HTTP 协议 {} 下行消息订阅者已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT HTTP 协议 {} 下行消息订阅者停止失败]\", getId(), e);\n            }\n            downstreamSubscriber = null;\n        }\n\n        // 2.1 关闭 HTTP 服务器\n        if (httpServer != null) {\n            try {\n                httpServer.close().result();\n                log.info(\"[stop][IoT HTTP 协议 {} 服务器已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT HTTP 协议 {} 服务器停止失败]\", getId(), e);\n            }\n            httpServer = null;\n        }\n        // 2.2 关闭 Vertx 实例\n        if (vertx != null) {\n            try {\n                vertx.close().result();\n                log.info(\"[stop][IoT HTTP 协议 {} Vertx 已关闭]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT HTTP 协议 {} Vertx 关闭失败]\", getId(), e);\n            }\n            vertx = null;\n        }\n        running = false;\n        log.info(\"[stop][IoT HTTP 协议 {} 已停止]\", getId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/downstream/IotHttpDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 HTTP 订阅者：接收下行给设备的消息\n *\n * @author 芋道源码\n */\n\n@Slf4j\npublic class IotHttpDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber {\n\n    public IotHttpDownstreamSubscriber(IotProtocol protocol, IotMessageBus messageBus) {\n        super(protocol, messageBus);\n    }\n\n    @Override\n    protected void handleMessage(IotDeviceMessage message) {\n        log.info(\"[handleMessage][IoT 网关 HTTP 协议不支持下行消息，忽略消息：{}]\", message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpAbstractHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;\nimport io.vertx.core.Handler;\nimport io.vertx.core.http.HttpHeaders;\nimport io.vertx.ext.web.RoutingContext;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.MediaType;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;\n\n/**\n * IoT 网关 HTTP 协议的处理器抽象基类：提供通用的前置处理（认证）、全局的异常捕获等\n *\n * @author 芋道源码\n */\n@Slf4j\npublic abstract class IotHttpAbstractHandler implements Handler<RoutingContext> {\n\n    private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);\n\n    @Override\n    public final void handle(RoutingContext context) {\n        try {\n            // 1. 前置处理\n            beforeHandle(context);\n\n            // 2. 执行逻辑\n            CommonResult<Object> result = handle0(context);\n            writeResponse(context, result);\n        } catch (ServiceException e) {\n            // 已知异常，返回对应的错误码和错误信息\n            writeResponse(context, CommonResult.error(e.getCode(), e.getMessage()));\n        } catch (IllegalArgumentException e) {\n            // 参数校验异常，返回 400 错误\n            writeResponse(context, CommonResult.error(BAD_REQUEST.getCode(), e.getMessage()));\n        } catch (Exception e) {\n            // 其他未知异常，返回 500 错误\n            log.error(\"[handle][path({}) 处理异常]\", context.request().path(), e);\n            writeResponse(context, CommonResult.error(INTERNAL_SERVER_ERROR));\n        }\n    }\n\n    /**\n     * 处理 HTTP 请求（子类实现）\n     *\n     * @param context RoutingContext 对象\n     * @return 处理结果\n     */\n    protected abstract CommonResult<Object> handle0(RoutingContext context);\n\n    /**\n     * 前置处理：认证等\n     *\n     * @param context RoutingContext 对象\n     */\n    private void beforeHandle(RoutingContext context) {\n        // 如果不需要认证，则不走前置处理\n        String path = context.request().path();\n        if (ObjectUtils.equalsAny(path, IotHttpAuthHandler.PATH, IotHttpRegisterHandler.PATH)) {\n            return;\n        }\n\n        // 解析参数\n        String token = context.request().getHeader(HttpHeaders.AUTHORIZATION);\n        if (StrUtil.isEmpty(token)) {\n            throw invalidParamException(\"token 不能为空\");\n        }\n        String productKey = context.pathParam(\"productKey\");\n        if (StrUtil.isEmpty(productKey)) {\n            throw invalidParamException(\"productKey 不能为空\");\n        }\n        String deviceName = context.pathParam(\"deviceName\");\n        if (StrUtil.isEmpty(deviceName)) {\n            throw invalidParamException(\"deviceName 不能为空\");\n        }\n\n        // 校验 token\n        IotDeviceIdentity deviceInfo = deviceTokenService.verifyToken(token);\n        Assert.notNull(deviceInfo, \"设备信息不能为空\");\n        // 校验设备信息是否匹配\n        if (ObjUtil.notEqual(productKey, deviceInfo.getProductKey())\n                || ObjUtil.notEqual(deviceName, deviceInfo.getDeviceName())) {\n            throw exception(FORBIDDEN);\n        }\n    }\n\n    // ========== 序列化相关方法 ==========\n\n    protected static  <T> T deserializeRequest(RoutingContext context, Class<T> clazz) {\n        byte[] body = context.body().buffer() != null ? context.body().buffer().getBytes() : null;\n        if (ArrayUtil.isEmpty(body)) {\n            throw invalidParamException(\"请求体不能为空\");\n        }\n        return JsonUtils.parseObject(body, clazz);\n    }\n\n    private static String serializeResponse(Object data) {\n        return JsonUtils.toJsonString(data);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static void writeResponse(RoutingContext context, CommonResult<?> data) {\n        context.response()\n                .setStatusCode(200)\n                .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)\n                .end(serializeResponse(data));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpAuthHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.ext.web.RoutingContext;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_AUTH_FAIL;\n\n/**\n * IoT 网关 HTTP 协议的【认证】处理器\n *\n * 参考 <a href=\"阿里云 IoT —— HTTPS 连接通信\">https://help.aliyun.com/zh/iot/user-guide/establish-connections-over-https</a>\n *\n * @author 芋道源码\n */\npublic class IotHttpAuthHandler extends IotHttpAbstractHandler {\n\n    public static final String PATH = \"/auth\";\n\n    private final String serverId;\n\n    private final IotDeviceTokenService deviceTokenService;\n\n    private final IotDeviceCommonApi deviceApi;\n\n    private final IotDeviceMessageService deviceMessageService;\n\n    public IotHttpAuthHandler(IotHttpProtocol protocol) {\n        this.serverId = protocol.getServerId();\n        this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n    }\n\n    @Override\n    @SuppressWarnings(\"DuplicatedCode\")\n    public CommonResult<Object> handle0(RoutingContext context) {\n        // 1. 解析参数\n        IotDeviceAuthReqDTO request = deserializeRequest(context, IotDeviceAuthReqDTO.class);\n        Assert.notNull(request, \"请求参数不能为空\");\n        Assert.notBlank(request.getClientId(), \"clientId 不能为空\");\n        Assert.notBlank(request.getUsername(), \"username 不能为空\");\n        Assert.notBlank(request.getPassword(), \"password 不能为空\");\n\n        // 2.1 执行认证\n        CommonResult<Boolean> result = deviceApi.authDevice(request);\n        result.checkError();\n        if (BooleanUtil.isFalse(result.getData())) {\n            throw exception(DEVICE_AUTH_FAIL);\n        }\n        // 2.2 生成 Token\n        IotDeviceIdentity deviceInfo = deviceTokenService.parseUsername(request.getUsername());\n        Assert.notNull(deviceInfo, \"设备信息不能为空\");\n        String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName());\n        Assert.notBlank(token, \"生成 token 不能为空位\");\n\n        // 3. 执行上线\n        IotDeviceMessage message = IotDeviceMessage.buildStateUpdateOnline();\n        deviceMessageService.sendDeviceMessage(message,\n                deviceInfo.getProductKey(), deviceInfo.getDeviceName(), serverId);\n\n        // 构建响应数据\n        return success(MapUtil.of(\"token\", token));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpRegisterHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport io.vertx.ext.web.RoutingContext;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * IoT 网关 HTTP 协议的【设备动态注册】处理器\n * <p>\n * 用于直连设备/网关的一型一密动态注册，不需要认证\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification\">阿里云 - 一型一密</a>\n */\npublic class IotHttpRegisterHandler extends IotHttpAbstractHandler {\n\n    public static final String PATH = \"/auth/register/device\";\n\n    private final IotDeviceCommonApi deviceApi;\n\n    public IotHttpRegisterHandler() {\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n    }\n\n    @Override\n    public CommonResult<Object> handle0(RoutingContext context) {\n        // 1. 解析参数\n        IotDeviceRegisterReqDTO request = deserializeRequest(context, IotDeviceRegisterReqDTO.class);\n        Assert.notNull(request, \"请求参数不能为空\");\n        Assert.notBlank(request.getProductKey(), \"productKey 不能为空\");\n        Assert.notBlank(request.getDeviceName(), \"deviceName 不能为空\");\n        Assert.notBlank(request.getSign(), \"sign 不能为空\");\n\n        // 2. 调用动态注册\n        CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(request);\n        result.checkError();\n\n        // 3. 返回结果\n        return success(result.getData());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpRegisterSubHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;\nimport io.vertx.ext.web.RoutingContext;\nimport lombok.Data;\n\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * IoT 网关 HTTP 协议的【子设备动态注册】处理器\n * <p>\n * 用于子设备的动态注册，需要网关认证\n *\n * @author 芋道源码\n * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/register-devices\">阿里云 - 动态注册子设备</a>\n */\npublic class IotHttpRegisterSubHandler extends IotHttpAbstractHandler {\n\n    /**\n     * 路径：/auth/register/sub-device/:productKey/:deviceName\n     * <p>\n     * productKey 和 deviceName 是网关设备的标识\n     */\n    public static final String PATH = \"/auth/register/sub-device/:productKey/:deviceName\";\n\n    private final IotDeviceCommonApi deviceApi;\n\n    public IotHttpRegisterSubHandler() {\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n    }\n\n    @Override\n    public CommonResult<Object> handle0(RoutingContext context) {\n        // 1.1 解析通用参数\n        String productKey = context.pathParam(\"productKey\");\n        String deviceName = context.pathParam(\"deviceName\");\n        // 1.2 解析子设备列表\n        SubDeviceRegisterRequest request = deserializeRequest(context, SubDeviceRegisterRequest.class);\n        Assert.notNull(request, \"请求参数不能为空\");\n        Assert.notEmpty(request.getParams(), \"params 不能为空\");\n\n        // 2. 调用子设备动态注册\n        IotSubDeviceRegisterFullReqDTO reqDTO = new IotSubDeviceRegisterFullReqDTO()\n                .setGatewayProductKey(productKey)\n                .setGatewayDeviceName(deviceName)\n                .setSubDevices(request.getParams());\n        CommonResult<List<IotSubDeviceRegisterRespDTO>> result = deviceApi.registerSubDevices(reqDTO);\n        result.checkError();\n\n        // 3. 返回结果\n        return success(result.getData());\n    }\n\n    @Data\n    public static class SubDeviceRegisterRequest {\n\n        private List<IotSubDeviceRegisterReqDTO> params;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpUpstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.text.StrPool;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.ext.web.RoutingContext;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 HTTP 协议的【上行】处理器\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotHttpUpstreamHandler extends IotHttpAbstractHandler {\n\n    public static final String PATH = \"/topic/sys/:productKey/:deviceName/*\";\n\n    private final String serverId;\n\n    private final IotDeviceMessageService deviceMessageService;\n\n    public IotHttpUpstreamHandler(IotHttpProtocol protocol) {\n        this.serverId = protocol.getServerId();\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n    }\n\n    @Override\n    protected CommonResult<Object> handle0(RoutingContext context) {\n        // 1.1 解析通用参数\n        String productKey = context.pathParam(\"productKey\");\n        String deviceName = context.pathParam(\"deviceName\");\n        String method = context.pathParam(\"*\").replaceAll(StrPool.SLASH, StrPool.DOT);\n        // 1.2 根据 Content-Type 反序列化消息\n        IotDeviceMessage message = deserializeRequest(context, IotDeviceMessage.class);\n        Assert.notNull(message, \"请求参数不能为空\");\n        Assert.equals(method, message.getMethod(), \"method 不匹配\");\n\n        // 2. 发送消息\n        deviceMessageService.sendDeviceMessage(message,\n                productKey, deviceName, serverId);\n\n        // 3. 返回结果\n        return CommonResult.success(MapUtil.of(\"messageId\", message.getId()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/package-info.java",
    "content": "/**\n * HTTP 协议实现包\n * <p>\n * 提供基于 Vert.x HTTP Server 的 IoT 设备连接和消息处理功能\n */\npackage cn.iocoder.yudao.module.iot.gateway.protocol.http;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/manager/AbstractIotModbusPollScheduler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.manager;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;\nimport io.vertx.core.Vertx;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentLinkedQueue;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n/**\n * Modbus 轮询调度器基类\n * <p>\n * 封装通用的定时器管理、per-device 请求队列限速逻辑。\n * 子类只需实现 {@link #pollPoint(Long, Long)} 定义具体的轮询动作。\n * <p>\n *\n * @author 芋道源码\n */\n@Slf4j\npublic abstract class AbstractIotModbusPollScheduler {\n\n    protected final Vertx vertx;\n\n    /**\n     * 同设备最小请求间隔（毫秒），防止 Modbus 设备性能不足时请求堆积\n     */\n    private static final long MIN_REQUEST_INTERVAL = 1000;\n    /**\n     * 每个设备请求队列的最大长度，超出时丢弃最旧请求\n     */\n    private static final int MAX_QUEUE_SIZE = 1000;\n\n    /**\n     * 设备点位的定时器映射：deviceId -> (pointId -> PointTimerInfo)\n     */\n    private final Map<Long, Map<Long, PointTimerInfo>> devicePointTimers = new ConcurrentHashMap<>();\n\n    /**\n     * per-device 请求队列：deviceId -> 待执行请求队列\n     */\n    private final Map<Long, Queue<Runnable>> deviceRequestQueues = new ConcurrentHashMap<>();\n    /**\n     * per-device 上次请求时间戳：deviceId -> lastRequestTimeMs\n     */\n    private final Map<Long, Long> deviceLastRequestTime = new ConcurrentHashMap<>();\n    /**\n     * per-device 延迟 timer 标记：deviceId -> 是否有延迟 timer 在等待\n     */\n    private final Map<Long, Boolean> deviceDelayTimerActive = new ConcurrentHashMap<>();\n\n    protected AbstractIotModbusPollScheduler(Vertx vertx) {\n        this.vertx = vertx;\n    }\n\n    /**\n     * 点位定时器信息\n     */\n    @Data\n    @AllArgsConstructor\n    private static class PointTimerInfo {\n\n        /**\n         * Vert.x 定时器 ID\n         */\n        private Long timerId;\n        /**\n         * 轮询间隔（用于判断是否需要更新定时器）\n         */\n        private Integer pollInterval;\n\n    }\n\n    // ========== 轮询管理 ==========\n\n    /**\n     * 更新轮询任务（增量更新）\n     *\n     * 1. 【删除】点位：停止对应的轮询定时器\n     * 2. 【新增】点位：创建对应的轮询定时器\n     * 3. 【修改】点位：pollInterval 变化，重建对应的轮询定时器\n     *    【修改】其他属性变化：不需要重建定时器（pollPoint 运行时从 configCache 取最新 point）\n     */\n    public void updatePolling(IotModbusDeviceConfigRespDTO config) {\n        Long deviceId = config.getDeviceId();\n        List<IotModbusPointRespDTO> newPoints = config.getPoints();\n        Map<Long, PointTimerInfo> currentTimers = devicePointTimers\n                .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());\n        // 1.1 计算新配置中的点位 ID 集合\n        Set<Long> newPointIds = convertSet(newPoints, IotModbusPointRespDTO::getId);\n        // 1.2 计算删除的点位 ID 集合\n        Set<Long> removedPointIds = new HashSet<>(currentTimers.keySet());\n        removedPointIds.removeAll(newPointIds);\n\n        // 2. 处理删除的点位：停止不再存在的定时器\n        for (Long pointId : removedPointIds) {\n            PointTimerInfo timerInfo = currentTimers.remove(pointId);\n            if (timerInfo != null) {\n                vertx.cancelTimer(timerInfo.getTimerId());\n                log.debug(\"[updatePolling][设备 {} 点位 {} 定时器已删除]\", deviceId, pointId);\n            }\n        }\n\n        // 3. 处理新增和修改的点位\n        if (CollUtil.isEmpty(newPoints)) {\n            return;\n        }\n        for (IotModbusPointRespDTO point : newPoints) {\n            Long pointId = point.getId();\n            Integer newPollInterval = point.getPollInterval();\n            PointTimerInfo existingTimer = currentTimers.get(pointId);\n            // 3.1 新增点位：创建定时器\n            if (existingTimer == null) {\n                Long timerId = createPollTimer(deviceId, pointId, newPollInterval);\n                if (timerId != null) {\n                    currentTimers.put(pointId, new PointTimerInfo(timerId, newPollInterval));\n                    log.debug(\"[updatePolling][设备 {} 点位 {} 定时器已创建, interval={}ms]\",\n                            deviceId, pointId, newPollInterval);\n                }\n            } else if (!Objects.equals(existingTimer.getPollInterval(), newPollInterval)) {\n                // 3.2 pollInterval 变化：重建定时器\n                vertx.cancelTimer(existingTimer.getTimerId());\n                Long timerId = createPollTimer(deviceId, pointId, newPollInterval);\n                if (timerId != null) {\n                    currentTimers.put(pointId, new PointTimerInfo(timerId, newPollInterval));\n                    log.debug(\"[updatePolling][设备 {} 点位 {} 定时器已更新, interval={}ms -> {}ms]\",\n                            deviceId, pointId, existingTimer.getPollInterval(), newPollInterval);\n                } else {\n                    currentTimers.remove(pointId);\n                }\n            }\n            // 3.3 其他属性变化：无需重建定时器，因为 pollPoint() 运行时从 configCache 获取最新 point，自动使用新配置\n        }\n    }\n\n    /**\n     * 创建轮询定时器\n     */\n    private Long createPollTimer(Long deviceId, Long pointId, Integer pollInterval) {\n        if (pollInterval == null || pollInterval <= 0) {\n            return null;\n        }\n        return vertx.setPeriodic(pollInterval, timerId -> {\n            try {\n                submitPollRequest(deviceId, pointId);\n            } catch (Exception e) {\n                log.error(\"[createPollTimer][轮询点位失败, deviceId={}, pointId={}]\", deviceId, pointId, e);\n            }\n        });\n    }\n\n    // ========== 请求队列（per-device 限速） ==========\n\n    /**\n     * 提交轮询请求到设备请求队列（保证同设备请求间隔）\n     */\n    private void submitPollRequest(Long deviceId, Long pointId) {\n        // 1. 【重要】将请求添加到设备的请求队列\n        Queue<Runnable> queue = deviceRequestQueues.computeIfAbsent(deviceId, k -> new ConcurrentLinkedQueue<>());\n        while (queue.size() >= MAX_QUEUE_SIZE) {\n            // 超出上限时，丢弃最旧的请求\n            queue.poll();\n            log.warn(\"[submitPollRequest][设备 {} 请求队列已满({}), 丢弃最旧请求]\", deviceId, MAX_QUEUE_SIZE);\n        }\n        queue.offer(() -> pollPoint(deviceId, pointId));\n\n        // 2. 处理设备请求队列（如果没有延迟 timer 在等待）\n        processDeviceQueue(deviceId);\n    }\n\n    /**\n     * 处理设备请求队列\n     */\n    private void processDeviceQueue(Long deviceId) {\n        Queue<Runnable> queue = deviceRequestQueues.get(deviceId);\n        if (CollUtil.isEmpty(queue)) {\n            return;\n        }\n        // 检查是否已有延迟 timer 在等待\n        if (Boolean.TRUE.equals(deviceDelayTimerActive.get(deviceId))) {\n            return;\n        }\n\n        // 不满足间隔要求，延迟执行\n        long now = System.currentTimeMillis();\n        long lastTime = deviceLastRequestTime.getOrDefault(deviceId, 0L);\n        long elapsed = now - lastTime;\n        if (elapsed < MIN_REQUEST_INTERVAL) {\n            scheduleNextRequest(deviceId, MIN_REQUEST_INTERVAL - elapsed);\n            return;\n        }\n\n        // 满足间隔要求，立即执行\n        Runnable task = queue.poll();\n        if (task == null) {\n            return;\n        }\n        deviceLastRequestTime.put(deviceId, now);\n        task.run();\n        // 继续处理队列中的下一个（如果有的话，需要延迟）\n        if (CollUtil.isNotEmpty(queue)) {\n            scheduleNextRequest(deviceId);\n        }\n    }\n\n    private void scheduleNextRequest(Long deviceId) {\n        scheduleNextRequest(deviceId, MIN_REQUEST_INTERVAL);\n    }\n\n    private void scheduleNextRequest(Long deviceId, long delayMs) {\n        deviceDelayTimerActive.put(deviceId, true);\n        vertx.setTimer(delayMs, id -> {\n            deviceDelayTimerActive.put(deviceId, false);\n            Queue<Runnable> queue = deviceRequestQueues.get(deviceId);\n            if (CollUtil.isEmpty(queue)) {\n                return;\n            }\n\n            // 满足间隔要求，立即执行\n            Runnable task = queue.poll();\n            if (task == null) {\n                return;\n            }\n            deviceLastRequestTime.put(deviceId, System.currentTimeMillis());\n            task.run();\n            // 继续处理队列中的下一个（如果有的话，需要延迟）\n            if (CollUtil.isNotEmpty(queue)) {\n                scheduleNextRequest(deviceId);\n            }\n        });\n    }\n\n    // ========== 轮询执行 ==========\n\n    /**\n     * 轮询单个点位（子类实现具体的读取逻辑）\n     *\n     * @param deviceId 设备 ID\n     * @param pointId  点位 ID\n     */\n    protected abstract void pollPoint(Long deviceId, Long pointId);\n\n    // ========== 停止 ==========\n\n    /**\n     * 停止设备的轮询\n     */\n    public void stopPolling(Long deviceId) {\n        Map<Long, PointTimerInfo> timers = devicePointTimers.remove(deviceId);\n        if (CollUtil.isEmpty(timers)) {\n            return;\n        }\n        for (PointTimerInfo timerInfo : timers.values()) {\n            vertx.cancelTimer(timerInfo.getTimerId());\n        }\n        // 清理请求队列\n        deviceRequestQueues.remove(deviceId);\n        deviceLastRequestTime.remove(deviceId);\n        deviceDelayTimerActive.remove(deviceId);\n        log.debug(\"[stopPolling][设备 {} 停止了 {} 个轮询定时器]\", deviceId, timers.size());\n    }\n\n    /**\n     * 停止所有轮询\n     */\n    public void stopAll() {\n        for (Long deviceId : new ArrayList<>(devicePointTimers.keySet())) {\n            stopPolling(deviceId);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/utils/IotModbusCommonUtils.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusByteOrderEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusRawDataTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrame;\nimport lombok.experimental.UtilityClass;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\n\n/**\n * IoT Modbus 协议工具类\n * <p>\n * 提供 Modbus 协议全链路能力：\n * <ul>\n *   <li>协议常量：功能码（FC01~FC16）、异常掩码等</li>\n *   <li>功能码判断：读/写/异常分类、可写判断、写功能码映射</li>\n *   <li>CRC-16/MODBUS 计算和校验</li>\n *   <li>数据转换：原始值 ↔ 物模型属性值（{@link #convertToPropertyValue} / {@link #convertToRawValues}）</li>\n *   <li>帧值提取：从 Modbus 帧提取寄存器/线圈值（{@link #extractValues}）</li>\n *   <li>点位查找（{@link #findPoint}）</li>\n * </ul>\n *\n * @author 芋道源码\n */\n@UtilityClass\n@Slf4j\npublic class IotModbusCommonUtils {\n\n    /** FC01: 读线圈 */\n    public static final int FC_READ_COILS = 1;\n    /** FC02: 读离散输入 */\n    public static final int FC_READ_DISCRETE_INPUTS = 2;\n    /** FC03: 读保持寄存器 */\n    public static final int FC_READ_HOLDING_REGISTERS = 3;\n    /** FC04: 读输入寄存器 */\n    public static final int FC_READ_INPUT_REGISTERS = 4;\n\n    /** FC05: 写单个线圈 */\n    public static final int FC_WRITE_SINGLE_COIL = 5;\n    /** FC06: 写单个寄存器 */\n    public static final int FC_WRITE_SINGLE_REGISTER = 6;\n    /** FC15: 写多个线圈 */\n    public static final int FC_WRITE_MULTIPLE_COILS = 15;\n    /** FC16: 写多个寄存器 */\n    public static final int FC_WRITE_MULTIPLE_REGISTERS = 16;\n\n    /**\n     * 异常响应掩码：响应帧的功能码最高位为 1 时，表示异常响应\n     * 例如：请求 FC=0x03，异常响应 FC=0x83（0x03 | 0x80）\n     */\n    public static final int FC_EXCEPTION_MASK = 0x80;\n\n    /**\n     * 功能码掩码：用于从异常响应中提取原始功能码\n     * 例如：异常 FC=0x83，原始 FC = 0x83 & 0x7F = 0x03\n     */\n    public static final int FC_MASK = 0x7F;\n\n    // ==================== 功能码分类判断 ====================\n\n    /**\n     * 判断是否为读响应（FC01-04）\n     */\n    public static boolean isReadResponse(int functionCode) {\n        return functionCode >= FC_READ_COILS && functionCode <= FC_READ_INPUT_REGISTERS;\n    }\n\n    /**\n     * 判断是否为写响应（FC05/06/15/16）\n     */\n    public static boolean isWriteResponse(int functionCode) {\n        return functionCode == FC_WRITE_SINGLE_COIL || functionCode == FC_WRITE_SINGLE_REGISTER\n                || functionCode == FC_WRITE_MULTIPLE_COILS || functionCode == FC_WRITE_MULTIPLE_REGISTERS;\n    }\n\n    /**\n     * 判断是否为异常响应\n     */\n    public static boolean isExceptionResponse(int functionCode) {\n        return (functionCode & FC_EXCEPTION_MASK) != 0;\n    }\n\n    /**\n     * 从异常响应中提取原始功能码\n     */\n    public static int extractOriginalFunctionCode(int exceptionFunctionCode) {\n        return exceptionFunctionCode & FC_MASK;\n    }\n\n    /**\n     * 判断读功能码是否支持写操作\n     * <p>\n     * FC01（读线圈）和 FC03（读保持寄存器）支持写操作；\n     * FC02（读离散输入）和 FC04（读输入寄存器）为只读。\n     *\n     * @param readFunctionCode 读功能码（FC01-04）\n     * @return 是否支持写操作\n     */\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    public static boolean isWritable(int readFunctionCode) {\n        return readFunctionCode == FC_READ_COILS || readFunctionCode == FC_READ_HOLDING_REGISTERS;\n    }\n\n    /**\n     * 获取单写功能码\n     * <p>\n     * FC01（读线圈）→ FC05（写单个线圈）；\n     * FC03（读保持寄存器）→ FC06（写单个寄存器）；\n     * 其他返回 null（不支持写）。\n     *\n     * @param readFunctionCode 读功能码\n     * @return 单写功能码，不支持写时返回 null\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    public static Integer getWriteSingleFunctionCode(int readFunctionCode) {\n        switch (readFunctionCode) {\n            case FC_READ_COILS:\n                return FC_WRITE_SINGLE_COIL;\n            case FC_READ_HOLDING_REGISTERS:\n                return FC_WRITE_SINGLE_REGISTER;\n            default:\n                return null;\n        }\n    }\n\n    /**\n     * 获取多写功能码\n     * <p>\n     * FC01（读线圈）→ FC15（写多个线圈）；\n     * FC03（读保持寄存器）→ FC16（写多个寄存器）；\n     * 其他返回 null（不支持写）。\n     *\n     * @param readFunctionCode 读功能码\n     * @return 多写功能码，不支持写时返回 null\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    public static Integer getWriteMultipleFunctionCode(int readFunctionCode) {\n        switch (readFunctionCode) {\n            case FC_READ_COILS:\n                return FC_WRITE_MULTIPLE_COILS;\n            case FC_READ_HOLDING_REGISTERS:\n                return FC_WRITE_MULTIPLE_REGISTERS;\n            default:\n                return null;\n        }\n    }\n\n    // ==================== CRC16 工具 ====================\n\n    /**\n     * 计算 CRC-16/MODBUS\n     *\n     * @param data   数据\n     * @param length 计算长度\n     * @return CRC16 值\n     */\n    public static int calculateCrc16(byte[] data, int length) {\n        int crc = 0xFFFF;\n        for (int i = 0; i < length; i++) {\n            crc ^= (data[i] & 0xFF);\n            for (int j = 0; j < 8; j++) {\n                if ((crc & 0x0001) != 0) {\n                    crc >>= 1;\n                    crc ^= 0xA001;\n                } else {\n                    crc >>= 1;\n                }\n            }\n        }\n        return crc;\n    }\n\n    /**\n     * 校验 CRC16\n     *\n     * @param data 包含 CRC 的完整数据\n     * @return 校验是否通过\n     */\n    public static boolean verifyCrc16(byte[] data) {\n        if (data.length < 3) {\n            return false;\n        }\n        int computed = calculateCrc16(data, data.length - 2);\n        int received = (data[data.length - 2] & 0xFF) | ((data[data.length - 1] & 0xFF) << 8);\n        return computed == received;\n    }\n\n    // ==================== 数据转换 ====================\n\n    /**\n     * 将原始值转换为物模型属性值\n     *\n     * @param rawValues 原始值数组（寄存器值或线圈值）\n     * @param point     点位配置\n     * @return 转换后的属性值\n     */\n    public static Object convertToPropertyValue(int[] rawValues, IotModbusPointRespDTO point) {\n        if (ArrayUtil.isEmpty(rawValues)) {\n            return null;\n        }\n        String rawDataType = point.getRawDataType();\n        String byteOrder = point.getByteOrder();\n        BigDecimal scale = ObjectUtil.defaultIfNull(point.getScale(), BigDecimal.ONE);\n\n        // 1. 根据原始数据类型解析原始数值\n        Number rawNumber = parseRawValue(rawValues, rawDataType, byteOrder);\n        if (rawNumber == null) {\n            return null;\n        }\n\n        // 2. 应用缩放因子：实际值 = 原始值 × scale\n        BigDecimal actualValue = new BigDecimal(rawNumber.toString()).multiply(scale);\n\n        // 3. 根据数据类型返回合适的 Java 类型\n        return formatValue(actualValue, rawDataType);\n    }\n\n    /**\n     * 将物模型属性值转换为原始寄存器值\n     *\n     * @param propertyValue 属性值\n     * @param point         点位配置\n     * @return 原始值数组\n     */\n    public static int[] convertToRawValues(Object propertyValue, IotModbusPointRespDTO point) {\n        if (propertyValue == null) {\n            return new int[0];\n        }\n        String rawDataType = point.getRawDataType();\n        String byteOrder = point.getByteOrder();\n        BigDecimal scale = ObjectUtil.defaultIfNull(point.getScale(), BigDecimal.ONE);\n        int registerCount = ObjectUtil.defaultIfNull(point.getRegisterCount(), 1);\n\n        // 1. 转换为 BigDecimal\n        BigDecimal actualValue = new BigDecimal(propertyValue.toString());\n\n        // 2. 应用缩放因子：原始值 = 实际值 ÷ scale\n        BigDecimal rawValue = actualValue.divide(scale, 0, RoundingMode.HALF_UP);\n\n        // 3. 根据原始数据类型编码为寄存器值\n        return encodeToRegisters(rawValue, rawDataType, byteOrder, registerCount);\n    }\n\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private static Number parseRawValue(int[] rawValues, String rawDataType, String byteOrder) {\n        IotModbusRawDataTypeEnum dataTypeEnum = IotModbusRawDataTypeEnum.getByType(rawDataType);\n        if (dataTypeEnum == null) {\n            log.warn(\"[parseRawValue][不支持的数据类型: {}]\", rawDataType);\n            return rawValues[0];\n        }\n        switch (dataTypeEnum) {\n            case BOOLEAN:\n                return rawValues[0] != 0 ? 1 : 0;\n            case INT16:\n                return (short) rawValues[0];\n            case UINT16:\n                return rawValues[0] & 0xFFFF;\n            case INT32:\n                return parseInt32(rawValues, byteOrder);\n            case UINT32:\n                return parseUint32(rawValues, byteOrder);\n            case FLOAT:\n                return parseFloat(rawValues, byteOrder);\n            case DOUBLE:\n                return parseDouble(rawValues, byteOrder);\n            default:\n                log.warn(\"[parseRawValue][不支持的数据类型: {}]\", rawDataType);\n                return rawValues[0];\n        }\n    }\n\n    private static int parseInt32(int[] rawValues, String byteOrder) {\n        if (rawValues.length < 2) {\n            return rawValues[0];\n        }\n        byte[] bytes = reorderBytes(registersToBytes(rawValues, 2), byteOrder);\n        return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();\n    }\n\n    private static long parseUint32(int[] rawValues, String byteOrder) {\n        if (rawValues.length < 2) {\n            return rawValues[0] & 0xFFFFFFFFL;\n        }\n        byte[] bytes = reorderBytes(registersToBytes(rawValues, 2), byteOrder);\n        return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt() & 0xFFFFFFFFL;\n    }\n\n    private static float parseFloat(int[] rawValues, String byteOrder) {\n        if (rawValues.length < 2) {\n            return (float) rawValues[0];\n        }\n        byte[] bytes = reorderBytes(registersToBytes(rawValues, 2), byteOrder);\n        return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat();\n    }\n\n    private static double parseDouble(int[] rawValues, String byteOrder) {\n        if (rawValues.length < 4) {\n            return rawValues[0];\n        }\n        byte[] bytes = reorderBytes(registersToBytes(rawValues, 4), byteOrder);\n        return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getDouble();\n    }\n\n    private static byte[] registersToBytes(int[] registers, int count) {\n        byte[] bytes = new byte[count * 2];\n        for (int i = 0; i < Math.min(registers.length, count); i++) {\n            bytes[i * 2] = (byte) ((registers[i] >> 8) & 0xFF);\n            bytes[i * 2 + 1] = (byte) (registers[i] & 0xFF);\n        }\n        return bytes;\n    }\n\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private static byte[] reorderBytes(byte[] bytes, String byteOrder) {\n        IotModbusByteOrderEnum byteOrderEnum = IotModbusByteOrderEnum.getByOrder(byteOrder);\n        // null 或者大端序，不需要调整\n        if (ObjectUtils.equalsAny(byteOrderEnum, null, IotModbusByteOrderEnum.ABCD, IotModbusByteOrderEnum.AB)) {\n            return bytes;\n        }\n\n        // 其他字节序调整\n        byte[] result = new byte[bytes.length];\n        switch (byteOrderEnum) {\n            case BA: // 小端序：按每 2 字节一组交换（16 位场景 [1,0]，32 位场景 [1,0,3,2]）\n                for (int i = 0; i + 1 < bytes.length; i += 2) {\n                    result[i] = bytes[i + 1];\n                    result[i + 1] = bytes[i];\n                }\n                break;\n            case CDAB: // 大端字交换（32 位）\n                if (bytes.length >= 4) {\n                    result[0] = bytes[2];\n                    result[1] = bytes[3];\n                    result[2] = bytes[0];\n                    result[3] = bytes[1];\n                }\n                break;\n            case DCBA: // 小端序（32 位）\n                if (bytes.length >= 4) {\n                    result[0] = bytes[3];\n                    result[1] = bytes[2];\n                    result[2] = bytes[1];\n                    result[3] = bytes[0];\n                }\n                break;\n            case BADC: // 小端字交换（32 位）\n                if (bytes.length >= 4) {\n                    result[0] = bytes[1];\n                    result[1] = bytes[0];\n                    result[2] = bytes[3];\n                    result[3] = bytes[2];\n                }\n                break;\n            default:\n                return bytes;\n        }\n        return result;\n    }\n\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private static int[] encodeToRegisters(BigDecimal rawValue, String rawDataType, String byteOrder, int registerCount) {\n        IotModbusRawDataTypeEnum dataTypeEnum = IotModbusRawDataTypeEnum.getByType(rawDataType);\n        if (dataTypeEnum == null) {\n            return new int[]{rawValue.intValue()};\n        }\n        switch (dataTypeEnum) {\n            case BOOLEAN:\n                return new int[]{rawValue.intValue() != 0 ? 1 : 0};\n            case INT16:\n            case UINT16:\n                return new int[]{rawValue.intValue() & 0xFFFF};\n            case INT32:\n                return encodeInt32(rawValue.intValue(), byteOrder);\n            case UINT32:\n                // 使用 longValue() 避免超过 Integer.MAX_VALUE 时溢出，\n                // 强转 int 保留低 32 位 bit pattern，写入寄存器的字节是正确的无符号值\n                return encodeInt32((int) rawValue.longValue(), byteOrder);\n            case FLOAT:\n                return encodeFloat(rawValue.floatValue(), byteOrder);\n            case DOUBLE:\n                return encodeDouble(rawValue.doubleValue(), byteOrder);\n            default:\n                return new int[]{rawValue.intValue()};\n        }\n    }\n\n    private static int[] encodeInt32(int value, String byteOrder) {\n        byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(value).array();\n        bytes = reorderBytes(bytes, byteOrder);\n        return bytesToRegisters(bytes);\n    }\n\n    private static int[] encodeFloat(float value, String byteOrder) {\n        byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(value).array();\n        bytes = reorderBytes(bytes, byteOrder);\n        return bytesToRegisters(bytes);\n    }\n\n    private static int[] encodeDouble(double value, String byteOrder) {\n        byte[] bytes = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putDouble(value).array();\n        bytes = reorderBytes(bytes, byteOrder);\n        return bytesToRegisters(bytes);\n    }\n\n    private static int[] bytesToRegisters(byte[] bytes) {\n        int[] registers = new int[bytes.length / 2];\n        for (int i = 0; i < registers.length; i++) {\n            registers[i] = ((bytes[i * 2] & 0xFF) << 8) | (bytes[i * 2 + 1] & 0xFF);\n        }\n        return registers;\n    }\n\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private static Object formatValue(BigDecimal value, String rawDataType) {\n        IotModbusRawDataTypeEnum dataTypeEnum = IotModbusRawDataTypeEnum.getByType(rawDataType);\n        if (dataTypeEnum == null) {\n            return value;\n        }\n        switch (dataTypeEnum) {\n            case BOOLEAN:\n                return value.intValue() != 0;\n            case INT16:\n            case INT32:\n                return value.intValue();\n            case UINT16:\n            case UINT32:\n                return value.longValue();\n            case FLOAT:\n                return value.floatValue();\n            case DOUBLE:\n                return value.doubleValue();\n            default:\n                return value;\n        }\n    }\n\n    // ==================== 帧值提取 ====================\n\n    /**\n     * 从帧中提取寄存器值（FC01-04 读响应）\n     *\n     * @param frame 解码后的 Modbus 帧\n     * @return 寄存器值数组（int[]），失败返回 null\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    public static int[] extractValues(IotModbusFrame frame) {\n        if (frame == null || frame.isException()) {\n            return null;\n        }\n        byte[] pdu = frame.getPdu();\n        if (pdu == null || pdu.length < 1) {\n            return null;\n        }\n\n        int functionCode = frame.getFunctionCode();\n        switch (functionCode) {\n            case FC_READ_COILS:\n            case FC_READ_DISCRETE_INPUTS:\n                return extractCoilValues(pdu);\n            case FC_READ_HOLDING_REGISTERS:\n            case FC_READ_INPUT_REGISTERS:\n                return extractRegisterValues(pdu);\n            default:\n                log.warn(\"[extractValues][不支持的功能码: {}]\", functionCode);\n                return null;\n        }\n    }\n\n    private static int[] extractCoilValues(byte[] pdu) {\n        if (pdu.length < 2) {\n            return null;\n        }\n        int byteCount = pdu[0] & 0xFF;\n        int bitCount = byteCount * 8;\n        int[] values = new int[bitCount];\n        for (int i = 0; i < bitCount && (1 + i / 8) < pdu.length; i++) {\n            values[i] = ((pdu[1 + i / 8] >> (i % 8)) & 0x01);\n        }\n        return values;\n    }\n\n    private static int[] extractRegisterValues(byte[] pdu) {\n        if (pdu.length < 2) {\n            return null;\n        }\n        int byteCount = pdu[0] & 0xFF;\n        int registerCount = byteCount / 2;\n        int[] values = new int[registerCount];\n        for (int i = 0; i < registerCount && (1 + i * 2 + 1) < pdu.length; i++) {\n            values[i] = ((pdu[1 + i * 2] & 0xFF) << 8) | (pdu[1 + i * 2 + 1] & 0xFF);\n        }\n        return values;\n    }\n\n    /**\n     * 从响应帧中提取 registerCount（通过 PDU 的 byteCount 推断）\n     *\n     * @param frame 解码后的 Modbus 响应帧\n     * @return registerCount，无法提取时返回 -1（匹配时跳过校验）\n     */\n    public static int extractRegisterCountFromResponse(IotModbusFrame frame) {\n        byte[] pdu = frame.getPdu();\n        if (pdu == null || pdu.length < 1) {\n            return -1;\n        }\n        int byteCount = pdu[0] & 0xFF;\n        int fc = frame.getFunctionCode();\n        // FC03/04 寄存器读响应：registerCount = byteCount / 2\n        if (fc == FC_READ_HOLDING_REGISTERS || fc == FC_READ_INPUT_REGISTERS) {\n            return byteCount / 2;\n        }\n        // FC01/02 线圈/离散输入读响应：按 bit 打包有余位，无法精确反推，返回 -1 跳过校验\n        return -1;\n    }\n\n    // ==================== 点位查找 ====================\n\n    /**\n     * 查找点位配置\n     *\n     * @param config     设备 Modbus 配置\n     * @param identifier 点位标识符\n     * @return 匹配的点位配置，未找到返回 null\n     */\n    public static IotModbusPointRespDTO findPoint(IotModbusDeviceConfigRespDTO config, String identifier) {\n        if (config == null || StrUtil.isBlank(identifier)) {\n            return null;\n        }\n        return CollUtil.findOne(config.getPoints(), p -> identifier.equals(p.getIdentifier()));\n    }\n\n    /**\n     * 根据点位 ID 查找点位配置\n     *\n     * @param config  设备 Modbus 配置\n     * @param pointId 点位 ID\n     * @return 匹配的点位配置，未找到返回 null\n     */\n    public static IotModbusPointRespDTO findPointById(IotModbusDeviceConfigRespDTO config, Long pointId) {\n        if (config == null || pointId == null) {\n            return null;\n        }\n        return CollUtil.findOne(config.getPoints(), p -> p.getId().equals(pointId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/utils/IotModbusTcpClientUtils.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils;\n\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.manager.IotModbusTcpClientConnectionManager;\nimport com.ghgande.j2mod.modbus.io.ModbusTCPTransaction;\nimport com.ghgande.j2mod.modbus.msg.*;\nimport com.ghgande.j2mod.modbus.procimg.InputRegister;\nimport com.ghgande.j2mod.modbus.procimg.Register;\nimport com.ghgande.j2mod.modbus.procimg.SimpleRegister;\nimport com.ghgande.j2mod.modbus.util.BitVector;\nimport io.vertx.core.Future;\nimport lombok.experimental.UtilityClass;\nimport lombok.extern.slf4j.Slf4j;\n\nimport static cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils.*;\n\n/**\n * IoT Modbus TCP 客户端工具类\n * <p>\n * 封装基于 j2mod 的 Modbus TCP 读写操作：\n * 1. 根据功能码创建对应的 Modbus 读/写请求\n * 2. 通过 {@link IotModbusTcpClientConnectionManager.ModbusConnection} 执行事务\n * 3. 从响应中提取原始值\n *\n * @author 芋道源码\n */\n@UtilityClass\n@Slf4j\npublic class IotModbusTcpClientUtils {\n\n    /**\n     * 读取 Modbus 数据\n     *\n     * @param connection Modbus 连接\n     * @param slaveId    从站地址\n     * @param point      点位配置\n     * @return 原始值（int 数组）\n     */\n    public static Future<int[]> read(IotModbusTcpClientConnectionManager.ModbusConnection connection,\n                                     Integer slaveId,\n                                     IotModbusPointRespDTO point) {\n        return connection.executeBlocking(tcpConnection -> {\n            try {\n                // 1. 创建请求\n                ModbusRequest request = createReadRequest(point.getFunctionCode(),\n                        point.getRegisterAddress(), point.getRegisterCount());\n                request.setUnitID(slaveId);\n\n                // 2. 执行事务（请求）\n                ModbusTCPTransaction transaction = new ModbusTCPTransaction(tcpConnection);\n                transaction.setRequest(request);\n                transaction.execute();\n\n                // 3. 解析响应\n                ModbusResponse response = transaction.getResponse();\n                return extractValues(response, point.getFunctionCode());\n            } catch (Exception e) {\n                throw new RuntimeException(String.format(\"Modbus 读取失败 [slaveId=%d, identifier=%s, address=%d]\",\n                        slaveId, point.getIdentifier(), point.getRegisterAddress()), e);\n            }\n        });\n    }\n\n    /**\n     * 写入 Modbus 数据\n     *\n     * @param connection Modbus 连接\n     * @param slaveId    从站地址\n     * @param point      点位配置\n     * @param values     要写入的值\n     * @return 是否成功\n     */\n    public static Future<Boolean> write(IotModbusTcpClientConnectionManager.ModbusConnection connection,\n                                        Integer slaveId,\n                                        IotModbusPointRespDTO point,\n                                        int[] values) {\n        return connection.executeBlocking(tcpConnection -> {\n            try {\n                // 1. 创建请求\n                ModbusRequest request = createWriteRequest(point.getFunctionCode(),\n                        point.getRegisterAddress(), point.getRegisterCount(), values);\n                if (request == null) {\n                    throw new RuntimeException(\"功能码 \" + point.getFunctionCode() + \" 不支持写操作\");\n                }\n                request.setUnitID(slaveId);\n\n                // 2. 执行事务（请求）\n                ModbusTCPTransaction transaction = new ModbusTCPTransaction(tcpConnection);\n                transaction.setRequest(request);\n                transaction.execute();\n                return true;\n            } catch (Exception e) {\n                throw new RuntimeException(String.format(\"Modbus 写入失败 [slaveId=%d, identifier=%s, address=%d]\",\n                        slaveId, point.getIdentifier(), point.getRegisterAddress()), e);\n            }\n        });\n    }\n\n    /**\n     * 创建读取请求\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private static ModbusRequest createReadRequest(Integer functionCode, Integer address, Integer count) {\n        switch (functionCode) {\n            case FC_READ_COILS:\n                return new ReadCoilsRequest(address, count);\n            case FC_READ_DISCRETE_INPUTS:\n                return new ReadInputDiscretesRequest(address, count);\n            case FC_READ_HOLDING_REGISTERS:\n                return new ReadMultipleRegistersRequest(address, count);\n            case FC_READ_INPUT_REGISTERS:\n                return new ReadInputRegistersRequest(address, count);\n            default:\n                throw new IllegalArgumentException(\"不支持的功能码: \" + functionCode);\n        }\n    }\n\n    /**\n     * 创建写入请求\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private static ModbusRequest createWriteRequest(Integer functionCode, Integer address, Integer count, int[] values) {\n        switch (functionCode) {\n            case FC_READ_COILS: // 写线圈（使用功能码 5 或 15）\n                if (count == 1) {\n                    return new WriteCoilRequest(address, values[0] != 0);\n                } else {\n                    BitVector bv = new BitVector(count);\n                    for (int i = 0; i < Math.min(values.length, count); i++) {\n                        bv.setBit(i, values[i] != 0);\n                    }\n                    return new WriteMultipleCoilsRequest(address, bv);\n                }\n            case FC_READ_HOLDING_REGISTERS: // 写保持寄存器（使用功能码 6 或 16）\n                if (count == 1) {\n                    return new WriteSingleRegisterRequest(address, new SimpleRegister(values[0]));\n                } else {\n                    Register[] registers = new SimpleRegister[count];\n                    for (int i = 0; i < count; i++) {\n                        registers[i] = new SimpleRegister(i < values.length ? values[i] : 0);\n                    }\n                    return new WriteMultipleRegistersRequest(address, registers);\n                }\n            case FC_READ_DISCRETE_INPUTS: // 只读\n            case FC_READ_INPUT_REGISTERS: // 只读\n                return null;\n            default:\n                throw new IllegalArgumentException(\"不支持的功能码: \" + functionCode);\n        }\n    }\n\n    /**\n     * 从响应中提取值\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private static int[] extractValues(ModbusResponse response, Integer functionCode) {\n        switch (functionCode) {\n            case FC_READ_COILS:\n                ReadCoilsResponse coilsResponse = (ReadCoilsResponse) response;\n                int bitCount = coilsResponse.getBitCount();\n                int[] coilValues = new int[bitCount];\n                for (int i = 0; i < bitCount; i++) {\n                    coilValues[i] = coilsResponse.getCoilStatus(i) ? 1 : 0;\n                }\n                return coilValues;\n            case FC_READ_DISCRETE_INPUTS:\n                ReadInputDiscretesResponse discretesResponse = (ReadInputDiscretesResponse) response;\n                int discreteCount = discretesResponse.getBitCount();\n                int[] discreteValues = new int[discreteCount];\n                for (int i = 0; i < discreteCount; i++) {\n                    discreteValues[i] = discretesResponse.getDiscreteStatus(i) ? 1 : 0;\n                }\n                return discreteValues;\n            case FC_READ_HOLDING_REGISTERS:\n                ReadMultipleRegistersResponse holdingResponse = (ReadMultipleRegistersResponse) response;\n                InputRegister[] holdingRegisters = holdingResponse.getRegisters();\n                int[] holdingValues = new int[holdingRegisters.length];\n                for (int i = 0; i < holdingRegisters.length; i++) {\n                    holdingValues[i] = holdingRegisters[i].getValue();\n                }\n                return holdingValues;\n            case FC_READ_INPUT_REGISTERS:\n                ReadInputRegistersResponse inputResponse = (ReadInputRegistersResponse) response;\n                InputRegister[] inputRegisters = inputResponse.getRegisters();\n                int[] inputValues = new int[inputRegisters.length];\n                for (int i = 0; i < inputRegisters.length; i++) {\n                    inputValues[i] = inputRegisters[i].getValue();\n                }\n                return inputValues;\n            default:\n                throw new IllegalArgumentException(\"不支持的功能码: \" + functionCode);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/IotModbusTcpClientConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\n\n/**\n * IoT Modbus TCP Client 协议配置\n *\n * @author 芋道源码\n */\n@Data\npublic class IotModbusTcpClientConfig {\n\n    /**\n     * 配置刷新间隔（秒）\n     */\n    @NotNull(message = \"配置刷新间隔不能为空\")\n    @Min(value = 1, message = \"配置刷新间隔不能小于 1 秒\")\n    private Integer configRefreshInterval = 30;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/IotModbusTcpClientProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.handler.downstream.IotModbusTcpClientDownstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.handler.downstream.IotModbusTcpClientDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.handler.upstream.IotModbusTcpClientUpstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.manager.IotModbusTcpClientConfigCacheService;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.manager.IotModbusTcpClientConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.manager.IotModbusTcpClientPollScheduler;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.core.Vertx;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.redisson.api.RedissonClient;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * IoT 网关 Modbus TCP Client 协议：主动轮询 Modbus 从站设备数据\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpClientProtocol implements IotProtocol {\n\n    /**\n     * 协议配置\n     */\n    private final ProtocolProperties properties;\n    /**\n     * 服务器 ID（用于消息追踪，全局唯一）\n     */\n    @Getter\n    private final String serverId;\n\n    /**\n     * 运行状态\n     */\n    @Getter\n    private volatile boolean running = false;\n\n    /**\n     * Vert.x 实例\n     */\n    private final Vertx vertx;\n    /**\n     * 配置刷新定时器 ID\n     */\n    private Long configRefreshTimerId;\n\n    /**\n     * 连接管理器\n     */\n    private final IotModbusTcpClientConnectionManager connectionManager;\n    /**\n     * 下行消息订阅者\n     */\n    private IotModbusTcpClientDownstreamSubscriber downstreamSubscriber;\n\n    private final IotModbusTcpClientConfigCacheService configCacheService;\n    private final IotModbusTcpClientPollScheduler pollScheduler;\n\n    public IotModbusTcpClientProtocol(ProtocolProperties properties) {\n        IotModbusTcpClientConfig modbusTcpClientConfig = properties.getModbusTcpClient();\n        Assert.notNull(modbusTcpClientConfig, \"Modbus TCP Client 协议配置（modbusTcpClient）不能为空\");\n        this.properties = properties;\n        this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());\n\n        // 初始化 Vertx\n        this.vertx = Vertx.vertx();\n\n        // 初始化 Manager\n        RedissonClient redissonClient = SpringUtil.getBean(RedissonClient.class);\n        IotDeviceCommonApi deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n        IotDeviceMessageService messageService = SpringUtil.getBean(IotDeviceMessageService.class);\n        this.configCacheService = new IotModbusTcpClientConfigCacheService(deviceApi);\n        this.connectionManager = new IotModbusTcpClientConnectionManager(redissonClient, vertx,\n                messageService, configCacheService, serverId);\n\n        // 初始化 Handler\n        IotModbusTcpClientUpstreamHandler upstreamHandler = new IotModbusTcpClientUpstreamHandler(messageService, serverId);\n\n        // 初始化轮询调度器\n        this.pollScheduler = new IotModbusTcpClientPollScheduler(vertx, connectionManager, upstreamHandler, configCacheService);\n    }\n\n    @Override\n    public String getId() {\n        return properties.getId();\n    }\n\n    @Override\n    public IotProtocolTypeEnum getType() {\n        return IotProtocolTypeEnum.MODBUS_TCP_CLIENT;\n    }\n\n    @Override\n    public void start() {\n        if (running) {\n            log.warn(\"[start][IoT Modbus TCP Client 协议 {} 已经在运行中]\", getId());\n            return;\n        }\n\n        try {\n            // 1.1 首次加载配置\n            refreshConfig();\n            // 1.2 启动配置刷新定时器\n            int refreshInterval = properties.getModbusTcpClient().getConfigRefreshInterval();\n            configRefreshTimerId = vertx.setPeriodic(\n                    TimeUnit.SECONDS.toMillis(refreshInterval),\n                    id -> refreshConfig()\n            );\n            running = true;\n            log.info(\"[start][IoT Modbus TCP Client 协议 {} 启动成功，serverId={}]\", getId(), serverId);\n\n            // 2. 启动下行消息订阅者\n            IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);\n            IotModbusTcpClientDownstreamHandler downstreamHandler = new IotModbusTcpClientDownstreamHandler(connectionManager,\n                    configCacheService);\n            this.downstreamSubscriber = new IotModbusTcpClientDownstreamSubscriber(this, downstreamHandler, messageBus);\n            this.downstreamSubscriber.start();\n        } catch (Exception e) {\n            log.error(\"[start][IoT Modbus TCP Client 协议 {} 启动失败]\", getId(), e);\n            stop0();\n            throw e;\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        stop0();\n    }\n\n    private void stop0() {\n        // 1. 停止下行消息订阅者\n        if (downstreamSubscriber != null) {\n            try {\n                downstreamSubscriber.stop();\n                log.info(\"[stop][IoT Modbus TCP Client 协议 {} 下行消息订阅者已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT Modbus TCP Client 协议 {} 下行消息订阅者停止失败]\", getId(), e);\n            }\n            downstreamSubscriber = null;\n        }\n\n        // 2.1 取消配置刷新定时器\n        if (configRefreshTimerId != null) {\n            vertx.cancelTimer(configRefreshTimerId);\n            configRefreshTimerId = null;\n        }\n        // 2.2 停止轮询调度器\n        pollScheduler.stopAll();\n        // 2.3 关闭所有连接\n        connectionManager.closeAll();\n\n        // 3. 关闭 Vert.x 实例\n        if (vertx != null) {\n            try {\n                vertx.close().result();\n                log.info(\"[stop][IoT Modbus TCP Client 协议 {} Vertx 已关闭]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT Modbus TCP Client 协议 {} Vertx 关闭失败]\", getId(), e);\n            }\n        }\n        running = false;\n        log.info(\"[stop][IoT Modbus TCP Client 协议 {} 已停止]\", getId());\n    }\n\n    /**\n     * 刷新配置\n     */\n    private synchronized void refreshConfig() {\n        try {\n            // 1. 从 biz 拉取最新配置（API 失败时返回 null）\n            List<IotModbusDeviceConfigRespDTO> configs = configCacheService.refreshConfig();\n            if (configs == null) {\n                log.warn(\"[refreshConfig][API 失败，跳过本轮刷新]\");\n                return;\n            }\n            log.debug(\"[refreshConfig][获取到 {} 个 Modbus 设备配置]\", configs.size());\n\n            // 2. 更新连接和轮询任务\n            for (IotModbusDeviceConfigRespDTO config : configs) {\n                try {\n                    // 2.1 确保连接存在\n                    connectionManager.ensureConnection(config);\n                    // 2.2 更新轮询任务\n                    pollScheduler.updatePolling(config);\n                } catch (Exception e) {\n                    log.error(\"[refreshConfig][处理设备配置失败, deviceId={}]\", config.getDeviceId(), e);\n                }\n            }\n\n            // 3. 清理已删除设备的资源\n            Set<Long> removedDeviceIds = configCacheService.cleanupRemovedDevices(configs);\n            for (Long deviceId : removedDeviceIds) {\n                pollScheduler.stopPolling(deviceId);\n                connectionManager.removeDevice(deviceId);\n            }\n        } catch (Exception e) {\n            log.error(\"[refreshConfig][刷新配置失败]\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/handler/downstream/IotModbusTcpClientDownstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.handler.downstream;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusTcpClientUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.manager.IotModbusTcpClientConfigCacheService;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.manager.IotModbusTcpClientConnectionManager;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Map;\n\n/**\n * IoT Modbus TCP Client 下行消息处理器\n * <p>\n * 负责：\n * 1. 处理下行消息（如属性设置 thing.service.property.set）\n * 2. 将属性值转换为 Modbus 写指令，通过 TCP 连接发送给设备\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class IotModbusTcpClientDownstreamHandler {\n\n    private final IotModbusTcpClientConnectionManager connectionManager;\n    private final IotModbusTcpClientConfigCacheService configCacheService;\n\n    /**\n     * 处理下行消息\n     */\n    @SuppressWarnings({\"unchecked\", \"DuplicatedCode\"})\n    public void handle(IotDeviceMessage message) {\n        // 1.1 检查是否是属性设置消息\n        if (ObjUtil.equals(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(), message.getMethod())) {\n            return;\n        }\n        if (ObjUtil.notEqual(IotDeviceMessageMethodEnum.PROPERTY_SET.getMethod(), message.getMethod())) {\n            log.warn(\"[handle][忽略非属性设置消息: {}]\", message.getMethod());\n            return;\n        }\n        // 1.2 获取设备配置\n        IotModbusDeviceConfigRespDTO config = configCacheService.getConfig(message.getDeviceId());\n        if (config == null) {\n            log.warn(\"[handle][设备 {} 没有 Modbus 配置]\", message.getDeviceId());\n            return;\n        }\n\n        // 2. 解析属性值并写入\n        Object params = message.getParams();\n        if (!(params instanceof Map)) {\n            log.warn(\"[handle][params 不是 Map 类型: {}]\", params);\n            return;\n        }\n        Map<String, Object> propertyMap = (Map<String, Object>) params;\n        for (Map.Entry<String, Object> entry : propertyMap.entrySet()) {\n            String identifier = entry.getKey();\n            Object value = entry.getValue();\n            // 2.1 查找对应的点位配置\n            IotModbusPointRespDTO point = IotModbusCommonUtils.findPoint(config, identifier);\n            if (point == null) {\n                log.warn(\"[handle][设备 {} 没有点位配置: {}]\", message.getDeviceId(), identifier);\n                continue;\n            }\n            // 2.2 检查是否支持写操作\n            if (!IotModbusCommonUtils.isWritable(point.getFunctionCode())) {\n                log.warn(\"[handle][点位 {} 不支持写操作, 功能码={}]\", identifier, point.getFunctionCode());\n                continue;\n            }\n\n            // 2.3 执行写入\n            writeProperty(config, point, value);\n        }\n    }\n\n    /**\n     * 写入属性值\n     */\n    private void writeProperty(IotModbusDeviceConfigRespDTO config, IotModbusPointRespDTO point, Object value) {\n        // 1.1 获取连接\n        IotModbusTcpClientConnectionManager.ModbusConnection connection = connectionManager.getConnection(config.getDeviceId());\n        if (connection == null) {\n            log.warn(\"[writeProperty][设备 {} 没有连接]\", config.getDeviceId());\n            return;\n        }\n        // 1.2 获取 slave ID\n        Integer slaveId = connectionManager.getSlaveId(config.getDeviceId());\n        if (slaveId == null) {\n            log.warn(\"[writeProperty][设备 {} 没有 slaveId]\", config.getDeviceId());\n            return;\n        }\n\n        // 2.1 转换属性值为原始值\n        int[] rawValues = IotModbusCommonUtils.convertToRawValues(value, point);\n        // 2.2 执行 Modbus 写入\n        IotModbusTcpClientUtils.write(connection, slaveId, point, rawValues)\n                .onSuccess(success -> log.info(\"[writeProperty][写入成功, deviceId={}, identifier={}, value={}]\",\n                        config.getDeviceId(), point.getIdentifier(), value))\n                .onFailure(e -> log.error(\"[writeProperty][写入失败, deviceId={}, identifier={}]\",\n                        config.getDeviceId(), point.getIdentifier(), e));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/handler/downstream/IotModbusTcpClientDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.IotModbusTcpClientProtocol;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT Modbus TCP 下行消息订阅器：订阅消息总线的下行消息并转发给处理器\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpClientDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber {\n\n    private final IotModbusTcpClientDownstreamHandler downstreamHandler;\n\n    public IotModbusTcpClientDownstreamSubscriber(IotModbusTcpClientProtocol protocol,\n                                                  IotModbusTcpClientDownstreamHandler downstreamHandler,\n                                                  IotMessageBus messageBus) {\n        super(protocol, messageBus);\n        this.downstreamHandler = downstreamHandler;\n    }\n\n    @Override\n    protected void handleMessage(IotDeviceMessage message) {\n        downstreamHandler.handle(message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/handler/upstream/IotModbusTcpClientUpstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.handler.upstream;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Map;\n\n/**\n * IoT Modbus TCP 上行数据处理器：将原始值转换为物模型属性值并上报\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpClientUpstreamHandler {\n\n    private final IotDeviceMessageService messageService;\n\n    private final String serverId;\n\n    public IotModbusTcpClientUpstreamHandler(IotDeviceMessageService messageService,\n                                             String serverId) {\n        this.messageService = messageService;\n        this.serverId = serverId;\n    }\n\n    /**\n     * 处理 Modbus 读取结果\n     *\n     * @param config   设备配置\n     * @param point    点位配置\n     * @param rawValue 原始值（int 数组）\n     */\n    public void handleReadResult(IotModbusDeviceConfigRespDTO config,\n                                 IotModbusPointRespDTO point,\n                                 int[] rawValue) {\n        try {\n            // 1.1 转换原始值为物模型属性值（点位翻译）\n            Object convertedValue = IotModbusCommonUtils.convertToPropertyValue(rawValue, point);\n            log.debug(\"[handleReadResult][设备={}, 属性={}, 原始值={}, 转换值={}]\",\n                    config.getDeviceId(), point.getIdentifier(), rawValue, convertedValue);\n            // 1.2 构造属性上报消息\n            Map<String, Object> params = MapUtil.of(point.getIdentifier(), convertedValue);\n            IotDeviceMessage message = IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(), params);\n\n            // 2. 发送到消息总线\n            messageService.sendDeviceMessage(message, config.getProductKey(),\n                    config.getDeviceName(), serverId);\n        } catch (Exception e) {\n            log.error(\"[handleReadResult][处理读取结果失败, deviceId={}, identifier={}]\",\n                    config.getDeviceId(), point.getIdentifier(), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/manager/IotModbusTcpClientConfigCacheService.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.manager;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusModeEnum;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n/**\n * IoT Modbus TCP Client 配置缓存服务\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class IotModbusTcpClientConfigCacheService {\n\n    private final IotDeviceCommonApi deviceApi;\n\n    /**\n     * 配置缓存：deviceId -> 配置\n     */\n    private final Map<Long, IotModbusDeviceConfigRespDTO> configCache = new ConcurrentHashMap<>();\n\n    /**\n     * 已知的设备 ID 集合（作用：用于检测已删除的设备）\n     *\n     * @see #cleanupRemovedDevices(List)\n     */\n    private final Set<Long> knownDeviceIds = ConcurrentHashMap.newKeySet();\n\n    /**\n     * 刷新配置\n     *\n     * @return 最新的配置列表；API 失败时返回 null（调用方应跳过 cleanup）\n     */\n    public List<IotModbusDeviceConfigRespDTO> refreshConfig() {\n        try {\n            // 1. 从远程获取配置\n            CommonResult<List<IotModbusDeviceConfigRespDTO>> result = deviceApi.getModbusDeviceConfigList(\n                    new IotModbusDeviceConfigListReqDTO().setStatus(CommonStatusEnum.ENABLE.getStatus())\n                            .setMode(IotModbusModeEnum.POLLING.getMode()).setProtocolType(IotProtocolTypeEnum.MODBUS_TCP_CLIENT.getType()));\n            result.checkError();\n            List<IotModbusDeviceConfigRespDTO> configs = result.getData();\n\n            // 2. 更新缓存（注意：不在这里更新 knownDeviceIds，由 cleanupRemovedDevices 统一管理）\n            for (IotModbusDeviceConfigRespDTO config : configs) {\n                configCache.put(config.getDeviceId(), config);\n            }\n            return configs;\n        } catch (Exception e) {\n            log.error(\"[refreshConfig][刷新配置失败]\", e);\n            return null;\n        }\n    }\n\n    /**\n     * 获取设备配置\n     *\n     * @param deviceId 设备 ID\n     * @return 配置\n     */\n    public IotModbusDeviceConfigRespDTO getConfig(Long deviceId) {\n        return configCache.get(deviceId);\n    }\n\n    /**\n     * 计算已删除设备的 ID 集合，清理缓存，并更新已知设备 ID 集合\n     *\n     * @param currentConfigs 当前有效的配置列表\n     * @return 已删除的设备 ID 集合\n     */\n    public Set<Long> cleanupRemovedDevices(List<IotModbusDeviceConfigRespDTO> currentConfigs) {\n        // 1.1 获取当前有效的设备 ID\n        Set<Long> currentDeviceIds = convertSet(currentConfigs, IotModbusDeviceConfigRespDTO::getDeviceId);\n        // 1.2 找出已删除的设备（基于旧的 knownDeviceIds）\n        Set<Long> removedDeviceIds = new HashSet<>(knownDeviceIds);\n        removedDeviceIds.removeAll(currentDeviceIds);\n\n        // 2. 清理已删除设备的缓存\n        for (Long deviceId : removedDeviceIds) {\n            log.info(\"[cleanupRemovedDevices][清理已删除设备: {}]\", deviceId);\n            configCache.remove(deviceId);\n        }\n\n        // 3. 更新已知设备 ID 集合为当前有效的设备 ID\n        knownDeviceIds.clear();\n        knownDeviceIds.addAll(currentDeviceIds);\n        return removedDeviceIds;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/manager/IotModbusTcpClientConnectionManager.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.manager;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport com.ghgande.j2mod.modbus.net.TCPMasterConnection;\nimport io.vertx.core.Context;\nimport io.vertx.core.Future;\nimport io.vertx.core.Vertx;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.redisson.api.RLock;\nimport org.redisson.api.RedissonClient;\n\nimport java.net.InetAddress;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * IoT Modbus TCP 连接管理器\n * <p>\n * 统一管理 Modbus TCP 连接：\n * 1. 管理 TCP 连接（相同 ip:port 共用连接）\n * 2. 分布式锁管理（连接级别），避免多节点重复创建连接\n * 3. 连接重试和故障恢复\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpClientConnectionManager {\n\n    private static final String LOCK_KEY_PREFIX = \"iot:modbus-tcp:connection:\";\n\n    private final RedissonClient redissonClient;\n    private final Vertx vertx;\n    private final IotDeviceMessageService messageService;\n    private final IotModbusTcpClientConfigCacheService configCacheService;\n    private final String serverId;\n\n    /**\n     * 连接池：key = ip:port\n     */\n    private final Map<String, ModbusConnection> connectionPool = new ConcurrentHashMap<>();\n\n    /**\n     * 设备 ID 到连接 key 的映射\n     */\n    private final Map<Long, String> deviceConnectionMap = new ConcurrentHashMap<>();\n\n    public IotModbusTcpClientConnectionManager(RedissonClient redissonClient, Vertx vertx,\n                                                IotDeviceMessageService messageService,\n                                                IotModbusTcpClientConfigCacheService configCacheService,\n                                                String serverId) {\n        this.redissonClient = redissonClient;\n        this.vertx = vertx;\n        this.messageService = messageService;\n        this.configCacheService = configCacheService;\n        this.serverId = serverId;\n    }\n\n    /**\n     * 确保连接存在\n     * <p>\n     * 首次建连成功时，直接发送设备上线消息\n     *\n     * @param config 设备配置\n     */\n    public void ensureConnection(IotModbusDeviceConfigRespDTO config) {\n        // 1.1 检查设备是否切换了 IP/端口，若是则先清理旧连接\n        String connectionKey = buildConnectionKey(config.getIp(), config.getPort());\n        String oldConnectionKey = deviceConnectionMap.get(config.getDeviceId());\n        if (oldConnectionKey != null && ObjUtil.notEqual(oldConnectionKey, connectionKey)) {\n            log.info(\"[ensureConnection][设备 {} IP/端口变更: {} -> {}, 清理旧连接]\",\n                    config.getDeviceId(), oldConnectionKey, connectionKey);\n            removeDevice(config.getDeviceId());\n        }\n        // 1.2 记录设备与连接的映射\n        deviceConnectionMap.put(config.getDeviceId(), connectionKey);\n\n        // 2. 情况一：连接已存在，注册设备并发送上线消息\n        ModbusConnection connection = connectionPool.get(connectionKey);\n        if (connection != null) {\n            addDeviceAndOnline(connection, config);\n            return;\n        }\n\n        // 3. 情况二：连接不存在，加分布式锁创建新连接\n        RLock lock = redissonClient.getLock(LOCK_KEY_PREFIX + connectionKey);\n        if (!lock.tryLock()) {\n            log.debug(\"[ensureConnection][获取锁失败, 由其他节点负责: {}]\", connectionKey);\n            return;\n        }\n        try {\n            // 3.1 double-check：拿到锁后再次检查，避免并发创建重复连接\n            connection = connectionPool.get(connectionKey);\n            if (connection != null) {\n                addDeviceAndOnline(connection, config);\n                lock.unlock();\n                return;\n            }\n            // 3.2 创建新连接\n            connection = createConnection(config);\n            connection.setLock(lock);\n            connectionPool.put(connectionKey, connection);\n            log.info(\"[ensureConnection][创建 Modbus 连接成功: {}]\", connectionKey);\n            // 3.3 注册设备并发送上线消息\n            addDeviceAndOnline(connection, config);\n        } catch (Exception e) {\n            log.error(\"[ensureConnection][创建 Modbus 连接失败: {}]\", connectionKey, e);\n            // 建连失败，释放锁让其他节点可重试\n            lock.unlock();\n        }\n    }\n\n    /**\n     * 创建 Modbus TCP 连接\n     */\n    private ModbusConnection createConnection(IotModbusDeviceConfigRespDTO config) throws Exception {\n        // 1. 创建 TCP 连接\n        TCPMasterConnection tcpConnection = new TCPMasterConnection(InetAddress.getByName(config.getIp()));\n        tcpConnection.setPort(config.getPort());\n        tcpConnection.setTimeout(config.getTimeout());\n        tcpConnection.connect();\n\n        // 2. 创建 Modbus 连接对象\n        return new ModbusConnection()\n                .setConnectionKey(buildConnectionKey(config.getIp(), config.getPort()))\n                .setTcpConnection(tcpConnection).setContext(vertx.getOrCreateContext())\n                .setTimeout(config.getTimeout()).setRetryInterval(config.getRetryInterval());\n    }\n\n    /**\n     * 获取连接\n     */\n    public ModbusConnection getConnection(Long deviceId) {\n        String connectionKey = deviceConnectionMap.get(deviceId);\n        if (connectionKey == null) {\n            return null;\n        }\n        return connectionPool.get(connectionKey);\n    }\n\n    /**\n     * 获取设备的 slave ID\n     */\n    public Integer getSlaveId(Long deviceId) {\n        ModbusConnection connection = getConnection(deviceId);\n        if (connection == null) {\n            return null;\n        }\n        return connection.getSlaveId(deviceId);\n    }\n\n    /**\n     * 移除设备\n     * <p>\n     * 移除时直接发送设备下线消息\n     */\n    public void removeDevice(Long deviceId) {\n        // 1.1 移除设备时，发送下线消息\n        sendOfflineMessage(deviceId);\n        // 1.2 移除设备引用\n        String connectionKey = deviceConnectionMap.remove(deviceId);\n        if (connectionKey == null) {\n            return;\n        }\n\n        // 2.1 移除连接中的设备引用\n        ModbusConnection connection = connectionPool.get(connectionKey);\n        if (connection == null) {\n            return;\n        }\n        connection.removeDevice(deviceId);\n        // 2.2 如果没有设备引用了，关闭连接\n        if (connection.getDeviceCount() == 0) {\n            closeConnection(connectionKey);\n        }\n    }\n\n    // ==================== 设备连接 & 上下线消息 ====================\n\n    /**\n     * 注册设备到连接，并发送上线消息\n     */\n    private void addDeviceAndOnline(ModbusConnection connection,\n                                    IotModbusDeviceConfigRespDTO config) {\n        Integer previous = connection.addDevice(config.getDeviceId(), config.getSlaveId());\n        // 首次注册，发送上线消息\n        if (previous == null) {\n            sendOnlineMessage(config);\n        }\n    }\n\n    /**\n     * 发送设备上线消息\n     */\n    private void sendOnlineMessage(IotModbusDeviceConfigRespDTO config) {\n        try {\n            IotDeviceMessage onlineMessage = IotDeviceMessage.buildStateUpdateOnline();\n            messageService.sendDeviceMessage(onlineMessage,\n                    config.getProductKey(), config.getDeviceName(), serverId);\n        } catch (Exception ex) {\n            log.error(\"[sendOnlineMessage][发送设备上线消息失败, deviceId={}]\", config.getDeviceId(), ex);\n        }\n    }\n\n    /**\n     * 发送设备下线消息\n     */\n    private void sendOfflineMessage(Long deviceId) {\n        IotModbusDeviceConfigRespDTO config = configCacheService.getConfig(deviceId);\n        if (config == null) {\n            return;\n        }\n        try {\n            IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();\n            messageService.sendDeviceMessage(offlineMessage,\n                    config.getProductKey(), config.getDeviceName(), serverId);\n        } catch (Exception ex) {\n            log.error(\"[sendOfflineMessage][发送设备下线消息失败, deviceId={}]\", deviceId, ex);\n        }\n    }\n\n    /**\n     * 关闭指定连接\n     */\n    private void closeConnection(String connectionKey) {\n        ModbusConnection connection = connectionPool.remove(connectionKey);\n        if (connection == null) {\n            return;\n        }\n\n        try {\n            if (connection.getTcpConnection() != null) {\n                connection.getTcpConnection().close();\n            }\n            // 释放分布式锁，让其他节点可接管\n            RLock lock = connection.getLock();\n            if (lock != null && lock.isHeldByCurrentThread()) {\n                lock.unlock();\n            }\n            log.info(\"[closeConnection][关闭 Modbus 连接: {}]\", connectionKey);\n        } catch (Exception e) {\n            log.error(\"[closeConnection][关闭连接失败: {}]\", connectionKey, e);\n        }\n    }\n\n    /**\n     * 关闭所有连接\n     */\n    public void closeAll() {\n        // 先复制再遍历，避免 closeConnection 中 remove 导致并发修改\n        List<String> connectionKeys = new ArrayList<>(connectionPool.keySet());\n        for (String connectionKey : connectionKeys) {\n            closeConnection(connectionKey);\n        }\n        deviceConnectionMap.clear();\n    }\n\n    private String buildConnectionKey(String ip, Integer port) {\n        return ip + \":\" + port;\n    }\n\n    /**\n     * Modbus 连接信息\n     */\n    @Data\n    public static class ModbusConnection {\n\n        private String connectionKey;\n        private TCPMasterConnection tcpConnection;\n        private Integer timeout;\n        private Integer retryInterval;\n        /**\n         * 设备 ID 到 slave ID 的映射\n         */\n        private final Map<Long, Integer> deviceSlaveMap = new ConcurrentHashMap<>();\n\n        /**\n         * 分布式锁，锁住连接的创建和销毁，避免多节点重复连接同一从站\n         */\n        private RLock lock;\n\n        /**\n         * Vert.x Context，用于 executeBlocking 执行 Modbus 操作，保证同一连接的操作串行执行\n         */\n        private Context context;\n\n        public Integer addDevice(Long deviceId, Integer slaveId) {\n            return deviceSlaveMap.putIfAbsent(deviceId, slaveId);\n        }\n\n        public void removeDevice(Long deviceId) {\n            deviceSlaveMap.remove(deviceId);\n        }\n\n        public int getDeviceCount() {\n            return deviceSlaveMap.size();\n        }\n\n        public Integer getSlaveId(Long deviceId) {\n            return deviceSlaveMap.get(deviceId);\n        }\n\n        /**\n         * 执行 Modbus 读取操作（阻塞方式，在 Vert.x worker 线程执行）\n         */\n        public <T> Future<T> executeBlocking(java.util.function.Function<TCPMasterConnection, T> operation) {\n            // ordered=true 保证同一 Context 的操作串行执行，不同连接之间可并行\n            return context.executeBlocking(() -> operation.apply(tcpConnection), true);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/manager/IotModbusTcpClientPollScheduler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.manager;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.manager.AbstractIotModbusPollScheduler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusTcpClientUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.handler.upstream.IotModbusTcpClientUpstreamHandler;\nimport io.vertx.core.Vertx;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT Modbus TCP Client 轮询调度器：管理点位的轮询定时器，调度读取任务并上报结果\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpClientPollScheduler extends AbstractIotModbusPollScheduler {\n\n    private final IotModbusTcpClientConnectionManager connectionManager;\n    private final IotModbusTcpClientUpstreamHandler upstreamHandler;\n    private final IotModbusTcpClientConfigCacheService configCacheService;\n\n    public IotModbusTcpClientPollScheduler(Vertx vertx,\n                                           IotModbusTcpClientConnectionManager connectionManager,\n                                           IotModbusTcpClientUpstreamHandler upstreamHandler,\n                                           IotModbusTcpClientConfigCacheService configCacheService) {\n        super(vertx);\n        this.connectionManager = connectionManager;\n        this.upstreamHandler = upstreamHandler;\n        this.configCacheService = configCacheService;\n    }\n\n    // ========== 轮询执行 ==========\n\n    /**\n     * 轮询单个点位\n     */\n    @Override\n    protected void pollPoint(Long deviceId, Long pointId) {\n        // 1.1 从 configCache 获取最新配置\n        IotModbusDeviceConfigRespDTO config = configCacheService.getConfig(deviceId);\n        if (config == null || CollUtil.isEmpty(config.getPoints())) {\n            log.warn(\"[pollPoint][设备 {} 没有配置]\", deviceId);\n            return;\n        }\n        // 1.2 查找点位\n        IotModbusPointRespDTO point = IotModbusCommonUtils.findPointById(config, pointId);\n        if (point == null) {\n            log.warn(\"[pollPoint][设备 {} 点位 {} 未找到]\", deviceId, pointId);\n            return;\n        }\n\n        // 2.1 获取连接\n        IotModbusTcpClientConnectionManager.ModbusConnection connection = connectionManager.getConnection(deviceId);\n        if (connection == null) {\n            log.warn(\"[pollPoint][设备 {} 没有连接]\", deviceId);\n            return;\n        }\n        // 2.2 获取 slave ID\n        Integer slaveId = connectionManager.getSlaveId(deviceId);\n        Assert.notNull(slaveId, \"设备 {} 没有配置 slaveId\", deviceId);\n\n        // 3. 执行 Modbus 读取\n        IotModbusTcpClientUtils.read(connection, slaveId, point)\n                .onSuccess(rawValue -> upstreamHandler.handleReadResult(config, point, rawValue))\n                .onFailure(e -> log.error(\"[pollPoint][读取点位失败, deviceId={}, identifier={}]\",\n                        deviceId, point.getIdentifier(), e));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/package-info.java",
    "content": "/**\n * Modbus TCP Client（主站）协议：网关主动连接并轮询 Modbus 从站设备\n * <p>\n * 基于 j2mod 实现，支持 FC01-04 读、FC05/06/15/16 写，定时轮询 + 下发属性设置\n */\npackage cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/IotModbusTcpServerConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\n\n/**\n * IoT Modbus TCP Server 协议配置\n *\n * @author 芋道源码\n */\n@Data\npublic class IotModbusTcpServerConfig {\n\n    /**\n     * 配置刷新间隔（秒）\n     */\n    @NotNull(message = \"配置刷新间隔不能为空\")\n    @Min(value = 1, message = \"配置刷新间隔不能小于 1 秒\")\n    private Integer configRefreshInterval = 30;\n\n    /**\n     * 自定义功能码（用于认证等扩展交互）\n     * Modbus 协议保留 65-72 给用户自定义，默认 65\n     */\n    @NotNull(message = \"自定义功能码不能为空\")\n    @Min(value = 65, message = \"自定义功能码不能小于 65\")\n    @Max(value = 72, message = \"自定义功能码不能大于 72\")\n    private Integer customFunctionCode = 65;\n\n    /**\n     * Pending Request 超时时间（毫秒）\n     */\n    @NotNull(message = \"请求超时时间不能为空\")\n    private Integer requestTimeout = 5000;\n\n    /**\n     * Pending Request 清理间隔（毫秒）\n     */\n    @NotNull(message = \"请求清理间隔不能为空\")\n    private Integer requestCleanupInterval = 10000;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/IotModbusTcpServerProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameDecoder;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameEncoder;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.handler.downstream.IotModbusTcpServerDownstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.handler.downstream.IotModbusTcpServerDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.handler.upstream.IotModbusTcpServerUpstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConfigCacheService;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConnectionManager.ConnectionInfo;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerPendingRequestManager;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerPollScheduler;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.net.NetServer;\nimport io.vertx.core.net.NetServerOptions;\nimport io.vertx.core.net.NetSocket;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * IoT 网关 Modbus TCP Server 协议\n * <p>\n * 作为 TCP Server 接收设备主动连接：\n * 1. 设备通过自定义功能码（FC 65）发送认证请求\n * 2. 认证成功后，网关主动发送 Modbus 读请求，设备响应（云端轮询模式）\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpServerProtocol implements IotProtocol {\n\n    /**\n     * 协议配置\n     */\n    private final ProtocolProperties properties;\n    /**\n     * 服务器 ID（用于消息追踪，全局唯一）\n     */\n    @Getter\n    private final String serverId;\n\n    /**\n     * 运行状态\n     */\n    @Getter\n    private volatile boolean running = false;\n\n    /**\n     * Vert.x 实例\n     */\n    private final Vertx vertx;\n    /**\n     * TCP Server\n     */\n    private NetServer netServer;\n    /**\n     * 配置刷新定时器 ID\n     */\n    private Long configRefreshTimerId;\n    /**\n     * Pending Request 清理定时器 ID\n     */\n    private Long requestCleanupTimerId;\n\n    /**\n     * 连接管理器\n     */\n    private final IotModbusTcpServerConnectionManager connectionManager;\n    /**\n     * 下行消息订阅者\n     */\n    private IotModbusTcpServerDownstreamSubscriber downstreamSubscriber;\n\n    private final IotModbusFrameDecoder frameDecoder;\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final IotModbusFrameEncoder frameEncoder;\n\n    private final IotModbusTcpServerConfigCacheService configCacheService;\n    private final IotModbusTcpServerPendingRequestManager pendingRequestManager;\n    private final IotModbusTcpServerUpstreamHandler upstreamHandler;\n    private final IotModbusTcpServerPollScheduler pollScheduler;\n    private final IotDeviceMessageService messageService;\n\n    public IotModbusTcpServerProtocol(ProtocolProperties properties) {\n        IotModbusTcpServerConfig slaveConfig = properties.getModbusTcpServer();\n        Assert.notNull(slaveConfig, \"Modbus TCP Server 协议配置（modbusTcpServer）不能为空\");\n        this.properties = properties;\n        this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());\n\n        // 初始化 Vertx\n        this.vertx = Vertx.vertx();\n\n        // 初始化 Manager\n        IotDeviceCommonApi deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n        this.connectionManager = new IotModbusTcpServerConnectionManager();\n        this.configCacheService = new IotModbusTcpServerConfigCacheService(deviceApi);\n        this.pendingRequestManager = new IotModbusTcpServerPendingRequestManager();\n\n        // 初始化帧编解码器\n        this.frameDecoder = new IotModbusFrameDecoder(slaveConfig.getCustomFunctionCode());\n        this.frameEncoder = new IotModbusFrameEncoder(slaveConfig.getCustomFunctionCode());\n\n        // 初始化共享事务 ID 自增器（PollScheduler 和 DownstreamHandler 共用，避免 transactionId 冲突）\n        AtomicInteger transactionIdCounter = new AtomicInteger(0);\n        // 初始化轮询调度器\n        this.pollScheduler = new IotModbusTcpServerPollScheduler(\n                vertx, connectionManager, frameEncoder, pendingRequestManager,\n                slaveConfig.getRequestTimeout(), transactionIdCounter, configCacheService);\n\n        // 初始化 Handler\n        this.messageService = SpringUtil.getBean(IotDeviceMessageService.class);\n        IotDeviceService deviceService = SpringUtil.getBean(IotDeviceService.class);\n        this.upstreamHandler = new IotModbusTcpServerUpstreamHandler(\n                deviceApi, this.messageService, frameEncoder,\n                connectionManager, configCacheService, pendingRequestManager,\n                pollScheduler, deviceService, serverId);\n    }\n\n    @Override\n    public String getId() {\n        return properties.getId();\n    }\n\n    @Override\n    public IotProtocolTypeEnum getType() {\n        return IotProtocolTypeEnum.MODBUS_TCP_SERVER;\n    }\n\n    @Override\n    public void start() {\n        if (running) {\n            log.warn(\"[start][IoT Modbus TCP Server 协议 {} 已经在运行中]\", getId());\n            return;\n        }\n\n        try {\n            // 1. 启动配置刷新定时器\n            IotModbusTcpServerConfig slaveConfig = properties.getModbusTcpServer();\n            configRefreshTimerId = vertx.setPeriodic(\n                    TimeUnit.SECONDS.toMillis(slaveConfig.getConfigRefreshInterval()),\n                    id -> refreshConfig());\n\n            // 2.1 启动 TCP Server\n            startTcpServer();\n\n            // 2.2 启动 PendingRequest 清理定时器\n            requestCleanupTimerId = vertx.setPeriodic(\n                    slaveConfig.getRequestCleanupInterval(),\n                    id -> pendingRequestManager.cleanupExpired());\n            running = true;\n            log.info(\"[start][IoT Modbus TCP Server 协议 {} 启动成功, serverId={}, port={}]\",\n                    getId(), serverId, properties.getPort());\n\n            // 3. 启动下行消息订阅\n            IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);\n            IotModbusTcpServerDownstreamHandler downstreamHandler = new IotModbusTcpServerDownstreamHandler(\n                    connectionManager, configCacheService, frameEncoder, this.pollScheduler.getTransactionIdCounter());\n            this.downstreamSubscriber = new IotModbusTcpServerDownstreamSubscriber(\n                    this, downstreamHandler, messageBus);\n            downstreamSubscriber.start();\n        } catch (Exception e) {\n            log.error(\"[start][IoT Modbus TCP Server 协议 {} 启动失败]\", getId(), e);\n            stop0();\n            throw e;\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        stop0();\n    }\n\n    private void stop0() {\n        // 1. 停止下行消息订阅\n        if (downstreamSubscriber != null) {\n            try {\n                downstreamSubscriber.stop();\n            } catch (Exception e) {\n                log.error(\"[stop][下行消息订阅器停止失败]\", e);\n            }\n            downstreamSubscriber = null;\n        }\n\n        // 2.1 取消定时器\n        if (configRefreshTimerId != null) {\n            vertx.cancelTimer(configRefreshTimerId);\n            configRefreshTimerId = null;\n        }\n        if (requestCleanupTimerId != null) {\n            vertx.cancelTimer(requestCleanupTimerId);\n            requestCleanupTimerId = null;\n        }\n        // 2.2 停止轮询\n        pollScheduler.stopAll();\n        // 2.3 清理 PendingRequest\n        pendingRequestManager.clear();\n        // 2.4 关闭所有连接\n        connectionManager.closeAll();\n        // 2.5 关闭 TCP Server\n        if (netServer != null) {\n            try {\n                netServer.close().result();\n                log.info(\"[stop][TCP Server 已关闭]\");\n            } catch (Exception e) {\n                log.error(\"[stop][TCP Server 关闭失败]\", e);\n            }\n            netServer = null;\n        }\n\n        // 3. 关闭 Vertx\n        if (vertx != null) {\n            try {\n                vertx.close().result();\n            } catch (Exception e) {\n                log.error(\"[stop][Vertx 关闭失败]\", e);\n            }\n        }\n        running = false;\n        log.info(\"[stop][IoT Modbus TCP Server 协议 {} 已停止]\", getId());\n    }\n\n    /**\n     * 启动 TCP Server\n     */\n    private void startTcpServer() {\n        // 1. 创建 TCP Server\n        NetServerOptions options = new NetServerOptions()\n                .setPort(properties.getPort());\n        netServer = vertx.createNetServer(options);\n\n        // 2. 设置连接处理器\n        netServer.connectHandler(this::handleConnection);\n        try {\n            netServer.listen().toCompletionStage().toCompletableFuture().get();\n            log.info(\"[startTcpServer][TCP Server 启动成功, port={}]\", properties.getPort());\n        } catch (Exception e) {\n            throw new RuntimeException(\"[startTcpServer][TCP Server 启动失败]\", e);\n        }\n    }\n\n    /**\n     * 处理新连接\n     */\n    private void handleConnection(NetSocket socket) {\n        log.info(\"[handleConnection][新连接, remoteAddress={}]\", socket.remoteAddress());\n\n        // 1. 创建 RecordParser 并设置为数据处理器\n        RecordParser recordParser =  frameDecoder.createRecordParser((frame, frameFormat) -> {\n            // 【重要】帧处理分发，即消息处理\n            upstreamHandler.handleFrame(socket, frame, frameFormat);\n        });\n        socket.handler(recordParser);\n\n        // 2.1 连接关闭处理\n        socket.closeHandler(v -> {\n            ConnectionInfo info = connectionManager.removeConnection(socket);\n            if (info == null || info.getDeviceId() == null) {\n                log.info(\"[handleConnection][未认证连接关闭, remoteAddress={}]\", socket.remoteAddress());\n                return;\n            }\n            pollScheduler.stopPolling(info.getDeviceId());\n            pendingRequestManager.removeDevice(info.getDeviceId());\n            configCacheService.removeConfig(info.getDeviceId());\n            // 发送设备下线消息\n            try {\n                IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();\n                messageService.sendDeviceMessage(offlineMessage, info.getProductKey(), info.getDeviceName(), serverId);\n            } catch (Exception ex) {\n                log.error(\"[handleConnection][发送设备下线消息失败, deviceId={}]\", info.getDeviceId(), ex);\n            }\n            log.info(\"[handleConnection][连接关闭, deviceId={}, remoteAddress={}]\",\n                    info.getDeviceId(), socket.remoteAddress());\n        });\n        // 2.2 异常处理\n        socket.exceptionHandler(e -> {\n            log.error(\"[handleConnection][连接异常, remoteAddress={}]\", socket.remoteAddress(), e);\n            socket.close();\n        });\n    }\n\n    /**\n     * 刷新已连接设备的配置（定时调用）\n     */\n    private synchronized void refreshConfig() {\n        try {\n            // 1. 只刷新已连接设备的配置\n            Set<Long> connectedDeviceIds = connectionManager.getConnectedDeviceIds();\n            if (CollUtil.isEmpty(connectedDeviceIds)) {\n                return;\n            }\n            List<IotModbusDeviceConfigRespDTO> configs =\n                    configCacheService.refreshConnectedDeviceConfigList(connectedDeviceIds);\n            if (configs == null) {\n                log.warn(\"[refreshConfig][刷新配置失败，跳过本次刷新]\");\n                return;\n            }\n            log.debug(\"[refreshConfig][刷新了 {} 个已连接设备的配置]\", configs.size());\n\n            // 2. 更新已连接设备的轮询任务\n            for (IotModbusDeviceConfigRespDTO config : configs) {\n                try {\n                    pollScheduler.updatePolling(config);\n                } catch (Exception e) {\n                    log.error(\"[refreshConfig][处理设备配置失败, deviceId={}]\", config.getDeviceId(), e);\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"[refreshConfig][刷新配置失败]\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/codec/IotModbusFrame.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec;\n\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n/**\n * IoT Modbus 统一帧数据模型（TCP/RTU 公用）\n *\n * @author 芋道源码\n */\n@Data\n@Accessors(chain = true)\npublic class IotModbusFrame {\n\n    /**\n     * 从站地址\n     */\n    private int slaveId;\n    /**\n     * 功能码\n     */\n    private int functionCode;\n    /**\n     * PDU 数据（不含 slaveId）\n     */\n    private byte[] pdu;\n    /**\n     * 事务标识符\n     * <p>\n     * 仅 {@link IotModbusFrameFormatEnum#MODBUS_TCP} 格式有值\n     */\n    private Integer transactionId;\n\n    /**\n     * 异常码\n     * <p>\n     * 当功能码最高位为 1 时（异常响应），此字段存储异常码。\n     *\n     * @see IotModbusCommonUtils#FC_EXCEPTION_MASK\n     */\n    private Integer exceptionCode;\n\n    /**\n     * 自定义功能码时的 JSON 字符串（用于 auth 认证等等）\n     */\n    private String customData;\n\n    /**\n     * 是否异常响应（基于 exceptionCode 是否有值判断）\n     */\n    public boolean isException() {\n        return exceptionCode != null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/codec/IotModbusFrameDecoder.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec;\n\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport io.vertx.core.Handler;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.function.BiConsumer;\n\n/**\n * IoT Modbus 帧解码器：集成 TCP 拆包 + 帧格式探测 + 帧解码，一条龙完成从 TCP 字节流到 IotModbusFrame 的转换。\n * <p>\n * 流程：\n * 1. 首帧检测：读前 6 字节，判断 MODBUS_TCP（ProtocolId==0x0000 且 Length 合理）或 MODBUS_RTU\n * 2. 检测后切换到对应的拆包 Handler，并将首包 6 字节通过 handleFirstBytes() 交给新 Handler 处理\n * 3. 拆包完成后解码为 IotModbusFrame，通过回调返回\n *      - MODBUS_TCP：两阶段 RecordParser（MBAP length 字段驱动）\n *      - MODBUS_RTU：功能码驱动的状态机\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class IotModbusFrameDecoder {\n\n    private static final Boolean REQUEST_MODE_DEFAULT = false;\n\n    /**\n     * 自定义功能码\n     */\n    private final int customFunctionCode;\n\n    /**\n     * 创建带自动帧格式检测的 RecordParser（默认响应模式）\n     *\n     * @param frameHandler 完整帧回调（解码后的 IotModbusFrame + 检测到的帧格式）\n     * @return RecordParser 实例\n     */\n    public RecordParser createRecordParser(BiConsumer<IotModbusFrame, IotModbusFrameFormatEnum> frameHandler) {\n        return createRecordParser(frameHandler, REQUEST_MODE_DEFAULT);\n    }\n\n    /**\n     * 创建带自动帧格式检测的 RecordParser\n     *\n     * @param frameHandler 完整帧回调（解码后的 IotModbusFrame + 检测到的帧格式）\n     * @param requestMode  是否为请求模式（true：接收方收到的是 Modbus 请求帧，FC01-04 按固定 8 字节解析；\n     *                     false：接收方收到的是 Modbus 响应帧，FC01-04 按 byteCount 变长解析）\n     * @return RecordParser 实例\n     */\n    public RecordParser createRecordParser(BiConsumer<IotModbusFrame, IotModbusFrameFormatEnum> frameHandler,\n                                           boolean requestMode) {\n        // 先创建一个 RecordParser：使用 fixedSizeMode(6) 读取首帧前 6 字节进行帧格式检测\n        RecordParser parser = RecordParser.newFixed(6);\n        parser.handler(new DetectPhaseHandler(parser, customFunctionCode, frameHandler, requestMode));\n        return parser;\n    }\n\n    // ==================== 帧解码 ====================\n\n    /**\n     * 解码响应帧（拆包后的完整帧 byte[]）\n     *\n     * @param data   完整帧字节数组\n     * @param format 帧格式\n     * @return 解码后的 IotModbusFrame\n     */\n    private IotModbusFrame decodeResponse(byte[] data, IotModbusFrameFormatEnum format) {\n        if (format == IotModbusFrameFormatEnum.MODBUS_TCP) {\n            return decodeTcpResponse(data);\n        } else {\n            return decodeRtuResponse(data);\n        }\n    }\n\n    /**\n     * 解码 MODBUS_TCP 响应\n     * 格式：[TransactionId(2)] [ProtocolId(2)] [Length(2)] [UnitId(1)] [FC(1)] [Data...]\n     */\n    private IotModbusFrame decodeTcpResponse(byte[] data) {\n        if (data.length < 8) {\n            log.warn(\"[decodeTcpResponse][数据长度不足: {}]\", data.length);\n            return null;\n        }\n        ByteBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);\n        int transactionId = buf.getShort() & 0xFFFF;\n        buf.getShort(); // protocolId：固定 0x0000，Modbus 协议标识\n        buf.getShort(); // length：后续字节数（UnitId + PDU），拆包阶段已使用\n        int slaveId = buf.get() & 0xFF;\n        int functionCode = buf.get() & 0xFF;\n        // 提取 PDU 数据（从 functionCode 之后到末尾）\n        byte[] pdu = new byte[data.length - 8];\n        System.arraycopy(data, 8, pdu, 0, pdu.length);\n        // 构建 IotModbusFrame\n        return buildFrame(slaveId, functionCode, pdu, transactionId);\n    }\n\n    /**\n     * 解码 MODBUS_RTU 响应\n     * 格式：[SlaveId(1)] [FC(1)] [Data...] [CRC(2)]\n     */\n    private IotModbusFrame decodeRtuResponse(byte[] data) {\n        if (data.length < 4) {\n            log.warn(\"[decodeRtuResponse][数据长度不足: {}]\", data.length);\n            return null;\n        }\n        // 校验 CRC\n        if (!IotModbusCommonUtils.verifyCrc16(data)) {\n            log.warn(\"[decodeRtuResponse][CRC 校验失败]\");\n            return null;\n        }\n        int slaveId = data[0] & 0xFF;\n        int functionCode = data[1] & 0xFF;\n        // PDU 数据（不含 slaveId、functionCode、CRC）\n        byte[] pdu = new byte[data.length - 4];\n        System.arraycopy(data, 2, pdu, 0, pdu.length);\n        // 构建 IotModbusFrame\n        return buildFrame(slaveId, functionCode, pdu, null);\n    }\n\n    /**\n     * 构建 IotModbusFrame\n     */\n    private IotModbusFrame buildFrame(int slaveId, int functionCode, byte[] pdu, Integer transactionId) {\n        IotModbusFrame frame = new IotModbusFrame()\n                .setSlaveId(slaveId)\n                .setFunctionCode(functionCode)\n                .setPdu(pdu)\n                .setTransactionId(transactionId);\n        // 异常响应\n        if (IotModbusCommonUtils.isExceptionResponse(functionCode)) {\n            frame.setFunctionCode(IotModbusCommonUtils.extractOriginalFunctionCode(functionCode));\n            if (pdu.length >= 1) {\n                frame.setExceptionCode(pdu[0] & 0xFF);\n            }\n            return frame;\n        }\n        // 自定义功能码\n        if (functionCode == customFunctionCode) {\n            // data 区格式：[byteCount(1)] [JSON data(N)]\n            if (pdu.length >= 1) {\n                int byteCount = pdu[0] & 0xFF;\n                if (pdu.length >= 1 + byteCount) {\n                    frame.setCustomData(new String(pdu, 1, byteCount, StandardCharsets.UTF_8));\n                }\n            }\n        }\n        return frame;\n    }\n\n    // ==================== 拆包 Handler ====================\n\n    /**\n     * 帧格式检测阶段 Handler（仅处理首包，探测后切换到对应的拆包 Handler）\n     */\n    @RequiredArgsConstructor\n    private class DetectPhaseHandler implements Handler<Buffer> {\n\n        private final RecordParser parser;\n        private final int customFunctionCode;\n        private final BiConsumer<IotModbusFrame, IotModbusFrameFormatEnum> frameHandler;\n        private final boolean requestMode;\n\n        @Override\n        public void handle(Buffer buffer) {\n            // 检测帧格式：protocolId==0x0000 且 length 合法 → MODBUS_TCP，否则 → MODBUS_RTU\n            byte[] bytes = buffer.getBytes();\n            int protocolId = ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);\n            int length = ((bytes[4] & 0xFF) << 8) | (bytes[5] & 0xFF);\n\n            // 分别处理 MODBUS_TCP、MODBUS_RTU 两种情况\n            if (protocolId == 0x0000 && length >= 1 && length <= 253) {\n                // MODBUS_TCP：切换到 TCP 拆包 Handler\n                log.debug(\"[DetectPhaseHandler][检测到 MODBUS_TCP 帧格式]\");\n                TcpFrameHandler tcpHandler = new TcpFrameHandler(parser, frameHandler);\n                parser.handler(tcpHandler);\n                // 当前 bytes 就是 MBAP 的前 6 字节，直接交给 tcpHandler 处理\n                tcpHandler.handleFirstBytes(bytes);\n            } else {\n                // MODBUS_RTU：切换到 RTU 拆包 Handler\n                log.debug(\"[DetectPhaseHandler][检测到 MODBUS_RTU 帧格式]\");\n                RtuFrameHandler rtuHandler = new RtuFrameHandler(parser, frameHandler, customFunctionCode, requestMode);\n                parser.handler(rtuHandler);\n                // 当前 bytes 包含前 6 字节（slaveId + FC + 部分数据），交给 rtuHandler 处理\n                rtuHandler.handleFirstBytes(bytes);\n            }\n        }\n    }\n\n    /**\n     * MODBUS_TCP 拆包 Handler（两阶段 RecordParser）\n     * <p>\n     * Phase 1: fixedSizeMode(6) → 读 MBAP 前 6 字节，提取 length\n     * Phase 2: fixedSizeMode(length) → 读 unitId + PDU\n     */\n    @RequiredArgsConstructor\n    private class TcpFrameHandler implements Handler<Buffer> {\n\n        private final RecordParser parser;\n        private final BiConsumer<IotModbusFrame, IotModbusFrameFormatEnum> frameHandler;\n\n        private byte[] mbapHeader;\n        private boolean waitingForBody = false;\n\n        /**\n         * 处理探测阶段传来的首帧 6 字节（即 MBAP 头）\n         *\n         * @param bytes 探测阶段消费的 6 字节\n         */\n        void handleFirstBytes(byte[] bytes) {\n            int length = ((bytes[4] & 0xFF) << 8) | (bytes[5] & 0xFF);\n            this.mbapHeader = bytes;\n            this.waitingForBody = true;\n            parser.fixedSizeMode(length);\n        }\n\n        @Override\n        public void handle(Buffer buffer) {\n            if (waitingForBody) {\n                // Phase 2: 收到 body（unitId + PDU）\n                byte[] body = buffer.getBytes();\n                // 拼接完整帧：MBAP(6) + body\n                byte[] fullFrame = new byte[mbapHeader.length + body.length];\n                System.arraycopy(mbapHeader, 0, fullFrame, 0, mbapHeader.length);\n                System.arraycopy(body, 0, fullFrame, mbapHeader.length, body.length);\n                // 解码并回调\n                IotModbusFrame frame = decodeResponse(fullFrame, IotModbusFrameFormatEnum.MODBUS_TCP);\n                if (frame != null) {\n                    frameHandler.accept(frame, IotModbusFrameFormatEnum.MODBUS_TCP);\n                }\n                // 切回 Phase 1\n                waitingForBody = false;\n                mbapHeader = null;\n                parser.fixedSizeMode(6);\n            } else {\n                // Phase 1: 收到 MBAP 头 6 字节\n                byte[] header = buffer.getBytes();\n                int length = ((header[4] & 0xFF) << 8) | (header[5] & 0xFF);\n                if (length < 1 || length > 253) {\n                    log.warn(\"[TcpFrameHandler][MBAP Length 异常: {}]\", length);\n                    parser.fixedSizeMode(6);\n                    return;\n                }\n                this.mbapHeader = header;\n                this.waitingForBody = true;\n                parser.fixedSizeMode(length);\n            }\n        }\n    }\n\n    /**\n     * MODBUS_RTU 拆包 Handler（功能码驱动的状态机）\n     * <p>\n     * 状态机流程：\n     * Phase 1: fixedSizeMode(2) → 读 slaveId + functionCode\n     * Phase 2: 根据 functionCode 确定剩余长度：\n     * - 异常响应 (FC & EXCEPTION_MASK)：fixedSizeMode(3) → exceptionCode(1) + CRC(2)\n     * - 自定义 FC / FC01-04 响应：fixedSizeMode(1) → 读 byteCount → fixedSizeMode(byteCount + 2)\n     * - FC05/06 响应：fixedSizeMode(6) → addr(2) + value(2) + CRC(2)\n     * - FC15/16 响应：fixedSizeMode(6) → addr(2) + quantity(2) + CRC(2)\n     * <p>\n     * 请求模式（requestMode=true）时，FC01-04 按固定 8 字节解析（与写响应相同路径），\n     * 因为读请求格式为 [SlaveId(1)][FC(1)][StartAddr(2)][Quantity(2)][CRC(2)]\n     */\n    @RequiredArgsConstructor\n    private class RtuFrameHandler implements Handler<Buffer> {\n\n        private static final int STATE_HEADER = 0;\n        private static final int STATE_EXCEPTION_BODY = 1;\n        private static final int STATE_READ_BYTE_COUNT = 2;\n        private static final int STATE_READ_DATA = 3;\n        private static final int STATE_WRITE_BODY = 4;\n\n        private final RecordParser parser;\n        private final BiConsumer<IotModbusFrame, IotModbusFrameFormatEnum> frameHandler;\n        private final int customFunctionCode;\n        /**\n         * 请求模式：\n         *    - true 表示接收方收到的是 Modbus 请求帧（如设备端收到网关下发的读请求），FC01-04 按固定 8 字节帧解析\n         *    - false 表示接收方收到的是 Modbus 响应帧，FC01-04 按 byteCount 变长解析\n         */\n        private final boolean requestMode;\n\n        private int state = STATE_HEADER;\n        private byte slaveId;\n        private byte functionCode;\n        private byte byteCount;\n        private Buffer pendingData;\n        private int expectedDataLen;\n\n        /**\n         * 处理探测阶段传来的首帧 6 字节\n         * <p>\n         * 由于 RTU 首帧被探测阶段消费了 6 字节，这里需要从中提取 slaveId + FC 并根据 FC 处理剩余数据\n         *\n         * @param bytes 探测阶段消费的 6 字节：[slaveId][FC][...4 bytes...]\n         */\n        void handleFirstBytes(byte[] bytes) {\n            this.slaveId = bytes[0];\n            this.functionCode = bytes[1];\n            int fc = functionCode & 0xFF;\n            if (IotModbusCommonUtils.isExceptionResponse(fc)) {\n                // 异常响应：完整帧 = slaveId(1) + FC(1) + exceptionCode(1) + CRC(2) = 5 字节\n                // 已有 6 字节（多 1 字节），取前 5 字节组装\n                Buffer frame = Buffer.buffer(5);\n                frame.appendByte(slaveId);\n                frame.appendByte(functionCode);\n                frame.appendBytes(bytes, 2, 3); // exceptionCode + CRC\n                emitFrame(frame);\n                resetToHeader();\n            } else if (IotModbusCommonUtils.isReadResponse(fc) && requestMode) {\n                // 请求模式下的读请求：固定 8 字节 [SlaveId(1)][FC(1)][StartAddr(2)][Quantity(2)][CRC(2)]\n                // 已有 6 字节，还需 2 字节（CRC）\n                state = STATE_WRITE_BODY;\n                this.pendingData = Buffer.buffer();\n                this.pendingData.appendBytes(bytes, 2, 4); // 暂存已有的 4 字节（StartAddr + Quantity）\n                parser.fixedSizeMode(2); // 还需 2 字节（CRC）\n            } else if (IotModbusCommonUtils.isReadResponse(fc) || fc == customFunctionCode) {\n                // 读响应或自定义 FC：bytes[2] = byteCount\n                this.byteCount = bytes[2];\n                int bc = byteCount & 0xFF;\n                // 已有数据：bytes[3..5] = 3 字节\n                // 还需：byteCount + CRC(2) - 3 字节已有\n                int remaining = bc + 2 - 3;\n                if (remaining <= 0) {\n                    // 数据已足够，组装完整帧\n                    int totalLen = 2 + 1 + bc + 2; // slaveId + FC + byteCount + data + CRC\n                    Buffer frame = Buffer.buffer(totalLen);\n                    frame.appendByte(slaveId);\n                    frame.appendByte(functionCode);\n                    frame.appendByte(byteCount);\n                    frame.appendBytes(bytes, 3, bc + 2); // data + CRC\n                    emitFrame(frame);\n                    resetToHeader();\n                } else {\n                    // 需要继续读\n                    state = STATE_READ_DATA;\n                    this.pendingData = Buffer.buffer();\n                    this.pendingData.appendBytes(bytes, 3, 3); // 暂存已有的 3 字节\n                    this.expectedDataLen = bc + 2; // byteCount 个数据 + 2 CRC\n                    parser.fixedSizeMode(remaining);\n                }\n            } else if (IotModbusCommonUtils.isWriteResponse(fc)) {\n                // 写响应：总长 = slaveId(1) + FC(1) + addr(2) + value/qty(2) + CRC(2) = 8 字节\n                // 已有 6 字节，还需 2 字节\n                state = STATE_WRITE_BODY;\n                this.pendingData = Buffer.buffer();\n                this.pendingData.appendBytes(bytes, 2, 4); // 暂存已有的 4 字节\n                parser.fixedSizeMode(2); // 还需 2 字节（CRC）\n            } else {\n                log.warn(\"[RtuFrameHandler][未知功能码: 0x{}]\", Integer.toHexString(fc));\n                resetToHeader();\n            }\n        }\n\n        @Override\n        public void handle(Buffer buffer) {\n            switch (state) {\n                case STATE_HEADER:\n                    handleHeader(buffer);\n                    break;\n                case STATE_EXCEPTION_BODY:\n                    handleExceptionBody(buffer);\n                    break;\n                case STATE_READ_BYTE_COUNT:\n                    handleReadByteCount(buffer);\n                    break;\n                case STATE_READ_DATA:\n                    handleReadData(buffer);\n                    break;\n                case STATE_WRITE_BODY:\n                    handleWriteBody(buffer);\n                    break;\n                default:\n                    resetToHeader();\n            }\n        }\n\n        private void handleHeader(Buffer buffer) {\n            byte[] header = buffer.getBytes();\n            this.slaveId = header[0];\n            this.functionCode = header[1];\n            int fc = functionCode & 0xFF;\n            if (IotModbusCommonUtils.isExceptionResponse(fc)) {\n                // 异常响应\n                state = STATE_EXCEPTION_BODY;\n                parser.fixedSizeMode(3); // exceptionCode(1) + CRC(2)\n            } else if (IotModbusCommonUtils.isReadResponse(fc) && requestMode) {\n                // 请求模式下的读请求：固定 8 字节，已读 2 字节（slaveId + FC），还需 6 字节\n                state = STATE_WRITE_BODY;\n                pendingData = Buffer.buffer();\n                parser.fixedSizeMode(6); // StartAddr(2) + Quantity(2) + CRC(2)\n            } else if (IotModbusCommonUtils.isReadResponse(fc) || fc == customFunctionCode) {\n                // 读响应或自定义 FC\n                state = STATE_READ_BYTE_COUNT;\n                parser.fixedSizeMode(1); // byteCount\n            } else if (IotModbusCommonUtils.isWriteResponse(fc)) {\n                // 写响应\n                state = STATE_WRITE_BODY;\n                pendingData = Buffer.buffer();\n                parser.fixedSizeMode(6); // addr(2) + value(2) + CRC(2)\n            } else {\n                log.warn(\"[RtuFrameHandler][未知功能码: 0x{}]\", Integer.toHexString(fc));\n                resetToHeader();\n            }\n        }\n\n        private void handleExceptionBody(Buffer buffer) {\n            // buffer = exceptionCode(1) + CRC(2)\n            Buffer frame = Buffer.buffer();\n            frame.appendByte(slaveId);\n            frame.appendByte(functionCode);\n            frame.appendBuffer(buffer);\n            emitFrame(frame);\n            resetToHeader();\n        }\n\n        private void handleReadByteCount(Buffer buffer) {\n            this.byteCount = buffer.getByte(0);\n            int bc = byteCount & 0xFF;\n            state = STATE_READ_DATA;\n            pendingData = Buffer.buffer();\n            expectedDataLen = bc + 2; // data(bc) + CRC(2)\n            parser.fixedSizeMode(expectedDataLen);\n        }\n\n        private void handleReadData(Buffer buffer) {\n            pendingData.appendBuffer(buffer);\n            if (pendingData.length() >= expectedDataLen) {\n                // 组装完整帧\n                Buffer frame = Buffer.buffer();\n                frame.appendByte(slaveId);\n                frame.appendByte(functionCode);\n                frame.appendByte(byteCount);\n                frame.appendBuffer(pendingData);\n                emitFrame(frame);\n                resetToHeader();\n            }\n            // 否则继续等待（不应该发生，因为我们精确设置了 fixedSizeMode）\n        }\n\n        private void handleWriteBody(Buffer buffer) {\n            pendingData.appendBuffer(buffer);\n            // 完整帧\n            Buffer frame = Buffer.buffer();\n            frame.appendByte(slaveId);\n            frame.appendByte(functionCode);\n            frame.appendBuffer(pendingData);\n            emitFrame(frame);\n            resetToHeader();\n        }\n\n        /**\n         * 发射完整帧：解码并回调\n         */\n        private void emitFrame(Buffer frameBuffer) {\n            IotModbusFrame frame = decodeResponse(frameBuffer.getBytes(), IotModbusFrameFormatEnum.MODBUS_RTU);\n            if (frame != null) {\n                frameHandler.accept(frame, IotModbusFrameFormatEnum.MODBUS_RTU);\n            }\n        }\n\n        private void resetToHeader() {\n            state = STATE_HEADER;\n            pendingData = null;\n            parser.fixedSizeMode(2); // slaveId + FC\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/codec/IotModbusFrameEncoder.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec;\n\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.nio.charset.StandardCharsets;\n\n/**\n * IoT Modbus 帧编码器：负责将 Modbus 请求/响应编码为字节数组，支持 MODBUS_TCP（MBAP）和 MODBUS_RTU（CRC16）两种帧格式。\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class IotModbusFrameEncoder {\n\n    private final int customFunctionCode;\n\n    // ==================== 编码 ====================\n\n    /**\n     * 编码读请求\n     *\n     * @param slaveId       从站地址\n     * @param functionCode  功能码\n     * @param startAddress  起始寄存器地址\n     * @param quantity      寄存器数量\n     * @param format        帧格式\n     * @param transactionId 事务 ID（TCP 模式下使用，RTU 模式传 null）\n     * @return 编码后的字节数组\n     */\n    public byte[] encodeReadRequest(int slaveId, int functionCode, int startAddress, int quantity,\n                                    IotModbusFrameFormatEnum format, Integer transactionId) {\n        // PDU: [FC(1)] [StartAddress(2)] [Quantity(2)]\n        byte[] pdu = new byte[5];\n        pdu[0] = (byte) functionCode;\n        pdu[1] = (byte) ((startAddress >> 8) & 0xFF);\n        pdu[2] = (byte) (startAddress & 0xFF);\n        pdu[3] = (byte) ((quantity >> 8) & 0xFF);\n        pdu[4] = (byte) (quantity & 0xFF);\n        return wrapFrame(slaveId, pdu, format, transactionId);\n    }\n\n    /**\n     * 编码写请求（单个寄存器 FC06 / 单个线圈 FC05）\n     *\n     * @param slaveId       从站地址\n     * @param functionCode  功能码\n     * @param address       寄存器地址\n     * @param value         值\n     * @param format        帧格式\n     * @param transactionId 事务 ID（TCP 模式下使用，RTU 模式传 null）\n     * @return 编码后的字节数组\n     */\n    public byte[] encodeWriteSingleRequest(int slaveId, int functionCode, int address, int value,\n                                           IotModbusFrameFormatEnum format, Integer transactionId) {\n        // FC05 单写线圈：Modbus 标准要求 value 为 0xFF00（ON）或 0x0000（OFF）\n        if (functionCode == IotModbusCommonUtils.FC_WRITE_SINGLE_COIL) {\n            value = (value != 0) ? 0xFF00 : 0x0000;\n        }\n        // PDU: [FC(1)] [Address(2)] [Value(2)]\n        byte[] pdu = new byte[5];\n        pdu[0] = (byte) functionCode;\n        pdu[1] = (byte) ((address >> 8) & 0xFF);\n        pdu[2] = (byte) (address & 0xFF);\n        pdu[3] = (byte) ((value >> 8) & 0xFF);\n        pdu[4] = (byte) (value & 0xFF);\n        return wrapFrame(slaveId, pdu, format, transactionId);\n    }\n\n    /**\n     * 编码写多个寄存器请求（FC16）\n     *\n     * @param slaveId       从站地址\n     * @param address       起始地址\n     * @param values        值数组\n     * @param format        帧格式\n     * @param transactionId 事务 ID（TCP 模式下使用，RTU 模式传 null）\n     * @return 编码后的字节数组\n     */\n    public byte[] encodeWriteMultipleRegistersRequest(int slaveId, int address, int[] values,\n                                                      IotModbusFrameFormatEnum format, Integer transactionId) {\n        // PDU: [FC(1)] [Address(2)] [Quantity(2)] [ByteCount(1)] [Values(N*2)]\n        int quantity = values.length;\n        int byteCount = quantity * 2;\n        byte[] pdu = new byte[6 + byteCount];\n        pdu[0] = (byte) 16; // FC16\n        pdu[1] = (byte) ((address >> 8) & 0xFF);\n        pdu[2] = (byte) (address & 0xFF);\n        pdu[3] = (byte) ((quantity >> 8) & 0xFF);\n        pdu[4] = (byte) (quantity & 0xFF);\n        pdu[5] = (byte) byteCount;\n        for (int i = 0; i < quantity; i++) {\n            pdu[6 + i * 2] = (byte) ((values[i] >> 8) & 0xFF);\n            pdu[6 + i * 2 + 1] = (byte) (values[i] & 0xFF);\n        }\n        return wrapFrame(slaveId, pdu, format, transactionId);\n    }\n\n    /**\n     * 编码写多个线圈请求（FC15）\n     * <p>\n     * 按 Modbus FC15 标准，线圈值按 bit 打包（每个 byte 包含 8 个线圈状态）。\n     *\n     * @param slaveId       从站地址\n     * @param address       起始地址\n     * @param values        线圈值数组（int[]，非0 表示 ON，0 表示 OFF）\n     * @param format        帧格式\n     * @param transactionId 事务 ID（TCP 模式下使用，RTU 模式传 null）\n     * @return 编码后的字节数组\n     */\n    public byte[] encodeWriteMultipleCoilsRequest(int slaveId, int address, int[] values,\n                                                   IotModbusFrameFormatEnum format, Integer transactionId) {\n        // PDU: [FC(1)] [Address(2)] [Quantity(2)] [ByteCount(1)] [CoilValues(N)]\n        int quantity = values.length;\n        int byteCount = (quantity + 7) / 8; // 向上取整\n        byte[] pdu = new byte[6 + byteCount];\n        pdu[0] = (byte) IotModbusCommonUtils.FC_WRITE_MULTIPLE_COILS; // FC15\n        pdu[1] = (byte) ((address >> 8) & 0xFF);\n        pdu[2] = (byte) (address & 0xFF);\n        pdu[3] = (byte) ((quantity >> 8) & 0xFF);\n        pdu[4] = (byte) (quantity & 0xFF);\n        pdu[5] = (byte) byteCount;\n        // 按 bit 打包：每个 byte 的 bit0 对应最低地址的线圈\n        for (int i = 0; i < quantity; i++) {\n            if (values[i] != 0) {\n                pdu[6 + i / 8] |= (byte) (1 << (i % 8));\n            }\n        }\n        return wrapFrame(slaveId, pdu, format, transactionId);\n    }\n\n    /**\n     * 编码自定义功能码帧（认证响应等）\n     *\n     * @param slaveId       从站地址\n     * @param jsonData      JSON 数据\n     * @param format        帧格式\n     * @param transactionId 事务 ID（TCP 模式下使用，RTU 模式传 null）\n     * @return 编码后的字节数组\n     */\n    public byte[] encodeCustomFrame(int slaveId, String jsonData,\n                                    IotModbusFrameFormatEnum format, Integer transactionId) {\n        byte[] jsonBytes = jsonData.getBytes(StandardCharsets.UTF_8);\n        // PDU: [FC(1)] [ByteCount(1)] [JSON data(N)]\n        byte[] pdu = new byte[2 + jsonBytes.length];\n        pdu[0] = (byte) customFunctionCode;\n        pdu[1] = (byte) jsonBytes.length;\n        System.arraycopy(jsonBytes, 0, pdu, 2, jsonBytes.length);\n        return wrapFrame(slaveId, pdu, format, transactionId);\n    }\n\n    // ==================== 帧封装 ====================\n\n    /**\n     * 将 PDU 封装为完整帧\n     *\n     * @param slaveId       从站地址\n     * @param pdu           PDU 数据（含 functionCode）\n     * @param format        帧格式\n     * @param transactionId 事务 ID（TCP 模式下使用，RTU 模式可为 null）\n     * @return 完整帧字节数组\n     */\n    private byte[] wrapFrame(int slaveId, byte[] pdu, IotModbusFrameFormatEnum format, Integer transactionId) {\n        if (format == IotModbusFrameFormatEnum.MODBUS_TCP) {\n            return wrapTcpFrame(slaveId, pdu, transactionId != null ? transactionId : 0);\n        } else {\n            return wrapRtuFrame(slaveId, pdu);\n        }\n    }\n\n    /**\n     * 封装 MODBUS_TCP 帧\n     * [TransactionId(2)] [ProtocolId(2,=0x0000)] [Length(2)] [UnitId(1)] [PDU...]\n     */\n    private byte[] wrapTcpFrame(int slaveId, byte[] pdu, int transactionId) {\n        int length = 1 + pdu.length; // UnitId + PDU\n        byte[] frame = new byte[6 + length]; // MBAP(6) + UnitId(1) + PDU\n        // MBAP Header\n        frame[0] = (byte) ((transactionId >> 8) & 0xFF);\n        frame[1] = (byte) (transactionId & 0xFF);\n        frame[2] = 0; // Protocol ID high\n        frame[3] = 0; // Protocol ID low\n        frame[4] = (byte) ((length >> 8) & 0xFF);\n        frame[5] = (byte) (length & 0xFF);\n        // Unit ID\n        frame[6] = (byte) slaveId;\n        // PDU\n        System.arraycopy(pdu, 0, frame, 7, pdu.length);\n        return frame;\n    }\n\n    /**\n     * 封装 MODBUS_RTU 帧\n     * [SlaveId(1)] [PDU...] [CRC(2)]\n     */\n    private byte[] wrapRtuFrame(int slaveId, byte[] pdu) {\n        byte[] frame = new byte[1 + pdu.length + 2]; // SlaveId + PDU + CRC\n        frame[0] = (byte) slaveId;\n        System.arraycopy(pdu, 0, frame, 1, pdu.length);\n        // 计算并追加 CRC16\n        int crc = IotModbusCommonUtils.calculateCrc16(frame, frame.length - 2);\n        frame[frame.length - 2] = (byte) (crc & 0xFF);        // CRC Low\n        frame[frame.length - 1] = (byte) ((crc >> 8) & 0xFF); // CRC High\n        return frame;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/handler/downstream/IotModbusTcpServerDownstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.handler.downstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameEncoder;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConfigCacheService;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConnectionManager.ConnectionInfo;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * IoT Modbus TCP Server 下行消息处理器\n * <p>\n * 负责：\n * 1. 处理下行消息（如属性设置 thing.service.property.set）\n * 2. 将属性值转换为 Modbus 写指令，通过 TCP 连接发送给设备\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpServerDownstreamHandler {\n\n    private final IotModbusTcpServerConnectionManager connectionManager;\n    private final IotModbusTcpServerConfigCacheService configCacheService;\n    private final IotModbusFrameEncoder frameEncoder;\n\n    /**\n     * TCP 事务 ID 自增器（与 PollScheduler 共享）\n     */\n    private final AtomicInteger transactionIdCounter;\n\n    public IotModbusTcpServerDownstreamHandler(IotModbusTcpServerConnectionManager connectionManager,\n                                              IotModbusTcpServerConfigCacheService configCacheService,\n                                              IotModbusFrameEncoder frameEncoder,\n                                              AtomicInteger transactionIdCounter) {\n        this.connectionManager = connectionManager;\n        this.configCacheService = configCacheService;\n        this.frameEncoder = frameEncoder;\n        this.transactionIdCounter = transactionIdCounter;\n    }\n\n    /**\n     * 处理下行消息\n     */\n    @SuppressWarnings({\"unchecked\", \"DuplicatedCode\"})\n    public void handle(IotDeviceMessage message) {\n        // 1.1 检查是否是属性设置消息\n        if (ObjUtil.equals(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(), message.getMethod())) {\n            return;\n        }\n        if (ObjUtil.notEqual(IotDeviceMessageMethodEnum.PROPERTY_SET.getMethod(), message.getMethod())) {\n            log.debug(\"[handle][忽略非属性设置消息: {}]\", message.getMethod());\n            return;\n        }\n        // 1.2 获取设备配置\n        IotModbusDeviceConfigRespDTO config = configCacheService.getConfig(message.getDeviceId());\n        if (config == null) {\n            log.warn(\"[handle][设备 {} 没有 Modbus 配置]\", message.getDeviceId());\n            return;\n        }\n        // 1.3 获取连接信息\n        ConnectionInfo connInfo = connectionManager.getConnectionInfoByDeviceId(message.getDeviceId());\n        if (connInfo == null) {\n            log.warn(\"[handle][设备 {} 没有连接]\", message.getDeviceId());\n            return;\n        }\n\n        // 2. 解析属性值并写入\n        Object params = message.getParams();\n        if (!(params instanceof Map)) {\n            log.warn(\"[handle][params 不是 Map 类型: {}]\", params);\n            return;\n        }\n        Map<String, Object> propertyMap = (Map<String, Object>) params;\n        for (Map.Entry<String, Object> entry : propertyMap.entrySet()) {\n            String identifier = entry.getKey();\n            Object value = entry.getValue();\n            // 2.1 查找对应的点位配置\n            IotModbusPointRespDTO point = IotModbusCommonUtils.findPoint(config, identifier);\n            if (point == null) {\n                log.warn(\"[handle][设备 {} 没有点位配置: {}]\", message.getDeviceId(), identifier);\n                continue;\n            }\n            // 2.2 检查是否支持写操作\n            if (!IotModbusCommonUtils.isWritable(point.getFunctionCode())) {\n                log.warn(\"[handle][点位 {} 不支持写操作, 功能码={}]\", identifier, point.getFunctionCode());\n                continue;\n            }\n\n            // 2.3 执行写入\n            writeProperty(config.getDeviceId(), connInfo, point, value);\n        }\n    }\n\n    /**\n     * 写入属性值\n     */\n    private void writeProperty(Long deviceId, ConnectionInfo connInfo,\n                                IotModbusPointRespDTO point, Object value) {\n        // 1.1 转换属性值为原始值\n        int[] rawValues = IotModbusCommonUtils.convertToRawValues(value, point);\n\n        // 1.2 确定帧格式和事务 ID\n        IotModbusFrameFormatEnum frameFormat = connInfo.getFrameFormat();\n        Assert.notNull(frameFormat, \"连接帧格式不能为空\");\n        Integer transactionId = frameFormat == IotModbusFrameFormatEnum.MODBUS_TCP\n                ? (transactionIdCounter.incrementAndGet() & 0xFFFF)\n                : null;\n        int slaveId = connInfo.getSlaveId() != null ? connInfo.getSlaveId() : 1;\n        // 1.3 编码写请求\n        byte[] data;\n        int readFunctionCode = point.getFunctionCode();\n        Integer writeSingleCode = IotModbusCommonUtils.getWriteSingleFunctionCode(readFunctionCode);\n        Integer writeMultipleCode = IotModbusCommonUtils.getWriteMultipleFunctionCode(readFunctionCode);\n        if (rawValues.length == 1 && writeSingleCode != null) {\n            // 单个值：使用单写功能码（FC05/FC06）\n            data = frameEncoder.encodeWriteSingleRequest(slaveId, writeSingleCode,\n                    point.getRegisterAddress(), rawValues[0], frameFormat, transactionId);\n        } else if (writeMultipleCode != null) {\n            // 多个值：使用多写功能码（FC15/FC16）\n            if (writeMultipleCode == IotModbusCommonUtils.FC_WRITE_MULTIPLE_COILS) {\n                data = frameEncoder.encodeWriteMultipleCoilsRequest(slaveId,\n                        point.getRegisterAddress(), rawValues, frameFormat, transactionId);\n            } else {\n                data = frameEncoder.encodeWriteMultipleRegistersRequest(slaveId,\n                        point.getRegisterAddress(), rawValues, frameFormat, transactionId);\n            }\n        } else {\n            log.warn(\"[writeProperty][点位 {} 不支持写操作, 功能码={}]\", point.getIdentifier(), readFunctionCode);\n            return;\n        }\n\n        // 2. 发送消息\n        connectionManager.sendToDevice(deviceId, data).onSuccess(v ->\n                log.info(\"[writeProperty][写入成功, deviceId={}, identifier={}, value={}]\",\n                        deviceId, point.getIdentifier(), value)\n        ).onFailure(e ->\n                log.error(\"[writeProperty][写入失败, deviceId={}, identifier={}, value={}]\",\n                        deviceId, point.getIdentifier(), value, e)\n        );\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/handler/downstream/IotModbusTcpServerDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.IotModbusTcpServerProtocol;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT Modbus TCP Server 下行消息订阅器：订阅消息总线的下行消息并转发给处理器\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpServerDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber {\n\n    private final IotModbusTcpServerDownstreamHandler downstreamHandler;\n\n    public IotModbusTcpServerDownstreamSubscriber(IotModbusTcpServerProtocol protocol,\n                                                  IotModbusTcpServerDownstreamHandler downstreamHandler,\n                                                  IotMessageBus messageBus) {\n        super(protocol, messageBus);\n        this.downstreamHandler = downstreamHandler;\n    }\n\n    @Override\n    protected void handleMessage(IotDeviceMessage message) {\n        downstreamHandler.handle(message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/handler/upstream/IotModbusTcpServerUpstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrame;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameEncoder;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConfigCacheService;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConnectionManager.ConnectionInfo;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerPendingRequestManager;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerPendingRequestManager.PendingRequest;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerPollScheduler;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.core.net.NetSocket;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;\n\n/**\n * IoT Modbus TCP Server 上行数据处理器\n * <p>\n * 处理：\n * 1. 自定义 FC 认证\n * 2. 轮询响应 → 点位翻译 → thing.property.post\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpServerUpstreamHandler {\n\n    private static final String METHOD_AUTH = \"auth\";\n\n    private final IotDeviceCommonApi deviceApi;\n    private final IotDeviceMessageService messageService;\n    private final IotModbusFrameEncoder frameEncoder;\n    private final IotModbusTcpServerConnectionManager connectionManager;\n    private final IotModbusTcpServerConfigCacheService configCacheService;\n    private final IotModbusTcpServerPendingRequestManager pendingRequestManager;\n    private final IotModbusTcpServerPollScheduler pollScheduler;\n    private final IotDeviceService deviceService;\n\n    private final String serverId;\n\n    public IotModbusTcpServerUpstreamHandler(IotDeviceCommonApi deviceApi,\n                                            IotDeviceMessageService messageService,\n                                            IotModbusFrameEncoder frameEncoder,\n                                            IotModbusTcpServerConnectionManager connectionManager,\n                                            IotModbusTcpServerConfigCacheService configCacheService,\n                                            IotModbusTcpServerPendingRequestManager pendingRequestManager,\n                                            IotModbusTcpServerPollScheduler pollScheduler,\n                                            IotDeviceService deviceService,\n                                            String serverId) {\n        this.deviceApi = deviceApi;\n        this.messageService = messageService;\n        this.frameEncoder = frameEncoder;\n        this.connectionManager = connectionManager;\n        this.configCacheService = configCacheService;\n        this.pendingRequestManager = pendingRequestManager;\n        this.pollScheduler = pollScheduler;\n        this.deviceService = deviceService;\n        this.serverId = serverId;\n    }\n\n    // ========== 帧处理入口 ==========\n\n    /**\n     * 处理帧\n     */\n    public void handleFrame(NetSocket socket, IotModbusFrame frame, IotModbusFrameFormatEnum frameFormat) {\n        if (frame == null) {\n            return;\n        }\n        // 1. 异常响应\n        if (frame.isException()) {\n            log.warn(\"[handleFrame][设备异常响应, slaveId={}, FC={}, exceptionCode={}]\",\n                    frame.getSlaveId(), frame.getFunctionCode(), frame.getExceptionCode());\n            return;\n        }\n\n        // 2. 情况一：自定义功能码（认证等扩展）\n        if (StrUtil.isNotEmpty(frame.getCustomData())) {\n            handleCustomFrame(socket, frame, frameFormat);\n            return;\n        }\n\n        // 3. 情况二：标准 Modbus 响应 → 轮询响应处理\n        handlePollingResponse(socket, frame, frameFormat);\n    }\n\n    // ========== 自定义 FC 处理（认证等） ==========\n\n    /**\n     * 处理自定义功能码帧\n     * <p>\n     * 异常分层翻译，参考 {@link cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpAbstractHandler}\n     */\n    private void handleCustomFrame(NetSocket socket, IotModbusFrame frame, IotModbusFrameFormatEnum frameFormat) {\n        String method = null;\n        try {\n            IotDeviceMessage message = JsonUtils.parseObject(frame.getCustomData(), IotDeviceMessage.class);\n            if (message == null) {\n                throw invalidParamException(\"自定义 FC 数据解析失败\");\n            }\n            method = message.getMethod();\n            if (METHOD_AUTH.equals(method)) {\n                handleAuth(socket, frame, frameFormat, message.getParams());\n                return;\n            }\n            log.warn(\"[handleCustomFrame][未知 method: {}, frame: slaveId={}, FC={}, customData={}]\",\n                    method, frame.getSlaveId(), frame.getFunctionCode(), frame.getCustomData());\n        } catch (ServiceException e) {\n            // 已知业务异常，返回对应的错误码和错误信息\n            sendCustomResponse(socket, frame, frameFormat, method, e.getCode(), e.getMessage());\n        } catch (IllegalArgumentException e) {\n            // 参数校验异常，返回 400 错误\n            sendCustomResponse(socket, frame, frameFormat, method, BAD_REQUEST.getCode(), e.getMessage());\n        } catch (Exception e) {\n            // 其他未知异常，返回 500 错误\n            log.error(\"[handleCustomFrame][解析自定义 FC 数据失败, frame: slaveId={}, FC={}, customData={}]\",\n                    frame.getSlaveId(), frame.getFunctionCode(), frame.getCustomData(), e);\n            sendCustomResponse(socket, frame, frameFormat, method,\n                    INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());\n        }\n    }\n\n    /**\n     * 处理认证请求\n     */\n    @SuppressWarnings(\"DataFlowIssue\")\n    private void handleAuth(NetSocket socket, IotModbusFrame frame, IotModbusFrameFormatEnum frameFormat, Object params) {\n        // 1. 解析认证参数\n        IotDeviceAuthReqDTO request = JsonUtils.convertObject(params, IotDeviceAuthReqDTO.class);\n        Assert.notNull(request, \"认证参数不能为空\");\n        Assert.notBlank(request.getUsername(), \"username 不能为空\");\n        Assert.notBlank(request.getPassword(), \"password 不能为空\");\n        // 特殊：考虑到 modbus 消息体积较小，默认 clientId 传递空串\n        if (StrUtil.isBlank(request.getClientId())) {\n            request.setClientId(IotDeviceAuthUtils.buildClientIdFromUsername(request.getUsername()));\n        }\n        Assert.notBlank(request.getClientId(), \"clientId 不能为空\");\n\n        // 2.1 调用认证 API\n        CommonResult<Boolean> result = deviceApi.authDevice(request);\n        result.checkError();\n        if (BooleanUtil.isFalse(result.getData())) {\n            log.warn(\"[handleAuth][认证失败, clientId={}, username={}]\", request.getClientId(), request.getUsername());\n            sendCustomResponse(socket, frame, frameFormat, METHOD_AUTH, BAD_REQUEST.getCode(), \"认证失败\");\n            return;\n        }\n        // 2.2 解析设备信息\n        IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(request.getUsername());\n        Assert.notNull(deviceInfo, \"解析设备信息失败\");\n        // 2.3 获取设备信息\n        IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(), deviceInfo.getDeviceName());\n        Assert.notNull(device, \"设备不存在\");\n        // 2.4 加载设备 Modbus 配置，无配置则阻断认证\n        IotModbusDeviceConfigRespDTO modbusConfig = configCacheService.loadDeviceConfig(device.getId());\n        if (modbusConfig == null) {\n            log.warn(\"[handleAuth][设备 {} 没有 Modbus 点位配置, 拒绝认证]\", device.getId());\n            sendCustomResponse(socket, frame, frameFormat, METHOD_AUTH, BAD_REQUEST.getCode(), \"设备无 Modbus 配置\");\n            return;\n        }\n        // 2.5 协议不一致，阻断认证\n        if (ObjUtil.notEqual(frameFormat.getFormat(), modbusConfig.getFrameFormat())) {\n            log.warn(\"[handleAuth][设备 {} frameFormat 不一致, 连接协议={}, 设备配置={}，拒绝认证]\",\n                    device.getId(), frameFormat.getFormat(), modbusConfig.getFrameFormat());\n            sendCustomResponse(socket, frame, frameFormat, METHOD_AUTH, BAD_REQUEST.getCode(),\n                    \"frameFormat 协议不一致\");\n            return;\n        }\n\n        // 3.1 注册连接\n        ConnectionInfo connectionInfo = new ConnectionInfo()\n                .setDeviceId(device.getId())\n                .setProductKey(deviceInfo.getProductKey())\n                .setDeviceName(deviceInfo.getDeviceName())\n                .setSlaveId(frame.getSlaveId())\n                .setFrameFormat(frameFormat);\n        connectionManager.registerConnection(socket, connectionInfo);\n        // 3.2 发送上线消息\n        IotDeviceMessage onlineMessage = IotDeviceMessage.buildStateUpdateOnline();\n        messageService.sendDeviceMessage(onlineMessage, deviceInfo.getProductKey(), deviceInfo.getDeviceName(), serverId);\n        // 3.3 发送成功响应\n        sendCustomResponse(socket, frame, frameFormat, METHOD_AUTH,\n                GlobalErrorCodeConstants.SUCCESS.getCode(), \"success\");\n        log.info(\"[handleAuth][认证成功, clientId={}, deviceId={}]\", request.getClientId(), device.getId());\n\n        // 4. 启动轮询\n        pollScheduler.updatePolling(modbusConfig);\n    }\n\n    /**\n     * 发送自定义功能码响应\n     */\n    private void sendCustomResponse(NetSocket socket, IotModbusFrame frame,\n                                    IotModbusFrameFormatEnum frameFormat,\n                                    String method, int code, String message) {\n        Map<String, Object> response = MapUtil.<String, Object>builder()\n                .put(\"method\", method)\n                .put(\"code\", code)\n                .put(\"message\", message)\n                .build();\n        byte[] data = frameEncoder.encodeCustomFrame(frame.getSlaveId(), JsonUtils.toJsonString(response),\n                frameFormat, frame.getTransactionId());\n        connectionManager.sendToSocket(socket, data);\n    }\n\n    // ========== 轮询响应处理 ==========\n\n    /**\n     * 处理轮询响应（云端轮询模式）\n     */\n    private void handlePollingResponse(NetSocket socket, IotModbusFrame frame,\n                                       IotModbusFrameFormatEnum frameFormat) {\n        // 1. 获取连接信息（未认证连接丢弃）\n        ConnectionInfo info = connectionManager.getConnectionInfo(socket);\n        if (info == null) {\n            log.warn(\"[handlePollingResponse][未认证连接, 丢弃数据, remoteAddress={}]\", socket.remoteAddress());\n            return;\n        }\n\n        // 2.1 匹配 PendingRequest\n        PendingRequest request = pendingRequestManager.matchResponse(\n                info.getDeviceId(), frame, frameFormat);\n        if (request == null) {\n            log.debug(\"[handlePollingResponse][未匹配到 PendingRequest, deviceId={}, FC={}]\",\n                    info.getDeviceId(), frame.getFunctionCode());\n            return;\n        }\n        // 2.2 提取寄存器值\n        int[] rawValues = IotModbusCommonUtils.extractValues(frame);\n        if (rawValues == null) {\n            log.warn(\"[handlePollingResponse][提取寄存器值失败, deviceId={}, identifier={}]\",\n                    info.getDeviceId(), request.getIdentifier());\n            return;\n        }\n        // 2.3 查找点位配置\n        IotModbusDeviceConfigRespDTO config = configCacheService.getConfig(info.getDeviceId());\n        IotModbusPointRespDTO point = IotModbusCommonUtils.findPointById(config, request.getPointId());\n        if (point == null) {\n            return;\n        }\n\n        // 3.1 转换原始值为物模型属性值（点位翻译）\n        Object convertedValue = IotModbusCommonUtils.convertToPropertyValue(rawValues, point);\n        // 3.2 构造属性上报消息\n        Map<String, Object> params = MapUtil.of(request.getIdentifier(), convertedValue);\n        IotDeviceMessage message = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(), params);\n\n        // 4. 发送到消息总线\n        messageService.sendDeviceMessage(message, info.getProductKey(), info.getDeviceName(), serverId);\n        log.debug(\"[handlePollingResponse][设备={}, 属性={}, 原始值={}, 转换值={}]\",\n                info.getDeviceId(), request.getIdentifier(), rawValues, convertedValue);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/manager/IotModbusTcpServerConfigCacheService.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusModeEnum;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * IoT Modbus TCP Server 配置缓存：认证时按需加载，断连时清理，定时刷新已连接设备\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Slf4j\npublic class IotModbusTcpServerConfigCacheService {\n\n    private final IotDeviceCommonApi deviceApi;\n\n    /**\n     * 配置缓存：deviceId -> 配置\n     */\n    private final Map<Long, IotModbusDeviceConfigRespDTO> configCache = new ConcurrentHashMap<>();\n\n    /**\n     * 加载单个设备的配置（认证成功后调用）\n     *\n     * @param deviceId 设备 ID\n     * @return 设备配置\n     */\n    public IotModbusDeviceConfigRespDTO loadDeviceConfig(Long deviceId) {\n        try {\n            // 1. 从远程 API 获取配置\n            IotModbusDeviceConfigListReqDTO reqDTO = new IotModbusDeviceConfigListReqDTO()\n                    .setStatus(CommonStatusEnum.ENABLE.getStatus())\n                    .setMode(IotModbusModeEnum.POLLING.getMode())\n                    .setProtocolType(IotProtocolTypeEnum.MODBUS_TCP_SERVER.getType())\n                    .setDeviceIds(Collections.singleton(deviceId));\n            CommonResult<List<IotModbusDeviceConfigRespDTO>> result = deviceApi.getModbusDeviceConfigList(reqDTO);\n            result.checkError();\n            IotModbusDeviceConfigRespDTO modbusConfig = CollUtil.getFirst(result.getData());\n            if (modbusConfig == null) {\n                log.warn(\"[loadDeviceConfig][远程获取配置失败，未找到数据, deviceId={}]\", deviceId);\n                return null;\n            }\n\n            // 2. 更新缓存并返回\n            configCache.put(modbusConfig.getDeviceId(), modbusConfig);\n            return modbusConfig;\n        } catch (Exception e) {\n            log.error(\"[loadDeviceConfig][从远程获取配置失败, deviceId={}]\", deviceId, e);\n            return null;\n        }\n    }\n\n    /**\n     * 刷新已连接设备的配置缓存\n     * <p>\n     * 定时调用，从远程 API 拉取最新配置，只更新已连接设备的缓存。\n     *\n     * @param connectedDeviceIds 当前已连接的设备 ID 集合\n     * @return 已连接设备的最新配置列表\n     */\n    public List<IotModbusDeviceConfigRespDTO> refreshConnectedDeviceConfigList(Set<Long> connectedDeviceIds) {\n        if (CollUtil.isEmpty(connectedDeviceIds)) {\n            return Collections.emptyList();\n        }\n        try {\n            // 1. 从远程获取已连接设备的配置\n            CommonResult<List<IotModbusDeviceConfigRespDTO>> result = deviceApi.getModbusDeviceConfigList(\n                    new IotModbusDeviceConfigListReqDTO().setStatus(CommonStatusEnum.ENABLE.getStatus())\n                            .setMode(IotModbusModeEnum.POLLING.getMode())\n                            .setProtocolType(IotProtocolTypeEnum.MODBUS_TCP_SERVER.getType())\n                            .setDeviceIds(connectedDeviceIds));\n            List<IotModbusDeviceConfigRespDTO> modbusConfigs = result.getCheckedData();\n\n            // 2. 更新缓存并返回\n            for (IotModbusDeviceConfigRespDTO config : modbusConfigs) {\n                configCache.put(config.getDeviceId(), config);\n            }\n            return modbusConfigs;\n        } catch (Exception e) {\n            log.error(\"[refreshConnectedDeviceConfigList][刷新配置失败]\", e);\n            return null;\n        }\n    }\n\n    /**\n     * 获取设备配置\n     */\n    public IotModbusDeviceConfigRespDTO getConfig(Long deviceId) {\n        IotModbusDeviceConfigRespDTO config = configCache.get(deviceId);\n        if (config != null) {\n            return config;\n        }\n        // 缓存未命中，从远程 API 获取\n        return loadDeviceConfig(deviceId);\n    }\n\n    /**\n     * 移除设备配置缓存（设备断连时调用）\n     */\n    public void removeConfig(Long deviceId) {\n        configCache.remove(deviceId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/manager/IotModbusTcpServerConnectionManager.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager;\n\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport io.vertx.core.Future;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.net.NetSocket;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * IoT Modbus TCP Server 连接管理器\n * <p>\n * 管理设备 TCP 连接：socket ↔ 设备双向映射\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpServerConnectionManager {\n\n    /**\n     * socket → 连接信息\n     */\n    private final Map<NetSocket, ConnectionInfo> connectionMap = new ConcurrentHashMap<>();\n\n    /**\n     * deviceId → socket\n     */\n    private final Map<Long, NetSocket> deviceSocketMap = new ConcurrentHashMap<>();\n\n    /**\n     * 连接信息\n     */\n    @Data\n    @Accessors(chain = true)\n    public static class ConnectionInfo {\n\n        /**\n         * 设备编号\n         */\n        private Long deviceId;\n        /**\n         * 产品标识\n         */\n        private String productKey;\n        /**\n         * 设备名称\n         */\n        private String deviceName;\n        /**\n         * 从站地址\n         */\n        private Integer slaveId;\n\n        /**\n         * 帧格式（首帧自动检测得到）\n         */\n        private IotModbusFrameFormatEnum frameFormat;\n\n    }\n\n    /**\n     * 注册已认证的连接\n     */\n    public void registerConnection(NetSocket socket, ConnectionInfo info) {\n        // 先检查该设备是否有旧连接，若有且不是同一个 socket，关闭旧 socket\n        NetSocket oldSocket = deviceSocketMap.get(info.getDeviceId());\n        if (oldSocket != null && oldSocket != socket) {\n            log.info(\"[registerConnection][设备 {} 存在旧连接, 关闭旧 socket, oldRemote={}, newRemote={}]\",\n                    info.getDeviceId(), oldSocket.remoteAddress(), socket.remoteAddress());\n            connectionMap.remove(oldSocket);\n            try {\n                oldSocket.close();\n            } catch (Exception e) {\n                log.warn(\"[registerConnection][关闭旧 socket 失败, deviceId={}, oldRemote={}]\",\n                        info.getDeviceId(), oldSocket.remoteAddress(), e);\n            }\n        }\n\n        // 注册新连接\n        connectionMap.put(socket, info);\n        deviceSocketMap.put(info.getDeviceId(), socket);\n        log.info(\"[registerConnection][设备 {} 连接已注册, remoteAddress={}]\",\n                info.getDeviceId(), socket.remoteAddress());\n    }\n\n    /**\n     * 获取连接信息\n     */\n    public ConnectionInfo getConnectionInfo(NetSocket socket) {\n        return connectionMap.get(socket);\n    }\n\n    /**\n     * 根据设备 ID 获取连接信息\n     */\n    public ConnectionInfo getConnectionInfoByDeviceId(Long deviceId) {\n        NetSocket socket = deviceSocketMap.get(deviceId);\n        return socket != null ? connectionMap.get(socket) : null;\n    }\n\n    /**\n     * 获取所有已连接设备的 ID 集合\n     */\n    public Set<Long> getConnectedDeviceIds() {\n        return deviceSocketMap.keySet();\n    }\n\n    /**\n     * 移除连接\n     */\n    public ConnectionInfo removeConnection(NetSocket socket) {\n        ConnectionInfo info = connectionMap.remove(socket);\n        if (info != null && info.getDeviceId() != null) {\n            // 使用两参数 remove：只有当 deviceSocketMap 中对应的 socket 就是当前 socket 时才删除，\n            // 避免新 socket 已注册后旧 socket 关闭时误删新映射\n            boolean removed = deviceSocketMap.remove(info.getDeviceId(), socket);\n            if (removed) {\n                log.info(\"[removeConnection][设备 {} 连接已移除]\", info.getDeviceId());\n            } else {\n                log.info(\"[removeConnection][设备 {} 旧连接关闭, 新连接仍在线, 跳过清理]\", info.getDeviceId());\n            }\n        }\n        return info;\n    }\n\n    /**\n     * 发送数据到设备\n     *\n     * @return 发送结果 Future\n     */\n    public Future<Void> sendToDevice(Long deviceId, byte[] data) {\n        NetSocket socket = deviceSocketMap.get(deviceId);\n        if (socket == null) {\n            log.warn(\"[sendToDevice][设备 {} 没有连接]\", deviceId);\n            return Future.failedFuture(\"设备 \" + deviceId + \" 没有连接\");\n        }\n        return sendToSocket(socket, data);\n    }\n\n    /**\n     * 发送数据到指定 socket\n     *\n     * @return 发送结果 Future\n     */\n    public Future<Void> sendToSocket(NetSocket socket, byte[] data) {\n        return socket.write(Buffer.buffer(data));\n    }\n\n    /**\n     * 关闭所有连接\n     */\n    public void closeAll() {\n        // 1. 先复制再清空，避免 closeHandler 回调时并发修改\n        List<NetSocket> sockets = new ArrayList<>(connectionMap.keySet());\n        connectionMap.clear();\n        deviceSocketMap.clear();\n        // 2. 关闭所有 socket（closeHandler 中 removeConnection 发现 map 为空会安全跳过）\n        for (NetSocket socket : sockets) {\n            try {\n                socket.close();\n            } catch (Exception e) {\n                log.error(\"[closeAll][关闭连接失败]\", e);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/manager/IotModbusTcpServerPendingRequestManager.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrame;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Deque;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentLinkedDeque;\n\n/**\n * IoT Modbus TCP Server 待响应请求管理器\n * <p>\n * 管理轮询下发的请求，用于匹配设备响应：\n * - TCP 模式：按 transactionId 精确匹配\n * - RTU 模式：按 slaveId + functionCode FIFO 匹配\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpServerPendingRequestManager {\n\n    /**\n     * deviceId → 有序队列\n     */\n    private final Map<Long, Deque<PendingRequest>> pendingRequests = new ConcurrentHashMap<>();\n\n    /**\n     * 待响应请求信息\n     */\n    @Data\n    @AllArgsConstructor\n    public static class PendingRequest {\n\n        private Long deviceId;\n        private Long pointId;\n        private String identifier;\n        private int slaveId;\n        private int functionCode;\n        private int registerAddress;\n        private int registerCount;\n        private Integer transactionId;\n        private long expireAt;\n\n    }\n\n    /**\n     * 添加待响应请求\n     */\n    public void addRequest(PendingRequest request) {\n        pendingRequests.computeIfAbsent(request.getDeviceId(), k -> new ConcurrentLinkedDeque<>())\n                .addLast(request);\n    }\n\n    /**\n     * 匹配响应（TCP 模式按 transactionId，RTU 模式按 FIFO）\n     *\n     * @param deviceId    设备 ID\n     * @param frame       收到的响应帧\n     * @param frameFormat 帧格式\n     * @return 匹配到的 PendingRequest，没有匹配返回 null\n     */\n    public PendingRequest matchResponse(Long deviceId, IotModbusFrame frame,\n                                        IotModbusFrameFormatEnum frameFormat) {\n        Deque<PendingRequest> queue = pendingRequests.get(deviceId);\n        if (CollUtil.isEmpty(queue)) {\n            return null;\n        }\n\n        // TCP 模式：按 transactionId 精确匹配\n        if (frameFormat == IotModbusFrameFormatEnum.MODBUS_TCP && frame.getTransactionId() != null) {\n            return matchByTransactionId(queue, frame.getTransactionId());\n        }\n        // RTU 模式：FIFO，匹配 slaveId + functionCode + registerCount\n        int responseRegisterCount = IotModbusCommonUtils.extractRegisterCountFromResponse(frame);\n        return matchByFifo(queue, frame.getSlaveId(), frame.getFunctionCode(), responseRegisterCount);\n    }\n\n    /**\n     * 按 transactionId 匹配\n     */\n    private PendingRequest matchByTransactionId(Deque<PendingRequest> queue, int transactionId) {\n        Iterator<PendingRequest> it = queue.iterator();\n        while (it.hasNext()) {\n            PendingRequest req = it.next();\n            if (req.getTransactionId() != null && req.getTransactionId() == transactionId) {\n                it.remove();\n                return req;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 按 FIFO 匹配（slaveId + functionCode + registerCount）\n     */\n    private PendingRequest matchByFifo(Deque<PendingRequest> queue, int slaveId, int functionCode,\n                                        int responseRegisterCount) {\n        Iterator<PendingRequest> it = queue.iterator();\n        while (it.hasNext()) {\n            PendingRequest req = it.next();\n            if (req.getSlaveId() == slaveId\n                    && req.getFunctionCode() == functionCode\n                    && (responseRegisterCount <= 0 || req.getRegisterCount() == responseRegisterCount)) {\n                it.remove();\n                return req;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 清理过期请求\n     */\n    public void cleanupExpired() {\n        long now = System.currentTimeMillis();\n        for (Map.Entry<Long, Deque<PendingRequest>> entry : pendingRequests.entrySet()) {\n            Deque<PendingRequest> queue = entry.getValue();\n            int removed = 0;\n            Iterator<PendingRequest> it = queue.iterator();\n            while (it.hasNext()) {\n                PendingRequest req = it.next();\n                if (req.getExpireAt() < now) {\n                    it.remove();\n                    removed++;\n                }\n            }\n            if (removed > 0) {\n                log.debug(\"[cleanupExpired][设备 {} 清理了 {} 个过期请求]\", entry.getKey(), removed);\n            }\n        }\n    }\n\n    /**\n     * 清理指定设备的所有待响应请求\n     */\n    public void removeDevice(Long deviceId) {\n        pendingRequests.remove(deviceId);\n    }\n\n    /**\n     * 清理所有待响应请求\n     */\n    public void clear() {\n        pendingRequests.clear();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/manager/IotModbusTcpServerPollScheduler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.manager.AbstractIotModbusPollScheduler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameEncoder;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConnectionManager.ConnectionInfo;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerPendingRequestManager.PendingRequest;\nimport io.vertx.core.Vertx;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * IoT Modbus TCP Server 轮询调度器：编码读请求帧，通过 TCP 连接发送到设备，注册 PendingRequest 等待响应\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotModbusTcpServerPollScheduler extends AbstractIotModbusPollScheduler {\n\n    private final IotModbusTcpServerConnectionManager connectionManager;\n    private final IotModbusFrameEncoder frameEncoder;\n    private final IotModbusTcpServerPendingRequestManager pendingRequestManager;\n    private final IotModbusTcpServerConfigCacheService configCacheService;\n    private final int requestTimeout;\n    /**\n     * TCP 事务 ID 自增器（与 DownstreamHandler 共享）\n     */\n    @Getter\n    private final AtomicInteger transactionIdCounter;\n\n    public IotModbusTcpServerPollScheduler(Vertx vertx,\n                                          IotModbusTcpServerConnectionManager connectionManager,\n                                          IotModbusFrameEncoder frameEncoder,\n                                          IotModbusTcpServerPendingRequestManager pendingRequestManager,\n                                          int requestTimeout,\n                                          AtomicInteger transactionIdCounter,\n                                          IotModbusTcpServerConfigCacheService configCacheService) {\n        super(vertx);\n        this.connectionManager = connectionManager;\n        this.frameEncoder = frameEncoder;\n        this.pendingRequestManager = pendingRequestManager;\n        this.requestTimeout = requestTimeout;\n        this.transactionIdCounter = transactionIdCounter;\n        this.configCacheService = configCacheService;\n    }\n\n    // ========== 轮询执行 ==========\n\n    /**\n     * 轮询单个点位\n     */\n    @Override\n    @SuppressWarnings(\"DuplicatedCode\")\n    protected void pollPoint(Long deviceId, Long pointId) {\n        // 1.1 从 configCache 获取最新配置\n        IotModbusDeviceConfigRespDTO config = configCacheService.getConfig(deviceId);\n        if (config == null || CollUtil.isEmpty(config.getPoints())) {\n            log.warn(\"[pollPoint][设备 {} 没有配置]\", deviceId);\n            return;\n        }\n        // 1.2 查找点位\n        IotModbusPointRespDTO point = IotModbusCommonUtils.findPointById(config, pointId);\n        if (point == null) {\n            log.warn(\"[pollPoint][设备 {} 点位 {} 未找到]\", deviceId, pointId);\n            return;\n        }\n\n        // 2.1 获取连接\n        ConnectionInfo connection = connectionManager.getConnectionInfoByDeviceId(deviceId);\n        if (connection == null) {\n            log.debug(\"[pollPoint][设备 {} 没有连接，跳过轮询]\", deviceId);\n            return;\n        }\n        // 2.2 获取 slave ID\n        IotModbusFrameFormatEnum frameFormat = connection.getFrameFormat();\n        Assert.notNull(frameFormat, \"设备 {} 的帧格式不能为空\", deviceId);\n        Integer slaveId = connection.getSlaveId();\n        Assert.notNull(connection.getSlaveId(), \"设备 {} 的 slaveId 不能为空\", deviceId);\n\n        // 3.1 编码读请求\n        Integer transactionId = frameFormat == IotModbusFrameFormatEnum.MODBUS_TCP\n                ? (transactionIdCounter.incrementAndGet() & 0xFFFF)\n                : null;\n        byte[] data = frameEncoder.encodeReadRequest(slaveId, point.getFunctionCode(),\n                point.getRegisterAddress(), point.getRegisterCount(), frameFormat, transactionId);\n        // 3.2 注册 PendingRequest\n        PendingRequest pendingRequest = new PendingRequest(\n                deviceId, point.getId(), point.getIdentifier(),\n                slaveId, point.getFunctionCode(),\n                point.getRegisterAddress(), point.getRegisterCount(),\n                transactionId,\n                System.currentTimeMillis() + requestTimeout);\n        pendingRequestManager.addRequest(pendingRequest);\n        // 3.3 发送读请求\n        connectionManager.sendToDevice(deviceId, data).onSuccess(v ->\n                log.debug(\"[pollPoint][设备={}, 点位={}, FC={}, 地址={}, 数量={}]\",\n                        deviceId, point.getIdentifier(), point.getFunctionCode(),\n                        point.getRegisterAddress(), point.getRegisterCount())\n        ).onFailure(e ->\n                log.warn(\"[pollPoint][发送失败, 设备={}, 点位={}]\", deviceId, point.getIdentifier(), e)\n        );\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/package-info.java",
    "content": "/**\n * Modbus TCP Server（从站）协议：设备主动连接网关，自定义 FC65 认证后由网关云端轮询\n * <p>\n * TCP Server 模式，支持 MODBUS_TCP / MODBUS_RTU 帧格式自动检测\n */\npackage cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\n\n/**\n * IoT 网关 MQTT 协议配置\n *\n * @author 芋道源码\n */\n@Data\npublic class IotMqttConfig {\n\n    /**\n     * 最大消息大小（字节）\n     */\n    @NotNull(message = \"最大消息大小不能为空\")\n    @Min(value = 1024, message = \"最大消息大小不能小于 1024 字节\")\n    private Integer maxMessageSize = 8192;\n\n    /**\n     * 连接超时时间（秒）\n     */\n    @NotNull(message = \"连接超时时间不能为空\")\n    @Min(value = 1, message = \"连接超时时间不能小于 1 秒\")\n    private Integer connectTimeoutSeconds = 60;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.downstream.IotMqttDownstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.downstream.IotMqttDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.upstream.IotMqttAuthHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.upstream.IotMqttRegisterHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.upstream.IotMqttUpstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;\nimport io.netty.handler.codec.mqtt.MqttConnectReturnCode;\nimport io.netty.handler.codec.mqtt.MqttQoS;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.net.PemKeyCertOptions;\nimport io.vertx.mqtt.MqttEndpoint;\nimport io.vertx.mqtt.MqttServer;\nimport io.vertx.mqtt.MqttServerOptions;\nimport io.vertx.mqtt.MqttTopicSubscription;\nimport io.vertx.mqtt.messages.MqttPublishMessage;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * IoT 网关 MQTT 协议：接收设备上行消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotMqttProtocol implements IotProtocol {\n\n    /**\n     * 注册连接的 clientId 标识\n     *\n     * @see #handleEndpoint(MqttEndpoint)\n     */\n    private static final String AUTH_TYPE_REGISTER = \"|authType=register|\";\n\n    /**\n     * 协议配置\n     */\n    private final ProtocolProperties properties;\n    /**\n     * 服务器 ID（用于消息追踪，全局唯一）\n     */\n    @Getter\n    private final String serverId;\n\n    /**\n     * 运行状态\n     */\n    @Getter\n    private volatile boolean running = false;\n\n    /**\n     * Vert.x 实例\n     */\n    private Vertx vertx;\n    /**\n     * MQTT 服务器\n     */\n    private MqttServer mqttServer;\n\n    /**\n     * 连接管理器\n     */\n    private final IotMqttConnectionManager connectionManager;\n    /**\n     * 下行消息订阅者\n     */\n    private IotMqttDownstreamSubscriber downstreamSubscriber;\n\n    private final IotDeviceMessageService deviceMessageService;\n\n    private final IotMqttAuthHandler authHandler;\n    private final IotMqttRegisterHandler registerHandler;\n    private final IotMqttUpstreamHandler upstreamHandler;\n\n    public IotMqttProtocol(ProtocolProperties properties) {\n        IotMqttConfig mqttConfig = properties.getMqtt();\n        Assert.notNull(mqttConfig, \"MQTT 协议配置（mqtt）不能为空\");\n        this.properties = properties;\n        this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());\n\n        // 初始化连接管理器\n        this.connectionManager = new IotMqttConnectionManager();\n\n        // 初始化 Handler\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n        IotDeviceCommonApi deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n        this.authHandler = new IotMqttAuthHandler(connectionManager, deviceMessageService, deviceApi, serverId);\n        this.registerHandler = new IotMqttRegisterHandler(connectionManager, deviceMessageService);\n        this.upstreamHandler = new IotMqttUpstreamHandler(connectionManager, deviceMessageService, serverId);\n    }\n\n    @Override\n    public String getId() {\n        return properties.getId();\n    }\n\n    @Override\n    public IotProtocolTypeEnum getType() {\n        return IotProtocolTypeEnum.MQTT;\n    }\n\n    @Override\n    public void start() {\n        if (running) {\n            log.warn(\"[start][IoT MQTT 协议 {} 已经在运行中]\", getId());\n            return;\n        }\n\n        // 1.1 创建 Vertx 实例\n        this.vertx = Vertx.vertx();\n\n        // 1.2 创建服务器选项\n        IotMqttConfig mqttConfig = properties.getMqtt();\n        MqttServerOptions options = new MqttServerOptions()\n                .setPort(properties.getPort())\n                .setMaxMessageSize(mqttConfig.getMaxMessageSize())\n                .setTimeoutOnConnect(mqttConfig.getConnectTimeoutSeconds());\n        IotGatewayProperties.SslConfig sslConfig = properties.getSsl();\n        if (sslConfig != null && Boolean.TRUE.equals(sslConfig.getSsl())) {\n            PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions()\n                    .setKeyPath(sslConfig.getSslKeyPath())\n                    .setCertPath(sslConfig.getSslCertPath());\n            options.setSsl(true).setKeyCertOptions(pemKeyCertOptions);\n        }\n\n        // 1.3 创建服务器并设置连接处理器\n        mqttServer = MqttServer.create(vertx, options);\n        mqttServer.endpointHandler(this::handleEndpoint);\n\n        // 1.4 启动 MQTT 服务器\n        try {\n            mqttServer.listen().result();\n            running = true;\n            log.info(\"[start][IoT MQTT 协议 {} 启动成功，端口：{}，serverId：{}]\",\n                    getId(), properties.getPort(), serverId);\n\n            // 2. 启动下行消息订阅者\n            IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);\n            IotMqttDownstreamHandler downstreamHandler = new IotMqttDownstreamHandler(deviceMessageService, connectionManager);\n            this.downstreamSubscriber = new IotMqttDownstreamSubscriber(this, downstreamHandler, messageBus);\n            this.downstreamSubscriber.start();\n        } catch (Exception e) {\n            log.error(\"[start][IoT MQTT 协议 {} 启动失败]\", getId(), e);\n            stop0();\n            throw e;\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        stop0();\n    }\n\n    private void stop0() {\n        // 1. 停止下行消息订阅者\n        if (downstreamSubscriber != null) {\n            try {\n                downstreamSubscriber.stop();\n                log.info(\"[stop][IoT MQTT 协议 {} 下行消息订阅者已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT MQTT 协议 {} 下行消息订阅者停止失败]\", getId(), e);\n            }\n            downstreamSubscriber = null;\n        }\n\n        // 2.1 关闭所有连接\n        connectionManager.closeAll();\n        // 2.2 关闭 MQTT 服务器\n        if (mqttServer != null) {\n            try {\n                mqttServer.close().result();\n                log.info(\"[stop][IoT MQTT 协议 {} 服务器已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT MQTT 协议 {} 服务器停止失败]\", getId(), e);\n            }\n            mqttServer = null;\n        }\n        // 2.3 关闭 Vertx 实例\n        if (vertx != null) {\n            try {\n                vertx.close().result();\n                log.info(\"[stop][IoT MQTT 协议 {} Vertx 已关闭]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT MQTT 协议 {} Vertx 关闭失败]\", getId(), e);\n            }\n            vertx = null;\n        }\n        running = false;\n        log.info(\"[stop][IoT MQTT 协议 {} 已停止]\", getId());\n    }\n\n    // ======================================= MQTT 连接处理 ======================================\n\n    /**\n     * 处理 MQTT 连接端点\n     *\n     * @param endpoint MQTT 连接端点\n     */\n    private void handleEndpoint(MqttEndpoint endpoint) {\n        // 1. 如果是注册请求，注册待认证连接；否则走正常认证流程\n        String clientId = endpoint.clientIdentifier();\n        if (StrUtil.endWith(clientId, AUTH_TYPE_REGISTER)) {\n            // 情况一：设备注册请求\n            registerHandler.handleRegister(endpoint);\n            return;\n        } else {\n            // 情况二：普通认证请求\n            if (!authHandler.handleAuthenticationRequest(endpoint)) {\n                endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD);\n                return;\n            }\n        }\n\n        // 2.1 设置异常和关闭处理器\n        endpoint.exceptionHandler(ex -> {\n            log.warn(\"[handleEndpoint][连接异常，客户端 ID: {}，地址: {}，异常: {}]\",\n                    clientId, connectionManager.getEndpointAddress(endpoint), ex.getMessage());\n            endpoint.close();\n        });\n        endpoint.closeHandler(v -> cleanupConnection(endpoint)); // 处理底层连接关闭（网络中断、异常等）\n        endpoint.disconnectHandler(v -> { // 处理 MQTT DISCONNECT 报文\n            log.debug(\"[handleEndpoint][设备断开连接，客户端 ID: {}]\", clientId);\n            cleanupConnection(endpoint);\n        });\n        // 2.2 设置心跳处理器\n        endpoint.pingHandler(v -> log.debug(\"[handleEndpoint][收到客户端心跳，客户端 ID: {}]\", clientId));\n\n        // 3.1 设置消息处理器\n        endpoint.publishHandler(message -> processMessage(endpoint, message));\n        // 3.2 设置 QoS 2 消息的 PUBREL 处理器\n        endpoint.publishReleaseHandler(endpoint::publishComplete);\n\n        // 4.1 设置订阅处理器（带 ACL 校验）\n        endpoint.subscribeHandler(subscribe -> {\n            IotMqttConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(endpoint);\n            List<MqttQoS> grantedQoSLevels = new ArrayList<>();\n            for (MqttTopicSubscription sub : subscribe.topicSubscriptions()) {\n                String topicName = sub.topicName();\n                // 校验主题是否属于当前设备\n                if (connectionInfo != null && IotMqttTopicUtils.isTopicSubscribeAllowed(\n                        topicName, connectionInfo.getProductKey(), connectionInfo.getDeviceName())) {\n                    grantedQoSLevels.add(sub.qualityOfService());\n                    log.debug(\"[handleEndpoint][订阅成功，客户端 ID: {}，主题: {}]\", clientId, topicName);\n                } else {\n                    log.warn(\"[handleEndpoint][订阅被拒绝，客户端 ID: {}，主题: {}]\", clientId, topicName);\n                    grantedQoSLevels.add(MqttQoS.FAILURE);\n                }\n            }\n            endpoint.subscribeAcknowledge(subscribe.messageId(), grantedQoSLevels);\n        });\n        // 4.2 设置取消订阅处理器\n        endpoint.unsubscribeHandler(unsubscribe -> {\n            log.debug(\"[handleEndpoint][设备取消订阅，客户端 ID: {}，主题: {}]\", clientId, unsubscribe.topics());\n            endpoint.unsubscribeAcknowledge(unsubscribe.messageId());\n        });\n\n        // 5. 接受连接\n        endpoint.accept(false);\n    }\n\n    /**\n     * 处理消息（发布）\n     *\n     * @param endpoint MQTT 连接端点\n     * @param message  发布消息\n     */\n    private void processMessage(MqttEndpoint endpoint, MqttPublishMessage message) {\n        String clientId = endpoint.clientIdentifier();\n        try {\n            // 1. 处理业务消息\n            String topic = message.topicName();\n            byte[] payload = message.payload().getBytes();\n            upstreamHandler.handleBusinessRequest(endpoint, topic, payload);\n\n            // 2. 根据 QoS 级别发送相应的确认消息\n            handleQoSAck(endpoint, message);\n        } catch (Exception e) {\n            log.error(\"[processMessage][消息处理失败，断开连接，客户端 ID: {}，地址: {}，错误: {}]\",\n                    clientId, connectionManager.getEndpointAddress(endpoint), e.getMessage());\n            endpoint.close();\n        }\n    }\n\n    /**\n     * 处理 QoS 确认\n     *\n     * @param endpoint MQTT 连接端点\n     * @param message  发布消息\n     */\n    private void handleQoSAck(MqttEndpoint endpoint, MqttPublishMessage message) {\n        if (message.qosLevel() == MqttQoS.AT_LEAST_ONCE) {\n            // QoS 1: 发送 PUBACK 确认\n            endpoint.publishAcknowledge(message.messageId());\n        } else if (message.qosLevel() == MqttQoS.EXACTLY_ONCE) {\n            // QoS 2: 发送 PUBREC 确认\n            endpoint.publishReceived(message.messageId());\n        }\n        // QoS 0 无需确认\n    }\n\n    /**\n     * 清理连接\n     *\n     * @param endpoint MQTT 连接端点\n     */\n    private void cleanupConnection(MqttEndpoint endpoint) {\n        try {\n            // 1. 发送设备离线消息\n            IotMqttConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(endpoint);\n            if (connectionInfo != null) {\n                IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();\n                deviceMessageService.sendDeviceMessage(offlineMessage, connectionInfo.getProductKey(),\n                        connectionInfo.getDeviceName(), serverId);\n            }\n\n            // 2. 注销连接\n            connectionManager.unregisterConnection(endpoint);\n        } catch (Exception e) {\n            log.error(\"[cleanupConnection][清理连接失败，客户端 ID: {}，错误: {}]\",\n                    endpoint.clientIdentifier(), e.getMessage());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/downstream/IotMqttDownstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.downstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;\nimport io.netty.handler.codec.mqtt.MqttQoS;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 MQTT 协议：下行消息处理器\n *\n * @author 芋道源码\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class IotMqttDownstreamHandler {\n\n    private final IotDeviceMessageService deviceMessageService;\n\n    private final IotMqttConnectionManager connectionManager;\n\n    /**\n     * 处理下行消息\n     *\n     * @param message 设备消息\n     */\n    public void handle(IotDeviceMessage message) {\n        try {\n            log.info(\"[handle][处理下行消息，设备 ID: {}，方法: {}，消息 ID: {}]\",\n                    message.getDeviceId(), message.getMethod(), message.getId());\n\n            // 1. 检查设备连接\n            IotMqttConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfoByDeviceId(\n                    message.getDeviceId());\n            if (connectionInfo == null) {\n                log.warn(\"[handle][连接信息不存在，设备 ID: {}，方法: {}，消息 ID: {}]\",\n                        message.getDeviceId(), message.getMethod(), message.getId());\n                return;\n            }\n\n            // 2.1 序列化消息\n            byte[] payload = deviceMessageService.serializeDeviceMessage(message, connectionInfo.getProductKey(),\n                    connectionInfo.getDeviceName());\n            Assert.isTrue(payload != null && payload.length > 0, \"消息编码结果不能为空\");\n            // 2.2 构建主题\n            Assert.notBlank(message.getMethod(), \"消息方法不能为空\");\n            boolean isReply = IotDeviceMessageUtils.isReplyMessage(message);\n            String topic = IotMqttTopicUtils.buildTopicByMethod(message.getMethod(), connectionInfo.getProductKey(),\n                    connectionInfo.getDeviceName(), isReply);\n            Assert.notBlank(topic, \"主题不能为空\");\n\n            // 3. 发送到设备\n            boolean success = connectionManager.sendToDevice(message.getDeviceId(), topic, payload,\n                    MqttQoS.AT_LEAST_ONCE.value(), false);\n            if (!success) {\n                throw new RuntimeException(\"下行消息发送失败\");\n            }\n            log.info(\"[handle][下行消息发送成功，设备 ID: {}，方法: {}，消息 ID: {}，主题: {}，数据长度: {} 字节]\",\n                    message.getDeviceId(), message.getMethod(), message.getId(), topic, payload.length);\n        } catch (Exception e) {\n            log.error(\"[handle][处理下行消息失败，设备 ID: {}，方法: {}，消息内容: {}]\",\n                    message.getDeviceId(), message.getMethod(), message, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/downstream/IotMqttDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttProtocol;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 MQTT 协议：接收下行给设备的消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotMqttDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber {\n\n    private final IotMqttDownstreamHandler downstreamHandler;\n\n    public IotMqttDownstreamSubscriber(IotMqttProtocol protocol,\n                                       IotMqttDownstreamHandler downstreamHandler,\n                                       IotMessageBus messageBus) {\n        super(protocol, messageBus);\n        this.downstreamHandler = downstreamHandler;\n    }\n\n    @Override\n    protected void handleMessage(IotDeviceMessage message) {\n        downstreamHandler.handle(message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttAbstractHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.upstream;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;\nimport io.netty.handler.codec.mqtt.MqttQoS;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.mqtt.MqttEndpoint;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 MQTT 协议的处理器抽象基类\n * <p>\n * 提供通用的连接校验、响应发送等功能\n *\n * @author 芋道源码\n */\n@Slf4j\n@RequiredArgsConstructor\npublic abstract class IotMqttAbstractHandler {\n\n    protected final IotMqttConnectionManager connectionManager;\n    protected final IotDeviceMessageService deviceMessageService;\n\n    /**\n     * 发送成功响应到设备\n     *\n     * @param endpoint    MQTT 连接端点\n     * @param productKey  产品 Key\n     * @param deviceName  设备名称\n     * @param requestId   请求 ID\n     * @param method      方法名\n     * @param data        响应数据\n     */\n    @SuppressWarnings(\"SameParameterValue\")\n    protected void sendSuccessResponse(MqttEndpoint endpoint, String productKey, String deviceName,\n                                       String requestId, String method, Object data) {\n        IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, 0, null);\n        writeResponse(endpoint, productKey, deviceName, method, responseMessage);\n    }\n\n    /**\n     * 发送错误响应到设备\n     *\n     * @param endpoint     MQTT 连接端点\n     * @param productKey   产品 Key\n     * @param deviceName   设备名称\n     * @param requestId    请求 ID\n     * @param method       方法名\n     * @param errorCode    错误码\n     * @param errorMessage 错误消息\n     */\n    protected void sendErrorResponse(MqttEndpoint endpoint, String productKey, String deviceName,\n                                      String requestId, String method, Integer errorCode, String errorMessage) {\n        IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, null, errorCode, errorMessage);\n        writeResponse(endpoint, productKey, deviceName, method, responseMessage);\n    }\n\n    /**\n     * 写入响应消息到设备\n     *\n     * @param endpoint        MQTT 连接端点\n     * @param productKey      产品 Key\n     * @param deviceName      设备名称\n     * @param method          方法名\n     * @param responseMessage 响应消息\n     */\n    private void writeResponse(MqttEndpoint endpoint, String productKey, String deviceName,\n                               String method, IotDeviceMessage responseMessage) {\n        try {\n            // 1.1 序列化消息（根据设备配置的序列化类型）\n            byte[] encodedData = deviceMessageService.serializeDeviceMessage(responseMessage, productKey, deviceName);\n            // 1.2 构建响应主题\n            String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method, productKey, deviceName, true);\n\n            // 2. 发送响应消息\n            endpoint.publish(replyTopic, Buffer.buffer(encodedData), MqttQoS.AT_LEAST_ONCE, false, false);\n            log.debug(\"[writeResponse][发送响应，主题: {}，code: {}]\", replyTopic, responseMessage.getCode());\n        } catch (Exception e) {\n            log.error(\"[writeResponse][发送响应异常，客户端 ID: {}]\", endpoint.clientIdentifier(), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttAuthHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.mqtt.MqttEndpoint;\nimport lombok.extern.slf4j.Slf4j;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_AUTH_FAIL;\n\n/**\n * IoT 网关 MQTT 认证处理器\n * <p>\n * 处理 MQTT CONNECT 事件，完成设备认证、连接注册、上线通知\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotMqttAuthHandler extends IotMqttAbstractHandler {\n\n    private final IotDeviceCommonApi deviceApi;\n    private final IotDeviceService deviceService;\n    private final String serverId;\n\n    public IotMqttAuthHandler(IotMqttConnectionManager connectionManager,\n                              IotDeviceMessageService deviceMessageService,\n                              IotDeviceCommonApi deviceApi,\n                              String serverId) {\n        super(connectionManager, deviceMessageService);\n        this.deviceApi = deviceApi;\n        this.deviceService = SpringUtil.getBean(IotDeviceService.class);\n        this.serverId = serverId;\n    }\n\n    /**\n     * 处理 MQTT 连接（认证）请求\n     *\n     * @param endpoint MQTT 连接端点\n     * @return 认证是否成功\n     */\n    @SuppressWarnings(\"DataFlowIssue\")\n    public boolean handleAuthenticationRequest(MqttEndpoint endpoint) {\n        String clientId = endpoint.clientIdentifier();\n        String username = endpoint.auth() != null ? endpoint.auth().getUsername() : null;\n        String password = endpoint.auth() != null ? endpoint.auth().getPassword() : null;\n        log.debug(\"[handleConnect][设备连接请求，客户端 ID: {}，用户名: {}，地址: {}]\",\n                clientId, username, connectionManager.getEndpointAddress(endpoint));\n\n        try {\n            // 1.1 解析认证参数\n            Assert.notBlank(clientId, \"clientId 不能为空\");\n            Assert.notBlank(username, \"username 不能为空\");\n            Assert.notBlank(password, \"password 不能为空\");\n            // 1.2 构建认证参数\n            IotDeviceAuthReqDTO authParams = new IotDeviceAuthReqDTO()\n                    .setClientId(clientId)\n                    .setUsername(username)\n                    .setPassword(password);\n\n            // 2.1 执行认证\n            CommonResult<Boolean> authResult = deviceApi.authDevice(authParams);\n            authResult.checkError();\n            if (BooleanUtil.isFalse(authResult.getData())) {\n                throw exception(DEVICE_AUTH_FAIL);\n            }\n            // 2.2 解析设备信息\n            IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);\n            Assert.notNull(deviceInfo, \"解析设备信息失败\");\n            // 2.3 获取设备信息\n            IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(), deviceInfo.getDeviceName());\n            Assert.notNull(device, \"设备不存在\");\n\n            // 3.1 注册连接\n            registerConnection(endpoint, device, clientId);\n            // 3.2 发送设备上线消息\n            sendOnlineMessage(device);\n            log.info(\"[handleConnect][设备认证成功，建立连接，客户端 ID: {}，用户名: {}]\", clientId, username);\n            return true;\n        } catch (Exception e) {\n            log.warn(\"[handleConnect][设备认证失败，拒绝连接，客户端 ID: {}，用户名: {}，错误: {}]\",\n                    clientId, username, e.getMessage());\n            return false;\n        }\n    }\n\n    /**\n     * 注册连接\n     */\n    private void registerConnection(MqttEndpoint endpoint, IotDeviceRespDTO device, String clientId) {\n        IotMqttConnectionManager.ConnectionInfo connectionInfo = new IotMqttConnectionManager.ConnectionInfo()\n                .setDeviceId(device.getId())\n                .setProductKey(device.getProductKey())\n                .setDeviceName(device.getDeviceName())\n                .setRemoteAddress(connectionManager.getEndpointAddress(endpoint));\n        connectionManager.registerConnection(endpoint, connectionInfo);\n    }\n\n    /**\n     * 发送设备上线消息\n     */\n    private void sendOnlineMessage(IotDeviceRespDTO device) {\n        IotDeviceMessage onlineMessage = IotDeviceMessage.buildStateUpdateOnline();\n        deviceMessageService.sendDeviceMessage(onlineMessage, device.getProductKey(),\n                device.getDeviceName(), serverId);\n        log.info(\"[sendOnlineMessage][设备上线，设备 ID: {}，设备名称: {}]\", device.getId(), device.getDeviceName());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttRegisterHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.mqtt.MqttEndpoint;\nimport lombok.extern.slf4j.Slf4j;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;\n\n/**\n * IoT 网关 MQTT 设备注册处理器：处理设备动态注册消息（一型一密）\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotMqttRegisterHandler extends IotMqttAbstractHandler {\n\n    private final IotDeviceCommonApi deviceApi;\n\n    public IotMqttRegisterHandler(IotMqttConnectionManager connectionManager,\n                                  IotDeviceMessageService deviceMessageService) {\n        super(connectionManager, deviceMessageService);\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n    }\n\n    /**\n     * 处理注册连接\n     * <p>\n     * 通过 MQTT 连接的 username 解析设备信息，password 作为签名，直接处理设备注册\n     *\n     * @param endpoint MQTT 连接端点\n     * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification\">阿里云 - 一型一密</a>\n     */\n    @SuppressWarnings(\"DataFlowIssue\")\n    public void handleRegister(MqttEndpoint endpoint) {\n        String clientId = endpoint.clientIdentifier();\n        String username = endpoint.auth() != null ? endpoint.auth().getUsername() : null;\n        String password = endpoint.auth() != null ? endpoint.auth().getPassword() : null;\n        String method = IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod();\n        String productKey = null;\n        String deviceName = null;\n\n        try {\n            // 1.1 校验参数\n            Assert.notBlank(clientId, \"clientId 不能为空\");\n            Assert.notBlank(username, \"username 不能为空\");\n            Assert.notBlank(password, \"password 不能为空\");\n            IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);\n            Assert.notNull(deviceInfo, \"解析设备信息失败\");\n            productKey = deviceInfo.getProductKey();\n            deviceName = deviceInfo.getDeviceName();\n            log.info(\"[handleRegister][设备注册连接，客户端 ID: {}，设备: {}.{}]\",\n                    clientId, productKey, deviceName);\n            // 1.2 构建注册参数\n            IotDeviceRegisterReqDTO params = new IotDeviceRegisterReqDTO()\n                    .setProductKey(productKey)\n                    .setDeviceName(deviceName)\n                    .setSign(password);\n\n            // 2. 调用动态注册 API\n            CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params);\n            result.checkError();\n\n            // 3. 接受连接，并发送成功响应\n            endpoint.accept(false);\n            sendSuccessResponse(endpoint, productKey, deviceName, null, method, result.getData());\n            log.info(\"[handleRegister][注册成功，设备: {}.{}，客户端 ID: {}]\", productKey, deviceName, clientId);\n        } catch (Exception e) {\n            log.warn(\"[handleRegister][注册失败，客户端 ID: {}，错误: {}]\", clientId, e.getMessage());\n            // 接受连接，并发送错误响应\n            endpoint.accept(false);\n            sendErrorResponse(endpoint, productKey, deviceName, null, method,\n                    INTERNAL_SERVER_ERROR.getCode(), e.getMessage());\n        } finally {\n            // 注册完成后关闭连接（一型一密只用于获取 deviceSecret，不保持连接）\n            endpoint.close();\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttUpstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;\nimport io.vertx.mqtt.MqttEndpoint;\nimport lombok.extern.slf4j.Slf4j;\n\n\n/**\n * IoT 网关 MQTT 上行消息处理器：处理业务消息（属性上报、事件上报等）\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotMqttUpstreamHandler extends IotMqttAbstractHandler {\n\n    private final String serverId;\n\n    public IotMqttUpstreamHandler(IotMqttConnectionManager connectionManager,\n                                  IotDeviceMessageService deviceMessageService,\n                                  String serverId) {\n        super(connectionManager, deviceMessageService);\n        this.serverId = serverId;\n    }\n\n    /**\n     * 处理业务消息\n     *\n     * @param endpoint MQTT 连接端点\n     * @param topic    主题\n     * @param payload  消息内容\n     */\n    public void handleBusinessRequest(MqttEndpoint endpoint, String topic, byte[] payload) {\n        String clientId = endpoint.clientIdentifier();\n        try {\n            // 1.1 基础检查\n            if (ArrayUtil.isEmpty(payload)) {\n                return;\n            }\n            // 1.2 解析主题，获取 productKey 和 deviceName\n            String[] topicParts = topic.split(\"/\");\n            String productKey = ArrayUtil.get(topicParts, 2);\n            String deviceName = ArrayUtil.get(topicParts, 3);\n            Assert.notBlank(productKey, \"产品 Key 不能为空\");\n            Assert.notBlank(deviceName, \"设备名称不能为空\");\n            // 1.3 校验设备信息，防止伪造设备消息\n            IotMqttConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(endpoint);\n            Assert.notNull(connectionInfo, \"无法获取连接信息\");\n            Assert.equals(productKey, connectionInfo.getProductKey(), \"产品 Key 不匹配\");\n            Assert.equals(deviceName, connectionInfo.getDeviceName(), \"设备名称不匹配\");\n            // 1.4 校验 topic 是否允许发布\n            if (!IotMqttTopicUtils.isTopicPublishAllowed(topic, productKey, deviceName)) {\n                log.warn(\"[handleBusinessRequest][topic 不允许发布，客户端 ID: {}，主题: {}]\", clientId, topic);\n                return;\n            }\n\n            // 2.1 反序列化消息\n            IotDeviceMessage message = deviceMessageService.deserializeDeviceMessage(payload, productKey, deviceName);\n            if (message == null) {\n                log.warn(\"[handleBusinessRequest][消息解码失败，客户端 ID: {}，主题: {}]\", clientId, topic);\n                return;\n            }\n            // 2.2 标准化回复消息的 method（MQTT 协议中，设备回复消息的 method 会携带 _reply 后缀）\n            IotMqttTopicUtils.normalizeReplyMethod(message);\n\n            // 3. 处理业务消息\n            deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId);\n            log.debug(\"[handleBusinessRequest][消息处理成功，客户端 ID: {}，主题: {}]\", clientId, topic);\n        } catch (Exception e) {\n            log.error(\"[handleBusinessRequest][消息处理异常，客户端 ID: {}，主题: {}，错误: {}]\",\n                    clientId, topic, e.getMessage(), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/manager/IotMqttConnectionManager.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager;\n\nimport cn.hutool.core.util.StrUtil;\nimport io.netty.handler.codec.mqtt.MqttQoS;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.mqtt.MqttEndpoint;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * IoT 网关 MQTT 连接管理器\n * <p>\n * 统一管理 MQTT 连接的认证状态、设备会话和消息发送功能：\n * 1. 管理 MQTT 连接的认证状态\n * 2. 管理设备会话和在线状态\n * 3. 管理消息发送到设备\n *\n * @author 芋道源码\n */\n@Slf4j\n@Component\npublic class IotMqttConnectionManager {\n\n    /**\n     * 未知地址常量（当获取端点地址失败时使用）\n     */\n    private static final String UNKNOWN_ADDRESS = \"unknown\";\n\n    /**\n     * 连接信息映射：MqttEndpoint -> 连接信息\n     */\n    private final Map<MqttEndpoint, ConnectionInfo> connectionMap = new ConcurrentHashMap<>();\n\n    /**\n     * 设备 ID -> MqttEndpoint 的映射\n     */\n    private final Map<Long, MqttEndpoint> deviceEndpointMap = new ConcurrentHashMap<>();\n\n    /**\n     * 安全获取 endpoint 地址\n     * <p>\n     * 优先从缓存获取地址，缓存为空时再尝试实时获取\n     *\n     * @param endpoint MQTT 连接端点\n     * @return 地址字符串，获取失败时返回 \"unknown\"\n     */\n    public String getEndpointAddress(MqttEndpoint endpoint) {\n        String realTimeAddress = UNKNOWN_ADDRESS;\n        if (endpoint == null) {\n            return realTimeAddress;\n        }\n\n        // 1. 优先从缓存获取（避免连接关闭时的异常）\n        ConnectionInfo connectionInfo = connectionMap.get(endpoint);\n        if (connectionInfo != null && StrUtil.isNotBlank(connectionInfo.getRemoteAddress())) {\n            return connectionInfo.getRemoteAddress();\n        }\n\n        // 2. 缓存为空时尝试实时获取\n        try {\n            realTimeAddress = endpoint.remoteAddress().toString();\n        } catch (Exception ignored) {\n            // 连接已关闭，忽略异常\n        }\n        return realTimeAddress;\n    }\n\n    /**\n     * 注册设备连接（包含认证信息）\n     *\n     * @param endpoint       MQTT 连接端点\n     * @param connectionInfo 连接信息\n     */\n    public void registerConnection(MqttEndpoint endpoint, ConnectionInfo connectionInfo) {\n        Long deviceId = connectionInfo.getDeviceId();\n        // 如果设备已有其他连接，先清理旧连接\n        MqttEndpoint oldEndpoint = deviceEndpointMap.get(deviceId);\n        if (oldEndpoint != null && oldEndpoint != endpoint) {\n            log.info(\"[registerConnection][设备已有其他连接，断开旧连接，设备 ID: {}，旧连接: {}]\",\n                    deviceId, getEndpointAddress(oldEndpoint));\n            // 先清理映射，再关闭连接（避免旧连接处理器干扰）\n            connectionMap.remove(oldEndpoint);\n            oldEndpoint.close();\n        }\n\n        // 注册新连接\n        connectionMap.put(endpoint, connectionInfo);\n        deviceEndpointMap.put(deviceId, endpoint);\n        log.info(\"[registerConnection][注册设备连接，设备 ID: {}，连接: {}，productKey: {}，deviceName: {}]\",\n                deviceId, getEndpointAddress(endpoint), connectionInfo.getProductKey(), connectionInfo.getDeviceName());\n    }\n\n    /**\n     * 注销设备连接\n     *\n     * @param endpoint MQTT 连接端点\n     */\n    public void unregisterConnection(MqttEndpoint endpoint) {\n        ConnectionInfo connectionInfo = connectionMap.remove(endpoint);\n        if (connectionInfo == null) {\n            return;\n        }\n        Long deviceId = connectionInfo.getDeviceId();\n        deviceEndpointMap.remove(deviceId);\n        log.info(\"[unregisterConnection][注销设备连接，设备 ID: {}，连接: {}]\", deviceId, getEndpointAddress(endpoint));\n    }\n\n    /**\n     * 获取连接信息\n     */\n    public ConnectionInfo getConnectionInfo(MqttEndpoint endpoint) {\n        return connectionMap.get(endpoint);\n    }\n\n    /**\n     * 根据设备 ID 获取连接信息\n     *\n     * @param deviceId 设备 ID\n     * @return 连接信息\n     */\n    public ConnectionInfo getConnectionInfoByDeviceId(Long deviceId) {\n        // 通过设备 ID 获取连接端点\n        MqttEndpoint endpoint = getDeviceEndpoint(deviceId);\n        if (endpoint == null) {\n            return null;\n        }\n        // 获取连接信息\n        return getConnectionInfo(endpoint);\n    }\n\n    /**\n     * 发送消息到设备\n     *\n     * @param deviceId 设备 ID\n     * @param topic    主题\n     * @param payload  消息内容\n     * @param qos      服务质量\n     * @param retain   是否保留消息\n     * @return 是否发送成功\n     */\n    public boolean sendToDevice(Long deviceId, String topic, byte[] payload, int qos, boolean retain) {\n        MqttEndpoint endpoint = deviceEndpointMap.get(deviceId);\n        if (endpoint == null) {\n            log.warn(\"[sendToDevice][设备离线，无法发送消息，设备 ID: {}，主题: {}]\", deviceId, topic);\n            return false;\n        }\n\n        try {\n            endpoint.publish(topic, Buffer.buffer(payload), MqttQoS.valueOf(qos), false, retain);\n            log.debug(\"[sendToDevice][发送消息成功，设备 ID: {}，主题: {}，QoS: {}]\", deviceId, topic, qos);\n            return true;\n        } catch (Exception e) {\n            log.error(\"[sendToDevice][发送消息失败，设备 ID: {}，主题: {}，错误: {}]\", deviceId, topic, e.getMessage());\n            return false;\n        }\n    }\n\n    /**\n     * 获取设备连接端点\n     */\n    public MqttEndpoint getDeviceEndpoint(Long deviceId) {\n        return deviceEndpointMap.get(deviceId);\n    }\n\n    /**\n     * 关闭所有连接\n     */\n    public void closeAll() {\n        // 1. 先复制再清空，避免 closeHandler 回调时并发修改\n        List<MqttEndpoint> endpoints = new ArrayList<>(connectionMap.keySet());\n        connectionMap.clear();\n        deviceEndpointMap.clear();\n        // 2. 关闭所有连接（closeHandler 中 unregisterConnection 发现 map 为空会安全跳过）\n        for (MqttEndpoint endpoint : endpoints) {\n            try {\n                endpoint.close();\n            } catch (Exception ignored) {\n                // 连接可能已关闭，忽略异常\n            }\n        }\n    }\n\n    /**\n     * 连接信息\n     */\n    @Data\n    public static class ConnectionInfo {\n\n        /**\n         * 设备 ID\n         */\n        private Long deviceId;\n        /**\n         * 产品 Key\n         */\n        private String productKey;\n        /**\n         * 设备名称\n         */\n        private String deviceName;\n\n        /**\n         * 连接地址\n         */\n        private String remoteAddress;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/package-info.java",
    "content": "/**\n * MQTT 协议实现包\n * <p>\n * 提供基于 Vert.x MQTT Server 的 IoT 设备连接和消息处理功能\n */\npackage cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/package-info.java",
    "content": "/**\n * 设备接入协议：MQTT、EMQX、HTTP、TCP 等协议的实现\n */\npackage cn.iocoder.yudao.module.iot.gateway.protocol;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;\n\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpCodecTypeEnum;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\n\n/**\n * IoT TCP 协议配置\n *\n * @author 芋道源码\n */\n@Data\npublic class IotTcpConfig {\n\n    /**\n     * 最大连接数\n     */\n    @NotNull(message = \"最大连接数不能为空\")\n    @Min(value = 1, message = \"最大连接数必须大于 0\")\n    private Integer maxConnections = 1000;\n    /**\n     * 心跳超时时间（毫秒）\n     */\n    @NotNull(message = \"心跳超时时间不能为空\")\n    @Min(value = 1000, message = \"心跳超时时间必须大于 1000 毫秒\")\n    private Long keepAliveTimeoutMs = 30000L;\n\n    /**\n     * 拆包配置\n     */\n    @Valid\n    private CodecConfig codec;\n\n    /**\n     * TCP 拆包配置\n     */\n    @Data\n    public static class CodecConfig {\n\n        /**\n         * 拆包类型\n         *\n         * @see IotTcpCodecTypeEnum\n         */\n        @NotNull(message = \"拆包类型不能为空\")\n        private String type;\n\n        /**\n         * LENGTH_FIELD: 长度字段偏移量\n         * <p>\n         * 表示长度字段在消息中的起始位置（从 0 开始）\n         */\n        private Integer lengthFieldOffset;\n        /**\n         * LENGTH_FIELD: 长度字段长度（字节数）\n         * <p>\n         * 常见值：1（最大 255）、2（最大 65535）、4（最大 2GB）\n         */\n        private Integer lengthFieldLength;\n        /**\n         * LENGTH_FIELD: 长度调整值\n         * <p>\n         * 用于调整长度字段的值，例如长度字段包含头部长度时需要减去头部长度\n         */\n        private Integer lengthAdjustment = 0;\n        /**\n         * LENGTH_FIELD: 跳过的初始字节数\n         * <p>\n         * 解码后跳过的字节数，通常等于 lengthFieldOffset + lengthFieldLength\n         */\n        private Integer initialBytesToStrip = 0;\n\n        /**\n         * DELIMITER: 分隔符\n         * <p>\n         * 支持转义字符：\\n（换行）、\\r（回车）、\\r\\n（回车换行）\n         */\n        private String delimiter;\n\n        /**\n         * FIXED_LENGTH: 固定消息长度（字节）\n         * <p>\n         * 每条消息的固定长度\n         */\n        private Integer fixedLength;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodecFactory;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.downstream.IotTcpDownstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.downstream.IotTcpDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.upstream.IotTcpUpstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.net.NetServer;\nimport io.vertx.core.net.NetServerOptions;\nimport io.vertx.core.net.PemKeyCertOptions;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT TCP 协议实现\n * <p>\n * 基于 Vert.x 实现 TCP 服务器，接收设备上行消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotTcpProtocol implements IotProtocol {\n\n    /**\n     * 协议配置\n     */\n    private final ProtocolProperties properties;\n    /**\n     * 服务器 ID（用于消息追踪，全局唯一）\n     */\n    @Getter\n    private final String serverId;\n\n    /**\n     * 运行状态\n     */\n    @Getter\n    private volatile boolean running = false;\n\n    /**\n     * Vert.x 实例\n     */\n    private Vertx vertx;\n    /**\n     * TCP 服务器\n     */\n    private NetServer tcpServer;\n    /**\n     * TCP 连接管理器\n     */\n    private final IotTcpConnectionManager connectionManager;\n\n    /**\n     * 下行消息订阅者\n     */\n    private IotTcpDownstreamSubscriber downstreamSubscriber;\n\n    /**\n     * 消息序列化器\n     */\n    private final IotMessageSerializer serializer;\n    /**\n     * TCP 帧编解码器\n     */\n    private final IotTcpFrameCodec frameCodec;\n\n    public IotTcpProtocol(ProtocolProperties properties) {\n        IotTcpConfig tcpConfig = properties.getTcp();\n        Assert.notNull(tcpConfig, \"TCP 协议配置（tcp）不能为空\");\n        Assert.notNull(tcpConfig.getCodec(), \"TCP 拆包配置（tcp.codec）不能为空\");\n        this.properties = properties;\n        this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());\n\n        // 初始化序列化器\n        IotSerializeTypeEnum serializeType = IotSerializeTypeEnum.of(properties.getSerialize());\n        Assert.notNull(serializeType, \"不支持的序列化类型：\" + properties.getSerialize());\n        IotMessageSerializerManager serializerManager = SpringUtil.getBean(IotMessageSerializerManager.class);\n        this.serializer = serializerManager.get(serializeType);\n        // 初始化帧编解码器\n        this.frameCodec = IotTcpFrameCodecFactory.create(tcpConfig.getCodec());\n\n        // 初始化连接管理器\n        this.connectionManager = new IotTcpConnectionManager(tcpConfig.getMaxConnections());\n    }\n\n    @Override\n    public String getId() {\n        return properties.getId();\n    }\n\n    @Override\n    public IotProtocolTypeEnum getType() {\n        return IotProtocolTypeEnum.TCP;\n    }\n\n    @Override\n    public void start() {\n        if (running) {\n            log.warn(\"[start][IoT TCP 协议 {} 已经在运行中]\", getId());\n            return;\n        }\n\n        // 1.1 创建 Vertx 实例\n        this.vertx = Vertx.vertx();\n\n        // 1.2 创建服务器选项\n        IotTcpConfig tcpConfig = properties.getTcp();\n        NetServerOptions options = new NetServerOptions()\n                .setPort(properties.getPort())\n                .setTcpKeepAlive(true)\n                .setTcpNoDelay(true)\n                .setReuseAddress(true)\n                .setIdleTimeout((int) (tcpConfig.getKeepAliveTimeoutMs() / 1000)); // 设置空闲超时\n        IotGatewayProperties.SslConfig sslConfig = properties.getSsl();\n        if (sslConfig != null && Boolean.TRUE.equals(sslConfig.getSsl())) {\n            PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions()\n                    .setKeyPath(sslConfig.getSslKeyPath())\n                    .setCertPath(sslConfig.getSslCertPath());\n            options.setSsl(true).setKeyCertOptions(pemKeyCertOptions);\n        }\n\n        // 1.3 创建服务器并设置连接处理器\n        tcpServer = vertx.createNetServer(options);\n        tcpServer.connectHandler(socket -> {\n            IotTcpUpstreamHandler handler = new IotTcpUpstreamHandler(serverId, frameCodec, serializer, connectionManager);\n            handler.handle(socket);\n        });\n\n        // 1.4 启动 TCP 服务器\n        try {\n            tcpServer.listen().result();\n            running = true;\n            log.info(\"[start][IoT TCP 协议 {} 启动成功，端口：{}，serverId：{}]\",\n                    getId(), properties.getPort(), serverId);\n\n            // 2. 启动下行消息订阅者\n            IotTcpDownstreamHandler downstreamHandler = new IotTcpDownstreamHandler(connectionManager, frameCodec, serializer);\n            IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);\n            this.downstreamSubscriber = new IotTcpDownstreamSubscriber(this, downstreamHandler, messageBus);\n            this.downstreamSubscriber.start();\n        } catch (Exception e) {\n            log.error(\"[start][IoT TCP 协议 {} 启动失败]\", getId(), e);\n            stop0();\n            throw e;\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        stop0();\n    }\n\n    private void stop0() {\n        // 1. 停止下行消息订阅者\n        if (downstreamSubscriber != null) {\n            try {\n                downstreamSubscriber.stop();\n                log.info(\"[stop][IoT TCP 协议 {} 下行消息订阅者已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT TCP 协议 {} 下行消息订阅者停止失败]\", getId(), e);\n            }\n            downstreamSubscriber = null;\n        }\n\n        // 2.1 关闭所有连接\n        connectionManager.closeAll();\n        // 2.2 关闭 TCP 服务器\n        if (tcpServer != null) {\n            try {\n                tcpServer.close().result();\n                log.info(\"[stop][IoT TCP 协议 {} 服务器已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT TCP 协议 {} 服务器停止失败]\", getId(), e);\n            }\n            tcpServer = null;\n        }\n        // 2.3 关闭 Vertx 实例\n        if (vertx != null) {\n            try {\n                vertx.close().result();\n                log.info(\"[stop][IoT TCP 协议 {} Vertx 已关闭]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT TCP 协议 {} Vertx 关闭失败]\", getId(), e);\n            }\n            vertx = null;\n        }\n        running = false;\n        log.info(\"[stop][IoT TCP 协议 {} 已停止]\", getId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/codec/IotTcpCodecTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.delimiter.IotTcpDelimiterFrameCodec;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.length.IotTcpFixedLengthFrameCodec;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.length.IotTcpLengthFieldFrameCodec;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * IoT TCP 拆包类型枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum IotTcpCodecTypeEnum {\n\n    /**\n     * 基于固定长度的拆包\n     */\n    FIXED_LENGTH(\"fixed_length\", IotTcpFixedLengthFrameCodec.class),\n\n    /**\n     * 基于分隔符的拆包\n     */\n    DELIMITER(\"delimiter\", IotTcpDelimiterFrameCodec.class),\n\n    /**\n     * 基于长度字段的拆包\n     */\n    LENGTH_FIELD(\"length_field\", IotTcpLengthFieldFrameCodec.class),\n    ;\n\n    /**\n     * 类型标识\n     */\n    private final String type;\n    /**\n     * 编解码器类\n     */\n    private final Class<? extends IotTcpFrameCodec> codecClass;\n\n    /**\n     * 根据类型获取枚举\n     *\n     * @param type 类型标识\n     * @return 枚举值\n     */\n    public static IotTcpCodecTypeEnum of(String type) {\n        return ArrayUtil.firstMatch(e -> e.getType().equalsIgnoreCase(type), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/codec/IotTcpFrameCodec.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec;\n\nimport io.vertx.core.Handler;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.parsetools.RecordParser;\n\n/**\n * IoT TCP 帧编解码器接口\n * <p>\n * 用于解决 TCP 粘包/拆包问题，提供解码（拆包）和编码（加帧）能力\n *\n * @author 芋道源码\n */\npublic interface IotTcpFrameCodec {\n\n    /**\n     * 获取编解码器类型\n     *\n     * @return 编解码器类型\n     */\n    IotTcpCodecTypeEnum getType();\n\n    /**\n     * 创建解码器（RecordParser）\n     * <p>\n     * 每个连接调用一次，返回的 parser 需绑定到 socket.handler()\n     *\n     * @param handler 消息处理器，当收到完整的消息帧后回调\n     * @return RecordParser 实例\n     */\n    RecordParser createDecodeParser(Handler<Buffer> handler);\n\n    /**\n     * 编码消息（加帧）\n     * <p>\n     * 根据不同的编解码类型添加帧头/分隔符\n     *\n     * @param data 原始数据\n     * @return 编码后的数据（带帧头/分隔符）\n     */\n    Buffer encode(byte[] data);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/codec/IotTcpFrameCodecFactory.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConfig;\n\n/**\n * IoT TCP 帧编解码器工厂\n *\n * @author 芋道源码\n */\npublic class IotTcpFrameCodecFactory {\n\n    /**\n     * 根据配置创建编解码器\n     *\n     * @param config 拆包配置\n     * @return 编解码器实例，如果配置为空则返回 null\n     */\n    public static IotTcpFrameCodec create(IotTcpConfig.CodecConfig config) {\n        Assert.notNull(config, \"CodecConfig 不能为空\");\n        IotTcpCodecTypeEnum type = IotTcpCodecTypeEnum.of(config.getType());\n        Assert.notNull(type, \"不支持的 CodecType 类型：\" + config.getType());\n        return ReflectUtil.newInstance(type.getCodecClass(), config);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/codec/delimiter/IotTcpDelimiterFrameCodec.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.delimiter;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpCodecTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;\nimport io.vertx.core.Handler;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT TCP 分隔符帧编解码器\n * <p>\n * 基于分隔符的拆包策略，消息格式：消息内容 + 分隔符\n * <p>\n * 支持的分隔符：\n * <ul>\n *   <li>\\n - 换行符</li>\n *   <li>\\r - 回车符</li>\n *   <li>\\r\\n - 回车换行</li>\n *   <li>自定义字符串</li>\n * </ul>\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotTcpDelimiterFrameCodec implements IotTcpFrameCodec {\n\n    /**\n     * 最大记录大小（64KB），防止 DoS 攻击\n     */\n    private static final int MAX_RECORD_SIZE = 65536;\n\n    /**\n     * 解析后的分隔符字节数组\n     */\n    private final byte[] delimiterBytes;\n\n    public IotTcpDelimiterFrameCodec(IotTcpConfig.CodecConfig config) {\n        Assert.notBlank(config.getDelimiter(), \"delimiter 不能为空\");\n        this.delimiterBytes = parseDelimiter(config.getDelimiter());\n    }\n\n    @Override\n    public IotTcpCodecTypeEnum getType() {\n        return IotTcpCodecTypeEnum.DELIMITER;\n    }\n\n    @Override\n    public RecordParser createDecodeParser(Handler<Buffer> handler) {\n        RecordParser parser = RecordParser.newDelimited(Buffer.buffer(delimiterBytes));\n        parser.maxRecordSize(MAX_RECORD_SIZE); // 设置最大记录大小，防止 DoS 攻击\n        // 处理完整消息（不包含分隔符）\n        parser.handler(handler);\n        parser.exceptionHandler(ex -> {\n            throw new RuntimeException(\"[createDecodeParser][解析异常]\", ex);\n        });\n        return parser;\n    }\n\n    @Override\n    public Buffer encode(byte[] data) {\n        Buffer buffer = Buffer.buffer();\n        buffer.appendBytes(data);\n        buffer.appendBytes(delimiterBytes);\n        return buffer;\n    }\n\n    /**\n     * 解析分隔符字符串为字节数组\n     * <p>\n     * 支持转义字符：\\n、\\r、\\r\\n、\\t\n     *\n     * @param delimiter 分隔符字符串\n     * @return 分隔符字节数组\n     */\n    private byte[] parseDelimiter(String delimiter) {\n        // 处理转义字符\n        String parsed = delimiter\n                .replace(\"\\\\r\\\\n\", \"\\r\\n\")\n                .replace(\"\\\\r\", \"\\r\")\n                .replace(\"\\\\n\", \"\\n\")\n                .replace(\"\\\\t\", \"\\t\");\n        return StrUtil.utf8Bytes(parsed);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/codec/length/IotTcpFixedLengthFrameCodec.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.length;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpCodecTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;\nimport io.vertx.core.Handler;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT TCP 定长帧编解码器\n * <p>\n * 基于固定长度的拆包策略，每条消息固定字节数\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotTcpFixedLengthFrameCodec implements IotTcpFrameCodec {\n\n    /**\n     * 固定消息长度\n     */\n    private final int fixedLength;\n\n    public IotTcpFixedLengthFrameCodec(IotTcpConfig.CodecConfig config) {\n        Assert.notNull(config.getFixedLength(), \"fixedLength 不能为空\");\n        this.fixedLength = config.getFixedLength();\n    }\n\n    @Override\n    public IotTcpCodecTypeEnum getType() {\n        return IotTcpCodecTypeEnum.FIXED_LENGTH;\n    }\n\n    @Override\n    public RecordParser createDecodeParser(Handler<Buffer> handler) {\n        RecordParser parser = RecordParser.newFixed(fixedLength);\n        parser.handler(handler);\n        parser.exceptionHandler(ex -> {\n            throw new RuntimeException(\"[createDecodeParser][解析异常]\", ex);\n        });\n        return parser;\n    }\n\n    @Override\n    public Buffer encode(byte[] data) {\n        // 校验数据长度不能超过固定长度\n        if (data.length > fixedLength) {\n            throw new IllegalArgumentException(String.format(\n                    \"数据长度 %d 超过固定长度 %d\", data.length, fixedLength));\n        }\n        Buffer buffer = Buffer.buffer(fixedLength);\n        buffer.appendBytes(data);\n        // 如果数据不足固定长度，填充 0（RecordParser.newFixed 解码时按固定长度读取，所以发送端需要填充）\n        if (data.length < fixedLength) {\n            byte[] padding = new byte[fixedLength - data.length];\n            buffer.appendBytes(padding);\n        }\n        return buffer;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/codec/length/IotTcpLengthFieldFrameCodec.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.length;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConfig;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpCodecTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;\nimport io.vertx.core.Handler;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.concurrent.atomic.AtomicReference;\n\n/**\n * IoT TCP 长度字段帧编解码器\n * <p>\n * 基于长度字段的拆包策略，消息格式：[长度字段][消息体]\n * <p>\n * 参数说明：\n * <ul>\n *   <li>lengthFieldOffset: 长度字段在消息中的偏移量</li>\n *   <li>lengthFieldLength: 长度字段的字节数（1/2/4）</li>\n *   <li>lengthAdjustment: 长度调整值，用于调整长度字段的实际含义</li>\n *   <li>initialBytesToStrip: 解码后跳过的字节数</li>\n * </ul>\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotTcpLengthFieldFrameCodec implements IotTcpFrameCodec {\n\n    /**\n     * 最大帧长度（64KB），防止 DoS 攻击\n     */\n    private static final int MAX_FRAME_LENGTH = 65536;\n\n    private final int lengthFieldOffset;\n    private final int lengthFieldLength;\n    private final int lengthAdjustment;\n    private final int initialBytesToStrip;\n\n    /**\n     * 头部长度 = 长度字段偏移量 + 长度字段长度\n     */\n    private final int headerLength;\n\n    public IotTcpLengthFieldFrameCodec(IotTcpConfig.CodecConfig config) {\n        Assert.notNull(config.getLengthFieldOffset(), \"lengthFieldOffset 不能为空\");\n        Assert.notNull(config.getLengthFieldLength(), \"lengthFieldLength 不能为空\");\n        Assert.notNull(config.getLengthAdjustment(), \"lengthAdjustment 不能为空\");\n        Assert.notNull(config.getInitialBytesToStrip(), \"initialBytesToStrip 不能为空\");\n        this.lengthFieldOffset = config.getLengthFieldOffset();\n        this.lengthFieldLength = config.getLengthFieldLength();\n        this.lengthAdjustment = config.getLengthAdjustment();\n        this.initialBytesToStrip = config.getInitialBytesToStrip();\n        this.headerLength = lengthFieldOffset + lengthFieldLength;\n    }\n\n    @Override\n    public IotTcpCodecTypeEnum getType() {\n        return IotTcpCodecTypeEnum.LENGTH_FIELD;\n    }\n\n    @Override\n    public RecordParser createDecodeParser(Handler<Buffer> handler) {\n        // 创建状态机：先读取头部，再读取消息体\n        RecordParser parser = RecordParser.newFixed(headerLength);\n        parser.maxRecordSize(MAX_FRAME_LENGTH); // 设置最大记录大小，防止 DoS 攻击\n        final AtomicReference<Integer> bodyLength = new AtomicReference<>(null); // 消息体长度，null 表示读取头部阶段\n        final AtomicReference<Buffer> headerBuffer = new AtomicReference<>(null); // 头部消息\n\n        // 处理读取到的数据\n        parser.handler(buffer -> {\n            if (bodyLength.get() == null) {\n                // 阶段 1: 读取头部，解析长度字段\n                headerBuffer.set(buffer.copy());\n                int length = readLength(buffer, lengthFieldOffset, lengthFieldLength);\n                int frameBodyLength = length + lengthAdjustment;\n                // 检查帧长度是否合法\n                if (frameBodyLength < 0) {\n                    throw new IllegalStateException(String.format(\n                            \"[createDecodeParser][帧长度异常，length: %d, frameBodyLength: %d]\",\n                            length, frameBodyLength));\n                }\n                // 消息体为空，抛出异常\n                if (frameBodyLength == 0) {\n                    throw new IllegalStateException(\"[createDecodeParser][消息体不能为空]\");\n                }\n\n                // 【重要】切换到读取消息体模式\n                bodyLength.set(frameBodyLength);\n                parser.fixedSizeMode(frameBodyLength);\n            } else {\n                // 阶段 2: 读取消息体，组装完整帧\n                Buffer frame = processFrame(headerBuffer.get(), buffer);\n                // 重置状态，准备读取下一帧\n                bodyLength.set(null);\n                headerBuffer.set(null);\n                parser.fixedSizeMode(headerLength);\n\n                // 【重要】处理完整消息\n                handler.handle(frame);\n            }\n        });\n        parser.exceptionHandler(ex -> {\n            throw new RuntimeException(\"[createDecodeParser][解析异常]\", ex);\n        });\n        return parser;\n    }\n\n    @Override\n    public Buffer encode(byte[] data) {\n        Buffer buffer = Buffer.buffer();\n        // 计算要写入的长度值\n        int lengthValue = data.length - lengthAdjustment;\n        // 写入偏移量前的填充字节（如果有）\n        for (int i = 0; i < lengthFieldOffset; i++) {\n            buffer.appendByte((byte) 0);\n        }\n        // 写入长度字段\n        writeLength(buffer, lengthValue, lengthFieldLength);\n        // 写入消息体\n        buffer.appendBytes(data);\n        return buffer;\n    }\n\n    /**\n     * 从 Buffer 中读取长度字段\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private int readLength(Buffer buffer, int offset, int length) {\n        switch (length) {\n            case 1:\n                return buffer.getUnsignedByte(offset);\n            case 2:\n                return buffer.getUnsignedShort(offset);\n            case 4:\n                return buffer.getInt(offset);\n            default:\n                throw new IllegalArgumentException(\"不支持的长度字段长度: \" + length);\n        }\n    }\n\n    /**\n     * 向 Buffer 中写入长度字段\n     */\n    private void writeLength(Buffer buffer, int length, int fieldLength) {\n        switch (fieldLength) {\n            case 1:\n                buffer.appendByte((byte) length);\n                break;\n            case 2:\n                buffer.appendShort((short) length);\n                break;\n            case 4:\n                buffer.appendInt(length);\n                break;\n            default:\n                throw new IllegalArgumentException(\"不支持的长度字段长度: \" + fieldLength);\n        }\n    }\n\n    /**\n     * 处理帧数据（根据 initialBytesToStrip 跳过指定字节）\n     */\n    private Buffer processFrame(Buffer header, Buffer body) {\n        Buffer fullFrame = Buffer.buffer();\n        if (header != null) {\n            fullFrame.appendBuffer(header);\n        }\n        if (body != null) {\n            fullFrame.appendBuffer(body);\n        }\n        // 根据 initialBytesToStrip 跳过指定字节\n        if (initialBytesToStrip > 0 && initialBytesToStrip < fullFrame.length()) {\n            return fullFrame.slice(initialBytesToStrip, fullFrame.length());\n        }\n        return fullFrame;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/handler/downstream/IotTcpDownstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.downstream;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport io.vertx.core.buffer.Buffer;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 TCP 下行消息处理器\n *\n * @author 芋道源码\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class IotTcpDownstreamHandler {\n\n    private final IotTcpConnectionManager connectionManager;\n\n    /**\n     * TCP 帧编解码器（处理粘包/拆包）\n     */\n    private final IotTcpFrameCodec codec;\n    /**\n     * 消息序列化器（处理业务消息序列化/反序列化）\n     */\n    private final IotMessageSerializer serializer;\n\n    /**\n     * 处理下行消息\n     */\n    public void handle(IotDeviceMessage message) {\n        try {\n            // 1.1 检查是否是属性设置消息\n            if (ObjUtil.equals(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(), message.getMethod())) {\n                return;\n            }\n            if (ObjUtil.notEqual(IotDeviceMessageMethodEnum.PROPERTY_SET.getMethod(), message.getMethod())) {\n                log.warn(\"[handle][忽略非属性设置消息: {}]\", message.getMethod());\n                return;\n            }\n            log.info(\"[handle][处理下行消息，设备 ID: {}，方法: {}，消息 ID: {}]\",\n                    message.getDeviceId(), message.getMethod(), message.getId());\n            // 1.2 检查设备连接\n            IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfoByDeviceId(\n                    message.getDeviceId());\n            if (connectionInfo == null) {\n                log.warn(\"[handle][连接信息不存在，设备 ID: {}，方法: {}，消息 ID: {}]\",\n                        message.getDeviceId(), message.getMethod(), message.getId());\n                return;\n            }\n\n            // 2. 序列化 + 帧编码\n            byte[] payload = serializer.serialize(message);\n            Buffer frameData = codec.encode(payload);\n\n            // 3. 发送到设备\n            boolean success = connectionManager.sendToDevice(message.getDeviceId(), frameData.getBytes());\n            if (!success) {\n                throw new RuntimeException(\"下行消息发送失败\");\n            }\n            log.info(\"[handle][下行消息发送成功，设备 ID: {}，方法: {}，消息 ID: {}，数据长度: {} 字节]\",\n                    message.getDeviceId(), message.getMethod(), message.getId(), frameData.length());\n        } catch (Exception e) {\n            log.error(\"[handle][处理下行消息失败，设备 ID: {}，方法: {}，消息内容: {}]\",\n                    message.getDeviceId(), message.getMethod(), message, e);\n        }\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/handler/downstream/IotTcpDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 TCP 下游订阅者：接收下行给设备的消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotTcpDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber {\n\n    private final IotTcpDownstreamHandler downstreamHandler;\n\n    public IotTcpDownstreamSubscriber(IotProtocol protocol,\n                                      IotTcpDownstreamHandler downstreamHandler,\n                                      IotMessageBus messageBus) {\n        super(protocol, messageBus);\n        this.downstreamHandler = downstreamHandler;\n    }\n\n    @Override\n    protected void handleMessage(IotDeviceMessage message) {\n        downstreamHandler.handle(message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/handler/upstream/IotTcpUpstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.core.Handler;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.net.NetSocket;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.extern.slf4j.Slf4j;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_AUTH_FAIL;\n\n/**\n * TCP 上行消息处理器\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotTcpUpstreamHandler implements Handler<NetSocket> {\n\n    private static final String AUTH_METHOD = \"auth\";\n\n    private final String serverId;\n\n    /**\n     * TCP 帧编解码器（处理粘包/拆包）\n     */\n    private final IotTcpFrameCodec codec;\n    /**\n     * 消息序列化器（处理业务消息序列化/反序列化）\n     */\n    private final IotMessageSerializer serializer;\n    /**\n     * TCP 连接管理器\n     */\n    private final IotTcpConnectionManager connectionManager;\n\n    private final IotDeviceMessageService deviceMessageService;\n    private final IotDeviceService deviceService;\n    private final IotDeviceCommonApi deviceApi;\n\n    public IotTcpUpstreamHandler(String serverId,\n                                 IotTcpFrameCodec codec,\n                                 IotMessageSerializer serializer,\n                                 IotTcpConnectionManager connectionManager) {\n        Assert.notNull(codec, \"TCP FrameCodec 必须配置\");\n        Assert.notNull(serializer, \"消息序列化器必须配置\");\n        Assert.notNull(connectionManager, \"连接管理器不能为空\");\n        this.serverId = serverId;\n        this.codec = codec;\n        this.serializer = serializer;\n        this.connectionManager = connectionManager;\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n        this.deviceService = SpringUtil.getBean(IotDeviceService.class);\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n    }\n\n    @Override\n    @SuppressWarnings(\"DuplicatedCode\")\n    public void handle(NetSocket socket) {\n        String remoteAddress = String.valueOf(socket.remoteAddress());\n        log.debug(\"[handle][设备连接，地址: {}]\", remoteAddress);\n\n        // 1. 设置异常和关闭处理器\n        socket.exceptionHandler(ex -> {\n            log.warn(\"[handle][连接异常，地址: {}]\", remoteAddress, ex);\n            socket.close();\n        });\n        socket.closeHandler(v -> {\n            log.debug(\"[handle][连接关闭，地址: {}]\", remoteAddress);\n            cleanupConnection(socket);\n        });\n\n        // 2.1 设置消息处理器\n        Handler<Buffer> messageHandler = buffer -> {\n            try {\n                processMessage(buffer, socket);\n            } catch (Exception e) {\n                log.error(\"[handle][消息处理失败，地址: {}]\", remoteAddress, e);\n                socket.close();\n            }\n        };\n        // 2.2 使用拆包器处理粘包/拆包\n        RecordParser parser = codec.createDecodeParser(messageHandler);\n        socket.handler(parser);\n        log.debug(\"[handle][启用 {} 拆包器，地址: {}]\", codec.getType(), remoteAddress);\n    }\n\n    /**\n     * 处理消息\n     *\n     * @param buffer   消息\n     * @param socket   网络连接\n     */\n    private void processMessage(Buffer buffer, NetSocket socket) {\n        IotDeviceMessage message = null;\n        try {\n            // 1. 反序列化消息\n            message = serializer.deserialize(buffer.getBytes());\n            if (message == null) {\n                sendErrorResponse(socket, null, null, BAD_REQUEST.getCode(), \"消息反序列化失败\");\n                return;\n            }\n\n            // 2. 根据消息类型路由处理\n            if (AUTH_METHOD.equals(message.getMethod())) {\n                // 认证请求\n                handleAuthenticationRequest(message, socket);\n            } else if (IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod().equals(message.getMethod())) {\n                // 设备动态注册请求\n                handleRegisterRequest(message, socket);\n            } else {\n                // 业务消息\n                handleBusinessRequest(message, socket);\n            }\n        } catch (ServiceException e) {\n            // 业务异常，返回对应的错误码和错误信息\n            log.warn(\"[processMessage][业务异常，地址: {}，错误: {}]\", socket.remoteAddress(), e.getMessage());\n            String requestId = message != null ? message.getRequestId() : null;\n            String method = message != null ? message.getMethod() : null;\n            sendErrorResponse(socket, requestId, method, e.getCode(), e.getMessage());\n        } catch (IllegalArgumentException e) {\n            // 参数校验失败，返回 400\n            log.warn(\"[processMessage][参数校验失败，地址: {}，错误: {}]\", socket.remoteAddress(), e.getMessage());\n            String requestId = message != null ? message.getRequestId() : null;\n            String method = message != null ? message.getMethod() : null;\n            sendErrorResponse(socket, requestId, method, BAD_REQUEST.getCode(), e.getMessage());\n        } catch (Exception e) {\n            // 其他异常，返回 500，并重新抛出让上层关闭连接\n            log.error(\"[processMessage][处理消息失败，地址: {}]\", socket.remoteAddress(), e);\n            String requestId = message != null ? message.getRequestId() : null;\n            String method = message != null ? message.getMethod() : null;\n            sendErrorResponse(socket, requestId, method,\n                    INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());\n            throw e;\n        }\n    }\n\n    /**\n     * 处理认证请求\n     *\n     * @param message  消息信息\n     * @param socket   网络连接\n     */\n    @SuppressWarnings(\"DuplicatedCode\")\n    private void handleAuthenticationRequest(IotDeviceMessage message, NetSocket socket) {\n        // 1. 解析认证参数\n        IotDeviceAuthReqDTO authParams = JsonUtils.convertObject(message.getParams(), IotDeviceAuthReqDTO.class);\n        Assert.notNull(authParams, \"认证参数不能为空\");\n        Assert.notBlank(authParams.getUsername(), \"username 不能为空\");\n        Assert.notBlank(authParams.getPassword(), \"password 不能为空\");\n\n        // 2.1 执行认证\n        CommonResult<Boolean> authResult = deviceApi.authDevice(authParams);\n        authResult.checkError();\n        if (BooleanUtil.isFalse(authResult.getData())) {\n            throw exception(DEVICE_AUTH_FAIL);\n        }\n        // 2.2 解析设备信息\n        IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());\n        Assert.notNull(deviceInfo, \"解析设备信息失败\");\n        // 2.3 获取设备信息\n        IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(), deviceInfo.getDeviceName());\n        Assert.notNull(device, \"设备不存在\");\n\n        // 3.1 注册连接\n        registerConnection(socket, device);\n        // 3.2 发送上线消息\n        sendOnlineMessage(device);\n        // 3.3 发送成功响应\n        sendSuccessResponse(socket, message.getRequestId(), AUTH_METHOD, \"认证成功\");\n        log.info(\"[handleAuthenticationRequest][认证成功，设备 ID: {}，设备名: {}]\", device.getId(), device.getDeviceName());\n    }\n\n    /**\n     * 处理设备动态注册请求（一型一密，不需要认证）\n     *\n     * @param message  消息信息\n     * @param socket   网络连接\n     * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification\">阿里云 - 一型一密</a>\n     */\n    @SuppressWarnings(\"DuplicatedCode\")\n    private void handleRegisterRequest(IotDeviceMessage message, NetSocket socket) {\n        // 1. 解析注册参数\n        IotDeviceRegisterReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceRegisterReqDTO.class);\n        Assert.notNull(params, \"注册参数不能为空\");\n        Assert.notBlank(params.getProductKey(), \"productKey 不能为空\");\n        Assert.notBlank(params.getDeviceName(), \"deviceName 不能为空\");\n        Assert.notBlank(params.getSign(), \"sign 不能为空\");\n\n        // 2. 调用动态注册\n        CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params);\n        result.checkError();\n\n        // 3. 发送成功响应\n        sendSuccessResponse(socket, message.getRequestId(),\n                IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), result.getData());\n        log.info(\"[handleRegisterRequest][注册成功，地址: {}，设备名: {}]\",\n                socket.remoteAddress(), params.getDeviceName());\n    }\n\n    /**\n     * 处理业务请求\n     *\n     * @param message  消息信息\n     * @param socket   网络连接\n     */\n    private void handleBusinessRequest(IotDeviceMessage message, NetSocket socket) {\n        // 1. 获取认证信息并处理业务消息\n        IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);\n        if (connectionInfo == null) {\n            log.error(\"[handleBusinessRequest][无法获取连接信息，地址: {}]\", socket.remoteAddress());\n            sendErrorResponse(socket, message.getRequestId(), message.getMethod(),\n                    UNAUTHORIZED.getCode(), \"设备未认证，无法处理业务消息\");\n            return;\n        }\n\n        // 2. 发送消息到消息总线\n        deviceMessageService.sendDeviceMessage(message, connectionInfo.getProductKey(),\n                connectionInfo.getDeviceName(), serverId);\n        log.info(\"[handleBusinessRequest][发送消息到消息总线，地址: {}，消息: {}]\", socket.remoteAddress(), message);\n    }\n\n    /**\n     * 注册连接信息\n     *\n     * @param socket   网络连接\n     * @param device   设备\n     */\n    private void registerConnection(NetSocket socket, IotDeviceRespDTO device) {\n        IotTcpConnectionManager.ConnectionInfo connectionInfo = new IotTcpConnectionManager.ConnectionInfo()\n                .setDeviceId(device.getId())\n                .setProductKey(device.getProductKey())\n                .setDeviceName(device.getDeviceName());\n        connectionManager.registerConnection(socket, device.getId(), connectionInfo);\n    }\n\n    /**\n     * 发送设备上线消息\n     *\n     * @param device 设备信息\n     */\n    private void sendOnlineMessage(IotDeviceRespDTO device) {\n        IotDeviceMessage onlineMessage = IotDeviceMessage.buildStateUpdateOnline();\n        deviceMessageService.sendDeviceMessage(onlineMessage, device.getProductKey(),\n                device.getDeviceName(), serverId);\n    }\n\n    /**\n     * 清理连接\n     *\n     * @param socket 网络连接\n     */\n    private void cleanupConnection(NetSocket socket) {\n        // 1. 发送离线消息\n        IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);\n        if (connectionInfo != null) {\n            IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();\n            deviceMessageService.sendDeviceMessage(offlineMessage, connectionInfo.getProductKey(),\n                    connectionInfo.getDeviceName(), serverId);\n        }\n\n        // 2. 注销连接\n        connectionManager.unregisterConnection(socket);\n    }\n\n    // ===================== 发送响应消息 =====================\n\n    /**\n     * 发送成功响应\n     *\n     * @param socket    网络连接\n     * @param requestId 请求 ID\n     * @param method    方法名\n     * @param data      响应数据\n     */\n    private void sendSuccessResponse(NetSocket socket, String requestId, String method, Object data) {\n        IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, SUCCESS.getCode(), null);\n        writeResponse(socket, responseMessage);\n    }\n\n    /**\n     * 发送错误响应\n     *\n     * @param socket       网络连接\n     * @param requestId    请求 ID\n     * @param method       方法名\n     * @param code         错误码\n     * @param msg          错误消息\n     */\n    private void sendErrorResponse(NetSocket socket, String requestId, String method, Integer code, String msg) {\n        IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, null, code, msg);\n        writeResponse(socket, responseMessage);\n    }\n\n    /**\n     * 写入响应到 Socket\n     *\n     * @param socket          网络连接\n     * @param responseMessage 响应消息\n     */\n    private void writeResponse(NetSocket socket, IotDeviceMessage responseMessage) {\n        byte[] serializedData = serializer.serialize(responseMessage);\n        Buffer frameData = codec.encode(serializedData);\n        socket.write(frameData);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/manager/IotTcpConnectionManager.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager;\n\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.net.NetSocket;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * IoT 网关 TCP 连接管理器\n * <p>\n * 统一管理 TCP 连接的认证状态、设备会话和消息发送功能：\n * 1. 管理 TCP 连接的认证状态\n * 2. 管理设备会话和在线状态\n * 3. 管理消息发送到设备\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotTcpConnectionManager {\n\n    /**\n     * 最大连接数\n     */\n    private final int maxConnections;\n\n    /**\n     * 连接信息映射：NetSocket -> 连接信息\n     */\n    private final Map<NetSocket, ConnectionInfo> connectionMap = new ConcurrentHashMap<>();\n\n    /**\n     * 设备 ID -> NetSocket 的映射\n     */\n    private final Map<Long, NetSocket> deviceSocketMap = new ConcurrentHashMap<>();\n\n    public IotTcpConnectionManager(int maxConnections) {\n        this.maxConnections = maxConnections;\n    }\n\n    /**\n     * 注册设备连接（包含认证信息）\n     *\n     * @param socket         TCP 连接\n     * @param deviceId       设备 ID\n     * @param connectionInfo 连接信息\n     */\n    public synchronized void registerConnection(NetSocket socket, Long deviceId, ConnectionInfo connectionInfo) {\n        // 检查连接数是否已达上限（同步方法确保检查和注册的原子性）\n        if (connectionMap.size() >= maxConnections) {\n            throw new IllegalStateException(\"连接数已达上限: \" + maxConnections);\n        }\n        // 如果设备已有其他连接，先清理旧连接\n        NetSocket oldSocket = deviceSocketMap.get(deviceId);\n        if (oldSocket != null && oldSocket != socket) {\n            log.info(\"[registerConnection][设备已有其他连接，断开旧连接，设备 ID: {}，旧连接: {}]\",\n                    deviceId, oldSocket.remoteAddress());\n            // 先清理映射，再关闭连接\n            connectionMap.remove(oldSocket);\n            oldSocket.close();\n        }\n\n        // 注册新连接\n        connectionMap.put(socket, connectionInfo);\n        deviceSocketMap.put(deviceId, socket);\n        log.info(\"[registerConnection][注册设备连接，设备 ID: {}，连接: {}，product key: {}，device name: {}]\",\n                deviceId, socket.remoteAddress(), connectionInfo.getProductKey(), connectionInfo.getDeviceName());\n    }\n\n    /**\n     * 注销设备连接\n     *\n     * @param socket TCP 连接\n     */\n    public void unregisterConnection(NetSocket socket) {\n        ConnectionInfo connectionInfo = connectionMap.remove(socket);\n        if (connectionInfo == null) {\n            return;\n        }\n        Long deviceId = connectionInfo.getDeviceId();\n        // 仅当 deviceSocketMap 中的 socket 是当前 socket 时才移除，避免误删新连接\n        deviceSocketMap.remove(deviceId, socket);\n        log.info(\"[unregisterConnection][注销设备连接，设备 ID: {}，连接: {}]\", deviceId, socket.remoteAddress());\n    }\n\n    /**\n     * 获取连接信息\n     */\n    public ConnectionInfo getConnectionInfo(NetSocket socket) {\n        return connectionMap.get(socket);\n    }\n\n    /**\n     * 根据设备 ID 获取连接信息\n     */\n    public ConnectionInfo getConnectionInfoByDeviceId(Long deviceId) {\n        NetSocket socket = deviceSocketMap.get(deviceId);\n        return socket != null ? connectionMap.get(socket) : null;\n    }\n\n    /**\n     * 发送消息到设备\n     */\n    public boolean sendToDevice(Long deviceId, byte[] data) {\n        NetSocket socket = deviceSocketMap.get(deviceId);\n        if (socket == null) {\n            log.warn(\"[sendToDevice][设备未连接，设备 ID: {}]\", deviceId);\n            return false;\n        }\n\n        try {\n            socket.write(Buffer.buffer(data));\n            log.debug(\"[sendToDevice][发送消息成功，设备 ID: {}，数据长度: {} 字节]\", deviceId, data.length);\n            return true;\n        } catch (Exception e) {\n            log.error(\"[sendToDevice][发送消息失败，设备 ID: {}]\", deviceId, e);\n            // 发送失败时清理连接\n            unregisterConnection(socket);\n            return false;\n        }\n    }\n\n    /**\n     * 关闭所有连接\n     */\n    public void closeAll() {\n        // 1. 先复制再清空，避免 closeHandler 回调时并发修改\n        List<NetSocket> sockets = new ArrayList<>(connectionMap.keySet());\n        connectionMap.clear();\n        deviceSocketMap.clear();\n        // 2. 关闭所有连接（closeHandler 中 unregisterConnection 发现 map 为空会安全跳过）\n        for (NetSocket socket : sockets) {\n            try {\n                socket.close();\n            } catch (Exception ignored) {\n                // 连接可能已关闭，忽略异常\n            }\n        }\n    }\n\n    /**\n     * 连接信息（包含认证信息）\n     */\n    @Data\n    public static class ConnectionInfo {\n\n        /**\n         * 设备 ID\n         */\n        private Long deviceId;\n        /**\n         * 产品 Key\n         */\n        private String productKey;\n        /**\n         * 设备名称\n         */\n        private String deviceName;\n\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/package-info.java",
    "content": "/**\n * TCP 协议实现包\n * <p>\n * 提供基于 Vert.x TCP Server 的 IoT 设备连接和消息处理功能\n */\npackage cn.iocoder.yudao.module.iot.gateway.protocol.tcp;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotUdpConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.udp;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\n\n/**\n * IoT UDP 协议配置\n *\n * @author 芋道源码\n */\n@Data\npublic class IotUdpConfig {\n\n    /**\n     * 最大会话数\n     */\n    @NotNull(message = \"最大会话数不能为空\")\n    @Min(value = 1, message = \"最大会话数必须大于 0\")\n    private Integer maxSessions = 1000;\n    /**\n     * 会话超时时间（毫秒）\n     * <p>\n     * 基于 Guava Cache 的 expireAfterAccess 实现自动过期清理\n     */\n    @NotNull(message = \"会话超时时间不能为空\")\n    @Min(value = 1000, message = \"会话超时时间必须大于 1000 毫秒\")\n    private Long sessionTimeoutMs = 60000L;\n\n    /**\n     * 接收缓冲区大小（字节）\n     */\n    @NotNull(message = \"接收缓冲区大小不能为空\")\n    @Min(value = 1024, message = \"接收缓冲区大小必须大于 1024 字节\")\n    private Integer receiveBufferSize = 65536;\n    /**\n     * 发送缓冲区大小（字节）\n     */\n    @NotNull(message = \"发送缓冲区大小不能为空\")\n    @Min(value = 1024, message = \"发送缓冲区大小必须大于 1024 字节\")\n    private Integer sendBufferSize = 65536;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotUdpProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.udp;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.udp.handler.downstream.IotUdpDownstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.udp.handler.downstream.IotUdpDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.udp.handler.upstream.IotUdpUpstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.datagram.DatagramSocket;\nimport io.vertx.core.datagram.DatagramSocketOptions;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT UDP 协议实现\n * <p>\n * 基于 Vert.x 实现 UDP 服务器，接收设备上行消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotUdpProtocol implements IotProtocol {\n\n    /**\n     * 协议配置\n     */\n    private final ProtocolProperties properties;\n    /**\n     * 服务器 ID（用于消息追踪，全局唯一）\n     */\n    @Getter\n    private final String serverId;\n\n    /**\n     * 运行状态\n     */\n    @Getter\n    private volatile boolean running = false;\n\n    /**\n     * Vert.x 实例\n     */\n    private Vertx vertx;\n    /**\n     * UDP 服务器\n     */\n    @Getter\n    private DatagramSocket udpSocket;\n    /**\n     * UDP 会话管理器\n     */\n    private final IotUdpSessionManager sessionManager;\n\n    /**\n     * 下行消息订阅者\n     */\n    private IotUdpDownstreamSubscriber downstreamSubscriber;\n\n    /**\n     * 消息序列化器\n     */\n    private final IotMessageSerializer serializer;\n\n    public IotUdpProtocol(ProtocolProperties properties) {\n        IotUdpConfig udpConfig = properties.getUdp();\n        Assert.notNull(udpConfig, \"UDP 协议配置（udp）不能为空\");\n        this.properties = properties;\n        this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());\n\n        // 初始化序列化器\n        IotSerializeTypeEnum serializeType = IotSerializeTypeEnum.of(properties.getSerialize());\n        Assert.notNull(serializeType, \"不支持的序列化类型：\" + properties.getSerialize());\n        IotMessageSerializerManager serializerManager = SpringUtil.getBean(IotMessageSerializerManager.class);\n        this.serializer = serializerManager.get(serializeType);\n\n        // 初始化会话管理器\n        this.sessionManager = new IotUdpSessionManager(udpConfig.getMaxSessions(), udpConfig.getSessionTimeoutMs());\n\n    }\n\n    @Override\n    public String getId() {\n        return properties.getId();\n    }\n\n    @Override\n    public IotProtocolTypeEnum getType() {\n        return IotProtocolTypeEnum.UDP;\n    }\n\n    @Override\n    public void start() {\n        if (running) {\n            log.warn(\"[start][IoT UDP 协议 {} 已经在运行中]\", getId());\n            return;\n        }\n\n        // 1.1 创建 Vertx 实例 和 下行消息订阅者\n        this.vertx = Vertx.vertx();\n        IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);\n        IotUdpDownstreamHandler downstreamHandler = new IotUdpDownstreamHandler(this, sessionManager, serializer);\n        this.downstreamSubscriber = new IotUdpDownstreamSubscriber(this, downstreamHandler, messageBus);\n\n        // 1.2 创建 UDP Socket 选项\n        IotUdpConfig udpConfig = properties.getUdp();\n        DatagramSocketOptions options = new DatagramSocketOptions()\n                .setReceiveBufferSize(udpConfig.getReceiveBufferSize())\n                .setSendBufferSize(udpConfig.getSendBufferSize())\n                .setReuseAddress(true);\n\n        // 1.3 创建 UDP Socket\n        udpSocket = vertx.createDatagramSocket(options);\n\n        // 1.4 创建上行消息处理器\n        IotUdpUpstreamHandler upstreamHandler = new IotUdpUpstreamHandler(serverId, sessionManager, serializer);\n\n        // 1.5 启动 UDP 服务器（阻塞式）\n        try {\n            udpSocket.listen(properties.getPort(), \"0.0.0.0\").result();\n            // 设置数据包处理器\n            udpSocket.handler(packet -> upstreamHandler.handle(packet, udpSocket));\n            running = true;\n            log.info(\"[start][IoT UDP 协议 {} 启动成功，端口：{}，serverId：{}]\",\n                    getId(), properties.getPort(), serverId);\n\n            // 2. 启动下行消息订阅者\n            this.downstreamSubscriber.start();\n        } catch (Exception e) {\n            log.error(\"[start][IoT UDP 协议 {} 启动失败]\", getId(), e);\n            stop0();\n            throw e;\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        stop0();\n    }\n\n    private void stop0() {\n        // 1. 停止下行消息订阅者\n        if (downstreamSubscriber != null) {\n            try {\n                downstreamSubscriber.stop();\n                log.info(\"[stop][IoT UDP 协议 {} 下行消息订阅者已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT UDP 协议 {} 下行消息订阅者停止失败]\", getId(), e);\n            }\n            downstreamSubscriber = null;\n        }\n\n        // 2.1 关闭 UDP Socket\n        if (udpSocket != null) {\n            try {\n                udpSocket.close().result();\n                log.info(\"[stop][IoT UDP 协议 {} 服务器已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT UDP 协议 {} 服务器停止失败]\", getId(), e);\n            }\n            udpSocket = null;\n        }\n        // 2.2 关闭 Vertx 实例\n        if (vertx != null) {\n            try {\n                vertx.close().result();\n                log.info(\"[stop][IoT UDP 协议 {} Vertx 已关闭]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT UDP 协议 {} Vertx 关闭失败]\", getId(), e);\n            }\n            vertx = null;\n        }\n        running = false;\n        log.info(\"[stop][IoT UDP 协议 {} 已停止]\", getId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/handler/downstream/IotUdpDownstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.udp.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport io.vertx.core.datagram.DatagramSocket;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 UDP 下行消息处理器\n *\n * @author 芋道源码\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class IotUdpDownstreamHandler {\n\n    private final IotUdpProtocol protocol;\n\n    private final IotUdpSessionManager sessionManager;\n\n    /**\n     * 消息序列化器（处理业务消息序列化/反序列化）\n     */\n    private final IotMessageSerializer serializer;\n\n    /**\n     * 处理下行消息\n     */\n    public void handle(IotDeviceMessage message) {\n        try {\n            log.info(\"[handle][处理下行消息，设备 ID: {}，方法: {}，消息 ID: {}]\",\n                    message.getDeviceId(), message.getMethod(), message.getId());\n            // 1. 检查设备会话\n            IotUdpSessionManager.SessionInfo sessionInfo = sessionManager.getSession(message.getDeviceId());\n            if (sessionInfo == null) {\n                log.warn(\"[handle][会话信息不存在，设备 ID: {}，方法: {}，消息 ID: {}]\",\n                        message.getDeviceId(), message.getMethod(), message.getId());\n                return;\n            }\n            DatagramSocket socket = protocol.getUdpSocket();\n            if (socket == null) {\n                log.error(\"[handle][UDP Socket 不可用，设备 ID: {}]\", message.getDeviceId());\n                return;\n            }\n\n            // 2. 序列化消息\n            byte[] serializedData = serializer.serialize(message);\n\n            // 3. 发送到设备\n            boolean success = sessionManager.sendToDevice(message.getDeviceId(), serializedData, socket);\n            if (!success) {\n                throw new RuntimeException(\"下行消息发送失败\");\n            }\n            log.info(\"[handle][下行消息发送成功，设备 ID: {}，方法: {}，消息 ID: {}，数据长度: {} 字节]\",\n                    message.getDeviceId(), message.getMethod(), message.getId(), serializedData.length);\n        } catch (Exception e) {\n            log.error(\"[handle][处理下行消息失败，设备 ID: {}，方法: {}，消息内容: {}]\",\n                    message.getDeviceId(), message.getMethod(), message, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/handler/downstream/IotUdpDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.udp.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 UDP 下游订阅者：接收下行给设备的消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotUdpDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber {\n\n    private final IotUdpDownstreamHandler downstreamHandler;\n\n    public IotUdpDownstreamSubscriber(IotProtocol protocol,\n                                      IotUdpDownstreamHandler downstreamHandler,\n                                      IotMessageBus messageBus) {\n        super(protocol, messageBus);\n        this.downstreamHandler = downstreamHandler;\n    }\n\n    @Override\n    protected void handleMessage(IotDeviceMessage message) {\n        downstreamHandler.handle(message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/handler/upstream/IotUdpUpstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.udp.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.datagram.DatagramPacket;\nimport io.vertx.core.datagram.DatagramSocket;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.net.InetSocketAddress;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_AUTH_FAIL;\n\n/**\n * UDP 上行消息处理器\n * <p>\n * 采用无状态 Token 机制（每次请求携带 token）：\n * 1. 认证请求：设备发送 auth 消息，携带 clientId、username、password\n * 2. 返回 Token：服务端验证后返回 JWT token\n * 3. 后续请求：每次请求在 params 中携带 token\n * 4. 服务端验证：每次请求通过 IotDeviceTokenService.verifyToken() 验证\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotUdpUpstreamHandler {\n\n    private static final String AUTH_METHOD = \"auth\";\n\n    /**\n     * Token 参数 Key\n     */\n    private static final String PARAM_KEY_TOKEN = \"token\";\n    /**\n     * Body 参数 Key（实际请求内容）\n     */\n    private static final String PARAM_KEY_BODY = \"body\";\n\n    private final String serverId;\n\n    /**\n     * 消息序列化器（处理业务消息序列化/反序列化）\n     */\n    private final IotMessageSerializer serializer;\n    /**\n     * UDP 会话管理器\n     */\n    private final IotUdpSessionManager sessionManager;\n\n    private final IotDeviceMessageService deviceMessageService;\n    private final IotDeviceService deviceService;\n    private final IotDeviceTokenService deviceTokenService;\n    private final IotDeviceCommonApi deviceApi;\n\n    public IotUdpUpstreamHandler(String serverId,\n                                 IotUdpSessionManager sessionManager,\n                                 IotMessageSerializer serializer) {\n        Assert.notNull(serializer, \"消息序列化器必须配置\");\n        Assert.notNull(sessionManager, \"会话管理器不能为空\");\n        this.serverId = serverId;\n        this.sessionManager = sessionManager;\n        this.serializer = serializer;\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n        this.deviceService = SpringUtil.getBean(IotDeviceService.class);\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n        this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);\n    }\n\n    /**\n     * 处理 UDP 数据包\n     *\n     * @param packet 数据包\n     * @param socket UDP Socket\n     */\n    public void handle(DatagramPacket packet, DatagramSocket socket) {\n        InetSocketAddress senderAddress = new InetSocketAddress(packet.sender().host(), packet.sender().port());\n        Buffer data = packet.data();\n        String addressKey = sessionManager.buildAddressKey(senderAddress);\n        log.debug(\"[handle][收到 UDP 数据包，来源: {}，数据长度: {} 字节]\", addressKey, data.length());\n        processMessage(data, senderAddress, socket);\n    }\n\n    /**\n     * 处理消息\n     *\n     * @param buffer        消息\n     * @param senderAddress 发送者地址\n     * @param socket        UDP Socket\n     */\n    private void processMessage(Buffer buffer, InetSocketAddress senderAddress, DatagramSocket socket) {\n        String addressKey = sessionManager.buildAddressKey(senderAddress);\n        // 1.1 基础检查\n        if (ArrayUtil.isEmpty(buffer)) {\n            return;\n        }\n        // 1.2 反序列化消息\n        IotDeviceMessage message = serializer.deserialize(buffer.getBytes());\n        if (message == null) {\n            sendErrorResponse(socket, senderAddress, null, null, BAD_REQUEST.getCode(), \"消息反序列化失败\");\n            return;\n        }\n\n        // 2. 根据消息类型路由处理\n        try {\n            if (AUTH_METHOD.equals(message.getMethod())) {\n                // 认证请求\n                handleAuthenticationRequest(message, senderAddress, socket);\n            } else if (IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod().equals(message.getMethod())) {\n                // 设备动态注册请求\n                handleRegisterRequest(message, senderAddress, socket);\n            } else {\n                // 业务消息\n                handleBusinessRequest(message, senderAddress, socket);\n            }\n        } catch (ServiceException e) {\n            // 业务异常，返回对应的错误码和错误信息\n            log.warn(\"[processMessage][业务异常，来源: {}，requestId: {}，method: {}，错误: {}]\",\n                    addressKey, message.getRequestId(), message.getMethod(), e.getMessage());\n            sendErrorResponse(socket, senderAddress, message.getRequestId(), message.getMethod(),\n                    e.getCode(), e.getMessage());\n        } catch (IllegalArgumentException e) {\n            // 参数校验失败，返回 400\n            log.warn(\"[processMessage][参数校验失败，来源: {}，requestId: {}，method: {}，错误: {}]\",\n                    addressKey, message.getRequestId(), message.getMethod(), e.getMessage());\n            sendErrorResponse(socket, senderAddress, message.getRequestId(), message.getMethod(),\n                    BAD_REQUEST.getCode(), e.getMessage());\n        } catch (Exception e) {\n            // 其他异常，返回 500\n            log.error(\"[processMessage][处理消息失败，来源: {}，requestId: {}，method: {}]\",\n                    addressKey, message.getRequestId(), message.getMethod(), e);\n            sendErrorResponse(socket, senderAddress, message.getRequestId(), message.getMethod(),\n                    INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());\n        }\n    }\n\n    /**\n     * 处理认证请求\n     *\n     * @param message       消息信息\n     * @param senderAddress 发送者地址\n     * @param socket        UDP Socket\n     */\n    @SuppressWarnings(\"DuplicatedCode\")\n    private void handleAuthenticationRequest(IotDeviceMessage message, InetSocketAddress senderAddress,\n                                             DatagramSocket socket) {\n        String clientId = IdUtil.simpleUUID();\n        // 1. 解析认证参数\n        IotDeviceAuthReqDTO authParams = JsonUtils.convertObject(message.getParams(), IotDeviceAuthReqDTO.class);\n        Assert.notNull(authParams, \"认证参数不能为空\");\n        Assert.notBlank(authParams.getUsername(), \"username 不能为空\");\n        Assert.notBlank(authParams.getPassword(), \"password 不能为空\");\n\n        // 2.1 执行认证\n        CommonResult<Boolean> authResult = deviceApi.authDevice(authParams);\n        authResult.checkError();\n        if (!BooleanUtil.isTrue(authResult.getData())) {\n            throw exception(DEVICE_AUTH_FAIL);\n        }\n        // 2.2 解析设备信息\n        IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());\n        Assert.notNull(deviceInfo, \"解析设备信息失败\");\n        // 2.3 获取设备信息\n        IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(),\n                deviceInfo.getDeviceName());\n        Assert.notNull(device, \"设备不存在\");\n\n        // 3. 生成 JWT Token（无状态）\n        String token = deviceTokenService.createToken(device.getProductKey(), device.getDeviceName());\n\n        // 4.1 注册会话\n        registerSession(senderAddress, device, clientId);\n        // 4.2 发送上线消息\n        sendOnlineMessage(device);\n        // 4.3 发送成功响应（包含 token）\n        sendSuccessResponse(socket, senderAddress, message.getRequestId(), AUTH_METHOD,\n                MapUtil.of(\"token\", token));\n        log.info(\"[handleAuthenticationRequest][认证成功，设备 ID: {}，设备名: {}，来源: {}]\",\n                device.getId(), device.getDeviceName(), sessionManager.buildAddressKey(senderAddress));\n    }\n\n    /**\n     * 处理设备动态注册请求（一型一密，不需要认证）\n     *\n     * @param message       消息信息\n     * @param senderAddress 发送者地址\n     * @param socket        UDP Socket\n     * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification\">阿里云 - 一型一密</a>\n     */\n    @SuppressWarnings(\"DuplicatedCode\")\n    private void handleRegisterRequest(IotDeviceMessage message, InetSocketAddress senderAddress,\n                                       DatagramSocket socket) {\n        // 1. 解析注册参数\n        IotDeviceRegisterReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceRegisterReqDTO.class);\n        Assert.notNull(params, \"注册参数不能为空\");\n        Assert.notBlank(params.getProductKey(), \"productKey 不能为空\");\n        Assert.notBlank(params.getDeviceName(), \"deviceName 不能为空\");\n        Assert.notBlank(params.getSign(), \"sign 不能为空\");\n\n        // 2. 调用动态注册\n        CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params);\n        result.checkError();\n\n        // 3. 发送成功响应\n        sendSuccessResponse(socket, senderAddress, message.getRequestId(),\n                IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), result.getData());\n        log.info(\"[handleRegisterRequest][注册成功，来源: {}，设备名: {}]\",\n                sessionManager.buildAddressKey(senderAddress), params.getDeviceName());\n    }\n\n    /**\n     * 处理业务请求\n     * <p>\n     * 请求参数格式：\n     * - token：JWT 令牌\n     * - body：实际请求内容（可以是 Map、List 或其他类型）\n     *\n     * @param message       消息信息\n     * @param senderAddress 发送者地址\n     * @param socket        UDP Socket\n     */\n    @SuppressWarnings(\"unchecked\")\n    private void handleBusinessRequest(IotDeviceMessage message, InetSocketAddress senderAddress,\n                                       DatagramSocket socket) {\n        String addressKey = sessionManager.buildAddressKey(senderAddress);\n        // 1.1 从消息中提取 token 和 body\n        String token = null;\n        Object body = null;\n        if (message.getParams() instanceof Map) {\n            Map<String, Object> paramsMap = (Map<String, Object>) message.getParams();\n            token = (String) paramsMap.get(PARAM_KEY_TOKEN);\n            body = paramsMap.get(PARAM_KEY_BODY);\n        }\n        if (StrUtil.isBlank(token)) {\n            log.warn(\"[handleBusinessRequest][缺少 token，来源: {}]\", addressKey);\n            sendErrorResponse(socket, senderAddress, message.getRequestId(), message.getMethod(),\n                    UNAUTHORIZED.getCode(), \"请先进行认证\");\n            return;\n        }\n        // 1.2 验证 token，获取设备信息\n        IotDeviceIdentity deviceInfo = deviceTokenService.verifyToken(token);\n        if (deviceInfo == null) {\n            log.warn(\"[handleBusinessRequest][token 无效或已过期，来源: {}]\", addressKey);\n            sendErrorResponse(socket, senderAddress, message.getRequestId(), message.getMethod(),\n                    UNAUTHORIZED.getCode(), \"token 无效或已过期\");\n            return;\n        }\n        // 1.3 获取设备详细信息\n        IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(),\n                deviceInfo.getDeviceName());\n        if (device == null) {\n            log.warn(\"[handleBusinessRequest][设备不存在，来源: {}，productKey: {}，deviceName: {}]\",\n                    addressKey, deviceInfo.getProductKey(), deviceInfo.getDeviceName());\n            sendErrorResponse(socket, senderAddress, message.getRequestId(), message.getMethod(),\n                    BAD_REQUEST.getCode(), \"设备不存在\");\n            return;\n        }\n\n        // 2. 更新会话地址（如有变化）\n        sessionManager.updateSessionAddress(device.getId(), senderAddress);\n\n        // 3. 将 body 设置为实际的 params，发送消息到消息总线\n        message.setParams(body);\n        deviceMessageService.sendDeviceMessage(message, device.getProductKey(),\n                device.getDeviceName(), serverId);\n        log.debug(\"[handleBusinessRequest][业务消息处理成功，设备 ID: {}，方法: {}，来源: {}]\",\n                device.getId(), message.getMethod(), addressKey);\n    }\n\n    /**\n     * 注册会话信息\n     *\n     * @param address  设备地址\n     * @param device   设备\n     * @param clientId 客户端 ID\n     */\n    private void registerSession(InetSocketAddress address, IotDeviceRespDTO device, String clientId) {\n        IotUdpSessionManager.SessionInfo sessionInfo = new IotUdpSessionManager.SessionInfo()\n                .setDeviceId(device.getId())\n                .setProductKey(device.getProductKey())\n                .setDeviceName(device.getDeviceName())\n                .setAddress(address);\n        sessionManager.registerSession(device.getId(), sessionInfo);\n    }\n\n    /**\n     * 发送设备上线消息\n     *\n     * @param device 设备信息\n     */\n    private void sendOnlineMessage(IotDeviceRespDTO device) {\n        IotDeviceMessage onlineMessage = IotDeviceMessage.buildStateUpdateOnline();\n        deviceMessageService.sendDeviceMessage(onlineMessage, device.getProductKey(),\n                device.getDeviceName(), serverId);\n    }\n\n    // ===================== 发送响应消息 =====================\n\n    /**\n     * 发送成功响应\n     *\n     * @param socket    UDP Socket\n     * @param address   目标地址\n     * @param requestId 请求 ID\n     * @param method    方法名\n     * @param data      响应数据\n     */\n    private void sendSuccessResponse(DatagramSocket socket, InetSocketAddress address,\n                                     String requestId, String method, Object data) {\n        IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, SUCCESS.getCode(), null);\n        writeResponse(socket, address, responseMessage);\n    }\n\n    /**\n     * 发送错误响应\n     *\n     * @param socket    UDP Socket\n     * @param address   目标地址\n     * @param requestId 请求 ID\n     * @param method    方法名\n     * @param code      错误码\n     * @param msg       错误消息\n     */\n    private void sendErrorResponse(DatagramSocket socket, InetSocketAddress address,\n                                   String requestId, String method, Integer code, String msg) {\n        IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, null, code, msg);\n        writeResponse(socket, address, responseMessage);\n    }\n\n    /**\n     * 写入响应到 Socket\n     *\n     * @param socket          UDP Socket\n     * @param address         目标地址\n     * @param responseMessage 响应消息\n     */\n    private void writeResponse(DatagramSocket socket, InetSocketAddress address, IotDeviceMessage responseMessage) {\n        try {\n            byte[] serializedData = serializer.serialize(responseMessage);\n            socket.send(Buffer.buffer(serializedData), address.getPort(), address.getHostString(), result -> {\n                if (result.failed()) {\n                    log.error(\"[writeResponse][发送响应失败，地址: {}]\",\n                            sessionManager.buildAddressKey(address), result.cause());\n                }\n            });\n        } catch (Exception e) {\n            log.error(\"[writeResponse][发送响应异常，地址: {}]\",\n                    sessionManager.buildAddressKey(address), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/manager/IotUdpSessionManager.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager;\n\nimport cn.hutool.core.util.ObjUtil;\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.datagram.DatagramSocket;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.net.InetSocketAddress;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * IoT 网关 UDP 会话管理器\n * <p>\n * 基于 Guava Cache 实现会话的自动过期清理：\n * 1. 管理设备会话信息（设备 ID -> 地址映射）\n * 2. 自动清理超时会话（expireAfterAccess）\n * 3. 限制最大会话数（maximumSize）\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotUdpSessionManager {\n\n    /**\n     * 设备会话缓存：设备 ID -> 会话信息\n     * <p>\n     * 使用 Guava Cache 自动管理过期：expireAfterAccess：每次访问（get/put）自动刷新过期时间\n     */\n    private final Cache<Long, SessionInfo> deviceSessionCache;\n\n    private final int maxSessions;\n\n    public IotUdpSessionManager(int maxSessions, long sessionTimeoutMs) {\n        this.maxSessions = maxSessions;\n        this.deviceSessionCache = CacheBuilder.newBuilder()\n                .maximumSize(maxSessions)\n                .expireAfterAccess(sessionTimeoutMs, TimeUnit.MILLISECONDS)\n                .build();\n    }\n\n    /**\n     * 注册设备会话\n     *\n     * @param deviceId    设备 ID\n     * @param sessionInfo 会话信息\n     */\n    public synchronized void registerSession(Long deviceId, SessionInfo sessionInfo) {\n        // 检查是否为新设备，且会话数已达上限（同步方法确保检查和注册的原子性）\n        if (deviceSessionCache.getIfPresent(deviceId) == null\n                && deviceSessionCache.size() >= maxSessions) {\n            throw new IllegalStateException(\"会话数已达上限: \" + maxSessions);\n        }\n        // 注册会话\n        deviceSessionCache.put(deviceId, sessionInfo);\n        log.info(\"[registerSession][注册设备会话，设备 ID: {}，地址: {}，productKey: {}，deviceName: {}]\",\n                deviceId, buildAddressKey(sessionInfo.getAddress()),\n                sessionInfo.getProductKey(), sessionInfo.getDeviceName());\n    }\n\n    /**\n     * 获取会话信息\n     * <p>\n     * 注意：调用此方法会自动刷新会话的过期时间\n     *\n     * @param deviceId 设备 ID\n     * @return 会话信息，不存在则返回 null\n     */\n    public SessionInfo getSession(Long deviceId) {\n        return deviceSessionCache.getIfPresent(deviceId);\n    }\n\n    /**\n     * 更新设备会话地址（设备地址变更时调用）\n     * <p>\n     * 注意：getIfPresent 已自动刷新过期时间，无需重新 put\n     *\n     * @param deviceId   设备 ID\n     * @param newAddress 新地址\n     */\n    public void updateSessionAddress(Long deviceId, InetSocketAddress newAddress) {\n        // 地址未变化，无需更新\n        SessionInfo sessionInfo = deviceSessionCache.getIfPresent(deviceId);\n        if (sessionInfo == null) {\n            return;\n        }\n        if (ObjUtil.equals(newAddress, sessionInfo.getAddress())) {\n            return;\n        }\n\n        // 更新地址\n        String oldAddressKey = buildAddressKey(sessionInfo.getAddress());\n        sessionInfo.setAddress(newAddress);\n        log.debug(\"[updateSessionAddress][更新设备地址，设备 ID: {}，旧地址: {}，新地址: {}]\",\n                deviceId, oldAddressKey, buildAddressKey(newAddress));\n    }\n\n    /**\n     * 发送消息到设备\n     *\n     * @param deviceId 设备 ID\n     * @param data     数据\n     * @param socket   UDP Socket\n     * @return 是否发送成功\n     */\n    public boolean sendToDevice(Long deviceId, byte[] data, DatagramSocket socket) {\n        SessionInfo sessionInfo = deviceSessionCache.getIfPresent(deviceId);\n        if (sessionInfo == null || sessionInfo.getAddress() == null) {\n            log.warn(\"[sendToDevice][设备会话不存在，设备 ID: {}]\", deviceId);\n            return false;\n        }\n        InetSocketAddress address = sessionInfo.getAddress();\n        try {\n            // 使用 CompletableFuture 同步等待发送结果\n            CompletableFuture<Boolean> future = new CompletableFuture<>();\n            socket.send(Buffer.buffer(data), address.getPort(), address.getHostString(), result -> {\n                if (result.succeeded()) {\n                    log.debug(\"[sendToDevice][发送消息成功，设备 ID: {}，地址: {}，数据长度: {} 字节]\",\n                            deviceId, buildAddressKey(address), data.length);\n                    future.complete(true);\n                } else {\n                    log.error(\"[sendToDevice][发送消息失败，设备 ID: {}，地址: {}]\",\n                            deviceId, buildAddressKey(address), result.cause());\n                    future.complete(false);\n                }\n            });\n            // 同步等待结果，超时 5 秒\n            return future.get(5, TimeUnit.SECONDS);\n        } catch (Exception e) {\n            log.error(\"[sendToDevice][发送消息异常，设备 ID: {}]\", deviceId, e);\n            return false;\n        }\n    }\n\n    /**\n     * 构建地址 Key（用于日志输出）\n     *\n     * @param address 地址\n     * @return 地址 Key\n     */\n    public String buildAddressKey(InetSocketAddress address) {\n        return address.getHostString() + \":\" + address.getPort();\n    }\n\n    /**\n     * 会话信息\n     */\n    @Data\n    public static class SessionInfo {\n\n        /**\n         * 设备 ID\n         */\n        private Long deviceId;\n        /**\n         * 产品 Key\n         */\n        private String productKey;\n        /**\n         * 设备名称\n         */\n        private String deviceName;\n\n        /**\n         * 设备地址\n         */\n        private InetSocketAddress address;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/package-info.java",
    "content": "/**\n * UDP 协议实现包\n * <p>\n * 提供基于 Vert.x DatagramSocket 的 IoT 设备连接和消息处理功能\n */\npackage cn.iocoder.yudao.module.iot.gateway.protocol.udp;"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotWebSocketConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.websocket;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * IoT WebSocket 协议配置\n *\n * @author 芋道源码\n */\n@Data\npublic class IotWebSocketConfig {\n\n    /**\n     * WebSocket 路径（默认：/ws）\n     */\n    @NotEmpty(message = \"WebSocket 路径不能为空\")\n    private String path = \"/ws\";\n\n    /**\n     * 最大消息大小（字节，默认 64KB）\n     */\n    @NotNull(message = \"最大消息大小不能为空\")\n    private Integer maxMessageSize = 65536;\n    /**\n     * 最大帧大小（字节，默认 64KB）\n     */\n    @NotNull(message = \"最大帧大小不能为空\")\n    private Integer maxFrameSize = 65536;\n\n    /**\n     * 空闲超时时间（秒，默认 60）\n     */\n    @NotNull(message = \"空闲超时时间不能为空\")\n    private Integer idleTimeoutSeconds = 60;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotWebSocketProtocol.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.websocket;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.websocket.handler.downstream.IotWebSocketDownstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.websocket.handler.downstream.IotWebSocketDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.websocket.handler.upstream.IotWebSocketUpstreamHandler;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.http.HttpServer;\nimport io.vertx.core.http.HttpServerOptions;\nimport io.vertx.core.net.PemKeyCertOptions;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT WebSocket 协议实现\n * <p>\n * 基于 Vert.x 实现 WebSocket 服务器，接收设备上行消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotWebSocketProtocol implements IotProtocol {\n\n    /**\n     * 协议配置\n     */\n    private final ProtocolProperties properties;\n    /**\n     * 服务器 ID（用于消息追踪，全局唯一）\n     */\n    @Getter\n    private final String serverId;\n\n    /**\n     * 运行状态\n     */\n    @Getter\n    private volatile boolean running = false;\n\n    /**\n     * Vert.x 实例\n     */\n    private Vertx vertx;\n    /**\n     * WebSocket 服务器\n     */\n    private HttpServer httpServer;\n    /**\n     * WebSocket 连接管理器\n     */\n    private final IotWebSocketConnectionManager connectionManager;\n\n    /**\n     * 下行消息订阅者\n     */\n    private IotWebSocketDownstreamSubscriber downstreamSubscriber;\n\n    /**\n     * 消息序列化器\n     */\n    private final IotMessageSerializer serializer;\n\n    public IotWebSocketProtocol(ProtocolProperties properties) {\n        Assert.notNull(properties, \"协议实例配置不能为空\");\n        Assert.notNull(properties.getWebsocket(), \"WebSocket 协议配置（websocket）不能为空\");\n        this.properties = properties;\n        this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());\n\n        // 初始化序列化器\n        IotSerializeTypeEnum serializeType = IotSerializeTypeEnum.of(properties.getSerialize());\n        Assert.notNull(serializeType, \"不支持的序列化类型：\" + properties.getSerialize());\n        IotMessageSerializerManager serializerManager = SpringUtil.getBean(IotMessageSerializerManager.class);\n        this.serializer = serializerManager.get(serializeType);\n\n        // 初始化连接管理器\n        this.connectionManager = new IotWebSocketConnectionManager();\n\n    }\n\n    @Override\n    public String getId() {\n        return properties.getId();\n    }\n\n    @Override\n    public IotProtocolTypeEnum getType() {\n        return IotProtocolTypeEnum.WEBSOCKET;\n    }\n\n    @Override\n    @SuppressWarnings(\"deprecation\")\n    public void start() {\n        if (running) {\n            log.warn(\"[start][IoT WebSocket 协议 {} 已经在运行中]\", getId());\n            return;\n        }\n\n        // 1.1 创建 Vertx 实例\n        this.vertx = Vertx.vertx();\n\n        // 1.2 创建服务器选项\n        IotWebSocketConfig wsConfig = properties.getWebsocket();\n        HttpServerOptions options = new HttpServerOptions()\n                .setPort(properties.getPort())\n                .setIdleTimeout(wsConfig.getIdleTimeoutSeconds())\n                .setMaxWebSocketFrameSize(wsConfig.getMaxFrameSize())\n                .setMaxWebSocketMessageSize(wsConfig.getMaxMessageSize());\n        IotGatewayProperties.SslConfig sslConfig = properties.getSsl();\n        if (sslConfig != null && Boolean.TRUE.equals(sslConfig.getSsl())) {\n            PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions()\n                    .setKeyPath(sslConfig.getSslKeyPath())\n                    .setCertPath(sslConfig.getSslCertPath());\n            options.setSsl(true).setKeyCertOptions(pemKeyCertOptions);\n        }\n\n        // 1.3 创建服务器并设置 WebSocket 处理器\n        httpServer = vertx.createHttpServer(options);\n        httpServer.webSocketHandler(socket -> {\n            // 验证路径\n            if (ObjUtil.notEqual(wsConfig.getPath(), socket.path())) {\n                log.warn(\"[webSocketHandler][WebSocket 路径不匹配，拒绝连接，路径: {}，期望: {}]\",\n                        socket.path(), wsConfig.getPath());\n                socket.reject();\n                return;\n            }\n            // 创建上行处理器\n            IotWebSocketUpstreamHandler handler = new IotWebSocketUpstreamHandler(serverId, serializer, connectionManager);\n            handler.handle(socket);\n        });\n\n        // 1.4 启动服务器\n        try {\n            httpServer.listen().result();\n            running = true;\n            log.info(\"[start][IoT WebSocket 协议 {} 启动成功，端口：{}，路径：{}，serverId：{}]\",\n                    getId(), properties.getPort(), wsConfig.getPath(), serverId);\n\n            // 2. 启动下行消息订阅者\n            IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);\n            IotWebSocketDownstreamHandler downstreamHandler = new IotWebSocketDownstreamHandler(serializer, connectionManager);\n            this.downstreamSubscriber = new IotWebSocketDownstreamSubscriber(this, downstreamHandler, messageBus);\n            downstreamSubscriber.start();\n        } catch (Exception e) {\n            log.error(\"[start][IoT WebSocket 协议 {} 启动失败]\", getId(), e);\n            stop0();\n            throw e;\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        stop0();\n    }\n\n    private void stop0() {\n        // 1. 停止下行消息订阅者\n        if (downstreamSubscriber != null) {\n            try {\n                downstreamSubscriber.stop();\n                log.info(\"[stop][IoT WebSocket 协议 {} 下行消息订阅者已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT WebSocket 协议 {} 下行消息订阅者停止失败]\", getId(), e);\n            }\n            downstreamSubscriber = null;\n        }\n\n        // 2.1 关闭所有连接\n        connectionManager.closeAll();\n        // 2.2 关闭 WebSocket 服务器\n        if (httpServer != null) {\n            try {\n                httpServer.close().result();\n                log.info(\"[stop][IoT WebSocket 协议 {} 服务器已停止]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT WebSocket 协议 {} 服务器停止失败]\", getId(), e);\n            }\n            httpServer = null;\n        }\n        // 2.3 关闭 Vertx 实例\n        if (vertx != null) {\n            try {\n                vertx.close().result();\n                log.info(\"[stop][IoT WebSocket 协议 {} Vertx 已关闭]\", getId());\n            } catch (Exception e) {\n                log.error(\"[stop][IoT WebSocket 协议 {} Vertx 关闭失败]\", getId(), e);\n            }\n            vertx = null;\n        }\n        running = false;\n        log.info(\"[stop][IoT WebSocket 协议 {} 已停止]\", getId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/handler/downstream/IotWebSocketDownstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.websocket.handler.downstream;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 WebSocket 下行消息处理器\n *\n * @author 芋道源码\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class IotWebSocketDownstreamHandler {\n\n    private final IotMessageSerializer serializer;\n\n    private final IotWebSocketConnectionManager connectionManager;\n\n    /**\n     * 处理下行消息\n     */\n    public void handle(IotDeviceMessage message) {\n        try {\n            log.info(\"[handle][处理下行消息，设备 ID: {}，方法: {}，消息 ID: {}]\",\n                    message.getDeviceId(), message.getMethod(), message.getId());\n\n            // 1. 获取连接信息\n            IotWebSocketConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfoByDeviceId(\n                    message.getDeviceId());\n            if (connectionInfo == null) {\n                log.error(\"[handle][连接信息不存在，设备 ID: {}]\", message.getDeviceId());\n                return;\n            }\n\n            // 2. 序列化\n            byte[] bytes = serializer.serialize(message);\n            String bytesContent = StrUtil.utf8Str(bytes);\n\n            // 3. 发送到设备\n            boolean success = connectionManager.sendToDevice(connectionInfo.getDeviceId(), bytesContent);\n            if (!success) {\n                throw new RuntimeException(\"下行消息发送失败\");\n            }\n            log.info(\"[handle][下行消息发送成功，设备 ID: {}，方法: {}，消息 ID: {}，数据长度: {} 字节]\",\n                    message.getDeviceId(), message.getMethod(), message.getId(), bytes.length);\n        } catch (Exception e) {\n            log.error(\"[handle][处理下行消息失败，设备 ID: {}，方法: {}，消息内容: {}]\",\n                    message.getDeviceId(), message.getMethod(), message, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/handler/downstream/IotWebSocketDownstreamSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.websocket.handler.downstream;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketProtocol;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * IoT 网关 WebSocket 下游订阅者：接收下行给设备的消息\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotWebSocketDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber {\n\n    private final IotWebSocketDownstreamHandler downstreamHandler;\n\n    public IotWebSocketDownstreamSubscriber(IotWebSocketProtocol protocol,\n                                            IotWebSocketDownstreamHandler downstreamHandler,\n                                            IotMessageBus messageBus) {\n        super(protocol, messageBus);\n        this.downstreamHandler = downstreamHandler;\n    }\n\n    @Override\n    protected void handleMessage(IotDeviceMessage message) {\n        downstreamHandler.handle(message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/handler/upstream/IotWebSocketUpstreamHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.websocket.handler.upstream;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;\nimport io.vertx.core.Handler;\nimport io.vertx.core.http.ServerWebSocket;\nimport lombok.extern.slf4j.Slf4j;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_AUTH_FAIL;\n\n\n/**\n * WebSocket 上行消息处理器\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotWebSocketUpstreamHandler implements Handler<ServerWebSocket> {\n\n    private static final String AUTH_METHOD = \"auth\";\n\n    private final String serverId;\n\n    /**\n     * 消息序列化器（处理业务消息序列化/反序列化）\n     */\n    private final IotMessageSerializer serializer;\n    /**\n     * 连接管理器\n     */\n    private final IotWebSocketConnectionManager connectionManager;\n\n    private final IotDeviceMessageService deviceMessageService;\n    private final IotDeviceService deviceService;\n    private final IotDeviceCommonApi deviceApi;\n\n    public IotWebSocketUpstreamHandler(String serverId,\n                                       IotMessageSerializer serializer,\n                                       IotWebSocketConnectionManager connectionManager) {\n        this.serverId = serverId;\n        this.serializer = serializer;\n        this.connectionManager = connectionManager;\n        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);\n        this.deviceService = SpringUtil.getBean(IotDeviceService.class);\n        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);\n    }\n\n    @Override\n    @SuppressWarnings(\"DuplicatedCode\")\n    public void handle(ServerWebSocket socket) {\n        String remoteAddress = String.valueOf(socket.remoteAddress());\n        log.debug(\"[handle][设备连接，地址: {}]\", remoteAddress);\n\n        // 1. 设置异常和关闭处理器\n        socket.exceptionHandler(ex -> {\n            log.warn(\"[handle][连接异常，地址: {}]\", remoteAddress, ex);\n            socket.close();\n        });\n        socket.closeHandler(v -> {\n            log.debug(\"[handle][连接关闭，地址: {}]\", remoteAddress);\n            cleanupConnection(socket);\n        });\n\n        // 2. 设置消息处理器（仅支持文本帧）\n        socket.textMessageHandler(message -> {\n            try {\n                processMessage(StrUtil.utf8Bytes(message), socket);\n            } catch (Exception e) {\n                log.error(\"[handle][消息解码失败，断开连接，地址: {}，错误: {}]\", remoteAddress, e.getMessage());\n                socket.close();\n            }\n        });\n    }\n\n    /**\n     * 处理消息\n     *\n     * @param payload  消息负载\n     * @param socket   WebSocket 连接\n     */\n    private void processMessage(byte[] payload, ServerWebSocket socket) {\n        IotDeviceMessage message = null;\n        try {\n            // 1.1 基础检查\n            if (ArrayUtil.isEmpty(payload)) {\n                return;\n            }\n            // 1.2 解码消息\n            message = serializer.deserialize(payload);\n            Assert.notNull(message, \"消息反序列化失败\");\n            Assert.notBlank(message.getMethod(), \"method 不能为空\");\n\n            // 2. 根据消息类型路由处理\n            if (AUTH_METHOD.equals(message.getMethod())) {\n                handleAuthenticationRequest(message, socket);\n            } else if (IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod().equals(message.getMethod())) {\n                handleRegisterRequest(message, socket);\n            } else {\n                handleBusinessRequest(message, socket);\n            }\n        } catch (ServiceException e) {\n            log.warn(\"[processMessage][业务异常，错误: {}]\", e.getMessage());\n            String requestId = message != null ? message.getRequestId() : null;\n            String method = message != null ? message.getMethod() : null;\n            sendErrorResponse(socket, requestId, method, e.getCode(), e.getMessage());\n        } catch (IllegalArgumentException e) {\n            log.warn(\"[processMessage][参数校验失败，错误: {}]\", e.getMessage());\n            String requestId = message != null ? message.getRequestId() : null;\n            String method = message != null ? message.getMethod() : null;\n            sendErrorResponse(socket, requestId, method, BAD_REQUEST.getCode(), e.getMessage());\n        } catch (Exception e) {\n            log.error(\"[processMessage][处理消息失败]\", e);\n            String requestId = message != null ? message.getRequestId() : null;\n            String method = message != null ? message.getMethod() : null;\n            sendErrorResponse(socket, requestId, method, INTERNAL_SERVER_ERROR.getCode(),\n                    INTERNAL_SERVER_ERROR.getMsg());\n            throw e;\n        }\n    }\n\n    /**\n     * 处理认证请求\n     *\n     * @param message  消息信息\n     * @param socket   WebSocket 连接\n     */\n    @SuppressWarnings(\"DuplicatedCode\")\n    private void handleAuthenticationRequest(IotDeviceMessage message, ServerWebSocket socket) {\n        // 1. 解析认证参数\n        IotDeviceAuthReqDTO authParams = JsonUtils.convertObject(message.getParams(), IotDeviceAuthReqDTO.class);\n        Assert.notNull(authParams, \"认证参数不能为空\");\n        Assert.notBlank(authParams.getUsername(), \"username 不能为空\");\n        Assert.notBlank(authParams.getPassword(), \"password 不能为空\");\n\n        // 2.1 执行认证\n        CommonResult<Boolean> authResult = deviceApi.authDevice(authParams);\n        authResult.checkError();\n        if (BooleanUtil.isFalse(authResult.getData())) {\n            throw exception(DEVICE_AUTH_FAIL);\n        }\n        // 2.2 解析设备信息\n        IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());\n        Assert.notNull(deviceInfo, \"解析设备信息失败\");\n        // 2.3 获取设备信息\n        IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(), deviceInfo.getDeviceName());\n        Assert.notNull(device, \"设备不存在\");\n\n        // 3.1 注册连接\n        registerConnection(socket, device);\n        // 3.2 发送上线消息\n        sendOnlineMessage(device);\n        // 3.3 发送成功响应\n        sendSuccessResponse(socket, message.getRequestId(), AUTH_METHOD, \"认证成功\");\n        log.info(\"[handleAuthenticationRequest][认证成功，设备 ID: {}，设备名: {}]\", device.getId(), device.getDeviceName());\n    }\n\n    /**\n     * 处理设备动态注册请求（一型一密，不需要认证）\n     *\n     * @param message  消息信息\n     * @param socket   WebSocket 连接\n     * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification\">阿里云 - 一型一密</a>\n     */\n    @SuppressWarnings(\"DuplicatedCode\")\n    private void handleRegisterRequest(IotDeviceMessage message, ServerWebSocket socket) {\n        // 1. 解析注册参数\n        IotDeviceRegisterReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceRegisterReqDTO.class);\n        Assert.notNull(params, \"注册参数不能为空\");\n        Assert.notBlank(params.getProductKey(), \"productKey 不能为空\");\n        Assert.notBlank(params.getDeviceName(), \"deviceName 不能为空\");\n        Assert.notBlank(params.getSign(), \"sign 不能为空\");\n\n        // 2. 调用动态注册\n        CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params);\n        result.checkError();\n\n        // 3. 发送成功响应（包含 deviceSecret）\n        sendSuccessResponse(socket, message.getRequestId(),\n                IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), result.getData());\n        log.info(\"[handleRegisterRequest][注册成功，设备名: {}]\", params.getDeviceName());\n    }\n\n    /**\n     * 处理业务请求\n     *\n     * @param message  消息信息\n     * @param socket   WebSocket 连接\n     */\n    private void handleBusinessRequest(IotDeviceMessage message, ServerWebSocket socket) {\n        // 1. 获取认证信息并处理业务消息\n        IotWebSocketConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);\n        if (connectionInfo == null) {\n            log.warn(\"[handleBusinessRequest][连接未认证，拒绝处理业务消息]\");\n            sendErrorResponse(socket, message.getRequestId(), message.getMethod(),\n                    UNAUTHORIZED.getCode(), \"设备未认证，无法处理业务消息\");\n            return;\n        }\n\n        // 2. 发送消息到消息总线\n        deviceMessageService.sendDeviceMessage(message, connectionInfo.getProductKey(),\n                connectionInfo.getDeviceName(), serverId);\n        log.info(\"[handleBusinessRequest][发送消息到消息总线，消息: {}]\", message);\n    }\n\n    /**\n     * 注册连接信息\n     *\n     * @param socket   WebSocket 连接\n     * @param device   设备\n     */\n    private void registerConnection(ServerWebSocket socket, IotDeviceRespDTO device) {\n        IotWebSocketConnectionManager.ConnectionInfo connectionInfo = new IotWebSocketConnectionManager.ConnectionInfo()\n                .setDeviceId(device.getId())\n                .setProductKey(device.getProductKey())\n                .setDeviceName(device.getDeviceName());\n        connectionManager.registerConnection(socket, device.getId(), connectionInfo);\n    }\n\n    /**\n     * 发送设备上线消息\n     *\n     * @param device 设备信息\n     */\n    private void sendOnlineMessage(IotDeviceRespDTO device) {\n        try {\n            IotDeviceMessage onlineMessage = IotDeviceMessage.buildStateUpdateOnline();\n            deviceMessageService.sendDeviceMessage(onlineMessage, device.getProductKey(),\n                    device.getDeviceName(), serverId);\n        } catch (Exception e) {\n            log.error(\"[sendOnlineMessage][发送上线消息失败，设备: {}]\", device.getDeviceName(), e);\n        }\n    }\n\n    /**\n     * 清理连接\n     *\n     * @param socket WebSocket 连接\n     */\n    private void cleanupConnection(ServerWebSocket socket) {\n        try {\n            // 1. 发送离线消息（如果已认证）\n            IotWebSocketConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);\n            if (connectionInfo != null) {\n                IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();\n                deviceMessageService.sendDeviceMessage(offlineMessage, connectionInfo.getProductKey(),\n                        connectionInfo.getDeviceName(), serverId);\n            }\n\n            // 2. 注销连接\n            connectionManager.unregisterConnection(socket);\n        } catch (Exception e) {\n            log.error(\"[cleanupConnection][清理连接失败]\", e);\n        }\n    }\n\n    // ===================== 发送响应消息 =====================\n\n    /**\n     * 发送响应消息\n     *\n     * @param socket    WebSocket 连接\n     * @param requestId 请求 ID\n     * @param method    请求方法\n     * @param data      响应数据\n     */\n    private void sendSuccessResponse(ServerWebSocket socket, String requestId, String method, Object data) {\n        IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, SUCCESS.getCode(), null);\n        writeResponse(socket, responseMessage);\n    }\n\n    private void sendErrorResponse(ServerWebSocket socket, String requestId, String method, Integer code, String msg) {\n        IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, null, code, msg);\n        writeResponse(socket, responseMessage);\n    }\n\n    /**\n     * 写入响应消息\n     */\n    private void writeResponse(ServerWebSocket socket, IotDeviceMessage responseMessage) {\n        byte[] payload = serializer.serialize(responseMessage);\n        socket.writeTextMessage(StrUtil.utf8Str(payload));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/manager/IotWebSocketConnectionManager.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager;\n\nimport io.vertx.core.http.ServerWebSocket;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * IoT 网关 WebSocket 连接管理器\n * <p>\n * 统一管理 WebSocket 连接的认证状态、设备会话和消息发送功能：\n * 1. 管理 WebSocket 连接的认证状态\n * 2. 管理设备会话和在线状态\n * 3. 管理消息发送到设备\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotWebSocketConnectionManager {\n\n    /**\n     * 连接信息映射：ServerWebSocket -> 连接信息\n     */\n    private final Map<ServerWebSocket, ConnectionInfo> connectionMap = new ConcurrentHashMap<>();\n\n    /**\n     * 设备 ID -> ServerWebSocket 的映射\n     */\n    private final Map<Long, ServerWebSocket> deviceSocketMap = new ConcurrentHashMap<>();\n\n    /**\n     * 注册设备连接（包含认证信息）\n     *\n     * @param socket         WebSocket 连接\n     * @param deviceId       设备 ID\n     * @param connectionInfo 连接信息\n     */\n    public void registerConnection(ServerWebSocket socket, Long deviceId, ConnectionInfo connectionInfo) {\n        // 如果设备已有其他连接，先清理旧连接\n        ServerWebSocket oldSocket = deviceSocketMap.get(deviceId);\n        if (oldSocket != null && oldSocket != socket) {\n            log.info(\"[registerConnection][设备已有其他连接，断开旧连接，设备 ID: {}，旧连接: {}]\",\n                    deviceId, oldSocket.remoteAddress());\n            oldSocket.close();\n            // 清理旧连接的映射\n            connectionMap.remove(oldSocket);\n        }\n\n        // 注册新连接\n        connectionMap.put(socket, connectionInfo);\n        deviceSocketMap.put(deviceId, socket);\n        log.info(\"[registerConnection][注册设备连接，设备 ID: {}，连接: {}，product key: {}，device name: {}]\",\n                deviceId, socket.remoteAddress(), connectionInfo.getProductKey(), connectionInfo.getDeviceName());\n    }\n\n    /**\n     * 注销设备连接\n     *\n     * @param socket WebSocket 连接\n     */\n    public void unregisterConnection(ServerWebSocket socket) {\n        ConnectionInfo connectionInfo = connectionMap.remove(socket);\n        if (connectionInfo == null) {\n            return;\n        }\n        Long deviceId = connectionInfo.getDeviceId();\n        // 仅当 deviceSocketMap 中的 socket 是当前 socket 时才移除，避免误删新连接\n        deviceSocketMap.remove(deviceId, socket);\n        log.info(\"[unregisterConnection][注销设备连接，设备 ID: {}，连接: {}]\",\n                deviceId, socket.remoteAddress());\n    }\n\n    /**\n     * 获取连接信息\n     */\n    public ConnectionInfo getConnectionInfo(ServerWebSocket socket) {\n        return connectionMap.get(socket);\n    }\n\n    /**\n     * 根据设备 ID 获取连接信息\n     */\n    public ConnectionInfo getConnectionInfoByDeviceId(Long deviceId) {\n        ServerWebSocket socket = deviceSocketMap.get(deviceId);\n        return socket != null ? connectionMap.get(socket) : null;\n    }\n\n    /**\n     * 发送消息到设备（文本消息）\n     *\n     * @param deviceId 设备 ID\n     * @param message  JSON 消息\n     * @return 是否发送成功\n     */\n    public boolean sendToDevice(Long deviceId, String message) {\n        ServerWebSocket socket = deviceSocketMap.get(deviceId);\n        if (socket == null) {\n            log.warn(\"[sendToDevice][设备未连接，设备 ID: {}]\", deviceId);\n            return false;\n        }\n\n        try {\n            socket.writeTextMessage(message);\n            log.debug(\"[sendToDevice][发送消息成功，设备 ID: {}，数据长度: {} 字节]\", deviceId, message.length());\n            return true;\n        } catch (Exception e) {\n            log.error(\"[sendToDevice][发送消息失败，设备 ID: {}]\", deviceId, e);\n            // 发送失败时清理连接\n            unregisterConnection(socket);\n            return false;\n        }\n    }\n\n    /**\n     * 关闭所有连接\n     */\n    public void closeAll() {\n        // 1. 先复制再清空，避免 closeHandler 回调时并发修改\n        List<ServerWebSocket> sockets = new ArrayList<>(connectionMap.keySet());\n        connectionMap.clear();\n        deviceSocketMap.clear();\n        // 2. 关闭所有连接（closeHandler 中 unregisterConnection 发现 map 为空会安全跳过）\n        for (ServerWebSocket socket : sockets) {\n            try {\n                socket.close();\n            } catch (Exception ignored) {\n                // 连接可能已关闭，忽略异常\n            }\n        }\n    }\n\n    /**\n     * 连接信息（包含认证信息）\n     */\n    @Data\n    @Accessors(chain = true)\n    public static class ConnectionInfo {\n\n        /**\n         * 设备 ID\n         */\n        private Long deviceId;\n        /**\n         * 产品 Key\n         */\n        private String productKey;\n        /**\n         * 设备名称\n         */\n        private String deviceName;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/IotMessageSerializer.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.serialize;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\n\n/**\n * IoT 设备消息序列化器接口\n *\n * 用于序列化和反序列化设备消息\n *\n * @author 芋道源码\n */\npublic interface IotMessageSerializer {\n\n    /**\n     * 序列化消息\n     *\n     * @param message 消息\n     * @return 编码后的消息内容\n     */\n    byte[] serialize(IotDeviceMessage message);\n\n    /**\n     * 反序列化消息\n     *\n     * @param bytes 消息内容\n     * @return 解码后的消息内容\n     */\n    IotDeviceMessage deserialize(byte[] bytes);\n\n    /**\n     * 获取序列化类型\n     *\n     * @return 序列化类型枚举\n     */\n    IotSerializeTypeEnum getType();\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/IotMessageSerializerManager.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.serialize;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.binary.IotBinarySerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.EnumMap;\nimport java.util.Map;\n\n/**\n * IoT 序列化器管理器\n *\n * 负责根据枚举创建和管理序列化器实例\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotMessageSerializerManager {\n\n    private final Map<IotSerializeTypeEnum, IotMessageSerializer> serializerMap = new EnumMap<>(IotSerializeTypeEnum.class);\n\n    public IotMessageSerializerManager() {\n        // 遍历枚举，创建对应的序列化器\n        for (IotSerializeTypeEnum type : IotSerializeTypeEnum.values()) {\n            IotMessageSerializer serializer = createSerializer(type);\n            serializerMap.put(type, serializer);\n            log.info(\"[IotSerializerManager][序列化器 {} 创建成功]\", type);\n        }\n    }\n\n    /**\n     * 根据类型创建序列化器\n     *\n     * @param type 序列化类型\n     * @return 序列化器实例\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    private IotMessageSerializer createSerializer(IotSerializeTypeEnum type) {\n        switch (type) {\n            case JSON:\n                return new IotJsonSerializer();\n            case BINARY:\n                return new IotBinarySerializer();\n            default:\n                throw new IllegalArgumentException(\"未知的序列化类型：\" + type);\n        }\n    }\n\n    /**\n     * 获取序列化器\n     *\n     * @param type 序列化类型\n     * @return 序列化器实例\n     */\n    public IotMessageSerializer get(IotSerializeTypeEnum type) {\n        return serializerMap.get(type);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/binary/IotBinarySerializer.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.serialize.binary;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport io.vertx.core.buffer.Buffer;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.nio.charset.StandardCharsets;\n\n/**\n * 二进制格式的消息序列化器\n *\n * 二进制协议格式（所有数值使用大端序）：\n *\n * <pre>\n * +--------+--------+--------+---------------------------+--------+--------+\n * | 魔术字 | 版本号 | 消息类型|         消息长度(4 字节)          |\n * +--------+--------+--------+---------------------------+--------+--------+\n * |           消息 ID 长度(2 字节)        |      消息 ID (变长字符串)         |\n * +--------+--------+--------+--------+--------+--------+--------+--------+\n * |           方法名长度(2 字节)        |      方法名(变长字符串)         |\n * +--------+--------+--------+--------+--------+--------+--------+--------+\n * |                        消息体数据(变长)                              |\n * +--------+--------+--------+--------+--------+--------+--------+--------+\n * </pre>\n *\n * 消息体格式：\n * - 请求消息：params 数据(JSON)\n * - 响应消息：code (4字节) + msg 长度(2字节) + msg 字符串 + data 数据(JSON)\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotBinarySerializer implements IotMessageSerializer {\n\n    /**\n     * 协议魔术字，用于协议识别\n     */\n    private static final byte MAGIC_NUMBER = (byte) 0x7E;\n\n    /**\n     * 协议版本号\n     */\n    private static final byte PROTOCOL_VERSION = (byte) 0x01;\n\n    /**\n     * 请求消息类型\n     */\n    private static final byte REQUEST = (byte) 0x01;\n\n    /**\n     * 响应消息类型\n     */\n    private static final byte RESPONSE = (byte) 0x02;\n\n    /**\n     * 协议头部固定长度（魔术字 + 版本号 + 消息类型 + 消息长度）\n     */\n    private static final int HEADER_FIXED_LENGTH = 7;\n\n    /**\n     * 最小消息长度（头部 + 消息ID长度 + 方法名长度）\n     */\n    private static final int MIN_MESSAGE_LENGTH = HEADER_FIXED_LENGTH + 4;\n\n    @Override\n    public IotSerializeTypeEnum getType() {\n        return IotSerializeTypeEnum.BINARY;\n    }\n\n    @Override\n    public byte[] serialize(IotDeviceMessage message) {\n        Assert.notNull(message, \"消息不能为空\");\n        Assert.notBlank(message.getMethod(), \"消息方法不能为空\");\n        try {\n            // 1. 确定消息类型\n            byte messageType = determineMessageType(message);\n            // 2. 构建消息体\n            byte[] bodyData = buildMessageBody(message, messageType);\n            // 3. 构建完整消息\n            return buildCompleteMessage(message, messageType, bodyData);\n        } catch (Exception e) {\n            log.error(\"[encode][二进制消息编码失败，消息: {}]\", message, e);\n            throw new RuntimeException(\"二进制消息编码失败: \" + e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public IotDeviceMessage deserialize(byte[] bytes) {\n        Assert.notNull(bytes, \"待解码数据不能为空\");\n        Assert.isTrue(bytes.length >= MIN_MESSAGE_LENGTH, \"数据包长度不足\");\n        try {\n            Buffer buffer = Buffer.buffer(bytes);\n            int index = 0;\n\n            // 1. 验证魔术字\n            byte magic = buffer.getByte(index++);\n            Assert.isTrue(magic == MAGIC_NUMBER, \"无效的协议魔术字: \" + magic);\n\n            // 2. 验证版本号\n            byte version = buffer.getByte(index++);\n            Assert.isTrue(version == PROTOCOL_VERSION, \"不支持的协议版本: \" + version);\n\n            // 3. 读取消息类型\n            byte messageType = buffer.getByte(index++);\n            Assert.isTrue(messageType == REQUEST || messageType == RESPONSE, \"无效的消息类型: \" + messageType);\n\n            // 4. 读取消息长度\n            int messageLength = buffer.getInt(index);\n            index += 4;\n            Assert.isTrue(messageLength == buffer.length(),\n                    \"消息长度不匹配，期望: \" + messageLength + \", 实际: \" + buffer.length());\n\n            // 5. 读取消息 ID\n            short messageIdLength = buffer.getShort(index);\n            index += 2;\n            String messageId = buffer.getString(index, index + messageIdLength, StandardCharsets.UTF_8.name());\n            index += messageIdLength;\n\n            // 6. 读取方法名\n            short methodLength = buffer.getShort(index);\n            index += 2;\n            String method = buffer.getString(index, index + methodLength, StandardCharsets.UTF_8.name());\n            index += methodLength;\n\n            // 7. 解析消息体\n            return parseMessageBody(buffer, index, messageType, messageId, method);\n        } catch (Exception e) {\n            log.error(\"[decode][二进制消息解码失败，数据长度: {}]\", bytes.length, e);\n            throw new RuntimeException(\"二进制消息解码失败: \" + e.getMessage(), e);\n        }\n    }\n\n    /**\n     * 快速检测是否为二进制格式\n     *\n     * @param data 数据\n     * @return 是否为二进制格式\n     */\n    public static boolean isBinaryFormat(byte[] data) {\n        return data != null && data.length >= 1 && data[0] == MAGIC_NUMBER;\n    }\n\n    private byte determineMessageType(IotDeviceMessage message) {\n        if (message.getCode() != null) {\n            return RESPONSE;\n        }\n        return REQUEST;\n    }\n\n    private byte[] buildMessageBody(IotDeviceMessage message, byte messageType) {\n        Buffer bodyBuffer = Buffer.buffer();\n        if (messageType == RESPONSE) {\n            // code\n            bodyBuffer.appendInt(message.getCode() != null ? message.getCode() : 0);\n            // msg\n            String msg = message.getMsg() != null ? message.getMsg() : \"\";\n            byte[] msgBytes = StrUtil.utf8Bytes(msg);\n            bodyBuffer.appendShort((short) msgBytes.length);\n            bodyBuffer.appendBytes(msgBytes);\n            // data\n            if (message.getData() != null) {\n                bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getData()));\n            }\n        } else {\n            // 请求消息只处理 params 参数\n            if (message.getParams() != null) {\n                bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getParams()));\n            }\n        }\n        return bodyBuffer.getBytes();\n    }\n\n    private byte[] buildCompleteMessage(IotDeviceMessage message, byte messageType, byte[] bodyData) {\n        Buffer buffer = Buffer.buffer();\n        // 1. 写入协议头部\n        buffer.appendByte(MAGIC_NUMBER);\n        buffer.appendByte(PROTOCOL_VERSION);\n        buffer.appendByte(messageType);\n        // 2. 预留消息长度位置\n        int lengthPosition = buffer.length();\n        buffer.appendInt(0);\n        // 3. 写入消息 ID\n        String messageId = StrUtil.isNotBlank(message.getRequestId()) ? message.getRequestId()\n                : IotDeviceMessageUtils.generateMessageId();\n        byte[] messageIdBytes = StrUtil.utf8Bytes(messageId);\n        buffer.appendShort((short) messageIdBytes.length);\n        buffer.appendBytes(messageIdBytes);\n        // 4. 写入方法名\n        byte[] methodBytes = StrUtil.utf8Bytes(message.getMethod());\n        buffer.appendShort((short) methodBytes.length);\n        buffer.appendBytes(methodBytes);\n        // 5. 写入消息体\n        buffer.appendBytes(bodyData);\n        // 6. 更新消息长度\n        buffer.setInt(lengthPosition, buffer.length());\n        return buffer.getBytes();\n    }\n\n    private IotDeviceMessage parseMessageBody(Buffer buffer, int startIndex, byte messageType,\n                                              String messageId, String method) {\n        if (startIndex >= buffer.length()) {\n            return IotDeviceMessage.of(messageId, method, null, null, null, null);\n        }\n\n        if (messageType == RESPONSE) {\n            return parseResponseMessage(buffer, startIndex, messageId, method);\n        } else {\n            Object payload = parseJsonData(buffer, startIndex, buffer.length());\n            return IotDeviceMessage.of(messageId, method, payload, null, null, null);\n        }\n    }\n\n    private IotDeviceMessage parseResponseMessage(Buffer buffer, int startIndex, String messageId, String method) {\n        int index = startIndex;\n\n        // 1. 读取响应码\n        Integer code = buffer.getInt(index);\n        index += 4;\n\n        // 2. 读取响应消息\n        short msgLength = buffer.getShort(index);\n        index += 2;\n        String msg = msgLength > 0 ? buffer.getString(index, index + msgLength, StandardCharsets.UTF_8.name()) : null;\n        index += msgLength;\n\n        // 3. 读取响应数据\n        Object data = null;\n        if (index < buffer.length()) {\n            data = parseJsonData(buffer, index, buffer.length());\n        }\n\n        return IotDeviceMessage.of(messageId, method, null, data, code, msg);\n    }\n\n    private Object parseJsonData(Buffer buffer, int startIndex, int endIndex) {\n        if (startIndex >= endIndex) {\n            return null;\n        }\n        try {\n            String jsonStr = buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name());\n            return JsonUtils.parseObject(jsonStr, Object.class);\n        } catch (Exception e) {\n            log.warn(\"[parseJsonData][JSON 解析失败，返回原始字符串]\", e);\n            return buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/json/IotJsonSerializer.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.serialize.json;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\n\n/**\n * JSON 格式的消息序列化器\n *\n * 直接使用 JsonUtils 序列化/反序列化 {@link IotDeviceMessage}，不包装额外字段\n *\n * @author 芋道源码\n */\npublic class IotJsonSerializer implements IotMessageSerializer {\n\n    @Override\n    public IotSerializeTypeEnum getType() {\n        return IotSerializeTypeEnum.JSON;\n    }\n\n    @Override\n    public byte[] serialize(IotDeviceMessage message) {\n        Assert.notNull(message, \"消息不能为空\");\n        return JsonUtils.toJsonByte(message);\n    }\n\n    @Override\n    public IotDeviceMessage deserialize(byte[] bytes) {\n        Assert.notNull(bytes, \"待解码数据不能为空\");\n        IotDeviceMessage message = JsonUtils.parseObject(bytes, IotDeviceMessage.class);\n        Assert.notNull(message, \"消息解码失败\");\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/package-info.java",
    "content": "/**\n * 消息序列化：将设备消息转换为字节数组（JSON、二进制等格式）\n *\n * @see cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer\n */\npackage cn.iocoder.yudao.module.iot.gateway.serialize;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenService.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.service.auth;\n\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\n\n/**\n * IoT 设备 Token Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDeviceTokenService {\n\n    /**\n     * 创建设备 Token\n     *\n     * @param productKey 产品 Key\n     * @param deviceName 设备名称\n     * @return 设备 Token\n     */\n    String createToken(String productKey, String deviceName);\n\n    /**\n     * 验证设备 Token\n     *\n     * @param token 设备 Token\n     * @return 设备信息\n     */\n    IotDeviceIdentity verifyToken(String token);\n\n    /**\n     * 解析用户名\n     *\n     * @param username 用户名\n     * @return 设备信息\n     */\n    IotDeviceIdentity parseUsername(String username);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.service.auth;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.json.JSONObject;\nimport cn.hutool.jwt.JWT;\nimport cn.hutool.jwt.JWTUtil;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_TOKEN_EXPIRED;\n\n/**\n * IoT 设备 Token Service 实现类：调用远程的 device http 接口，进行设备 Token 生成、解析等逻辑\n *\n * 注意：目前仅 HTTP 协议使用\n *\n * @author 芋道源码\n */\n@Service\n@Slf4j\npublic class IotDeviceTokenServiceImpl implements IotDeviceTokenService {\n\n    @Resource\n    private IotGatewayProperties gatewayProperties;\n\n    @Override\n    public String createToken(String productKey, String deviceName) {\n        Assert.notBlank(productKey, \"productKey 不能为空\");\n        Assert.notBlank(deviceName, \"deviceName 不能为空\");\n        // 构建 JWT payload\n        Map<String, Object> payload = new HashMap<>();\n        payload.put(\"productKey\", productKey);\n        payload.put(\"deviceName\", deviceName);\n        LocalDateTime expireTime = LocalDateTimeUtils.addTime(gatewayProperties.getToken().getExpiration());\n        payload.put(\"exp\", LocalDateTimeUtils.toEpochSecond(expireTime)); // 过期时间（exp 是 JWT 规范推荐）\n\n        // 生成 JWT Token\n        return JWTUtil.createToken(payload, gatewayProperties.getToken().getSecret().getBytes());\n    }\n\n    @Override\n    public IotDeviceIdentity verifyToken(String token) {\n        Assert.notBlank(token, \"token 不能为空\");\n        // 校验 JWT Token\n        boolean verify = JWTUtil.verify(token, gatewayProperties.getToken().getSecret().getBytes());\n        if (!verify) {\n            throw exception(DEVICE_TOKEN_EXPIRED);\n        }\n\n        // 解析 Token\n        JWT jwt = JWTUtil.parseToken(token);\n        JSONObject payload = jwt.getPayloads();\n        // 检查过期时间\n        Long exp = payload.getLong(\"exp\");\n        if (exp == null || exp < System.currentTimeMillis() / 1000) {\n            throw exception(DEVICE_TOKEN_EXPIRED);\n        }\n        String productKey = payload.getStr(\"productKey\");\n        String deviceName = payload.getStr(\"deviceName\");\n        Assert.notBlank(productKey, \"productKey 不能为空\");\n        Assert.notBlank(deviceName, \"deviceName 不能为空\");\n        return new IotDeviceIdentity(productKey, deviceName);\n    }\n\n    @Override\n    public IotDeviceIdentity parseUsername(String username) {\n        return IotDeviceAuthUtils.parseUsername(username);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceService.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.service.device;\n\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;\n\n/**\n * IoT 设备信息 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDeviceService {\n\n    /**\n     * 根据 productKey 和 deviceName 获取设备信息\n     *\n     * @param productKey 产品标识\n     * @param deviceName 设备名称\n     * @return 设备信息\n     */\n    IotDeviceRespDTO getDeviceFromCache(String productKey, String deviceName);\n\n    /**\n     * 根据 id 获取设备信息\n     *\n     * @param id 设备编号\n     * @return 设备信息\n     */\n    IotDeviceRespDTO getDeviceFromCache(Long id);\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.service.device;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\n\nimport static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;\n\n/**\n * IoT 设备信息 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Slf4j\npublic class IotDeviceServiceImpl implements IotDeviceService {\n\n    private static final Duration CACHE_EXPIRE = Duration.ofMinutes(1);\n\n    /**\n     * 通过 id 查询设备的缓存\n     */\n    private final LoadingCache<Long, IotDeviceRespDTO> deviceCaches = buildAsyncReloadingCache(\n            CACHE_EXPIRE,\n            new CacheLoader<Long, IotDeviceRespDTO>() {\n\n                @Override\n                public IotDeviceRespDTO load(Long id) {\n                    CommonResult<IotDeviceRespDTO> result = deviceApi.getDevice(new IotDeviceGetReqDTO().setId(id));\n                    IotDeviceRespDTO device = result.getCheckedData();\n                    Assert.notNull(device, \"设备({}) 不能为空\", id);\n                    // 相互缓存\n                    deviceCaches2.put(new KeyValue<>(device.getProductKey(), device.getDeviceName()), device);\n                    return device;\n                }\n\n            });\n\n    /**\n     * 通过 productKey + deviceName 查询设备的缓存\n     */\n    private final LoadingCache<KeyValue<String, String>, IotDeviceRespDTO> deviceCaches2 = buildAsyncReloadingCache(\n            CACHE_EXPIRE,\n            new CacheLoader<KeyValue<String, String>, IotDeviceRespDTO>() {\n\n                @Override\n                public IotDeviceRespDTO load(KeyValue<String, String> kv) {\n                    CommonResult<IotDeviceRespDTO> result = deviceApi.getDevice(new IotDeviceGetReqDTO()\n                            .setProductKey(kv.getKey()).setDeviceName(kv.getValue()));\n                    IotDeviceRespDTO device = result.getCheckedData();\n                    Assert.notNull(device, \"设备({}/{}) 不能为空\", kv.getKey(), kv.getValue());\n                    // 相互缓存\n                    deviceCaches.put(device.getId(), device);\n                    return device;\n                }\n            });\n\n    @Resource\n    private IotDeviceCommonApi deviceApi;\n\n    @Override\n    public IotDeviceRespDTO getDeviceFromCache(String productKey, String deviceName) {\n        return deviceCaches2.getUnchecked(new KeyValue<>(productKey, deviceName));\n    }\n\n    @Override\n    public IotDeviceRespDTO getDeviceFromCache(Long id) {\n        return deviceCaches.getUnchecked(id);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageService.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.service.device.message;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\n\n/**\n * IoT 设备消息 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDeviceMessageService {\n\n    /**\n     * 序列化消息\n     *\n     * @param message    消息\n     * @param productKey 产品 Key\n     * @param deviceName 设备名称\n     * @return 序列化后的消息内容\n     */\n    byte[] serializeDeviceMessage(IotDeviceMessage message,\n                                  String productKey, String deviceName);\n\n    /**\n     * 序列化消息\n     *\n     * @param message       消息\n     * @param serializeType 序列化类型\n     * @return 序列化后的消息内容\n     */\n    byte[] serializeDeviceMessage(IotDeviceMessage message,\n                                  IotSerializeTypeEnum serializeType);\n\n    /**\n     * 反序列化消息\n     *\n     * @param bytes      消息内容\n     * @param productKey 产品 Key\n     * @param deviceName 设备名称\n     * @return 反序列化后的消息内容\n     */\n    IotDeviceMessage deserializeDeviceMessage(byte[] bytes,\n                                              String productKey, String deviceName);\n\n    /**\n     * 反序列化消息\n     *\n     * @param bytes         消息内容\n     * @param serializeType 序列化类型\n     * @return 反序列化后的消息内容\n     */\n    IotDeviceMessage deserializeDeviceMessage(byte[] bytes, IotSerializeTypeEnum serializeType);\n\n    /**\n     * 发送消息\n     *\n     * @param message    消息\n     * @param productKey 产品 Key\n     * @param deviceName 设备名称\n     * @param serverId   设备连接的 serverId\n     */\n    void sendDeviceMessage(IotDeviceMessage message,\n                           String productKey, String deviceName, String serverId);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.service.device.message;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;\nimport cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS;\n\n/**\n * IoT 设备消息 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Slf4j\npublic class IotDeviceMessageServiceImpl implements IotDeviceMessageService {\n\n    @Resource\n    private IotDeviceService deviceService;\n\n    @Resource\n    private IotDeviceMessageProducer deviceMessageProducer;\n\n    @Resource\n    private IotMessageSerializerManager messageSerializerManager;\n\n    @Override\n    public byte[] serializeDeviceMessage(IotDeviceMessage message,\n                                         String productKey, String deviceName) {\n        // 1.1 获取设备信息\n        IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);\n        if (device == null) {\n            throw exception(DEVICE_NOT_EXISTS, productKey, deviceName);\n        }\n        // 1.2 获取序列化器\n        IotSerializeTypeEnum serializeType = IotSerializeTypeEnum.of(device.getSerializeType());\n        Assert.notNull(serializeType, \"设备序列化类型不能为空\");\n\n        // 2. 序列化消息\n        return serializeDeviceMessage(message, serializeType);\n    }\n\n    @Override\n    public byte[] serializeDeviceMessage(IotDeviceMessage message,\n                                         IotSerializeTypeEnum serializeType) {\n        // 1. 获取序列化器\n        IotMessageSerializer serializer = messageSerializerManager.get(serializeType);\n        if (serializer == null) {\n            throw new IllegalArgumentException(StrUtil.format(\"序列化器({}) 不存在\", serializeType));\n        }\n\n        // 2. 序列化消息\n        return serializer.serialize(message);\n    }\n\n    @Override\n    public IotDeviceMessage deserializeDeviceMessage(byte[] bytes,\n                                                     String productKey, String deviceName) {\n        // 1.1 获取设备信息\n        IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);\n        if (device == null) {\n            throw exception(DEVICE_NOT_EXISTS, productKey, deviceName);\n        }\n        // 1.2 获取序列化器\n        IotSerializeTypeEnum serializeType = IotSerializeTypeEnum.of(device.getSerializeType());\n        Assert.notNull(serializeType, \"设备序列化类型不能为空\");\n\n        // 2. 反序列化消息\n        return deserializeDeviceMessage(bytes, serializeType);\n    }\n\n    @Override\n    public IotDeviceMessage deserializeDeviceMessage(byte[] bytes, IotSerializeTypeEnum serializeType) {\n        // 1. 获取序列化器\n        IotMessageSerializer serializer = messageSerializerManager.get(serializeType);\n        if (serializer == null) {\n            throw new IllegalArgumentException(StrUtil.format(\"序列化器({}) 不存在\", serializeType));\n        }\n\n        // 2. 反序列化消息\n        return serializer.deserialize(bytes);\n    }\n\n    @Override\n    public void sendDeviceMessage(IotDeviceMessage message,\n                                  String productKey, String deviceName, String serverId) {\n        // 1. 获取设备信息\n        IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);\n        if (device == null) {\n            throw exception(DEVICE_NOT_EXISTS, productKey, deviceName);\n        }\n\n        // 2. 发送消息\n        appendDeviceMessage(message, device, serverId);\n        deviceMessageProducer.sendDeviceMessage(message);\n    }\n\n    /**\n     * 补充消息的后端字段\n     *\n     * @param message  消息\n     * @param device   设备信息\n     * @param serverId 设备连接的 serverId\n     */\n    private void appendDeviceMessage(IotDeviceMessage message,\n                                     IotDeviceRespDTO device, String serverId) {\n        message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now())\n                .setDeviceId(device.getId()).setTenantId(device.getTenantId()).setServerId(serverId);\n        // 特殊：如果设备没有指定 requestId，则使用 messageId\n        if (StrUtil.isEmpty(message.getRequestId())) {\n            message.setRequestId(message.getId());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.service.device.remote;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.*;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.web.client.RestTemplateBuilder;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;\n\n/**\n * Iot 设备信息 Service 实现类：调用远程的 device http 接口，进行设备认证、设备获取等\n *\n * @author 芋道源码\n */\n@Service\n@Slf4j\npublic class IotDeviceApiImpl implements IotDeviceCommonApi {\n\n    @Resource\n    private IotGatewayProperties gatewayProperties;\n\n    private RestTemplate restTemplate;\n\n    @PostConstruct\n    public void init() {\n        IotGatewayProperties.RpcProperties rpc = gatewayProperties.getRpc();\n        restTemplate = new RestTemplateBuilder()\n                .rootUri(rpc.getUrl())\n                .setReadTimeout(rpc.getReadTimeout())\n                .setConnectTimeout(rpc.getConnectTimeout())\n                .build();\n    }\n\n    @Override\n    public CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO) {\n        return doPost(\"/rpc-api/iot/device/auth\", authReqDTO, new ParameterizedTypeReference<CommonResult<Boolean>>() { });\n    }\n\n    @Override\n    public CommonResult<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO getReqDTO) {\n        return doPost(\"/rpc-api/iot/device/get\", getReqDTO, new ParameterizedTypeReference<CommonResult<IotDeviceRespDTO>>() { });\n    }\n\n    @Override\n    public CommonResult<List<IotModbusDeviceConfigRespDTO>> getModbusDeviceConfigList(IotModbusDeviceConfigListReqDTO listReqDTO) {\n        return doPost(\"/rpc-api/iot/modbus/config-list\", listReqDTO, new ParameterizedTypeReference<CommonResult<List<IotModbusDeviceConfigRespDTO>>>() { });\n    }\n\n    @Override\n    public CommonResult<IotDeviceRegisterRespDTO> registerDevice(IotDeviceRegisterReqDTO reqDTO) {\n        return doPost(\"/register\", reqDTO, new ParameterizedTypeReference<CommonResult<IotDeviceRegisterRespDTO>>() { });\n    }\n\n    @Override\n    public CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO) {\n        return doPost(\"/register-sub\", reqDTO, new ParameterizedTypeReference<CommonResult<List<IotSubDeviceRegisterRespDTO>>>() { });\n    }\n\n    private <T, R> CommonResult<R> doPost(String url, T body,\n                                          ParameterizedTypeReference<CommonResult<R>> responseType) {\n        try {\n            // 请求\n            HttpEntity<T> requestEntity = new HttpEntity<>(body);\n            ResponseEntity<CommonResult<R>> response = restTemplate.exchange(\n                    url, HttpMethod.POST, requestEntity, responseType);\n            // 响应\n            CommonResult<R> result = response.getBody();\n            Assert.notNull(result, \"请求结果不能为空\");\n            return result;\n        } catch (Exception e) {\n            log.error(\"[doPost][url({}) body({}) 发生异常]\", url, body, e);\n            return CommonResult.error(INTERNAL_SERVER_ERROR);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/util/IotMqttTopicUtils.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.util;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\n\n/**\n * IoT 网关 MQTT 主题工具类\n * <p>\n * 用于统一管理 MQTT 协议中的主题常量，基于 Alink 协议规范\n *\n * @author 芋道源码\n */\npublic final class IotMqttTopicUtils {\n\n    // ========== 静态常量 ==========\n\n    /**\n     * 系统主题前缀\n     */\n    private static final String SYS_TOPIC_PREFIX = \"/sys/\";\n\n    /**\n     * 回复主题后缀\n     */\n    private static final String REPLY_TOPIC_SUFFIX = \"_reply\";\n\n    // ========== MQTT HTTP 接口路径常量 ==========\n\n    /**\n     * MQTT 认证接口路径\n     * 对应 EMQX HTTP 认证插件的认证请求接口\n     */\n    public static final String MQTT_AUTH_PATH = \"/mqtt/auth\";\n\n    /**\n     * MQTT 统一事件处理接口路径\n     * 对应 EMQX Webhook 的统一事件处理接口，支持所有客户端事件\n     * 包括：client.connected、client.disconnected、message.publish 等\n     */\n    public static final String MQTT_EVENT_PATH = \"/mqtt/event\";\n\n    /**\n     * MQTT ACL 接口路径\n     * 对应 EMQX HTTP ACL 插件的 ACL 请求接口\n     */\n    public static final String MQTT_ACL_PATH = \"/mqtt/acl\";\n\n    // ========== 消息方法标准化 ==========\n\n    /**\n     * 标准化设备回复消息的 method\n     * <p>\n     * MQTT 协议中，设备回复下行指令时，topic 和 method 会携带 _reply 后缀\n     * （如 thing.service.invoke_reply）。平台内部统一使用基础 method（如 thing.service.invoke），\n     * 通过 {@link IotDeviceMessage#getCode()} 非空来识别回复消息。\n     * <p>\n     * 此方法剥离 _reply 后缀，并确保 code 字段被设置。\n     *\n     * @param message 设备消息\n     */\n    public static void normalizeReplyMethod(IotDeviceMessage message) {\n        String method = message.getMethod();\n        if (!StrUtil.endWith(method, REPLY_TOPIC_SUFFIX)) {\n            return;\n        }\n        // 1. 剥离 _reply 后缀\n        message.setMethod(method.substring(0, method.length() - REPLY_TOPIC_SUFFIX.length()));\n        // 2. 确保 code 被设置，使 isReplyMessage() 能正确识别\n        if (message.getCode() == null) {\n            message.setCode(GlobalErrorCodeConstants.SUCCESS.getCode());\n        }\n    }\n\n    // ========== 工具方法 ==========\n\n    /**\n     * 根据消息方法构建对应的主题\n     *\n     * @param method 消息方法，例如 thing.property.post\n     * @param productKey 产品 Key\n     * @param deviceName 设备名称\n     * @param isReply 是否为回复消息\n     * @return 完整的主题路径\n     */\n    public static String buildTopicByMethod(String method, String productKey, String deviceName, boolean isReply) {\n        if (StrUtil.isBlank(method)) {\n            return null;\n        }\n        // 1. 将点分隔符转换为斜杠\n        String topicSuffix = method.replace('.', '/');\n        // 2. 对于回复消息，添加 _reply 后缀\n        if (isReply) {\n            topicSuffix += REPLY_TOPIC_SUFFIX;\n        }\n        // 3. 构建完整主题\n        return SYS_TOPIC_PREFIX + productKey + \"/\" + deviceName + \"/\" + topicSuffix;\n    }\n\n    /**\n     * 校验主题是否允许订阅\n     * <p>\n     * 规则：主题必须以 /sys/{productKey}/{deviceName}/ 开头，\n     * 或者是通配符形式 /sys/{productKey}/{deviceName}/#\n     *\n     * @param topic      订阅的主题\n     * @param productKey 产品 Key\n     * @param deviceName 设备名称\n     * @return 是否允许订阅\n     */\n    public static boolean isTopicSubscribeAllowed(String topic, String productKey, String deviceName) {\n        if (!StrUtil.isAllNotBlank(topic, productKey, deviceName)) {\n            return false;\n        }\n        // 构建设备主题前缀\n        String deviceTopicPrefix = SYS_TOPIC_PREFIX + productKey + \"/\" + deviceName + \"/\";\n        // 主题必须以设备前缀开头，或者是设备前缀的通配符形式\n        return topic.startsWith(deviceTopicPrefix)\n                || topic.equals(SYS_TOPIC_PREFIX + productKey + \"/\" + deviceName + \"/#\");\n    }\n\n    /**\n     * 校验主题是否允许发布\n     * <p>\n     * 规则：主题必须以 /sys/{productKey}/{deviceName}/ 开头，且不允许包含通配符（+/#）。\n     *\n     * @param topic      发布的主题\n     * @param productKey 产品 Key\n     * @param deviceName 设备名称\n     * @return 是否允许发布\n     */\n    public static boolean isTopicPublishAllowed(String topic, String productKey, String deviceName) {\n        if (!StrUtil.isAllNotBlank(topic, productKey, deviceName)) {\n            return false;\n        }\n        // MQTT publish topic 不允许包含通配符，但这里做一次兜底校验\n        if (topic.contains(\"#\") || topic.contains(\"+\")) {\n            return false;\n        }\n        String deviceTopicPrefix = SYS_TOPIC_PREFIX + productKey + \"/\" + deviceName + \"/\";\n        return topic.startsWith(deviceTopicPrefix);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: iot-gateway-server\n  profiles:\n    active: local # 默认激活本地开发环境\n\n  # Redis 配置\n  redis:\n    host: 127.0.0.1 # Redis 服务器地址\n    port: 6379 # Redis 服务器端口\n    database: 0 # Redis 数据库索引\n    # password: # Redis 密码，如果有的话\n    timeout: 30000ms # 连接超时时间\n\n--- #################### 消息队列相关 ####################\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n  # Producer 配置项\n  producer:\n    group: ${spring.application.name}_PRODUCER # 生产者分组\n\n--- #################### IoT 网关相关配置 ####################\n\nyudao:\n  iot:\n    # 消息总线配置\n    message-bus:\n      type: redis # 消息总线的类型\n\n    # 网关配置\n    gateway:\n      # 设备 RPC 配置\n      rpc:\n        url: http://127.0.0.1:48091 # 主程序 API 地址\n        connect-timeout: 30s\n        read-timeout: 30s\n      # 设备 Token 配置\n      token:\n        secret: yudaoIotGatewayTokenSecret123456789 # Token 密钥，至少32位\n        expiration: 7d\n\n      # 协议实例列表\n      protocols:\n        # ====================================\n        # 针对引入的 HTTP 组件的配置\n        # ====================================\n        - id: http-json\n          protocol: http\n          port: 8092\n          enabled: false\n        # ====================================\n        # 针对引入的 TCP 组件的配置\n        # ====================================\n        - id: tcp-json\n          enabled: false\n          protocol: tcp\n          port: 8091\n          serialize: json\n          tcp:\n            max-connections: 1000\n            keep-alive-timeout-ms: 30000\n            codec:\n              type: delimiter              # 拆包类型：length_field / delimiter / fixed_length\n              delimiter: \"\\\\n\"             # 分隔符（支持转义：\\\\n=换行, \\\\r=回车, \\\\t=制表符）\n#              type: length_field           # 拆包类型：length_field / delimiter / fixed_length\n#              length-field-offset: 0       # 长度字段偏移量\n#              length-field-length: 4       # 长度字段长度\n#              length-adjustment: 0         # 长度调整值\n#              initial-bytes-to-strip: 4    # 初始跳过的字节数\n#              type: fixed_length           # 拆包类型：length_field / delimiter / fixed_length\n#              fixed-length: 256            # 固定长度\n        # ====================================\n        # 针对引入的 UDP 组件的配置\n        # ====================================\n        - id: udp-json\n          enabled: false\n          protocol: udp\n          port: 8093\n          serialize: json\n          udp:\n            max-sessions: 1000               # 最大会话数\n            session-timeout-ms: 60000        # 会话超时时间（毫秒），基于 Guava Cache 自动过期\n            receive-buffer-size: 65536       # 接收缓冲区大小（字节）\n            send-buffer-size: 65536          # 发送缓冲区大小（字节）\n        # ====================================\n        # 针对引入的 WebSocket 组件的配置\n        # ====================================\n        - id: websocket-json\n          enabled: false\n          protocol: websocket\n          port: 8094\n          serialize: json\n          websocket:\n            path: /ws\n            max-message-size: 65536          # 最大消息大小（字节，默认 64KB）\n            max-frame-size: 65536            # 最大帧大小（字节，默认 64KB）\n            idle-timeout-seconds: 60         # 空闲超时时间（秒，默认 60）\n        # ====================================\n        # 针对引入的 CoAP 组件的配置\n        # ====================================\n        - id: coap-json\n          enabled: false\n          protocol: coap\n          port: 5683\n          coap:\n            max-message-size: 1024           # 最大消息大小（字节）\n            ack-timeout-ms: 2000             # ACK 超时时间（毫秒）\n            max-retransmit: 4                # 最大重传次数\n        # ====================================\n        # 针对引入的 MQTT 组件的配置\n        # ====================================\n        - id: mqtt-json\n          enabled: false\n          protocol: mqtt\n          port: 1883\n          serialize: json\n          mqtt:\n            max-message-size: 8192           # 最大消息大小（字节）\n            connect-timeout-seconds: 60      # 连接超时时间（秒）\n        # ====================================\n        # 针对引入的 EMQX 组件的配置\n        # ====================================\n        - id: emqx-1\n          enabled: false\n          protocol: emqx\n          port: 8090 # EMQX HTTP Hook 端口（/mqtt/auth、/mqtt/event）\n          emqx:\n            mqtt-host: 127.0.0.1                  # MQTT Broker 地址\n            mqtt-port: 1883                       # MQTT Broker 端口\n            mqtt-username: admin                  # MQTT 用户名\n            mqtt-password: public                 # MQTT 密码\n            mqtt-client-id: iot-gateway-mqtt      # MQTT 客户端 ID\n            mqtt-ssl: false                       # 是否开启 SSL\n            mqtt-topics:\n              - \"/sys/#\"                          # 系统主题\n            mqtt-qos: 1                           # 默认 QoS\n            clean-session: true                   # 是否启用 Clean Session (默认: true)\n            keep-alive-interval-seconds: 60       # 心跳间隔，单位秒 (默认: 60)\n            max-inflight-queue: 10000             # 最大飞行消息队列，单位：条\n            connect-timeout-seconds: 10           # 连接超时，单位：秒\n            reconnect-delay-ms: 5000              # 重连延迟，单位：毫秒\n            # 是否信任所有 SSL 证书 (默认: false)。警告：生产环境必须为 false！\n            # 仅在开发环境或内网测试时，如果使用了自签名证书，可以临时设置为 true\n            trust-all: true # 在 dev 环境可以设为 true\n            # EMQX HTTP Hook 回调网关的 HTTPS 配置（可选）\n            http:\n              ssl-enabled: false\n              # ssl-cert-path: \"path/to/server.crt\"\n              # ssl-key-path: \"path/to/server.key\"\n            # 遗嘱消息配置 (用于网关异常下线时通知其他系统)\n            will:\n              enabled: true # 生产环境强烈建议开启\n              topic: \"gateway/status/iot-gateway-mqtt\" # 遗嘱消息主题\n              payload: \"offline\" # 遗嘱消息负载\n              qos: 1 # 遗嘱消息 QoS\n              retain: true # 遗嘱消息是否保留\n            # 高级 SSL/TLS 配置 (当 trust-all: false 且 mqtt-ssl: true 时生效)\n            ssl-options:\n              key-store-path: \"classpath:certs/client.jks\" # 客户端证书库路径\n              key-store-password: \"your-keystore-password\" # 客户端证书库密码\n              trust-store-path: \"classpath:certs/trust.jks\" # 信任的 CA 证书库路径\n              trust-store-password: \"your-truststore-password\" # 信任的 CA 证书库密码\n        # ====================================\n        # 针对引入的 Modbus TCP Client 组件的配置\n        # ====================================\n        - id: modbus-tcp-client-1\n          enabled: false\n          protocol: modbus_tcp_client\n          port: 502\n          modbus-tcp-client:\n            config-refresh-interval: 30  # 配置刷新间隔（秒）\n        # ====================================\n        # 针对引入的 Modbus TCP Server 组件的配置\n        # ====================================\n        - id: modbus-tcp-server-1\n          enabled: false\n          protocol: modbus_tcp_server\n          port: 503\n          modbus-tcp-server:\n            config-refresh-interval: 30     # 配置刷新间隔（秒）\n            custom-function-code: 65        # 自定义功能码（用于认证等扩展交互）\n            request-timeout: 5000           # Pending Request 超时时间（毫秒）\n            request-cleanup-interval: 10000 # Pending Request 清理间隔（毫秒）\n\n--- #################### 日志相关配置 ####################\n\n# 基础日志配置\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n  level:\n    # 应用基础日志级别\n    cn.iocoder.yudao.module.iot.gateway: INFO\n    org.springframework.boot: INFO\n    # RocketMQ 日志\n    org.apache.rocketmq: WARN\n    # MQTT 客户端日志\n    # io.vertx.mqtt: DEBUG\n    # 开发环境详细日志\n    cn.iocoder.yudao.module.iot.gateway.protocol.emqx: DEBUG\n    cn.iocoder.yudao.module.iot.gateway.protocol.http: DEBUG\n    cn.iocoder.yudao.module.iot.gateway.protocol.mqtt: DEBUG\n    cn.iocoder.yudao.module.iot.gateway.protocol.coap: DEBUG\n    cn.iocoder.yudao.module.iot.gateway.protocol.websocket: DEBUG\n    cn.iocoder.yudao.module.iot.gateway.protocol.modbus: DEBUG\n    # 根日志级别\n    root: INFO\n\ndebug: false\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotDirectDeviceCoapProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.CoapClient;\nimport org.eclipse.californium.core.CoapResponse;\nimport org.eclipse.californium.core.coap.MediaTypeRegistry;\nimport org.eclipse.californium.core.coap.Option;\nimport org.eclipse.californium.core.coap.Request;\nimport org.eclipse.californium.core.config.CoapConfig;\nimport org.eclipse.californium.elements.config.Configuration;\nimport org.eclipse.californium.elements.config.UdpConfig;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport static cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapAbstractHandler.OPTION_TOKEN;\n\n/**\n * IoT 直连设备 CoAP 协议集成测试（手动测试）\n *\n * <p>测试场景：直连设备（IotProductDeviceTypeEnum 的 DIRECT 类型）通过 CoAP 协议直接连接平台\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（CoAP 端口 5683）</li>\n *     <li>运行 {@link #testDeviceRegister()} 测试直连设备动态注册（一型一密）</li>\n *     <li>运行 {@link #testAuth()} 获取设备 token，将返回的 token 粘贴到 {@link #TOKEN} 常量</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testPropertyPost()} - 设备属性上报</li>\n *             <li>{@link #testEventPost()} - 设备事件上报</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotDirectDeviceCoapProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 5683;\n\n    // ===================== 直连设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String PRODUCT_KEY = \"4aymZgOTOOCrDKRT\";\n    private static final String DEVICE_NAME = \"small\";\n    private static final String DEVICE_SECRET = \"0baa4c2ecc104ae1a26b4070c218bdf3\";\n\n    /**\n     * 直连设备 Token：从 {@link #testAuth()} 方法获取后，粘贴到这里\n     */\n    private static final String TOKEN = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiNGF5bVpnT1RPT0NyREtSVCIsImV4cCI6MTc2OTk5MjgxOSwiZGV2aWNlTmFtZSI6InNtYWxsIn0.UHLCXsoGNsKbtJcbTV3n1psp03G75hVcVpV4wwd39r4\";\n\n    @BeforeAll\n    public static void initCaliforniumConfig() {\n        // 注册 Californium 配置定义\n        CoapConfig.register();\n        UdpConfig.register();\n        // 创建默认配置\n        Configuration.setStandard(Configuration.createStandardWithoutFile());\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 认证测试：获取设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/auth\", SERVER_HOST, SERVER_PORT);\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        String payload = JsonUtils.toJsonString(authReqDTO);\n        // 1.2 输出请求\n        log.info(\"[testAuth][请求 URI: {}]\", uri);\n        log.info(\"[testAuth][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            CoapResponse response = client.post(payload, MediaTypeRegistry.APPLICATION_JSON);\n            // 2.2 输出结果\n            log.info(\"[testAuth][响应码: {}]\", response.getCode());\n            log.info(\"[testAuth][响应体: {}]\", response.getResponseText());\n            log.info(\"[testAuth][请将返回的 token 复制到 TOKEN 常量中]\");\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    // ===================== 直连设备属性上报测试 =====================\n\n    /**\n     * 属性上报测试\n     */\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void testPropertyPost() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/topic/sys/%s/%s/thing/property/post\",\n                SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME);\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"id\", IdUtil.fastSimpleUUID())\n                .put(\"method\", IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())\n                .put(\"version\", \"1.0\")\n                .put(\"params\", IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                        .put(\"width\", 1)\n                        .put(\"height\", \"2\")\n                        .build())\n                )\n                .build());\n        // 1.2 输出请求\n        log.info(\"[testPropertyPost][请求 URI: {}]\", uri);\n        log.info(\"[testPropertyPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            Request request = Request.newPost();\n            request.setURI(uri);\n            request.setPayload(payload);\n            request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);\n            request.getOptions().addOption(new Option(OPTION_TOKEN, TOKEN));\n\n            CoapResponse response = client.advanced(request);\n            // 2.2 输出结果\n            log.info(\"[testPropertyPost][响应码: {}]\", response.getCode());\n            log.info(\"[testPropertyPost][响应体: {}]\", response.getResponseText());\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    // ===================== 直连设备事件上报测试 =====================\n\n    /**\n     * 事件上报测试\n     */\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void testEventPost() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/topic/sys/%s/%s/thing/event/post\",\n                SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME);\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"id\", IdUtil.fastSimpleUUID())\n                .put(\"method\", IotDeviceMessageMethodEnum.EVENT_POST.getMethod())\n                .put(\"version\", \"1.0\")\n                .put(\"params\", IotDeviceEventPostReqDTO.of(\n                        \"eat\",\n                        MapUtil.<String, Object>builder().put(\"rice\", 3).build(),\n                        System.currentTimeMillis())\n                )\n                .build());\n        // 1.2 输出请求\n        log.info(\"[testEventPost][请求 URI: {}]\", uri);\n        log.info(\"[testEventPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            Request request = Request.newPost();\n            request.setURI(uri);\n            request.setPayload(payload);\n            request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);\n            request.getOptions().addOption(new Option(OPTION_TOKEN, TOKEN));\n\n            CoapResponse response = client.advanced(request);\n            // 2.2 输出结果\n            log.info(\"[testEventPost][响应码: {}]\", response.getCode());\n            log.info(\"[testEventPost][响应体: {}]\", response.getResponseText());\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    // ===================== 动态注册测试 =====================\n\n    /**\n     * 直连设备动态注册测试（一型一密）\n     * <p>\n     * 使用产品密钥（productSecret）验证身份，成功后返回设备密钥（deviceSecret）\n     * <p>\n     * 注意：此接口不需要 Token 认证\n     */\n    @Test\n    public void testDeviceRegister() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/auth/register/device\", SERVER_HOST, SERVER_PORT);\n        // 1.2 构建请求参数\n        String deviceName = \"test-\" + System.currentTimeMillis();\n        String productSecret = \"test-product-secret\"; // 替换为实际的 productSecret\n        String sign = IotProductAuthUtils.buildSign(PRODUCT_KEY, deviceName, productSecret);\n        IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO()\n                .setProductKey(PRODUCT_KEY)\n                .setDeviceName(deviceName)\n                .setSign(sign);\n        String payload = JsonUtils.toJsonString(reqDTO);\n        // 1.3 输出请求\n        log.info(\"[testDeviceRegister][请求 URI: {}]\", uri);\n        log.info(\"[testDeviceRegister][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            CoapResponse response = client.post(payload, MediaTypeRegistry.APPLICATION_JSON);\n            // 2.2 输出结果\n            log.info(\"[testDeviceRegister][响应码: {}]\", response.getCode());\n            log.info(\"[testDeviceRegister][响应体: {}]\", response.getResponseText());\n            log.info(\"[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]\");\n        } finally {\n            client.shutdown();\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotGatewayDeviceCoapProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.CoapClient;\nimport org.eclipse.californium.core.CoapResponse;\nimport org.eclipse.californium.core.coap.MediaTypeRegistry;\nimport org.eclipse.californium.core.coap.Option;\nimport org.eclipse.californium.core.coap.Request;\nimport org.eclipse.californium.core.config.CoapConfig;\nimport org.eclipse.californium.elements.config.Configuration;\nimport org.eclipse.californium.elements.config.UdpConfig;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapAbstractHandler.OPTION_TOKEN;\n\n/**\n * IoT 网关设备 CoAP 协议集成测试（手动测试）\n *\n * <p>测试场景：网关设备（IotProductDeviceTypeEnum 的 GATEWAY 类型）通过 CoAP 协议管理子设备拓扑关系\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（CoAP 端口 5683）</li>\n *     <li>运行 {@link #testAuth()} 获取网关设备 token，将返回的 token 粘贴到 {@link #GATEWAY_TOKEN} 常量</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testTopoAdd()} - 添加子设备拓扑关系</li>\n *             <li>{@link #testTopoDelete()} - 删除子设备拓扑关系</li>\n *             <li>{@link #testTopoGet()} - 获取子设备拓扑关系</li>\n *             <li>{@link #testSubDeviceRegister()} - 子设备动态注册</li>\n *             <li>{@link #testPropertyPackPost()} - 批量上报属性（网关 + 子设备）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewayDeviceCoapProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 5683;\n\n    // ===================== 网关设备信息（根据实际情况修改，从 iot_device 表查询网关设备） =====================\n\n    private static final String GATEWAY_PRODUCT_KEY = \"m6XcS1ZJ3TW8eC0v\";\n    private static final String GATEWAY_DEVICE_NAME = \"sub-ddd\";\n    private static final String GATEWAY_DEVICE_SECRET = \"b3d62c70f8a4495487ed1d35d61ac2b3\";\n\n    /**\n     * 网关设备 Token：从 {@link #testAuth()} 方法获取后，粘贴到这里\n     */\n    private static final String GATEWAY_TOKEN = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoibTZYY1MxWkozVFc4ZUMwdiIsImV4cCI6MTc2OTg2NjY3OCwiZGV2aWNlTmFtZSI6InN1Yi1kZGQifQ.nCLSAfHEjXLtTDRXARjOoFqpuo5WfArjFWweUAzrjKU\";\n\n    // ===================== 子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String SUB_DEVICE_PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String SUB_DEVICE_NAME = \"chazuo-it\";\n    private static final String SUB_DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    @BeforeAll\n    public static void initCaliforniumConfig() {\n        // 注册 Californium 配置定义\n        CoapConfig.register();\n        UdpConfig.register();\n        // 创建默认配置\n        Configuration.setStandard(Configuration.createStandardWithoutFile());\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 网关设备认证测试：获取网关设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/auth\", SERVER_HOST, SERVER_PORT);\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        String payload = JsonUtils.toJsonString(authReqDTO);\n        // 1.2 输出请求\n        log.info(\"[testAuth][请求 URI: {}]\", uri);\n        log.info(\"[testAuth][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            CoapResponse response = client.post(payload, MediaTypeRegistry.APPLICATION_JSON);\n            // 2.2 输出结果\n            log.info(\"[testAuth][响应码: {}]\", response.getCode());\n            log.info(\"[testAuth][响应体: {}]\", response.getResponseText());\n            log.info(\"[testAuth][请将返回的 token 复制到 GATEWAY_TOKEN 常量中]\");\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    // ===================== 拓扑管理测试 =====================\n\n    /**\n     * 添加子设备拓扑关系测试\n     * <p>\n     * 网关设备向平台上报需要绑定的子设备信息\n     */\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void testTopoAdd() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/topic/sys/%s/%s/thing/topo/add\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建子设备认证信息\n        IotDeviceAuthReqDTO subAuthInfo = IotDeviceAuthUtils.getAuthInfo(\n                SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME, SUB_DEVICE_SECRET);\n        IotDeviceAuthReqDTO subDeviceAuth = new IotDeviceAuthReqDTO()\n                .setClientId(subAuthInfo.getClientId())\n                .setUsername(subAuthInfo.getUsername())\n                .setPassword(subAuthInfo.getPassword());\n        // 1.3 构建请求参数\n        IotDeviceTopoAddReqDTO params = new IotDeviceTopoAddReqDTO();\n        params.setSubDevices(Collections.singletonList(subDeviceAuth));\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"id\", IdUtil.fastSimpleUUID())\n                .put(\"method\", IotDeviceMessageMethodEnum.TOPO_ADD.getMethod())\n                .put(\"version\", \"1.0\")\n                .put(\"params\", params)\n                .build());\n        // 1.4 输出请求\n        log.info(\"[testTopoAdd][请求 URI: {}]\", uri);\n        log.info(\"[testTopoAdd][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            Request request = Request.newPost();\n            request.setURI(uri);\n            request.setPayload(payload);\n            request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);\n            request.getOptions().addOption(new Option(OPTION_TOKEN, GATEWAY_TOKEN));\n\n            CoapResponse response = client.advanced(request);\n            // 2.2 输出结果\n            log.info(\"[testTopoAdd][响应码: {}]\", response.getCode());\n            log.info(\"[testTopoAdd][响应体: {}]\", response.getResponseText());\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    /**\n     * 删除子设备拓扑关系测试\n     * <p>\n     * 网关设备向平台上报需要解绑的子设备信息\n     */\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void testTopoDelete() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/topic/sys/%s/%s/thing/topo/delete\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建请求参数\n        IotDeviceTopoDeleteReqDTO params = new IotDeviceTopoDeleteReqDTO();\n        params.setSubDevices(Collections.singletonList(\n                new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME)));\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"id\", IdUtil.fastSimpleUUID())\n                .put(\"method\", IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod())\n                .put(\"version\", \"1.0\")\n                .put(\"params\", params)\n                .build());\n        // 1.3 输出请求\n        log.info(\"[testTopoDelete][请求 URI: {}]\", uri);\n        log.info(\"[testTopoDelete][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            Request request = Request.newPost();\n            request.setURI(uri);\n            request.setPayload(payload);\n            request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);\n            request.getOptions().addOption(new Option(OPTION_TOKEN, GATEWAY_TOKEN));\n\n            CoapResponse response = client.advanced(request);\n            // 2.2 输出结果\n            log.info(\"[testTopoDelete][响应码: {}]\", response.getCode());\n            log.info(\"[testTopoDelete][响应体: {}]\", response.getResponseText());\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    /**\n     * 获取子设备拓扑关系测试\n     * <p>\n     * 网关设备向平台查询已绑定的子设备列表\n     */\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void testTopoGet() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/topic/sys/%s/%s/thing/topo/get\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建请求参数（目前为空，预留扩展）\n        IotDeviceTopoGetReqDTO params = new IotDeviceTopoGetReqDTO();\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"id\", IdUtil.fastSimpleUUID())\n                .put(\"method\", IotDeviceMessageMethodEnum.TOPO_GET.getMethod())\n                .put(\"version\", \"1.0\")\n                .put(\"params\", params)\n                .build());\n        // 1.3 输出请求\n        log.info(\"[testTopoGet][请求 URI: {}]\", uri);\n        log.info(\"[testTopoGet][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            Request request = Request.newPost();\n            request.setURI(uri);\n            request.setPayload(payload);\n            request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);\n            request.getOptions().addOption(new Option(OPTION_TOKEN, GATEWAY_TOKEN));\n\n            CoapResponse response = client.advanced(request);\n            // 2.2 输出结果\n            log.info(\"[testTopoGet][响应码: {}]\", response.getCode());\n            log.info(\"[testTopoGet][响应体: {}]\", response.getResponseText());\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    // ===================== 子设备注册测试 =====================\n\n    /**\n     * 子设备动态注册测试\n     * <p>\n     * 网关设备代理子设备进行动态注册，平台返回子设备的 deviceSecret\n     * <p>\n     * 注意：此接口需要网关 Token 认证\n     */\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void testSubDeviceRegister() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/auth/register/sub-device/%s/%s\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建请求参数\n        IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO();\n        subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY);\n        subDevice.setDeviceName(\"mougezishebei\");\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"id\", IdUtil.fastSimpleUUID())\n                .put(\"method\", IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod())\n                .put(\"version\", \"1.0\")\n                .put(\"params\", Collections.singletonList(subDevice))\n                .build());\n        // 1.3 输出请求\n        log.info(\"[testSubDeviceRegister][请求 URI: {}]\", uri);\n        log.info(\"[testSubDeviceRegister][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            Request request = Request.newPost();\n            request.setURI(uri);\n            request.setPayload(payload);\n            request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);\n            request.getOptions().addOption(new Option(OPTION_TOKEN, GATEWAY_TOKEN));\n\n            CoapResponse response = client.advanced(request);\n            // 2.2 输出结果\n            log.info(\"[testSubDeviceRegister][响应码: {}]\", response.getCode());\n            log.info(\"[testSubDeviceRegister][响应体: {}]\", response.getResponseText());\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    // ===================== 批量上报测试 =====================\n\n    /**\n     * 批量上报属性测试（网关 + 子设备）\n     * <p>\n     * 网关设备批量上报自身属性、事件，以及子设备的属性、事件\n     */\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void testPropertyPackPost() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/topic/sys/%s/%s/thing/event/property/pack/post\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建【网关设备】自身属性\n        Map<String, Object> gatewayProperties = MapUtil.<String, Object>builder()\n                .put(\"temperature\", 25.5)\n                .build();\n        // 1.3 构建【网关设备】自身事件\n        IotDevicePropertyPackPostReqDTO.EventValue gatewayEvent = new IotDevicePropertyPackPostReqDTO.EventValue();\n        gatewayEvent.setValue(MapUtil.builder().put(\"message\", \"gateway started\").build());\n        gatewayEvent.setTime(System.currentTimeMillis());\n        Map<String, IotDevicePropertyPackPostReqDTO.EventValue> gatewayEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                .put(\"statusReport\", gatewayEvent)\n                .build();\n        // 1.4 构建【网关子设备】属性\n        Map<String, Object> subDeviceProperties = MapUtil.<String, Object>builder()\n                .put(\"power\", 100)\n                .build();\n        // 1.5 构建【网关子设备】事件\n        IotDevicePropertyPackPostReqDTO.EventValue subDeviceEvent = new IotDevicePropertyPackPostReqDTO.EventValue();\n        subDeviceEvent.setValue(MapUtil.builder().put(\"errorCode\", 0).build());\n        subDeviceEvent.setTime(System.currentTimeMillis());\n        Map<String, IotDevicePropertyPackPostReqDTO.EventValue> subDeviceEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                .put(\"healthCheck\", subDeviceEvent)\n                .build();\n        // 1.6 构建子设备数据\n        IotDevicePropertyPackPostReqDTO.SubDeviceData subDeviceData = new IotDevicePropertyPackPostReqDTO.SubDeviceData();\n        subDeviceData.setIdentity(new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME));\n        subDeviceData.setProperties(subDeviceProperties);\n        subDeviceData.setEvents(subDeviceEvents);\n        // 1.7 构建请求参数\n        IotDevicePropertyPackPostReqDTO params = new IotDevicePropertyPackPostReqDTO();\n        params.setProperties(gatewayProperties);\n        params.setEvents(gatewayEvents);\n        params.setSubDevices(ListUtil.of(subDeviceData));\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"id\", IdUtil.fastSimpleUUID())\n                .put(\"method\", IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod())\n                .put(\"version\", \"1.0\")\n                .put(\"params\", params)\n                .build());\n        // 1.8 输出请求\n        log.info(\"[testPropertyPackPost][请求 URI: {}]\", uri);\n        log.info(\"[testPropertyPackPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            Request request = Request.newPost();\n            request.setURI(uri);\n            request.setPayload(payload);\n            request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);\n            request.getOptions().addOption(new Option(OPTION_TOKEN, GATEWAY_TOKEN));\n\n            CoapResponse response = client.advanced(request);\n            // 2.2 输出结果\n            log.info(\"[testPropertyPackPost][响应码: {}]\", response.getCode());\n            log.info(\"[testPropertyPackPost][响应体: {}]\", response.getResponseText());\n        } finally {\n            client.shutdown();\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotGatewaySubDeviceCoapProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.coap;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.eclipse.californium.core.CoapClient;\nimport org.eclipse.californium.core.CoapResponse;\nimport org.eclipse.californium.core.coap.MediaTypeRegistry;\nimport org.eclipse.californium.core.coap.Option;\nimport org.eclipse.californium.core.coap.Request;\nimport org.eclipse.californium.core.config.CoapConfig;\nimport org.eclipse.californium.elements.config.Configuration;\nimport org.eclipse.californium.elements.config.UdpConfig;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport static cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapAbstractHandler.OPTION_TOKEN;\n\n/**\n * IoT 网关子设备 CoAP 协议集成测试（手动测试）\n *\n * <p>测试场景：子设备（IotProductDeviceTypeEnum 的 SUB 类型）通过网关设备代理上报数据\n *\n * <p><b>重要说明：子设备无法直接连接平台，所有请求均由网关设备（Gateway）代为转发。</b>\n * <p>网关设备转发子设备请求时，Token 使用子设备自己的信息。\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（CoAP 端口 5683）</li>\n *     <li>确保子设备已通过 {@link IotGatewayDeviceCoapProtocolIntegrationTest#testTopoAdd()} 绑定到网关</li>\n *     <li>运行 {@link #testAuth()} 获取子设备 token，将返回的 token 粘贴到 {@link #TOKEN} 常量</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testPropertyPost()} - 子设备属性上报（由网关代理转发）</li>\n *             <li>{@link #testEventPost()} - 子设备事件上报（由网关代理转发）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewaySubDeviceCoapProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 5683;\n\n    // ===================== 网关子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String DEVICE_NAME = \"chazuo-it\";\n    private static final String DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    /**\n     * 网关子设备 Token：从 {@link #testAuth()} 方法获取后，粘贴到这里\n     */\n    private static final String TOKEN = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiakF1ZkVNVEYxVzZ3blBobiIsImV4cCI6MTc2OTk1NDY3OSwiZGV2aWNlTmFtZSI6ImNoYXp1by1pdCJ9.jfbUAoU0xkJl4UvO-NUvcJ6yITPRgUjQ4MKATPuwneg\";\n\n    @BeforeAll\n    public static void initCaliforniumConfig() {\n        // 注册 Californium 配置定义\n        CoapConfig.register();\n        UdpConfig.register();\n        // 创建默认配置\n        Configuration.setStandard(Configuration.createStandardWithoutFile());\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 子设备认证测试：获取子设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/auth\", SERVER_HOST, SERVER_PORT);\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        String payload = JsonUtils.toJsonString(authReqDTO);\n        // 1.2 输出请求\n        log.info(\"[testAuth][请求 URI: {}]\", uri);\n        log.info(\"[testAuth][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            CoapResponse response = client.post(payload, MediaTypeRegistry.APPLICATION_JSON);\n            // 2.2 输出结果\n            log.info(\"[testAuth][响应码: {}]\", response.getCode());\n            log.info(\"[testAuth][响应体: {}]\", response.getResponseText());\n            log.info(\"[testAuth][请将返回的 token 复制到 TOKEN 常量中]\");\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    // ===================== 子设备属性上报测试 =====================\n\n    /**\n     * 子设备属性上报测试\n     */\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void testPropertyPost() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/topic/sys/%s/%s/thing/property/post\",\n                SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME);\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"id\", IdUtil.fastSimpleUUID())\n                .put(\"method\", IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())\n                .put(\"version\", \"1.0\")\n                .put(\"params\", IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                        .put(\"power\", 100)\n                        .put(\"status\", \"online\")\n                        .put(\"temperature\", 36.5)\n                        .build()))\n                .build());\n        // 1.2 输出请求\n        log.info(\"[testPropertyPost][子设备属性上报 - 请求实际由 Gateway 代为转发]\");\n        log.info(\"[testPropertyPost][请求 URI: {}]\", uri);\n        log.info(\"[testPropertyPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            Request request = Request.newPost();\n            request.setURI(uri);\n            request.setPayload(payload);\n            request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);\n            request.getOptions().addOption(new Option(OPTION_TOKEN, TOKEN));\n\n            CoapResponse response = client.advanced(request);\n            // 2.2 输出结果\n            log.info(\"[testPropertyPost][响应码: {}]\", response.getCode());\n            log.info(\"[testPropertyPost][响应体: {}]\", response.getResponseText());\n        } finally {\n            client.shutdown();\n        }\n    }\n\n    // ===================== 子设备事件上报测试 =====================\n\n    /**\n     * 子设备事件上报测试\n     */\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void testEventPost() throws Exception {\n        // 1.1 构建请求\n        String uri = String.format(\"coap://%s:%d/topic/sys/%s/%s/thing/event/post\",\n                SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME);\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"id\", IdUtil.fastSimpleUUID())\n                .put(\"method\", IotDeviceMessageMethodEnum.EVENT_POST.getMethod())\n                .put(\"version\", \"1.0\")\n                .put(\"params\", IotDeviceEventPostReqDTO.of(\n                        \"alarm\",\n                        MapUtil.<String, Object>builder()\n                                .put(\"level\", \"warning\")\n                                .put(\"message\", \"temperature too high\")\n                                .put(\"threshold\", 40)\n                                .put(\"current\", 42)\n                                .build(),\n                        System.currentTimeMillis()))\n                .build());\n        // 1.2 输出请求\n        log.info(\"[testEventPost][子设备事件上报 - 请求实际由 Gateway 代为转发]\");\n        log.info(\"[testEventPost][请求 URI: {}]\", uri);\n        log.info(\"[testEventPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        CoapClient client = new CoapClient(uri);\n        try {\n            Request request = Request.newPost();\n            request.setURI(uri);\n            request.setPayload(payload);\n            request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);\n            request.getOptions().addOption(new Option(OPTION_TOKEN, TOKEN));\n\n            CoapResponse response = client.advanced(request);\n            // 2.2 输出结果\n            log.info(\"[testEventPost][响应码: {}]\", response.getCode());\n            log.info(\"[testEventPost][响应体: {}]\", response.getResponseText());\n        } finally {\n            client.shutdown();\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/package-info.java",
    "content": "/**\n * IoT 网关 EMQX 协议集成测试包\n *\n * <p>\n * 测试类直接使用 mqtt 包下的单测即可，因为设备都是通过 MQTT 协议连接 EMQX Broker。\n *\n * @see cn.iocoder.yudao.module.iot.gateway.protocol.mqtt\n *\n * <h2>架构</h2>\n * <pre>\n * +--------+      MQTT       +-------------+     HTTP Hook     +---------+\n * |  设备  | --------------> | EMQX Broker | ----------------> |  网关   |\n * +--------+                 +-------------+                   +---------+\n * </pre>\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.iot.gateway.protocol.emqx;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.http.HttpResponse;\nimport cn.hutool.http.HttpUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n\n/**\n * IoT 直连设备 HTTP 协议集成测试（手动测试）\n *\n * <p>测试场景：直连设备（IotProductDeviceTypeEnum 的 DIRECT 类型）通过 HTTP 协议直接连接平台\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（HTTP 端口 8092）</li>\n *     <li>运行 {@link #testDeviceRegister()} 测试直连设备动态注册（一型一密）</li>\n *     <li>运行 {@link #testAuth()} 获取设备 token，将返回的 token 粘贴到 {@link #TOKEN} 常量</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testPropertyPost()} - 设备属性上报</li>\n *             <li>{@link #testEventPost()} - 设备事件上报</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\n@SuppressWarnings(\"HttpUrlsUsage\")\npublic class IotDirectDeviceHttpProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8092;\n\n    // ===================== 直连设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String PRODUCT_KEY = \"4aymZgOTOOCrDKRT\";\n    private static final String DEVICE_NAME = \"small\";\n    private static final String DEVICE_SECRET = \"0baa4c2ecc104ae1a26b4070c218bdf3\";\n\n    /**\n     * 直连设备 Token：从 {@link #testAuth()} 方法获取后，粘贴到这里\n     */\n    private static final String TOKEN = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiNGF5bVpnT1RPT0NyREtSVCIsImV4cCI6MTc2OTMwNTA1NSwiZGV2aWNlTmFtZSI6InNtYWxsIn0.mf3MEATCn5bp6cXgULunZjs8d00RGUxj96JEz0hMS7k\";\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 认证测试：获取设备 Token\n     */\n    @Test\n    public void testAuth() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/auth\", SERVER_HOST, SERVER_PORT);\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        String payload = JsonUtils.toJsonString(authReqDTO);\n        // 1.2 输出请求\n        log.info(\"[testAuth][请求 URL: {}]\", url);\n        log.info(\"[testAuth][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        String response = HttpUtil.post(url, payload);\n        // 2.2 输出结果\n        log.info(\"[testAuth][响应体: {}]\", response);\n        log.info(\"[testAuth][请将返回的 token 复制到 TOKEN 常量中]\");\n    }\n\n    // ===================== 直连设备属性上报测试 =====================\n\n    /**\n     * 属性上报测试\n     */\n    @Test\n    public void testPropertyPost() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/topic/sys/%s/%s/thing/property/post\",\n                SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME);\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"method\", IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())\n                .put(\"params\", IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                        .put(\"width\", 1)\n                        .put(\"height\", \"2\")\n                        .build())\n                )\n                .build());\n        // 1.2 输出请求\n        log.info(\"[testPropertyPost][请求 URL: {}]\", url);\n        log.info(\"[testPropertyPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        try (HttpResponse httpResponse = HttpUtil.createPost(url)\n                .header(\"Authorization\", TOKEN)\n                .body(payload)\n                .execute()) {\n            // 2.2 输出结果\n            log.info(\"[testPropertyPost][响应体: {}]\", httpResponse.body());\n        }\n    }\n\n    // ===================== 直连设备事件上报测试 =====================\n\n    /**\n     * 事件上报测试\n     */\n    @Test\n    public void testEventPost() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/topic/sys/%s/%s/thing/event/post\",\n                SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME);\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"method\", IotDeviceMessageMethodEnum.EVENT_POST.getMethod())\n                .put(\"params\", IotDeviceEventPostReqDTO.of(\n                        \"eat\",\n                        MapUtil.<String, Object>builder().put(\"rice\", 3).build(),\n                        System.currentTimeMillis())\n                )\n                .build());\n        // 1.2 输出请求\n        log.info(\"[testEventPost][请求 URL: {}]\", url);\n        log.info(\"[testEventPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        try (HttpResponse httpResponse = HttpUtil.createPost(url)\n                .header(\"Authorization\", TOKEN)\n                .body(payload)\n                .execute()) {\n            // 2.2 输出结果\n            log.info(\"[testEventPost][响应体: {}]\", httpResponse.body());\n        }\n    }\n\n    // ===================== 动态注册测试 =====================\n\n    /**\n     * 直连设备动态注册测试（一型一密）\n     * <p>\n     * 使用产品密钥（productSecret）验证身份，成功后返回设备密钥（deviceSecret）\n     * <p>\n     * 注意：此接口不需要 Token 认证\n     */\n    @Test\n    public void testDeviceRegister() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/auth/register/device\", SERVER_HOST, SERVER_PORT);\n        // 1.2 构建请求参数\n        String deviceName = \"test-\" + System.currentTimeMillis();\n        String productSecret = \"test-product-secret\"; // 替换为实际的 productSecret\n        String sign = IotProductAuthUtils.buildSign(PRODUCT_KEY, deviceName, productSecret);\n        IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO()\n                .setProductKey(PRODUCT_KEY)\n                .setDeviceName(deviceName)\n                .setSign(sign);\n        String payload = JsonUtils.toJsonString(reqDTO);\n        // 1.3 输出请求\n        log.info(\"[testDeviceRegister][请求 URL: {}]\", url);\n        log.info(\"[testDeviceRegister][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        String response = HttpUtil.post(url, payload);\n        // 2.2 输出结果\n        log.info(\"[testDeviceRegister][响应体: {}]\", response);\n        log.info(\"[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewayDeviceHttpProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.http.HttpResponse;\nimport cn.hutool.http.HttpUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\nimport java.util.Map;\n\n\n/**\n * IoT 网关设备 HTTP 协议集成测试（手动测试）\n *\n * <p>测试场景：网关设备（IotProductDeviceTypeEnum 的 GATEWAY 类型）通过 HTTP 协议管理子设备拓扑关系\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（HTTP 端口 8092）</li>\n *     <li>运行 {@link #testAuth()} 获取网关设备 token，将返回的 token 粘贴到 {@link #GATEWAY_TOKEN} 常量</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testTopoAdd()} - 添加子设备拓扑关系</li>\n *             <li>{@link #testTopoDelete()} - 删除子设备拓扑关系</li>\n *             <li>{@link #testTopoGet()} - 获取子设备拓扑关系</li>\n *             <li>{@link #testSubDeviceRegister()} - 子设备动态注册</li>\n *             <li>{@link #testPropertyPackPost()} - 批量上报属性（网关 + 子设备）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\n@SuppressWarnings(\"HttpUrlsUsage\")\npublic class IotGatewayDeviceHttpProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8092;\n\n    // ===================== 网关设备信息（根据实际情况修改，从 iot_device 表查询网关设备） =====================\n\n    private static final String GATEWAY_PRODUCT_KEY = \"m6XcS1ZJ3TW8eC0v\";\n    private static final String GATEWAY_DEVICE_NAME = \"sub-ddd\";\n    private static final String GATEWAY_DEVICE_SECRET = \"b3d62c70f8a4495487ed1d35d61ac2b3\";\n\n    /**\n     * 网关设备 Token：从 {@link #testAuth()} 方法获取后，粘贴到这里\n     */\n    private static final String GATEWAY_TOKEN = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoibTZYY1MxWkozVFc4ZUMwdiIsImV4cCI6MTc2OTg2NjY3OCwiZGV2aWNlTmFtZSI6InN1Yi1kZGQifQ.nCLSAfHEjXLtTDRXARjOoFqpuo5WfArjFWweUAzrjKU\";\n\n    // ===================== 子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String SUB_DEVICE_PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String SUB_DEVICE_NAME = \"chazuo-it\";\n    private static final String SUB_DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 网关设备认证测试：获取网关设备 Token\n     */\n    @Test\n    public void testAuth() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/auth\", SERVER_HOST, SERVER_PORT);\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        String payload = JsonUtils.toJsonString(authReqDTO);\n        // 1.2 输出请求\n        log.info(\"[testAuth][请求 URL: {}]\", url);\n        log.info(\"[testAuth][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        String response = HttpUtil.post(url, payload);\n        // 2.2 输出结果\n        log.info(\"[testAuth][响应体: {}]\", response);\n        log.info(\"[testAuth][请将返回的 token 复制到 GATEWAY_TOKEN 常量中]\");\n    }\n\n    // ===================== 拓扑管理测试 =====================\n\n    /**\n     * 添加子设备拓扑关系测试\n     * <p>\n     * 网关设备向平台上报需要绑定的子设备信息\n     */\n    @Test\n    public void testTopoAdd() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/topic/sys/%s/%s/thing/topo/add\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建子设备认证信息\n        IotDeviceAuthReqDTO subAuthInfo = IotDeviceAuthUtils.getAuthInfo(\n                SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME, SUB_DEVICE_SECRET);\n        IotDeviceAuthReqDTO subDeviceAuth = new IotDeviceAuthReqDTO()\n                .setClientId(subAuthInfo.getClientId())\n                .setUsername(subAuthInfo.getUsername())\n                .setPassword(subAuthInfo.getPassword());\n        // 1.3 构建请求参数\n        IotDeviceTopoAddReqDTO params = new IotDeviceTopoAddReqDTO();\n        params.setSubDevices(Collections.singletonList(subDeviceAuth));\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"method\", IotDeviceMessageMethodEnum.TOPO_ADD.getMethod())\n                .put(\"params\", params)\n                .build());\n        // 1.4 输出请求\n        log.info(\"[testTopoAdd][请求 URL: {}]\", url);\n        log.info(\"[testTopoAdd][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        try (HttpResponse httpResponse = HttpUtil.createPost(url)\n                .header(\"Authorization\", GATEWAY_TOKEN)\n                .body(payload)\n                .execute()) {\n            // 2.2 输出结果\n            log.info(\"[testTopoAdd][响应体: {}]\", httpResponse.body());\n        }\n    }\n\n    /**\n     * 删除子设备拓扑关系测试\n     * <p>\n     * 网关设备向平台上报需要解绑的子设备信息\n     */\n    @Test\n    public void testTopoDelete() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/topic/sys/%s/%s/thing/topo/delete\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建请求参数\n        IotDeviceTopoDeleteReqDTO params = new IotDeviceTopoDeleteReqDTO();\n        params.setSubDevices(Collections.singletonList(\n                new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME)));\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"method\", IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod())\n                .put(\"params\", params)\n                .build());\n        // 1.3 输出请求\n        log.info(\"[testTopoDelete][请求 URL: {}]\", url);\n        log.info(\"[testTopoDelete][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        try (HttpResponse httpResponse = HttpUtil.createPost(url)\n                .header(\"Authorization\", GATEWAY_TOKEN)\n                .body(payload)\n                .execute()) {\n            // 2.2 输出结果\n            log.info(\"[testTopoDelete][响应体: {}]\", httpResponse.body());\n        }\n    }\n\n    /**\n     * 获取子设备拓扑关系测试\n     * <p>\n     * 网关设备向平台查询已绑定的子设备列表\n     */\n    @Test\n    public void testTopoGet() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/topic/sys/%s/%s/thing/topo/get\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建请求参数（目前为空，预留扩展）\n        IotDeviceTopoGetReqDTO params = new IotDeviceTopoGetReqDTO();\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"method\", IotDeviceMessageMethodEnum.TOPO_GET.getMethod())\n                .put(\"params\", params)\n                .build());\n        // 1.3 输出请求\n        log.info(\"[testTopoGet][请求 URL: {}]\", url);\n        log.info(\"[testTopoGet][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        try (HttpResponse httpResponse = HttpUtil.createPost(url)\n                .header(\"Authorization\", GATEWAY_TOKEN)\n                .body(payload)\n                .execute()) {\n            // 2.2 输出结果\n            log.info(\"[testTopoGet][响应体: {}]\", httpResponse.body());\n        }\n    }\n\n    // ===================== 子设备注册测试 =====================\n\n    /**\n     * 子设备动态注册测试\n     * <p>\n     * 网关设备代理子设备进行动态注册，平台返回子设备的 deviceSecret\n     * <p>\n     * 注意：此接口需要网关 Token 认证\n     */\n    @Test\n    public void testSubDeviceRegister() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/auth/register/sub-device/%s/%s\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建请求参数\n        IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO();\n        subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY);\n        subDevice.setDeviceName(\"mougezishebei\");\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"method\", IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod())\n                .put(\"params\", Collections.singletonList(subDevice))\n                .build());\n        // 1.3 输出请求\n        log.info(\"[testSubDeviceRegister][请求 URL: {}]\", url);\n        log.info(\"[testSubDeviceRegister][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        try (HttpResponse httpResponse = HttpUtil.createPost(url)\n                .header(\"Authorization\", GATEWAY_TOKEN)\n                .body(payload)\n                .execute()) {\n            // 2.2 输出结果\n            log.info(\"[testSubDeviceRegister][响应体: {}]\", httpResponse.body());\n        }\n    }\n\n    // ===================== 批量上报测试 =====================\n\n    /**\n     * 批量上报属性测试（网关 + 子设备）\n     * <p>\n     * 网关设备批量上报自身属性、事件，以及子设备的属性、事件\n     */\n    @Test\n    public void testPropertyPackPost() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/topic/sys/%s/%s/thing/event/property/pack/post\",\n                SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n        // 1.2 构建【网关设备】自身属性\n        Map<String, Object> gatewayProperties = MapUtil.<String, Object>builder()\n                .put(\"temperature\", 25.5)\n                .build();\n        // 1.3 构建【网关设备】自身事件\n        IotDevicePropertyPackPostReqDTO.EventValue gatewayEvent = new IotDevicePropertyPackPostReqDTO.EventValue()\n                .setValue(MapUtil.builder().put(\"message\", \"gateway started\").build())\n                .setTime(System.currentTimeMillis());\n        Map<String, IotDevicePropertyPackPostReqDTO.EventValue> gatewayEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                .put(\"statusReport\", gatewayEvent)\n                .build();\n        // 1.4 构建【网关子设备】属性\n        Map<String, Object> subDeviceProperties = MapUtil.<String, Object>builder()\n                .put(\"power\", 100)\n                .build();\n        // 1.5 构建【网关子设备】事件\n        IotDevicePropertyPackPostReqDTO.EventValue subDeviceEvent = new IotDevicePropertyPackPostReqDTO.EventValue()\n                .setValue(MapUtil.builder().put(\"errorCode\", 0).build())\n                .setTime(System.currentTimeMillis());\n        Map<String, IotDevicePropertyPackPostReqDTO.EventValue> subDeviceEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                .put(\"healthCheck\", subDeviceEvent)\n                .build();\n        // 1.6 构建子设备数据\n        IotDevicePropertyPackPostReqDTO.SubDeviceData subDeviceData = new IotDevicePropertyPackPostReqDTO.SubDeviceData()\n                .setIdentity(new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME))\n                .setProperties(subDeviceProperties)\n                .setEvents(subDeviceEvents);\n        // 1.7 构建请求参数\n        IotDevicePropertyPackPostReqDTO params = new IotDevicePropertyPackPostReqDTO();\n        params.setProperties(gatewayProperties);\n        params.setEvents(gatewayEvents);\n        params.setSubDevices(ListUtil.of(subDeviceData));\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"method\", IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod())\n                .put(\"params\", params)\n                .build());\n        // 1.8 输出请求\n        log.info(\"[testPropertyPackPost][请求 URL: {}]\", url);\n        log.info(\"[testPropertyPackPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        try (HttpResponse httpResponse = HttpUtil.createPost(url)\n                .header(\"Authorization\", GATEWAY_TOKEN)\n                .body(payload)\n                .execute()) {\n            // 2.2 输出结果\n            log.info(\"[testPropertyPackPost][响应体: {}]\", httpResponse.body());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewaySubDeviceHttpProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.http;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.http.HttpResponse;\nimport cn.hutool.http.HttpUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n\n/**\n * IoT 网关子设备 HTTP 协议集成测试（手动测试）\n *\n * <p>测试场景：子设备（IotProductDeviceTypeEnum 的 SUB 类型）通过网关设备代理上报数据\n *\n * <p><b>重要说明：子设备无法直接连接平台，所有请求均由网关设备（Gateway）代为转发。</b>\n * <p>网关设备转发子设备请求时，URL 和 Token 都使用子设备自己的信息。\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（HTTP 端口 8092）</li>\n *     <li>确保子设备已通过 {@link IotGatewayDeviceHttpProtocolIntegrationTest#testTopoAdd()} 绑定到网关</li>\n *     <li>运行 {@link #testAuth()} 获取子设备 token，将返回的 token 粘贴到 {@link #TOKEN} 常量</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testPropertyPost()} - 子设备属性上报（由网关代理转发）</li>\n *             <li>{@link #testEventPost()} - 子设备事件上报（由网关代理转发）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\n@SuppressWarnings(\"HttpUrlsUsage\")\npublic class IotGatewaySubDeviceHttpProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8092;\n\n    // ===================== 网关子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String DEVICE_NAME = \"chazuo-it\";\n    private static final String DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    /**\n     * 网关子设备 Token：从 {@link #testAuth()} 方法获取后，粘贴到这里\n     */\n    private static final String TOKEN = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiakF1ZkVNVEYxVzZ3blBobiIsImV4cCI6MTc2OTg3MTI3NCwiZGV2aWNlTmFtZSI6ImNoYXp1by1pdCJ9.99sAlRalzMU3CqRlGStDzCwWSBJq6u3PJw48JQ3NpzQ\";\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 子设备认证测试：获取子设备 Token\n     */\n    @Test\n    public void testAuth() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/auth\", SERVER_HOST, SERVER_PORT);\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        String payload = JsonUtils.toJsonString(authReqDTO);\n        // 1.2 输出请求\n        log.info(\"[testAuth][请求 URL: {}]\", url);\n        log.info(\"[testAuth][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        String response = HttpUtil.post(url, payload);\n        // 2.2 输出结果\n        log.info(\"[testAuth][响应体: {}]\", response);\n        log.info(\"[testAuth][请将返回的 token 复制到 TOKEN 常量中]\");\n    }\n\n    // ===================== 子设备属性上报测试 =====================\n\n    /**\n     * 子设备属性上报测试\n     */\n    @Test\n    public void testPropertyPost() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/topic/sys/%s/%s/thing/property/post\",\n                SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME);\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"method\", IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())\n                .put(\"params\", IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                        .put(\"power\", 100)\n                        .put(\"status\", \"online\")\n                        .put(\"temperature\", 36.5)\n                        .build())\n                )\n                .build());\n        // 1.2 输出请求\n        log.info(\"[testPropertyPost][子设备属性上报 - 请求实际由 Gateway 代为转发]\");\n        log.info(\"[testPropertyPost][请求 URL: {}]\", url);\n        log.info(\"[testPropertyPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        try (HttpResponse httpResponse = HttpUtil.createPost(url)\n                .header(\"Authorization\", TOKEN)\n                .body(payload)\n                .execute()) {\n            // 2.2 输出结果\n            log.info(\"[testPropertyPost][响应体: {}]\", httpResponse.body());\n        }\n    }\n\n    // ===================== 子设备事件上报测试 =====================\n\n    /**\n     * 子设备事件上报测试\n     */\n    @Test\n    public void testEventPost() {\n        // 1.1 构建请求\n        String url = String.format(\"http://%s:%d/topic/sys/%s/%s/thing/event/post\",\n                SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME);\n        String payload = JsonUtils.toJsonString(MapUtil.builder()\n                .put(\"method\", IotDeviceMessageMethodEnum.EVENT_POST.getMethod())\n                .put(\"params\", IotDeviceEventPostReqDTO.of(\n                        \"alarm\",\n                        MapUtil.<String, Object>builder()\n                                .put(\"level\", \"warning\")\n                                .put(\"message\", \"temperature too high\")\n                                .put(\"threshold\", 40)\n                                .put(\"current\", 42)\n                                .build(),\n                        System.currentTimeMillis())\n                )\n                .build());\n        // 1.2 输出请求\n        log.info(\"[testEventPost][子设备事件上报 - 请求实际由 Gateway 代为转发]\");\n        log.info(\"[testEventPost][请求 URL: {}]\", url);\n        log.info(\"[testEventPost][请求体: {}]\", payload);\n\n        // 2.1 发送请求\n        try (HttpResponse httpResponse = HttpUtil.createPost(url)\n                .header(\"Authorization\", TOKEN)\n                .body(payload)\n                .execute()) {\n            // 2.2 输出结果\n            log.info(\"[testEventPost][响应体: {}]\", httpResponse.body());\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/ModbusRtuOverTcpDemo.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus;\n\nimport com.ghgande.j2mod.modbus.io.ModbusRTUTCPTransport;\nimport com.ghgande.j2mod.modbus.msg.*;\nimport com.ghgande.j2mod.modbus.procimg.*;\nimport com.ghgande.j2mod.modbus.slave.ModbusSlave;\nimport com.ghgande.j2mod.modbus.slave.ModbusSlaveFactory;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.ServerSocket;\nimport java.net.Socket;\n\n/**\n * Modbus RTU over TCP 完整 Demo\n *\n * 架构：Master（主站）启动 TCP Server 监听 → Slave（从站）主动 TCP 连接上来\n * 通信协议：RTU 帧格式（带 CRC）通过 TCP 传输，而非标准 MBAP 头\n *\n * 流程：\n * 1. Master 启动 TCP ServerSocket 监听端口\n * 2. Slave（从站模拟器）作为 TCP Client 连接到 Master\n * 3. Master 通过 accept 得到的 Socket，使用 {@link ModbusRTUTCPTransport} 发送读写请求\n *\n * 实现说明：\n * 因为 j2mod 的 ModbusSlave 只能以 TCP Server 模式运行（监听端口等待 Master 连接），\n * 不支持\"Slave 作为 TCP Client 主动连接 Master\"的模式。\n * 所以这里用一个 TCP 桥接（bridge）来模拟：\n * - Slave 在本地内部端口启动（RTU over TCP 模式）\n * - 一个桥接线程同时连接 Master Server 和 Slave 内部端口，做双向数据转发\n * - Master 视角：看到的是 Slave 主动连上来\n *\n * 依赖：j2mod 3.2.1（pom.xml 中已声明）\n *\n * @author 芋道源码\n */\n@Deprecated // 仅技术演示，非是必须的\npublic class ModbusRtuOverTcpDemo {\n\n    /**\n     * Master（主站）TCP Server 监听端口\n     */\n    private static final int PORT = 5021;\n    /**\n     * Slave 内部端口（仅本地中转用，不对外暴露）\n     */\n    private static final int SLAVE_INTERNAL_PORT = PORT + 100;\n    /**\n     * Modbus 从站地址\n     */\n    private static final int SLAVE_ID = 1;\n\n    public static void main(String[] args) throws Exception {\n        // ===================== 第一步：Master 启动 TCP Server 监听 =====================\n        ServerSocket serverSocket = new ServerSocket(PORT);\n        System.out.println(\"===================================================\");\n        System.out.println(\"[Master] TCP Server 已启动，监听端口: \" + PORT);\n        System.out.println(\"[Master] 等待 Slave 连接...\");\n        System.out.println(\"===================================================\");\n\n        // ===================== 第二步：后台启动 Slave，它会主动连接 Master =====================\n        ModbusSlave slave = startSlaveInBackground();\n\n        // Master accept Slave 的连接\n        Socket slaveSocket = serverSocket.accept();\n        System.out.println(\"[Master] Slave 已连接: \" + slaveSocket.getRemoteSocketAddress());\n\n        // ===================== 第三步：Master 通过 RTU over TCP 发送读写请求 =====================\n        // 使用 ModbusRTUTCPTransport 包装 Socket（RTU 帧 = SlaveID + 功能码 + 数据 + CRC，无 MBAP 头）\n        ModbusRTUTCPTransport transport = new ModbusRTUTCPTransport(slaveSocket);\n\n        try {\n            System.out.println(\"[Master] RTU over TCP 通道已建立\\n\");\n\n            // 1. 读操作演示：4 种功能码\n            demoReadCoils(transport);           // 功能码 01：读线圈\n            demoReadDiscreteInputs(transport);  // 功能码 02：读离散输入\n            demoReadHoldingRegisters(transport); // 功能码 03：读保持寄存器\n            demoReadInputRegisters(transport);   // 功能码 04：读输入寄存器\n\n            // 2. 写操作演示 + 读回验证\n            demoWriteCoil(transport);            // 功能码 05：写单个线圈\n            demoWriteRegister(transport);        // 功能码 06：写单个保持寄存器\n\n            System.out.println(\"\\n===================================================\");\n            System.out.println(\"所有 RTU over TCP 读写操作执行成功！\");\n            System.out.println(\"===================================================\");\n        } finally {\n            // 清理资源\n            transport.close();\n            slaveSocket.close();\n            serverSocket.close();\n            slave.close();\n            System.out.println(\"[Master] 资源已关闭\");\n        }\n    }\n\n    // ===================== Slave 设备模拟（作为 TCP Client 连接 Master） =====================\n\n    /**\n     * 在后台启动从站模拟器，并通过 TCP 桥接连到 Master Server\n     *\n     * @return ModbusSlave 实例（用于最后关闭资源）\n     */\n    private static ModbusSlave startSlaveInBackground() throws Exception {\n        // 1. 创建进程映像，初始化寄存器数据\n        SimpleProcessImage spi = new SimpleProcessImage(SLAVE_ID);\n        // 1.1 线圈（Coil，功能码 01/05）- 可读写，地址 0~9\n        for (int i = 0; i < 10; i++) {\n            spi.addDigitalOut(new SimpleDigitalOut(i % 2 == 0));\n        }\n        // 1.2 离散输入（Discrete Input，功能码 02）- 只读，地址 0~9\n        for (int i = 0; i < 10; i++) {\n            spi.addDigitalIn(new SimpleDigitalIn(i % 3 == 0));\n        }\n        // 1.3 保持寄存器（Holding Register，功能码 03/06/16）- 可读写，地址 0~19\n        for (int i = 0; i < 20; i++) {\n            spi.addRegister(new SimpleRegister(i * 100));\n        }\n        // 1.4 输入寄存器（Input Register，功能码 04）- 只读，地址 0~19\n        for (int i = 0; i < 20; i++) {\n            spi.addInputRegister(new SimpleInputRegister(i * 10 + 1));\n        }\n\n        // 2. 启动 Slave（RTU over TCP 模式，在本地内部端口监听）\n        ModbusSlave slave = ModbusSlaveFactory.createTCPSlave(SLAVE_INTERNAL_PORT, 5, true);\n        slave.addProcessImage(SLAVE_ID, spi);\n        slave.open();\n        System.out.println(\"[Slave] 从站模拟器已启动（内部端口: \" + SLAVE_INTERNAL_PORT + \"）\");\n\n        // 3. 启动桥接线程：TCP Client 连接 Master Server，同时连接 Slave 内部端口，双向转发\n        Thread bridgeThread = new Thread(() -> {\n            try {\n                Socket toMaster = new Socket(\"127.0.0.1\", PORT);\n                Socket toSlave = new Socket(\"127.0.0.1\", SLAVE_INTERNAL_PORT);\n                System.out.println(\"[Bridge] 已建立桥接: Master(\" + PORT + \") <-> Slave(\" + SLAVE_INTERNAL_PORT + \")\");\n\n                // 双向桥接：Master ↔ Bridge ↔ Slave\n                Thread forward = new Thread(() -> bridge(toMaster, toSlave), \"bridge-master→slave\");\n                Thread backward = new Thread(() -> bridge(toSlave, toMaster), \"bridge-slave→master\");\n                forward.setDaemon(true);\n                backward.setDaemon(true);\n                forward.start();\n                backward.start();\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }, \"bridge-setup\");\n        bridgeThread.setDaemon(true);\n        bridgeThread.start();\n\n        return slave;\n    }\n\n    /**\n     * TCP 双向桥接：从 src 读取数据，写入 dst\n     */\n    private static void bridge(Socket src, Socket dst) {\n        try {\n            byte[] buf = new byte[1024];\n            InputStream in = src.getInputStream();\n            OutputStream out = dst.getOutputStream();\n            int len;\n            while ((len = in.read(buf)) != -1) {\n                out.write(buf, 0, len);\n                out.flush();\n            }\n        } catch (Exception ignored) {\n            // 连接关闭时正常退出\n        }\n    }\n\n    // ===================== Master 读写操作 =====================\n\n    /**\n     * 发送请求并接收响应（通用方法）\n     */\n    private static ModbusResponse sendRequest(ModbusRTUTCPTransport transport, ModbusRequest request) throws Exception {\n        request.setUnitID(SLAVE_ID);\n        transport.writeRequest(request);\n        return transport.readResponse();\n    }\n\n    /**\n     * 功能码 01：读线圈（Read Coils）\n     */\n    private static void demoReadCoils(ModbusRTUTCPTransport transport) throws Exception {\n        ReadCoilsRequest request = new ReadCoilsRequest(0, 5);\n        ReadCoilsResponse response = (ReadCoilsResponse) sendRequest(transport, request);\n\n        StringBuilder sb = new StringBuilder(\"[功能码 01] 读线圈(0~4): \");\n        for (int i = 0; i < 5; i++) {\n            sb.append(response.getCoilStatus(i) ? \"ON\" : \"OFF\");\n            if (i < 4) {\n                sb.append(\", \");\n            }\n        }\n        System.out.println(sb);\n    }\n\n    /**\n     * 功能码 02：读离散输入（Read Discrete Inputs）\n     */\n    private static void demoReadDiscreteInputs(ModbusRTUTCPTransport transport) throws Exception {\n        ReadInputDiscretesRequest request = new ReadInputDiscretesRequest(0, 5);\n        ReadInputDiscretesResponse response = (ReadInputDiscretesResponse) sendRequest(transport, request);\n\n        StringBuilder sb = new StringBuilder(\"[功能码 02] 读离散输入(0~4): \");\n        for (int i = 0; i < 5; i++) {\n            sb.append(response.getDiscreteStatus(i) ? \"ON\" : \"OFF\");\n            if (i < 4) {\n                sb.append(\", \");\n            }\n        }\n        System.out.println(sb);\n    }\n\n    /**\n     * 功能码 03：读保持寄存器（Read Holding Registers）\n     */\n    private static void demoReadHoldingRegisters(ModbusRTUTCPTransport transport) throws Exception {\n        ReadMultipleRegistersRequest request = new ReadMultipleRegistersRequest(0, 5);\n        ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) sendRequest(transport, request);\n\n        StringBuilder sb = new StringBuilder(\"[功能码 03] 读保持寄存器(0~4): \");\n        for (int i = 0; i < response.getWordCount(); i++) {\n            sb.append(response.getRegisterValue(i));\n            if (i < response.getWordCount() - 1) {\n                sb.append(\", \");\n            }\n        }\n        System.out.println(sb);\n    }\n\n    /**\n     * 功能码 04：读输入寄存器（Read Input Registers）\n     */\n    private static void demoReadInputRegisters(ModbusRTUTCPTransport transport) throws Exception {\n        ReadInputRegistersRequest request = new ReadInputRegistersRequest(0, 5);\n        ReadInputRegistersResponse response = (ReadInputRegistersResponse) sendRequest(transport, request);\n\n        StringBuilder sb = new StringBuilder(\"[功能码 04] 读输入寄存器(0~4): \");\n        for (int i = 0; i < response.getWordCount(); i++) {\n            sb.append(response.getRegisterValue(i));\n            if (i < response.getWordCount() - 1) {\n                sb.append(\", \");\n            }\n        }\n        System.out.println(sb);\n    }\n\n    /**\n     * 功能码 05：写单个线圈（Write Single Coil）+ 读回验证\n     */\n    private static void demoWriteCoil(ModbusRTUTCPTransport transport) throws Exception {\n        int address = 0;\n\n        // 1. 先读取当前值\n        ReadCoilsRequest readReq = new ReadCoilsRequest(address, 1);\n        ReadCoilsResponse readResp = (ReadCoilsResponse) sendRequest(transport, readReq);\n        boolean beforeValue = readResp.getCoilStatus(0);\n\n        // 2. 写入相反的值\n        boolean writeValue = !beforeValue;\n        WriteCoilRequest writeReq = new WriteCoilRequest(address, writeValue);\n        sendRequest(transport, writeReq);\n\n        // 3. 读回验证\n        ReadCoilsResponse verifyResp = (ReadCoilsResponse) sendRequest(transport, readReq);\n        boolean afterValue = verifyResp.getCoilStatus(0);\n\n        System.out.println(\"[功能码 05] 写线圈: 地址=\" + address\n                + \", 写入前=\" + (beforeValue ? \"ON\" : \"OFF\")\n                + \", 写入值=\" + (writeValue ? \"ON\" : \"OFF\")\n                + \", 读回值=\" + (afterValue ? \"ON\" : \"OFF\")\n                + (afterValue == writeValue ? \" ✓ 验证通过\" : \" ✗ 验证失败\"));\n    }\n\n    /**\n     * 功能码 06：写单个保持寄存器（Write Single Register）+ 读回验证\n     */\n    private static void demoWriteRegister(ModbusRTUTCPTransport transport) throws Exception {\n        int address = 0;\n        int writeValue = 12345;\n\n        // 1. 先读取当前值\n        ReadMultipleRegistersRequest readReq = new ReadMultipleRegistersRequest(address, 1);\n        ReadMultipleRegistersResponse readResp = (ReadMultipleRegistersResponse) sendRequest(transport, readReq);\n        int beforeValue = readResp.getRegisterValue(0);\n\n        // 2. 写入新值\n        WriteSingleRegisterRequest writeReq = new WriteSingleRegisterRequest(address, new SimpleRegister(writeValue));\n        sendRequest(transport, writeReq);\n\n        // 3. 读回验证\n        ReadMultipleRegistersResponse verifyResp = (ReadMultipleRegistersResponse) sendRequest(transport, readReq);\n        int afterValue = verifyResp.getRegisterValue(0);\n\n        System.out.println(\"[功能码 06] 写保持寄存器: 地址=\" + address\n                + \", 写入前=\" + beforeValue\n                + \", 写入值=\" + writeValue\n                + \", 读回值=\" + afterValue\n                + (afterValue == writeValue ? \" ✓ 验证通过\" : \" ✗ 验证失败\"));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/IoTModbusTcpClientIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient;\n\nimport com.ghgande.j2mod.modbus.procimg.*;\nimport com.ghgande.j2mod.modbus.slave.ModbusSlave;\nimport com.ghgande.j2mod.modbus.slave.ModbusSlaveFactory;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Modbus TCP 从站模拟器（手动测试）\n *\n * <p>测试场景：模拟一个标准 Modbus TCP 从站设备，供 Modbus TCP Client 网关连接和读写数据\n *\n * <p>使用步骤：\n * <ol>\n *     <li>运行 {@link #testStartSlaveSimulator()} 启动模拟从站（默认端口 5020，从站地址 1）</li>\n *     <li>启动 yudao-module-iot-gateway 服务（需开启 modbus-tcp-client 协议）</li>\n *     <li>确保数据库有对应的 Modbus Client 设备配置（ip=127.0.0.1, port=5020, slaveId=1）</li>\n *     <li>网关会自动连接模拟从站并开始轮询读取寄存器数据</li>\n *     <li>模拟器每 5 秒自动更新输入寄存器和保持寄存器的值，模拟传感器数据变化</li>\n * </ol>\n *\n * <p>可用寄存器：\n * <ul>\n *     <li>线圈 (Coil, 功能码 01/05): 地址 0-9，交替 true/false</li>\n *     <li>离散输入 (Discrete Input, 功能码 02): 地址 0-9，每 3 个一个 true</li>\n *     <li>保持寄存器 (Holding Register, 功能码 03/06/16): 地址 0-19，初始值 0,100,200,...</li>\n *     <li>输入寄存器 (Input Register, 功能码 04): 地址 0-19，初始值 1,11,21,...</li>\n * </ul>\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IoTModbusTcpClientIntegrationTest {\n\n    private static final int PORT = 5020;\n    private static final int SLAVE_ID = 1;\n\n    /**\n     * 启动 Modbus TCP 从站模拟器\n     *\n     * <p>模拟器会持续运行，每 5 秒更新一次寄存器数据，直到手动停止\n     */\n    @SuppressWarnings({\"InfiniteLoopStatement\", \"BusyWait\"})\n    @Test\n    public void testStartSlaveSimulator() throws Exception {\n        // 1. 创建进程映像（Process Image），存储寄存器数据\n        SimpleProcessImage spi = new SimpleProcessImage(SLAVE_ID);\n\n        // 2.1 初始化线圈（Coil，功能码 01/05）- 离散输出，可读写\n        // 地址 0-9，共 10 个线圈\n        for (int i = 0; i < 10; i++) {\n            spi.addDigitalOut(new SimpleDigitalOut(i % 2 == 0)); // 交替 true/false\n        }\n\n        // 2.2 初始化离散输入（Discrete Input，功能码 02）- 只读\n        // 地址 0-9，共 10 个离散输入\n        for (int i = 0; i < 10; i++) {\n            spi.addDigitalIn(new SimpleDigitalIn(i % 3 == 0)); // 每 3 个一个 true\n        }\n\n        // 2.3 初始化保持寄存器（Holding Register，功能码 03/06/16）- 可读写\n        // 地址 0-19，共 20 个寄存器\n        for (int i = 0; i < 20; i++) {\n            spi.addRegister(new SimpleRegister(i * 100)); // 值为 0, 100, 200, ...\n        }\n\n        // 2.4 初始化输入寄存器（Input Register，功能码 04）- 只读\n        // 地址 0-19，共 20 个寄存器\n        SimpleInputRegister[] inputRegisters = new SimpleInputRegister[20];\n        for (int i = 0; i < 20; i++) {\n            inputRegisters[i] = new SimpleInputRegister(i * 10 + 1); // 值为 1, 11, 21, ...\n            spi.addInputRegister(inputRegisters[i]);\n        }\n\n        // 3.1 创建 Modbus TCP 从站\n        ModbusSlave slave = ModbusSlaveFactory.createTCPSlave(PORT, 5);\n        slave.addProcessImage(SLAVE_ID, spi);\n        // 3.2 启动从站\n        slave.open();\n\n        log.info(\"[testStartSlaveSimulator][Modbus TCP 从站模拟器已启动, 端口: {}, 从站地址: {}]\", PORT, SLAVE_ID);\n        log.info(\"[testStartSlaveSimulator][可用寄存器: 线圈(01/05) 0-9, 离散输入(02) 0-9, \" +\n                \"保持寄存器(03/06/16) 0-19, 输入寄存器(04) 0-19]\");\n\n        // 4. 添加关闭钩子\n        Runtime.getRuntime().addShutdownHook(new Thread(() -> {\n            log.info(\"[testStartSlaveSimulator][正在关闭模拟器...]\");\n            slave.close();\n            log.info(\"[testStartSlaveSimulator][模拟器已关闭]\");\n        }));\n\n        // 5. 保持运行，定时更新输入寄存器模拟数据变化\n        int counter = 0;\n        while (true) {\n            Thread.sleep(5000); // 每 5 秒更新一次\n            counter++;\n\n            // 更新输入寄存器的值，模拟传感器数据变化\n            for (int i = 0; i < 20; i++) {\n                int newValue = (i * 10 + 1) + counter;\n                inputRegisters[i].setValue(newValue);\n            }\n\n            // 更新保持寄存器的第一个值\n            spi.getRegister(0).setValue(counter * 100);\n            log.info(\"[testStartSlaveSimulator][数据已更新, counter={}, 保持寄存器[0]={}, 输入寄存器[0]={}]\",\n                    counter, counter * 100, 1 + counter);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/IotModbusTcpServerRtuIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver;\n\nimport cn.hutool.core.util.HexUtil;\nimport cn.hutool.json.JSONObject;\nimport cn.hutool.json.JSONUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrame;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameDecoder;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameEncoder;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.net.NetClient;\nimport io.vertx.core.net.NetClientOptions;\nimport io.vertx.core.net.NetSocket;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * IoT Modbus TCP Server 协议集成测试 — MODBUS_RTU 帧格式（手动测试）\n *\n * <p>测试场景：设备（TCP Client）连接到网关（TCP Server），使用 MODBUS_RTU（CRC16）帧格式通信\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（需开启 modbus-tcp-server 协议，默认端口 503）</li>\n *     <li>确保数据库有对应的 Modbus 设备配置（mode=1, frameFormat=modbus_rtu）</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 自定义功能码认证</li>\n *             <li>{@link #testPollingResponse()} - 轮询响应</li>\n *             <li>{@link #testPropertySetWrite()} - 属性设置（接收写指令）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotModbusTcpServerRtuIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 503;\n    private static final int TIMEOUT_MS = 5000;\n\n    private static final int CUSTOM_FC = 65;\n    private static final int SLAVE_ID = 1;\n\n    private static Vertx vertx;\n    private static NetClient netClient;\n\n    // ===================== 编解码器 =====================\n\n    private static final IotModbusFrameDecoder FRAME_DECODER = new IotModbusFrameDecoder(CUSTOM_FC);\n    private static final IotModbusFrameEncoder FRAME_ENCODER = new IotModbusFrameEncoder(CUSTOM_FC);\n\n    // ===================== 设备信息（根据实际情况修改，从 iot_device 表查询） =====================\n\n    private static final String PRODUCT_KEY = \"modbus_tcp_server_product_demo\";\n    private static final String DEVICE_NAME = \"modbus_tcp_server_device_demo_rtu\";\n    private static final String DEVICE_SECRET = \"af01c55eb8e3424bb23fc6c783936b2e\";\n\n    @BeforeAll\n    static void setUp() {\n        vertx = Vertx.vertx();\n        NetClientOptions options = new NetClientOptions()\n                .setConnectTimeout(TIMEOUT_MS)\n                .setIdleTimeout(TIMEOUT_MS);\n        netClient = vertx.createNetClient(options);\n    }\n\n    @AfterAll\n    static void tearDown() {\n        if (netClient != null) {\n            netClient.close();\n        }\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 认证测试：发送自定义功能码 FC65 认证帧（RTU 格式），验证认证成功响应\n     */\n    @Test\n    public void testAuth() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 构造并发送认证帧\n            IotModbusFrame response = authenticate(socket);\n\n            // 2. 验证响应\n            log.info(\"[testAuth][认证响应帧: slaveId={}, FC={}, customData={}]\",\n                    response.getSlaveId(), response.getFunctionCode(), response.getCustomData());\n            JSONObject json = JSONUtil.parseObj(response.getCustomData());\n            assertEquals(0, json.getInt(\"code\"));\n            log.info(\"[testAuth][认证结果: code={}, message={}]\", json.getInt(\"code\"), json.getStr(\"message\"));\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 轮询响应测试 =====================\n\n    /**\n     * 轮询响应测试：认证后持续监听网关下发的读请求（RTU 格式），每次收到都自动构造读响应帧发回\n     */\n    @Test\n    public void testPollingResponse() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先认证\n            IotModbusFrame authResponse = authenticate(socket);\n            log.info(\"[testPollingResponse][认证响应: {}]\", authResponse.getCustomData());\n            JSONObject authJson = JSONUtil.parseObj(authResponse.getCustomData());\n            assertEquals(0, authJson.getInt(\"code\"));\n\n            // 2. 设置持续监听：每收到一个读请求，自动回复\n            log.info(\"[testPollingResponse][开始持续监听网关下发的读请求...]\");\n            CompletableFuture<Void> done = new CompletableFuture<>();\n            // 注意：使用 requestMode=true，因为设备端收到的是网关下发的读请求（非响应）\n            RecordParser parser = FRAME_DECODER.createRecordParser((frame, frameFormat) -> {\n                log.info(\"[testPollingResponse][收到请求: slaveId={}, FC={}]\",\n                        frame.getSlaveId(), frame.getFunctionCode());\n                // 解析读请求中的起始地址和数量\n                byte[] pdu = frame.getPdu();\n                int startAddress = ((pdu[0] & 0xFF) << 8) | (pdu[1] & 0xFF);\n                int quantity = ((pdu[2] & 0xFF) << 8) | (pdu[3] & 0xFF);\n                log.info(\"[testPollingResponse][读请求参数: startAddress={}, quantity={}]\", startAddress, quantity);\n\n                // 构造读响应帧（模拟寄存器数据，RTU 格式）\n                int[] registerValues = new int[quantity];\n                for (int i = 0; i < quantity; i++) {\n                    registerValues[i] = 100 + i * 100; // 模拟值: 100, 200, 300, ...\n                }\n                byte[] responseData = buildReadResponse(frame.getSlaveId(),\n                        frame.getFunctionCode(), registerValues);\n                socket.write(Buffer.buffer(responseData));\n                log.info(\"[testPollingResponse][已发送读响应, registerValues={}]\", registerValues);\n            }, true);\n            socket.handler(parser);\n\n            // 3. 持续等待（200 秒），期间会自动回复所有收到的读请求\n            Thread.sleep(200000);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 属性设置测试 =====================\n\n    /**\n     * 属性设置测试：认证后等待接收网关下发的 FC06/FC16 写请求（RTU 格式）\n     * <p>\n     * 注意：需手动在平台触发 property.set\n     */\n    @Test\n    public void testPropertySetWrite() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先认证\n            IotModbusFrame authResponse = authenticate(socket);\n            log.info(\"[testPropertySetWrite][认证响应: {}]\", authResponse.getCustomData());\n\n            // 2. 等待网关下发写请求（需手动在平台触发 property.set）\n            log.info(\"[testPropertySetWrite][等待网关下发写请求（请在平台触发 property.set）...]\");\n            IotModbusFrame writeRequest = waitForRequest(socket);\n            log.info(\"[testPropertySetWrite][收到写请求: slaveId={}, FC={}, pdu={}]\",\n                    writeRequest.getSlaveId(), writeRequest.getFunctionCode(),\n                    HexUtil.encodeHexStr(writeRequest.getPdu()));\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 建立 TCP 连接\n     */\n    private CompletableFuture<NetSocket> connect() {\n        CompletableFuture<NetSocket> future = new CompletableFuture<>();\n        netClient.connect(SERVER_PORT, SERVER_HOST)\n                .onSuccess(future::complete)\n                .onFailure(future::completeExceptionally);\n        return future;\n    }\n\n    /**\n     * 执行认证并返回响应帧\n     */\n    private IotModbusFrame authenticate(NetSocket socket) throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        authInfo.setClientId(\"\");\n        byte[] authFrame = buildAuthFrame(authInfo.getClientId(), authInfo.getUsername(), authInfo.getPassword());\n        return sendAndReceive(socket, authFrame);\n    }\n\n    /**\n     * 发送帧并等待响应（使用 IotModbusFrameDecoder 自动检测帧格式并解码）\n     */\n    private IotModbusFrame sendAndReceive(NetSocket socket, byte[] frameData) throws Exception {\n        CompletableFuture<IotModbusFrame> responseFuture = new CompletableFuture<>();\n        // 使用 FrameDecoder 创建拆包器（自动检测帧格式 + 解码，直接回调 IotModbusFrame）\n        RecordParser parser = FRAME_DECODER.createRecordParser(\n                (frame, frameFormat) -> {\n                    try {\n                        log.info(\"[sendAndReceive][检测到帧格式: {}]\", frameFormat);\n                        responseFuture.complete(frame);\n                    } catch (Exception e) {\n                        responseFuture.completeExceptionally(e);\n                    }\n                });\n        socket.handler(parser);\n\n        // 发送请求\n        log.info(\"[sendAndReceive][发送帧, 长度={}]\", frameData.length);\n        socket.write(Buffer.buffer(frameData));\n\n        // 等待响应\n        return responseFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n    }\n\n    /**\n     * 等待接收网关下发的请求帧（不发送，只等待接收）\n     */\n    private IotModbusFrame waitForRequest(NetSocket socket) throws Exception {\n        CompletableFuture<IotModbusFrame> requestFuture = new CompletableFuture<>();\n        // 使用 FrameDecoder 创建拆包器（直接回调 IotModbusFrame）\n        RecordParser parser = FRAME_DECODER.createRecordParser(\n                (frame, frameFormat) -> {\n                    try {\n                        log.info(\"[waitForRequest][检测到帧格式: {}]\", frameFormat);\n                        requestFuture.complete(frame);\n                    } catch (Exception e) {\n                        requestFuture.completeExceptionally(e);\n                    }\n                });\n        socket.handler(parser);\n\n        // 等待（超时 30 秒，因为轮询间隔可能比较长）\n        return requestFuture.get(30000, TimeUnit.MILLISECONDS);\n    }\n\n    /**\n     * 构造认证帧（MODBUS_RTU 格式）\n     * <p>\n     * JSON: {\"method\":\"auth\",\"params\":{\"clientId\":\"...\",\"username\":\"...\",\"password\":\"...\"}}\n     * <p>\n     * RTU 帧格式：[SlaveId(1)] [FC=0x41(1)] [ByteCount(1)] [JSON(N)] [CRC16(2)]\n     */\n    private byte[] buildAuthFrame(String clientId, String username, String password) {\n        JSONObject params = new JSONObject();\n        params.set(\"clientId\", clientId);\n        params.set(\"username\", username);\n        params.set(\"password\", password);\n        JSONObject json = new JSONObject();\n        json.set(\"method\", \"auth\");\n        json.set(\"params\", params);\n        return FRAME_ENCODER.encodeCustomFrame(SLAVE_ID, json.toString(),\n                IotModbusFrameFormatEnum.MODBUS_RTU, 0);\n    }\n\n    /**\n     * 构造 FC03/FC01-04 读响应帧（MODBUS_RTU 格式）\n     * <p>\n     * RTU 帧格式：[SlaveId(1)] [FC(1)] [ByteCount(1)] [RegisterData(N*2)] [CRC16(2)]\n     */\n    private byte[] buildReadResponse(int slaveId, int functionCode, int[] registerValues) {\n        int byteCount = registerValues.length * 2;\n        // 帧长度：SlaveId(1) + FC(1) + ByteCount(1) + Data(N*2) + CRC(2)\n        int totalLength = 1 + 1 + 1 + byteCount + 2;\n        byte[] frame = new byte[totalLength];\n        frame[0] = (byte) slaveId;\n        frame[1] = (byte) functionCode;\n        frame[2] = (byte) byteCount;\n        for (int i = 0; i < registerValues.length; i++) {\n            frame[3 + i * 2] = (byte) ((registerValues[i] >> 8) & 0xFF);\n            frame[3 + i * 2 + 1] = (byte) (registerValues[i] & 0xFF);\n        }\n        // 计算 CRC16\n        int crc = IotModbusCommonUtils.calculateCrc16(frame, totalLength - 2);\n        frame[totalLength - 2] = (byte) (crc & 0xFF);        // CRC Low\n        frame[totalLength - 1] = (byte) ((crc >> 8) & 0xFF); // CRC High\n        return frame;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/IotModbusTcpServerTcpIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver;\n\nimport cn.hutool.core.util.HexUtil;\nimport cn.hutool.json.JSONObject;\nimport cn.hutool.json.JSONUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrame;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameDecoder;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameEncoder;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.net.NetClient;\nimport io.vertx.core.net.NetClientOptions;\nimport io.vertx.core.net.NetSocket;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * IoT Modbus TCP Server 协议集成测试 — MODBUS_TCP 帧格式（手动测试）\n *\n * <p>测试场景：设备（TCP Client）连接到网关（TCP Server），使用 MODBUS_TCP（MBAP 头）帧格式通信\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（需开启 modbus-tcp-server 协议，默认端口 503）</li>\n *     <li>确保数据库有对应的 Modbus 设备配置（mode=1, frameFormat=modbus_tcp）</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 自定义功能码认证</li>\n *             <li>{@link #testPollingResponse()} - 轮询响应</li>\n *             <li>{@link #testPropertySetWrite()} - 属性设置（接收写指令）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotModbusTcpServerTcpIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 503;\n    private static final int TIMEOUT_MS = 5000;\n\n    private static final int CUSTOM_FC = 65;\n    private static final int SLAVE_ID = 1;\n\n    private static Vertx vertx;\n    private static NetClient netClient;\n\n    // ===================== 编解码器 =====================\n\n    private static final IotModbusFrameDecoder FRAME_DECODER = new IotModbusFrameDecoder(CUSTOM_FC);\n    private static final IotModbusFrameEncoder FRAME_ENCODER = new IotModbusFrameEncoder(CUSTOM_FC);\n\n    // ===================== 设备信息（根据实际情况修改，从 iot_device 表查询） =====================\n\n    private static final String PRODUCT_KEY = \"modbus_tcp_server_product_demo\";\n    private static final String DEVICE_NAME = \"modbus_tcp_server_device_demo_tcp\";\n    private static final String DEVICE_SECRET = \"8e4adeb3d25342ab88643421d3fba3f6\";\n\n    @BeforeAll\n    static void setUp() {\n        vertx = Vertx.vertx();\n        NetClientOptions options = new NetClientOptions()\n                .setConnectTimeout(TIMEOUT_MS)\n                .setIdleTimeout(TIMEOUT_MS);\n        netClient = vertx.createNetClient(options);\n    }\n\n    @AfterAll\n    static void tearDown() {\n        if (netClient != null) {\n            netClient.close();\n        }\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 认证测试：发送自定义功能码 FC65 认证帧，验证认证成功响应\n     */\n    @Test\n    public void testAuth() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 构造并发送认证帧\n            IotModbusFrame response = authenticate(socket);\n\n            // 2. 验证响应\n            log.info(\"[testAuth][认证响应帧: slaveId={}, FC={}, customData={}]\",\n                    response.getSlaveId(), response.getFunctionCode(), response.getCustomData());\n            JSONObject json = JSONUtil.parseObj(response.getCustomData());\n            assertEquals(0, json.getInt(\"code\"));\n            log.info(\"[testAuth][认证结果: code={}, message={}]\", json.getInt(\"code\"), json.getStr(\"message\"));\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 轮询响应测试 =====================\n\n    /**\n     * 轮询响应测试：认证后持续监听网关下发的读请求，每次收到都自动构造读响应帧发回\n     */\n    @Test\n    public void testPollingResponse() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先认证\n            IotModbusFrame authResponse = authenticate(socket);\n            log.info(\"[testPollingResponse][认证响应: {}]\", authResponse.getCustomData());\n            JSONObject authJson = JSONUtil.parseObj(authResponse.getCustomData());\n            assertEquals(0, authJson.getInt(\"code\"));\n\n            // 2. 设置持续监听：每收到一个读请求，自动回复\n            log.info(\"[testPollingResponse][开始持续监听网关下发的读请求...]\");\n            RecordParser parser = FRAME_DECODER.createRecordParser((frame, frameFormat) -> {\n                log.info(\"[testPollingResponse][收到请求: slaveId={}, FC={}, transactionId={}]\",\n                        frame.getSlaveId(), frame.getFunctionCode(), frame.getTransactionId());\n                // 解析读请求中的起始地址和数量\n                byte[] pdu = frame.getPdu();\n                int startAddress = ((pdu[0] & 0xFF) << 8) | (pdu[1] & 0xFF);\n                int quantity = ((pdu[2] & 0xFF) << 8) | (pdu[3] & 0xFF);\n                log.info(\"[testPollingResponse][读请求参数: startAddress={}, quantity={}]\", startAddress, quantity);\n\n                // 构造读响应帧（模拟寄存器数据）\n                int[] registerValues = new int[quantity];\n                for (int i = 0; i < quantity; i++) {\n                    registerValues[i] = 100 + i * 100; // 模拟值: 100, 200, 300, ...\n                }\n                byte[] responseData = buildReadResponse(frame.getTransactionId(),\n                        frame.getSlaveId(), frame.getFunctionCode(), registerValues);\n                socket.write(Buffer.buffer(responseData));\n                log.info(\"[testPollingResponse][已发送读响应, registerValues={}]\", registerValues);\n            });\n            socket.handler(parser);\n\n            // 3. 持续等待（200 秒），期间会自动回复所有收到的读请求\n            Thread.sleep(200000);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 属性设置测试 =====================\n\n    /**\n     * 属性设置测试：认证后等待接收网关下发的 FC06/FC16 写请求\n     * <p>\n     * 注意：需手动在平台触发 property.set\n     */\n    @Test\n    public void testPropertySetWrite() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先认证\n            IotModbusFrame authResponse = authenticate(socket);\n            log.info(\"[testPropertySetWrite][认证响应: {}]\", authResponse.getCustomData());\n\n            // 2. 等待网关下发写请求（需手动在平台触发 property.set）\n            log.info(\"[testPropertySetWrite][等待网关下发写请求（请在平台触发 property.set）...]\");\n            IotModbusFrame writeRequest = waitForRequest(socket);\n            log.info(\"[testPropertySetWrite][收到写请求: slaveId={}, FC={}, transactionId={}, pdu={}]\",\n                    writeRequest.getSlaveId(), writeRequest.getFunctionCode(),\n                    writeRequest.getTransactionId(), HexUtil.encodeHexStr(writeRequest.getPdu()));\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 建立 TCP 连接\n     */\n    private CompletableFuture<NetSocket> connect() {\n        CompletableFuture<NetSocket> future = new CompletableFuture<>();\n        netClient.connect(SERVER_PORT, SERVER_HOST)\n                .onSuccess(future::complete)\n                .onFailure(future::completeExceptionally);\n        return future;\n    }\n\n    /**\n     * 执行认证并返回响应帧\n     */\n    private IotModbusFrame authenticate(NetSocket socket) throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        authInfo.setClientId(\"\");  // 特殊：考虑到 modbus 消息长度限制，默认 clientId 不发送\n        byte[] authFrame = buildAuthFrame(authInfo.getClientId(), authInfo.getUsername(), authInfo.getPassword());\n        return sendAndReceive(socket, authFrame);\n    }\n\n    /**\n     * 发送帧并等待响应（使用 IotModbusFrameDecoder 自动检测帧格式并解码）\n     */\n    private IotModbusFrame sendAndReceive(NetSocket socket, byte[] frameData) throws Exception {\n        CompletableFuture<IotModbusFrame> responseFuture = new CompletableFuture<>();\n        // 使用 FrameDecoder 创建拆包器（自动检测帧格式 + 解码，直接回调 IotModbusFrame）\n        RecordParser parser = FRAME_DECODER.createRecordParser(\n                (frame, frameFormat) -> {\n                    try {\n                        log.info(\"[sendAndReceive][检测到帧格式: {}]\", frameFormat);\n                        responseFuture.complete(frame);\n                    } catch (Exception e) {\n                        responseFuture.completeExceptionally(e);\n                    }\n                });\n        socket.handler(parser);\n\n        // 发送请求\n        log.info(\"[sendAndReceive][发送帧, 长度={}]\", frameData.length);\n        socket.write(Buffer.buffer(frameData));\n\n        // 等待响应\n        return responseFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n    }\n\n    /**\n     * 等待接收网关下发的请求帧（不发送，只等待接收）\n     */\n    private IotModbusFrame waitForRequest(NetSocket socket) throws Exception {\n        CompletableFuture<IotModbusFrame> requestFuture = new CompletableFuture<>();\n        // 使用 FrameDecoder 创建拆包器（直接回调 IotModbusFrame）\n        RecordParser parser = FRAME_DECODER.createRecordParser(\n                (frame, frameFormat) -> {\n                    try {\n                        log.info(\"[waitForRequest][检测到帧格式: {}]\", frameFormat);\n                        requestFuture.complete(frame);\n                    } catch (Exception e) {\n                        requestFuture.completeExceptionally(e);\n                    }\n                });\n        socket.handler(parser);\n\n        // 等待（超时 30 秒，因为轮询间隔可能比较长）\n        return requestFuture.get(30000, TimeUnit.MILLISECONDS);\n    }\n\n    /**\n     * 构造认证帧（MODBUS_TCP 格式）\n     * <p>\n     * JSON: {\"method\":\"auth\",\"params\":{\"clientId\":\"...\",\"username\":\"...\",\"password\":\"...\"}}\n     */\n    private byte[] buildAuthFrame(String clientId, String username, String password) {\n        JSONObject params = new JSONObject();\n        params.set(\"clientId\", clientId);\n        params.set(\"username\", username);\n        params.set(\"password\", password);\n        JSONObject json = new JSONObject();\n        json.set(\"method\", \"auth\");\n        json.set(\"params\", params);\n        return FRAME_ENCODER.encodeCustomFrame(SLAVE_ID, json.toString(),\n                IotModbusFrameFormatEnum.MODBUS_TCP, 1);\n    }\n\n    /**\n     * 构造 FC03/FC01-04 读响应帧（MODBUS_TCP 格式）\n     * <p>\n     * 格式：[MBAP(6)] [UnitId(1)] [FC(1)] [ByteCount(1)] [RegisterData(N*2)]\n     */\n    private byte[] buildReadResponse(int transactionId, int slaveId, int functionCode, int[] registerValues) {\n        int byteCount = registerValues.length * 2;\n        // PDU: FC(1) + ByteCount(1) + Data(N*2)\n        int pduLength = 1 + 1 + byteCount;\n        // 完整帧：MBAP(6) + UnitId(1) + PDU\n        int totalLength = 6 + 1 + pduLength;\n        ByteBuffer buf = ByteBuffer.allocate(totalLength).order(ByteOrder.BIG_ENDIAN);\n        // MBAP Header\n        buf.putShort((short) transactionId);  // Transaction ID\n        buf.putShort((short) 0);              // Protocol ID\n        buf.putShort((short) (1 + pduLength)); // Length (UnitId + PDU)\n        // UnitId\n        buf.put((byte) slaveId);\n        // PDU\n        buf.put((byte) functionCode);\n        buf.put((byte) byteCount);\n        for (int value : registerValues) {\n            buf.putShort((short) value);\n        }\n        return buf.array();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotDirectDeviceMqttProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport io.netty.handler.codec.mqtt.MqttQoS;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.mqtt.MqttClient;\nimport io.vertx.mqtt.MqttClientOptions;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * IoT 直连设备 MQTT 协议集成测试（手动测试）\n *\n * <p>测试场景：直连设备（IotProductDeviceTypeEnum 的 DIRECT 类型）通过 MQTT 协议直接连接平台\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（MQTT 端口 1883）</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 设备连接认证</li>\n *             <li>{@link #testPropertyPost()} - 设备属性上报</li>\n *             <li>{@link #testEventPost()} - 设备事件上报</li>\n *             <li>{@link #testSubscribe()} - 订阅下行消息</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：MQTT 协议是有状态的长连接，认证在连接时通过 username/password 完成，\n * 认证成功后同一连接上的后续请求无需再携带认证信息\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotDirectDeviceMqttProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 1883;\n    private static final int TIMEOUT_SECONDS = 10;\n\n    private static Vertx vertx;\n\n    // ===================== 序列化器 =====================\n\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    // ===================== 直连设备信息（根据实际情况修改，从 iot_device 表查询） =====================\n\n    private static final String PRODUCT_KEY = \"4aymZgOTOOCrDKRT\";\n    private static final String DEVICE_NAME = \"small\";\n    private static final String DEVICE_SECRET = \"0baa4c2ecc104ae1a26b4070c218bdf3\";\n\n    @BeforeAll\n    public static void setUp() {\n        vertx = Vertx.vertx();\n    }\n\n    @AfterAll\n    public static void tearDown() {\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 连接认证测试 =====================\n\n    /**\n     * 认证测试：获取设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1. 构建认证信息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        log.info(\"[testAuth][认证信息: clientId={}, username={}, password={}]\",\n                authInfo.getClientId(), authInfo.getUsername(), authInfo.getPassword());\n\n        // 2. 创建客户端并连接\n        MqttClient client = createClient(authInfo);\n        try {\n            client.connect(SERVER_PORT, SERVER_HOST)\n                    .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n            log.info(\"[testAuth][连接成功，客户端 ID: {}]\", client.clientId());\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 直连设备属性上报测试 =====================\n\n    /**\n     * 属性上报测试\n     */\n    @Test\n    public void testPropertyPost() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testPropertyPost][连接认证成功]\");\n\n        try {\n            // 2.1 构建属性上报消息\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),\n                    IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                            .put(\"width\", 1)\n                            .put(\"height\", \"2\")\n                            .build()));\n\n            // 2.2 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/property/post_reply\", PRODUCT_KEY, DEVICE_NAME);\n            subscribe(client, replyTopic);\n\n            // 2.2 发布消息并等待响应\n            String topic = String.format(\"/sys/%s/%s/thing/property/post\", PRODUCT_KEY, DEVICE_NAME);\n            IotDeviceMessage response = publishAndWaitReply(client, topic, request);\n            log.info(\"[testPropertyPost][响应消息: {}]\", response);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 直连设备事件上报测试 =====================\n\n    /**\n     * 事件上报测试\n     */\n    @Test\n    public void testEventPost() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testEventPost][连接认证成功]\");\n\n        try {\n            // 2.1 构建事件上报消息\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                    IotDeviceEventPostReqDTO.of(\n                            \"eat\",\n                            MapUtil.<String, Object>builder().put(\"rice\", 3).build(),\n                            System.currentTimeMillis()));\n\n            // 2.2 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/event/post_reply\", PRODUCT_KEY, DEVICE_NAME);\n            subscribe(client, replyTopic);\n\n            // 3. 发布消息并等待响应\n            String topic = String.format(\"/sys/%s/%s/thing/event/post\", PRODUCT_KEY, DEVICE_NAME);\n            IotDeviceMessage response = publishAndWaitReply(client, topic, request);\n            log.info(\"[testEventPost][响应消息: {}]\", response);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 设备动态注册测试（一型一密） =====================\n\n    /**\n     * 直连设备动态注册测试（一型一密）\n     * <p>\n     * 认证方式：\n     * - clientId: 任意值 + \"|authType=register|\" 后缀\n     * - username: {deviceName}&{productKey}（与普通认证相同）\n     * - password: 签名（使用 productSecret 对 \"deviceName\" + deviceName + \"productKey\" + productKey 进行 HMAC-SHA256）\n     * <p>\n     * 成功后返回设备密钥（deviceSecret），可用于后续一机一密认证\n     */\n    @Test\n    public void testDeviceRegister() throws Exception {\n        // 1.1 构建注册参数\n        String deviceName = \"test-mqtt-\" + System.currentTimeMillis();\n        String productSecret = \"test-product-secret\"; // 替换为实际的 productSecret\n        String sign = IotProductAuthUtils.buildSign(PRODUCT_KEY, deviceName, productSecret);\n        // 1.2 构建 MQTT 连接参数（clientId 需要添加 |authType=register| 后缀）\n        String clientId = IotDeviceAuthUtils.buildClientId(PRODUCT_KEY, deviceName) + \"|authType=register|\";\n        String username = IotDeviceAuthUtils.buildUsername(PRODUCT_KEY, deviceName);\n        log.info(\"[testDeviceRegister][注册参数: clientId={}, username={}, sign={}]\",\n                clientId, username, sign);\n        // 1.3 创建客户端并连接（连接时服务端自动处理注册）\n        MqttClientOptions options = new MqttClientOptions()\n                .setClientId(clientId)\n                .setUsername(username)\n                .setPassword(sign)\n                .setCleanSession(true)\n                .setKeepAliveInterval(60);\n        MqttClient client = MqttClient.create(vertx, options);\n\n        try {\n            // 2. 连接服务器（连接成功后服务端会自动处理注册并发送响应）\n            client.connect(SERVER_PORT, SERVER_HOST)\n                    .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n            log.info(\"[testDeviceRegister][连接成功，等待注册响应...]\");\n\n            // 3.1 设置消息处理器，接收注册响应\n            CompletableFuture<IotDeviceMessage> responseFuture = new CompletableFuture<>();\n            client.publishHandler(message -> {\n                log.info(\"[testDeviceRegister][收到响应: topic={}, payload={}]\",\n                        message.topicName(), message.payload().toString());\n                IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());\n                responseFuture.complete(response);\n            });\n            // 3.2 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/auth/register_reply\", PRODUCT_KEY, deviceName);\n            subscribe(client, replyTopic);\n\n            // 4. 等待注册响应\n            IotDeviceMessage response = responseFuture.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n            log.info(\"[testDeviceRegister][注册响应: {}]\", response);\n            log.info(\"[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]\");\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 订阅下行消息测试 =====================\n\n    /**\n     * 订阅下行消息测试：订阅服务端下发的消息\n     */\n    @Test\n    public void testSubscribe() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testSubscribe][连接认证成功]\");\n\n        try {\n            // 2. 设置消息处理器：收到属性设置时，回复 _reply 消息\n            client.publishHandler(message -> {\n                log.info(\"[testSubscribe][收到消息: topic={}, payload={}]\",\n                        message.topicName(), message.payload().toString());\n                // 收到属性设置消息时，回复 _reply\n                if (message.topicName().endsWith(\"/thing/property/set\")) {\n                    try {\n                        IotDeviceMessage received = SERIALIZER.deserialize(message.payload().getBytes());\n                        IotDeviceMessage reply = IotDeviceMessage.replyOf(\n                                received.getRequestId(), \"thing.property.set_reply\", null, 0, null);\n                        String replyTopic = String.format(\"/sys/%s/%s/thing/property/set_reply\", PRODUCT_KEY, DEVICE_NAME);\n                        byte[] replyPayload = SERIALIZER.serialize(reply);\n                        client.publish(replyTopic, Buffer.buffer(replyPayload), MqttQoS.AT_LEAST_ONCE, false, false);\n                        log.info(\"[testSubscribe][已回复属性设置: topic={}]\", replyTopic);\n                    } catch (Exception e) {\n                        log.error(\"[testSubscribe][回复属性设置异常]\", e);\n                    }\n                }\n            });\n\n            // 3. 订阅下行主题（属性设置 + 服务调用）\n            String topic = String.format(\"/sys/%s/%s/#\", PRODUCT_KEY, DEVICE_NAME);\n            log.info(\"[testSubscribe][订阅主题: {}]\", topic);\n            subscribe(client, topic);\n            log.info(\"[testSubscribe][订阅成功，等待下行消息... (30秒后自动断开)]\");\n\n            // 4. 保持连接 30 秒等待消息\n            Thread.sleep(30000);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 创建 MQTT 客户端\n     *\n     * @param authInfo 认证信息\n     * @return MQTT 客户端\n     */\n    private MqttClient createClient(IotDeviceAuthReqDTO authInfo) {\n        MqttClientOptions options = new MqttClientOptions()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword())\n                .setCleanSession(true)\n                .setKeepAliveInterval(60);\n        return MqttClient.create(vertx, options);\n    }\n\n    /**\n     * 连接并认证设备\n     *\n     * @return 已认证的 MQTT 客户端\n     */\n    private MqttClient connectAndAuth() throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        MqttClient client = createClient(authInfo);\n        client.connect(SERVER_PORT, SERVER_HOST)\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        return client;\n    }\n\n    /**\n     * 订阅主题\n     *\n     * @param client MQTT 客户端\n     * @param topic  主题\n     */\n    private void subscribe(MqttClient client, String topic) throws Exception {\n        client.subscribe(topic, MqttQoS.AT_LEAST_ONCE.value())\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        log.info(\"[subscribe][订阅主题成功: {}]\", topic);\n    }\n\n    /**\n     * 发布消息并等待响应\n     *\n     * @param client  MQTT 客户端\n     * @param topic   发布主题\n     * @param request 请求消息\n     * @return 响应消息\n     */\n    private IotDeviceMessage publishAndWaitReply(MqttClient client, String topic, IotDeviceMessage request)\n            throws Exception {\n        // 1. 设置消息处理器，接收响应\n        CompletableFuture<IotDeviceMessage> responseFuture = new CompletableFuture<>();\n        client.publishHandler(message -> {\n            log.info(\"[publishAndWaitReply][收到响应: topic={}, payload={}]\",\n                    message.topicName(), message.payload().toString());\n            IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());\n            responseFuture.complete(response);\n        });\n\n        // 2. 序列化并发布消息\n        byte[] payload = SERIALIZER.serialize(request);\n        log.info(\"[publishAndWaitReply][Serializer: {}, 发送消息: topic={}, payload={}]\",\n                SERIALIZER.getType(), topic, new String(payload));\n        client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false)\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        log.info(\"[publishAndWaitReply][消息发布成功]\");\n\n        // 3. 等待响应\n        try {\n            return responseFuture.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        } catch (Exception e) {\n            log.warn(\"[publishAndWaitReply][等待响应超时或失败]\");\n            return null;\n        }\n    }\n\n    /**\n     * 断开连接\n     *\n     * @param client MQTT 客户端\n     */\n    private void disconnect(MqttClient client) throws Exception {\n        client.disconnect()\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        log.info(\"[disconnect][断开连接成功]\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotGatewayDeviceMqttProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport io.netty.handler.codec.mqtt.MqttQoS;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.mqtt.MqttClient;\nimport io.vertx.mqtt.MqttClientOptions;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * IoT 网关设备 MQTT 协议集成测试（手动测试）\n *\n * <p>测试场景：网关设备（IotProductDeviceTypeEnum 的 GATEWAY 类型）通过 MQTT 协议管理子设备拓扑关系\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（MQTT 端口 1883）</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 网关设备连接认证</li>\n *             <li>{@link #testTopoAdd()} - 添加子设备拓扑关系</li>\n *             <li>{@link #testTopoDelete()} - 删除子设备拓扑关系</li>\n *             <li>{@link #testTopoGet()} - 获取子设备拓扑关系</li>\n *             <li>{@link #testSubDeviceRegister()} - 子设备动态注册</li>\n *             <li>{@link #testPropertyPackPost()} - 批量上报属性（网关 + 子设备）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：MQTT 协议是有状态的长连接，认证在连接时通过 username/password 完成，\n * 认证成功后同一连接上的后续请求无需再携带认证信息\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewayDeviceMqttProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 1883;\n    private static final int TIMEOUT_SECONDS = 10;\n\n    private static Vertx vertx;\n\n    // ===================== 序列化器 =====================\n\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    // ===================== 网关设备信息（根据实际情况修改，从 iot_device 表查询网关设备） =====================\n\n    private static final String GATEWAY_PRODUCT_KEY = \"m6XcS1ZJ3TW8eC0v\";\n    private static final String GATEWAY_DEVICE_NAME = \"sub-ddd\";\n    private static final String GATEWAY_DEVICE_SECRET = \"b3d62c70f8a4495487ed1d35d61ac2b3\";\n\n    // ===================== 子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String SUB_DEVICE_PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String SUB_DEVICE_NAME = \"chazuo-it\";\n    private static final String SUB_DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    @BeforeAll\n    public static void setUp() {\n        vertx = Vertx.vertx();\n    }\n\n    @AfterAll\n    public static void tearDown() {\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 连接认证测试 =====================\n\n    /**\n     * 网关设备认证测试：获取网关设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1. 构建认证信息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);\n        log.info(\"[testAuth][认证信息: clientId={}, username={}, password={}]\",\n                authInfo.getClientId(), authInfo.getUsername(), authInfo.getPassword());\n\n        // 2. 创建客户端并连接\n        MqttClient client = createClient(authInfo);\n        try {\n            client.connect(SERVER_PORT, SERVER_HOST)\n                    .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n            log.info(\"[testAuth][连接成功，客户端 ID: {}]\", client.clientId());\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 拓扑管理测试 =====================\n\n    /**\n     * 添加子设备拓扑关系测试\n     * <p>\n     * 网关设备向平台上报需要绑定的子设备信息\n     */\n    @Test\n    public void testTopoAdd() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testTopoAdd][连接认证成功]\");\n\n        try {\n            // 2.1 构建子设备认证信息\n            IotDeviceAuthReqDTO subAuthInfo = IotDeviceAuthUtils.getAuthInfo(\n                    SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME, SUB_DEVICE_SECRET);\n            IotDeviceAuthReqDTO subDeviceAuth = new IotDeviceAuthReqDTO()\n                    .setClientId(subAuthInfo.getClientId())\n                    .setUsername(subAuthInfo.getUsername())\n                    .setPassword(subAuthInfo.getPassword());\n\n            // 2.2 构建请求消息\n            IotDeviceTopoAddReqDTO params = new IotDeviceTopoAddReqDTO()\n                    .setSubDevices(Collections.singletonList(subDeviceAuth));\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.TOPO_ADD.getMethod(),\n                    params);\n\n            // 2.3 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/topo/add_reply\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            subscribe(client, replyTopic);\n\n            // 3. 发布消息并等待响应\n            String topic = String.format(\"/sys/%s/%s/thing/topo/add\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            IotDeviceMessage response = publishAndWaitReply(client, topic, request);\n            log.info(\"[testTopoAdd][响应消息: {}]\", response);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    /**\n     * 删除子设备拓扑关系测试\n     * <p>\n     * 网关设备向平台上报需要解绑的子设备信息\n     */\n    @Test\n    public void testTopoDelete() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testTopoDelete][连接认证成功]\");\n\n        try {\n            // 2.1 构建请求消息\n            IotDeviceTopoDeleteReqDTO params = new IotDeviceTopoDeleteReqDTO()\n                    .setSubDevices(Collections.singletonList(\n                            new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME)));\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod(),\n                    params);\n\n            // 2.2 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/topo/delete_reply\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            subscribe(client, replyTopic);\n\n            // 3. 发布消息并等待响应\n            String topic = String.format(\"/sys/%s/%s/thing/topo/delete\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            IotDeviceMessage response = publishAndWaitReply(client, topic, request);\n            log.info(\"[testTopoDelete][响应消息: {}]\", response);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    /**\n     * 获取子设备拓扑关系测试\n     * <p>\n     * 网关设备向平台查询已绑定的子设备列表\n     */\n    @Test\n    public void testTopoGet() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testTopoGet][连接认证成功]\");\n\n        try {\n            // 2.1 构建请求消息\n            IotDeviceTopoGetReqDTO params = new IotDeviceTopoGetReqDTO();\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.TOPO_GET.getMethod(),\n                    params);\n\n            // 2.2 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/topo/get_reply\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            subscribe(client, replyTopic);\n\n            // 3. 发布消息并等待响应\n            String topic = String.format(\"/sys/%s/%s/thing/topo/get\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            IotDeviceMessage response = publishAndWaitReply(client, topic, request);\n            log.info(\"[testTopoGet][响应消息: {}]\", response);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 子设备注册测试 =====================\n\n    /**\n     * 子设备动态注册测试\n     * <p>\n     * 网关设备代理子设备进行动态注册，平台返回子设备的 deviceSecret\n     * <p>\n     * 注意：此接口需要网关认证\n     */\n    @Test\n    public void testSubDeviceRegister() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testSubDeviceRegister][连接认证成功]\");\n\n        try {\n            // 2.1 构建请求消息\n            IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO()\n                    .setProductKey(SUB_DEVICE_PRODUCT_KEY)\n                    .setDeviceName(\"mougezishebei-mqtt\");\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod(),\n                    Collections.singletonList(subDevice));\n\n            // 2.2 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/auth/sub-device/register_reply\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            subscribe(client, replyTopic);\n\n            // 3. 发布消息并等待响应\n            String topic = String.format(\"/sys/%s/%s/thing/auth/sub-device/register\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            IotDeviceMessage response = publishAndWaitReply(client, topic, request);\n            log.info(\"[testSubDeviceRegister][响应消息: {}]\", response);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 批量上报测试 =====================\n\n    /**\n     * 批量上报属性测试（网关 + 子设备）\n     * <p>\n     * 网关设备批量上报自身属性、事件，以及子设备的属性、事件\n     */\n    @Test\n    public void testPropertyPackPost() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testPropertyPackPost][连接认证成功]\");\n\n        try {\n            // 2.1 构建【网关设备】自身属性\n            Map<String, Object> gatewayProperties = MapUtil.<String, Object>builder()\n                    .put(\"temperature\", 25.5)\n                    .build();\n\n            // 2.2 构建【网关设备】自身事件\n            IotDevicePropertyPackPostReqDTO.EventValue gatewayEvent = new IotDevicePropertyPackPostReqDTO.EventValue()\n                    .setValue(MapUtil.builder().put(\"message\", \"gateway started\").build())\n                    .setTime(System.currentTimeMillis());\n            Map<String, IotDevicePropertyPackPostReqDTO.EventValue> gatewayEvents = MapUtil\n                    .<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                    .put(\"statusReport\", gatewayEvent)\n                    .build();\n\n            // 2.3 构建【网关子设备】属性\n            Map<String, Object> subDeviceProperties = MapUtil.<String, Object>builder()\n                    .put(\"power\", 100)\n                    .build();\n\n            // 2.4 构建【网关子设备】事件\n            IotDevicePropertyPackPostReqDTO.EventValue subDeviceEvent = new IotDevicePropertyPackPostReqDTO.EventValue()\n                    .setValue(MapUtil.builder().put(\"errorCode\", 0).build())\n                    .setTime(System.currentTimeMillis());\n            Map<String, IotDevicePropertyPackPostReqDTO.EventValue> subDeviceEvents = MapUtil\n                    .<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                    .put(\"healthCheck\", subDeviceEvent)\n                    .build();\n\n            // 2.5 构建子设备数据\n            IotDevicePropertyPackPostReqDTO.SubDeviceData subDeviceData = new IotDevicePropertyPackPostReqDTO.SubDeviceData()\n                    .setIdentity(new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME))\n                    .setProperties(subDeviceProperties)\n                    .setEvents(subDeviceEvents);\n\n            // 2.6 构建请求消息\n            IotDevicePropertyPackPostReqDTO params = new IotDevicePropertyPackPostReqDTO()\n                    .setProperties(gatewayProperties)\n                    .setEvents(gatewayEvents)\n                    .setSubDevices(ListUtil.of(subDeviceData));\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod(),\n                    params);\n\n            // 2.7 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/event/property/pack/post_reply\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            subscribe(client, replyTopic);\n\n            // 3. 发布消息并等待响应\n            String topic = String.format(\"/sys/%s/%s/thing/event/property/pack/post\",\n                    GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);\n            IotDeviceMessage response = publishAndWaitReply(client, topic, request);\n            log.info(\"[testPropertyPackPost][响应消息: {}]\", response);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 创建 MQTT 客户端\n     *\n     * @param authInfo 认证信息\n     * @return MQTT 客户端\n     */\n    private MqttClient createClient(IotDeviceAuthReqDTO authInfo) {\n        MqttClientOptions options = new MqttClientOptions()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword())\n                .setCleanSession(true)\n                .setKeepAliveInterval(60);\n        return MqttClient.create(vertx, options);\n    }\n\n    /**\n     * 连接并认证网关设备\n     *\n     * @return 已认证的 MQTT 客户端\n     */\n    private MqttClient connectAndAuth() throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);\n        MqttClient client = createClient(authInfo);\n        client.connect(SERVER_PORT, SERVER_HOST)\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        return client;\n    }\n\n    /**\n     * 订阅主题\n     *\n     * @param client MQTT 客户端\n     * @param topic  主题\n     */\n    private void subscribe(MqttClient client, String topic) throws Exception {\n        client.subscribe(topic, MqttQoS.AT_LEAST_ONCE.value())\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        log.info(\"[subscribe][订阅主题成功: {}]\", topic);\n    }\n\n    /**\n     * 发布消息并等待响应\n     *\n     * @param client  MQTT 客户端\n     * @param topic   发布主题\n     * @param request 请求消息\n     * @return 响应消息\n     */\n    private IotDeviceMessage publishAndWaitReply(MqttClient client, String topic, IotDeviceMessage request)\n            throws Exception {\n        // 1. 设置消息处理器，接收响应\n        CompletableFuture<IotDeviceMessage> responseFuture = new CompletableFuture<>();\n        client.publishHandler(message -> {\n            log.info(\"[publishAndWaitReply][收到响应: topic={}, payload={}]\",\n                    message.topicName(), message.payload().toString());\n            IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());\n            responseFuture.complete(response);\n        });\n\n        // 2. 序列化并发布消息\n        byte[] payload = SERIALIZER.serialize(request);\n        log.info(\"[publishAndWaitReply][Serializer: {}, 发送消息: topic={}, payload={}]\",\n                SERIALIZER.getType(), topic, new String(payload));\n        client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false)\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        log.info(\"[publishAndWaitReply][消息发布成功]\");\n\n        // 3. 等待响应\n        try {\n            return responseFuture.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        } catch (Exception e) {\n            log.warn(\"[publishAndWaitReply][等待响应超时或失败]\");\n            return null;\n        }\n    }\n\n    /**\n     * 断开连接\n     *\n     * @param client MQTT 客户端\n     */\n    private void disconnect(MqttClient client) throws Exception {\n        client.disconnect()\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        log.info(\"[disconnect][断开连接成功]\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotGatewaySubDeviceMqttProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport io.netty.handler.codec.mqtt.MqttQoS;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.mqtt.MqttClient;\nimport io.vertx.mqtt.MqttClientOptions;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * IoT 网关子设备 MQTT 协议集成测试（手动测试）\n *\n * <p>测试场景：子设备（IotProductDeviceTypeEnum 的 SUB 类型）通过网关设备代理上报数据\n *\n * <p><b>重要说明：子设备无法直接连接平台，所有请求均由网关设备（Gateway）代为转发。</b>\n * <p>网关设备转发子设备请求时，使用子设备自己的认证信息连接。\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（MQTT 端口 1883）</li>\n *     <li>确保子设备已通过 {@link IotGatewayDeviceMqttProtocolIntegrationTest#testTopoAdd()} 绑定到网关</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 子设备连接认证</li>\n *             <li>{@link #testPropertyPost()} - 子设备属性上报（由网关代理转发）</li>\n *             <li>{@link #testEventPost()} - 子设备事件上报（由网关代理转发）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：MQTT 协议是有状态的长连接，认证在连接时通过 username/password 完成，\n * 认证成功后同一连接上的后续请求无需再携带认证信息\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewaySubDeviceMqttProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 1883;\n    private static final int TIMEOUT_SECONDS = 10;\n\n    private static Vertx vertx;\n\n    // ===================== 序列化器 =====================\n\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    // ===================== 网关子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String DEVICE_NAME = \"chazuo-it\";\n    private static final String DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    @BeforeAll\n    public static void setUp() {\n        vertx = Vertx.vertx();\n    }\n\n    @AfterAll\n    public static void tearDown() {\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 连接认证测试 =====================\n\n    /**\n     * 子设备认证测试：获取子设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1. 构建认证信息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        log.info(\"[testAuth][认证信息: clientId={}, username={}, password={}]\",\n                authInfo.getClientId(), authInfo.getUsername(), authInfo.getPassword());\n\n        // 2. 创建客户端并连接\n        MqttClient client = createClient(authInfo);\n        try {\n            client.connect(SERVER_PORT, SERVER_HOST)\n                    .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n            log.info(\"[testAuth][连接成功，客户端 ID: {}]\", client.clientId());\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 子设备属性上报测试 =====================\n\n    /**\n     * 子设备属性上报测试\n     */\n    @Test\n    public void testPropertyPost() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testPropertyPost][连接认证成功]\");\n        log.info(\"[testPropertyPost][子设备属性上报 - 请求实际由 Gateway 代为转发]\");\n\n        try {\n            // 2.1 构建属性上报消息\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),\n                    IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                            .put(\"power\", 100)\n                            .put(\"status\", \"online\")\n                            .put(\"temperature\", 36.5)\n                            .build()));\n\n            // 2.2 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/property/post_reply\", PRODUCT_KEY, DEVICE_NAME);\n            subscribe(client, replyTopic);\n\n            // 3. 发布消息并等待响应\n            String topic = String.format(\"/sys/%s/%s/thing/property/post\", PRODUCT_KEY, DEVICE_NAME);\n            IotDeviceMessage response = publishAndWaitReply(client, topic, request);\n            log.info(\"[testPropertyPost][响应消息: {}]\", response);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 子设备事件上报测试 =====================\n\n    /**\n     * 子设备事件上报测试\n     */\n    @Test\n    public void testEventPost() throws Exception {\n        // 1. 连接并认证\n        MqttClient client = connectAndAuth();\n        log.info(\"[testEventPost][连接认证成功]\");\n        log.info(\"[testEventPost][子设备事件上报 - 请求实际由 Gateway 代为转发]\");\n\n        try {\n            // 2.1 构建事件上报消息\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                    IotDeviceEventPostReqDTO.of(\n                            \"alarm\",\n                            MapUtil.<String, Object>builder()\n                                    .put(\"level\", \"warning\")\n                                    .put(\"message\", \"temperature too high\")\n                                    .put(\"threshold\", 40)\n                                    .put(\"current\", 42)\n                                    .build(),\n                            System.currentTimeMillis()));\n\n            // 2.2 订阅 _reply 主题\n            String replyTopic = String.format(\"/sys/%s/%s/thing/event/post_reply\", PRODUCT_KEY, DEVICE_NAME);\n            subscribe(client, replyTopic);\n\n            // 3. 发布消息并等待响应\n            String topic = String.format(\"/sys/%s/%s/thing/event/post\", PRODUCT_KEY, DEVICE_NAME);\n            IotDeviceMessage response = publishAndWaitReply(client, topic, request);\n            log.info(\"[testEventPost][响应消息: {}]\", response);\n        } finally {\n            disconnect(client);\n        }\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 创建 MQTT 客户端\n     *\n     * @param authInfo 认证信息\n     * @return MQTT 客户端\n     */\n    private MqttClient createClient(IotDeviceAuthReqDTO authInfo) {\n        MqttClientOptions options = new MqttClientOptions()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword())\n                .setCleanSession(true)\n                .setKeepAliveInterval(60);\n        return MqttClient.create(vertx, options);\n    }\n\n    /**\n     * 连接并认证子设备\n     *\n     * @return 已认证的 MQTT 客户端\n     */\n    private MqttClient connectAndAuth() throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        MqttClient client = createClient(authInfo);\n        client.connect(SERVER_PORT, SERVER_HOST)\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        return client;\n    }\n\n    /**\n     * 订阅主题\n     *\n     * @param client MQTT 客户端\n     * @param topic  主题\n     */\n    private void subscribe(MqttClient client, String topic) throws Exception {\n        client.subscribe(topic, MqttQoS.AT_LEAST_ONCE.value())\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        log.info(\"[subscribe][订阅主题成功: {}]\", topic);\n    }\n\n    /**\n     * 发布消息并等待响应\n     *\n     * @param client  MQTT 客户端\n     * @param topic   发布主题\n     * @param request 请求消息\n     * @return 响应消息\n     */\n    private IotDeviceMessage publishAndWaitReply(MqttClient client, String topic, IotDeviceMessage request)\n            throws Exception {\n        // 1. 设置消息处理器，接收响应\n        CompletableFuture<IotDeviceMessage> responseFuture = new CompletableFuture<>();\n        client.publishHandler(message -> {\n            log.info(\"[publishAndWaitReply][收到响应: topic={}, payload={}]\",\n                    message.topicName(), message.payload().toString());\n            IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());\n            responseFuture.complete(response);\n        });\n\n        // 2. 序列化并发布消息\n        byte[] payload = SERIALIZER.serialize(request);\n        log.info(\"[publishAndWaitReply][Serializer: {}, 发送消息: topic={}, payload={}]\",\n                SERIALIZER.getType(), topic, new String(payload));\n        client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false)\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        log.info(\"[publishAndWaitReply][消息发布成功]\");\n\n        // 3. 等待响应\n        try {\n            return responseFuture.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        } catch (Exception e) {\n            log.warn(\"[publishAndWaitReply][等待响应超时或失败]\");\n            return null;\n        }\n    }\n\n    /**\n     * 断开连接\n     *\n     * @param client MQTT 客户端\n     */\n    private void disconnect(MqttClient client) throws Exception {\n        client.disconnect()\n                .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        log.info(\"[disconnect][断开连接成功]\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotDirectDeviceTcpProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpCodecTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodecFactory;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.net.NetClient;\nimport io.vertx.core.net.NetClientOptions;\nimport io.vertx.core.net.NetSocket;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * IoT 直连设备 TCP 协议集成测试（手动测试）\n *\n * <p>测试场景：直连设备（IotProductDeviceTypeEnum 的 DIRECT 类型）通过 TCP 协议直接连接平台\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（TCP 端口 8091）</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 设备认证</li>\n *             <li>{@link #testDeviceRegister()} - 设备动态注册（一型一密）</li>\n *             <li>{@link #testPropertyPost()} - 设备属性上报</li>\n *             <li>{@link #testEventPost()} - 设备事件上报</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：TCP 协议是有状态的长连接，认证成功后同一连接上的后续请求无需再携带认证信息\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotDirectDeviceTcpProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8091;\n    private static final int TIMEOUT_MS = 5000;\n\n    private static Vertx vertx;\n    private static NetClient netClient;\n\n    // ===================== 编解码器 =====================\n\n    /**\n     * 消息序列化器\n     */\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    /**\n     * TCP 帧编解码器\n     */\n    private static final IotTcpFrameCodec FRAME_CODEC = IotTcpFrameCodecFactory.create(\n            new IotTcpConfig.CodecConfig()\n                    .setType(IotTcpCodecTypeEnum.DELIMITER.getType())\n                    .setDelimiter(\"\\\\n\")\n//                    .setType(IotTcpCodecTypeEnum.LENGTH_FIELD.getType())\n//                    .setLengthFieldOffset(0)\n//                    .setLengthFieldLength(4)\n//                    .setLengthAdjustment(0)\n//                    .setInitialBytesToStrip(4)\n//                    .setType(IotTcpCodecTypeEnum.LENGTH_FIELD.getType())\n//                    .setFixedLength(256)\n    );\n\n    // ===================== 直连设备信息（根据实际情况修改，从 iot_device 表查询） =====================\n\n    private static final String PRODUCT_KEY = \"4aymZgOTOOCrDKRT\";\n    private static final String DEVICE_NAME = \"small\";\n    private static final String DEVICE_SECRET = \"0baa4c2ecc104ae1a26b4070c218bdf3\";\n\n    @BeforeAll\n    static void setUp() {\n        vertx = Vertx.vertx();\n        NetClientOptions options = new NetClientOptions()\n                .setConnectTimeout(TIMEOUT_MS)\n                .setIdleTimeout(TIMEOUT_MS);\n        netClient = vertx.createNetClient(options);\n    }\n\n    @AfterAll\n    static void tearDown() {\n        if (netClient != null) {\n            netClient.close();\n        }\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 认证测试：获取设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1. 构建认证消息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\"auth\", authReqDTO);\n\n        // 2. 发送并接收响应\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testAuth][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 动态注册测试 =====================\n\n    /**\n     * 直连设备动态注册测试（一型一密）\n     * <p>\n     * 使用产品密钥（productSecret）验证身份，成功后返回设备密钥（deviceSecret）\n     * <p>\n     * 注意：此接口不需要认证\n     */\n    @Test\n    public void testDeviceRegister() throws Exception {\n        // 1. 构建注册消息\n        String deviceName = \"test-tcp-\" + System.currentTimeMillis();\n        String productSecret = \"test-product-secret\"; // 替换为实际的 productSecret\n        String sign = IotProductAuthUtils.buildSign(PRODUCT_KEY, deviceName, productSecret);\n        IotDeviceRegisterReqDTO registerReqDTO = new IotDeviceRegisterReqDTO()\n                .setProductKey(PRODUCT_KEY)\n                .setDeviceName(deviceName)\n                .setSign(sign);\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), registerReqDTO);\n\n        // 2. 发送并接收响应\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testDeviceRegister][响应消息: {}]\", response);\n            log.info(\"[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]\");\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 直连设备属性上报测试 =====================\n\n    /**\n     * 属性上报测试\n     */\n    @Test\n    public void testPropertyPost() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先进行认证\n            IotDeviceMessage authResponse = authenticate(socket);\n            log.info(\"[testPropertyPost][认证响应: {}]\", authResponse);\n\n            // 2. 构建属性上报消息\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),\n                    IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                            .put(\"width\", 1)\n                            .put(\"height\", \"2\")\n                            .build()));\n\n            // 3. 发送并接收响应\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testPropertyPost][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 直连设备事件上报测试 =====================\n\n    /**\n     * 事件上报测试\n     */\n    @Test\n    public void testEventPost() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先进行认证\n            IotDeviceMessage authResponse = authenticate(socket);\n            log.info(\"[testEventPost][认证响应: {}]\", authResponse);\n\n            // 2. 构建事件上报消息\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                    IotDeviceEventPostReqDTO.of(\n                            \"eat\",\n                            MapUtil.<String, Object>builder().put(\"rice\", 3).build(),\n                            System.currentTimeMillis()));\n\n            // 3. 发送并接收响应\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testEventPost][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 建立 TCP 连接\n     *\n     * @return 连接 Future\n     */\n    private CompletableFuture<NetSocket> connect() {\n        CompletableFuture<NetSocket> future = new CompletableFuture<>();\n        netClient.connect(SERVER_PORT, SERVER_HOST)\n                .onSuccess(future::complete)\n                .onFailure(future::completeExceptionally);\n        return future;\n    }\n\n    /**\n     * 执行设备认证\n     *\n     * @param socket TCP 连接\n     * @return 认证响应消息\n     */\n    private IotDeviceMessage authenticate(NetSocket socket) throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\"auth\", authInfo);\n        return sendAndReceive(socket, request);\n    }\n\n    /**\n     * 发送消息并接收响应\n     *\n     * @param socket  TCP 连接\n     * @param request 请求消息\n     * @return 响应消息\n     */\n    private IotDeviceMessage sendAndReceive(NetSocket socket, IotDeviceMessage request) throws Exception {\n        // 1. 使用 FRAME_CODEC 创建解码器\n        CompletableFuture<IotDeviceMessage> responseFuture = new CompletableFuture<>();\n        RecordParser parser = FRAME_CODEC.createDecodeParser(buffer -> {\n            try {\n                // 反序列化响应\n                IotDeviceMessage response = SERIALIZER.deserialize(buffer.getBytes());\n                responseFuture.complete(response);\n            } catch (Exception e) {\n                responseFuture.completeExceptionally(e);\n            }\n        });\n        socket.handler(parser);\n\n        // 2.1 序列化 + 帧编码\n        byte[] serializedData = SERIALIZER.serialize(request);\n        Buffer frameData = FRAME_CODEC.encode(serializedData);\n        log.info(\"[sendAndReceive][发送消息: {}，数据长度: {} 字节]\", request.getMethod(), frameData.length());\n        // 2.2 发送请求\n        socket.write(frameData);\n\n        // 3. 等待响应\n        IotDeviceMessage response = responseFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        log.info(\"[sendAndReceive][收到响应，数据长度: {} 字节]\", SERIALIZER.serialize(response).length);\n        return response;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotGatewayDeviceTcpProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpCodecTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodecFactory;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.net.NetClient;\nimport io.vertx.core.net.NetClientOptions;\nimport io.vertx.core.net.NetSocket;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * IoT 网关设备 TCP 协议集成测试（手动测试）\n *\n * <p>测试场景：网关设备（IotProductDeviceTypeEnum 的 GATEWAY 类型）通过 TCP 协议管理子设备拓扑关系\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（TCP 端口 8091）</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 网关设备认证</li>\n *             <li>{@link #testTopoAdd()} - 添加子设备拓扑关系</li>\n *             <li>{@link #testTopoDelete()} - 删除子设备拓扑关系</li>\n *             <li>{@link #testTopoGet()} - 获取子设备拓扑关系</li>\n *             <li>{@link #testSubDeviceRegister()} - 子设备动态注册</li>\n *             <li>{@link #testPropertyPackPost()} - 批量上报属性（网关 + 子设备）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：TCP 协议是有状态的长连接，认证成功后同一连接上的后续请求无需再携带认证信息\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewayDeviceTcpProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8091;\n    private static final int TIMEOUT_MS = 5000;\n\n    private static Vertx vertx;\n    private static NetClient netClient;\n\n    // ===================== 编解码器 =====================\n\n    /**\n     * 消息序列化器\n     */\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    /**\n     * TCP 帧编解码器\n     */\n    private static final IotTcpFrameCodec FRAME_CODEC = IotTcpFrameCodecFactory.create(\n            new IotTcpConfig.CodecConfig()\n                    .setType(IotTcpCodecTypeEnum.DELIMITER.getType())\n                    .setDelimiter(\"\\\\n\")\n//                    .setType(IotTcpCodecTypeEnum.LENGTH_FIELD.getType())\n//                    .setLengthFieldOffset(0)\n//                    .setLengthFieldLength(4)\n//                    .setLengthAdjustment(0)\n//                    .setInitialBytesToStrip(4)\n//                    .setType(IotTcpCodecTypeEnum.LENGTH_FIELD.getType())\n//                    .setFixedLength(256)\n    );\n\n    // ===================== 网关设备信息（根据实际情况修改，从 iot_device 表查询网关设备） =====================\n\n    private static final String GATEWAY_PRODUCT_KEY = \"m6XcS1ZJ3TW8eC0v\";\n    private static final String GATEWAY_DEVICE_NAME = \"sub-ddd\";\n    private static final String GATEWAY_DEVICE_SECRET = \"b3d62c70f8a4495487ed1d35d61ac2b3\";\n\n    // ===================== 子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String SUB_DEVICE_PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String SUB_DEVICE_NAME = \"chazuo-it\";\n    private static final String SUB_DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    @BeforeAll\n    static void setUp() {\n        vertx = Vertx.vertx();\n        NetClientOptions options = new NetClientOptions()\n                .setConnectTimeout(TIMEOUT_MS)\n                .setIdleTimeout(TIMEOUT_MS);\n        netClient = vertx.createNetClient(options);\n    }\n\n    @AfterAll\n    static void tearDown() {\n        if (netClient != null) {\n            netClient.close();\n        }\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 网关设备认证测试：获取网关设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1. 构建认证消息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\"auth\", authReqDTO);\n\n        // 2. 发送并接收响应\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testAuth][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 拓扑管理测试 =====================\n\n    /**\n     * 添加子设备拓扑关系测试\n     */\n    @Test\n    public void testTopoAdd() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先进行认证\n            IotDeviceMessage authResponse = authenticate(socket);\n            log.info(\"[testTopoAdd][认证响应: {}]\", authResponse);\n\n            // 2.1 构建子设备认证信息\n            IotDeviceAuthReqDTO subAuthInfo = IotDeviceAuthUtils.getAuthInfo(\n                    SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME, SUB_DEVICE_SECRET);\n            IotDeviceAuthReqDTO subDeviceAuth = new IotDeviceAuthReqDTO()\n                    .setClientId(subAuthInfo.getClientId())\n                    .setUsername(subAuthInfo.getUsername())\n                    .setPassword(subAuthInfo.getPassword());\n            // 2.2 构建请求参数\n            IotDeviceTopoAddReqDTO params = new IotDeviceTopoAddReqDTO();\n            params.setSubDevices(Collections.singletonList(subDeviceAuth));\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IdUtil.fastSimpleUUID(),\n                    IotDeviceMessageMethodEnum.TOPO_ADD.getMethod(),\n                    params);\n\n            // 3. 发送并接收响应\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testTopoAdd][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    /**\n     * 删除子设备拓扑关系测试\n     */\n    @Test\n    public void testTopoDelete() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先进行认证\n            IotDeviceMessage authResponse = authenticate(socket);\n            log.info(\"[testTopoDelete][认证响应: {}]\", authResponse);\n\n            // 2. 构建请求参数\n            IotDeviceTopoDeleteReqDTO params = new IotDeviceTopoDeleteReqDTO();\n            params.setSubDevices(Collections.singletonList(\n                    new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME)));\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod(),\n                    params);\n\n            // 3. 发送并接收响应\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testTopoDelete][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    /**\n     * 获取子设备拓扑关系测试\n     */\n    @Test\n    public void testTopoGet() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先进行认证\n            IotDeviceMessage authResponse = authenticate(socket);\n            log.info(\"[testTopoGet][认证响应: {}]\", authResponse);\n\n            // 2. 构建请求参数\n            IotDeviceTopoGetReqDTO params = new IotDeviceTopoGetReqDTO();\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.TOPO_GET.getMethod(),\n                    params);\n\n            // 3. 发送并接收响应\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testTopoGet][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 子设备注册测试 =====================\n\n    /**\n     * 子设备动态注册测试\n     */\n    @Test\n    public void testSubDeviceRegister() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先进行认证\n            IotDeviceMessage authResponse = authenticate(socket);\n            log.info(\"[testSubDeviceRegister][认证响应: {}]\", authResponse);\n\n            // 2. 构建请求参数\n            IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO()\n                    .setProductKey(SUB_DEVICE_PRODUCT_KEY)\n                    .setDeviceName(\"mougezishebei\");\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod(),\n                    Collections.singletonList(subDevice));\n\n            // 3. 发送并接收响应\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testSubDeviceRegister][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 批量上报测试 =====================\n\n    /**\n     * 批量上报属性测试（网关 + 子设备）\n     */\n    @Test\n    public void testPropertyPackPost() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先进行认证\n            IotDeviceMessage authResponse = authenticate(socket);\n            log.info(\"[testPropertyPackPost][认证响应: {}]\", authResponse);\n\n            // 2.1 构建【网关设备】自身属性\n            Map<String, Object> gatewayProperties = MapUtil.<String, Object>builder()\n                    .put(\"temperature\", 25.5)\n                    .build();\n            // 2.2 构建【网关设备】自身事件\n            IotDevicePropertyPackPostReqDTO.EventValue gatewayEvent = new IotDevicePropertyPackPostReqDTO.EventValue()\n                    .setValue(MapUtil.builder().put(\"message\", \"gateway started\").build())\n                    .setTime(System.currentTimeMillis());\n            Map<String, IotDevicePropertyPackPostReqDTO.EventValue> gatewayEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                    .put(\"statusReport\", gatewayEvent)\n                    .build();\n            // 2.3 构建【网关子设备】属性\n            Map<String, Object> subDeviceProperties = MapUtil.<String, Object>builder()\n                    .put(\"power\", 100)\n                    .build();\n            // 2.4 构建【网关子设备】事件\n            IotDevicePropertyPackPostReqDTO.EventValue subDeviceEvent = new IotDevicePropertyPackPostReqDTO.EventValue()\n                    .setValue(MapUtil.builder().put(\"errorCode\", 0).build())\n                    .setTime(System.currentTimeMillis());\n            Map<String, IotDevicePropertyPackPostReqDTO.EventValue> subDeviceEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                    .put(\"healthCheck\", subDeviceEvent)\n                    .build();\n            // 2.5 构建子设备数据\n            IotDevicePropertyPackPostReqDTO.SubDeviceData subDeviceData = new IotDevicePropertyPackPostReqDTO.SubDeviceData()\n                    .setIdentity(new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME))\n                    .setProperties(subDeviceProperties)\n                    .setEvents(subDeviceEvents);\n            // 2.6 构建请求参数\n            IotDevicePropertyPackPostReqDTO params = new IotDevicePropertyPackPostReqDTO();\n            params.setProperties(gatewayProperties);\n            params.setEvents(gatewayEvents);\n            params.setSubDevices(ListUtil.of(subDeviceData));\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod(),\n                    params);\n\n            // 3. 发送并接收响应\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testPropertyPackPost][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 建立 TCP 连接\n     *\n     * @return 连接 Future\n     */\n    private CompletableFuture<NetSocket> connect() {\n        CompletableFuture<NetSocket> future = new CompletableFuture<>();\n        netClient.connect(SERVER_PORT, SERVER_HOST)\n                .onSuccess(future::complete)\n                .onFailure(future::completeExceptionally);\n        return future;\n    }\n\n    /**\n     * 执行网关设备认证\n     *\n     * @param socket TCP 连接\n     * @return 认证响应消息\n     */\n    private IotDeviceMessage authenticate(NetSocket socket) throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\"auth\", authInfo);\n        return sendAndReceive(socket, request);\n    }\n\n    /**\n     * 发送消息并接收响应（复用 IotTcpFrameCodec 编解码逻辑）\n     *\n     * @param socket  TCP 连接\n     * @param request 请求消息\n     * @return 响应消息\n     */\n    private IotDeviceMessage sendAndReceive(NetSocket socket, IotDeviceMessage request) throws Exception {\n        // 1. 使用 FRAME_CODEC 创建解码器（复用 gateway 的拆包逻辑）\n        CompletableFuture<IotDeviceMessage> responseFuture = new CompletableFuture<>();\n        RecordParser parser = FRAME_CODEC.createDecodeParser(buffer -> {\n            try {\n                // 反序列化响应\n                IotDeviceMessage response = SERIALIZER.deserialize(buffer.getBytes());\n                responseFuture.complete(response);\n            } catch (Exception e) {\n                responseFuture.completeExceptionally(e);\n            }\n        });\n        socket.handler(parser);\n\n        // 2.1 序列化 + 帧编码\n        byte[] serializedData = SERIALIZER.serialize(request);\n        Buffer frameData = FRAME_CODEC.encode(serializedData);\n        log.info(\"[sendAndReceive][发送消息: {}，数据长度: {} 字节]\", request.getMethod(), frameData.length());\n        // 2.2 发送请求\n        socket.write(frameData);\n\n        // 3. 等待响应\n        IotDeviceMessage response = responseFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        log.info(\"[sendAndReceive][收到响应，数据长度: {} 字节]\",\n                SERIALIZER.serialize(response).length);\n        return response;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotGatewaySubDeviceTcpProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpCodecTypeEnum;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;\nimport cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodecFactory;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.buffer.Buffer;\nimport io.vertx.core.net.NetClient;\nimport io.vertx.core.net.NetClientOptions;\nimport io.vertx.core.net.NetSocket;\nimport io.vertx.core.parsetools.RecordParser;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * IoT 网关子设备 TCP 协议集成测试（手动测试）\n *\n * <p>测试场景：子设备（IotProductDeviceTypeEnum 的 SUB 类型）通过网关设备代理上报数据\n *\n * <p><b>重要说明：子设备无法直接连接平台，所有请求均由网关设备（Gateway）代为转发。</b>\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（TCP 端口 8091）</li>\n *     <li>确保子设备已通过 {@link IotGatewayDeviceTcpProtocolIntegrationTest#testTopoAdd()} 绑定到网关</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 子设备认证</li>\n *             <li>{@link #testPropertyPost()} - 子设备属性上报（由网关代理转发）</li>\n *             <li>{@link #testEventPost()} - 子设备事件上报（由网关代理转发）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：TCP 协议是有状态的长连接，认证成功后同一连接上的后续请求无需再携带认证信息\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewaySubDeviceTcpProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8091;\n    private static final int TIMEOUT_MS = 5000;\n\n    private static Vertx vertx;\n    private static NetClient netClient;\n\n    // ===================== 编解码器 =====================\n\n    /**\n     * 消息序列化器\n     */\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    /**\n     * TCP 帧编解码器\n     */\n    private static final IotTcpFrameCodec FRAME_CODEC = IotTcpFrameCodecFactory.create(\n            new IotTcpConfig.CodecConfig()\n                    .setType(IotTcpCodecTypeEnum.DELIMITER.getType())\n                    .setDelimiter(\"\\\\n\")\n//                    .setType(IotTcpCodecTypeEnum.LENGTH_FIELD.getType())\n//                    .setLengthFieldOffset(0)\n//                    .setLengthFieldLength(4)\n//                    .setLengthAdjustment(0)\n//                    .setInitialBytesToStrip(4)\n//                    .setType(IotTcpCodecTypeEnum.LENGTH_FIELD.getType())\n//                    .setFixedLength(256)\n    );\n\n    // ===================== 网关子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String DEVICE_NAME = \"chazuo-it\";\n    private static final String DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    @BeforeAll\n    static void setUp() {\n        vertx = Vertx.vertx();\n        NetClientOptions options = new NetClientOptions()\n                .setConnectTimeout(TIMEOUT_MS)\n                .setIdleTimeout(TIMEOUT_MS);\n        netClient = vertx.createNetClient(options);\n    }\n\n    @AfterAll\n    static void tearDown() {\n        if (netClient != null) {\n            netClient.close();\n        }\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 子设备认证测试：获取子设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1. 构建认证消息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\"auth\", authReqDTO);\n\n        // 2. 发送并接收响应\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testAuth][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 子设备属性上报测试 =====================\n\n    /**\n     * 子设备属性上报测试\n     */\n    @Test\n    public void testPropertyPost() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先进行认证\n            IotDeviceMessage authResponse = authenticate(socket);\n            log.info(\"[testPropertyPost][认证响应: {}]\", authResponse);\n\n            // 2. 构建属性上报消息\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),\n                    IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                            .put(\"power\", 100)\n                            .put(\"status\", \"online\")\n                            .put(\"temperature\", 36.5)\n                            .build()));\n            log.info(\"[testPropertyPost][子设备属性上报 - 请求实际由 Gateway 代为转发]\");\n\n            // 3. 发送并接收响应\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testPropertyPost][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 子设备事件上报测试 =====================\n\n    /**\n     * 子设备事件上报测试\n     */\n    @Test\n    public void testEventPost() throws Exception {\n        NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        try {\n            // 1. 先进行认证\n            IotDeviceMessage authResponse = authenticate(socket);\n            log.info(\"[testEventPost][认证响应: {}]\", authResponse);\n\n            // 2. 构建事件上报消息\n            IotDeviceMessage request = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                    IotDeviceEventPostReqDTO.of(\n                            \"alarm\",\n                            MapUtil.<String, Object>builder()\n                                    .put(\"level\", \"warning\")\n                                    .put(\"message\", \"temperature too high\")\n                                    .put(\"threshold\", 40)\n                                    .put(\"current\", 42)\n                                    .build(),\n                            System.currentTimeMillis()));\n            log.info(\"[testEventPost][子设备事件上报 - 请求实际由 Gateway 代为转发]\");\n\n            // 3. 发送并接收响应\n            IotDeviceMessage response = sendAndReceive(socket, request);\n            log.info(\"[testEventPost][响应消息: {}]\", response);\n        } finally {\n            socket.close();\n        }\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 建立 TCP 连接\n     *\n     * @return 连接 Future\n     */\n    private CompletableFuture<NetSocket> connect() {\n        CompletableFuture<NetSocket> future = new CompletableFuture<>();\n        netClient.connect(SERVER_PORT, SERVER_HOST)\n                .onSuccess(future::complete)\n                .onFailure(future::completeExceptionally);\n        return future;\n    }\n\n    /**\n     * 执行子设备认证\n     *\n     * @param socket TCP 连接\n     * @return 认证响应消息\n     */\n    private IotDeviceMessage authenticate(NetSocket socket) throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\"auth\", authInfo);\n        return sendAndReceive(socket, request);\n    }\n\n    /**\n     * 发送消息并接收响应（复用 IotTcpFrameCodec 编解码逻辑）\n     *\n     * @param socket  TCP 连接\n     * @param request 请求消息\n     * @return 响应消息\n     */\n    private IotDeviceMessage sendAndReceive(NetSocket socket, IotDeviceMessage request) throws Exception {\n        // 1. 使用 FRAME_CODEC 创建解码器（复用 gateway 的拆包逻辑）\n        CompletableFuture<IotDeviceMessage> responseFuture = new CompletableFuture<>();\n        RecordParser parser = FRAME_CODEC.createDecodeParser(buffer -> {\n            try {\n                // 反序列化响应\n                IotDeviceMessage response = SERIALIZER.deserialize(buffer.getBytes());\n                responseFuture.complete(response);\n            } catch (Exception e) {\n                responseFuture.completeExceptionally(e);\n            }\n        });\n        socket.handler(parser);\n\n        // 2.1 序列化 + 帧编码\n        byte[] serializedData = SERIALIZER.serialize(request);\n        Buffer frameData = FRAME_CODEC.encode(serializedData);\n        log.info(\"[sendAndReceive][发送消息: {}，数据长度: {} 字节]\", request.getMethod(), frameData.length());\n        // 2.2 发送请求\n        socket.write(frameData);\n\n        // 3. 等待响应\n        IotDeviceMessage response = responseFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        log.info(\"[sendAndReceive][收到响应，数据长度: {} 字节]\",\n                SERIALIZER.serialize(response).length);\n        return response;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/tcp-binary-packet-examples.md",
    "content": "# TCP 二进制协议数据包格式说明\n\n## 1. 协议概述\n\nTCP 二进制协议是一种高效的自定义协议格式，采用紧凑的二进制格式传输数据，适用于对带宽和性能要求较高的 IoT 场景。\n\n### 1.1 协议特点\n\n- **高效传输**：完全二进制格式，减少数据传输量\n- **版本控制**：内置协议版本号，支持协议升级\n- **类型安全**：明确的消息类型标识\n- **简洁设计**：去除冗余字段，协议更加精简\n- **兼容性**：与现有 `IotDeviceMessage` 接口完全兼容\n\n## 2. 协议格式\n\n### 2.1 整体结构\n\n```\n+--------+--------+--------+---------------------------+--------+--------+\n| 魔术字 | 版本号 | 消息类型|         消息长度(4字节)          |\n+--------+--------+--------+---------------------------+--------+--------+\n|           消息 ID 长度(2字节)        |      消息 ID (变长字符串)         |\n+--------+--------+--------+--------+--------+--------+--------+--------+\n|           方法名长度(2字节)        |      方法名(变长字符串)         |\n+--------+--------+--------+--------+--------+--------+--------+--------+\n|                        消息体数据(变长)                              |\n+--------+--------+--------+--------+--------+--------+--------+--------+\n```\n\n### 2.2 字段详细说明\n\n| 字段 | 长度 | 类型 | 说明 |\n|------|------|------|------|\n| 魔术字 | 1字节 | byte | `0x7E` - 协议识别标识，用于数据同步 |\n| 版本号 | 1字节 | byte | `0x01` - 协议版本号，支持版本控制 |\n| 消息类型 | 1字节 | byte | `0x01`=请求, `0x02`=响应 |\n| 消息长度 | 4字节 | int | 整个消息的总长度（包含头部） |\n| 消息 ID 长度 | 2字节 | short | 消息 ID 字符串的字节长度 |\n| 消息 ID | 变长 | string | 消息唯一标识符（UTF-8编码） |\n| 方法名长度 | 2字节 | short | 方法名字符串的字节长度 |\n| 方法名 | 变长 | string | 消息方法名（UTF-8编码） |\n| 消息体 | 变长 | binary | 根据消息类型的不同数据格式 |\n\n**⚠️ 重要说明**：deviceId 不包含在协议中，由服务器根据连接上下文自动设置\n\n### 2.3 协议常量定义\n\n```java\n// 协议标识\nprivate static final byte MAGIC_NUMBER = (byte) 0x7E;\nprivate static final byte PROTOCOL_VERSION = (byte) 0x01;\n\n// 消息类型\nprivate static final byte REQUEST = (byte) 0x01;   // 请求消息\nprivate static final byte RESPONSE = (byte) 0x02;  // 响应消息\n\n// 协议长度\nprivate static final int HEADER_FIXED_LENGTH = 7;      // 固定头部长度\nprivate static final int MIN_MESSAGE_LENGTH = 11;      // 最小消息长度\n```\n\n## 3. 消息类型和格式\n\n### 3.1 请求消息 (REQUEST - 0x01)\n\n请求消息用于设备向服务器发送数据或请求。\n\n#### 3.1.1 消息体格式\n```\n消息体 = params 数据(JSON格式)\n```\n\n#### 3.1.2 示例：设备认证请求\n\n**消息内容：**\n- 消息 ID: `auth_1704067200000_123`\n- 方法名: `auth`\n- 参数: `{\"clientId\":\"device_001\",\"username\":\"productKey_deviceName\",\"password\":\"device_password\"}`\n\n**二进制数据包结构：**\n```\n7E                              // 魔术字 (0x7E)\n01                              // 版本号 (0x01)\n01                              // 消息类型 (REQUEST)\n00 00 00 89                     // 消息长度 (137字节)\n00 19                           // 消息 ID 长度 (25字节)\n61 75 74 68 5F 31 37 30 34 30   // 消息 ID: \"auth_1704067200000_123\"\n36 37 32 30 30 30 30 30 5F 31 \n32 33\n00 04                           // 方法名长度 (4字节)\n61 75 74 68                     // 方法名: \"auth\"\n7B 22 63 6C 69 65 6E 74 49 64   // JSON参数数据\n22 3A 22 64 65 76 69 63 65 5F   // {\"clientId\":\"device_001\",\n30 30 31 22 2C 22 75 73 65 72   //  \"username\":\"productKey_deviceName\",\n6E 61 6D 65 22 3A 22 70 72 6F   //  \"password\":\"device_password\"}\n64 75 63 74 4B 65 79 5F 64 65\n76 69 63 65 4E 61 6D 65 22 2C\n22 70 61 73 73 77 6F 72 64 22\n3A 22 64 65 76 69 63 65 5F 70\n61 73 73 77 6F 72 64 22 7D\n```\n\n#### 3.1.3 示例：属性数据上报\n\n**消息内容：**\n- 消息 ID: `property_1704067200000_456`\n- 方法名: `thing.property.post`\n- 参数: `{\"temperature\":25.5,\"humidity\":60.2,\"pressure\":1013.25}`\n\n### 3.2 响应消息 (RESPONSE - 0x02)\n\n响应消息用于服务器向设备回复请求结果。\n\n#### 3.2.1 消息体格式\n```\n消息体 = 响应码(4字节) + 响应消息长度(2字节) + 响应消息(UTF-8) + 响应数据(JSON)\n```\n\n#### 3.2.2 字段说明\n\n| 字段 | 长度 | 类型 | 说明 |\n|------|------|------|------|\n| 响应码 | 4字节 | int | HTTP状态码风格，0=成功，其他=错误 |\n| 响应消息长度 | 2字节 | short | 响应消息字符串的字节长度 |\n| 响应消息 | 变长 | string | 响应提示信息（UTF-8编码） |\n| 响应数据 | 变长 | binary | JSON格式的响应数据（可选） |\n\n#### 3.2.3 示例：认证成功响应\n\n**消息内容：**\n- 消息 ID: `auth_response_1704067200000_123`\n- 方法名: `auth`\n- 响应码: `0`\n- 响应消息: `认证成功`\n- 响应数据: `{\"success\":true,\"message\":\"认证成功\"}`\n\n**二进制数据包结构：**\n```\n7E                              // 魔术字 (0x7E)\n01                              // 版本号 (0x01)\n02                              // 消息类型 (RESPONSE)\n00 00 00 A4                     // 消息长度 (164字节)\n00 22                           // 消息 ID 长度 (34字节)\n61 75 74 68 5F 72 65 73 70 6F   // 消息 ID: \"auth_response_1704067200000_123\"\n6E 73 65 5F 31 37 30 34 30 36\n37 32 30 30 30 30 30 5F 31 32\n33\n00 04                           // 方法名长度 (4字节)\n61 75 74 68                     // 方法名: \"auth\"\n00 00 00 00                     // 响应码 (0 = 成功)\n00 0C                           // 响应消息长度 (12字节)\nE8 AE A4 E8 AF 81 E6 88 90 E5   // 响应消息: \"认证成功\" (UTF-8)\n8A 9F\n7B 22 73 75 63 63 65 73 73 22   // JSON响应数据\n3A 74 72 75 65 2C 22 6D 65 73   // {\"success\":true,\"message\":\"认证成功\"}\n73 61 67 65 22 3A 22 E8 AE A4\nE8 AF 81 E6 88 90 E5 8A 9F 22\n7D\n```\n\n## 4. 编解码器标识\n\n```java\npublic static final String TYPE = \"TCP_BINARY\";\n```\n\n## 5. 协议优势\n\n- **数据紧凑**：二进制格式，相比 JSON 减少 30-50% 的数据量\n- **解析高效**：直接二进制操作，减少字符串转换开销\n- **类型安全**：明确的消息类型和字段定义\n- **设计简洁**：去除冗余字段，协议更加精简高效\n- **版本控制**：内置版本号支持协议升级\n\n## 6. 与 JSON 协议对比\n\n| 特性   | 二进制协议       | JSON协议 |\n|------|-------------|--------|\n| 数据大小 | 小（节省30-50%） | 大      |\n| 解析性能 | 高           | 中等     |\n| 网络开销 | 低           | 高      |\n| 可读性  | 差           | 优秀     |\n| 调试难度 | 高           | 低      |\n| 扩展性  | 良好          | 优秀     |\n\n**推荐场景**：\n- ✅ **高频数据传输**：传感器数据实时上报\n- ✅ **带宽受限环境**：移动网络、卫星通信\n- ✅ **性能要求高**：需要低延迟、高吞吐的场景\n- ✅ **设备资源有限**：嵌入式设备、低功耗设备\n- ❌ **开发调试阶段**：调试困难，建议使用 JSON 协议\n- ❌ **快速原型开发**：开发效率低\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/tcp-json-packet-examples.md",
    "content": "# TCP JSON 格式协议说明\n\n## 1. 协议概述\n\nTCP JSON 格式协议采用纯 JSON 格式进行数据传输，具有以下特点：\n\n- **标准化**：使用标准 JSON 格式，易于解析和处理\n- **可读性**：人类可读，便于调试和维护\n- **扩展性**：可以轻松添加新字段，向后兼容\n- **跨平台**：JSON 格式支持所有主流编程语言\n- **安全优化**：移除冗余的 deviceId 字段，提高安全性\n\n## 2. 消息格式\n\n### 2.1 基础消息结构\n\n```json\n{\n  \"id\": \"消息唯一标识\",\n  \"method\": \"消息方法\",\n  \"params\": {\n    // 请求参数\n  },\n  \"data\": {\n    // 响应数据\n  },\n  \"code\": 响应码,\n  \"msg\": \"响应消息\",\n  \"timestamp\": 时间戳\n}\n```\n\n**⚠️ 重要说明**：\n- **不包含 deviceId 字段**：由服务器通过 TCP 连接上下文自动确定设备 ID\n- **避免伪造攻击**：防止设备伪造其他设备的 ID 发送消息\n\n### 2.2 字段详细说明\n\n| 字段名 | 类型 | 必填 | 用途 | 说明 |\n|--------|------|------|------|------|\n| id | String | 是 | 所有消息 | 消息唯一标识 |\n| method | String | 是 | 所有消息 | 消息方法，如 `auth`、`thing.property.post` |\n| params | Object | 否 | 请求消息 | 请求参数，具体内容根据method而定 |\n| data | Object | 否 | 响应消息 | 响应数据，服务器返回的结果数据 |\n| code | Integer | 否 | 响应消息 | 响应码，0=成功，其他=错误 |\n| msg | String | 否 | 响应消息 | 响应提示信息 |\n| timestamp | Long | 是 | 所有消息 | 时间戳（毫秒），编码时自动生成 |\n\n### 2.3 消息分类\n\n#### 2.3.1 请求消息（上行）\n- **特征**：包含 `params` 字段，不包含 `code`、`msg` 字段\n- **方向**：设备 → 服务器\n- **用途**：设备认证、数据上报、状态更新等\n\n#### 2.3.2 响应消息（下行）\n- **特征**：包含 `code`、`msg` 字段，可能包含 `data` 字段\n- **方向**：服务器 → 设备  \n- **用途**：认证结果、指令响应、错误提示等\n\n## 3. 消息示例\n\n### 3.1 设备认证 (auth)\n\n#### 认证请求格式\n**消息方向**：设备 → 服务器\n\n```json\n{\n  \"id\": \"auth_1704067200000_123\",\n  \"method\": \"auth\",\n  \"params\": {\n    \"clientId\": \"device_001\",\n    \"username\": \"productKey_deviceName\",\n    \"password\": \"设备密码\"\n  },\n  \"timestamp\": 1704067200000\n}\n```\n\n**认证参数说明：**\n\n| 字段名 | 类型 | 必填 | 说明 |\n|--------|------|------|------|\n| clientId | String | 是 | 客户端唯一标识，用于连接管理 |\n| username | String | 是 | 设备用户名，格式为 `productKey_deviceName` |\n| password | String | 是 | 设备密码，在设备管理平台配置 |\n\n#### 认证响应格式\n**消息方向**：服务器 → 设备\n\n**认证成功响应：**\n```json\n{\n  \"id\": \"response_auth_1704067200000_123\",\n  \"method\": \"auth\",\n  \"data\": {\n    \"success\": true,\n    \"message\": \"认证成功\"\n  },\n  \"code\": 0,\n  \"msg\": \"认证成功\",\n  \"timestamp\": 1704067200001\n}\n```\n\n**认证失败响应：**\n```json\n{\n  \"id\": \"response_auth_1704067200000_123\", \n  \"method\": \"auth\",\n  \"data\": {\n    \"success\": false,\n    \"message\": \"认证失败：用户名或密码错误\"\n  },\n  \"code\": 401,\n  \"msg\": \"认证失败\",\n  \"timestamp\": 1704067200001\n}\n```\n\n### 3.2 属性数据上报 (thing.property.post)\n\n**消息方向**：设备 → 服务器\n\n**示例：温度传感器数据上报**\n```json\n{\n  \"id\": \"property_1704067200000_456\",\n  \"method\": \"thing.property.post\",\n  \"params\": {\n    \"temperature\": 25.5,\n    \"humidity\": 60.2,\n    \"pressure\": 1013.25,\n    \"battery\": 85,\n    \"signal_strength\": -65\n  },\n  \"timestamp\": 1704067200000\n}\n```\n\n### 3.3 设备状态更新 (thing.state.update)\n\n**消息方向**：设备 → 服务器\n\n**示例：心跳请求**\n```json\n{\n  \"id\": \"heartbeat_1704067200000_321\",\n  \"method\": \"thing.state.update\",\n  \"params\": {\n    \"state\": \"online\",\n    \"uptime\": 86400,\n    \"memory_usage\": 65.2,\n    \"cpu_usage\": 12.8\n  },\n  \"timestamp\": 1704067200000\n}\n```\n\n## 4. 编解码器标识\n\n```java\npublic static final String TYPE = \"TCP_JSON\";\n```\n\n## 5. 协议优势\n\n- **开发效率高**：JSON 格式，开发和调试简单\n- **跨语言支持**：所有主流语言都支持 JSON\n- **可读性优秀**：可以直接查看消息内容\n- **扩展性强**：可以轻松添加新字段\n- **安全性高**：移除 deviceId 字段，防止伪造攻击\n\n## 6. 与二进制协议对比\n\n| 特性 | JSON协议 | 二进制协议 |\n|------|----------|------------|\n| 开发难度 | 低 | 高 |\n| 调试难度 | 低 | 高 |\n| 可读性 | 优秀 | 差 |\n| 数据大小 | 中等 | 小（节省30-50%） |\n| 解析性能 | 中等 | 高 |\n| 学习成本 | 低 | 高 |\n\n**推荐场景**：\n- ✅ **开发调试阶段**：调试友好，开发效率高\n- ✅ **快速原型开发**：实现简单，快速迭代\n- ✅ **多语言集成**：广泛的语言支持\n- ❌ **高频数据传输**：建议使用二进制协议\n- ❌ **带宽受限环境**：建议使用二进制协议"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotDirectDeviceUdpProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.udp;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.net.DatagramPacket;\nimport java.net.DatagramSocket;\nimport java.net.InetAddress;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * IoT 直连设备 UDP 协议集成测试（手动测试）\n *\n * <p>测试场景：直连设备（IotProductDeviceTypeEnum 的 DIRECT 类型）通过 UDP 协议直接连接平台\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（UDP 端口 8093）</li>\n *     <li>运行 {@link #testAuth()} 获取设备 token，将返回的 token 粘贴到 {@link #TOKEN} 常量</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testPropertyPost()} - 设备属性上报</li>\n *             <li>{@link #testEventPost()} - 设备事件上报</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：UDP 协议是无状态的，每次请求需要在 params 中携带 token（与 HTTP 通过 Header 传递不同）\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotDirectDeviceUdpProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8093;\n    private static final int TIMEOUT_MS = 5000;\n\n    // ===================== 序列化器 =====================\n\n    /**\n     * 消息序列化器\n     */\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    // ===================== 直连设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String PRODUCT_KEY = \"4aymZgOTOOCrDKRT\";\n    private static final String DEVICE_NAME = \"small\";\n    private static final String DEVICE_SECRET = \"0baa4c2ecc104ae1a26b4070c218bdf3\";\n\n    /**\n     * 直连设备 Token：从 {@link #testAuth()} 方法获取后，粘贴到这里\n     */\n    private static final String TOKEN = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiNGF5bVpnT1RPT0NyREtSVCIsImV4cCI6MTc3MDUyNTA0MywiZGV2aWNlTmFtZSI6InNtYWxsIn0.W9Mo-Oe1ZNLDkINndKieUeW1XhDzhVp0W0zTAwO6hJM\";\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 认证测试：获取设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1. 构建认证消息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\"auth\", authReqDTO);\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testAuth][响应消息: {}]\", response);\n        log.info(\"[testAuth][请将返回的 token 复制到 TOKEN 常量中]\");\n    }\n\n    // ===================== 动态注册测试 =====================\n\n    /**\n     * 直连设备动态注册测试（一型一密）\n     * <p>\n     * 使用产品密钥（productSecret）验证身份，成功后返回设备密钥（deviceSecret）\n     * <p>\n     * 注意：此接口不需要认证\n     */\n    @Test\n    public void testDeviceRegister() throws Exception {\n        // 1. 构建注册消息\n        String deviceName = \"test-udp-\" + System.currentTimeMillis();\n        String productSecret = \"test-product-secret\"; // 替换为实际的 productSecret\n        String sign = IotProductAuthUtils.buildSign(PRODUCT_KEY, deviceName, productSecret);\n        IotDeviceRegisterReqDTO registerReqDTO = new IotDeviceRegisterReqDTO()\n                .setProductKey(PRODUCT_KEY)\n                .setDeviceName(deviceName)\n                .setSign(sign);\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), registerReqDTO);\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testDeviceRegister][响应消息: {}]\", response);\n        log.info(\"[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]\");\n    }\n\n    // ===================== 直连设备属性上报测试 =====================\n\n    /**\n     * 属性上报测试\n     */\n    @Test\n    public void testPropertyPost() throws Exception {\n        // 1. 构建属性上报消息\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),\n                withToken(IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                        .put(\"width\", 1)\n                        .put(\"height\", \"2\")\n                        .build())));\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testPropertyPost][响应消息: {}]\", response);\n    }\n\n    // ===================== 直连设备事件上报测试 =====================\n\n    /**\n     * 事件上报测试\n     */\n    @Test\n    public void testEventPost() throws Exception {\n        // 1. 构建事件上报消息\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                withToken(IotDeviceEventPostReqDTO.of(\n                        \"eat\",\n                        MapUtil.<String, Object>builder().put(\"rice\", 3).build(),\n                        System.currentTimeMillis())));\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testEventPost][响应消息: {}]\", response);\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 构建带 token 的 params\n     * <p>\n     * 返回格式：{token: \"xxx\", body: params}\n     * - token：JWT 令牌\n     * - body：实际请求内容（可以是 Map、List 或其他类型）\n     *\n     * @param params 原始参数（Map、List 或对象）\n     * @return 包含 token 和 body 的 Map\n     */\n    private Map<String, Object> withToken(Object params) {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"token\", TOKEN);\n        result.put(\"body\", params);\n        return result;\n    }\n\n    /**\n     * 发送 UDP 消息并接收响应\n     *\n     * @param request 请求消息\n     * @return 响应消息\n     */\n    private IotDeviceMessage sendAndReceive(IotDeviceMessage request) throws Exception {\n        // 1. 序列化请求\n        byte[] payload = SERIALIZER.serialize(request);\n        log.info(\"[sendAndReceive][发送消息: {}，数据长度: {} 字节]\", request.getMethod(), payload.length);\n\n        // 2. 发送请求\n        try (DatagramSocket socket = new DatagramSocket()) {\n            socket.setSoTimeout(TIMEOUT_MS);\n            InetAddress address = InetAddress.getByName(SERVER_HOST);\n            DatagramPacket sendPacket = new DatagramPacket(payload, payload.length, address, SERVER_PORT);\n            socket.send(sendPacket);\n\n            // 3. 接收响应\n            byte[] receiveData = new byte[4096];\n            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);\n            try {\n                socket.receive(receivePacket);\n                byte[] responseBytes = new byte[receivePacket.getLength()];\n                System.arraycopy(receivePacket.getData(), 0, responseBytes, 0, receivePacket.getLength());\n                log.info(\"[sendAndReceive][收到响应，数据长度: {} 字节]\", responseBytes.length);\n                return SERIALIZER.deserialize(responseBytes);\n            } catch (java.net.SocketTimeoutException e) {\n                log.warn(\"[sendAndReceive][接收响应超时]\");\n                return null;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotGatewayDeviceUdpProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.udp;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.net.DatagramPacket;\nimport java.net.DatagramSocket;\nimport java.net.InetAddress;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * IoT 网关设备 UDP 协议集成测试（手动测试）\n *\n * <p>测试场景：网关设备（IotProductDeviceTypeEnum 的 GATEWAY 类型）通过 UDP 协议管理子设备拓扑关系\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（UDP 端口 8093）</li>\n *     <li>运行 {@link #testAuth()} 获取网关设备 token，将返回的 token 粘贴到 {@link #GATEWAY_TOKEN} 常量</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testTopoAdd()} - 添加子设备拓扑关系</li>\n *             <li>{@link #testTopoDelete()} - 删除子设备拓扑关系</li>\n *             <li>{@link #testTopoGet()} - 获取子设备拓扑关系</li>\n *             <li>{@link #testSubDeviceRegister()} - 子设备动态注册</li>\n *             <li>{@link #testPropertyPackPost()} - 批量上报属性（网关 + 子设备）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：UDP 协议是无状态的，每次请求需要在 params 中携带 token（与 HTTP 通过 Header 传递不同）\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewayDeviceUdpProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8093;\n    private static final int TIMEOUT_MS = 5000;\n\n    // ===================== 序列化器 =====================\n\n    /**\n     * 消息序列化器\n     */\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    // ===================== 网关设备信息（根据实际情况修改，从 iot_device 表查询网关设备） =====================\n\n    private static final String GATEWAY_PRODUCT_KEY = \"m6XcS1ZJ3TW8eC0v\";\n    private static final String GATEWAY_DEVICE_NAME = \"sub-ddd\";\n    private static final String GATEWAY_DEVICE_SECRET = \"b3d62c70f8a4495487ed1d35d61ac2b3\";\n\n    /**\n     * 网关设备 Token：从 {@link #testAuth()} 方法获取后，粘贴到这里\n     */\n    private static final String GATEWAY_TOKEN = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoibTZYY1MxWkozVFc4ZUMwdiIsImV4cCI6MTc2OTk1NDcxNSwiZGV2aWNlTmFtZSI6InN1Yi1kZGQifQ.Vg5iateNrpg0FVQI2eJomggxrYXGpwug8wsz9BsVr5w\";\n\n    // ===================== 子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n    private static final String SUB_DEVICE_PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String SUB_DEVICE_NAME = \"chazuo-it\";\n    private static final String SUB_DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 网关设备认证测试：获取网关设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1. 构建认证消息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\"auth\", authReqDTO);\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testAuth][响应消息: {}]\", response);\n        log.info(\"[testAuth][请将返回的 token 复制到 GATEWAY_TOKEN 常量中]\");\n    }\n\n    // ===================== 拓扑管理测试 =====================\n\n    /**\n     * 添加子设备拓扑关系测试\n     */\n    @Test\n    public void testTopoAdd() throws Exception {\n        // 1. 构建子设备认证信息\n        IotDeviceAuthReqDTO subAuthInfo = IotDeviceAuthUtils.getAuthInfo(\n                SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME, SUB_DEVICE_SECRET);\n        IotDeviceAuthReqDTO subDeviceAuth = new IotDeviceAuthReqDTO()\n                .setClientId(subAuthInfo.getClientId())\n                .setUsername(subAuthInfo.getUsername())\n                .setPassword(subAuthInfo.getPassword());\n        IotDeviceTopoAddReqDTO params = new IotDeviceTopoAddReqDTO();\n        params.setSubDevices(Collections.singletonList(subDeviceAuth));\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.TOPO_ADD.getMethod(), withToken(params));\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testTopoAdd][响应消息: {}]\", response);\n    }\n\n    /**\n     * 删除子设备拓扑关系测试\n     */\n    @Test\n    public void testTopoDelete() throws Exception {\n        // 1. 构建请求参数\n        IotDeviceTopoDeleteReqDTO params = new IotDeviceTopoDeleteReqDTO();\n        params.setSubDevices(Collections.singletonList(\n                new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME)));\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod(), withToken(params));\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testTopoDelete][响应消息: {}]\", response);\n    }\n\n    /**\n     * 获取子设备拓扑关系测试\n     */\n    @Test\n    public void testTopoGet() throws Exception {\n        // 1. 构建请求参数\n        IotDeviceTopoGetReqDTO params = new IotDeviceTopoGetReqDTO();\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.TOPO_GET.getMethod(), withToken(params));\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testTopoGet][响应消息: {}]\", response);\n    }\n\n    // ===================== 子设备注册测试 =====================\n\n    /**\n     * 子设备动态注册测试\n     */\n    @Test\n    public void testSubDeviceRegister() throws Exception {\n        // 1. 构建请求参数\n        IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO();\n        subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY);\n        subDevice.setDeviceName(\"mougezishebei\");\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod(),\n                withToken(Collections.singletonList(subDevice)));\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testSubDeviceRegister][响应消息: {}]\", response);\n    }\n\n    // ===================== 批量上报测试 =====================\n\n    /**\n     * 批量上报属性测试（网关 + 子设备）\n     */\n    @Test\n    public void testPropertyPackPost() throws Exception {\n        // 1.1 构建【网关设备】自身属性\n        Map<String, Object> gatewayProperties = MapUtil.<String, Object>builder()\n                .put(\"temperature\", 25.5)\n                .build();\n        // 1.2 构建【网关设备】自身事件\n        IotDevicePropertyPackPostReqDTO.EventValue gatewayEvent = new IotDevicePropertyPackPostReqDTO.EventValue();\n        gatewayEvent.setValue(MapUtil.builder().put(\"message\", \"gateway started\").build());\n        gatewayEvent.setTime(System.currentTimeMillis());\n        Map<String, IotDevicePropertyPackPostReqDTO.EventValue> gatewayEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                .put(\"statusReport\", gatewayEvent)\n                .build();\n        // 1.3 构建【网关子设备】属性\n        Map<String, Object> subDeviceProperties = MapUtil.<String, Object>builder()\n                .put(\"power\", 100)\n                .build();\n        // 1.4 构建【网关子设备】事件\n        IotDevicePropertyPackPostReqDTO.EventValue subDeviceEvent = new IotDevicePropertyPackPostReqDTO.EventValue();\n        subDeviceEvent.setValue(MapUtil.builder().put(\"errorCode\", 0).build());\n        subDeviceEvent.setTime(System.currentTimeMillis());\n        Map<String, IotDevicePropertyPackPostReqDTO.EventValue> subDeviceEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                .put(\"healthCheck\", subDeviceEvent)\n                .build();\n        // 1.5 构建子设备数据\n        IotDevicePropertyPackPostReqDTO.SubDeviceData subDeviceData = new IotDevicePropertyPackPostReqDTO.SubDeviceData();\n        subDeviceData.setIdentity(new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME));\n        subDeviceData.setProperties(subDeviceProperties);\n        subDeviceData.setEvents(subDeviceEvents);\n        // 1.6 构建请求参数\n        IotDevicePropertyPackPostReqDTO params = new IotDevicePropertyPackPostReqDTO();\n        params.setProperties(gatewayProperties);\n        params.setEvents(gatewayEvents);\n        params.setSubDevices(ListUtil.of(subDeviceData));\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod(), withToken(params));\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testPropertyPackPost][响应消息: {}]\", response);\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 构建带 token 的 params\n     */\n    private Map<String, Object> withToken(Object params) {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"token\", GATEWAY_TOKEN);\n        result.put(\"body\", params);\n        return result;\n    }\n\n    /**\n     * 发送 UDP 消息并接收响应\n     */\n    private IotDeviceMessage sendAndReceive(IotDeviceMessage request) throws Exception {\n        byte[] payload = SERIALIZER.serialize(request);\n        log.info(\"[sendAndReceive][发送消息: {}，数据长度: {} 字节]\", request.getMethod(), payload.length);\n\n        try (DatagramSocket socket = new DatagramSocket()) {\n            socket.setSoTimeout(TIMEOUT_MS);\n            InetAddress address = InetAddress.getByName(SERVER_HOST);\n            DatagramPacket sendPacket = new DatagramPacket(payload, payload.length, address, SERVER_PORT);\n            socket.send(sendPacket);\n\n            byte[] receiveData = new byte[4096];\n            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);\n            try {\n                socket.receive(receivePacket);\n                byte[] responseBytes = new byte[receivePacket.getLength()];\n                System.arraycopy(receivePacket.getData(), 0, responseBytes, 0, receivePacket.getLength());\n                return SERIALIZER.deserialize(responseBytes);\n            } catch (java.net.SocketTimeoutException e) {\n                log.warn(\"[sendAndReceive][接收响应超时]\");\n                return null;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotGatewaySubDeviceUdpProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.udp;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.net.DatagramPacket;\nimport java.net.DatagramSocket;\nimport java.net.InetAddress;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * IoT 网关子设备 UDP 协议集成测试（手动测试）\n *\n * <p>测试场景：子设备（IotProductDeviceTypeEnum 的 SUB 类型）通过网关设备代理上报数据\n *\n * <p><b>重要说明：子设备无法直接连接平台，所有请求均由网关设备（Gateway）代为转发。</b>\n * <p>网关设备转发子设备请求时，Token 使用子设备自己的信息。\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（UDP 端口 8093）</li>\n *     <li>确保子设备已通过 {@link IotGatewayDeviceUdpProtocolIntegrationTest#testTopoAdd()} 绑定到网关</li>\n *     <li>运行 {@link #testAuth()} 获取子设备 token，将返回的 token 粘贴到 {@link #TOKEN} 常量</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testPropertyPost()} - 子设备属性上报（由网关代理转发）</li>\n *             <li>{@link #testEventPost()} - 子设备事件上报（由网关代理转发）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：UDP 协议是无状态的，每次请求需要在 params 中携带 token（与 HTTP 通过 Header 传递不同）\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewaySubDeviceUdpProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8093;\n    private static final int TIMEOUT_MS = 5000;\n\n    // ===================== 序列化器 =====================\n\n    /**\n     * 消息序列化器\n     */\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    // ===================== 网关子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String DEVICE_NAME = \"chazuo-it\";\n    private static final String DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    /**\n     * 网关子设备 Token：从 {@link #testAuth()} 方法获取后，粘贴到这里\n     */\n    private static final String TOKEN = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiakF1ZkVNVEYxVzZ3blBobiIsImV4cCI6MTc2OTk1NDY3OSwiZGV2aWNlTmFtZSI6ImNoYXp1by1pdCJ9.jfbUAoU0xkJl4UvO-NUvcJ6yITPRgUjQ4MKATPuwneg\";\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 子设备认证测试：获取子设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1. 构建认证消息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\"auth\", authReqDTO);\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testAuth][响应消息: {}]\", response);\n        log.info(\"[testAuth][请将返回的 token 复制到 TOKEN 常量中]\");\n    }\n\n    // ===================== 子设备属性上报测试 =====================\n\n    /**\n     * 子设备属性上报测试\n     */\n    @Test\n    public void testPropertyPost() throws Exception {\n        // 1. 构建属性上报消息\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),\n                withToken(IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                        .put(\"power\", 100)\n                        .put(\"status\", \"online\")\n                        .put(\"temperature\", 36.5)\n                        .build())));\n        log.info(\"[testPropertyPost][子设备属性上报 - 请求实际由 Gateway 代为转发]\");\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testPropertyPost][响应消息: {}]\", response);\n    }\n\n    // ===================== 子设备事件上报测试 =====================\n\n    /**\n     * 子设备事件上报测试\n     */\n    @Test\n    public void testEventPost() throws Exception {\n        // 1. 构建事件上报消息\n        IotDeviceMessage request = IotDeviceMessage.requestOf(\n                IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                withToken(IotDeviceEventPostReqDTO.of(\n                        \"alarm\",\n                        MapUtil.<String, Object>builder()\n                                .put(\"level\", \"warning\")\n                                .put(\"message\", \"temperature too high\")\n                                .put(\"threshold\", 40)\n                                .put(\"current\", 42)\n                                .build(),\n                        System.currentTimeMillis())));\n        log.info(\"[testEventPost][子设备事件上报 - 请求实际由 Gateway 代为转发]\");\n\n        // 2. 发送并接收响应\n        IotDeviceMessage response = sendAndReceive(request);\n        log.info(\"[testEventPost][响应消息: {}]\", response);\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 构建带 token 的 params\n     */\n    private Map<String, Object> withToken(Object params) {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"token\", TOKEN);\n        result.put(\"body\", params);\n        return result;\n    }\n\n    /**\n     * 发送 UDP 消息并接收响应\n     */\n    private IotDeviceMessage sendAndReceive(IotDeviceMessage request) throws Exception {\n        byte[] payload = SERIALIZER.serialize(request);\n        log.info(\"[sendAndReceive][发送消息: {}，数据长度: {} 字节]\", request.getMethod(), payload.length);\n\n        try (DatagramSocket socket = new DatagramSocket()) {\n            socket.setSoTimeout(TIMEOUT_MS);\n            InetAddress address = InetAddress.getByName(SERVER_HOST);\n            DatagramPacket sendPacket = new DatagramPacket(payload, payload.length, address, SERVER_PORT);\n            socket.send(sendPacket);\n\n            byte[] receiveData = new byte[4096];\n            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);\n            try {\n                socket.receive(receivePacket);\n                byte[] responseBytes = new byte[receivePacket.getLength()];\n                System.arraycopy(receivePacket.getData(), 0, responseBytes, 0, receivePacket.getLength());\n                return SERIALIZER.deserialize(responseBytes);\n            } catch (java.net.SocketTimeoutException e) {\n                log.warn(\"[sendAndReceive][接收响应超时]\");\n                return null;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotDirectDeviceWebSocketProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.websocket;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.http.WebSocket;\nimport io.vertx.core.http.WebSocketClient;\nimport io.vertx.core.http.WebSocketConnectOptions;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicReference;\n\n/**\n * IoT 直连设备 WebSocket 协议集成测试（手动测试）\n *\n * <p>测试场景：直连设备（IotProductDeviceTypeEnum 的 DIRECT 类型）通过 WebSocket 协议直接连接平台\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（WebSocket 端口 8094）</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 设备认证</li>\n *             <li>{@link #testDeviceRegister()} - 设备动态注册（一型一密）</li>\n *             <li>{@link #testPropertyPost()} - 设备属性上报</li>\n *             <li>{@link #testEventPost()} - 设备事件上报</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：WebSocket 协议是有状态的长连接，认证成功后同一连接上的后续请求无需再携带认证信息\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotDirectDeviceWebSocketProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8094;\n    private static final String WS_PATH = \"/ws\";\n    private static final int TIMEOUT_SECONDS = 5;\n\n    private static Vertx vertx;\n\n    // ===================== 编解码器选择 =====================\n\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    // ===================== 直连设备信息（根据实际情况修改，从 iot_device 表查询） =====================\n\n    private static final String PRODUCT_KEY = \"4aymZgOTOOCrDKRT\";\n    private static final String DEVICE_NAME = \"small\";\n    private static final String DEVICE_SECRET = \"0baa4c2ecc104ae1a26b4070c218bdf3\";\n\n    @BeforeAll\n    public static void setUp() {\n        vertx = Vertx.vertx();\n    }\n\n    @AfterAll\n    public static void tearDown() {\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 认证测试：获取设备 Token\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1.1 构建认证消息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(), \"auth\", authReqDTO, null, null, null);\n        // 1.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testAuth][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 2.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testAuth][WebSocket 连接成功]\");\n\n        // 2.2 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n\n        // 3. 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testAuth][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testAuth][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 动态注册测试 =====================\n\n    /**\n     * 直连设备动态注册测试（一型一密）\n     * <p>\n     * 使用产品密钥（productSecret）验证身份，成功后返回设备密钥（deviceSecret）\n     * <p>\n     * 注意：此接口不需要认证\n     */\n    @Test\n    public void testDeviceRegister() throws Exception {\n        // 1.1 构建注册消息\n        String deviceName = \"test-ws-\" + System.currentTimeMillis();\n        String productSecret = \"test-product-secret\"; // 替换为实际的 productSecret\n        String sign = IotProductAuthUtils.buildSign(PRODUCT_KEY, deviceName, productSecret);\n        IotDeviceRegisterReqDTO registerReqDTO = new IotDeviceRegisterReqDTO()\n                .setProductKey(PRODUCT_KEY)\n                .setDeviceName(deviceName)\n                .setSign(sign);\n        IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), registerReqDTO, null, null, null);\n        // 1.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testDeviceRegister][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 2.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testDeviceRegister][WebSocket 连接成功]\");\n\n        // 2.2 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n\n        // 3. 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testDeviceRegister][响应消息: {}]\", responseMessage);\n            log.info(\"[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]\");\n        } else {\n            log.warn(\"[testDeviceRegister][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 直连设备属性上报测试 =====================\n\n    /**\n     * 属性上报测试\n     */\n    @Test\n    public void testPropertyPost() throws Exception {\n        // 1.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testPropertyPost][WebSocket 连接成功]\");\n\n        // 1.2 先进行认证\n        IotDeviceMessage authResponse = authenticate(ws);\n        log.info(\"[testPropertyPost][认证响应: {}]\", authResponse);\n\n        // 2.1 构建属性上报消息\n        IotDeviceMessage request = IotDeviceMessage.of(\n                IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),\n                IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                        .put(\"width\", 1)\n                        .put(\"height\", \"2\")\n                        .build()),\n                null, null, null);\n        // 2.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testPropertyPost][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 3.1 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n        // 3.2 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testPropertyPost][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testPropertyPost][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 直连设备事件上报测试 =====================\n\n    /**\n     * 事件上报测试\n     */\n    @Test\n    public void testEventPost() throws Exception {\n        // 1.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testEventPost][WebSocket 连接成功]\");\n\n        // 1.2 先进行认证\n        IotDeviceMessage authResponse = authenticate(ws);\n        log.info(\"[testEventPost][认证响应: {}]\", authResponse);\n\n        // 2.1 构建事件上报消息\n        IotDeviceMessage request = IotDeviceMessage.of(\n                IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                IotDeviceEventPostReqDTO.of(\n                        \"eat\",\n                        MapUtil.<String, Object>builder().put(\"rice\", 3).build(),\n                        System.currentTimeMillis()),\n                null, null, null);\n        // 2.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testEventPost][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 3.1 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n        // 3.2 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testEventPost][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testEventPost][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 创建 WebSocket 连接（同步）\n     *\n     * @return WebSocket 连接\n     */\n    private WebSocket createWebSocketConnection() throws Exception {\n        WebSocketClient wsClient = vertx.createWebSocketClient();\n        WebSocketConnectOptions options = new WebSocketConnectOptions()\n                .setHost(SERVER_HOST)\n                .setPort(SERVER_PORT)\n                .setURI(WS_PATH);\n        return wsClient.connect(options).toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n    }\n\n    /**\n     * 发送消息并等待响应（同步）\n     *\n     * @param ws      WebSocket 连接\n     * @param message 请求消息\n     * @return 响应消息\n     */\n    public static String sendAndReceive(WebSocket ws, String message) throws Exception {\n        CountDownLatch latch = new CountDownLatch(1);\n        AtomicReference<String> responseRef = new AtomicReference<>();\n\n        // 设置消息处理器\n        ws.textMessageHandler(response -> {\n            log.info(\"[sendAndReceive][收到响应: {}]\", response);\n            responseRef.set(response);\n            latch.countDown();\n        });\n\n        // 发送请求\n        log.info(\"[sendAndReceive][发送请求: {}]\", message);\n        ws.writeTextMessage(message);\n\n        // 等待响应\n        boolean completed = latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        if (!completed) {\n            log.warn(\"[sendAndReceive][等待响应超时]\");\n        }\n        return responseRef.get();\n    }\n\n    /**\n     * 执行设备认证（同步）\n     *\n     * @param ws WebSocket 连接\n     * @return 认证响应消息\n     */\n    private IotDeviceMessage authenticate(WebSocket ws) throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(), \"auth\", authReqDTO, null, null, null);\n\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[authenticate][发送认证请求: {}]\", jsonMessage);\n\n        String response = sendAndReceive(ws, jsonMessage);\n        if (response != null) {\n            return SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotGatewayDeviceWebSocketProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.websocket;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.http.WebSocket;\nimport io.vertx.core.http.WebSocketClient;\nimport io.vertx.core.http.WebSocketConnectOptions;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicReference;\n\n/**\n * IoT 网关设备 WebSocket 协议集成测试（手动测试）\n *\n * <p>测试场景：网关设备（IotProductDeviceTypeEnum 的 GATEWAY 类型）通过 WebSocket 协议管理子设备拓扑关系\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（WebSocket 端口 8094）</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 网关设备认证</li>\n *             <li>{@link #testTopoAdd()} - 添加子设备拓扑关系</li>\n *             <li>{@link #testTopoDelete()} - 删除子设备拓扑关系</li>\n *             <li>{@link #testTopoGet()} - 获取子设备拓扑关系</li>\n *             <li>{@link #testSubDeviceRegister()} - 子设备动态注册</li>\n *             <li>{@link #testPropertyPackPost()} - 批量上报属性（网关 + 子设备）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：WebSocket 协议是有状态的长连接，认证成功后同一连接上的后续请求无需再携带认证信息\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewayDeviceWebSocketProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8094;\n    private static final String WS_PATH = \"/ws\";\n    private static final int TIMEOUT_SECONDS = 5;\n\n    private static Vertx vertx;\n\n    // ===================== 序列化器选择 =====================\n\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    // ===================== 网关设备信息（根据实际情况修改，从 iot_device 表查询网关设备） =====================\n\n    private static final String GATEWAY_PRODUCT_KEY = \"m6XcS1ZJ3TW8eC0v\";\n    private static final String GATEWAY_DEVICE_NAME = \"sub-ddd\";\n    private static final String GATEWAY_DEVICE_SECRET = \"b3d62c70f8a4495487ed1d35d61ac2b3\";\n\n    // ===================== 子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String SUB_DEVICE_PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String SUB_DEVICE_NAME = \"chazuo-it\";\n    private static final String SUB_DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    @BeforeAll\n    public static void setUp() {\n        vertx = Vertx.vertx();\n    }\n\n    @AfterAll\n    public static void tearDown() {\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 网关设备认证测试\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1.1 构建认证消息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(), \"auth\", authReqDTO, null, null, null);\n        // 1.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testAuth][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 2.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testAuth][WebSocket 连接成功]\");\n\n        // 2.2 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n\n        // 3. 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testAuth][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testAuth][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 拓扑管理测试 =====================\n\n    /**\n     * 添加子设备拓扑关系测试\n     */\n    @Test\n    public void testTopoAdd() throws Exception {\n        // 1.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testTopoAdd][WebSocket 连接成功]\");\n\n        // 1.2 先进行认证\n        IotDeviceMessage authResponse = authenticate(ws);\n        log.info(\"[testTopoAdd][认证响应: {}]\", authResponse);\n\n        // 2.1 构建子设备认证信息\n        IotDeviceAuthReqDTO subAuthInfo = IotDeviceAuthUtils.getAuthInfo(\n                SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME, SUB_DEVICE_SECRET);\n        IotDeviceAuthReqDTO subDeviceAuth = new IotDeviceAuthReqDTO()\n                .setClientId(subAuthInfo.getClientId())\n                .setUsername(subAuthInfo.getUsername())\n                .setPassword(subAuthInfo.getPassword());\n        // 2.2 构建请求参数\n        IotDeviceTopoAddReqDTO params = new IotDeviceTopoAddReqDTO();\n        params.setSubDevices(Collections.singletonList(subDeviceAuth));\n        IotDeviceMessage request = IotDeviceMessage.of(\n                IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.TOPO_ADD.getMethod(),\n                params,\n                null, null, null);\n        // 2.3 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testTopoAdd][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 3.1 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n        // 3.2 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testTopoAdd][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testTopoAdd][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    /**\n     * 删除子设备拓扑关系测试\n     */\n    @Test\n    public void testTopoDelete() throws Exception {\n        // 1.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testTopoDelete][WebSocket 连接成功]\");\n\n        // 1.2 先进行认证\n        IotDeviceMessage authResponse = authenticate(ws);\n        log.info(\"[testTopoDelete][认证响应: {}]\", authResponse);\n\n        // 2.1 构建请求参数\n        IotDeviceTopoDeleteReqDTO params = new IotDeviceTopoDeleteReqDTO();\n        params.setSubDevices(Collections.singletonList(\n                new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME)));\n        IotDeviceMessage request = IotDeviceMessage.of(\n                IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod(),\n                params,\n                null, null, null);\n        // 2.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testTopoDelete][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 3.1 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n        // 3.2 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testTopoDelete][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testTopoDelete][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    /**\n     * 获取子设备拓扑关系测试\n     */\n    @Test\n    public void testTopoGet() throws Exception {\n        // 1.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testTopoGet][WebSocket 连接成功]\");\n\n        // 1.2 先进行认证\n        IotDeviceMessage authResponse = authenticate(ws);\n        log.info(\"[testTopoGet][认证响应: {}]\", authResponse);\n\n        // 2.1 构建请求参数\n        IotDeviceTopoGetReqDTO params = new IotDeviceTopoGetReqDTO();\n        IotDeviceMessage request = IotDeviceMessage.of(\n                IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.TOPO_GET.getMethod(),\n                params,\n                null, null, null);\n        // 2.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testTopoGet][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 3.1 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n        // 3.2 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testTopoGet][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testTopoGet][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 子设备注册测试 =====================\n\n    /**\n     * 子设备动态注册测试\n     */\n    @Test\n    public void testSubDeviceRegister() throws Exception {\n        // 1.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testSubDeviceRegister][WebSocket 连接成功]\");\n\n        // 1.2 先进行认证\n        IotDeviceMessage authResponse = authenticate(ws);\n        log.info(\"[testSubDeviceRegister][认证响应: {}]\", authResponse);\n\n        // 2.1 构建请求参数\n        IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO();\n        subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY);\n        subDevice.setDeviceName(\"mougezishebei-ws\");\n        IotDeviceMessage request = IotDeviceMessage.of(\n                IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod(),\n                Collections.singletonList(subDevice),\n                null, null, null);\n        // 2.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testSubDeviceRegister][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 3.1 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n        // 3.2 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testSubDeviceRegister][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testSubDeviceRegister][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 批量上报测试 =====================\n\n    /**\n     * 批量上报属性测试（网关 + 子设备）\n     */\n    @Test\n    public void testPropertyPackPost() throws Exception {\n        // 1.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testPropertyPackPost][WebSocket 连接成功]\");\n\n        // 1.2 先进行认证\n        IotDeviceMessage authResponse = authenticate(ws);\n        log.info(\"[testPropertyPackPost][认证响应: {}]\", authResponse);\n\n        // 2.1 构建【网关设备】自身属性\n        Map<String, Object> gatewayProperties = MapUtil.<String, Object>builder()\n                .put(\"temperature\", 25.5)\n                .build();\n        // 2.2 构建【网关设备】自身事件\n        IotDevicePropertyPackPostReqDTO.EventValue gatewayEvent = new IotDevicePropertyPackPostReqDTO.EventValue();\n        gatewayEvent.setValue(MapUtil.builder().put(\"message\", \"gateway started\").build());\n        gatewayEvent.setTime(System.currentTimeMillis());\n        Map<String, IotDevicePropertyPackPostReqDTO.EventValue> gatewayEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                .put(\"statusReport\", gatewayEvent)\n                .build();\n        // 2.3 构建【网关子设备】属性\n        Map<String, Object> subDeviceProperties = MapUtil.<String, Object>builder()\n                .put(\"power\", 100)\n                .build();\n        // 2.4 构建【网关子设备】事件\n        IotDevicePropertyPackPostReqDTO.EventValue subDeviceEvent = new IotDevicePropertyPackPostReqDTO.EventValue();\n        subDeviceEvent.setValue(MapUtil.builder().put(\"errorCode\", 0).build());\n        subDeviceEvent.setTime(System.currentTimeMillis());\n        Map<String, IotDevicePropertyPackPostReqDTO.EventValue> subDeviceEvents = MapUtil.<String, IotDevicePropertyPackPostReqDTO.EventValue>builder()\n                .put(\"healthCheck\", subDeviceEvent)\n                .build();\n        // 2.5 构建子设备数据\n        IotDevicePropertyPackPostReqDTO.SubDeviceData subDeviceData = new IotDevicePropertyPackPostReqDTO.SubDeviceData();\n        subDeviceData.setIdentity(new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME));\n        subDeviceData.setProperties(subDeviceProperties);\n        subDeviceData.setEvents(subDeviceEvents);\n        // 2.6 构建请求参数\n        IotDevicePropertyPackPostReqDTO params = new IotDevicePropertyPackPostReqDTO();\n        params.setProperties(gatewayProperties);\n        params.setEvents(gatewayEvents);\n        params.setSubDevices(ListUtil.of(subDeviceData));\n        IotDeviceMessage request = IotDeviceMessage.of(\n                IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod(),\n                params,\n                null, null, null);\n        // 2.7 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testPropertyPackPost][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 3.1 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n        // 3.2 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testPropertyPackPost][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testPropertyPackPost][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 创建 WebSocket 连接（同步）\n     *\n     * @return WebSocket 连接\n     */\n    private WebSocket createWebSocketConnection() throws Exception {\n        WebSocketClient wsClient = vertx.createWebSocketClient();\n        WebSocketConnectOptions options = new WebSocketConnectOptions()\n                .setHost(SERVER_HOST)\n                .setPort(SERVER_PORT)\n                .setURI(WS_PATH);\n        return wsClient.connect(options).toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n    }\n\n    /**\n     * 发送消息并等待响应（同步）\n     *\n     * @param ws      WebSocket 连接\n     * @param message 请求消息\n     * @return 响应消息\n     */\n    private String sendAndReceive(WebSocket ws, String message) throws Exception {\n        CountDownLatch latch = new CountDownLatch(1);\n        AtomicReference<String> responseRef = new AtomicReference<>();\n\n        // 设置消息处理器\n        ws.textMessageHandler(response -> {\n            log.info(\"[sendAndReceive][收到响应: {}]\", response);\n            responseRef.set(response);\n            latch.countDown();\n        });\n\n        // 发送请求\n        log.info(\"[sendAndReceive][发送请求: {}]\", message);\n        ws.writeTextMessage(message);\n\n        // 等待响应\n        boolean completed = latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        if (!completed) {\n            log.warn(\"[sendAndReceive][等待响应超时]\");\n        }\n        return responseRef.get();\n    }\n\n    /**\n     * 执行网关设备认证（同步）\n     *\n     * @param ws WebSocket 连接\n     * @return 认证响应消息\n     */\n    private IotDeviceMessage authenticate(WebSocket ws) throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(), \"auth\", authReqDTO, null, null, null);\n\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[authenticate][发送认证请求: {}]\", jsonMessage);\n\n        String response = sendAndReceive(ws, jsonMessage);\n        if (response != null) {\n            return SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotGatewaySubDeviceWebSocketProtocolIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.gateway.protocol.websocket;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;\nimport cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;\nimport io.vertx.core.Vertx;\nimport io.vertx.core.http.WebSocket;\nimport io.vertx.core.http.WebSocketClient;\nimport io.vertx.core.http.WebSocketConnectOptions;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicReference;\n\n/**\n * IoT 网关子设备 WebSocket 协议集成测试（手动测试）\n *\n * <p>测试场景：子设备（IotProductDeviceTypeEnum 的 SUB 类型）通过网关设备代理上报数据\n *\n * <p><b>重要说明：子设备无法直接连接平台，所有请求均由网关设备（Gateway）代为转发。</b>\n *\n * <p>使用步骤：\n * <ol>\n *     <li>启动 yudao-module-iot-gateway 服务（WebSocket 端口 8094）</li>\n *     <li>确保子设备已通过 {@link IotGatewayDeviceWebSocketProtocolIntegrationTest#testTopoAdd()} 绑定到网关</li>\n *     <li>运行以下测试方法：\n *         <ul>\n *             <li>{@link #testAuth()} - 子设备认证</li>\n *             <li>{@link #testPropertyPost()} - 子设备属性上报（由网关代理转发）</li>\n *             <li>{@link #testEventPost()} - 子设备事件上报（由网关代理转发）</li>\n *         </ul>\n *     </li>\n * </ol>\n *\n * <p>注意：WebSocket 协议是有状态的长连接，认证成功后同一连接上的后续请求无需再携带认证信息\n *\n * @author 芋道源码\n */\n@Slf4j\n@Disabled\npublic class IotGatewaySubDeviceWebSocketProtocolIntegrationTest {\n\n    private static final String SERVER_HOST = \"127.0.0.1\";\n    private static final int SERVER_PORT = 8094;\n    private static final String WS_PATH = \"/ws\";\n    private static final int TIMEOUT_SECONDS = 5;\n\n    private static Vertx vertx;\n\n    // ===================== 序列化器选择 =====================\n\n    private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();\n\n    // ===================== 网关子设备信息（根据实际情况修改，从 iot_device 表查询子设备） =====================\n\n    private static final String PRODUCT_KEY = \"jAufEMTF1W6wnPhn\";\n    private static final String DEVICE_NAME = \"chazuo-it\";\n    private static final String DEVICE_SECRET = \"d46ef9b28ab14238b9c00a3a668032af\";\n\n    @BeforeAll\n    public static void setUp() {\n        vertx = Vertx.vertx();\n    }\n\n    @AfterAll\n    public static void tearDown() {\n        if (vertx != null) {\n            vertx.close();\n        }\n    }\n\n    // ===================== 认证测试 =====================\n\n    /**\n     * 子设备认证测试\n     */\n    @Test\n    public void testAuth() throws Exception {\n        // 1.1 构建认证消息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(), \"auth\", authReqDTO, null, null, null);\n        // 1.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testAuth][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 2.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testAuth][WebSocket 连接成功]\");\n\n        // 2.2 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n\n        // 3. 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testAuth][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testAuth][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 子设备属性上报测试 =====================\n\n    /**\n     * 子设备属性上报测试\n     */\n    @Test\n    public void testPropertyPost() throws Exception {\n        // 1.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testPropertyPost][WebSocket 连接成功]\");\n\n        // 1.2 先进行认证\n        IotDeviceMessage authResponse = authenticate(ws);\n        log.info(\"[testPropertyPost][认证响应: {}]\", authResponse);\n        log.info(\"[testPropertyPost][子设备属性上报 - 请求实际由 Gateway 代为转发]\");\n\n        // 2.1 构建属性上报消息\n        IotDeviceMessage request = IotDeviceMessage.of(\n                IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),\n                IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()\n                        .put(\"power\", 100)\n                        .put(\"status\", \"online\")\n                        .put(\"temperature\", 36.5)\n                        .build()),\n                null, null, null);\n        // 2.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testPropertyPost][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 3.1 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n        // 3.2 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testPropertyPost][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testPropertyPost][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 子设备事件上报测试 =====================\n\n    /**\n     * 子设备事件上报测试\n     */\n    @Test\n    public void testEventPost() throws Exception {\n        // 1.1 创建 WebSocket 连接（同步）\n        WebSocket ws = createWebSocketConnection();\n        log.info(\"[testEventPost][WebSocket 连接成功]\");\n\n        // 1.2 先进行认证\n        IotDeviceMessage authResponse = authenticate(ws);\n        log.info(\"[testEventPost][认证响应: {}]\", authResponse);\n        log.info(\"[testEventPost][子设备事件上报 - 请求实际由 Gateway 代为转发]\");\n\n        // 2.1 构建事件上报消息\n        IotDeviceMessage request = IotDeviceMessage.of(\n                IdUtil.fastSimpleUUID(),\n                IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                IotDeviceEventPostReqDTO.of(\n                        \"alarm\",\n                        MapUtil.<String, Object>builder()\n                                .put(\"level\", \"warning\")\n                                .put(\"message\", \"temperature too high\")\n                                .put(\"threshold\", 40)\n                                .put(\"current\", 42)\n                                .build(),\n                        System.currentTimeMillis()),\n                null, null, null);\n        // 2.2 序列化\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[testEventPost][Serialize: {}, 请求消息: {}]\", SERIALIZER.getType(), request);\n\n        // 3.1 发送并等待响应\n        String response = sendAndReceive(ws, jsonMessage);\n        // 3.2 解码响应\n        if (response != null) {\n            IotDeviceMessage responseMessage = SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n            log.info(\"[testEventPost][响应消息: {}]\", responseMessage);\n        } else {\n            log.warn(\"[testEventPost][未收到响应]\");\n        }\n\n        // 4. 关闭连接\n        ws.close();\n    }\n\n    // ===================== 辅助方法 =====================\n\n    /**\n     * 创建 WebSocket 连接（同步）\n     *\n     * @return WebSocket 连接\n     */\n    private WebSocket createWebSocketConnection() throws Exception {\n        WebSocketClient wsClient = vertx.createWebSocketClient();\n        WebSocketConnectOptions options = new WebSocketConnectOptions()\n                .setHost(SERVER_HOST)\n                .setPort(SERVER_PORT)\n                .setURI(WS_PATH);\n        return wsClient.connect(options).toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n    }\n\n    /**\n     * 发送消息并等待响应（同步）\n     *\n     * @param ws      WebSocket 连接\n     * @param message 请求消息\n     * @return 响应消息\n     */\n    private String sendAndReceive(WebSocket ws, String message) throws Exception {\n        CountDownLatch latch = new CountDownLatch(1);\n        AtomicReference<String> responseRef = new AtomicReference<>();\n\n        // 设置消息处理器\n        ws.textMessageHandler(response -> {\n            log.info(\"[sendAndReceive][收到响应: {}]\", response);\n            responseRef.set(response);\n            latch.countDown();\n        });\n\n        // 发送请求\n        log.info(\"[sendAndReceive][发送请求: {}]\", message);\n        ws.writeTextMessage(message);\n\n        // 等待响应\n        boolean completed = latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        if (!completed) {\n            log.warn(\"[sendAndReceive][等待响应超时]\");\n        }\n        return responseRef.get();\n    }\n\n    /**\n     * 执行子设备认证（同步）\n     *\n     * @param ws WebSocket 连接\n     * @return 认证响应消息\n     */\n    private IotDeviceMessage authenticate(WebSocket ws) throws Exception {\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);\n        IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()\n                .setClientId(authInfo.getClientId())\n                .setUsername(authInfo.getUsername())\n                .setPassword(authInfo.getPassword());\n        IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(), \"auth\", authReqDTO, null, null, null);\n\n        byte[] payload = SERIALIZER.serialize(request);\n        String jsonMessage = StrUtil.utf8Str(payload);\n        log.info(\"[authenticate][发送认证请求: {}]\", jsonMessage);\n\n        String response = sendAndReceive(ws, jsonMessage);\n        if (response != null) {\n            return SERIALIZER.deserialize(StrUtil.utf8Bytes(response));\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/resources/tcp-binary-packet-examples.md",
    "content": "# TCP 二进制协议数据包格式说明\n\n## 1. 协议概述\n\nTCP 二进制协议是一种高效的自定义协议格式，采用紧凑的二进制格式传输数据，适用于对带宽和性能要求较高的 IoT 场景。\n\n### 1.1 协议特点\n\n- **高效传输**：完全二进制格式，减少数据传输量\n- **版本控制**：内置协议版本号，支持协议升级\n- **类型安全**：明确的消息类型标识\n- **简洁设计**：去除冗余字段，协议更加精简\n- **兼容性**：与现有 `IotDeviceMessage` 接口完全兼容\n\n## 2. 协议格式\n\n### 2.1 整体结构\n\n```\n+--------+--------+--------+---------------------------+--------+--------+\n| 魔术字 | 版本号 | 消息类型|         消息长度(4字节)          |\n+--------+--------+--------+---------------------------+--------+--------+\n|           消息 ID 长度(2字节)        |      消息 ID (变长字符串)         |\n+--------+--------+--------+--------+--------+--------+--------+--------+\n|           方法名长度(2字节)        |      方法名(变长字符串)         |\n+--------+--------+--------+--------+--------+--------+--------+--------+\n|                        消息体数据(变长)                              |\n+--------+--------+--------+--------+--------+--------+--------+--------+\n```\n\n### 2.2 字段详细说明\n\n| 字段 | 长度 | 类型 | 说明 |\n|------|------|------|------|\n| 魔术字 | 1字节 | byte | `0x7E` - 协议识别标识，用于数据同步 |\n| 版本号 | 1字节 | byte | `0x01` - 协议版本号，支持版本控制 |\n| 消息类型 | 1字节 | byte | `0x01`=请求, `0x02`=响应 |\n| 消息长度 | 4字节 | int | 整个消息的总长度（包含头部） |\n| 消息 ID 长度 | 2字节 | short | 消息 ID 字符串的字节长度 |\n| 消息 ID | 变长 | string | 消息唯一标识符（UTF-8编码） |\n| 方法名长度 | 2字节 | short | 方法名字符串的字节长度 |\n| 方法名 | 变长 | string | 消息方法名（UTF-8编码） |\n| 消息体 | 变长 | binary | 根据消息类型的不同数据格式 |\n\n**⚠️ 重要说明**：deviceId 不包含在协议中，由服务器根据连接上下文自动设置\n\n### 2.3 协议常量定义\n\n```java\n// 协议标识\nprivate static final byte MAGIC_NUMBER = (byte) 0x7E;\nprivate static final byte PROTOCOL_VERSION = (byte) 0x01;\n\n// 消息类型\nprivate static final byte REQUEST = (byte) 0x01;   // 请求消息\nprivate static final byte RESPONSE = (byte) 0x02;  // 响应消息\n\n// 协议长度\nprivate static final int HEADER_FIXED_LENGTH = 7;      // 固定头部长度\nprivate static final int MIN_MESSAGE_LENGTH = 11;      // 最小消息长度\n```\n\n## 3. 消息类型和格式\n\n### 3.1 请求消息 (REQUEST - 0x01)\n\n请求消息用于设备向服务器发送数据或请求。\n\n#### 3.1.1 消息体格式\n```\n消息体 = params 数据(JSON格式)\n```\n\n#### 3.1.2 示例：设备认证请求\n\n**消息内容：**\n- 消息 ID: `auth_1704067200000_123`\n- 方法名: `auth`\n- 参数: `{\"clientId\":\"device_001\",\"username\":\"productKey_deviceName\",\"password\":\"device_password\"}`\n\n**二进制数据包结构：**\n```\n7E                              // 魔术字 (0x7E)\n01                              // 版本号 (0x01)\n01                              // 消息类型 (REQUEST)\n00 00 00 89                     // 消息长度 (137字节)\n00 19                           // 消息 ID 长度 (25字节)\n61 75 74 68 5F 31 37 30 34 30   // 消息 ID: \"auth_1704067200000_123\"\n36 37 32 30 30 30 30 30 5F 31 \n32 33\n00 04                           // 方法名长度 (4字节)\n61 75 74 68                     // 方法名: \"auth\"\n7B 22 63 6C 69 65 6E 74 49 64   // JSON参数数据\n22 3A 22 64 65 76 69 63 65 5F   // {\"clientId\":\"device_001\",\n30 30 31 22 2C 22 75 73 65 72   //  \"username\":\"productKey_deviceName\",\n6E 61 6D 65 22 3A 22 70 72 6F   //  \"password\":\"device_password\"}\n64 75 63 74 4B 65 79 5F 64 65\n76 69 63 65 4E 61 6D 65 22 2C\n22 70 61 73 73 77 6F 72 64 22\n3A 22 64 65 76 69 63 65 5F 70\n61 73 73 77 6F 72 64 22 7D\n```\n\n#### 3.1.3 示例：属性数据上报\n\n**消息内容：**\n- 消息 ID: `property_1704067200000_456`\n- 方法名: `thing.property.post`\n- 参数: `{\"temperature\":25.5,\"humidity\":60.2,\"pressure\":1013.25}`\n\n### 3.2 响应消息 (RESPONSE - 0x02)\n\n响应消息用于服务器向设备回复请求结果。\n\n#### 3.2.1 消息体格式\n```\n消息体 = 响应码(4字节) + 响应消息长度(2字节) + 响应消息(UTF-8) + 响应数据(JSON)\n```\n\n#### 3.2.2 字段说明\n\n| 字段 | 长度 | 类型 | 说明 |\n|------|------|------|------|\n| 响应码 | 4字节 | int | HTTP状态码风格，0=成功，其他=错误 |\n| 响应消息长度 | 2字节 | short | 响应消息字符串的字节长度 |\n| 响应消息 | 变长 | string | 响应提示信息（UTF-8编码） |\n| 响应数据 | 变长 | binary | JSON格式的响应数据（可选） |\n\n#### 3.2.3 示例：认证成功响应\n\n**消息内容：**\n- 消息 ID: `auth_response_1704067200000_123`\n- 方法名: `auth`\n- 响应码: `0`\n- 响应消息: `认证成功`\n- 响应数据: `{\"success\":true,\"message\":\"认证成功\"}`\n\n**二进制数据包结构：**\n```\n7E                              // 魔术字 (0x7E)\n01                              // 版本号 (0x01)\n02                              // 消息类型 (RESPONSE)\n00 00 00 A4                     // 消息长度 (164字节)\n00 22                           // 消息 ID 长度 (34字节)\n61 75 74 68 5F 72 65 73 70 6F   // 消息 ID: \"auth_response_1704067200000_123\"\n6E 73 65 5F 31 37 30 34 30 36\n37 32 30 30 30 30 30 5F 31 32\n33\n00 04                           // 方法名长度 (4字节)\n61 75 74 68                     // 方法名: \"auth\"\n00 00 00 00                     // 响应码 (0 = 成功)\n00 0C                           // 响应消息长度 (12字节)\nE8 AE A4 E8 AF 81 E6 88 90 E5   // 响应消息: \"认证成功\" (UTF-8)\n8A 9F\n7B 22 73 75 63 63 65 73 73 22   // JSON响应数据\n3A 74 72 75 65 2C 22 6D 65 73   // {\"success\":true,\"message\":\"认证成功\"}\n73 61 67 65 22 3A 22 E8 AE A4\nE8 AF 81 E6 88 90 E5 8A 9F 22\n7D\n```\n\n## 4. 编解码器标识\n\n```java\npublic static final String TYPE = \"TCP_BINARY\";\n```\n\n## 5. 协议优势\n\n- **数据紧凑**：二进制格式，相比 JSON 减少 30-50% 的数据量\n- **解析高效**：直接二进制操作，减少字符串转换开销\n- **类型安全**：明确的消息类型和字段定义\n- **设计简洁**：去除冗余字段，协议更加精简高效\n- **版本控制**：内置版本号支持协议升级\n\n## 6. 与 JSON 协议对比\n\n| 特性   | 二进制协议       | JSON协议 |\n|------|-------------|--------|\n| 数据大小 | 小（节省30-50%） | 大      |\n| 解析性能 | 高           | 中等     |\n| 网络开销 | 低           | 高      |\n| 可读性  | 差           | 优秀     |\n| 调试难度 | 高           | 低      |\n| 扩展性  | 良好          | 优秀     |\n\n**推荐场景**：\n- ✅ **高频数据传输**：传感器数据实时上报\n- ✅ **带宽受限环境**：移动网络、卫星通信\n- ✅ **性能要求高**：需要低延迟、高吞吐的场景\n- ✅ **设备资源有限**：嵌入式设备、低功耗设备\n- ❌ **开发调试阶段**：调试困难，建议使用 JSON 协议\n- ❌ **快速原型开发**：开发效率低\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-gateway/src/test/resources/tcp-json-packet-examples.md",
    "content": "# TCP JSON 格式协议说明\n\n## 1. 协议概述\n\nTCP JSON 格式协议采用纯 JSON 格式进行数据传输，具有以下特点：\n\n- **标准化**：使用标准 JSON 格式，易于解析和处理\n- **可读性**：人类可读，便于调试和维护\n- **扩展性**：可以轻松添加新字段，向后兼容\n- **跨平台**：JSON 格式支持所有主流编程语言\n- **安全优化**：移除冗余的 deviceId 字段，提高安全性\n\n## 2. 消息格式\n\n### 2.1 基础消息结构\n\n```json\n{\n  \"id\": \"消息唯一标识\",\n  \"method\": \"消息方法\",\n  \"params\": {\n    // 请求参数\n  },\n  \"data\": {\n    // 响应数据\n  },\n  \"code\": 响应码,\n  \"msg\": \"响应消息\",\n  \"timestamp\": 时间戳\n}\n```\n\n**⚠️ 重要说明**：\n- **不包含 deviceId 字段**：由服务器通过 TCP 连接上下文自动确定设备 ID\n- **避免伪造攻击**：防止设备伪造其他设备的 ID 发送消息\n\n### 2.2 字段详细说明\n\n| 字段名 | 类型 | 必填 | 用途 | 说明 |\n|--------|------|------|------|------|\n| id | String | 是 | 所有消息 | 消息唯一标识 |\n| method | String | 是 | 所有消息 | 消息方法，如 `auth`、`thing.property.post` |\n| params | Object | 否 | 请求消息 | 请求参数，具体内容根据method而定 |\n| data | Object | 否 | 响应消息 | 响应数据，服务器返回的结果数据 |\n| code | Integer | 否 | 响应消息 | 响应码，0=成功，其他=错误 |\n| msg | String | 否 | 响应消息 | 响应提示信息 |\n| timestamp | Long | 是 | 所有消息 | 时间戳（毫秒），编码时自动生成 |\n\n### 2.3 消息分类\n\n#### 2.3.1 请求消息（上行）\n- **特征**：包含 `params` 字段，不包含 `code`、`msg` 字段\n- **方向**：设备 → 服务器\n- **用途**：设备认证、数据上报、状态更新等\n\n#### 2.3.2 响应消息（下行）\n- **特征**：包含 `code`、`msg` 字段，可能包含 `data` 字段\n- **方向**：服务器 → 设备  \n- **用途**：认证结果、指令响应、错误提示等\n\n## 3. 消息示例\n\n### 3.1 设备认证 (auth)\n\n#### 认证请求格式\n**消息方向**：设备 → 服务器\n\n```json\n{\n  \"id\": \"auth_1704067200000_123\",\n  \"method\": \"auth\",\n  \"params\": {\n    \"clientId\": \"device_001\",\n    \"username\": \"productKey_deviceName\",\n    \"password\": \"设备密码\"\n  },\n  \"timestamp\": 1704067200000\n}\n```\n\n**认证参数说明：**\n\n| 字段名 | 类型 | 必填 | 说明 |\n|--------|------|------|------|\n| clientId | String | 是 | 客户端唯一标识，用于连接管理 |\n| username | String | 是 | 设备用户名，格式为 `productKey_deviceName` |\n| password | String | 是 | 设备密码，在设备管理平台配置 |\n\n#### 认证响应格式\n**消息方向**：服务器 → 设备\n\n**认证成功响应：**\n```json\n{\n  \"id\": \"response_auth_1704067200000_123\",\n  \"method\": \"auth\",\n  \"data\": {\n    \"success\": true,\n    \"message\": \"认证成功\"\n  },\n  \"code\": 0,\n  \"msg\": \"认证成功\",\n  \"timestamp\": 1704067200001\n}\n```\n\n**认证失败响应：**\n```json\n{\n  \"id\": \"response_auth_1704067200000_123\", \n  \"method\": \"auth\",\n  \"data\": {\n    \"success\": false,\n    \"message\": \"认证失败：用户名或密码错误\"\n  },\n  \"code\": 401,\n  \"msg\": \"认证失败\",\n  \"timestamp\": 1704067200001\n}\n```\n\n### 3.2 属性数据上报 (thing.property.post)\n\n**消息方向**：设备 → 服务器\n\n**示例：温度传感器数据上报**\n```json\n{\n  \"id\": \"property_1704067200000_456\",\n  \"method\": \"thing.property.post\",\n  \"params\": {\n    \"temperature\": 25.5,\n    \"humidity\": 60.2,\n    \"pressure\": 1013.25,\n    \"battery\": 85,\n    \"signal_strength\": -65\n  },\n  \"timestamp\": 1704067200000\n}\n```\n\n### 3.3 设备状态更新 (thing.state.update)\n\n**消息方向**：设备 → 服务器\n\n**示例：心跳请求**\n```json\n{\n  \"id\": \"heartbeat_1704067200000_321\",\n  \"method\": \"thing.state.update\",\n  \"params\": {\n    \"state\": \"online\",\n    \"uptime\": 86400,\n    \"memory_usage\": 65.2,\n    \"cpu_usage\": 12.8\n  },\n  \"timestamp\": 1704067200000\n}\n```\n\n## 4. 编解码器标识\n\n```java\npublic static final String TYPE = \"TCP_JSON\";\n```\n\n## 5. 协议优势\n\n- **开发效率高**：JSON 格式，开发和调试简单\n- **跨语言支持**：所有主流语言都支持 JSON\n- **可读性优秀**：可以直接查看消息内容\n- **扩展性强**：可以轻松添加新字段\n- **安全性高**：移除 deviceId 字段，防止伪造攻击\n\n## 6. 与二进制协议对比\n\n| 特性 | JSON协议 | 二进制协议 |\n|------|----------|------------|\n| 开发难度 | 低 | 高 |\n| 调试难度 | 低 | 高 |\n| 可读性 | 优秀 | 差 |\n| 数据大小 | 中等 | 小（节省30-50%） |\n| 解析性能 | 中等 | 高 |\n| 学习成本 | 低 | 高 |\n\n**推荐场景**：\n- ✅ **开发调试阶段**：调试友好，开发效率高\n- ✅ **快速原型开发**：实现简单，快速迭代\n- ✅ **多语言集成**：广泛的语言支持\n- ❌ **高频数据传输**：建议使用二进制协议\n- ❌ **带宽受限环境**：建议使用二进制协议"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao-module-iot</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>jar</packaging>\n\n    <artifactId>yudao-module-iot-server</artifactId>\n\n    <name>${project.artifactId}</name>\n    <description>\n        物联网 模块，主要实现 产品管理、设备管理、协议管理等功能。\n                <!-- TODO 芋艿：后续补充下 -->\n    </description>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-system-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-iot-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-iot-core</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>com.taosdata.jdbc</groupId>\n            <artifactId>taos-jdbcdriver</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- Job 定时任务相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-job</artifactId>\n        </dependency>\n\n        <!-- TODO @芋艿：引入下，看看情况 -->\n        <dependency>\n            <groupId>org.quartz-scheduler</groupId>\n            <artifactId>quartz</artifactId>\n            <version>2.3.2</version>\n        </dependency>\n\n        <!-- 消息队列相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mq</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <!-- OkHttp -->\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>okhttp</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>mockwebserver</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- 消息队列相关 -->\n        <!-- TODO @芋艿：临时打开 -->\n        <dependency>\n            <groupId>org.apache.rocketmq</groupId>\n            <artifactId>rocketmq-spring-boot-starter</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/IoTServerApplication.java",
    "content": "package cn.iocoder.yudao.module.iot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n *\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication\npublic class IoTServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(IoTServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.api.device;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.*;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusConfigService;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n/**\n * IoT 设备 API 实现类\n *\n * @author haohao\n */\n@RestController\n@Validated\n@Primary // 保证优先匹配，因为 yudao-iot-gateway 也有 IotDeviceCommonApi 的实现，并且也可能会被 biz 引入\npublic class IoTDeviceApiImpl implements IotDeviceCommonApi {\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotProductService productService;\n    @Resource\n    @Lazy // 延迟加载，解决循环依赖\n    private IotDeviceModbusConfigService modbusConfigService;\n    @Resource\n    @Lazy // 延迟加载，解决循环依赖\n    private IotDeviceModbusPointService modbusPointService;\n\n    @Override\n    @PostMapping(RpcConstants.RPC_API_PREFIX + \"/iot/device/auth\")\n    @PermitAll\n    public CommonResult<Boolean> authDevice(@RequestBody IotDeviceAuthReqDTO authReqDTO) {\n        return success(deviceService.authDevice(authReqDTO));\n    }\n\n    @Override\n    @PostMapping(RpcConstants.RPC_API_PREFIX + \"/iot/device/get\") // 特殊：方便调用，暂时使用 POST，实际更推荐 GET\n    @PermitAll\n    public CommonResult<IotDeviceRespDTO> getDevice(@RequestBody IotDeviceGetReqDTO getReqDTO) {\n        IotDeviceDO device = getReqDTO.getId() != null ? deviceService.getDeviceFromCache(getReqDTO.getId())\n                : deviceService.getDeviceFromCache(getReqDTO.getProductKey(), getReqDTO.getDeviceName());\n        return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> {\n            IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId());\n            if (product != null) {\n                deviceDTO.setProtocolType(product.getProtocolType()).setSerializeType(product.getSerializeType());\n            }\n        }));\n    }\n\n    @Override\n    @PostMapping(RpcConstants.RPC_API_PREFIX + \"/iot/modbus/config-list\")\n    @PermitAll\n    @TenantIgnore\n    public CommonResult<List<IotModbusDeviceConfigRespDTO>> getModbusDeviceConfigList(\n            @RequestBody IotModbusDeviceConfigListReqDTO listReqDTO) {\n        // 1. 获取 Modbus 连接配置\n        List<IotDeviceModbusConfigDO> configList = modbusConfigService.getDeviceModbusConfigList(listReqDTO);\n        if (CollUtil.isEmpty(configList)) {\n            return success(new ArrayList<>());\n        }\n\n        // 2. 组装返回结果\n        Set<Long> deviceIds = convertSet(configList, IotDeviceModbusConfigDO::getDeviceId);\n        Map<Long, IotDeviceDO> deviceMap = deviceService.getDeviceMap(deviceIds);\n        Map<Long, List<IotDeviceModbusPointDO>> pointMap = modbusPointService.getEnabledDeviceModbusPointMapByDeviceIds(deviceIds);\n        Map<Long, IotProductDO> productMap = productService.getProductMap(convertSet(deviceMap.values(), IotDeviceDO::getProductId));\n        List<IotModbusDeviceConfigRespDTO> result = new ArrayList<>(configList.size());\n        for (IotDeviceModbusConfigDO config : configList) {\n            // 3.1 获取设备信息\n            IotDeviceDO device = deviceMap.get(config.getDeviceId());\n            if (device == null) {\n                continue;\n            }\n            // 3.2 按 protocolType 筛选（如果非空）\n            if (StrUtil.isNotEmpty(listReqDTO.getProtocolType())) {\n                IotProductDO product = productMap.get(device.getProductId());\n                if (product == null || ObjUtil.notEqual(listReqDTO.getProtocolType(), product.getProtocolType())) {\n                    continue;\n                }\n            }\n            // 3.3 获取启用的点位列表\n            List<IotDeviceModbusPointDO> pointList = pointMap.get(config.getDeviceId());\n            if (CollUtil.isEmpty(pointList)) {\n                continue;\n            }\n\n            // 3.4 构建 IotModbusDeviceConfigRespDTO 对象\n            IotModbusDeviceConfigRespDTO configDTO = BeanUtils.toBean(config, IotModbusDeviceConfigRespDTO.class, o ->\n                    o.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())\n                            .setPoints(BeanUtils.toBean(pointList, IotModbusPointRespDTO.class)));\n            result.add(configDTO);\n        }\n        return success(result);\n    }\n\n    @Override\n    @PostMapping(RpcConstants.RPC_API_PREFIX + \"/iot/device/register\")\n    @PermitAll\n    public CommonResult<IotDeviceRegisterRespDTO> registerDevice(@RequestBody IotDeviceRegisterReqDTO reqDTO) {\n        return success(deviceService.registerDevice(reqDTO));\n    }\n\n    @Override\n    @PostMapping(RpcConstants.RPC_API_PREFIX + \"/iot/device/register-sub\")\n    @PermitAll\n    public CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(@RequestBody IotSubDeviceRegisterFullReqDTO reqDTO) {\n        return success(deviceService.registerSubDevices(reqDTO));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java",
    "content": "/**\n * iot API 包，定义并实现提供给其它模块的 API\n */\npackage cn.iocoder.yudao.module.iot.api;"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertConfigController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.alert;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;\nimport cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;\n\n@Tag(name = \"管理后台 - IoT 告警配置\")\n@RestController\n@RequestMapping(\"/iot/alert-config\")\n@Validated\npublic class IotAlertConfigController {\n\n    @Resource\n    private IotAlertConfigService alertConfigService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建告警配置\")\n    @PreAuthorize(\"@ss.hasPermission('iot:alert-config:create')\")\n    public CommonResult<Long> createAlertConfig(@Valid @RequestBody IotAlertConfigSaveReqVO createReqVO) {\n        return success(alertConfigService.createAlertConfig(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新告警配置\")\n    @PreAuthorize(\"@ss.hasPermission('iot:alert-config:update')\")\n    public CommonResult<Boolean> updateAlertConfig(@Valid @RequestBody IotAlertConfigSaveReqVO updateReqVO) {\n        alertConfigService.updateAlertConfig(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除告警配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:alert-config:delete')\")\n    public CommonResult<Boolean> deleteAlertConfig(@RequestParam(\"id\") Long id) {\n        alertConfigService.deleteAlertConfig(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得告警配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:alert-config:query')\")\n    public CommonResult<IotAlertConfigRespVO> getAlertConfig(@RequestParam(\"id\") Long id) {\n        IotAlertConfigDO alertConfig = alertConfigService.getAlertConfig(id);\n        return success(BeanUtils.toBean(alertConfig, IotAlertConfigRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得告警配置分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:alert-config:query')\")\n    public CommonResult<PageResult<IotAlertConfigRespVO>> getAlertConfigPage(@Valid IotAlertConfigPageReqVO pageReqVO) {\n        PageResult<IotAlertConfigDO> pageResult = alertConfigService.getAlertConfigPage(pageReqVO);\n\n        // 转换返回\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(\n                convertSetByFlatMap(pageResult.getList(), config -> config.getReceiveUserIds().stream()));\n        return success(BeanUtils.toBean(pageResult, IotAlertConfigRespVO.class, vo -> {\n            vo.setReceiveUserNames(vo.getReceiveUserIds().stream()\n                    .map(userMap::get)\n                    .filter(Objects::nonNull)\n                    .map(AdminUserRespDTO::getNickname)\n                    .collect(Collectors.toList()));\n        }));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得告警配置简单列表\", description = \"只包含被开启的告警配置，主要用于前端的下拉选项\")\n    @PreAuthorize(\"@ss.hasPermission('iot:alert-config:query')\")\n    public CommonResult<List<IotAlertConfigRespVO>> getAlertConfigSimpleList() {\n        List<IotAlertConfigDO> list = alertConfigService.getAlertConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, config -> // 只返回 id、name 字段\n                new IotAlertConfigRespVO().setId(config.getId()).setName(config.getName())));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertRecordController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.alert;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordProcessReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordRespVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;\nimport cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static java.util.Collections.singleton;\n\n@Tag(name = \"管理后台 - IoT 告警记录\")\n@RestController\n@RequestMapping(\"/iot/alert-record\")\n@Validated\npublic class IotAlertRecordController {\n\n    @Resource\n    private IotAlertRecordService alertRecordService;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得告警记录\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:alert-record:query')\")\n    public CommonResult<IotAlertRecordRespVO> getAlertRecord(@RequestParam(\"id\") Long id) {\n        IotAlertRecordDO alertRecord = alertRecordService.getAlertRecord(id);\n        return success(BeanUtils.toBean(alertRecord, IotAlertRecordRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得告警记录分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:alert-record:query')\")\n    public CommonResult<PageResult<IotAlertRecordRespVO>> getAlertRecordPage(@Valid IotAlertRecordPageReqVO pageReqVO) {\n        PageResult<IotAlertRecordDO> pageResult = alertRecordService.getAlertRecordPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotAlertRecordRespVO.class));\n    }\n\n    @PutMapping(\"/process\")\n    @Operation(summary = \"处理告警记录\")\n    @PreAuthorize(\"@ss.hasPermission('iot:alert-record:process')\")\n    public CommonResult<Boolean> processAlertRecord(@Valid @RequestBody IotAlertRecordProcessReqVO processReqVO) {\n        alertRecordService.processAlertRecordList(singleton(processReqVO.getId()), processReqVO.getProcessRemark());\n        return success(true);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 告警配置分页 Request VO\")\n@Data\npublic class IotAlertConfigPageReqVO extends PageParam {\n\n    @Schema(description = \"配置名称\", example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"配置状态\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - IoT 告警配置 Response VO\")\n@Data\npublic class IotAlertConfigRespVO {\n\n    @Schema(description = \"配置编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3566\")\n    private Long id;\n\n    @Schema(description = \"配置名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"配置描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"告警级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer level;\n\n    @Schema(description = \"配置状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"关联的场景联动规则编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1,2,3\")\n    private List<Long> sceneRuleIds;\n\n    @Schema(description = \"接收的用户编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100,200\")\n    private List<Long> receiveUserIds;\n\n    @Schema(description = \"接收的用户名称数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三,李四\")\n    private List<String> receiveUserNames;\n\n    @Schema(description = \"接收的类型数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1,2,3\")\n    private List<Integer> receiveTypes;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - IoT 告警配置新增/修改 Request VO\")\n@Data\npublic class IotAlertConfigSaveReqVO {\n\n    @Schema(description = \"配置编号\", example = \"3566\")\n    private Long id;\n\n    @Schema(description = \"配置名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    @NotEmpty(message = \"配置名称不能为空\")\n    private String name;\n\n    @Schema(description = \"配置描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"告警级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"告警级别不能为空\")\n    private Integer level;\n\n    @Schema(description = \"配置状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"配置状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"关联的场景联动规则编号数组\")\n    @NotEmpty(message = \"关联的场景联动规则编号数组不能为空\")\n    private List<Long> sceneRuleIds;\n\n    @Schema(description = \"接收的用户编号数组\")\n    @NotEmpty(message = \"接收的用户编号数组不能为空\")\n    private List<Long> receiveUserIds;\n\n    @Schema(description = \"接收的类型数组\")\n    @NotEmpty(message = \"接收的类型数组不能为空\")\n    private List<Integer> receiveTypes;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 告警记录分页 Request VO\")\n@Data\npublic class IotAlertRecordPageReqVO extends PageParam {\n\n    @Schema(description = \"告警配置编号\", example = \"29320\")\n    private Long configId;\n\n    @Schema(description = \"告警级别\", example = \"1\")\n    private Integer level;\n\n    @Schema(description = \"产品编号\", example = \"2050\")\n    private Long productId;\n\n    @Schema(description = \"设备编号\", example = \"21727\")\n    private String deviceId;\n\n    @Schema(description = \"是否处理\", example = \"true\")\n    private Boolean processStatus;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordProcessReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 告警记录处理 Request VO\")\n@Data\npublic class IotAlertRecordProcessReqVO {\n\n    @Schema(description = \"记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"记录编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"处理结果（备注）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"已处理告警，问题已解决\")\n    private String processRemark;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT 告警记录 Response VO\")\n@Data\npublic class IotAlertRecordRespVO {\n\n    @Schema(description = \"记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19904\")\n    private Long id;\n\n    @Schema(description = \"告警配置编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29320\")\n    private Long configId;\n\n    @Schema(description = \"告警名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    private String configName;\n\n    @Schema(description = \"告警级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer configLevel;\n\n    @Schema(description = \"产品编号\", example = \"2050\")\n    private Long productId;\n\n    @Schema(description = \"设备编号\", example = \"21727\")\n    private Long deviceId;\n\n    @Schema(description = \"触发的设备消息\")\n    private IotDeviceMessage deviceMessage;\n\n    @Schema(description = \"是否处理\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Boolean processStatus;\n\n    @Schema(description = \"处理结果（备注）\", example = \"你说的对\")\n    private String processRemark;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n@Tag(name = \"管理后台 - IoT 设备\")\n@RestController\n@RequestMapping(\"/iot/device\")\n@Validated\npublic class IotDeviceController {\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotProductService productService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建设备\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:create')\")\n    public CommonResult<Long> createDevice(@Valid @RequestBody IotDeviceSaveReqVO createReqVO) {\n        return success(deviceService.createDevice(createReqVO));\n    }\n\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新设备\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:update')\")\n    public CommonResult<Boolean> updateDevice(@Valid @RequestBody IotDeviceSaveReqVO updateReqVO) {\n        deviceService.updateDevice(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/bind-gateway\")\n    @Operation(summary = \"绑定子设备到网关\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:update')\")\n    public CommonResult<Boolean> bindDeviceGateway(@Valid @RequestBody IotDeviceBindGatewayReqVO reqVO) {\n        deviceService.bindDeviceGateway(reqVO.getSubIds(), reqVO.getGatewayId());\n        return success(true);\n    }\n\n    @PutMapping(\"/unbind-gateway\")\n    @Operation(summary = \"解绑子设备与网关\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:update')\")\n    public CommonResult<Boolean> unbindDeviceGateway(@Valid @RequestBody IotDeviceUnbindGatewayReqVO reqVO) {\n        deviceService.unbindDeviceGateway(reqVO.getSubIds(), reqVO.getGatewayId());\n        return success(true);\n    }\n\n    @GetMapping(\"/sub-device-list\")\n    @Operation(summary = \"获取网关的子设备列表\")\n    @Parameter(name = \"gatewayId\", description = \"网关设备编号\", required = true, example = \"1\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<List<IotDeviceRespVO>> getSubDeviceList(@RequestParam(\"gatewayId\") Long gatewayId) {\n        List<IotDeviceDO> list = deviceService.getDeviceListByGatewayId(gatewayId);\n        if (CollUtil.isEmpty(list)) {\n            return success(Collections.emptyList());\n        }\n\n        // 补充产品名称\n        Map<Long, IotProductDO> productMap = convertMap(productService.getProductList(), IotProductDO::getId);\n        return success(convertList(list, device -> {\n            IotDeviceRespVO respVO = BeanUtils.toBean(device, IotDeviceRespVO.class);\n            MapUtils.findAndThen(productMap, device.getProductId(),\n                    product -> respVO.setProductName(product.getName()));\n            return respVO;\n        }));\n    }\n\n    @GetMapping(\"/unbound-sub-device-page\")\n    @Operation(summary = \"获取未绑定网关的子设备分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<PageResult<IotDeviceRespVO>> getUnboundSubDevicePage(@Valid IotDevicePageReqVO pageReqVO) {\n        PageResult<IotDeviceDO> pageResult = deviceService.getUnboundSubDevicePage(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n\n        // 补充产品名称\n        Map<Long, IotProductDO> productMap = convertMap(productService.getProductList(), IotProductDO::getId);\n        PageResult<IotDeviceRespVO> result = BeanUtils.toBean(pageResult, IotDeviceRespVO.class, device ->\n                MapUtils.findAndThen(productMap, device.getProductId(), product -> device.setProductName(product.getName())));\n        return success(result);\n    }\n\n    @PutMapping(\"/update-group\")\n    @Operation(summary = \"更新设备分组\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:update')\")\n    public CommonResult<Boolean> updateDeviceGroup(@Valid @RequestBody IotDeviceUpdateGroupReqVO updateReqVO) {\n        deviceService.updateDeviceGroup(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除单个设备\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:device:delete')\")\n    public CommonResult<Boolean> deleteDevice(@RequestParam(\"id\") Long id) {\n        deviceService.deleteDevice(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete-list\")\n    @Operation(summary = \"删除多个设备\")\n    @Parameter(name = \"ids\", description = \"编号数组\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:device:delete')\")\n    public CommonResult<Boolean> deleteDeviceList(@RequestParam(\"ids\") Collection<Long> ids) {\n        deviceService.deleteDeviceList(ids);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得设备\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<IotDeviceRespVO> getDevice(@RequestParam(\"id\") Long id) {\n        IotDeviceDO device = deviceService.getDevice(id);\n        return success(BeanUtils.toBean(device, IotDeviceRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得设备分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<PageResult<IotDeviceRespVO>> getDevicePage(@Valid IotDevicePageReqVO pageReqVO) {\n        PageResult<IotDeviceDO> pageResult = deviceService.getDevicePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotDeviceRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出设备 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportDeviceExcel(@Valid IotDevicePageReqVO exportReqVO,\n                                  HttpServletResponse response) throws IOException {\n        exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        CommonResult<PageResult<IotDeviceRespVO>> result = getDevicePage(exportReqVO);\n        // 导出 Excel\n        ExcelUtils.write(response, \"设备.xls\", \"数据\", IotDeviceRespVO.class,\n                result.getData().getList());\n    }\n\n    @GetMapping(\"/count\")\n    @Operation(summary = \"获得设备数量\")\n    @Parameter(name = \"productId\", description = \"产品编号\", example = \"1\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<Long> getDeviceCount(@RequestParam(\"productId\") Long productId) {\n        return success(deviceService.getDeviceCountByProductId(productId));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获取设备的精简信息列表\", description = \"主要用于前端的下拉选项\")\n    @Parameters({\n            @Parameter(name = \"deviceType\", description = \"设备类型\", example = \"1\"),\n            @Parameter(name = \"productId\", description = \"产品编号\", example = \"1024\")\n    })\n    public CommonResult<List<IotDeviceRespVO>> getDeviceSimpleList(\n            @RequestParam(value = \"deviceType\", required = false) Integer deviceType,\n            @RequestParam(value = \"productId\", required = false) Long productId) {\n        List<IotDeviceDO> list = deviceService.getDeviceListByCondition(deviceType, productId);\n        return success(convertList(list, device ->  // 只返回 id、name、productId 字段\n                new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName())\n                        .setProductId(device.getProductId()).setState(device.getState())));\n    }\n\n    @GetMapping(\"/location-list\")\n    @Operation(summary = \"获取设备位置列表\", description = \"获取有经纬度信息的设备列表，用于地图展示\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<List<IotDeviceRespVO>> getDeviceLocationList() {\n        // 1. 获取有位置信息的设备列表\n        List<IotDeviceDO> devices = deviceService.getDeviceListByHasLocation();\n        if (CollUtil.isEmpty(devices)) {\n            return success(Collections.emptyList());\n        }\n\n        // 2. 转换并返回\n        Map<Long, IotProductDO> productMap = convertMap(productService.getProductList(), IotProductDO::getId);\n        return success(convertList(devices, device -> {\n            IotDeviceRespVO respVO = BeanUtils.toBean(device, IotDeviceRespVO.class);\n            MapUtils.findAndThen(productMap, device.getProductId(),\n                    product -> respVO.setProductName(product.getName()));\n            return respVO;\n        }));\n    }\n\n    @PostMapping(\"/import\")\n    @Operation(summary = \"导入设备\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:import')\")\n    public CommonResult<IotDeviceImportRespVO> importDevice(\n            @RequestParam(\"file\") MultipartFile file,\n            @RequestParam(value = \"updateSupport\", required = false, defaultValue = \"false\") Boolean updateSupport)\n            throws Exception {\n        List<IotDeviceImportExcelVO> list = ExcelUtils.read(file, IotDeviceImportExcelVO.class);\n        return success(deviceService.importDevice(list, updateSupport));\n    }\n\n    @GetMapping(\"/get-import-template\")\n    @Operation(summary = \"获得导入设备模板\")\n    public void importTemplate(HttpServletResponse response) throws IOException {\n        // 手动创建导出 demo\n        List<IotDeviceImportExcelVO> list = Arrays.asList(\n                IotDeviceImportExcelVO.builder().deviceName(\"温度传感器001\").parentDeviceName(\"gateway110\")\n                        .productKey(\"1de24640dfe\").groupNames(\"灰度分组,生产分组\").build(),\n                IotDeviceImportExcelVO.builder().deviceName(\"biubiu\").productKey(\"YzvHxd4r67sT4s2B\")\n                        .groupNames(\"\").build());\n        // 输出\n        ExcelUtils.write(response, \"设备导入模板.xls\", \"数据\", IotDeviceImportExcelVO.class, list);\n    }\n\n    @GetMapping(\"/get-auth-info\")\n    @Operation(summary = \"获得设备连接信息\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:auth-info')\")\n    public CommonResult<IotDeviceAuthInfoRespVO> getDeviceAuthInfo(@RequestParam(\"id\") Long id) {\n        return success(deviceService.getDeviceAuthInfo(id));\n    }\n\n    // TODO @haohao：可以使用 @RequestParam(\"productKey\") String productKey, @RequestParam(\"deviceNames\") List<String> deviceNames 来接收哇？\n    @GetMapping(\"/list-by-product-key-and-names\")\n    @Operation(summary = \"通过产品标识和设备名称列表获取设备\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<List<IotDeviceRespVO>> getDevicesByProductKeyAndNames(@Valid IotDeviceByProductKeyAndNamesReqVO reqVO) {\n        List<IotDeviceDO> devices = deviceService.getDeviceListByProductKeyAndNames(reqVO.getProductKey(), reqVO.getDeviceNames());\n        return success(BeanUtils.toBean(devices, IotDeviceRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceGroupController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceGroupService;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - IoT 设备分组\")\n@RestController\n@RequestMapping(\"/iot/device-group\")\n@Validated\npublic class IotDeviceGroupController {\n\n    @Resource\n    private IotDeviceGroupService deviceGroupService;\n    @Resource\n    private IotDeviceService deviceService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建设备分组\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device-group:create')\")\n    public CommonResult<Long> createDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO createReqVO) {\n        return success(deviceGroupService.createDeviceGroup(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新设备分组\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device-group:update')\")\n    public CommonResult<Boolean> updateDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO updateReqVO) {\n        deviceGroupService.updateDeviceGroup(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除设备分组\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:device-group:delete')\")\n    public CommonResult<Boolean> deleteDeviceGroup(@RequestParam(\"id\") Long id) {\n        deviceGroupService.deleteDeviceGroup(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得设备分组\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device-group:query')\")\n    public CommonResult<IotDeviceGroupRespVO> getDeviceGroup(@RequestParam(\"id\") Long id) {\n        IotDeviceGroupDO deviceGroup = deviceGroupService.getDeviceGroup(id);\n        return success(BeanUtils.toBean(deviceGroup, IotDeviceGroupRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得设备分组分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device-group:query')\")\n    public CommonResult<PageResult<IotDeviceGroupRespVO>> getDeviceGroupPage(@Valid IotDeviceGroupPageReqVO pageReqVO) {\n        PageResult<IotDeviceGroupDO> pageResult = deviceGroupService.getDeviceGroupPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotDeviceGroupRespVO.class,\n                group -> group.setDeviceCount(deviceService.getDeviceCountByGroupId(group.getId()))));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获取设备分组的精简信息列表\", description = \"只包含被开启的分组，主要用于前端的下拉选项\")\n    public CommonResult<List<IotDeviceGroupRespVO>> getSimpleDeviceGroupList() {\n        List<IotDeviceGroupDO> list = deviceGroupService.getDeviceGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, group -> // 只返回 id、name 字段\n                new IotDeviceGroupRespVO().setId(group.getId()).setName(group.getName())));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.http",
    "content": "### 请求 /iot/device/message/send 接口（属性上报）=> 成功\nPOST {{baseUrl}}/iot/device/message/send\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"deviceId\": 25,\n  \"method\": \"thing.property.post\",\n  \"params\": {\n    \"width\": 1,\n    \"height\": \"2\",\n    \"oneThree\": \"3\"\n  }\n}\n\n### 请求 /iot/device/downstream 接口（服务调用）=> 成功 TODO 芋艿：未更新为最新\nPOST {{baseUrl}}/iot/device/downstream\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"id\": 25,\n  \"type\": \"service\",\n  \"identifier\": \"temperature\",\n  \"data\": {\n    \"xx\": \"yy\"\n  }\n}\n\n### 请求 /iot/device/downstream 接口（属性设置）=> 成功 TODO 芋艿：未更新为最新\nPOST {{baseUrl}}/iot/device/downstream\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"id\": 25,\n  \"type\": \"property\",\n  \"identifier\": \"set\",\n  \"data\": {\n    \"xx\": \"yy\"\n  }\n}\n\n### 请求 /iot/device/downstream 接口（属性获取）=> 成功 TODO 芋艿：未更新为最新\nPOST {{baseUrl}}/iot/device/downstream\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"id\": 25,\n  \"type\": \"property\",\n  \"identifier\": \"get\",\n  \"data\": [\"xx\", \"yy\"]\n}\n\n### 请求 /iot/device/downstream 接口（配置设置）=> 成功 TODO 芋艿：未更新为最新\nPOST {{baseUrl}}/iot/device/downstream\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"id\": 25,\n  \"type\": \"config\",\n  \"identifier\": \"set\"\n}\n\n### 请求 /iot/device/downstream 接口（OTA 升级）=> 成功 TODO 芋艿：未更新为最新\nPOST {{baseUrl}}/iot/device/downstream\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"id\": 25,\n  \"type\": \"ota\",\n  \"identifier\": \"upgrade\",\n  \"data\": {\n    \"firmwareId\": 1,\n    \"version\": \"1.0.0\",\n    \"signMethod\": \"MD5\",\n    \"fileSign\": \"d41d8cd98f00b204e9800998ecf8427e\",\n    \"fileSize\": 1024,\n    \"fileUrl\": \"http://example.com/firmware.bin\",\n    \"information\": \"{\\\"desc\\\":\\\"升级到最新版本\\\"}\"\n  }\n}\n\n### 查询设备消息对分页 - 基础查询（设备编号25）\nGET {{baseUrl}}/iot/device/message/pair-page?deviceId=25&pageNo=1&pageSize=10\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 查询设备消息对分页 - 按标识符过滤（identifier=eat）\nGET {{baseUrl}}/iot/device/message/pair-page?deviceId=25&identifier=eat&pageNo=1&pageSize=10\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageRespPairVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageSendReqVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;\nimport cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n@Tag(name = \"管理后台 - IoT 设备消息\")\n@RestController\n@RequestMapping(\"/iot/device/message\")\n@Validated\npublic class IotDeviceMessageController {\n\n    @Resource\n    private IotDeviceMessageService deviceMessageService;\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotThingModelService thingModelService;\n    @Resource\n    private IotDeviceMessageMapper deviceMessageMapper;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得设备消息分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:message-query')\")\n    public CommonResult<PageResult<IotDeviceMessageRespVO>> getDeviceMessagePage(\n            @Valid IotDeviceMessagePageReqVO pageReqVO) {\n        PageResult<IotDeviceMessageDO> pageResult = deviceMessageService.getDeviceMessagePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotDeviceMessageRespVO.class));\n    }\n\n    @GetMapping(\"/pair-page\")\n    @Operation(summary = \"获得设备消息对分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:message-query')\")\n    public CommonResult<PageResult<IotDeviceMessageRespPairVO>> getDeviceMessagePairPage(\n            @Valid IotDeviceMessagePageReqVO pageReqVO) {\n        // 1.1 先按照条件，查询 request 的消息（非 reply）\n        pageReqVO.setReply(false);\n        PageResult<IotDeviceMessageDO> requestMessagePageResult = deviceMessageService.getDeviceMessagePage(pageReqVO);\n        if (CollUtil.isEmpty(requestMessagePageResult.getList())) {\n            return success(PageResult.empty());\n        }\n        // 1.2 接着按照 requestIds，批量查询 reply 消息\n        List<String> requestIds = convertList(requestMessagePageResult.getList(), IotDeviceMessageDO::getRequestId);\n        List<IotDeviceMessageDO> replyMessageList = deviceMessageService.getDeviceMessageListByRequestIdsAndReply(\n                pageReqVO.getDeviceId(), requestIds, true);\n        Map<String, IotDeviceMessageDO> replyMessages = convertMap(replyMessageList, IotDeviceMessageDO::getRequestId);\n\n        // 2. 组装结果\n        List<IotDeviceMessageRespPairVO> pairMessages = convertList(requestMessagePageResult.getList(),\n                requestMessage -> {\n            IotDeviceMessageDO replyMessage = replyMessages.get(requestMessage.getRequestId());\n            return new IotDeviceMessageRespPairVO()\n                    .setRequest(BeanUtils.toBean(requestMessage, IotDeviceMessageRespVO.class))\n                    .setReply(BeanUtils.toBean(replyMessage, IotDeviceMessageRespVO.class));\n        });\n        return success(new PageResult<>(pairMessages, requestMessagePageResult.getTotal()));\n    }\n\n    @PostMapping(\"/send\")\n    @Operation(summary = \"发送消息\", description = \"可用于设备模拟\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:message-end')\")\n    public CommonResult<Boolean> sendDeviceMessage(@Valid @RequestBody IotDeviceMessageSendReqVO sendReqVO) {\n        deviceMessageService.sendDeviceMessage(BeanUtils.toBean(sendReqVO, IotDeviceMessage.class));\n        return success(true);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - IoT 设备 Modbus 连接配置\")\n@RestController\n@RequestMapping(\"/iot/device-modbus-config\")\n@Validated\npublic class IotDeviceModbusConfigController {\n\n    @Resource\n    private IotDeviceModbusConfigService modbusConfigService;\n\n    @PostMapping(\"/save\")\n    @Operation(summary = \"保存设备 Modbus 连接配置\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:update')\")\n    public CommonResult<Boolean> saveDeviceModbusConfig(@Valid @RequestBody IotDeviceModbusConfigSaveReqVO saveReqVO) {\n        modbusConfigService.saveDeviceModbusConfig(saveReqVO);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得设备 Modbus 连接配置\")\n    @Parameter(name = \"id\", description = \"编号\", example = \"1024\")\n    @Parameter(name = \"deviceId\", description = \"设备编号\", example = \"2048\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<IotDeviceModbusConfigRespVO> getDeviceModbusConfig(\n            @RequestParam(value = \"id\", required = false) Long id,\n            @RequestParam(value = \"deviceId\", required = false) Long deviceId) {\n        IotDeviceModbusConfigDO modbusConfig = null;\n        if (id != null) {\n            modbusConfig = modbusConfigService.getDeviceModbusConfig(id);\n        } else if (deviceId != null) {\n            modbusConfig = modbusConfigService.getDeviceModbusConfigByDeviceId(deviceId);\n        }\n        return success(BeanUtils.toBean(modbusConfig, IotDeviceModbusConfigRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - IoT 设备 Modbus 点位配置\")\n@RestController\n@RequestMapping(\"/iot/device-modbus-point\")\n@Validated\npublic class IotDeviceModbusPointController {\n\n    @Resource\n    private IotDeviceModbusPointService modbusPointService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建设备 Modbus 点位配置\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:update')\")\n    public CommonResult<Long> createDeviceModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO createReqVO) {\n        return success(modbusPointService.createDeviceModbusPoint(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新设备 Modbus 点位配置\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:update')\")\n    public CommonResult<Boolean> updateDeviceModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO updateReqVO) {\n        modbusPointService.updateDeviceModbusPoint(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除设备 Modbus 点位配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:device:update')\")\n    public CommonResult<Boolean> deleteDeviceModbusPoint(@RequestParam(\"id\") Long id) {\n        modbusPointService.deleteDeviceModbusPoint(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得设备 Modbus 点位配置\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<IotDeviceModbusPointRespVO> getDeviceModbusPoint(@RequestParam(\"id\") Long id) {\n        IotDeviceModbusPointDO modbusPoint = modbusPointService.getDeviceModbusPoint(id);\n        return success(BeanUtils.toBean(modbusPoint, IotDeviceModbusPointRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得设备 Modbus 点位配置分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:query')\")\n    public CommonResult<PageResult<IotDeviceModbusPointRespVO>> getDeviceModbusPointPage(@Valid IotDeviceModbusPointPageReqVO pageReqVO) {\n        PageResult<IotDeviceModbusPointDO> pageResult = modbusPointService.getDeviceModbusPointPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotDeviceModbusPointRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyDetailRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;\nimport cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - IoT 设备属性\")\n@RestController\n@RequestMapping(\"/iot/device/property\")\n@Validated\npublic class IotDevicePropertyController {\n\n    @Resource\n    private IotDevicePropertyService devicePropertyService;\n    @Resource\n    private IotThingModelService thingModelService;\n    @Resource\n    private IotDeviceService deviceService;\n\n    @GetMapping(\"/get-latest\")\n    @Operation(summary = \"获取设备属性最新属性\")\n    @Parameter(name = \"deviceId\", description = \"设备编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:device:property-query')\")\n    public CommonResult<List<IotDevicePropertyDetailRespVO>> getLatestDeviceProperties(\n            @RequestParam(\"deviceId\") Long deviceId) {\n        // 1.1 获取设备信息\n        IotDeviceDO device = deviceService.getDevice(deviceId);\n        Assert.notNull(device, \"设备不存在\");\n        // 1.2 获取设备最新属性\n        Map<String, IotDevicePropertyDO> properties = devicePropertyService.getLatestDeviceProperties(deviceId);\n        // 1.3 根据 productId + type 查询属性类型的物模型\n        List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductIdAndType(\n                device.getProductId(), IotThingModelTypeEnum.PROPERTY.getType());\n\n        // 2. 基于 thingModels 遍历，拼接 properties\n        return success(convertList(thingModels, thingModel -> {\n            ThingModelProperty thingModelProperty = thingModel.getProperty();\n            Assert.notNull(thingModelProperty, \"属性不能为空\");\n            IotDevicePropertyDetailRespVO result = new IotDevicePropertyDetailRespVO()\n                    .setName(thingModel.getName()).setDataType(thingModelProperty.getDataType())\n                    .setDataSpecs(thingModelProperty.getDataSpecs())\n                    .setDataSpecsList(thingModelProperty.getDataSpecsList());\n            result.setIdentifier(thingModel.getIdentifier());\n            IotDevicePropertyDO property = properties.get(thingModel.getIdentifier());\n            if (property != null) {\n                result.setValue(property.getValue())\n                        .setUpdateTime(LocalDateTimeUtil.toEpochMilli(property.getUpdateTime()));\n            }\n            return result;\n        }));\n    }\n\n    @GetMapping(\"/history-list\")\n    @Operation(summary = \"获取设备属性历史数据列表\")\n    @PreAuthorize(\"@ss.hasPermission('iot:device:property-query')\")\n    public CommonResult<List<IotDevicePropertyRespVO>> getHistoryDevicePropertyList(\n            @Valid IotDevicePropertyHistoryListReqVO listReqVO) {\n        return success(devicePropertyService.getHistoryDevicePropertyList(listReqVO));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceAuthInfoRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\n\n@Schema(description = \"管理后台 - IoT 设备认证信息 Response VO\")\n@Data\npublic class IotDeviceAuthInfoRespVO {\n\n    @Schema(description = \"客户端 ID\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"product123.device001\")\n    @NotBlank(message = \"客户端 ID 不能为空\")\n    private String clientId;\n\n    @Schema(description = \"用户名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"device001&product123\")\n    @NotBlank(message = \"用户名不能为空\")\n    private String username;\n\n    @Schema(description = \"密码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1a2b3c4d5e6f7890abcdef1234567890\")\n    @NotBlank(message = \"密码不能为空\")\n    private String password;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceBindGatewayReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - IoT 设备绑定网关 Request VO\")\n@Data\npublic class IotDeviceBindGatewayReqVO {\n\n    @Schema(description = \"子设备编号列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1,2,3\")\n    @NotEmpty(message = \"子设备编号列表不能为空\")\n    private Set<Long> subIds;\n\n    @Schema(description = \"网关设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"网关设备编号不能为空\")\n    private Long gatewayId;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceByProductKeyAndNamesReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 通过产品标识和设备名称列表获取设备 Request VO\")\n@Data\npublic class IotDeviceByProductKeyAndNamesReqVO {\n\n    @Schema(description = \"产品标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1de24640dfe\")\n    @NotBlank(message = \"产品标识不能为空\")\n    private String productKey;\n\n    @Schema(description = \"设备名称列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"device001,device002\")\n    @NotEmpty(message = \"设备名称列表不能为空\")\n    private List<String> deviceNames;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportExcelVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * 设备 Excel 导入 VO\n */\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class IotDeviceImportExcelVO {\n\n    @ExcelProperty(\"设备名称\")\n    @NotEmpty(message = \"设备名称不能为空\")\n    private String deviceName;\n\n    @ExcelProperty(\"父设备名称\")\n    @Schema(description = \"父设备名称\", example = \"网关001\")\n    private String parentDeviceName;\n\n    @ExcelProperty(\"产品标识\")\n    @NotEmpty(message = \"产品标识不能为空\")\n    private String productKey;\n\n    @ExcelProperty(\"设备分组\")\n    private String groupNames;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Builder;\nimport lombok.Data;\n\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"管理后台 - IoT 设备导入 Response VO\")\n@Data\n@Builder\npublic class IotDeviceImportRespVO {\n\n    @Schema(description = \"创建成功的设备名称数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> createDeviceNames;\n\n    @Schema(description = \"更新成功的设备名称数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> updateDeviceNames;\n\n    @Schema(description = \"导入失败的设备集合,key为设备名称,value为失败原因\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Map<String, String> failureDeviceNames;\n} "
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - IoT 设备分页 Request VO\")\n@Data\npublic class IotDevicePageReqVO extends PageParam {\n\n    @Schema(description = \"设备名称\", example = \"王五\")\n    private String deviceName;\n\n    @Schema(description = \"备注名称\", example = \"张三\")\n    private String nickname;\n\n    @Schema(description = \"产品编号\", example = \"26202\")\n    private Long productId;\n\n    @Schema(description = \"设备类型\", example = \"1\")\n    @InEnum(IotProductDeviceTypeEnum.class)\n    private Integer deviceType;\n\n    @Schema(description = \"设备状态\", example = \"1\")\n    @InEnum(IotDeviceStateEnum.class)\n    private Integer status;\n\n    @Schema(description = \"设备分组编号\", example = \"1024\")\n    private Long groupId;\n\n    @Schema(description = \"网关设备 ID\", example = \"16380\")\n    private Long gatewayId;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.DEVICE_STATE;\n\n@Schema(description = \"管理后台 - IoT 设备 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class IotDeviceRespVO {\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"177\")\n    private Long id;\n\n    @Schema(description = \"设备名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    @ExcelProperty(\"设备名称\")\n    private String deviceName;\n\n    @Schema(description = \"设备备注名称\", example = \"张三\")\n    @ExcelProperty(\"设备备注名称\")\n    private String nickname;\n\n    @Schema(description = \"设备序列号\", example = \"1024\")\n    @ExcelProperty(\"设备序列号\")\n    private String serialNumber;\n\n    @Schema(description = \"设备图片\", example = \"我是一名码农\")\n    @ExcelProperty(\"设备图片\")\n    private String picUrl;\n\n    @Schema(description = \"设备分组编号数组\", example = \"1,2\")\n    private Set<Long> groupIds;\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"26202\")\n    @ExcelProperty(\"产品编号\")\n    private Long productId;\n\n    @Schema(description = \"产品名称\", example = \"温湿度传感器\")\n    private String productName; // 只有部分接口返回，例如 getDeviceLocationList\n\n    @Schema(description = \"产品标识\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品 Key\")\n    private String productKey;\n\n    @Schema(description = \"设备类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(\"设备类型\")\n    private Integer deviceType;\n\n    @Schema(description = \"网关设备 ID\", example = \"16380\")\n    private Long gatewayId;\n\n    @Schema(description = \"设备状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"设备状态\", converter = DictConvert.class)\n    @DictFormat(DEVICE_STATE)\n    private Integer state;\n\n    @Schema(description = \"最后上线时间\")\n    @ExcelProperty(\"最后上线时间\")\n    private LocalDateTime onlineTime;\n\n    @Schema(description = \"最后离线时间\")\n    @ExcelProperty(\"最后离线时间\")\n    private LocalDateTime offlineTime;\n\n    @Schema(description = \"设备激活时间\")\n    @ExcelProperty(\"设备激活时间\")\n    private LocalDateTime activeTime;\n\n    @Schema(description = \"设备密钥，用于设备认证\")\n    @ExcelProperty(\"设备密钥\")\n    private String deviceSecret;\n\n    @Schema(description = \"设备配置\", example = \"{\\\"abc\\\": \\\"efg\\\"}\")\n    private String config;\n\n    @Schema(description = \"设备位置的纬度\", example = \"45.000000\")\n    private BigDecimal latitude;\n\n    @Schema(description = \"设备位置的经度\", example = \"45.000000\")\n    private BigDecimal longitude;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.DecimalMax;\nimport javax.validation.constraints.DecimalMin;\nimport java.math.BigDecimal;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - IoT 设备新增/修改 Request VO\")\n@Data\npublic class IotDeviceSaveReqVO {\n\n    @Schema(description = \"设备编号\", example = \"177\")\n    private Long id;\n\n    @Schema(description = \"设备名称\", requiredMode = Schema.RequiredMode.AUTO, example = \"王五\")\n    private String deviceName;\n\n    @Schema(description = \"备注名称\", example = \"张三\")\n    private String nickname;\n\n    @Schema(description = \"设备序列号\", example = \"123456\")\n    private String serialNumber;\n\n    @Schema(description = \"设备图片\", example = \"https://iocoder.cn/1.png\")\n    private String picUrl;\n\n    @Schema(description = \"设备分组编号数组\", example = \"1,2\")\n    private Set<Long> groupIds;\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"26202\")\n    private Long productId;\n\n    @Schema(description = \"网关设备 ID\", example = \"16380\")\n    private Long gatewayId;\n\n    @Schema(description = \"设备配置\", example = \"{\\\"abc\\\": \\\"efg\\\"}\")\n    private String config;\n\n    @Schema(description = \"设备位置的纬度\", example = \"39.915\")\n    @DecimalMin(value = \"-90\", message = \"纬度范围为 -90 到 90\")\n    @DecimalMax(value = \"90\", message = \"纬度范围为 -90 到 90\")\n    private BigDecimal latitude;\n\n    @Schema(description = \"设备位置的经度\", example = \"116.404\")\n    @DecimalMin(value = \"-180\", message = \"经度范围为 -180 到 180\")\n    @DecimalMax(value = \"180\", message = \"经度范围为 -180 到 180\")\n    private BigDecimal longitude;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUnbindGatewayReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - IoT 设备解绑网关 Request VO\")\n@Data\npublic class IotDeviceUnbindGatewayReqVO {\n\n    @Schema(description = \"子设备编号列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1,2,3\")\n    @NotEmpty(message = \"子设备编号列表不能为空\")\n    private Set<Long> subIds;\n\n    @Schema(description = \"网关设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"网关设备编号不能为空\")\n    private Long gatewayId;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUpdateGroupReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - IoT 设备更新分组 Request VO\")\n@Data\npublic class IotDeviceUpdateGroupReqVO {\n\n    @Schema(description = \"设备编号列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1,2,3\")\n    @NotEmpty(message = \"设备编号列表不能为空\")\n    private Set<Long> ids;\n\n    @Schema(description = \"分组编号列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1,2,3\")\n    @NotEmpty(message = \"分组编号列表不能为空\")\n    private Set<Long> groupIds;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 设备分组分页 Request VO\")\n@Data\npublic class IotDeviceGroupPageReqVO extends PageParam {\n\n    @Schema(description = \"分组名字\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT 设备分组 Response VO\")\n@Data\npublic class IotDeviceGroupRespVO {\n\n    @Schema(description = \"分组 ID\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3583\")\n    private Long id;\n\n    @Schema(description = \"分组名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    private String name;\n\n    @Schema(description = \"分组状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"分组描述\", example = \"你说的对\")\n    private String description;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"设备数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long deviceCount;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 设备分组新增/修改 Request VO\")\n@Data\npublic class IotDeviceGroupSaveReqVO {\n\n    @Schema(description = \"分组 ID\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3583\")\n    private Long id;\n\n    @Schema(description = \"分组名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotEmpty(message = \"分组名字不能为空\")\n    private String name;\n\n    @Schema(description = \"分组状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"分组状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"分组描述\", example = \"你说的对\")\n    private String description;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessagePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 设备消息分页查询 Request VO\")\n@Data\npublic class IotDeviceMessagePageReqVO extends PageParam {\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"设备编号不能为空\")\n    private Long deviceId;\n\n    @Schema(description = \"消息类型\", example = \"property\")\n    @InEnum(IotDeviceMessageMethodEnum.class)\n    private String method;\n\n    @Schema(description = \"是否上行\", example = \"true\")\n    private Boolean upstream;\n\n    @Schema(description = \"是否回复\", example = \"true\")\n    private Boolean reply;\n\n    @Schema(description = \"标识符\", example = \"temperature\")\n    private String identifier;\n\n    @Schema(description = \"时间范围\", requiredMode = Schema.RequiredMode.NOT_REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Size(min = 2, max = 2, message = \"请选择时间范围\")\n    private LocalDateTime[] times;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespPairVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - IoT 设备消息对 Response VO\")\n@Data\npublic class IotDeviceMessageRespPairVO {\n\n    @Schema(description = \"请求消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private IotDeviceMessageRespVO request;\n\n    @Schema(description = \"响应消息\")\n    private IotDeviceMessageRespVO reply; // 通过 requestId 配对\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT 设备消息 Response VO\")\n@Data\npublic class IotDeviceMessageRespVO {\n\n    @Schema(description = \"消息编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String id;\n\n    @Schema(description = \"上报时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime reportTime;\n\n    @Schema(description = \"记录时间戳\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime ts;\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123\")\n    private Long deviceId;\n\n    @Schema(description = \"服务编号\", example = \"server_123\")\n    private String serverId;\n\n    @Schema(description = \"是否上行消息\", example = \"true\", examples = \"false\")\n    private Boolean upstream;\n\n    @Schema(description = \"是否回复消息\", example = \"false\", examples = \"true\")\n    private Boolean reply;\n\n    @Schema(description = \"标识符\", example = \"temperature\")\n    private String identifier;\n\n    // ========== codec（编解码）字段 ==========\n\n    @Schema(description = \"请求编号\", example = \"req_123\")\n    private String requestId;\n\n    @Schema(description = \"请求方法\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"thing.property.post\")\n    private String method;\n\n    @Schema(description = \"请求参数\")\n    private Object params;\n\n    @Schema(description = \"响应结果\")\n    private Object data;\n\n    @Schema(description = \"响应错误码\", example = \"200\")\n    private Integer code;\n\n    @Schema(description = \"响应提示\", example = \"操作成功\")\n    private String msg;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageSendReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 设备消息发送 Request VO\") // 属性上报、事件上报、状态变更等\n@Data\npublic class IotDeviceMessageSendReqVO {\n\n    @Schema(description = \"请求方法\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"report\")\n    @NotEmpty(message = \"请求方法不能为空\")\n    @InEnum(IotDeviceMessageMethodEnum.class)\n    private String method;\n\n    @Schema(description = \"请求参数\")\n    private Object params; // 例如说：属性上报的 properties、事件上报的 params\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"177\")\n    @NotNull(message = \"设备编号不能为空\")\n    private Long deviceId;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT 设备 Modbus 连接配置 Response VO\")\n@Data\npublic class IotDeviceModbusConfigRespVO {\n\n    @Schema(description = \"主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long deviceId;\n\n    @Schema(description = \"设备名称\", example = \"温湿度传感器\")\n    private String deviceName;\n\n    @Schema(description = \"Modbus 服务器 IP 地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"192.168.1.100\")\n    private String ip;\n\n    @Schema(description = \"Modbus 端口\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"502\")\n    private Integer port;\n\n    @Schema(description = \"从站地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer slaveId;\n\n    @Schema(description = \"连接超时时间（毫秒）\", example = \"3000\")\n    private Integer timeout;\n\n    @Schema(description = \"重试间隔（毫秒）\", example = \"1000\")\n    private Integer retryInterval;\n\n    @Schema(description = \"工作模式\", example = \"1\")\n    private Integer mode;\n\n    @Schema(description = \"数据帧格式\", example = \"1\")\n    private Integer frameFormat;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusModeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 设备 Modbus 连接配置新增/修改 Request VO\")\n@Data\npublic class IotDeviceModbusConfigSaveReqVO {\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"设备编号不能为空\")\n    private Long deviceId;\n\n    @Schema(description = \"Modbus 服务器 IP 地址\", example = \"192.168.1.100\")\n    private String ip;\n\n    @Schema(description = \"Modbus 端口\", example = \"502\")\n    private Integer port;\n\n    @Schema(description = \"从站地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"从站地址不能为空\")\n    private Integer slaveId;\n\n    @Schema(description = \"连接超时时间（毫秒）\", example = \"3000\")\n    private Integer timeout;\n\n    @Schema(description = \"重试间隔（毫秒）\", example = \"1000\")\n    private Integer retryInterval;\n\n    @Schema(description = \"工作模式\", example = \"1\")\n    @InEnum(IotModbusModeEnum.class)\n    private Integer mode;\n\n    @Schema(description = \"数据帧格式\", example = \"1\")\n    @InEnum(IotModbusFrameFormatEnum.class)\n    private Integer frameFormat;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - IoT 设备 Modbus 点位配置分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class IotDeviceModbusPointPageReqVO extends PageParam {\n\n    @Schema(description = \"设备编号\", example = \"1024\")\n    private Long deviceId;\n\n    @Schema(description = \"属性标识符\", example = \"temperature\")\n    private String identifier;\n\n    @Schema(description = \"属性名称\", example = \"温度\")\n    private String name;\n\n    @Schema(description = \"Modbus 功能码\", example = \"3\")\n    private Integer functionCode;\n\n    @Schema(description = \"状态\", example = \"0\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT 设备 Modbus 点位配置 Response VO\")\n@Data\npublic class IotDeviceModbusPointRespVO {\n\n    @Schema(description = \"主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long deviceId;\n\n    @Schema(description = \"物模型属性编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long thingModelId;\n\n    @Schema(description = \"属性标识符\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"temperature\")\n    private String identifier;\n\n    @Schema(description = \"属性名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"温度\")\n    private String name;\n\n    @Schema(description = \"Modbus 功能码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3\")\n    private Integer functionCode;\n\n    @Schema(description = \"寄存器起始地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer registerAddress;\n\n    @Schema(description = \"寄存器数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer registerCount;\n\n    @Schema(description = \"字节序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"AB\")\n    private String byteOrder;\n\n    @Schema(description = \"原始数据类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"INT16\")\n    private String rawDataType;\n\n    @Schema(description = \"缩放因子\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1.0\")\n    private BigDecimal scale;\n\n    @Schema(description = \"轮询间隔（毫秒）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5000\")\n    private Integer pollInterval;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.math.BigDecimal;\n\n@Schema(description = \"管理后台 - IoT 设备 Modbus 点位配置新增/修改 Request VO\")\n@Data\npublic class IotDeviceModbusPointSaveReqVO {\n\n    @Schema(description = \"主键\", example = \"1\")\n    private Long id;\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"设备编号不能为空\")\n    private Long deviceId;\n\n    @Schema(description = \"物模型属性编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    @NotNull(message = \"物模型属性编号不能为空\")\n    private Long thingModelId;\n\n    @Schema(description = \"Modbus 功能码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3\")\n    @NotNull(message = \"Modbus 功能码不能为空\")\n    private Integer functionCode;\n\n    @Schema(description = \"寄存器起始地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"寄存器起始地址不能为空\")\n    private Integer registerAddress;\n\n    @Schema(description = \"寄存器数量\", example = \"1\")\n    private Integer registerCount;\n\n    @Schema(description = \"字节序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"AB\")\n    @NotEmpty(message = \"字节序不能为空\")\n    private String byteOrder;\n\n    @Schema(description = \"原始数据类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"INT16\")\n    @NotEmpty(message = \"原始数据类型不能为空\")\n    private String rawDataType;\n\n    @Schema(description = \"缩放因子\", example = \"1.0\")\n    private BigDecimal scale;\n\n    @Schema(description = \"轮询间隔（毫秒）\", example = \"5000\")\n    private Integer pollInterval;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.property;\n\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDataSpecs;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - IoT 设备属性详细 Response VO\") // 额外增加 来自 ThingModelProperty 的变量 属性\n@Data\npublic class IotDevicePropertyDetailRespVO extends IotDevicePropertyRespVO {\n\n    @Schema(description = \"属性名称\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String name;\n\n    @Schema(description = \"数据类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"int\")\n    private String dataType;\n\n    @Schema(description = \"数据定义\")\n    private ThingModelDataSpecs dataSpecs;\n\n    @Schema(description = \"数据定义列表\")\n    private List<ThingModelDataSpecs> dataSpecsList;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyHistoryListReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.property;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 设备属性历史列表 Request VO\")\n@Data\npublic class IotDevicePropertyHistoryListReqVO {\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"177\")\n    @NotNull(message = \"设备编号不能为空\")\n    private Long deviceId;\n\n    @Schema(description = \"属性标识符\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"属性标识符不能为空\")\n    private String identifier;\n\n    @Schema(description = \"时间范围\", requiredMode = Schema.RequiredMode.NOT_REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Size(min = 2, max = 2, message = \"请选择时间范围\")\n    private LocalDateTime[] times;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.device.vo.property;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - IoT 设备属性 Response VO\")\n@Data\npublic class IotDevicePropertyRespVO {\n\n    @Schema(description = \"属性标识符\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String identifier;\n\n    @Schema(description = \"属性值\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Object value;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Long updateTime; // 由于从 TDengine 查询出来的是 Long 类型，所以这里也使用 Long 类型\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - IoT OTA 固件\")\n@RestController\n@RequestMapping(\"/iot/ota/firmware\")\n@Validated\npublic class IotOtaFirmwareController {\n\n    @Resource\n    private IotOtaFirmwareService otaFirmwareService;\n    @Resource\n    private IotProductService productService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建 OTA 固件\")\n    @PreAuthorize(\"@ss.hasPermission('iot:ota-firmware:create')\")\n    public CommonResult<Long> createOtaFirmware(@Valid @RequestBody IotOtaFirmwareCreateReqVO createReqVO) {\n        return success(otaFirmwareService.createOtaFirmware(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新 OTA 固件\")\n    @PreAuthorize(\"@ss.hasPermission('iot:ota-firmware:update')\")\n    public CommonResult<Boolean> updateOtaFirmware(@Valid @RequestBody IotOtaFirmwareUpdateReqVO updateReqVO) {\n        otaFirmwareService.updateOtaFirmware(updateReqVO);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得 OTA 固件\")\n    @PreAuthorize(\"@ss.hasPermission('iot:ota-firmware:query')\")\n    public CommonResult<IotOtaFirmwareRespVO> getOtaFirmware(@RequestParam(\"id\") Long id) {\n        IotOtaFirmwareDO firmware = otaFirmwareService.getOtaFirmware(id);\n        if (firmware == null) {\n            return success(null);\n        }\n        return success(BeanUtils.toBean(firmware, IotOtaFirmwareRespVO.class, o -> {\n            IotProductDO product = productService.getProduct(firmware.getProductId());\n            if (product != null) {\n                o.setProductName(product.getName());\n            }\n        }));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得 OTA 固件分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:ota-firmware:query')\")\n    public CommonResult<PageResult<IotOtaFirmwareRespVO>> getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO) {\n        PageResult<IotOtaFirmwareDO> pageResult = otaFirmwareService.getOtaFirmwarePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotOtaFirmwareRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskRespVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;\nimport cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - IoT OTA 升级任务\")\n@RestController\n@RequestMapping(\"/iot/ota/task\")\n@Validated\npublic class IotOtaTaskController {\n\n    @Resource\n    private IotOtaTaskService otaTaskService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建 OTA 升级任务\")\n    @PreAuthorize(value = \"@ss.hasPermission('iot:ota-task:create')\")\n    public CommonResult<Long> createOtaTask(@Valid @RequestBody IotOtaTaskCreateReqVO createReqVO) {\n        return success(otaTaskService.createOtaTask(createReqVO));\n    }\n\n    @PostMapping(\"/cancel\")\n    @Operation(summary = \"取消 OTA 升级任务\")\n    @Parameter(name = \"id\", description = \"升级任务编号\", required = true)\n    @PreAuthorize(value = \"@ss.hasPermission('iot:ota-task:cancel')\")\n    public CommonResult<Boolean> cancelOtaTask(@RequestParam(\"id\") Long id) {\n        otaTaskService.cancelOtaTask(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得 OTA 升级任务分页\")\n    @PreAuthorize(value = \"@ss.hasPermission('iot:ota-task:query')\")\n    public CommonResult<PageResult<IotOtaTaskRespVO>> getOtaTaskPage(@Valid IotOtaTaskPageReqVO pageReqVO) {\n        PageResult<IotOtaTaskDO> pageResult = otaTaskService.getOtaTaskPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotOtaTaskRespVO.class));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得 OTA 升级任务\")\n    @Parameter(name = \"id\", description = \"升级任务编号\", required = true, example = \"1024\")\n    @PreAuthorize(value = \"@ss.hasPermission('iot:ota-task:query')\")\n    public CommonResult<IotOtaTaskRespVO> getOtaTask(@RequestParam(\"id\") Long id) {\n        IotOtaTaskDO upgradeTask = otaTaskService.getOtaTask(id);\n        return success(BeanUtils.toBean(upgradeTask, IotOtaTaskRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskRecordController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordRespVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService;\nimport cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - IoT OTA 升级任务记录\")\n@RestController\n@RequestMapping(\"/iot/ota/task/record\")\n@Validated\npublic class IotOtaTaskRecordController {\n\n    @Resource\n    private IotOtaTaskRecordService otaTaskRecordService;\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotOtaFirmwareService otaFirmwareService;\n\n    @GetMapping(\"/get-status-statistics\")\n    @Operation(summary = \"获得 OTA 升级记录状态统计\")\n    @Parameters({\n        @Parameter(name = \"firmwareId\", description = \"固件编号\", example = \"1024\"),\n        @Parameter(name = \"taskId\", description = \"升级任务编号\", example = \"2048\")\n    })\n    @PreAuthorize(\"@ss.hasPermission('iot:ota-task-record:query')\")\n    public CommonResult<Map<Integer, Long>> getOtaTaskRecordStatusStatistics(\n            @RequestParam(value = \"firmwareId\", required = false) Long firmwareId,\n            @RequestParam(value = \"taskId\", required = false) Long taskId) {\n        return success(otaTaskRecordService.getOtaTaskRecordStatusStatistics(firmwareId, taskId));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得 OTA 升级记录分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:ota-task-record:query')\")\n    public CommonResult<PageResult<IotOtaTaskRecordRespVO>> getOtaTaskRecordPage(\n            @Valid IotOtaTaskRecordPageReqVO pageReqVO) {\n        PageResult<IotOtaTaskRecordDO> pageResult = otaTaskRecordService.getOtaTaskRecordPage(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n\n         // 批量查询固件信息\n         Map<Long, IotOtaFirmwareDO> firmwareMap = otaFirmwareService.getOtaFirmwareMap(\n            convertSet(pageResult.getList(), IotOtaTaskRecordDO::getFromFirmwareId));\n        Map<Long, IotDeviceDO> deviceMap = deviceService.getDeviceMap(\n            convertSet(pageResult.getList(), IotOtaTaskRecordDO::getDeviceId));\n        // 转换为响应 VO\n        return success(BeanUtils.toBean(pageResult, IotOtaTaskRecordRespVO.class, (vo) -> {\n            MapUtils.findAndThen(firmwareMap, vo.getFromFirmwareId(), firmware ->\n                vo.setFromFirmwareVersion(firmware.getVersion()));\n            MapUtils.findAndThen(deviceMap, vo.getDeviceId(), device ->\n                vo.setDeviceName(device.getDeviceName()));\n        }));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得 OTA 升级记录\")\n    @PreAuthorize(\"@ss.hasPermission('iot:ota-task-record:query')\")\n    @Parameter(name = \"id\", description = \"升级记录编号\", required = true, example = \"1024\")\n    public CommonResult<IotOtaTaskRecordRespVO> getOtaTaskRecord(@RequestParam(\"id\") Long id) {\n        IotOtaTaskRecordDO upgradeRecord = otaTaskRecordService.getOtaTaskRecord(id);\n        return success(BeanUtils.toBean(upgradeRecord, IotOtaTaskRecordRespVO.class));\n    }\n\n    @PutMapping(\"/cancel\")\n    @Operation(summary = \"取消 OTA 升级记录\")\n    @PreAuthorize(\"@ss.hasPermission('iot:ota-task-record:cancel')\")\n    @Parameter(name = \"id\", description = \"升级记录编号\", required = true, example = \"1024\")\n    public CommonResult<Boolean> cancelOtaTaskRecord(@RequestParam(\"id\") Long id) {\n        otaTaskRecordService.cancelOtaTaskRecord(id);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT OTA 固件创建 Request VO\")\n@Data\npublic class IotOtaFirmwareCreateReqVO {\n\n    @Schema(description = \"固件名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"智能开关固件\")\n    @NotEmpty(message = \"固件名称不能为空\")\n    private String name;\n\n    @Schema(description = \"固件描述\", example = \"某品牌型号固件，测试用\")\n    private String description;\n\n    @Schema(description = \"版本号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1.0.0\")\n    @NotEmpty(message = \"版本号不能为空\")\n    private String version;\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"产品编号不能为空\")\n    private Long productId;\n\n    @Schema(description = \"固件文件 URL\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.zip\")\n    @NotEmpty(message = \"固件文件 URL 不能为空\")\n    @URL(message = \"固件文件 URL 格式错误\")\n    private String fileUrl;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT OTA 固件分页 Request VO\")\n@Data\npublic class IotOtaFirmwarePageReqVO extends PageParam {\n\n    @Schema(description = \"固件名称\", example = \"智能开关固件\")\n    private String name;\n\n    @Schema(description = \"产品标识\", example = \"1024\")\n    private String productId;\n\n    @Schema(description = \"创建时间\", example = \"[2022-07-01 00:00:00, 2022-07-01 23:59:59]\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;\n\nimport com.fhs.core.trans.vo.VO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT OTA 固件 Response VO\")\n@Data\npublic class IotOtaFirmwareRespVO implements VO {\n\n    @Schema(description = \"固件编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"固件名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"OTA 固件\")\n    private String name;\n\n    @Schema(description = \"固件描述\")\n    private String description;\n\n    @Schema(description = \"版本号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1.0.0\")\n    private String version;\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long productId;\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"智能设备\")\n    private String productName;\n\n    @Schema(description = \"固件文件 URL\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/firmware.bin\")\n    private String fileUrl;\n\n    @Schema(description = \"固件文件大小\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long fileSize;\n\n    @Schema(description = \"固件文件签名算法\", example = \"MD5\")\n    private String fileDigestAlgorithm;\n\n    @Schema(description = \"固件文件签名结果\", example = \"d41d8cd98f00b204e9800998ecf8427e\")\n    private String fileDigestValue;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime updateTime;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT OTA 固件更新 Request VO\")\n@Data\npublic class IotOtaFirmwareUpdateReqVO {\n\n    @Schema(description = \"固件编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"固件编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"固件名称\", example = \"智能开关固件\")\n    private String name;\n\n    @Schema(description = \"固件描述\", example = \"某品牌型号固件，测试用\")\n    private String description;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - IoT OTA 升级任务创建 Request VO\")\n@Data\npublic class IotOtaTaskCreateReqVO {\n\n    @NotEmpty(message = \"任务名称不能为空\")\n    @Schema(description = \"任务名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"升级任务\")\n    private String name;\n\n    @Schema(description = \"任务描述\", example = \"升级任务\")\n    private String description;\n\n    @Schema(description = \"固件编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"固件编号不能为空\")\n    private Long firmwareId;\n\n    @Schema(description = \"升级范围\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"升级范围不能为空\")\n    @InEnum(value = IotOtaTaskDeviceScopeEnum.class)\n    private Integer deviceScope;\n\n    @Schema(description = \"选中的设备编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1,2,3\")\n    private List<Long> deviceIds;\n\n    // TODO @li：如果 deviceScope 等于 2 时，deviceIds 校验非空；\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - IoT OTA 升级任务分页 Request VO\")\n@Data\npublic class IotOtaTaskPageReqVO extends PageParam {\n\n    @Schema(description = \"任务名称\", example = \"升级任务\")\n    private String name;\n\n    @Schema(description = \"固件编号\", example = \"1024\")\n    private Long firmwareId;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task;\n\nimport com.fhs.core.trans.vo.VO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT OTA 升级任务 Response VO\")\n@Data\npublic class IotOtaTaskRespVO implements VO {\n\n    @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"任务名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"升级任务\")\n    private String name;\n\n    @Schema(description = \"任务描述\", example = \"升级任务\")\n    private String description;\n\n    @Schema(description = \"固件编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long firmwareId;\n\n    @Schema(description = \"任务状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer status;\n\n    @Schema(description = \"升级范围\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer deviceScope;\n\n    @Schema(description = \"设备总共数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer deviceTotalCount;\n\n    @Schema(description = \"设备成功数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"66\")\n    private Integer deviceSuccessCount;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2022-07-08 07:30:00\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - IoT OTA 升级记录分页 Request VO\")\n@Data\npublic class IotOtaTaskRecordPageReqVO extends PageParam {\n\n    @Schema(description = \"升级任务编号\", example = \"1024\")\n    private Long taskId;\n\n    @Schema(description = \"升级记录状态\", example = \"5\")\n    @InEnum(IotOtaTaskRecordStatusEnum.class)\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT OTA 升级任务记录 Response VO\")\n@Data\npublic class IotOtaTaskRecordRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"固件编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long firmwareId;\n\n    @Schema(description = \"任务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long taskId;\n\n    @Schema(description = \"设备编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long deviceId;\n\n    @Schema(description = \"设备名称\", example = \"智能开关\")\n    private String deviceName;\n\n    @Schema(description = \"来源的固件编号\", example = \"1023\")\n    private Long fromFirmwareId;\n\n    @Schema(description = \"来源固件版本\", example = \"1.0.0\")\n    private String fromFirmwareVersion;\n\n    @Schema(description = \"升级状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"升级进度，百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n    private Integer progress;\n\n    @Schema(description = \"升级进度描述\", example = \"正在下载固件...\")\n    private String description;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime updateTime;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.product;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - IoT 产品分类\")\n@RestController\n@RequestMapping(\"/iot/product-category\")\n@Validated\npublic class IotProductCategoryController {\n\n    @Resource\n    private IotProductCategoryService productCategoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建产品分类\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product-category:create')\")\n    public CommonResult<Long> createProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO createReqVO) {\n        return success(productCategoryService.createProductCategory(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新产品分类\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product-category:update')\")\n    public CommonResult<Boolean> updateProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO updateReqVO) {\n        productCategoryService.updateProductCategory(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除产品分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:product-category:delete')\")\n    public CommonResult<Boolean> deleteProductCategory(@RequestParam(\"id\") Long id) {\n        productCategoryService.deleteProductCategory(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product-category:query')\")\n    public CommonResult<IotProductCategoryRespVO> getProductCategory(@RequestParam(\"id\") Long id) {\n        IotProductCategoryDO productCategory = productCategoryService.getProductCategory(id);\n        return success(BeanUtils.toBean(productCategory, IotProductCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得产品分类分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product-category:query')\")\n    public CommonResult<PageResult<IotProductCategoryRespVO>> getProductCategoryPage(@Valid IotProductCategoryPageReqVO pageReqVO) {\n        PageResult<IotProductCategoryDO> pageResult = productCategoryService.getProductCategoryPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotProductCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得所有产品分类列表\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product-category:query')\")\n    public CommonResult<List<IotProductCategoryRespVO>> getSimpleProductCategoryList() {\n        List<IotProductCategoryDO> list = productCategoryService.getProductCategoryListByStatus(\n                CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, category ->\n                new IotProductCategoryRespVO().setId(category.getId()).setName(category.getName())));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.http",
    "content": "### 请求 /iot/product/sync-property-table 接口 => 成功\nPOST {{baseUrl}}/iot/product/sync-property-table\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.product;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - IoT 产品\")\n@RestController\n@RequestMapping(\"/iot/product\")\n@Validated\npublic class IotProductController {\n\n    @Resource\n    private IotProductService productService;\n    @Resource\n    private IotProductCategoryService categoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建产品\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product:create')\")\n    public CommonResult<Long> createProduct(@Valid @RequestBody IotProductSaveReqVO createReqVO) {\n        return success(productService.createProduct(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新产品\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product:update')\")\n    public CommonResult<Boolean> updateProduct(@Valid @RequestBody IotProductSaveReqVO updateReqVO) {\n        productService.updateProduct(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新产品状态\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @Parameter(name = \"status\", description = \"状态\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:product:update')\")\n    public CommonResult<Boolean> updateProductStatus(@RequestParam(\"id\") Long id,\n                                                     @RequestParam(\"status\") Integer status) {\n        productService.updateProductStatus(id, status);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除产品\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:product:delete')\")\n    public CommonResult<Boolean> deleteProduct(@RequestParam(\"id\") Long id) {\n        productService.deleteProduct(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product:query')\")\n    public CommonResult<IotProductRespVO> getProduct(@RequestParam(\"id\") Long id) {\n        IotProductDO product = productService.getProduct(id);\n        if (product == null) {\n            return success(null);\n        }\n        // 拼接数据\n        IotProductCategoryDO category = categoryService.getProductCategory(product.getCategoryId());\n        return success(BeanUtils.toBean(product, IotProductRespVO.class, bean -> {\n            if (category != null) {\n                bean.setCategoryName(category.getName());\n            }\n        }));\n    }\n\n    @GetMapping(\"/get-by-key\")\n    @Operation(summary = \"通过 ProductKey 获得产品\")\n    @Parameter(name = \"productKey\", description = \"产品Key\", required = true, example = \"abc123\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product:query')\")\n    public CommonResult<IotProductRespVO> getProductByKey(@RequestParam(\"productKey\") String productKey) {\n        IotProductDO product = productService.getProductByProductKey(productKey);\n        if (product == null) {\n            return success(null);\n        }\n        // 拼接数据\n        IotProductCategoryDO category = categoryService.getProductCategory(product.getCategoryId());\n        return success(BeanUtils.toBean(product, IotProductRespVO.class, bean -> {\n            if (category != null) {\n                bean.setCategoryName(category.getName());\n            }\n        }));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得产品分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product:query')\")\n    public CommonResult<PageResult<IotProductRespVO>> getProductPage(@Valid IotProductPageReqVO pageReqVO) {\n        PageResult<IotProductDO> pageResult = productService.getProductPage(pageReqVO);\n        // 拼接数据\n        Map<Long, IotProductCategoryDO> categoryMap = categoryService.getProductCategoryMap(\n                convertList(pageResult.getList(), IotProductDO::getCategoryId));\n        return success(BeanUtils.toBean(pageResult, IotProductRespVO.class, bean -> {\n            MapUtils.findAndThen(categoryMap, bean.getCategoryId(),\n                    category -> bean.setCategoryName(category.getName()));\n        }));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出产品 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportProductExcel(@Valid IotProductPageReqVO exportReqVO,\n                                   HttpServletResponse response) throws IOException {\n        exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);\n        CommonResult<PageResult<IotProductRespVO>> result = getProductPage(exportReqVO);\n        // 导出 Excel\n        ExcelUtils.write(response, \"产品.xls\", \"数据\", IotProductRespVO.class,\n                result.getData().getList());\n    }\n\n    @PostMapping(\"/sync-property-table\")\n    @Operation(summary = \"同步产品属性表结构到 TDengine\")\n    @PreAuthorize(\"@ss.hasPermission('iot:product:update')\")\n    public CommonResult<Boolean> syncProductPropertyTable() {\n        productService.syncProductPropertyTable();\n        return success(true);\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获取产品的精简信息列表\", description = \"主要用于前端的下拉选项\")\n    @Parameter(name = \"deviceType\", description = \"设备类型\", example = \"1\")\n    public CommonResult<List<IotProductRespVO>> getProductSimpleList(\n            @RequestParam(value = \"deviceType\", required = false) Integer deviceType) {\n        List<IotProductDO> list = productService.getProductList(deviceType);\n        return success(convertList(list, product -> // 只返回 id、name、productKey 字段\n                new IotProductRespVO().setId(product.getId()).setName(product.getName()).setStatus(product.getStatus())\n                        .setDeviceType(product.getDeviceType()).setProductKey(product.getProductKey())));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 产品分类分页 Request VO\")\n@Data\npublic class IotProductCategoryPageReqVO extends PageParam {\n\n    @Schema(description = \"分类名字\", example = \"王五\")\n    private String name;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.module.system.enums.DictTypeConstants;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT 产品分类 Response VO\")\n@Data\npublic class IotProductCategoryRespVO {\n\n    @Schema(description = \"分类 ID\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25284\")\n    private Long id;\n\n    @Schema(description = \"分类名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    private String name;\n\n    @Schema(description = \"分类排序\")\n    private Integer sort;\n\n    @Schema(description = \"分类状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @DictFormat(DictTypeConstants.COMMON_STATUS)\n    private Integer status;\n\n    @Schema(description = \"分类描述\", example = \"随便\")\n    private String description;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 产品分类新增/修改 Request VO\")\n@Data\npublic class IotProductCategorySaveReqVO {\n\n    @Schema(description = \"分类 ID\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25284\")\n    private Long id;\n\n    @Schema(description = \"分类名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    @NotEmpty(message = \"分类名字不能为空\")\n    private String name;\n\n    @Schema(description = \"分类排序\")\n    private Integer sort;\n\n    @Schema(description = \"分类状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"分类状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"分类描述\", example = \"随便\")\n    private String description;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - IoT 产品分页 Request VO\")\n@Data\npublic class IotProductPageReqVO extends PageParam {\n\n    @Schema(description = \"产品名称\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"产品标识\")\n    private String productKey;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.iot.enums.DictTypeConstants;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT 产品 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class IotProductRespVO {\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"26087\")\n    @ExcelProperty(\"产品编号\")\n    private Long id;\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @ExcelProperty(\"产品名称\")\n    private String name;\n\n    @Schema(description = \"产品标识\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"产品标识\")\n    private String productKey;\n\n    @Schema(description = \"产品密钥\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String productSecret;\n\n    @Schema(description = \"是否开启动态注册\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean registerEnabled;\n\n    @Schema(description = \"产品分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long categoryId;\n\n    @Schema(description = \"产品分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(\"产品分类\")\n    private String categoryName;\n\n    @Schema(description = \"产品图标\", example = \"https://iocoder.cn/1.svg\")\n    @ExcelProperty(\"产品图标\")\n    private String icon;\n\n    @Schema(description = \"产品图片\", example = \"https://iocoder.cn/1.png\")\n    @ExcelProperty(\"产品图片\")\n    private String picUrl;\n\n    @Schema(description = \"产品描述\", example = \"你猜\")\n    @ExcelProperty(\"产品描述\")\n    private String description;\n\n    @Schema(description = \"产品状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"产品状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.PRODUCT_STATUS)\n    private Integer status;\n\n    @Schema(description = \"设备类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(value = \"设备类型\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.PRODUCT_DEVICE_TYPE)\n    private Integer deviceType;\n\n    @Schema(description = \"联网方式\", example = \"2\")\n    @ExcelProperty(value = \"联网方式\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.NET_TYPE)\n    private Integer netType;\n\n    @Schema(description = \"协议类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"mqtt\")\n    @ExcelProperty(value = \"协议类型\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.PROTOCOL_TYPE)\n    private String protocolType;\n\n    @Schema(description = \"序列化类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"json\")\n    @ExcelProperty(value = \"序列化类型\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.SERIALIZE_TYPE)\n    private String serializeType;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 产品新增/修改 Request VO\")\n@Data\npublic class IotProductSaveReqVO {\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.AUTO, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"产品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"温湿度\")\n    @NotEmpty(message = \"产品名称不能为空\")\n    private String name;\n\n    @Schema(description = \"产品 Key\", requiredMode = Schema.RequiredMode.AUTO, example = \"12345abc\")\n    private String productKey;\n\n    @Schema(description = \"产品分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"产品分类编号不能为空\")\n    private Long categoryId;\n\n    @Schema(description = \"产品图标\", example = \"https://iocoder.cn/1.svg\")\n    private String icon;\n\n    @Schema(description = \"产品图片\", example = \"https://iocoder.cn/1.png\")\n    private String picUrl;\n\n    @Schema(description = \"产品描述\", example = \"描述\")\n    private String description;\n\n    @Schema(description = \"设备类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @InEnum(value = IotProductDeviceTypeEnum.class, message = \"设备类型必须是 {value}\")\n    @NotNull(message = \"设备类型不能为空\")\n    private Integer deviceType;\n\n    @Schema(description = \"联网方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @InEnum(value = IotNetTypeEnum.class, message = \"联网方式必须是 {value}\")\n    private Integer netType;\n\n    @Schema(description = \"协议类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"mqtt\")\n    @InEnum(value = IotProtocolTypeEnum.class, message = \"协议类型必须是 {value}\")\n    @NotEmpty(message = \"协议类型不能为空\")\n    private String protocolType;\n\n    @Schema(description = \"序列化类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"json\")\n    @InEnum(value = IotSerializeTypeEnum.class, message = \"序列化类型必须是 {value}\")\n    @NotEmpty(message = \"序列化类型不能为空\")\n    private String serializeType;\n\n    @Schema(description = \"是否开启动态注册\", example = \"false\")\n    @NotNull(message = \"是否开启动态注册不能为空\")\n    private Boolean registerEnabled;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataRuleController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;\nimport cn.iocoder.yudao.module.iot.service.rule.data.IotDataRuleService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - IoT 数据流转规则\")\n@RestController\n@RequestMapping(\"/iot/data-rule\")\n@Validated\npublic class IotDataRuleController {\n\n    @Resource\n    private IotDataRuleService dataRuleService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建数据流转规则\")\n    @PreAuthorize(\"@ss.hasPermission('iot:data-rule:create')\")\n    public CommonResult<Long> createDataRule(@Valid @RequestBody IotDataRuleSaveReqVO createReqVO) {\n        return success(dataRuleService.createDataRule(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新数据流转规则\")\n    @PreAuthorize(\"@ss.hasPermission('iot:data-rule:update')\")\n    public CommonResult<Boolean> updateDataRule(@Valid @RequestBody IotDataRuleSaveReqVO updateReqVO) {\n        dataRuleService.updateDataRule(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除数据流转规则\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:data-rule:delete')\")\n    public CommonResult<Boolean> deleteDataRule(@RequestParam(\"id\") Long id) {\n        dataRuleService.deleteDataRule(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得数据流转规则\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:data-rule:query')\")\n    public CommonResult<IotDataRuleRespVO> getDataRule(@RequestParam(\"id\") Long id) {\n        IotDataRuleDO dataRule = dataRuleService.getDataRule(id);\n        return success(BeanUtils.toBean(dataRule, IotDataRuleRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得数据流转规则分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:data-rule:query')\")\n    public CommonResult<PageResult<IotDataRuleRespVO>> getDataRulePage(@Valid IotDataRulePageReqVO pageReqVO) {\n        PageResult<IotDataRuleDO> pageResult = dataRuleService.getDataRulePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotDataRuleRespVO.class));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;\nimport cn.iocoder.yudao.module.iot.service.rule.data.IotDataSinkService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - IoT 数据流转目的\")\n@RestController\n@RequestMapping(\"/iot/data-sink\")\n@Validated\npublic class IotDataSinkController {\n\n    @Resource\n    private IotDataSinkService dataSinkService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建数据目的\")\n    @PreAuthorize(\"@ss.hasPermission('iot:data-sink:create')\")\n    public CommonResult<Long> createDataSink(@Valid @RequestBody IotDataSinkSaveReqVO createReqVO) {\n        return success(dataSinkService.createDataSink(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新数据目的\")\n    @PreAuthorize(\"@ss.hasPermission('iot:data-sink:update')\")\n    public CommonResult<Boolean> updateDataSink(@Valid @RequestBody IotDataSinkSaveReqVO updateReqVO) {\n        dataSinkService.updateDataSink(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除数据目的\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:data-sink:delete')\")\n    public CommonResult<Boolean> deleteDataSink(@RequestParam(\"id\") Long id) {\n        dataSinkService.deleteDataSink(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得数据目的\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:data-sink:query')\")\n    public CommonResult<IotDataSinkRespVO> getDataSink(@RequestParam(\"id\") Long id) {\n        IotDataSinkDO sink = dataSinkService.getDataSink(id);\n        return success(BeanUtils.toBean(sink, IotDataSinkRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得数据目的分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:data-sink:query')\")\n    public CommonResult<PageResult<IotDataSinkRespVO>> getDataSinkPage(@Valid IotDataSinkPageReqVO pageReqVO) {\n        PageResult<IotDataSinkDO> pageResult = dataSinkService.getDataSinkPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotDataSinkRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获取数据目的的精简信息列表\", description = \"主要用于前端的下拉选项\")\n    public CommonResult<List<IotDataSinkRespVO>> getDataSinkSimpleList() {\n        List<IotDataSinkDO> list = dataSinkService.getDataSinkListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, sink -> // 只返回 id、name 字段\n                new IotDataSinkRespVO().setId(sink.getId()).setName(sink.getName())));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotSceneRuleController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleUpdateStatusReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - IoT 场景联动\")\n@RestController\n@RequestMapping(\"/iot/scene-rule\")\n@Validated\npublic class IotSceneRuleController {\n\n    @Resource\n    private IotSceneRuleService sceneRuleService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建场景联动\")\n    @PreAuthorize(\"@ss.hasPermission('iot:scene-rule:create')\")\n    public CommonResult<Long> createSceneRule(@Valid @RequestBody IotSceneRuleSaveReqVO createReqVO) {\n        return success(sceneRuleService.createSceneRule(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新场景联动\")\n    @PreAuthorize(\"@ss.hasPermission('iot:scene-rule:update')\")\n    public CommonResult<Boolean> updateSceneRule(@Valid @RequestBody IotSceneRuleSaveReqVO updateReqVO) {\n        sceneRuleService.updateSceneRule(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新场景联动状态\")\n    @PreAuthorize(\"@ss.hasPermission('iot:scene-rule:update')\")\n    public CommonResult<Boolean> updateSceneRuleStatus(@Valid @RequestBody IotSceneRuleUpdateStatusReqVO updateReqVO) {\n        sceneRuleService.updateSceneRuleStatus(updateReqVO.getId(), updateReqVO.getStatus());\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除场景联动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:scene-rule:delete')\")\n    public CommonResult<Boolean> deleteSceneRule(@RequestParam(\"id\") Long id) {\n        sceneRuleService.deleteSceneRule(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得场景联动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:scene-rule:query')\")\n    public CommonResult<IotSceneRuleRespVO> getSceneRule(@RequestParam(\"id\") Long id) {\n        IotSceneRuleDO sceneRule = sceneRuleService.getSceneRule(id);\n        return success(BeanUtils.toBean(sceneRule, IotSceneRuleRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得场景联动分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:scene-rule:query')\")\n    public CommonResult<PageResult<IotSceneRuleRespVO>> getSceneRulePage(@Valid IotSceneRulePageReqVO pageReqVO) {\n        PageResult<IotSceneRuleDO> pageResult = sceneRuleService.getSceneRulePage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotSceneRuleRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获取场景联动的精简信息列表\", description = \"主要用于前端的下拉选项\")\n    public CommonResult<List<IotSceneRuleRespVO>> getSceneRuleSimpleList() {\n        List<IotSceneRuleDO> list = sceneRuleService.getSceneRuleListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(convertList(list, scene -> // 只返回 id、name 字段\n                new IotSceneRuleRespVO().setId(scene.getId()).setName(scene.getName())));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data;"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRulePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 数据流转规则分页 Request VO\")\n@Data\npublic class IotDataRulePageReqVO extends PageParam {\n\n    @Schema(description = \"数据流转规则名称\", example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"数据流转规则状态\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule;\n\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - IoT 数据流转规则 Response VO\")\n@Data\npublic class IotDataRuleRespVO {\n\n    @Schema(description = \"数据流转规则编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8540\")\n    private Long id;\n\n    @Schema(description = \"数据流转规则名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    private String name;\n\n    @Schema(description = \"数据流转规则描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"数据流转规则状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"数据源配置数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<IotDataRuleDO.SourceConfig> sourceConfigs;\n\n    @Schema(description = \"数据目的编号数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Long> sinkIds;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - IoT 数据流转规则新增/修改 Request VO\")\n@Data\npublic class IotDataRuleSaveReqVO {\n\n    @Schema(description = \"数据流转规则编号\", example = \"8540\")\n    private Long id;\n\n    @Schema(description = \"数据流转规则名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    @NotEmpty(message = \"数据流转规则名称不能为空\")\n    private String name;\n\n    @Schema(description = \"数据流转规则描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"数据流转规则状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"数据流转规则状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"数据源配置数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"数据源配置数组不能为空\")\n    private List<IotDataRuleDO.SourceConfig> sourceConfigs;\n\n    @Schema(description = \"数据目的编号数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"数据目的编号数组不能为空\")\n    private List<Long> sinkIds;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 数据流转目的分页 Request VO\")\n@Data\npublic class IotDataSinkPageReqVO extends PageParam {\n\n    @Schema(description = \"数据目的名称\", example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"数据目的状态\", example = \"2\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"数据目的类型\", example = \"1\")\n    @InEnum(IotDataSinkTypeEnum.class)\n    private Integer type;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink;\n\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotAbstractDataSinkConfig;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT 数据流转目的 Response VO\")\n@Data\npublic class IotDataSinkRespVO {\n\n    @Schema(description = \"数据目的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18564\")\n    private Long id;\n\n    @Schema(description = \"数据目的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"数据目的描述\", example = \"随便\")\n    private String description;\n\n    @Schema(description = \"数据目的状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"数据目的类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"数据目的配置\")\n    private IotAbstractDataSinkConfig config;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotAbstractDataSinkConfig;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 数据流转目的新增/修改 Request VO\")\n@Data\npublic class IotDataSinkSaveReqVO {\n\n    @Schema(description = \"数据目的编号\", example = \"18564\")\n    private Long id;\n\n    @Schema(description = \"数据目的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    @NotEmpty(message = \"数据目的名称不能为空\")\n    private String name;\n\n    @Schema(description = \"数据目的描述\", example = \"随便\")\n    private String description;\n\n    @Schema(description = \"数据目的状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"数据目的状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"数据目的类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"数据目的类型不能为空\")\n    @InEnum(IotDataSinkTypeEnum.class)\n    private Integer type;\n\n    @Schema(description = \"数据目的配置\")\n    @NotNull(message = \"数据目的配置不能为空\")\n    private IotAbstractDataSinkConfig config;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRulePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 场景联动分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class IotSceneRulePageReqVO extends PageParam {\n\n    @Schema(description = \"场景名称\", example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"场景描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"场景状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;\n\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - IoT 场景联动 Response VO\")\n@Data\npublic class IotSceneRuleRespVO {\n\n    @Schema(description = \"场景编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15865\")\n    private Long id;\n\n    @Schema(description = \"场景名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"场景描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"场景状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"触发器数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<IotSceneRuleDO.Trigger> triggers;\n\n    @Schema(description = \"执行器数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<IotSceneRuleDO.Action> actions;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - IoT 场景联动新增/修改 Request VO\")\n@Data\npublic class IotSceneRuleSaveReqVO {\n\n    @Schema(description = \"场景编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15865\")\n    private Long id;\n\n    @Schema(description = \"场景名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    @NotEmpty(message = \"场景名称不能为空\")\n    private String name;\n\n    @Schema(description = \"场景描述\", example = \"你猜\")\n    private String description;\n\n    @Schema(description = \"场景状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"场景状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"触发器数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"触发器数组不能为空\")\n    private List<IotSceneRuleDO.Trigger> triggers;\n\n    @Schema(description = \"执行器数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"执行器数组不能为空\")\n    private List<IotSceneRuleDO.Action> actions;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleUpdateStatusReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 场景联动更新状态 Request VO\")\n@Data\npublic class IotSceneRuleUpdateStatusReqVO {\n\n    @Schema(description = \"场景联动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"场景联动编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"状态不能为空\")\n    @InEnum(value = CommonStatusEnum.class, message = \"修改状态必须是 {value}\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.http",
    "content": "### 请求 /iot/statistics/get-device-message-summary-by-date 接口（小时）\nGET {{baseUrl}}/iot/statistics/get-device-message-summary-by-date?interval=0&times[0]=2025-06-13 00:00:00&times[1]=2025-06-14 23:59:59\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n\n### 请求 /iot/statistics/get-device-message-summary-by-date 接口（天）\nGET {{baseUrl}}/iot/statistics/get-device-message-summary-by-date?interval=1&times[0]=2025-06-13 00:00:00&times[1]=2025-06-14 23:59:59\nContent-Type: application/json\ntenant-id: {{adminTenantId}}\nAuthorization: Bearer {{token}}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.statistics;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - IoT 数据统计\")\n@RestController\n@RequestMapping(\"/iot/statistics\")\n@Validated\npublic class IotStatisticsController {\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotProductCategoryService productCategoryService;\n    @Resource\n    private IotProductService productService;\n    @Resource\n    private IotDeviceMessageService deviceMessageService;\n\n    @GetMapping(\"/get-summary\")\n    @Operation(summary = \"获取全局的数据统计\")\n    public CommonResult<IotStatisticsSummaryRespVO> getStatisticsSummary(){\n        IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO();\n        // 1.1 获取总数\n        respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null));\n        respVO.setProductCount(productService.getProductCount(null));\n        respVO.setDeviceCount(deviceService.getDeviceCount(null));\n        respVO.setDeviceMessageCount(deviceMessageService.getDeviceMessageCount(null));\n        // 1.2 获取今日新增数量\n        LocalDateTime todayStart = LocalDateTimeUtils.getToday();\n        respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart));\n        respVO.setProductTodayCount(productService.getProductCount(todayStart));\n        respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart));\n        respVO.setDeviceMessageTodayCount(deviceMessageService.getDeviceMessageCount(todayStart));\n\n        // 2. 获取各个品类下设备数量统计\n        respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap());\n\n        // 3. 获取设备状态数量统计\n        Map<Integer, Long> deviceCountMap = deviceService.getDeviceCountMapByState();\n        respVO.setDeviceOnlineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.ONLINE.getState(), 0L));\n        respVO.setDeviceOfflineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.OFFLINE.getState(), 0L));\n        respVO.setDeviceInactiveCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.INACTIVE.getState(), 0L));\n        return success(respVO);\n    }\n\n    @GetMapping(\"/get-device-message-summary-by-date\")\n    @Operation(summary = \"获取设备消息的数据统计\")\n    public CommonResult<List<IotStatisticsDeviceMessageSummaryByDateRespVO>> getDeviceMessageSummaryByDate(\n            @Valid IotStatisticsDeviceMessageReqVO reqVO) {\n        return success(deviceMessageService.getDeviceMessageSummaryByDate(reqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo;\n\nimport cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - IoT 设备消息数量统计 Response VO\")\n@Data\npublic class IotStatisticsDeviceMessageReqVO {\n\n    @Schema(description = \"时间间隔类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @InEnum(value = DateIntervalEnum.class, message = \"时间间隔类型，必须是 {value}\")\n    private Integer interval;\n\n    @Schema(description = \"时间范围\", requiredMode = Schema.RequiredMode.NOT_REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Size(min = 2, max = 2, message = \"请选择时间范围\")\n    private LocalDateTime[] times;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryByDateRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - IoT 设备消息数量统计 Response VO\")\n@Data\npublic class IotStatisticsDeviceMessageSummaryByDateRespVO {\n\n    @Schema(description = \"时间轴\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"202401\")\n    private String time;\n\n    @Schema(description = \"上行消息数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer upstreamCount;\n\n    @Schema(description = \"上行消息数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Integer downstreamCount;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.Map;\n\n/**\n * 管理后台 - IoT 统计 Response VO\n */\n@Schema(description = \"管理后台 - IoT 统计 Response VO\")\n@Data\npublic class IotStatisticsSummaryRespVO {\n\n    @Schema(description = \"品类数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long productCategoryCount;\n\n    @Schema(description = \"产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Long productCount;\n\n    @Schema(description = \"设备数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Long deviceCount;\n\n    @Schema(description = \"上报数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    private Long deviceMessageCount;\n\n    @Schema(description = \"今日新增品类数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long productCategoryTodayCount;\n\n    @Schema(description = \"今日新增产品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Long productTodayCount;\n\n    @Schema(description = \"今日新增设备数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Long deviceTodayCount;\n\n    @Schema(description = \"今日新增上报数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    private Long deviceMessageTodayCount;\n\n    @Schema(description = \"在线数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"80\")\n    private Long deviceOnlineCount;\n\n    @Schema(description = \"离线数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15\")\n    private Long deviceOfflineCount;\n\n    @Schema(description = \"待激活设备数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    private Long deviceInactiveCount;\n\n    @Schema(description = \"按品类统计的设备数量\")\n    private Map<String, Integer> productCategoryDeviceCounts;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http",
    "content": "### 请求 /iot/product-thing-model/create 接口 => 成功\nPOST {{baseUrl}}/iot/product-thing-model/create\nContent-Type: application/json\ntenant-id: {{adminTenentId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"productId\": 12,\n  \"productKey\": \"CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5\",\n  \"identifier\": \"Temperature\",\n  \"name\": \"温度\",\n  \"description\": \"当前温度值\",\n  \"type\": 1,\n  \"property\": {\n    \"identifier\": \"Temperature\",\n    \"name\": \"温度\",\n    \"accessMode\": \"r\",\n    \"required\": true,\n    \"dataType\": \"int\",\n    \"dataSpecs\": {\n      \"dataType\": \"int\",\n      \"max\": \"200\",\n      \"min\": \"0\",\n      \"step\": \"10\",\n      \"defaultValue\": \"30\",\n      \"unit\": \"%\",\n      \"unitName\": \"百分比\"\n    }\n  }\n}\n\n### 请求 /iot/product-thing-model/create 接口 => 成功\nPOST {{baseUrl}}/iot/product-thing-model/create\nContent-Type: application/json\ntenant-id: {{adminTenentId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"productId\": 12,\n  \"productKey\": \"CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5\",\n  \"identifier\": \"switch\",\n  \"name\": \"开关\",\n  \"description\": \"温度计开关\",\n  \"type\": 1,\n  \"property\": {\n    \"identifier\": \"switch\",\n    \"name\": \"开关\",\n    \"accessMode\": \"rw\",\n    \"required\": true,\n    \"dataType\": \"bool\",\n    \"dataSpecsList\": [\n      {\n        \"dataType\": \"bool\",\n        \"name\": \"关\",\n        \"value\": 0\n      },\n      {\n        \"dataType\": \"bool\",\n        \"name\": \"开\",\n        \"value\": 1\n      }\n    ]\n  }\n}\n\n### 请求 /iot/product-thing-model/create 接口 => 成功\nPOST {{baseUrl}}/iot/product-thing-model/create\nContent-Type: application/json\ntenant-id: {{adminTenentId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"productId\": 12,\n  \"productKey\": \"CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5\",\n  \"identifier\": \"argb\",\n  \"name\": \"温度计 argb 颜色\",\n  \"description\": \"温度计 argb 颜色\",\n  \"type\": 1,\n  \"property\": {\n    \"identifier\": \"argb\",\n    \"name\": \"温度计 argb 颜色\",\n    \"accessMode\": \"rw\",\n    \"required\": true,\n    \"dataType\": \"array\",\n    \"dataSpecs\": {\n      \"dataType\": \"array\",\n      \"size\": 10,\n      \"childDataType\": \"struct\",\n      \"dataSpecsList\": [\n        {\n          \"identifier\": \"switch\",\n          \"name\": \"开关\",\n          \"accessMode\": \"rw\",\n          \"required\": true,\n          \"dataType\": \"struct\",\n          \"childDataType\": \"bool\",\n          \"dataSpecsList\": [\n            {\n              \"dataType\": \"bool\",\n              \"name\": \"关\",\n              \"value\": 0\n            },\n            {\n              \"dataType\": \"bool\",\n              \"name\": \"开\",\n              \"value\": 1\n            }\n          ]\n        },\n        {\n          \"identifier\": \"Temperature\",\n          \"name\": \"温度\",\n          \"accessMode\": \"r\",\n          \"required\": true,\n          \"dataType\": \"struct\",\n          \"childDataType\": \"int\",\n          \"dataSpecs\": {\n            \"dataType\": \"int\",\n            \"max\": \"200\",\n            \"min\": \"0\",\n            \"step\": \"10\",\n            \"defaultValue\": \"30\",\n            \"unit\": \"%\",\n            \"unitName\": \"百分比\"\n          }\n        }\n      ]\n    }\n  }\n}\n\n### 请求 /iot/product-thing-model/update 接口 => 成功\nPUT {{baseUrl}}/iot/product-thing-model/update\nContent-Type: application/json\ntenant-id: {{adminTenentId}}\nAuthorization: Bearer {{token}}\n\n{\n  \"id\": 33,\n  \"productId\": 12,\n  \"productKey\": \"CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5\",\n  \"identifier\": \"switch\",\n  \"name\": \"开关\",\n  \"description\": \"温度计开关\",\n  \"type\": 1,\n  \"property\": {\n    \"identifier\": \"switch\",\n    \"name\": \"开关\",\n    \"accessMode\": \"r\",\n    \"required\": true,\n    \"dataType\": \"bool\",\n    \"dataSpecsList\": [\n      {\n        \"dataType\": \"bool\",\n        \"name\": \"关\",\n        \"value\": 0\n      },\n      {\n        \"dataType\": \"bool\",\n        \"name\": \"开\",\n        \"value\": 1\n      }\n    ]\n  }\n}\n\n### 请求 /iot/product-thing-model/delete 接口 => 成功\nDELETE {{baseUrl}}/iot/product-thing-model/delete?id=36\ntenant-id: {{adminTenentId}}\nAuthorization: Bearer {{token}}\n\n### 请求 /iot/product-thing-model/get 接口 => 成功\nGET {{baseUrl}}/iot/product-thing-model/get?id=67\ntenant-id: {{adminTenentId}}\nAuthorization: Bearer {{token}}\n\n### 请求 /iot/product-thing-model/get-tsl 接口 => 成功\nGET {{baseUrl}}/iot/product-thing-model/get-tsl?productId=1001\ntenant-id: {{adminTenentId}}\nAuthorization: Bearer {{token}}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.thingmodel;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.*;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;\nimport com.google.common.base.Objects;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;\n\n@Tag(name = \"管理后台 - IoT 产品物模型\")\n@RestController\n@RequestMapping(\"/iot/thing-model\")\n@Validated\npublic class IotThingModelController {\n\n    @Resource\n    private IotThingModelService thingModelService;\n    @Resource\n    private IotProductService productService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建产品物模型\")\n    @PreAuthorize(\"@ss.hasPermission('iot:thing-model:create')\")\n    public CommonResult<Long> createThingModel(@Valid @RequestBody IotThingModelSaveReqVO createReqVO) {\n        return success(thingModelService.createThingModel(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新产品物模型\")\n    @PreAuthorize(\"@ss.hasPermission('iot:thing-model:update')\")\n    public CommonResult<Boolean> updateThingModel(@Valid @RequestBody IotThingModelSaveReqVO updateReqVO) {\n        thingModelService.updateThingModel(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除产品物模型\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:thing-model:delete')\")\n    public CommonResult<Boolean> deleteThingModel(@RequestParam(\"id\") Long id) {\n        thingModelService.deleteThingModel(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得产品物模型\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('iot:thing-model:query')\")\n    public CommonResult<IotThingModelRespVO> getThingModel(@RequestParam(\"id\") Long id) {\n        IotThingModelDO thingModel = thingModelService.getThingModel(id);\n        return success(BeanUtils.toBean(thingModel, IotThingModelRespVO.class));\n    }\n\n    @GetMapping(\"/get-tsl\")\n    @Operation(summary = \"获得产品物模型 TSL\")\n    @Parameter(name = \"productId\", description = \"产品 ID\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('iot:thing-model:query')\")\n    public CommonResult<IotThingModelTSLRespVO> getThingModelTsl(@RequestParam(\"productId\") Long productId) {\n        // 1. 获得产品\n        IotProductDO product = productService.getProduct(productId);\n        if (product == null) {\n            return success(null);\n        }\n        IotThingModelTSLRespVO tslRespVO = new IotThingModelTSLRespVO()\n                .setProductId(product.getId()).setProductKey(product.getProductKey());\n        // 2. 获得物模型定义\n        List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductId(productId);\n        tslRespVO.setProperties(convertList(filterList(thingModels, item ->\n                        Objects.equal(IotThingModelTypeEnum.PROPERTY.getType(), item.getType())), IotThingModelDO::getProperty))\n                .setServices(convertList(filterList(thingModels, item ->\n                        Objects.equal(IotThingModelTypeEnum.SERVICE.getType(), item.getType())), IotThingModelDO::getService))\n                .setEvents(convertList(filterList(thingModels, item ->\n                        Objects.equal(IotThingModelTypeEnum.EVENT.getType(), item.getType())), IotThingModelDO::getEvent));\n        return success(tslRespVO);\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得产品物模型列表\")\n    @PreAuthorize(\"@ss.hasPermission('iot:thing-model:query')\")\n    public CommonResult<List<IotThingModelRespVO>> getThingModelListByProductId(@Valid IotThingModelListReqVO reqVO) {\n        List<IotThingModelDO> list = thingModelService.getThingModelList(reqVO);\n        return success(BeanUtils.toBean(list, IotThingModelRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得产品物模型分页\")\n    @PreAuthorize(\"@ss.hasPermission('iot:thing-model:query')\")\n    public CommonResult<PageResult<IotThingModelRespVO>> getThingModelPage(@Valid IotThingModelPageReqVO pageReqVO) {\n        PageResult<IotThingModelDO> pageResult = thingModelService.getProductThingModelPage(pageReqVO);\n        return success(BeanUtils.toBean(pageResult, IotThingModelRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 产品物模型 List Request VO\")\n@Data\npublic class IotThingModelListReqVO {\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"产品编号不能为空\")\n    private Long productId;\n\n    @Schema(description = \"功能标识\", example = \"temperature\")\n    private String identifier;\n\n    @Schema(description = \"功能名称\", example = \"温度\")\n    private String name;\n\n    @Schema(description = \"功能类型\", example = \"1\")\n    @InEnum(IotThingModelTypeEnum.class)\n    private Integer type;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 产品物模型分页 Request VO\")\n@Data\npublic class IotThingModelPageReqVO extends PageParam {\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"产品编号不能为空\")\n    private Long productId;\n\n    @Schema(description = \"功能标识\", example = \"temperature\")\n    private String identifier;\n\n    @Schema(description = \"功能名称\", example = \"温度\")\n    private String name;\n\n    @Schema(description = \"功能类型\", example = \"1\")\n    @InEnum(IotThingModelTypeEnum.class)\n    private Integer type;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo;\n\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - IoT 产品物模型 Response VO\")\n@Data\npublic class IotThingModelRespVO {\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"21816\")\n    private Long id;\n\n    @Schema(description = \"产品标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long productId;\n\n    @Schema(description = \"产品标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"temperature_sensor\")\n    private String productKey;\n\n    @Schema(description = \"功能标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"temperature\")\n    private String identifier;\n\n    @Schema(description = \"功能名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"温度\")\n    private String name;\n\n    @Schema(description = \"功能描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"测量当前环境温度\")\n    private String description;\n\n    @Schema(description = \"功能类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"属性\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private ThingModelProperty property;\n\n    @Schema(description = \"服务\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private ThingModelEvent event;\n\n    @Schema(description = \"事件\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private ThingModelService service;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - IoT 产品物模型新增/修改 Request VO\")\n@Data\npublic class IotThingModelSaveReqVO {\n\n    @Schema(description = \"编号\", example = \"1\")\n    private Long id;\n\n    @Schema(description = \"产品ID\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"产品ID不能为空\")\n    private Long productId;\n\n    @Schema(description = \"产品标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"temperature_001\")\n    @NotEmpty(message = \"产品标识不能为空\")\n    private String productKey;\n\n    @Schema(description = \"功能标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"temp_monitor\")\n    @NotEmpty(message = \"功能标识不能为空\")\n    private String identifier;\n\n    @Schema(description = \"功能名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"温度监测器\")\n    @NotEmpty(message = \"功能名称不能为空\")\n    private String name;\n\n    @Schema(description = \"功能描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"用于监测环境温度的传感器\")\n    private String description;\n\n    @Schema(description = \"功能类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"功能类型不能为空\")\n    @InEnum(IotThingModelTypeEnum.class)\n    private Integer type;\n\n    @Schema(description = \"属性\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @Valid\n    private ThingModelProperty property;\n\n    @Schema(description = \"服务\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @Valid\n    private ThingModelService service;\n\n    @Schema(description = \"事件\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @Valid\n    private ThingModelEvent event;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelTSLRespVO.java",
    "content": "package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo;\n\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - IoT 产品物模型 TSL Response VO\")\n@Data\npublic class IotThingModelTSLRespVO {\n\n    @Schema(description = \"产品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long productId;\n\n    @Schema(description = \"产品标识\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"temperature_sensor\")\n    private String productKey;\n\n    @Schema(description = \"属性列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<ThingModelProperty> properties;\n\n    @Schema(description = \"服务列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<ThingModelEvent> events;\n\n    @Schema(description = \"事件列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<ThingModelService> services;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/controller/package-info.java",
    "content": "/**\n * 提供 RESTful API 给前端：\n * 1. admin 包：提供给管理后台 yudao-ui-admin 前端项目\n * 2. app 包：提供给用户 APP yudao-ui-app 前端项目，它的 Controller 和 VO 都要添加 App 前缀，用于和管理后台进行区分\n */\npackage cn.iocoder.yudao.module.iot.controller;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java",
    "content": "/**\n * 提供 POJO 类的实体转换\n *\n * 目前使用 MapStruct 框架\n */\npackage cn.iocoder.yudao.module.iot.convert;"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java",
    "content": "package cn.iocoder.yudao.module.iot.convert.thingmodel;\n\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Named;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.Objects;\n\n@Mapper\npublic interface IotThingModelConvert {\n\n    IotThingModelConvert INSTANCE = Mappers.getMapper(IotThingModelConvert.class);\n\n    @Mapping(target = \"property\", expression = \"java(convertToProperty(bean))\")\n    @Mapping(target = \"event\", expression = \"java(convertToEvent(bean))\")\n    @Mapping(target = \"service\", expression = \"java(convertToService(bean))\")\n    IotThingModelDO convert(IotThingModelSaveReqVO bean);\n\n    @Named(\"convertToProperty\")\n    default ThingModelProperty convertToProperty(IotThingModelSaveReqVO bean) {\n        if (Objects.equals(bean.getType(), IotThingModelTypeEnum.PROPERTY.getType())) {\n            return bean.getProperty();\n        }\n        return null;\n    }\n\n    @Named(\"convertToEvent\")\n    default ThingModelEvent convertToEvent(IotThingModelSaveReqVO bean) {\n        if (Objects.equals(bean.getType(), IotThingModelTypeEnum.EVENT.getType())) {\n            return bean.getEvent();\n        }\n        return null;\n    }\n\n    @Named(\"convertToService\")\n    default ThingModelService convertToService(IotThingModelSaveReqVO bean) {\n        if (Objects.equals(bean.getType(), IotThingModelTypeEnum.SERVICE.getType())) {\n            return bean.getService();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertConfigDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.alert;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.iot.enums.alert.IotAlertReceiveTypeEnum;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n/**\n * IoT 告警配置 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"iot_alert_config\", autoResultMap = true)\n@KeySequence(\"iot_alert_config_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotAlertConfigDO extends BaseDO {\n\n    /**\n     * 配置编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 配置名称\n     */\n    private String name;\n    /**\n     * 配置描述\n     */\n    private String description;\n    /**\n     * 配置状态\n     *\n     * 字典 {@link DictTypeConstants#ALERT_LEVEL}\n     */\n    private Integer level;\n    /**\n     * 配置状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n    /**\n     * 关联的场景联动规则编号数组\n     *\n     * 关联 {@link IotSceneRuleDO#getId()}\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> sceneRuleIds;\n\n    /**\n     * 接收的用户编号数组\n     *\n     * 关联 {@link AdminUserRespDTO#getId()}\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> receiveUserIds;\n    /**\n     * 接收的类型数组\n     *\n     * 枚举 {@link IotAlertReceiveTypeEnum}\n     */\n    @TableField(typeHandler = IntegerListTypeHandler.class)\n    private List<Integer> receiveTypes;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertRecordDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.alert;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 告警记录 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"iot_alert_record\", autoResultMap = true)\n@KeySequence(\"iot_alert_record_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotAlertRecordDO extends BaseDO {\n\n    /**\n     * 记录编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 告警名称\n     *\n     * 冗余 {@link IotAlertConfigDO#getId()}\n     */\n    private Long configId;\n    /**\n     * 告警名称\n     *\n     * 冗余 {@link IotAlertConfigDO#getName()}\n     */\n    private String configName;\n    /**\n     * 告警级别\n     *\n     * 冗余 {@link IotAlertConfigDO#getLevel()}\n     * 字典 {@link cn.iocoder.yudao.module.iot.enums.DictTypeConstants#ALERT_LEVEL}\n     */\n    private Integer configLevel;\n    /**\n     * 场景规则编号\n     *\n     * 关联 {@link IotSceneRuleDO#getId()}\n     */\n    private Long sceneRuleId;\n\n    /**\n     * 产品编号\n     *\n     * 关联 {@link IotProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 设备编号\n     *\n     * 关联 {@link IotDeviceDO#getId()}\n     */\n    private Long deviceId;\n    /**\n     * 触发的设备消息\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private IotDeviceMessage deviceMessage;\n\n    /**\n     * 是否处理\n     */\n    private Boolean processStatus;\n    /**\n     * 处理结果（备注）\n     */\n    private String processRemark;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.device;\n\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler;\nimport cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.Set;\n\n/**\n * IoT 设备 DO\n *\n * @author haohao\n */\n@TableName(value = \"iot_device\", autoResultMap = true)\n@KeySequence(\"iot_device_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceDO extends TenantBaseDO {\n\n    /**\n     * 设备编号 - 全部设备\n     */\n    public static final Long DEVICE_ID_ALL = 0L;\n\n    /**\n     * 设备 ID，主键，自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * 设备名称，在产品内唯一，用于标识设备\n     */\n    private String deviceName;\n    /**\n     * 设备备注名称\n     */\n    private String nickname;\n    /**\n     * 设备序列号\n     */\n    private String serialNumber;\n    /**\n     * 设备图片\n     */\n    private String picUrl;\n    /**\n     * 设备分组编号集合\n     *\n     * 关联 {@link IotDeviceGroupDO#getId()}\n     */\n    @TableField(typeHandler = LongSetTypeHandler.class)\n    private Set<Long> groupIds;\n\n    /**\n     * 产品编号\n     * <p>\n     * 关联 {@link IotProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品标识\n     * <p>\n     * 冗余 {@link IotProductDO#getProductKey()}\n     */\n    private String productKey;\n    /**\n     * 设备类型\n     * <p>\n     * 冗余 {@link IotProductDO#getDeviceType()}\n     */\n    private Integer deviceType;\n    /**\n     * 网关设备编号\n     * <p>\n     * 子设备需要关联的网关设备 ID\n     * <p>\n     * 关联 {@link IotDeviceDO#getId()}\n     */\n    private Long gatewayId;\n\n    /**\n     * 设备状态\n     * <p>\n     * 枚举 {@link IotDeviceStateEnum}\n     */\n    private Integer state;\n    /**\n     * 最后上线时间\n     */\n    private LocalDateTime onlineTime;\n    /**\n     * 最后离线时间\n     */\n    private LocalDateTime offlineTime;\n    /**\n     * 设备激活时间\n     */\n    private LocalDateTime activeTime;\n\n    /**\n     * 固件编号\n     *\n     * 关联 {@link IotOtaFirmwareDO#getId()}\n     */\n    private Long firmwareId;\n\n    /**\n     * 设备密钥，用于设备认证\n     */\n    private String deviceSecret;\n\n    /**\n     * 设备位置的纬度\n     */\n    private BigDecimal latitude;\n    /**\n     * 设备位置的经度\n     */\n    private BigDecimal longitude;\n\n    /**\n     * 设备配置\n     *\n     * JSON 格式，可下发给 device 进行自定义配置\n     */\n    private String config;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.device;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 设备分组 DO\n *\n * @author 芋道源码\n */\n@TableName(\"iot_device_group\")\n@KeySequence(\"iot_device_group_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceGroupDO extends BaseDO {\n\n    /**\n     * 分组 ID\n     */\n    @TableId\n    private Long id;\n    /**\n     * 分组名字\n     */\n    private String name;\n    /**\n     * 分组状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 分组描述\n     */\n    private String description;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.device;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 设备消息数据 DO\n *\n * 目前使用 TDengine 存储\n *\n * @author alwayssuper\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceMessageDO {\n\n    /**\n     * 消息编号\n     */\n    private String id;\n    /**\n     * 上报时间戳\n     */\n    private Long reportTime;\n    /**\n     * 存储时间戳\n     */\n    private Long ts;\n\n    /**\n     * 设备编号\n     *\n     * 关联 {@link IotDeviceDO#getId()}\n     */\n    private Long deviceId;\n    /**\n     * 租户编号\n     */\n    private Long tenantId;\n\n    /**\n     * 服务编号，该消息由哪个 server 发送\n     */\n    private String serverId;\n\n    /**\n     * 是否上行消息\n     *\n     * 由 {@link cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils#isUpstreamMessage(IotDeviceMessage)} 计算。\n     * 计算并存储的目的：方便计算多少条上行、多少条下行\n     */\n    private Boolean upstream;\n    /**\n     * 是否回复消息\n     *\n     * 由 {@link cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils#isReplyMessage(IotDeviceMessage)} 计算。\n     * 计算并存储的目的：方便计算多少条请求、多少条回复\n     */\n    private Boolean reply;\n    /**\n     * 标识符\n     *\n     * 例如说：{@link IotThingModelDO#getIdentifier()}\n     * 目前，只有事件上报、服务调用才有！！！\n     */\n    private String identifier;\n\n    // ========== codec（编解码）字段 ==========\n\n    /**\n     * 请求编号\n     *\n     * 由设备生成，对应阿里云 IoT 的 Alink 协议中的 id、华为云 IoTDA 协议的 request_id\n     */\n    private String requestId;\n    /**\n     * 请求方法\n     *\n     * 枚举 {@link IotDeviceMessageMethodEnum}\n     * 例如说：thing.property.post 属性上报\n     */\n    private String method;\n    /**\n     * 请求参数\n     *\n     * 例如说：属性上报的 properties、事件上报的 params\n     */\n    private Object params;\n    /**\n     * 响应结果\n     */\n    private Object data;\n    /**\n     * 响应错误码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String msg;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusConfigDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.device;\n\nimport cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusModeEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 设备 Modbus 连接配置 DO\n *\n * @author 芋道源码\n */\n@TableName(\"iot_device_modbus_config\")\n@KeySequence(\"iot_device_modbus_config_seq\")\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceModbusConfigDO extends TenantBaseDO {\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 产品编号\n     *\n     * 关联 {@link IotProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 设备编号\n     *\n     * 关联 {@link IotDeviceDO#getId()}\n     */\n    private Long deviceId;\n\n    /**\n     * Modbus 服务器 IP 地址\n     */\n    private String ip;\n    /**\n     * Modbus 服务器端口\n     */\n    private Integer port;\n    /**\n     * 从站地址\n     */\n    private Integer slaveId;\n    /**\n     * 连接超时时间，单位：毫秒\n     */\n    private Integer timeout;\n    /**\n     * 重试间隔，单位：毫秒\n     */\n    private Integer retryInterval;\n    /**\n     * 模式\n     *\n     * @see IotModbusModeEnum\n     */\n    private Integer mode;\n    /**\n     * 数据帧格式\n     *\n     * @see IotModbusFrameFormatEnum\n     */\n    private Integer frameFormat;\n    /**\n     * 状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusPointDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.device;\n\nimport cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusByteOrderEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusRawDataTypeEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.math.BigDecimal;\n\n/**\n * IoT 设备 Modbus 点位配置 DO\n *\n * @author 芋道源码\n */\n@TableName(\"iot_device_modbus_point\")\n@KeySequence(\"iot_device_modbus_point_seq\")\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDeviceModbusPointDO extends TenantBaseDO {\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 设备编号\n     *\n     * 关联 {@link IotDeviceDO#getId()}\n     */\n    private Long deviceId;\n    /**\n     * 物模型属性编号\n     *\n     * 关联 {@link IotThingModelDO#getId()}\n     */\n    private Long thingModelId;\n    /**\n     * 属性标识符\n     *\n     * 冗余 {@link IotThingModelDO#getIdentifier()}\n     */\n    private String identifier;\n    /**\n     * 属性名称\n     *\n     * 冗余 {@link IotThingModelDO#getName()}\n     */\n    private String name;\n\n    // ========== Modbus 协议配置 ==========\n\n    /**\n     * Modbus 功能码\n     *\n     * 取值范围：FC01-04（读线圈、读离散输入、读保持寄存器、读输入寄存器）\n     */\n    private Integer functionCode;\n    /**\n     * 寄存器起始地址\n     */\n    private Integer registerAddress;\n    /**\n     * 寄存器数量\n     */\n    private Integer registerCount;\n    /**\n     * 字节序\n     *\n     * 枚举 {@link IotModbusByteOrderEnum}\n     */\n    private String byteOrder;\n    /**\n     * 原始数据类型\n     *\n     * 枚举 {@link IotModbusRawDataTypeEnum}\n     */\n    private String rawDataType;\n    /**\n     * 缩放因子\n     */\n    private BigDecimal scale;\n    /**\n     * 轮询间隔（毫秒）\n     */\n    private Integer pollInterval;\n    /**\n     * 状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDevicePropertyDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.device;\n\nimport cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.time.LocalDateTime;\n\n/**\n * IoT 设备属性项 Redis DO\n *\n * @see cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants#DEVICE_PROPERTY\n * @see DevicePropertyRedisDAO\n *\n * @author haohao\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDevicePropertyDO {\n\n    /**\n     * 属性值（最新）\n     */\n    private Object value;\n\n    /**\n     * 更新时间\n     */\n    private LocalDateTime updateTime;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.ota;\n\nimport cn.hutool.crypto.digest.DigestAlgorithm;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT OTA 固件 DO\n *\n * @see <a href=\"阿里云 IoT - OTA 升级\">https://help.aliyun.com/zh/iot/user-guide/ota-upgrade-overview</a>\n *\n * @author 芋道源码\n */\n@TableName(value = \"iot_ota_firmware\", autoResultMap = true)\n@KeySequence(\"iot_ota_firmware_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotOtaFirmwareDO extends BaseDO {\n\n    /**\n     * 固件编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 固件名称\n     */\n    private String name;\n    /**\n     * 固件描述\n     */\n    private String description;\n    /**\n     * 版本号\n     */\n    private String version;\n\n    /**\n     * 产品编号\n     *\n     * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()}\n     */\n    private Long productId;\n\n    /**\n     * 固件文件 URL\n     */\n    private String fileUrl;\n    /**\n     * 固件文件大小\n     */\n    private Long fileSize;\n    /**\n     * 固件文件签名算法\n     *\n     * 枚举 {@link DigestAlgorithm}，目前只使用 MD5\n     */\n    private String fileDigestAlgorithm;\n    /**\n     * 固件文件签名结果\n     */\n    private String fileDigestValue;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.ota;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT OTA 升级任务 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"iot_ota_task\", autoResultMap = true)\n@KeySequence(\"iot_ota_task_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotOtaTaskDO extends BaseDO {\n\n    /**\n     * 任务编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 任务名称\n     */\n    private String name;\n    /**\n     * 任务描述\n     */\n    private String description;\n\n    /**\n     * 固件编号\n     * <p>\n     * 关联 {@link IotOtaFirmwareDO#getId()}\n     */\n    private Long firmwareId;\n\n    /**\n     * 任务状态\n     * <p>\n     * 关联 {@link IotOtaTaskStatusEnum}\n     */\n    private Integer status;\n\n    /**\n     * 设备升级范围\n     * <p>\n     * 关联 {@link IotOtaTaskDeviceScopeEnum}\n     */\n    private Integer deviceScope;\n    /**\n     * 设备总数数量\n     */\n    private Integer deviceTotalCount;\n    /**\n     * 设备成功数量\n     */\n    private Integer deviceSuccessCount;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskRecordDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.ota;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT OTA 升级任务记录 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"iot_ota_task_record\", autoResultMap = true)\n@KeySequence(\"iot_ota_task_record_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotOtaTaskRecordDO extends BaseDO {\n\n    public static final String DESCRIPTION_CANCEL_BY_TASK = \"管理员手动取消升级任务（批量）\";\n\n    public static final String DESCRIPTION_CANCEL_BY_RECORD = \"管理员手动取消升级记录（单个）\";\n\n    /**\n     * 升级记录编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 固件编号\n     *\n     * 关联 {@link IotOtaFirmwareDO#getId()}\n     */\n    private Long firmwareId;\n    /**\n     * 任务编号\n     *\n     * 关联 {@link IotOtaTaskDO#getId()}\n     */\n    private Long taskId;\n\n    /**\n     * 设备编号\n     *\n     * 关联 {@link IotDeviceDO#getId()}\n     */\n    private Long deviceId;\n    /**\n     * 来源的固件编号\n     *\n     * 关联 {@link IotDeviceDO#getFirmwareId()}\n     */\n    private Long fromFirmwareId;\n\n    /**\n     * 升级状态\n     *\n     * 关联 {@link IotOtaTaskRecordStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 升级进度，百分比\n     */\n    private Integer progress;\n    /**\n     * 升级进度描述\n     *\n     * 注意，只记录设备最后一次的升级进度描述\n     * 如果想看历史记录，可以查看 {@link IotDeviceMessageDO} 设备日志\n     */\n    private String description;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.product;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 产品分类 DO\n *\n * @author 芋道源码\n */\n@TableName(\"iot_product_category\")\n@KeySequence(\"iot_product_category_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotProductCategoryDO extends BaseDO {\n\n    /**\n     * 分类 ID\n     */\n    @TableId\n    private Long id;\n    /**\n     * 分类名字\n     */\n    private String name;\n    /**\n     * 分类排序\n     */\n    private Integer sort;\n    /**\n     * 分类状态\n     *\n     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 分类描述\n     */\n    private String description;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.product;\n\nimport cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 产品 DO\n *\n * @author ahh\n */\n@TableName(\"iot_product\")\n@KeySequence(\"iot_product_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotProductDO extends TenantBaseDO {\n\n    /**\n     * 产品 ID\n     */\n    @TableId\n    private Long id;\n    /**\n     * 产品名称\n     */\n    private String name;\n    /**\n     * 产品标识\n     */\n    private String productKey;\n    /**\n     * 产品密钥，用于一型一密动态注册\n     */\n    private String productSecret;\n    /**\n     * 是否开启动态注册\n     */\n    private Boolean registerEnabled;\n    /**\n     * 产品分类编号\n     * <p>\n     * 关联 {@link IotProductCategoryDO#getId()}\n     */\n    private Long categoryId;\n    /**\n     * 产品图标\n     */\n    private String icon;\n    /**\n     * 产品图片\n     */\n    private String picUrl;\n    /**\n     * 产品描述\n     */\n    private String description;\n\n    /**\n     * 产品状态\n     * <p>\n     * 枚举 {@link cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 设备类型\n     * <p>\n     * 枚举 {@link cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum}\n     */\n    private Integer deviceType;\n    /**\n     * 联网方式\n     * <p>\n     * 枚举 {@link cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum}\n     */\n    private Integer netType;\n    /**\n     * 协议类型\n     * <p>\n     * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum}\n     */\n    private String protocolType;\n    /**\n     * 序列化类型\n     * <p>\n     * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum}\n     */\n    private String serializeType;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\n\n/**\n * IoT 数据流转规则 DO\n *\n * 监听 {@link SourceConfig} 数据源，转发到 {@link IotDataSinkDO} 数据目的\n *\n * @author 芋道源码\n */\n@TableName(value = \"iot_data_rule\", autoResultMap = true)\n@KeySequence(\"iot_data_rule_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDataRuleDO extends BaseDO {\n\n    /**\n     * 数据流转规格编号\n     */\n    private Long id;\n    /**\n     * 数据流转规格名称\n     */\n    private String name;\n    /**\n     * 数据流转规格描述\n     */\n    private String description;\n    /**\n     * 数据流转规格状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n    /**\n     * 数据源配置数组\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<SourceConfig> sourceConfigs;\n    /**\n     * 数据目的编号数组\n     *\n     * 关联 {@link IotDataSinkDO#getId()}\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> sinkIds;\n\n    // TODO @芋艿：未来考虑使用 groovy；支持数据处理；\n\n    /**\n     * 数据源配置\n     */\n    @Data\n    public static class SourceConfig {\n\n        /**\n         * 消息方法\n         *\n         * 枚举 {@link IotDeviceMessageMethodEnum} 中的 upstream 上行部分\n         */\n        @NotEmpty(message = \"消息方法不能为空\")\n        private String method;\n\n        /**\n         * 产品编号\n         *\n         * 关联 {@link IotProductDO#getId()}\n         */\n        private Long productId;\n        /**\n         * 设备编号\n         *\n         * 关联 {@link IotDeviceDO#getId()}\n         * 特殊：如果为 {@link IotDeviceDO#DEVICE_ID_ALL} 时，则是全部设备\n         */\n        @NotEmpty(message = \"设备编号不能为空\")\n        private Long deviceId;\n\n        /**\n         * 标识符\n         *\n         * 1. 物模型时，对应：{@link IotThingModelDO#getIdentifier()}\n         */\n        private String identifier;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotAbstractDataSinkConfig;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 数据流转目的 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"iot_data_sink\", autoResultMap = true)\n@KeySequence(\"iot_data_bridge_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotDataSinkDO extends BaseDO {\n\n    /**\n     * 数据流转目的编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 数据流转目的名称\n     */\n    private String name;\n    /**\n     * 数据流转目的描述\n     */\n    private String description;\n    /**\n     * 数据流转目的状态\n     *\n     *  枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n    /**\n     * 数据流转目的类型\n     *\n     * 枚举 {@link IotDataSinkTypeEnum}\n     */\n    private Integer type;\n    /**\n     * 数据流转目的配置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private IotAbstractDataSinkConfig config;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotSceneRuleDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * IoT 场景联动规则 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"iot_scene_rule\", autoResultMap = true)\n@KeySequence(\"iot_scene_rule_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotSceneRuleDO extends TenantBaseDO {\n\n    /**\n     * 场景联动编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 场景联动名称\n     */\n    private String name;\n    /**\n     * 场景联动描述\n     */\n    private String description;\n    /**\n     * 场景联动状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n    /**\n     * 最后触发时间\n     */\n    private LocalDateTime lastTriggerTime;\n\n    /**\n     * 场景定义配置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<Trigger> triggers;\n\n    /**\n     * 场景动作配置\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<Action> actions;\n\n    /**\n     * 场景定义配置\n     */\n    @Data\n    public static class Trigger {\n\n        // ========== 事件部分 ==========\n\n        /**\n         * 场景事件类型\n         *\n         * 枚举 {@link IotSceneRuleTriggerTypeEnum}\n         * 1. {@link IotSceneRuleTriggerTypeEnum#DEVICE_STATE_UPDATE} 时，operator 非空，并且 value 为在线状态\n         * 2. {@link IotSceneRuleTriggerTypeEnum#DEVICE_PROPERTY_POST}\n         *    {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST} 时，identifier、operator 非空，并且 value 为属性值\n         * 3. {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST}\n         *    {@link IotSceneRuleTriggerTypeEnum#DEVICE_SERVICE_INVOKE} 时，identifier 非空，但是 operator、value 为空\n         * 4. {@link IotSceneRuleTriggerTypeEnum#TIMER} 时，conditions 非空，并且设备无关（无需 productId、deviceId 字段）\n         */\n        private Integer type;\n\n        /**\n         * 产品编号\n         *\n         * 关联 {@link IotProductDO#getId()}\n         */\n        private Long productId;\n        /**\n         * 设备编号\n         *\n         * 关联 {@link IotDeviceDO#getId()}\n         * 特殊：如果为 {@link IotDeviceDO#DEVICE_ID_ALL} 时，则是全部设备\n         */\n        private Long deviceId;\n        /**\n         * 物模型标识符\n         *\n         * 对应：{@link IotThingModelDO#getIdentifier()}\n         */\n        private String identifier;\n        /**\n         * 操作符\n         *\n         * 枚举 {@link IotSceneRuleConditionOperatorEnum}\n         */\n        private String operator;\n        /**\n         * 参数（属性值、在线状态）\n         * <p>\n         * 如果有多个值，则使用 \",\" 分隔，类似 \"1,2,3\"。\n         * 例如说，{@link IotSceneRuleConditionOperatorEnum#IN}、{@link IotSceneRuleConditionOperatorEnum#BETWEEN}\n         */\n        private String value;\n\n        /**\n         * CRON 表达式\n         */\n        private String cronExpression;\n\n        // ========== 条件部分 ==========\n\n        /**\n         * 触发条件分组（状态条件分组）的数组\n         * <p>\n         * 第一层 List：分组与分组之间，是“或”的关系\n         * 第二层 List：条件与条件之间，是“且”的关系\n         */\n        private List<List<TriggerCondition>> conditionGroups;\n\n    }\n\n    /**\n     * 触发条件（状态条件）\n     */\n    @Data\n    public static class TriggerCondition {\n\n        /**\n         * 触发条件类型\n         *\n         * 枚举 {@link IotSceneRuleConditionTypeEnum}\n         * 1. {@link IotSceneRuleConditionTypeEnum#DEVICE_STATE} 时，operator 非空，并且 value 为在线状态\n         * 2. {@link IotSceneRuleConditionTypeEnum#DEVICE_PROPERTY} 时，identifier、operator 非空，并且 value 为属性值\n         * 3. {@link IotSceneRuleConditionTypeEnum#CURRENT_TIME} 时，operator 非空（使用 DATE_TIME_ 和 TIME_ 部分），并且 value 非空\n         */\n        private Integer type;\n\n        /**\n         * 产品编号\n         *\n         * 关联 {@link IotProductDO#getId()}\n         */\n        private Long productId;\n        /**\n         * 设备编号\n         *\n         * 关联 {@link IotDeviceDO#getId()}\n         */\n        private Long deviceId;\n        /**\n         * 标识符（属性）\n         *\n         * 关联 {@link IotThingModelDO#getIdentifier()}\n         */\n        private String identifier;\n        /**\n         * 操作符\n         *\n         * 枚举 {@link IotSceneRuleConditionOperatorEnum}\n         */\n        private String operator;\n        /**\n         * 参数\n         *\n         * 如果有多个值，则使用 \",\" 分隔，类似 \"1,2,3\"。\n         * 例如说，{@link IotSceneRuleConditionOperatorEnum#IN}、{@link IotSceneRuleConditionOperatorEnum#BETWEEN}\n         */\n        private String param;\n\n    }\n\n    /**\n     * 场景动作配置\n     */\n    @Data\n    public static class Action {\n\n        /**\n         * 执行类型\n         *\n         * 枚举 {@link IotSceneRuleActionTypeEnum}\n         * 1. {@link IotSceneRuleActionTypeEnum#DEVICE_PROPERTY_SET} 时，params 非空\n         *    {@link IotSceneRuleActionTypeEnum#DEVICE_SERVICE_INVOKE} 时，params 非空\n         * 2. {@link IotSceneRuleActionTypeEnum#ALERT_TRIGGER} 时，alertConfigId 为空，因为是 {@link IotAlertConfigDO} 里面关联它\n         * 3. {@link IotSceneRuleActionTypeEnum#ALERT_RECOVER} 时，alertConfigId 非空\n         */\n        private Integer type;\n\n        /**\n         * 产品编号\n         *\n         * 关联 {@link IotProductDO#getId()}\n         */\n        private Long productId;\n        /**\n         * 设备编号\n         *\n         * 关联 {@link IotDeviceDO#getId()}\n         */\n        private Long deviceId;\n\n        /**\n         * 标识符（服务）\n         * <p>\n         * 关联 {@link IotThingModelDO#getIdentifier()}\n         */\n        private String identifier;\n\n        /**\n         * 请求参数\n         *\n         * 一般来说，对应 {@link IotDeviceMessage#getParams()} 请求参数\n         */\n        private String params;\n\n        /**\n         * 告警配置编号\n         *\n         * 关联 {@link IotAlertConfigDO#getId()}\n         */\n        private Long alertConfigId;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotAbstractDataSinkConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;\n\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport com.fasterxml.jackson.annotation.JsonSubTypes;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport lombok.Data;\n\n/**\n * IoT IotDataBridgeConfig 抽象类\n *\n * 用于表示数据目的配置数据的通用类型，根据具体的 \"type\" 字段动态映射到对应的子类\n * 提供多态支持，适用于不同类型的数据结构序列化和反序列化场景。\n *\n * @author HUIHUI\n */\n@Data\n@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = \"type\", visible = true)\n@JsonSubTypes({\n        @JsonSubTypes.Type(value = IotDataSinkHttpConfig.class, name = \"1\"),\n        @JsonSubTypes.Type(value = IotDataSinkTcpConfig.class, name = \"2\"),\n        @JsonSubTypes.Type(value = IotDataSinkWebSocketConfig.class, name = \"3\"),\n        @JsonSubTypes.Type(value = IotDataSinkMqttConfig.class, name = \"10\"),\n        @JsonSubTypes.Type(value = IotDataSinkRedisConfig.class, name = \"21\"),\n        @JsonSubTypes.Type(value = IotDataSinkRocketMQConfig.class, name = \"30\"),\n        @JsonSubTypes.Type(value = IotDataSinkRabbitMQConfig.class, name = \"31\"),\n        @JsonSubTypes.Type(value = IotDataSinkKafkaConfig.class, name = \"32\"),\n})\npublic abstract class IotAbstractDataSinkConfig {\n\n    /**\n     * 配置类型\n     *\n     * 枚举 {@link IotDataSinkTypeEnum#getType()}\n     */\n    private String type;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkHttpConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;\n\nimport lombok.Data;\n\nimport java.util.Map;\n\n/**\n * IoT HTTP 配置 {@link IotAbstractDataSinkConfig} 实现类\n *\n * @author HUIHUI\n */\n@Data\npublic class IotDataSinkHttpConfig extends IotAbstractDataSinkConfig {\n\n    /**\n     * 请求 URL\n     */\n    private String url;\n    /**\n     * 请求方法\n     */\n    private String method;\n    /**\n     * 请求头\n     */\n    private Map<String, String> headers;\n    /**\n     * 请求参数\n     */\n    private Map<String, String> query;\n    /**\n     * 请求体\n     */\n    private String body;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkKafkaConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;\n\nimport lombok.Data;\n\n/**\n * IoT Kafka 配置 {@link IotAbstractDataSinkConfig} 实现类\n *\n * @author HUIHUI\n */\n@Data\npublic class IotDataSinkKafkaConfig extends IotAbstractDataSinkConfig {\n\n    /**\n     * Kafka 服务器地址\n     */\n    private String bootstrapServers;\n    /**\n     * 用户名\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 是否启用 SSL\n     */\n    private Boolean ssl;\n\n    /**\n     * 主题\n     */\n    private String topic;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkMqttConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;\n\nimport lombok.Data;\n\n/**\n * IoT MQTT 配置 {@link IotAbstractDataSinkConfig} 实现类\n *\n * @author HUIHUI\n */\n@Data\npublic class IotDataSinkMqttConfig extends IotAbstractDataSinkConfig {\n\n    /**\n     * MQTT 服务器地址\n     */\n    private String url;\n    /**\n     * 用户名\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 客户端编号\n     */\n    private String clientId;\n    /**\n     * 主题\n     */\n    private String topic;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRabbitMQConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;\n\nimport lombok.Data;\n\n/**\n * IoT RabbitMQ 配置 {@link IotAbstractDataSinkConfig} 实现类\n *\n * @author HUIHUI\n */\n@Data\npublic class IotDataSinkRabbitMQConfig extends IotAbstractDataSinkConfig {\n\n    /**\n     * RabbitMQ 服务器地址\n     */\n    private String host;\n    /**\n     * 端口\n     */\n    private Integer port;\n    /**\n     * 虚拟主机\n     */\n    private String virtualHost;\n    /**\n     * 用户名\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    /**\n     * 交换机名称\n     */\n    private String exchange;\n    /**\n     * 路由键\n     */\n    private String routingKey;\n    /**\n     * 队列名称\n     */\n    private String queue;\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRedisConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotRedisDataStructureEnum;\nimport lombok.Data;\n\n/**\n * IoT Redis 配置 {@link IotAbstractDataSinkConfig} 实现类\n *\n * @author HUIHUI\n */\n@Data\npublic class IotDataSinkRedisConfig extends IotAbstractDataSinkConfig {\n\n    /**\n     * Redis 服务器地址\n     */\n    private String host;\n    /**\n     * 端口\n     */\n    private Integer port;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 数据库索引\n     */\n    private Integer database;\n\n    /**\n     * Redis 数据结构类型\n     * <p>\n     * 枚举 {@link IotRedisDataStructureEnum}\n     */\n    @InEnum(IotRedisDataStructureEnum.class)\n    private Integer dataStructure;\n\n    /**\n     * 主题/键名\n     * <p>\n     * 对于不同的数据结构：\n     * - Stream: 流的键名\n     * - Hash: Hash 的键名\n     * - List: 列表的键名\n     * - Set: 集合的键名\n     * - ZSet: 有序集合的键名\n     * - String: 字符串的键名\n     */\n    private String topic;\n\n    /**\n     * Hash 字段名（仅当 dataStructure 为 HASH 时使用）\n     */\n    private String hashField;\n\n    /**\n     * ZSet 分数字段（仅当 dataStructure 为 ZSET 时使用）\n     * 指定消息中哪个字段作为分数，如果不指定则使用当前时间戳\n     */\n    private String scoreField;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRocketMQConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;\n\nimport lombok.Data;\n\n/**\n * IoT RocketMQ 配置 {@link IotAbstractDataSinkConfig} 实现类\n *\n * @author HUIHUI\n */\n@Data\npublic class IotDataSinkRocketMQConfig extends IotAbstractDataSinkConfig {\n\n    /**\n     * RocketMQ 名称服务器地址\n     */\n    private String nameServer;\n    /**\n     * 访问密钥\n     */\n    private String accessKey;\n    /**\n     * 秘密钥匙\n     */\n    private String secretKey;\n\n    /**\n     * 生产者组\n     */\n    private String group;\n    /**\n     * 主题\n     */\n    private String topic;\n    /**\n     * 标签\n     */\n    private String tags;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkTcpConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;\n\nimport lombok.Data;\n\n/**\n * IoT TCP 配置 {@link IotAbstractDataSinkConfig} 实现类\n *\n * @author HUIHUI\n */\n@Data\npublic class IotDataSinkTcpConfig extends IotAbstractDataSinkConfig {\n\n    /**\n     * 默认连接超时时间（毫秒）\n     */\n    public static final int DEFAULT_CONNECT_TIMEOUT_MS = 5000;\n    /**\n     * 默认读取超时时间（毫秒）\n     */\n    public static final int DEFAULT_READ_TIMEOUT_MS = 10000;\n    /**\n     * 默认是否启用 SSL\n     */\n    public static final boolean DEFAULT_SSL = false;\n    /**\n     * 默认数据格式\n     */\n    public static final String DEFAULT_DATA_FORMAT = \"JSON\";\n    /**\n     * 默认心跳间隔时间（毫秒）\n     */\n    public static final long DEFAULT_HEARTBEAT_INTERVAL_MS = 30000L;\n    /**\n     * 默认重连间隔时间（毫秒）\n     */\n    public static final long DEFAULT_RECONNECT_INTERVAL_MS = 5000L;\n    /**\n     * 默认最大重连次数\n     */\n    public static final int DEFAULT_MAX_RECONNECT_ATTEMPTS = 3;\n\n    /**\n     * TCP 服务器地址\n     */\n    private String host;\n\n    /**\n     * TCP 服务器端口\n     */\n    private Integer port;\n\n    /**\n     * 连接超时时间（毫秒）\n     */\n    private Integer connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS;\n\n    /**\n     * 读取超时时间（毫秒）\n     */\n    private Integer readTimeoutMs = DEFAULT_READ_TIMEOUT_MS;\n\n    /**\n     * 是否启用 SSL\n     */\n    private Boolean ssl = DEFAULT_SSL;\n\n    /**\n     * SSL 证书路径（当 ssl=true 时需要）\n     */\n    private String sslCertPath;\n\n    /**\n     * 数据格式：JSON 或 BINARY\n     */\n    private String dataFormat = DEFAULT_DATA_FORMAT;\n\n    /**\n     * 心跳间隔时间（毫秒），0 表示不启用心跳\n     */\n    private Long heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS;\n\n    /**\n     * 重连间隔时间（毫秒）\n     */\n    private Long reconnectIntervalMs = DEFAULT_RECONNECT_INTERVAL_MS;\n\n    /**\n     * 最大重连次数\n     */\n    private Integer maxReconnectAttempts = DEFAULT_MAX_RECONNECT_ATTEMPTS;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkWebSocketConfig.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;\n\nimport lombok.Data;\n\n/**\n * IoT WebSocket 配置 {@link IotAbstractDataSinkConfig} 实现类\n * <p>\n * 配置设备消息通过 WebSocket 协议发送到外部 WebSocket 服务器\n * 支持 WebSocket (ws://) 和 WebSocket Secure (wss://) 连接\n *\n * @author HUIHUI\n */\n@Data\npublic class IotDataSinkWebSocketConfig extends IotAbstractDataSinkConfig {\n\n    /**\n     * 默认连接超时时间（毫秒）\n     */\n    public static final int DEFAULT_CONNECT_TIMEOUT_MS = 5000;\n    /**\n     * 默认发送超时时间（毫秒）\n     */\n    public static final int DEFAULT_SEND_TIMEOUT_MS = 10000;\n    /**\n     * 默认心跳间隔时间（毫秒）\n     */\n    public static final long DEFAULT_HEARTBEAT_INTERVAL_MS = 30000L;\n    /**\n     * 默认心跳消息内容\n     */\n    public static final String DEFAULT_HEARTBEAT_MESSAGE = \"{\\\"type\\\":\\\"heartbeat\\\"}\";\n    /**\n     * 默认是否启用 SSL 证书验证\n     */\n    public static final boolean DEFAULT_VERIFY_SSL_CERT = true;\n    /**\n     * 默认数据格式\n     */\n    public static final String DEFAULT_DATA_FORMAT = \"JSON\";\n    /**\n     * 默认重连间隔时间（毫秒）\n     */\n    public static final long DEFAULT_RECONNECT_INTERVAL_MS = 5000L;\n    /**\n     * 默认最大重连次数\n     */\n    public static final int DEFAULT_MAX_RECONNECT_ATTEMPTS = 3;\n    /**\n     * 默认是否启用压缩\n     */\n    public static final boolean DEFAULT_ENABLE_COMPRESSION = false;\n    /**\n     * 默认消息发送重试次数\n     */\n    public static final int DEFAULT_SEND_RETRY_COUNT = 1;\n    /**\n     * 默认消息发送重试间隔（毫秒）\n     */\n    public static final long DEFAULT_SEND_RETRY_INTERVAL_MS = 1000L;\n\n    /**\n     * WebSocket 服务器地址\n     * 例如：ws://localhost:8080/ws 或 wss://example.com/ws\n     */\n    private String serverUrl;\n\n    /**\n     * 连接超时时间（毫秒）\n     */\n    private Integer connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS;\n\n    /**\n     * 发送超时时间（毫秒）\n     */\n    private Integer sendTimeoutMs = DEFAULT_SEND_TIMEOUT_MS;\n\n    /**\n     * 心跳间隔时间（毫秒），0 表示不启用心跳\n     */\n    private Long heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS;\n\n    /**\n     * 心跳消息内容（JSON 格式）\n     */\n    private String heartbeatMessage = DEFAULT_HEARTBEAT_MESSAGE;\n\n    /**\n     * 子协议列表（逗号分隔）\n     */\n    private String subprotocols;\n\n    /**\n     * 自定义请求头（JSON 格式）\n     */\n    private String customHeaders;\n\n    /**\n     * 是否启用 SSL 证书验证（仅对 wss:// 生效）\n     */\n    private Boolean verifySslCert = DEFAULT_VERIFY_SSL_CERT;\n\n    /**\n     * 数据格式：JSON 或 TEXT\n     */\n    private String dataFormat = DEFAULT_DATA_FORMAT;\n\n    /**\n     * 重连间隔时间（毫秒）\n     */\n    private Long reconnectIntervalMs = DEFAULT_RECONNECT_INTERVAL_MS;\n\n    /**\n     * 最大重连次数\n     */\n    private Integer maxReconnectAttempts = DEFAULT_MAX_RECONNECT_ATTEMPTS;\n\n    /**\n     * 是否启用压缩\n     */\n    private Boolean enableCompression = DEFAULT_ENABLE_COMPRESSION;\n\n    /**\n     * 消息发送重试次数\n     */\n    private Integer sendRetryCount = DEFAULT_SEND_RETRY_COUNT;\n\n    /**\n     * 消息发送重试间隔（毫秒）\n     */\n    private Long sendRetryIntervalMs = DEFAULT_SEND_RETRY_INTERVAL_MS;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * IoT 产品物模型功能 DO\n * <p>\n * 每个 {@link IotProductDO} 和 {@link IotThingModelDO} 是“一对多”的关系，它的每个属性、事件、服务都对应一条记录\n *\n * @author 芋道源码\n */\n@TableName(value = \"iot_thing_model\", autoResultMap = true)\n@KeySequence(\"iot_thing_model_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IotThingModelDO extends BaseDO {\n\n    /**\n     * 物模型功能编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 功能标识\n     */\n    private String identifier;\n    /**\n     * 功能名称\n     */\n    private String name;\n    /**\n     * 功能描述\n     */\n    private String description;\n\n    /**\n     * 产品标识\n     * <p>\n     * 关联 {@link IotProductDO#getId()}\n     */\n    private Long productId;\n    /**\n     * 产品标识\n     * <p>\n     * 关联 {@link IotProductDO#getProductKey()}\n     */\n    private String productKey;\n\n    /**\n     * 功能类型\n     * <p>\n     * 枚举 {@link IotThingModelTypeEnum}\n     */\n    private Integer type;\n\n    /**\n     * 属性\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private ThingModelProperty property;\n\n    /**\n     * 事件\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private ThingModelEvent event;\n\n    /**\n     * 服务\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private ThingModelService service;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelEvent.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceEventTypeEnum;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.Pattern;\nimport java.util.List;\n\n// TODO @puhui999：感觉这个，是不是放到 dal 里会好点？（讨论下，先不改哈）\n/**\n * IoT 物模型中的事件\n *\n * @author HUIHUI\n */\n@Data\npublic class ThingModelEvent {\n\n    /**\n     * 事件标识符\n     */\n    @NotEmpty(message = \"事件标识符不能为空\")\n    @Pattern(regexp = \"^[a-zA-Z][a-zA-Z0-9_]{0,31}$\", message = \"事件标识符只能由字母、数字和下划线组成，必须以字母开头，长度不超过 32 个字符\")\n    private String identifier;\n    /**\n     * 事件名称\n     */\n    @NotEmpty(message = \"事件名称不能为空\")\n    private String name;\n    /**\n     * 是否是标准品类的必选事件\n     */\n    private Boolean required;\n    /**\n     * 事件类型\n     *\n     * 枚举 {@link IotThingModelServiceEventTypeEnum}\n     */\n    @NotEmpty(message = \"事件类型不能为空\")\n    @InEnum(IotThingModelServiceEventTypeEnum.class)\n    private String type;\n    /**\n     * 事件的输出参数\n     *\n     * 输出参数定义事件调用后返回的结果或反馈信息，用于确认操作结果或提供额外的信息。\n     */\n    @Valid\n    private List<ThingModelParam> outputParams;\n    /**\n     * 标识设备需要执行的具体操作\n     */\n    private String method;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelParam.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDataSpecs;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelParamDirectionEnum;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.Pattern;\nimport java.util.List;\n\n/**\n * IoT 产品物模型中的参数\n *\n * @author HUIHUI\n */\n@Data\npublic class ThingModelParam {\n\n    /**\n     * 参数标识符\n     */\n    @NotEmpty(message = \"参数标识符不能为空\")\n    @Pattern(regexp = \"^[a-zA-Z][a-zA-Z0-9_]{0,31}$\", message = \"参数标识符只能由字母、数字和下划线组成，必须以字母开头，长度不超过 32 个字符\")\n    private String identifier;\n    /**\n     * 参数名称\n     */\n    @NotEmpty(message = \"参数名称不能为空\")\n    private String name;\n    /**\n     * 用于区分输入或输出参数\n     *\n     * 枚举 {@link IotThingModelParamDirectionEnum}\n     */\n    @NotEmpty(message = \"参数方向不能为空\")\n    @InEnum(IotThingModelParamDirectionEnum.class)\n    private String direction;\n    /**\n     * 参数的序号。从 0 开始排序，且不能重复。\n     *\n     * TODO 考虑要不要序号，感觉是要的, 先留一手看看\n     */\n    private Integer paraOrder;\n    /**\n     * 参数值的数据类型，与 dataSpecs 的 dataType 保持一致\n     *\n     * 枚举 {@link IotDataSpecsDataTypeEnum}\n     */\n    @NotEmpty(message = \"数据类型不能为空\")\n    @InEnum(IotDataSpecsDataTypeEnum.class)\n    private String dataType;\n    /**\n     * 参数值的数据类型（dataType）为非列表型（int、float、double、text、date、array）的数据规范存储在 dataSpecs 中\n     */\n    private ThingModelDataSpecs dataSpecs;\n    /**\n     * 参数值的数据类型（dataType）为列表型（enum、bool、struct）的数据规范存储在 dataSpecsList 中\n     */\n    private List<ThingModelDataSpecs> dataSpecsList;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelProperty.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDataSpecs;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.Pattern;\nimport java.util.List;\n\n/**\n * IoT 物模型中的属性\n *\n * dataSpecs 和 dataSpecsList 之中必须传入且只能传入一个\n *\n * @author HUIHUI\n */\n@Data\npublic class ThingModelProperty {\n\n    /**\n     * 属性标识符\n     */\n    @NotEmpty(message = \"属性标识符不能为空\")\n    @Pattern(regexp = \"^[a-zA-Z][a-zA-Z0-9_]{0,31}$\", message = \"属性标识符只能由字母、数字和下划线组成，必须以字母开头，长度不超过 32 个字符\")\n    private String identifier;\n    /**\n     * 属性名称\n     */\n    @NotEmpty(message = \"属性名称不能为空\")\n    private String name;\n    /**\n     * 云端可以对该属性进行的操作类型\n     *\n     * 枚举 {@link IotThingModelAccessModeEnum}\n     */\n    @NotEmpty(message = \"操作类型不能为空\")\n    @InEnum(IotThingModelAccessModeEnum.class)\n    private String accessMode;\n    /**\n     * 是否是标准品类的必选服务\n     */\n    private Boolean required;\n    /**\n     * 参数值的数据类型，与 dataSpecs 的 dataType 保持一致\n     *\n     * 枚举 {@link IotDataSpecsDataTypeEnum}\n     */\n    @NotEmpty(message = \"数据类型不能为空\")\n    @InEnum(IotDataSpecsDataTypeEnum.class)\n    private String dataType;\n    /**\n     * 数据类型（dataType）为非列表型（int、float、double、text、date、array）的数据规范存储在 dataSpecs 中\n     */\n    private ThingModelDataSpecs dataSpecs;\n    /**\n     * 数据类型（dataType）为列表型（enum、bool、struct）的数据规范存储在 dataSpecsList 中\n     */\n    private List<ThingModelDataSpecs> dataSpecsList;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelService.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceCallTypeEnum;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.Pattern;\nimport java.util.List;\n\n/**\n * IoT 物模型中的服务\n *\n * @author HUIHUI\n */\n@Data\npublic class ThingModelService {\n\n    /**\n     * 服务标识符\n     */\n    @NotEmpty(message = \"服务标识符不能为空\")\n    @Pattern(regexp = \"^[a-zA-Z][a-zA-Z0-9_]{0,31}$\", message = \"服务标识符只能由字母、数字和下划线组成，必须以字母开头，长度不超过 32 个字符\")\n    private String identifier;\n    /**\n     * 服务名称\n     */\n    @NotEmpty(message = \"服务名称不能为空\")\n    private String name;\n    /**\n     * 是否是标准品类的必选服务\n     */\n    private Boolean required;\n    /**\n     * 调用类型\n     *\n     * 枚举 {@link IotThingModelServiceCallTypeEnum}\n     */\n    @NotEmpty(message = \"调用类型不能为空\")\n    @InEnum(IotThingModelServiceCallTypeEnum.class)\n    private String callType;\n    /**\n     * 服务的输入参数\n     *\n     * 输入参数定义服务调用时所需提供的信息，用于控制设备行为或执行特定任务\n     */\n    @Valid\n    private List<ThingModelParam> inputParams;\n    /**\n     * 服务的输出参数\n     *\n     * 输出参数定义服务调用后返回的结果或反馈信息，用于确认操作结果或提供额外的信息。\n     */\n    @Valid\n    private List<ThingModelParam> outputParams;\n    /**\n     * 标识设备需要执行的具体操作\n     */\n    private String method;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelArrayDataSpecs.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Pattern;\nimport java.util.List;\n\n/**\n * IoT 物模型数据类型为数组的 DataSpec 定义\n *\n * @author HUIHUI\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@JsonIgnoreProperties({\"dataType\"}) // 忽略子类中的 dataType 字段，从而避免重复\npublic class ThingModelArrayDataSpecs extends ThingModelDataSpecs {\n\n    @NotNull(message = \"数组元素个数不能为空\")\n    private Integer size;\n\n    @NotEmpty(message = \"数组元素的数据类型不能为空\")\n    @Pattern(regexp = \"^(struct|int|float|double|text)$\", message = \"数组元素的数据类型必须为：struct、int、float、double 或 text\")\n    private String childDataType;\n    /**\n     * 数据类型（childDataType）为列表型 struct 的数据规范存储在 dataSpecsList 中\n     * 此时 struct 取值范围为：int、float、double、text、date、enum、bool\n     */\n    @Valid\n    private List<ThingModelDataSpecs> dataSpecsList;\n\n}\n\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Pattern;\n\n/**\n * IoT 物模型数据类型为布尔型或枚举型的 DataSpec 定义\n *\n * 数据类型，取值为 bool 或 enum\n *\n * @author HUIHUI\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@JsonIgnoreProperties({\"dataType\"}) // 忽略子类中的 dataType 字段，从而避免重复\npublic class ThingModelBoolOrEnumDataSpecs extends ThingModelDataSpecs {\n\n    @NotEmpty(message = \"枚举项的名称不能为空\")\n    @Pattern(regexp = \"^[\\\\u4e00-\\\\u9fa5a-zA-Z0-9][\\\\u4e00-\\\\u9fa5a-zA-Z0-9_-]{0,19}$\",\n            message = \"枚举项的名称只能包含中文、大小写英文字母、数字、下划线和短划线，必须以中文、英文字母或数字开头，长度不超过 20 个字符\")\n    private String name;\n\n    @NotNull(message = \"枚举值不能为空\")\n    private Integer value;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDataSpecs.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;\n\nimport com.fasterxml.jackson.annotation.JsonSubTypes;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport lombok.Data;\n\n/**\n * IoT ThingModelDataSpecs 抽象类\n *\n * 用于表示物模型数据的通用类型，根据具体的 \"dataType\" 字段动态映射到对应的子类\n * 提供多态支持，适用于不同类型的数据结构序列化和反序列化场景\n *\n * @author HUIHUI\n */\n@Data\n@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = \"dataType\", visible = true)\n@JsonSubTypes({\n        @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = \"int\"),\n        @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = \"float\"),\n        @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = \"double\"),\n        @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = \"text\"),\n        @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = \"date\"),\n        @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = \"bool\"),\n        @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = \"enum\"),\n        @JsonSubTypes.Type(value = ThingModelArrayDataSpecs.class, name = \"array\"),\n        @JsonSubTypes.Type(value = ThingModelStructDataSpecs.class, name = \"struct\")\n})\npublic abstract class ThingModelDataSpecs {\n\n    /**\n     * 数据类型\n     */\n    private String dataType;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.Max;\n\n/**\n * IoT 物模型数据类型为时间型或文本型的 DataSpec 定义\n *\n * 数据类型，取值为 date 或 text\n *\n * @author HUIHUI\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@JsonIgnoreProperties({\"dataType\"}) // 忽略子类中的 dataType 字段，从而避免重复\npublic class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs {\n\n    /**\n     * 数据长度，单位为字节。取值不能超过 2048\n     *\n     * 当 dataType 为 text 时，需传入该参数\n     */\n    @Max(value = 2048, message = \"数据长度不能超过 2048\")\n    private Integer length;\n    /**\n     * 默认值，可选参数，用于存储默认值\n     */\n    private String defaultValue;\n\n}\n\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelNumericDataSpec.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.Pattern;\n\n/**\n * IoT 物模型数据类型为数值的 DataSpec 定义\n *\n * 数据类型，取值为 int、float 或 double\n *\n * @author HUIHUI\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@JsonIgnoreProperties({\"dataType\"}) // 忽略子类中的 dataType 字段，从而避免重复\npublic class ThingModelNumericDataSpec extends ThingModelDataSpecs {\n\n    /**\n     * 最大值，需转为字符串类型。值必须与 dataType 类型一致\n     */\n    @NotEmpty(message = \"最大值不能为空\")\n    @Pattern(regexp = \"^-?\\\\d+(\\\\.\\\\d+)?$\", message = \"最大值必须为数值类型\")\n    private String max;\n    /**\n     * 最小值，需转为字符串类型。值必须与 dataType 类型一致\n     */\n    @NotEmpty(message = \"最小值不能为空\")\n    @Pattern(regexp = \"^-?\\\\d+(\\\\.\\\\d+)?$\", message = \"最小值必须为数值类型\")\n    private String min;\n    /**\n     * 步长，需转为字符串类型。值必须与 dataType 类型一致\n     */\n    @NotEmpty(message = \"步长不能为空\")\n    @Pattern(regexp = \"^-?\\\\d+(\\\\.\\\\d+)?$\", message = \"步长必须为数值类型\")\n    private String step;\n    /**\n     * 精度。当 dataType 为 float 或 double 时可选传入\n     */\n    private String precise;\n    /**\n     * 默认值，可传入用于存储的默认值\n     */\n    private String defaultValue;\n    /**\n     * 单位的符号\n     */\n    private String unit;\n    /**\n     * 单位的名称\n     */\n    private String unitName;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelStructDataSpecs.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.Pattern;\nimport java.util.List;\n\n/**\n * IoT 物模型数据类型为 struct 的 DataSpec 定义\n *\n * @author HUIHUI\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@JsonIgnoreProperties({\"dataType\"}) // 忽略子类中的 dataType 字段，从而避免重复\npublic class ThingModelStructDataSpecs extends ThingModelDataSpecs {\n\n    @NotEmpty(message = \"属性标识符不能为空\")\n    @Pattern(regexp = \"^[a-zA-Z][a-zA-Z0-9_]{0,31}$\", message = \"属性标识符只能由字母、数字和下划线组成，必须以字母开头，长度不超过 32 个字符\")\n    private String identifier;\n\n    @NotEmpty(message = \"属性名称不能为空\")\n    private String name;\n\n    @NotEmpty(message = \"操作类型不能为空\")\n    @InEnum(IotThingModelAccessModeEnum.class)\n    private String accessMode;\n\n    /**\n     * 是否是标准品类的必选服务\n     */\n    private Boolean required;\n\n    @NotEmpty(message = \"数据类型不能为空\")\n    @Pattern(regexp = \"^(int|float|double|text|date|enum|bool)$\", message = \"数据类型必须为：int、float、double、text、date、enum、bool\")\n    private String childDataType;\n\n    /**\n     * 数据类型（dataType）为非列表型（int、float、double、text、date、array）的数据规范存储在 dataSpecs 中\n     */\n    @Valid\n    private ThingModelDataSpecs dataSpecs;\n\n    /**\n     * 数据类型（dataType）为列表型（enum、bool、struct）的数据规范存储在 dataSpecsList 中\n     */\n    @Valid\n    private List<ThingModelDataSpecs> dataSpecsList;\n\n}\n\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.alert;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * IoT 告警配置 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface IotAlertConfigMapper extends BaseMapperX<IotAlertConfigDO> {\n\n    default PageResult<IotAlertConfigDO> selectPage(IotAlertConfigPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotAlertConfigDO>()\n                .likeIfPresent(IotAlertConfigDO::getName, reqVO.getName())\n                .eqIfPresent(IotAlertConfigDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(IotAlertConfigDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(IotAlertConfigDO::getId));\n    }\n\n    default List<IotAlertConfigDO> selectListByStatus(Integer status) {\n        return selectList(IotAlertConfigDO::getStatus, status);\n    }\n\n    default List<IotAlertConfigDO> selectListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) {\n        return selectList(new LambdaQueryWrapperX<IotAlertConfigDO>()\n                .eq(IotAlertConfigDO::getStatus, status)\n                .apply(MyBatisUtils.findInSet(\"scene_rule_ids\", sceneRuleId)));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertRecordMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.alert;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * IoT 告警记录 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface IotAlertRecordMapper extends BaseMapperX<IotAlertRecordDO> {\n\n    default PageResult<IotAlertRecordDO> selectPage(IotAlertRecordPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotAlertRecordDO>()\n                .eqIfPresent(IotAlertRecordDO::getConfigId, reqVO.getConfigId())\n                .eqIfPresent(IotAlertRecordDO::getConfigLevel, reqVO.getLevel())\n                .eqIfPresent(IotAlertRecordDO::getProductId, reqVO.getProductId())\n                .eqIfPresent(IotAlertRecordDO::getDeviceId, reqVO.getDeviceId())\n                .eqIfPresent(IotAlertRecordDO::getProcessStatus, reqVO.getProcessStatus())\n                .betweenIfPresent(IotAlertRecordDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(IotAlertRecordDO::getId));\n    }\n\n    default List<IotAlertRecordDO> selectListBySceneRuleId(Long sceneRuleId, Long deviceId, Boolean processStatus) {\n        return selectList(new LambdaQueryWrapperX<IotAlertRecordDO>()\n                .eq(IotAlertRecordDO::getSceneRuleId, sceneRuleId)\n                .eqIfPresent(IotAlertRecordDO::getDeviceId, deviceId)\n                .eqIfPresent(IotAlertRecordDO::getProcessStatus, processStatus)\n                .orderByDesc(IotAlertRecordDO::getId));\n    }\n\n    default int updateList(Collection<Long> ids, IotAlertRecordDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<IotAlertRecordDO>()\n                .in(IotAlertRecordDO::getId, ids));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.device;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * IoT 设备分组 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface IotDeviceGroupMapper extends BaseMapperX<IotDeviceGroupDO> {\n\n    default PageResult<IotDeviceGroupDO> selectPage(IotDeviceGroupPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceGroupDO>()\n                .likeIfPresent(IotDeviceGroupDO::getName, reqVO.getName())\n                .betweenIfPresent(IotDeviceGroupDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(IotDeviceGroupDO::getId));\n    }\n\n    default List<IotDeviceGroupDO> selectListByStatus(Integer status) {\n        return selectList(IotDeviceGroupDO::getStatus, status);\n    }\n\n    default IotDeviceGroupDO selectByName(String name) {\n        return selectOne(IotDeviceGroupDO::getName, name);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.device;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport javax.annotation.Nullable;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * IoT 设备 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {\n\n    default PageResult<IotDeviceDO> selectPage(IotDevicePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>()\n                .likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName())\n                .eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId())\n                .eqIfPresent(IotDeviceDO::getDeviceType, reqVO.getDeviceType())\n                .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname())\n                .eqIfPresent(IotDeviceDO::getState, reqVO.getStatus())\n                .eqIfPresent(IotDeviceDO::getGatewayId, reqVO.getGatewayId())\n                .apply(ObjectUtil.isNotNull(reqVO.getGroupId()), \"FIND_IN_SET(\" + reqVO.getGroupId() + \",group_ids) > 0\")\n                .orderByDesc(IotDeviceDO::getId));\n    }\n\n    default IotDeviceDO selectByDeviceName(String deviceName) {\n        return selectOne(IotDeviceDO::getDeviceName, deviceName);\n    }\n\n    default IotDeviceDO selectByProductKeyAndDeviceName(String productKey, String deviceName) {\n        return selectOne(IotDeviceDO::getProductKey, productKey,\n                IotDeviceDO::getDeviceName, deviceName);\n    }\n\n    default long selectCountByGatewayId(Long id) {\n        return selectCount(IotDeviceDO::getGatewayId, id);\n    }\n\n    default Long selectCountByProductId(Long productId) {\n        return selectCount(IotDeviceDO::getProductId, productId);\n    }\n\n    default List<IotDeviceDO> selectListByCondition(@Nullable Integer deviceType,\n                                                    @Nullable Long productId) {\n        return selectList(new LambdaQueryWrapperX<IotDeviceDO>()\n                .eqIfPresent(IotDeviceDO::getDeviceType, deviceType)\n                .eqIfPresent(IotDeviceDO::getProductId, productId));\n    }\n\n    default List<IotDeviceDO> selectListByState(Integer state) {\n        return selectList(IotDeviceDO::getState, state);\n    }\n\n    default List<IotDeviceDO> selectListByProductId(Long productId) {\n        return selectList(IotDeviceDO::getProductId, productId);\n    }\n\n    default Long selectCountByGroupId(Long groupId) {\n        return selectCount(new LambdaQueryWrapperX<IotDeviceDO>()\n                .apply(\"FIND_IN_SET(\" + groupId + \",group_ids) > 0\"));\n    }\n\n    default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) {\n        return selectCount(new LambdaQueryWrapperX<IotDeviceDO>()\n                .geIfPresent(IotDeviceDO::getCreateTime, createTime));\n    }\n\n    default List<IotDeviceDO> selectByProductKeyAndDeviceNames(String productKey, Collection<String> deviceNames) {\n        return selectList(new LambdaQueryWrapperX<IotDeviceDO>()\n                .eq(IotDeviceDO::getProductKey, productKey)\n                .in(IotDeviceDO::getDeviceName, deviceNames));\n    }\n\n    default IotDeviceDO selectBySerialNumber(String serialNumber) {\n        return selectOne(IotDeviceDO::getSerialNumber, serialNumber);\n    }\n\n    /**\n     * 查询指定产品下的设备数量\n     *\n     * @return 产品编号 -> 设备数量的映射\n     */\n    default Map<Long, Integer> selectDeviceCountMapByProductId() {\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<IotDeviceDO>()\n                .select(\"product_id AS productId\", \"COUNT(1) AS deviceCount\")\n                .groupBy(\"product_id\"));\n        return result.stream().collect(Collectors.toMap(\n            map -> Long.valueOf(map.get(\"productId\").toString()),\n            map -> Integer.valueOf(map.get(\"deviceCount\").toString())\n        ));\n    }\n\n    /**\n     * 查询各个状态下的设备数量\n     *\n     * @return 设备状态 -> 设备数量的映射\n     */\n    default Map<Integer, Long> selectDeviceCountGroupByState() {\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<IotDeviceDO>()\n                .select(\"state\", \"COUNT(1) AS deviceCount\")\n                .groupBy(\"state\"));\n        return result.stream().collect(Collectors.toMap(\n            map -> Integer.valueOf(map.get(\"state\").toString()),\n            map -> Long.valueOf(map.get(\"deviceCount\").toString())\n        ));\n    }\n\n    /**\n     * 查询有位置信息的设备列表\n     *\n     * @return 设备列表\n     */\n    default List<IotDeviceDO> selectListByHasLocation() {\n        return selectList(new LambdaQueryWrapperX<IotDeviceDO>()\n                .isNotNull(IotDeviceDO::getLatitude)\n                .isNotNull(IotDeviceDO::getLongitude));\n    }\n\n    // ========== 网关-子设备绑定相关 ==========\n\n    /**\n     * 根据网关编号查询子设备列表\n     *\n     * @param gatewayId 网关设备编号\n     * @return 子设备列表\n     */\n    default List<IotDeviceDO> selectListByGatewayId(Long gatewayId) {\n        return selectList(IotDeviceDO::getGatewayId, gatewayId);\n    }\n\n    /**\n     * 分页查询未绑定网关的子设备\n     *\n     * @param reqVO 分页查询参数\n     * @return 子设备分页\n     */\n    default PageResult<IotDeviceDO> selectUnboundSubDevicePage(IotDevicePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>()\n                .likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName())\n                .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname())\n                .eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId())\n                // 仅查询子设备 + 未绑定网关\n                .eq(IotDeviceDO::getDeviceType, IotProductDeviceTypeEnum.GATEWAY_SUB.getType())\n                .isNull(IotDeviceDO::getGatewayId)\n                .orderByDesc(IotDeviceDO::getId));\n    }\n\n    /**\n     * 批量更新设备的网关编号\n     *\n     * @param ids       设备编号列表\n     * @param gatewayId 网关设备编号（可以为 null，表示解绑）\n     */\n    default void updateGatewayIdBatch(Collection<Long> ids, Long gatewayId) {\n        update(null, new LambdaUpdateWrapper<IotDeviceDO>()\n                .set(IotDeviceDO::getGatewayId, gatewayId)\n                .in(IotDeviceDO::getId, ids));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusConfigMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.device;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * IoT 设备 Modbus 连接配置 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface IotDeviceModbusConfigMapper extends BaseMapperX<IotDeviceModbusConfigDO> {\n\n    default IotDeviceModbusConfigDO selectByDeviceId(Long deviceId) {\n        return selectOne(IotDeviceModbusConfigDO::getDeviceId, deviceId);\n    }\n\n    default List<IotDeviceModbusConfigDO> selectList(IotModbusDeviceConfigListReqDTO reqDTO) {\n        return selectList(new LambdaQueryWrapperX<IotDeviceModbusConfigDO>()\n                .eqIfPresent(IotDeviceModbusConfigDO::getStatus, reqDTO.getStatus())\n                .eqIfPresent(IotDeviceModbusConfigDO::getMode, reqDTO.getMode())\n                .inIfPresent(IotDeviceModbusConfigDO::getDeviceId, reqDTO.getDeviceIds()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.device;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * IoT 设备 Modbus 点位配置 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface IotDeviceModbusPointMapper extends BaseMapperX<IotDeviceModbusPointDO> {\n\n    default PageResult<IotDeviceModbusPointDO> selectPage(IotDeviceModbusPointPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceModbusPointDO>()\n                .eqIfPresent(IotDeviceModbusPointDO::getDeviceId, reqVO.getDeviceId())\n                .likeIfPresent(IotDeviceModbusPointDO::getIdentifier, reqVO.getIdentifier())\n                .likeIfPresent(IotDeviceModbusPointDO::getName, reqVO.getName())\n                .eqIfPresent(IotDeviceModbusPointDO::getFunctionCode, reqVO.getFunctionCode())\n                .eqIfPresent(IotDeviceModbusPointDO::getStatus, reqVO.getStatus())\n                .orderByDesc(IotDeviceModbusPointDO::getId));\n    }\n\n    default List<IotDeviceModbusPointDO> selectListByDeviceIdsAndStatus(Collection<Long> deviceIds, Integer status) {\n        return selectList(new LambdaQueryWrapperX<IotDeviceModbusPointDO>()\n                .in(IotDeviceModbusPointDO::getDeviceId, deviceIds)\n                .eq(IotDeviceModbusPointDO::getStatus, status));\n    }\n\n    default IotDeviceModbusPointDO selectByDeviceIdAndIdentifier(Long deviceId, String identifier) {\n        return selectOne(IotDeviceModbusPointDO::getDeviceId, deviceId,\n                IotDeviceModbusPointDO::getIdentifier, identifier);\n    }\n\n    default void updateByThingModelId(Long thingModelId, IotDeviceModbusPointDO updateObj) {\n        update(updateObj, new LambdaQueryWrapperX<IotDeviceModbusPointDO>()\n                .eq(IotDeviceModbusPointDO::getThingModelId, thingModelId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.ota;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface IotOtaFirmwareMapper extends BaseMapperX<IotOtaFirmwareDO> {\n\n    default IotOtaFirmwareDO selectByProductIdAndVersion(Long productId, String version) {\n        return selectOne(IotOtaFirmwareDO::getProductId, productId,\n                IotOtaFirmwareDO::getVersion, version);\n    }\n\n    default PageResult<IotOtaFirmwareDO> selectPage(IotOtaFirmwarePageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaFirmwareDO>()\n                .likeIfPresent(IotOtaFirmwareDO::getName, pageReqVO.getName())\n                .eqIfPresent(IotOtaFirmwareDO::getProductId, pageReqVO.getProductId())\n                .betweenIfPresent(IotOtaFirmwareDO::getCreateTime, pageReqVO.getCreateTime())\n                .orderByDesc(IotOtaFirmwareDO::getCreateTime));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.ota;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface IotOtaTaskMapper extends BaseMapperX<IotOtaTaskDO> {\n\n    default IotOtaTaskDO selectByFirmwareIdAndName(Long firmwareId, String name) {\n        return selectOne(IotOtaTaskDO::getFirmwareId, firmwareId,\n                IotOtaTaskDO::getName, name);\n    }\n\n    default PageResult<IotOtaTaskDO> selectPage(IotOtaTaskPageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaTaskDO>()\n                .eqIfPresent(IotOtaTaskDO::getFirmwareId, pageReqVO.getFirmwareId())\n                .likeIfPresent(IotOtaTaskDO::getName, pageReqVO.getName())\n                .orderByDesc(IotOtaTaskDO::getId));\n    }\n\n    default int updateByIdAndStatus(Long id, Integer whereStatus, IotOtaTaskDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<IotOtaTaskDO>()\n                .eq(IotOtaTaskDO::getId, id)\n                .eq(IotOtaTaskDO::getStatus, whereStatus));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskRecordMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.ota;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\n\n@Mapper\npublic interface IotOtaTaskRecordMapper extends BaseMapperX<IotOtaTaskRecordDO> {\n\n    default List<IotOtaTaskRecordDO> selectListByFirmwareIdAndTaskId(Long firmwareId, Long taskId) {\n        return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()\n                .eqIfPresent(IotOtaTaskRecordDO::getFirmwareId, firmwareId)\n                .eqIfPresent(IotOtaTaskRecordDO::getTaskId, taskId)\n                .select(IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus));\n    }\n\n    default PageResult<IotOtaTaskRecordDO> selectPage(IotOtaTaskRecordPageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<IotOtaTaskRecordDO>()\n                .eqIfPresent(IotOtaTaskRecordDO::getTaskId, pageReqVO.getTaskId())\n                .eqIfPresent(IotOtaTaskRecordDO::getStatus, pageReqVO.getStatus()));\n    }\n\n    default List<IotOtaTaskRecordDO> selectListByTaskIdAndStatus(Long taskId, Collection<Integer> statuses) {\n        return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()\n               .eq(IotOtaTaskRecordDO::getTaskId, taskId)\n               .in(IotOtaTaskRecordDO::getStatus, statuses));\n    }\n\n    default Long selectCountByTaskIdAndStatus(Long taskId, Collection<Integer> statuses) {\n        return selectCount(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()\n                .eq(IotOtaTaskRecordDO::getTaskId, taskId)\n                .in(IotOtaTaskRecordDO::getStatus, statuses));\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status,\n                                    IotOtaTaskRecordDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<IotOtaTaskRecordDO>()\n                .eq(IotOtaTaskRecordDO::getId, id)\n                .eq(IotOtaTaskRecordDO::getStatus, status));\n    }\n\n    default int updateByIdAndStatus(Long id, Collection<Integer> whereStatuses,\n                                    IotOtaTaskRecordDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<IotOtaTaskRecordDO>()\n                .eq(IotOtaTaskRecordDO::getId, id)\n                .in(IotOtaTaskRecordDO::getStatus, whereStatuses));\n    }\n\n    default void updateListByIdAndStatus(Collection<Long> ids, Collection<Integer> whereStatuses,\n                                         IotOtaTaskRecordDO updateObj) {\n        update(updateObj, new LambdaUpdateWrapper<IotOtaTaskRecordDO>()\n                .in(IotOtaTaskRecordDO::getId, ids)\n                .in(IotOtaTaskRecordDO::getStatus, whereStatuses));\n    }\n\n    default List<IotOtaTaskRecordDO> selectListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses) {\n        return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()\n                .inIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceIds)\n                .inIfPresent(IotOtaTaskRecordDO::getStatus, statuses));\n    }\n\n    default List<IotOtaTaskRecordDO> selectListByDeviceIdAndStatus(Long deviceId, Set<Integer> statuses) {\n        return selectList(new LambdaQueryWrapperX<IotOtaTaskRecordDO>()\n                .eqIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceId)\n                .inIfPresent(IotOtaTaskRecordDO::getStatus, statuses));\n    }\n\n    default List<IotOtaTaskRecordDO> selectListByStatus(Integer status) {\n        return selectList(IotOtaTaskRecordDO::getStatus, status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport javax.annotation.Nullable;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * IoT 产品分类 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface IotProductCategoryMapper extends BaseMapperX<IotProductCategoryDO> {\n\n    default PageResult<IotProductCategoryDO> selectPage(IotProductCategoryPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotProductCategoryDO>()\n                .likeIfPresent(IotProductCategoryDO::getName, reqVO.getName())\n                .betweenIfPresent(IotProductCategoryDO::getCreateTime, reqVO.getCreateTime())\n                .orderByAsc(IotProductCategoryDO::getSort));\n    }\n\n    default List<IotProductCategoryDO> selectListByStatus(Integer status) {\n        return selectList(IotProductCategoryDO::getStatus, status);\n    }\n\n    default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) {\n        return selectCount(new LambdaQueryWrapperX<IotProductCategoryDO>()\n                .geIfPresent(IotProductCategoryDO::getCreateTime, createTime));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport javax.annotation.Nullable;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * IoT 产品 Mapper\n *\n * @author ahh\n */\n@Mapper\npublic interface IotProductMapper extends BaseMapperX<IotProductDO> {\n\n    default PageResult<IotProductDO> selectPage(IotProductPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotProductDO>()\n                .likeIfPresent(IotProductDO::getName, reqVO.getName())\n                .likeIfPresent(IotProductDO::getProductKey, reqVO.getProductKey())\n                .orderByDesc(IotProductDO::getId));\n    }\n\n    default List<IotProductDO> selectList(Integer deviceType) {\n        return selectList(new LambdaQueryWrapperX<IotProductDO>()\n                .eqIfPresent(IotProductDO::getDeviceType, deviceType)\n                .orderByDesc(IotProductDO::getId));\n    }\n\n    default IotProductDO selectByProductKey(String productKey) {\n        return selectOne(new LambdaQueryWrapper<IotProductDO>()\n                .apply(\"LOWER(product_key) = {0}\", productKey.toLowerCase()));\n    }\n\n    default List<IotProductDO> selectListByStatus(Integer status) {\n        return selectList(IotProductDO::getStatus, status);\n    }\n\n    default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) {\n        return selectCount(new LambdaQueryWrapperX<IotProductDO>()\n                .geIfPresent(IotProductDO::getCreateTime, createTime));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataRuleMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.rule;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * IoT 数据流转规则 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface IotDataRuleMapper extends BaseMapperX<IotDataRuleDO> {\n\n    default PageResult<IotDataRuleDO> selectPage(IotDataRulePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotDataRuleDO>()\n                .likeIfPresent(IotDataRuleDO::getName, reqVO.getName())\n                .eqIfPresent(IotDataRuleDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(IotDataRuleDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(IotDataRuleDO::getId));\n    }\n\n    default List<IotDataRuleDO> selectListBySinkId(Long sinkId) {\n        return selectList(new LambdaQueryWrapperX<IotDataRuleDO>()\n                .apply(MyBatisUtils.findInSet(\"sink_ids\", sinkId)));\n    }\n\n    default List<IotDataRuleDO> selectListByStatus(Integer status) {\n        return selectList(IotDataRuleDO::getStatus, status);\n    }\n\n    default IotDataRuleDO selectByName(String name) {\n        return selectOne(IotDataRuleDO::getName, name);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.rule;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * IoT 数据流转目的 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface IotDataSinkMapper extends BaseMapperX<IotDataSinkDO> {\n\n    default PageResult<IotDataSinkDO> selectPage(IotDataSinkPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotDataSinkDO>()\n                .likeIfPresent(IotDataSinkDO::getName, reqVO.getName())\n                .eqIfPresent(IotDataSinkDO::getStatus, reqVO.getStatus())\n                .eqIfPresent(IotDataSinkDO::getType, reqVO.getType())\n                .betweenIfPresent(IotDataSinkDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(IotDataSinkDO::getId));\n    }\n\n    default List<IotDataSinkDO> selectListByStatus(Integer status) {\n        return selectList(IotDataSinkDO::getStatus, status);\n    }\n\n    default IotDataSinkDO selectByName(String name) {\n        return selectOne(IotDataSinkDO::getName, name);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotSceneRuleMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.rule;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * IoT 场景联动 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface IotSceneRuleMapper extends BaseMapperX<IotSceneRuleDO> {\n\n    default PageResult<IotSceneRuleDO> selectPage(IotSceneRulePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotSceneRuleDO>()\n                .likeIfPresent(IotSceneRuleDO::getName, reqVO.getName())\n                .likeIfPresent(IotSceneRuleDO::getDescription, reqVO.getDescription())\n                .eqIfPresent(IotSceneRuleDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(IotSceneRuleDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(IotSceneRuleDO::getId));\n    }\n\n    default List<IotSceneRuleDO> selectListByStatus(Integer status) {\n        return selectList(IotSceneRuleDO::getStatus, status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * IoT 产品物模型 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface IotThingModelMapper extends BaseMapperX<IotThingModelDO> {\n\n    default PageResult<IotThingModelDO> selectPage(IotThingModelPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<IotThingModelDO>()\n                .eqIfPresent(IotThingModelDO::getIdentifier, reqVO.getIdentifier())\n                .likeIfPresent(IotThingModelDO::getName, reqVO.getName())\n                .eqIfPresent(IotThingModelDO::getType, reqVO.getType())\n                .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId())\n                .orderByDesc(IotThingModelDO::getId));\n    }\n\n    default List<IotThingModelDO> selectList(IotThingModelListReqVO reqVO) {\n        return selectList(new LambdaQueryWrapperX<IotThingModelDO>()\n                .eqIfPresent(IotThingModelDO::getIdentifier, reqVO.getIdentifier())\n                .likeIfPresent(IotThingModelDO::getName, reqVO.getName())\n                .eqIfPresent(IotThingModelDO::getType, reqVO.getType())\n                .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId())\n                .orderByDesc(IotThingModelDO::getId));\n    }\n\n    default IotThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) {\n        return selectOne(IotThingModelDO::getProductId, productId,\n                IotThingModelDO::getIdentifier, identifier);\n    }\n\n    default List<IotThingModelDO> selectListByProductId(Long productId) {\n        return selectList(IotThingModelDO::getProductId, productId);\n    }\n\n    default List<IotThingModelDO> selectListByProductIdAndIdentifiers(Long productId, Collection<String> identifiers) {\n        return selectList(new LambdaQueryWrapperX<IotThingModelDO>()\n                .eq(IotThingModelDO::getProductId, productId)\n                .in(IotThingModelDO::getIdentifier, identifiers));\n    }\n\n    default List<IotThingModelDO> selectListByProductIdAndType(Long productId, Integer type) {\n        return selectList(IotThingModelDO::getProductId, productId,\n                IotThingModelDO::getType, type);\n    }\n\n    default IotThingModelDO selectByProductIdAndName(Long productId, String name) {\n        return selectOne(IotThingModelDO::getProductId, productId,\n                IotThingModelDO::getName, name);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.redis;\n\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;\n\n/**\n * IoT Redis Key 枚举类\n *\n * @author 芋道源码\n */\npublic interface RedisKeyConstants {\n\n    /**\n     * 设备属性的数据缓存，采用 HASH 结构\n     * <p>\n     * KEY 格式：device_property:{deviceId}\n     * HASH KEY：identifier 属性标识\n     * VALUE 数据类型：String(JSON) {@link IotDevicePropertyDO}\n     */\n    String DEVICE_PROPERTY = \"iot:device_property:%d\";\n\n    /**\n     * 设备的最后上报时间，采用 ZSET 结构\n     *\n     * KEY 格式：{deviceId}\n     * SCORE：上报时间\n     */\n    String DEVICE_REPORT_TIMES = \"iot:device_report_times\";\n\n    /**\n     * 设备关联的网关 serverId 缓存，采用 HASH 结构\n     *\n     * KEY 格式：device_server_id\n     * HASH KEY：{deviceId}\n     * VALUE 数据类型：String serverId\n     */\n    String DEVICE_SERVER_ID = \"iot:device_server_id\";\n\n    /**\n     * 设备信息的数据缓存，使用 Spring Cache 操作（忽略租户）\n     *\n     * KEY 格式 1：device_${deviceId}\n     * KEY 格式 2：device_${productKey}_${deviceName}\n     * VALUE 数据类型：String(JSON)\n     */\n    String DEVICE = \"iot:device\";\n\n    /**\n     * 产品信息的数据缓存，使用 Spring Cache 操作（忽略租户）\n     *\n     * KEY 格式：product_${productId}\n     * VALUE 数据类型：String(JSON)\n     */\n    String PRODUCT = \"iot:product\";\n\n    /**\n     * 物模型的数据缓存，使用 Spring Cache 操作（忽略租户）\n     *\n     * KEY 格式：thing_model_${productId}\n     * VALUE 数据类型：String 数组(JSON)，即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表\n     */\n    String THING_MODEL_LIST = \"iot:thing_model_list\";\n\n    /**\n     * 数据流转规则的数据缓存，使用 Spring Cache 操作\n     *\n     * KEY 格式：data_rule_list_${deviceId}_${method}_${identifier}\n     * VALUE 数据类型：String 数组(JSON)，即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO} 列表\n     */\n    String DATA_RULE_LIST = \"iot:data_rule_list\";\n\n    /**\n     * 数据目的的数据缓存，使用 Spring Cache 操作\n     *\n     * KEY 格式：data_sink_${id}\n     * VALUE 数据类型：String(JSON)，即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO}\n     */\n    String DATA_SINK = \"iot:data_sink\";\n\n    /**\n     * 场景联动规则的数据缓存，使用 Spring Cache 操作\n     * <p>\n     * KEY 格式：scene_rule_list_${productId}_${deviceId}\n     * VALUE 数据类型：String 数组(JSON)，即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO} 列表\n     */\n    String SCENE_RULE_LIST = \"iot:scene_rule_list\";\n\n    /**\n     * WebSocket 连接分布式锁\n     * <p>\n     * KEY 格式：websocket_connect_lock:${serverUrl}\n     * 用于保证 WebSocket 重连操作的线程安全\n     */\n    String WEBSOCKET_CONNECT_LOCK = \"iot:websocket_connect_lock:%s\";\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.redis.device;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport javax.annotation.Resource;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants.DEVICE_PROPERTY;\n\n/**\n * {@link IotDevicePropertyDO} 的 Redis DAO\n */\n@Repository\npublic class DevicePropertyRedisDAO {\n\n    @Resource\n    private StringRedisTemplate stringRedisTemplate;\n\n    public Map<String, IotDevicePropertyDO> get(Long id) {\n        String redisKey = formatKey(id);\n        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(redisKey);\n        if (CollUtil.isEmpty(entries)) {\n            return Collections.emptyMap();\n        }\n        return convertMap(entries.entrySet(),\n                entry -> (String) entry.getKey(),\n                entry -> JsonUtils.parseObject((String) entry.getValue(), IotDevicePropertyDO.class));\n    }\n\n    public void putAll(Long id, Map<String, IotDevicePropertyDO> properties) {\n        if (CollUtil.isEmpty(properties)) {\n            return;\n        }\n        String redisKey = formatKey(id);\n        stringRedisTemplate.opsForHash().putAll(redisKey, convertMap(properties.entrySet(),\n                Map.Entry::getKey,\n                entry -> JsonUtils.toJsonString(entry.getValue())));\n    }\n\n    private static String formatKey(Long id) {\n        return String.format(DEVICE_PROPERTY, id);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.redis.device;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n/**\n * 设备的最后上报时间的 Redis DAO\n *\n * @author 芋道源码\n */\n@Repository\npublic class DeviceReportTimeRedisDAO {\n\n    @Resource\n    private StringRedisTemplate stringRedisTemplate;\n\n    public void update(Long deviceId, LocalDateTime reportTime) {\n        stringRedisTemplate.opsForZSet().add(RedisKeyConstants.DEVICE_REPORT_TIMES, String.valueOf(deviceId),\n                LocalDateTimeUtil.toEpochMilli(reportTime));\n    }\n\n    public Set<Long> range(LocalDateTime maxReportTime) {\n        Set<String> values = stringRedisTemplate.opsForZSet().rangeByScore(RedisKeyConstants.DEVICE_REPORT_TIMES,\n                0, LocalDateTimeUtil.toEpochMilli(maxReportTime));\n        return convertSet(values, Long::parseLong);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceServerIdRedisDAO.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.redis.device;\n\nimport cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport javax.annotation.Resource;\n\n/**\n * 设备关联的网关 serverId 的 Redis DAO\n *\n * @author 芋道源码\n */\n@Repository\npublic class DeviceServerIdRedisDAO {\n\n    @Resource\n    private StringRedisTemplate stringRedisTemplate;\n\n    public void update(Long deviceId, String serverId) {\n        stringRedisTemplate.opsForHash().put(RedisKeyConstants.DEVICE_SERVER_ID,\n                String.valueOf(deviceId), serverId);\n    }\n\n    public String get(Long deviceId) {\n        Object value = stringRedisTemplate.opsForHash().get(RedisKeyConstants.DEVICE_SERVER_ID,\n                String.valueOf(deviceId));\n        return value != null ? (String) value : null;\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.tdengine;\n\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;\nimport cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS;\nimport com.baomidou.mybatisplus.annotation.InterceptorIgnore;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 设备消息 {@link IotDeviceMessageDO} Mapper 接口\n */\n@Mapper\n@TDengineDS\n@InterceptorIgnore(tenantLine = \"true\") // 避免 SQL 解析，因为 JSqlParser 对 TDengine 的 SQL 解析会报错\npublic interface IotDeviceMessageMapper {\n\n    /**\n     * 创建设备消息超级表\n     */\n    void createSTable();\n\n    /**\n     * 查询设备消息表是否存在\n     *\n     * @return 存在则返回表名；不存在则返回 null\n     */\n    String showSTable();\n\n    /**\n     * 插入设备消息数据\n     *\n     * 如果子表不存在，会自动创建子表\n     *\n     * @param message 设备消息数据\n     */\n    void insert(IotDeviceMessageDO message);\n\n    /**\n     * 获得设备消息分页\n     *\n     * @param reqVO 分页查询条件\n     * @return 设备消息列表\n     */\n    IPage<IotDeviceMessageDO> selectPage(IPage<IotDeviceMessageDO> page,\n                                         @Param(\"reqVO\") IotDeviceMessagePageReqVO reqVO);\n\n    /**\n     * 统计设备消息数量\n     *\n     * @param createTime 创建时间，如果为空，则统计所有消息数量\n     * @return 消息数量\n     */\n    Long selectCountByCreateTime(@Param(\"createTime\") Long createTime);\n\n    /**\n     * 按照 requestIds 批量查询消息\n     *\n     * @param deviceId 设备编号\n     * @param requestIds 请求编号集合\n     * @param reply 是否回复消息\n     * @return 消息列表\n     */\n    List<IotDeviceMessageDO> selectListByRequestIdsAndReply(@Param(\"deviceId\") Long deviceId,\n                                                            @Param(\"requestIds\") Collection<String> requestIds,\n                                                            @Param(\"reply\") Boolean reply);\n\n    /**\n     * 按照时间范围（小时），统计设备的消息数量\n     */\n    List<Map<String, Object>> selectDeviceMessageCountGroupByDate(@Param(\"startTime\") Long startTime,\n                                                                  @Param(\"endTime\") Long endTime);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java",
    "content": "package cn.iocoder.yudao.module.iot.dal.tdengine;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;\nimport cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS;\nimport com.baomidou.mybatisplus.annotation.InterceptorIgnore;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n@Mapper\n@TDengineDS\n@InterceptorIgnore(tenantLine = \"true\") // 避免 SQL 解析，因为 JSqlParser 对 TDengine 的 SQL 解析会报错\npublic interface IotDevicePropertyMapper {\n\n    List<TDengineTableField> getProductPropertySTableFieldList(@Param(\"productId\") Long productId);\n\n    void createProductPropertySTable(@Param(\"productId\") Long productId,\n                                     @Param(\"fields\") List<TDengineTableField> fields);\n\n    @SuppressWarnings(\"SimplifyStreamApiCallChains\") // 保持 JDK8 兼容性\n    default void alterProductPropertySTable(Long productId,\n                                            List<TDengineTableField> oldFields,\n                                            List<TDengineTableField> newFields) {\n        oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(),\n                TDengineTableField.FIELD_TS, \"report_time\", \"device_id\"));\n        List<TDengineTableField> addFields = newFields.stream().filter( // 新增的字段\n                        newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField())))\n                .collect(Collectors.toList());\n        List<TDengineTableField> dropFields = oldFields.stream().filter( // 删除的字段\n                        oldField -> newFields.stream().noneMatch(n -> n.getField().equals(oldField.getField())))\n                .collect(Collectors.toList());\n        List<TDengineTableField> modifyTypeFields = new ArrayList<>(); // 变更类型的字段\n        List<TDengineTableField> modifyLengthFields = new ArrayList<>(); // 变更长度的字段\n        newFields.forEach(newField -> {\n            TDengineTableField oldField = CollUtil.findOne(oldFields, field -> field.getField().equals(newField.getField()));\n            if (oldField == null) {\n                return;\n            }\n            if (ObjectUtil.notEqual(oldField.getType(), newField.getType())) {\n                modifyTypeFields.add(newField);\n                return;\n            }\n            if (newField.getLength() != null) {\n                if (newField.getLength() > oldField.getLength()) {\n                    modifyLengthFields.add(newField);\n                } else if (newField.getLength() < oldField.getLength()) {\n                    // 特殊：TDengine 长度修改时，只允许变长，所以此时认为是修改类型\n                    modifyTypeFields.add(newField);\n                }\n            }\n        });\n\n        // 执行\n        addFields.forEach(field -> alterProductPropertySTableAddField(productId, field));\n        dropFields.forEach(field -> alterProductPropertySTableDropField(productId, field));\n        modifyLengthFields.forEach(field -> alterProductPropertySTableModifyField(productId, field));\n        modifyTypeFields.forEach(field -> {\n            alterProductPropertySTableDropField(productId, field);\n            alterProductPropertySTableAddField(productId, field);\n        });\n    }\n\n    void alterProductPropertySTableAddField(@Param(\"productId\") Long productId,\n                                            @Param(\"field\") TDengineTableField field);\n\n    void alterProductPropertySTableModifyField(@Param(\"productId\") Long productId,\n                                               @Param(\"field\") TDengineTableField field);\n\n    void alterProductPropertySTableDropField(@Param(\"productId\") Long productId,\n                                             @Param(\"field\") TDengineTableField field);\n\n    void insert(@Param(\"device\") IotDeviceDO device,\n                @Param(\"properties\") Map<String, Object> properties,\n                @Param(\"reportTime\") Long reportTime);\n\n    List<IotDevicePropertyRespVO> selectListByHistory(@Param(\"reqVO\") IotDevicePropertyHistoryListReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/iot/config/YudaoIotProperties.java",
    "content": "package cn.iocoder.yudao.module.iot.framework.iot.config;\n\nimport lombok.Data;\nimport org.springframework.stereotype.Component;\n\nimport java.time.Duration;\n\n/**\n * 芋道 IoT 全局配置类\n *\n * @author 芋道源码\n */\n@Component\n@Data\npublic class YudaoIotProperties {\n\n    /**\n     * 设备连接超时时间\n     */\n    private Duration keepAliveTime = Duration.ofMinutes(10);\n    /**\n     * 设备连接超时时间的因子\n     *\n     * 因为设备可能会有网络抖动，所以需要乘以一个因子，避免误判\n     */\n    private double keepAliveFactor = 1.5D;\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/iot/package-info.java",
    "content": "/**\n * iot 模块的【全局】拓展封装\n */\npackage cn.iocoder.yudao.module.iot.framework.iot;"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/job/config/IotJobConfiguration.java",
    "content": "package cn.iocoder.yudao.module.iot.framework.job.config;\n\nimport cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.sql.DataSource;\n\n/**\n * IoT 模块的 Job 自动配置类\n *\n * @author 芋道源码\n */\n@Configuration\npublic class IotJobConfiguration {\n\n    @Bean(initMethod = \"start\", destroyMethod = \"stop\")\n    public IotSchedulerManager iotSchedulerManager(DataSource dataSource,\n                                                   ApplicationContext applicationContext) {\n        return new IotSchedulerManager(dataSource, applicationContext);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java",
    "content": "package cn.iocoder.yudao.module.iot.framework.job.core;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.quartz.*;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.scheduling.quartz.SchedulerFactoryBean;\nimport org.springframework.scheduling.quartz.SpringBeanJobFactory;\n\nimport javax.sql.DataSource;\nimport java.util.Map;\nimport java.util.Properties;\n\n/**\n * IoT 模块的 Scheduler 管理类，基于 Quartz 实现\n *\n * 疑问：为什么 IoT 模块不复用全局的 SchedulerManager 呢？\n * 回复：yudao-cloud 项目，使用的是 XXL-Job 作为调度中心，无法动态添加任务。\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotSchedulerManager {\n\n    private static final String SCHEDULER_NAME = \"iotScheduler\";\n\n    private final SchedulerFactoryBean schedulerFactoryBean;\n\n    private Scheduler scheduler;\n\n    public IotSchedulerManager(DataSource dataSource,\n                               ApplicationContext applicationContext) {\n        // 1. 参考 SchedulerFactoryBean 类\n        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();\n        SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();\n        jobFactory.setApplicationContext(applicationContext);\n        schedulerFactoryBean.setJobFactory(jobFactory);\n        schedulerFactoryBean.setAutoStartup(true);\n        schedulerFactoryBean.setSchedulerName(SCHEDULER_NAME);\n        schedulerFactoryBean.setDataSource(dataSource);\n        schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);\n        Properties properties = new Properties();\n        schedulerFactoryBean.setQuartzProperties(properties);\n        // 2. 参考 application-local.yaml 配置文件\n        // 2.1 Scheduler 相关配置\n        properties.put(\"org.quartz.scheduler.instanceName\", SCHEDULER_NAME);\n        properties.put(\"org.quartz.scheduler.instanceId\", \"AUTO\");\n        // 2.2 JobStore 相关配置\n        properties.put(\"org.quartz.jobStore.class\", \"org.springframework.scheduling.quartz.LocalDataSourceJobStore\");\n        properties.put(\"org.quartz.jobStore.isClustered\", \"true\");\n        properties.put(\"org.quartz.jobStore.clusterCheckinInterval\", \"15000\");\n        properties.put(\"org.quartz.jobStore.misfireThreshold\", \"60000\");\n        // 2.3 线程池相关配置\n        properties.put(\"org.quartz.threadPool.threadCount\", \"25\");\n        properties.put(\"org.quartz.threadPool.threadPriority\", \"5\");\n        properties.put(\"org.quartz.threadPool.class\", \"org.quartz.simpl.SimpleThreadPool\");\n        this.schedulerFactoryBean = schedulerFactoryBean;\n    }\n\n    public void start() throws Exception {\n        log.info(\"[start][Scheduler 初始化开始]\");\n        // 初始化\n        schedulerFactoryBean.afterPropertiesSet();\n        schedulerFactoryBean.start();\n        // 获得 Scheduler 对象\n        this.scheduler = schedulerFactoryBean.getScheduler();\n        log.info(\"[start][Scheduler 初始化完成]\");\n    }\n\n    public void stop() {\n        log.info(\"[stop][Scheduler 关闭开始]\");\n        schedulerFactoryBean.stop();\n        this.scheduler = null;\n        log.info(\"[stop][Scheduler 关闭完成]\");\n    }\n\n    // ========== 参考 SchedulerManager 实现 ==========\n\n    /**\n     * 添加或更新 Job 到 Quartz 中\n     *\n     * @param jobClass 任务处理器的类\n     * @param jobName 任务名\n     * @param cronExpression CRON 表达式\n     * @param jobDataMap 任务数据\n     * @throws SchedulerException 添加异常\n     */\n    public void addOrUpdateJob(Class <? extends Job> jobClass, String jobName,\n                               String cronExpression, Map<String, Object> jobDataMap)\n            throws SchedulerException {\n        if (scheduler.checkExists(new JobKey(jobName))) {\n            this.updateJob(jobName, cronExpression);\n        } else {\n            this.addJob(jobClass, jobName, cronExpression, jobDataMap);\n        }\n    }\n\n    /**\n     * 添加 Job 到 Quartz 中\n     *\n     * @param jobClass 任务处理器的类\n     * @param jobName 任务名\n     * @param cronExpression CRON 表达式\n     * @param jobDataMap 任务数据\n     * @throws SchedulerException 添加异常\n     */\n    public void addJob(Class <? extends Job> jobClass, String jobName,\n                       String cronExpression, Map<String, Object> jobDataMap)\n            throws SchedulerException {\n        // 创建 JobDetail 对象\n        JobDetail jobDetail = JobBuilder.newJob(jobClass)\n                .usingJobData(new JobDataMap(jobDataMap))\n                .withIdentity(jobName).build();\n        // 创建 Trigger 对象\n        Trigger trigger = this.buildTrigger(jobName, cronExpression);\n        // 新增 Job 调度\n        scheduler.scheduleJob(jobDetail, trigger);\n    }\n\n    /**\n     * 更新 Job 到 Quartz\n     *\n     * @param jobName 任务名\n     * @param cronExpression CRON 表达式\n     * @throws SchedulerException 更新异常\n     */\n    public void updateJob(String jobName, String cronExpression)\n            throws SchedulerException {\n        // 创建新 Trigger 对象\n        Trigger newTrigger = this.buildTrigger(jobName, cronExpression);\n        // 修改调度\n        scheduler.rescheduleJob(new TriggerKey(jobName), newTrigger);\n    }\n\n    /**\n     * 删除 Quartz 中的 Job\n     *\n     * @param jobName 任务名\n     * @throws SchedulerException 删除异常\n     */\n    public void deleteJob(String jobName) throws SchedulerException {\n        // 暂停 Trigger 对象\n        scheduler.pauseTrigger(new TriggerKey(jobName));\n        // 取消并删除 Job 调度\n        scheduler.unscheduleJob(new TriggerKey(jobName));\n        scheduler.deleteJob(new JobKey(jobName));\n    }\n\n    /**\n     * 暂停 Quartz 中的 Job\n     *\n     * @param jobName 任务名\n     * @throws SchedulerException 暂停异常\n     */\n    public void pauseJob(String jobName) throws SchedulerException {\n        scheduler.pauseJob(new JobKey(jobName));\n    }\n\n    /**\n     * 启动 Quartz 中的 Job\n     *\n     * @param jobName 任务名\n     * @throws SchedulerException 启动异常\n     */\n    public void resumeJob(String jobName) throws SchedulerException {\n        scheduler.resumeJob(new JobKey(jobName));\n        scheduler.resumeTrigger(new TriggerKey(jobName));\n    }\n\n    /**\n     * 立即触发一次 Quartz 中的 Job\n     *\n     * @param jobName 任务名\n     * @throws SchedulerException 触发异常\n     */\n    public void triggerJob(String jobName) throws SchedulerException {\n        scheduler.triggerJob(new JobKey(jobName));\n    }\n\n    private Trigger buildTrigger(String jobName, String cronExpression) {\n        return TriggerBuilder.newTrigger()\n                .withIdentity(jobName)\n                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/package-info.java",
    "content": "/**\n * 属于 iot 模块的 framework 封装\n *\n * @author ahh\n */\npackage cn.iocoder.yudao.module.iot.framework;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/rpc/config/RpcConfiguration.java",
    "content": "package cn.iocoder.yudao.module.iot.framework.rpc.config;\n\nimport cn.iocoder.yudao.module.system.api.mail.MailSendApi;\nimport cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;\nimport cn.iocoder.yudao.module.system.api.sms.SmsSendApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(value = \"iotRpcConfiguration\", proxyBeanMethods = false)\n@EnableFeignClients(clients = {\n        AdminUserApi.class, SmsSendApi.class, MailSendApi.class, NotifyMessageSendApi.class\n})\npublic class RpcConfiguration {\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/rpc/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.iot.framework.rpc;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java",
    "content": "package cn.iocoder.yudao.module.iot.framework.security.config;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.module.iot.enums.ApiConstants;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\n/**\n * IoT 模块的 Security 配置\n */\n@Configuration(\"iotSecurityConfiguration\")\npublic class SecurityConfiguration {\n\n    @Bean(\"iotAuthorizeRequestsCustomizer\")\n    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {\n        return new AuthorizeRequestsCustomizer() {\n\n            @Override\n            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n                // Swagger 接口文档\n                registry.requestMatchers(\"/v3/api-docs/**\").permitAll()\n                        .requestMatchers(\"/webjars/**\").permitAll()\n                        .requestMatchers(\"/swagger-ui\").permitAll()\n                        .requestMatchers(\"/swagger-ui/**\").permitAll();\n                // Spring Boot Actuator 的安全配置\n                registry.requestMatchers(\"/actuator\").anonymous()\n                        .requestMatchers(\"/actuator/**\").anonymous();\n                // Druid 监控\n                registry.requestMatchers(\"/druid/**\").anonymous();\n                // RPC 服务的安全配置\n                registry.requestMatchers(ApiConstants.PREFIX + \"/**\").permitAll();\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.iot.framework.security.core;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java",
    "content": "package cn.iocoder.yudao.module.iot.framework.tdengine.config;\n\nimport cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.stereotype.Component;\n\n/**\n * TDengine 表初始化的 Configuration\n *\n * @author alwayssuper\n */\n@Component\n@RequiredArgsConstructor\n@Slf4j\npublic class TDengineTableInitRunner implements ApplicationRunner {\n\n    private final IotDeviceMessageService deviceMessageService;\n\n    @Override\n    public void run(ApplicationArguments args) {\n        try {\n            // 初始化设备消息表\n            deviceMessageService.defineDeviceMessageStable();\n        } catch (Exception ex) {\n            // 初始化失败时打印错误消息并退出系统\n            log.error(\"[run][TDengine初始化设备消息表结构失败，系统无法正常运行，即将退出]\", ex);\n            System.exit(1);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java",
    "content": "package cn.iocoder.yudao.module.iot.framework.tdengine.core;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * TDEngine 表字段\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TDengineTableField {\n\n    /**\n     * 字段名 - TDengine 默认 ts 字段，默认会被 TDengine 创建\n     */\n    public static final String FIELD_TS = \"ts\";\n\n    public static final String TYPE_TINYINT = \"TINYINT\";\n    public static final String TYPE_INT = \"INT\";\n    public static final String TYPE_FLOAT = \"FLOAT\";\n    public static final String TYPE_DOUBLE = \"DOUBLE\";\n    public static final String TYPE_BOOL = \"BOOL\";\n    public static final String TYPE_NCHAR = \"NCHAR\";\n    public static final String TYPE_VARCHAR = \"VARCHAR\";\n    public static final String TYPE_TIMESTAMP = \"TIMESTAMP\";\n\n    /**\n     * 字段长度 - VARCHAR 默认长度\n     */\n    public static final int LENGTH_VARCHAR = 1024;\n\n    /**\n     * 注释 - TAG 字段\n     */\n    public static final String NOTE_TAG = \"TAG\";\n\n    /**\n     * 字段名\n     */\n    private String field;\n\n    /**\n     * 字段类型\n     */\n    private String type;\n\n    /**\n     * 字段长度\n     */\n    private Integer length;\n\n    /**\n     * 注释\n     */\n    private String note;\n\n    public TDengineTableField(String field, String type) {\n        this.field = field;\n        this.type = type;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java",
    "content": "package cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation;\n\nimport com.baomidou.dynamic.datasource.annotation.DS;\n\nimport java.lang.annotation.*;\n\n/**\n * TDEngine 数据源\n *\n * @author 芋道源码\n */\n@Target({ElementType.TYPE, ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@DS(\"tdengine\")\npublic @interface TDengineDS {\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java",
    "content": "/**\n * iot 模块的 tdengine 拓展封装\n */\npackage cn.iocoder.yudao.module.iot.framework.tdengine;"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/web/config/IotWebConfiguration.java",
    "content": "package cn.iocoder.yudao.module.iot.framework.web.config;\n\nimport cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration;\nimport org.springdoc.core.GroupedOpenApi;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * iot 模块的 web 组件的 Configuration\n *\n * @author ahh\n */\n@Configuration(proxyBeanMethods = false)\npublic class IotWebConfiguration {\n\n    /**\n     * iot 模块的 API 分组\n     */\n    @Bean\n    public GroupedOpenApi iotGroupedOpenApi() {\n        return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi(\"iot\");\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/framework/web/package-info.java",
    "content": "/**\n * iot 模块的 web 拓展封装\n */\npackage cn.iocoder.yudao.module.iot.framework.web;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java",
    "content": "package cn.iocoder.yudao.module.iot.job.device;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.tenant.core.job.TenantJob;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.framework.iot.config.YudaoIotProperties;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * IoT 设备离线检查 Job\n *\n * 检测逻辑：设备最后一条 {@link IotDeviceMessage} 消息超过一定时间，则认为设备离线\n *\n * @see <a href=\"https://help.aliyun.com/zh/iot/support/faq-about-device-status#98f7056b2957y\">阿里云 IoT —— 设备离线分析</a>\n * @author 芋道源码\n */\n@Component\npublic class IotDeviceOfflineCheckJob {\n\n    @Resource\n    private YudaoIotProperties iotProperties;\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotDevicePropertyService devicePropertyService;\n    @Resource\n    private IotDeviceMessageService deviceMessageService;\n\n    @XxlJob(\"deviceOfflineCheckJob\")\n    @TenantJob // 多租户\n    public String execute(String param) {\n        // 1.1 获得在线设备列表\n        List<IotDeviceDO> devices = deviceService.getDeviceListByState(IotDeviceStateEnum.ONLINE.getState());\n        if (CollUtil.isEmpty(devices)) {\n            return JsonUtils.toJsonString(Collections.emptyList());\n        }\n        // 1.2 获取超时的设备集合\n        Set<Long> timeoutDeviceIds = devicePropertyService.getDeviceIdListByReportTime(getTimeoutTime());\n\n        // 2. 下线设备\n        List<String[]> offlineDevices = CollUtil.newArrayList();\n        for (IotDeviceDO device : devices) {\n            if (!timeoutDeviceIds.contains(device.getId())) {\n                continue;\n            }\n            offlineDevices.add(new String[]{device.getProductKey(), device.getDeviceName()});\n            // 为什么不直接更新状态呢？因为通过 IotDeviceMessage 可以经过一系列的处理，例如说记录日志等等\n            deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateOffline().setDeviceId(device.getId()));\n        }\n        return JsonUtils.toJsonString(offlineDevices);\n    }\n\n    private LocalDateTime getTimeoutTime() {\n        return LocalDateTime.now().minus(Duration.ofNanos(\n                (long) (iotProperties.getKeepAliveTime().toNanos() * iotProperties.getKeepAliveFactor())));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java",
    "content": "package cn.iocoder.yudao.module.iot.job.ota;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.tenant.core.job.TenantJob;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService;\nimport cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * IoT OTA 升级推送 Job：查询待推送的 OTA 升级记录，并推送给设备\n *\n * @author 芋道源码\n */\n@Component\n@Slf4j\npublic class IotOtaUpgradeJob {\n\n    @Resource\n    private IotOtaTaskRecordService otaTaskRecordService;\n    @Resource\n    private IotOtaFirmwareService otaFirmwareService;\n    @Resource\n    private IotDeviceService deviceService;\n\n    @XxlJob(\"deviceUpgradeJob\")\n    @TenantJob // 多租户\n    public String execute(String param) throws Exception {\n        // 1. 查询待推送的 OTA 升级记录\n        List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaRecordListByStatus(\n                IotOtaTaskRecordStatusEnum.PENDING.getStatus());\n        if (CollUtil.isEmpty(records)) {\n            return null;\n        }\n\n        // TODO 芋艿：可以优化成批量获取 原因是：1. N+1 问题；2. offline 的设备无需查询\n        // 2. 遍历推送记录\n        int successCount = 0;\n        int failureCount = 0;\n        Map<Long, IotOtaFirmwareDO> otaFirmwares = new HashMap<>();\n        for (IotOtaTaskRecordDO record : records) {\n            try {\n                // 2.1 设备如果不在线，直接跳过\n                IotDeviceDO device = deviceService.getDeviceFromCache(record.getDeviceId());\n                // TODO 芋艿：【优化】当前逻辑跳过了离线的设备，但未充分利用 MQTT 的离线消息能力。\n                // 1. MQTT 协议本身支持持久化会话（Clean Session=false）和 QoS > 0 的消息，允许 broker 为离线设备缓存消息。\n                // 2. 对于 OTA 升级这类非实时性强的任务，即使设备当前离线，也应该可以推送升级指令。设备在下次上线时即可收到。\n                // 3. 后续可以考虑：增加一个“允许离线推送”的选项。如果开启，即使设备状态为 OFFLINE，也应尝试推送消息，依赖 MQTT Broker 的能力进行离线缓存。\n                if (device == null || IotDeviceStateEnum.isNotOnline(device.getState())) {\n                    continue;\n                }\n                // 2.2 获取 OTA 固件信息\n                IotOtaFirmwareDO fireware = otaFirmwares.get(record.getFirmwareId());\n                if (fireware == null) {\n                    fireware = otaFirmwareService.getOtaFirmware(record.getFirmwareId());\n                    otaFirmwares.put(record.getFirmwareId(), fireware);\n                }\n                // 2.3 推送 OTA 升级任务\n                boolean result = otaTaskRecordService.pushOtaTaskRecord(record, fireware, device);\n                if (result) {\n                    successCount++;\n                } else {\n                    failureCount++;\n                }\n            } catch (Exception e) {\n                failureCount++;\n                log.error(\"[execute][推送 OTA 升级任务({})发生异常]\", record.getId(), e);\n            }\n        }\n        return StrUtil.format(\"升级任务推送成功：{} 条，送失败：{} 条\", successCount, failureCount);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotSceneRuleJob.java",
    "content": "package cn.iocoder.yudao.module.iot.job.rule;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.quartz.JobExecutionContext;\nimport org.springframework.scheduling.quartz.QuartzJobBean;\n\nimport javax.annotation.Resource;\nimport java.util.Map;\n\n/**\n * IoT 规则场景 Job，用于执行 {@link IotSceneRuleTriggerTypeEnum#TIMER} 类型的规则场景\n *\n * @author 芋道源码\n */\n@Slf4j\npublic class IotSceneRuleJob extends QuartzJobBean {\n\n    /**\n     * JobData Key - 规则场景编号\n     */\n    public static final String JOB_DATA_KEY_RULE_SCENE_ID = \"sceneRuleId\";\n\n    @Resource\n    private IotSceneRuleService sceneRuleService;\n\n    @Override\n    protected void executeInternal(JobExecutionContext context) {\n        // 获得规则场景编号\n        Long sceneRuleId = context.getMergedJobDataMap().getLong(JOB_DATA_KEY_RULE_SCENE_ID);\n\n        // 执行规则场景\n        sceneRuleService.executeSceneRuleByTimer(sceneRuleId);\n    }\n\n    /**\n     * 创建 JobData Map\n     *\n     * @param sceneRuleId 规则场景编号\n     * @return JobData Map\n     */\n    public static Map<String, Object> buildJobDataMap(Long sceneRuleId) {\n        return MapUtil.of(JOB_DATA_KEY_RULE_SCENE_ID, sceneRuleId);\n    }\n\n    /**\n     * 创建 Job 名字\n     *\n     * @param sceneRuleId 规则场景编号\n     * @return Job 名字\n     */\n    public static String buildJobName(Long sceneRuleId) {\n        return String.format(\"%s_%d\", IotSceneRuleJob.class.getSimpleName(), sceneRuleId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.mq.consumer.device;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.Objects;\n\n/**\n * 针对 {@link IotDeviceMessage} 的业务处理器：调用 method 对应的逻辑。例如说：\n * 1. {@link IotDeviceMessageMethodEnum#PROPERTY_POST} 属性上报时，记录设备属性\n *\n * @author alwayssuper\n */\n@Component\n@Slf4j\npublic class IotDeviceMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotDevicePropertyService devicePropertyService;\n    @Resource\n    private IotDeviceMessageService deviceMessageService;\n\n    @Resource\n    private IotMessageBus messageBus;\n\n    @PostConstruct\n    public void init() {\n        messageBus.register(this);\n    }\n\n    @Override\n    public String getTopic() {\n        return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC;\n    }\n\n    @Override\n    public String getGroup() {\n        return \"iot_device_message_consumer\";\n    }\n\n    @Override\n    public void onMessage(IotDeviceMessage message) {\n        if (!IotDeviceMessageUtils.isUpstreamMessage(message)) {\n            log.error(\"[onMessage][message({}) 非上行消息，不进行处理]\", message);\n            return;\n        }\n\n        TenantUtils.execute(message.getTenantId(), () -> {\n            // 1.1 更新设备的最后时间\n            IotDeviceDO device = deviceService.validateDeviceExistsFromCache(message.getDeviceId());\n            devicePropertyService.updateDeviceReportTimeAsync(device.getId(), LocalDateTime.now());\n            // 1.2 更新设备的连接 server\n            devicePropertyService.updateDeviceServerIdAsync(device.getId(), message.getServerId());\n\n            // 2. 未上线的设备，强制上线\n            forceDeviceOnline(message, device);\n\n            // 3. 核心：处理消息\n            deviceMessageService.handleUpstreamDeviceMessage(message, device);\n        });\n    }\n\n    private void forceDeviceOnline(IotDeviceMessage message, IotDeviceDO device) {\n        // 已经在线，无需处理\n        if (ObjectUtil.equal(device.getState(), IotDeviceStateEnum.ONLINE.getState())) {\n            return;\n        }\n        // 如果是 STATE 相关的消息，无需处理，不然就重复处理状态了\n        if (Objects.equals(message.getMethod(), IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod())) {\n            return;\n        }\n\n        // 特殊：设备非在线时，主动标记设备为在线\n        // 为什么不直接更新状态呢？因为通过 IotDeviceMessage 可以经过一系列的处理，例如说记录日志、规则引擎等等\n        try {\n            deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateUpdateOnline().setDeviceId(device.getId()));\n        } catch (Exception e) {\n            // 注意：即使执行失败，也不影响主流程\n            log.error(\"[forceDeviceOnline][message({}) device({}) 强制设备上线失败]\", message, device, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotDataRuleMessageSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.mq.consumer.rule;\n\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.service.rule.data.IotDataRuleService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.Resource;\n\n/**\n * 针对 {@link IotDeviceMessage} 的消费者，处理数据流转\n *\n * @author 芋道源码\n */\n@Component\n@Slf4j\npublic class IotDataRuleMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {\n\n    @Resource\n    private IotDataRuleService dataRuleService;\n\n    @Resource\n    private IotMessageBus messageBus;\n\n    @PostConstruct\n    public void init() {\n        messageBus.register(this);\n    }\n\n    @Override\n    public String getTopic() {\n        return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC;\n    }\n\n    @Override\n    public String getGroup() {\n        return \"iot_data_rule_consumer\";\n    }\n\n    @Override\n    public void onMessage(IotDeviceMessage message) {\n        TenantUtils.execute(message.getTenantId(), () -> dataRuleService.executeDataRule(message));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageSubscriber.java",
    "content": "package cn.iocoder.yudao.module.iot.mq.consumer.rule;\n\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;\nimport cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.Resource;\n\n/**\n * 针对 {@link IotDeviceMessage} 的消费者，处理规则场景\n *\n * @author 芋道源码\n */\n@Component\n@Slf4j\npublic class IotSceneRuleMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {\n\n    @Resource\n    private IotSceneRuleService sceneRuleService;\n\n    @Resource\n    private IotMessageBus messageBus;\n\n    @PostConstruct\n    public void init() {\n        messageBus.register(this);\n    }\n\n    @Override\n    public String getTopic() {\n        return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC;\n    }\n\n    @Override\n    public String getGroup() {\n        return \"iot_rule_consumer\";\n    }\n\n    @Override\n    public void onMessage(IotDeviceMessage message) {\n        log.info(\"[onMessage][消息内容({})]\", message);\n        sceneRuleService.executeSceneRuleByDevice(message);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java",
    "content": "/**\n * 消息队列的生产者\n */\npackage cn.iocoder.yudao.module.iot.mq.producer;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/package-info.java",
    "content": "/**\n * iot 模块，物联网 模块，主要实现 产品管理、设备管理、协议管理等功能。TODO 芋艿：完善下\n *\n * 1. Controller URL：以 /iot/ 开头，避免和其它 Module 冲突\n * 2. DataObject 表名：以 iot_ 开头，方便在数据库中区分\n */\npackage cn.iocoder.yudao.module.iot;\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.alert;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * IoT 告警配置 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotAlertConfigService {\n\n    /**\n     * 创建告警配置\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createAlertConfig(@Valid IotAlertConfigSaveReqVO createReqVO);\n\n    /**\n     * 更新告警配置\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateAlertConfig(@Valid IotAlertConfigSaveReqVO updateReqVO);\n\n    /**\n     * 删除告警配置\n     *\n     * @param id 编号\n     */\n    void deleteAlertConfig(Long id);\n\n    /**\n     * 获得告警配置\n     *\n     * @param id 编号\n     * @return 告警配置\n     */\n    IotAlertConfigDO getAlertConfig(Long id);\n\n    /**\n     * 获得告警配置分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 告警配置分页\n     */\n    PageResult<IotAlertConfigDO> getAlertConfigPage(IotAlertConfigPageReqVO pageReqVO);\n\n    /**\n     * 获得告警配置列表\n     *\n     * @param status 状态\n     * @return 告警配置列表\n     */\n    List<IotAlertConfigDO> getAlertConfigListByStatus(Integer status);\n\n    /**\n     * 获得告警配置列表\n     *\n     * @param sceneRuleId 场景流动规则编号\n     * @return 告警配置列表\n     */\n    List<IotAlertConfigDO> getAlertConfigListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status);\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.alert;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertConfigMapper;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.ALERT_CONFIG_NOT_EXISTS;\n\n/**\n * IoT 告警配置 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class IotAlertConfigServiceImpl implements IotAlertConfigService {\n\n    @Resource\n    private IotAlertConfigMapper alertConfigMapper;\n\n    @Resource\n    @Lazy // 延迟，避免循环依赖报错\n    private IotSceneRuleService sceneRuleService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @Override\n    public Long createAlertConfig(IotAlertConfigSaveReqVO createReqVO) {\n        // 校验关联数据是否存在\n        sceneRuleService.validateSceneRuleList(createReqVO.getSceneRuleIds());\n        adminUserApi.validateUserList(createReqVO.getReceiveUserIds());\n\n        // 插入\n        IotAlertConfigDO alertConfig = BeanUtils.toBean(createReqVO, IotAlertConfigDO.class);\n        alertConfigMapper.insert(alertConfig);\n        return alertConfig.getId();\n    }\n\n    @Override\n    public void updateAlertConfig(IotAlertConfigSaveReqVO updateReqVO) {\n        // 校验存在\n        validateAlertConfigExists(updateReqVO.getId());\n        // 校验关联数据是否存在\n        sceneRuleService.validateSceneRuleList(updateReqVO.getSceneRuleIds());\n        adminUserApi.validateUserList(updateReqVO.getReceiveUserIds());\n\n        // 更新\n        IotAlertConfigDO updateObj = BeanUtils.toBean(updateReqVO, IotAlertConfigDO.class);\n        alertConfigMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteAlertConfig(Long id) {\n        // 校验存在\n        validateAlertConfigExists(id);\n        // 删除\n        alertConfigMapper.deleteById(id);\n    }\n\n    private void validateAlertConfigExists(Long id) {\n        if (alertConfigMapper.selectById(id) == null) {\n            throw exception(ALERT_CONFIG_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public IotAlertConfigDO getAlertConfig(Long id) {\n        return alertConfigMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<IotAlertConfigDO> getAlertConfigPage(IotAlertConfigPageReqVO pageReqVO) {\n        return alertConfigMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<IotAlertConfigDO> getAlertConfigListByStatus(Integer status) {\n        return alertConfigMapper.selectListByStatus(status);\n    }\n\n    @Override\n    public List<IotAlertConfigDO> getAlertConfigListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) {\n        return alertConfigMapper.selectListBySceneRuleIdAndStatus(sceneRuleId, status);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.alert;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * IoT 告警记录 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotAlertRecordService {\n\n    /**\n     * 获得告警记录\n     *\n     * @param id 编号\n     * @return 告警记录\n     */\n    IotAlertRecordDO getAlertRecord(Long id);\n\n    /**\n     * 获得告警记录分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 告警记录分页\n     */\n    PageResult<IotAlertRecordDO> getAlertRecordPage(IotAlertRecordPageReqVO pageReqVO);\n\n    /**\n     * 获得指定场景规则的告警记录列表\n     *\n     * @param sceneRuleId 场景规则编号\n     * @param deviceId 设备编号\n     * @param processStatus 处理状态，允许空\n     * @return 告警记录列表\n     */\n    List<IotAlertRecordDO> getAlertRecordListBySceneRuleId(@NotNull(message = \"场景规则编号不能为空\") Long sceneRuleId,\n                                                           Long deviceId, Boolean processStatus);\n\n    /**\n     * 处理告警记录\n     *\n     * @param ids 告警记录编号\n     * @param remark 处理结果（备注）\n     */\n    void processAlertRecordList(Collection<Long> ids, String remark);\n\n    /**\n     * 创建告警记录（包含场景规则编号）\n     *\n     * @param config 告警配置\n     * @param sceneRuleId 场景规则编号\n     * @param deviceMessage 设备消息，可为空\n     * @return 告警记录编号\n     */\n    Long createAlertRecord(IotAlertConfigDO config, Long sceneRuleId, IotDeviceMessage deviceMessage);\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.alert;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertRecordMapper;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * IoT 告警记录 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class IotAlertRecordServiceImpl implements IotAlertRecordService {\n\n    @Resource\n    private IotAlertRecordMapper alertRecordMapper;\n\n    @Resource\n    private IotDeviceService deviceService;\n\n    @Override\n    public IotAlertRecordDO getAlertRecord(Long id) {\n        return alertRecordMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<IotAlertRecordDO> getAlertRecordPage(IotAlertRecordPageReqVO pageReqVO) {\n        return alertRecordMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<IotAlertRecordDO> getAlertRecordListBySceneRuleId(Long sceneRuleId, Long deviceId, Boolean processStatus) {\n        return alertRecordMapper.selectListBySceneRuleId(sceneRuleId, deviceId, processStatus);\n    }\n\n    @Override\n    public void processAlertRecordList(Collection<Long> ids, String processRemark) {\n        if (CollUtil.isEmpty(ids)) {\n            return;\n        }\n        // 批量更新告警记录的处理状态\n        alertRecordMapper.updateList(ids, IotAlertRecordDO.builder()\n                .processStatus(true).processRemark(processRemark).build());\n    }\n\n    @Override\n    public Long createAlertRecord(IotAlertConfigDO config, Long sceneRuleId, IotDeviceMessage message) {\n        // 构建告警记录\n        IotAlertRecordDO.IotAlertRecordDOBuilder builder = IotAlertRecordDO.builder()\n                .configId(config.getId()).configName(config.getName()).configLevel(config.getLevel())\n                .sceneRuleId(sceneRuleId).processStatus(false);\n        if (message != null) {\n            builder.deviceMessage(message);\n            // 填充设备信息\n            IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId());\n            if (device != null) {\n                builder.productId(device.getProductId()).deviceId(device.getId());\n            }\n        }\n\n        // 插入记录\n        IotAlertRecordDO record = builder.build();\n        alertRecordMapper.insert(record);\n        return record.getId();\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * IoT 设备分组 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDeviceGroupService {\n\n    /**\n     * 创建设备分组\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDeviceGroup(@Valid IotDeviceGroupSaveReqVO createReqVO);\n\n    /**\n     * 更新设备分组\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDeviceGroup(@Valid IotDeviceGroupSaveReqVO updateReqVO);\n\n    /**\n     * 删除设备分组\n     *\n     * @param id 编号\n     */\n    void deleteDeviceGroup(Long id);\n\n    /**\n     * 校验设备分组是否存在\n     *\n     * @param ids 设备分组 ID 数组\n     */\n    default List<IotDeviceGroupDO> validateDeviceGroupExists(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return ListUtil.empty();\n        }\n        return convertList(ids, this::validateDeviceGroupExists);\n    }\n\n    /**\n     * 校验设备分组是否存在\n     *\n     * @param id 设备分组 ID\n     * @return 设备分组\n     */\n    IotDeviceGroupDO validateDeviceGroupExists(Long id);\n\n    /**\n     * 获得设备分组\n     *\n     * @param id 编号\n     * @return 设备分组\n     */\n    IotDeviceGroupDO getDeviceGroup(Long id);\n\n    /**\n     * 获得设备分组\n     *\n     * @param name 名称\n     * @return 设备分组\n     */\n    IotDeviceGroupDO getDeviceGroupByName(String name);\n\n    /**\n     * 获得设备分组分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 设备分组分页\n     */\n    PageResult<IotDeviceGroupDO> getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO);\n\n    /**\n     * 获得设备分组列表\n     *\n     * @param status 状态\n     * @return 设备分组列表\n     */\n    List<IotDeviceGroupDO> getDeviceGroupListByStatus(Integer status);\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceGroupMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_NOT_EXISTS;\n\n/**\n * IoT 设备分组 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class IotDeviceGroupServiceImpl implements IotDeviceGroupService {\n\n    @Resource\n    private IotDeviceGroupMapper deviceGroupMapper;\n\n    @Resource\n    private IotDeviceService deviceService;\n\n    @Override\n    public Long createDeviceGroup(IotDeviceGroupSaveReqVO createReqVO) {\n        // 插入\n        IotDeviceGroupDO deviceGroup = BeanUtils.toBean(createReqVO, IotDeviceGroupDO.class);\n        deviceGroupMapper.insert(deviceGroup);\n        // 返回\n        return deviceGroup.getId();\n    }\n\n    @Override\n    public void updateDeviceGroup(IotDeviceGroupSaveReqVO updateReqVO) {\n        // 校验存在\n        validateDeviceGroupExists(updateReqVO.getId());\n        // 更新\n        IotDeviceGroupDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceGroupDO.class);\n        deviceGroupMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteDeviceGroup(Long id) {\n        // 1.1 校验存在\n        validateDeviceGroupExists(id);\n        // 1.2 校验是否存在设备\n        if (deviceService.getDeviceCountByGroupId(id) > 0) {\n            throw exception(DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS);\n        }\n\n        // 删除\n        deviceGroupMapper.deleteById(id);\n    }\n\n    @Override\n    public IotDeviceGroupDO validateDeviceGroupExists(Long id) {\n        IotDeviceGroupDO group = deviceGroupMapper.selectById(id);\n        if (group == null) {\n            throw exception(DEVICE_GROUP_NOT_EXISTS);\n        }\n        return group;\n    }\n\n    @Override\n    public IotDeviceGroupDO getDeviceGroup(Long id) {\n        return deviceGroupMapper.selectById(id);\n    }\n\n    @Override\n    public IotDeviceGroupDO getDeviceGroupByName(String name) {\n        return deviceGroupMapper.selectByName(name);\n    }\n\n    @Override\n    public PageResult<IotDeviceGroupDO> getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO) {\n        return deviceGroupMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<IotDeviceGroupDO> getDeviceGroupListByStatus(Integer status) {\n        return deviceGroupMapper.selectListByStatus(status);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device;\n\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * IoT 设备 Modbus 连接配置 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDeviceModbusConfigService {\n\n    /**\n     * 保存设备 Modbus 连接配置（新增或更新）\n     *\n     * @param saveReqVO 保存信息\n     */\n    void saveDeviceModbusConfig(@Valid IotDeviceModbusConfigSaveReqVO saveReqVO);\n\n    /**\n     * 获得设备 Modbus 连接配置\n     *\n     * @param id 编号\n     * @return 设备 Modbus 连接配置\n     */\n    IotDeviceModbusConfigDO getDeviceModbusConfig(Long id);\n\n    /**\n     * 根据设备编号获得 Modbus 连接配置\n     *\n     * @param deviceId 设备编号\n     * @return 设备 Modbus 连接配置\n     */\n    IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId);\n\n    /**\n     * 获得 Modbus 连接配置列表\n     *\n     * @param listReqDTO 查询参数\n     * @return Modbus 连接配置列表\n     */\n    List<IotDeviceModbusConfigDO> getDeviceModbusConfigList(IotModbusDeviceConfigListReqDTO listReqDTO);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceModbusConfigMapper;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\n/**\n * IoT 设备 Modbus 连接配置 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class IotDeviceModbusConfigServiceImpl implements IotDeviceModbusConfigService {\n\n    @Resource\n    private IotDeviceModbusConfigMapper modbusConfigMapper;\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotProductService productService;\n\n    @Override\n    public void saveDeviceModbusConfig(IotDeviceModbusConfigSaveReqVO saveReqVO) {\n        // 1.1 校验设备存在\n        IotDeviceDO device = deviceService.validateDeviceExists(saveReqVO.getDeviceId());\n        // 1.2 根据产品 protocolType 条件校验\n        IotProductDO product = productService.getProduct(device.getProductId());\n        Assert.notNull(product, \"产品不存在\");\n        validateModbusConfigByProtocolType(saveReqVO, product.getProtocolType());\n\n        // 2. 根据数据库中是否已有配置，决定是新增还是更新\n        IotDeviceModbusConfigDO existConfig = modbusConfigMapper.selectByDeviceId(saveReqVO.getDeviceId());\n        if (existConfig == null) {\n            IotDeviceModbusConfigDO modbusConfig = BeanUtils.toBean(saveReqVO, IotDeviceModbusConfigDO.class);\n            modbusConfigMapper.insert(modbusConfig);\n        } else {\n            IotDeviceModbusConfigDO updateObj = BeanUtils.toBean(saveReqVO, IotDeviceModbusConfigDO.class,\n                    o -> o.setId(existConfig.getId()));\n            modbusConfigMapper.updateById(updateObj);\n        }\n    }\n\n    @Override\n    public IotDeviceModbusConfigDO getDeviceModbusConfig(Long id) {\n        return modbusConfigMapper.selectById(id);\n    }\n\n    @Override\n    public IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId) {\n        return modbusConfigMapper.selectByDeviceId(deviceId);\n    }\n\n    @Override\n    public List<IotDeviceModbusConfigDO> getDeviceModbusConfigList(IotModbusDeviceConfigListReqDTO listReqDTO) {\n        return modbusConfigMapper.selectList(listReqDTO);\n    }\n\n    private void validateModbusConfigByProtocolType(IotDeviceModbusConfigSaveReqVO saveReqVO, String protocolType) {\n        IotProtocolTypeEnum protocolTypeEnum = IotProtocolTypeEnum.of(protocolType);\n        if (protocolTypeEnum == null) {\n            return;\n        }\n        if (protocolTypeEnum == IotProtocolTypeEnum.MODBUS_TCP_CLIENT) {\n            Assert.isTrue(StrUtil.isNotEmpty(saveReqVO.getIp()), \"Client 模式下，IP 地址不能为空\");\n            Assert.notNull(saveReqVO.getPort(), \"Client 模式下，端口不能为空\");\n            Assert.notNull(saveReqVO.getTimeout(), \"Client 模式下，连接超时时间不能为空\");\n            Assert.notNull(saveReqVO.getRetryInterval(), \"Client 模式下，重试间隔不能为空\");\n        } else if (protocolTypeEnum == IotProtocolTypeEnum.MODBUS_TCP_SERVER) {\n            Assert.notNull(saveReqVO.getMode(), \"Server 模式下，工作模式不能为空\");\n            Assert.notNull(saveReqVO.getFrameFormat(), \"Server 模式下，数据帧格式不能为空\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * IoT 设备 Modbus 点位配置 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDeviceModbusPointService {\n\n    /**\n     * 创建设备 Modbus 点位配置\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDeviceModbusPoint(@Valid IotDeviceModbusPointSaveReqVO createReqVO);\n\n    /**\n     * 更新设备 Modbus 点位配置\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDeviceModbusPoint(@Valid IotDeviceModbusPointSaveReqVO updateReqVO);\n\n    /**\n     * 删除设备 Modbus 点位配置\n     *\n     * @param id 编号\n     */\n    void deleteDeviceModbusPoint(Long id);\n\n    /**\n     * 获得设备 Modbus 点位配置\n     *\n     * @param id 编号\n     * @return 设备 Modbus 点位配置\n     */\n    IotDeviceModbusPointDO getDeviceModbusPoint(Long id);\n\n    /**\n     * 获得设备 Modbus 点位配置分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 设备 Modbus 点位配置分页\n     */\n    PageResult<IotDeviceModbusPointDO> getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO);\n\n    /**\n     * 物模型变更时，更新关联点位的冗余字段（identifier、name）\n     *\n     * @param thingModelId 物模型编号\n     * @param identifier   物模型标识符\n     * @param name         物模型名称\n     */\n    void updateDeviceModbusPointByThingModel(Long thingModelId, String identifier, String name);\n\n    /**\n     * 根据设备编号批量获得启用的点位配置 Map\n     *\n     * @param deviceIds 设备编号集合\n     * @return 设备点位 Map，key 为设备编号，value 为点位配置列表\n     */\n    Map<Long, List<IotDeviceModbusPointDO>> getEnabledDeviceModbusPointMapByDeviceIds(Collection<Long> deviceIds);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceModbusPointMapper;\nimport cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;\n\n/**\n * IoT 设备 Modbus 点位配置 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class IotDeviceModbusPointServiceImpl implements IotDeviceModbusPointService {\n\n    @Resource\n    private IotDeviceModbusPointMapper modbusPointMapper;\n\n    @Resource\n    private IotDeviceService deviceService;\n\n    @Resource\n    private IotThingModelService thingModelService;\n\n    @Override\n    public Long createDeviceModbusPoint(IotDeviceModbusPointSaveReqVO createReqVO) {\n        // 1.1 校验设备存在\n        deviceService.validateDeviceExists(createReqVO.getDeviceId());\n        // 1.2 校验物模型属性存在\n        IotThingModelDO thingModel = validateThingModelExists(createReqVO.getThingModelId());\n        // 1.3 校验同一设备下点位唯一性（基于 identifier）\n        validateDeviceModbusPointUnique(createReqVO.getDeviceId(), thingModel.getIdentifier(), null);\n\n        // 2. 插入\n        IotDeviceModbusPointDO modbusPoint = BeanUtils.toBean(createReqVO, IotDeviceModbusPointDO.class,\n                o -> o.setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName()));\n        modbusPointMapper.insert(modbusPoint);\n        return modbusPoint.getId();\n    }\n\n    @Override\n    public void updateDeviceModbusPoint(IotDeviceModbusPointSaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        validateDeviceModbusPointExists(updateReqVO.getId());\n        // 1.2 校验设备存在\n        deviceService.validateDeviceExists(updateReqVO.getDeviceId());\n        // 1.3 校验物模型属性存在\n        IotThingModelDO thingModel = validateThingModelExists(updateReqVO.getThingModelId());\n        // 1.4 校验同一设备下点位唯一性\n        validateDeviceModbusPointUnique(updateReqVO.getDeviceId(), thingModel.getIdentifier(), updateReqVO.getId());\n\n        // 2. 更新\n        IotDeviceModbusPointDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceModbusPointDO.class,\n                o -> o.setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName()));\n        modbusPointMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void updateDeviceModbusPointByThingModel(Long thingModelId, String identifier, String name) {\n        IotDeviceModbusPointDO updateObj = new IotDeviceModbusPointDO()\n                .setIdentifier(identifier).setName(name);\n        modbusPointMapper.updateByThingModelId(thingModelId, updateObj);\n    }\n\n    private IotThingModelDO validateThingModelExists(Long id) {\n        IotThingModelDO thingModel = thingModelService.getThingModel(id);\n        if (thingModel == null) {\n            throw exception(THING_MODEL_NOT_EXISTS);\n        }\n        return thingModel;\n    }\n\n    @Override\n    public void deleteDeviceModbusPoint(Long id) {\n        // 校验存在\n        validateDeviceModbusPointExists(id);\n        // 删除\n        modbusPointMapper.deleteById(id);\n    }\n\n    private void validateDeviceModbusPointExists(Long id) {\n        IotDeviceModbusPointDO point = modbusPointMapper.selectById(id);\n        if (point == null) {\n            throw exception(DEVICE_MODBUS_POINT_NOT_EXISTS);\n        }\n    }\n\n    private void validateDeviceModbusPointUnique(Long deviceId, String identifier, Long excludeId) {\n        IotDeviceModbusPointDO point = modbusPointMapper.selectByDeviceIdAndIdentifier(deviceId, identifier);\n        if (point != null && ObjUtil.notEqual(point.getId(), excludeId)) {\n            throw exception(DEVICE_MODBUS_POINT_EXISTS);\n        }\n    }\n\n    @Override\n    public IotDeviceModbusPointDO getDeviceModbusPoint(Long id) {\n        return modbusPointMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<IotDeviceModbusPointDO> getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO) {\n        return modbusPointMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public Map<Long, List<IotDeviceModbusPointDO>> getEnabledDeviceModbusPointMapByDeviceIds(Collection<Long> deviceIds) {\n        if (CollUtil.isEmpty(deviceIds)) {\n            return Collections.emptyMap();\n        }\n        List<IotDeviceModbusPointDO> pointList = modbusPointMapper.selectListByDeviceIdsAndStatus(\n                deviceIds, CommonStatusEnum.ENABLE.getStatus());\n        return convertMultiMap(pointList, IotDeviceModbusPointDO::getDeviceId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetRespDTO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\n\nimport javax.annotation.Nullable;\nimport javax.validation.Valid;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * IoT 设备 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDeviceService {\n\n    /**\n     * 【管理员】创建设备\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDevice(@Valid IotDeviceSaveReqVO createReqVO);\n\n    /**\n     * 更新设备\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO);\n\n    /**\n     * 更新设备状态\n     *\n     * @param device 设备信息\n     * @param state 状态\n     */\n    void updateDeviceState(IotDeviceDO device, Integer state);\n\n    /**\n     * 更新设备状态\n     *\n     * @param id    编号\n     * @param state 状态\n     */\n    void updateDeviceState(Long id, Integer state);\n\n    /**\n     * 更新设备分组\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO);\n\n    /**\n     * 删除单个设备\n     *\n     * @param id 编号\n     */\n    void deleteDevice(Long id);\n\n    /**\n     * 删除多个设备\n     *\n     * @param ids 编号数组\n     */\n    void deleteDeviceList(Collection<Long> ids);\n\n    /**\n     * 校验设备是否存在\n     *\n     * @param id 设备 ID\n     * @return 设备对象\n     */\n    IotDeviceDO validateDeviceExists(Long id);\n\n    /**\n     * 【缓存】校验设备是否存在\n     *\n     * @param id 设备 ID\n     * @return 设备对象\n     */\n    IotDeviceDO validateDeviceExistsFromCache(Long id);\n\n    /**\n     * 获得设备\n     *\n     * @param id 编号\n     * @return IoT 设备\n     */\n    IotDeviceDO getDevice(Long id);\n\n    /**\n     * 【缓存】获得设备信息\n     * <p>\n     * 注意：该方法会忽略租户信息，所以调用时，需要确认会不会有跨租户访问的风险！！！\n     *\n     * @param id 编号\n     * @return IoT 设备\n     */\n    IotDeviceDO getDeviceFromCache(Long id);\n\n    /**\n     * 【缓存】根据产品 key 和设备名称，获得设备信息\n     * <p>\n     * 注意：该方法会忽略租户信息，所以调用时，需要确认会不会有跨租户访问的风险！！！\n     *\n     * @param productKey 产品 key\n     * @param deviceName 设备名称\n     * @return 设备信息\n     */\n    IotDeviceDO getDeviceFromCache(String productKey, String deviceName);\n\n    /**\n     * 获得设备分页\n     *\n     * @param pageReqVO 分页查询\n     * @return IoT 设备分页\n     */\n    PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO);\n\n    /**\n     * 根据条件，获得设备列表\n     *\n     * @param deviceType 设备类型\n     * @param productId 产品编号\n     * @return 设备列表\n     */\n    List<IotDeviceDO> getDeviceListByCondition(@Nullable Integer deviceType,\n                                               @Nullable Long productId);\n\n    /**\n     * 获得状态，获得设备列表\n     *\n     * @param state 状态\n     * @return 设备列表\n     */\n    List<IotDeviceDO> getDeviceListByState(Integer state);\n\n    /**\n     * 根据产品编号，获取设备列表\n     *\n     * @param productId 产品编号\n     * @return 设备列表\n     */\n    List<IotDeviceDO> getDeviceListByProductId(Long productId);\n\n    /**\n     * 基于产品编号，获得设备数量\n     *\n     * @param productId 产品编号\n     * @return 设备数量\n     */\n    Long getDeviceCountByProductId(Long productId);\n\n    /**\n     * 获得设备数量\n     *\n     * @param groupId 分组编号\n     * @return 设备数量\n     */\n    Long getDeviceCountByGroupId(Long groupId);\n\n    /**\n     * 导入设备\n     *\n     * @param importDevices 导入设备列表\n     * @param updateSupport 是否支持更新\n     * @return 导入结果\n     */\n    IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport);\n\n    /**\n     * 获得设备数量\n     *\n     * @param createTime 创建时间，如果为空，则统计所有设备数量\n     * @return 设备数量\n     */\n    Long getDeviceCount(@Nullable LocalDateTime createTime);\n\n    /**\n     * 获得设备认证信息\n     *\n     * @param id 设备编号\n     * @return MQTT 连接参数\n     */\n    IotDeviceAuthInfoRespVO getDeviceAuthInfo(Long id);\n\n    /**\n     * 获得各个产品下的设备数量 Map\n     *\n     * @return key: 产品编号, value: 设备数量\n     */\n    Map<Long, Integer> getDeviceCountMapByProductId();\n\n    /**\n     * 获得各个状态下的设备数量 Map\n     *\n     * @return key: 设备状态枚举 {@link IotDeviceStateEnum}\n     *         value: 设备数量\n     */\n    Map<Integer, Long> getDeviceCountMapByState();\n\n    /**\n     * 通过产品标识和设备名称列表获取设备列表\n     *\n     * @param productKey  产品标识\n     * @param deviceNames 设备名称列表\n     * @return 设备列表\n     */\n    List<IotDeviceDO> getDeviceListByProductKeyAndNames(String productKey, List<String> deviceNames);\n\n    /**\n     * 认证设备\n     *\n     * @param authReqDTO 认证信息\n     * @return 是否认证成功\n     */\n    boolean authDevice(@Valid IotDeviceAuthReqDTO authReqDTO);\n\n    /**\n     * 校验设备是否存在\n     *\n     * @param ids 设备编号数组\n     */\n    List<IotDeviceDO> validateDeviceListExists(Collection<Long> ids);\n\n    /**\n     * 获得设备列表\n     *\n     * @param ids 设备编号数组\n     * @return 设备列表\n     */\n    List<IotDeviceDO> getDeviceList(Collection<Long> ids);\n\n    /**\n     * 获得设备 Map\n     *\n     * @param ids 设备编号数组\n     * @return 设备 Map\n     */\n    default Map<Long, IotDeviceDO> getDeviceMap(Collection<Long> ids) {\n        return convertMap(getDeviceList(ids), IotDeviceDO::getId);\n    }\n\n    /**\n     * 更新设备固件版本\n     *\n     * @param deviceId 设备编号\n     * @param firmwareId 固件编号\n     */\n    void updateDeviceFirmware(Long deviceId, Long firmwareId);\n\n    /**\n     * 更新设备定位信息（GeoLocation 上报时调用）\n     *\n     * @param device    设备信息（用于清除缓存）\n     * @param longitude 经度\n     * @param latitude  纬度\n     */\n    void updateDeviceLocation(IotDeviceDO device, BigDecimal longitude, BigDecimal latitude);\n\n    /**\n     * 获得有位置信息的设备列表\n     *\n     * @return 设备列表\n     */\n    List<IotDeviceDO> getDeviceListByHasLocation();\n\n    // ========== 网关-拓扑管理（后台操作） ==========\n\n    /**\n     * 绑定子设备到网关\n     *\n     * @param subIds    子设备编号列表\n     * @param gatewayId 网关设备编号\n     */\n    void bindDeviceGateway(Collection<Long> subIds, Long gatewayId);\n\n    /**\n     * 解绑子设备与网关\n     *\n     * @param subIds    子设备编号列表\n     * @param gatewayId 网关设备编号\n     */\n    void unbindDeviceGateway(Collection<Long> subIds, Long gatewayId);\n\n    /**\n     * 获取未绑定网关的子设备分页\n     *\n     * @param pageReqVO 分页查询参数（仅使用 productId、deviceName、nickname）\n     * @return 子设备分页\n     */\n    PageResult<IotDeviceDO> getUnboundSubDevicePage(IotDevicePageReqVO pageReqVO);\n\n    /**\n     * 根据网关编号获取子设备列表\n     *\n     * @param gatewayId 网关设备编号\n     * @return 子设备列表\n     */\n    List<IotDeviceDO> getDeviceListByGatewayId(Long gatewayId);\n\n    // ========== 网关-拓扑管理（设备上报） ==========\n\n    /**\n     * 处理添加拓扑关系消息（网关设备上报）\n     *\n     * @param message       消息\n     * @param gatewayDevice 网关设备\n     * @return 成功添加的子设备列表\n     */\n    List<IotDeviceIdentity> handleTopoAddMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice);\n\n    /**\n     * 处理删除拓扑关系消息（网关设备上报）\n     *\n     * @param message       消息\n     * @param gatewayDevice 网关设备\n     * @return 成功删除的子设备列表\n     */\n    List<IotDeviceIdentity> handleTopoDeleteMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice);\n\n    /**\n     * 处理获取拓扑关系消息（网关设备上报）\n     *\n     * @param gatewayDevice 网关设备\n     * @return 拓扑关系响应\n     */\n    IotDeviceTopoGetRespDTO handleTopoGetMessage(IotDeviceDO gatewayDevice);\n\n    // ========== 设备动态注册 ==========\n\n    /**\n     * 直连/网关设备动态注册\n     *\n     * @param reqDTO 动态注册请求\n     * @return 注册结果（包含 DeviceSecret）\n     */\n    IotDeviceRegisterRespDTO registerDevice(@Valid IotDeviceRegisterReqDTO reqDTO);\n\n    /**\n     * 网关子设备动态注册\n     * <p>\n     * 与 {@link #handleSubDeviceRegisterMessage} 方法的区别：\n     * 该方法网关设备信息通过 reqDTO 参数传入，而 {@link #handleSubDeviceRegisterMessage} 方法通过 gatewayDevice 参数传入\n     *\n     * @param reqDTO 子设备注册请求（包含网关设备信息）\n     * @return 注册结果列表\n     */\n    List<IotSubDeviceRegisterRespDTO> registerSubDevices(@Valid IotSubDeviceRegisterFullReqDTO reqDTO);\n\n    /**\n     * 处理子设备动态注册消息（网关设备上报）\n     *\n     * @param message       消息\n     * @param gatewayDevice 网关设备\n     * @return 注册结果列表\n     */\n    List<IotSubDeviceRegisterRespDTO> handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;\nimport cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoChangeReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetRespDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;\nimport cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper;\nimport cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;\nimport cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.cache.annotation.Caching;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Nullable;\nimport javax.annotation.Resource;\nimport javax.validation.ConstraintViolationException;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;\nimport static java.util.Collections.singletonList;\n\n/**\n * IoT 设备 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class IotDeviceServiceImpl implements IotDeviceService {\n\n    @Resource\n    private IotDeviceMapper deviceMapper;\n\n    @Resource\n    @Lazy  // 延迟加载，解决循环依赖\n    private IotProductService productService;\n    @Resource\n    @Lazy // 延迟加载，解决循环依赖\n    private IotDeviceGroupService deviceGroupService;\n    @Resource\n    @Lazy // 延迟加载，解决循环依赖\n    private IotDeviceMessageService deviceMessageService;\n\n    private IotDeviceServiceImpl getSelf() {\n        return SpringUtil.getBean(getClass());\n    }\n\n    @Override\n    public Long createDevice(IotDeviceSaveReqVO createReqVO) {\n        return createDevice0(createReqVO).getId();\n    }\n\n    private IotDeviceDO createDevice0(IotDeviceSaveReqVO createReqVO) {\n        // 1.1 校验产品是否存在\n        IotProductDO product = productService.getProduct(createReqVO.getProductId());\n        if (product == null) {\n            throw exception(PRODUCT_NOT_EXISTS);\n        }\n        // 1.2 统一校验\n        validateCreateDeviceParam(product.getProductKey(), createReqVO.getDeviceName(),\n                createReqVO.getGatewayId(), product);\n        // 1.3 校验分组存在\n        deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds());\n        // 1.4 校验设备序列号全局唯一\n        validateSerialNumberUnique(createReqVO.getSerialNumber(), null);\n\n        // 2. 插入到数据库\n        IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class);\n        initDevice(device, product);\n        deviceMapper.insert(device);\n        return device;\n    }\n\n    private void validateCreateDeviceParam(String productKey, String deviceName,\n                                           Long gatewayId, IotProductDO product) {\n        // 校验设备名称在同一产品下是否唯一\n        TenantUtils.executeIgnore(() -> {\n            if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) {\n                throw exception(DEVICE_NAME_EXISTS);\n            }\n        });\n        // 校验父设备是否为合法网关\n        if (IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType())\n                && gatewayId != null) {\n            validateGatewayDeviceExists(gatewayId);\n        }\n    }\n\n    /**\n     * 校验设备序列号全局唯一性\n     *\n     * @param serialNumber 设备序列号\n     * @param excludeId 排除的设备编号（用于更新时排除自身）\n     */\n    private void validateSerialNumberUnique(String serialNumber, Long excludeId) {\n        if (StrUtil.isBlank(serialNumber)) {\n            return;\n        }\n        IotDeviceDO existDevice = deviceMapper.selectBySerialNumber(serialNumber);\n        if (existDevice != null && ObjUtil.notEqual(existDevice.getId(), excludeId)) {\n            throw exception(DEVICE_SERIAL_NUMBER_EXISTS);\n        }\n    }\n\n    private void initDevice(IotDeviceDO device, IotProductDO product) {\n        device.setProductId(product.getId()).setProductKey(product.getProductKey())\n                .setDeviceType(product.getDeviceType())\n                .setDeviceSecret(generateDeviceSecret()) // 生成密钥\n                .setState(IotDeviceStateEnum.INACTIVE.getState()); // 默认未激活\n    }\n\n    private String generateDeviceSecret() {\n        return IdUtil.fastSimpleUUID();\n    }\n\n    @Override\n    public void updateDevice(IotDeviceSaveReqVO updateReqVO) {\n        updateReqVO.setDeviceName(null).setProductId(null); // 不允许更新\n        // 1.1 校验存在\n        IotDeviceDO device = validateDeviceExists(updateReqVO.getId());\n        // 1.2 校验父设备是否为合法网关\n        if (IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType())\n                && updateReqVO.getGatewayId() != null) {\n            validateGatewayDeviceExists(updateReqVO.getGatewayId());\n        }\n        // 1.3 校验分组存在\n        deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds());\n        // 1.4 校验设备序列号全局唯一\n        validateSerialNumberUnique(updateReqVO.getSerialNumber(), updateReqVO.getId());\n\n        // 2. 更新到数据库\n        IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);\n        deviceMapper.updateById(updateObj);\n\n        // 3. 清空对应缓存\n        deleteDeviceCache(device);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateDeviceGroup(IotDeviceUpdateGroupReqVO updateReqVO) {\n        // 1.1 校验设备存在\n        List<IotDeviceDO> devices = deviceMapper.selectByIds(updateReqVO.getIds());\n        if (CollUtil.isEmpty(devices)) {\n            return;\n        }\n        // 1.2 校验分组存在\n        deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds());\n\n        // 3. 更新设备分组\n        deviceMapper.updateBatch(convertList(devices, device -> new IotDeviceDO()\n                .setId(device.getId()).setGroupIds(updateReqVO.getGroupIds())));\n\n        // 4. 清空对应缓存\n        deleteDeviceCache(devices);\n    }\n\n    @Override\n    public void deleteDevice(Long id) {\n        // 1.1 校验存在\n        IotDeviceDO device = validateDeviceExists(id);\n        // 1.2 如果是网关设备，检查是否有子设备绑定\n        if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType())\n                && deviceMapper.selectCountByGatewayId(id) > 0) {\n            throw exception(DEVICE_GATEWAY_HAS_SUB);\n        }\n\n        // 2. 删除设备\n        deviceMapper.deleteById(id);\n\n        // 3. 清空对应缓存\n        deleteDeviceCache(device);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteDeviceList(Collection<Long> ids) {\n        // 1.1 校验存在\n        if (CollUtil.isEmpty(ids)) {\n            return;\n        }\n        List<IotDeviceDO> devices = deviceMapper.selectByIds(ids);\n        if (CollUtil.isEmpty(devices)) {\n            return;\n        }\n        // 1.2 如果是网关设备，检查是否有子设备绑定\n        for (IotDeviceDO device : devices) {\n            if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType())\n                    && deviceMapper.selectCountByGatewayId(device.getId()) > 0) {\n                throw exception(DEVICE_GATEWAY_HAS_SUB);\n            }\n        }\n\n        // 2. 删除设备\n        deviceMapper.deleteByIds(ids);\n\n        // 3. 清空对应缓存\n        deleteDeviceCache(devices);\n    }\n\n    @Override\n    public IotDeviceDO validateDeviceExists(Long id) {\n        IotDeviceDO device = deviceMapper.selectById(id);\n        if (device == null) {\n            throw exception(DEVICE_NOT_EXISTS);\n        }\n        return device;\n    }\n\n    @Override\n    public IotDeviceDO validateDeviceExistsFromCache(Long id) {\n        IotDeviceDO device = getSelf().getDeviceFromCache(id);\n        if (device == null) {\n            throw exception(DEVICE_NOT_EXISTS);\n        }\n        return device;\n    }\n\n    /**\n     * 校验网关设备是否存在\n     *\n     * @param id 设备 ID\n     */\n    private void validateGatewayDeviceExists(Long id) {\n        IotDeviceDO device = deviceMapper.selectById(id);\n        if (device == null) {\n            throw exception(DEVICE_GATEWAY_NOT_EXISTS);\n        }\n        if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {\n            throw exception(DEVICE_NOT_GATEWAY);\n        }\n    }\n\n    @Override\n    public IotDeviceDO getDevice(Long id) {\n        return deviceMapper.selectById(id);\n    }\n\n    @Override\n    @Cacheable(value = RedisKeyConstants.DEVICE, key = \"#id\", unless = \"#result == null\")\n    @TenantIgnore // 忽略租户信息\n    public IotDeviceDO getDeviceFromCache(Long id) {\n        return deviceMapper.selectById(id);\n    }\n\n    @Override\n    @Cacheable(value = RedisKeyConstants.DEVICE, key = \"#productKey + '_' + #deviceName\", unless = \"#result == null\")\n    @TenantIgnore // 忽略租户信息，跨租户 productKey + deviceName 是唯一的\n    public IotDeviceDO getDeviceFromCache(String productKey, String deviceName) {\n        return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);\n    }\n\n    @Override\n    public PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO) {\n        return deviceMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<IotDeviceDO> getDeviceListByCondition(@Nullable Integer deviceType, @Nullable Long productId) {\n        return deviceMapper.selectListByCondition(deviceType, productId);\n    }\n\n    @Override\n    public List<IotDeviceDO> getDeviceListByState(Integer state) {\n        return deviceMapper.selectListByState(state);\n    }\n\n    @Override\n    public List<IotDeviceDO> getDeviceListByProductId(Long productId) {\n        return deviceMapper.selectListByProductId(productId);\n    }\n\n    @Override\n    public void updateDeviceState(IotDeviceDO device, Integer state) {\n        // 1. 更新状态和时间\n        IotDeviceDO updateObj = new IotDeviceDO().setId(device.getId()).setState(state);\n        if (device.getOnlineTime() == null\n                && Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) {\n            updateObj.setActiveTime(LocalDateTime.now());\n        }\n        if (Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) {\n            updateObj.setOnlineTime(LocalDateTime.now());\n        } else if (Objects.equals(state, IotDeviceStateEnum.OFFLINE.getState())) {\n            updateObj.setOfflineTime(LocalDateTime.now());\n        }\n        deviceMapper.updateById(updateObj);\n\n        // 2. 清空对应缓存\n        deleteDeviceCache(device);\n\n        // 3. 网关设备下线时，联动所有子设备下线\n        if (Objects.equals(state, IotDeviceStateEnum.OFFLINE.getState())\n                && IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {\n            handleGatewayOffline(device);\n        }\n    }\n\n    /**\n     * 处理网关下线，联动所有子设备下线\n     *\n     * @param gatewayDevice 网关设备\n     */\n    private void handleGatewayOffline(IotDeviceDO gatewayDevice) {\n        List<IotDeviceDO> subDevices = deviceMapper.selectListByGatewayId(gatewayDevice.getId());\n        if (CollUtil.isEmpty(subDevices)) {\n            return;\n        }\n        for (IotDeviceDO subDevice : subDevices) {\n            if (Objects.equals(subDevice.getState(), IotDeviceStateEnum.ONLINE.getState())) {\n                try {\n                    updateDeviceState(subDevice, IotDeviceStateEnum.OFFLINE.getState());\n                    log.info(\"[handleGatewayOffline][网关({}/{}) 下线，子设备({}/{}) 联动下线]\",\n                            gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(),\n                            subDevice.getProductKey(), subDevice.getDeviceName());\n                } catch (Exception ex) {\n                    log.error(\"[handleGatewayOffline][子设备({}/{}) 下线失败]\",\n                            subDevice.getProductKey(), subDevice.getDeviceName(), ex);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void updateDeviceState(Long id, Integer state) {\n        // 校验存在\n        IotDeviceDO device = validateDeviceExists(id);\n        // 执行更新\n        updateDeviceState(device, state);\n    }\n\n    @Override\n    public Long getDeviceCountByProductId(Long productId) {\n        return deviceMapper.selectCountByProductId(productId);\n    }\n\n    @Override\n    public Long getDeviceCountByGroupId(Long groupId) {\n        return deviceMapper.selectCountByGroupId(groupId);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class) // 添加事务，异常则回滚所有导入\n    public IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport) {\n        // 1. 参数校验\n        if (CollUtil.isEmpty(importDevices)) {\n            throw exception(DEVICE_IMPORT_LIST_IS_EMPTY);\n        }\n\n        // 2. 遍历，逐个创建 or 更新\n        IotDeviceImportRespVO respVO = IotDeviceImportRespVO.builder().createDeviceNames(new ArrayList<>())\n                .updateDeviceNames(new ArrayList<>()).failureDeviceNames(new LinkedHashMap<>()).build();\n        importDevices.forEach(importDevice -> {\n            try {\n                // 2.1.1 校验字段是否符合要求\n                try {\n                    ValidationUtils.validate(importDevice);\n                } catch (ConstraintViolationException ex) {\n                    respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());\n                    return;\n                }\n                // 2.1.2 校验产品是否存在\n                IotProductDO product = productService.validateProductExists(importDevice.getProductKey());\n                // 2.1.3 校验父设备是否存在\n                Long gatewayId = null;\n                if (StrUtil.isNotEmpty(importDevice.getParentDeviceName())) {\n                    IotDeviceDO gatewayDevice = deviceMapper.selectByDeviceName(importDevice.getParentDeviceName());\n                    if (gatewayDevice == null) {\n                        throw exception(DEVICE_GATEWAY_NOT_EXISTS);\n                    }\n                    if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {\n                        throw exception(DEVICE_NOT_GATEWAY);\n                    }\n                    gatewayId = gatewayDevice.getId();\n                }\n                // 2.1.4 校验设备分组是否存在\n                Set<Long> groupIds = new HashSet<>();\n                if (StrUtil.isNotEmpty(importDevice.getGroupNames())) {\n                    String[] groupNames = importDevice.getGroupNames().split(\",\");\n                    for (String groupName : groupNames) {\n                        IotDeviceGroupDO group = deviceGroupService.getDeviceGroupByName(groupName);\n                        if (group == null) {\n                            throw exception(DEVICE_GROUP_NOT_EXISTS);\n                        }\n                        groupIds.add(group.getId());\n                    }\n                }\n\n                // 2.2.1 判断如果不存在，在进行插入\n                IotDeviceDO existDevice = deviceMapper.selectByDeviceName(importDevice.getDeviceName());\n                if (existDevice == null) {\n                    createDevice(new IotDeviceSaveReqVO()\n                            .setDeviceName(importDevice.getDeviceName())\n                            .setProductId(product.getId()).setGatewayId(gatewayId).setGroupIds(groupIds));\n                    respVO.getCreateDeviceNames().add(importDevice.getDeviceName());\n                    return;\n                }\n                // 2.2.2 如果存在，判断是否允许更新\n                if (!updateSupport) {\n                    throw exception(DEVICE_KEY_EXISTS);\n                }\n                updateDevice(new IotDeviceSaveReqVO().setId(existDevice.getId())\n                        .setGatewayId(gatewayId).setGroupIds(groupIds));\n                respVO.getUpdateDeviceNames().add(importDevice.getDeviceName());\n            } catch (ServiceException ex) {\n                respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());\n            }\n        });\n        return respVO;\n    }\n\n    @Override\n    public IotDeviceAuthInfoRespVO getDeviceAuthInfo(Long id) {\n        IotDeviceDO device = validateDeviceExists(id);\n        // 使用 IotDeviceAuthUtils 生成认证信息\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(\n                device.getProductKey(), device.getDeviceName(), device.getDeviceSecret());\n        return BeanUtils.toBean(authInfo, IotDeviceAuthInfoRespVO.class);\n    }\n\n    private void deleteDeviceCache(IotDeviceDO device) {\n        // 保证 Spring AOP 触发\n        getSelf().deleteDeviceCache0(device);\n    }\n\n    private void deleteDeviceCache(List<IotDeviceDO> devices) {\n        devices.forEach(this::deleteDeviceCache);\n    }\n\n    @SuppressWarnings(\"unused\")\n    @Caching(evict = {\n        @CacheEvict(value = RedisKeyConstants.DEVICE, key = \"#device.id\"),\n        @CacheEvict(value = RedisKeyConstants.DEVICE, key = \"#device.productKey + '_' + #device.deviceName\")\n    })\n    public void deleteDeviceCache0(IotDeviceDO device) {\n    }\n\n    @Override\n    public Long getDeviceCount(LocalDateTime createTime) {\n        return deviceMapper.selectCountByCreateTime(createTime);\n    }\n\n    @Override\n    public Map<Long, Integer> getDeviceCountMapByProductId() {\n        return deviceMapper.selectDeviceCountMapByProductId();\n    }\n\n    @Override\n    public Map<Integer, Long> getDeviceCountMapByState() {\n        return deviceMapper.selectDeviceCountGroupByState();\n    }\n\n    @Override\n    public List<IotDeviceDO> getDeviceListByProductKeyAndNames(String productKey, List<String> deviceNames) {\n        if (StrUtil.isBlank(productKey) || CollUtil.isEmpty(deviceNames)) {\n            return Collections.emptyList();\n        }\n        return deviceMapper.selectByProductKeyAndDeviceNames(productKey, deviceNames);\n    }\n\n    @Override\n    public boolean authDevice(IotDeviceAuthReqDTO authReqDTO) {\n        // 1. 校验设备是否存在\n        IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authReqDTO.getUsername());\n        if (deviceInfo == null) {\n            log.error(\"[authDevice][认证失败，username({}) 格式不正确]\", authReqDTO.getUsername());\n            return false;\n        }\n        String deviceName = deviceInfo.getDeviceName();\n        String productKey = deviceInfo.getProductKey();\n        IotDeviceDO device = getSelf().getDeviceFromCache(productKey, deviceName);\n        if (device == null) {\n            log.warn(\"[authDevice][设备({}/{}) 不存在]\", productKey, deviceName);\n            return false;\n        }\n\n        // 2. 校验密码\n        IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(productKey, deviceName, device.getDeviceSecret());\n        if (ObjUtil.notEqual(authInfo.getPassword(), authReqDTO.getPassword())) {\n            log.error(\"[authDevice][设备({}/{}) 密码不正确]\", productKey, deviceName);\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public List<IotDeviceDO> validateDeviceListExists(Collection<Long> ids) {\n        List<IotDeviceDO> devices = getDeviceList(ids);\n        if (devices.size() != ids.size()) {\n            throw exception(DEVICE_NOT_EXISTS);\n        }\n        return devices;\n    }\n\n    @Override\n    public List<IotDeviceDO> getDeviceList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        return deviceMapper.selectByIds(ids);\n    }\n\n    @Override\n    public void updateDeviceFirmware(Long deviceId, Long firmwareId) {\n        // 1. 校验设备是否存在\n        IotDeviceDO device = validateDeviceExists(deviceId);\n\n        // 2. 更新设备固件版本\n        IotDeviceDO updateObj = new IotDeviceDO().setId(deviceId).setFirmwareId(firmwareId);\n        deviceMapper.updateById(updateObj);\n\n        // 3. 清空对应缓存\n        deleteDeviceCache(device);\n    }\n\n    @Override\n    public void updateDeviceLocation(IotDeviceDO device, BigDecimal longitude, BigDecimal latitude) {\n        // 1. 更新定位信息\n        deviceMapper.updateById(new IotDeviceDO().setId(device.getId())\n                .setLongitude(longitude).setLatitude(latitude));\n\n        // 2. 清空对应缓存\n        deleteDeviceCache(device);\n    }\n\n    @Override\n    public List<IotDeviceDO> getDeviceListByHasLocation() {\n        return deviceMapper.selectListByHasLocation();\n    }\n\n    // ========== 网关-拓扑管理（后台操作） ==========\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void bindDeviceGateway(Collection<Long> subIds, Long gatewayId) {\n        if (CollUtil.isEmpty(subIds)) {\n            return;\n        }\n        // 1.1 校验网关设备存在且类型正确\n        validateGatewayDeviceExists(gatewayId);\n        // 1.2 校验每个设备是否可绑定\n        List<IotDeviceDO> devices = deviceMapper.selectByIds(subIds);\n        for (IotDeviceDO device : devices) {\n            checkSubDeviceCanBind(device, gatewayId);\n        }\n\n        // 2. 批量更新数据库\n        List<IotDeviceDO> updateList = convertList(devices, device ->\n                new IotDeviceDO().setId(device.getId()).setGatewayId(gatewayId));\n        deviceMapper.updateBatch(updateList);\n\n        // 3. 清空对应缓存\n        deleteDeviceCache(devices);\n\n        // 4. 下发网关设备拓扑变更通知（增加）\n        sendTopoChangeNotify(gatewayId, IotDeviceTopoChangeReqDTO.STATUS_CREATE, devices);\n    }\n\n    private void checkSubDeviceCanBind(IotDeviceDO device, Long gatewayId) {\n        if (!IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType())) {\n            throw exception(DEVICE_NOT_GATEWAY_SUB, device.getProductKey(), device.getDeviceName());\n        }\n        // 已绑定到其他网关，拒绝绑定（需先解绑）\n        if (device.getGatewayId() != null && ObjUtil.notEqual(device.getGatewayId(), gatewayId)) {\n            throw exception(DEVICE_GATEWAY_BINDTO_EXISTS, device.getProductKey(), device.getDeviceName());\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void unbindDeviceGateway(Collection<Long> subIds, Long gatewayId) {\n        // 1. 校验设备存在\n        if (CollUtil.isEmpty(subIds)) {\n            return;\n        }\n        List<IotDeviceDO> devices = deviceMapper.selectByIds(subIds);\n        devices.removeIf(device -> ObjUtil.notEqual(device.getGatewayId(), gatewayId));\n        if (CollUtil.isEmpty(devices)) {\n            return;\n        }\n\n        // 2. 批量更新数据库（将 gatewayId 设置为 null）\n        deviceMapper.updateGatewayIdBatch(convertList(devices, IotDeviceDO::getId), null);\n\n        // 3. 清空对应缓存\n        deleteDeviceCache(devices);\n\n        // 4. 下发网关设备拓扑变更通知（删除）\n        sendTopoChangeNotify(gatewayId, IotDeviceTopoChangeReqDTO.STATUS_DELETE, devices);\n    }\n\n    @Override\n    public PageResult<IotDeviceDO> getUnboundSubDevicePage(IotDevicePageReqVO pageReqVO) {\n        return deviceMapper.selectUnboundSubDevicePage(pageReqVO);\n    }\n\n    @Override\n    public List<IotDeviceDO> getDeviceListByGatewayId(Long gatewayId) {\n        return deviceMapper.selectListByGatewayId(gatewayId);\n    }\n\n    // ========== 网关-拓扑管理（设备上报） ==========\n\n    @Override\n    public List<IotDeviceIdentity> handleTopoAddMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice) {\n        // 1.1 校验网关设备类型\n        if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {\n            throw exception(DEVICE_NOT_GATEWAY);\n        }\n        // 1.2 解析参数\n        IotDeviceTopoAddReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceTopoAddReqDTO.class);\n        if (params == null || CollUtil.isEmpty(params.getSubDevices())) {\n            throw exception(DEVICE_TOPO_PARAMS_INVALID);\n        }\n\n        // 2. 遍历处理每个子设备\n        List<IotDeviceIdentity> addedSubDevices = new ArrayList<>();\n        for (IotDeviceAuthReqDTO subDeviceAuth : params.getSubDevices()) {\n            try {\n                IotDeviceDO subDevice = addDeviceTopo(gatewayDevice, subDeviceAuth);\n                addedSubDevices.add(new IotDeviceIdentity(subDevice.getProductKey(), subDevice.getDeviceName()));\n            } catch (Exception ex) {\n                log.warn(\"[handleTopoAddMessage][网关({}/{}) 添加子设备失败，message={}]\",\n                        gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), message, ex);\n            }\n        }\n\n        // 3. 返回响应数据（包含成功添加的子设备列表）\n        return addedSubDevices;\n    }\n\n    private IotDeviceDO addDeviceTopo(IotDeviceDO gatewayDevice, IotDeviceAuthReqDTO subDeviceAuth) {\n        // 1.1 解析子设备信息\n        IotDeviceIdentity subDeviceInfo = IotDeviceAuthUtils.parseUsername(subDeviceAuth.getUsername());\n        if (subDeviceInfo == null) {\n            throw exception(DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID);\n        }\n        // 1.2 校验子设备认证信息\n        if (!authDevice(subDeviceAuth)) {\n            throw exception(DEVICE_TOPO_SUB_DEVICE_AUTH_FAILED);\n        }\n        // 1.3 获取子设备\n        IotDeviceDO subDevice = getSelf().getDeviceFromCache(subDeviceInfo.getProductKey(), subDeviceInfo.getDeviceName());\n        if (subDevice == null) {\n            throw exception(DEVICE_NOT_EXISTS);\n        }\n        // 1.4 校验子设备类型\n        checkSubDeviceCanBind(subDevice, gatewayDevice.getId());\n\n        // 2. 更新数据库\n        deviceMapper.updateById(new IotDeviceDO().setId(subDevice.getId()).setGatewayId(gatewayDevice.getId()));\n        log.info(\"[addDeviceTopo][网关({}/{}) 绑定子设备({}/{})]\",\n                gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(),\n                subDevice.getProductKey(), subDevice.getDeviceName());\n\n        // 3. 清空对应缓存\n        deleteDeviceCache(subDevice);\n        return subDevice;\n    }\n\n    @Override\n    public List<IotDeviceIdentity> handleTopoDeleteMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice) {\n        // 1.1 校验网关设备类型\n        if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {\n            throw exception(DEVICE_NOT_GATEWAY);\n        }\n        // 1.2 解析参数\n        IotDeviceTopoDeleteReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceTopoDeleteReqDTO.class);\n        if (params == null || CollUtil.isEmpty(params.getSubDevices())) {\n            throw exception(DEVICE_TOPO_PARAMS_INVALID);\n        }\n\n        // 2. 遍历处理每个子设备\n        List<IotDeviceIdentity> deletedSubDevices = new ArrayList<>();\n        for (IotDeviceIdentity subDeviceIdentity : params.getSubDevices()) {\n            try {\n                deleteDeviceTopo(gatewayDevice, subDeviceIdentity);\n                deletedSubDevices.add(subDeviceIdentity);\n            } catch (Exception ex) {\n                log.warn(\"[handleTopoDeleteMessage][网关({}/{}) 删除子设备失败，productKey={}, deviceName={}]\",\n                        gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(),\n                        subDeviceIdentity.getProductKey(), subDeviceIdentity.getDeviceName(), ex);\n            }\n        }\n\n        // 3. 返回响应数据（包含成功删除的子设备列表）\n        return deletedSubDevices;\n    }\n\n    private void deleteDeviceTopo(IotDeviceDO gatewayDevice, IotDeviceIdentity subDeviceIdentity) {\n        // 1.1 获取子设备\n        IotDeviceDO subDevice = getSelf().getDeviceFromCache(subDeviceIdentity.getProductKey(), subDeviceIdentity.getDeviceName());\n        if (subDevice == null) {\n            throw exception(DEVICE_NOT_EXISTS);\n        }\n        // 1.2 校验子设备是否绑定到该网关\n        if (ObjUtil.notEqual(subDevice.getGatewayId(), gatewayDevice.getId())) {\n            throw exception(DEVICE_TOPO_SUB_NOT_BINDTO_GATEWAY,\n                    subDeviceIdentity.getProductKey(), subDeviceIdentity.getDeviceName());\n        }\n\n        // 2. 更新数据库（将 gatewayId 设置为 null）\n        deviceMapper.updateGatewayIdBatch(singletonList(subDevice.getId()), null);\n        log.info(\"[deleteDeviceTopo][网关({}/{}) 解绑子设备({}/{})]\",\n                gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(),\n                subDevice.getProductKey(), subDevice.getDeviceName());\n\n        // 3. 清空对应缓存\n        deleteDeviceCache(subDevice);\n\n        // 4. 子设备下线\n        if (Objects.equals(subDevice.getState(), IotDeviceStateEnum.ONLINE.getState())) {\n            updateDeviceState(subDevice, IotDeviceStateEnum.OFFLINE.getState());\n        }\n    }\n\n    @Override\n    public IotDeviceTopoGetRespDTO handleTopoGetMessage(IotDeviceDO gatewayDevice) {\n        // 1. 校验网关设备类型\n        if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {\n            throw exception(DEVICE_NOT_GATEWAY);\n        }\n\n        // 2. 获取子设备列表并转换\n        List<IotDeviceDO> subDevices = deviceMapper.selectListByGatewayId(gatewayDevice.getId());\n        List<IotDeviceIdentity> subDeviceIdentities = convertList(subDevices, subDevice ->\n                new IotDeviceIdentity(subDevice.getProductKey(), subDevice.getDeviceName()));\n        return new IotDeviceTopoGetRespDTO().setSubDevices(subDeviceIdentities);\n    }\n\n    /**\n     * 发送拓扑变更通知给网关设备\n     *\n     * @param gatewayId  网关设备编号\n     * @param status     变更状态（0-创建, 1-删除）\n     * @param subDevices 子设备列表\n     * @see <a href=\"https://help.aliyun.com/zh/marketplace/notify-gateway-topology-changes\">阿里云 - 通知网关拓扑关系变化</a>\n     */\n    private void sendTopoChangeNotify(Long gatewayId, Integer status, List<IotDeviceDO> subDevices) {\n        if (CollUtil.isEmpty(subDevices)) {\n            return;\n        }\n        // 1. 获取网关设备\n        IotDeviceDO gatewayDevice = deviceMapper.selectById(gatewayId);\n        if (gatewayDevice == null) {\n            log.warn(\"[sendTopoChangeNotify][网关设备({}) 不存在，无法发送拓扑变更通知]\", gatewayId);\n            return;\n        }\n\n        try {\n            // 2.1 构建拓扑变更通知消息\n            List<IotDeviceIdentity> subList = convertList(subDevices, subDevice ->\n                    new IotDeviceIdentity(subDevice.getProductKey(), subDevice.getDeviceName()));\n            IotDeviceTopoChangeReqDTO params = new IotDeviceTopoChangeReqDTO(status, subList);\n            IotDeviceMessage notifyMessage = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.TOPO_CHANGE.getMethod(), params);\n\n            // 2.2 发送消息\n            deviceMessageService.sendDeviceMessage(notifyMessage, gatewayDevice);\n            log.info(\"[sendTopoChangeNotify][网关({}/{}) 发送拓扑变更通知成功，status={}, subDevices={}]\",\n                    gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(),\n                    status, subList);\n        } catch (Exception ex) {\n            log.error(\"[sendTopoChangeNotify][网关({}/{}) 发送拓扑变更通知失败，status={}]\",\n                    gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), status, ex);\n        }\n    }\n\n    // ========== 设备动态注册 ==========\n\n    @Override\n    public IotDeviceRegisterRespDTO registerDevice(IotDeviceRegisterReqDTO reqDTO) {\n        // 1.1 校验产品\n        IotProductDO product = TenantUtils.executeIgnore(() ->\n                productService.getProductByProductKey(reqDTO.getProductKey()));\n        if (product == null) {\n            throw exception(PRODUCT_NOT_EXISTS);\n        }\n        // 1.2 校验产品是否开启动态注册\n        if (BooleanUtil.isFalse(product.getRegisterEnabled())) {\n            throw exception(DEVICE_REGISTER_DISABLED);\n        }\n        // 1.3 【重要！！！】验证签名\n        if (!IotProductAuthUtils.verifySign(reqDTO.getProductKey(), reqDTO.getDeviceName(),\n                product.getProductSecret(), reqDTO.getSign())) {\n            throw exception(DEVICE_REGISTER_SECRET_INVALID);\n        }\n        return TenantUtils.execute(product.getTenantId(), () -> {\n            // 1.4 校验设备是否已存在（已存在则不允许重复注册）\n            IotDeviceDO device = getSelf().getDeviceFromCache(reqDTO.getProductKey(), reqDTO.getDeviceName());\n            if (device != null) {\n                throw exception(DEVICE_REGISTER_ALREADY_EXISTS);\n            }\n\n            // 2.1 自动创建设备\n            IotDeviceSaveReqVO createReqVO = new IotDeviceSaveReqVO()\n                    .setDeviceName(reqDTO.getDeviceName())\n                    .setProductId(product.getId());\n            device = createDevice0(createReqVO);\n            log.info(\"[registerDevice][产品({}) 自动创建设备({})]\",\n                    reqDTO.getProductKey(), reqDTO.getDeviceName());\n            // 2.2 返回设备密钥\n            return new IotDeviceRegisterRespDTO(device.getProductKey(), device.getDeviceName(), device.getDeviceSecret());\n        });\n    }\n\n    @Override\n    public List<IotSubDeviceRegisterRespDTO> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO) {\n        // 1. 校验网关设备\n        IotDeviceDO gatewayDevice = getSelf().getDeviceFromCache(reqDTO.getGatewayProductKey(), reqDTO.getGatewayDeviceName());\n\n        // 2. 遍历注册每个子设备\n        return TenantUtils.execute(gatewayDevice.getTenantId(), () ->\n                registerSubDevices0(gatewayDevice, reqDTO.getSubDevices()));\n    }\n\n    @Override\n    public List<IotSubDeviceRegisterRespDTO> handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice) {\n        // 1. 解析参数\n        if (!(message.getParams() instanceof List)) {\n            throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID);\n        }\n        List<IotSubDeviceRegisterReqDTO> subDevices = JsonUtils.convertList(message.getParams(), IotSubDeviceRegisterReqDTO.class);\n\n        // 2. 遍历注册每个子设备\n        return registerSubDevices0(gatewayDevice, subDevices);\n    }\n\n    private List<IotSubDeviceRegisterRespDTO> registerSubDevices0(IotDeviceDO gatewayDevice,\n                                                                   List<IotSubDeviceRegisterReqDTO> subDevices) {\n        // 1.1 校验网关设备\n        if (gatewayDevice == null) {\n            throw exception(DEVICE_NOT_EXISTS);\n        }\n        if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {\n            throw exception(DEVICE_NOT_GATEWAY);\n        }\n        // 1.2 注册设备不能为空\n        if (CollUtil.isEmpty(subDevices)) {\n            throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID);\n        }\n\n        // 2. 遍历注册每个子设备\n        List<IotSubDeviceRegisterRespDTO> results = new ArrayList<>(subDevices.size());\n        for (IotSubDeviceRegisterReqDTO subDevice : subDevices) {\n            try {\n                IotDeviceDO device = registerSubDevice0(gatewayDevice, subDevice);\n                results.add(new IotSubDeviceRegisterRespDTO(\n                        subDevice.getProductKey(), subDevice.getDeviceName(), device.getDeviceSecret()));\n            } catch (Exception ex) {\n                log.error(\"[registerSubDevices0][子设备({}/{}) 注册失败]\",\n                        subDevice.getProductKey(), subDevice.getDeviceName(), ex);\n            }\n        }\n        return results;\n    }\n\n    private IotDeviceDO registerSubDevice0(IotDeviceDO gatewayDevice, IotSubDeviceRegisterReqDTO params) {\n        // 1.1 校验产品\n        IotProductDO product = productService.getProductByProductKey(params.getProductKey());\n        if (product == null) {\n            throw exception(PRODUCT_NOT_EXISTS);\n        }\n        // 1.2 校验产品是否为网关子设备类型\n        if (!IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType())) {\n            throw exception(DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB, params.getProductKey());\n        }\n        // 1.3 校验设备是否已存在（子设备动态注册：设备必须已预注册）\n        IotDeviceDO existDevice = getSelf().getDeviceFromCache(params.getProductKey(), params.getDeviceName());\n        if (existDevice == null) {\n            throw exception(DEVICE_NOT_EXISTS);\n        }\n        // 1.4 校验是否绑定到其他网关\n        if (existDevice.getGatewayId() != null && ObjUtil.notEqual(existDevice.getGatewayId(), gatewayDevice.getId())) {\n            throw exception(DEVICE_GATEWAY_BINDTO_EXISTS,\n                    existDevice.getProductKey(), existDevice.getDeviceName());\n        }\n\n        // 2. 绑定到网关（如果尚未绑定）\n        if (existDevice.getGatewayId() == null) {\n            // 2.1 更新数据库\n            deviceMapper.updateById(new IotDeviceDO().setId(existDevice.getId()).setGatewayId(gatewayDevice.getId()));\n            // 2.2 清空对应缓存\n            deleteDeviceCache(existDevice);\n            log.info(\"[registerSubDevice][网关({}/{}) 绑定子设备({}/{})]\",\n                    gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(),\n                    existDevice.getProductKey(), existDevice.getDeviceName());\n        }\n        return existDevice;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device.message;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;\n\nimport javax.annotation.Nullable;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * IoT 设备消息 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDeviceMessageService {\n\n    /**\n     * 初始化设备消息的 TDengine 超级表\n     *\n     * 系统启动时，会自动初始化一次\n     */\n    void defineDeviceMessageStable();\n\n    /**\n     * 发送设备消息\n     *\n     * @param message 消息（“codec（编解码）字段” 部分字段）\n     * @param device 设备\n     * @return 设备消息\n     */\n    IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device);\n\n    /**\n     * 发送设备消息\n     *\n     * @param message 消息（“codec（编解码）字段” 部分字段）\n     * @return 设备消息\n     */\n    IotDeviceMessage sendDeviceMessage(IotDeviceMessage message);\n\n    /**\n     * 处理设备上行的消息，包括如下步骤：\n     *\n     * 1. 处理消息\n     * 2. 记录消息\n     * 3. 回复消息\n     *\n     * @param message 消息\n     * @param device 设备\n     */\n    void handleUpstreamDeviceMessage(IotDeviceMessage message, IotDeviceDO device);\n\n    /**\n     * 获得设备消息分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 设备消息分页\n     */\n    PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO);\n\n    /**\n     * 获得指定 requestId 的设备消息列表\n     *\n     * @param deviceId 设备编号\n     * @param requestIds requestId 列表\n     * @param reply 是否回复\n     * @return 设备消息列表\n     */\n    List<IotDeviceMessageDO> getDeviceMessageListByRequestIdsAndReply(\n            @NotNull(message = \"设备编号不能为空\") Long deviceId,\n            List<String> requestIds,\n            Boolean reply);\n\n    /**\n     * 获得设备消息数量\n     *\n     * @param createTime 创建时间，如果为空，则统计所有消息数量\n     * @return 消息数量\n     */\n    Long getDeviceMessageCount(@Nullable LocalDateTime createTime);\n\n    /**\n     * 获取设备消息的数据统计\n     *\n     * @param reqVO 统计请求\n     * @return 设备消息的数据统计\n     */\n    List<IotStatisticsDeviceMessageSummaryByDateRespVO> getDeviceMessageSummaryByDate(\n            IotStatisticsDeviceMessageReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device.message;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;\nimport cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;\nimport cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;\nimport cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;\nimport cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.google.common.base.Objects;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.sql.Timestamp;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL;\n\n/**\n * IoT 设备消息 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class IotDeviceMessageServiceImpl implements IotDeviceMessageService {\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotDevicePropertyService devicePropertyService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private IotOtaTaskRecordService otaTaskRecordService;\n\n    @Resource\n    private IotDeviceMessageMapper deviceMessageMapper;\n\n    @Resource\n    private IotDeviceMessageProducer deviceMessageProducer;\n\n    @Override\n    public void defineDeviceMessageStable() {\n        if (StrUtil.isNotEmpty(deviceMessageMapper.showSTable())) {\n            log.info(\"[defineDeviceMessageStable][设备消息超级表已存在，创建跳过]\");\n            return;\n        }\n        log.info(\"[defineDeviceMessageStable][设备消息超级表不存在，创建开始...]\");\n        deviceMessageMapper.createSTable();\n        log.info(\"[defineDeviceMessageStable][设备消息超级表不存在，创建成功]\");\n    }\n\n    @Async\n    void createDeviceLogAsync(IotDeviceMessage message) {\n        IotDeviceMessageDO messageDO = BeanUtils.toBean(message, IotDeviceMessageDO.class)\n                .setUpstream(IotDeviceMessageUtils.isUpstreamMessage(message))\n                .setReply(IotDeviceMessageUtils.isReplyMessage(message))\n                .setIdentifier(IotDeviceMessageUtils.getIdentifier(message));\n        if (message.getParams() != null) {\n            messageDO.setParams(JsonUtils.toJsonString(messageDO.getParams()));\n        }\n        if (messageDO.getData() != null) {\n            messageDO.setData(JsonUtils.toJsonString(messageDO.getData()));\n        }\n        deviceMessageMapper.insert(messageDO);\n    }\n\n    @Override\n    public IotDeviceMessage sendDeviceMessage(IotDeviceMessage message) {\n        IotDeviceDO device = deviceService.validateDeviceExists(message.getDeviceId());\n        return sendDeviceMessage(message, device);\n    }\n\n    @Override\n    public IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {\n        return sendDeviceMessage(message, device, null);\n    }\n\n    private IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, String serverId) {\n        // 1. 补充信息\n        appendDeviceMessage(message, device);\n\n        // 2.1 情况一：发送上行消息\n        boolean upstream = IotDeviceMessageUtils.isUpstreamMessage(message);\n        if (upstream) {\n            deviceMessageProducer.sendDeviceMessage(message);\n            return message;\n        }\n\n        // 2.2 情况二：发送下行消息\n        // 如果是下行消息，需要校验 serverId 存在\n        // TODO 芋艿：【设计】下行消息需要区分 PUSH 和 PULL 模型\n        // 1. PUSH 模型：适用于 MQTT 等长连接协议。通过 serverId 将消息路由到指定网关，实时推送。\n        // 2. PULL 模型：适用于 HTTP 等短连接协议。设备无固定 serverId，无法主动推送。\n        // 解决方案：\n        // 当 serverId 不存在时，将下行消息存入“待拉取消息表”（例如 iot_device_pull_message）。\n        // 设备端通过定时轮询一个新增的 API（例如 /iot/message/pull）来拉取属于自己的消息。\n        if (StrUtil.isEmpty(serverId)) {\n            serverId = devicePropertyService.getDeviceServerId(device.getId());\n            if (StrUtil.isEmpty(serverId)) {\n                throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL);\n            }\n        }\n        deviceMessageProducer.sendDeviceMessageToGateway(serverId, message);\n        // 特殊：记录消息日志。原因：上行消息，消费时，已经会记录；下行消息，因为消费在 Gateway 端，所以需要在这里记录\n        getSelf().createDeviceLogAsync(message);\n        return message;\n    }\n\n    /**\n     * 补充消息的后端字段\n     *\n     * @param message 消息\n     * @param device  设备信息\n     */\n    private void appendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {\n        message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now())\n                .setDeviceId(device.getId()).setTenantId(device.getTenantId());\n        // 特殊：如果设备没有指定 requestId，则使用 messageId\n        if (StrUtil.isEmpty(message.getRequestId())) {\n            message.setRequestId(message.getId());\n        }\n    }\n\n    @Override\n    public void handleUpstreamDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {\n        // 1. 处理消息\n        Object replyData = null;\n        ServiceException serviceException = null;\n        try {\n            replyData = handleUpstreamDeviceMessage0(message, device);\n        } catch (ServiceException ex) {\n            serviceException = ex;\n            log.warn(\"[handleUpstreamDeviceMessage][message({}) 业务异常]\", message, serviceException);\n        } catch (Exception ex) {\n            log.error(\"[handleUpstreamDeviceMessage][message({}) 发生异常]\", message, ex);\n            throw ex;\n        }\n\n        // 2. 记录消息\n        getSelf().createDeviceLogAsync(message);\n\n        // 3. 回复消息。前提：非 _reply 消息、非禁用回复的消息\n        if (IotDeviceMessageUtils.isReplyMessage(message)\n                || IotDeviceMessageMethodEnum.isReplyDisabled(message.getMethod())\n                || StrUtil.isEmpty(message.getServerId())) {\n            return;\n        }\n        try {\n            IotDeviceMessage replyMessage = IotDeviceMessage.replyOf(message.getRequestId(), message.getMethod(), replyData,\n                    serviceException != null ? serviceException.getCode() : null,\n                    serviceException != null ? serviceException.getMessage() : null);\n            sendDeviceMessage(replyMessage, device, message.getServerId());\n        } catch (Exception ex) {\n            log.error(\"[handleUpstreamDeviceMessage][message({}) 回复消息失败]\", message, ex);\n        }\n    }\n\n    // TODO @芋艿：可优化：未来逻辑复杂后，可以独立拆除 Processor 处理器\n    private Object handleUpstreamDeviceMessage0(IotDeviceMessage message, IotDeviceDO device) {\n        // 设备上下线\n        if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod())) {\n            String stateStr = IotDeviceMessageUtils.getIdentifier(message);\n            assert stateStr != null;\n            Assert.notEmpty(stateStr, \"设备状态不能为空\");\n            Integer state = Integer.valueOf(stateStr);\n            deviceService.updateDeviceState(device, state);\n            return null;\n        }\n\n        // 属性上报\n        if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())) {\n            devicePropertyService.saveDeviceProperty(device, message);\n            return null;\n        }\n        // 批量上报（属性+事件+子设备）\n        if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod())) {\n            handlePackMessage(message, device);\n            return null;\n        }\n\n        // OTA 上报升级进度\n        if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.OTA_PROGRESS.getMethod())) {\n            otaTaskRecordService.updateOtaRecordProgress(device, message);\n            return null;\n        }\n\n        // 添加拓扑关系\n        if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_ADD.getMethod())) {\n            return deviceService.handleTopoAddMessage(message, device);\n        }\n        // 删除拓扑关系\n        if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod())) {\n            return deviceService.handleTopoDeleteMessage(message, device);\n        }\n        // 获取拓扑关系\n        if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_GET.getMethod())) {\n            return deviceService.handleTopoGetMessage(device);\n        }\n\n        // 子设备动态注册\n        if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod())) {\n            return deviceService.handleSubDeviceRegisterMessage(message, device);\n        }\n\n        return null;\n    }\n\n    // ========== 批量上报处理方法 ==========\n\n    /**\n     * 处理批量上报消息\n     * <p>\n     * 将 pack 消息拆分成多条标准消息，发送到 MQ 让规则引擎处理\n     *\n     * @param packMessage   批量消息\n     * @param gatewayDevice 网关设备\n     */\n    private void handlePackMessage(IotDeviceMessage packMessage, IotDeviceDO gatewayDevice) {\n        // 1. 解析参数\n        IotDevicePropertyPackPostReqDTO params = JsonUtils.convertObject(\n                packMessage.getParams(), IotDevicePropertyPackPostReqDTO.class);\n        if (params == null) {\n            log.warn(\"[handlePackMessage][消息({}) 参数解析失败]\", packMessage);\n            return;\n        }\n\n        // 2. 处理网关设备（自身）的数据\n        sendDevicePackData(gatewayDevice, packMessage.getServerId(), params.getProperties(), params.getEvents());\n\n        // 3. 处理子设备的数据\n        if (CollUtil.isEmpty(params.getSubDevices())) {\n            return;\n        }\n        for (IotDevicePropertyPackPostReqDTO.SubDeviceData subDeviceData : params.getSubDevices()) {\n            try {\n                IotDeviceIdentity identity = subDeviceData.getIdentity();\n                IotDeviceDO subDevice = deviceService.getDeviceFromCache(identity.getProductKey(), identity.getDeviceName());\n                if (subDevice == null) {\n                    log.warn(\"[handlePackMessage][子设备({}/{}) 不存在]\", identity.getProductKey(), identity.getDeviceName());\n                    continue;\n                }\n                // 特殊：子设备不需要指定 serverId，因为子设备实际可能连接在不同的 gateway-server 上，导致 serverId 不同\n                sendDevicePackData(subDevice, null, subDeviceData.getProperties(), subDeviceData.getEvents());\n            } catch (Exception ex) {\n                log.error(\"[handlePackMessage][子设备({}/{}) 数据处理失败]\", subDeviceData.getIdentity().getProductKey(),\n                        subDeviceData.getIdentity().getDeviceName(), ex);\n            }\n        }\n    }\n\n    /**\n     * 发送设备 pack 数据到 MQ（属性 + 事件）\n     *\n     * @param device     设备\n     * @param serverId   服务标识\n     * @param properties 属性数据\n     * @param events     事件数据\n     */\n    private void sendDevicePackData(IotDeviceDO device, String serverId,\n                                    Map<String, Object> properties,\n                                    Map<String, IotDevicePropertyPackPostReqDTO.EventValue> events) {\n        // 1. 发送属性消息\n        if (MapUtil.isNotEmpty(properties)) {\n            IotDeviceMessage propertyMsg = IotDeviceMessage.requestOf(\n                    device.getId(), device.getTenantId(), serverId,\n                    IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),\n                    IotDevicePropertyPostReqDTO.of(properties));\n            deviceMessageProducer.sendDeviceMessage(propertyMsg);\n        }\n\n        // 2. 发送事件消息\n        if (MapUtil.isNotEmpty(events)) {\n            for (Map.Entry<String, IotDevicePropertyPackPostReqDTO.EventValue> eventEntry : events.entrySet()) {\n                String eventId = eventEntry.getKey();\n                IotDevicePropertyPackPostReqDTO.EventValue eventValue = eventEntry.getValue();\n                if (eventValue == null) {\n                    continue;\n                }\n                IotDeviceMessage eventMsg = IotDeviceMessage.requestOf(\n                        device.getId(), device.getTenantId(), serverId,\n                        IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),\n                        IotDeviceEventPostReqDTO.of(eventId, eventValue.getValue(), eventValue.getTime()));\n                deviceMessageProducer.sendDeviceMessage(eventMsg);\n            }\n        }\n    }\n\n    // ========= 设备消息查询 ==========\n\n    @Override\n    public PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO) {\n        try {\n            IPage<IotDeviceMessageDO> page = deviceMessageMapper.selectPage(\n                    new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO);\n            return new PageResult<>(page.getRecords(), page.getTotal());\n        } catch (Exception exception) {\n            if (exception.getMessage().contains(\"Table does not exist\")) {\n                return PageResult.empty();\n            }\n            throw exception;\n        }\n    }\n\n    @Override\n    public List<IotDeviceMessageDO> getDeviceMessageListByRequestIdsAndReply(Long deviceId, List<String> requestIds, Boolean reply) {\n        if (CollUtil.isEmpty(requestIds)) {\n            return ListUtil.of();\n        }\n        return deviceMessageMapper.selectListByRequestIdsAndReply(deviceId, requestIds, reply);\n    }\n\n    @Override\n    public Long getDeviceMessageCount(LocalDateTime createTime) {\n        return deviceMessageMapper.selectCountByCreateTime(\n                createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null);\n    }\n\n    @Override\n    public List<IotStatisticsDeviceMessageSummaryByDateRespVO> getDeviceMessageSummaryByDate(\n            IotStatisticsDeviceMessageReqVO reqVO) {\n        // 1. 按小时统计，获取分项统计数据\n        List<Map<String, Object>> countList = deviceMessageMapper.selectDeviceMessageCountGroupByDate(\n                LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[0]),\n                LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[1]));\n\n        // 2. 按照日期间隔，合并数据\n        List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1],\n                reqVO.getInterval());\n        return convertList(timeRanges, times -> {\n            Integer upstreamCount = countList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get(\"time\")))\n                    .mapToInt(value -> MapUtil.getInt(value, \"upstream_count\")).sum();\n            Integer downstreamCount = countList.stream()\n                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get(\"time\")))\n                    .mapToInt(value -> MapUtil.getInt(value, \"downstream_count\")).sum();\n            return new IotStatisticsDeviceMessageSummaryByDateRespVO()\n                    .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))\n                    .setUpstreamCount(upstreamCount).setDownstreamCount(downstreamCount);\n        });\n    }\n\n    private IotDeviceMessageServiceImpl getSelf() {\n        return SpringUtil.getBean(getClass());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device.property;\n\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;\n\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * IoT 设备【属性】数据 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDevicePropertyService {\n\n    // ========== 设备属性相关操作 ==========\n\n    /**\n     * 定义设备属性数据的结构\n     *\n     * @param productId 产品编号\n     */\n    void defineDevicePropertyData(Long productId);\n\n    /**\n     * 保存设备数据\n     *\n     * @param device 设备\n     * @param message 设备消息\n     */\n    void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message);\n\n    /**\n     * 获得设备属性最新数据\n     *\n     * @param deviceId 设备编号\n     * @return 设备属性最新数据\n     */\n    Map<String, IotDevicePropertyDO> getLatestDeviceProperties(Long deviceId);\n\n    /**\n     * 获得设备属性历史数据\n     *\n     * @param listReqVO 列表请求\n     * @return 设备属性历史数据\n     */\n    List<IotDevicePropertyRespVO> getHistoryDevicePropertyList(@Valid IotDevicePropertyHistoryListReqVO listReqVO);\n\n    // ========== 设备时间相关操作 ==========\n\n    /**\n     * 获得最后上报时间小于指定时间的设备编号集合\n     *\n     * @param maxReportTime 最大上报时间\n     * @return 设备编号集合\n     */\n    Set<Long> getDeviceIdListByReportTime(LocalDateTime maxReportTime);\n\n    /**\n     * 更新设备上报时间\n     *\n     * @param id 设备编号\n     * @param reportTime 上报时间\n     */\n    void updateDeviceReportTimeAsync(Long id, LocalDateTime reportTime);\n\n    /**\n     * 更新设备关联的网关服务 serverId\n     *\n     * @param id 设备编号\n     * @param serverId 网关 serverId\n     */\n    void updateDeviceServerIdAsync(Long id, String serverId);\n\n    /**\n     * 获得设备关联的网关服务 serverId\n     *\n     * @param id 设备编号\n     * @return 网关 serverId\n     */\n    String getDeviceServerId(Long id);\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.device.property;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs;\nimport cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO;\nimport cn.iocoder.yudao.module.iot.dal.redis.device.DeviceReportTimeRedisDAO;\nimport cn.iocoder.yudao.module.iot.dal.redis.device.DeviceServerIdRedisDAO;\nimport cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyMapper;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;\nimport cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.getBigDecimal;\n\n/**\n * IoT 设备【属性】数据 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Slf4j\npublic class IotDevicePropertyServiceImpl implements IotDevicePropertyService {\n\n    /**\n     * 物模型的数据类型，与 TDengine 数据类型的映射关系\n     *\n     * @see <a href=\"https://docs.taosdata.com/reference/taos-sql/data-type/\">TDEngine 数据类型</a>\n     */\n    private static final Map<String, String> TYPE_MAPPING = MapUtil.<String, String>builder()\n            .put(IotDataSpecsDataTypeEnum.INT.getDataType(), TDengineTableField.TYPE_INT)\n            .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT)\n            .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE)\n            .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT)\n            .put(IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT)\n            .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_VARCHAR)\n            .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP)\n            .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_VARCHAR)\n            .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_VARCHAR)\n            .build();\n\n    @Resource\n    private IotThingModelService thingModelService;\n    @Resource\n    @Lazy  // 延迟加载，解决循环依赖\n    private IotProductService productService;\n    @Resource\n    @Lazy  // 延迟加载，解决循环依赖\n    private IotDeviceService deviceService;\n\n    @Resource\n    private DevicePropertyRedisDAO deviceDataRedisDAO;\n    @Resource\n    private DeviceReportTimeRedisDAO deviceReportTimeRedisDAO;\n    @Resource\n    private DeviceServerIdRedisDAO deviceServerIdRedisDAO;\n\n    @Resource\n    private IotDevicePropertyMapper devicePropertyMapper;\n\n    // ========== 设备属性相关操作 ==========\n\n    @Override\n    public void defineDevicePropertyData(Long productId) {\n        // 1.1 查询产品和物模型\n        IotProductDO product = productService.validateProductExists(productId);\n        List<IotThingModelDO> thingModels = filterList(thingModelService.getThingModelListByProductId(productId),\n                thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType()));\n        // 1.2 解析 DB 里的字段\n        List<TDengineTableField> oldFields = new ArrayList<>();\n        try {\n            oldFields.addAll(devicePropertyMapper.getProductPropertySTableFieldList(product.getId()));\n        } catch (Exception e) {\n            if (!e.getMessage().contains(\"Table does not exist\")) {\n                throw e;\n            }\n        }\n\n        // 2.1 情况一：如果是新增的时候，需要创建表\n        List<TDengineTableField> newFields = buildTableFieldList(thingModels);\n        if (CollUtil.isEmpty(oldFields)) {\n            if (CollUtil.isEmpty(newFields)) {\n                log.info(\"[defineDevicePropertyData][productId({}) 没有需要定义的属性]\", productId);\n                return;\n            }\n            devicePropertyMapper.createProductPropertySTable(product.getId(), newFields);\n            return;\n        }\n        // 2.2 情况二：如果是修改的时候，需要更新表\n        devicePropertyMapper.alterProductPropertySTable(product.getId(), oldFields, newFields);\n    }\n\n    private List<TDengineTableField> buildTableFieldList(List<IotThingModelDO> thingModels) {\n        return convertList(thingModels, thingModel -> {\n            TDengineTableField field = new TDengineTableField(\n                    StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写\n                    TYPE_MAPPING.get(thingModel.getProperty().getDataType()));\n            String dataType = thingModel.getProperty().getDataType();\n            if (Objects.equals(dataType, IotDataSpecsDataTypeEnum.TEXT.getDataType())) {\n                field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength());\n            } else if (ObjectUtils.equalsAny(dataType, IotDataSpecsDataTypeEnum.STRUCT.getDataType(),\n                    IotDataSpecsDataTypeEnum.ARRAY.getDataType())) {\n                field.setLength(TDengineTableField.LENGTH_VARCHAR);\n            }\n            return field;\n        });\n    }\n\n    @Override\n    @SuppressWarnings(\"PatternVariableCanBeUsed\")\n    public void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message) {\n        if (!(message.getParams() instanceof Map)) {\n            log.error(\"[saveDeviceProperty][消息内容({}) 的 data 类型不正确]\", message);\n            return;\n        }\n        Map<?, ?> params = (Map<?, ?>) message.getParams();\n        if (CollUtil.isEmpty(params)) {\n            log.error(\"[saveDeviceProperty][消息内容({}) 的 data 为空]\", message);\n            return;\n        }\n\n        // 1. 根据物模型，拼接合法的属性\n        // TODO @芋艿：【待定 004】赋能后，属性到底以 thingModel 为准（ik），还是 db 的表结构为准（tl）？\n        List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductIdFromCache(device.getProductId());\n        Map<String, Object> properties = new HashMap<>();\n        params.forEach((key, value) -> {\n            IotThingModelDO thingModel = CollUtil.findOne(thingModels, o -> o.getIdentifier().equals(key));\n            if (thingModel == null || thingModel.getProperty() == null) {\n                log.error(\"[saveDeviceProperty][消息({}) 的属性({}) 不存在]\", message, key);\n                return;\n            }\n            String dataType = thingModel.getProperty().getDataType();\n            if (ObjectUtils.equalsAny(dataType,\n                    IotDataSpecsDataTypeEnum.STRUCT.getDataType(), IotDataSpecsDataTypeEnum.ARRAY.getDataType())) {\n                // 特殊：STRUCT 和 ARRAY 类型，在 TDengine 里，有没对应数据类型，只能通过 JSON 来存储\n                properties.put((String) key, JsonUtils.toJsonString(value));\n            } else if (IotDataSpecsDataTypeEnum.INT.getDataType().equals(dataType)) {\n                properties.put((String) key, Convert.toInt(value));\n            } else if (IotDataSpecsDataTypeEnum.FLOAT.getDataType().equals(dataType)) {\n                properties.put((String) key, Convert.toFloat(value));\n            } else if (IotDataSpecsDataTypeEnum.DOUBLE.getDataType().equals(dataType)) {\n                properties.put((String) key, Convert.toDouble(value));\n            }  else if (IotDataSpecsDataTypeEnum.BOOL.getDataType().equals(dataType)) {\n                properties.put((String) key, Convert.toByte(value));\n            }  else {\n                properties.put((String) key, value);\n            }\n        });\n        if (CollUtil.isEmpty(properties)) {\n            log.error(\"[saveDeviceProperty][消息({}) 没有合法的属性]\", message);\n        } else {\n            // 2.1 保存设备属性【数据】\n            devicePropertyMapper.insert(device, properties, LocalDateTimeUtil.toEpochMilli(message.getReportTime()));\n\n            // 2.2 保存设备属性【日志】\n            Map<String, IotDevicePropertyDO> properties2 = convertMap(properties.entrySet(), Map.Entry::getKey, entry ->\n                    IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build());\n            deviceDataRedisDAO.putAll(device.getId(), properties2);\n        }\n\n        // 2.3 提取 GeoLocation 并更新设备定位\n        // 为什么 properties 为空，也要执行定位更新？因为可能上报的属性里，没有合法属性，但是包含 GeoLocation 定位属性\n        extractAndUpdateDeviceLocation(device, (Map<?, ?>) message.getParams());\n    }\n\n    @Override\n    public Map<String, IotDevicePropertyDO> getLatestDeviceProperties(Long deviceId) {\n        return deviceDataRedisDAO.get(deviceId);\n    }\n\n    @Override\n    public List<IotDevicePropertyRespVO> getHistoryDevicePropertyList(IotDevicePropertyHistoryListReqVO listReqVO) {\n        try {\n            return devicePropertyMapper.selectListByHistory(listReqVO);\n        } catch (Exception exception) {\n            if (exception.getMessage().contains(\"Table does not exist\")) {\n                return Collections.emptyList();\n            }\n            throw exception;\n        }\n    }\n\n    // ========== 设备时间相关操作 ==========\n\n    @Override\n    public Set<Long> getDeviceIdListByReportTime(LocalDateTime maxReportTime) {\n        return deviceReportTimeRedisDAO.range(maxReportTime);\n    }\n\n    @Override\n    @Async\n    public void updateDeviceReportTimeAsync(Long id, LocalDateTime reportTime) {\n        deviceReportTimeRedisDAO.update(id, reportTime);\n    }\n\n    @Override\n    public void updateDeviceServerIdAsync(Long id, String serverId) {\n        if (StrUtil.isEmpty(serverId)) {\n            return;\n        }\n        deviceServerIdRedisDAO.update(id, serverId);\n    }\n\n    @Override\n    public String getDeviceServerId(Long id) {\n        return deviceServerIdRedisDAO.get(id);\n    }\n\n    // ========== 设备定位相关操作 ==========\n\n    /**\n     * 从属性中提取 GeoLocation 并更新设备定位\n     *\n     * @see <a href=\"https://help.aliyun.com/zh/iot/user-guide/device-geolocation\">阿里云规范</a>\n     * GeoLocation 结构体包含：Longitude, Latitude, Altitude, CoordinateSystem\n     */\n    private void extractAndUpdateDeviceLocation(IotDeviceDO device, Map<?, ?> params) {\n        // 1. 解析 GeoLocation 经纬度坐标\n        BigDecimal[] location = parseGeoLocation(params);\n        if (location == null) {\n            return;\n        }\n\n        // 2. 更新设备定位\n        deviceService.updateDeviceLocation(device, location[0], location[1]);\n        log.info(\"[extractAndUpdateGeoLocation][设备({}) 定位更新: lng={}, lat={}]\",\n                device.getId(), location[0], location[1]);\n    }\n\n    /**\n     * 从属性参数中解析 GeoLocation，返回经纬度坐标数组 [longitude, latitude]\n     *\n     * @param params 属性参数\n     * @return [经度, 纬度]，解析失败返回 null\n     */\n    @SuppressWarnings(\"unchecked\")\n    private BigDecimal[] parseGeoLocation(Map<?, ?> params) {\n        if (params == null) {\n            return null;\n        }\n        // 1. 查找 GeoLocation 属性（标识符为 GeoLocation 或 geoLocation）\n        Object geoValue = params.get(\"GeoLocation\");\n        if (geoValue == null) {\n            geoValue = params.get(\"geoLocation\");\n        }\n        if (geoValue == null) {\n            return null;\n        }\n\n        // 2. 转换为 Map\n        Map<String, Object> geoLocation = null;\n        if (geoValue instanceof Map) {\n            geoLocation = (Map<String, Object>) geoValue;\n        } else if (geoValue instanceof String) {\n            geoLocation = JsonUtils.parseObject((String) geoValue, Map.class);\n        }\n        if (geoLocation == null) {\n            return null;\n        }\n\n        // 3. 提取经纬度（支持阿里云命名规范：首字母大写）\n        BigDecimal longitude = getBigDecimal(geoLocation, \"Longitude\");\n        if (longitude == null) {\n            longitude = getBigDecimal(geoLocation, \"longitude\");\n        }\n        BigDecimal latitude = getBigDecimal(geoLocation, \"Latitude\");\n        if (latitude == null) {\n            latitude = getBigDecimal(geoLocation, \"latitude\");\n        }\n        if (longitude == null || latitude == null) {\n            return null;\n        }\n        // 校验经纬度范围：经度 -180 到 180，纬度 -90 到 90\n        if (longitude.compareTo(BigDecimal.valueOf(-180)) < 0 || longitude.compareTo(BigDecimal.valueOf(180)) > 0\n                || latitude.compareTo(BigDecimal.valueOf(-90)) < 0 || latitude.compareTo(BigDecimal.valueOf(90)) > 0) {\n            log.warn(\"[parseGeoLocation][经纬度超出有效范围: lng={}, lat={}]\", longitude, latitude);\n            return null;\n        }\n        return new BigDecimal[]{longitude, latitude};\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.ota;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * OTA 固件管理 Service 接口\n *\n * @author Shelly Chan\n */\npublic interface IotOtaFirmwareService {\n\n    /**\n     * 创建 OTA 固件\n     *\n     * @param saveReqVO 固件信息\n     * @return 固件编号\n     */\n    Long createOtaFirmware(@Valid IotOtaFirmwareCreateReqVO saveReqVO);\n\n    /**\n     * 更新 OTA 固件信息\n     *\n     * @param updateReqVO 固件信息\n     */\n    void updateOtaFirmware(@Valid IotOtaFirmwareUpdateReqVO updateReqVO);\n\n    /**\n     * 获取 OTA 固件信息\n     *\n     * @param id 固件编号\n     * @return 固件信息\n     */\n    IotOtaFirmwareDO getOtaFirmware(Long id);\n\n    /**\n     * 根据产品、版本号，获取 OTA 固件信息\n     *\n     * @param productId 产品编号\n     * @param version 版本号\n     * @return OTA 固件信息\n     */\n    IotOtaFirmwareDO getOtaFirmwareByProductIdAndVersion(Long productId, String version);\n\n    /**\n     * 获取 OTA 固件信息列表\n     *\n     * @param ids 固件编号集合\n     * @return 固件信息列表\n     */\n    List<IotOtaFirmwareDO> getOtaFirmwareList(Collection<Long> ids);\n\n    /**\n     * 获取 OTA 固件信息 Map\n     *\n     * @param ids 固件编号集合\n     * @return 固件信息 Map\n     */\n    default Map<Long, IotOtaFirmwareDO> getOtaFirmwareMap(Collection<Long> ids) {\n        return convertMap(getOtaFirmwareList(ids), IotOtaFirmwareDO::getId);\n    }\n\n    /**\n     * 分页查询 OTA 固件信息\n     *\n     * @param pageReqVO 分页查询条件\n     * @return 分页结果\n     */\n    PageResult<IotOtaFirmwareDO> getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO);\n\n    /**\n     * 验证 OTA 固件是否存在\n     *\n     * @param id  固件编号\n     * @return 固件信息\n     */\n    IotOtaFirmwareDO validateFirmwareExists(Long id);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.ota;\n\nimport cn.hutool.crypto.digest.DigestAlgorithm;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport cn.hutool.http.HttpUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaFirmwareMapper;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.io.ByteArrayInputStream;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE;\n\n/**\n * OTA 固件管理 Service 实现类\n *\n * @author Shelly Chan\n */\n@Service\n@Validated\n@Slf4j\npublic class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService {\n\n    @Resource\n    private IotOtaFirmwareMapper otaFirmwareMapper;\n    @Lazy\n    @Resource\n    private IotProductService productService;\n\n    @Override\n    public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) {\n        // 1.1 校验固件产品 + 版本号不能重复\n        if (otaFirmwareMapper.selectByProductIdAndVersion(saveReqVO.getProductId(), saveReqVO.getVersion()) != null) {\n            throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE);\n        }\n        // 1.2 校验产品存在\n        productService.validateProductExists(saveReqVO.getProductId());\n\n        // 2. 构建对象 + 存储\n        IotOtaFirmwareDO firmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class);\n        // 2.1 计算文件签名等属性\n        try {\n            calculateFileDigest(firmware);\n        } catch (Exception e) {\n            log.error(\"[createOtaFirmware][url({}) 计算文件签名失败]\", firmware.getFileUrl(), e);\n            throw new RuntimeException(\"计算文件签名失败: \" + e.getMessage());\n        }\n        otaFirmwareMapper.insert(firmware);\n        return firmware.getId();\n    }\n\n    @Override\n    public void updateOtaFirmware(IotOtaFirmwareUpdateReqVO updateReqVO) {\n        // 1. 校验存在\n        validateFirmwareExists(updateReqVO.getId());\n\n        // 2. 更新数据\n        IotOtaFirmwareDO updateObj = BeanUtils.toBean(updateReqVO, IotOtaFirmwareDO.class);\n        otaFirmwareMapper.updateById(updateObj);\n    }\n\n    @Override\n    public IotOtaFirmwareDO getOtaFirmware(Long id) {\n        return otaFirmwareMapper.selectById(id);\n    }\n\n    @Override\n    public IotOtaFirmwareDO getOtaFirmwareByProductIdAndVersion(Long productId, String version) {\n        return otaFirmwareMapper.selectByProductIdAndVersion(productId, version);\n    }\n\n    @Override\n    public List<IotOtaFirmwareDO> getOtaFirmwareList(Collection<Long> ids) {\n        if (ids == null || ids.isEmpty()) {\n            return Collections.emptyList();\n        }\n        return otaFirmwareMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<IotOtaFirmwareDO> getOtaFirmwarePage(IotOtaFirmwarePageReqVO pageReqVO) {\n        return otaFirmwareMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public IotOtaFirmwareDO validateFirmwareExists(Long id) {\n        IotOtaFirmwareDO firmware = otaFirmwareMapper.selectById(id);\n        if (firmware == null) {\n            throw exception(OTA_FIRMWARE_NOT_EXISTS);\n        }\n        return firmware;\n    }\n\n    /**\n     * 计算文件签名\n     *\n     * @param firmware 固件对象\n     */\n    private void calculateFileDigest(IotOtaFirmwareDO firmware) {\n        String fileUrl = firmware.getFileUrl();\n        // 下载文件并计算签名\n        byte[] fileBytes = HttpUtil.downloadBytes(fileUrl);\n        // 设置文件大小\n        firmware.setFileSize((long) fileBytes.length);\n        // 计算 MD5 签名\n        firmware.setFileDigestAlgorithm(DigestAlgorithm.MD5.getValue());\n        String md5Hex = DigestUtil.digester(firmware.getFileDigestAlgorithm()).digestHex(new ByteArrayInputStream(fileBytes));\n        firmware.setFileDigestValue(md5Hex);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.ota;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * IoT OTA 升级记录 Service 接口\n */\npublic interface IotOtaTaskRecordService {\n\n    /**\n     * 批量创建 OTA 升级记录\n     *\n     * @param devices      设备列表\n     * @param firmwareId   固件编号\n     * @param taskId       任务编号\n     */\n    void createOtaTaskRecordList(List<IotDeviceDO> devices, Long firmwareId, Long taskId);\n\n    /**\n     * 获取 OTA 升级记录的状态统计\n     *\n     * @param firmwareId 固件编号\n     * @param taskId     任务编号\n     * @return 状态统计 Map，key 为状态码，value 为对应状态的升级记录数量\n     */\n    Map<Integer, Long> getOtaTaskRecordStatusStatistics(Long firmwareId, Long taskId);\n\n    /**\n     * 获取 OTA 升级记录\n     *\n     * @param id 编号\n     * @return OTA 升级记录\n     */\n    IotOtaTaskRecordDO getOtaTaskRecord(Long id);\n\n    /**\n     * 获取 OTA 升级记录分页\n     *\n     * @param pageReqVO 分页查询\n     * @return OTA 升级记录分页\n     */\n    PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(@Valid IotOtaTaskRecordPageReqVO pageReqVO);\n\n    /**\n     * 根据 OTA 任务编号，取消未结束的升级记录\n     *\n     * @param taskId 升级任务编号\n     */\n    void cancelTaskRecordListByTaskId(Long taskId);\n\n    /**\n     * 根据设备编号和记录状态，获取 OTA 升级记录列表\n     *\n     * @param deviceIds 设备编号集合\n     * @param statuses  记录状态集合\n     * @return OTA 升级记录列表\n     */\n    List<IotOtaTaskRecordDO> getOtaTaskRecordListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses);\n\n    /**\n     * 根据记录状态，获取 OTA 升级记录列表\n     *\n     * @param status 升级记录状态\n     * @return 升级记录列表\n     */\n    List<IotOtaTaskRecordDO> getOtaRecordListByStatus(Integer status);\n\n    /**\n     * 取消 OTA 升级记录\n     *\n     * @param id 记录编号\n     */\n    void cancelOtaTaskRecord(Long id);\n\n    /**\n     * 推送 OTA 升级任务记录\n     *\n     * @param record   任务记录\n     * @param fireware 固件信息\n     * @param device   设备信息\n     * @return 是否推送成功\n     */\n    boolean pushOtaTaskRecord(IotOtaTaskRecordDO record, IotOtaFirmwareDO fireware, IotDeviceDO device);\n\n    /**\n     * 更新 OTA 升级记录进度\n     *\n     * @param device   设备信息\n     * @param message  设备消息\n     */\n    void updateOtaRecordProgress(IotDeviceDO device, IotDeviceMessage message);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.ota;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.topic.ota.IotDeviceOtaProgressReqDTO;\nimport cn.iocoder.yudao.module.iot.core.topic.ota.IotDeviceOtaUpgradeReqDTO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskRecordMapper;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;\n\n/**\n * OTA 升级任务记录 Service 实现类\n */\n@Service\n@Validated\n@Slf4j\npublic class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService {\n\n    @Resource\n    private IotOtaTaskRecordMapper otaTaskRecordMapper;\n\n    @Resource\n    private IotOtaFirmwareService otaFirmwareService;\n    @Resource\n    private IotOtaTaskService otaTaskService;\n    @Resource\n    private IotDeviceMessageService deviceMessageService;\n    @Resource\n    private IotDeviceService deviceService;\n\n    @Override\n    public void createOtaTaskRecordList(List<IotDeviceDO> devices, Long firmwareId, Long taskId) {\n        List<IotOtaTaskRecordDO> records = convertList(devices, device ->\n                IotOtaTaskRecordDO.builder().firmwareId(firmwareId).taskId(taskId)\n                        .deviceId(device.getId()).fromFirmwareId(Convert.toLong(device.getFirmwareId()))\n                        .status(IotOtaTaskRecordStatusEnum.PENDING.getStatus()).progress(0).build());\n        otaTaskRecordMapper.insertBatch(records);\n    }\n\n    @Override\n    public Map<Integer, Long> getOtaTaskRecordStatusStatistics(Long firmwareId, Long taskId) {\n        // 按照 status 枚举，初始化 countMap 为 0\n        Map<Integer, Long> countMap = convertMap(Arrays.asList(IotOtaTaskRecordStatusEnum.values()),\n                IotOtaTaskRecordStatusEnum::getStatus, iotOtaTaskRecordStatusEnum -> 0L);\n\n        // 查询记录，只返回 id、status 字段\n        List<IotOtaTaskRecordDO> records = otaTaskRecordMapper.selectListByFirmwareIdAndTaskId(firmwareId, taskId);\n        Map<Long, List<Integer>> deviceStatusesMap = convertMultiMap(records,\n                IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus);\n        // 找到第一个匹配的优先级状态，避免重复计算\n        deviceStatusesMap.forEach((deviceId, statuses) -> {\n            for (Integer priorityStatus : IotOtaTaskRecordStatusEnum.PRIORITY_STATUSES) {\n                if (statuses.contains(priorityStatus)) {\n                    countMap.put(priorityStatus, countMap.get(priorityStatus) + 1);\n                    return;\n                }\n            }\n        });\n        return countMap;\n    }\n\n    @Override\n    public IotOtaTaskRecordDO getOtaTaskRecord(Long id) {\n        return otaTaskRecordMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<IotOtaTaskRecordDO> getOtaTaskRecordPage(IotOtaTaskRecordPageReqVO pageReqVO) {\n        return otaTaskRecordMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public void cancelTaskRecordListByTaskId(Long taskId) {\n        List<IotOtaTaskRecordDO> records = otaTaskRecordMapper.selectListByTaskIdAndStatus(\n                taskId, IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);\n        if (CollUtil.isEmpty(records)) {\n            return;\n        }\n        // 批量更新\n        Collection<Long> ids = convertSet(records, IotOtaTaskRecordDO::getId);\n        otaTaskRecordMapper.updateListByIdAndStatus(ids, IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES,\n                IotOtaTaskRecordDO.builder().status(IotOtaTaskRecordStatusEnum.CANCELED.getStatus())\n                        .description(IotOtaTaskRecordDO.DESCRIPTION_CANCEL_BY_TASK).build());\n    }\n\n    @Override\n    public List<IotOtaTaskRecordDO> getOtaTaskRecordListByDeviceIdAndStatus(Set<Long> deviceIds, Set<Integer> statuses) {\n        return otaTaskRecordMapper.selectListByDeviceIdAndStatus(deviceIds, statuses);\n    }\n\n    @Override\n    public List<IotOtaTaskRecordDO> getOtaRecordListByStatus(Integer status) {\n        return otaTaskRecordMapper.selectListByStatus(status);\n    }\n\n    @Override\n    public void cancelOtaTaskRecord(Long id) {\n        // 1. 校验记录是否存在\n        IotOtaTaskRecordDO record = validateUpgradeRecordExists(id);\n\n        // 2. 更新记录状态为取消\n        int updateCount = otaTaskRecordMapper.updateByIdAndStatus(record.getId(), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES,\n                IotOtaTaskRecordDO.builder().id(id).status(IotOtaTaskRecordStatusEnum.CANCELED.getStatus())\n                .description(IotOtaTaskRecordDO.DESCRIPTION_CANCEL_BY_RECORD).build());\n        if (updateCount == 0) {\n            throw exception(OTA_TASK_RECORD_CANCEL_FAIL_STATUS_ERROR);\n        }\n\n        // 3. 检查并更新任务状态\n        checkAndUpdateOtaTaskStatus(record.getTaskId());\n    }\n\n    @Override\n    public boolean pushOtaTaskRecord(IotOtaTaskRecordDO record, IotOtaFirmwareDO fireware, IotDeviceDO device) {\n        try {\n            // 1. 推送 OTA 任务记录\n            IotDeviceOtaUpgradeReqDTO params = BeanUtils.toBean(fireware, IotDeviceOtaUpgradeReqDTO.class);\n            IotDeviceMessage message = IotDeviceMessage.requestOf(\n                    IotDeviceMessageMethodEnum.OTA_UPGRADE.getMethod(), params);\n            deviceMessageService.sendDeviceMessage(message, device);\n\n            // 2. 更新 OTA 升级记录状态为进行中\n            int updateCount = otaTaskRecordMapper.updateByIdAndStatus(\n                    record.getId(), IotOtaTaskRecordStatusEnum.PENDING.getStatus(),\n                    IotOtaTaskRecordDO.builder().status(IotOtaTaskRecordStatusEnum.PUSHED.getStatus())\n                            .description(StrUtil.format(\"已推送，设备消息编号({})\", message.getId())).build());\n            Assert.isTrue(updateCount == 1, \"更新设备记录({})状态失败\", record.getId());\n            return true;\n        } catch (Exception ex) {\n            log.error(\"[pushOtaTaskRecord][推送 OTA 任务记录({}) 失败]\", record.getId(), ex);\n            otaTaskRecordMapper.updateById(IotOtaTaskRecordDO.builder().id(record.getId())\n                    .description(StrUtil.format(\"推送失败，错误信息({})\", ex.getMessage())).build());\n            return false;\n        }\n    }\n\n    private IotOtaTaskRecordDO validateUpgradeRecordExists(Long id) {\n        IotOtaTaskRecordDO upgradeRecord = otaTaskRecordMapper.selectById(id);\n        if (upgradeRecord == null) {\n            throw exception(OTA_TASK_RECORD_NOT_EXISTS);\n        }\n        return upgradeRecord;\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateOtaRecordProgress(IotDeviceDO device, IotDeviceMessage message) {\n        // 1.1 参数解析\n        IotDeviceOtaProgressReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceOtaProgressReqDTO.class);\n        String version = params.getVersion();\n        Assert.notBlank(version, \"version 不能为空\");\n        Integer status = params.getStatus();\n        Assert.notNull(status, \"status 不能为空\");\n        Assert.notNull(IotOtaTaskRecordStatusEnum.of(status), \"status 状态不正确\");\n        String description = params.getDescription();\n        Integer progress = params.getProgress();\n        Assert.notNull(progress, \"progress 不能为空\");\n        Assert.isTrue(progress >= 0 && progress <= 100, \"progress 必须在 0-100 之间\");\n        // 1.2 查询 OTA 升级记录\n        List<IotOtaTaskRecordDO> records = otaTaskRecordMapper.selectListByDeviceIdAndStatus(\n                device.getId(), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);\n        if (CollUtil.isEmpty(records)) {\n            throw exception(OTA_TASK_RECORD_UPDATE_PROGRESS_FAIL_NO_EXISTS);\n        }\n        if (records.size() > 1) {\n            log.warn(\"[updateOtaRecordProgress][message({}) 对应升级记录过多({})]\", message, records);\n        }\n        IotOtaTaskRecordDO record = CollUtil.getFirst(records);\n        // 1.3 查询 OTA 固件\n        IotOtaFirmwareDO firmware = otaFirmwareService.getOtaFirmwareByProductIdAndVersion(\n                device.getProductId(), version);\n        if (firmware == null) {\n            throw exception(OTA_FIRMWARE_NOT_EXISTS);\n        }\n\n        // 2. 更新 OTA 升级记录状态\n        int updateCount = otaTaskRecordMapper.updateByIdAndStatus(\n                record.getId(), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES,\n                IotOtaTaskRecordDO.builder().status(status).description(description).progress(progress).build());\n        if (updateCount == 0) {\n            throw exception(OTA_TASK_RECORD_UPDATE_PROGRESS_FAIL_NO_EXISTS);\n        }\n\n        // 3. 如果升级成功，则更新设备固件版本\n        if (IotOtaTaskRecordStatusEnum.SUCCESS.getStatus().equals(status)) {\n            deviceService.updateDeviceFirmware(device.getId(), firmware.getId());\n        }\n\n        // 4. 如果状态是“已结束”（非进行中），则更新任务状态\n        if (!IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES.contains(status)) {\n            checkAndUpdateOtaTaskStatus(record.getTaskId());\n        }\n    }\n\n    /**\n     * 检查并更新任务状态\n     * 如果任务下没有进行中的记录，则将任务状态更新为已结束\n     */\n    private void checkAndUpdateOtaTaskStatus(Long taskId) {\n        // 如果还有进行中的记录，直接返回\n        Long inProcessCount = otaTaskRecordMapper.selectCountByTaskIdAndStatus(\n                taskId, IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);\n        if (inProcessCount > 0) {\n            return;\n        }\n\n        // 没有进行中的记录，将任务状态更新为已结束\n        otaTaskService.updateOtaTaskStatusEnd(taskId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.ota;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;\n\nimport javax.validation.Valid;\n\n/**\n * IoT OTA 升级任务 Service 接口\n *\n * @author Shelly Chan\n */\npublic interface IotOtaTaskService {\n\n    /**\n     * 创建 OTA 升级任务\n     *\n     * @param createReqVO 创建请求对象\n     * @return 升级任务编号\n     */\n    Long createOtaTask(@Valid IotOtaTaskCreateReqVO createReqVO);\n\n    /**\n     * 取消 OTA 升级任务\n     *\n     * @param id 升级任务编号\n     */\n    void cancelOtaTask(Long id);\n\n    /**\n     * 获取 OTA 升级任务\n     *\n     * @param id 升级任务编号\n     * @return 升级任务\n     */\n    IotOtaTaskDO getOtaTask(Long id);\n\n    /**\n     * 分页查询 OTA 升级任务\n     *\n     * @param pageReqVO 分页查询请求\n     * @return 升级任务分页结果\n     */\n    PageResult<IotOtaTaskDO> getOtaTaskPage(@Valid IotOtaTaskPageReqVO pageReqVO);\n\n    /**\n     * 更新 OTA 任务状态为已结束\n     *\n     * @param id 任务编号\n     */\n    void updateOtaTaskStatusEnd(Long id);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.ota;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskMapper;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum;\nimport cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskStatusEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;\n\n/**\n * IoT OTA 升级任务 Service 实现类\n *\n * @author Shelly Chan\n */\n@Service\n@Validated\n@Slf4j\npublic class IotOtaTaskServiceImpl implements IotOtaTaskService {\n\n    @Resource\n    private IotOtaTaskMapper otaTaskMapper;\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotOtaFirmwareService otaFirmwareService;\n    @Resource\n    @Lazy // 延迟，避免循环依赖报错\n    private IotOtaTaskRecordService otaTaskRecordService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createOtaTask(IotOtaTaskCreateReqVO createReqVO) {\n        // 1.1 校验固件信息是否存在\n        IotOtaFirmwareDO firmware = otaFirmwareService.validateFirmwareExists(createReqVO.getFirmwareId());\n        // 1.2 校验同一固件的升级任务名称不重复\n        if (otaTaskMapper.selectByFirmwareIdAndName(firmware.getId(), createReqVO.getName()) != null) {\n            throw exception(OTA_TASK_CREATE_FAIL_NAME_DUPLICATE);\n        }\n        // 1.3 校验设备范围信息\n        List<IotDeviceDO> devices = validateOtaTaskDeviceScope(createReqVO, firmware.getProductId());\n\n        // 2. 保存升级任务，直接转换\n        IotOtaTaskDO task = BeanUtils.toBean(createReqVO, IotOtaTaskDO.class)\n                .setStatus(IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())\n                .setDeviceTotalCount(devices.size()).setDeviceSuccessCount(0);\n        otaTaskMapper.insert(task);\n\n        // 3. 生成设备升级记录\n        otaTaskRecordService.createOtaTaskRecordList(devices, firmware.getId(), task.getId());\n        return task.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void cancelOtaTask(Long id) {\n        // 1.1 校验升级任务是否存在\n        IotOtaTaskDO upgradeTask = validateUpgradeTaskExists(id);\n        // 1.2 校验升级任务是否可以取消\n        if (ObjUtil.notEqual(upgradeTask.getStatus(), IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())) {\n            throw exception(OTA_TASK_CANCEL_FAIL_STATUS_END);\n        }\n\n        // 2. 更新升级任务状态为已取消\n        otaTaskMapper.updateById(IotOtaTaskDO.builder()\n                .id(id).status(IotOtaTaskStatusEnum.CANCELED.getStatus())\n                .build());\n\n        // 3. 更新升级记录状态为已取消\n        otaTaskRecordService.cancelTaskRecordListByTaskId(id);\n    }\n\n    @Override\n    public IotOtaTaskDO getOtaTask(Long id) {\n        return otaTaskMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<IotOtaTaskDO> getOtaTaskPage(IotOtaTaskPageReqVO pageReqVO) {\n        return otaTaskMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public void updateOtaTaskStatusEnd(Long taskId) {\n        int updateCount = otaTaskMapper.updateByIdAndStatus(taskId, IotOtaTaskStatusEnum.IN_PROGRESS.getStatus(),\n                new IotOtaTaskDO().setStatus(IotOtaTaskStatusEnum.END.getStatus()));\n        if (updateCount == 0) {\n            log.warn(\"[updateOtaTaskStatusEnd][任务({})不存在或状态不是进行中，无法更新]\", taskId);\n        }\n    }\n\n    private List<IotDeviceDO> validateOtaTaskDeviceScope(IotOtaTaskCreateReqVO createReqVO, Long productId) {\n        // 情况一：选择设备\n        if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.SELECT.getScope())) {\n            // 1.1 校验设备存在\n            List<IotDeviceDO> devices = deviceService.validateDeviceListExists(createReqVO.getDeviceIds());\n            for (IotDeviceDO device : devices) {\n                if (ObjUtil.notEqual(device.getProductId(), productId)) {\n                    throw exception(DEVICE_NOT_EXISTS);\n                }\n            }\n            // 1.2 校验设备是否已经是该固件版本\n            devices.forEach(device -> {\n                if (Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId())) {\n                    throw exception(OTA_TASK_CREATE_FAIL_DEVICE_FIRMWARE_EXISTS, device.getDeviceName());\n                }\n            });\n            // 1.3 校验设备是否已经在升级中\n            List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(\n                    convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);\n            devices.forEach(device -> {\n                if (CollUtil.contains(records, item -> item.getDeviceId().equals(device.getId()))) {\n                    throw exception(OTA_TASK_CREATE_FAIL_DEVICE_OTA_IN_PROCESS, device.getDeviceName());\n                }\n            });\n            return devices;\n        }\n        // 情况二：全部设备\n        if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.ALL.getScope())) {\n            List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(productId);\n            // 2.1.1 移除已经是该固件版本的设备\n            devices.removeIf(device -> Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId()));\n            // 2.1.2 移除已经在升级中的设备\n            List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(\n                    convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);\n            devices.removeIf(device -> CollUtil.contains(records,\n                    item -> item.getDeviceId().equals(device.getId())));\n            // 2.2 校验是否有可升级的设备\n            if (CollUtil.isEmpty(devices)) {\n                throw exception(OTA_TASK_CREATE_FAIL_DEVICE_EMPTY);\n            }\n            return devices;\n        }\n        throw new IllegalArgumentException(\"不支持的设备范围：\" + createReqVO.getDeviceScope());\n    }\n\n    private IotOtaTaskDO validateUpgradeTaskExists(Long id) {\n        IotOtaTaskDO upgradeTask = otaTaskMapper.selectById(id);\n        if (Objects.isNull(upgradeTask)) {\n            throw exception(OTA_TASK_NOT_EXISTS);\n        }\n        return upgradeTask;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;\n\nimport javax.annotation.Nullable;\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * IoT 产品分类 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotProductCategoryService {\n\n    /**\n     * 创建产品分类\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProductCategory(@Valid IotProductCategorySaveReqVO createReqVO);\n\n    /**\n     * 更新产品分类\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProductCategory(@Valid IotProductCategorySaveReqVO updateReqVO);\n\n    /**\n     * 删除产品分类\n     *\n     * @param id 编号\n     */\n    void deleteProductCategory(Long id);\n\n    /**\n     * 获得产品分类\n     *\n     * @param id 编号\n     * @return 产品分类\n     */\n    IotProductCategoryDO getProductCategory(Long id);\n\n    /**\n     * 获得产品分类列表\n     *\n     * @param ids 编号\n     * @return 产品分类列表\n     */\n    List<IotProductCategoryDO> getProductCategoryList(Collection<Long> ids);\n\n    /**\n     * 获得产品分类 Map\n     *\n     * @param ids 编号\n     * @return 产品分类 Map\n     */\n    default Map<Long, IotProductCategoryDO> getProductCategoryMap(Collection<Long> ids) {\n        return convertMap(getProductCategoryList(ids), IotProductCategoryDO::getId);\n    }\n\n    /**\n     * 获得产品分类分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 产品分类分页\n     */\n    PageResult<IotProductCategoryDO> getProductCategoryPage(IotProductCategoryPageReqVO pageReqVO);\n\n    /**\n     * 获得产品分类列表，根据状态\n     *\n     * @param status 状态\n     * @return 产品分类列表\n     */\n    List<IotProductCategoryDO> getProductCategoryListByStatus(Integer status);\n\n    /**\n     * 获得产品分类数量\n     *\n     * @param createTime 创建时间，如果为空，则统计所有分类数量\n     * @return 产品分类数量\n     */\n    Long getProductCategoryCount(@Nullable LocalDateTime createTime);\n\n    /**\n     * 获得各个品类下设备数量统计，其中 key 是产品分类名\n     *\n     * @return 品类设备统计列表\n     */\n    Map<String, Integer> getProductCategoryDeviceCountMap();\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.product;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductCategoryMapper;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS;\n\n/**\n * IoT 产品分类 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class IotProductCategoryServiceImpl implements IotProductCategoryService {\n\n    @Resource\n    private IotProductCategoryMapper iotProductCategoryMapper;\n\n    @Resource\n    private IotProductService productService;\n\n    @Resource\n    private IotDeviceService deviceService;\n\n    public Long createProductCategory(IotProductCategorySaveReqVO createReqVO) {\n        // 插入\n        IotProductCategoryDO productCategory = BeanUtils.toBean(createReqVO, IotProductCategoryDO.class);\n        iotProductCategoryMapper.insert(productCategory);\n        // 返回\n        return productCategory.getId();\n    }\n\n    @Override\n    public void updateProductCategory(IotProductCategorySaveReqVO updateReqVO) {\n        // 校验存在\n        validateProductCategoryExists(updateReqVO.getId());\n        // 更新\n        IotProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, IotProductCategoryDO.class);\n        iotProductCategoryMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteProductCategory(Long id) {\n        // 校验存在\n        validateProductCategoryExists(id);\n        // 删除\n        iotProductCategoryMapper.deleteById(id);\n    }\n\n    private void validateProductCategoryExists(Long id) {\n        if (iotProductCategoryMapper.selectById(id) == null) {\n            throw exception(PRODUCT_CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public IotProductCategoryDO getProductCategory(Long id) {\n        return iotProductCategoryMapper.selectById(id);\n    }\n\n    @Override\n    public List<IotProductCategoryDO> getProductCategoryList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return CollUtil.newArrayList();\n        }\n        return iotProductCategoryMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<IotProductCategoryDO> getProductCategoryPage(IotProductCategoryPageReqVO pageReqVO) {\n        return iotProductCategoryMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<IotProductCategoryDO> getProductCategoryListByStatus(Integer status) {\n        return iotProductCategoryMapper.selectListByStatus(status);\n    }\n\n    @Override\n    public Long getProductCategoryCount(LocalDateTime createTime) {\n        return iotProductCategoryMapper.selectCountByCreateTime(createTime);\n    }\n\n    @Override\n    public Map<String, Integer> getProductCategoryDeviceCountMap() {\n        // 1. 获取所有数据\n        List<IotProductCategoryDO> categories = iotProductCategoryMapper.selectList();\n        List<IotProductDO> products = productService.getProductList();\n        Map<Long, Integer> deviceCountMapByProductId = deviceService.getDeviceCountMapByProductId();\n\n        // 2. 统计每个分类下的设备数量\n        Map<String, Integer> categoryDeviceCountMap = new HashMap<>();\n        for (IotProductCategoryDO category : categories) {\n            // 2.1 找到该分类下的所有产品\n            List<IotProductDO> categoryProducts = filterList(products, \n                product -> Objects.equals(product.getCategoryId(), category.getId()));\n            // 2.2 累加设备数量\n            Integer totalDeviceCount = getSumValue(categoryProducts, \n                product -> deviceCountMapByProductId.getOrDefault(product.getId(), 0), \n                Integer::sum, 0);\n            categoryDeviceCountMap.put(category.getName(), totalDeviceCount);\n        }\n        return categoryDeviceCountMap;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\n\nimport javax.annotation.Nullable;\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * IoT 产品 Service 接口\n *\n * @author ahh\n */\npublic interface IotProductService {\n\n    /**\n     * 创建产品\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProduct(@Valid IotProductSaveReqVO createReqVO);\n\n    /**\n     * 更新产品\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProduct(@Valid IotProductSaveReqVO updateReqVO);\n\n    /**\n     * 删除产品\n     *\n     * @param id 编号\n     */\n    void deleteProduct(Long id);\n\n    /**\n     * 获得产品\n     *\n     * @param id 编号\n     * @return 产品\n     */\n    IotProductDO getProduct(Long id);\n\n    /**\n     * 【缓存】获得产品\n     * <p>\n     * 注意：该方法会忽略租户信息，所以调用时，需要确认会不会有跨租户访问的风险！！！\n     *\n     * @param id 编号\n     * @return 产品\n     */\n    IotProductDO getProductFromCache(Long id);\n\n    /**\n     * 根据产品 key 获得产品\n     *\n     * @param productKey 产品 key\n     * @return 产品\n     */\n    IotProductDO getProductByProductKey(String productKey);\n\n    /**\n     * 校验产品存在\n     *\n     * @param id 编号\n     * @return 产品\n     */\n    IotProductDO validateProductExists(Long id);\n\n    /**\n     * 校验产品存在\n     *\n     * @param productKey 产品 key\n     * @return 产品\n     */\n    IotProductDO validateProductExists(String productKey);\n\n    /**\n     * 获得产品分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 产品分页\n     */\n    PageResult<IotProductDO> getProductPage(IotProductPageReqVO pageReqVO);\n\n    /**\n     * 更新产品状态\n     *\n     * @param id 编号\n     * @param status 状态\n     */\n    void updateProductStatus(Long id, Integer status);\n\n    /**\n     * 获得所有产品\n     *\n     * @return 产品列表\n     */\n    List<IotProductDO> getProductList();\n\n    /**\n     * 根据设备类型获得产品列表\n     *\n     * @param deviceType 设备类型（可选）\n     * @return 产品列表\n     */\n    List<IotProductDO> getProductList(@Nullable Integer deviceType);\n\n    /**\n     * 获得产品数量\n     *\n     * @param createTime 创建时间，如果为空，则统计所有产品数量\n     * @return 产品数量\n     */\n    Long getProductCount(@Nullable LocalDateTime createTime);\n\n    /**\n     * 批量获得产品列表\n     *\n     * @param ids 产品编号集合\n     * @return 产品列表\n     */\n    List<IotProductDO> getProductList(Collection<Long> ids);\n\n    /**\n     * 批量获得产品 Map\n     *\n     * @param ids 产品编号集合\n     * @return 产品 Map（key: 产品编号, value: 产品）\n     */\n    default Map<Long, IotProductDO> getProductMap(Collection<Long> ids) {\n        return convertMap(getProductList(ids), IotProductDO::getId);\n    }\n\n    /**\n     * 批量校验产品存在\n     *\n     * @param ids 产品编号集合\n     */\n    void validateProductsExist(Collection<Long> ids);\n\n    /**\n     * 同步产品的 TDengine 表结构\n     *\n     * 目的：当 MySQL 和 TDengine 不同步时，强制将已发布产品的表结构同步到 TDengine 中\n     */\n    void syncProductPropertyTable();\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.product;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper;\nimport cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;\nimport cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;\nimport com.baomidou.dynamic.datasource.annotation.DSTransactional;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;\n\n/**\n * IoT 产品 Service 实现类\n *\n * @author ahh\n */\n@Slf4j\n@Service\n@Validated\npublic class IotProductServiceImpl implements IotProductService {\n\n    @Resource\n    private IotProductMapper productMapper;\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private IotDevicePropertyService devicePropertyDataService;\n\n    @Override\n    public Long createProduct(IotProductSaveReqVO createReqVO) {\n        // 1. 校验 ProductKey\n        if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) {\n            throw exception(PRODUCT_KEY_EXISTS);\n        }\n\n        // 2. 插入\n        IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class)\n                .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus())\n                .setProductSecret(generateProductSecret());\n        productMapper.insert(product);\n        return product.getId();\n    }\n\n    private String generateProductSecret() {\n        return IdUtil.fastSimpleUUID();\n    }\n\n    @Override\n    @CacheEvict(value = RedisKeyConstants.PRODUCT, key = \"#updateReqVO.id\")\n    public void updateProduct(IotProductSaveReqVO updateReqVO) {\n        updateReqVO.setProductKey(null); // 不更新产品标识\n        // 1. 校验存在\n        validateProductExists(updateReqVO.getId());\n\n        // 2. 更新\n        IotProductDO updateObj = BeanUtils.toBean(updateReqVO, IotProductDO.class);\n        productMapper.updateById(updateObj);\n    }\n\n    @Override\n    @CacheEvict(value = RedisKeyConstants.PRODUCT, key = \"#id\")\n    public void deleteProduct(Long id) {\n        // 1.1 校验存在\n        IotProductDO product = validateProductExists(id);\n        // 1.2 发布状态不可删除\n        validateProductStatus(product);\n        // 1.3 校验是否有设备\n        if (deviceService.getDeviceCountByProductId(id) > 0) {\n            throw exception(PRODUCT_DELETE_FAIL_HAS_DEVICE);\n        }\n\n        // 2. 删除\n        productMapper.deleteById(id);\n    }\n\n    @Override\n    public IotProductDO validateProductExists(Long id) {\n        IotProductDO product = productMapper.selectById(id);\n        if (product == null) {\n            throw exception(PRODUCT_NOT_EXISTS);\n        }\n        return product;\n    }\n\n    @Override\n    public IotProductDO validateProductExists(String productKey) {\n        IotProductDO product = productMapper.selectByProductKey(productKey);\n        if (product == null) {\n            throw exception(PRODUCT_NOT_EXISTS);\n        }\n        return product;\n    }\n\n    private void validateProductStatus(IotProductDO product) {\n        if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) {\n            throw exception(PRODUCT_STATUS_NOT_DELETE);\n        }\n    }\n\n    @Override\n    public IotProductDO getProduct(Long id) {\n        return productMapper.selectById(id);\n    }\n\n    @Override\n    @Cacheable(value = RedisKeyConstants.PRODUCT, key = \"#id\", unless = \"#result == null\")\n    @TenantIgnore // 忽略租户信息\n    public IotProductDO getProductFromCache(Long id) {\n        return productMapper.selectById(id);\n    }\n\n    @Override\n    public IotProductDO getProductByProductKey(String productKey) {\n        return productMapper.selectByProductKey(productKey);\n    }\n\n    @Override\n    public PageResult<IotProductDO> getProductPage(IotProductPageReqVO pageReqVO) {\n        return productMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    @DSTransactional(rollbackFor = Exception.class)\n    @CacheEvict(value = RedisKeyConstants.PRODUCT, key = \"#id\")\n    public void updateProductStatus(Long id, Integer status) {\n        // 1. 校验存在\n        validateProductExists(id);\n\n        // 2. 更新为发布状态，需要创建产品超级表数据模型\n        // TODO @芋艿：【待定 001】1）是否需要操作后，在 redis 进行缓存，实现一个“快照”的情况，类似 tl；\n        if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) {\n            devicePropertyDataService.defineDevicePropertyData(id);\n        }\n\n        // 3. 更新\n        IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build();\n        productMapper.updateById(updateObj);\n    }\n\n    @Override\n    public List<IotProductDO> getProductList() {\n        return productMapper.selectList();\n    }\n\n    @Override\n    public List<IotProductDO> getProductList(Integer deviceType) {\n        return productMapper.selectList(deviceType);\n    }\n\n    @Override\n    public Long getProductCount(LocalDateTime createTime) {\n        return productMapper.selectCountByCreateTime(createTime);\n    }\n\n    @Override\n    public List<IotProductDO> getProductList(Collection<Long> ids) {\n        return productMapper.selectByIds(ids);\n    }\n\n    @Override\n    public void syncProductPropertyTable() {\n        // 1. 获取所有已发布的产品\n        List<IotProductDO> products = productMapper.selectListByStatus(\n                IotProductStatusEnum.PUBLISHED.getStatus());\n        log.info(\"[syncProductPropertyTable][开始同步，已发布产品数量({})]\", products.size());\n\n        // 2. 遍历同步 TDengine 表结构（创建产品超级表数据模型）\n        int successCount = 0;\n        for (IotProductDO product : products) {\n            try {\n                devicePropertyDataService.defineDevicePropertyData(product.getId());\n                successCount++;\n                log.info(\"[syncProductPropertyTable][产品({}/{}) 同步成功]\", product.getId(), product.getName());\n            } catch (Exception e) {\n                log.error(\"[syncProductPropertyTable][产品({}/{}) 同步失败]\", product.getId(), product.getName(), e);\n            }\n        }\n        log.info(\"[syncProductPropertyTable][同步完成，成功({}/{})个]\", successCount, products.size());\n    }\n\n    @Override\n    public void validateProductsExist(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return;\n        }\n        List<IotProductDO> products = productMapper.selectByIds(ids);\n        if (products.size() != ids.size()) {\n            throw exception(PRODUCT_NOT_EXISTS);\n        }\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleSaveReqVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * IoT 数据流转规则 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotDataRuleService {\n\n    /**\n     * 创建数据流转规则\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDataRule(@Valid IotDataRuleSaveReqVO createReqVO);\n\n    /**\n     * 更新数据流转规则\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDataRule(@Valid IotDataRuleSaveReqVO updateReqVO);\n\n    /**\n     * 删除数据流转规则\n     *\n     * @param id 编号\n     */\n    void deleteDataRule(Long id);\n\n    /**\n     * 获得数据流转规则\n     *\n     * @param id 编号\n     * @return 数据流转规则\n     */\n    IotDataRuleDO getDataRule(Long id);\n\n    /**\n     * 获得数据流转规则分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 数据流转规则分页\n     */\n    PageResult<IotDataRuleDO> getDataRulePage(IotDataRulePageReqVO pageReqVO);\n\n    /**\n     * 根据数据目的编号，获得数据流转规则列表\n     *\n     * @param sinkId 数据目的编号\n     * @return 是否被使用\n     */\n    List<IotDataRuleDO> getDataRuleListBySinkId(Long sinkId);\n\n    /**\n     * 执行数据流转规则\n     *\n     * @param message 消息\n     */\n    void executeDataRule(IotDeviceMessage message);\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleSaveReqVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataRuleMapper;\nimport cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport cn.iocoder.yudao.module.iot.service.rule.data.action.IotDataRuleAction;\nimport cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_RULE_NAME_EXISTS;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_RULE_NOT_EXISTS;\n\n/**\n * IoT 数据流转规则 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class IotDataRuleServiceImpl implements IotDataRuleService {\n\n    @Resource\n    private IotDataRuleMapper dataRuleMapper;\n\n    @Resource\n    private IotProductService productService;\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotThingModelService thingModelService;\n    @Resource\n    private IotDataSinkService dataSinkService;\n\n    @Resource\n    private List<IotDataRuleAction> dataRuleActions;\n\n    @Override\n    @CacheEvict(value = RedisKeyConstants.DATA_RULE_LIST, allEntries = true)\n    public Long createDataRule(IotDataRuleSaveReqVO createReqVO) {\n        // 校验名称唯一\n        validateDataRuleNameUnique(null, createReqVO.getName());\n        // 校验数据源配置和数据目的\n        validateDataRuleConfig(createReqVO);\n        // 新增\n        IotDataRuleDO dataRule = BeanUtils.toBean(createReqVO, IotDataRuleDO.class);\n        dataRuleMapper.insert(dataRule);\n        return dataRule.getId();\n    }\n\n    @Override\n    @CacheEvict(value = RedisKeyConstants.DATA_RULE_LIST, allEntries = true)\n    public void updateDataRule(IotDataRuleSaveReqVO updateReqVO) {\n        // 校验存在\n        validateDataRuleExists(updateReqVO.getId());\n        // 校验名称唯一\n        validateDataRuleNameUnique(updateReqVO.getId(), updateReqVO.getName());\n        // 校验数据源配置和数据目的\n        validateDataRuleConfig(updateReqVO);\n\n        // 更新\n        IotDataRuleDO updateObj = BeanUtils.toBean(updateReqVO, IotDataRuleDO.class);\n        dataRuleMapper.updateById(updateObj);\n    }\n\n    @Override\n    @CacheEvict(value = RedisKeyConstants.DATA_RULE_LIST, allEntries = true)\n    public void deleteDataRule(Long id) {\n        // 校验存在\n        validateDataRuleExists(id);\n        // 删除\n        dataRuleMapper.deleteById(id);\n    }\n\n    private void validateDataRuleExists(Long id) {\n        if (dataRuleMapper.selectById(id) == null) {\n            throw exception(DATA_RULE_NOT_EXISTS);\n        }\n    }\n\n    /**\n     * 校验数据流转规则名称唯一性\n     *\n     * @param id   数据流转规则编号（用于更新时排除自身）\n     * @param name 数据流转规则名称\n     */\n    private void validateDataRuleNameUnique(Long id, String name) {\n        if (StrUtil.isBlank(name)) {\n            return;\n        }\n        IotDataRuleDO dataRule = dataRuleMapper.selectByName(name);\n        if (dataRule == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的规则\n        if (id == null) {\n            throw exception(DATA_RULE_NAME_EXISTS);\n        }\n        if (!dataRule.getId().equals(id)) {\n            throw exception(DATA_RULE_NAME_EXISTS);\n        }\n    }\n\n    /**\n     * 校验数据流转规则配置\n     *\n     * @param reqVO 数据流转规则保存请求VO\n     */\n    private void validateDataRuleConfig(IotDataRuleSaveReqVO reqVO) {\n        // 1. 校验数据源配置\n        validateSourceConfigs(reqVO.getSourceConfigs());\n        // 2. 校验数据目的\n        dataSinkService.validateDataSinksExist(reqVO.getSinkIds());\n    }\n\n    /**\n     * 校验数据源配置\n     *\n     * @param sourceConfigs 数据源配置列表\n     */\n    private void validateSourceConfigs(List<IotDataRuleDO.SourceConfig> sourceConfigs) {\n        // 1. 校验产品\n        productService.validateProductsExist(\n                convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getProductId));\n\n        // 2. 校验设备\n        deviceService.validateDeviceListExists(convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getDeviceId,\n                config -> ObjUtil.notEqual(config.getDeviceId(), IotDeviceDO.DEVICE_ID_ALL)));\n\n        // 3. 校验物模型存在\n        validateThingModelsExist(sourceConfigs);\n    }\n\n    /**\n     * 校验物模型存在\n     *\n     * @param sourceConfigs 数据源配置列表\n     */\n    private void validateThingModelsExist(List<IotDataRuleDO.SourceConfig> sourceConfigs) {\n        Map<Long, Set<String>> productIdIdentifiers = new HashMap<>();\n        for (IotDataRuleDO.SourceConfig config : sourceConfigs) {\n            if (StrUtil.isEmpty(config.getIdentifier())) {\n                continue;\n            }\n            productIdIdentifiers.computeIfAbsent(config.getProductId(),\n                            productId -> new HashSet<>()).add(config.getIdentifier());\n        }\n        for (Map.Entry<Long, Set<String>> entry : productIdIdentifiers.entrySet()) {\n            thingModelService.validateThingModelListExists(entry.getKey(), entry.getValue());\n        }\n    }\n\n    @Override\n    public IotDataRuleDO getDataRule(Long id) {\n        return dataRuleMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<IotDataRuleDO> getDataRulePage(IotDataRulePageReqVO pageReqVO) {\n        return dataRuleMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<IotDataRuleDO> getDataRuleListBySinkId(Long sinkId) {\n        return dataRuleMapper.selectListBySinkId(sinkId);\n    }\n\n    @Cacheable(value = RedisKeyConstants.DATA_RULE_LIST,\n               key = \"#deviceId + '_' + #method + '_' + (#identifier ?: '')\")\n    public List<IotDataRuleDO> getDataRuleListByConditionFromCache(Long deviceId, String method, String identifier) {\n        // 1. 查询所有开启的数据流转规则\n        List<IotDataRuleDO> rules = dataRuleMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        // 2. 内存里过滤匹配的规则\n        List<IotDataRuleDO> matchedRules = new ArrayList<>();\n        for (IotDataRuleDO rule : rules) {\n            IotDataRuleDO.SourceConfig found = CollUtil.findOne(rule.getSourceConfigs(),\n                    config -> ObjectUtils.equalsAny(config.getDeviceId(), deviceId, IotDeviceDO.DEVICE_ID_ALL)\n                            && Objects.equals(config.getMethod(), method)\n                            && (StrUtil.isEmpty(config.getIdentifier()) || ObjUtil.equal(config.getIdentifier(), identifier)));\n            if (found != null) {\n                matchedRules.add(new IotDataRuleDO().setId(rule.getId()).setSinkIds(rule.getSinkIds()));\n            }\n        }\n        return matchedRules;\n    }\n\n    @Override\n    public void executeDataRule(IotDeviceMessage message) {\n        try {\n            // 1. 获取匹配的数据流转规则\n            Long deviceId = message.getDeviceId();\n            String method = message.getMethod();\n            String identifier = IotDeviceMessageUtils.getIdentifier(message);\n            List<IotDataRuleDO> rules = getSelf().getDataRuleListByConditionFromCache(deviceId, method, identifier);\n            if (CollUtil.isEmpty(rules)) {\n                log.debug(\"[executeDataRule][设备({}) 方法({}) 标识符({}) 没有匹配的数据流转规则]\",\n                        deviceId, method, identifier);\n                return;\n            }\n            log.info(\"[executeDataRule][设备({}) 方法({}) 标识符({}) 匹配到 {} 条数据流转规则]\",\n                    deviceId, method, identifier, rules.size());\n\n            // 2. 遍历规则，执行数据流转\n            rules.forEach(rule -> executeDataRule(message, rule));\n        } catch (Exception e) {\n            log.error(\"[executeDataRule][消息({}) 执行数据流转规则异常]\", message, e);\n        }\n    }\n\n    /**\n     * 为指定规则的所有数据目的执行数据流转\n     *\n     * @param message 设备消息\n     * @param rule    数据流转规则\n     */\n    private void executeDataRule(IotDeviceMessage message, IotDataRuleDO rule) {\n        rule.getSinkIds().forEach(sinkId -> {\n            try {\n                // 获取数据目的配置\n                IotDataSinkDO dataSink = dataSinkService.getDataSinkFromCache(sinkId);\n                if (dataSink == null) {\n                    log.error(\"[executeDataRule][规则({}) 对应的数据目的({}) 不存在]\", rule.getId(), sinkId);\n                    return;\n                }\n                if (CommonStatusEnum.isDisable(dataSink.getStatus())) {\n                    log.info(\"[executeDataRule][规则({}) 对应的数据目的({}) 状态为禁用]\", rule.getId(), sinkId);\n                    return;\n                }\n\n                // 执行数据桥接操作\n                executeDataRuleAction(message, dataSink);\n            } catch (Exception e) {\n                log.error(\"[executeDataRule][规则({}) 数据目的({}) 执行异常]\", rule.getId(), sinkId, e);\n            }\n        });\n    }\n\n    /**\n     * 执行数据流转操作\n     *\n     * @param message  设备消息\n     * @param dataSink 数据目的\n     */\n    private void executeDataRuleAction(IotDeviceMessage message, IotDataSinkDO dataSink) {\n        dataRuleActions.forEach(action -> {\n            if (ObjUtil.notEqual(action.getType(), dataSink.getType())) {\n                return;\n            }\n            if (CommonStatusEnum.isDisable(dataSink.getStatus())) {\n                log.warn(\"[executeDataRuleAction][消息({}) 数据目的({}) 状态为禁用]\", message.getId(), dataSink.getId());\n                return;\n            }\n            try {\n                action.execute(message, dataSink);\n                log.info(\"[executeDataRuleAction][消息({}) 数据目的({}) 执行成功]\", message.getId(), dataSink.getId());\n            } catch (Exception e) {\n                log.error(\"[executeDataRuleAction][消息({}) 数据目的({}) 执行异常]\", message.getId(), dataSink.getId(), e);\n            }\n        });\n    }\n\n    private IotDataRuleServiceImpl getSelf() {\n        return SpringUtils.getBean(IotDataRuleServiceImpl.class);\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * IoT 数据流转目的 Service 接口\n *\n * @author HUIHUI\n */\npublic interface IotDataSinkService {\n\n    /**\n     * 创建数据流转目的\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDataSink(@Valid IotDataSinkSaveReqVO createReqVO);\n\n    /**\n     * 更新数据流转目的\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDataSink(@Valid IotDataSinkSaveReqVO updateReqVO);\n\n    /**\n     * 删除数据流转目的\n     *\n     * @param id 编号\n     */\n    void deleteDataSink(Long id);\n\n    /**\n     * 获得数据流转目的\n     *\n     * @param id 编号\n     * @return 数据流转目的\n     */\n    IotDataSinkDO getDataSink(Long id);\n\n    /**\n     * 从缓存中获得数据流转目的\n     *\n     * @param id 编号\n     * @return 数据流转目的\n     */\n    IotDataSinkDO getDataSinkFromCache(Long id);\n\n    /**\n     * 获得数据流转目的分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 数据流转目的分页\n     */\n    PageResult<IotDataSinkDO> getDataSinkPage(IotDataSinkPageReqVO pageReqVO);\n\n    /**\n     * 获取数据流转目的列表\n     *\n     * @param status 状态，如果为空，则不进行筛选\n     * @return 数据流转目的列表\n     */\n    List<IotDataSinkDO> getDataSinkListByStatus(Integer status);\n\n    /**\n     * 批量校验数据目的存在\n     *\n     * @param ids 数据目的编号集合\n     */\n    void validateDataSinksExist(Collection<Long> ids);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataSinkMapper;\nimport cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;\n\n/**\n * IoT 数据流转目的 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class IotDataSinkServiceImpl implements IotDataSinkService {\n\n    @Resource\n    private IotDataSinkMapper dataSinkMapper;\n\n    @Resource\n    @Lazy // 延迟，避免循环依赖报错\n    private IotDataRuleService dataRuleService;\n\n    @Override\n    public Long createDataSink(IotDataSinkSaveReqVO createReqVO) {\n        // 校验名称唯一\n        validateDataSinkNameUnique(null, createReqVO.getName());\n        // 新增\n        IotDataSinkDO dataBridge = BeanUtils.toBean(createReqVO, IotDataSinkDO.class);\n        dataSinkMapper.insert(dataBridge);\n        return dataBridge.getId();\n    }\n\n    @Override\n    public void updateDataSink(IotDataSinkSaveReqVO updateReqVO) {\n        // 校验存在\n        validateDataBridgeExists(updateReqVO.getId());\n        // 校验名称唯一\n        validateDataSinkNameUnique(updateReqVO.getId(), updateReqVO.getName());\n        // 更新\n        IotDataSinkDO updateObj = BeanUtils.toBean(updateReqVO, IotDataSinkDO.class);\n        dataSinkMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteDataSink(Long id) {\n        // 校验存在\n        validateDataBridgeExists(id);\n        // 校验是否被数据流转规则使用\n        if (CollUtil.isNotEmpty(dataRuleService.getDataRuleListBySinkId(id))) {\n            throw exception(DATA_SINK_DELETE_FAIL_USED_BY_RULE);\n        }\n        // 删除\n        dataSinkMapper.deleteById(id);\n    }\n\n    private void validateDataBridgeExists(Long id) {\n        if (dataSinkMapper.selectById(id) == null) {\n            throw exception(DATA_SINK_NOT_EXISTS);\n        }\n    }\n\n    /**\n     * 校验数据流转目的名称唯一性\n     *\n     * @param id   数据流转目的编号（用于更新时排除自身）\n     * @param name 数据流转目的名称\n     */\n    private void validateDataSinkNameUnique(Long id, String name) {\n        if (StrUtil.isBlank(name)) {\n            return;\n        }\n        IotDataSinkDO dataSink = dataSinkMapper.selectByName(name);\n        if (dataSink == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的目的\n        if (id == null) {\n            throw exception(DATA_SINK_NAME_EXISTS);\n        }\n        if (!dataSink.getId().equals(id)) {\n            throw exception(DATA_SINK_NAME_EXISTS);\n        }\n    }\n\n    @Override\n    public IotDataSinkDO getDataSink(Long id) {\n        return dataSinkMapper.selectById(id);\n    }\n\n    @Override\n    @Cacheable(value = RedisKeyConstants.DATA_SINK, key = \"#id\")\n    public IotDataSinkDO getDataSinkFromCache(Long id) {\n        return dataSinkMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<IotDataSinkDO> getDataSinkPage(IotDataSinkPageReqVO pageReqVO) {\n        return dataSinkMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<IotDataSinkDO> getDataSinkListByStatus(Integer status) {\n        return dataSinkMapper.selectListByStatus(status);\n    }\n\n    @Override\n    public void validateDataSinksExist(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return;\n        }\n        List<IotDataSinkDO> sinks = dataSinkMapper.selectByIds(ids);\n        if (sinks.size() != ids.size()) {\n            throw exception(DATA_SINK_NOT_EXISTS);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;\n\n/**\n * IoT 数据流转目的的执行器 action 接口\n *\n * @author HUIHUI\n */\npublic interface IotDataRuleAction {\n\n    /**\n     * 获取数据流转目的类型\n     *\n     * @return 数据流转目的类型\n     */\n    Integer getType();\n\n    /**\n     * 执行数据流转目的操作\n     *\n     * @param message    设备消息\n     * @param dataSink 数据流转目的\n     */\n    void execute(IotDeviceMessage message, IotDataSinkDO dataSink);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleCacheableAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport com.google.common.cache.RemovalListener;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.time.Duration;\n\n// TODO @芋艿：数据库\n// TODO @芋艿：mqtt\n\n/**\n * 可缓存的 {@link IotDataRuleAction} 抽象实现\n *\n * 该类提供了一个通用的缓存机制，用于管理各类数据桥接的生产者(Producer)实例。\n *\n * 主要特点:\n * - 基于Guava Cache实现高效的生产者实例缓存管理\n * - 自动处理生产者的生命周期（创建、获取、关闭）\n * - 支持 30 分钟未访问自动过期清理机制\n * - 异常处理与日志记录，便于问题排查\n *\n * 子类需要实现:\n * - initProducer(Config) - 初始化特定类型的生产者实例\n * - closeProducer(Producer) - 关闭生产者实例并释放资源\n *\n * @param <Config>   配置信息类型，用于初始化生产者\n * @param <Producer> 生产者类型，负责将数据发送到目标系统\n * @author HUIHUI\n */\n@Slf4j\npublic abstract class IotDataRuleCacheableAction<Config, Producer> implements IotDataRuleAction {\n\n    /**\n     * Producer 缓存\n     */\n    private final LoadingCache<Config, Producer> PRODUCER_CACHE = CacheBuilder.newBuilder()\n            .expireAfterAccess(Duration.ofMinutes(30)) // 30 分钟未访问就提前过期\n            .removalListener((RemovalListener<Config, Producer>) notification -> {\n                Producer producer = notification.getValue();\n                try {\n                    closeProducer(producer);\n                    log.info(\"[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]\", notification.getKey());\n                } catch (Exception e) {\n                    log.error(\"[PRODUCER_CACHE][配置({}) 对应的 producer 关闭失败]\", notification.getKey(), e);\n                }\n            })\n            .build(new CacheLoader<Config, Producer>() {\n\n                @Override\n                public Producer load(Config config) throws Exception {\n                    try {\n                        Producer producer = initProducer(config);\n                        log.info(\"[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]\", config);\n                        return producer;\n                    } catch (Exception e) {\n                        log.error(\"[PRODUCER_CACHE][配置({}) 对应的 producer 创建启动失败]\", config, e);\n                        throw e; // 抛出异常，触发缓存加载失败机制\n                    }\n                }\n\n            });\n\n    /**\n     * 获取生产者\n     *\n     * @param config 配置信息\n     * @return 生产者对象\n     */\n    protected Producer getProducer(Config config) throws Exception {\n        return PRODUCER_CACHE.get(config);\n    }\n\n    /**\n     * 初始化生产者\n     *\n     * @param config 配置信息\n     * @return 生产者对象\n     * @throws Exception 如果初始化失败\n     */\n    protected abstract Producer initProducer(Config config) throws Exception;\n\n    /**\n     * 关闭生产者\n     *\n     * @param producer 生产者对象\n     */\n    protected abstract void closeProducer(Producer producer) throws Exception;\n\n    @Override\n    @SuppressWarnings({\"unchecked\"})\n    public void execute(IotDeviceMessage message, IotDataSinkDO dataSink) {\n        Assert.isTrue(ObjUtil.equal(dataSink.getType(), getType()), \"类型({})不匹配\", dataSink.getType());\n        try {\n            execute(message, (Config) dataSink.getConfig());\n        } catch (Exception e) {\n            log.error(\"[execute][桥梁配置 config({}) 对应的 message({}) 发送异常]\", dataSink.getConfig(), message, e);\n        }\n    }\n\n    /**\n     * 执行数据流转\n     *\n     * @param message 设备消息\n     * @param config  配置信息\n     */\n    protected abstract void execute(IotDeviceMessage message, Config config) throws Exception;\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataSinkAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.util.http.HttpUtils;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkHttpConfig;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.*;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport javax.annotation.Resource;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;\n\n/**\n * HTTP 的 {@link IotDataRuleAction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class IotHttpDataSinkAction implements IotDataRuleAction {\n\n    @Resource\n    private RestTemplate restTemplate;\n\n    @Override\n    public Integer getType() {\n        return IotDataSinkTypeEnum.HTTP.getType();\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void execute(IotDeviceMessage message, IotDataSinkDO dataSink) {\n        IotDataSinkHttpConfig config = (IotDataSinkHttpConfig) dataSink.getConfig();\n        Assert.notNull(config, \"配置({})不能为空\", dataSink.getId());\n        String url = null;\n        HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase());\n        HttpEntity<String> requestEntity = null;\n        ResponseEntity<String> responseEntity = null;\n        try {\n            // 1.1 构建 Header\n            HttpHeaders headers = new HttpHeaders();\n            if (CollUtil.isNotEmpty(config.getHeaders())) {\n                config.getHeaders().putAll(config.getHeaders());\n            }\n            headers.add(HEADER_TENANT_ID, message.getTenantId().toString());\n            // 1.2 构建 URL\n            UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(config.getUrl());\n            if (CollUtil.isNotEmpty(config.getQuery())) {\n                config.getQuery().forEach(uriBuilder::queryParam);\n            }\n            // 1.3 构建请求体\n            if (method == HttpMethod.GET) {\n                uriBuilder.queryParam(\"message\", HttpUtils.encodeUtf8(JsonUtils.toJsonString(message)));\n                url = uriBuilder.build().toUriString();\n                requestEntity = new HttpEntity<>(headers);\n            } else {\n                url = uriBuilder.build().toUriString();\n                Map<String, Object> requestBody = JsonUtils.parseObject(config.getBody(), Map.class);\n                if (requestBody == null) {\n                    requestBody = new HashMap<>();\n                }\n                requestBody.put(\"message\", message);\n                headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);\n                requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers);\n            }\n\n            // 2. 发送请求\n            responseEntity = restTemplate.exchange(url, method, requestEntity, String.class);\n            if (responseEntity.getStatusCode().is2xxSuccessful()) {\n                log.info(\"[execute][message({}) config({}) url({}) method({}) requestEntity({}) 请求成功({})]\",\n                        message, config, url, method, requestEntity, responseEntity);\n            } else {\n                log.error(\"[execute][message({}) config({}) url({}) method({}) requestEntity({}) 请求失败({})]\",\n                        message, config, url, method, requestEntity, responseEntity);\n            }\n        } catch (Exception e) {\n            log.error(\"[execute][message({}) config({}) url({}) method({}) requestEntity({}) 请求异常({})]\",\n                    message, config, url, method, requestEntity, responseEntity, e);\n        }\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotKafkaDataRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkKafkaConfig;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.kafka.clients.producer.ProducerConfig;\nimport org.apache.kafka.common.serialization.StringSerializer;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.kafka.core.DefaultKafkaProducerFactory;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\n\nimport java.time.Duration;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Kafka 的 {@link IotDataRuleAction} 实现类\n *\n * @author HUIHUI\n */\n@ConditionalOnClass(name = \"org.springframework.kafka.core.KafkaTemplate\")\n@Component\n@Slf4j\npublic class IotKafkaDataRuleAction extends\n        IotDataRuleCacheableAction<IotDataSinkKafkaConfig, KafkaTemplate<String, String>> {\n\n    private static final Duration SEND_TIMEOUT = Duration.ofSeconds(10);\n\n    @Override\n    public Integer getType() {\n        return IotDataSinkTypeEnum.KAFKA.getType();\n    }\n\n    @Override\n    public void execute(IotDeviceMessage message, IotDataSinkKafkaConfig config) throws Exception {\n        try {\n            // 1. 获取或创建 KafkaTemplate\n            KafkaTemplate<String, String> kafkaTemplate = getProducer(config);\n\n            // 2. 发送消息并等待结果\n            SendResult<String, String> sendResult = kafkaTemplate.send(config.getTopic(), JsonUtils.toJsonString(message))\n                    .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS);\n            // 3. 处理发送结果\n            if (sendResult != null && sendResult.getRecordMetadata() != null) {\n                log.info(\"[execute][message({}) config({}) 发送成功，结果: partition={}, offset={}, timestamp={}]\",\n                        message, config,\n                        sendResult.getRecordMetadata().partition(),\n                        sendResult.getRecordMetadata().offset(),\n                        sendResult.getRecordMetadata().timestamp());\n            } else {\n                log.warn(\"[execute][message({}) config({}) 发送结果为空]\", message, config);\n            }\n        } catch (Exception e) {\n            log.error(\"[execute][message({}) config({}) 发送失败]\", message, config, e);\n            throw e;\n        }\n    }\n\n    @Override\n    protected KafkaTemplate<String, String> initProducer(IotDataSinkKafkaConfig config) {\n        // 1.1 构建生产者配置\n        Map<String, Object> props = new HashMap<>();\n        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers());\n        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);\n        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);\n        // 1.2 如果配置了认证信息\n        if (config.getUsername() != null && config.getPassword() != null) {\n            props.put(\"security.protocol\", \"SASL_PLAINTEXT\");\n            props.put(\"sasl.mechanism\", \"PLAIN\");\n            props.put(\"sasl.jaas.config\",\n                    \"org.apache.kafka.common.security.plain.PlainLoginModule required username=\\\"\"\n                            + config.getUsername() + \"\\\" password=\\\"\" + config.getPassword() + \"\\\";\");\n        }\n        // 1.3 如果启用 SSL\n        if (Boolean.TRUE.equals(config.getSsl())) {\n            props.put(\"security.protocol\", \"SSL\");\n        }\n\n        // 2. 创建 KafkaTemplate\n        DefaultKafkaProducerFactory<String, String> producerFactory = new DefaultKafkaProducerFactory<>(props);\n        return new KafkaTemplate<>(producerFactory);\n    }\n\n    @Override\n    protected void closeProducer(KafkaTemplate<String, String> producer) {\n        producer.destroy();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRabbitMQConfig;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport com.rabbitmq.client.Channel;\nimport com.rabbitmq.client.Connection;\nimport com.rabbitmq.client.ConnectionFactory;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.stereotype.Component;\n\n/**\n * RabbitMQ 的 {@link IotDataRuleAction} 实现类\n *\n * @author HUIHUI\n */\n@ConditionalOnClass(name = \"com.rabbitmq.client.Channel\")\n@Component\n@Slf4j\npublic class IotRabbitMQDataRuleAction\n        extends IotDataRuleCacheableAction<IotDataSinkRabbitMQConfig, Channel> {\n\n    @Override\n    public Integer getType() {\n        return IotDataSinkTypeEnum.RABBITMQ.getType();\n    }\n\n    @Override\n    public void execute(IotDeviceMessage message, IotDataSinkRabbitMQConfig config) throws Exception {\n        try {\n            // 1.1 获取或创建 Channel\n            Channel channel = getProducer(config);\n            // 1.2 声明交换机、队列和绑定关系\n            channel.exchangeDeclare(config.getExchange(), \"direct\", true);\n            channel.queueDeclare(config.getQueue(), true, false, false, null);\n            channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey());\n\n            // 2. 发送消息\n            channel.basicPublish(config.getExchange(), config.getRoutingKey(), null,\n                    JsonUtils.toJsonByte(message));\n            log.info(\"[execute][message({}) config({}) 发送成功]\", message, config);\n        } catch (Exception e) {\n            log.error(\"[execute][message({}) config({}) 发送失败]\", message, config, e);\n            throw e;\n        }\n    }\n\n    @Override\n    @SuppressWarnings(\"resource\")\n    protected Channel initProducer(IotDataSinkRabbitMQConfig config) throws Exception {\n        // 1. 创建连接工厂\n        ConnectionFactory factory = new ConnectionFactory();\n        factory.setHost(config.getHost());\n        factory.setPort(config.getPort());\n        factory.setVirtualHost(config.getVirtualHost());\n        factory.setUsername(config.getUsername());\n        factory.setPassword(config.getPassword());\n        // 2. 创建连接\n        Connection connection = factory.newConnection();\n        // 3. 创建信道\n        return connection.createChannel();\n    }\n\n    @Override\n    protected void closeProducer(Channel channel) throws Exception {\n        if (channel.isOpen()) {\n            channel.close();\n        }\n        Connection connection = channel.getConnection();\n        if (connection.isOpen()) {\n            connection.close();\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRedisConfig;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotRedisDataStructureEnum;\nimport lombok.extern.slf4j.Slf4j;\nimport org.redisson.Redisson;\nimport org.redisson.api.RedissonClient;\nimport org.redisson.config.Config;\nimport org.redisson.config.SingleServerConfig;\nimport org.redisson.spring.data.connection.RedissonConnectionFactory;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.connection.stream.ObjectRecord;\nimport org.springframework.data.redis.connection.stream.StreamRecords;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.serializer.RedisSerializer;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Map;\n\n/**\n * Redis 的 {@link IotDataRuleAction} 实现类\n * 支持多种 Redis 数据结构：Stream、Hash、List、Set、ZSet、String\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class IotRedisRuleAction extends\n        IotDataRuleCacheableAction<IotDataSinkRedisConfig, RedisTemplate<String, Object>> {\n\n    @Override\n    public Integer getType() {\n        return IotDataSinkTypeEnum.REDIS.getType();\n    }\n\n    @Override\n    public void execute(IotDeviceMessage message, IotDataSinkRedisConfig config) throws Exception {\n        // 1. 获取 RedisTemplate\n        RedisTemplate<String, Object> redisTemplate = getProducer(config);\n\n        // 2. 根据数据结构类型执行不同的操作\n        String messageJson = JsonUtils.toJsonString(message);\n        IotRedisDataStructureEnum dataStructure = getDataStructureByType(config.getDataStructure());\n        switch (dataStructure) {\n            case STREAM:\n                executeStream(redisTemplate, config, messageJson);\n                break;\n            case HASH:\n                executeHash(redisTemplate, config, message, messageJson);\n                break;\n            case LIST:\n                executeList(redisTemplate, config, messageJson);\n                break;\n            case SET:\n                executeSet(redisTemplate, config, messageJson);\n                break;\n            case ZSET:\n                executeZSet(redisTemplate, config, message, messageJson);\n                break;\n            case STRING:\n                executeString(redisTemplate, config, messageJson);\n                break;\n            default:\n                throw new IllegalArgumentException(\"不支持的 Redis 数据结构类型: \" + dataStructure);\n        }\n\n        log.info(\"[execute][消息发送成功] dataStructure: {}, config: {}\", dataStructure.getName(), config);\n    }\n\n    /**\n     * 执行 Stream 操作\n     */\n    private void executeStream(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {\n        ObjectRecord<String, ?> record = StreamRecords.newRecord()\n                .ofObject(messageJson).withStreamKey(config.getTopic());\n        redisTemplate.opsForStream().add(record);\n    }\n\n    /**\n     * 执行 Hash 操作\n     */\n    private void executeHash(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config,\n                             IotDeviceMessage message, String messageJson) {\n        String hashField = StrUtil.isNotBlank(config.getHashField()) ?\n                config.getHashField() : String.valueOf(message.getDeviceId());\n        redisTemplate.opsForHash().put(config.getTopic(), hashField, messageJson);\n    }\n\n    /**\n     * 执行 List 操作\n     */\n    private void executeList(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {\n        redisTemplate.opsForList().rightPush(config.getTopic(), messageJson);\n    }\n\n    /**\n     * 执行 Set 操作\n     */\n    private void executeSet(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {\n        redisTemplate.opsForSet().add(config.getTopic(), messageJson);\n    }\n\n    /**\n     * 执行 ZSet 操作\n     */\n    private void executeZSet(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config,\n                             IotDeviceMessage message, String messageJson) {\n        double score;\n        if (StrUtil.isNotBlank(config.getScoreField())) {\n            // 尝试从消息中获取分数字段\n            try {\n                Map<String, Object> messageMap = JsonUtils.parseObject(messageJson, Map.class);\n                Object scoreValue = messageMap.get(config.getScoreField());\n                score = scoreValue instanceof Number ? ((Number) scoreValue).doubleValue() : System.currentTimeMillis();\n            } catch (Exception e) {\n                score = System.currentTimeMillis();\n            }\n        } else {\n            // 使用当前时间戳作为分数\n            score = System.currentTimeMillis();\n        }\n        redisTemplate.opsForZSet().add(config.getTopic(), messageJson, score);\n    }\n\n    /**\n     * 执行 String 操作\n     */\n    private void executeString(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {\n        redisTemplate.opsForValue().set(config.getTopic(), messageJson);\n    }\n\n    @Override\n    protected RedisTemplate<String, Object> initProducer(IotDataSinkRedisConfig config) {\n        // 1.1 创建 Redisson 配置\n        Config redissonConfig = new Config();\n        SingleServerConfig serverConfig = redissonConfig.useSingleServer()\n                .setAddress(\"redis://\" + config.getHost() + \":\" + config.getPort())\n                .setDatabase(config.getDatabase());\n        // 1.2 设置密码（如果有）\n        if (StrUtil.isNotBlank(config.getPassword())) {\n            serverConfig.setPassword(config.getPassword());\n        }\n\n        // 2.1 创建 RedisTemplate 并配置\n        RedissonClient redisson = Redisson.create(redissonConfig);\n        RedisTemplate<String, Object> template = new RedisTemplate<>();\n        template.setConnectionFactory(new RedissonConnectionFactory(redisson));\n        // 2.2 设置序列化器\n        template.setKeySerializer(RedisSerializer.string());\n        template.setHashKeySerializer(RedisSerializer.string());\n        template.setValueSerializer(RedisSerializer.json());\n        template.setHashValueSerializer(RedisSerializer.json());\n        template.afterPropertiesSet();\n        return template;\n    }\n\n    @Override\n    protected void closeProducer(RedisTemplate<String, Object> producer) throws Exception {\n        RedisConnectionFactory factory = producer.getConnectionFactory();\n        if (factory != null) {\n            ((RedissonConnectionFactory) factory).destroy();\n        }\n    }\n\n    /**\n     * 根据类型值获取数据结构枚举\n     */\n    private IotRedisDataStructureEnum getDataStructureByType(Integer type) {\n        for (IotRedisDataStructureEnum dataStructure : IotRedisDataStructureEnum.values()) {\n            if (dataStructure.getType().equals(type)) {\n                return dataStructure;\n            }\n        }\n        throw new IllegalArgumentException(\"不支持的 Redis 数据结构类型: \" + type);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRocketMQConfig;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.rocketmq.client.producer.DefaultMQProducer;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.client.producer.SendStatus;\nimport org.apache.rocketmq.common.message.Message;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.stereotype.Component;\n\n/**\n * RocketMQ 的 {@link IotDataRuleAction} 实现类\n *\n * @author HUIHUI\n */\n@ConditionalOnClass(name = \"org.apache.rocketmq.client.producer.DefaultMQProducer\")\n@Component\n@Slf4j\npublic class IotRocketMQDataRuleAction extends\n        IotDataRuleCacheableAction<IotDataSinkRocketMQConfig, DefaultMQProducer> {\n\n    @Override\n    public Integer getType() {\n        return IotDataSinkTypeEnum.ROCKETMQ.getType();\n    }\n\n    @Override\n    public void execute(IotDeviceMessage message, IotDataSinkRocketMQConfig config) throws Exception {\n        // 1. 获取或创建 Producer\n        DefaultMQProducer producer = getProducer(config);\n\n        // 2.1 创建消息对象，指定 Topic、Tag 和消息体\n        Message msg = new Message(config.getTopic(), config.getTags(), JsonUtils.toJsonByte(message));\n        // 2.2 发送同步消息并处理结果\n        SendResult sendResult = producer.send(msg);\n        // 2.3 处理发送结果\n        if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) {\n            log.info(\"[execute][message({}) config({}) 发送成功，结果({})]\", message, config, sendResult);\n        } else {\n            log.error(\"[execute][message({}) config({}) 发送失败，结果({})]\", message, config, sendResult);\n        }\n    }\n\n    @Override\n    protected DefaultMQProducer initProducer(IotDataSinkRocketMQConfig config) throws Exception {\n        DefaultMQProducer producer = new DefaultMQProducer(config.getGroup());\n        producer.setNamesrvAddr(config.getNameServer());\n        producer.start();\n        return producer;\n    }\n\n    @Override\n    protected void closeProducer(DefaultMQProducer producer) {\n        producer.shutdown();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotTcpDataRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.data.action.tcp.IotTcpClient;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * TCP 的 {@link IotDataRuleAction} 实现类\n * <p>\n * 负责将设备消息发送到外部 TCP 服务器\n * 支持普通 TCP 和 SSL TCP 连接，支持 JSON 和 BINARY 数据格式\n * 使用连接池管理 TCP 连接，提高性能和资源利用率\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class IotTcpDataRuleAction extends\n        IotDataRuleCacheableAction<IotDataSinkTcpConfig, IotTcpClient> {\n\n    @Override\n    public Integer getType() {\n        return IotDataSinkTypeEnum.TCP.getType();\n    }\n\n    @Override\n    protected IotTcpClient initProducer(IotDataSinkTcpConfig config) throws Exception {\n        // 1.1 参数校验\n        if (config.getHost() == null || config.getHost().trim().isEmpty()) {\n            throw new IllegalArgumentException(\"TCP 服务器地址不能为空\");\n        }\n        if (config.getPort() == null || config.getPort() <= 0 || config.getPort() > 65535) {\n            throw new IllegalArgumentException(\"TCP 服务器端口无效\");\n        }\n\n        // 2.1 创建 TCP 客户端\n        IotTcpClient tcpClient = new IotTcpClient(\n                config.getHost(),\n                config.getPort(),\n                config.getConnectTimeoutMs(),\n                config.getReadTimeoutMs(),\n                config.getSsl(),\n                config.getDataFormat()\n        );\n        // 2.2 连接服务器\n        tcpClient.connect();\n        log.info(\"[initProducer][TCP 客户端创建并连接成功，服务器: {}:{}，SSL: {}，数据格式: {}]\",\n                config.getHost(), config.getPort(), config.getSsl(), config.getDataFormat());\n        return tcpClient;\n    }\n\n    @Override\n    protected void closeProducer(IotTcpClient producer) throws Exception {\n        if (producer != null) {\n            producer.close();\n        }\n    }\n\n    @Override\n    protected void execute(IotDeviceMessage message, IotDataSinkTcpConfig config) throws Exception {\n        try {\n            // 1.1 获取或创建 TCP 客户端\n            IotTcpClient tcpClient = getProducer(config);\n            // 1.2 检查连接状态，如果断开则重新连接\n            if (!tcpClient.isConnected()) {\n                log.warn(\"[execute][TCP 连接已断开，尝试重新连接，服务器: {}:{}]\", config.getHost(), config.getPort());\n                tcpClient.connect();\n            }\n\n            // 2.1 发送消息并等待结果\n            tcpClient.sendMessage(message);\n            // 2.2 记录发送成功日志\n            log.info(\"[execute][message({}) config({}) 发送成功，TCP 服务器: {}:{}]\",\n                    message, config, config.getHost(), config.getPort());\n        } catch (Exception e) {\n            log.error(\"[execute][message({}) config({}) 发送失败，TCP 服务器: {}:{}]\",\n                    message, config, config.getHost(), config.getPort(), e);\n            throw e;\n        }\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotWebSocketDataRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkWebSocketConfig;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.data.action.websocket.IotWebSocketClient;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * WebSocket 的 {@link IotDataRuleAction} 实现类\n * <p>\n * 负责将设备消息发送到外部 WebSocket 服务器\n * 支持 ws:// 和 wss:// 协议，支持 JSON 和 TEXT 数据格式\n * 使用连接池管理 WebSocket 连接，提高性能和资源利用率\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class IotWebSocketDataRuleAction extends\n        IotDataRuleCacheableAction<IotDataSinkWebSocketConfig, IotWebSocketClient> {\n\n    /**\n     * 锁等待超时时间（毫秒）\n     */\n    private static final long LOCK_WAIT_TIME_MS = 5000;\n\n    /**\n     * 重连锁，key 为 WebSocket 服务器地址\n     * <p>\n     * WebSocket 连接是与特定服务器实例绑定的，使用单机锁即可保证重连的线程安全\n     */\n    private final ConcurrentHashMap<String, ReentrantLock> reconnectLocks = new ConcurrentHashMap<>();\n\n    @Override\n    public Integer getType() {\n        return IotDataSinkTypeEnum.WEBSOCKET.getType();\n    }\n\n    @Override\n    protected IotWebSocketClient initProducer(IotDataSinkWebSocketConfig config) throws Exception {\n        // 1. 参数校验\n        if (StrUtil.isBlank(config.getServerUrl())) {\n            throw new IllegalArgumentException(\"WebSocket 服务器地址不能为空\");\n        }\n        if (!StrUtil.startWithAny(config.getServerUrl(), \"ws://\", \"wss://\")) {\n            throw new IllegalArgumentException(\"WebSocket 服务器地址必须以 ws:// 或 wss:// 开头\");\n        }\n\n        // 2.1 创建 WebSocket 客户端\n        IotWebSocketClient webSocketClient = new IotWebSocketClient(\n                config.getServerUrl(),\n                config.getConnectTimeoutMs(),\n                config.getSendTimeoutMs(),\n                config.getDataFormat()\n        );\n        // 2.2 连接服务器\n        webSocketClient.connect();\n        log.info(\"[initProducer][WebSocket 客户端创建并连接成功，服务器: {}，数据格式: {}]\",\n                config.getServerUrl(), config.getDataFormat());\n        return webSocketClient;\n    }\n\n    @Override\n    protected void closeProducer(IotWebSocketClient producer) throws Exception {\n        if (producer != null) {\n            producer.close();\n        }\n    }\n\n    @Override\n    protected void execute(IotDeviceMessage message, IotDataSinkWebSocketConfig config) throws Exception {\n        try {\n            // 1.1 获取或创建 WebSocket 客户端\n            IotWebSocketClient webSocketClient = getProducer(config);\n\n            // 1.2 检查连接状态，如果断开则使用分布式锁保证重连的线程安全\n            if (!webSocketClient.isConnected()) {\n                reconnectWithLock(webSocketClient, config);\n            }\n\n            // 2.1 发送消息\n            webSocketClient.sendMessage(message);\n            // 2.2 记录发送成功日志\n            log.info(\"[execute][message({}) config({}) 发送成功，WebSocket 服务器: {}]\",\n                    message, config, config.getServerUrl());\n        } catch (Exception e) {\n            log.error(\"[execute][message({}) config({}) 发送失败，WebSocket 服务器: {}]\",\n                    message, config, config.getServerUrl(), e);\n            throw e;\n        }\n    }\n\n    // TODO @puhui999：为什么这里要加锁呀？\n    /**\n     * 使用锁进行重连，保证同一服务器地址的重连操作线程安全\n     *\n     * @param webSocketClient WebSocket 客户端\n     * @param config          配置信息\n     */\n    private void reconnectWithLock(IotWebSocketClient webSocketClient, IotDataSinkWebSocketConfig config) throws Exception {\n        ReentrantLock lock = reconnectLocks.computeIfAbsent(config.getServerUrl(), k -> new ReentrantLock());\n        boolean acquired = false;\n        try {\n            acquired = lock.tryLock(LOCK_WAIT_TIME_MS, TimeUnit.MILLISECONDS);\n            if (!acquired) {\n                throw new RuntimeException(\"获取 WebSocket 重连锁超时，服务器: \" + config.getServerUrl());\n            }\n            // 双重检查：获取锁后再次检查连接状态，避免重复连接\n            if (!webSocketClient.isConnected()) {\n                log.warn(\"[reconnectWithLock][WebSocket 连接已断开，尝试重新连接，服务器: {}]\", config.getServerUrl());\n                webSocketClient.connect();\n            }\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new RuntimeException(\"获取 WebSocket 重连锁被中断，服务器: \" + config.getServerUrl(), e);\n        } finally {\n            if (acquired && lock.isHeldByCurrentThread()) {\n                lock.unlock();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClient.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action.tcp;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig;\nimport lombok.extern.slf4j.Slf4j;\n\nimport javax.net.ssl.SSLSocketFactory;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.nio.charset.StandardCharsets;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * IoT TCP 客户端\n * <p>\n * 负责与外部 TCP 服务器建立连接并发送设备消息\n * 支持 JSON 和 BINARY 两种数据格式，支持 SSL 加密连接\n *\n * @author HUIHUI\n */\n@Slf4j\npublic class IotTcpClient {\n\n    private final String host;\n    private final Integer port;\n    private final Integer connectTimeoutMs;\n    private final Integer readTimeoutMs;\n    private final Boolean ssl;\n    private final String dataFormat;\n\n    private Socket socket;\n    private OutputStream outputStream;\n    private BufferedReader reader;\n    private final AtomicBoolean connected = new AtomicBoolean(false);\n\n    public IotTcpClient(String host, Integer port, Integer connectTimeoutMs, Integer readTimeoutMs,\n                        Boolean ssl, String dataFormat) {\n        this.host = host;\n        this.port = port;\n        this.connectTimeoutMs = connectTimeoutMs != null ? connectTimeoutMs : IotDataSinkTcpConfig.DEFAULT_CONNECT_TIMEOUT_MS;\n        this.readTimeoutMs = readTimeoutMs != null ? readTimeoutMs : IotDataSinkTcpConfig.DEFAULT_READ_TIMEOUT_MS;\n        this.ssl = ssl != null ? ssl : IotDataSinkTcpConfig.DEFAULT_SSL;\n        this.dataFormat = ObjUtil.defaultIfBlank(dataFormat, IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT);\n    }\n\n    /**\n     * 连接到 TCP 服务器\n     */\n    public void connect() throws Exception {\n        if (connected.get()) {\n            log.warn(\"[connect][TCP 客户端已经连接，无需重复连接]\");\n            return;\n        }\n\n        try {\n            if (ssl) {\n                // SSL 连接\n                SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();\n                socket = sslSocketFactory.createSocket();\n            } else {\n                // 普通连接\n                socket = new Socket();\n            }\n\n            // 连接服务器\n            socket.connect(new InetSocketAddress(host, port), connectTimeoutMs);\n            socket.setSoTimeout(readTimeoutMs);\n\n            // 获取输入输出流\n            outputStream = socket.getOutputStream();\n            reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));\n\n            // 更新状态\n            connected.set(true);\n            log.info(\"[connect][TCP 客户端连接成功，服务器地址: {}:{}]\", host, port);\n        } catch (Exception e) {\n            close();\n            log.error(\"[connect][TCP 客户端连接失败，服务器地址: {}:{}]\", host, port, e);\n            throw e;\n        }\n    }\n\n    /**\n     * 发送设备消息\n     *\n     * @param message 设备消息\n     * @throws Exception 发送异常\n     */\n    public void sendMessage(IotDeviceMessage message) throws Exception {\n        if (!connected.get()) {\n            throw new IllegalStateException(\"TCP 客户端未连接\");\n        }\n\n        try {\n            String messageData;\n            if (IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT.equalsIgnoreCase(dataFormat)) {\n                // JSON 格式\n                messageData = JsonUtils.toJsonString(message);\n            } else {\n                // BINARY 格式（这里简化为字符串，实际可能需要自定义二进制协议）\n                messageData = message.toString();\n            }\n\n            // 发送消息\n            outputStream.write(messageData.getBytes(StandardCharsets.UTF_8));\n            outputStream.write('\\n'); // 添加换行符作为消息分隔符\n            outputStream.flush();\n            log.debug(\"[sendMessage][发送消息成功，设备 ID: {}，消息长度: {}]\",\n                    message.getDeviceId(), messageData.length());\n        } catch (Exception e) {\n            log.error(\"[sendMessage][发送消息失败，设备 ID: {}]\", message.getDeviceId(), e);\n            throw e;\n        }\n    }\n\n    /**\n     * 关闭连接\n     */\n    public void close() {\n        if (!connected.get()) {\n            return;\n        }\n\n        try {\n            // 关闭资源\n            if (reader != null) {\n                try {\n                    reader.close();\n                } catch (IOException e) {\n                    log.warn(\"[close][关闭输入流失败]\", e);\n                }\n            }\n            if (outputStream != null) {\n                try {\n                    outputStream.close();\n                } catch (IOException e) {\n                    log.warn(\"[close][关闭输出流失败]\", e);\n                }\n            }\n            if (socket != null) {\n                try {\n                    socket.close();\n                } catch (IOException e) {\n                    log.warn(\"[close][关闭 Socket 失败]\", e);\n                }\n            }\n\n            // 更新状态\n            connected.set(false);\n            log.info(\"[close][TCP 客户端连接已关闭，服务器地址: {}:{}]\", host, port);\n        } catch (Exception e) {\n            log.error(\"[close][关闭 TCP 客户端连接异常]\", e);\n        }\n    }\n\n    /**\n     * 检查连接状态\n     *\n     * @return 是否已连接\n     */\n    public boolean isConnected() {\n        return connected.get() && socket != null && !socket.isClosed();\n    }\n\n    @Override\n    public String toString() {\n        return \"IotTcpClient{\" +\n                \"host='\" + host + '\\'' +\n                \", port=\" + port +\n                \", ssl=\" + ssl +\n                \", dataFormat='\" + dataFormat + '\\'' +\n                \", connected=\" + connected.get() +\n                '}';\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/websocket/IotWebSocketClient.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action.websocket;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkWebSocketConfig;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.*;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * IoT WebSocket 客户端\n * <p>\n * 负责与外部 WebSocket 服务器建立连接并发送设备消息\n * 支持 ws:// 和 wss:// 协议，支持 JSON 和 TEXT 数据格式\n * 基于 OkHttp WebSocket 实现，兼容 JDK 8+\n * <p>\n * 注意：该类的线程安全由调用方（IotWebSocketDataRuleAction）通过分布式锁保证\n *\n * @author HUIHUI\n */\n@Slf4j\npublic class IotWebSocketClient {\n\n    private final String serverUrl;\n    private final Integer connectTimeoutMs;\n    private final Integer sendTimeoutMs;\n    private final String dataFormat;\n\n    private OkHttpClient okHttpClient;\n    private volatile WebSocket webSocket;\n    private final AtomicBoolean connected = new AtomicBoolean(false);\n\n    public IotWebSocketClient(String serverUrl, Integer connectTimeoutMs, Integer sendTimeoutMs, String dataFormat) {\n        this.serverUrl = serverUrl;\n        this.connectTimeoutMs = connectTimeoutMs != null ? connectTimeoutMs : IotDataSinkWebSocketConfig.DEFAULT_CONNECT_TIMEOUT_MS;\n        this.sendTimeoutMs = sendTimeoutMs != null ? sendTimeoutMs : IotDataSinkWebSocketConfig.DEFAULT_SEND_TIMEOUT_MS;\n        this.dataFormat = dataFormat != null ? dataFormat : IotDataSinkWebSocketConfig.DEFAULT_DATA_FORMAT;\n    }\n\n    /**\n     * 连接到 WebSocket 服务器\n     * <p>\n     * 注意：调用方需要通过分布式锁保证并发安全\n     */\n    public void connect() throws Exception {\n        if (connected.get()) {\n            log.warn(\"[connect][WebSocket 客户端已经连接，无需重复连接]\");\n            return;\n        }\n\n        try {\n            // 创建 OkHttpClient\n            okHttpClient = new OkHttpClient.Builder()\n                    .connectTimeout(connectTimeoutMs, TimeUnit.MILLISECONDS)\n                    .readTimeout(sendTimeoutMs, TimeUnit.MILLISECONDS)\n                    .writeTimeout(sendTimeoutMs, TimeUnit.MILLISECONDS)\n                    .build();\n\n            // 创建 WebSocket 请求\n            Request request = new Request.Builder()\n                    .url(serverUrl)\n                    .build();\n\n            // 使用 CountDownLatch 等待连接完成\n            CountDownLatch connectLatch = new CountDownLatch(1);\n            AtomicBoolean connectSuccess = new AtomicBoolean(false);\n            // 创建 WebSocket 连接\n            webSocket = okHttpClient.newWebSocket(request, new IotWebSocketListener(connectLatch, connectSuccess));\n\n            // 等待连接完成\n            boolean await = connectLatch.await(connectTimeoutMs, TimeUnit.MILLISECONDS);\n            if (!await || !connectSuccess.get()) {\n                close();\n                throw new Exception(\"WebSocket 连接超时或失败，服务器地址: \" + serverUrl);\n            }\n            log.info(\"[connect][WebSocket 客户端连接成功，服务器地址: {}]\", serverUrl);\n        } catch (Exception e) {\n            close();\n            log.error(\"[connect][WebSocket 客户端连接失败，服务器地址: {}]\", serverUrl, e);\n            throw e;\n        }\n    }\n\n    /**\n     * 发送设备消息\n     *\n     * @param message 设备消息\n     * @throws Exception 发送异常\n     */\n    public void sendMessage(IotDeviceMessage message) throws Exception {\n        WebSocket ws = this.webSocket;\n        if (!connected.get() || ws == null) {\n            throw new IllegalStateException(\"WebSocket 客户端未连接\");\n        }\n\n        try {\n            String messageData;\n            if (IotDataSinkWebSocketConfig.DEFAULT_DATA_FORMAT.equalsIgnoreCase(dataFormat)) {\n                messageData = JsonUtils.toJsonString(message);\n            } else {\n                messageData = message.toString();\n            }\n\n            // 发送消息\n            boolean success = ws.send(messageData);\n            if (!success) {\n                throw new Exception(\"WebSocket 发送消息失败，消息队列已满或连接已关闭\");\n            }\n            log.debug(\"[sendMessage][发送消息成功，设备 ID: {}，消息长度: {}]\",\n                    message.getDeviceId(), messageData.length());\n        } catch (Exception e) {\n            log.error(\"[sendMessage][发送消息失败，设备 ID: {}]\", message.getDeviceId(), e);\n            throw e;\n        }\n    }\n\n    /**\n     * 关闭连接\n     */\n    public void close() {\n        try {\n            if (webSocket != null) {\n                // 发送正常关闭帧，状态码 1000 表示正常关闭\n                // TODO @puhui999：有没 1000 的枚举哈？在 okhttp 里\n                webSocket.close(1000, \"客户端主动关闭\");\n                webSocket = null;\n            }\n            if (okHttpClient != null) {\n                // 关闭连接池和调度器\n                okHttpClient.dispatcher().executorService().shutdown();\n                okHttpClient.connectionPool().evictAll();\n                okHttpClient = null;\n            }\n            connected.set(false);\n            log.info(\"[close][WebSocket 客户端连接已关闭，服务器地址: {}]\", serverUrl);\n        } catch (Exception e) {\n            log.error(\"[close][关闭 WebSocket 客户端连接异常]\", e);\n        }\n    }\n\n    /**\n     * 检查连接状态\n     *\n     * @return 是否已连接\n     */\n    public boolean isConnected() {\n        return connected.get() && webSocket != null;\n    }\n\n    @Override\n    public String toString() {\n        return \"IotWebSocketClient{\" +\n                \"serverUrl='\" + serverUrl + '\\'' +\n                \", dataFormat='\" + dataFormat + '\\'' +\n                \", connected=\" + connected.get() +\n                '}';\n    }\n\n    /**\n     * OkHttp WebSocket 监听器\n     */\n    @SuppressWarnings(\"NullableProblems\")\n    private class IotWebSocketListener extends WebSocketListener {\n\n        private final CountDownLatch connectLatch;\n        private final AtomicBoolean connectSuccess;\n\n        public IotWebSocketListener(CountDownLatch connectLatch, AtomicBoolean connectSuccess) {\n            this.connectLatch = connectLatch;\n            this.connectSuccess = connectSuccess;\n        }\n\n        @Override\n        public void onOpen(WebSocket webSocket, Response response) {\n            connected.set(true);\n            connectSuccess.set(true);\n            connectLatch.countDown();\n            log.info(\"[onOpen][WebSocket 连接已打开，服务器: {}]\", serverUrl);\n        }\n\n        @Override\n        public void onMessage(WebSocket webSocket, String text) {\n            log.debug(\"[onMessage][收到消息: {}]\", text);\n        }\n\n        @Override\n        public void onClosing(WebSocket webSocket, int code, String reason) {\n            connected.set(false);\n            log.info(\"[onClosing][WebSocket 正在关闭，code: {}, reason: {}]\", code, reason);\n        }\n\n        @Override\n        public void onClosed(WebSocket webSocket, int code, String reason) {\n            connected.set(false);\n            log.info(\"[onClosed][WebSocket 已关闭，code: {}, reason: {}]\", code, reason);\n        }\n\n        @Override\n        public void onFailure(WebSocket webSocket, Throwable t, Response response) {\n            connected.set(false);\n            connectLatch.countDown(); // 确保连接失败时也释放等待\n            log.error(\"[onFailure][WebSocket 连接失败]\", t);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * IoT 规则场景规则 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotSceneRuleService {\n\n    /**\n     * 创建场景联动\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createSceneRule(@Valid IotSceneRuleSaveReqVO createReqVO);\n\n    /**\n     * 更新场景联动\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateSceneRule(@Valid IotSceneRuleSaveReqVO updateReqVO);\n\n    /**\n     * 更新场景联动状态\n     *\n     * @param id     场景联动编号\n     * @param status 状态\n     */\n    void updateSceneRuleStatus(Long id, Integer status);\n\n    /**\n     * 删除场景联动\n     *\n     * @param id 编号\n     */\n    void deleteSceneRule(Long id);\n\n    /**\n     * 获得场景联动\n     *\n     * @param id 编号\n     * @return 场景联动\n     */\n    IotSceneRuleDO getSceneRule(Long id);\n\n    /**\n     * 获得场景联动分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 场景联动分页\n     */\n    PageResult<IotSceneRuleDO> getSceneRulePage(IotSceneRulePageReqVO pageReqVO);\n\n    /**\n     * 校验规则场景联动规则编号们是否存在。如下情况，视为无效：\n     * 1. 规则场景联动规则编号不存在\n     *\n     * @param ids 场景联动规则编号数组\n     */\n    void validateSceneRuleList(Collection<Long> ids);\n\n    /**\n     * 获得指定状态的场景联动列表\n     *\n     * @param status 状态\n     * @return 场景联动列表\n     */\n    List<IotSceneRuleDO> getSceneRuleListByStatus(Integer status);\n\n    /**\n     * 【缓存】获得指定设备的场景列表\n     *\n     * @param productId 产品 ID\n     * @param deviceId  设备 ID\n     * @return 场景列表\n     */\n    List<IotSceneRuleDO> getSceneRuleListByProductIdAndDeviceIdFromCache(Long productId, Long deviceId);\n\n    /**\n     * 基于 {@link IotSceneRuleTriggerTypeEnum} 场景，执行规则场景\n     * 1. {@link IotSceneRuleTriggerTypeEnum#DEVICE_STATE_UPDATE}\n     * 2. {@link IotSceneRuleTriggerTypeEnum#DEVICE_PROPERTY_POST}\n     *    {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST}\n     * 3. {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST}\n     *    {@link IotSceneRuleTriggerTypeEnum#DEVICE_SERVICE_INVOKE}\n     * @param message 消息\n     */\n    void executeSceneRuleByDevice(IotDeviceMessage message);\n\n    /**\n     * 基于 {@link IotSceneRuleTriggerTypeEnum#TIMER} 场景，执行规则场景\n     *\n     * @param id 场景联动规则编号\n     */\n    void executeSceneRuleByTimer(Long id);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;\nimport cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotSceneRuleTimerHandler;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotTimerConditionEvaluator;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NOT_EXISTS;\n\n/**\n * IoT 规则场景 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class IotSceneRuleServiceImpl implements IotSceneRuleService {\n\n    @Resource\n    private IotSceneRuleMapper sceneRuleMapper;\n\n    @Resource\n    private IotProductService productService;\n    @Resource\n    private IotDeviceService deviceService;\n\n    @Resource\n    private IotSceneRuleMatcherManager sceneRuleMatcherManager;\n    @Resource\n    private List<IotSceneRuleAction> sceneRuleActions;\n    @Resource\n    private IotSceneRuleTimerHandler timerHandler;\n    @Resource\n    private IotTimerConditionEvaluator timerConditionEvaluator;\n\n    @Override\n    @CacheEvict(value = RedisKeyConstants.SCENE_RULE_LIST, allEntries = true)\n    public Long createSceneRule(IotSceneRuleSaveReqVO createReqVO) {\n        IotSceneRuleDO sceneRule = BeanUtils.toBean(createReqVO, IotSceneRuleDO.class);\n        sceneRuleMapper.insert(sceneRule);\n\n        // 注册定时触发器\n        timerHandler.registerTimerTriggers(sceneRule);\n\n        return sceneRule.getId();\n    }\n\n    @Override\n    @CacheEvict(value = RedisKeyConstants.SCENE_RULE_LIST, allEntries = true)\n    public void updateSceneRule(IotSceneRuleSaveReqVO updateReqVO) {\n        // 校验存在\n        validateSceneRuleExists(updateReqVO.getId());\n        // 更新\n        IotSceneRuleDO updateObj = BeanUtils.toBean(updateReqVO, IotSceneRuleDO.class);\n        sceneRuleMapper.updateById(updateObj);\n\n        // 更新定时触发器\n        timerHandler.updateTimerTriggers(updateObj);\n    }\n\n    @Override\n    @CacheEvict(value = RedisKeyConstants.SCENE_RULE_LIST, allEntries = true)\n    public void updateSceneRuleStatus(Long id, Integer status) {\n        // 1. 校验存在\n        validateSceneRuleExists(id);\n\n        // 2. 更新状态\n        IotSceneRuleDO updateObj = new IotSceneRuleDO().setId(id).setStatus(status);\n        sceneRuleMapper.updateById(updateObj);\n\n        // 3. 根据状态管理定时触发器\n        if (CommonStatusEnum.isEnable(status)) {\n            // 启用时，获取完整的场景规则信息并注册定时触发器\n            IotSceneRuleDO sceneRule = sceneRuleMapper.selectById(id);\n            if (sceneRule != null) {\n                timerHandler.registerTimerTriggers(sceneRule);\n            }\n        } else {\n            // 禁用时，暂停定时触发器\n            timerHandler.pauseTimerTriggers(id);\n        }\n    }\n\n    @Override\n    @CacheEvict(value = RedisKeyConstants.SCENE_RULE_LIST, allEntries = true)\n    public void deleteSceneRule(Long id) {\n        // 1. 校验存在\n        validateSceneRuleExists(id);\n\n        // 2. 删除\n        sceneRuleMapper.deleteById(id);\n\n        // 3. 删除定时触发器\n        timerHandler.unregisterTimerTriggers(id);\n    }\n\n    private void validateSceneRuleExists(Long id) {\n        if (sceneRuleMapper.selectById(id) == null) {\n            throw exception(RULE_SCENE_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public IotSceneRuleDO getSceneRule(Long id) {\n        return sceneRuleMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<IotSceneRuleDO> getSceneRulePage(IotSceneRulePageReqVO pageReqVO) {\n        return sceneRuleMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public void validateSceneRuleList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return;\n        }\n        // 批量查询存在的规则场景\n        List<IotSceneRuleDO> existingScenes = sceneRuleMapper.selectByIds(ids);\n        if (existingScenes.size() != ids.size()) {\n            throw exception(RULE_SCENE_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public List<IotSceneRuleDO> getSceneRuleListByStatus(Integer status) {\n        return sceneRuleMapper.selectListByStatus(status);\n    }\n\n    @Override\n    @Cacheable(value = RedisKeyConstants.SCENE_RULE_LIST, key = \"#productId + '_' + #deviceId \")\n    @TenantIgnore // 忽略租户隔离：因为 IotSceneRuleMessageHandler 调用时，一般未传递租户，所以需要忽略\n    public List<IotSceneRuleDO> getSceneRuleListByProductIdAndDeviceIdFromCache(Long productId, Long deviceId) {\n        // 1. 查询启用状态的规则场景\n        List<IotSceneRuleDO> enabledList = sceneRuleMapper.selectList(IotSceneRuleDO::getStatus, CommonStatusEnum.ENABLE.getStatus());\n\n        // 2. 根据 productKey 和 deviceName 进行匹配\n        return filterList(enabledList, sceneRule -> {\n            if (CollUtil.isEmpty(sceneRule.getTriggers())) {\n                return false;\n            }\n\n            for (IotSceneRuleDO.Trigger trigger : sceneRule.getTriggers()) {\n                // 检查触发器是否匹配指定的产品和设备\n                try {\n                    // 检查产品是否匹配\n                    if (trigger.getProductId() == null || trigger.getDeviceId() == null) {\n                        return false;\n                    }\n                    // 检查是否是全部设备的特殊标识\n                    if (IotDeviceDO.DEVICE_ID_ALL.equals(trigger.getDeviceId())) {\n                        return true;\n                    }\n                    // 检查具体设备 ID 是否匹配\n                    return ObjUtil.equal(productId, trigger.getProductId()) && ObjUtil.equal(deviceId, trigger.getDeviceId());\n                } catch (Exception e) {\n                    log.warn(\"[getSceneRuleListByProductIdAndDeviceIdFromCache][产品({}) 设备({}) 匹配触发器异常]\",\n                            productId, deviceId, e);\n                    return false;\n                }\n            }\n            return false;\n        });\n    }\n\n    @Override\n    public void executeSceneRuleByDevice(IotDeviceMessage message) {\n        // 1.1 这里的 tenantId，通过设备获取；\n        IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId());\n        TenantUtils.execute(device.getTenantId(), () -> {\n            // 1.2 获得设备匹配的规则场景\n            List<IotSceneRuleDO> sceneRules = getMatchedSceneRuleListByMessage(message);\n            if (CollUtil.isEmpty(sceneRules)) {\n                return;\n            }\n\n            // 2. 执行规则场景\n            executeSceneRuleAction(message, sceneRules);\n        });\n    }\n\n    @Override\n    public void executeSceneRuleByTimer(Long id) {\n        // 1.1 获得规则场景\n        IotSceneRuleDO scene = TenantUtils.executeIgnore(() -> sceneRuleMapper.selectById(id));\n        if (scene == null) {\n            log.error(\"[executeSceneRuleByTimer][规则场景({}) 不存在]\", id);\n            return;\n        }\n        if (CommonStatusEnum.isDisable(scene.getStatus())) {\n            log.info(\"[executeSceneRuleByTimer][规则场景({}) 已被禁用]\", id);\n            return;\n        }\n        // 1.2 判断是否有定时触发器，避免脏数据\n        IotSceneRuleDO.Trigger timerTrigger = CollUtil.findOne(scene.getTriggers(),\n                trigger -> ObjUtil.equals(trigger.getType(), IotSceneRuleTriggerTypeEnum.TIMER.getType()));\n        if (timerTrigger == null) {\n            log.error(\"[executeSceneRuleByTimer][规则场景({}) 不存在定时触发器]\", scene);\n            return;\n        }\n\n        // 2. 评估条件组（新增逻辑）\n        log.info(\"[executeSceneRuleByTimer][规则场景({}) 开始评估条件组]\", id);\n        if (!evaluateTimerConditionGroups(scene, timerTrigger)) {\n            log.info(\"[executeSceneRuleByTimer][规则场景({}) 条件组不满足，跳过执行]\", id);\n            return;\n        }\n        log.info(\"[executeSceneRuleByTimer][规则场景({}) 条件组评估通过，准备执行动作]\", id);\n\n        // 3. 执行规则场景\n        TenantUtils.execute(scene.getTenantId(),\n                () -> executeSceneRuleAction(null, ListUtil.toList(scene)));\n    }\n\n    /**\n     * 评估定时触发器的条件组\n     *\n     * @param scene   场景规则\n     * @param trigger 定时触发器\n     * @return 是否满足条件\n     */\n    private boolean evaluateTimerConditionGroups(IotSceneRuleDO scene, IotSceneRuleDO.Trigger trigger) {\n        // 1. 如果没有条件组，直接返回 true（直接执行动作）\n        if (CollUtil.isEmpty(trigger.getConditionGroups())) {\n            log.debug(\"[evaluateTimerConditionGroups][规则场景({}) 无条件组配置，直接执行]\", scene.getId());\n            return true;\n        }\n\n        // 2. 条件组之间是 OR 关系，任一条件组满足即可\n        for (List<IotSceneRuleDO.TriggerCondition> conditionGroup : trigger.getConditionGroups()) {\n            if (evaluateSingleConditionGroup(scene, conditionGroup)) {\n                log.debug(\"[evaluateTimerConditionGroups][规则场景({}) 条件组匹配成功]\", scene.getId());\n                return true;\n            }\n        }\n\n        // 3. 所有条件组都不满足\n        log.debug(\"[evaluateTimerConditionGroups][规则场景({}) 所有条件组都不满足]\", scene.getId());\n        return false;\n    }\n\n    /**\n     * 评估单个条件组\n     *\n     * @param scene          场景规则\n     * @param conditionGroup 条件组\n     * @return 是否满足条件\n     */\n    private boolean evaluateSingleConditionGroup(IotSceneRuleDO scene,\n                                                 List<IotSceneRuleDO.TriggerCondition> conditionGroup) {\n        // 1. 空条件组视为满足\n        if (CollUtil.isEmpty(conditionGroup)) {\n            return true;\n        }\n\n        // 2. 条件之间是 AND 关系，所有条件都必须满足\n        for (IotSceneRuleDO.TriggerCondition condition : conditionGroup) {\n            if (!evaluateTimerCondition(scene, condition)) {\n                log.debug(\"[evaluateSingleConditionGroup][规则场景({}) 条件({}) 不满足]\",\n                        scene.getId(), condition);\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * 评估单个条件（定时触发器专用）\n     *\n     * @param scene     场景规则\n     * @param condition 条件\n     * @return 是否满足条件\n     */\n    private boolean evaluateTimerCondition(IotSceneRuleDO scene, IotSceneRuleDO.TriggerCondition condition) {\n        try {\n            boolean result = timerConditionEvaluator.evaluate(condition);\n            log.debug(\"[evaluateTimerCondition][规则场景({}) 条件类型({}) 评估结果: {}]\",\n                    scene.getId(), condition.getType(), result);\n            return result;\n        } catch (Exception e) {\n            log.error(\"[evaluateTimerCondition][规则场景({}) 条件评估异常]\", scene.getId(), e);\n            return false;\n        }\n    }\n\n    /**\n     * 基于消息，获得匹配的规则场景列表\n     *\n     * @param message 设备消息\n     * @return 规则场景列表\n     */\n    private List<IotSceneRuleDO> getMatchedSceneRuleListByMessage(IotDeviceMessage message) {\n        // 1.1 通过 deviceId 获取设备信息\n        IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId());\n        if (device == null) {\n            log.warn(\"[getMatchedSceneRuleListByMessage][设备({}) 不存在]\", message.getDeviceId());\n            return ListUtil.of();\n        }\n        // 1.2 通过 productId 获取产品信息\n        IotProductDO product = productService.getProductFromCache(device.getProductId());\n        if (product == null) {\n            log.warn(\"[getMatchedSceneRuleListByMessage][产品({}) 不存在]\", device.getProductId());\n            return ListUtil.of();\n        }\n        // 1.3 获取匹配的规则场景\n        List<IotSceneRuleDO> sceneRules = getSelf().getSceneRuleListByProductIdAndDeviceIdFromCache(\n                product.getId(), device.getId());\n        if (CollUtil.isEmpty(sceneRules)) {\n            return sceneRules;\n        }\n\n        // 2. 使用重构后的触发器匹配逻辑\n        return filterList(sceneRules, sceneRule -> matchSceneRuleTriggers(message, sceneRule));\n    }\n\n    /**\n     * 匹配场景规则的所有触发器\n     *\n     * @param message   设备消息\n     * @param sceneRule 场景规则\n     * @return 是否匹配\n     */\n    private boolean matchSceneRuleTriggers(IotDeviceMessage message, IotSceneRuleDO sceneRule) {\n        if (CollUtil.isEmpty(sceneRule.getTriggers())) {\n            log.debug(\"[matchSceneRuleTriggers][规则场景({}) 没有配置触发器]\", sceneRule.getId());\n            return false;\n        }\n\n        for (IotSceneRuleDO.Trigger trigger : sceneRule.getTriggers()) {\n            if (matchSingleTrigger(message, trigger, sceneRule)) {\n                log.info(\"[matchSceneRuleTriggers][消息({}) 匹配到规则场景编号({}) 的触发器({})]\",\n                        message.getRequestId(), sceneRule.getId(), trigger.getType());\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 匹配单个触发器\n     *\n     * @param message   设备消息\n     * @param trigger   触发器\n     * @param sceneRule 场景规则（用于日志）\n     * @return 是否匹配\n     */\n    private boolean matchSingleTrigger(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) {\n        try {\n            return sceneRuleMatcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule);\n        } catch (Exception e) {\n            log.error(\"[matchSingleTrigger][触发器匹配异常] sceneRuleId: {}, triggerType: {}, message: {}\",\n                    sceneRule.getId(), trigger.getType(), message, e);\n            return false;\n        }\n    }\n\n    /**\n     * 检查触发器的条件分组是否匹配\n     *\n     * @param message   设备消息\n     * @param trigger   触发器\n     * @param sceneRule 场景规则（用于日志）\n     * @return 是否匹配\n     */\n    private boolean isTriggerConditionGroupsMatched(IotDeviceMessage message,\n                                                    IotSceneRuleDO.Trigger trigger,\n                                                    IotSceneRuleDO sceneRule) {\n        // 1. 如果没有条件分组，则认为匹配成功（只依赖基础触发器匹配）\n        if (CollUtil.isEmpty(trigger.getConditionGroups())) {\n            return true;\n        }\n\n        // 2. 检查条件分组：分组与分组之间是\"或\"的关系，条件与条件之间是\"且\"的关系\n        for (List<IotSceneRuleDO.TriggerCondition> conditionGroup : trigger.getConditionGroups()) {\n            if (CollUtil.isEmpty(conditionGroup)) {\n                continue;\n            }\n            // 检查当前分组中的所有条件是否都匹配（且关系）\n            boolean allConditionsMatched = true;\n            for (IotSceneRuleDO.TriggerCondition condition : conditionGroup) {\n                if (!isTriggerConditionMatched(message, condition, sceneRule, trigger)) {\n                    allConditionsMatched = false;\n                    break;\n                }\n            }\n            // 如果当前分组的所有条件都匹配，则整个触发器匹配成功\n            if (allConditionsMatched) {\n                return true;\n            }\n        }\n\n        // 3. 所有分组都不匹配\n        return false;\n    }\n\n    /**\n     * 基于消息，判断触发器的子条件是否匹配\n     *\n     * @param message   设备消息\n     * @param condition 触发条件\n     * @param sceneRule 规则场景（用于日志，无其它作用）\n     * @param trigger   触发器（用于日志，无其它作用）\n     * @return 是否匹配\n     */\n    private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,\n                                              IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) {\n        try {\n            return sceneRuleMatcherManager.isConditionMatched(message, condition);\n        } catch (Exception e) {\n            log.error(\"[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]\",\n                    sceneRule.getId(), trigger, e);\n            return false;\n        }\n    }\n\n    /**\n     * 执行规则场景的动作\n     *\n     * @param message    设备消息\n     * @param sceneRules 规则场景列表\n     */\n    private void executeSceneRuleAction(IotDeviceMessage message, List<IotSceneRuleDO> sceneRules) {\n        // 1. 遍历规则场景\n        sceneRules.forEach(sceneRule -> {\n            // 2. 遍历规则场景的动作\n            sceneRule.getActions().forEach(actionConfig -> {\n                // 2.1 获取对应的动作 Action 数组\n                List<IotSceneRuleAction> actions = filterList(sceneRuleActions,\n                        action -> action.getType().getType().equals(actionConfig.getType()));\n                if (CollUtil.isEmpty(actions)) {\n                    return;\n                }\n                // 2.2 执行动作\n                actions.forEach(action -> {\n                    try {\n                        action.execute(message, sceneRule, actionConfig);\n                        log.info(\"[executeSceneRuleAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]\",\n                                message, sceneRule.getId(), actionConfig);\n                    } catch (Exception e) {\n                        log.error(\"[executeSceneRuleAction][消息({}) 规则场景编号({}) 的执行动作({}) 执行异常]\",\n                                message, sceneRule.getId(), actionConfig, e);\n                    }\n                });\n            });\n\n            // 3. 更新最后触发时间\n            updateLastTriggerTime(sceneRule.getId());\n        });\n    }\n\n    /**\n     * 更新规则场景的最后触发时间\n     *\n     * @param id 规则场景编号\n     */\n    private void updateLastTriggerTime(Long id) {\n        try {\n            sceneRuleMapper.updateById(new IotSceneRuleDO().setId(id).setLastTriggerTime(LocalDateTime.now()));\n        } catch (Exception e) {\n            log.error(\"[updateLastTriggerTime][规则场景编号({}) 更新最后触发时间异常]\", id, e);\n        }\n    }\n\n    private IotSceneRuleServiceImpl getSelf() {\n        return SpringUtil.getBean(IotSceneRuleServiceImpl.class);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimeHelper.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.text.CharPool;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition.IotCurrentTimeConditionMatcher;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotTimerConditionEvaluator;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.util.List;\n\n/**\n * IoT 场景规则时间匹配工具类\n * <p>\n * 提供时间条件匹配的通用方法，供 {@link IotCurrentTimeConditionMatcher} 和 {@link IotTimerConditionEvaluator} 共同使用。\n *\n * @author HUIHUI\n */\n@Slf4j\npublic class IotSceneRuleTimeHelper {\n\n    /**\n     * 时间格式化器 - HH:mm:ss\n     */\n    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(\"HH:mm:ss\");\n\n    /**\n     * 时间格式化器 - HH:mm\n     */\n    private static final DateTimeFormatter TIME_FORMATTER_SHORT = DateTimeFormatter.ofPattern(\"HH:mm\");\n\n    // TODO @puhui999：可以使用 lombok 简化\n    private IotSceneRuleTimeHelper() {\n        // 工具类，禁止实例化\n    }\n\n    /**\n     * 判断是否为日期时间操作符\n     *\n     * @param operatorEnum 操作符枚举\n     * @return 是否为日期时间操作符\n     */\n    public static boolean isDateTimeOperator(IotSceneRuleConditionOperatorEnum operatorEnum) {\n        return operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN\n                || operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN\n                || operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN;\n    }\n\n    /**\n     * 判断是否为时间操作符（包括日期时间操作符和当日时间操作符）\n     *\n     * @param operatorEnum 操作符枚举\n     * @return 是否为时间操作符\n     */\n    public static boolean isTimeOperator(IotSceneRuleConditionOperatorEnum operatorEnum) {\n        return operatorEnum != IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN\n                && operatorEnum != IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN\n                && operatorEnum != IotSceneRuleConditionOperatorEnum.TIME_BETWEEN\n                && !isDateTimeOperator(operatorEnum);\n    }\n\n    /**\n     * 执行时间匹配逻辑\n     *\n     * @param operatorEnum 操作符枚举\n     * @param param        参数值\n     * @return 是否匹配\n     */\n    public static boolean executeTimeMatching(IotSceneRuleConditionOperatorEnum operatorEnum, String param) {\n        try {\n            LocalDateTime now = LocalDateTime.now();\n            if (isDateTimeOperator(operatorEnum)) {\n                // 日期时间匹配（时间戳，秒级）\n                long currentTimestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond();\n                return matchDateTime(currentTimestamp, operatorEnum, param);\n            } else {\n                // 当日时间匹配（HH:mm:ss）\n                return matchTime(now.toLocalTime(), operatorEnum, param);\n            }\n        } catch (Exception e) {\n            log.error(\"[executeTimeMatching][operatorEnum({}) param({}) 时间匹配异常]\", operatorEnum, param, e);\n            return false;\n        }\n    }\n\n    /**\n     * 匹配日期时间（时间戳，秒级）\n     *\n     * @param currentTimestamp 当前时间戳\n     * @param operatorEnum     操作符枚举\n     * @param param            参数值\n     * @return 是否匹配\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    public static boolean matchDateTime(long currentTimestamp, IotSceneRuleConditionOperatorEnum operatorEnum,\n                                        String param) {\n        try {\n            // DATE_TIME_BETWEEN 需要解析两个时间戳，单独处理\n            if (operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN) {\n                return matchDateTimeBetween(currentTimestamp, param);\n            }\n            // 其他操作符只需要解析一个时间戳\n            long targetTimestamp = Long.parseLong(param);\n            switch (operatorEnum) {\n                case DATE_TIME_GREATER_THAN:\n                    return currentTimestamp > targetTimestamp;\n                case DATE_TIME_LESS_THAN:\n                    return currentTimestamp < targetTimestamp;\n                default:\n                    log.warn(\"[matchDateTime][operatorEnum({}) 不支持的日期时间操作符]\", operatorEnum);\n                    return false;\n            }\n        } catch (Exception e) {\n            log.error(\"[matchDateTime][operatorEnum({}) param({}) 日期时间匹配异常]\", operatorEnum, param, e);\n            return false;\n        }\n    }\n\n    /**\n     * 匹配日期时间区间\n     *\n     * @param currentTimestamp 当前时间戳\n     * @param param            参数值（格式：startTimestamp,endTimestamp）\n     * @return 是否匹配\n     */\n    public static boolean matchDateTimeBetween(long currentTimestamp, String param) {\n        List<String> timestampRange = StrUtil.splitTrim(param, CharPool.COMMA);\n        if (timestampRange.size() != 2) {\n            log.warn(\"[matchDateTimeBetween][param({}) 时间戳区间参数格式错误]\", param);\n            return false;\n        }\n        long startTimestamp = Long.parseLong(timestampRange.get(0).trim());\n        long endTimestamp = Long.parseLong(timestampRange.get(1).trim());\n        // TODO @puhui999：hutool 里，看看有没 between 方法\n        return currentTimestamp >= startTimestamp && currentTimestamp <= endTimestamp;\n    }\n\n    /**\n     * 匹配当日时间（HH:mm:ss 或 HH:mm）\n     *\n     * @param currentTime  当前时间\n     * @param operatorEnum 操作符枚举\n     * @param param        参数值\n     * @return 是否匹配\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    public static boolean matchTime(LocalTime currentTime, IotSceneRuleConditionOperatorEnum operatorEnum,\n                                    String param) {\n        try {\n            // TIME_BETWEEN 需要解析两个时间，单独处理\n            if (operatorEnum == IotSceneRuleConditionOperatorEnum.TIME_BETWEEN) {\n                return matchTimeBetween(currentTime, param);\n            }\n            // 其他操作符只需要解析一个时间\n            LocalTime targetTime = parseTime(param);\n            switch (operatorEnum) {\n                case TIME_GREATER_THAN:\n                    return currentTime.isAfter(targetTime);\n                case TIME_LESS_THAN:\n                    return currentTime.isBefore(targetTime);\n                default:\n                    log.warn(\"[matchTime][operatorEnum({}) 不支持的时间操作符]\", operatorEnum);\n                    return false;\n            }\n        } catch (Exception e) {\n            log.error(\"[matchTime][operatorEnum({}) param({}) 时间解析异常]\", operatorEnum, param, e);\n            return false;\n        }\n    }\n\n    /**\n     * 匹配时间区间\n     *\n     * @param currentTime 当前时间\n     * @param param       参数值（格式：startTime,endTime）\n     * @return 是否匹配\n     */\n    public static boolean matchTimeBetween(LocalTime currentTime, String param) {\n        List<String> timeRange = StrUtil.splitTrim(param, CharPool.COMMA);\n        if (timeRange.size() != 2) {\n            log.warn(\"[matchTimeBetween][param({}) 时间区间参数格式错误]\", param);\n            return false;\n        }\n        LocalTime startTime = parseTime(timeRange.get(0).trim());\n        LocalTime endTime = parseTime(timeRange.get(1).trim());\n        // TODO @puhui999：hutool 里，看看有没 between 方法\n        return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime);\n    }\n\n    /**\n     * 解析时间字符串\n     * 支持 HH:mm 和 HH:mm:ss 两种格式\n     *\n     * @param timeStr 时间字符串\n     * @return 解析后的 LocalTime\n     */\n    public static LocalTime parseTime(String timeStr) {\n        Assert.isFalse(StrUtil.isBlank(timeStr), \"时间字符串不能为空\");\n        try {\n            // 尝试不同的时间格式\n            if (timeStr.length() == 5) { // HH:mm\n                return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT);\n            } else if (timeStr.length() == 8) { // HH:mm:ss\n                return LocalTime.parse(timeStr, TIME_FORMATTER);\n            } else {\n                throw new IllegalArgumentException(\"时间格式长度不正确，期望 HH:mm 或 HH:mm:ss 格式\");\n            }\n        } catch (Exception e) {\n            log.error(\"[parseTime][timeStr({}) 时间格式解析失败]\", timeStr, e);\n            throw new IllegalArgumentException(\"时间格式无效: \" + timeStr, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertRecoverSceneRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.action;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * IoT 告警恢复的 {@link IotSceneRuleAction} 实现类\n *\n * @author 芋道源码\n */\n@Component\npublic class IotAlertRecoverSceneRuleAction implements IotSceneRuleAction {\n\n    private static final String PROCESS_REMARK = \"告警自动回复，基于【{}】场景联动规则\";\n\n    @Resource\n    private IotAlertRecordService alertRecordService;\n\n    @Override\n    public void execute(IotDeviceMessage message,\n                        IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) throws Exception {\n        Long deviceId = message != null ? message.getDeviceId() : null;\n        List<IotAlertRecordDO> alertRecords = alertRecordService.getAlertRecordListBySceneRuleId(\n                rule.getId(), deviceId, false);\n        if (CollUtil.isEmpty(alertRecords)) {\n            return;\n        }\n        alertRecordService.processAlertRecordList(convertList(alertRecords, IotAlertRecordDO::getId),\n                StrUtil.format(PROCESS_REMARK, rule.getName()));\n    }\n\n    @Override\n    public IotSceneRuleActionTypeEnum getType() {\n        return IotSceneRuleActionTypeEnum.ALERT_RECOVER;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.action;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService;\nimport cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;\nimport cn.iocoder.yudao.module.system.api.mail.MailSendApi;\nimport cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;\nimport cn.iocoder.yudao.module.system.api.sms.SmsSendApi;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Nullable;\nimport javax.annotation.Resource;\nimport java.util.List;\n\n/**\n * IoT 告警触发的 {@link IotSceneRuleAction} 实现类\n *\n * @author 芋道源码\n */\n@Component\npublic class IotAlertTriggerSceneRuleAction implements IotSceneRuleAction {\n\n    @Resource\n    private IotAlertConfigService alertConfigService;\n    @Resource\n    private IotAlertRecordService alertRecordService;\n\n    @Resource\n    private SmsSendApi smsSendApi;\n    @Resource\n    private MailSendApi mailSendApi;\n    @Resource\n    private NotifyMessageSendApi notifyMessageSendApi;\n\n    @Override\n    public void execute(@Nullable IotDeviceMessage message,\n                        IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) throws Exception {\n        List<IotAlertConfigDO> alertConfigs = alertConfigService.getAlertConfigListBySceneRuleIdAndStatus(\n                rule.getId(), CommonStatusEnum.ENABLE.getStatus());\n        if (CollUtil.isEmpty(alertConfigs)) {\n            return;\n        }\n        alertConfigs.forEach(alertConfig -> {\n            // 记录告警记录，传递场景规则ID\n            alertRecordService.createAlertRecord(alertConfig, rule.getId(), message);\n            // 发送告警消息\n            sendAlertMessage(alertConfig, message);\n        });\n    }\n\n    private void sendAlertMessage(IotAlertConfigDO config, IotDeviceMessage deviceMessage) {\n        // TODO @芋艿：等场景联动开发完，再实现\n        // TODO @芋艿：短信\n        // TODO @芋艿：邮箱\n        // TODO @芋艿：站内信\n    }\n\n    @Override\n    public IotSceneRuleActionTypeEnum getType() {\n        return IotSceneRuleActionTypeEnum.ALERT_TRIGGER;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDevicePropertySetSceneRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.action;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\n/**\n * IoT 设备属性设置的 {@link IotSceneRuleAction} 实现类\n *\n * @author 芋道源码\n */\n@Component\n@Slf4j\npublic class IotDevicePropertySetSceneRuleAction implements IotSceneRuleAction {\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotDeviceMessageService deviceMessageService;\n\n    @Override\n    public void execute(IotDeviceMessage message,\n                        IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {\n        // 1. 参数校验\n        if (actionConfig.getDeviceId() == null) {\n            log.error(\"[execute][规则场景({}) 动作配置({}) 设备编号不能为空]\", rule.getId(), actionConfig);\n            return;\n        }\n        if (StrUtil.isEmpty(actionConfig.getIdentifier())) {\n            log.error(\"[execute][规则场景({}) 动作配置({}) 属性标识符不能为空]\", rule.getId(), actionConfig);\n            return;\n        }\n\n        // 2. 判断是否为全部设备\n        if (IotDeviceDO.DEVICE_ID_ALL.equals(actionConfig.getDeviceId())) {\n            executeForAllDevices(message, rule, actionConfig);\n        } else {\n            executeForSingleDevice(message, rule, actionConfig);\n        }\n    }\n\n    /**\n     * 为单个设备执行属性设置\n     */\n    private void executeForSingleDevice(IotDeviceMessage message,\n                                        IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {\n        // 1. 获取设备信息\n        IotDeviceDO device = deviceService.getDeviceFromCache(actionConfig.getDeviceId());\n        if (device == null) {\n            log.error(\"[executeForSingleDevice][规则场景({}) 动作配置({}) 对应的设备({}) 不存在]\",\n                    rule.getId(), actionConfig, actionConfig.getDeviceId());\n            return;\n        }\n\n        // 2. 执行属性设置\n        executePropertySetForDevice(rule, actionConfig, device);\n    }\n\n    /**\n     * 为产品下的所有设备执行属性设置\n     */\n    private void executeForAllDevices(IotDeviceMessage message,\n                                      IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {\n        // 1. 参数校验\n        if (actionConfig.getProductId() == null) {\n            log.error(\"[executeForAllDevices][规则场景({}) 动作配置({}) 产品编号不能为空]\", rule.getId(), actionConfig);\n            return;\n        }\n\n        // 2. 获取产品下的所有设备\n        List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(actionConfig.getProductId());\n        if (CollUtil.isEmpty(devices)) {\n            log.warn(\"[executeForAllDevices][规则场景({}) 动作配置({}) 产品({}) 下没有设备]\",\n                    rule.getId(), actionConfig, actionConfig.getProductId());\n            return;\n        }\n\n        // 3. 遍历所有设备执行属性设置\n        for (IotDeviceDO device : devices) {\n            executePropertySetForDevice(rule, actionConfig, device);\n        }\n    }\n\n    /**\n     * 为指定设备执行属性设置\n     */\n    private void executePropertySetForDevice(IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {\n        // 1. 构建属性设置消息\n        IotDeviceMessage downstreamMessage = buildPropertySetMessage(actionConfig, device);\n        if (downstreamMessage == null) {\n            log.error(\"[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 构建属性设置消息失败]\",\n                    rule.getId(), actionConfig, device.getId());\n            return;\n        }\n\n        // 2. 发送设备消息\n        try {\n            IotDeviceMessage result = deviceMessageService.sendDeviceMessage(downstreamMessage, device);\n            log.info(\"[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 属性设置消息({}) 发送成功]\",\n                    rule.getId(), actionConfig, device.getId(), result.getId());\n        } catch (Exception e) {\n            log.error(\"[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 属性设置消息发送失败]\",\n                    rule.getId(), actionConfig, device.getId(), e);\n        }\n    }\n\n    /**\n     * 构建属性设置消息\n     *\n     * @param actionConfig 动作配置\n     * @param device       设备信息\n     * @return 设备消息\n     */\n    private IotDeviceMessage buildPropertySetMessage(IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {\n        try {\n            // 属性设置参数格式: {\"properties\": {\"identifier\": value}}\n            Object params = MapUtil.of(\"properties\", MapUtil.of(actionConfig.getIdentifier(), actionConfig.getParams()));\n            return IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.PROPERTY_SET.getMethod(), params);\n        } catch (Exception e) {\n            log.error(\"[buildPropertySetMessage][构建属性设置消息异常]\", e);\n            return null;\n        }\n    }\n\n    @Override\n    public IotSceneRuleActionTypeEnum getType() {\n        return IotSceneRuleActionTypeEnum.DEVICE_PROPERTY_SET;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceServiceInvokeSceneRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.action;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * IoT 设备服务调用的 {@link IotSceneRuleAction} 实现类\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class IotDeviceServiceInvokeSceneRuleAction implements IotSceneRuleAction {\n\n    @Resource\n    private IotDeviceService deviceService;\n    @Resource\n    private IotDeviceMessageService deviceMessageService;\n\n    @Override\n    public void execute(IotDeviceMessage message,\n                        IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {\n        // 1. 参数校验\n        if (actionConfig.getDeviceId() == null) {\n            log.error(\"[execute][规则场景({}) 动作配置({}) 设备编号不能为空]\", rule.getId(), actionConfig);\n            return;\n        }\n        if (StrUtil.isEmpty(actionConfig.getIdentifier())) {\n            log.error(\"[execute][规则场景({}) 动作配置({}) 服务标识符不能为空]\", rule.getId(), actionConfig);\n            return;\n        }\n\n        // 2. 判断是否为全部设备\n        if (IotDeviceDO.DEVICE_ID_ALL.equals(actionConfig.getDeviceId())) {\n            executeForAllDevices(message, rule, actionConfig);\n        } else {\n            executeForSingleDevice(message, rule, actionConfig);\n        }\n    }\n\n    /**\n     * 为单个设备执行服务调用\n     */\n    private void executeForSingleDevice(IotDeviceMessage message,\n                                        IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {\n        // 1. 获取设备信息\n        IotDeviceDO device = deviceService.getDeviceFromCache(actionConfig.getDeviceId());\n        if (device == null) {\n            log.error(\"[executeForSingleDevice][规则场景({}) 动作配置({}) 对应的设备({}) 不存在]\",\n                    rule.getId(), actionConfig, actionConfig.getDeviceId());\n            return;\n        }\n\n        // 2. 执行服务调用\n        executeServiceInvokeForDevice(rule, actionConfig, device);\n    }\n\n    /**\n     * 为产品下的所有设备执行服务调用\n     */\n    private void executeForAllDevices(IotDeviceMessage message,\n                                      IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {\n        // 1. 参数校验\n        if (actionConfig.getProductId() == null) {\n            log.error(\"[executeForAllDevices][规则场景({}) 动作配置({}) 产品编号不能为空]\", rule.getId(), actionConfig);\n            return;\n        }\n\n        // 2. 获取产品下的所有设备\n        List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(actionConfig.getProductId());\n        if (CollUtil.isEmpty(devices)) {\n            log.warn(\"[executeForAllDevices][规则场景({}) 动作配置({}) 产品({}) 下没有设备]\",\n                    rule.getId(), actionConfig, actionConfig.getProductId());\n            return;\n        }\n\n        // 3. 遍历所有设备执行服务调用\n        for (IotDeviceDO device : devices) {\n            executeServiceInvokeForDevice(rule, actionConfig, device);\n        }\n    }\n\n    /**\n     * 为指定设备执行服务调用\n     */\n    private void executeServiceInvokeForDevice(IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {\n        // 1. 构建服务调用消息\n        IotDeviceMessage downstreamMessage = buildServiceInvokeMessage(actionConfig, device);\n        if (downstreamMessage == null) {\n            log.error(\"[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 构建服务调用消息失败]\",\n                    rule.getId(), actionConfig, device.getId());\n            return;\n        }\n\n        // 2. 发送设备消息\n        try {\n            IotDeviceMessage result = deviceMessageService.sendDeviceMessage(downstreamMessage, device);\n            log.info(\"[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 服务调用消息({}) 发送成功]\",\n                    rule.getId(), actionConfig, device.getId(), result.getId());\n        } catch (Exception e) {\n            log.error(\"[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 服务调用消息发送失败]\",\n                    rule.getId(), actionConfig, device.getId(), e);\n        }\n    }\n\n    /**\n     * 构建服务调用消息\n     *\n     * @param actionConfig 动作配置\n     * @param device       设备信息\n     * @return 设备消息\n     */\n    private IotDeviceMessage buildServiceInvokeMessage(IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {\n        try {\n            // 服务调用参数格式: {\"identifier\": \"serviceId\", \"params\": {...}}\n            Object params = MapUtil.builder()\n                    .put(\"identifier\", actionConfig.getIdentifier())\n                    .put(\"params\", actionConfig.getParams() != null ? actionConfig.getParams() : Collections.emptyMap())\n                    .build();\n            return IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod(), params);\n        } catch (Exception e) {\n            log.error(\"[buildServiceInvokeMessage][构建服务调用消息异常]\", e);\n            return null;\n        }\n    }\n\n    @Override\n    public IotSceneRuleActionTypeEnum getType() {\n        return IotSceneRuleActionTypeEnum.DEVICE_SERVICE_INVOKE;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotSceneRuleAction.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.action;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;\n\nimport javax.annotation.Nullable;\n\n/**\n * IoT 场景联动的执行器接口\n *\n * @author 芋道源码\n */\npublic interface IotSceneRuleAction {\n\n    /**\n     * 执行场景联动\n     *\n     * @param message 消息，允许空\n     *                1. 空的情况：定时触发\n     *                2. 非空的情况：设备触发\n     * @param rule    规则\n     * @param actionConfig  执行配置（实际对应规则里的哪条执行配置）\n     */\n    void execute(@Nullable IotDeviceMessage message,\n                 IotSceneRuleDO rule,\n                 IotSceneRuleDO.Action actionConfig) throws Exception;\n\n    /**\n     * 获得类型\n     *\n     * @return 类型\n     */\n    IotSceneRuleActionTypeEnum getType();\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;\n\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition.IotSceneRuleConditionMatcher;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger.IotSceneRuleTriggerMatcher;\n\n/**\n * IoT 场景规则匹配器基础接口：定义所有匹配器的通用行为，包括优先级、名称和启用状态\n *\n * - {@link IotSceneRuleTriggerMatcher} 触发器匹配器\n * - {@link IotSceneRuleConditionMatcher} 条件匹配器\n *\n * @author HUIHUI\n */\npublic interface IotSceneRuleMatcher {\n\n    /**\n     * 获取匹配优先级（数值越小优先级越高）\n     * <p>\n     * 用于在多个匹配器支持同一类型时确定优先级\n     *\n     * @return 优先级数值\n     */\n    default int getPriority() {\n        return 100;\n    }\n\n    /**\n     * 是否启用该匹配器\n     * <p>\n     * 可用于动态开关某些匹配器\n     *\n     * @return 是否启用\n     */\n    default boolean isEnabled() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherHelper.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;\n\nimport cn.hutool.core.text.CharPool;\nimport cn.hutool.core.util.NumberUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n/**\n * IoT 场景规则匹配器工具类：提供通用的条件评估逻辑和工具方法，供触发器和条件匹配器使用\n *\n * 该类包含了匹配器实现中常用的工具方法，如条件评估、参数校验、日志记录等\n *\n * @author HUIHUI\n */\n@Slf4j\npublic final class IotSceneRuleMatcherHelper {\n\n    /**\n     * 私有构造函数，防止实例化\n     */\n    private IotSceneRuleMatcherHelper() {\n    }\n\n    /**\n     * 评估条件是否匹配\n     *\n     * @param sourceValue 源值（来自消息）\n     * @param operator    操作符\n     * @param paramValue  参数值（来自条件配置）\n     * @return 是否匹配\n     */\n    public static boolean evaluateCondition(Object sourceValue, String operator, String paramValue) {\n        try {\n            // 1. 校验操作符是否合法\n            IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);\n            if (operatorEnum == null) {\n                log.warn(\"[evaluateCondition][operator({}) 操作符无效]\", operator);\n                return false;\n            }\n\n            // 2. 构建 Spring 表达式变量\n            return evaluateConditionWithOperatorEnum(sourceValue, operatorEnum, paramValue);\n        } catch (Exception e) {\n            log.error(\"[evaluateCondition][sourceValue({}) operator({}) paramValue({}) 条件评估异常]\",\n                    sourceValue, operator, paramValue, e);\n            return false;\n        }\n    }\n\n    /**\n     * 使用操作符枚举评估条件是否匹配\n     *\n     * @param sourceValue  源值（来自消息）\n     * @param operatorEnum 操作符枚举\n     * @param paramValue   参数值（来自条件配置）\n     * @return 是否匹配\n     */\n    @SuppressWarnings(\"DataFlowIssue\")\n    public static boolean evaluateConditionWithOperatorEnum(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) {\n        try {\n            // 1. 构建 Spring 表达式变量\n            Map<String, Object> springExpressionVariables = buildSpringExpressionVariables(sourceValue, operatorEnum, paramValue);\n\n            // 2. 计算 Spring 表达式\n            return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);\n        } catch (Exception e) {\n            log.error(\"[evaluateConditionWithOperatorEnum][sourceValue({}) operatorEnum({}) paramValue({}) 条件评估异常]\",\n                    sourceValue, operatorEnum, paramValue, e);\n            return false;\n        }\n    }\n\n    /**\n     * 构建 Spring 表达式变量\n     */\n    private static Map<String, Object> buildSpringExpressionVariables(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) {\n        Map<String, Object> springExpressionVariables = new HashMap<>();\n\n        // 设置源值\n        springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, StrUtil.toString(sourceValue));\n\n        // 处理参数值\n        if (StrUtil.isNotBlank(paramValue)) {\n            List<String> parameterValues = StrUtil.splitTrim(paramValue, CharPool.COMMA);\n\n            // 设置原始参数值\n            springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, paramValue);\n            springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues);\n\n            // 特殊处理：解决数字比较问题\n            // Spring 表达式基于 compareTo 方法，对数字的比较存在问题，需要转换为数字类型\n            if (isNumericComparisonOperator(operatorEnum) && isNumericComparison(sourceValue, parameterValues)) {\n                springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE,\n                        NumberUtil.parseDouble(String.valueOf(sourceValue)));\n                springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,\n                        NumberUtil.parseDouble(paramValue));\n                springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,\n                        convertList(parameterValues, NumberUtil::parseDouble));\n            }\n        }\n\n        return springExpressionVariables;\n    }\n\n    /**\n     * 判断是否为数字比较操作符\n     */\n    private static boolean isNumericComparisonOperator(IotSceneRuleConditionOperatorEnum operatorEnum) {\n        return ObjectUtils.equalsAny(operatorEnum,\n                IotSceneRuleConditionOperatorEnum.BETWEEN,\n                IotSceneRuleConditionOperatorEnum.NOT_BETWEEN,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS,\n                IotSceneRuleConditionOperatorEnum.LESS_THAN,\n                IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS);\n    }\n\n    /**\n     * 判断是否为数字比较场景\n     */\n    private static boolean isNumericComparison(Object sourceValue, List<String> parameterValues) {\n        return NumberUtil.isNumber(String.valueOf(sourceValue)) && NumberUtils.isAllNumber(parameterValues);\n    }\n\n    // ========== 【触发器】相关工具方法 ==========\n\n    /**\n     * 检查基础触发器参数是否有效\n     *\n     * @param trigger 触发器配置\n     * @return 是否有效\n     */\n    public static boolean isBasicTriggerValid(IotSceneRuleDO.Trigger trigger) {\n        return trigger != null && trigger.getType() != null;\n    }\n\n    /**\n     * 检查触发器操作符和值是否有效\n     *\n     * @param trigger 触发器配置\n     * @return 是否有效\n     */\n    public static boolean isTriggerOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) {\n        return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue());\n    }\n\n    /**\n     * 记录触发器匹配成功日志\n     *\n     * @param message     设备消息\n     * @param trigger     触发器配置\n     */\n    public static void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {\n        log.debug(\"[isMatched][message({}) trigger({}) 匹配触发器成功]\", message.getRequestId(), trigger.getType());\n    }\n\n    /**\n     * 记录触发器匹配失败日志\n     *\n     * @param message     设备消息\n     * @param trigger     触发器配置\n     * @param reason      失败原因\n     */\n    public static void logTriggerMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {\n        log.debug(\"[isMatched][message({}) trigger({}) reason({}) 匹配触发器失败]\", message.getRequestId(), trigger.getType(), reason);\n    }\n\n    // ========== 【条件】相关工具方法 ==========\n\n    /**\n     * 检查基础条件参数是否有效\n     *\n     * @param condition 触发条件\n     * @return 是否有效\n     */\n    public static boolean isBasicConditionValid(IotSceneRuleDO.TriggerCondition condition) {\n        return condition != null && condition.getType() != null;\n    }\n\n    /**\n     * 检查条件操作符和参数是否有效\n     *\n     * @param condition 触发条件\n     * @return 是否有效\n     */\n    public static boolean isConditionOperatorAndParamValid(IotSceneRuleDO.TriggerCondition condition) {\n        return StrUtil.isNotBlank(condition.getOperator()) && StrUtil.isNotBlank(condition.getParam());\n    }\n\n    /**\n     * 记录条件匹配成功日志\n     *\n     * @param message     设备消息\n     * @param condition   触发条件\n     */\n    public static void logConditionMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {\n        log.debug(\"[isMatched][message({}) condition({}) 匹配条件成功]\", message.getRequestId(), condition.getType());\n    }\n\n    /**\n     * 记录条件匹配失败日志\n     *\n     * @param message     设备消息\n     * @param condition   触发条件\n     * @param reason      失败原因\n     */\n    public static void logConditionMatchFailure(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) {\n        log.debug(\"[isMatched][message({}) condition({}) reason({}) 匹配条件失败]\", message.getRequestId(), condition.getType(), reason);\n    }\n\n    // ========== 【通用】工具方法 ==========\n\n    /**\n     * 检查标识符是否匹配\n     *\n     * @param expectedIdentifier 期望的标识符\n     * @param actualIdentifier   实际的标识符\n     * @return 是否匹配\n     */\n    public static boolean isIdentifierMatched(String expectedIdentifier, String actualIdentifier) {\n        return StrUtil.isNotBlank(expectedIdentifier) && expectedIdentifier.equals(actualIdentifier);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition.IotSceneRuleConditionMatcher;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger.IotSceneRuleTriggerMatcher;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * IoT 场景规则匹配器统一管理器：负责管理所有匹配器（触发器匹配器和条件匹配器），并提供统一的匹配入口\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class IotSceneRuleMatcherManager {\n\n    /**\n     * 触发器匹配器映射表\n     */\n    private final Map<IotSceneRuleTriggerTypeEnum, IotSceneRuleTriggerMatcher> triggerMatchers;\n\n    /**\n     * 条件匹配器映射表\n     */\n    private final Map<IotSceneRuleConditionTypeEnum, IotSceneRuleConditionMatcher> conditionMatchers;\n\n    public IotSceneRuleMatcherManager(List<IotSceneRuleMatcher> matchers) {\n        if (CollUtil.isEmpty(matchers)) {\n            log.warn(\"[IotSceneRuleMatcherManager][没有找到任何匹配器]\");\n            this.triggerMatchers = new HashMap<>();\n            this.conditionMatchers = new HashMap<>();\n            return;\n        }\n\n        // 1.1 按优先级排序并过滤启用的匹配器\n        List<IotSceneRuleMatcher> allMatchers = matchers.stream()\n                .filter(IotSceneRuleMatcher::isEnabled)\n                .sorted(Comparator.comparing(IotSceneRuleMatcher::getPriority))\n                .collect(Collectors.toList());\n        // 1.2 分离触发器匹配器和条件匹配器\n        List<IotSceneRuleTriggerMatcher> triggerMatchers = allMatchers.stream()\n                .filter(matcher -> matcher instanceof IotSceneRuleTriggerMatcher)\n                .map(matcher -> (IotSceneRuleTriggerMatcher) matcher)\n                .collect(Collectors.toList());\n        List<IotSceneRuleConditionMatcher> conditionMatchers = allMatchers.stream()\n                .filter(matcher -> matcher instanceof IotSceneRuleConditionMatcher)\n                .map(matcher -> (IotSceneRuleConditionMatcher) matcher)\n                .collect(Collectors.toList());\n\n        // 2.1 构建触发器匹配器映射表\n        this.triggerMatchers = convertMap(triggerMatchers, IotSceneRuleTriggerMatcher::getSupportedTriggerType,\n                Function.identity(),\n                (existing, replacement) -> {\n                    log.warn(\"[IotSceneRuleMatcherManager][触发器类型({})存在多个匹配器，使用优先级更高的: {}]\",\n                            existing.getSupportedTriggerType(),\n                            existing.getPriority() <= replacement.getPriority() ?\n                                    existing.getSupportedTriggerType() : replacement.getSupportedTriggerType());\n                    return existing.getPriority() <= replacement.getPriority() ? existing : replacement;\n                }, LinkedHashMap::new);\n        // 2.2 构建条件匹配器映射表\n        this.conditionMatchers = convertMap(conditionMatchers, IotSceneRuleConditionMatcher::getSupportedConditionType,\n                Function.identity(),\n                (existing, replacement) -> {\n                    log.warn(\"[IotSceneRuleMatcherManager][条件类型({})存在多个匹配器，使用优先级更高的: {}]\",\n                            existing.getSupportedConditionType(),\n                            existing.getPriority() <= replacement.getPriority() ?\n                                    existing.getSupportedConditionType() : replacement.getSupportedConditionType());\n                    return existing.getPriority() <= replacement.getPriority() ? existing : replacement;\n                },\n                LinkedHashMap::new);\n\n        // 3. 日志输出初始化信息\n        log.info(\"[IotSceneRuleMatcherManager][初始化完成，共加载({})个匹配器，其中触发器匹配器({})个，条件匹配器({})个]\",\n                allMatchers.size(), this.triggerMatchers.size(), this.conditionMatchers.size());\n        this.triggerMatchers.forEach((type, matcher) ->\n                log.info(\"[IotSceneRuleMatcherManager][触发器匹配器类型: ({}), 优先级: ({})] \", type, matcher.getPriority()));\n        this.conditionMatchers.forEach((type, matcher) ->\n                log.info(\"[IotSceneRuleMatcherManager][条件匹配器类型: ({}), 优先级: ({})]\", type, matcher.getPriority()));\n    }\n\n    /**\n     * 检查触发器是否匹配消息（主条件匹配）\n     *\n     * @param message 设备消息\n     * @param trigger 触发器配置\n     * @return 是否匹配\n     */\n    public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {\n        if (message == null || trigger == null || trigger.getType() == null) {\n            log.debug(\"[isMatched][message({}) trigger({}) 参数无效]\", message, trigger);\n            return false;\n        }\n        IotSceneRuleTriggerTypeEnum triggerType = IotSceneRuleTriggerTypeEnum.typeOf(trigger.getType());\n        if (triggerType == null) {\n            log.warn(\"[isMatched][triggerType({}) 未知的触发器类型]\", trigger.getType());\n            return false;\n        }\n        IotSceneRuleTriggerMatcher matcher = triggerMatchers.get(triggerType);\n        if (matcher == null) {\n            log.warn(\"[isMatched][triggerType({}) 没有对应的匹配器]\", triggerType);\n            return false;\n        }\n\n        try {\n            return matcher.matches(message, trigger);\n        } catch (Exception e) {\n            log.error(\"[isMatched][触发器匹配异常] message: {}, trigger: {}\", message, trigger, e);\n            return false;\n        }\n    }\n\n    /**\n     * 检查子条件是否匹配消息\n     *\n     * @param message   设备消息\n     * @param condition 触发条件\n     * @return 是否匹配\n     */\n    public boolean isConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {\n        if (message == null || condition == null || condition.getType() == null) {\n            log.debug(\"[isConditionMatched][message({}) condition({}) 参数无效]\", message, condition);\n            return false;\n        }\n\n        // 1. 根据条件类型查找对应的匹配器\n        IotSceneRuleConditionTypeEnum conditionType = IotSceneRuleConditionTypeEnum.typeOf(condition.getType());\n        if (conditionType == null) {\n            log.warn(\"[isConditionMatched][conditionType({}) 未知的条件类型]\", condition.getType());\n            return false;\n        }\n        IotSceneRuleConditionMatcher matcher = conditionMatchers.get(conditionType);\n        if (matcher == null) {\n            log.warn(\"[isConditionMatched][conditionType({}) 没有对应的匹配器]\", conditionType);\n            return false;\n        }\n\n        // 2. 执行匹配逻辑\n        try {\n            return matcher.matches(message, condition);\n        } catch (Exception e) {\n            log.error(\"[isConditionMatched][message({}) condition({}) 条件匹配异常]\", message, condition, e);\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleTimeHelper;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * 当前时间条件匹配器：处理时间相关的子条件匹配逻辑\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class IotCurrentTimeConditionMatcher implements IotSceneRuleConditionMatcher {\n\n    @Override\n    public IotSceneRuleConditionTypeEnum getSupportedConditionType() {\n        return IotSceneRuleConditionTypeEnum.CURRENT_TIME;\n    }\n\n    @Override\n    public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {\n        // 1.1 基础参数校验\n        if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"条件基础参数无效\");\n            return false;\n        }\n\n        // 1.2 检查操作符和参数是否有效\n        if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"操作符或参数无效\");\n            return false;\n        }\n\n        // 1.3 验证操作符是否为支持的时间操作符\n        String operator = condition.getOperator();\n        IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);\n        if (operatorEnum == null) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"无效的操作符: \" + operator);\n            return false;\n        }\n\n        if (IotSceneRuleTimeHelper.isTimeOperator(operatorEnum)) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"不支持的时间操作符: \" + operator);\n            return false;\n        }\n\n        // 2.1 执行时间匹配\n        boolean matched = IotSceneRuleTimeHelper.executeTimeMatching(operatorEnum, condition.getParam());\n\n        // 2.2 记录匹配结果\n        if (matched) {\n            IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition);\n        } else {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"时间条件不匹配\");\n        }\n\n        return matched;\n    }\n\n    @Override\n    public int getPriority() {\n        return 40; // 较低优先级\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;\n\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;\nimport org.springframework.stereotype.Component;\n\n\n/**\n * 设备属性条件匹配器：处理设备属性相关的子条件匹配逻辑\n *\n * @author HUIHUI\n */\n@Component\npublic class IotDevicePropertyConditionMatcher implements IotSceneRuleConditionMatcher {\n\n    @Override\n    public IotSceneRuleConditionTypeEnum getSupportedConditionType() {\n        return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY;\n    }\n\n    @Override\n    public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {\n        // 1.1 基础参数校验\n        if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"条件基础参数无效\");\n            return false;\n        }\n\n        // 1.2 检查标识符是否匹配\n        String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);\n        if (!IotSceneRuleMatcherHelper.isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"标识符不匹配，期望: \" + condition.getIdentifier() + \", 实际: \" + messageIdentifier);\n            return false;\n        }\n\n        // 1.3 检查操作符和参数是否有效\n        if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"操作符或参数无效\");\n            return false;\n        }\n\n        // 2.1. 获取属性值 - 使用工具类方法正确提取属性值\n        Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, condition.getIdentifier());\n        if (propertyValue == null) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"消息中属性值为空或未找到指定属性\");\n            return false;\n        }\n\n        // 2.2 使用条件评估器进行匹配\n        boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, condition.getOperator(), condition.getParam());\n        if (matched) {\n            IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition);\n        } else {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"设备属性条件不匹配\");\n        }\n        return matched;\n    }\n\n    @Override\n    public int getPriority() {\n        return 25; // 中等优先级\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;\nimport org.springframework.stereotype.Component;\n\n/**\n * 设备状态条件匹配器：处理设备状态相关的子条件匹配逻辑\n *\n * @author HUIHUI\n */\n@Component\npublic class IotDeviceStateConditionMatcher implements IotSceneRuleConditionMatcher {\n\n    @Override\n    public IotSceneRuleConditionTypeEnum getSupportedConditionType() {\n        return IotSceneRuleConditionTypeEnum.DEVICE_STATE;\n    }\n\n    @Override\n    public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {\n        // 1.1 基础参数校验\n        if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"条件基础参数无效\");\n            return false;\n        }\n\n        // 1.2 检查操作符和参数是否有效\n        if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"操作符或参数无效\");\n            return false;\n        }\n\n        // 2.1 获取设备状态值 - 使用工具类方法正确提取状态值\n        // 对于设备状态条件，状态值通过 getIdentifier 获取（实际是从 params.state 字段）\n        String stateValue = IotDeviceMessageUtils.getIdentifier(message);\n        if (stateValue == null) {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"消息中设备状态值为空\");\n            return false;\n        }\n\n        // 2.2 使用条件评估器进行匹配\n        boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, condition.getOperator(), condition.getParam());\n        if (matched) {\n            IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition);\n        } else {\n            IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, \"设备状态条件不匹配\");\n        }\n        return matched;\n    }\n\n    @Override\n    public int getPriority() {\n        return 30; // 中等优先级\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotSceneRuleConditionMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcher;\n\n/**\n * IoT 场景规则条件匹配器接口：专门处理子条件的匹配逻辑，如设备状态、属性值、时间条件等\n *\n * 条件匹配器负责判断设备消息是否满足场景规则的附加条件，在触发器匹配成功后进行进一步的条件筛选\n *\n * @author HUIHUI\n */\npublic interface IotSceneRuleConditionMatcher extends IotSceneRuleMatcher {\n\n    /**\n     * 获取支持的条件类型\n     *\n     * @return 条件类型枚举\n     */\n    IotSceneRuleConditionTypeEnum getSupportedConditionType();\n\n    /**\n     * 检查条件是否匹配消息\n     * <p>\n     * 判断设备消息是否满足指定的触发条件\n     *\n     * @param message   设备消息\n     * @param condition 触发条件\n     * @return 是否匹配\n     */\n    boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;\nimport org.springframework.stereotype.Component;\n\n/**\n * 设备事件上报触发器匹配器：处理设备事件上报的触发器匹配逻辑\n *\n * @author HUIHUI\n */\n@Component\npublic class IotDeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher {\n\n    @Override\n    public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {\n        return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST;\n    }\n\n    @Override\n    public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {\n        // 1.1 基础参数校验\n        if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"触发器基础参数无效\");\n            return false;\n        }\n\n        // 1.2 检查消息方法是否匹配\n        if (!IotDeviceMessageMethodEnum.EVENT_POST.getMethod().equals(message.getMethod())) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"消息方法不匹配，期望: \" +\n                    IotDeviceMessageMethodEnum.EVENT_POST.getMethod() + \", 实际: \" + message.getMethod());\n            return false;\n        }\n\n        // 1.3 检查标识符是否匹配\n        String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);\n        if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"标识符不匹配，期望: \" +\n                    trigger.getIdentifier() + \", 实际: \" + messageIdentifier);\n            return false;\n        }\n\n        // 2. 对于事件触发器，通常不需要检查操作符和值，只要事件发生即匹配\n        // 但如果配置了操作符和值，则需要进行条件匹配\n        if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) {\n            Object eventParams = message.getParams();\n            if (eventParams == null) {\n                IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"消息中事件参数为空\");\n                return false;\n            }\n\n            boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(eventParams, trigger.getOperator(), trigger.getValue());\n            if (!matched) {\n                IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"事件数据条件不匹配\");\n                return false;\n            }\n        }\n\n        IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);\n        return true;\n    }\n\n    @Override\n    public int getPriority() {\n        return 30; // 中等优先级\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;\nimport org.springframework.stereotype.Component;\n\n/**\n * 设备属性上报触发器匹配器：处理设备属性数据上报的触发器匹配逻辑\n *\n * @author HUIHUI\n */\n@Component\npublic class IotDevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatcher {\n\n    @Override\n    public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {\n        return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST;\n    }\n\n    @Override\n    public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {\n        // 1.1 基础参数校验\n        if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"触发器基础参数无效\");\n            return false;\n        }\n\n        // 1.2 检查消息方法是否匹配\n        if (!IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod().equals(message.getMethod())) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"消息方法不匹配，期望: \" +\n                    IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod() + \", 实际: \" + message.getMethod());\n            return false;\n        }\n\n        // 1.3 检查消息中是否包含触发器指定的属性标识符\n        // 注意：属性上报可能同时上报多个属性，所以需要判断 trigger.getIdentifier() 是否在 message 的 params 中\n        if (IotDeviceMessageUtils.notContainsIdentifier(message, trigger.getIdentifier())) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"消息中不包含属性: \" +\n                    trigger.getIdentifier());\n            return false;\n        }\n\n        // 1.4 检查操作符和值是否有效\n        if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"操作符或值无效\");\n            return false;\n        }\n\n        // 2.1 获取属性值 - 使用工具类方法正确提取属性值\n        Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, trigger.getIdentifier());\n        if (propertyValue == null) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"消息中属性值为空或未找到指定属性\");\n            return false;\n        }\n\n        // 2.2 使用条件评估器进行匹配\n        boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue());\n        if (matched) {\n            IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);\n        } else {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"属性值条件不匹配\");\n        }\n        return matched;\n    }\n\n    @Override\n    public int getPriority() {\n        return 20; // 中等优先级\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Map;\n\n/**\n * 设备服务调用触发器匹配器：处理设备服务调用的触发器匹配逻辑\n *\n * @author HUIHUI\n */\n@Component\npublic class IotDeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMatcher {\n\n    @Override\n    public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {\n        return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE;\n    }\n\n    @Override\n    public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {\n        // 1.1 基础参数校验\n        if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"触发器基础参数无效\");\n            return false;\n        }\n        // 1.2 检查消息方法是否匹配\n        if (!IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod().equals(message.getMethod())) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"消息方法不匹配，期望: \" + IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod() + \", 实际: \" + message.getMethod());\n            return false;\n        }\n        // 1.3 检查标识符是否匹配\n        String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);\n        if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"标识符不匹配，期望: \" + trigger.getIdentifier() + \", 实际: \" + messageIdentifier);\n            return false;\n        }\n\n        // 2. 检查是否配置了参数条件\n        if (hasParameterCondition(trigger)) {\n            return matchParameterCondition(message, trigger);\n        }\n\n        // 3. 无参数条件时，标识符匹配即成功\n        IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);\n        return true;\n    }\n\n    /**\n     * 判断触发器是否配置了参数条件\n     *\n     * @param trigger 触发器配置\n     * @return 是否配置了参数条件\n     */\n    private boolean hasParameterCondition(IotSceneRuleDO.Trigger trigger) {\n        return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue());\n    }\n\n    /**\n     * 匹配参数条件\n     *\n     * @param message 设备消息\n     * @param trigger 触发器配置\n     * @return 是否匹配\n     */\n    private boolean matchParameterCondition(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {\n        // 1.1 从消息中提取服务调用的输入参数\n        Map<String, Object> inputParams = IotDeviceMessageUtils.extractServiceInputParams(message);\n        // TODO @puhui999：要考虑 empty 的情况么？\n        if (inputParams == null) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"消息中缺少服务输入参数\");\n            return false;\n        }\n        // 1.2 获取要匹配的参数值（使用 identifier 作为参数名）\n        Object paramValue = inputParams.get(trigger.getIdentifier());\n        if (paramValue == null) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"服务输入参数中缺少指定参数: \" + trigger.getIdentifier());\n            return false;\n        }\n\n        // 2. 使用条件评估器进行匹配\n        boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(paramValue, trigger.getOperator(), trigger.getValue());\n        if (matched) {\n            IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);\n        } else {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"服务输入参数条件不匹配\");\n        }\n        return matched;\n    }\n\n    @Override\n    public int getPriority() {\n        return 40; // 较低优先级\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;\nimport org.springframework.stereotype.Component;\n\n/**\n * 设备状态更新触发器匹配器：处理设备上下线状态变更的触发器匹配逻辑\n *\n * @author HUIHUI\n */\n@Component\npublic class IotDeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatcher {\n\n    @Override\n    public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {\n        return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE;\n    }\n\n    @Override\n    public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {\n        // 1.1 基础参数校验\n        if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"触发器基础参数无效\");\n            return false;\n        }\n\n        // 1.2 检查消息方法是否匹配\n        if (!IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod().equals(message.getMethod())) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"消息方法不匹配，期望: \" +\n                    IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod() + \", 实际: \" + message.getMethod());\n            return false;\n        }\n\n        // 1.3 检查操作符和值是否有效\n        if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"操作符或值无效\");\n            return false;\n        }\n\n        // 2.1 获取设备状态值 - 使用工具类方法正确提取状态值\n        // 对于状态更新消息，状态值通过 getIdentifier 获取（实际是从 params.state 字段）\n        String stateIdentifier = IotDeviceMessageUtils.getIdentifier(message);\n        if (stateIdentifier == null) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"消息中设备状态值为空\");\n            return false;\n        }\n\n        // 2.2 使用条件评估器进行匹配\n        // 状态值通常是字符串或数字，直接使用标识符作为状态值\n        boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateIdentifier, trigger.getOperator(), trigger.getValue());\n        if (matched) {\n            IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);\n        } else {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"状态值条件不匹配\");\n        }\n        return matched;\n    }\n\n    @Override\n    public int getPriority() {\n        return 10; // 高优先级\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotSceneRuleTriggerMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcher;\n\n/**\n * IoT 场景规则触发器匹配器接口：专门处理主触发条件的匹配逻辑，如设备消息类型、定时器等\n *\n * 触发器匹配器负责判断设备消息是否满足场景规则的主触发条件，是场景规则执行的第一道门槛\n *\n * @author HUIHUI\n */\npublic interface IotSceneRuleTriggerMatcher extends IotSceneRuleMatcher {\n\n    /**\n     * 获取支持的触发器类型\n     *\n     * @return 触发器类型枚举\n     */\n    IotSceneRuleTriggerTypeEnum getSupportedTriggerType();\n\n    /**\n     * 检查触发器是否匹配消息\n     * <p>\n     * 判断设备消息是否满足指定的触发器条件\n     *\n     * @param message 设备消息\n     * @param trigger 触发器配置\n     * @return 是否匹配\n     */\n    boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger);\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcher.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;\nimport org.quartz.CronExpression;\nimport org.springframework.stereotype.Component;\n\n/**\n * 定时触发器匹配器：处理定时触发的触发器匹配逻辑\n *\n * 注意：定时触发器不依赖设备消息，主要用于定时任务场景\n *\n * @author HUIHUI\n */\n@Component\npublic class IotTimerTriggerMatcher implements IotSceneRuleTriggerMatcher {\n\n    @Override\n    public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {\n        return IotSceneRuleTriggerTypeEnum.TIMER;\n    }\n\n    @Override\n    public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {\n        // 1.1 基础参数校验\n        if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"触发器基础参数无效\");\n            return false;\n        }\n\n        // 1.2 检查 CRON 表达式是否存在\n        if (StrUtil.isBlank(trigger.getCronExpression())) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"定时触发器缺少 CRON 表达式\");\n            return false;\n        }\n\n        // 1.3 定时触发器通常不依赖具体的设备消息\n        // 它是通过定时任务调度器触发的，这里主要是验证配置的有效性\n        if (!CronExpression.isValidExpression(trigger.getCronExpression())) {\n            IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, \"CRON 表达式格式无效: \" + trigger.getCronExpression());\n            return false;\n        }\n\n        IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);\n        return true;\n    }\n\n    @Override\n    public int getPriority() {\n        return 50; // 最低优先级，因为定时触发器不依赖消息\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandler.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.timer;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;\nimport cn.iocoder.yudao.module.iot.job.rule.IotSceneRuleJob;\nimport lombok.extern.slf4j.Slf4j;\nimport org.quartz.SchedulerException;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;\n\n/**\n * IoT 场景规则定时触发器处理器：负责管理定时触发器的注册、更新、删除等操作\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class IotSceneRuleTimerHandler {\n\n    @Resource(name = \"iotSchedulerManager\")\n    private IotSchedulerManager schedulerManager;\n\n    /**\n     * 注册场景规则的定时触发器\n     *\n     * @param sceneRule 场景规则\n     */\n    public void registerTimerTriggers(IotSceneRuleDO sceneRule) {\n        // 1. 过滤出定时触发器\n        if (sceneRule == null || CollUtil.isEmpty(sceneRule.getTriggers())) {\n            return;\n        }\n        List<IotSceneRuleDO.Trigger> timerTriggers = filterList(sceneRule.getTriggers(),\n                trigger -> ObjUtil.equals(trigger.getType(), IotSceneRuleTriggerTypeEnum.TIMER.getType()));\n        if (CollUtil.isEmpty(timerTriggers)) {\n            return;\n        }\n\n        // 2. 注册每个定时触发器\n        timerTriggers.forEach(trigger -> registerSingleTimerTrigger(sceneRule, trigger));\n    }\n\n    /**\n     * 更新场景规则的定时触发器\n     *\n     * @param sceneRule 场景规则\n     */\n    public void updateTimerTriggers(IotSceneRuleDO sceneRule) {\n        if (sceneRule == null) {\n            return;\n        }\n\n        // 1. 先删除旧的定时任务\n        unregisterTimerTriggers(sceneRule.getId());\n\n        // 2.1 如果场景规则已禁用，则不重新注册\n        if (CommonStatusEnum.isDisable(sceneRule.getStatus())) {\n            log.info(\"[updateTimerTriggers][场景规则({}) 已禁用，不注册定时触发器]\", sceneRule.getId());\n            return;\n        }\n\n        // 2.2 重新注册定时触发器\n        registerTimerTriggers(sceneRule);\n    }\n\n    /**\n     * 注销场景规则的定时触发器\n     *\n     * @param sceneRuleId 场景规则 ID\n     */\n    public void unregisterTimerTriggers(Long sceneRuleId) {\n        if (sceneRuleId == null) {\n            return;\n        }\n\n        String jobName = buildJobName(sceneRuleId);\n        try {\n            schedulerManager.deleteJob(jobName);\n            log.info(\"[unregisterTimerTriggers][场景规则({}) 定时触发器注销成功]\", sceneRuleId);\n        } catch (SchedulerException e) {\n            log.error(\"[unregisterTimerTriggers][场景规则({}) 定时触发器注销失败]\", sceneRuleId, e);\n        }\n    }\n\n    /**\n     * 暂停场景规则的定时触发器\n     *\n     * @param sceneRuleId 场景规则 ID\n     */\n    public void pauseTimerTriggers(Long sceneRuleId) {\n        if (sceneRuleId == null) {\n            return;\n        }\n\n        String jobName = buildJobName(sceneRuleId);\n        try {\n            schedulerManager.pauseJob(jobName);\n            log.info(\"[pauseTimerTriggers][场景规则({}) 定时触发器暂停成功]\", sceneRuleId);\n        } catch (SchedulerException e) {\n            log.error(\"[pauseTimerTriggers][场景规则({}) 定时触发器暂停失败]\", sceneRuleId, e);\n        }\n    }\n\n    /**\n     * 注册单个定时触发器\n     *\n     * @param sceneRule 场景规则\n     * @param trigger   定时触发器配置\n     */\n    private void registerSingleTimerTrigger(IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) {\n        // 1. 参数校验\n        if (StrUtil.isBlank(trigger.getCronExpression())) {\n            log.error(\"[registerSingleTimerTrigger][场景规则({}) 定时触发器缺少 CRON 表达式]\", sceneRule.getId());\n            return;\n        }\n\n        try {\n            // 2.1 构建任务名称和数据\n            String jobName = buildJobName(sceneRule.getId());\n            // 2.2 注册定时任务\n            schedulerManager.addOrUpdateJob(\n                    IotSceneRuleJob.class,\n                    jobName,\n                    trigger.getCronExpression(),\n                    IotSceneRuleJob.buildJobDataMap(sceneRule.getId())\n            );\n            log.info(\"[registerSingleTimerTrigger][场景规则({}) 定时触发器注册成功，CRON: {}]\",\n                    sceneRule.getId(), trigger.getCronExpression());\n        } catch (SchedulerException e) {\n            log.error(\"[registerSingleTimerTrigger][场景规则({}) 定时触发器注册失败，CRON: {}]\",\n                    sceneRule.getId(), trigger.getCronExpression(), e);\n        }\n    }\n\n    /**\n     * 构建任务名称\n     *\n     * @param sceneRuleId 场景规则 ID\n     * @return 任务名称\n     */\n    private String buildJobName(Long sceneRuleId) {\n        return \"iot_scene_rule_timer_\" + sceneRuleId;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotTimerConditionEvaluator.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.timer;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleTimeHelper;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Map;\n\n/**\n * IoT 定时触发器条件评估器\n * <p>\n * 与设备触发器不同，定时触发器没有设备消息上下文，\n * 需要主动查询设备属性和状态来评估条件。\n *\n * @author HUIHUI\n */\n@Component\n@Slf4j\npublic class IotTimerConditionEvaluator {\n\n    @Resource\n    private IotDevicePropertyService devicePropertyService;\n\n    @Resource\n    private IotDeviceService deviceService;\n\n    /**\n     * 评估条件\n     *\n     * @param condition 条件配置\n     * @return 是否满足条件\n     */\n    @SuppressWarnings(\"EnhancedSwitchMigration\")\n    public boolean evaluate(IotSceneRuleDO.TriggerCondition condition) {\n        // 1.1 基础参数校验\n        if (condition == null || condition.getType() == null) {\n            log.warn(\"[evaluate][条件为空或类型为空]\");\n            return false;\n        }\n        // 1.2 根据条件类型分发到具体的评估方法\n        IotSceneRuleConditionTypeEnum conditionType =\n                IotSceneRuleConditionTypeEnum.typeOf(condition.getType());\n        if (conditionType == null) {\n            log.warn(\"[evaluate][未知的条件类型: {}]\", condition.getType());\n            return false;\n        }\n\n        // 2. 分发评估\n        switch (conditionType) {\n            case DEVICE_PROPERTY:\n                return evaluateDevicePropertyCondition(condition);\n            case DEVICE_STATE:\n                return evaluateDeviceStateCondition(condition);\n            case CURRENT_TIME:\n                return evaluateCurrentTimeCondition(condition);\n            default:\n                log.warn(\"[evaluate][未知的条件类型: {}]\", conditionType);\n                return false;\n        }\n    }\n\n    /**\n     * 评估设备属性条件\n     *\n     * @param condition 条件配置\n     * @return 是否满足条件\n     */\n    private boolean evaluateDevicePropertyCondition(IotSceneRuleDO.TriggerCondition condition) {\n        // 1. 校验必要参数\n        if (condition.getDeviceId() == null) {\n            log.debug(\"[evaluateDevicePropertyCondition][设备ID为空]\");\n            return false;\n        }\n        if (StrUtil.isBlank(condition.getIdentifier())) {\n            log.debug(\"[evaluateDevicePropertyCondition][属性标识符为空]\");\n            return false;\n        }\n        if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {\n            log.debug(\"[evaluateDevicePropertyCondition][操作符或参数无效]\");\n            return false;\n        }\n\n        // 2.1 获取设备最新属性值\n        Map<String, IotDevicePropertyDO> properties =\n                devicePropertyService.getLatestDeviceProperties(condition.getDeviceId());\n        if (CollUtil.isEmpty(properties)) {\n            log.debug(\"[evaluateDevicePropertyCondition][设备({}) 无属性数据]\", condition.getDeviceId());\n            return false;\n        }\n        // 2.2 获取指定属性\n        IotDevicePropertyDO property = properties.get(condition.getIdentifier());\n        if (property == null || property.getValue() == null) {\n            log.debug(\"[evaluateDevicePropertyCondition][设备({}) 属性({}) 不存在或值为空]\",\n                    condition.getDeviceId(), condition.getIdentifier());\n            return false;\n        }\n\n        // 3. 使用现有的条件评估逻辑进行比较\n        boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(\n                property.getValue(), condition.getOperator(), condition.getParam());\n        log.debug(\"[evaluateDevicePropertyCondition][设备({}) 属性({}) 值({}) 操作符({}) 参数({}) 匹配结果: {}]\",\n                condition.getDeviceId(), condition.getIdentifier(), property.getValue(),\n                condition.getOperator(), condition.getParam(), matched);\n        return matched;\n    }\n\n    /**\n     * 评估设备状态条件\n     *\n     * @param condition 条件配置\n     * @return 是否满足条件\n     */\n    private boolean evaluateDeviceStateCondition(IotSceneRuleDO.TriggerCondition condition) {\n        // 1. 校验必要参数\n        if (condition.getDeviceId() == null) {\n            log.debug(\"[evaluateDeviceStateCondition][设备ID为空]\");\n            return false;\n        }\n        if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {\n            log.debug(\"[evaluateDeviceStateCondition][操作符或参数无效]\");\n            return false;\n        }\n\n        // 2.1 获取设备信息\n        IotDeviceDO device = deviceService.getDevice(condition.getDeviceId());\n        if (device == null) {\n            log.debug(\"[evaluateDeviceStateCondition][设备({}) 不存在]\", condition.getDeviceId());\n            return false;\n        }\n        // 2.2 获取设备状态\n        Integer state = device.getState();\n        if (state == null) {\n            log.debug(\"[evaluateDeviceStateCondition][设备({}) 状态为空]\", condition.getDeviceId());\n            return false;\n        }\n\n        // 3. 比较状态\n        boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(\n                state.toString(), condition.getOperator(), condition.getParam());\n        log.debug(\"[evaluateDeviceStateCondition][设备({}) 状态({}) 操作符({}) 参数({}) 匹配结果: {}]\",\n                condition.getDeviceId(), state, condition.getOperator(), condition.getParam(), matched);\n        return matched;\n    }\n\n    /**\n     * 评估当前时间条件\n     *\n     * @param condition 条件配置\n     * @return 是否满足条件\n     */\n    private boolean evaluateCurrentTimeCondition(IotSceneRuleDO.TriggerCondition condition) {\n        // 1.1 校验必要参数\n        if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {\n            log.debug(\"[evaluateCurrentTimeCondition][操作符或参数无效]\");\n            return false;\n        }\n        // 1.2 验证操作符是否为支持的时间操作符\n        IotSceneRuleConditionOperatorEnum operatorEnum =\n                IotSceneRuleConditionOperatorEnum.operatorOf(condition.getOperator());\n        if (operatorEnum == null) {\n            log.debug(\"[evaluateCurrentTimeCondition][无效的操作符: {}]\", condition.getOperator());\n            return false;\n        }\n        if (IotSceneRuleTimeHelper.isTimeOperator(operatorEnum)) {\n            log.debug(\"[evaluateCurrentTimeCondition][不支持的时间操作符: {}]\", condition.getOperator());\n            return false;\n        }\n\n        // 2. 执行时间匹配\n        boolean matched = IotSceneRuleTimeHelper.executeTimeMatching(operatorEnum, condition.getParam());\n        log.debug(\"[evaluateCurrentTimeCondition][操作符({}) 参数({}) 匹配结果: {}]\",\n                condition.getOperator(), condition.getParam(), matched);\n        return matched;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java",
    "content": "package cn.iocoder.yudao.module.iot.service.thingmodel;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * IoT 产品物模型 Service 接口\n *\n * @author 芋道源码\n */\npublic interface IotThingModelService {\n\n    /**\n     * 创建产品物模型\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createThingModel(@Valid IotThingModelSaveReqVO createReqVO);\n\n    /**\n     * 更新产品物模型\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateThingModel(@Valid IotThingModelSaveReqVO updateReqVO);\n\n    /**\n     * 删除产品物模型\n     *\n     * @param id 编号\n     */\n    void deleteThingModel(Long id);\n\n    /**\n     * 获得产品物模型\n     *\n     * @param id 编号\n     * @return 产品物模型\n     */\n    IotThingModelDO getThingModel(Long id);\n\n    /**\n     * 获得产品物模型列表\n     *\n     * @param productId 产品编号\n     * @return 产品物模型列表\n     */\n    List<IotThingModelDO> getThingModelListByProductId(Long productId);\n\n    /**\n     * 获得产品物模型列表\n     *\n     * @param productId 产品编号\n     * @param identifiers 功能标识列表\n     * @return 产品物模型列表\n     */\n    List<IotThingModelDO> getThingModelListByProductIdAndIdentifiers(Long productId, Collection<String> identifiers);\n\n    /**\n     * 获得产品物模型列表\n     *\n     * @param productId 产品编号\n     * @param type 物模型类型\n     * @return 产品物模型列表\n     */\n    List<IotThingModelDO> getThingModelListByProductIdAndType(Long productId, Integer type);\n\n    /**\n     * 【缓存】获得产品物模型列表\n     *\n     * 注意：该方法会忽略租户信息，所以调用时，需要确认会不会有跨租户访问的风险！！！\n     *\n     * @param productId 产品编号\n     * @return 产品物模型列表\n     */\n    List<IotThingModelDO> getThingModelListByProductIdFromCache(Long productId);\n\n    /**\n     * 获得产品物模型分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 产品物模型分页\n     */\n    PageResult<IotThingModelDO> getProductThingModelPage(IotThingModelPageReqVO pageReqVO);\n\n    /**\n     * 获得产品物模型列表\n     *\n     * @param reqVO 列表查询\n     * @return 产品物模型列表\n     */\n    List<IotThingModelDO> getThingModelList(IotThingModelListReqVO reqVO);\n\n    /**\n     * 批量校验物模型存在\n     *\n     * @param productId 产品编号\n     * @param identifiers 标识符集合\n     */\n    void validateThingModelListExists(Long productId, Set<String> identifiers);\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.iot.service.thingmodel;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;\nimport cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;\nimport cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper;\nimport cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;\nimport cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;\n\n/**\n * IoT 产品物模型 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\n@Slf4j\npublic class IotThingModelServiceImpl implements IotThingModelService {\n\n    @Resource\n    private IotThingModelMapper thingModelMapper;\n\n    @Resource\n    @Lazy // 延迟加载，解决循环依赖\n    private IotProductService productService;\n    @Resource\n    @Lazy // 延迟加载，解决循环依赖\n    private IotDeviceModbusPointService deviceModbusPointService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createThingModel(IotThingModelSaveReqVO createReqVO) {\n        // 1.1 校验功能标识符在同一产品下是否唯一\n        validateIdentifierUnique(null, createReqVO.getProductId(), createReqVO.getIdentifier());\n        // 1.2 功能名称在同一产品下是否唯一\n        validateNameUnique(createReqVO.getProductId(), createReqVO.getName());\n        // 1.3 校验产品状态，发布状态下，不允许新增功能\n        validateProductStatus(createReqVO.getProductId());\n\n        // 2. 插入数据库\n        IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(createReqVO);\n        thingModelMapper.insert(thingModel);\n\n        // 3. 删除缓存\n        deleteThingModelListCache(createReqVO.getProductId());\n        return thingModel.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateThingModel(IotThingModelSaveReqVO updateReqVO) {\n        // 1.1 校验功能是否存在\n        validateProductThingModelMapperExists(updateReqVO.getId());\n        // 1.2 校验功能标识符是否唯一\n        validateIdentifierUnique(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier());\n        // 1.3 校验产品状态，发布状态下，不允许操作功能\n        validateProductStatus(updateReqVO.getProductId());\n\n        // 2. 更新数据库\n        IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO);\n        thingModelMapper.updateById(thingModel);\n\n        // 3. 同步更新 Modbus 点位的冗余字段（identifier、name）\n        deviceModbusPointService.updateDeviceModbusPointByThingModel(\n                updateReqVO.getId(), updateReqVO.getIdentifier(), updateReqVO.getName());\n\n        // 4. 删除缓存\n        deleteThingModelListCache(updateReqVO.getProductId());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteThingModel(Long id) {\n        // 1.1 校验功能是否存在\n        IotThingModelDO thingModel = thingModelMapper.selectById(id);\n        if (thingModel == null) {\n            throw exception(THING_MODEL_NOT_EXISTS);\n        }\n        // 1.2 校验产品状态，发布状态下，不允许操作功能\n        validateProductStatus(thingModel.getProductId());\n\n        // 2. 删除功能\n        thingModelMapper.deleteById(id);\n\n        // 3. 删除缓存\n        deleteThingModelListCache(thingModel.getProductId());\n    }\n\n    @Override\n    public IotThingModelDO getThingModel(Long id) {\n        return thingModelMapper.selectById(id);\n    }\n\n    @Override\n    public List<IotThingModelDO> getThingModelListByProductId(Long productId) {\n        return thingModelMapper.selectListByProductId(productId);\n    }\n\n    @Override\n    public List<IotThingModelDO> getThingModelListByProductIdAndIdentifiers(Long productId, Collection<String> identifiers) {\n        return thingModelMapper.selectListByProductIdAndIdentifiers(productId, identifiers);\n    }\n\n    @Override\n    public List<IotThingModelDO> getThingModelListByProductIdAndType(Long productId, Integer type) {\n        return thingModelMapper.selectListByProductIdAndType(productId, type);\n    }\n\n    @Override\n    @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = \"#productId\")\n    @TenantIgnore // 忽略租户信息\n    public List<IotThingModelDO> getThingModelListByProductIdFromCache(Long productId) {\n        return thingModelMapper.selectListByProductId(productId);\n    }\n\n    @Override\n    public PageResult<IotThingModelDO> getProductThingModelPage(IotThingModelPageReqVO pageReqVO) {\n        return thingModelMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<IotThingModelDO> getThingModelList(IotThingModelListReqVO reqVO) {\n        return thingModelMapper.selectList(reqVO);\n    }\n\n    @Override\n    public void validateThingModelListExists(Long productId, Set<String> identifiers) {\n        if (CollUtil.isEmpty(identifiers)) {\n            return;\n        }\n        List<IotThingModelDO> thingModels = thingModelMapper.selectListByProductIdAndIdentifiers(\n            productId, identifiers);\n        Set<String> foundIdentifiers = convertSet(thingModels, IotThingModelDO::getIdentifier);\n        for (String identifier : identifiers) {\n            if (!foundIdentifiers.contains(identifier)) {\n                throw exception(THING_MODEL_NOT_EXISTS);\n            }\n        }\n    }\n\n    private void validateProductThingModelMapperExists(Long id) {\n        if (thingModelMapper.selectById(id) == null) {\n            throw exception(THING_MODEL_NOT_EXISTS);\n        }\n    }\n\n    private void validateIdentifierUnique(Long id, Long productId, String identifier) {\n        // 1. 情况一：创建时校验\n        if (id == null) {\n            // 1.1 系统保留字段，不能用于标识符定义\n            if (StrUtil.equalsAny(identifier, \"set\", \"get\", \"post\", \"property\", \"event\", \"time\", \"value\")) {\n                throw exception(THING_MODEL_IDENTIFIER_INVALID);\n            }\n            // 1.2 校验唯一\n            IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier);\n            if (thingModel != null) {\n                throw exception(THING_MODEL_IDENTIFIER_EXISTS);\n            }\n            return;\n        }\n\n        // 2. 情况二：更新时校验\n        IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier);\n        if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) {\n            throw exception(THING_MODEL_IDENTIFIER_EXISTS);\n        }\n    }\n\n    private void validateProductStatus(Long createReqVO) {\n        IotProductDO product = productService.validateProductExists(createReqVO);\n        if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) {\n            throw exception(PRODUCT_STATUS_NOT_ALLOW_THING_MODEL);\n        }\n    }\n\n    private void validateNameUnique(Long productId, String name) {\n        IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndName(productId, name);\n        if (thingModel != null) {\n            throw exception(THING_MODEL_NAME_EXISTS);\n        }\n    }\n\n    private void deleteThingModelListCache(Long productId) {\n        // 保证 Spring AOP 触发\n        getSelf().deleteThingModelListCache0(productId);\n    }\n\n    @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = \"#productId\")\n    @TenantIgnore // 忽略租户信息\n    public void deleteThingModelListCache0(Long productId) {\n    }\n\n    private IotThingModelServiceImpl getSelf() {\n        return SpringUtil.getBean(getClass());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 5 # 初始连接数\n        min-idle: 10 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n        slave: # 模拟从库，可根据自己需要修改 # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 400-infra.server.iocoder.cn # 地址\n    port: 6379 # 端口\n    database: 1 # 数据库索引\n#      password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n\nspring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n      # Spring Boot Admin Server 服务端的相关配置\n      context-path: /admin # 配置 Spring\n\n# 日志文件配置\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 1 # 初始连接数\n        min-idle: 1 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒（1 分钟）\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例\n          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例\n          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例\n          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例\n          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例\n          username: root\n          password: 123456\n        #          username: sa # SQL Server 连接的示例\n        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例\n        #          username: SYSDBA # DM 连接的示例\n        #          password: SYSDBA # DM 连接的示例\n        slave: # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n#        tdengine: # IoT 数据库（需要 IoT 物联网再开启噢！）\n#          url: jdbc:TAOS-WS://127.0.0.1:6041/ruoyi_vue_pro\n#          driver-class-name: com.taosdata.jdbc.rs.RestfulDriver\n#          username: root\n#          password: taosdata\n#          druid:\n#            validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 6379 # 端口\n    database: 0 # 数据库索引\n#      password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n\nspring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n# 日志文件配置\nlogging:\n  level:\n    # 配置自己写的 MyBatis Mapper 打印日志\n    cn.iocoder.yudao.module.iot.dal.mysql: debug\n    cn.iocoder.yudao.module.iot.dal.mysql.sms.SmsChannelMapper: INFO # 配置 SmsChannelMapper 的日志级别为 info\n    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿：先禁用，Spring Boot 3.X 存在部分错误的 WARN 提示"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: iot-server\n\n  profiles:\n    active: local\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n    allow-bean-definition-overriding: true # 允许 Bean 覆盖，例如说 Feign 等会存在重复定义的服务\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  # Servlet 配置\n  servlet:\n    # 文件上传相关配置项\n    multipart:\n      max-file-size: 16MB # 单个文件大小\n      max-request-size: 32MB # 设置总上传的文件大小\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  # Cache 配置项\n  cache:\n    type: REDIS\n    redis:\n      time-to-live: 1h # 设置过期时间为 1 小时\n\nserver:\n  port: 48091\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\n--- #################### 接口文档配置 ####################\n\nspringdoc:\n  api-docs:\n    enabled: true # 1. 是否开启 Swagger 接文档的元数据\n    path: /v3/api-docs\n  swagger-ui:\n    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面\n    path: /swagger-ui\n  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档\n\nknife4j:\n  enable: true\n  setting:\n    language: zh_cn\n\n# MyBatis Plus 的配置项\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: NONE # “智能”模式，基于 IdTypeEnvironmentPostProcessor + 数据源的类型，自动适配成 AUTO、INPUT 模式。\n      #      id-type: AUTO # 自增 ID，适合 MySQL 等直接自增的数据库\n      #      id-type: INPUT # 用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n      #      id-type: ASSIGN_ID # 分配 ID，默认使用雪花算法。注意，Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时，需要去除实体类上的 @KeySequence 注解\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n    banner: false # 关闭控制台的 Banner 打印\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  encryptor:\n    password: XDV71a+xqStEA3WH # 加解密的秘钥，可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成\n\nmybatis-plus-join:\n  banner: false # 关闭控制台的 Banner 打印\n\n# Spring Data Redis 配置\nspring:\n  data:\n    redis:\n      repositories:\n        enabled: false # 项目未使用到 Spring Data Redis 的 Repository，所以直接禁用，保证启动速度\n\n# VO 转换（数据翻译）相关\neasy-trans:\n  is-enable-global: false # 【默认禁用，对性能确认压力大】启用全局翻译（拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置，或通过 @IgnoreTrans 忽略某个接口\n\n--- #################### RPC 远程调用相关配置 ####################\n\n--- #################### 消息队列相关 ####################\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  # Producer 配置项\n  producer:\n    group: ${spring.application.name}_PRODUCER # 生产者分组\n\nspring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring.json.trusted.packages: '*'\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    executor:\n      appname: ${spring.application.name} # 执行器 AppName\n      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径\n    accessToken: default_token # 执行器通讯TOKEN\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0\n    base-package: cn.iocoder.yudao.module.iot\n  web:\n    admin-ui:\n      url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址\n  xss:\n    enable: false\n    exclude-urls: # 如下 url，仅仅是为了演示，去掉配置也没关系\n      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求\n  swagger:\n    title: 管理后台\n    description: 提供管理员管理的所有功能\n    version: ${yudao.info.version}\n  tenant: # 多租户相关配置项\n    enable: true\n    ignore-urls:\n    ignore-tables:\n    ignore-caches:\n      - iot:device\n      - iot:thing_model_list\n  iot:\n    message-bus:\n      type: redis # 消息总线的类型\n\ndebug: false"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/resources/mapper/device/IotDeviceMessageMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper\">\n\n    <update id=\"createSTable\">\n        CREATE STABLE IF NOT EXISTS device_message (\n            ts TIMESTAMP,\n            id NCHAR(50),\n            report_time TIMESTAMP,\n            tenant_id BIGINT,\n            server_id NCHAR(50),\n            upstream BOOL,\n            reply BOOL,\n            identifier NCHAR(100),\n            request_id NCHAR(50),\n            method NCHAR(100),\n            params NCHAR(2048),\n            data NCHAR(2048),\n            code INT,\n            msg NCHAR(256)\n        ) TAGS (\n            device_id BIGINT\n        )\n    </update>\n\n    <select id=\"showSTable\" resultType=\"String\">\n        SHOW STABLES LIKE 'device_message'\n    </select>\n\n    <insert id=\"insert\">\n        INSERT INTO device_message_${deviceId} (\n            ts, id, report_time, tenant_id, server_id,\n            upstream, reply, identifier, request_id, method,\n            params, data, code, msg\n        )\n        USING device_message\n        TAGS (#{deviceId})\n        VALUES (\n            NOW, #{id}, #{reportTime}, #{tenantId},  #{serverId},\n            #{upstream}, #{reply}, #{identifier}, #{requestId}, #{method}, \n            #{params}, #{data}, #{code}, #{msg}\n        )\n    </insert>\n\n    <select id=\"selectPage\" resultType=\"cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO\">\n        SELECT ts, id, report_time, tenant_id, server_id,\n               upstream, reply, identifier, request_id, method, \n               params, data, code, msg\n        FROM device_message_${reqVO.deviceId}\n        <where>\n            <if test=\"reqVO.method != null and reqVO.method != ''\">\n                AND method = #{reqVO.method}\n            </if>\n            <if test=\"reqVO.upstream != null\">\n                AND upstream = #{reqVO.upstream}\n            </if>\n            <if test=\"reqVO.reply != null\">\n                AND reply = #{reqVO.reply}\n            </if>\n            <if test=\"reqVO.identifier != null and reqVO.identifier != ''\">\n                AND identifier = #{reqVO.identifier}\n            </if>\n        </where>\n        ORDER BY ts DESC\n    </select>\n\n    <select id=\"selectListByRequestIdsAndReply\" resultType=\"cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO\">\n        SELECT ts, id, report_time, tenant_id, server_id,\n               upstream, reply, identifier, request_id, method, \n               params, data, code, msg\n        FROM device_message_${deviceId}\n        WHERE reply = #{reply}\n        AND request_id IN\n        <foreach collection=\"requestIds\" item=\"requestId\" open=\"(\" close=\")\" separator=\",\">\n            #{requestId}\n        </foreach>\n        ORDER BY ts DESC\n    </select>\n\n    <select id=\"selectCountByCreateTime\" resultType=\"Long\">\n        SELECT COUNT(*)\n        FROM device_message\n        <where>\n            <if test=\"createTime != null\">\n                AND ts >= #{createTime}\n            </if>\n        </where>\n    </select>\n\n    <select id=\"selectDeviceMessageCountGroupByDate\" resultType=\"java.util.Map\">\n        SELECT\n            TIMETRUNCATE(ts, 1h) AS time,\n            SUM(CASE WHEN upstream = true THEN 1 ELSE 0 END) AS upstream_count,\n            SUM(CASE WHEN upstream = false THEN 1 ELSE 0 END) AS downstream_count\n        FROM device_message\n        <where>\n            <if test=\"startTime != null\">\n                AND ts >= #{startTime}\n            </if>\n            <if test=\"endTime != null\">\n                AND ts &lt;= #{endTime}\n            </if>\n        </where>\n        GROUP BY TIMETRUNCATE(ts, 1h)\n    </select>\n\n</mapper>"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/main/resources/mapper/device/IotDevicePropertyMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyMapper\">\n\n    <select id=\"getProductPropertySTableFieldList\" resultType=\"cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField\">\n        DESCRIBE product_property_${productId}\n    </select>\n\n    <update id=\"createProductPropertySTable\">\n        CREATE STABLE product_property_${productId} (\n            ts TIMESTAMP,\n            report_time TIMESTAMP,\n        <foreach item=\"field\" collection=\"fields\" separator=\",\">\n            ${field.field} ${field.type}\n            <if test=\"field.length != null and field.length > 0\">\n                (${field.length})\n            </if>\n        </foreach>\n        )\n        TAGS (\n            device_id BIGINT\n        )\n    </update>\n\n    <update id=\"alterProductPropertySTableAddField\">\n        ALTER STABLE product_property_${productId}\n        ADD COLUMN ${field.field} ${field.type}\n        <if test=\"field.length != null and field.length > 0\">\n            (${field.length})\n        </if>\n    </update>\n\n    <update id=\"alterProductPropertySTableModifyField\">\n        ALTER STABLE product_property_${productId}\n        MODIFY COLUMN ${field.field} ${field.type}\n        <if test=\"field.length != null and field.length > 0\">\n            (${field.length})\n        </if>\n    </update>\n\n    <update id=\"alterProductPropertySTableDropField\">\n        ALTER STABLE product_property_${productId}\n        DROP COLUMN ${field.field}\n    </update>\n\n    <insert id=\"insert\">\n        INSERT INTO device_property_${device.id}\n        USING product_property_${device.productId}\n        TAGS ('${device.id}')\n        (ts, report_time,\n        <foreach item=\"key\" collection=\"properties.keys\" separator=\",\">\n            ${@cn.hutool.core.util.StrUtil@toUnderlineCase(key)}\n        </foreach>\n        )\n        VALUES\n        (NOW, #{reportTime},\n        <foreach item=\"value\" collection=\"properties.values\" separator=\",\">\n            #{value}\n        </foreach>\n        )\n    </insert>\n\n    <select id=\"describeSuperTable\" resultType=\"java.util.Map\">\n        DESCRIBE product_property_${productId}\n    </select>\n\n    <select id=\"selectListByHistory\"\n            resultType=\"cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO\">\n        SELECT ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} AS `value`, ts AS update_time\n        FROM device_property_${reqVO.deviceId}\n        WHERE ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} IS NOT NULL\n          AND ts BETWEEN ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[0])}\n            AND ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[1])}\n        ORDER BY ts DESC\n    </select>\n\n</mapper>"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.action.databridge;\n\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.*;\nimport cn.iocoder.yudao.module.iot.service.rule.data.action.*;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.client.RestTemplate;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.when;\n\n/**\n * {@link IotDataRuleAction} 实现类的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // 默认禁用，需要手动启用测试\n@Slf4j\npublic class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {\n\n    private IotDeviceMessage message;\n\n    @Mock\n    private RestTemplate restTemplate;\n\n    @InjectMocks\n    private IotHttpDataSinkAction httpDataBridgeExecute;\n\n    @BeforeEach\n    public void setUp() {\n        // TODO @芋艿：@puhui999：需要调整下；\n        // 创建共享的测试消息\n        //message = IotDeviceMessage.builder().messageId(\"TEST-001\").reportTime(LocalDateTime.now())\n        //        .productKey(\"testProduct\").deviceName(\"testDevice\")\n        //        .type(\"property\").identifier(\"temperature\").data(\"{\\\"value\\\": 60}\")\n        //        .build();\n    }\n\n    @Test\n    public void testKafkaMQDataBridge() throws Exception {\n        // 1. 创建执行器实例\n        IotKafkaDataRuleAction action = new IotKafkaDataRuleAction();\n\n        // 2. 创建配置\n        IotDataSinkKafkaConfig config = new IotDataSinkKafkaConfig()\n                .setBootstrapServers(\"127.0.0.1:9092\")\n                .setTopic(\"test-topic\")\n                .setSsl(false)\n                .setUsername(null)\n                .setPassword(null);\n\n        // 3. 执行测试并验证缓存\n        executeAndVerifyCache(action, config, \"KafkaMQ\");\n    }\n\n    @Test\n    public void testRabbitMQDataBridge() throws Exception {\n        // 1. 创建执行器实例\n        IotRabbitMQDataRuleAction action = new IotRabbitMQDataRuleAction();\n\n        // 2. 创建配置\n        IotDataSinkRabbitMQConfig config = new IotDataSinkRabbitMQConfig()\n                .setHost(\"localhost\")\n                .setPort(5672)\n                .setVirtualHost(\"/\")\n                .setUsername(\"admin\")\n                .setPassword(\"123456\")\n                .setExchange(\"test-exchange\")\n                .setRoutingKey(\"test-key\")\n                .setQueue(\"test-queue\");\n\n        // 3. 执行测试并验证缓存\n        executeAndVerifyCache(action, config, \"RabbitMQ\");\n    }\n\n    @Test\n    public void testRedisDataBridge() throws Exception {\n        // 1. 创建执行器实例\n        IotRedisRuleAction action = new IotRedisRuleAction();\n\n        // 2. 创建配置 - 测试 Stream 数据结构\n        IotDataSinkRedisConfig config = new IotDataSinkRedisConfig();\n        config.setHost(\"127.0.0.1\");\n        config.setPort(6379);\n        config.setDatabase(0);\n        config.setPassword(\"123456\");\n        config.setTopic(\"test-stream\");\n        config.setDataStructure(1); // Stream 类型\n\n        // 3. 执行测试并验证缓存\n        executeAndVerifyCache(action, config, \"Redis\");\n    }\n\n    @Test\n    public void testRocketMQDataBridge() throws Exception {\n        // 1. 创建执行器实例\n        IotRocketMQDataRuleAction action = new IotRocketMQDataRuleAction();\n\n        // 2. 创建配置\n        IotDataSinkRocketMQConfig config = new IotDataSinkRocketMQConfig()\n                .setNameServer(\"127.0.0.1:9876\")\n                .setGroup(\"test-group\")\n                .setTopic(\"test-topic\")\n                .setTags(\"test-tag\");\n\n        // 3. 执行测试并验证缓存\n        executeAndVerifyCache(action, config, \"RocketMQ\");\n    }\n\n    @Test\n    public void testHttpDataBridge() throws Exception {\n        // 1. 配置 RestTemplate mock 返回成功响应\n        when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(), any(Class.class)))\n                .thenReturn(new ResponseEntity<>(\"Success\", HttpStatus.OK));\n\n        // 2. 创建配置\n        IotDataSinkHttpConfig config = new IotDataSinkHttpConfig()\n                .setUrl(\"https://doc.iocoder.cn/\").setMethod(HttpMethod.GET.name());\n\n        // 3. 执行测试\n        log.info(\"[testHttpDataBridge][执行HTTP数据桥接测试]\");\n        httpDataBridgeExecute.execute(message, new IotDataSinkDO()\n                .setType(httpDataBridgeExecute.getType()).setConfig(config));\n    }\n\n    /**\n     * 执行测试并验证缓存的通用方法\n     *\n     * @param action 执行器实例\n     * @param config 配置对象\n     * @param type MQ 类型\n     * @throws Exception 如果执行过程中发生异常\n     */\n    private void executeAndVerifyCache(IotDataRuleAction action, IotAbstractDataSinkConfig config, String type)\n            throws Exception {\n        log.info(\"[test{}DataBridge][第一次执行，应该会创建新的 producer]\", type);\n        action.execute(message, new IotDataSinkDO().setType(action.getType()).setConfig(config));\n\n        log.info(\"[test{}DataBridge][第二次执行，应该会复用缓存的 producer]\", type);\n        action.execute(message, new IotDataSinkDO().setType(action.getType()).setConfig(config));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotTcpDataRuleActionTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action;\n\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig;\nimport cn.iocoder.yudao.module.iot.service.rule.data.action.tcp.IotTcpClient;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link IotTcpDataRuleAction} 的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\nclass IotTcpDataRuleActionTest {\n\n    private IotTcpDataRuleAction tcpDataRuleAction;\n\n    @Mock\n    private IotTcpClient mockTcpClient;\n\n    @BeforeEach\n    public void setUp() {\n        MockitoAnnotations.openMocks(this);\n        tcpDataRuleAction = new IotTcpDataRuleAction();\n    }\n\n    @Test\n    public void testGetType() {\n        // 准备参数\n        Integer expectedType = 2; // 数据接收类型枚举中 TCP 类型的值\n\n        // 调用方法\n        Integer actualType = tcpDataRuleAction.getType();\n\n        // 断言结果\n        assertEquals(expectedType, actualType);\n    }\n\n    // TODO @puhui999：_ 后面是小写哈，单测的命名规则。\n    @Test\n    public void testInitProducer_Success() throws Exception {\n        // 准备参数\n        IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();\n        config.setHost(\"localhost\");\n        config.setPort(8080);\n        config.setDataFormat(\"JSON\");\n        config.setSsl(false);\n\n        // 调用方法 & 断言结果\n        // 此测试需要实际的 TCP 连接，在单元测试中可能不可用\n        // 目前我们只验证配置是否有效\n        assertNotNull(config.getHost());\n        assertTrue(config.getPort() > 0 && config.getPort() <= 65535);\n    }\n\n    @Test\n    public void testInitProducer_InvalidHost() {\n        // 准备参数\n        IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();\n        config.setHost(\"\");\n        config.setPort(8080);\n\n        // 调用方法 & 断言结果\n        IotTcpDataRuleAction action = new IotTcpDataRuleAction();\n\n        // 测试验证逻辑（通常在 initProducer 方法中）\n        assertThrows(IllegalArgumentException.class, () -> {\n            if (config.getHost() == null || config.getHost().trim().isEmpty()) {\n                throw new IllegalArgumentException(\"TCP 服务器地址不能为空\");\n            }\n        });\n    }\n\n    @Test\n    public void testInitProducer_InvalidPort() {\n        // 准备参数\n        IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();\n        config.setHost(\"localhost\");\n        config.setPort(-1);\n\n        // 调用方法 & 断言结果\n        assertThrows(IllegalArgumentException.class, () -> {\n            if (config.getPort() == null || config.getPort() <= 0 || config.getPort() > 65535) {\n                throw new IllegalArgumentException(\"TCP 服务器端口无效\");\n            }\n        });\n    }\n\n    @Test\n    public void testCloseProducer() throws Exception {\n        // 准备参数\n        IotTcpClient client = mock(IotTcpClient.class);\n\n        // 调用方法\n        tcpDataRuleAction.closeProducer(client);\n\n        // 断言结果\n        verify(client, times(1)).close();\n    }\n\n    @Test\n    public void testExecute_WithValidConfig() {\n        // 准备参数\n        IotDeviceMessage message = IotDeviceMessage.requestOf(\"thing.property.report\",\n                \"{\\\"temperature\\\": 25.5, \\\"humidity\\\": 60}\");\n\n        IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();\n        config.setHost(\"localhost\");\n        config.setPort(8080);\n        config.setDataFormat(\"JSON\");\n\n        // 调用方法 & 断言结果\n        // 通常这需要实际的 TCP 连接\n        // 在单元测试中，我们只验证输入参数\n        assertNotNull(message);\n        assertNotNull(config);\n        assertNotNull(config.getHost());\n        assertTrue(config.getPort() > 0);\n    }\n\n    @Test\n    public void testConfig_DefaultValues() {\n        // 准备参数\n        IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();\n\n        // 调用方法 & 断言结果\n        // 验证默认值\n        assertEquals(\"JSON\", config.getDataFormat());\n        assertEquals(5000, config.getConnectTimeoutMs());\n        assertEquals(10000, config.getReadTimeoutMs());\n        assertEquals(false, config.getSsl());\n        assertEquals(30000L, config.getHeartbeatIntervalMs());\n        assertEquals(5000L, config.getReconnectIntervalMs());\n        assertEquals(3, config.getMaxReconnectAttempts());\n    }\n\n    @Test\n    public void testMessageSerialization() {\n        // 准备参数\n        IotDeviceMessage message = IotDeviceMessage.builder()\n                .deviceId(123L)\n                .method(\"thing.property.report\")\n                .params(\"{\\\"temperature\\\": 25.5}\")\n                .build();\n\n        // 调用方法\n        String json = JsonUtils.toJsonString(message);\n\n        // 断言结果\n        assertNotNull(json);\n        assertTrue(json.contains(\"\\\"deviceId\\\":123\"));\n        assertTrue(json.contains(\"\\\"method\\\":\\\"thing.property.report\\\"\"));\n        assertTrue(json.contains(\"\\\"temperature\\\":25.5\"));\n    }\n\n}"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClientTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action.tcp;\n\nimport cn.hutool.core.util.ReflectUtil;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotTcpClient} 的单元测试\n * <p>\n * 测试 dataFormat 默认值行为\n * Property 1: TCP 客户端 dataFormat 默认值行为\n * Validates: Requirements 1.1, 1.2\n *\n * @author HUIHUI\n */\nclass IotTcpClientTest {\n\n    @Test\n    public void testConstructor_dataFormatNull() {\n        // 准备参数\n        String host = \"localhost\";\n        Integer port = 8080;\n\n        // 调用\n        IotTcpClient client = new IotTcpClient(host, port, null, null, null, null);\n\n        // 断言：dataFormat 为 null 时应使用默认值\n        assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT,\n                ReflectUtil.getFieldValue(client, \"dataFormat\"));\n    }\n\n    @Test\n    public void testConstructor_dataFormatEmpty() {\n        // 准备参数\n        String host = \"localhost\";\n        Integer port = 8080;\n\n        // 调用\n        IotTcpClient client = new IotTcpClient(host, port, null, null, null, \"\");\n\n        // 断言：dataFormat 为空字符串时应使用默认值\n        assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT,\n                ReflectUtil.getFieldValue(client, \"dataFormat\"));\n    }\n\n    @Test\n    public void testConstructor_dataFormatBlank() {\n        // 准备参数\n        String host = \"localhost\";\n        Integer port = 8080;\n\n        // 调用\n        IotTcpClient client = new IotTcpClient(host, port, null, null, null, \"   \");\n\n        // 断言：dataFormat 为纯空白字符串时应使用默认值\n        assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT,\n                ReflectUtil.getFieldValue(client, \"dataFormat\"));\n    }\n\n    @Test\n    public void testConstructor_dataFormatValid() {\n        // 准备参数\n        String host = \"localhost\";\n        Integer port = 8080;\n        String dataFormat = \"BINARY\";\n\n        // 调用\n        IotTcpClient client = new IotTcpClient(host, port, null, null, null, dataFormat);\n\n        // 断言：dataFormat 为有效值时应保持原值\n        assertEquals(dataFormat, ReflectUtil.getFieldValue(client, \"dataFormat\"));\n    }\n\n    @Test\n    public void testConstructor_defaultValues() {\n        // 准备参数\n        String host = \"localhost\";\n        Integer port = 8080;\n\n        // 调用\n        IotTcpClient client = new IotTcpClient(host, port, null, null, null, null);\n\n        // 断言：验证所有默认值\n        assertEquals(host, ReflectUtil.getFieldValue(client, \"host\"));\n        assertEquals(port, ReflectUtil.getFieldValue(client, \"port\"));\n        assertEquals(IotDataSinkTcpConfig.DEFAULT_CONNECT_TIMEOUT_MS,\n                ReflectUtil.getFieldValue(client, \"connectTimeoutMs\"));\n        assertEquals(IotDataSinkTcpConfig.DEFAULT_READ_TIMEOUT_MS,\n                ReflectUtil.getFieldValue(client, \"readTimeoutMs\"));\n        assertEquals(IotDataSinkTcpConfig.DEFAULT_SSL,\n                ReflectUtil.getFieldValue(client, \"ssl\"));\n        assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT,\n                ReflectUtil.getFieldValue(client, \"dataFormat\"));\n    }\n\n    @Test\n    public void testConstructor_customValues() {\n        // 准备参数\n        String host = \"192.168.1.100\";\n        Integer port = 9090;\n        Integer connectTimeoutMs = 3000;\n        Integer readTimeoutMs = 8000;\n        Boolean ssl = true;\n        String dataFormat = \"BINARY\";\n\n        // 调用\n        IotTcpClient client = new IotTcpClient(host, port, connectTimeoutMs, readTimeoutMs, ssl, dataFormat);\n\n        // 断言：验证自定义值\n        assertEquals(host, ReflectUtil.getFieldValue(client, \"host\"));\n        assertEquals(port, ReflectUtil.getFieldValue(client, \"port\"));\n        assertEquals(connectTimeoutMs, ReflectUtil.getFieldValue(client, \"connectTimeoutMs\"));\n        assertEquals(readTimeoutMs, ReflectUtil.getFieldValue(client, \"readTimeoutMs\"));\n        assertEquals(ssl, ReflectUtil.getFieldValue(client, \"ssl\"));\n        assertEquals(dataFormat, ReflectUtil.getFieldValue(client, \"dataFormat\"));\n    }\n\n    @Test\n    public void testIsConnected_initialState() {\n        // 准备参数\n        String host = \"localhost\";\n        Integer port = 8080;\n\n        // 调用\n        IotTcpClient client = new IotTcpClient(host, port, null, null, null, null);\n\n        // 断言：初始状态应为未连接\n        assertFalse(client.isConnected());\n    }\n\n    @Test\n    public void testToString() {\n        // 准备参数\n        String host = \"localhost\";\n        Integer port = 8080;\n\n        // 调用\n        IotTcpClient client = new IotTcpClient(host, port, null, null, null, null);\n        String result = client.toString();\n\n        // 断言\n        assertNotNull(result);\n        assertTrue(result.contains(\"host='localhost'\"));\n        assertTrue(result.contains(\"port=8080\"));\n        assertTrue(result.contains(\"dataFormat='JSON'\"));\n        assertTrue(result.contains(\"connected=false\"));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/websocket/IotWebSocketClientTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.data.action.websocket;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport okhttp3.Response;\nimport okhttp3.WebSocket;\nimport okhttp3.WebSocketListener;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotWebSocketClient} 的单元测试\n *\n * @author HUIHUI\n */\nclass IotWebSocketClientTest {\n\n    private MockWebServer mockWebServer;\n\n    @BeforeEach\n    public void setUp() throws Exception {\n        mockWebServer = new MockWebServer();\n        mockWebServer.start();\n    }\n\n    @AfterEach\n    public void tearDown() throws Exception {\n        if (mockWebServer != null) {\n            mockWebServer.shutdown();\n        }\n    }\n\n    /**\n     * 简单的 WebSocket 监听器，用于测试\n     */\n    private static class TestWebSocketListener extends WebSocketListener {\n        @Override\n        public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {\n            // 连接打开\n        }\n\n        @Override\n        public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {\n            // 收到消息\n        }\n\n        @Override\n        public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) {\n            webSocket.close(code, reason);\n        }\n\n        @Override\n        public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) {\n            // 连接失败\n        }\n    }\n\n    @Test\n    public void testConstructor_defaultValues() {\n        // 准备参数\n        String serverUrl = \"ws://localhost:8080\";\n\n        // 调用\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, null, null, null);\n\n        // 断言：验证默认值被正确设置\n        assertNotNull(client);\n        assertFalse(client.isConnected());\n    }\n\n    @Test\n    public void testConstructor_customValues() {\n        // 准备参数\n        String serverUrl = \"ws://localhost:8080\";\n        Integer connectTimeoutMs = 3000;\n        Integer sendTimeoutMs = 5000;\n        String dataFormat = \"TEXT\";\n\n        // 调用\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, connectTimeoutMs, sendTimeoutMs, dataFormat);\n\n        // 断言\n        assertNotNull(client);\n        assertFalse(client.isConnected());\n    }\n\n    @Test\n    public void testConnect_success() throws Exception {\n        // 准备参数：使用 MockWebServer 的 WebSocket 端点\n        String serverUrl = \"ws://\" + mockWebServer.getHostName() + \":\" + mockWebServer.getPort();\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, 5000, 5000, \"JSON\");\n\n        // mock：设置 MockWebServer 响应 WebSocket 升级请求\n        mockWebServer.enqueue(new MockResponse().withWebSocketUpgrade(new TestWebSocketListener()));\n\n        // 调用\n        client.connect();\n\n        // 断言\n        assertTrue(client.isConnected());\n\n        // 清理\n        client.close();\n    }\n\n    @Test\n    public void testConnect_alreadyConnected() throws Exception {\n        // 准备参数\n        String serverUrl = \"ws://\" + mockWebServer.getHostName() + \":\" + mockWebServer.getPort();\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, 5000, 5000, \"JSON\");\n\n        // mock\n        mockWebServer.enqueue(new MockResponse().withWebSocketUpgrade(new TestWebSocketListener()));\n\n        // 调用：第一次连接\n        client.connect();\n        assertTrue(client.isConnected());\n\n        // 调用：第二次连接（应该不会重复连接）\n        client.connect();\n        assertTrue(client.isConnected());\n\n        // 清理\n        client.close();\n    }\n\n    @Test\n    public void testSendMessage_success() throws Exception {\n        // 准备参数\n        String serverUrl = \"ws://\" + mockWebServer.getHostName() + \":\" + mockWebServer.getPort();\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, 5000, 5000, \"JSON\");\n\n        IotDeviceMessage message = IotDeviceMessage.builder()\n                .deviceId(123L)\n                .method(\"thing.property.report\")\n                .params(\"{\\\"temperature\\\": 25.5}\")\n                .build();\n\n        // mock\n        mockWebServer.enqueue(new MockResponse().withWebSocketUpgrade(new TestWebSocketListener()));\n\n        // 调用\n        client.connect();\n        client.sendMessage(message);\n\n        // 断言：消息发送成功不抛异常\n        assertTrue(client.isConnected());\n\n        // 清理\n        client.close();\n    }\n\n    @Test\n    public void testSendMessage_notConnected() {\n        // 准备参数\n        String serverUrl = \"ws://localhost:8080\";\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, 5000, 5000, \"JSON\");\n\n        IotDeviceMessage message = IotDeviceMessage.builder()\n                .deviceId(123L)\n                .method(\"thing.property.report\")\n                .params(\"{\\\"temperature\\\": 25.5}\")\n                .build();\n\n        // 调用 & 断言：未连接时发送消息应抛出异常\n        assertThrows(IllegalStateException.class, () -> client.sendMessage(message));\n    }\n\n    @Test\n    public void testClose_success() throws Exception {\n        // 准备参数\n        String serverUrl = \"ws://\" + mockWebServer.getHostName() + \":\" + mockWebServer.getPort();\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, 5000, 5000, \"JSON\");\n\n        // mock\n        mockWebServer.enqueue(new MockResponse().withWebSocketUpgrade(new TestWebSocketListener()));\n\n        // 调用\n        client.connect();\n        assertTrue(client.isConnected());\n\n        client.close();\n\n        // 断言\n        assertFalse(client.isConnected());\n    }\n\n    @Test\n    public void testClose_notConnected() {\n        // 准备参数\n        String serverUrl = \"ws://localhost:8080\";\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, 5000, 5000, \"JSON\");\n\n        // 调用：关闭未连接的客户端不应抛异常\n        assertDoesNotThrow(client::close);\n        assertFalse(client.isConnected());\n    }\n\n    @Test\n    public void testIsConnected_initialState() {\n        // 准备参数\n        String serverUrl = \"ws://localhost:8080\";\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, 5000, 5000, \"JSON\");\n\n        // 断言：初始状态应为未连接\n        assertFalse(client.isConnected());\n    }\n\n    @Test\n    public void testToString() {\n        // 准备参数\n        String serverUrl = \"ws://localhost:8080\";\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, 5000, 5000, \"JSON\");\n\n        // 调用\n        String result = client.toString();\n\n        // 断言\n        assertNotNull(result);\n        assertTrue(result.contains(\"serverUrl='ws://localhost:8080'\"));\n        assertTrue(result.contains(\"dataFormat='JSON'\"));\n        assertTrue(result.contains(\"connected=false\"));\n    }\n\n    @Test\n    public void testSendMessage_textFormat() throws Exception {\n        // 准备参数\n        String serverUrl = \"ws://\" + mockWebServer.getHostName() + \":\" + mockWebServer.getPort();\n        IotWebSocketClient client = new IotWebSocketClient(serverUrl, 5000, 5000, \"TEXT\");\n\n        IotDeviceMessage message = IotDeviceMessage.builder()\n                .deviceId(123L)\n                .method(\"thing.property.report\")\n                .params(\"{\\\"temperature\\\": 25.5}\")\n                .build();\n\n        // mock\n        mockWebServer.enqueue(new MockResponse().withWebSocketUpgrade(new TestWebSocketListener()));\n\n        // 调用\n        client.connect();\n        client.sendMessage(message);\n\n        // 断言：消息发送成功不抛异常\n        assertTrue(client.isConnected());\n\n        // 清理\n        client.close();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceSimpleTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;\nimport cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.product.IotProductService;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link IotSceneRuleServiceImpl} 的简化单元测试类\n * 使用 Mockito 进行纯单元测试，不依赖 Spring 容器\n *\n * @author 芋道源码\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotSceneRuleServiceSimpleTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private IotSceneRuleServiceImpl sceneRuleService;\n\n    @Mock\n    private IotSceneRuleMapper sceneRuleMapper;\n\n    @Mock\n    private List<IotSceneRuleAction> sceneRuleActions;\n\n    @Mock\n    private IotSchedulerManager schedulerManager;\n\n    @Mock\n    private IotProductService productService;\n\n    @Mock\n    private IotDeviceService deviceService;\n\n    @Test\n    public void testCreateScene_Rule_success() {\n        // 准备参数\n        IotSceneRuleSaveReqVO createReqVO = randomPojo(IotSceneRuleSaveReqVO.class, o -> {\n            o.setId(null);\n            o.setStatus(CommonStatusEnum.ENABLE.getStatus());\n            o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class)));\n            o.setActions(Collections.singletonList(randomPojo(IotSceneRuleDO.Action.class)));\n        });\n\n        // Mock 行为\n        Long expectedId = randomLongId();\n        when(sceneRuleMapper.insert(any(IotSceneRuleDO.class))).thenAnswer(invocation -> {\n            IotSceneRuleDO sceneRule = invocation.getArgument(0);\n            sceneRule.setId(expectedId);\n            return 1;\n        });\n\n        // 调用\n        Long sceneRuleId = sceneRuleService.createSceneRule(createReqVO);\n\n        // 断言\n        assertEquals(expectedId, sceneRuleId);\n        verify(sceneRuleMapper, times(1)).insert(any(IotSceneRuleDO.class));\n    }\n\n    @Test\n    public void testUpdateScene_Rule_success() {\n        // 准备参数\n        Long id = randomLongId();\n        IotSceneRuleSaveReqVO updateReqVO = randomPojo(IotSceneRuleSaveReqVO.class, o -> {\n            o.setId(id);\n            o.setStatus(CommonStatusEnum.ENABLE.getStatus());\n            o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class)));\n            o.setActions(Collections.singletonList(randomPojo(IotSceneRuleDO.Action.class)));\n        });\n\n        // Mock 行为\n        IotSceneRuleDO existingSceneRule = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));\n        when(sceneRuleMapper.selectById(id)).thenReturn(existingSceneRule);\n        when(sceneRuleMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1);\n\n        // 调用\n        assertDoesNotThrow(() -> sceneRuleService.updateSceneRule(updateReqVO));\n\n        // 验证\n        verify(sceneRuleMapper, times(1)).selectById(id);\n        verify(sceneRuleMapper, times(1)).updateById(any(IotSceneRuleDO.class));\n    }\n\n    @Test\n    public void testDeleteSceneRule_success() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // Mock 行为\n        IotSceneRuleDO existingSceneRule = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));\n        when(sceneRuleMapper.selectById(id)).thenReturn(existingSceneRule);\n        when(sceneRuleMapper.deleteById(id)).thenReturn(1);\n\n        // 调用\n        assertDoesNotThrow(() -> sceneRuleService.deleteSceneRule(id));\n\n        // 验证\n        verify(sceneRuleMapper, times(1)).selectById(id);\n        verify(sceneRuleMapper, times(1)).deleteById(id);\n    }\n\n    @Test\n    public void testGetSceneRule() {\n        // 准备参数\n        Long id = randomLongId();\n        IotSceneRuleDO expectedSceneRule = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));\n\n        // Mock 行为\n        when(sceneRuleMapper.selectById(id)).thenReturn(expectedSceneRule);\n\n        // 调用\n        IotSceneRuleDO result = sceneRuleService.getSceneRule(id);\n\n        // 断言\n        assertEquals(expectedSceneRule, result);\n        verify(sceneRuleMapper, times(1)).selectById(id);\n    }\n\n    @Test\n    public void testUpdateSceneRuleStatus_success() {\n        // 准备参数\n        Long id = randomLongId();\n        Integer status = CommonStatusEnum.DISABLE.getStatus();\n\n        // Mock 行为\n        IotSceneRuleDO existingSceneRule = randomPojo(IotSceneRuleDO.class, o -> {\n            o.setId(id);\n            o.setStatus(CommonStatusEnum.ENABLE.getStatus());\n        });\n        when(sceneRuleMapper.selectById(id)).thenReturn(existingSceneRule);\n        when(sceneRuleMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1);\n\n        // 调用\n        assertDoesNotThrow(() -> sceneRuleService.updateSceneRuleStatus(id, status));\n\n        // 验证\n        verify(sceneRuleMapper, times(1)).selectById(id);\n        verify(sceneRuleMapper, times(1)).updateById(any(IotSceneRuleDO.class));\n    }\n\n    @Test\n    public void testExecuteSceneRuleByTimer_success() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // Mock 行为\n        IotSceneRuleDO sceneRule = randomPojo(IotSceneRuleDO.class, o -> {\n            o.setId(id);\n            o.setStatus(CommonStatusEnum.ENABLE.getStatus());\n        });\n        when(sceneRuleMapper.selectById(id)).thenReturn(sceneRule);\n\n        // 调用\n        assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(id));\n\n        // 验证\n        verify(sceneRuleMapper, times(1)).selectById(id);\n    }\n\n    @Test\n    public void testExecuteSceneRuleByTimer_notExists() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // Mock 行为\n        when(sceneRuleMapper.selectById(id)).thenReturn(null);\n\n        // 调用 - 不存在的场景规则应该不会抛异常，只是记录日志\n        assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(id));\n\n        // 验证\n        verify(sceneRuleMapper, times(1)).selectById(id);\n    }\n\n    @Test\n    public void testExecuteSceneRuleByTimer_disabled() {\n        // 准备参数\n        Long id = randomLongId();\n\n        // Mock 行为\n        IotSceneRuleDO sceneRule = randomPojo(IotSceneRuleDO.class, o -> {\n            o.setId(id);\n            o.setStatus(CommonStatusEnum.DISABLE.getStatus());\n        });\n        when(sceneRuleMapper.selectById(id)).thenReturn(sceneRule);\n\n        // 调用 - 禁用的场景规则应该不会执行，只是记录日志\n        assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(id));\n\n        // 验证\n        verify(sceneRuleMapper, times(1)).selectById(id);\n    }\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimerConditionIntegrationTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.device.IotDeviceService;\nimport cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotSceneRuleTimerHandler;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotTimerConditionEvaluator;\nimport org.junit.jupiter.api.*;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\n\nimport java.lang.reflect.Field;\nimport java.util.*;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link IotSceneRuleServiceImpl} 定时触发器条件组集成测试\n * <p>\n * 测试定时触发器的条件组评估功能：\n * - 空条件组直接执行动作\n * - 条件组评估后决定是否执行动作\n * - 条件组之间的 OR 逻辑\n * - 条件组内的 AND 逻辑\n * - 所有条件组不满足时跳过执行\n * <p>\n * Validates: Requirements 2.1, 2.2, 2.3, 2.4, 2.5\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotSceneRuleTimerConditionIntegrationTest extends BaseMockitoUnitTest {\n\n    @InjectMocks\n    private IotSceneRuleServiceImpl sceneRuleService;\n\n    @Mock\n    private IotSceneRuleMapper sceneRuleMapper;\n\n    @Mock\n    private IotDeviceService deviceService;\n\n    @Mock\n    private IotDevicePropertyService devicePropertyService;\n\n    @Mock\n    private List<IotSceneRuleAction> sceneRuleActions;\n\n    @Mock\n    private IotSceneRuleTimerHandler timerHandler;\n\n    private IotTimerConditionEvaluator timerConditionEvaluator;\n\n    // 测试常量\n    private static final Long SCENE_RULE_ID = 1L;\n    private static final Long TENANT_ID = 1L;\n    private static final Long DEVICE_ID = 100L;\n    private static final String PROPERTY_IDENTIFIER = \"temperature\";\n\n    @BeforeEach\n    void setUp() {\n        // 创建并注入 timerConditionEvaluator 的依赖\n        timerConditionEvaluator = new IotTimerConditionEvaluator();\n        try {\n            Field devicePropertyServiceField = IotTimerConditionEvaluator.class.getDeclaredField(\"devicePropertyService\");\n            devicePropertyServiceField.setAccessible(true);\n            devicePropertyServiceField.set(timerConditionEvaluator, devicePropertyService);\n\n            Field deviceServiceField = IotTimerConditionEvaluator.class.getDeclaredField(\"deviceService\");\n            deviceServiceField.setAccessible(true);\n            deviceServiceField.set(timerConditionEvaluator, deviceService);\n\n            Field evaluatorField = IotSceneRuleServiceImpl.class.getDeclaredField(\"timerConditionEvaluator\");\n            evaluatorField.setAccessible(true);\n            evaluatorField.set(sceneRuleService, timerConditionEvaluator);\n        } catch (Exception e) {\n            throw new RuntimeException(\"Failed to inject dependencies\", e);\n        }\n    }\n\n    // ========== 辅助方法 ==========\n\n    private IotSceneRuleDO createBaseSceneRule() {\n        IotSceneRuleDO sceneRule = new IotSceneRuleDO();\n        sceneRule.setId(SCENE_RULE_ID);\n        sceneRule.setTenantId(TENANT_ID);\n        sceneRule.setName(\"测试定时触发器\");\n        sceneRule.setStatus(CommonStatusEnum.ENABLE.getStatus());\n        sceneRule.setActions(Collections.emptyList());\n        return sceneRule;\n    }\n\n    private IotSceneRuleDO.Trigger createTimerTrigger(String cronExpression,\n                                                      List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups) {\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());\n        trigger.setCronExpression(cronExpression);\n        trigger.setConditionGroups(conditionGroups);\n        return trigger;\n    }\n\n    private IotSceneRuleDO.TriggerCondition createDevicePropertyCondition(Long deviceId, String identifier,\n                                                                          String operator, String param) {\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());\n        condition.setDeviceId(deviceId);\n        condition.setIdentifier(identifier);\n        condition.setOperator(operator);\n        condition.setParam(param);\n        return condition;\n    }\n\n    private IotSceneRuleDO.TriggerCondition createDeviceStateCondition(Long deviceId, String operator, String param) {\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());\n        condition.setDeviceId(deviceId);\n        condition.setOperator(operator);\n        condition.setParam(param);\n        return condition;\n    }\n\n    private void mockDeviceProperty(Long deviceId, String identifier, Object value) {\n        Map<String, IotDevicePropertyDO> properties = new HashMap<>();\n        IotDevicePropertyDO property = new IotDevicePropertyDO();\n        property.setValue(value);\n        properties.put(identifier, property);\n        when(devicePropertyService.getLatestDeviceProperties(deviceId)).thenReturn(properties);\n    }\n\n    private void mockDeviceState(Long deviceId, Integer state) {\n        IotDeviceDO device = new IotDeviceDO();\n        device.setId(deviceId);\n        device.setState(state);\n        when(deviceService.getDevice(deviceId)).thenReturn(device);\n    }\n\n    /**\n     * 创建单条件的条件组列表\n     */\n    private List<List<IotSceneRuleDO.TriggerCondition>> createSingleConditionGroups(\n            IotSceneRuleDO.TriggerCondition condition) {\n        List<IotSceneRuleDO.TriggerCondition> group = new ArrayList<>();\n        group.add(condition);\n        List<List<IotSceneRuleDO.TriggerCondition>> groups = new ArrayList<>();\n        groups.add(group);\n        return groups;\n    }\n\n    /**\n     * 创建两个单条件组的条件组列表\n     */\n    private List<List<IotSceneRuleDO.TriggerCondition>> createTwoSingleConditionGroups(\n            IotSceneRuleDO.TriggerCondition cond1, IotSceneRuleDO.TriggerCondition cond2) {\n        List<IotSceneRuleDO.TriggerCondition> group1 = new ArrayList<>();\n        group1.add(cond1);\n        List<IotSceneRuleDO.TriggerCondition> group2 = new ArrayList<>();\n        group2.add(cond2);\n        List<List<IotSceneRuleDO.TriggerCondition>> groups = new ArrayList<>();\n        groups.add(group1);\n        groups.add(group2);\n        return groups;\n    }\n\n    /**\n     * 创建单个多条件组的条件组列表\n     */\n    private List<List<IotSceneRuleDO.TriggerCondition>> createSingleGroupWithMultipleConditions(\n            IotSceneRuleDO.TriggerCondition... conditions) {\n        List<IotSceneRuleDO.TriggerCondition> group = new ArrayList<>(Arrays.asList(conditions));\n        List<List<IotSceneRuleDO.TriggerCondition>> groups = new ArrayList<>();\n        groups.add(group);\n        return groups;\n    }\n\n    // ========== 测试用例 ==========\n\n    @Nested\n    @DisplayName(\"空条件组测试 - Validates: Requirement 2.1\")\n    class EmptyConditionGroupsTest {\n\n        @Test\n        @DisplayName(\"定时触发器无条件组时，应直接执行动作\")\n        void testTimerTrigger_withNullConditionGroups_shouldExecuteActions() {\n            // 准备数据\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", null);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(sceneRuleMapper, times(1)).selectById(SCENE_RULE_ID);\n            verify(devicePropertyService, never()).getLatestDeviceProperties(any());\n            verify(deviceService, never()).getDevice(any());\n        }\n\n        @Test\n        @DisplayName(\"定时触发器条件组为空列表时，应直接执行动作\")\n        void testTimerTrigger_withEmptyConditionGroups_shouldExecuteActions() {\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", Collections.emptyList());\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(sceneRuleMapper, times(1)).selectById(SCENE_RULE_ID);\n            verify(devicePropertyService, never()).getLatestDeviceProperties(any());\n        }\n    }\n\n    @Nested\n    @DisplayName(\"条件组 OR 逻辑测试 - Validates: Requirements 2.2, 2.3\")\n    class ConditionGroupOrLogicTest {\n\n        @Test\n        @DisplayName(\"多个条件组中第一个满足时，应执行动作\")\n        void testMultipleConditionGroups_firstGroupMatches_shouldExecuteActions() {\n            IotSceneRuleDO.TriggerCondition condition1 = createDevicePropertyCondition(\n                    DEVICE_ID, PROPERTY_IDENTIFIER, IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"20\");\n            IotSceneRuleDO.TriggerCondition condition2 = createDevicePropertyCondition(\n                    DEVICE_ID + 1, PROPERTY_IDENTIFIER, IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"50\");\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups =\n                    createTwoSingleConditionGroups(condition1, condition2);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            mockDeviceProperty(DEVICE_ID, PROPERTY_IDENTIFIER, 30);\n            mockDeviceProperty(DEVICE_ID + 1, PROPERTY_IDENTIFIER, 30);\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n        }\n\n        @Test\n        @DisplayName(\"多个条件组中第二个满足时，应执行动作\")\n        void testMultipleConditionGroups_secondGroupMatches_shouldExecuteActions() {\n            IotSceneRuleDO.TriggerCondition condition1 = createDevicePropertyCondition(\n                    DEVICE_ID, PROPERTY_IDENTIFIER, IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"50\");\n            IotSceneRuleDO.TriggerCondition condition2 = createDevicePropertyCondition(\n                    DEVICE_ID + 1, PROPERTY_IDENTIFIER, IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"20\");\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups =\n                    createTwoSingleConditionGroups(condition1, condition2);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            mockDeviceProperty(DEVICE_ID, PROPERTY_IDENTIFIER, 30);\n            mockDeviceProperty(DEVICE_ID + 1, PROPERTY_IDENTIFIER, 30);\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID + 1);\n        }\n    }\n\n    @Nested\n    @DisplayName(\"条件组内 AND 逻辑测试 - Validates: Requirement 2.4\")\n    class ConditionGroupAndLogicTest {\n\n        @Test\n        @DisplayName(\"条件组内所有条件都满足时，该组应匹配成功\")\n        void testSingleConditionGroup_allConditionsMatch_shouldPass() {\n            IotSceneRuleDO.TriggerCondition condition1 = createDevicePropertyCondition(\n                    DEVICE_ID, \"temperature\", IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"20\");\n            IotSceneRuleDO.TriggerCondition condition2 = createDevicePropertyCondition(\n                    DEVICE_ID, \"humidity\", IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), \"80\");\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups =\n                    createSingleGroupWithMultipleConditions(condition1, condition2);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            Map<String, IotDevicePropertyDO> properties = new HashMap<>();\n            IotDevicePropertyDO tempProperty = new IotDevicePropertyDO();\n            tempProperty.setValue(30);\n            properties.put(\"temperature\", tempProperty);\n            IotDevicePropertyDO humidityProperty = new IotDevicePropertyDO();\n            humidityProperty.setValue(60);\n            properties.put(\"humidity\", humidityProperty);\n            when(devicePropertyService.getLatestDeviceProperties(DEVICE_ID)).thenReturn(properties);\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n        }\n\n        @Test\n        @DisplayName(\"条件组内有一个条件不满足时，该组应匹配失败\")\n        void testSingleConditionGroup_oneConditionFails_shouldFail() {\n            IotSceneRuleDO.TriggerCondition condition1 = createDevicePropertyCondition(\n                    DEVICE_ID, \"temperature\", IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"20\");\n            IotSceneRuleDO.TriggerCondition condition2 = createDevicePropertyCondition(\n                    DEVICE_ID, \"humidity\", IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), \"50\");\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups =\n                    createSingleGroupWithMultipleConditions(condition1, condition2);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            Map<String, IotDevicePropertyDO> properties = new HashMap<>();\n            IotDevicePropertyDO tempProperty = new IotDevicePropertyDO();\n            tempProperty.setValue(30);\n            properties.put(\"temperature\", tempProperty);\n            IotDevicePropertyDO humidityProperty = new IotDevicePropertyDO();\n            humidityProperty.setValue(60); // 不满足 < 50\n            properties.put(\"humidity\", humidityProperty);\n            when(devicePropertyService.getLatestDeviceProperties(DEVICE_ID)).thenReturn(properties);\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n        }\n    }\n\n    @Nested\n    @DisplayName(\"所有条件组不满足测试 - Validates: Requirement 2.5\")\n    class AllConditionGroupsFailTest {\n\n        @Test\n        @DisplayName(\"所有条件组都不满足时，应跳过动作执行\")\n        void testAllConditionGroups_allFail_shouldSkipExecution() {\n            IotSceneRuleDO.TriggerCondition condition1 = createDevicePropertyCondition(\n                    DEVICE_ID, PROPERTY_IDENTIFIER, IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"50\");\n            IotSceneRuleDO.TriggerCondition condition2 = createDevicePropertyCondition(\n                    DEVICE_ID + 1, PROPERTY_IDENTIFIER, IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"50\");\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups =\n                    createTwoSingleConditionGroups(condition1, condition2);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            mockDeviceProperty(DEVICE_ID, PROPERTY_IDENTIFIER, 30);\n            mockDeviceProperty(DEVICE_ID + 1, PROPERTY_IDENTIFIER, 30);\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID + 1);\n        }\n    }\n\n    @Nested\n    @DisplayName(\"设备状态条件测试 - Validates: Requirements 4.1, 4.2\")\n    class DeviceStateConditionTest {\n\n        @Test\n        @DisplayName(\"设备在线状态条件满足时，应匹配成功\")\n        void testDeviceStateCondition_online_shouldMatch() {\n            IotSceneRuleDO.TriggerCondition condition = createDeviceStateCondition(\n                    DEVICE_ID, IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                    String.valueOf(IotDeviceStateEnum.ONLINE.getState()));\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups = createSingleConditionGroups(condition);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            mockDeviceState(DEVICE_ID, IotDeviceStateEnum.ONLINE.getState());\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(deviceService, atLeastOnce()).getDevice(DEVICE_ID);\n        }\n\n        @Test\n        @DisplayName(\"设备不存在时，条件应不匹配\")\n        void testDeviceStateCondition_deviceNotExists_shouldNotMatch() {\n            IotSceneRuleDO.TriggerCondition condition = createDeviceStateCondition(\n                    DEVICE_ID, IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                    String.valueOf(IotDeviceStateEnum.ONLINE.getState()));\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups = createSingleConditionGroups(condition);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            when(deviceService.getDevice(DEVICE_ID)).thenReturn(null);\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(deviceService, atLeastOnce()).getDevice(DEVICE_ID);\n        }\n    }\n\n    @Nested\n    @DisplayName(\"设备属性条件测试 - Validates: Requirements 3.1, 3.2, 3.3\")\n    class DevicePropertyConditionTest {\n\n        @Test\n        @DisplayName(\"设备属性条件满足时，应匹配成功\")\n        void testDevicePropertyCondition_match_shouldPass() {\n            IotSceneRuleDO.TriggerCondition condition = createDevicePropertyCondition(\n                    DEVICE_ID, PROPERTY_IDENTIFIER, IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"25\");\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups = createSingleConditionGroups(condition);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            mockDeviceProperty(DEVICE_ID, PROPERTY_IDENTIFIER, 30);\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n        }\n\n        @Test\n        @DisplayName(\"设备属性不存在时，条件应不匹配\")\n        void testDevicePropertyCondition_propertyNotExists_shouldNotMatch() {\n            IotSceneRuleDO.TriggerCondition condition = createDevicePropertyCondition(\n                    DEVICE_ID, \"nonexistent\", IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"25\");\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups = createSingleConditionGroups(condition);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            when(devicePropertyService.getLatestDeviceProperties(DEVICE_ID)).thenReturn(Collections.emptyMap());\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n        }\n\n        @Test\n        @DisplayName(\"设备属性等于条件测试\")\n        void testDevicePropertyCondition_equals_shouldMatch() {\n            IotSceneRuleDO.TriggerCondition condition = createDevicePropertyCondition(\n                    DEVICE_ID, PROPERTY_IDENTIFIER, IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), \"30\");\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups = createSingleConditionGroups(condition);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            mockDeviceProperty(DEVICE_ID, PROPERTY_IDENTIFIER, 30);\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n        }\n    }\n\n    @Nested\n    @DisplayName(\"场景规则状态测试\")\n    class SceneRuleStatusTest {\n\n        @Test\n        @DisplayName(\"场景规则不存在时，应直接返回\")\n        void testSceneRule_notExists_shouldReturn() {\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(null);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, never()).getLatestDeviceProperties(any());\n        }\n\n        @Test\n        @DisplayName(\"场景规则已禁用时，应直接返回\")\n        void testSceneRule_disabled_shouldReturn() {\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            sceneRule.setStatus(CommonStatusEnum.DISABLE.getStatus());\n\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, never()).getLatestDeviceProperties(any());\n        }\n\n        @Test\n        @DisplayName(\"场景规则无定时触发器时，应直接返回\")\n        void testSceneRule_noTimerTrigger_shouldReturn() {\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger deviceTrigger = new IotSceneRuleDO.Trigger();\n            deviceTrigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());\n            sceneRule.setTriggers(ListUtil.toList(deviceTrigger));\n\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, never()).getLatestDeviceProperties(any());\n        }\n    }\n\n    @Nested\n    @DisplayName(\"复杂条件组合测试\")\n    class ComplexConditionCombinationTest {\n\n        @Test\n        @DisplayName(\"混合条件类型测试：设备属性 + 设备状态\")\n        void testMixedConditionTypes_propertyAndState() {\n            IotSceneRuleDO.TriggerCondition propertyCondition = createDevicePropertyCondition(\n                    DEVICE_ID, PROPERTY_IDENTIFIER, IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"20\");\n            IotSceneRuleDO.TriggerCondition stateCondition = createDeviceStateCondition(\n                    DEVICE_ID, IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                    String.valueOf(IotDeviceStateEnum.ONLINE.getState()));\n\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups =\n                    createSingleGroupWithMultipleConditions(propertyCondition, stateCondition);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            mockDeviceProperty(DEVICE_ID, PROPERTY_IDENTIFIER, 30);\n            mockDeviceState(DEVICE_ID, IotDeviceStateEnum.ONLINE.getState());\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n            verify(deviceService, atLeastOnce()).getDevice(DEVICE_ID);\n        }\n\n        @Test\n        @DisplayName(\"多条件组 OR 逻辑 + 组内 AND 逻辑综合测试\")\n        void testComplexOrAndLogic() {\n            // 条件组1：温度 > 30 AND 湿度 < 50（不满足）\n            // 条件组2：温度 > 20 AND 设备在线（满足）\n            IotSceneRuleDO.TriggerCondition group1Cond1 = createDevicePropertyCondition(\n                    DEVICE_ID, \"temperature\", IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"30\");\n            IotSceneRuleDO.TriggerCondition group1Cond2 = createDevicePropertyCondition(\n                    DEVICE_ID, \"humidity\", IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), \"50\");\n\n            IotSceneRuleDO.TriggerCondition group2Cond1 = createDevicePropertyCondition(\n                    DEVICE_ID, \"temperature\", IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), \"20\");\n            IotSceneRuleDO.TriggerCondition group2Cond2 = createDeviceStateCondition(\n                    DEVICE_ID, IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                    String.valueOf(IotDeviceStateEnum.ONLINE.getState()));\n\n            // 创建两个条件组\n            List<IotSceneRuleDO.TriggerCondition> group1 = new ArrayList<>();\n            group1.add(group1Cond1);\n            group1.add(group1Cond2);\n            List<IotSceneRuleDO.TriggerCondition> group2 = new ArrayList<>();\n            group2.add(group2Cond1);\n            group2.add(group2Cond2);\n            List<List<IotSceneRuleDO.TriggerCondition>> conditionGroups = new ArrayList<>();\n            conditionGroups.add(group1);\n            conditionGroups.add(group2);\n\n            IotSceneRuleDO sceneRule = createBaseSceneRule();\n            IotSceneRuleDO.Trigger trigger = createTimerTrigger(\"0 0 12 * * ?\", conditionGroups);\n            sceneRule.setTriggers(ListUtil.toList(trigger));\n\n            // Mock：温度 25，湿度 60，设备在线\n            Map<String, IotDevicePropertyDO> properties = new HashMap<>();\n            IotDevicePropertyDO tempProperty = new IotDevicePropertyDO();\n            tempProperty.setValue(25);\n            properties.put(\"temperature\", tempProperty);\n            IotDevicePropertyDO humidityProperty = new IotDevicePropertyDO();\n            humidityProperty.setValue(60);\n            properties.put(\"humidity\", humidityProperty);\n            when(devicePropertyService.getLatestDeviceProperties(DEVICE_ID)).thenReturn(properties);\n            mockDeviceState(DEVICE_ID, IotDeviceStateEnum.ONLINE.getState());\n            when(sceneRuleMapper.selectById(SCENE_RULE_ID)).thenReturn(sceneRule);\n\n            assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(SCENE_RULE_ID));\n\n            verify(devicePropertyService, atLeastOnce()).getLatestDeviceProperties(DEVICE_ID);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotBaseConditionMatcherTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.test.context.junit.jupiter.SpringJUnitConfig;\n\n/**\n * Matcher 测试基类\n * 提供通用的 Spring 测试配置\n *\n * @author HUIHUI\n */\n@SpringJUnitConfig\npublic abstract class IotBaseConditionMatcherTest {\n\n    /**\n     * 注入一下 SpringUtil，解析 EL 表达式时需要\n     * {@link SpringExpressionUtils#parseExpression}\n     */\n    @Configuration\n    static class TestConfig {\n\n        @Bean\n        public SpringUtil springUtil() {\n            return new SpringUtil();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcherTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.time.LocalDateTime;\nimport java.time.ZoneOffset;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotCurrentTimeConditionMatcher} 的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotCurrentTimeConditionMatcherTest extends IotBaseConditionMatcherTest {\n\n    private IotCurrentTimeConditionMatcher matcher;\n\n    @BeforeEach\n    public void setUp() {\n        matcher = new IotCurrentTimeConditionMatcher();\n    }\n\n    @Test\n    public void testGetSupportedConditionType() {\n        // 调用\n        IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType();\n\n        // 断言\n        assertEquals(IotSceneRuleConditionTypeEnum.CURRENT_TIME, result);\n    }\n\n    @Test\n    public void testGetPriority() {\n        // 调用\n        int result = matcher.getPriority();\n\n        // 断言\n        assertEquals(40, result);\n    }\n\n    @Test\n    public void testIsEnabled() {\n        // 调用\n        boolean result = matcher.isEnabled();\n\n        // 断言\n        assertTrue(result);\n    }\n\n    // ========== 时间戳条件测试 ==========\n\n    @Test\n    public void testMatches_DateTimeGreaterThan_success() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        long pastTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of(\"+8\"));\n        IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(\n                IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(),\n                String.valueOf(pastTimestamp)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_DateTimeGreaterThan_fail() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of(\"+8\"));\n        IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(\n                IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(),\n                String.valueOf(futureTimestamp)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_DateTimeLessThan_success() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of(\"+8\"));\n        IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(\n                IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN.getOperator(),\n                String.valueOf(futureTimestamp)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_DateTimeBetween_success() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        long startTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of(\"+8\"));\n        long endTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of(\"+8\"));\n        IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(\n                IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(),\n                startTimestamp + \",\" + endTimestamp\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_DateTimeBetween_fail() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        long startTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of(\"+8\"));\n        long endTimestamp = LocalDateTime.now().plusHours(2).toEpochSecond(ZoneOffset.of(\"+8\"));\n        IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(\n                IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(),\n                startTimestamp + \",\" + endTimestamp\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    // ========== 当日时间条件测试 ==========\n\n    @Test\n    public void testMatches_TimeGreaterThan_earlyMorning() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.TriggerCondition condition = createTimeCondition(\n                IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(),\n                \"06:00:00\" // 早上6点\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        // 结果取决于当前时间，如果当前时间大于6点则为true\n        assertNotNull(result);\n    }\n\n    @Test\n    public void testMatches_TimeLessThan_lateNight() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.TriggerCondition condition = createTimeCondition(\n                IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN.getOperator(),\n                \"23:59:59\" // 晚上11点59分59秒\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        // 大部分情况下应该为true，除非在午夜前1秒运行测试\n        assertNotNull(result);\n    }\n\n    @Test\n    public void testMatches_TimeBetween_allDay() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.TriggerCondition condition = createTimeCondition(\n                IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(),\n                \"00:00:00,23:59:59\" // 全天\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result); // 全天范围应该总是匹配\n    }\n\n    @Test\n    public void testMatches_TimeBetween_workingHours() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.TriggerCondition condition = createTimeCondition(\n                IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(),\n                \"09:00:00,17:00:00\" // 工作时间\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        // 结果取决于当前时间是否在工作时间内\n        assertNotNull(result);\n    }\n\n    // ========== 异常情况测试 ==========\n\n    @Test\n    public void testMatches_nullCondition() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n\n        // 调用\n        boolean result = matcher.matches(message, null);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullConditionType() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(null);\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_invalidOperator() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType());\n        condition.setOperator(randomString()); // 随机无效操作符\n        condition.setParam(\"12:00:00\");\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_invalidTimeFormat() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.TriggerCondition condition = createTimeCondition(\n                IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(),\n                randomString() // 随机无效时间格式\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_invalidTimestampFormat() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(\n                IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(),\n                randomString() // 随机无效时间戳格式\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_invalidBetweenFormat() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.TriggerCondition condition = createTimeCondition(\n                IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(),\n                \"09:00:00\" // 缺少结束时间\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    // ========== 辅助方法 ==========\n\n    /**\n     * 创建设备消息\n     */\n    private IotDeviceMessage createDeviceMessage() {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        return message;\n    }\n\n    /**\n     * 创建日期时间条件\n     */\n    private IotSceneRuleDO.TriggerCondition createDateTimeCondition(String operator, String param) {\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType());\n        condition.setOperator(operator);\n        condition.setParam(param);\n        return condition;\n    }\n\n    /**\n     * 创建当日时间条件\n     */\n    private IotSceneRuleDO.TriggerCondition createTimeCondition(String operator, String param) {\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType());\n        condition.setOperator(operator);\n        condition.setParam(param);\n        return condition;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcherTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotDevicePropertyConditionMatcher} 的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotDevicePropertyConditionMatcherTest extends IotBaseConditionMatcherTest {\n\n    private IotDevicePropertyConditionMatcher matcher;\n\n    @BeforeEach\n    public void setUp() {\n        matcher = new IotDevicePropertyConditionMatcher();\n    }\n\n    @Test\n    public void testGetSupportedConditionType() {\n        // 调用\n        IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType();\n\n        // 断言\n        assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY, result);\n    }\n\n    @Test\n    public void testGetPriority() {\n        // 调用\n        int result = matcher.getPriority();\n\n        // 断言\n        assertEquals(25, result); // 修正：实际返回值是 25\n    }\n\n    @Test\n    public void testMatches_temperatureEquals_success() {\n        // 准备参数：创建属性上报消息\n        String propertyIdentifier = \"temperature\";\n        Double propertyValue = 25.5;\n        IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                propertyIdentifier,\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                String.valueOf(propertyValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_humidityGreaterThan_success() {\n        // 准备参数：创建属性上报消息\n        String propertyIdentifier = \"humidity\";\n        Integer propertyValue = 75;\n        Integer compareValue = 70;\n        IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                propertyIdentifier,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                String.valueOf(compareValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_pressureLessThan_success() {\n        // 准备参数：创建属性上报消息\n        String propertyIdentifier = \"pressure\";\n        Double propertyValue = 1010.5;\n        Integer compareValue = 1020;\n        IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                propertyIdentifier,\n                IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),\n                String.valueOf(compareValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_statusNotEquals_success() {\n        // 准备参数：创建属性上报消息\n        String propertyIdentifier = \"status\";\n        String propertyValue = \"active\";\n        String compareValue = \"inactive\";\n        IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                propertyIdentifier,\n                IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),\n                compareValue\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_propertyMismatch_fail() {\n        // 准备参数：创建属性上报消息，值不满足条件\n        String propertyIdentifier = \"temperature\";\n        Double propertyValue = 15.0;\n        Integer compareValue = 20;\n        IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                propertyIdentifier,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                String.valueOf(compareValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_identifierMismatch_fail() {\n        // 准备参数：标识符不匹配\n        String messageIdentifier = \"temperature\";\n        String conditionIdentifier = \"humidity\";\n        Double propertyValue = 25.5;\n        IotDeviceMessage message = createPropertyPostMessage(messageIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                conditionIdentifier,\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                String.valueOf(propertyValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullCondition_fail() {\n        // 准备参数\n        IotDeviceMessage message = createPropertyPostMessage(\"temperature\", 25.5);\n\n        // 调用\n        boolean result = matcher.matches(message, null);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullConditionType_fail() {\n        // 准备参数\n        IotDeviceMessage message = createPropertyPostMessage(\"temperature\", 25.5);\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(null);\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_missingIdentifier_fail() {\n        // 准备参数\n        IotDeviceMessage message = createPropertyPostMessage(\"temperature\", 25.5);\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());\n        condition.setIdentifier(null); // 缺少标识符\n        condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator());\n        condition.setParam(\"20\");\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_missingOperator_fail() {\n        // 准备参数\n        IotDeviceMessage message = createPropertyPostMessage(\"temperature\", 25.5);\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());\n        condition.setIdentifier(\"temperature\");\n        condition.setOperator(null); // 缺少操作符\n        condition.setParam(\"20\");\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_missingParam_fail() {\n        // 准备参数\n        IotDeviceMessage message = createPropertyPostMessage(\"temperature\", 25.5);\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());\n        condition.setIdentifier(\"temperature\");\n        condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator());\n        condition.setParam(null); // 缺少参数\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullMessage_fail() {\n        // 准备参数\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                \"temperature\",\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                \"20\"\n        );\n\n        // 调用\n        boolean result = matcher.matches(null, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullDeviceProperties_fail() {\n        // 准备参数：消息的 params 为 null\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setParams(null);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                \"temperature\",\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                \"20\"\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_propertiesStructure_success() {\n        // 测试使用 properties 结构的消息（真实的属性上报场景）\n        String identifier = \"temperature\";\n        Double propertyValue = 25.5;\n        IotDeviceMessage message = createPropertyPostMessageWithProperties(identifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                identifier,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                \"20\"\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言：修复后的实现应该能正确从 properties 中提取属性值\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_simpleValueMessage_success() {\n        // 测试简单值消息（params 直接是属性值）\n        Double propertyValue = 25.5;\n        IotDeviceMessage message = createSimpleValueMessage(propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                \"any\", // 对于简单值消息，标识符匹配会被跳过\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                \"20\"\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言：修复后的实现应该能处理简单值消息\n        // 但由于标识符匹配失败，结果为 false\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_valueFieldStructure_success() {\n        // 测试使用 value 字段的消息结构\n        String identifier = \"temperature\";\n        Double propertyValue = 25.5;\n\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(\"thing.event.post\");\n\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"identifier\", identifier);\n        params.put(\"value\", propertyValue);\n        message.setParams(params);\n\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                identifier,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                \"20\"\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言：修复后的实现应该能从 value 字段提取属性值\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_voltageGreaterThanOrEquals_success() {\n        // 准备参数：创建属性上报消息\n        String propertyIdentifier = \"voltage\";\n        Double propertyValue = 12.0;\n        IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                propertyIdentifier,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(),\n                String.valueOf(propertyValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_currentLessThanOrEquals_success() {\n        // 准备参数：创建属性上报消息\n        String propertyIdentifier = \"current\";\n        Double propertyValue = 2.5;\n        Double compareValue = 3.0;\n        IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                propertyIdentifier,\n                IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(),\n                String.valueOf(compareValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_stringProperty_success() {\n        // 准备参数：创建属性上报消息\n        String propertyIdentifier = \"mode\";\n        String propertyValue = \"auto\";\n        IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                propertyIdentifier,\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                propertyValue\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_booleanProperty_success() {\n        // 准备参数：创建属性上报消息\n        String propertyIdentifier = \"enabled\";\n        Boolean propertyValue = true;\n        IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                propertyIdentifier,\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                String.valueOf(propertyValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    // ========== 辅助方法 ==========\n\n    /**\n     * 创建设备消息用于测试\n     *\n     * 支持的消息格式：\n     * 1. 直接属性值：params 直接是属性值（适用于简单消息）\n     * 2. 标识符+值：params 包含 identifier 和对应的属性值\n     * 3. properties 结构：params.properties[identifier] = value\n     * 4. data 结构：params.data[identifier] = value\n     * 5. value 字段：params.value = value\n     */\n    private IotDeviceMessage createPropertyPostMessage(String identifier, Object value) {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(\"thing.event.post\"); // 使用事件上报方法\n\n        // 创建符合修复后逻辑的 params 结构\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"identifier\", identifier);\n        // 直接将属性值放在标识符对应的字段中\n        params.put(identifier, value);\n        message.setParams(params);\n\n        return message;\n    }\n\n    /**\n     * 创建使用 properties 结构的消息（模拟真实的属性上报消息）\n     */\n    private IotDeviceMessage createPropertyPostMessageWithProperties(String identifier, Object value) {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(\"thing.property.post\"); // 属性上报方法\n\n        Map<String, Object> properties = new HashMap<>();\n        properties.put(identifier, value);\n\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"properties\", properties);\n        message.setParams(params);\n\n        return message;\n    }\n\n    /**\n     * 创建简单值消息（params 直接是属性值）\n     */\n    private IotDeviceMessage createSimpleValueMessage(Object value) {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(\"thing.property.post\");\n        // 直接将属性值作为 params\n        message.setParams(value);\n\n        return message;\n    }\n\n    /**\n     * 创建有效的条件\n     */\n    private IotSceneRuleDO.TriggerCondition createValidCondition(String identifier, String operator, String param) {\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());\n        condition.setIdentifier(identifier);\n        condition.setOperator(operator);\n        condition.setParam(param);\n        return condition;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcherTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;\n\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotDeviceStateConditionMatcher} 的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotDeviceStateConditionMatcherTest extends IotBaseConditionMatcherTest {\n\n    private IotDeviceStateConditionMatcher matcher;\n\n    @BeforeEach\n    public void setUp() {\n        matcher = new IotDeviceStateConditionMatcher();\n    }\n\n    @Test\n    public void testGetSupportedConditionType() {\n        // 调用\n        IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType();\n\n        // 断言\n        assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_STATE, result);\n    }\n\n    @Test\n    public void testGetPriority() {\n        // 调用\n        int result = matcher.getPriority();\n\n        // 断言\n        assertEquals(30, result);\n    }\n\n    @Test\n    public void testIsEnabled() {\n        // 调用\n        boolean result = matcher.isEnabled();\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_onlineState_success() {\n        // 准备参数\n        IotDeviceStateEnum deviceState = IotDeviceStateEnum.ONLINE;\n        IotDeviceMessage message = createDeviceMessage(deviceState.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                deviceState.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_offlineState_success() {\n        // 准备参数\n        IotDeviceStateEnum deviceState = IotDeviceStateEnum.OFFLINE;\n        IotDeviceMessage message = createDeviceMessage(deviceState.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                deviceState.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_inactiveState_success() {\n        // 准备参数\n        IotDeviceStateEnum deviceState = IotDeviceStateEnum.INACTIVE;\n        IotDeviceMessage message = createDeviceMessage(deviceState.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                deviceState.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_stateMismatch_fail() {\n        // 准备参数\n        IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE;\n        IotDeviceStateEnum expectedState = IotDeviceStateEnum.OFFLINE;\n        IotDeviceMessage message = createDeviceMessage(actualState.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                expectedState.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_notEqualsOperator_success() {\n        // 准备参数\n        IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE;\n        IotDeviceStateEnum compareState = IotDeviceStateEnum.OFFLINE;\n        IotDeviceMessage message = createDeviceMessage(actualState.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),\n                compareState.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_greaterThanOperator_success() {\n        // 准备参数\n        IotDeviceStateEnum actualState = IotDeviceStateEnum.OFFLINE; // 状态值为 2\n        IotDeviceStateEnum compareState = IotDeviceStateEnum.ONLINE; // 状态值为 1\n        IotDeviceMessage message = createDeviceMessage(actualState.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                compareState.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_lessThanOperator_success() {\n        // 准备参数\n        IotDeviceStateEnum actualState = IotDeviceStateEnum.INACTIVE; // 状态值为 0\n        IotDeviceStateEnum compareState = IotDeviceStateEnum.ONLINE; // 状态值为 1\n        IotDeviceMessage message = createDeviceMessage(actualState.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),\n                compareState.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_nullCondition_fail() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());\n\n        // 调用\n        boolean result = matcher.matches(message, null);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullConditionType_fail() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(null);\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_missingOperator_fail() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());\n        condition.setOperator(null);\n        condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString());\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_missingParam_fail() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());\n        condition.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator());\n        condition.setParam(null);\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullMessage_fail() {\n        // 准备参数\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                IotDeviceStateEnum.ONLINE.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(null, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullDeviceState_fail() {\n        // 准备参数\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setParams(null);\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                IotDeviceStateEnum.ONLINE.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_greaterThanOrEqualsOperator_success() {\n        // 准备参数\n        IotDeviceStateEnum deviceState = IotDeviceStateEnum.ONLINE; // 状态值为 1\n        IotDeviceMessage message = createDeviceMessage(deviceState.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(),\n                deviceState.getState().toString() // 比较值也为 1\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_lessThanOrEqualsOperator_success() {\n        // 准备参数\n        IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE; // 状态值为 1\n        IotDeviceStateEnum compareState = IotDeviceStateEnum.OFFLINE; // 状态值为 2\n        IotDeviceMessage message = createDeviceMessage(actualState.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(),\n                compareState.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_invalidOperator_fail() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());\n        condition.setOperator(randomString()); // 随机无效操作符\n        condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString());\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_invalidParamFormat_fail() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.TriggerCondition condition = createValidCondition(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                randomString() // 随机无效状态值\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, condition);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    // ========== 辅助方法 ==========\n\n    /**\n     * 创建设备消息\n     */\n    private IotDeviceMessage createDeviceMessage(Integer deviceState) {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setParams(deviceState);\n        return message;\n    }\n\n    /**\n     * 创建有效的条件\n     */\n    private IotSceneRuleDO.TriggerCondition createValidCondition(String operator, String param) {\n        IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();\n        condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());\n        condition.setOperator(operator);\n        condition.setParam(param);\n        return condition;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcherTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static cn.hutool.core.util.RandomUtil.randomInt;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotDeviceEventPostTriggerMatcher} 的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotDeviceEventPostTriggerMatcherTest extends IotBaseConditionMatcherTest {\n\n    private IotDeviceEventPostTriggerMatcher matcher;\n\n    @BeforeEach\n    public void setUp() {\n        matcher = new IotDeviceEventPostTriggerMatcher();\n    }\n\n    @Test\n    public void testGetSupportedTriggerType_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();\n\n        // 断言\n        assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST, result);\n    }\n\n    @Test\n    public void testGetPriority_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        int result = matcher.getPriority();\n\n        // 断言\n        assertEquals(30, result);\n    }\n\n    @Test\n    public void testIsEnabled_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        boolean result = matcher.isEnabled();\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_alarmEventSuccess() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", randomString())\n                        .put(\"message\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_errorEventSuccess() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"code\", randomInt())\n                        .put(\"description\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_infoEventSuccess() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"status\", randomString())\n                        .put(\"timestamp\", System.currentTimeMillis())\n                        .build())\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_eventIdentifierMismatch() {\n        // 准备参数\n        String messageIdentifier = randomString();\n        String triggerIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", messageIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_wrongMessageMethod() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法\n        message.setParams(eventParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTriggerIdentifier() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType());\n        trigger.setIdentifier(null); // 缺少标识符\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullMessageParams() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod());\n        message.setParams(null);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_invalidMessageParams() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod());\n        message.setParams(randomString()); // 不是 Map 类型\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_missingEventIdentifierInParams() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", randomString())\n                        .build()) // 缺少 identifier 字段\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTrigger() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n\n        // 调用\n        boolean result = matcher.matches(message, null);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTriggerType() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(null);\n        trigger.setIdentifier(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_complexEventValueSuccess() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"type\", randomString())\n                        .put(\"duration\", randomInt())\n                        .put(\"components\", new String[]{randomString(), randomString()})\n                        .put(\"priority\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_emptyEventValueSuccess() {\n        // 准备参数\n        String eventIdentifier = randomString();\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.ofEntries()) // 空的事件值\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_caseSensitiveIdentifierMismatch() {\n        // 准备参数\n        String eventIdentifier = randomString().toUpperCase(); // 大写\n        String triggerIdentifier = eventIdentifier.toLowerCase(); // 小写\n        Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", eventIdentifier)\n                .put(\"value\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createEventPostMessage(eventParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        // 根据实际实现，这里可能需要调整期望结果\n        // 如果实现是大小写敏感的，则应该为 false\n        assertFalse(result);\n    }\n\n    // ========== 辅助方法 ==========\n\n    /**\n     * 创建事件上报消息\n     */\n    private IotDeviceMessage createEventPostMessage(Map<String, Object> eventParams) {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod());\n        message.setParams(eventParams);\n        return message;\n    }\n\n    /**\n     * 创建有效的触发器\n     */\n    private IotSceneRuleDO.Trigger createValidTrigger(String identifier) {\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType());\n        trigger.setIdentifier(identifier);\n        return trigger;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcherTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static cn.hutool.core.util.RandomUtil.randomDouble;\nimport static cn.hutool.core.util.RandomUtil.randomInt;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotDevicePropertyPostTriggerMatcher} 的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotDevicePropertyPostTriggerMatcherTest extends IotBaseConditionMatcherTest {\n\n    private IotDevicePropertyPostTriggerMatcher matcher;\n\n    @BeforeEach\n    public void setUp() {\n        matcher = new IotDevicePropertyPostTriggerMatcher();\n    }\n\n    @Test\n    public void testGetSupportedTriggerType_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();\n\n        // 断言\n        assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST, result);\n    }\n\n    @Test\n    public void testGetPriority_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        int result = matcher.getPriority();\n\n        // 断言\n        assertEquals(20, result);\n    }\n\n    @Test\n    public void testIsEnabled_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        boolean result = matcher.isEnabled();\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_numericPropertyGreaterThanSuccess() {\n        // 准备参数\n        String propertyName = randomString();\n        Double propertyValue = 25.5;\n        Integer compareValue = 20;\n        Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())\n                .put(propertyName, propertyValue)\n                .build();\n        IotDeviceMessage message = createPropertyPostMessage(properties);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                propertyName,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                String.valueOf(compareValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_integerPropertyEqualsSuccess() {\n        // 准备参数\n        String propertyName = randomString();\n        Integer propertyValue = randomInt();\n        Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())\n                .put(propertyName, propertyValue)\n                .build();\n        IotDeviceMessage message = createPropertyPostMessage(properties);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                propertyName,\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                String.valueOf(propertyValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_propertyValueNotMeetCondition() {\n        // 准备参数\n        String propertyName = randomString();\n        Double propertyValue = 15.0;\n        Integer compareValue = 20;\n        Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())\n                .put(propertyName, propertyValue)\n                .build();\n        IotDeviceMessage message = createPropertyPostMessage(properties);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                propertyName,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                String.valueOf(compareValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_propertyNotFound() {\n        // 准备参数\n        String existingProperty = randomString();\n        String missingProperty = randomString();\n        Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())\n                .put(existingProperty, randomDouble())\n                .build();\n        IotDeviceMessage message = createPropertyPostMessage(properties);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                missingProperty, // 不存在的属性\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                String.valueOf(randomInt())\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_wrongMessageMethod() {\n        // 准备参数\n        String propertyName = randomString();\n        Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())\n                .put(propertyName, randomDouble())\n                .build();\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod());\n        message.setParams(properties);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                propertyName,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                String.valueOf(randomInt())\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTriggerIdentifier() {\n        // 准备参数\n        String propertyName = randomString();\n        Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())\n                .put(propertyName, randomDouble())\n                .build();\n        IotDeviceMessage message = createPropertyPostMessage(properties);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());\n        trigger.setIdentifier(null); // 缺少标识符\n        trigger.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator());\n        trigger.setValue(String.valueOf(randomInt()));\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullMessageParams() {\n        // 准备参数\n        String propertyName = randomString();\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());\n        message.setParams(null);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                propertyName,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                String.valueOf(randomInt())\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_invalidMessageParams() {\n        // 准备参数\n        String propertyName = randomString();\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());\n        message.setParams(randomString()); // 不是 Map 类型\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                propertyName,\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                String.valueOf(randomInt())\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_lessThanOperatorSuccess() {\n        // 准备参数\n        String propertyName = randomString();\n        Double propertyValue = 15.0;\n        Integer compareValue = 20;\n        Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())\n                .put(propertyName, propertyValue)\n                .build();\n        IotDeviceMessage message = createPropertyPostMessage(properties);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                propertyName,\n                IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),\n                String.valueOf(compareValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_notEqualsOperatorSuccess() {\n        // 准备参数\n        String propertyName = randomString();\n        String propertyValue = randomString();\n        String compareValue = randomString();\n        Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())\n                .put(propertyName, propertyValue)\n                .build();\n        IotDeviceMessage message = createPropertyPostMessage(properties);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                propertyName,\n                IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),\n                compareValue\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_multiplePropertiesTargetPropertySuccess() {\n        // 准备参数\n        String targetProperty = randomString();\n        Integer targetValue = randomInt();\n        Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())\n                .put(randomString(), randomDouble())\n                .put(targetProperty, targetValue)\n                .put(randomString(), randomString())\n                .build();\n        IotDeviceMessage message = createPropertyPostMessage(properties);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                targetProperty,\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                String.valueOf(targetValue)\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    // ========== 辅助方法 ==========\n\n    /**\n     * 创建属性上报消息\n     */\n    private IotDeviceMessage createPropertyPostMessage(Map<String, Object> properties) {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());\n        message.setParams(properties);\n        return message;\n    }\n\n    /**\n     * 创建有效的触发器\n     */\n    private IotSceneRuleDO.Trigger createValidTrigger(String identifier, String operator, String value) {\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());\n        trigger.setIdentifier(identifier);\n        trigger.setOperator(operator);\n        trigger.setValue(value);\n        return trigger;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcherTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static cn.hutool.core.util.RandomUtil.*;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotDeviceServiceInvokeTriggerMatcher} 的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotDeviceServiceInvokeTriggerMatcherTest extends IotBaseConditionMatcherTest {\n\n    private IotDeviceServiceInvokeTriggerMatcher matcher;\n\n    @BeforeEach\n    public void setUp() {\n        matcher = new IotDeviceServiceInvokeTriggerMatcher();\n    }\n\n    @Test\n    public void testGetSupportedTriggerType_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();\n\n        // 断言\n        assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE, result);\n    }\n\n    @Test\n    public void testGetPriority_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        int result = matcher.getPriority();\n\n        // 断言\n        assertEquals(40, result);\n    }\n\n    @Test\n    public void testIsEnabled_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        boolean result = matcher.isEnabled();\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_serviceInvokeSuccess() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"mode\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_configServiceSuccess() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"interval\", randomInt())\n                        .put(\"enabled\", randomBoolean())\n                        .put(\"threshold\", randomDouble())\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_updateServiceSuccess() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"version\", randomString())\n                        .put(\"url\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_serviceIdentifierMismatch() {\n        // 准备参数\n        String messageIdentifier = randomString();\n        String triggerIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", messageIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"mode\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); // 不匹配的服务标识符\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_wrongMessageMethod() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"mode\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法\n        message.setParams(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTriggerIdentifier() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"mode\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(null); // 缺少标识符\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullMessageParams() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod());\n        message.setParams(null);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_invalidMessageParams() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod());\n        message.setParams(randomString()); // 不是 Map 类型\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_missingServiceIdentifierInParams() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"mode\", randomString())\n                        .build()) // 缺少 identifier 字段\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTrigger() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"mode\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n\n        // 调用\n        boolean result = matcher.matches(message, null);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTriggerType() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"mode\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(null);\n        trigger.setIdentifier(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_emptyInputDataSuccess() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.ofEntries()) // 空的输入数据\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_noInputDataSuccess() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                // 没有 inputData 字段\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_complexInputDataSuccess() {\n        // 准备参数\n        String serviceIdentifier = randomString();\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"sensors\", new String[]{randomString(), randomString(), randomString()})\n                        .put(\"precision\", randomDouble())\n                        .put(\"duration\", randomInt())\n                        .put(\"autoSave\", randomBoolean())\n                        .put(\"config\", MapUtil.builder(new HashMap<String, Object>())\n                                .put(\"mode\", randomString())\n                                .put(\"level\", randomString())\n                                .build())\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_caseSensitiveIdentifierMismatch() {\n        // 准备参数\n        String serviceIdentifier = randomString().toUpperCase(); // 大写\n        String triggerIdentifier = serviceIdentifier.toLowerCase(); // 小写\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"mode\", randomString())\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        // 根据实际实现，这里可能需要调整期望结果\n        // 如果实现是大小写敏感的，则应该为 false\n        assertFalse(result);\n    }\n\n\n    // ========== 参数条件匹配测试 ==========\n\n    /**\n     * 测试无参数条件时的匹配逻辑 - 只要标识符匹配就返回 true\n     * **Property 4: 服务调用触发器参数匹配逻辑**\n     * **Validates: Requirements 5.2**\n     */\n    @Test\n    public void testMatches_noParameterCondition_success() {\n        // 准备参数\n        String serviceIdentifier = \"testService\";\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", 5)\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(serviceIdentifier);\n        trigger.setOperator(null); // 无参数条件\n        trigger.setValue(null);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    /**\n     * 测试有参数条件时的匹配逻辑 - 参数条件匹配成功\n     * **Property 4: 服务调用触发器参数匹配逻辑**\n     * **Validates: Requirements 5.1**\n     */\n    @Test\n    public void testMatches_withParameterCondition_greaterThan_success() {\n        // 准备参数\n        String serviceIdentifier = \"level\";\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", 5)\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(serviceIdentifier);\n        trigger.setOperator(\">\"); // 大于操作符\n        trigger.setValue(\"3\");\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    /**\n     * 测试有参数条件时的匹配逻辑 - 参数条件匹配失败\n     * **Property 4: 服务调用触发器参数匹配逻辑**\n     * **Validates: Requirements 5.1**\n     */\n    @Test\n    public void testMatches_withParameterCondition_greaterThan_failure() {\n        // 准备参数\n        String serviceIdentifier = \"level\";\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", 2)\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(serviceIdentifier);\n        trigger.setOperator(\">\"); // 大于操作符\n        trigger.setValue(\"3\");\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    /**\n     * 测试有参数条件时的匹配逻辑 - 等于操作符\n     * **Property 4: 服务调用触发器参数匹配逻辑**\n     * **Validates: Requirements 5.1**\n     */\n    @Test\n    public void testMatches_withParameterCondition_equals_success() {\n        // 准备参数\n        String serviceIdentifier = \"mode\";\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"mode\", \"auto\")\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(serviceIdentifier);\n        trigger.setOperator(\"==\"); // 等于操作符\n        trigger.setValue(\"auto\");\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    /**\n     * 测试参数缺失时的处理 - 消息中缺少 inputData\n     * **Property 4: 服务调用触发器参数匹配逻辑**\n     * **Validates: Requirements 5.3**\n     */\n    @Test\n    public void testMatches_withParameterCondition_missingInputData() {\n        // 准备参数\n        String serviceIdentifier = \"testService\";\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                // 缺少 inputData 字段\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(serviceIdentifier);\n        trigger.setOperator(\">\"); // 配置了参数条件\n        trigger.setValue(\"3\");\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    /**\n     * 测试参数缺失时的处理 - inputData 中缺少指定参数\n     * **Property 4: 服务调用触发器参数匹配逻辑**\n     * **Validates: Requirements 5.3**\n     */\n    @Test\n    public void testMatches_withParameterCondition_missingParam() {\n        // 准备参数\n        String serviceIdentifier = \"level\";\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"otherParam\", 5) // 不是 level 参数\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(serviceIdentifier);\n        trigger.setOperator(\">\"); // 配置了参数条件\n        trigger.setValue(\"3\");\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    /**\n     * 测试只有 operator 没有 value 时不触发参数条件匹配\n     * **Property 4: 服务调用触发器参数匹配逻辑**\n     * **Validates: Requirements 5.2**\n     */\n    @Test\n    public void testMatches_onlyOperator_noValue() {\n        // 准备参数\n        String serviceIdentifier = \"testService\";\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", 5)\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(serviceIdentifier);\n        trigger.setOperator(\">\"); // 只有 operator\n        trigger.setValue(null); // 没有 value\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言：只有 operator 没有 value 时，不触发参数条件匹配，标识符匹配即成功\n        assertTrue(result);\n    }\n\n    /**\n     * 测试只有 value 没有 operator 时不触发参数条件匹配\n     * **Property 4: 服务调用触发器参数匹配逻辑**\n     * **Validates: Requirements 5.2**\n     */\n    @Test\n    public void testMatches_onlyValue_noOperator() {\n        // 准备参数\n        String serviceIdentifier = \"testService\";\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputData\", MapUtil.builder(new HashMap<String, Object>())\n                        .put(\"level\", 5)\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(serviceIdentifier);\n        trigger.setOperator(null); // 没有 operator\n        trigger.setValue(\"3\"); // 只有 value\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言：只有 value 没有 operator 时，不触发参数条件匹配，标识符匹配即成功\n        assertTrue(result);\n    }\n\n    /**\n     * 测试使用 inputParams 字段（替代 inputData）\n     * **Property 4: 服务调用触发器参数匹配逻辑**\n     * **Validates: Requirements 5.1**\n     */\n    @Test\n    public void testMatches_withInputParams_success() {\n        // 准备参数\n        String serviceIdentifier = \"level\";\n        Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())\n                .put(\"identifier\", serviceIdentifier)\n                .put(\"inputParams\", MapUtil.builder(new HashMap<String, Object>()) // 使用 inputParams 而不是 inputData\n                        .put(\"level\", 5)\n                        .build())\n                .build();\n        IotDeviceMessage message = createServiceInvokeMessage(serviceParams);\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(serviceIdentifier);\n        trigger.setOperator(\">\"); // 大于操作符\n        trigger.setValue(\"3\");\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    // ========== 辅助方法 ==========\n\n    /**\n     * 创建服务调用消息\n     */\n    private IotDeviceMessage createServiceInvokeMessage(Map<String, Object> serviceParams) {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod());\n        message.setParams(serviceParams);\n        return message;\n    }\n\n    /**\n     * 创建有效的触发器\n     */\n    private IotSceneRuleDO.Trigger createValidTrigger(String identifier) {\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());\n        trigger.setIdentifier(identifier);\n        return trigger;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcherTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;\nimport cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotDeviceStateUpdateTriggerMatcher} 的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotDeviceStateUpdateTriggerMatcherTest extends IotBaseConditionMatcherTest {\n\n    private IotDeviceStateUpdateTriggerMatcher matcher;\n\n    @BeforeEach\n    public void setUp() {\n        matcher = new IotDeviceStateUpdateTriggerMatcher();\n    }\n\n    @Test\n    public void testGetSupportedTriggerType_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();\n\n        // 断言\n        assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE, result);\n    }\n\n    @Test\n    public void testGetPriority_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        int result = matcher.getPriority();\n\n        // 断言\n        assertEquals(10, result);\n    }\n\n    @Test\n    public void testIsEnabled_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        boolean result = matcher.isEnabled();\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_onlineStateSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                IotDeviceStateEnum.ONLINE.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_offlineStateSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.OFFLINE.getState());\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                IotDeviceStateEnum.OFFLINE.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_stateMismatch() {\n        // 准备参数\n        IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                IotDeviceStateEnum.OFFLINE.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTrigger() {\n        // 准备参数\n        IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());\n\n        // 调用\n        boolean result = matcher.matches(message, null);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTriggerType() {\n        // 准备参数\n        IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(null);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_wrongMessageMethod() {\n        // 准备参数\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());\n        message.setParams(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                IotDeviceStateEnum.ONLINE.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTriggerOperator() {\n        // 准备参数\n        IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());\n        trigger.setOperator(null);\n        trigger.setValue(IotDeviceStateEnum.ONLINE.getState().toString());\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTriggerValue() {\n        // 准备参数\n        IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());\n        trigger.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator());\n        trigger.setValue(null);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullMessageParams() {\n        // 准备参数\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod());\n        message.setParams(null);\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),\n                IotDeviceStateEnum.ONLINE.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_greaterThanOperatorSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),\n                IotDeviceStateEnum.INACTIVE.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_notEqualsOperatorSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\n                IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),\n                IotDeviceStateEnum.OFFLINE.getState().toString()\n        );\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    // ========== 辅助方法 ==========\n\n    /**\n     * 创建设备状态更新消息\n     */\n    private IotDeviceMessage createStateUpdateMessage(Integer state) {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod());\n        message.setParams(state);\n        return message;\n    }\n\n    /**\n     * 创建有效的触发器\n     */\n    private IotSceneRuleDO.Trigger createValidTrigger(String operator, String value) {\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());\n        trigger.setOperator(operator);\n        trigger.setValue(value);\n        return trigger;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcherTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;\n\nimport cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;\nimport static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * {@link IotTimerTriggerMatcher} 的单元测试\n *\n * @author HUIHUI\n */\n@Disabled // TODO @puhui999：单测有报错，先屏蔽\npublic class IotTimerTriggerMatcherTest extends IotBaseConditionMatcherTest {\n\n    private IotTimerTriggerMatcher matcher;\n\n    @BeforeEach\n    public void setUp() {\n        matcher = new IotTimerTriggerMatcher();\n    }\n\n    @Test\n    public void testGetSupportedTriggerType_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();\n\n        // 断言\n        assertEquals(IotSceneRuleTriggerTypeEnum.TIMER, result);\n    }\n\n    @Test\n    public void testGetPriority_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        int result = matcher.getPriority();\n\n        // 断言\n        assertEquals(50, result);\n    }\n\n    @Test\n    public void testIsEnabled_success() {\n        // 准备参数\n        // 无需准备参数\n\n        // 调用\n        boolean result = matcher.isEnabled();\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_validCronExpressionSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = \"0 0 12 * * ?\"; // 每天中午12点\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_everyMinuteCronSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = \"0 * * * * ?\"; // 每分钟\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_weekdaysCronSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = \"0 0 9 ? * MON-FRI\"; // 工作日上午9点\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_invalidCronExpression() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = randomString(); // 随机无效的 cron 表达式\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_emptyCronExpression() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = \"\"; // 空的 cron 表达式\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullCronExpression() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());\n        trigger.setCronExpression(null);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTrigger() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n\n        // 调用\n        boolean result = matcher.matches(message, null);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_nullTriggerType() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(null);\n        trigger.setCronExpression(\"0 0 12 * * ?\");\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_complexCronExpressionSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = \"0 15 10 ? * 6#3\"; // 每月第三个星期五上午10:15\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_incorrectCronFormat() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = \"0 0 12 * *\"; // 缺少字段的 cron 表达式\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_specificDateCronSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = \"0 0 0 1 1 ? 2025\"; // 2025年1月1日午夜\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_everySecondCronSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = \"* * * * * ?\"; // 每秒执行\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    @Test\n    public void testMatches_invalidCharactersCron() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        String cronExpression = \"0 0 12 * * @ #\"; // 包含无效字符的 cron 表达式\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertFalse(result);\n    }\n\n    @Test\n    public void testMatches_rangeCronSuccess() {\n        // 准备参数\n        IotDeviceMessage message = createDeviceMessage();\n        IotSceneRuleDO.Trigger trigger = createValidTrigger(\"0 0 9-17 * * MON-FRI\"); // 工作日9-17点\n\n        // 调用\n        boolean result = matcher.matches(message, trigger);\n\n        // 断言\n        assertTrue(result);\n    }\n\n    // ========== 辅助方法 ==========\n\n    /**\n     * 创建设备消息\n     */\n    private IotDeviceMessage createDeviceMessage() {\n        IotDeviceMessage message = new IotDeviceMessage();\n        message.setDeviceId(randomLongId());\n        return message;\n    }\n\n    /**\n     * 创建有效的定时触发器\n     */\n    private IotSceneRuleDO.Trigger createValidTrigger(String cronExpression) {\n        IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();\n        trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());\n        trigger.setCronExpression(cronExpression);\n        return trigger;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandlerTest.java",
    "content": "package cn.iocoder.yudao.module.iot.service.rule.scene.timer;\n\nimport cn.hutool.core.collection.ListUtil;\nimport cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;\nimport cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;\nimport cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;\nimport cn.iocoder.yudao.module.iot.job.rule.IotSceneRuleJob;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.quartz.SchedulerException;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\n/**\n * {@link IotSceneRuleTimerHandler} 的单元测试类\n *\n * @author HUIHUI\n */\n@ExtendWith(MockitoExtension.class)\npublic class IotSceneRuleTimerHandlerTest {\n\n    @Mock\n    private IotSchedulerManager schedulerManager;\n\n    @InjectMocks\n    private IotSceneRuleTimerHandler timerHandler;\n\n    @BeforeEach\n    void setUp() {\n        // 重置 Mock 对象\n        reset(schedulerManager);\n    }\n\n    @Test\n    public void testRegisterTimerTriggers_success() throws SchedulerException {\n        // 准备参数\n        Long sceneRuleId = 1L;\n        IotSceneRuleDO sceneRule = new IotSceneRuleDO();\n        sceneRule.setId(sceneRuleId);\n        sceneRule.setStatus(0); // 0 表示启用\n        // 创建定时触发器\n        IotSceneRuleDO.Trigger timerTrigger = new IotSceneRuleDO.Trigger();\n        timerTrigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());\n        timerTrigger.setCronExpression(\"0 0 12 * * ?\"); // 每天中午12点\n        sceneRule.setTriggers(ListUtil.toList(timerTrigger));\n\n        // 调用\n        timerHandler.registerTimerTriggers(sceneRule);\n\n        // 验证\n        verify(schedulerManager, times(1)).addOrUpdateJob(\n                eq(IotSceneRuleJob.class),\n                eq(\"iot_scene_rule_timer_\" + sceneRuleId),\n                eq(\"0 0 12 * * ?\"),\n                eq(IotSceneRuleJob.buildJobDataMap(sceneRuleId))\n        );\n    }\n\n    @Test\n    public void testRegisterTimerTriggers_noTimerTrigger() throws SchedulerException {\n        // 准备参数 - 没有定时触发器\n        IotSceneRuleDO sceneRule = new IotSceneRuleDO();\n        sceneRule.setStatus(0); // 0 表示启用\n        // 创建非定时触发器\n        IotSceneRuleDO.Trigger deviceTrigger = new IotSceneRuleDO.Trigger();\n        deviceTrigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());\n        sceneRule.setTriggers(ListUtil.toList(deviceTrigger));\n\n        // 调用\n        timerHandler.registerTimerTriggers(sceneRule);\n\n        // 验证 - 不应该调用调度器\n        verify(schedulerManager, never()).addOrUpdateJob(any(), any(), any(), any());\n    }\n\n    @Test\n    public void testRegisterTimerTriggers_emptyCronExpression() throws SchedulerException {\n        // 准备参数 - CRON 表达式为空\n        Long sceneRuleId = 2L;\n        IotSceneRuleDO sceneRule = new IotSceneRuleDO();\n        sceneRule.setId(sceneRuleId);\n        sceneRule.setStatus(0); // 0 表示启用\n        // 创建定时触发器但没有 CRON 表达式\n        IotSceneRuleDO.Trigger timerTrigger = new IotSceneRuleDO.Trigger();\n        timerTrigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());\n        timerTrigger.setCronExpression(\"\"); // 空的 CRON 表达式\n        sceneRule.setTriggers(ListUtil.toList(timerTrigger));\n\n        // 调用\n        timerHandler.registerTimerTriggers(sceneRule);\n\n        // 验证 - 不应该调用调度器\n        verify(schedulerManager, never()).addOrUpdateJob(any(), any(), any(), any());\n    }\n\n    @Test\n    public void testUnregisterTimerTriggers_success() throws SchedulerException {\n        // 准备参数\n        Long sceneRuleId = 3L;\n\n        // 调用\n        timerHandler.unregisterTimerTriggers(sceneRuleId);\n\n        // 验证\n        verify(schedulerManager, times(1)).deleteJob(\"iot_scene_rule_timer_\" + sceneRuleId);\n    }\n\n    @Test\n    public void testPauseTimerTriggers_success() throws SchedulerException {\n        // 准备参数\n        Long sceneRuleId = 4L;\n\n        // 调用\n        timerHandler.pauseTimerTriggers(sceneRuleId);\n\n        // 验证\n        verify(schedulerManager, times(1)).pauseJob(\"iot_scene_rule_timer_\" + sceneRuleId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/resources/application-unit-test.yaml",
    "content": "spring:\n  main:\n    lazy-initialization: true # 开启懒加载，加快速度\n    banner-mode: off # 单元测试，禁用 Banner\n  # 数据源配置项\n  datasource:\n    name: ruoyi-vue-pro\n    url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式；DATABASE_TO_UPPER 配置表和字段使用小写\n    driver-class-name: org.h2.Driver\n    username: sa\n    password:\n    druid:\n      async-init: true # 单元测试，异步初始化 Druid 连接池，提升启动速度\n      initial-size: 1 # 单元测试，配置为 1，提升启动速度\n  sql:\n    init:\n      schema-locations: classpath:/sql/create_tables.sql\n\nmybatis-plus:\n  lazy-initialization: true # 单元测试，设置 MyBatis Mapper 延迟加载，加速每个单元测试\n  type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject\n\n# 日志配置\nlogging:\n  level:\n    cn.iocoder.yudao.module.iot.service.rule.scene.matcher: DEBUG\n    cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager: INFO\n    cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition: DEBUG\n    cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger: DEBUG\n    root: WARN\n\n--- #################### 定时任务相关配置 ####################\n\n--- #################### 配置中心相关配置 ####################\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项（单元测试，禁用 Lock4j）\n\n--- #################### 监控相关配置 ####################\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  info:\n    base-package: cn.iocoder.yudao\n  tenant: # 多租户相关配置项\n    enable: true\n  xss:\n    enable: false\n  demo: false # 关闭演示模式\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/resources/logback.xml",
    "content": "<configuration>\n    <!-- 引用 Spring Boot 的 logback 基础配置 -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <!-- 控制台输出 -->\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n            <charset>UTF-8</charset>\n        </encoder>\n    </appender>\n\n    <!-- 设置特定包的日志级别 -->\n    <logger name=\"cn.iocoder.yudao.module.iot.service.rule.scene.matcher\" level=\"DEBUG\" additivity=\"false\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </logger>\n\n    <!-- 设置 IotSceneRuleMatcherManager 的日志级别 -->\n    <logger name=\"cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager\" level=\"INFO\"\n            additivity=\"false\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </logger>\n\n    <!-- 设置所有匹配器的日志级别 -->\n    <logger name=\"cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition\" level=\"DEBUG\" additivity=\"false\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </logger>\n\n    <logger name=\"cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger\" level=\"DEBUG\" additivity=\"false\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </logger>\n\n    <!-- 根日志级别 -->\n    <root level=\"WARN\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n</configuration>\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/resources/sql/clean.sql",
    "content": "DELETE FROM \"iot_scene_rule\";\nDELETE FROM \"iot_product\";\nDELETE FROM \"iot_device\";\nDELETE FROM \"iot_thing_model\";\nDELETE FROM \"iot_device_data\";\nDELETE FROM \"iot_alert_config\";\nDELETE FROM \"iot_alert_record\";\nDELETE FROM \"iot_ota_firmware\";\nDELETE FROM \"iot_ota_task\";\nDELETE FROM \"iot_ota_record\";\n"
  },
  {
    "path": "yudao-module-iot/yudao-module-iot-server/src/test/resources/sql/create_tables.sql",
    "content": "CREATE TABLE IF NOT EXISTS \"iot_scene_rule\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(255) NOT NULL DEFAULT '',\n    \"description\" varchar(500) DEFAULT NULL,\n    \"status\" tinyint NOT NULL DEFAULT '0',\n    \"triggers\" text,\n    \"actions\" text,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint NOT NULL DEFAULT '0',\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT 场景联动规则表';\n\nCREATE TABLE IF NOT EXISTS \"iot_product\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(255) NOT NULL DEFAULT '',\n    \"product_key\" varchar(100) NOT NULL DEFAULT '',\n    \"protocol_type\" tinyint NOT NULL DEFAULT '0',\n    \"category_id\" bigint DEFAULT NULL,\n    \"description\" varchar(500) DEFAULT NULL,\n    \"data_format\" tinyint NOT NULL DEFAULT '0',\n    \"device_type\" tinyint NOT NULL DEFAULT '0',\n    \"net_type\" tinyint NOT NULL DEFAULT '0',\n    \"validate_type\" tinyint NOT NULL DEFAULT '0',\n    \"status\" tinyint NOT NULL DEFAULT '0',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint NOT NULL DEFAULT '0',\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT 产品表';\n\nCREATE TABLE IF NOT EXISTS \"iot_device\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"device_name\" varchar(255) NOT NULL DEFAULT '',\n    \"product_id\" bigint NOT NULL,\n    \"device_key\" varchar(100) NOT NULL DEFAULT '',\n    \"device_secret\" varchar(100) NOT NULL DEFAULT '',\n    \"nickname\" varchar(255) DEFAULT NULL,\n    \"status\" tinyint NOT NULL DEFAULT '0',\n    \"status_last_update_time\" timestamp DEFAULT NULL,\n    \"last_online_time\" timestamp DEFAULT NULL,\n    \"last_offline_time\" timestamp DEFAULT NULL,\n    \"active_time\" timestamp DEFAULT NULL,\n    \"ip\" varchar(50) DEFAULT NULL,\n    \"firmware_version\" varchar(50) DEFAULT NULL,\n    \"device_type\" tinyint NOT NULL DEFAULT '0',\n    \"gateway_id\" bigint DEFAULT NULL,\n    \"sub_device_count\" int NOT NULL DEFAULT '0',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint NOT NULL DEFAULT '0',\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT 设备表';\n\nCREATE TABLE IF NOT EXISTS \"iot_thing_model\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"product_id\" bigint NOT NULL,\n    \"identifier\" varchar(100) NOT NULL DEFAULT '',\n    \"name\" varchar(255) NOT NULL DEFAULT '',\n    \"description\" varchar(500) DEFAULT NULL,\n    \"type\" tinyint NOT NULL DEFAULT '1',\n    \"property\" text,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint NOT NULL DEFAULT '0',\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT 物模型表';\n\nCREATE TABLE IF NOT EXISTS \"iot_device_data\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"device_id\" bigint NOT NULL,\n    \"product_id\" bigint NOT NULL,\n    \"identifier\" varchar(100) NOT NULL DEFAULT '',\n    \"type\" tinyint NOT NULL DEFAULT '1',\n    \"data\" text,\n    \"ts\" bigint NOT NULL DEFAULT '0',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT 设备数据表';\n\nCREATE TABLE IF NOT EXISTS \"iot_alert_config\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(255) NOT NULL DEFAULT '',\n    \"product_id\" bigint NOT NULL,\n    \"device_id\" bigint DEFAULT NULL,\n    \"rule_id\" bigint DEFAULT NULL,\n    \"status\" tinyint NOT NULL DEFAULT '0',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint NOT NULL DEFAULT '0',\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT 告警配置表';\n\nCREATE TABLE IF NOT EXISTS \"iot_alert_record\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"alert_config_id\" bigint NOT NULL,\n    \"alert_name\" varchar(255) NOT NULL DEFAULT '',\n    \"product_id\" bigint NOT NULL,\n    \"device_id\" bigint DEFAULT NULL,\n    \"rule_id\" bigint DEFAULT NULL,\n    \"alert_data\" text,\n    \"alert_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deal_status\" tinyint NOT NULL DEFAULT '0',\n    \"deal_time\" timestamp DEFAULT NULL,\n    \"deal_user_id\" bigint DEFAULT NULL,\n    \"deal_remark\" varchar(500) DEFAULT NULL,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint NOT NULL DEFAULT '0',\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT 告警记录表';\n\nCREATE TABLE IF NOT EXISTS \"iot_ota_firmware\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(255) NOT NULL DEFAULT '',\n    \"product_id\" bigint NOT NULL,\n    \"version\" varchar(50) NOT NULL DEFAULT '',\n    \"description\" varchar(500) DEFAULT NULL,\n    \"file_url\" varchar(500) DEFAULT NULL,\n    \"file_size\" bigint NOT NULL DEFAULT '0',\n    \"status\" tinyint NOT NULL DEFAULT '0',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint NOT NULL DEFAULT '0',\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT OTA 固件表';\n\nCREATE TABLE IF NOT EXISTS \"iot_ota_task\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\" varchar(255) NOT NULL DEFAULT '',\n    \"firmware_id\" bigint NOT NULL,\n    \"product_id\" bigint NOT NULL,\n    \"upgrade_type\" tinyint NOT NULL DEFAULT '0',\n    \"status\" tinyint NOT NULL DEFAULT '0',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint NOT NULL DEFAULT '0',\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT OTA 升级任务表';\n\nCREATE TABLE IF NOT EXISTS \"iot_ota_record\" (\n    \"id\" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"task_id\" bigint NOT NULL,\n    \"firmware_id\" bigint NOT NULL,\n    \"device_id\" bigint NOT NULL,\n    \"status\" tinyint NOT NULL DEFAULT '0',\n    \"progress\" int NOT NULL DEFAULT '0',\n    \"error_msg\" varchar(500) DEFAULT NULL,\n    \"start_time\" timestamp DEFAULT NULL,\n    \"end_time\" timestamp DEFAULT NULL,\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint NOT NULL DEFAULT '0',\n    PRIMARY KEY (\"id\")\n) COMMENT 'IoT OTA 升级记录表';\n"
  },
  {
    "path": "yudao-module-mall/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>yudao</artifactId>\n        <groupId>cn.iocoder.cloud</groupId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>yudao-module-mall</artifactId>\n    <packaging>pom</packaging>\n\n    <name>${project.artifactId}</name>\n\n    <description>\n        商城大模块，由 product 商品、promotion 营销、trade 交易、statistics 统计等组成\n    </description>\n    <modules>\n        <module>yudao-module-product-api</module>\n        <module>yudao-module-product-server</module>\n        <module>yudao-module-promotion-api</module>\n        <module>yudao-module-promotion-server</module>\n        <module>yudao-module-trade-api</module>\n        <module>yudao-module-trade-server</module>\n        <module>yudao-module-statistics-api</module>\n        <module>yudao-module-statistics-server</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-mall</artifactId>\n        <version>${revision}</version>\n    </parent>\n\n    <artifactId>yudao-module-product-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        product 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springdoc</groupId>\n            <artifactId>springdoc-openapi-ui</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/category/ProductCategoryApi.java",
    "content": "package cn.iocoder.yudao.module.product.api.category;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.product.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.Collection;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 商品分类\")\npublic interface ProductCategoryApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/category\";\n\n    @GetMapping(PREFIX + \"/valid\")\n    @Operation(summary = \"校验部门是否合法\")\n    @Parameter(name = \"ids\", description = \"商品分类编号数组\", example = \"1,2\", required = true)\n    CommonResult<Boolean> validateCategoryList(@RequestParam(\"ids\") Collection<Long> ids);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/comment/ProductCommentApi.java",
    "content": "package cn.iocoder.yudao.module.product.api.comment;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;\nimport cn.iocoder.yudao.module.product.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\n\nimport javax.validation.Valid;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 产品评论\")\npublic interface ProductCommentApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/comment\";\n\n    @PostMapping(PREFIX + \"/create\")\n    @Operation(summary = \"创建评论\")\n    CommonResult<Long> createComment(@RequestBody @Valid ProductCommentCreateReqDTO createReqDTO);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/comment/dto/ProductCommentCreateReqDTO.java",
    "content": "package cn.iocoder.yudao.module.product.api.comment.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"RPC 服务 - 商品评论创建 Request DTO\")\n@Data\npublic class ProductCommentCreateReqDTO {\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"商品 SKU 编号不能为空\")\n    private Long skuId;\n    @Schema(description = \"订单编号\", example = \"223\")\n    private Long orderId;\n    @Schema(description = \"交易订单项编号\", example = \"666\")\n    private Long orderItemId;\n\n    @Schema(description = \"描述星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"描述星级不能为空\")\n    private Integer descriptionScores;\n    @Schema(description = \"服务星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"服务星级不能为空\")\n    private Integer benefitScores;\n    @Schema(description = \"评论内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"好评\")\n    @NotNull(message = \"评论内容不能为空\")\n    private String content;\n    @Schema(description = \"评论图片地址数组，以逗号分隔最多上传 9 张\", example = \"https://www.iocoder.cn/xxx.jpg,http://www.iocoder.cn/yyy.jpg\")\n    private List<String> picUrls;\n\n    @Schema(description = \"是否匿名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否匿名不能为空\")\n    private Boolean anonymous;\n    @Schema(description = \"评价人\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n    @NotNull(message = \"评价人不能为空\")\n    private Long userId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.product.api;"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/property/dto/ProductPropertyValueDetailRespDTO.java",
    "content": "package cn.iocoder.yudao.module.product.api.property.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"RPC 服务 - 商品属性项的明细 Response DTO\")\n@Data\npublic class ProductPropertyValueDetailRespDTO {\n\n    @Schema(description = \"属性的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long propertyId;\n    @Schema(description = \"属性的名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"颜色\")\n    private String propertyName;\n\n    @Schema(description = \"属性值的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long valueId;\n    @Schema(description = \"属性值的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"红色\")\n    private String valueName;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java",
    "content": "package cn.iocoder.yudao.module.product.api.sku;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;\nimport cn.iocoder.yudao.module.product.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 商品 SKU\")\npublic interface ProductSkuApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/sku\";\n\n    @GetMapping(PREFIX + \"/get\")\n    @Operation(summary = \"查询 SKU 信息\")\n    @Parameter(name = \"id\", description = \"SKU 编号\", required = true, example = \"1024\")\n    CommonResult<ProductSkuRespDTO> getSku(@RequestParam(\"id\") Long id);\n\n    @GetMapping(PREFIX + \"/list\")\n    @Operation(summary = \"批量查询 SKU 信息\")\n    @Parameter(name = \"ids\", description = \"SKU 编号列表\", required = true, example = \"1024,2048\")\n    CommonResult<List<ProductSkuRespDTO>> getSkuList(@RequestParam(\"ids\") Collection<Long> ids);\n\n    /**\n     * 批量查询 SKU MAP\n     *\n     * @param ids SKU 编号列表\n     * @return SKU MAP\n     */\n    default Map<Long, ProductSkuRespDTO> getSkuMap(Collection<Long> ids) {\n        return convertMap(getSkuList(ids).getCheckedData(), ProductSkuRespDTO::getId);\n    }\n\n    @GetMapping(PREFIX + \"/list-by-spu-id\")\n    @Operation(summary = \"批量查询 SKU 信息\")\n    @Parameter(name = \"spuIds\", description = \"SPU 编号列表\", required = true, example = \"1024,2048\")\n    CommonResult<List<ProductSkuRespDTO>> getSkuListBySpuId(@RequestParam(\"spuIds\") Collection<Long> spuIds);\n\n    @PostMapping(PREFIX + \"/update-stock\")\n    @Operation(summary = \"更新 SKU 库存（增加 or 减少）\")\n    CommonResult<Boolean> updateSkuStock(@RequestBody @Valid ProductSkuUpdateStockReqDTO updateStockReqDTO);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java",
    "content": "package cn.iocoder.yudao.module.product.api.sku.dto;\n\nimport cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"RPC 服务 - 商品 SKU 信息 Response DTO\")\n@Data\npublic class ProductSkuRespDTO {\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n    @Schema(description = \"SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long spuId;\n\n    @Schema(description = \"属性数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<ProductPropertyValueDetailRespDTO> properties;\n\n    @Schema(description = \"销售价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer price;\n    @Schema(description = \"市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"200\")\n    private Integer marketPrice;\n    @Schema(description = \"成本价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"300\")\n    private Integer costPrice;\n    @Schema(description = \"SKU 的条形码\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123456789\")\n    private String barCode;\n    @Schema(description = \"图片地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xxx.jpg\")\n    private String picUrl;\n\n    @Schema(description = \"库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer stock;\n    @Schema(description = \"商品重量，单位：kg 千克\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1.5\")\n    private Double weight;\n    @Schema(description = \"商品体积，单位：m^3 平米\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3.0\")\n    private Double volume;\n\n    @Schema(description = \"一级分销的佣金，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"550\")\n    private Integer firstBrokeragePrice;\n    @Schema(description = \"二级分销的佣金，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"250\")\n    private Integer secondBrokeragePrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuUpdateStockReqDTO.java",
    "content": "package cn.iocoder.yudao.module.product.api.sku.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"RPC 服务 - 商品 SKU 更新库存 Request DTO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductSkuUpdateStockReqDTO {\n\n    @Schema(description = \"商品 SKU 数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"商品 SKU 不能为空\")\n    private List<Item> items;\n\n    @Data\n    public static class Item {\n\n        @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        @NotNull(message = \"商品 SKU 编号不能为空\")\n        private Long id;\n\n        @Schema(description = \"库存变化数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        @NotNull(message = \"库存变化数量不能为空\")\n        private Integer incrCount; // 正数：增加库存；负数：扣减库存\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java",
    "content": "package cn.iocoder.yudao.module.product.api.spu;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.product.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 商品 SPU\")\npublic interface ProductSpuApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/spu\";\n\n    @GetMapping(PREFIX + \"/list\")\n    @Schema(description = \"批量查询 SPU 数组\")\n    @Parameter(name = \"ids\", description = \"SPU 编号列表\", required = true, example = \"1,3,5\")\n    CommonResult<List<ProductSpuRespDTO>> getSpuList(@RequestParam(\"ids\") Collection<Long> ids);\n\n    /**\n     * 批量查询 SPU MAP\n     *\n     * @param ids SPU 编号列表\n     * @return SPU MAP\n     */\n    default Map<Long, ProductSpuRespDTO> getSpuMap(Collection<Long> ids) {\n        return convertMap(getSpuList(ids).getCheckedData(), ProductSpuRespDTO::getId);\n    }\n\n    @GetMapping(PREFIX + \"/valid\")\n    @Schema(description = \"批量查询 SPU 数组，并且校验是否 SPU 是否有效\")\n    @Parameter(name = \"ids\", description = \"SPU 编号列表\", required = true, example = \"1,3,5\")\n    CommonResult<List<ProductSpuRespDTO>> validateSpuList(@RequestParam(\"ids\") Collection<Long> ids);\n\n    @GetMapping(PREFIX + \"/get\")\n    @Schema(description = \"获得 SPU\")\n    @Parameter(name = \"id\", description = \"SPU 编号\", required = true, example = \"1\")\n    CommonResult<ProductSpuRespDTO> getSpu(@RequestParam(\"id\") Long id);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java",
    "content": "package cn.iocoder.yudao.module.product.api.spu.dto;\n\nimport cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;\nimport lombok.Data;\n\nimport java.util.List;\n\n// TODO @LeeYan9: ProductSpuRespDTO\n\n/**\n * 商品 SPU 信息 Response DTO\n *\n * @author LeeYan9\n * @since 2022-08-26\n */\n@Data\npublic class ProductSpuRespDTO {\n\n    /**\n     * 商品 SPU 编号，自增\n     */\n    private Long id;\n\n    // ========== 基本信息 =========\n\n    /**\n     * 商品名称\n     */\n    private String name;\n    /**\n     * 单位\n     *\n     * 对应 product_unit 数据字典\n     */\n    private Integer unit;\n\n    /**\n     * 商品分类编号\n     */\n    private Long categoryId;\n    /**\n     * 商品封面图\n     */\n    private String picUrl;\n\n    /**\n     * 商品状态\n     * <p>\n     * 枚举 {@link ProductSpuStatusEnum}\n     */\n    private Integer status;\n\n    // ========== SKU 相关字段 =========\n\n    /**\n     * 规格类型\n     *\n     * false - 单规格\n     * true - 多规格\n     */\n    private Boolean specType;\n    /**\n     * 商品价格，单位使用：分\n     */\n    private Integer price;\n    /**\n     * 市场价，单位使用：分\n     */\n    private Integer marketPrice;\n    /**\n     * 成本价，单位使用：分\n     */\n    private Integer costPrice;\n    /**\n     * 库存\n     */\n    private Integer stock;\n\n    // ========== 物流相关字段 =========\n\n    /**\n     * 配送方式数组\n     *\n     * 对应 DeliveryTypeEnum 枚举\n     */\n    private List<Integer> deliveryTypes;\n\n    /**\n     * 物流配置模板编号\n     *\n     * 对应 TradeDeliveryExpressTemplateDO 的 id 编号\n     */\n    private Long deliveryTemplateId;\n\n    // ========== 营销相关字段 =========\n\n    /**\n     * 赠送积分\n     */\n    private Integer giveIntegral;\n\n    // ========== 分销相关字段 =========\n\n    /**\n     * 分销类型\n     *\n     * false - 默认\n     * true - 自行设置\n     */\n    private Boolean subCommissionType;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ApiConstants.java",
    "content": "package cn.iocoder.yudao.module.product.enums;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\n\n/**\n * API 相关的枚举\n *\n * @author 芋道源码\n */\npublic class ApiConstants {\n\n    /**\n     * 服务名\n     *\n     * 注意，需要保证和 spring.application.name 保持一致\n     */\n    public static final String NAME = \"product-server\";\n\n    public static final String PREFIX = RpcConstants.RPC_API_PREFIX +  \"/product\";\n\n    public static final String VERSION = \"1.0.0\";\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/DictTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.product.enums;\n\n/**\n * product 字典类型的枚举类\n *\n * @author HUIHUI\n */\npublic interface DictTypeConstants {\n\n    String PRODUCT_SPU_STATUS = \"product_spu_status\"; // 商品 SPU 状态\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.product.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * Product 错误码枚举类\n *\n * product 系统，使用 1-008-000-000 段\n */\npublic interface ErrorCodeConstants {\n\n    // ========== 商品分类相关 1-008-001-000 ============\n    ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_008_001_000, \"商品分类不存在\");\n    ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_008_001_001, \"父分类不存在\");\n    ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_008_001_002, \"父分类不能是二级分类\");\n    ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_008_001_003, \"存在子分类，无法删除\");\n    ErrorCode CATEGORY_DISABLED = new ErrorCode(1_008_001_004, \"商品分类({})已禁用，无法使用\");\n    ErrorCode CATEGORY_HAVE_BIND_SPU = new ErrorCode(1_008_001_005, \"类别下存在商品，无法删除\");\n\n    // ========== 商品品牌相关编号 1-008-002-000 ==========\n    ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1_008_002_000, \"品牌不存在\");\n    ErrorCode BRAND_DISABLED = new ErrorCode(1_008_002_001, \"品牌已禁用\");\n    ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1_008_002_002, \"品牌名称已存在\");\n\n    // ========== 商品属性项 1-008-003-000 ==========\n    ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1_008_003_000, \"属性项不存在\");\n    ErrorCode PROPERTY_EXISTS = new ErrorCode(1_008_003_001, \"属性项的名称已存在\");\n    ErrorCode PROPERTY_DELETE_FAIL_VALUE_EXISTS = new ErrorCode(1_008_003_002, \"属性项下存在属性值，无法删除\");\n\n    // ========== 商品属性值 1-008-004-000 ==========\n    ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1_008_004_000, \"属性值不存在\");\n    ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1_008_004_001, \"属性值的名称已存在\");\n\n    // ========== 商品 SPU 1-008-005-000 ==========\n    ErrorCode SPU_NOT_EXISTS = new ErrorCode(1_008_005_000, \"商品 SPU 不存在\");\n    ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1_008_005_001, \"商品分类不正确，原因：必须使用第二级的商品分类及以下\");\n    ErrorCode SPU_SAVE_FAIL_COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1_008_005_002, \"商品 SPU 保存失败，原因：优惠劵不存在\");\n    ErrorCode SPU_NOT_ENABLE = new ErrorCode(1_008_005_003, \"商品 SPU【{}】不处于上架状态\");\n    ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1_008_005_004, \"商品 SPU 不处于回收站状态\");\n\n    // ========== 商品 SKU 1-008-006-000 ==========\n    ErrorCode SKU_NOT_EXISTS = new ErrorCode(1_008_006_000, \"商品 SKU 不存在\");\n    ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1_008_006_001, \"商品 SKU 的属性组合存在重复\");\n    ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1_008_006_002, \"一个 SPU 下的每个 SKU，其属性项必须一致\");\n    ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1_008_006_003, \"一个 SPU 下的每个 SKU，必须不重复\");\n    ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1_008_006_004, \"商品 SKU 库存不足\");\n\n    // ========== 商品 评价 1-008-007-000 ==========\n    ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1_008_007_000, \"商品评价不存在\");\n    ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1_008_007_001, \"订单的商品评价已存在\");\n\n    // ========== 商品 收藏 1-008-008-000 ==========\n    ErrorCode FAVORITE_EXISTS = new ErrorCode(1_008_008_000, \"该商品已经被收藏\");\n    ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1_008_008_001, \"商品收藏不存在\");\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ProductConstants.java",
    "content": "package cn.iocoder.yudao.module.product.enums;\n\n/**\n * Product 常量\n *\n * @author HUIHUI\n */\npublic interface ProductConstants {\n\n    /**\n     * 警戒库存 TODO 警戒库存暂时为 10，后期需要使用常量或者数据库配置替换\n     */\n    int ALERT_STOCK = 10;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/comment/ProductCommentAuditStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.product.enums.comment;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 商品评论的审批状态枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum ProductCommentAuditStatusEnum implements ArrayValuable<Integer> {\n\n    NONE(0, \"待审核\"),\n    APPROVE(1, \"审批通过\"),\n    REJECT(2, \"审批不通过\"),;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(ProductCommentAuditStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 审批状态\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/comment/ProductCommentScoresEnum.java",
    "content": "package cn.iocoder.yudao.module.product.enums.comment;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 商品评论的星级枚举\n *\n * @author wangzhs\n */\n@Getter\n@AllArgsConstructor\npublic enum ProductCommentScoresEnum implements ArrayValuable<Integer> {\n\n    ONE(1, \"1星\"),\n    TWO(2, \"2星\"),\n    THREE(3, \"3星\"),\n    FOUR(4, \"4星\"),\n    FIVE(5, \"5星\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(ProductCommentScoresEnum::getScores).toArray(Integer[]::new);\n\n    /**\n     * 星级\n     */\n    private final Integer scores;\n\n    /**\n     * 星级名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.product.enums.spu;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 商品 SPU 状态\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum ProductSpuStatusEnum implements ArrayValuable<Integer> {\n\n    RECYCLE(-1, \"回收站\"),\n    DISABLE(0, \"下架\"),\n    ENABLE(1, \"上架\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(ProductSpuStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    /**\n     * 判断是否处于【上架】状态\n     *\n     * @param status 状态\n     * @return 是否处于【上架】状态\n     */\n    public static boolean isEnable(Integer status) {\n        return ENABLE.getStatus().equals(status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:8-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-module-product-server\nWORKDIR /yudao-module-product-server\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-module-product-server.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48080 端口\nEXPOSE 48100\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-mall</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-product-server</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        product 模块，主要实现商品相关功能\n        例如：品牌、商品分类、spu、sku等功能。\n    </description>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-product-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-member-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/ProductServerApplication.java",
    "content": "package cn.iocoder.yudao.module.product;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n *\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication\npublic class ProductServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(ProductServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/api/category/ProductCategoryApiImpl.java",
    "content": "package cn.iocoder.yudao.module.product.api.category;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.product.service.category.ProductCategoryService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 商品分类 API 接口实现类\n *\n * @author owen\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class ProductCategoryApiImpl implements ProductCategoryApi {\n\n    @Resource\n    private ProductCategoryService productCategoryService;\n\n    @Override\n    public CommonResult<Boolean> validateCategoryList(Collection<Long> ids) {\n        productCategoryService.validateCategoryList(ids);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/api/comment/ProductCommentApiImpl.java",
    "content": "package cn.iocoder.yudao.module.product.api.comment;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;\nimport cn.iocoder.yudao.module.product.service.comment.ProductCommentService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 商品评论 API 实现类\n *\n * @author HUIHUI\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class ProductCommentApiImpl implements ProductCommentApi {\n\n    @Resource\n    private ProductCommentService productCommentService;\n\n    @Override\n    public CommonResult<Long> createComment(ProductCommentCreateReqDTO createReqDTO) {\n        return success(productCommentService.createComment(createReqDTO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/api/package-info.java",
    "content": "package cn.iocoder.yudao.module.product.api;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java",
    "content": "package cn.iocoder.yudao.module.product.api.sku;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;\nimport cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport cn.iocoder.yudao.module.product.service.sku.ProductSkuService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 商品 SKU API 实现类\n *\n * @author LeeYan9\n * @since 2022-09-06\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class ProductSkuApiImpl implements ProductSkuApi {\n\n    @Resource\n    private ProductSkuService productSkuService;\n\n    @Override\n    public CommonResult<ProductSkuRespDTO> getSku(Long id) {\n        ProductSkuDO sku = productSkuService.getSku(id);\n        return success(BeanUtils.toBean(sku, ProductSkuRespDTO.class));\n    }\n\n    @Override\n    public CommonResult<List<ProductSkuRespDTO>> getSkuList(Collection<Long> ids) {\n        List<ProductSkuDO> skus = productSkuService.getSkuList(ids);\n        return success(BeanUtils.toBean(skus, ProductSkuRespDTO.class));\n    }\n\n    @Override\n    public CommonResult<List<ProductSkuRespDTO>> getSkuListBySpuId(Collection<Long> spuIds) {\n        List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spuIds);\n        return success(BeanUtils.toBean(skus, ProductSkuRespDTO.class));\n    }\n\n    @Override\n    public CommonResult<Boolean> updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO) {\n        productSkuService.updateSkuStock(updateStockReqDTO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java",
    "content": "package cn.iocoder.yudao.module.product.api.spu;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 商品 SPU API 接口实现类\n *\n * @author LeeYan9\n * @since 2022-09-06\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class ProductSpuApiImpl implements ProductSpuApi {\n\n    @Resource\n    private ProductSpuService spuService;\n\n    @Override\n    public CommonResult<List<ProductSpuRespDTO>> getSpuList(Collection<Long> ids) {\n        List<ProductSpuDO> spus = spuService.getSpuList(ids);\n        return success(BeanUtils.toBean(spus, ProductSpuRespDTO.class));\n    }\n\n    @Override\n    public CommonResult<List<ProductSpuRespDTO>> validateSpuList(Collection<Long> ids) {\n        List<ProductSpuDO> spus = spuService.validateSpuList(ids);\n        return success(BeanUtils.toBean(spus, ProductSpuRespDTO.class));\n    }\n\n    @Override\n    public CommonResult<ProductSpuRespDTO> getSpu(Long id) {\n        ProductSpuDO spu = spuService.getSpu(id);\n        return success(BeanUtils.toBean(spu, ProductSpuRespDTO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/ProductBrandController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.brand;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.*;\nimport cn.iocoder.yudao.module.product.convert.brand.ProductBrandConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;\nimport cn.iocoder.yudao.module.product.service.brand.ProductBrandService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 商品品牌\")\n@RestController\n@RequestMapping(\"/product/brand\")\n@Validated\npublic class ProductBrandController {\n\n    @Resource\n    private ProductBrandService brandService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建品牌\")\n    @PreAuthorize(\"@ss.hasPermission('product:brand:create')\")\n    public CommonResult<Long> createBrand(@Valid @RequestBody ProductBrandCreateReqVO createReqVO) {\n        return success(brandService.createBrand(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新品牌\")\n    @PreAuthorize(\"@ss.hasPermission('product:brand:update')\")\n    public CommonResult<Boolean> updateBrand(@Valid @RequestBody ProductBrandUpdateReqVO updateReqVO) {\n        brandService.updateBrand(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除品牌\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('product:brand:delete')\")\n    public CommonResult<Boolean> deleteBrand(@RequestParam(\"id\") Long id) {\n        brandService.deleteBrand(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得品牌\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('product:brand:query')\")\n    public CommonResult<ProductBrandRespVO> getBrand(@RequestParam(\"id\") Long id) {\n        ProductBrandDO brand = brandService.getBrand(id);\n        return success(ProductBrandConvert.INSTANCE.convert(brand));\n    }\n\n    @GetMapping(\"/list-all-simple\")\n    @Operation(summary = \"获取品牌精简信息列表\", description = \"主要用于前端的下拉选项\")\n    public CommonResult<List<ProductBrandSimpleRespVO>> getSimpleBrandList() {\n        // 获取品牌列表，只要开启状态的\n        List<ProductBrandDO> list = brandService.getBrandListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        // 排序后，返回给前端\n        return success(ProductBrandConvert.INSTANCE.convertList1(list));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得品牌分页\")\n    @PreAuthorize(\"@ss.hasPermission('product:brand:query')\")\n    public CommonResult<PageResult<ProductBrandRespVO>> getBrandPage(@Valid ProductBrandPageReqVO pageVO) {\n        PageResult<ProductBrandDO> pageResult = brandService.getBrandPage(pageVO);\n        return success(ProductBrandConvert.INSTANCE.convertPage(pageResult));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得品牌列表\")\n    @PreAuthorize(\"@ss.hasPermission('product:brand:query')\")\n    public CommonResult<List<ProductBrandRespVO>> getBrandList(@Valid ProductBrandListReqVO listVO) {\n        List<ProductBrandDO> list = brandService.getBrandList(listVO);\n        list.sort(Comparator.comparing(ProductBrandDO::getSort));\n        return success(ProductBrandConvert.INSTANCE.convertList(list));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandBaseVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.brand.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n* 商品品牌 Base VO，提供给添加、修改、详细的子 VO 使用\n* 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n*/\n@Data\npublic class ProductBrandBaseVO {\n\n    @Schema(description = \"品牌名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"苹果\")\n    @NotNull(message = \"品牌名称不能为空\")\n    private String name;\n\n    @Schema(description = \"品牌图片\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"品牌图片不能为空\")\n    private String picUrl;\n\n    @Schema(description = \"品牌排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"品牌排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"品牌描述\", example = \"描述\")\n    private String description;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.brand.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 商品品牌创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductBrandCreateReqVO extends ProductBrandBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandListReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.brand.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 商品品牌分页 Request VO\")\n@Data\npublic class ProductBrandListReqVO {\n\n    @Schema(description = \"品牌名称\", example = \"苹果\")\n    private String name;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.brand.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 商品品牌分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductBrandPageReqVO extends PageParam {\n\n    @Schema(description = \"品牌名称\", example = \"苹果\")\n    private String name;\n\n    @Schema(description = \"状态\", example = \"0\")\n    private Integer status;\n\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"创建时间\")\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.brand.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 品牌 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductBrandRespVO extends ProductBrandBaseVO {\n\n    @Schema(description = \"品牌编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandSimpleRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.brand.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Schema(description = \"管理后台 - 品牌精简信息 Response VO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductBrandSimpleRespVO {\n\n    @Schema(description = \"品牌编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"品牌名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"苹果\")\n    private String name;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.brand.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 商品品牌更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductBrandUpdateReqVO extends ProductBrandBaseVO {\n\n    @Schema(description = \"品牌编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"品牌编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/ProductCategoryController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.category;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryRespVO;\nimport cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategorySaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;\nimport cn.iocoder.yudao.module.product.service.category.ProductCategoryService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 商品分类\")\n@RestController\n@RequestMapping(\"/product/category\")\n@Validated\npublic class ProductCategoryController {\n\n    @Resource\n    private ProductCategoryService categoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建商品分类\")\n    @PreAuthorize(\"@ss.hasPermission('product:category:create')\")\n    public CommonResult<Long> createCategory(@Valid @RequestBody ProductCategorySaveReqVO createReqVO) {\n        return success(categoryService.createCategory(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新商品分类\")\n    @PreAuthorize(\"@ss.hasPermission('product:category:update')\")\n    public CommonResult<Boolean> updateCategory(@Valid @RequestBody ProductCategorySaveReqVO updateReqVO) {\n        categoryService.updateCategory(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除商品分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('product:category:delete')\")\n    public CommonResult<Boolean> deleteCategory(@RequestParam(\"id\") Long id) {\n        categoryService.deleteCategory(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得商品分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('product:category:query')\")\n    public CommonResult<ProductCategoryRespVO> getCategory(@RequestParam(\"id\") Long id) {\n        ProductCategoryDO category = categoryService.getCategory(id);\n        return success(BeanUtils.toBean(category, ProductCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得商品分类列表\")\n    @PreAuthorize(\"@ss.hasPermission('product:category:query')\")\n    public CommonResult<List<ProductCategoryRespVO>> getCategoryList(@Valid ProductCategoryListReqVO listReqVO) {\n        List<ProductCategoryDO> list = categoryService.getCategoryList(listReqVO);\n        list.sort(Comparator.comparing(ProductCategoryDO::getSort));\n        return success(BeanUtils.toBean(list, ProductCategoryRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryListReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.category.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.Collection;\n\n@Schema(description = \"管理后台 - 商品分类列表查询 Request VO\")\n@Data\npublic class ProductCategoryListReqVO {\n\n    @Schema(description = \"分类名称\", example = \"办公文具\")\n    private String name;\n\n    @Schema(description = \"开启状态\", example = \"0\")\n    private Integer status;\n\n    @Schema(description = \"父分类编号\", example = \"1\")\n    private Long parentId;\n\n    @Schema(description = \"父分类编号数组\", example = \"1,2,3\")\n    private Collection<Long> parentIds;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.category.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 商品分类 Response VO\")\n@Data\npublic class ProductCategoryRespVO {\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Long id;\n\n    @Schema(description = \"父分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long parentId;\n\n    @Schema(description = \"分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"办公文具\")\n    private String name;\n\n    @Schema(description = \"移动端分类图\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String picUrl;\n\n    @Schema(description = \"分类排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer sort;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer status;\n\n    @Schema(description = \"分类描述\", example = \"描述\")\n    private String description;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategorySaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.category.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 商品分类新增/更新 Request VO\")\n@Data\npublic class ProductCategorySaveReqVO {\n\n    @Schema(description = \"分类编号\", example = \"2\")\n    private Long id;\n\n    @Schema(description = \"父分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"父分类编号不能为空\")\n    private Long parentId;\n\n    @Schema(description = \"分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"办公文具\")\n    @NotBlank(message = \"分类名称不能为空\")\n    private String name;\n\n    @Schema(description = \"移动端分类图\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotBlank(message = \"移动端分类图不能为空\")\n    private String picUrl;\n\n    @Schema(description = \"分类排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer sort;\n\n    @Schema(description = \"开启状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"开启状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"分类描述\", example = \"描述\")\n    private String description;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.http",
    "content": ""
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.comment;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.*;\nimport cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;\nimport cn.iocoder.yudao.module.product.service.comment.ProductCommentService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - 商品评价\")\n@RestController\n@RequestMapping(\"/product/comment\")\n@Validated\npublic class ProductCommentController {\n\n    @Resource\n    private ProductCommentService productCommentService;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得商品评价分页\")\n    @PreAuthorize(\"@ss.hasPermission('product:comment:query')\")\n    public CommonResult<PageResult<ProductCommentRespVO>> getCommentPage(@Valid ProductCommentPageReqVO pageVO) {\n        PageResult<ProductCommentDO> pageResult = productCommentService.getCommentPage(pageVO);\n        return success(BeanUtils.toBean(pageResult, ProductCommentRespVO.class));\n    }\n\n    @PutMapping(\"/update-visible\")\n    @Operation(summary = \"显示 / 隐藏评论\")\n    @PreAuthorize(\"@ss.hasPermission('product:comment:update')\")\n    public CommonResult<Boolean> updateCommentVisible(@Valid @RequestBody ProductCommentUpdateVisibleReqVO updateReqVO) {\n        productCommentService.updateCommentVisible(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/reply\")\n    @Operation(summary = \"商家回复\")\n    @PreAuthorize(\"@ss.hasPermission('product:comment:update')\")\n    public CommonResult<Boolean> commentReply(@Valid @RequestBody ProductCommentReplyReqVO replyVO) {\n        productCommentService.replyComment(replyVO, getLoginUserId());\n        return success(true);\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"添加自评\")\n    @PreAuthorize(\"@ss.hasPermission('product:comment:update')\")\n    public CommonResult<Boolean> createComment(@Valid @RequestBody ProductCommentCreateReqVO createReqVO) {\n        productCommentService.createComment(createReqVO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.comment.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 商品评价创建 Request VO\")\n@Data\npublic class ProductCommentCreateReqVO {\n\n    @Schema(description = \"评价人\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"16868\")\n    private Long userId;\n\n    @Schema(description = \"评价订单项\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19292\")\n    private Long orderItemId;\n\n    @Schema(description = \"评价人名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"小姑凉\")\n    @NotNull(message = \"评价人名称不能为空\")\n    private String userNickname;\n\n    @Schema(description = \"评价人头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xx.png\")\n    @NotNull(message = \"评价人头像不能为空\")\n    private String userAvatar;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"商品 SKU 编号不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"描述星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"描述星级不能为空\")\n    private Integer descriptionScores;\n\n    @Schema(description = \"服务星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"服务星级分不能为空\")\n    private Integer benefitScores;\n\n    @Schema(description = \"评论内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"穿起来非常丝滑凉快\")\n    @NotNull(message = \"评论内容不能为空\")\n    private String content;\n\n    @Schema(description = \"评论图片地址数组，以逗号分隔最多上传 9 张\", requiredMode = Schema.RequiredMode.REQUIRED,\n            example = \"[https://www.iocoder.cn/xx.png]\")\n    @Size(max = 9, message = \"评论图片地址数组长度不能超过 9 张\")\n    private List<String> picUrls;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.comment.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 商品评价分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductCommentPageReqVO extends PageParam {\n\n    @Schema(description = \"评价人名称\", example = \"王二狗\")\n    private String userNickname;\n\n    @Schema(description = \"交易订单编号\", example = \"24428\")\n    private Long orderId;\n\n    @Schema(description = \"商品SPU编号\", example = \"29502\")\n    private Long spuId;\n\n    @Schema(description = \"商品SPU名称\", example = \"感冒药\")\n    private String spuName;\n\n    @Schema(description = \"评分星级 1-5 分\", example = \"5\")\n    @InEnum(ProductCommentScoresEnum.class)\n    private Integer scores;\n\n    @Schema(description = \"商家是否回复\", example = \"true\")\n    private Boolean replyStatus;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentReplyReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.comment.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 商品评价的商家回复 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class ProductCommentReplyReqVO {\n\n    @Schema(description = \"评价编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15721\")\n    @NotNull(message = \"评价编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"商家回复内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"谢谢亲\")\n    @NotEmpty(message = \"商家回复内容不能为空\")\n    private String replyContent;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.comment.vo;\n\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuSaveReqVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 商品评价 Response VO\")\n@Data\npublic class ProductCommentRespVO {\n\n    @Schema(description = \"订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24965\")\n    private Long id;\n\n    @Schema(description = \"评价人\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"16868\")\n    private Long userId;\n\n    @Schema(description = \"评价人名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"小姑凉\")\n    private String userNickname;\n\n    @Schema(description = \"评价人头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xx.png\")\n    private String userAvatar;\n    @Schema(description = \"是否匿名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    private Boolean anonymous;\n\n    @Schema(description = \"交易订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24428\")\n    private Long orderId;\n\n    @Schema(description = \"评价订单项\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19292\")\n    private Long orderItemId;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉丝滑透气小短袖\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long skuId;\n\n    @Schema(description = \"商品 SPU 名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    private String spuName;\n\n    @Schema(description = \"商品 SKU 图片地址\", example = \"https://www.iocoder.cn/yudao.jpg\")\n    private String skuPicUrl;\n\n    @Schema(description = \"商品 SKU 规格值数组\")\n    private List<ProductSkuSaveReqVO.Property> skuProperties;\n\n    @Schema(description = \"是否可见\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Boolean visible;\n\n    @Schema(description = \"评分星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    private Integer scores;\n\n    @Schema(description = \"描述星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    private Integer descriptionScores;\n\n    @Schema(description = \"服务星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    private Integer benefitScores;\n\n    @Schema(description = \"评论内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"穿起来非常丝滑凉快\")\n    private String content;\n\n    @Schema(description = \"评论图片地址数组，以逗号分隔最多上传 9 张\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[https://www.iocoder.cn/xx.png]\")\n    private List<String> picUrls;\n\n    @Schema(description = \"商家是否回复\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Boolean replyStatus;\n\n    @Schema(description = \"回复管理员编号\", example = \"9527\")\n    private Long replyUserId;\n\n    @Schema(description = \"商家回复内容\", example = \"感谢好评哦亲(づ￣3￣)づ╭❤～\")\n    private String replyContent;\n\n    @Schema(description = \"商家回复时间\", example = \"2023-08-08 12:20:55\")\n    private LocalDateTime replyTime;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentUpdateVisibleReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.comment.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 商品评价可见修改 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class ProductCommentUpdateVisibleReqVO {\n\n    @Schema(description = \"评价编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15721\")\n    @NotNull(message = \"评价编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"是否可见\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    @NotNull(message = \"是否可见不能为空\")\n    private Boolean visible;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/ProductFavoriteController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.favorite;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoritePageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoriteRespVO;\nimport cn.iocoder.yudao.module.product.convert.favorite.ProductFavoriteConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.service.favorite.ProductFavoriteService;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 商品收藏\")\n@RestController\n@RequestMapping(\"/product/favorite\")\n@Validated\npublic class ProductFavoriteController {\n\n    @Resource\n    private ProductFavoriteService productFavoriteService;\n\n    @Resource\n    private ProductSpuService productSpuService;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得商品收藏分页\")\n    @PreAuthorize(\"@ss.hasPermission('product:favorite:query')\")\n    public CommonResult<PageResult<ProductFavoriteRespVO>> getFavoritePage(@Valid ProductFavoritePageReqVO pageVO) {\n        PageResult<ProductFavoriteDO> pageResult = productFavoriteService.getFavoritePage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n        // 拼接数据\n        List<ProductSpuDO> spuList = productSpuService.getSpuList(convertSet(pageResult.getList(), ProductFavoriteDO::getSpuId));\n        return success(ProductFavoriteConvert.INSTANCE.convertPage(pageResult, spuList));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/vo/ProductFavoriteBaseVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.favorite.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 商品收藏 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class ProductFavoriteBaseVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5036\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/vo/ProductFavoritePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.favorite.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 商品收藏分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductFavoritePageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"5036\")\n    private Long userId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/vo/ProductFavoriteReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.favorite.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 商品收藏的单个 Response VO\")\n@Data\n@ToString(callSuper = true)\npublic class ProductFavoriteReqVO extends  ProductFavoriteBaseVO {\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"32734\")\n    @NotNull(message = \"商品 SPU 编号不能为空\")\n    private Long spuId;\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/vo/ProductFavoriteRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.favorite.vo;\n\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 商品收藏 Response VO\")\n@Data\n@ToString(callSuper = true)\npublic class ProductFavoriteRespVO  extends ProductSpuRespVO {\n\n    @Schema(description = \"userId\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"111\")\n    private Long userId;\n\n    @Schema(description = \"spuId\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"111\")\n    private Long spuId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/ProductBrowseHistoryController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.history;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryRespVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 商品浏览记录\")\n@RestController\n@RequestMapping(\"/product/browse-history\")\n@Validated\npublic class ProductBrowseHistoryController {\n\n    @Resource\n    private ProductBrowseHistoryService browseHistoryService;\n    @Resource\n    private ProductSpuService productSpuService;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得商品浏览记录分页\")\n    @PreAuthorize(\"@ss.hasPermission('product:browse-history:query')\")\n    public CommonResult<PageResult<ProductBrowseHistoryRespVO>> getBrowseHistoryPage(@Valid ProductBrowseHistoryPageReqVO pageReqVO) {\n        PageResult<ProductBrowseHistoryDO> pageResult = browseHistoryService.getBrowseHistoryPage(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n\n        // 得到商品 spu 信息\n        Map<Long, ProductSpuDO> spuMap = productSpuService.getSpuMap(\n                convertSet(pageResult.getList(), ProductBrowseHistoryDO::getSpuId));\n        return success(BeanUtils.toBean(pageResult, ProductBrowseHistoryRespVO.class,\n                vo -> Optional.ofNullable(spuMap.get(vo.getSpuId()))\n                        .ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())\n                                .setSalesCount(spu.getSalesCount()).setStock(spu.getStock()))));\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.history.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.SortablePageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 商品浏览记录分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductBrowseHistoryPageReqVO extends SortablePageParam {\n\n    @Schema(description = \"用户编号\", example = \"4314\")\n    private Long userId;\n\n    @Schema(description = \"用户是否删除\", example = \"false\")\n    private Boolean userDeleted;\n\n    @Schema(description = \"商品 SPU 编号\", example = \"42\")\n    private Long spuId;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.history.vo;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 商品浏览记录 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ProductBrowseHistoryRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29502\")\n    private Long spuId;\n\n    // ========== 商品相关字段 ==========\n\n    @Schema(description = \"商品 SPU 名称\", example = \"赵六\")\n    private String spuName;\n\n    @Schema(description = \"商品封面图\", example = \"https://domain/pic.png\")\n    private String picUrl;\n\n    @Schema(description = \"商品单价\", example = \"100\")\n    private Integer price;\n\n    @Schema(description = \"商品销量\", example = \"100\")\n    private Integer salesCount;\n\n    @Schema(description = \"库存\", example = \"100\")\n    private Integer stock;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.property;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyRespVO;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertySaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;\nimport cn.iocoder.yudao.module.product.service.property.ProductPropertyService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - 商品属性项\")\n@RestController\n@RequestMapping(\"/product/property\")\n@Validated\npublic class ProductPropertyController {\n\n    @Resource\n    private ProductPropertyService productPropertyService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建属性项\")\n    @PreAuthorize(\"@ss.hasPermission('product:property:create')\")\n    public CommonResult<Long> createProperty(@Valid @RequestBody ProductPropertySaveReqVO createReqVO) {\n        return success(productPropertyService.createProperty(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新属性项\")\n    @PreAuthorize(\"@ss.hasPermission('product:property:update')\")\n    public CommonResult<Boolean> updateProperty(@Valid @RequestBody ProductPropertySaveReqVO updateReqVO) {\n        productPropertyService.updateProperty(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除属性项\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('product:property:delete')\")\n    public CommonResult<Boolean> deleteProperty(@RequestParam(\"id\") Long id) {\n        productPropertyService.deleteProperty(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得属性项\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('product:property:query')\")\n    public CommonResult<ProductPropertyRespVO> getProperty(@RequestParam(\"id\") Long id) {\n        ProductPropertyDO property = productPropertyService.getProperty(id);\n        return success(BeanUtils.toBean(property, ProductPropertyRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得属性项分页\")\n    @PreAuthorize(\"@ss.hasPermission('product:property:query')\")\n    public CommonResult<PageResult<ProductPropertyRespVO>> getPropertyPage(@Valid ProductPropertyPageReqVO pageVO) {\n        PageResult<ProductPropertyDO> pageResult = productPropertyService.getPropertyPage(pageVO);\n        return success(BeanUtils.toBean(pageResult, ProductPropertyRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得属性项精简列表\")\n    public CommonResult<List<ProductPropertyRespVO>> getPropertySimpleList() {\n        List<ProductPropertyDO> list = productPropertyService.getPropertyList();\n        return success(convertList(list, property -> new ProductPropertyRespVO() // 只返回 id、name 属性\n                .setId(property.getId()).setName(property.getName())));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyValueController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.property;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueSaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;\nimport cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.singleton;\n\n@Tag(name = \"管理后台 - 商品属性值\")\n@RestController\n@RequestMapping(\"/product/property/value\")\n@Validated\npublic class ProductPropertyValueController {\n\n    @Resource\n    private ProductPropertyValueService productPropertyValueService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建属性值\")\n    @PreAuthorize(\"@ss.hasPermission('product:property:create')\")\n    public CommonResult<Long> createPropertyValue(@Valid @RequestBody ProductPropertyValueSaveReqVO createReqVO) {\n        return success(productPropertyValueService.createPropertyValue(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新属性值\")\n    @PreAuthorize(\"@ss.hasPermission('product:property:update')\")\n    public CommonResult<Boolean> updatePropertyValue(@Valid @RequestBody ProductPropertyValueSaveReqVO updateReqVO) {\n        productPropertyValueService.updatePropertyValue(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除属性值\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('product:property:delete')\")\n    public CommonResult<Boolean> deletePropertyValue(@RequestParam(\"id\") Long id) {\n        productPropertyValueService.deletePropertyValue(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得属性值\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('product:property:query')\")\n    public CommonResult<ProductPropertyValueRespVO> getPropertyValue(@RequestParam(\"id\") Long id) {\n        ProductPropertyValueDO value = productPropertyValueService.getPropertyValue(id);\n        return success(BeanUtils.toBean(value, ProductPropertyValueRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得属性值分页\")\n    @PreAuthorize(\"@ss.hasPermission('product:property:query')\")\n    public CommonResult<PageResult<ProductPropertyValueRespVO>> getPropertyValuePage(@Valid ProductPropertyValuePageReqVO pageVO) {\n        PageResult<ProductPropertyValueDO> pageResult = productPropertyValueService.getPropertyValuePage(pageVO);\n        return success(BeanUtils.toBean(pageResult, ProductPropertyValueRespVO.class));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得属性值精简列表\")\n    @Parameter(name = \"propertyId\", description = \"属性项编号\", required = true, example = \"1024\")\n    public CommonResult<List<ProductPropertyValueRespVO>> getPropertyValueSimpleList(@RequestParam(\"propertyId\") Long propertyId) {\n        List<ProductPropertyValueDO> list = productPropertyValueService.getPropertyValueListByPropertyId(singleton(propertyId));\n        return success(convertList(list, value -> new ProductPropertyValueRespVO() // 只返回 id、name 属性\n                .setId(value.getId()).setName(value.getName())));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 属性项 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductPropertyPageReqVO extends PageParam {\n\n    @Schema(description = \"名称\", example = \"颜色\")\n    private String name;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"创建时间\")\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 属性项 Response VO\")\n@Data\npublic class ProductPropertyRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"颜色\")\n    private String name;\n\n    @Schema(description = \"备注\", example = \"颜色\")\n    private String remark;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertySaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\n\n@Schema(description = \"管理后台 - 属性项新增/更新 Request VO\")\n@Data\npublic class ProductPropertySaveReqVO {\n\n    @Schema(description = \"主键\", example = \"1\")\n    private Long id;\n\n    @Schema(description = \"名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"颜色\")\n    @NotBlank(message = \"名称不能为空\")\n    private String name;\n\n    @Schema(description = \"备注\", example = \"颜色\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValuePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 商品属性值分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductPropertyValuePageReqVO extends PageParam {\n\n    @Schema(description = \"属性项的编号\", example = \"1024\")\n    private String propertyId;\n\n    @Schema(description = \"名称\", example = \"红色\")\n    private String name;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 商品属性值 Response VO\")\n@Data\npublic class ProductPropertyValueRespVO {\n\n    @Schema(description = \"主键\", example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"属性项的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"属性项的编号不能为空\")\n    private Long propertyId;\n\n    @Schema(description = \"名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"红色\")\n    @NotEmpty(message = \"名称名字不能为空\")\n    private String name;\n\n    @Schema(description = \"备注\", example = \"颜色\")\n    private String remark;\n\n    @Schema(description = \"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 商品属性值新增/更新 Request VO\")\n@Data\npublic class ProductPropertyValueSaveReqVO {\n\n    @Schema(description = \"主键\", example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"属性项的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"属性项的编号不能为空\")\n    private Long propertyId;\n\n    @Schema(description = \"名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"红色\")\n    @NotEmpty(message = \"名称名字不能为空\")\n    private String name;\n\n    @Schema(description = \"备注\", example = \"颜色\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http",
    "content": "### 获得商品 SPU 明细\nGET {{baseUrl}}/product/spu/get-detail?id=4\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.spu;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;\nimport cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;\nimport cn.iocoder.yudao.module.product.service.sku.ProductSkuService;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;\n\n@Tag(name = \"管理后台 - 商品 SPU\")\n@RestController\n@RequestMapping(\"/product/spu\")\n@Validated\npublic class ProductSpuController {\n\n    @Resource\n    private ProductSpuService productSpuService;\n    @Resource\n    private ProductSkuService productSkuService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建商品 SPU\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:create')\")\n    public CommonResult<Long> createProductSpu(@Valid @RequestBody ProductSpuSaveReqVO createReqVO) {\n        return success(productSpuService.createSpu(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新商品 SPU\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:update')\")\n    public CommonResult<Boolean> updateSpu(@Valid @RequestBody ProductSpuSaveReqVO updateReqVO) {\n        productSpuService.updateSpu(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新商品 SPU Status\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:update')\")\n    public CommonResult<Boolean> updateStatus(@Valid @RequestBody ProductSpuUpdateStatusReqVO updateReqVO) {\n        productSpuService.updateSpuStatus(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除商品 SPU\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:delete')\")\n    public CommonResult<Boolean> deleteSpu(@RequestParam(\"id\") Long id) {\n        productSpuService.deleteSpu(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得商品 SPU 明细\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:query')\")\n    public CommonResult<ProductSpuRespVO> getSpuDetail(@RequestParam(\"id\") Long id) {\n        // 获得商品 SPU\n        ProductSpuDO spu = productSpuService.getSpu(id);\n        if (spu == null) {\n            return success(null);\n        }\n        // 查询商品 SKU\n        List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());\n        return success(ProductSpuConvert.INSTANCE.convert(spu, skus));\n    }\n\n    @GetMapping(\"/list-all-simple\")\n    @Operation(summary = \"获得商品 SPU 精简列表\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:query')\")\n    public CommonResult<List<ProductSpuSimpleRespVO>> getSpuSimpleList() {\n        List<ProductSpuDO> list = productSpuService.getSpuListByStatus(ProductSpuStatusEnum.ENABLE.getStatus());\n        // 降序排序后，返回给前端\n        list.sort(Comparator.comparing(ProductSpuDO::getSort).reversed());\n        return success(BeanUtils.toBean(list, ProductSpuSimpleRespVO.class));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得商品 SPU 详情列表\")\n    @Parameter(name = \"spuIds\", description = \"spu 编号列表\", required = true, example = \"[1,2,3]\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:query')\")\n    public CommonResult<List<ProductSpuRespVO>> getSpuList(@RequestParam(\"spuIds\") Collection<Long> spuIds) {\n        return success(ProductSpuConvert.INSTANCE.convertForSpuDetailRespListVO(\n                productSpuService.getSpuList(spuIds), productSkuService.getSkuListBySpuId(spuIds)));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得商品 SPU 分页\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:query')\")\n    public CommonResult<PageResult<ProductSpuRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) {\n        PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO);\n        return success(BeanUtils.toBean(pageResult, ProductSpuRespVO.class));\n    }\n\n    @GetMapping(\"/get-count\")\n    @Operation(summary = \"获得商品 SPU 分页 tab count\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:query')\")\n    public CommonResult<Map<Integer, Long>> getSpuCount() {\n        return success(productSpuService.getTabsCount());\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出商品\")\n    @PreAuthorize(\"@ss.hasPermission('product:spu:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportSpuList(@Validated ProductSpuPageReqVO reqVO,\n                               HttpServletResponse response) throws IOException {\n        reqVO.setPageSize(PAGE_SIZE_NONE);\n        List<ProductSpuDO> list = productSpuService.getSpuPage(reqVO).getList();\n        // 导出 Excel\n        ExcelUtils.write(response, \"商品列表.xls\", \"数据\", ProductSpuRespVO.class,\n                BeanUtils.toBean(list, ProductSpuRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSkuRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.spu.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 商品 SKU Response VO\")\n@Data\npublic class ProductSkuRespVO {\n\n    @Schema(description = \"主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"商品 SKU 名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖\")\n    private String name;\n\n    @Schema(description = \"销售价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1999\")\n    private Integer price;\n\n    @Schema(description = \"市场价\", example = \"2999\")\n    private Integer marketPrice;\n\n    @Schema(description = \"成本价\", example = \"19\")\n    private Integer costPrice;\n\n    @Schema(description = \"条形码\", example = \"15156165456\")\n    private String barCode;\n\n    @Schema(description = \"图片地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n\n    @Schema(description = \"库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"200\")\n    private Integer stock;\n\n    @Schema(description = \"商品重量,单位：kg 千克\", example = \"1.2\")\n    private Double weight;\n\n    @Schema(description = \"商品体积,单位：m^3 平米\", example = \"2.5\")\n    private Double volume;\n\n    @Schema(description = \"一级分销的佣金，单位：分\", example = \"199\")\n    private Integer firstBrokeragePrice;\n\n    @Schema(description = \"二级分销的佣金，单位：分\", example = \"19\")\n    private Integer secondBrokeragePrice;\n\n    @Schema(description = \"属性数组\")\n    private List<ProductSkuSaveReqVO.Property> properties;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSkuSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.spu.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 商品 SKU 创建/更新 Request VO\")\n@Data\npublic class ProductSkuSaveReqVO {\n\n    @Schema(description = \"商品 SKU 名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖\")\n    @NotEmpty(message = \"商品 SKU 名字不能为空\")\n    private String name;\n\n    @Schema(description = \"销售价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1999\")\n    @NotNull(message = \"销售价格，单位：分不能为空\")\n    private Integer price;\n\n    @Schema(description = \"市场价\", example = \"2999\")\n    private Integer marketPrice;\n\n    @Schema(description = \"成本价\", example = \"19\")\n    private Integer costPrice;\n\n    @Schema(description = \"条形码\", example = \"15156165456\")\n    private String barCode;\n\n    @Schema(description = \"图片地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xx.png\")\n    @NotNull(message = \"图片地址不能为空\")\n    private String picUrl;\n\n    @Schema(description = \"库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"200\")\n    @NotNull(message = \"库存不能为空\")\n    private Integer stock;\n\n    @Schema(description = \"商品重量,单位：kg 千克\", example = \"1.2\")\n    private Double weight;\n\n    @Schema(description = \"商品体积,单位：m^3 平米\", example = \"2.5\")\n    private Double volume;\n\n    @Schema(description = \"一级分销的佣金，单位：分\", example = \"199\")\n    private Integer firstBrokeragePrice;\n\n    @Schema(description = \"二级分销的佣金，单位：分\", example = \"19\")\n    private Integer secondBrokeragePrice;\n\n    @Schema(description = \"属性数组\")\n    private List<Property> properties;\n\n    @Schema(description = \"商品属性\")\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Property {\n\n        @Schema(description = \"属性编号\", example = \"10\")\n        private Long propertyId;\n\n        @Schema(description = \"属性名字\", example = \"颜色\")\n        private String propertyName;\n\n        @Schema(description = \"属性值编号\", example = \"10\")\n        private Long valueId;\n\n        @Schema(description = \"属性值名字\", example = \"红色\")\n        private String valueName;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.spu.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 商品 SPU 分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ProductSpuPageReqVO extends PageParam {\n\n    /**\n     * 出售中商品\n     */\n    public static final Integer FOR_SALE = 0;\n\n    /**\n     * 仓库中商品\n     */\n    public static final Integer IN_WAREHOUSE = 1;\n\n    /**\n     * 已售空商品\n     */\n    public static final Integer SOLD_OUT = 2;\n\n    /**\n     * 警戒库存\n     */\n    public static final Integer ALERT_STOCK = 3;\n\n    /**\n     * 商品回收站\n     */\n    public static final Integer RECYCLE_BIN = 4;\n\n    @Schema(description = \"商品名称\", example = \"清凉小短袖\")\n    private String name;\n\n    @Schema(description = \"前端请求的tab类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer tabType;\n\n    @Schema(description = \"商品分类编号\", example = \"1\")\n    private Long categoryId;\n\n    @Schema(description = \"创建时间\", example = \"[2022-07-01 00:00:00, 2022-07-01 23:59:59]\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.spu.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert;\nimport cn.iocoder.yudao.module.product.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 商品 SPU Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ProductSpuRespVO {\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"111\")\n    @ExcelProperty(\"商品编号\")\n    private Long id;\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖\")\n    @ExcelProperty(\"商品名称\")\n    private String name;\n\n    @Schema(description = \"关键字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉丝滑不出汗\")\n    @ExcelProperty(\"关键字\")\n    private String keyword;\n\n    @Schema(description = \"商品简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖简介\")\n    @ExcelProperty(\"商品简介\")\n    private String introduction;\n\n    @Schema(description = \"商品详情\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖详情\")\n    @ExcelProperty(\"商品详情\")\n    private String description;\n\n    @Schema(description = \"商品分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(\"商品分类编号\")\n    private Long categoryId;\n\n    @Schema(description = \"商品品牌编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(\"商品品牌编号\")\n    private Long brandId;\n\n    @Schema(description = \"商品封面图\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xx.png\")\n    @ExcelProperty(\"商品封面图\")\n    private String picUrl;\n\n    @Schema(description = \"商品轮播图\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]\")\n    private List<String> sliderPicUrls;\n\n    @Schema(description = \"排序字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(\"排序字段\")\n    private Integer sort;\n\n    @Schema(description = \"商品状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @ExcelProperty(value = \"商品状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.PRODUCT_SPU_STATUS)\n    private Integer status;\n\n    @Schema(description = \"商品创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2023-05-24 00:00:00\")\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    // ========== SKU 相关字段 =========\n\n    @Schema(description = \"规格类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(\"规格类型\")\n    private Boolean specType;\n\n    @Schema(description = \"商品价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1999\")\n    @ExcelProperty(value = \"商品价格\", converter = MoneyConvert.class)\n    private Integer price;\n\n    @Schema(description = \"市场价，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"199\")\n    @ExcelProperty(value = \"市场价\", converter = MoneyConvert.class)\n    private Integer marketPrice;\n\n    @Schema(description = \"成本价，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19\")\n    @ExcelProperty(value = \"成本价\", converter = MoneyConvert.class)\n    private Integer costPrice;\n\n    @Schema(description = \"商品库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n    @ExcelProperty(\"库存\")\n    private Integer stock;\n\n    @Schema(description = \"SKU 数组\")\n    private List<ProductSkuRespVO> skus;\n\n    // ========== 物流相关字段 =========\n\n    @Schema(description = \"配送方式数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private List<Integer> deliveryTypes;\n\n    @Schema(description = \"物流配置模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"111\")\n    @ExcelProperty(\"物流配置模板编号\")\n    private Long deliveryTemplateId;\n\n    // ========== 营销相关字段 =========\n\n    @Schema(description = \"赠送积分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"111\")\n    @ExcelProperty(\"赠送积分\")\n    private Integer giveIntegral;\n\n    @Schema(description = \"分销类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @ExcelProperty(\"分销类型\")\n    private Boolean subCommissionType;\n\n    // ========== 统计相关字段 =========\n\n    @Schema(description = \"商品销量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2000\")\n    @ExcelProperty(\"商品销量\")\n    private Integer salesCount;\n\n    @Schema(description = \"虚拟销量\", example = \"66\")\n    @ExcelProperty(\"虚拟销量\")\n    private Integer virtualSalesCount;\n\n    @Schema(description = \"浏览量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n    @ExcelProperty(\"商品点击量\")\n    private Integer browseCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.spu.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 商品 SPU 新增/更新 Request VO\")\n@Data\npublic class ProductSpuSaveReqVO {\n\n    @Schema(description = \"商品编号\", example = \"1\")\n    private Long id;\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖\")\n    @NotEmpty(message = \"商品名称不能为空\")\n    private String name;\n\n    @Schema(description = \"关键字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉丝滑不出汗\")\n    @NotEmpty(message = \"商品关键字不能为空\")\n    private String keyword;\n\n    @Schema(description = \"商品简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖简介\")\n    @NotEmpty(message = \"商品简介不能为空\")\n    private String introduction;\n\n    @Schema(description = \"商品详情\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖详情\")\n    @NotEmpty(message = \"商品详情不能为空\")\n    private String description;\n\n    @Schema(description = \"商品分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"商品分类不能为空\")\n    private Long categoryId;\n\n    @Schema(description = \"商品品牌编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"商品品牌不能为空\")\n    private Long brandId;\n\n    @Schema(description = \"商品封面图\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xx.png\")\n    @NotEmpty(message = \"商品封面图不能为空\")\n    private String picUrl;\n\n    @Schema(description = \"商品轮播图\", requiredMode = Schema.RequiredMode.REQUIRED,\n            example = \"[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]\")\n    private List<String> sliderPicUrls;\n\n    @Schema(description = \"排序字段\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"商品排序字段不能为空\")\n    private Integer sort;\n\n    // ========== SKU 相关字段 =========\n\n    @Schema(description = \"规格类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"商品规格类型不能为空\")\n    private Boolean specType;\n\n    // ========== 物流相关字段 =========\n\n    @Schema(description = \"配送方式数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotEmpty(message = \"配送方式不能为空\")\n    private List<Integer> deliveryTypes;\n\n    @Schema(description = \"物流配置模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"111\")\n    private Long deliveryTemplateId;\n\n    // ========== 营销相关字段 =========\n\n    @Schema(description = \"赠送积分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"111\")\n    @NotNull(message = \"商品赠送积分不能为空\")\n    private Integer giveIntegral;\n\n    @Schema(description = \"分销类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"商品分销类型不能为空\")\n    private Boolean subCommissionType;\n\n    // ========== 统计相关字段 =========\n\n    @Schema(description = \"虚拟销量\", example = \"66\")\n    private Integer virtualSalesCount;\n\n    @Schema(description = \"商品销量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1999\")\n    private Integer salesCount;\n\n    @Schema(description = \"浏览量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1999\")\n    private Integer browseCount;\n\n    // ========== SKU 相关字段 =========\n\n    @Schema(description = \"SKU 数组\")\n    @Valid\n    private List<ProductSkuSaveReqVO> skus;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.spu.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 商品 SPU 精简 Response VO\")\n@Data\n@ToString(callSuper = true)\npublic class ProductSpuSimpleRespVO {\n\n    @Schema(description = \"主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"213\")\n    private Long id;\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖\")\n    private String name;\n\n    @Schema(description = \"商品价格，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1999\")\n    private Integer price;\n\n    @Schema(description = \"商品市场价，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"199\")\n    private Integer marketPrice;\n\n    @Schema(description = \"商品成本价，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19\")\n    private Integer costPrice;\n\n    @Schema(description = \"商品库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2000\")\n    private Integer stock;\n\n    // ========== 统计相关字段 =========\n\n    @Schema(description = \"商品销量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"200\")\n    private Integer salesCount;\n\n    @Schema(description = \"商品虚拟销量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20000\")\n    private Integer virtualSalesCount;\n\n    @Schema(description = \"商品浏览量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2000\")\n    private Integer browseCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateStatusReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.admin.spu.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 商品 SPU Status 更新 Request VO\")\n@Data\npublic class ProductSpuUpdateStatusReqVO{\n\n    @Schema(description = \"商品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"商品编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"商品状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"商品状态不能为空\")\n    @InEnum(ProductSpuStatusEnum.class)\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/AppCategoryController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.category;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryRespVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;\nimport cn.iocoder.yudao.module.product.service.category.ProductCategoryService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 APP - 商品分类\")\n@RestController\n@RequestMapping(\"/product/category\")\n@Validated\npublic class AppCategoryController {\n\n    @Resource\n    private ProductCategoryService categoryService;\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得商品分类列表\")\n    @PermitAll\n    public CommonResult<List<AppCategoryRespVO>> getProductCategoryList() {\n        List<ProductCategoryDO> list = categoryService.getEnableCategoryList();\n        list.sort(Comparator.comparing(ProductCategoryDO::getSort));\n        return success(BeanUtils.toBean(list, AppCategoryRespVO.class));\n    }\n\n    @GetMapping(\"/list-by-ids\")\n    @Operation(summary = \"获得商品分类列表，指定编号\")\n    @Parameter(name = \"ids\", description = \"商品分类编号数组\", required = true)\n    @PermitAll\n    public CommonResult<List<AppCategoryRespVO>> getProductCategoryList(@RequestParam(\"ids\") List<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return success(Collections.emptyList());\n        }\n        List<ProductCategoryDO> list = categoryService.getEnableCategoryList(ids);\n        list.sort(Comparator.comparing(ProductCategoryDO::getSort));\n        return success(BeanUtils.toBean(list, AppCategoryRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/vo/AppCategoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.category.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n@Data\n@Schema(description = \"用户 APP - 商品分类 Response VO\")\npublic class AppCategoryRespVO {\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Long id;\n\n    @Schema(description = \"父分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"父分类编号不能为空\")\n    private Long parentId;\n\n    @Schema(description = \"分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"办公文具\")\n    @NotBlank(message = \"分类名称不能为空\")\n    private String name;\n\n    @Schema(description = \"分类图片\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotBlank(message = \"分类图片不能为空\")\n    private String picUrl;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/AppCommentController.http",
    "content": ""
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/AppProductCommentController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.comment;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;\nimport cn.iocoder.yudao.module.product.service.comment.ProductCommentService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 APP - 商品评价\")\n@RestController\n@RequestMapping(\"/product/comment\")\n@Validated\npublic class AppProductCommentController {\n\n    @Resource\n    private ProductCommentService productCommentService;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得商品评价分页\")\n    @PermitAll\n    public CommonResult<PageResult<AppProductCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) {\n        // 查询评论分页\n        PageResult<ProductCommentDO> pageResult = productCommentService.getCommentPage(pageVO, Boolean.TRUE);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接返回\n        pageResult.getList().forEach(item -> {\n            if (Boolean.TRUE.equals(item.getAnonymous())) {\n                item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS);\n            }\n        });\n        return success(BeanUtils.toBean(pageResult, AppProductCommentRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/vo/AppCommentPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.comment.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 商品评价分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AppCommentPageReqVO extends PageParam {\n\n    /**\n     * 好评\n     */\n    public static final Integer GOOD_COMMENT = 1;\n    /**\n     * 中评\n     */\n    public static final Integer MEDIOCRE_COMMENT = 2;\n    /**\n     * 差评\n     */\n    public static final Integer NEGATIVE_COMMENT = 3;\n\n    @Schema(description = \"商品 SPU 编号\", example = \"29502\")\n    @NotNull(message = \"商品 SPU 编号不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"app 评论页 tab 类型 (0 全部、1 好评、2 中评、3 差评)\", example = \"0\")\n    @NotNull(message = \"商品 SPU 编号不能为空\")\n    private Integer type;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/vo/AppProductCommentRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.comment.vo;\n\nimport cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 商品评价详情 Response VO\")\n@Data\n@ToString(callSuper = true)\npublic class AppProductCommentRespVO {\n\n    @Schema(description = \"评价人的用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15721\")\n    private Long userId;\n\n    @Schema(description = \"评价人名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    private String userNickname;\n\n    @Schema(description = \"评价人头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xx.png\")\n    private String userAvatar;\n\n    @Schema(description = \"订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24965\")\n    private Long id;\n\n    @Schema(description = \"是否匿名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    private Boolean anonymous;\n\n    @Schema(description = \"交易订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24428\")\n    private Long orderId;\n\n    @Schema(description = \"交易订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8233\")\n    private Long orderItemId;\n\n    @Schema(description = \"商家是否回复\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean replyStatus;\n\n    @Schema(description = \"回复管理员编号\", example = \"22212\")\n    private Long replyUserId;\n\n    @Schema(description = \"商家回复内容\", example = \"亲，你的好评就是我的动力(*^▽^*)\")\n    private String replyContent;\n\n    @Schema(description = \"商家回复时间\")\n    private LocalDateTime replyTime;\n\n    @Schema(description = \"追加评价内容\", example = \"穿了很久都很丝滑诶\")\n    private String additionalContent;\n\n    @Schema(description = \"追评评价图片地址数组，以逗号分隔最多上传 9 张\", example = \"[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]\")\n    private List<String> additionalPicUrls;\n\n    @Schema(description = \"追加评价时间\")\n    private LocalDateTime additionalTime;\n\n    @Schema(description = \"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"91192\")\n    @NotNull(message = \"商品 SPU 编号不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SPU 名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉丝滑小短袖\")\n    @NotNull(message = \"商品 SPU 名称不能为空\")\n    private String spuName;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"81192\")\n    @NotNull(message = \"商品 SKU 编号不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"商品 SKU 属性\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<AppProductPropertyValueDetailRespVO> skuProperties;\n\n    @Schema(description = \"评分星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"评分星级 1-5 分不能为空\")\n    private Integer scores;\n\n    @Schema(description = \"描述星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"描述星级 1-5 分不能为空\")\n    private Integer descriptionScores;\n\n    @Schema(description = \"服务星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"服务星级 1-5 分不能为空\")\n    private Integer benefitScores;\n\n    @Schema(description = \"评论内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"哇，真的很丝滑凉快诶，好评\")\n    @NotNull(message = \"评论内容不能为空\")\n    private String content;\n\n    @Schema(description = \"评论图片地址数组，以逗号分隔最多上传 9 张\", requiredMode = Schema.RequiredMode.REQUIRED,\n            example = \"[https://www.iocoder.cn/xx.png]\")\n    @Size(max = 9, message = \"评论图片地址数组长度不能超过 9 张\")\n    private List<String> picUrls;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/AppFavoriteController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.favorite;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO;\nimport cn.iocoder.yudao.module.product.convert.favorite.ProductFavoriteConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.service.favorite.ProductFavoriteService;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 APP - 商品收藏\")\n@RestController\n@RequestMapping(\"/product/favorite\")\npublic class AppFavoriteController {\n\n    @Resource\n    private ProductFavoriteService productFavoriteService;\n    @Resource\n    private ProductSpuService productSpuService;\n\n    @PostMapping(value = \"/create\")\n    @Operation(summary = \"添加商品收藏\")\n    public CommonResult<Long> createFavorite(@RequestBody @Valid AppFavoriteReqVO reqVO) {\n        return success(productFavoriteService.createFavorite(getLoginUserId(), reqVO.getSpuId()));\n    }\n\n    @DeleteMapping(value = \"/delete\")\n    @Operation(summary = \"取消单个商品收藏\")\n    public CommonResult<Boolean> deleteFavorite(@RequestBody @Valid AppFavoriteReqVO reqVO) {\n        productFavoriteService.deleteFavorite(getLoginUserId(), reqVO.getSpuId());\n        return success(Boolean.TRUE);\n    }\n\n    @GetMapping(value = \"/page\")\n    @Operation(summary = \"获得商品收藏分页\")\n    public CommonResult<PageResult<AppFavoriteRespVO>> getFavoritePage(AppFavoritePageReqVO reqVO) {\n        PageResult<ProductFavoriteDO> favoritePage = productFavoriteService.getFavoritePage(getLoginUserId(), reqVO);\n        if (CollUtil.isEmpty(favoritePage.getList())) {\n            return success(PageResult.empty());\n        }\n\n        // 得到商品 spu 信息\n        List<ProductFavoriteDO> favorites = favoritePage.getList();\n        List<Long> spuIds = convertList(favorites, ProductFavoriteDO::getSpuId);\n        List<ProductSpuDO> spus = productSpuService.getSpuList(spuIds);\n\n        // 转换 VO 结果\n        PageResult<AppFavoriteRespVO> pageResult = new PageResult<>(favoritePage.getTotal());\n        pageResult.setList(ProductFavoriteConvert.INSTANCE.convertList(favorites, spus));\n        return success(pageResult);\n    }\n\n    @GetMapping(value = \"/exits\")\n    @Operation(summary = \"检查是否收藏过商品\")\n    public CommonResult<Boolean> isFavoriteExists(AppFavoriteReqVO reqVO) {\n        ProductFavoriteDO favorite = productFavoriteService.getFavorite(getLoginUserId(), reqVO.getSpuId());\n        return success(favorite != null);\n    }\n\n    @GetMapping(value = \"/get-count\")\n    @Operation(summary = \"获得商品收藏数量\")\n    public CommonResult<Long> getFavoriteCount() {\n        return success(productFavoriteService.getFavoriteCount(getLoginUserId()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/vo/AppFavoriteBatchReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.favorite.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\n\n@Schema(description = \"用户 APP - 商品收藏的批量 Request VO\") // 用于收藏、取消收藏、获取收藏\n@Data\npublic class AppFavoriteBatchReqVO {\n\n    @Schema(description = \"商品 SPU 编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29502\")\n    @NotEmpty(message = \"商品 SPU 编号数组不能为空\")\n    private List<Long> spuIds;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/vo/AppFavoritePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.favorite.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 商品收藏分页查询 Request VO\")\n@Data\npublic class AppFavoritePageReqVO extends PageParam {\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/vo/AppFavoriteReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.favorite.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 APP - 商品收藏的单个 Request VO\") // 用于收藏、取消收藏、获取收藏\n@Data\npublic class AppFavoriteReqVO {\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29502\")\n    @NotNull(message = \"商品 SPU 编号不能为空\")\n    private Long spuId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/vo/AppFavoriteRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.favorite.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 商品收藏 Response VO\")\n@Data\npublic class AppFavoriteRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29502\")\n    private Long spuId;\n\n    // ========== 商品相关字段 ==========\n\n    @Schema(description = \"商品 SPU 名称\", example = \"赵六\")\n    private String spuName;\n\n    @Schema(description = \"商品封面图\", example = \"https://domain/pic.png\")\n    private String picUrl;\n\n    @Schema(description = \"商品单价\", example = \"100\")\n    private Integer price;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/AppProductBrowseHistoryController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.history;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryDeleteReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryRespVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 APP - 商品浏览记录\")\n@RestController\n@RequestMapping(\"/product/browse-history\")\npublic class AppProductBrowseHistoryController {\n\n    @Resource\n    private ProductBrowseHistoryService productBrowseHistoryService;\n    @Resource\n    private ProductSpuService productSpuService;\n\n    @DeleteMapping(value = \"/delete\")\n    @Operation(summary = \"删除商品浏览记录\")\n    public CommonResult<Boolean> deleteBrowseHistory(@RequestBody @Valid AppProductBrowseHistoryDeleteReqVO reqVO) {\n        productBrowseHistoryService.hideUserBrowseHistory(getLoginUserId(), reqVO.getSpuIds());\n        return success(Boolean.TRUE);\n    }\n\n    @DeleteMapping(value = \"/clean\")\n    @Operation(summary = \"清空商品浏览记录\")\n    public CommonResult<Boolean> deleteBrowseHistory() {\n        productBrowseHistoryService.hideUserBrowseHistory(getLoginUserId(), null);\n        return success(Boolean.TRUE);\n    }\n\n    @GetMapping(value = \"/page\")\n    @Operation(summary = \"获得商品浏览记录分页\")\n    public CommonResult<PageResult<AppProductBrowseHistoryRespVO>> getBrowseHistoryPage(AppProductBrowseHistoryPageReqVO reqVO) {\n        ProductBrowseHistoryPageReqVO pageReqVO = BeanUtils.toBean(reqVO, ProductBrowseHistoryPageReqVO.class)\n                .setUserId(getLoginUserId())\n                .setUserDeleted(false); // 排除用户已删除的（隐藏的）\n        PageResult<ProductBrowseHistoryDO> pageResult = productBrowseHistoryService.getBrowseHistoryPage(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n\n        // 得到商品 spu 信息\n        Set<Long> spuIds = convertSet(pageResult.getList(), ProductBrowseHistoryDO::getSpuId);\n        Map<Long, ProductSpuDO> spuMap = convertMap(productSpuService.getSpuList(spuIds), ProductSpuDO::getId);\n        return success(BeanUtils.toBean(pageResult, AppProductBrowseHistoryRespVO.class,\n                vo -> Optional.ofNullable(spuMap.get(vo.getSpuId()))\n                        .ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())\n                                .setSalesCount(spu.getSalesCount()).setStock(spu.getStock()))));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryDeleteReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.history.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\n\n@Schema(description = \"用户 APP - 删除商品浏览记录的 Request VO\")\n@Data\npublic class AppProductBrowseHistoryDeleteReqVO {\n\n    @Schema(description = \"商品 SPU 编号数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29502\")\n    @NotEmpty(message = \"商品 SPU 编号数组不能为空\")\n    private List<Long> spuIds;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.history.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"用户 APP - 商品浏览记录分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AppProductBrowseHistoryPageReqVO extends PageParam {\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.history.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 商品浏览记录 Response VO\")\n@Data\npublic class AppProductBrowseHistoryRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29502\")\n    private Long spuId;\n\n    // ========== 商品相关字段 ==========\n\n    @Schema(description = \"商品 SPU 名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    private String spuName;\n\n    @Schema(description = \"商品封面图\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/pic.png\")\n    private String picUrl;\n\n    @Schema(description = \"商品单价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n    private Integer price;\n\n    @Schema(description = \"商品销量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"60\")\n    private Integer salesCount;\n\n    @Schema(description = \"库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"80\")\n    private Integer stock;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/package-info.java",
    "content": "/**\n * 占位符，无时间作用，避免 package 缩进\n */\npackage cn.iocoder.yudao.module.product.controller.app.property;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/vo/property/package-info.java",
    "content": "/**\n * 占位符，无时间作用，避免 package 缩进\n */\npackage cn.iocoder.yudao.module.product.controller.app.property.vo.property;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/vo/value/AppProductPropertyValueDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.property.vo.value;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 商品属性值的明细 Response VO\")\n@Data\npublic class AppProductPropertyValueDetailRespVO {\n\n    @Schema(description = \"属性的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long propertyId;\n\n    @Schema(description = \"属性的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"颜色\")\n    private String propertyName;\n\n    @Schema(description = \"属性值的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long valueId;\n\n    @Schema(description = \"属性值的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"红色\")\n    private String valueName;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http",
    "content": "### 获得订单交易的分页（默认）\nGET {{appApi}}/product/spu/page?pageNo=1&pageSize=10\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n### 获得订单交易的分页（价格）\nGET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=price&sortAsc=true\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n### 获得订单交易的分页（销售）\nGET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=salesCount&sortAsc=true\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n### 获得商品 SPU 明细\nGET {{appApi}}/product/spu/get-detail?id=102\ntenant-id: {{appTenantId}}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.spu;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;\nimport cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuRespVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;\nimport cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;\nimport cn.iocoder.yudao.module.product.service.sku.ProductSkuService;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.validation.Valid;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_ENABLE;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;\n\n@Tag(name = \"用户 APP - 商品 SPU\")\n@RestController\n@RequestMapping(\"/product/spu\")\n@Validated\npublic class AppProductSpuController {\n\n    @Resource\n    private ProductSpuService productSpuService;\n    @Resource\n    private ProductSkuService productSkuService;\n    @Resource\n    private ProductBrowseHistoryService productBrowseHistoryService;\n\n    @GetMapping(\"/list-by-ids\")\n    @Operation(summary = \"获得商品 SPU 列表\")\n    @Parameter(name = \"ids\", description = \"编号列表\", required = true)\n    @PermitAll\n    public CommonResult<List<AppProductSpuRespVO>> getSpuList(@RequestParam(\"ids\") Set<Long> ids) {\n        List<ProductSpuDO> list = productSpuService.getSpuList(ids);\n        if (CollUtil.isEmpty(list)) {\n            return success(Collections.emptyList());\n        }\n\n        // 拼接返回\n        list.forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));\n        List<AppProductSpuRespVO> voList = BeanUtils.toBean(list, AppProductSpuRespVO.class);\n        return success(voList);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得商品 SPU 分页\")\n    @PermitAll\n    public CommonResult<PageResult<AppProductSpuRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {\n        PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接返回\n        pageResult.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));\n        PageResult<AppProductSpuRespVO> voPageResult = BeanUtils.toBean(pageResult, AppProductSpuRespVO.class);\n        return success(voPageResult);\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得商品 SPU 明细\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PermitAll\n    public CommonResult<AppProductSpuDetailRespVO> getSpuDetail(@RequestParam(\"id\") Long id) {\n        // 获得商品 SPU\n        ProductSpuDO spu = productSpuService.getSpu(id);\n        if (spu == null) {\n            throw exception(SPU_NOT_EXISTS);\n        }\n        if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {\n            throw exception(SPU_NOT_ENABLE, spu.getName());\n        }\n        // 获得商品 SKU\n        List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());\n\n        // 增加浏览量\n        productSpuService.updateBrowseCount(id, 1);\n        // 保存浏览记录\n        productBrowseHistoryService.createBrowseHistory(getLoginUserId(), id);\n\n        // 拼接返回\n        spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount());\n        AppProductSpuDetailRespVO spuVO = BeanUtils.toBean(spu, AppProductSpuDetailRespVO.class)\n                .setSkus(BeanUtils.toBean(skus, AppProductSpuDetailRespVO.Sku.class));\n        return success(spuVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.spu.vo;\n\nimport cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 商品 SPU 明细 Response VO\")\n@Data\npublic class AppProductSpuDetailRespVO {\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    // ========== 基本信息 =========\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"商品简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是一个快乐简介\")\n    private String introduction;\n\n    @Schema(description = \"商品详情\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是商品描述\")\n    private String description;\n\n    @Schema(description = \"商品分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long categoryId;\n\n    @Schema(description = \"商品封面图\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String picUrl;\n\n    @Schema(description = \"商品轮播图\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> sliderPicUrls;\n\n    // ========== 营销相关字段 =========\n\n    // ========== SKU 相关字段 =========\n\n    @Schema(description = \"规格类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean specType;\n\n    @Schema(description = \"商品价格，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer price;\n\n    @Schema(description = \"市场价，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer marketPrice;\n\n    @Schema(description = \"库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"666\")\n    private Integer stock;\n\n    /**\n     * SKU 数组\n     */\n    private List<Sku> skus;\n\n    // ========== 统计相关字段 =========\n\n    @Schema(description = \"商品销量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer salesCount;\n\n    @Schema(description = \"用户 App - 商品 SPU 明细的 SKU 信息\")\n    @Data\n    public static class Sku {\n\n        @Schema(description = \"商品 SKU 编号\", example = \"1\")\n        private Long id;\n\n        /**\n         * 商品属性数组\n         */\n        private List<AppProductPropertyValueDetailRespVO> properties;\n\n        @Schema(description = \"销售价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private Integer price;\n\n        @Schema(description = \"市场价，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private Integer marketPrice;\n\n        @Schema(description = \"VIP 价格，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"968\") // 通过会员等级，计算出折扣后价格\n        private Integer vipPrice;\n\n        @Schema(description = \"图片地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xx.png\")\n        private String picUrl;\n\n        @Schema(description = \"库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer stock;\n\n        @Schema(description = \"商品重量\", example = \"1\") // 单位：kg 千克\n        private Double weight;\n\n        @Schema(description = \"商品体积\", example = \"1024\") // 单位：m^3 平米\n        private Double volume;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.spu.vo;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.AssertTrue;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 商品 SPU 分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AppProductSpuPageReqVO extends PageParam {\n\n    public static final String SORT_FIELD_PRICE = \"price\";\n    public static final String SORT_FIELD_SALES_COUNT = \"salesCount\";\n    public static final String SORT_FIELD_CREATE_TIME = \"createTime\";\n\n    @Schema(description = \"商品 SPU 编号数组\", example = \"1,3,5\")\n    private List<Long> ids;\n\n    @Schema(description = \"分类编号\", example = \"1\")\n    private Long categoryId;\n\n    @Schema(description = \"分类编号数组\", example = \"1,2,3\")\n    private List<Long> categoryIds;\n\n    @Schema(description = \"关键字\", example = \"好看\")\n    private String keyword;\n\n    @Schema(description = \"排序字段\", example = \"price\") // 参见 AppProductSpuPageReqVO.SORT_FIELD_XXX 常量\n    private String sortField;\n\n    @Schema(description = \"排序方式\", example = \"true\")\n    private Boolean sortAsc;\n\n    @AssertTrue(message = \"排序字段不合法\")\n    @JsonIgnore\n    public boolean isSortFieldValid() {\n        if (StrUtil.isEmpty(sortField)) {\n            return true;\n        }\n        return StrUtil.equalsAny(sortField, SORT_FIELD_PRICE, SORT_FIELD_SALES_COUNT);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuRespVO.java",
    "content": "package cn.iocoder.yudao.module.product.controller.app.spu.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 商品 SPU Response VO\")\n@Data\npublic class AppProductSpuRespVO {\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"商品简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"清凉小短袖简介\")\n    private String introduction;\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Long categoryId;\n\n    @Schema(description = \"商品封面图\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String picUrl;\n\n    @Schema(description = \"商品轮播图\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> sliderPicUrls;\n\n    // ========== SKU 相关字段 =========\n\n    @Schema(description = \"规格类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean specType;\n\n    @Schema(description = \"商品价格，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer price;\n\n    @Schema(description = \"市场价，单位使用：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer marketPrice;\n\n    @Schema(description = \"库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"666\")\n    private Integer stock;\n\n    // ========== 营销相关字段 =========\n\n    // ========== 统计相关字段 =========\n\n    @Schema(description = \"商品销量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer salesCount;\n\n    // ========== 物流相关字段 =========\n\n    @Schema(description = \"配送方式数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private List<Integer> deliveryTypes;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/convert/brand/ProductBrandConvert.java",
    "content": "package cn.iocoder.yudao.module.product.convert.brand;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandCreateReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandRespVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandSimpleRespVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandUpdateReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 品牌 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ProductBrandConvert {\n\n    ProductBrandConvert INSTANCE = Mappers.getMapper(ProductBrandConvert.class);\n\n    ProductBrandDO convert(ProductBrandCreateReqVO bean);\n\n    ProductBrandDO convert(ProductBrandUpdateReqVO bean);\n\n    ProductBrandRespVO convert(ProductBrandDO bean);\n\n    List<ProductBrandSimpleRespVO> convertList1(List<ProductBrandDO> list);\n\n    List<ProductBrandRespVO> convertList(List<ProductBrandDO> list);\n\n    PageResult<ProductBrandRespVO> convertPage(PageResult<ProductBrandDO> page);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java",
    "content": "package cn.iocoder.yudao.module.product.convert.comment;\n\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\n/**\n * 商品评价 Convert\n *\n * @author wangzhs\n */\n@Mapper\npublic interface ProductCommentConvert {\n\n    ProductCommentConvert INSTANCE = Mappers.getMapper(ProductCommentConvert.class);\n\n    default ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO,\n                                     ProductSpuDO spu, ProductSkuDO sku, MemberUserRespDTO user) {\n        ProductCommentDO comment = BeanUtils.toBean(createReqDTO, ProductCommentDO.class)\n                .setScores(convertScores(createReqDTO.getDescriptionScores(), createReqDTO.getBenefitScores()));\n        if (user != null) {\n            comment.setUserId(user.getId()).setUserNickname(user.getNickname()).setUserAvatar(user.getAvatar());\n        }\n        if (spu != null) {\n            comment.setSpuId(spu.getId()).setSpuName(spu.getName());\n        }\n        if (sku != null) {\n            comment.setSkuPicUrl(sku.getPicUrl()).setSkuProperties(sku.getProperties());\n        }\n        return comment;\n    }\n\n    default ProductCommentDO convert(ProductCommentCreateReqVO createReq, ProductSpuDO spu, ProductSkuDO sku) {\n        ProductCommentDO comment = BeanUtils.toBean(createReq, ProductCommentDO.class)\n                .setVisible(true).setUserId(0L).setAnonymous(false)\n                .setScores(convertScores(createReq.getDescriptionScores(), createReq.getBenefitScores()));\n        if (spu != null) {\n            comment.setSpuId(spu.getId()).setSpuName(spu.getName());\n        }\n        if (sku != null) {\n            comment.setSkuPicUrl(sku.getPicUrl()).setSkuProperties(sku.getProperties());\n        }\n        return comment;\n    }\n\n    default Integer convertScores(Integer descriptionScores, Integer benefitScores) {\n        // 计算评价最终综合评分 最终星数 = （商品评星 + 服务评星） / 2\n        BigDecimal sumScore = new BigDecimal(descriptionScores + benefitScores);\n        BigDecimal divide = sumScore.divide(BigDecimal.valueOf(2L), 0, RoundingMode.DOWN);\n        return divide.intValue();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/convert/favorite/ProductFavoriteConvert.java",
    "content": "package cn.iocoder.yudao.module.product.convert.favorite;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoriteRespVO;\nimport cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n@Mapper\npublic interface ProductFavoriteConvert {\n\n    ProductFavoriteConvert INSTANCE = Mappers.getMapper(ProductFavoriteConvert.class);\n\n    ProductFavoriteDO convert(Long userId, Long spuId);\n\n    @Mapping(target = \"id\", source = \"favorite.id\")\n    @Mapping(target = \"spuName\", source = \"spu.name\")\n    AppFavoriteRespVO convert(ProductSpuDO spu, ProductFavoriteDO favorite);\n\n    default List<AppFavoriteRespVO> convertList(List<ProductFavoriteDO> favorites, List<ProductSpuDO> spus) {\n        List<AppFavoriteRespVO> resultList = new ArrayList<>(favorites.size());\n        Map<Long, ProductSpuDO> spuMap = convertMap(spus, ProductSpuDO::getId);\n        for (ProductFavoriteDO favorite : favorites) {\n            ProductSpuDO spuDO = spuMap.get(favorite.getSpuId());\n            resultList.add(convert(spuDO, favorite));\n        }\n        return resultList;\n    }\n\n    default PageResult<ProductFavoriteRespVO> convertPage(PageResult<ProductFavoriteDO> pageResult, List<ProductSpuDO> spuList) {\n        Map<Long, ProductSpuDO> spuMap = convertMap(spuList, ProductSpuDO::getId);\n        List<ProductFavoriteRespVO> voList = CollectionUtils.convertList(pageResult.getList(), favorite -> {\n            ProductSpuDO spu = spuMap.get(favorite.getSpuId());\n            return convert02(spu, favorite);\n        });\n        return new PageResult<>(voList, pageResult.getTotal());\n    }\n    @Mapping(target = \"id\", source = \"favorite.id\")\n    @Mapping(target = \"userId\", source = \"favorite.userId\")\n    @Mapping(target = \"spuId\", source = \"favorite.spuId\")\n    @Mapping(target = \"createTime\", source = \"favorite.createTime\")\n    ProductFavoriteRespVO convert02(ProductSpuDO spu, ProductFavoriteDO favorite);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java",
    "content": "package cn.iocoder.yudao.module.product.convert.sku;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 商品 SKU Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ProductSkuConvert {\n\n    ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class);\n\n    /**\n     * 获得 SPU 的库存变化 Map\n     *\n     * @param items SKU 库存变化\n     * @param skus SKU 列表\n     * @return SPU 的库存变化 Map\n     */\n    default Map<Long, Integer> convertSpuStockMap(List<ProductSkuUpdateStockReqDTO.Item> items,\n                                                  List<ProductSkuDO> skus) {\n        Map<Long, Long> skuIdAndSpuIdMap = convertMap(skus, ProductSkuDO::getId, ProductSkuDO::getSpuId); // SKU 与 SKU 编号的 Map 关系\n        Map<Long, Integer> spuIdAndStockMap = new HashMap<>(); // SPU 的库存变化 Map 关系\n        items.forEach(item -> {\n            Long spuId = skuIdAndSpuIdMap.get(item.getId());\n            if (spuId == null) {\n                return;\n            }\n            Integer stock = spuIdAndStockMap.getOrDefault(spuId, 0) + item.getIncrCount();\n            spuIdAndStockMap.put(spuId, stock);\n        });\n        return spuIdAndStockMap;\n    }\n\n    default String buildPropertyKey(ProductSkuDO bean) {\n        if (CollUtil.isEmpty(bean.getProperties())) {\n            return StrUtil.EMPTY;\n        }\n        List<ProductSkuDO.Property> properties = new ArrayList<>(bean.getProperties());\n        properties.sort(Comparator.comparing(ProductSkuDO.Property::getValueId));\n        return properties.stream().map(m -> String.valueOf(m.getValueId())).collect(Collectors.joining());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java",
    "content": "package cn.iocoder.yudao.module.product.convert.spu;\n\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuRespVO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO;\nimport cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\n\n/**\n * 商品 SPU Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ProductSpuConvert {\n\n    ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class);\n\n    ProductSpuPageReqVO convert(AppProductSpuPageReqVO bean);\n\n    default ProductSpuRespVO convert(ProductSpuDO spu, List<ProductSkuDO> skus) {\n        ProductSpuRespVO spuVO = BeanUtils.toBean(spu, ProductSpuRespVO.class);\n        spuVO.setSkus(BeanUtils.toBean(skus, ProductSkuRespVO.class));\n        return spuVO;\n    }\n\n    default List<ProductSpuRespVO> convertForSpuDetailRespListVO(List<ProductSpuDO> spus, List<ProductSkuDO> skus) {\n        Map<Long, List<ProductSkuDO>> skuMultiMap = convertMultiMap(skus, ProductSkuDO::getSpuId);\n        return CollectionUtils.convertList(spus, spu -> convert(spu, skuMultiMap.get(spu.getId())));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/brand/ProductBrandDO.java",
    "content": "package cn.iocoder.yudao.module.product.dal.dataobject.brand;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 商品品牌 DO\n *\n * @author 芋道源码\n */\n@TableName(\"product_brand\")\n@KeySequence(\"product_brand_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductBrandDO extends BaseDO {\n\n    /**\n     * 品牌编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 品牌名称\n     */\n    private String name;\n    /**\n     * 品牌图片\n     */\n    private String picUrl;\n    /**\n     * 品牌排序\n     */\n    private Integer sort;\n    /**\n     * 品牌描述\n     */\n    private String description;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java",
    "content": "package cn.iocoder.yudao.module.product.dal.dataobject.category;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 商品分类 DO\n *\n * @author 芋道源码\n */\n@TableName(\"product_category\")\n@KeySequence(\"product_category_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductCategoryDO extends BaseDO {\n\n    /**\n     * 父分类编号 - 根分类\n     */\n    public static final Long PARENT_ID_NULL = 0L;\n    /**\n     * 限定分类层级\n     */\n    public static final int CATEGORY_LEVEL = 2;\n\n    /**\n     * 分类编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 父分类编号\n     */\n    private Long parentId;\n    /**\n     * 分类名称\n     */\n    private String name;\n    /**\n     * 移动端分类图\n     *\n     * 建议 180*180 分辨率\n     */\n    private String picUrl;\n    /**\n     * 分类排序\n     */\n    private Integer sort;\n    /**\n     * 开启状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/comment/ProductCommentDO.java",
    "content": "package cn.iocoder.yudao.module.product.dal.dataobject.comment;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 商品评论 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"product_comment\", autoResultMap = true)\n@KeySequence(\"product_comment_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductCommentDO extends BaseDO {\n\n    /**\n     * 默认匿名昵称\n     */\n    public static final String NICKNAME_ANONYMOUS = \"匿名用户\";\n\n    /**\n     * 评论编号，主键自增\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 评价人的用户编号\n     *\n     * 关联 MemberUserDO 的 id 编号\n     */\n    private Long userId;\n    /**\n     * 评价人名称\n     */\n    private String userNickname;\n    /**\n     * 评价人头像\n     */\n    private String userAvatar;\n    /**\n     * 是否匿名\n     */\n    private Boolean anonymous;\n\n    /**\n     * 交易订单编号\n     *\n     * 关联 TradeOrderDO 的 id 编号\n     */\n    private Long orderId;\n    /**\n     * 交易订单项编号\n     *\n     * 关联 TradeOrderItemDO 的 id 编号\n     */\n    private Long orderItemId;\n\n    /**\n     * 商品 SPU 编号\n     *\n     * 关联 {@link ProductSpuDO#getId()}\n     */\n    private Long spuId;\n    /**\n     * 商品 SPU 名称\n     *\n     * 关联 {@link ProductSpuDO#getName()}\n     */\n    private String spuName;\n    /**\n     * 商品 SKU 编号\n     *\n     * 关联 {@link ProductSkuDO#getId()}\n     */\n    private Long skuId;\n    /**\n     * 商品 SKU 图片地址\n     *\n     * 关联 {@link ProductSkuDO#getPicUrl()}\n     */\n    private String skuPicUrl;\n    /**\n     * 属性数组，JSON 格式\n     *\n     * 关联 {@link ProductSkuDO#getProperties()}\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<ProductSkuDO.Property> skuProperties;\n\n    /**\n     * 是否可见\n     *\n     * true:显示\n     * false:隐藏\n     */\n    private Boolean visible;\n    /**\n     * 评分星级\n     *\n     * 1-5 分\n     */\n    private Integer scores;\n    /**\n     * 描述星级\n     *\n     * 1-5 星\n     */\n    private Integer descriptionScores;\n    /**\n     * 服务星级\n     *\n     * 1-5 星\n     */\n    private Integer benefitScores;\n    /**\n     * 评论内容\n     */\n    private String content;\n    /**\n     * 评论图片地址数组\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<String> picUrls;\n\n    /**\n     * 商家是否回复\n     */\n    private Boolean replyStatus;\n    /**\n     * 回复管理员编号\n     * 关联 AdminUserDO 的 id 编号\n     */\n    private Long replyUserId;\n    /**\n     * 商家回复内容\n     */\n    private String replyContent;\n    /**\n     * 商家回复时间\n     */\n    private LocalDateTime replyTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/favorite/ProductFavoriteDO.java",
    "content": "package cn.iocoder.yudao.module.product.dal.dataobject.favorite;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 商品收藏 DO\n *\n * @author 芋道源码\n */\n@TableName(\"product_favorite\")\n@KeySequence(\"product_favorite_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductFavoriteDO extends BaseDO {\n\n    /**\n     * 编号，主键自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * 用户编号\n     *\n     * 关联 MemberUserDO 的 id 编号\n     */\n    private Long userId;\n    /**\n     * 商品 SPU 编号\n     *\n     * 关联 {@link ProductSpuDO#getId()}\n     */\n    private Long spuId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/history/ProductBrowseHistoryDO.java",
    "content": "package cn.iocoder.yudao.module.product.dal.dataobject.history;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 商品浏览记录 DO\n *\n * @author owen\n */\n@TableName(\"product_browse_history\")\n@KeySequence(\"product_browse_history_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductBrowseHistoryDO extends BaseDO {\n\n    /**\n     * 记录编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 商品 SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n    /**\n     * 用户是否删除\n     */\n    private Boolean userDeleted;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java",
    "content": "package cn.iocoder.yudao.module.product.dal.dataobject.property;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 商品属性项 DO\n *\n * @author 芋道源码\n */\n@TableName(\"product_property\")\n@KeySequence(\"product_property_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductPropertyDO extends BaseDO {\n\n    /**\n     * SPU 单规格时，默认属性 id\n     */\n    public static final Long ID_DEFAULT = 0L;\n    /**\n     * SPU 单规格时，默认属性名字\n     */\n    public static final String NAME_DEFAULT = \"默认\";\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 名称\n     */\n    private String name;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java",
    "content": "package cn.iocoder.yudao.module.product.dal.dataobject.property;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n\n/**\n * 商品属性值 DO\n *\n * @author 芋道源码\n */\n@TableName(\"product_property_value\")\n@KeySequence(\"product_property_value_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductPropertyValueDO extends BaseDO {\n\n    /**\n     * SPU 单规格时，默认属性值 id\n     */\n    public static final Long ID_DEFAULT = 0L;\n    /**\n     * SPU 单规格时，默认属性值名字\n     */\n    public static final String NAME_DEFAULT = \"默认\";\n\n    /**\n     * 主键\n     */\n    @TableId\n    private Long id;\n    /**\n     * 属性项的编号\n     *\n     * 关联 {@link ProductPropertyDO#getId()}\n     */\n    private Long propertyId;\n    /**\n     * 名称\n     */\n    private String name;\n    /**\n     * 备注\n     *\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java",
    "content": "package cn.iocoder.yudao.module.product.dal.dataobject.sku;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.*;\n\nimport java.util.List;\n\n/**\n * 商品 SKU DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"product_sku\", autoResultMap = true)\n@KeySequence(\"product_sku_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductSkuDO extends BaseDO {\n\n    /**\n     * 商品 SKU 编号，自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * SPU 编号\n     *\n     * 关联 {@link ProductSpuDO#getId()}\n     */\n    private Long spuId;\n    /**\n     * 属性数组，JSON 格式\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<Property> properties;\n    /**\n     * 商品价格，单位：分\n     */\n    private Integer price;\n    /**\n     * 市场价，单位：分\n     */\n    private Integer marketPrice;\n    /**\n     * 成本价，单位：分\n     */\n    private Integer costPrice;\n    /**\n     * 商品条码\n     */\n    private String barCode;\n    /**\n     * 图片地址\n     */\n    private String picUrl;\n    /**\n     * 库存\n     */\n    private Integer stock;\n    /**\n     * 商品重量，单位：kg 千克\n     */\n    private Double weight;\n    /**\n     * 商品体积，单位：m^3 平米\n     */\n    private Double volume;\n\n    /**\n     * 一级分销的佣金，单位：分\n     */\n    private Integer firstBrokeragePrice;\n    /**\n     * 二级分销的佣金，单位：分\n     */\n    private Integer secondBrokeragePrice;\n\n    // ========== 营销相关字段 =========\n\n    // ========== 统计相关字段 =========\n    /**\n     * 商品销量\n     */\n    private Integer salesCount;\n\n    /**\n     * 商品属性\n     */\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Property {\n\n        /**\n         * 属性编号\n         * 关联 {@link ProductPropertyDO#getId()}\n         */\n        private Long propertyId;\n        /**\n         * 属性名字\n         * 冗余 {@link ProductPropertyDO#getName()}\n         *\n         * 注意：每次属性名字发生变化时，需要更新该冗余\n         */\n        private String propertyName;\n\n        /**\n         * 属性值编号\n         * 关联 {@link ProductPropertyValueDO#getId()}\n         */\n        private Long valueId;\n        /**\n         * 属性值名字\n         * 冗余 {@link ProductPropertyValueDO#getName()}\n         *\n         * 注意：每次属性值名字发生变化时，需要更新该冗余\n         */\n        private String valueName;\n\n    }\n\n}\n\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java",
    "content": "package cn.iocoder.yudao.module.product.dal.dataobject.spu;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;\nimport cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.*;\n\nimport java.util.List;\n\n/**\n * 商品 SPU DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"product_spu\", autoResultMap = true)\n@KeySequence(\"product_spu_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductSpuDO extends BaseDO {\n\n    /**\n     * 商品 SPU 编号，自增\n     */\n    @TableId\n    private Long id;\n\n    // ========== 基本信息 =========\n\n    /**\n     * 商品名称\n     */\n    private String name;\n    /**\n     * 关键字\n     */\n    private String keyword;\n    /**\n     * 商品简介\n     */\n    private String introduction;\n    /**\n     * 商品详情\n     */\n    private String description;\n\n    /**\n     * 商品分类编号\n     *\n     * 关联 {@link ProductCategoryDO#getId()}\n     */\n    private Long categoryId;\n    /**\n     * 商品品牌编号\n     *\n     * 关联 {@link ProductBrandDO#getId()}\n     */\n    private Long brandId;\n    /**\n     * 商品封面图\n     */\n    private String picUrl;\n    /**\n     * 商品轮播图\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<String> sliderPicUrls;\n\n    /**\n     * 排序字段\n     */\n    private Integer sort;\n    /**\n     * 商品状态\n     *\n     * 枚举 {@link ProductSpuStatusEnum}\n     */\n    private Integer status;\n\n    // ========== SKU 相关字段 =========\n\n    /**\n     * 规格类型\n     *\n     * false - 单规格\n     * true - 多规格\n     */\n    private Boolean specType;\n    /**\n     * 商品价格，单位使用：分\n     *\n     * 基于其对应的 {@link ProductSkuDO#getPrice()} sku单价最低的商品的\n     */\n    private Integer price;\n    /**\n     * 市场价，单位使用：分\n     *\n     * 基于其对应的 {@link ProductSkuDO#getMarketPrice()} sku单价最低的商品的\n     */\n    private Integer marketPrice;\n    /**\n     * 成本价，单位使用：分\n     *\n     * 基于其对应的 {@link ProductSkuDO#getCostPrice()} sku单价最低的商品的\n     */\n    private Integer costPrice;\n    /**\n     * 库存\n     *\n     * 基于其对应的 {@link ProductSkuDO#getStock()} 求和\n     */\n    private Integer stock;\n\n    // ========== 物流相关字段 =========\n\n    /**\n     * 配送方式数组\n     *\n     * 对应 DeliveryTypeEnum 枚举\n     */\n    @TableField(typeHandler = IntegerListTypeHandler.class)\n    private List<Integer> deliveryTypes;\n    /**\n     * 物流配置模板编号\n     *\n     * 对应 TradeDeliveryExpressTemplateDO 的 id 编号\n     */\n    private Long deliveryTemplateId;\n\n    // ========== 营销相关字段 =========\n\n    /**\n     * 赠送积分\n     */\n    private Integer giveIntegral;\n\n    // TODO @puhui999：字段估计要改成 brokerageType\n    /**\n     * 分销类型\n     *\n     * false - 默认\n     * true - 自行设置\n     */\n    private Boolean subCommissionType;\n\n    // ========== 统计相关字段 =========\n\n    /**\n     * 商品销量\n     */\n    private Integer salesCount;\n    /**\n     * 虚拟销量\n     */\n    private Integer virtualSalesCount;\n    /**\n     * 浏览量\n     */\n    private Integer browseCount;\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/ProductBrandMapper.java",
    "content": "package cn.iocoder.yudao.module.product.dal.mysql.brand;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandListReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n@Mapper\npublic interface ProductBrandMapper extends BaseMapperX<ProductBrandDO> {\n\n    default PageResult<ProductBrandDO> selectPage(ProductBrandPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ProductBrandDO>()\n                .likeIfPresent(ProductBrandDO::getName, reqVO.getName())\n                .eqIfPresent(ProductBrandDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(ProductBrandDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(ProductBrandDO::getId));\n    }\n\n\n    default List<ProductBrandDO> selectList(ProductBrandListReqVO reqVO) {\n        return selectList(new LambdaQueryWrapperX<ProductBrandDO>()\n                .likeIfPresent(ProductBrandDO::getName, reqVO.getName()));\n    }\n\n    default ProductBrandDO selectByName(String name) {\n        return selectOne(ProductBrandDO::getName, name);\n    }\n\n    default List<ProductBrandDO> selectListByStatus(Integer status) {\n        return selectList(ProductBrandDO::getStatus, status);\n    }\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/category/ProductCategoryMapper.java",
    "content": "package cn.iocoder.yudao.module.product.dal.mysql.category;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 商品分类 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface ProductCategoryMapper extends BaseMapperX<ProductCategoryDO> {\n\n    default List<ProductCategoryDO> selectList(ProductCategoryListReqVO listReqVO) {\n        return selectList(new LambdaQueryWrapperX<ProductCategoryDO>()\n                .likeIfPresent(ProductCategoryDO::getName, listReqVO.getName())\n                .eqIfPresent(ProductCategoryDO::getParentId, listReqVO.getParentId())\n                .inIfPresent(ProductCategoryDO::getId, listReqVO.getParentIds())\n                .eqIfPresent(ProductCategoryDO::getStatus, listReqVO.getStatus())\n                .orderByDesc(ProductCategoryDO::getId));\n    }\n\n    default Long selectCountByParentId(Long parentId) {\n        return selectCount(ProductCategoryDO::getParentId, parentId);\n    }\n\n    default List<ProductCategoryDO> selectListByStatus(Integer status) {\n        return selectList(ProductCategoryDO::getStatus, status);\n    }\n\n    default List<ProductCategoryDO> selectListByIdAndStatus(Collection<Long> ids, Integer status) {\n        return selectList(new LambdaQueryWrapperX<ProductCategoryDO>()\n                .in(ProductCategoryDO::getId, ids)\n                .eq(ProductCategoryDO::getStatus, status));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java",
    "content": "package cn.iocoder.yudao.module.product.dal.mysql.comment;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ProductCommentMapper extends BaseMapperX<ProductCommentDO> {\n\n    default PageResult<ProductCommentDO> selectPage(ProductCommentPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ProductCommentDO>()\n                .likeIfPresent(ProductCommentDO::getUserNickname, reqVO.getUserNickname())\n                .eqIfPresent(ProductCommentDO::getOrderId, reqVO.getOrderId())\n                .eqIfPresent(ProductCommentDO::getSpuId, reqVO.getSpuId())\n                .eqIfPresent(ProductCommentDO::getScores, reqVO.getScores())\n                .eqIfPresent(ProductCommentDO::getReplyStatus, reqVO.getReplyStatus())\n                .betweenIfPresent(ProductCommentDO::getCreateTime, reqVO.getCreateTime())\n                .likeIfPresent(ProductCommentDO::getSpuName, reqVO.getSpuName())\n                .orderByDesc(ProductCommentDO::getId));\n    }\n\n    static void appendTabQuery(LambdaQueryWrapperX<ProductCommentDO> queryWrapper, Integer type) {\n        // 构建好评查询语句：好评计算 总评 >= 4\n        if (ObjectUtil.equal(type, AppCommentPageReqVO.GOOD_COMMENT)) {\n            queryWrapper.ge(ProductCommentDO::getScores, 4);\n        }\n        // 构建中评查询语句：中评计算 总评 >= 3 且 总评 < 4\n        if (ObjectUtil.equal(type, AppCommentPageReqVO.MEDIOCRE_COMMENT)) {\n            queryWrapper.ge(ProductCommentDO::getScores, 3);\n            queryWrapper.lt(ProductCommentDO::getScores, 4);\n        }\n        // 构建差评查询语句：差评计算 总评 < 3\n        if (ObjectUtil.equal(type, AppCommentPageReqVO.NEGATIVE_COMMENT)) {\n            queryWrapper.lt(ProductCommentDO::getScores, 3);\n        }\n    }\n\n    default PageResult<ProductCommentDO> selectPage(AppCommentPageReqVO reqVO, Boolean visible) {\n        LambdaQueryWrapperX<ProductCommentDO> queryWrapper = new LambdaQueryWrapperX<ProductCommentDO>()\n                .eqIfPresent(ProductCommentDO::getSpuId, reqVO.getSpuId())\n                .eqIfPresent(ProductCommentDO::getVisible, visible);\n        // 构建评价查询语句\n        appendTabQuery(queryWrapper, reqVO.getType());\n        // 按评价时间排序最新的显示在前面\n        queryWrapper.orderByDesc(ProductCommentDO::getCreateTime);\n        return selectPage(reqVO, queryWrapper);\n    }\n\n    default ProductCommentDO selectByUserIdAndOrderItemId(Long userId, Long orderItemId) {\n        return selectOne(new LambdaQueryWrapperX<ProductCommentDO>()\n                .eq(ProductCommentDO::getUserId, userId)\n                .eq(ProductCommentDO::getOrderItemId, orderItemId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/favorite/ProductFavoriteMapper.java",
    "content": "package cn.iocoder.yudao.module.product.dal.mysql.favorite;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoritePageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ProductFavoriteMapper extends BaseMapperX<ProductFavoriteDO> {\n\n    default ProductFavoriteDO selectByUserIdAndSpuId(Long userId, Long spuId) {\n        return selectOne(ProductFavoriteDO::getUserId, userId,\n                ProductFavoriteDO::getSpuId, spuId);\n    }\n\n    default PageResult<ProductFavoriteDO> selectPageByUserAndType(Long userId, AppFavoritePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapper<ProductFavoriteDO>()\n                .eq(ProductFavoriteDO::getUserId, userId)\n                .orderByDesc(ProductFavoriteDO::getId));\n    }\n\n    default PageResult<ProductFavoriteDO> selectPageByUserId(ProductFavoritePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ProductFavoriteDO>()\n                .eqIfPresent(ProductFavoriteDO::getUserId, reqVO.getUserId())\n                .orderByDesc(ProductFavoriteDO::getId));\n    }\n\n    default Long selectCountByUserId(Long userId) {\n        return selectCount(ProductFavoriteDO::getUserId, userId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/history/ProductBrowseHistoryMapper.java",
    "content": "package cn.iocoder.yudao.module.product.dal.mysql.history;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\n\n/**\n * 商品浏览记录 Mapper\n *\n * @author owen\n */\n@Mapper\npublic interface ProductBrowseHistoryMapper extends BaseMapperX<ProductBrowseHistoryDO> {\n\n    default ProductBrowseHistoryDO selectByUserIdAndSpuId(Long userId, Long spuId) {\n        return selectFirstOne(ProductBrowseHistoryDO::getUserId, userId,\n                ProductBrowseHistoryDO::getSpuId, spuId);\n    }\n\n    default PageResult<ProductBrowseHistoryDO> selectPage(ProductBrowseHistoryPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ProductBrowseHistoryDO>()\n                .eqIfPresent(ProductBrowseHistoryDO::getUserId, reqVO.getUserId())\n                .eqIfPresent(ProductBrowseHistoryDO::getUserDeleted, reqVO.getUserDeleted())\n                .eqIfPresent(ProductBrowseHistoryDO::getSpuId, reqVO.getSpuId())\n                .betweenIfPresent(ProductBrowseHistoryDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(ProductBrowseHistoryDO::getId));\n    }\n\n    default void updateUserDeletedByUserId(Long userId, Collection<Long> spuIds, Boolean userDeleted) {\n        update(new LambdaUpdateWrapper<ProductBrowseHistoryDO>()\n                .eq(ProductBrowseHistoryDO::getUserId, userId)\n                .in(CollUtil.isNotEmpty(spuIds), ProductBrowseHistoryDO::getSpuId, spuIds)\n                .set(ProductBrowseHistoryDO::getUserDeleted, userDeleted));\n    }\n\n    default Page<ProductBrowseHistoryDO> selectPageByUserIdOrderByCreateTimeAsc(Long userId, Integer pageNo, Integer pageSize) {\n        Page<ProductBrowseHistoryDO> page = Page.of(pageNo, pageSize);\n        return selectPage(page, new LambdaQueryWrapperX<ProductBrowseHistoryDO>()\n                .eqIfPresent(ProductBrowseHistoryDO::getUserId, userId)\n                .orderByAsc(ProductBrowseHistoryDO::getCreateTime));\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java",
    "content": "package cn.iocoder.yudao.module.product.dal.mysql.property;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ProductPropertyMapper extends BaseMapperX<ProductPropertyDO> {\n\n    default PageResult<ProductPropertyDO> selectPage(ProductPropertyPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ProductPropertyDO>()\n                .likeIfPresent(ProductPropertyDO::getName, reqVO.getName())\n                .betweenIfPresent(ProductPropertyDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(ProductPropertyDO::getId));\n    }\n\n    default ProductPropertyDO selectByName(String name) {\n        return selectOne(ProductPropertyDO::getName, name);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyValueMapper.java",
    "content": "package cn.iocoder.yudao.module.product.dal.mysql.property;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n@Mapper\npublic interface ProductPropertyValueMapper extends BaseMapperX<ProductPropertyValueDO> {\n\n    default List<ProductPropertyValueDO> selectListByPropertyId(Collection<Long> propertyIds) {\n        return selectList(new LambdaQueryWrapperX<ProductPropertyValueDO>()\n                .inIfPresent(ProductPropertyValueDO::getPropertyId, propertyIds));\n    }\n\n    default ProductPropertyValueDO selectByName(Long propertyId, String name) {\n        return selectOne(new LambdaQueryWrapperX<ProductPropertyValueDO>()\n                .eq(ProductPropertyValueDO::getPropertyId, propertyId)\n                .eq(ProductPropertyValueDO::getName, name));\n    }\n\n    default void deleteByPropertyId(Long propertyId) {\n        delete(new LambdaQueryWrapperX<ProductPropertyValueDO>()\n                .eq(ProductPropertyValueDO::getPropertyId, propertyId));\n    }\n\n    default PageResult<ProductPropertyValueDO> selectPage(ProductPropertyValuePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ProductPropertyValueDO>()\n                .eqIfPresent(ProductPropertyValueDO::getPropertyId, reqVO.getPropertyId())\n                .likeIfPresent(ProductPropertyValueDO::getName, reqVO.getName())\n                .orderByDesc(ProductPropertyValueDO::getId));\n    }\n\n    default Integer selectCountByPropertyId(Long propertyId) {\n        return selectCount(ProductPropertyValueDO::getPropertyId, propertyId).intValue();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java",
    "content": "package cn.iocoder.yudao.module.product.dal.mysql.sku;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport org.apache.ibatis.annotations.*;\n\nimport java.util.Collection;\nimport java.util.List;\n\n@Mapper\npublic interface ProductSkuMapper extends BaseMapperX<ProductSkuDO> {\n\n    /**\n     * 查询商品 SKU（包含已删除）\n     * 注意：使用 @Results 手动指定 typeHandler，否则 @Select 不会应用 autoResultMap，properties 字段无法解析 JSON\n     */\n    @Select(\"SELECT * FROM product_sku WHERE id = #{id}\")\n    @Results({\n            @Result(column = \"properties\", property = \"properties\", typeHandler = JacksonTypeHandler.class),\n    })\n    ProductSkuDO selectByIdIncludeDeleted(@Param(\"id\") Long id);\n\n    default List<ProductSkuDO> selectListBySpuId(Long spuId) {\n        return selectList(ProductSkuDO::getSpuId, spuId);\n    }\n\n    default List<ProductSkuDO> selectListBySpuId(Collection<Long> spuIds) {\n        return selectList(ProductSkuDO::getSpuId, spuIds);\n    }\n\n    default void deleteBySpuId(Long spuId) {\n        delete(new LambdaQueryWrapperX<ProductSkuDO>().eq(ProductSkuDO::getSpuId, spuId));\n    }\n\n    /**\n     * 更新 SKU 库存（增加）、销量（减少）\n     *\n     * @param id        编号\n     * @param incrCount 增加库存（正数）\n     */\n    default void updateStockIncr(Long id, Integer incrCount) {\n        Assert.isTrue(incrCount > 0);\n        LambdaUpdateWrapper<ProductSkuDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<ProductSkuDO>()\n                .setSql(\" stock = stock + \" + incrCount\n                    + \", sales_count = sales_count - \" + incrCount)\n                .eq(ProductSkuDO::getId, id);\n        update(null, lambdaUpdateWrapper);\n    }\n\n    /**\n     * 更新 SKU 库存（减少）、销量（增加）\n     *\n     * @param id        编号\n     * @param incrCount 减少库存（负数）\n     * @return 更新条数\n     */\n    default int updateStockDecr(Long id, Integer incrCount) {\n        Assert.isTrue(incrCount < 0);\n        incrCount = - incrCount; // 取正\n        LambdaUpdateWrapper<ProductSkuDO> updateWrapper = new LambdaUpdateWrapper<ProductSkuDO>()\n                .setSql(\" stock = stock - \" + incrCount\n                    + \", sales_count = sales_count + \" + incrCount)\n                .eq(ProductSkuDO::getId, id)\n                .ge(ProductSkuDO::getStock, incrCount);\n        return update(null, updateWrapper);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java",
    "content": "package cn.iocoder.yudao.module.product.dal.mysql.spu;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.enums.ProductConstants;\nimport cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport org.apache.ibatis.annotations.*;\n\nimport java.util.Objects;\nimport java.util.Set;\n\n@Mapper\npublic interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {\n\n    /**\n     * 查询商品 SPU（包含已删除）\n     * 注意：使用 @Results 手动指定 typeHandler，否则 @Select 不会应用 autoResultMap，sliderPicUrls，deliveryTypes 字段无法解析 JSON\n     */\n    @Select(\"SELECT * FROM product_spu WHERE id = #{id}\")\n    @Results({\n            @Result(column = \"slider_pic_urls\", property = \"sliderPicUrls\", typeHandler = JacksonTypeHandler.class),\n            @Result(column = \"delivery_types\", property = \"deliveryTypes\", typeHandler = IntegerListTypeHandler.class),\n    })\n    ProductSpuDO selectByIdIncludeDeleted(@Param(\"id\") Long id);\n\n    /**\n     * 获取商品 SPU 分页列表数据\n     *\n     * @param reqVO 分页请求参数\n     * @return 商品 SPU 分页列表数据\n     */\n    default PageResult<ProductSpuDO> selectPage(ProductSpuPageReqVO reqVO) {\n        Integer tabType = reqVO.getTabType();\n        LambdaQueryWrapperX<ProductSpuDO> queryWrapper = new LambdaQueryWrapperX<ProductSpuDO>()\n                .likeIfPresent(ProductSpuDO::getName, reqVO.getName())\n                .eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId())\n                .betweenIfPresent(ProductSpuDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(ProductSpuDO::getSort)\n                .orderByDesc(ProductSpuDO::getId);\n        appendTabQuery(tabType, queryWrapper);\n        return selectPage(reqVO, queryWrapper);\n    }\n\n    /**\n     * 查询触发警戒库存的 SPU 数量\n     *\n     * @return 触发警戒库存的 SPU 数量\n     */\n    default Long selectCount() {\n        LambdaQueryWrapperX<ProductSpuDO> queryWrapper = new LambdaQueryWrapperX<>();\n        // 库存小于等于警戒库存\n        queryWrapper.le(ProductSpuDO::getStock, ProductConstants.ALERT_STOCK)\n                // 如果库存触发警戒库存且状态为回收站的话则不计入触发警戒库存的个数\n                .notIn(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus());\n        return selectCount(queryWrapper);\n    }\n\n    /**\n     * 获得商品 SPU 分页，提供给用户 App 使用\n     */\n    default PageResult<ProductSpuDO> selectPage(AppProductSpuPageReqVO pageReqVO, Set<Long> categoryIds) {\n        LambdaQueryWrapperX<ProductSpuDO> query = new LambdaQueryWrapperX<ProductSpuDO>()\n                // 关键字匹配，目前只匹配商品名\n                .likeIfPresent(ProductSpuDO::getName, pageReqVO.getKeyword())\n                // 分类\n                .inIfPresent(ProductSpuDO::getCategoryId, categoryIds);\n        // 上架状态 且有库存\n        query.eq(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus());\n\n        // 排序逻辑\n        if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) {\n            query.last(String.format(\" ORDER BY (sales_count + virtual_sales_count) %s, sort DESC, id DESC\",\n                    pageReqVO.getSortAsc() ? \"ASC\" : \"DESC\"));\n        } else if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_PRICE)) {\n            query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getPrice)\n                    .orderByDesc(ProductSpuDO::getSort).orderByDesc(ProductSpuDO::getId);\n        } else if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_CREATE_TIME)) {\n            query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getCreateTime)\n                    .orderByDesc(ProductSpuDO::getSort).orderByDesc(ProductSpuDO::getId);\n        } else {\n            query.orderByDesc(ProductSpuDO::getSort).orderByDesc(ProductSpuDO::getId);\n        }\n        return selectPage(pageReqVO, query);\n    }\n\n    /**\n     * 更新商品 SPU 库存\n     *\n     * @param id        商品 SPU 编号\n     * @param incrCount 增加的库存数量\n     */\n    default void updateStock(Long id, Integer incrCount) {\n        // 拼接 SQL\n        if (incrCount == 0) {\n            return;\n        }\n        String sql;\n        if (incrCount > 0) {\n            sql = \" stock = stock + \" + incrCount + \", sales_count = sales_count - \" + incrCount;\n        } else {\n            sql = \" stock = stock - \" + Math.abs(incrCount) + \", sales_count = sales_count + \" + Math.abs(incrCount);\n        }\n        // 执行更新\n        LambdaUpdateWrapper<ProductSpuDO> updateWrapper = new LambdaUpdateWrapper<ProductSpuDO>()\n                .setSql(sql)\n                .eq(ProductSpuDO::getId, id);\n        update(null, updateWrapper);\n    }\n\n    /**\n     * 添加后台 Tab 选项的查询条件\n     *\n     * @param tabType 标签类型\n     * @param query   查询条件\n     */\n    static void appendTabQuery(Integer tabType, LambdaQueryWrapperX<ProductSpuDO> query) {\n        // 出售中商品\n        if (ObjectUtil.equals(ProductSpuPageReqVO.FOR_SALE, tabType)) {\n            query.eqIfPresent(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus());\n        }\n        // 仓储中商品\n        if (ObjectUtil.equals(ProductSpuPageReqVO.IN_WAREHOUSE, tabType)) {\n            query.eqIfPresent(ProductSpuDO::getStatus, ProductSpuStatusEnum.DISABLE.getStatus());\n        }\n        // 已售空商品\n        if (ObjectUtil.equals(ProductSpuPageReqVO.SOLD_OUT, tabType)) {\n            query.eqIfPresent(ProductSpuDO::getStock, 0);\n        }\n        // 警戒库存\n        if (ObjectUtil.equals(ProductSpuPageReqVO.ALERT_STOCK, tabType)) {\n            query.le(ProductSpuDO::getStock, ProductConstants.ALERT_STOCK)\n                    // 如果库存触发警戒库存且状态为回收站的话则不在警戒库存列表展示\n                    .notIn(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus());\n        }\n        // 回收站\n        if (ObjectUtil.equals(ProductSpuPageReqVO.RECYCLE_BIN, tabType)) {\n            query.eqIfPresent(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus());\n        }\n    }\n\n    /**\n     * 更新商品 SPU 浏览量\n     *\n     * @param id        商品 SPU 编号\n     * @param incrCount 增加的数量\n     */\n    default void updateBrowseCount(Long id, int incrCount) {\n        LambdaUpdateWrapper<ProductSpuDO> updateWrapper = new LambdaUpdateWrapper<ProductSpuDO>()\n                .setSql(\" browse_count = browse_count +\" + incrCount)\n                .eq(ProductSpuDO::getId, id);\n        update(null, updateWrapper);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/framework/package-info.java",
    "content": "/**\n * 属于 product 模块的 framework 封装\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.product.framework;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/framework/rpc/config/RpcConfiguration.java",
    "content": "package cn.iocoder.yudao.module.product.framework.rpc.config;\n\nimport cn.iocoder.yudao.module.member.api.level.MemberLevelApi;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(value = \"productRpcConfiguration\", proxyBeanMethods = false)\n@EnableFeignClients(clients = {MemberUserApi.class, MemberLevelApi.class})\npublic class RpcConfiguration {\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/framework/rpc/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.product.framework.rpc;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/framework/security/config/SecurityConfiguration.java",
    "content": "package cn.iocoder.yudao.module.product.framework.security.config;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.module.product.enums.ApiConstants;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\n/**\n * Product 模块的 Security 配置\n */\n@Configuration(\"productSecurityConfiguration\")\npublic class SecurityConfiguration {\n\n    @Bean(\"productAuthorizeRequestsCustomizer\")\n    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {\n        return new AuthorizeRequestsCustomizer() {\n\n            @Override\n            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n                // Swagger 接口文档\n                registry.requestMatchers(\"/v3/api-docs/**\").permitAll()\n                        .requestMatchers(\"/webjars/**\").permitAll()\n                        .requestMatchers(\"/swagger-ui\").permitAll()\n                        .requestMatchers(\"/swagger-ui/**\").permitAll();\n                // Spring Boot Actuator 的安全配置\n                registry.requestMatchers(\"/actuator\").permitAll()\n                        .requestMatchers(\"/actuator/**\").permitAll();\n                // Druid 监控\n                registry.requestMatchers(\"/druid/**\").permitAll();\n                // RPC 服务的安全配置\n                registry.requestMatchers(ApiConstants.PREFIX + \"/**\").permitAll();\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/framework/security/core/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.product.framework.security.core;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/package-info.java",
    "content": "/**\n * product 模块，主要实现交易相关功能\n * 例如：订单、退款、购物车等功能。\n *\n * 1. Controller URL：以 /product/ 开头，避免和其它 Module 冲突\n * 2. DataObject 表名：以 product_ 开头，方便在数据库中区分\n */\npackage cn.iocoder.yudao.module.product;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandService.java",
    "content": "package cn.iocoder.yudao.module.product.service.brand;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandCreateReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandListReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandUpdateReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 商品品牌 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ProductBrandService {\n\n    /**\n     * 创建品牌\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createBrand(@Valid ProductBrandCreateReqVO createReqVO);\n\n    /**\n     * 更新品牌\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateBrand(@Valid ProductBrandUpdateReqVO updateReqVO);\n\n    /**\n     * 删除品牌\n     *\n     * @param id 编号\n     */\n    void deleteBrand(Long id);\n\n    /**\n     * 获得品牌\n     *\n     * @param id 编号\n     * @return 品牌\n     */\n    ProductBrandDO getBrand(Long id);\n\n    /**\n     * 获得品牌列表\n     *\n     * @param ids 编号\n     * @return 品牌列表\n     */\n    List<ProductBrandDO> getBrandList(Collection<Long> ids);\n\n    /**\n     * 获得品牌列表\n     *\n     * @param listReqVO 请求参数\n     * @return 品牌列表\n     */\n    List<ProductBrandDO> getBrandList(ProductBrandListReqVO listReqVO);\n\n    /**\n     * 验证选择的商品分类是否合法\n     *\n     * @param id 分类编号\n     */\n    void validateProductBrand(Long id);\n\n    /**\n     * 获得品牌分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 品牌分页\n     */\n    PageResult<ProductBrandDO> getBrandPage(ProductBrandPageReqVO pageReqVO);\n\n    /**\n     * 获取指定状态的品牌列表\n     *\n     * @param status 状态\n     * @return  返回品牌列表\n     */\n    List<ProductBrandDO> getBrandListByStatus(Integer status);\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.product.service.brand;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandCreateReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandListReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandUpdateReqVO;\nimport cn.iocoder.yudao.module.product.convert.brand.ProductBrandConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;\nimport cn.iocoder.yudao.module.product.dal.mysql.brand.ProductBrandMapper;\nimport com.google.common.annotations.VisibleForTesting;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;\n\n/**\n * 品牌 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ProductBrandServiceImpl implements ProductBrandService {\n\n    @Resource\n    private ProductBrandMapper brandMapper;\n\n    @Override\n    public Long createBrand(ProductBrandCreateReqVO createReqVO) {\n        // 校验\n        validateBrandNameUnique(null, createReqVO.getName());\n\n        // 插入\n        ProductBrandDO brand = ProductBrandConvert.INSTANCE.convert(createReqVO);\n        brandMapper.insert(brand);\n        // 返回\n        return brand.getId();\n    }\n\n    @Override\n    public void updateBrand(ProductBrandUpdateReqVO updateReqVO) {\n        // 校验存在\n        validateBrandExists(updateReqVO.getId());\n        validateBrandNameUnique(updateReqVO.getId(), updateReqVO.getName());\n        // 更新\n        ProductBrandDO updateObj = ProductBrandConvert.INSTANCE.convert(updateReqVO);\n        brandMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteBrand(Long id) {\n        // 校验存在\n        validateBrandExists(id);\n        // 删除\n        brandMapper.deleteById(id);\n    }\n\n    private void validateBrandExists(Long id) {\n        if (brandMapper.selectById(id) == null) {\n            throw exception(BRAND_NOT_EXISTS);\n        }\n    }\n\n    @VisibleForTesting\n    public void validateBrandNameUnique(Long id, String name) {\n        ProductBrandDO brand = brandMapper.selectByName(name);\n        if (brand == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的字典类型\n        if (id == null) {\n            throw exception(BRAND_NAME_EXISTS);\n        }\n        if (!brand.getId().equals(id)) {\n            throw exception(BRAND_NAME_EXISTS);\n        }\n    }\n\n    @Override\n    public ProductBrandDO getBrand(Long id) {\n        return brandMapper.selectById(id);\n    }\n\n    @Override\n    public List<ProductBrandDO> getBrandList(Collection<Long> ids) {\n        return brandMapper.selectByIds(ids);\n    }\n\n    @Override\n    public List<ProductBrandDO> getBrandList(ProductBrandListReqVO listReqVO) {\n        return brandMapper.selectList(listReqVO);\n    }\n\n    @Override\n    public void validateProductBrand(Long id) {\n        ProductBrandDO brand = brandMapper.selectById(id);\n        if (brand == null) {\n            throw exception(BRAND_NOT_EXISTS);\n        }\n        if (brand.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) {\n            throw exception(BRAND_DISABLED);\n        }\n    }\n\n    @Override\n    public PageResult<ProductBrandDO> getBrandPage(ProductBrandPageReqVO pageReqVO) {\n        return brandMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<ProductBrandDO> getBrandListByStatus(Integer status) {\n        return brandMapper.selectListByStatus(status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryService.java",
    "content": "package cn.iocoder.yudao.module.product.service.category;\n\nimport cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategorySaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 商品分类 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ProductCategoryService {\n\n    /**\n     * 创建商品分类\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createCategory(@Valid ProductCategorySaveReqVO createReqVO);\n\n    /**\n     * 更新商品分类\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCategory(@Valid ProductCategorySaveReqVO updateReqVO);\n\n    /**\n     * 删除商品分类\n     *\n     * @param id 编号\n     */\n    void deleteCategory(Long id);\n\n    /**\n     * 获得商品分类\n     *\n     * @param id 编号\n     * @return 商品分类\n     */\n    ProductCategoryDO getCategory(Long id);\n\n    /**\n     * 校验商品分类\n     *\n     * @param id 分类编号\n     */\n    void validateCategory(Long id);\n\n    /**\n     * 获得商品分类的层级\n     *\n     * @param id 编号\n     * @return 商品分类的层级\n     */\n    Integer getCategoryLevel(Long id);\n\n    /**\n     * 获得商品分类列表\n     *\n     * @param listReqVO 查询条件\n     * @return 商品分类列表\n     */\n    List<ProductCategoryDO> getCategoryList(ProductCategoryListReqVO listReqVO);\n\n    /**\n     * 获得开启状态的商品分类列表\n     *\n     * @return 商品分类列表\n     */\n    List<ProductCategoryDO> getEnableCategoryList();\n\n    /**\n     * 获得开启状态的商品分类列表，指定编号\n     *\n     * @param ids 商品分类编号数组\n     * @return 商品分类列表\n     */\n    List<ProductCategoryDO> getEnableCategoryList(List<Long> ids);\n\n    /**\n     * 校验商品分类是否有效。如下情况，视为无效：\n     * 1. 商品分类编号不存在\n     * 2. 商品分类被禁用\n     * 3. 商品分类层级校验，必须使用第二级的商品分类及以下\n     *\n     * @param ids 商品分类编号数组\n     */\n    void validateCategoryList(Collection<Long> ids);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.product.service.category;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategorySaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;\nimport cn.iocoder.yudao.module.product.dal.mysql.category.ProductCategoryMapper;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO.CATEGORY_LEVEL;\nimport static cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO.PARENT_ID_NULL;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;\n\n/**\n * 商品分类 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ProductCategoryServiceImpl implements ProductCategoryService {\n\n    @Resource\n    private ProductCategoryMapper productCategoryMapper;\n    @Resource\n    @Lazy // 循环依赖，避免报错\n    private ProductSpuService productSpuService;\n\n    @Override\n    public Long createCategory(ProductCategorySaveReqVO createReqVO) {\n        // 校验父分类存在\n        validateParentProductCategory(createReqVO.getParentId());\n\n        // 插入\n        ProductCategoryDO category = BeanUtils.toBean(createReqVO, ProductCategoryDO.class);\n        productCategoryMapper.insert(category);\n        // 返回\n        return category.getId();\n    }\n\n    @Override\n    public void updateCategory(ProductCategorySaveReqVO updateReqVO) {\n        // 校验分类是否存在\n        validateProductCategoryExists(updateReqVO.getId());\n        // 校验父分类存在\n        validateParentProductCategory(updateReqVO.getParentId());\n\n        // 更新\n        ProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, ProductCategoryDO.class);\n        productCategoryMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteCategory(Long id) {\n        // 校验分类是否存在\n        validateProductCategoryExists(id);\n        // 校验是否还有子分类\n        if (productCategoryMapper.selectCountByParentId(id) > 0) {\n            throw exception(CATEGORY_EXISTS_CHILDREN);\n        }\n        // 校验分类是否绑定了 SPU\n        Long spuCount = productSpuService.getSpuCountByCategoryId(id);\n        if (spuCount > 0) {\n            throw exception(CATEGORY_HAVE_BIND_SPU);\n        }\n        // 删除\n        productCategoryMapper.deleteById(id);\n    }\n\n    private void validateParentProductCategory(Long id) {\n        // 如果是根分类，无需验证\n        if (Objects.equals(id, PARENT_ID_NULL)) {\n            return;\n        }\n        // 父分类不存在\n        ProductCategoryDO category = productCategoryMapper.selectById(id);\n        if (category == null) {\n            throw exception(CATEGORY_PARENT_NOT_EXISTS);\n        }\n        // 父分类不能是二级分类\n        if (!Objects.equals(category.getParentId(), PARENT_ID_NULL)) {\n            throw exception(CATEGORY_PARENT_NOT_FIRST_LEVEL);\n        }\n    }\n\n    private void validateProductCategoryExists(Long id) {\n        ProductCategoryDO category = productCategoryMapper.selectById(id);\n        if (category == null) {\n            throw exception(CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public void validateCategoryList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return;\n        }\n        // 获得商品分类信息\n        List<ProductCategoryDO> list = productCategoryMapper.selectByIds(ids);\n        Map<Long, ProductCategoryDO> categoryMap = CollectionUtils.convertMap(list, ProductCategoryDO::getId);\n        // 校验\n        ids.forEach(id -> {\n            // 校验分类是否存在\n            ProductCategoryDO category = categoryMap.get(id);\n            if (category == null) {\n                throw exception(CATEGORY_NOT_EXISTS);\n            }\n            // 校验分类是否启用\n            if (!CommonStatusEnum.ENABLE.getStatus().equals(category.getStatus())) {\n                throw exception(CATEGORY_DISABLED, category.getName());\n            }\n            // 商品分类层级校验，必须使用第二级的商品分类\n            if (getCategoryLevel(id) < CATEGORY_LEVEL) {\n                throw exception(SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR);\n            }\n        });\n    }\n\n    @Override\n    public ProductCategoryDO getCategory(Long id) {\n        return productCategoryMapper.selectById(id);\n    }\n\n    @Override\n    public void validateCategory(Long id) {\n        ProductCategoryDO category = productCategoryMapper.selectById(id);\n        if (category == null) {\n            throw exception(CATEGORY_NOT_EXISTS);\n        }\n        if (Objects.equals(category.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {\n            throw exception(CATEGORY_DISABLED, category.getName());\n        }\n    }\n\n    @Override\n    public Integer getCategoryLevel(Long id) {\n        if (Objects.equals(id, PARENT_ID_NULL)) {\n            return 0;\n        }\n        int level = 1;\n        // for 的原因，是因为避免脏数据，导致可能的死循环。一般不会超过 100 层哈\n        for (int i = 0; i < Byte.MAX_VALUE; i++) {\n            // 如果没有父节点，break 结束\n            ProductCategoryDO category = productCategoryMapper.selectById(id);\n            if (category == null\n                    || Objects.equals(category.getParentId(), PARENT_ID_NULL)) {\n                break;\n            }\n            // 继续递归父节点\n            level++;\n            id = category.getParentId();\n        }\n        return level;\n    }\n\n    @Override\n    public List<ProductCategoryDO> getCategoryList(ProductCategoryListReqVO listReqVO) {\n        return productCategoryMapper.selectList(listReqVO);\n    }\n\n    @Override\n    public List<ProductCategoryDO> getEnableCategoryList() {\n        return productCategoryMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());\n    }\n\n    @Override\n    public List<ProductCategoryDO> getEnableCategoryList(List<Long> ids) {\n        return productCategoryMapper.selectListByIdAndStatus(ids, CommonStatusEnum.ENABLE.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java",
    "content": "package cn.iocoder.yudao.module.product.service.comment;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\n/**\n * 商品评论 Service 接口\n *\n * @author wangzhs\n */\n@Service\n@Validated\npublic interface ProductCommentService {\n\n    /**\n     * 创建商品评论\n     * 后台管理员创建评论使用\n     *\n     * @param createReqVO 商品评价创建 Request VO 对象\n     */\n    void createComment(ProductCommentCreateReqVO createReqVO);\n\n    /**\n     * 创建评论\n     * 创建商品评论 APP 端创建商品评论使用\n     *\n     * @param createReqDTO 创建请求 dto\n     * @return 返回评论 id\n     */\n    Long createComment(ProductCommentCreateReqDTO createReqDTO);\n\n    /**\n     * 修改评论是否可见\n     *\n     * @param updateReqVO 修改评论可见\n     */\n    void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO);\n\n    /**\n     * 商家回复\n     *\n     * @param replyVO     商家回复\n     * @param userId 管理后台商家登陆人 ID\n     */\n    void replyComment(ProductCommentReplyReqVO replyVO, Long userId);\n\n    /**\n     * 【管理员】获得商品评价分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 商品评价分页\n     */\n    PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO);\n\n    /**\n     * 【会员】获得商品评价分页\n     *\n     * @param pageVO  分页查询\n     * @param visible 是否可见\n     * @return 商品评价分页\n     */\n    PageResult<ProductCommentDO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.product.service.comment;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;\nimport cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;\nimport cn.iocoder.yudao.module.product.service.sku.ProductSkuService;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;\n\n/**\n * 商品评论 Service 实现类\n *\n * @author wangzhs\n */\n@Service\n@Validated\npublic class ProductCommentServiceImpl implements ProductCommentService {\n\n    @Resource\n    private ProductCommentMapper productCommentMapper;\n\n    @Resource\n    private ProductSpuService productSpuService;\n\n    @Resource\n    @Lazy\n    private ProductSkuService productSkuService;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @Override\n    public void createComment(ProductCommentCreateReqVO createReqVO) {\n        // 校验 SKU\n        ProductSkuDO sku = validateSku(createReqVO.getSkuId());\n        // 校验 SPU\n        ProductSpuDO spu = validateSpu(sku.getSpuId());\n\n        // 创建评论\n        ProductCommentDO comment = ProductCommentConvert.INSTANCE.convert(createReqVO, spu, sku);\n        productCommentMapper.insert(comment);\n    }\n\n    @Override\n    public Long createComment(ProductCommentCreateReqDTO createReqDTO) {\n        // 校验 SKU\n        ProductSkuDO sku = validateSku(createReqDTO.getSkuId());\n        // 校验 SPU\n        ProductSpuDO spu = validateSpu(sku.getSpuId());\n        // 校验评论\n        validateCommentExists(createReqDTO.getUserId(), createReqDTO.getOrderItemId());\n        // 获取用户详细信息\n        MemberUserRespDTO user = memberUserApi.getUser(createReqDTO.getUserId()).getCheckedData();\n\n        // 创建评论\n        ProductCommentDO comment = ProductCommentConvert.INSTANCE.convert(createReqDTO, spu, sku, user);\n        productCommentMapper.insert(comment);\n        return comment.getId();\n    }\n\n    /**\n     * 判断当前订单的当前商品用户是否评价过\n     *\n     * @param userId      用户编号\n     * @param orderItemId 订单项编号\n     */\n    private void validateCommentExists(Long userId, Long orderItemId) {\n        ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderItemId(userId, orderItemId);\n        if (exist != null) {\n            throw exception(COMMENT_ORDER_EXISTS);\n        }\n    }\n\n    private ProductSkuDO validateSku(Long skuId) {\n        ProductSkuDO sku = productSkuService.getSku(skuId, true);\n        if (sku == null) {\n            throw exception(SKU_NOT_EXISTS);\n        }\n        return sku;\n    }\n\n    private ProductSpuDO validateSpu(Long spuId) {\n        ProductSpuDO spu = productSpuService.getSpu(spuId, true);\n        if (null == spu) {\n            throw exception(SPU_NOT_EXISTS);\n        }\n        return spu;\n    }\n\n    @Override\n    public void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO) {\n        // 校验评论是否存在\n        validateCommentExists(updateReqVO.getId());\n\n        // 更新可见状态\n        productCommentMapper.updateById(new ProductCommentDO().setId(updateReqVO.getId())\n                .setVisible(updateReqVO.getVisible()));\n    }\n\n    @Override\n    public void replyComment(ProductCommentReplyReqVO replyVO, Long userId) {\n        // 校验评论是否存在\n        validateCommentExists(replyVO.getId());\n        // 回复评论\n        productCommentMapper.updateById(new ProductCommentDO().setId(replyVO.getId())\n                .setReplyTime(LocalDateTime.now()).setReplyUserId(userId)\n                .setReplyStatus(Boolean.TRUE).setReplyContent(replyVO.getReplyContent()));\n    }\n\n    private ProductCommentDO validateCommentExists(Long id) {\n        ProductCommentDO productComment = productCommentMapper.selectById(id);\n        if (productComment == null) {\n            throw exception(COMMENT_NOT_EXISTS);\n        }\n        return productComment;\n    }\n\n    @Override\n    public PageResult<ProductCommentDO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) {\n        return productCommentMapper.selectPage(pageVO, visible);\n    }\n\n    @Override\n    public PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO) {\n        return productCommentMapper.selectPage(pageReqVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteService.java",
    "content": "package cn.iocoder.yudao.module.product.service.favorite;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoritePageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;\n\nimport javax.validation.Valid;\n\n/**\n * 商品收藏 Service 接口\n *\n * @author jason\n */\npublic interface ProductFavoriteService {\n\n    /**\n     * 创建商品收藏\n     *\n     * @param userId 用户编号\n     * @param spuId SPU 编号\n     */\n    Long createFavorite(Long userId, Long spuId);\n\n    /**\n     * 取消商品收藏\n     *\n     * @param userId 用户编号\n     * @param spuId SPU 编号\n     */\n    void deleteFavorite(Long userId, Long spuId);\n\n    /**\n     * 分页查询用户收藏列表\n     *\n     * @param userId 用户编号\n     * @param reqVO 请求 vo\n     */\n    PageResult<ProductFavoriteDO> getFavoritePage(Long userId, @Valid AppFavoritePageReqVO reqVO);\n\n    /**\n     * 分页查询用户收藏列表\n     *\n     * @param reqVO 请求 vo\n     */\n    PageResult<ProductFavoriteDO> getFavoritePage(@Valid ProductFavoritePageReqVO reqVO);\n\n    /**\n     * 获取收藏过商品\n     *\n     * @param userId 用户编号\n     * @param spuId SPU 编号\n     */\n    ProductFavoriteDO getFavorite(Long userId, Long spuId);\n\n    /**\n     * 获取用户收藏数量\n     *\n     * @param userId 用户编号\n     * @return 数量\n     */\n    Long getFavoriteCount(Long userId);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.product.service.favorite;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoritePageReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO;\nimport cn.iocoder.yudao.module.product.convert.favorite.ProductFavoriteConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;\nimport cn.iocoder.yudao.module.product.dal.mysql.favorite.ProductFavoriteMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.FAVORITE_EXISTS;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.FAVORITE_NOT_EXISTS;\n\n/**\n * 商品收藏 Service 实现类\n *\n * @author jason\n */\n@Service\n@Validated\npublic class ProductFavoriteServiceImpl implements ProductFavoriteService {\n\n    @Resource\n    private ProductFavoriteMapper productFavoriteMapper;\n\n    @Override\n    public Long createFavorite(Long userId, Long spuId) {\n        ProductFavoriteDO favorite = productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId);\n        if (favorite != null) {\n            throw exception(FAVORITE_EXISTS);\n        }\n\n        ProductFavoriteDO entity = ProductFavoriteConvert.INSTANCE.convert(userId, spuId);\n        productFavoriteMapper.insert(entity);\n        return entity.getId();\n    }\n\n    @Override\n    public void deleteFavorite(Long userId, Long spuId) {\n        ProductFavoriteDO favorite = productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId);\n        if (favorite == null) {\n            throw exception(FAVORITE_NOT_EXISTS);\n        }\n\n        productFavoriteMapper.deleteById(favorite.getId());\n    }\n\n    @Override\n    public PageResult<ProductFavoriteDO> getFavoritePage(Long userId, @Valid AppFavoritePageReqVO reqVO) {\n        return productFavoriteMapper.selectPageByUserAndType(userId, reqVO);\n    }\n\n    @Override\n    public PageResult<ProductFavoriteDO> getFavoritePage(@Valid ProductFavoritePageReqVO reqVO) {\n        return productFavoriteMapper.selectPageByUserId(reqVO);\n    }\n\n    @Override\n    public ProductFavoriteDO getFavorite(Long userId, Long spuId) {\n        return productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId);\n    }\n\n    @Override\n    public Long getFavoriteCount(Long userId) {\n        return productFavoriteMapper.selectCountByUserId(userId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryService.java",
    "content": "package cn.iocoder.yudao.module.product.service.history;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;\nimport org.springframework.scheduling.annotation.Async;\n\nimport java.util.Collection;\n\n/**\n * 商品浏览记录 Service 接口\n *\n * @author owen\n */\npublic interface ProductBrowseHistoryService {\n\n    /**\n     * 创建商品浏览记录\n     *\n     * @param userId 用户编号\n     * @param spuId  SPU 编号\n     */\n    @Async\n    void createBrowseHistory(Long userId, Long spuId);\n\n    /**\n     * 隐藏用户商品浏览记录\n     *\n     * @param userId 用户编号\n     * @param spuId  SPU 编号\n     */\n    void hideUserBrowseHistory(Long userId, Collection<Long> spuId);\n\n    /**\n     * 获得商品浏览记录分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 商品浏览记录分页\n     */\n    PageResult<ProductBrowseHistoryDO> getBrowseHistoryPage(ProductBrowseHistoryPageReqVO pageReqVO);\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.product.service.history;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;\nimport cn.iocoder.yudao.module.product.dal.mysql.history.ProductBrowseHistoryMapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\n\n/**\n * 商品浏览记录 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class ProductBrowseHistoryServiceImpl implements ProductBrowseHistoryService {\n\n    private static final int USER_STORE_MAXIMUM = 100;\n\n    @Resource\n    private ProductBrowseHistoryMapper browseHistoryMapper;\n\n    @Override\n    public void createBrowseHistory(Long userId, Long spuId) {\n        // 用户未登录时不记录\n        if (userId == null) {\n            return;\n        }\n\n        // 情况一：同一个商品，只保留最新的一条记录\n        ProductBrowseHistoryDO history = browseHistoryMapper.selectByUserIdAndSpuId(userId, spuId);\n        if (history != null) {\n            browseHistoryMapper.deleteById(history);\n        } else {\n            // 情况二：限制每个用户的浏览记录的条数（只查一条最早地记录、记录总数）\n            // TODO @疯狂：这里最好先查询一次数量。如果发现超过了，再删除；主要考虑，可能有部分不超过，提前就多了一次 sql 查询了\n            Page<ProductBrowseHistoryDO> pageResult = browseHistoryMapper.selectPageByUserIdOrderByCreateTimeAsc(userId, 1, 1);\n            if (pageResult.getTotal() >= USER_STORE_MAXIMUM) {\n                browseHistoryMapper.deleteById(CollUtil.getFirst(pageResult.getRecords()));\n            }\n        }\n\n        // 插入\n        ProductBrowseHistoryDO browseHistory = new ProductBrowseHistoryDO()\n                .setUserId(userId)\n                .setSpuId(spuId);\n        browseHistoryMapper.insert(browseHistory);\n    }\n\n    @Override\n    public void hideUserBrowseHistory(Long userId, Collection<Long> spuIds) {\n        browseHistoryMapper.updateUserDeletedByUserId(userId, spuIds, true);\n    }\n\n    @Override\n    public PageResult<ProductBrowseHistoryDO> getBrowseHistoryPage(ProductBrowseHistoryPageReqVO pageReqVO) {\n        return browseHistoryMapper.selectPage(pageReqVO);\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java",
    "content": "package cn.iocoder.yudao.module.product.service.property;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertySaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 商品属性项 Service 接口\n *\n * @author 芋道源码\n */\npublic interface ProductPropertyService {\n\n    /**\n     * 创建属性项\n     * 注意，如果已经存在该属性项，直接返回它的编号即可\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createProperty(@Valid ProductPropertySaveReqVO createReqVO);\n\n    /**\n     * 更新属性项\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateProperty(@Valid ProductPropertySaveReqVO updateReqVO);\n\n    /**\n     * 删除属性项\n     *\n     * @param id 编号\n     */\n    void deleteProperty(Long id);\n\n    /**\n     * 获取属性名称分页\n     *\n     * @param pageReqVO 分页条件\n     * @return 属性项分页\n     */\n    PageResult<ProductPropertyDO> getPropertyPage(ProductPropertyPageReqVO pageReqVO);\n\n    /**\n     * 获得指定编号的属性项\n     *\n     * @param id 编号\n     * @return 属性项\n     */\n    ProductPropertyDO getProperty(Long id);\n\n    /**\n     * 根据属性项的编号的集合，获得对应的属性项数组\n     *\n     * @param ids 属性项的编号的集合\n     * @return 属性项数组\n     */\n    List<ProductPropertyDO> getPropertyList(Collection<Long> ids);\n\n    /**\n     * 获得指定状态的属性项列表\n     *\n     * @return 属性项列表\n     */\n    List<ProductPropertyDO> getPropertyList();\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.product.service.property;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertySaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;\nimport cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyMapper;\nimport cn.iocoder.yudao.module.product.service.sku.ProductSkuService;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;\n\n/**\n * 商品属性项 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ProductPropertyServiceImpl implements ProductPropertyService {\n\n    @Resource\n    private ProductPropertyMapper productPropertyMapper;\n\n    @Resource\n    @Lazy // 延迟加载，解决循环依赖问题\n    private ProductPropertyValueService productPropertyValueService;\n\n    @Resource\n    private ProductSkuService productSkuService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createProperty(ProductPropertySaveReqVO createReqVO) {\n        // 如果已经添加过该属性项，直接返回\n        ProductPropertyDO dbProperty = productPropertyMapper.selectByName(createReqVO.getName());\n        if (dbProperty != null) {\n            return dbProperty.getId();\n        }\n\n        // 插入\n        ProductPropertyDO property = BeanUtils.toBean(createReqVO, ProductPropertyDO.class);\n        productPropertyMapper.insert(property);\n        // 返回\n        return property.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateProperty(ProductPropertySaveReqVO updateReqVO) {\n        validatePropertyExists(updateReqVO.getId());\n        // 校验名字重复\n        ProductPropertyDO property = productPropertyMapper.selectByName(updateReqVO.getName());\n        if (property != null &&\n                ObjUtil.notEqual(property.getId(), updateReqVO.getId())) {\n            throw exception(PROPERTY_EXISTS);\n        }\n\n        // 更新\n        ProductPropertyDO updateObj = BeanUtils.toBean(updateReqVO, ProductPropertyDO.class);\n        productPropertyMapper.updateById(updateObj);\n        // 更新 sku 相关属性\n        productSkuService.updateSkuProperty(updateObj.getId(), updateObj.getName());\n    }\n\n    @Override\n    public void deleteProperty(Long id) {\n        // 校验存在\n        validatePropertyExists(id);\n        // 校验其下是否有规格值\n        if (productPropertyValueService.getPropertyValueCountByPropertyId(id) > 0) {\n            throw exception(PROPERTY_DELETE_FAIL_VALUE_EXISTS);\n        }\n\n        // 删除\n        productPropertyMapper.deleteById(id);\n        // 同步删除属性值\n        productPropertyValueService.deletePropertyValueByPropertyId(id);\n    }\n\n    private void validatePropertyExists(Long id) {\n        if (productPropertyMapper.selectById(id) == null) {\n            throw exception(PROPERTY_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public PageResult<ProductPropertyDO> getPropertyPage(ProductPropertyPageReqVO pageReqVO) {\n        return productPropertyMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public ProductPropertyDO getProperty(Long id) {\n        return productPropertyMapper.selectById(id);\n    }\n\n    @Override\n    public List<ProductPropertyDO> getPropertyList(Collection<Long> ids) {\n        return productPropertyMapper.selectByIds(ids);\n    }\n\n    @Override\n    public List<ProductPropertyDO> getPropertyList() {\n        return productPropertyMapper.selectList();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueService.java",
    "content": "package cn.iocoder.yudao.module.product.service.property;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueSaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 商品属性值 Service 接口\n *\n * @author LuoWenFeng\n */\npublic interface ProductPropertyValueService {\n\n    /**\n     * 创建属性值\n     * 注意，如果已经存在该属性值，直接返回它的编号即可\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createPropertyValue(ProductPropertyValueSaveReqVO createReqVO);\n\n    /**\n     * 更新属性值\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updatePropertyValue(ProductPropertyValueSaveReqVO updateReqVO);\n\n    /**\n     * 删除属性值\n     *\n     * @param id 编号\n     */\n    void deletePropertyValue(Long id);\n\n    /**\n     * 获得属性值\n     *\n     * @param id 编号\n     * @return 属性值\n     */\n    ProductPropertyValueDO getPropertyValue(Long id);\n\n    /**\n     * 根据属性项编号数组，获得属性值列表\n     *\n     * @param propertyIds 属性项目编号数组\n     * @return 属性值列表\n     */\n    List<ProductPropertyValueDO> getPropertyValueListByPropertyId(Collection<Long> propertyIds);\n\n    /**\n     * 根据属性项编号，活的属性值数量\n     *\n     * @param propertyId 属性项编号数\n     * @return 属性值数量\n     */\n    Integer getPropertyValueCountByPropertyId(Long propertyId);\n\n    /**\n     * 获取属性值的分页\n     *\n     * @param pageReqVO 查询条件\n     * @return 属性值的分页\n     */\n    PageResult<ProductPropertyValueDO> getPropertyValuePage(ProductPropertyValuePageReqVO pageReqVO);\n\n    /**\n     * 删除指定属性项编号下的属性值们\n     *\n     * @param propertyId 属性项的编号\n     */\n    void deletePropertyValueByPropertyId(Long propertyId);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.product.service.property;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueSaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;\nimport cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyValueMapper;\nimport cn.iocoder.yudao.module.product.service.sku.ProductSkuService;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_VALUE_EXISTS;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS;\n\n/**\n * 商品属性值 Service 实现类\n *\n * @author LuoWenFeng\n */\n@Service\n@Validated\npublic class ProductPropertyValueServiceImpl implements ProductPropertyValueService {\n\n    @Resource\n    private ProductPropertyValueMapper productPropertyValueMapper;\n\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private ProductSkuService productSkuService;\n\n    @Override\n    public Long createPropertyValue(ProductPropertyValueSaveReqVO createReqVO) {\n        // 如果已经添加过该属性值，直接返回\n        ProductPropertyValueDO dbValue = productPropertyValueMapper.selectByName(\n                createReqVO.getPropertyId(), createReqVO.getName());\n        if (dbValue != null) {\n            return dbValue.getId();\n        }\n\n        // 新增\n        ProductPropertyValueDO value = BeanUtils.toBean(createReqVO, ProductPropertyValueDO.class);\n        productPropertyValueMapper.insert(value);\n        return value.getId();\n    }\n\n    @Override\n    public void updatePropertyValue(ProductPropertyValueSaveReqVO updateReqVO) {\n        validatePropertyValueExists(updateReqVO.getId());\n        // 校验名字唯一\n        ProductPropertyValueDO value = productPropertyValueMapper.selectByName\n                (updateReqVO.getPropertyId(), updateReqVO.getName());\n        if (value != null && !value.getId().equals(updateReqVO.getId())) {\n            throw exception(PROPERTY_VALUE_EXISTS);\n        }\n\n        // 更新\n        ProductPropertyValueDO updateObj = BeanUtils.toBean(updateReqVO, ProductPropertyValueDO.class);\n        productPropertyValueMapper.updateById(updateObj);\n        // 更新 sku 相关属性\n        productSkuService.updateSkuPropertyValue(updateObj.getId(), updateObj.getName());\n    }\n\n    @Override\n    public void deletePropertyValue(Long id) {\n        validatePropertyValueExists(id);\n        productPropertyValueMapper.deleteById(id);\n    }\n\n    private void validatePropertyValueExists(Long id) {\n        if (productPropertyValueMapper.selectById(id) == null) {\n            throw exception(PROPERTY_VALUE_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ProductPropertyValueDO getPropertyValue(Long id) {\n        return productPropertyValueMapper.selectById(id);\n    }\n\n    @Override\n    public List<ProductPropertyValueDO> getPropertyValueListByPropertyId(Collection<Long> propertyIds) {\n        return productPropertyValueMapper.selectListByPropertyId(propertyIds);\n    }\n\n    @Override\n    public Integer getPropertyValueCountByPropertyId(Long propertyId) {\n        return productPropertyValueMapper.selectCountByPropertyId(propertyId);\n    }\n\n    @Override\n    public PageResult<ProductPropertyValueDO> getPropertyValuePage(ProductPropertyValuePageReqVO pageReqVO) {\n        return productPropertyValueMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public void deletePropertyValueByPropertyId(Long propertyId) {\n        productPropertyValueMapper.deleteByPropertyId(propertyId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java",
    "content": "package cn.iocoder.yudao.module.product.service.sku;\n\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuSaveReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 商品 SKU Service 接口\n *\n * @author 芋道源码\n */\npublic interface ProductSkuService {\n\n    /**\n     * 删除商品 SKU\n     *\n     * @param id 编号\n     */\n    void deleteSku(Long id);\n\n    /**\n     * 获得商品 SKU 信息\n     *\n     * @param id 编号\n     * @return 商品 SKU 信息\n     */\n    ProductSkuDO getSku(Long id);\n\n    /**\n     * 获得商品 SKU 信息\n     *\n     * @param id 编号\n     * @param includeDeleted 是否包含已删除的\n     * @return 商品 SKU 信息\n     */\n    ProductSkuDO getSku(Long id, boolean includeDeleted);\n\n    /**\n     * 获得商品 SKU 列表\n     *\n     * @param ids 编号\n     * @return 商品sku列表\n     */\n    List<ProductSkuDO> getSkuList(Collection<Long> ids);\n\n    /**\n     * 对 sku 的组合的属性等进行合法性校验\n     *\n     * @param list sku组合的集合\n     */\n    void validateSkuList(List<ProductSkuSaveReqVO> list, Boolean specType);\n\n    /**\n     * 批量创建 SKU\n     *\n     * @param spuId 商品 SPU 编号\n     * @param list  SKU 对象集合\n     */\n    void createSkuList(Long spuId, List<ProductSkuSaveReqVO> list);\n\n    /**\n     * 根据 SPU 编号，批量更新它的 SKU 信息\n     *\n     * @param spuId SPU 编码\n     * @param skus  SKU 的集合\n     */\n    void updateSkuList(Long spuId, List<ProductSkuSaveReqVO> skus);\n\n    /**\n     * 更新 SKU 库存（增量）\n     * <p>\n     * 如果更新的库存不足，会抛出异常\n     *\n     * @param updateStockReqDTO 更行请求\n     */\n    void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO);\n\n    /**\n     * 获得商品 SKU 集合\n     *\n     * @param spuId spu 编号\n     * @return 商品sku 集合\n     */\n    List<ProductSkuDO> getSkuListBySpuId(Long spuId);\n\n    /**\n     * 获得 spu 对应的 SKU 集合\n     *\n     * @param spuIds spu 编码集合\n     * @return 商品 sku 集合\n     */\n    List<ProductSkuDO> getSkuListBySpuId(Collection<Long> spuIds);\n\n    /**\n     * 通过 spuId 删除 sku 信息\n     *\n     * @param spuId spu 编码\n     */\n    void deleteSkuBySpuId(Long spuId);\n\n    /**\n     * 更新 sku 属性\n     *\n     * @param propertyId   属性 id\n     * @param propertyName 属性名\n     * @return int 影响的行数\n     */\n    int updateSkuProperty(Long propertyId, String propertyName);\n\n    /**\n     * 更新 sku 属性值\n     *\n     * @param propertyValueId   属性值 id\n     * @param propertyValueName 属性值名字\n     * @return int 影响的行数\n     */\n    int updateSkuPropertyValue(Long propertyValueId, String propertyValueName);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.product.service.sku;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuSaveReqVO;\nimport cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;\nimport cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;\nimport cn.iocoder.yudao.module.product.service.property.ProductPropertyService;\nimport cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;\nimport cn.iocoder.yudao.module.product.service.spu.ProductSpuService;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;\n\n/**\n * 商品 SKU Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ProductSkuServiceImpl implements ProductSkuService {\n\n    @Resource\n    private ProductSkuMapper productSkuMapper;\n\n    @Resource\n    @Lazy // 循环依赖，避免报错\n    private ProductSpuService productSpuService;\n    @Resource\n    @Lazy // 循环依赖，避免报错\n    private ProductPropertyService productPropertyService;\n    @Resource\n    private ProductPropertyValueService productPropertyValueService;\n\n    @Override\n    public void deleteSku(Long id) {\n        // 校验存在\n        validateSkuExists(id);\n        // 删除\n        productSkuMapper.deleteById(id);\n    }\n\n    private void validateSkuExists(Long id) {\n        if (productSkuMapper.selectById(id) == null) {\n            throw exception(SKU_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ProductSkuDO getSku(Long id) {\n        return productSkuMapper.selectById(id);\n    }\n\n    @Override\n    public ProductSkuDO getSku(Long id, boolean includeDeleted) {\n        if (includeDeleted) {\n            return productSkuMapper.selectByIdIncludeDeleted(id);\n        }\n        return getSku(id);\n    }\n\n    @Override\n    public List<ProductSkuDO> getSkuList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return ListUtil.empty();\n        }\n        return productSkuMapper.selectByIds(ids);\n    }\n\n    @Override\n    public void validateSkuList(List<ProductSkuSaveReqVO> skus, Boolean specType) {\n        // 0、校验skus是否为空\n        if (CollUtil.isEmpty(skus)) {\n            throw exception(SKU_NOT_EXISTS);\n        }\n        // 单规格，赋予单规格默认属性\n        if (ObjectUtil.equal(specType, false)) {\n            ProductSkuSaveReqVO skuVO = skus.get(0);\n            List<ProductSkuSaveReqVO.Property> properties = new ArrayList<>();\n            ProductSkuSaveReqVO.Property property = new ProductSkuSaveReqVO.Property()\n                    .setPropertyId(ProductPropertyDO.ID_DEFAULT).setPropertyName(ProductPropertyDO.NAME_DEFAULT)\n                    .setValueId(ProductPropertyValueDO.ID_DEFAULT).setValueName(ProductPropertyValueDO.NAME_DEFAULT);\n            properties.add(property);\n            skuVO.setProperties(properties);\n            return; // 单规格不需要后续的校验\n        }\n\n        // 1、校验属性项存在\n        Set<Long> propertyIds = skus.stream().filter(p -> p.getProperties() != null)\n                // 遍历多个 Property 属性\n                .flatMap(p -> p.getProperties().stream())\n                // 将每个 Property 转换成对应的 propertyId，最后形成集合\n                .map(ProductSkuSaveReqVO.Property::getPropertyId)\n                .collect(Collectors.toSet());\n        List<ProductPropertyDO> propertyList = productPropertyService.getPropertyList(propertyIds);\n        if (propertyList.size() != propertyIds.size()) {\n            throw exception(PROPERTY_NOT_EXISTS);\n        }\n\n        // 2. 校验，一个 SKU 下，没有重复的属性。校验方式是，遍历每个 SKU ，看看是否有重复的属性 propertyId\n        Map<Long, ProductPropertyValueDO> propertyValueMap = convertMap(productPropertyValueService.getPropertyValueListByPropertyId(propertyIds), ProductPropertyValueDO::getId);\n        skus.forEach(sku -> {\n            Set<Long> skuPropertyIds = convertSet(sku.getProperties(), propertyItem -> propertyValueMap.get(propertyItem.getValueId()).getPropertyId());\n            if (skuPropertyIds.size() != sku.getProperties().size()) {\n                throw exception(SKU_PROPERTIES_DUPLICATED);\n            }\n        });\n\n        // 3. 再校验，每个 Sku 的属性值的数量，是一致的。\n        int attrValueIdsSize = skus.get(0).getProperties().size();\n        for (int i = 1; i < skus.size(); i++) {\n            if (attrValueIdsSize != skus.get(i).getProperties().size()) {\n                throw exception(SPU_ATTR_NUMBERS_MUST_BE_EQUALS);\n            }\n        }\n\n        // 4. 最后校验，每个 Sku 之间不是重复的\n        // 每个元素，都是一个 Sku 的 attrValueId 集合。这样，通过最外层的 Set ，判断是否有重复的.\n        Set<Set<Long>> skuAttrValues = new HashSet<>();\n        for (ProductSkuSaveReqVO sku : skus) {\n            // 添加失败，说明重复\n            if (!skuAttrValues.add(convertSet(sku.getProperties(), ProductSkuSaveReqVO.Property::getValueId))) {\n                throw exception(SPU_SKU_NOT_DUPLICATE);\n            }\n        }\n    }\n\n    @Override\n    public void createSkuList(Long spuId, List<ProductSkuSaveReqVO> skuCreateReqList) {\n        List<ProductSkuDO> skus = BeanUtils.toBean(skuCreateReqList, ProductSkuDO.class, sku -> sku.setSpuId(spuId).setSalesCount(0));\n        productSkuMapper.insertBatch(skus);\n    }\n\n    @Override\n    public List<ProductSkuDO> getSkuListBySpuId(Long spuId) {\n        return productSkuMapper.selectListBySpuId(spuId);\n    }\n\n    @Override\n    public List<ProductSkuDO> getSkuListBySpuId(Collection<Long> spuIds) {\n        if (CollUtil.isEmpty(spuIds)) {\n            return Collections.emptyList();\n        }\n        return productSkuMapper.selectListBySpuId(spuIds);\n    }\n\n    @Override\n    public void deleteSkuBySpuId(Long spuId) {\n        productSkuMapper.deleteBySpuId(spuId);\n    }\n\n    @Override\n    public int updateSkuProperty(Long propertyId, String propertyName) {\n        // 获取所有的 sku\n        List<ProductSkuDO> skuDOList = productSkuMapper.selectList();\n        // 处理后需要更新的 sku\n        List<ProductSkuDO> updateSkus = new ArrayList<>();\n        if (CollUtil.isEmpty(skuDOList)) {\n            return 0;\n        }\n        skuDOList.stream().filter(sku -> sku.getProperties() != null)\n                .forEach(sku -> sku.getProperties().forEach(property -> {\n                    if (property.getPropertyId().equals(propertyId)) {\n                        property.setPropertyName(propertyName);\n                        updateSkus.add(sku);\n                    }\n                }));\n        if (CollUtil.isEmpty(updateSkus)) {\n            return 0;\n        }\n\n        productSkuMapper.updateBatch(updateSkus);\n        return updateSkus.size();\n    }\n\n    @Override\n    public int updateSkuPropertyValue(Long propertyValueId, String propertyValueName) {\n        // 获取所有的 sku\n        List<ProductSkuDO> skuDOList = productSkuMapper.selectList();\n        // 处理后需要更新的 sku\n        List<ProductSkuDO> updateSkus = new ArrayList<>();\n        if (CollUtil.isEmpty(skuDOList)) {\n            return 0;\n        }\n        skuDOList.stream()\n                .filter(sku -> sku.getProperties() != null)\n                .forEach(sku -> sku.getProperties().forEach(property -> {\n                    if (property.getValueId().equals(propertyValueId)) {\n                        property.setValueName(propertyValueName);\n                        updateSkus.add(sku);\n                    }\n                }));\n        if (CollUtil.isEmpty(updateSkus)) {\n            return 0;\n        }\n\n        productSkuMapper.updateBatch(updateSkus);\n        return updateSkus.size();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSkuList(Long spuId, List<ProductSkuSaveReqVO> skus) {\n        // 构建属性与 SKU 的映射关系;\n        Map<String, Long> existsSkuMap = convertMap(productSkuMapper.selectListBySpuId(spuId),\n                ProductSkuConvert.INSTANCE::buildPropertyKey, ProductSkuDO::getId);\n\n        // 拆分三个集合，新插入的、需要更新的、需要删除的\n        List<ProductSkuDO> insertSkus = new ArrayList<>();\n        List<ProductSkuDO> updateSkus = new ArrayList<>();\n        List<ProductSkuDO> allUpdateSkus = BeanUtils.toBean(skus, ProductSkuDO.class, sku -> sku.setSpuId(spuId));\n        allUpdateSkus.forEach(sku -> {\n            String propertiesKey = ProductSkuConvert.INSTANCE.buildPropertyKey(sku);\n            // 1、找得到的，进行更新\n            Long existsSkuId = existsSkuMap.remove(propertiesKey);\n            if (existsSkuId != null) {\n                sku.setId(existsSkuId);\n                updateSkus.add(sku);\n                return;\n            }\n            // 2、找不到，进行插入\n            sku.setSpuId(spuId);\n            insertSkus.add(sku);\n        });\n\n        // 执行最终的批量操作\n        if (CollUtil.isNotEmpty(insertSkus)) {\n            productSkuMapper.insertBatch(insertSkus);\n        }\n        if (CollUtil.isNotEmpty(updateSkus)) {\n            updateSkus.forEach(sku -> productSkuMapper.updateById(sku));\n        }\n        if (CollUtil.isNotEmpty(existsSkuMap)) {\n            productSkuMapper.deleteByIds(existsSkuMap.values());\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO) {\n        // 更新 SKU 库存\n        updateStockReqDTO.getItems().forEach(item -> {\n            if (item.getIncrCount() > 0) {\n                productSkuMapper.updateStockIncr(item.getId(), item.getIncrCount());\n            } else if (item.getIncrCount() < 0) {\n                int updateStockIncr = productSkuMapper.updateStockDecr(item.getId(), item.getIncrCount());\n                if (updateStockIncr == 0) {\n                    throw exception(SKU_STOCK_NOT_ENOUGH);\n                }\n            }\n        });\n\n        // 更新 SPU 库存\n        List<ProductSkuDO> skus = productSkuMapper.selectByIds(\n                convertSet(updateStockReqDTO.getItems(), ProductSkuUpdateStockReqDTO.Item::getId));\n        Map<Long, Integer> spuStockIncrCounts = ProductSkuConvert.INSTANCE.convertSpuStockMap(\n                updateStockReqDTO.getItems(), skus);\n        productSpuService.updateSpuStock(spuStockIncrCounts);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java",
    "content": "package cn.iocoder.yudao.module.product.service.spu;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuSaveReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateStatusReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport org.springframework.scheduling.annotation.Async;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 商品 SPU Service 接口\n *\n * @author 芋道源码\n */\npublic interface ProductSpuService {\n\n    /**\n     * 创建商品 SPU\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createSpu(@Valid ProductSpuSaveReqVO createReqVO);\n\n    /**\n     * 更新商品 SPU\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateSpu(@Valid ProductSpuSaveReqVO updateReqVO);\n\n    /**\n     * 删除商品 SPU\n     *\n     * @param id 编号\n     */\n    void deleteSpu(Long id);\n\n    /**\n     * 获得商品 SPU\n     *\n     * @param id 编号\n     * @return 商品 SPU\n     */\n    ProductSpuDO getSpu(Long id);\n\n    /**\n     * 获得商品 SPU\n     *\n     * @param id 编号\n     * @param includeDeleted 是否包含已删除的\n     * @return 商品 SPU\n     */\n    ProductSpuDO getSpu(Long id, boolean includeDeleted);\n\n    /**\n     * 获得商品 SPU 列表\n     *\n     * @param ids 编号数组\n     * @return 商品 SPU 列表\n     */\n    List<ProductSpuDO> getSpuList(Collection<Long> ids);\n\n    /**\n     * 获得商品 SPU Map\n     *\n     * @param ids 编号数组\n     * @return 商品 SPU Map\n     */\n    default Map<Long, ProductSpuDO> getSpuMap(Collection<Long> ids) {\n        List<ProductSpuDO> list = getSpuList(ids);\n        return CollectionUtils.convertMap(list, ProductSpuDO::getId);\n    }\n\n    /**\n     * 获得指定状态的商品 SPU 列表\n     *\n     * @param status 状态\n     * @return 商品 SPU 列表\n     */\n    List<ProductSpuDO> getSpuListByStatus(Integer status);\n\n    /**\n     * 获得商品 SPU 分页，提供给挂你兰后台使用\n     *\n     * @param pageReqVO 分页查询\n     * @return 商品spu分页\n     */\n    PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO);\n\n    /**\n     * 获得商品 SPU 分页，提供给用户 App 使用\n     *\n     * @param pageReqVO 分页查询\n     * @return 商品 SPU 分页\n     */\n    PageResult<ProductSpuDO> getSpuPage(AppProductSpuPageReqVO pageReqVO);\n\n    /**\n     * 更新商品 SPU 库存（增量）\n     *\n     * @param stockIncrCounts SPU 编号与库存变化（增量）的映射\n     */\n    void updateSpuStock(Map<Long, Integer> stockIncrCounts);\n\n    /**\n     * 更新 SPU 状态\n     *\n     * @param updateReqVO 更新请求\n     */\n    void updateSpuStatus(ProductSpuUpdateStatusReqVO updateReqVO);\n\n    /**\n     * 获取 SPU 列表标签对应的 Count 数量\n     *\n     * @return Count 数量\n     */\n    Map<Integer, Long> getTabsCount();\n\n    /**\n     * 通过分类 categoryId 查询 SPU 个数\n     *\n     * @param categoryId 分类 categoryId\n     * @return SPU 数量\n     */\n    Long getSpuCountByCategoryId(Long categoryId);\n\n\n    /**\n     * 校验商品是否有效。如下情况，视为无效：\n     * 1. 商品编号不存在\n     * 2. 商品被禁用\n     *\n     * @param ids 商品编号数组\n     * @return 商品 SPU 列表\n     */\n    List<ProductSpuDO> validateSpuList(Collection<Long> ids);\n\n    /**\n     * 更新商品 SPU 浏览量\n     *\n     * @param id        商品 SPU 编号\n     * @param incrCount 增加的数量\n     */\n    @Async\n    void updateBrowseCount(Long id, int incrCount);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.product.service.spu;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuSaveReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuSaveReqVO;\nimport cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateStatusReqVO;\nimport cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;\nimport cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;\nimport cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;\nimport cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;\nimport cn.iocoder.yudao.module.product.service.brand.ProductBrandService;\nimport cn.iocoder.yudao.module.product.service.category.ProductCategoryService;\nimport cn.iocoder.yudao.module.product.service.sku.ProductSkuService;\nimport com.google.common.collect.Maps;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.*;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO.CATEGORY_LEVEL;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;\n\n/**\n * 商品 SPU Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class ProductSpuServiceImpl implements ProductSpuService {\n\n    @Resource\n    private ProductSpuMapper productSpuMapper;\n\n    @Resource\n    @Lazy // 循环依赖，避免报错\n    private ProductSkuService productSkuService;\n    @Resource\n    private ProductBrandService brandService;\n    @Resource\n    private ProductCategoryService categoryService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createSpu(ProductSpuSaveReqVO createReqVO) {\n        // 校验分类、品牌\n        validateCategory(createReqVO.getCategoryId());\n        brandService.validateProductBrand(createReqVO.getBrandId());\n        // 校验 SKU\n        List<ProductSkuSaveReqVO> skuSaveReqList = createReqVO.getSkus();\n        productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType());\n\n        ProductSpuDO spu = BeanUtils.toBean(createReqVO, ProductSpuDO.class);\n        // 初始化 SPU 中 SKU 相关属性\n        initSpuFromSkus(spu, skuSaveReqList);\n        // 插入 SPU\n        productSpuMapper.insert(spu);\n        // 插入 SKU\n        productSkuService.createSkuList(spu.getId(), skuSaveReqList);\n        // 返回\n        return spu.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSpu(ProductSpuSaveReqVO updateReqVO) {\n        // 校验 SPU 是否存在\n        ProductSpuDO spu = validateSpuExists(updateReqVO.getId());\n        // 校验分类、品牌\n        validateCategory(updateReqVO.getCategoryId());\n        brandService.validateProductBrand(updateReqVO.getBrandId());\n        // 校验SKU\n        List<ProductSkuSaveReqVO> skuSaveReqList = updateReqVO.getSkus();\n        productSkuService.validateSkuList(skuSaveReqList, updateReqVO.getSpecType());\n\n        // 更新 SPU\n        ProductSpuDO updateObj = BeanUtils.toBean(updateReqVO, ProductSpuDO.class).setStatus(spu.getStatus());\n        initSpuFromSkus(updateObj, skuSaveReqList);\n        productSpuMapper.updateById(updateObj);\n        // 批量更新 SKU\n        productSkuService.updateSkuList(updateObj.getId(), updateReqVO.getSkus());\n    }\n\n    /**\n     * 基于 SKU 的信息，初始化 SPU 的信息\n     * 主要是计数相关的字段，例如说市场价、最大最小价、库存等等\n     *\n     * @param spu  商品 SPU\n     * @param skus 商品 SKU 数组\n     */\n    private void initSpuFromSkus(ProductSpuDO spu, List<ProductSkuSaveReqVO> skus) {\n        // sku 单价最低的商品的价格\n        spu.setPrice(getMinValue(skus, ProductSkuSaveReqVO::getPrice));\n        // sku 单价最低的商品的市场价格\n        spu.setMarketPrice(getMinValue(skus, ProductSkuSaveReqVO::getMarketPrice));\n        // sku 单价最低的商品的成本价格\n        spu.setCostPrice(getMinValue(skus, ProductSkuSaveReqVO::getCostPrice));\n        // skus 库存总数\n        spu.setStock(getSumValue(skus, ProductSkuSaveReqVO::getStock, Math::addExact));\n        // 若是 spu 已有状态则不处理\n        if (spu.getStatus() == null) {\n            spu.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); // 默认状态为上架\n            spu.setSalesCount(0); // 默认商品销量\n            spu.setBrowseCount(0); // 默认商品浏览量\n        }\n    }\n\n    /**\n     * 校验商品分类是否合法\n     *\n     * @param id 商品分类编号\n     */\n    private void validateCategory(Long id) {\n        categoryService.validateCategory(id);\n        // 校验层级\n        if (categoryService.getCategoryLevel(id) < CATEGORY_LEVEL) {\n            throw exception(SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR);\n        }\n    }\n\n    @Override\n    public List<ProductSpuDO> validateSpuList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        // 获得商品信息\n        List<ProductSpuDO> list = productSpuMapper.selectByIds(ids);\n        Map<Long, ProductSpuDO> spuMap = CollectionUtils.convertMap(list, ProductSpuDO::getId);\n        // 校验\n        ids.forEach(id -> {\n            ProductSpuDO spu = spuMap.get(id);\n            if (spu == null) {\n                throw exception(SPU_NOT_EXISTS);\n            }\n            if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {\n                throw exception(SPU_NOT_ENABLE, spu.getName());\n            }\n        });\n        return list;\n    }\n\n    @Override\n    public void updateBrowseCount(Long id, int incrCount) {\n        productSpuMapper.updateBrowseCount(id , incrCount);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteSpu(Long id) {\n        // 校验存在\n        validateSpuExists(id);\n        // 校验商品状态不是回收站不能删除\n        ProductSpuDO spuDO = productSpuMapper.selectById(id);\n        // 判断 SPU 状态是否为回收站\n        if (ObjectUtil.notEqual(spuDO.getStatus(), ProductSpuStatusEnum.RECYCLE.getStatus())) {\n            throw exception(SPU_NOT_RECYCLE);\n        }\n        // TODO 芋艿：【可选】参与活动中的商品，不允许删除？？？\n\n        // 删除 SPU\n        productSpuMapper.deleteById(id);\n        // 删除关联的 SKU\n        productSkuService.deleteSkuBySpuId(id);\n    }\n\n    private ProductSpuDO validateSpuExists(Long id) {\n        ProductSpuDO spuDO = productSpuMapper.selectById(id);\n        if (spuDO == null) {\n            throw exception(SPU_NOT_EXISTS);\n        }\n        return spuDO;\n    }\n\n    @Override\n    public ProductSpuDO getSpu(Long id) {\n        return productSpuMapper.selectById(id);\n    }\n\n    @Override\n    public ProductSpuDO getSpu(Long id, boolean includeDeleted) {\n        if (includeDeleted) {\n            return productSpuMapper.selectByIdIncludeDeleted(id);\n        }\n        return getSpu(id);\n    }\n\n    @Override\n    public List<ProductSpuDO> getSpuList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return Collections.emptyList();\n        }\n        Map<Long, ProductSpuDO> spuMap = convertMap(productSpuMapper.selectByIds(ids), ProductSpuDO::getId);\n        // 需要按照 ids 顺序返回。例如说：店铺装修选择了 [3, 1, 2] 三个商品，返回结果还是 [3, 1, 2]  这样的顺序\n        return convertList(ids, spuMap::get);\n    }\n\n    @Override\n    public List<ProductSpuDO> getSpuListByStatus(Integer status) {\n        return productSpuMapper.selectList(ProductSpuDO::getStatus, status);\n    }\n\n    @Override\n    public PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO) {\n        return productSpuMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public PageResult<ProductSpuDO> getSpuPage(AppProductSpuPageReqVO pageReqVO) {\n        // 查找时，如果查找某个分类编号，则包含它的子分类。因为顶级分类不包含商品\n        Set<Long> categoryIds = new HashSet<>();\n        if (pageReqVO.getCategoryId() != null && pageReqVO.getCategoryId() > 0) {\n            categoryIds.add(pageReqVO.getCategoryId());\n            List<ProductCategoryDO> categoryChildren = categoryService.getCategoryList(new ProductCategoryListReqVO()\n                    .setStatus(CommonStatusEnum.ENABLE.getStatus()).setParentId(pageReqVO.getCategoryId()));\n            categoryIds.addAll(convertList(categoryChildren, ProductCategoryDO::getId));\n        }\n        if (CollUtil.isNotEmpty(pageReqVO.getCategoryIds())) {\n            categoryIds.addAll(pageReqVO.getCategoryIds());\n            List<ProductCategoryDO> categoryChildren = categoryService.getCategoryList(new ProductCategoryListReqVO()\n                    .setStatus(CommonStatusEnum.ENABLE.getStatus()).setParentIds(pageReqVO.getCategoryIds()));\n            categoryIds.addAll(convertList(categoryChildren, ProductCategoryDO::getId));\n        }\n        // 分页查询\n        return productSpuMapper.selectPage(pageReqVO, categoryIds);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSpuStock(Map<Long, Integer> stockIncrCounts) {\n        stockIncrCounts.forEach((id, incCount) -> productSpuMapper.updateStock(id, incCount));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSpuStatus(ProductSpuUpdateStatusReqVO updateReqVO) {\n        // 校验存在\n        validateSpuExists(updateReqVO.getId());\n        // TODO 芋艿：【可选】参与活动中的商品，不允许下架？？？\n\n        // 更新状态\n        ProductSpuDO productSpuDO = productSpuMapper.selectById(updateReqVO.getId()).setStatus(updateReqVO.getStatus());\n        productSpuMapper.updateById(productSpuDO);\n    }\n\n    @Override\n    public Map<Integer, Long> getTabsCount() {\n        Map<Integer, Long> counts = Maps.newLinkedHashMapWithExpectedSize(5);\n        // 查询销售中的商品数量\n        counts.put(ProductSpuPageReqVO.FOR_SALE,\n                productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()));\n        // 查询仓库中的商品数量\n        counts.put(ProductSpuPageReqVO.IN_WAREHOUSE,\n                productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.DISABLE.getStatus()));\n        // 查询售空的商品数量\n        counts.put(ProductSpuPageReqVO.SOLD_OUT,\n                productSpuMapper.selectCount(ProductSpuDO::getStock, 0));\n        // 查询触发警戒库存的商品数量\n        counts.put(ProductSpuPageReqVO.ALERT_STOCK,\n                productSpuMapper.selectCount());\n        // 查询回收站中的商品数量\n        counts.put(ProductSpuPageReqVO.RECYCLE_BIN,\n                productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus()));\n        return counts;\n    }\n\n    @Override\n    public Long getSpuCountByCategoryId(Long categoryId) {\n        return productSpuMapper.selectCount(ProductSpuDO::getCategoryId, categoryId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 5 # 初始连接数\n        min-idle: 10 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n        slave: # 模拟从库，可根据自己需要修改 # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 400-infra.server.iocoder.cn # 地址\n    port: 6379 # 端口\n    database: 1 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n      # Spring Boot Admin Server 服务端的相关配置\n      context-path: /admin # 配置 Spring\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  demo: true # 开启演示模式\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 1 # 初始连接数\n        min-idle: 1 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例\n          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例\n          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例\n          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例\n          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例\n          username: root\n          password: 123456\n        #          username: sa # SQL Server 连接的示例\n        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例\n        #          username: SYSDBA # DM 连接的示例\n        #          password: SYSDBA # DM 连接的示例\n        slave: # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 6379 # 端口\n    database: 0 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n# 日志文件配置\nlogging:\n  level:\n    # 配置自己写的 MyBatis Mapper 打印日志\n    cn.iocoder.yudao.module.product.dal.mysql: debug\n    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿：先禁用，Spring Boot 3.X 存在部分错误的 WARN 提示\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  env: # 多环境的配置项\n    tag: ${HOSTNAME}\n  security:\n    mock-enable: true\n  access-log: # 访问日志的配置项\n    enable: false"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: product-server\n\n  profiles:\n    active: local\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n    allow-bean-definition-overriding: true # 允许 Bean 覆盖，例如说 Feign 等会存在重复定义的服务\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  # Servlet 配置\n  servlet:\n    # 文件上传相关配置项\n    multipart:\n      max-file-size: 16MB # 单个文件大小\n      max-request-size: 32MB # 设置总上传的文件大小\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  # Cache 配置项\n  cache:\n    type: REDIS\n    redis:\n      time-to-live: 1h # 设置过期时间为 1 小时\n\nserver:\n  port: 48100\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\n--- #################### 接口文档配置 ####################\n\nspringdoc:\n  api-docs:\n    enabled: true # 1. 是否开启 Swagger 接文档的元数据\n    path: /v3/api-docs\n  swagger-ui:\n    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面\n    path: /swagger-ui\n  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档\n\nknife4j:\n  enable: true\n  setting:\n    language: zh_cn\n\n# MyBatis Plus 的配置项\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: NONE # “智能”模式，基于 IdTypeEnvironmentPostProcessor + 数据源的类型，自动适配成 AUTO、INPUT 模式。\n      #      id-type: AUTO # 自增 ID，适合 MySQL 等直接自增的数据库\n      #      id-type: INPUT # 用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n      #      id-type: ASSIGN_ID # 分配 ID，默认使用雪花算法。注意，Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时，需要去除实体类上的 @KeySequence 注解\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n    banner: false # 关闭控制台的 Banner 打印\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  encryptor:\n    password: XDV71a+xqStEA3WH # 加解密的秘钥，可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成\n\nmybatis-plus-join:\n  banner: false # 关闭控制台的 Banner 打印\n\n# Spring Data Redis 配置\nspring:\n  data:\n    redis:\n      repositories:\n        enabled: false # 项目未使用到 Spring Data Redis 的 Repository，所以直接禁用，保证启动速度\n\n# VO 转换（数据翻译）相关\neasy-trans:\n  is-enable-global: false # 【默认禁用，对性能确认压力大】启用全局翻译（拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置，或通过 @IgnoreTrans 忽略某个接口\n\n--- #################### RPC 远程调用相关配置 ####################\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    executor:\n      appname: ${spring.application.name} # 执行器 AppName\n      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径\n    accessToken: default_token # 执行器通讯TOKEN\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0\n    base-package: cn.iocoder.yudao.module.product\n  web:\n    admin-ui:\n      url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址\n  xss:\n    enable: false\n    exclude-urls: # 如下 url，仅仅是为了演示，去掉配置也没关系\n      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求\n  swagger:\n    title: 管理后台\n    description: 提供管理员管理的所有功能\n    version: ${yudao.info.version}\n  tenant: # 多租户相关配置项\n    enable: true\n    ignore-urls:\n    ignore-tables:\n\ndebug: false\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/test/resources/application-unit-test.yaml",
    "content": "spring:\n  main:\n    lazy-initialization: true # 开启懒加载，加快速度\n    banner-mode: off # 单元测试，禁用 Banner\n\n--- #################### 数据库相关配置 ####################\n\nspring:\n  # 数据源配置项\n  datasource:\n    name: ruoyi-vue-pro\n    url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式；DATABASE_TO_UPPER 配置表和字段使用小写\n    driver-class-name: org.h2.Driver\n    username: sa\n    password:\n    druid:\n      async-init: true # 单元测试，异步初始化 Druid 连接池，提升启动速度\n      initial-size: 1 # 单元测试，配置为 1，提升启动速度\n  sql:\n    init:\n      schema-locations: classpath:/sql/create_tables.sql\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 16379 # 端口（单元测试，使用 16379 端口）\n    database: 0 # 数据库索引\n\nmybatis-plus:\n  lazy-initialization: true # 单元测试，设置 MyBatis Mapper 延迟加载，加速每个单元测试\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n\n--- #################### 定时任务相关配置 ####################\n\n--- #################### 配置中心相关配置 ####################\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项（单元测试，禁用 Lock4j）\n\n--- #################### 监控相关配置 ####################\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  info:\n    base-package: cn.iocoder.yudao.module.product\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/test/resources/logback.xml",
    "content": "<configuration>\n    <!-- 引用 Spring Boot 的 logback 基础配置 -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n</configuration>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/test/resources/sql/clean.sql",
    "content": "DELETE FROM \"product_sku\";\nDELETE FROM \"product_spu\";\nDELETE FROM \"product_category\";\nDELETE FROM \"product_brand\";\nDELETE FROM \"product_property\";\nDELETE FROM \"product_property_value\";\nDELETE FROM \"product_comment\";\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-product-server/src/test/resources/sql/create_tables.sql",
    "content": "CREATE TABLE IF NOT EXISTS `product_sku` (\n    `id` bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    `spu_id` bigint NOT NULL COMMENT 'spu编号',\n    `properties` varchar(512)  DEFAULT NULL COMMENT '属性数组，JSON 格式',\n    `price` int NOT NULL DEFAULT '-1' COMMENT '商品价格，单位：分',\n    `market_price` int DEFAULT NULL COMMENT '市场价，单位：分',\n    `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价，单位： 分',\n    `bar_code` varchar(64)  DEFAULT NULL COMMENT 'SKU 的条形码',\n    `pic_url` varchar(256)  NOT NULL COMMENT '图片地址',\n    `stock` int DEFAULT NULL COMMENT '库存',\n    `weight` double DEFAULT NULL COMMENT '商品重量，单位：kg 千克',\n    `volume` double DEFAULT NULL COMMENT '商品体积，单位：m^3 平米',\n    `sub_commission_first_price` int DEFAULT NULL COMMENT '一级分销的佣金，单位：分',\n    `sub_commission_second_price` int DEFAULT NULL COMMENT '二级分销的佣金，单位：分',\n    `sales_count` int DEFAULT NULL COMMENT '商品销量',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint not null default  '0',\n    PRIMARY KEY(\"id\")\n) COMMENT '商品sku';\n\nCREATE TABLE IF NOT EXISTS `product_spu` (\n    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品 SPU 编号，自增',\n    `name` varchar(128) NOT NULL COMMENT '商品名称',\n    `keyword` varchar(256) NOT NULL COMMENT '关键字',\n    `introduction` varchar(256) NOT NULL COMMENT '商品简介',\n    `description` text NOT NULL COMMENT '商品详情',\n    `bar_code` varchar(64) NOT NULL COMMENT '条形码',\n    `category_id` bigint NOT NULL COMMENT '商品分类编号',\n    `brand_id` int DEFAULT NULL COMMENT '商品品牌编号',\n    `pic_url` varchar(256) NOT NULL COMMENT '商品封面图',\n    `slider_pic_urls` varchar(2000)  DEFAULT '' COMMENT '商品轮播图地址\\n 数组，以逗号分隔\\n 最多上传15张',\n    `video_url` varchar(256) DEFAULT NULL COMMENT '商品视频',\n    `unit` tinyint NOT NULL COMMENT '单位',\n    `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',\n    `status` tinyint NOT NULL COMMENT '商品状态: 0 上架（开启） 1 下架（禁用）-1 回收',\n    `spec_type` bit(1) NOT NULL COMMENT '规格类型：0 单规格 1 多规格',\n    `price` int NOT NULL DEFAULT '-1' COMMENT '商品价格，单位使用：分',\n    `market_price` int NOT NULL COMMENT '市场价，单位使用：分',\n    `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价，单位： 分',\n    `stock` int NOT NULL DEFAULT '0' COMMENT '库存',\n    `delivery_template_id` bigint NOT NULL COMMENT '物流配置模板编号',\n    `recommend_hot` bit(1) NOT NULL COMMENT '是否热卖推荐: 0 默认 1 热卖',\n    `recommend_benefit` bit(1) NOT NULL COMMENT '是否优惠推荐: 0 默认 1 优选',\n    `recommend_best` bit(1) NOT NULL COMMENT '是否精品推荐: 0 默认 1 精品',\n    `recommend_new` bit(1) NOT NULL COMMENT '是否新品推荐: 0 默认 1 新品',\n    `recommend_good` bit(1) NOT NULL COMMENT '是否优品推荐',\n    `give_integral` int NOT NULL COMMENT '赠送积分',\n    `give_coupon_template_ids` varchar(512)  DEFAULT '' COMMENT '赠送的优惠劵编号的数组',\n    `sub_commission_type` bit(1) NOT NULL COMMENT '分销类型',\n    `activity_orders` varchar(16) NOT NULL DEFAULT '' COMMENT '活动显示排序0=默认, 1=秒杀，2=砍价，3=拼团',\n    `sales_count` int DEFAULT '0' COMMENT '商品销量',\n    `virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量',\n    `browse_count` int DEFAULT '0' COMMENT '商品点击量',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint not null default  '0',\n    PRIMARY KEY(\"id\")\n) COMMENT '商品spu';\n\nCREATE TABLE IF NOT EXISTS `product_category` (\n    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号',\n    `parent_id` bigint NOT NULL COMMENT '父分类编号',\n    `name` varchar(255) NOT NULL COMMENT '分类名称',\n    `pic_url` varchar(255) NOT NULL COMMENT '移动端分类图',\n    `big_pic_url` varchar(255) DEFAULT NULL COMMENT 'PC 端分类图',\n    `sort` int DEFAULT '0' COMMENT '分类排序',\n    `status` tinyint NOT NULL COMMENT '开启状态',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint not null default  '0',\n    PRIMARY KEY(\"id\")\n) COMMENT '商品分类';\n\nCREATE TABLE IF NOT EXISTS `product_brand` (\n    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '品牌编号',\n    `name` varchar(255) NOT NULL COMMENT '品牌名称',\n    `pic_url` varchar(255) NOT NULL COMMENT '品牌图片',\n    `sort` int DEFAULT '0' COMMENT '品牌排序',\n    `description` varchar(1024) DEFAULT NULL COMMENT '品牌描述',\n    `status` tinyint NOT NULL COMMENT '状态',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint not null default  '0',\n    PRIMARY KEY(\"id\")\n) COMMENT '商品品牌';\n\nCREATE TABLE IF NOT EXISTS `product_property` (\n    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `name` varchar(64) DEFAULT NULL COMMENT '规格名称',\n    `status` tinyint DEFAULT NULL COMMENT '状态： 0 开启 ，1 禁用',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint not null default  '0',\n    `remark` varchar(255) DEFAULT NULL COMMENT '备注',\n    PRIMARY KEY(\"id\")\n) COMMENT '规格名称';\n\nCREATE TABLE IF NOT EXISTS `product_property_value` (\n    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `property_id` bigint DEFAULT NULL COMMENT '规格键id',\n    `name` varchar(128) DEFAULT NULL COMMENT '规格值名字',\n    `status` tinyint DEFAULT NULL COMMENT '状态： 1 开启 ，2 禁用',\n    \"creator\" varchar(64) DEFAULT '',\n    \"create_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\" varchar(64) DEFAULT '',\n    \"update_time\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"deleted\" bit NOT NULL DEFAULT FALSE,\n    \"tenant_id\" bigint not null default  '0',\n    `remark` varchar(255) DEFAULT NULL COMMENT '备注',\n    PRIMARY KEY(\"id\")\n) COMMENT '规格值';\n\nDROP TABLE IF EXISTS `product_comment` (\n    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '评论编号，主键自增',\n    `user_id` bigint DEFAULT NULL COMMENT '评价人的用户编号关联 MemberUserDO 的 id 编号',\n    `user_nickname` varchar(255) DEFAULT NULL COMMENT '评价人名称',\n    `user_avatar` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '评价人头像',\n    `anonymous` bit(1) DEFAULT NULL COMMENT '是否匿名',\n    `order_id` bigint DEFAULT NULL COMMENT '交易订单编号关联 TradeOrderDO 的 id 编号',\n    `order_item_id` bigint DEFAULT NULL COMMENT '交易订单项编号关联 TradeOrderItemDO 的 id 编号',\n    `spu_id` bigint DEFAULT NULL COMMENT '商品 SPU 编号关联 ProductSpuDO 的 id',\n    `spu_name` varchar(255) DEFAULT NULL COMMENT '商品 SPU 名称',\n    `sku_id` bigint DEFAULT NULL COMMENT '商品 SKU 编号关联 ProductSkuDO 的 id 编号',\n    `visible` bit(1) DEFAULT NULL COMMENT '是否可见true:显示false:隐藏',\n    `scores` tinyint DEFAULT NULL COMMENT '评分星级1-5分',\n    `description_scores` tinyint DEFAULT NULL COMMENT '描述星级1-5 星',\n    `benefit_scores` tinyint DEFAULT NULL COMMENT '服务星级1-5 星',\n    `content` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '评论内容',\n    `pic_urls` varchar(4096) DEFAULT NULL COMMENT '评论图片地址数组',\n    `reply_status` bit(1) DEFAULT NULL COMMENT '商家是否回复',\n    `reply_user_id` bigint DEFAULT NULL COMMENT '回复管理员编号关联 AdminUserDO 的 id 编号',\n    `reply_content` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '商家回复内容',\n    `reply_time` datetime DEFAULT NULL COMMENT '商家回复时间',\n    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',\n    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',\n    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n    `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',\n    PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 26 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '商品评论';"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-mall</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-promotion-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        promotion 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springdoc</groupId>\n            <artifactId>springdoc-openapi-ui</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApi.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 砍价活动\")\npublic interface BargainActivityApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/bargain-activity\";\n\n    @PutMapping(PREFIX + \"/update-stock\")\n    @Operation(summary = \"更新砍价活动库存\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"砍价活动编号\", required = true, example = \"1024\"),\n            @Parameter(name = \"count\", description = \"购买数量\", required = true, example = \"1\"),\n    })\n    CommonResult<Boolean> updateBargainActivityStock(@RequestParam(\"id\") Long id,\n                                                     @RequestParam(\"count\") Integer count);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainRecordApi.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.bargain.dto.BargainValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 砍价记录\")\npublic interface BargainRecordApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/bargain-record\";\n\n    @GetMapping(PREFIX + \"/validate-join\")\n    @Operation(summary = \"【下单前】校验是否参与砍价活动\") // 如果校验失败，则抛出业务异常\n    @Parameters({\n            @Parameter(name = \"userId\", description = \"用户编号\", required = true, example = \"1024\"),\n            @Parameter(name = \"bargainRecordId\", description = \"砍价记录编号\", required = true, example = \"2048\"),\n            @Parameter(name = \"skuId\", description = \"SKU 编号\", required = true, example = \"4096\"),\n    })\n    CommonResult<BargainValidateJoinRespDTO> validateJoinBargain(@RequestParam(\"userId\") Long userId,\n                                                                 @RequestParam(\"bargainRecordId\") Long bargainRecordId,\n                                                                 @RequestParam(\"skuId\") Long skuId);\n\n    @PutMapping(PREFIX + \"/update-order-id\")\n    @Operation(summary = \"更新砍价记录的订单编号\") // 在砍价成功后，用户发起订单后，会记录该订单编号\n    @Parameters({\n            @Parameter(name = \"id\", description = \"砍价记录编号\", required = true, example = \"1024\"),\n            @Parameter(name = \"orderId\", description = \"订单编号\", required = true, example = \"2048\"),\n    })\n    CommonResult<Boolean> updateBargainRecordOrderId(@RequestParam(\"id\") Long id,\n                                                     @RequestParam(\"oderId\") Long orderId);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/dto/BargainValidateJoinRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.bargain.dto;\n\nimport lombok.Data;\n\n/**\n * 校验参与砍价 Response DTO\n */\n@Data\npublic class BargainValidateJoinRespDTO {\n\n    /**\n     * 砍价活动编号\n     */\n    private Long activityId;\n    /**\n     * 砍价活动名称\n     */\n    private String name;\n\n    /**\n     * 砍价金额\n     */\n    private Integer bargainPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.combination;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport javax.validation.Valid;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 拼团记录\")\npublic interface CombinationRecordApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/combination-record\";\n\n    @GetMapping(PREFIX + \"/validate\")\n    @Operation(summary = \"校验是否满足拼团条件\")\n    @Parameters({\n            @Parameter(name = \"userId\", description = \"用户编号\", required = true, example = \"1024\"),\n            @Parameter(name = \"activityId\", description = \"活动编号\", required = true, example = \"2048\"),\n            @Parameter(name = \"headId\", description = \"团长编号\", required = true, example = \"4096\"),\n            @Parameter(name = \"skuId\", description = \"SKU 编号\", required = true, example = \"8192\"),\n            @Parameter(name = \"count\", description = \"数量\", required = true, example = \"1\"),\n    })\n    CommonResult<Boolean> validateCombinationRecord(@RequestParam(\"userId\") Long userId,\n                                                    @RequestParam(\"activityId\") Long activityId,\n                                                    @RequestParam(\"headId\") Long headId,\n                                                    @RequestParam(\"skuId\") Long skuId,\n                                                    @RequestParam(\"count\") Integer count);\n\n    @PostMapping(PREFIX + \"/create\")\n    @Operation(summary = \"创建开团记录\")\n    CommonResult<CombinationRecordCreateRespDTO> createCombinationRecord(\n            @RequestBody @Valid CombinationRecordCreateReqDTO reqDTO);\n\n    @GetMapping(PREFIX + \"/get-by-order-id\")\n    @Operation(summary = \"基于订单编号，查询拼团记录\")\n    @Parameters({\n            @Parameter(name = \"userId\", description = \"用户编号\", required = true, example = \"1024\"),\n            @Parameter(name = \"orderId\", description = \"订单编号\", required = true, example = \"2048\"),\n    })\n    CommonResult<CombinationRecordRespDTO> getCombinationRecordByOrderId(@RequestParam(\"userId\") Long userId,\n                                                                         @RequestParam(\"orderId\") Long orderId);\n\n    @GetMapping(PREFIX + \"/validate-join\")\n    @Operation(summary = \"【下单前】校验是否满足拼团活动条件\") // 如果校验失败，则抛出业务异常\n    @Parameters({\n            @Parameter(name = \"userId\", description = \"用户编号\", required = true, example = \"1024\"),\n            @Parameter(name = \"activityId\", description = \"活动编号\", required = true, example = \"2048\"),\n            @Parameter(name = \"headId\", description = \"团长编号\", example = \"4096\"), // 如果新发起的团，headId 不用传递\n            @Parameter(name = \"skuId\", description = \"SKU 编号\", required = true, example = \"8192\"),\n            @Parameter(name = \"count\", description = \"数量\", required = true, example = \"1\"),\n    })\n    CommonResult<CombinationValidateJoinRespDTO> validateJoinCombination(@RequestParam(\"userId\") Long userId,\n                                                                         @RequestParam(\"activityId\") Long activityId,\n                                                                         @RequestParam(value = \"headId\", required = false) Long headId,\n                                                                         @RequestParam(\"skuId\") Long skuId,\n                                                                         @RequestParam(\"count\") Integer count);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordCreateReqDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.combination.dto;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 拼团记录的创建 Request DTO\n *\n * @author HUIHUI\n */\n@Data\npublic class CombinationRecordCreateReqDTO {\n\n    /**\n     * 拼团活动编号\n     */\n    @NotNull(message = \"拼团活动编号不能为空\")\n    private Long activityId;\n    /**\n     * spu 编号\n     */\n    @NotNull(message = \"spu 编号不能为空\")\n    private Long spuId;\n    /**\n     * sku 编号\n     */\n    @NotNull(message = \"sku 编号不能为空\")\n    private Long skuId;\n    /**\n     * 购买的商品数量\n     */\n    @NotNull(message = \"购买数量不能为空\")\n    private Integer count;\n    /**\n     * 订单编号\n     */\n    @NotNull(message = \"订单编号不能为空\")\n    private Long orderId;\n    /**\n     * 用户编号\n     */\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n    /**\n     * 团长编号\n     */\n    private Long headId;\n    /**\n     * 拼团商品单价\n     */\n    @NotNull(message = \"拼团商品单价不能为空\")\n    private Integer combinationPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordCreateRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.combination.dto;\n\nimport lombok.Data;\n\n/**\n * 拼团记录的创建 Response DTO\n *\n * @author HUIHUI\n */\n@Data\npublic class CombinationRecordCreateRespDTO {\n\n    /**\n     * 拼团活动编号\n     *\n     * 关联 CombinationActivityDO 的 id 字段\n     */\n    private Long combinationActivityId;\n    /**\n     * 拼团团长编号\n     *\n     * 关联 CombinationRecordDO 的 headId 字段\n     */\n    private Long combinationHeadId;\n    /**\n     * 拼团记录编号\n     *\n     * 关联 CombinationRecordDO 的 id 字段\n     */\n    private Long combinationRecordId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.combination.dto;\n\nimport cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n/**\n * 拼团记录 Response DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class CombinationRecordRespDTO {\n\n    /**\n     * 编号，主键自增\n     */\n    private Long id;\n\n    /**\n     * 拼团活动编号\n     *\n     * 关联 CombinationActivityDO 的 id 字段\n     */\n    private Long activityId;\n    /**\n     * 拼团商品单价\n     *\n     * 冗余 CombinationProductDO 的 combinationPrice 字段\n     */\n    private Integer combinationPrice;\n    /**\n     * SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 商品名字\n     */\n    private String spuName;\n    /**\n     * 商品图片\n     */\n    private String picUrl;\n    /**\n     * SKU 编号\n     */\n    private Long skuId;\n    /**\n     * 购买的商品数量\n     */\n    private Integer count;\n\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 用户昵称\n     */\n    private String nickname;\n    /**\n     * 用户头像\n     */\n    private String avatar;\n\n    /**\n     * 团长编号\n     */\n    private Long headId;\n    /**\n     * 开团状态\n     *\n     * 关联 {@link CombinationRecordStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 订单编号\n     */\n    private Long orderId;\n    /**\n     * 开团需要人数\n     *\n     * 关联 CombinationActivityDO 的 userSize 字段\n     */\n    private Integer userSize;\n    /**\n     * 已加入拼团人数\n     */\n    private Integer userCount;\n    /**\n     * 是否虚拟成团\n     */\n    private Boolean virtualGroup;\n\n    /**\n     * 过期时间\n     */\n    private LocalDateTime expireTime;\n    /**\n     * 开始时间 (订单付款后开始的时间)\n     */\n    private LocalDateTime startTime;\n    /**\n     * 结束时间（成团时间/失败时间）\n     */\n    private LocalDateTime endTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationValidateJoinRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.combination.dto;\n\nimport lombok.Data;\n\n/**\n * 校验参与拼团 Response DTO\n *\n * @author HUIHUI\n */\n@Data\npublic class CombinationValidateJoinRespDTO {\n\n    /**\n     * 砍价活动编号\n     */\n    private Long activityId;\n    /**\n     * 砍价活动名称\n     */\n    private String name;\n\n    /**\n     * 拼团金额\n     */\n    private Integer combinationPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.coupon;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;\nimport cn.iocoder.yudao.module.promotion.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 优惠劵\")\npublic interface CouponApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/coupon\";\n\n    @GetMapping(PREFIX + \"/list-by-user-id\")\n    @Operation(summary = \"获得用户的优惠劵列表\")\n    CommonResult<List<CouponRespDTO>> getCouponListByUserId(@RequestParam(\"userId\") Long userId,\n                                                            @RequestParam(\"status\") Integer status);\n\n    @PutMapping(PREFIX + \"/use\")\n    @Operation(summary = \"使用优惠劵\")\n    CommonResult<Boolean> useCoupon(@RequestBody @Valid CouponUseReqDTO useReqDTO);\n\n    @PutMapping(PREFIX + \"/return-used\")\n    @Operation(summary = \"退还已使用的优惠券\")\n    @Parameter(name = \"id\", description = \"优惠券编号\", required = true, example = \"1\")\n    CommonResult<Boolean> returnUsedCoupon(@RequestParam(\"id\") Long id);\n\n    @PostMapping(PREFIX + \"/take-by-admin\")\n    @Operation(summary = \"【管理员】给指定用户批量发送优惠券\") // 返回：优惠券编号列表\n    @Parameters({\n            @Parameter(name = \"giveCoupons\", description = \"key: 优惠劵模版编号，value：对应的数量\", required = true),\n            @Parameter(name = \"userId\", description = \"用户编号\", required = true)\n    })\n    CommonResult<List<Long>> takeCouponsByAdmin(@RequestBody Map<Long, Integer> giveCoupons,\n                                                @RequestParam(\"userId\") Long userId);\n\n    @PostMapping(PREFIX + \"/invalidate-by-admin\")\n    @Operation(summary = \"【管理员】作废指定用户的指定优惠劵\")\n    @Parameters({\n            @Parameter(name = \"giveCouponIds\", description = \"赠送的优惠券编号\", required = true),\n            @Parameter(name = \"userId\", description = \"用户编号\", required = true)\n    })\n    CommonResult<Boolean> invalidateCouponsByAdmin(@RequestParam(\"giveCouponIds\") List<Long> giveCouponIds,\n                                                   @RequestParam(\"userId\") Long userId);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.coupon.dto;\n\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 优惠劵 Response DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class CouponRespDTO {\n\n    // ========== 基本信息 BEGIN ==========\n    /**\n     * 优惠劵编号\n     */\n    private Long id;\n    /**\n     * 优惠劵模板编号\n     */\n    private Integer templateId;\n    /**\n     * 优惠劵名\n     */\n    private String name;\n    /**\n     * 优惠码状态\n     *\n     * 枚举 {@link CouponStatusEnum}\n     */\n    private Integer status;\n\n    // ========== 基本信息 END ==========\n\n    // ========== 领取情况 BEGIN ==========\n    /**\n     * 用户编号\n     *\n     * 关联 MemberUserDO 的 id 字段\n     */\n    private Long userId;\n    /**\n     * 领取类型\n     *\n     * 枚举 {@link CouponTakeTypeEnum}\n     */\n    private Integer takeType;\n    // ========== 领取情况 END ==========\n\n    // ========== 使用规则 BEGIN ==========\n    /**\n     * 是否设置满多少金额可用，单位：分\n     */\n    private Integer usePrice;\n    /**\n     * 生效开始时间\n     */\n    private LocalDateTime validStartTime;\n    /**\n     * 生效结束时间\n     */\n    private LocalDateTime validEndTime;\n    /**\n     * 商品范围\n     */\n    private Integer productScope;\n    /**\n     * 商品范围编号的数组\n     */\n    private List<Long> productScopeValues;\n    // ========== 使用规则 END ==========\n\n    // ========== 使用效果 BEGIN ==========\n    /**\n     * 折扣类型\n     */\n    private Integer discountType;\n    /**\n     * 折扣百分比\n     */\n    private Integer discountPercent;\n    /**\n     * 优惠金额，单位：分\n     */\n    private Integer discountPrice;\n    /**\n     * 折扣上限，仅在 {@link #discountType} 等于 {@link PromotionDiscountTypeEnum#PERCENT} 时生效\n     */\n    private Integer discountLimitPrice;\n    // ========== 使用效果 END ==========\n\n    // ========== 使用情况 BEGIN ==========\n    /**\n     * 使用订单号\n     */\n    private Long useOrderId;\n    /**\n     * 使用时间\n     */\n    private LocalDateTime useTime;\n\n    // ========== 使用情况 END ==========\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponTemplateRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.coupon.dto;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport lombok.Data;\n\n/**\n * 优惠券模版 Response DTO\n *\n * @author HUIHUI\n */\n@Data\npublic class CouponTemplateRespDTO {\n    /**\n     * 模板编号，自增唯一\n     */\n\n    private Long id;\n    /**\n     * 优惠劵名\n     */\n    private String name;\n\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponUseReqDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.coupon.dto;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 优惠劵使用 Request DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class CouponUseReqDTO {\n\n    /**\n     * 优惠劵编号\n     */\n    @NotNull(message = \"优惠劵编号不能为空\")\n    private Long id;\n\n    /**\n     * 用户编号\n     */\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    /**\n     * 订单编号\n     */\n    @NotNull(message = \"订单编号不能为空\")\n    private Long orderId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponValidReqDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.coupon.dto;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 优惠劵使用 Request DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class CouponValidReqDTO {\n\n    /**\n     * 优惠劵编号\n     */\n    @NotNull(message = \"优惠劵编号不能为空\")\n    private Long id;\n\n    /**\n     * 用户编号\n     */\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApi.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.discount;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;\nimport cn.iocoder.yudao.module.promotion.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.Collection;\nimport java.util.List;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 限时折扣\")\npublic interface DiscountActivityApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/discount-activity\";\n\n    @GetMapping(PREFIX + \"/list-by-sku-id\")\n    @Operation(summary = \"获得商品匹配的的限时折扣信息\")\n    @Parameter(name = \"skuIds\", description = \"商品 SKU 编号数组\", required = true, example = \"[1, 2]\")\n    CommonResult<List<DiscountProductRespDTO>> getMatchDiscountProductListBySkuIds(@RequestParam(\"skuIds\") Collection<Long> skuIds);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/dto/DiscountProductRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.discount.dto;\n\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n/**\n * 限时折扣活动商品 Response DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class DiscountProductRespDTO {\n\n    /**\n     * 编号，主键自增\n     */\n    private Long id;\n    /**\n     * 商品 SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 商品 SKU 编号\n     */\n    private Long skuId;\n    /**\n     * 折扣类型\n     */\n    private Integer discountType;\n    /**\n     * 折扣百分比\n     */\n    private Integer discountPercent;\n    /**\n     * 优惠金额，单位：分\n     */\n    private Integer discountPrice;\n\n    // ========== 活动字段 ==========\n    /**\n     * 限时折扣活动的编号\n     */\n    private Long activityId;\n    /**\n     * 活动标题\n     */\n    private String activityName;\n    /**\n     * 活动开始时间点\n     */\n    private LocalDateTime activityStartTime;\n    /**\n     * 活动结束时间点\n     */\n    private LocalDateTime activityEndTime;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApi.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.point;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 秒杀活动\")\npublic interface PointActivityApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/point-activity\";\n\n    @GetMapping(PREFIX + \"/validate-join\")\n    @Operation(summary = \"【下单前】校验是否参与积分商城活动\")\n    @Parameters({\n            @Parameter(name = \"activityId\", description = \"活动编号\", required = true, example = \"1\"),\n            @Parameter(name = \"skuId\", description = \"SKU 编号\", required = true, example = \"2\"),\n            @Parameter(name = \"count\", description = \"数量\", required = true, example = \"3\"),\n    })\n    CommonResult<PointValidateJoinRespDTO> validateJoinPointActivity(@RequestParam(\"activityId\") Long activityId,\n                                                                    @RequestParam(\"skuId\") Long skuId,\n                                                                    @RequestParam(\"count\")Integer count);\n\n    @PutMapping(PREFIX + \"/update-stock-decr\")\n    @Operation(summary = \"更新积分商品库存（减少）\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"活动编号\", required = true, example = \"1\"),\n            @Parameter(name = \"skuId\", description = \"SKU 编号\", required = true, example = \"2\"),\n            @Parameter(name = \"count\", description = \"数量\", required = true, example = \"3\"),\n    })\n    CommonResult<Boolean> updatePointStockDecr(@RequestParam(\"id\") Long id,\n                                               @RequestParam(\"skuId\") Long skuId,\n                                               @RequestParam(\"count\")Integer count);\n\n    @PutMapping(PREFIX + \"/update-stock-incr\")\n    @Operation(summary = \"更新积分商城商品库存（增加）\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"活动编号\", required = true, example = \"1\"),\n            @Parameter(name = \"skuId\", description = \"SKU 编号\", required = true, example = \"2\"),\n            @Parameter(name = \"count\", description = \"数量\", required = true, example = \"3\"),\n    })\n    CommonResult<Boolean> updatePointStockIncr(@RequestParam(\"id\") Long id,\n                                               @RequestParam(\"skuId\") Long skuId,\n                                               @RequestParam(\"count\")Integer count);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/dto/PointValidateJoinRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.point.dto;\n\nimport lombok.Data;\n\n/**\n * 校验参与积分商城 Response DTO\n */\n@Data\npublic class PointValidateJoinRespDTO {\n\n    /**\n     * 可兑换次数\n     */\n    private Integer count;\n    /**\n     * 所需兑换积分\n     */\n    private Integer point;\n    /**\n     * 所需兑换金额，单位：分\n     */\n    private Integer price;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApi.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.reward;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;\nimport cn.iocoder.yudao.module.promotion.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.Collection;\nimport java.util.List;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 满减送\")\npublic interface RewardActivityApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/reward-activity\";\n\n    @GetMapping(PREFIX + \"/list-by-spu-id\")\n    @Operation(summary = \"获得商品匹配的的满减送活动信息\")\n    @Parameter(name = \"spuIds\", description = \"商品 SPU 编号数组\", required = true, example = \"[1, 2]\")\n    CommonResult<List<RewardActivityMatchRespDTO>> getMatchRewardActivityListBySpuIds(@RequestParam(\"spuIds\") Collection<Long> spuIds);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/dto/RewardActivityMatchRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.reward.dto;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 满减送活动的匹配 Response DTO\n *\n * @author 芋道源码\n */\n@Data\npublic class RewardActivityMatchRespDTO {\n\n    /**\n     * 匹配的 SPU 数组\n     */\n    private List<Long> spuIds;\n\n    /**\n     * 活动编号，主键自增\n     */\n    private Long id;\n    /**\n     * 活动标题\n     */\n    private String name;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 开始时间\n     */\n    private LocalDateTime startTime;\n    /**\n     * 结束时间\n     */\n    private LocalDateTime endTime;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 条件类型\n     *\n     * 枚举 {@link PromotionConditionTypeEnum}\n     */\n    private Integer conditionType;\n    /**\n     * 商品范围\n     *\n     * 枚举 {@link PromotionProductScopeEnum}\n     */\n    private Integer productScope;\n    /**\n     * 商品 SPU 编号的数组\n     */\n    private List<Long> productScopeValues;\n    /**\n     * 优惠规则的数组\n     */\n    private List<Rule> rules;\n\n    /**\n     * 优惠规则\n     */\n    @Data\n    public static class Rule implements Serializable {\n\n        /**\n         * 优惠门槛\n         *\n         * 1. 满 N 元，单位：分\n         * 2. 满 N 件\n         */\n        private Integer limit;\n        /**\n         * 优惠价格，单位：分\n         */\n        private Integer discountPrice;\n        /**\n         * 是否包邮\n         */\n        private Boolean freeDelivery;\n        /**\n         * 赠送的积分\n         */\n        private Integer point;\n        /**\n         * 赠送的优惠劵\n         *\n         * key: 优惠劵模版编号\n         * value：对应的优惠券数量\n         *\n         * 目的：用于订单支付后赠送优惠券\n         */\n        private Map<Long, Integer> giveCouponTemplateCounts;\n\n        /**\n         * 规则描述\n         *\n         * 通过 {@link #limit}、{@link #discountPrice} 等字段进行拼接\n         */\n        private String description;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApi.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.seckill;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 秒杀活动\")\npublic interface SeckillActivityApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/discount-activity\";\n\n    @PutMapping(PREFIX + \"/update-stock-decr\")\n    @Operation(summary = \"更新秒杀库存（减少）\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"活动编号\", required = true, example = \"1\"),\n            @Parameter(name = \"skuId\", description = \"SKU 编号\", required = true, example = \"2\"),\n            @Parameter(name = \"count\", description = \"数量\", required = true, example = \"3\"),\n    })\n    CommonResult<Boolean> updateSeckillStockDecr(@RequestParam(\"id\") Long id,\n                                                 @RequestParam(\"skuId\") Long skuId,\n                                                 @RequestParam(\"count\")Integer count);\n\n    @PutMapping(PREFIX + \"/update-stock-incr\")\n    @Operation(summary = \"更新秒杀库存（增加）\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"活动编号\", required = true, example = \"1\"),\n            @Parameter(name = \"skuId\", description = \"SKU 编号\", required = true, example = \"2\"),\n            @Parameter(name = \"count\", description = \"数量\", required = true, example = \"3\"),\n    })\n    CommonResult<Boolean> updateSeckillStockIncr(@RequestParam(\"id\") Long id,\n                                                 @RequestParam(\"skuId\") Long skuId,\n                                                 @RequestParam(\"count\")Integer count);\n\n    @GetMapping(PREFIX + \"/validate-join\")\n    @Operation(summary = \"【下单前】校验是否参与秒杀活动\") // 如果校验失败，则抛出业务异常\n    @Parameters({\n            @Parameter(name = \"activityId\", description = \"活动编号\", required = true, example = \"1\"),\n            @Parameter(name = \"skuId\", description = \"SKU 编号\", required = true, example = \"2\"),\n            @Parameter(name = \"count\", description = \"数量\", required = true, example = \"3\"),\n    })\n    CommonResult<SeckillValidateJoinRespDTO> validateJoinSeckill(@RequestParam(\"activityId\") Long activityId,\n                                                                 @RequestParam(\"skuId\") Long skuId,\n                                                                 @RequestParam(\"count\")Integer count);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/dto/SeckillValidateJoinRespDTO.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.seckill.dto;\n\nimport lombok.Data;\n\n/**\n * 校验参与秒杀 Response DTO\n */\n@Data\npublic class SeckillValidateJoinRespDTO {\n\n    /**\n     * 秒杀活动名称\n     */\n    private String name;\n    /**\n     * 总限购数量\n     *\n     * 目的：目前只有 trade 有具体下单的数据，需要交给 trade 价格计算使用\n     */\n    private Integer totalLimitCount;\n\n    /**\n     * 秒杀金额\n     */\n    private Integer seckillPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ApiConstants.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\n\n/**\n * API 相关的枚举\n *\n * @author 芋道源码\n */\npublic class ApiConstants {\n\n    /**\n     * 服务名\n     *\n     * 注意，需要保证和 spring.application.name 保持一致\n     */\n    public static final String NAME = \"promotion-server\";\n\n    public static final String PREFIX = RpcConstants.RPC_API_PREFIX + \"/promotion\";\n\n    public static final String VERSION = \"1.0.0\";\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/DictTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums;\n\n/**\n * promotion 字典类型的枚举类\n *\n * @author HUIHUI\n */\npublic class DictTypeConstants {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * Promotion 错误码枚举类\n * <p>\n * promotion 系统，使用 1-013-000-000 段\n */\npublic interface ErrorCodeConstants {\n\n    // ========== 促销活动相关 1-013-001-000 ============\n    ErrorCode DISCOUNT_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_001_000, \"限时折扣活动不存在\");\n    ErrorCode DISCOUNT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_001_001, \"存在商品参加了其它限时折扣活动【{}】\");\n    ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_002, \"限时折扣活动已关闭，不能修改\");\n    ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_001_003, \"限时折扣活动未关闭，不能删除\");\n    ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_004, \"限时折扣活动已关闭，不能重复关闭\");\n\n    // ========== Banner 相关 1-013-002-000 ============\n    ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, \"Banner 不存在\");\n\n    // ========== Coupon 相关 1-013-003-000 ============\n\n    // ========== 优惠劵模板 1-013-004-000 ==========\n    ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1_013_004_000, \"优惠劵模板不存在\");\n    ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1_013_004_001, \"发放数量不能小于已领取数量({})\");\n    ErrorCode COUPON_TEMPLATE_NOT_ENOUGH = new ErrorCode(1_013_004_002, \"当前剩余数量不够领取\");\n    ErrorCode COUPON_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1_013_004_003, \"用户已领取过此优惠券\");\n    ErrorCode COUPON_TEMPLATE_EXPIRED = new ErrorCode(1_013_004_004, \"优惠券已过期\");\n    ErrorCode COUPON_TEMPLATE_CANNOT_TAKE = new ErrorCode(1_013_004_005, \"领取方式不正确\");\n\n    // ========== 优惠劵 1-013-005-000 ==========\n    ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1_013_005_000, \"优惠券不存在\");\n    ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1_013_005_001, \"回收优惠劵失败，优惠劵已被使用\");\n    ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1_013_005_002, \"优惠劵不处于待使用状态\");\n    ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1_013_005_003, \"优惠券不在使用时间范围内\");\n    ErrorCode COUPON_STATUS_NOT_USED = new ErrorCode(1_013_005_004, \"优惠劵不是已使用状态\");\n\n    // ========== 满减送活动 1-013-006-000 ==========\n    ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_006_000, \"满减送活动不存在\");\n    ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_006_001, \"该时间段存在商品参加了其它满减送活动\");\n    ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_002, \"满减送活动已关闭，不能修改\");\n    ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_006_003, \"满减送活动未关闭，不能删除\");\n    ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_004, \"满减送活动已关闭，不能重复关闭\");\n    ErrorCode REWARD_ACTIVITY_SCOPE_EXISTS = new ErrorCode(1_013_006_005, \"与该时间段满减送活动【{}】商品范围冲突，原因：{}\");\n\n    // ========== 积分商城活动 1-013-007-000 ==========\n    ErrorCode POINT_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_007_000, \"积分商城活动不存在\");\n    ErrorCode POINT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_007_001, \"存在商品参加了其它积分商城活动\");\n    ErrorCode POINT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_007_002, \"积分商城活动已关闭，不能修改\");\n    ErrorCode POINT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1_013_007_003, \"积分商城活动未关闭或未结束，不能删除\");\n    ErrorCode POINT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_007_004, \"积分商城活动已关闭，不能重复关闭\");\n    ErrorCode POINT_ACTIVITY_JOIN_ACTIVITY_STATUS_CLOSED = new ErrorCode(1_013_007_005, \"积分商品兑换失败，原因：积分商城活动已关闭\");\n    ErrorCode POINT_ACTIVITY_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_007_006, \"积分商品兑换失败，原因：单次限购超出\");\n    ErrorCode POINT_ACTIVITY_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS = new ErrorCode(1_013_007_007, \"积分商品兑换失败，原因：商品不存在\");\n    ErrorCode POINT_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1_013_007_008, \"积分商品兑换失败，原因：积分商品库存不足\");\n\n    // ========== 秒杀活动 1-013-008-000 ==========\n    ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_008_000, \"秒杀活动不存在\");\n    ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_008_002, \"存在商品参加了其它秒杀活动，秒杀时段冲突\");\n    ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_008_003, \"秒杀活动已关闭，不能修改\");\n    ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1_013_008_004, \"秒杀活动未关闭或未结束，不能删除\");\n    ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_008_005, \"秒杀活动已关闭，不能重复关闭\");\n    ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1_013_008_006, \"秒杀失败，原因：秒杀库存不足\");\n    ErrorCode SECKILL_JOIN_ACTIVITY_TIME_ERROR = new ErrorCode(1_013_008_007, \"秒杀失败，原因：不在活动时间范围内\");\n    ErrorCode SECKILL_JOIN_ACTIVITY_STATUS_CLOSED = new ErrorCode(1_013_008_008, \"秒杀失败，原因：秒杀活动已关闭\");\n    ErrorCode SECKILL_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_008_009, \"秒杀失败，原因：单次限购超出\");\n    ErrorCode SECKILL_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS = new ErrorCode(1_013_008_010, \"秒杀失败，原因：商品不存在\");\n\n    // ========== 秒杀时段 1-013-009-000 ==========\n    ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1_013_009_000, \"秒杀时段不存在\");\n    ErrorCode SECKILL_CONFIG_TIME_CONFLICTS = new ErrorCode(1_013_009_001, \"秒杀时段冲突\");\n    ErrorCode SECKILL_CONFIG_DISABLE = new ErrorCode(1_013_009_004, \"秒杀时段已关闭\");\n\n    // ========== 拼团活动 1-013-010-000 ==========\n    ErrorCode COMBINATION_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_010_000, \"拼团活动不存在\");\n    ErrorCode COMBINATION_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_010_001, \"存在商品参加了其它拼团活动\");\n    ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE_NOT_UPDATE = new ErrorCode(1_013_010_002, \"拼团活动已关闭不能修改\");\n    ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1_013_010_003, \"拼团活动未关闭或未结束，不能删除\");\n    ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1_013_010_004, \"拼团失败，原因：拼团活动已关闭\");\n    ErrorCode COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS = new ErrorCode(1_013_010_005, \"拼团失败，原因：拼团活动商品不存在\");\n    ErrorCode COMBINATION_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1_013_010_006, \"拼团失败，原因：拼团活动商品库存不足\");\n\n    // ========== 拼团记录 1-013-011-000 ==========\n    ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1_013_011_000, \"拼团不存在\");\n    ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1_013_011_001, \"拼团失败，已参与过该拼团\");\n    ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1_013_011_002, \"拼团失败，父拼团不存在\");\n    ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1_013_011_003, \"拼团失败，拼团人数已满\");\n    ErrorCode COMBINATION_RECORD_FAILED_HAVE_JOINED = new ErrorCode(1_013_011_004, \"拼团失败，原因：存在该活动正在进行的拼团记录\");\n    ErrorCode COMBINATION_RECORD_FAILED_TIME_NOT_START = new ErrorCode(1_013_011_005, \"拼团失败，活动未开始\");\n    ErrorCode COMBINATION_RECORD_FAILED_TIME_END = new ErrorCode(1_013_011_006, \"拼团失败，活动已经结束\");\n    ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_007, \"拼团失败，原因：单次限购超出\");\n    ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_008, \"拼团失败，原因：超出总购买次数\");\n    ErrorCode COMBINATION_RECORD_FAILED_ORDER_STATUS_UNPAID = new ErrorCode(1_013_011_009, \"拼团失败，原因：存在未支付订单，请先支付\");\n\n    // ========== 砍价活动 1-013-012-000 ==========\n    ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_012_000, \"砍价活动不存在\");\n    ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_012_001, \"存在商品参加了其它砍价活动\");\n    ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1_013_012_002, \"砍价活动已关闭，不能修改\");\n    ErrorCode BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1_013_012_003, \"砍价活动未关闭或未结束，不能删除\");\n    ErrorCode BARGAIN_ACTIVITY_STOCK_NOT_ENOUGH = new ErrorCode(1_013_012_004, \"砍价活动库存不足\");\n    ErrorCode BARGAIN_ACTIVITY_STATUS_CLOSED = new ErrorCode(1_013_012_005, \"砍价活动已关闭\");\n    ErrorCode BARGAIN_ACTIVITY_TIME_END = new ErrorCode(1_013_012_006, \"砍价活动已经结束\");\n\n    // ========== 砍价记录 1-013-013-000 ==========\n    ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1_013_013_000, \"砍价记录不存在\");\n    ErrorCode BARGAIN_RECORD_CREATE_FAIL_EXISTS = new ErrorCode(1_013_013_001, \"参与失败，您已经参与当前砍价活动\");\n    ErrorCode BARGAIN_RECORD_CREATE_FAIL_LIMIT = new ErrorCode(1_013_013_002, \"参与失败，您已达到当前砍价活动的参与上限\");\n    ErrorCode BARGAIN_JOIN_RECORD_NOT_SUCCESS = new ErrorCode(1_013_013_004, \"下单失败，砍价未成功\");\n    ErrorCode BARGAIN_JOIN_RECORD_ALREADY_ORDER = new ErrorCode(1_013_013_005, \"下单失败，该砍价已经下单\");\n\n    // ========== 砍价助力 1-013-014-000 ==========\n    ErrorCode BARGAIN_HELP_CREATE_FAIL_RECORD_NOT_IN_PROCESS = new ErrorCode(1_013_014_000, \"助力失败，砍价记录不处于进行中\");\n    ErrorCode BARGAIN_HELP_CREATE_FAIL_RECORD_SELF = new ErrorCode(1_013_014_001, \"助力失败，不能助力自己\");\n    ErrorCode BARGAIN_HELP_CREATE_FAIL_LIMIT = new ErrorCode(1_013_014_002, \"助力失败，您已达到当前砍价活动的助力上限\");\n    ErrorCode BARGAIN_HELP_CREATE_FAIL_CONFLICT = new ErrorCode(1_013_014_003, \"助力失败，请重试\");\n    ErrorCode BARGAIN_HELP_CREATE_FAIL_HELP_EXISTS = new ErrorCode(1_013_014_004, \"助力失败，您已经助力过了\");\n\n    // ========== 文章分类 1-013-015-000 ==========\n    ErrorCode ARTICLE_CATEGORY_NOT_EXISTS = new ErrorCode(1_013_015_000, \"文章分类不存在\");\n    ErrorCode ARTICLE_CATEGORY_DELETE_FAIL_HAVE_ARTICLES = new ErrorCode(1_013_015_001, \"文章分类删除失败，存在关联文章\");\n\n    // ========== 文章管理 1-013-016-000 ==========\n    ErrorCode ARTICLE_NOT_EXISTS = new ErrorCode(1_013_016_000, \"文章不存在\");\n\n    // ========== 装修模板 1-013-017-000 ==========\n    ErrorCode DIY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_013_017_000, \"装修模板不存在\");\n    ErrorCode DIY_TEMPLATE_NAME_USED = new ErrorCode(1_013_017_001, \"装修模板名称({})已经被使用\");\n    ErrorCode DIY_TEMPLATE_USED_CANNOT_DELETE = new ErrorCode(1_013_017_002, \"不能删除正在使用的装修模板\");\n\n    // ========== 装修页面 1-013-018-000 ==========\n    ErrorCode DIY_PAGE_NOT_EXISTS = new ErrorCode(1_013_018_000, \"装修页面不存在\");\n    ErrorCode DIY_PAGE_NAME_USED = new ErrorCode(1_013_018_001, \"装修页面名称({})已经被使用\");\n\n    // ========== 客服会话 1-013-019-000 ==========\n    ErrorCode KEFU_CONVERSATION_NOT_EXISTS = new ErrorCode(1_013_019_000, \"客服会话不存在\");\n\n    // ========== 客服消息 1-013-020-000 ==========\n    ErrorCode KEFU_MESSAGE_NOT_EXISTS = new ErrorCode(1_013_020_000, \"客服消息不存在\");\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/MessageTemplateConstants.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums;\n\n/**\n * 通知模板枚举类\n *\n * @author HUIHUI\n */\npublic interface MessageTemplateConstants {\n\n    //======================= 小程序订阅消息模版 =======================\n\n    String COMBINATION_SUCCESS = \"拼团结果通知\";\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/WebSocketMessageTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums;\n\n/**\n * Promotion 的 WebSocket 消息类型枚举类\n *\n * @author HUIHUI\n */\npublic interface WebSocketMessageTypeConstants {\n\n    // ======================= mall 客服 =======================\n\n    String KEFU_MESSAGE_TYPE = \"kefu_message_type\"; // 客服消息类型\n    String KEFU_MESSAGE_ADMIN_READ = \"kefu_message_read_status_change\"; // 客服消息管理员已读\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/banner/BannerPositionEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.banner;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * Banner Position 枚举\n *\n * @author HUIHUI\n */\n@AllArgsConstructor\n@Getter\npublic enum BannerPositionEnum implements ArrayValuable<Integer> {\n\n    HOME_POSITION(1, \"首页\"),\n    SECKILL_POSITION(2, \"秒杀活动页\"),\n    COMBINATION_POSITION(3, \"砍价活动页\"),\n    DISCOUNT_POSITION(4, \"限时折扣页\"),\n    REWARD_POSITION(5, \"满减送页\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BannerPositionEnum::getPosition).toArray(Integer[]::new);\n\n    /**\n     * 值\n     */\n    private final Integer position;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/bargain/BargainRecordStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.bargain;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 砍价记录的状态枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum BargainRecordStatusEnum implements ArrayValuable<Integer> {\n\n    IN_PROGRESS(1, \"砍价中\"),\n    SUCCESS(2, \"砍价成功\"),\n    FAILED(3, \"砍价失败\"), // 活动到期时，会自动将到期的砍价全部设置为过期\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BargainRecordStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 值\n     */\n    private final Integer status;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.combination;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 拼团状态枚举\n *\n * @author HUIHUI\n */\n@AllArgsConstructor\n@Getter\npublic enum CombinationRecordStatusEnum implements ArrayValuable<Integer> {\n\n    IN_PROGRESS(0, \"进行中\"),\n    SUCCESS(1, \"拼团成功\"),\n    FAILED(2, \"拼团失败\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CombinationRecordStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态值\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isSuccess(Integer status) {\n        return ObjectUtil.equal(status, SUCCESS.getStatus());\n    }\n\n    public static boolean isInProgress(Integer status) {\n        return ObjectUtil.equal(status, IN_PROGRESS.getStatus());\n    }\n\n    public static boolean isFailed(Integer status) {\n        return ObjectUtil.equal(status, FAILED.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionActivityStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.common;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n// TODO 芋艿：弱化这个状态\n/**\n * 促销活动的状态枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum PromotionActivityStatusEnum implements ArrayValuable<Integer> {\n\n    WAIT(10, \"未开始\"),\n    RUN(20, \"进行中\"),\n    END(30, \"已结束\"),\n    CLOSE(40, \"已关闭\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionActivityStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态值\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionConditionTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.common;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 营销的条件类型枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum PromotionConditionTypeEnum implements ArrayValuable<Integer> {\n\n    PRICE(10, \"满 N 元\"),\n    COUNT(20, \"满 N 件\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionConditionTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型值\n     */\n    private final Integer type;\n    /**\n     * 类型名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionDiscountTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.common;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 优惠类型枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum PromotionDiscountTypeEnum implements ArrayValuable<Integer> {\n\n    PRICE(1, \"满减\"), // 具体金额\n    PERCENT(2, \"折扣\"), // 百分比\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionDiscountTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 优惠类型\n     */\n    private final Integer type;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.common;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\nimport java.util.Objects;\n\n/**\n * 营销的商品范围枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum PromotionProductScopeEnum implements ArrayValuable<Integer> {\n\n    ALL(1, \"全部商品\"),\n    SPU(2, \"指定商品\"),\n    CATEGORY(3, \"指定品类\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionProductScopeEnum::getScope).toArray(Integer[]::new);\n\n    /**\n     * 范围值\n     */\n    private final Integer scope;\n    /**\n     * 范围名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isAll(Integer scope) {\n        return Objects.equals(scope, ALL.scope);\n    }\n\n    public static boolean isSpu(Integer scope) {\n        return Objects.equals(scope, SPU.scope);\n    }\n\n    public static boolean isCategory(Integer scope) {\n        return Objects.equals(scope, CATEGORY.scope);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.common;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 营销类型枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum PromotionTypeEnum implements ArrayValuable<Integer> {\n\n    SECKILL_ACTIVITY(1, \"秒杀活动\"),\n    BARGAIN_ACTIVITY(2, \"砍价活动\"),\n    COMBINATION_ACTIVITY(3, \"拼团活动\"),\n\n    DISCOUNT_ACTIVITY(4, \"限时折扣\"),\n    REWARD_ACTIVITY(5, \"满减送\"),\n\n    MEMBER_LEVEL(6, \"会员折扣\"),\n    COUPON(7, \"优惠劵\"),\n    POINT(8, \"积分\")\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型值\n     */\n    private final Integer type;\n    /**\n     * 类型名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.coupon;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 优惠劵状态枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum CouponStatusEnum implements ArrayValuable<Integer> {\n\n    UNUSED(1, \"未使用\"),\n    USED(2, \"已使用\"),\n    EXPIRE(3, \"已过期\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CouponStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 值\n     */\n    private final Integer status;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.coupon;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\nimport java.util.Objects;\n\n/**\n * 优惠劵领取方式\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum CouponTakeTypeEnum implements ArrayValuable<Integer> {\n\n    USER(1, \"直接领取\"), // 用户可在首页、每日领劵直接领取\n    ADMIN(2, \"指定发放\"), // 后台指定会员赠送优惠劵\n    REGISTER(3, \"新人券\"), // 注册时自动领取\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CouponTakeTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 值\n     */\n    private final Integer type;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isUser(Integer type) {\n        return Objects.equals(USER.getType(), type);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTemplateValidityTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.coupon;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 优惠劵模板的有限期类型的枚举\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum CouponTemplateValidityTypeEnum implements ArrayValuable<Integer> {\n\n    DATE(1, \"固定日期\"),\n    TERM(2, \"领取之后\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CouponTemplateValidityTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 值\n     */\n    private final Integer type;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/decorate/DecorateComponentEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.decorate;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * 页面组件枚举\n *\n * @author jason\n */\n@Getter\n@AllArgsConstructor\n@SuppressWarnings(\"JavadocLinkAsPlainText\")\npublic enum DecorateComponentEnum {\n\n    /**\n     * 格式：[{\n     *  \"name\": \"标题\"\n     *  \"picUrl\": \"https://www.iocoder.cn/xxx.png\",\n     *  \"url\": \"/pages/users/index\"\n     * }]\n     *\n     * 最多 10 个\n     */\n    MENU(\"menu\", \"菜单\"),\n    /**\n     * 格式：[{\n     *  \"name\": \"标题\"\n     *  \"url\": \"/pages/users/index\"\n     * }]\n     */\n    ROLLING_NEWS(\"scrolling-news\", \"滚动新闻\"),\n    /**\n     * 格式：[{\n     *  \"picUrl\": \"https://www.iocoder.cn/xxx.png\",\n     *  \"url\": \"/pages/users/index\"\n     * }]\n     */\n    SLIDE_SHOW(\"slide-show\", \"轮播图\"),\n    /**\n     * 格式：[{\n     *  \"name\": \"标题\"\n     *  \"type\": \"类型\", // best、hot、new、benefit、good\n     *  \"tag\": \"标签\" // 例如说：多买多省\n     * }]\n     *\n     * 最多 4 个\n     */\n    PRODUCT_RECOMMEND(\"product-recommend\", \"商品推荐\");\n\n    /**\n     * 页面组件代码\n     */\n    private final String code;\n\n    /**\n     * 页面组件说明\n     */\n    private final String desc;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/diy/DiyPageEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.diy;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 装修页面枚举\n *\n * @author jason\n */\n@AllArgsConstructor\n@Getter\npublic enum DiyPageEnum implements ArrayValuable<Integer> {\n\n    INDEX(1, \"首页\"),\n    MY(2, \"我的\"),\n    ;\n\n    private static final Integer[] ARRAYS = Arrays.stream(values()).map(DiyPageEnum::getPage).toArray(Integer[]::new);\n\n    /**\n     * 页面编号\n     */\n    private final Integer page;\n\n    /**\n     * 页面名称\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/kefu/KeFuMessageContentTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.promotion.enums.kefu;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 客服消息的类型枚举\n *\n * @author HUIHUI\n */\n@AllArgsConstructor\n@Getter\npublic enum KeFuMessageContentTypeEnum implements ArrayValuable<Integer> {\n\n    TEXT(1, \"文本消息\"),\n    IMAGE(2, \"图片消息\"),\n    VOICE(3, \"语音消息\"),\n    VIDEO(4, \"视频消息\"),\n    SYSTEM(5, \"系统消息\"),\n\n    // ========== 商城特殊消息 ==========\n    PRODUCT(10, \"商品消息\"),\n    ORDER(11, \"订单消息\");\n\n    private static final Integer[] ARRAYS = Arrays.stream(values()).map(KeFuMessageContentTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n\n    /**\n     * 名称\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:8-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-module-promotion-server\nWORKDIR /yudao-module-promotion-server\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-module-promotion-server.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48080 端口\nEXPOSE 48101\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-mall</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>jar</packaging>\n    <artifactId>yudao-module-promotion-server</artifactId>\n\n    <name>${project.artifactId}</name>\n\n    <description>\n        promotion 模块，主要实现营销相关功能\n        例如：营销活动、banner 广告、优惠券、优惠码等功能。\n    </description>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-system-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-infra-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-promotion-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-product-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-trade-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-member-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- Job 定时任务相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-job</artifactId>\n        </dependency>\n\n        <!-- 消息队列相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mq</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.vavr</groupId>\n            <artifactId>vavr</artifactId>\n            <version>0.10.2</version>\n            <scope>compile</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/PromotionServerApplication.java",
    "content": "package cn.iocoder.yudao.module.promotion;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n *\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication\npublic class PromotionServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(PromotionServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApiImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 砍价活动 Api 接口实现类\n *\n * @author HUIHUI\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class BargainActivityApiImpl implements BargainActivityApi {\n\n    @Resource\n    private BargainActivityService bargainActivityService;\n\n    @Override\n    public CommonResult<Boolean> updateBargainActivityStock(Long id, Integer count) {\n        bargainActivityService.updateBargainActivityStock(id, count);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainRecordApiImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.bargain.dto.BargainValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainRecordService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 砍价活动 API 实现类\n *\n * @author HUIHUI\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class BargainRecordApiImpl implements BargainRecordApi {\n\n    @Resource\n    private BargainRecordService bargainRecordService;\n\n    @Override\n    public CommonResult<BargainValidateJoinRespDTO> validateJoinBargain(Long userId, Long bargainRecordId, Long skuId) {\n        return success(bargainRecordService.validateJoinBargain(userId, bargainRecordId, skuId));\n    }\n\n    @Override\n    public CommonResult<Boolean> updateBargainRecordOrderId(Long id, Long orderId) {\n        bargainRecordService.updateBargainRecordOrderId(id, orderId);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.combination;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;\nimport cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 拼团活动 API 实现类\n *\n * @author HUIHUI\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class CombinationRecordApiImpl implements CombinationRecordApi {\n\n    @Resource\n    private CombinationRecordService combinationRecordService;\n\n    @Override\n    public CommonResult<Boolean> validateCombinationRecord(Long userId, Long activityId, Long headId, Long skuId, Integer count) {\n        combinationRecordService.validateCombinationRecord(userId, activityId, headId, skuId, count);\n        return success(true);\n    }\n\n    @Override\n    public CommonResult<CombinationRecordCreateRespDTO> createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {\n        return success(CombinationActivityConvert.INSTANCE.convert4(combinationRecordService.createCombinationRecord(reqDTO)));\n    }\n\n    @Override\n    public CommonResult<CombinationRecordRespDTO> getCombinationRecordByOrderId(Long userId, Long orderId) {\n        CombinationRecordDO record = combinationRecordService.getCombinationRecord(userId, orderId);\n        return success(BeanUtils.toBean(record, CombinationRecordRespDTO.class));\n    }\n\n    @Override\n    public CommonResult<CombinationValidateJoinRespDTO> validateJoinCombination(\n            Long userId, Long activityId, Long headId, Long skuId, Integer count) {\n        return success(combinationRecordService.validateJoinCombination(userId, activityId, headId, skuId, count));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.coupon;\n\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;\nimport cn.iocoder.yudao.module.promotion.service.coupon.CouponService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 优惠劵 API 实现类\n *\n * @author 芋道源码\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class CouponApiImpl implements CouponApi {\n\n    @Resource\n    private CouponService couponService;\n\n    @Override\n    public CommonResult<List<CouponRespDTO>> getCouponListByUserId(Long userId, Integer status) {\n        return success(BeanUtils.toBean(couponService.getCouponList(userId, status), CouponRespDTO.class));\n    }\n\n    @Override\n    public CommonResult<Boolean> useCoupon(CouponUseReqDTO useReqDTO) {\n        couponService.useCoupon(useReqDTO.getId(), useReqDTO.getUserId(), useReqDTO.getOrderId());\n        return success(true);\n    }\n\n    @Override\n    public CommonResult<Boolean> returnUsedCoupon(Long id) {\n        couponService.returnUsedCoupon(id);\n        return success(true);\n    }\n\n    @Override\n    public CommonResult<List<Long>> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId) {\n        return success(couponService.takeCouponsByAdmin(giveCoupons, userId));\n    }\n\n    @Override\n    public CommonResult<Boolean> invalidateCouponsByAdmin(List<Long> giveCouponIds, Long userId) {\n        couponService.invalidateCouponsByAdmin(giveCouponIds, userId);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApiImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.discount;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;\nimport cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 限时折扣 API 实现类\n *\n * @author 芋道源码\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class DiscountActivityApiImpl implements DiscountActivityApi {\n\n    @Resource\n    private DiscountActivityService discountActivityService;\n\n    @Override\n    public CommonResult<List<DiscountProductRespDTO>> getMatchDiscountProductListBySkuIds(Collection<Long> skuIds) {\n        List<DiscountProductDO> list = discountActivityService.getMatchDiscountProductListBySkuIds(skuIds);\n        return success(BeanUtils.toBean(list, DiscountProductRespDTO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.point;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.service.point.PointActivityService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 积分商城活动 Api 接口实现类\n *\n * @author HUIHUI\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class PointActivityApiImpl implements PointActivityApi {\n\n    @Resource\n    private PointActivityService pointActivityService;\n\n    @Override\n    public CommonResult<PointValidateJoinRespDTO> validateJoinPointActivity(Long activityId, Long skuId, Integer count) {\n        return success(pointActivityService.validateJoinPointActivity(activityId, skuId, count));\n    }\n\n    @Override\n    public CommonResult<Boolean> updatePointStockDecr(Long id, Long skuId, Integer count) {\n        pointActivityService.updatePointStockDecr(id, skuId, count);\n        return success(true);\n    }\n\n    @Override\n    public CommonResult<Boolean> updatePointStockIncr(Long id, Long skuId, Integer count) {\n        pointActivityService.updatePointStockIncr(id, skuId, count);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/RewardActivityApiImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.reward;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;\nimport cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 满减送活动 API 实现类\n *\n * @author 芋道源码\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class RewardActivityApiImpl implements RewardActivityApi {\n\n    @Resource\n    private RewardActivityService rewardActivityService;\n\n    @Override\n    public CommonResult<List<RewardActivityMatchRespDTO>> getMatchRewardActivityListBySpuIds(Collection<Long> spuIds) {\n        return success(rewardActivityService.getMatchRewardActivityListBySpuIds(spuIds));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApiImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.api.seckill;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 秒杀活动接口 Api 接口实现类\n *\n * @author HUIHUI\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class SeckillActivityApiImpl implements SeckillActivityApi {\n\n    @Resource\n    private SeckillActivityService activityService;\n\n    @Override\n    public CommonResult<Boolean> updateSeckillStockDecr(Long id, Long skuId, Integer count) {\n        activityService.updateSeckillStockDecr(id, skuId, count);\n        return success(true);\n    }\n\n    @Override\n    public CommonResult<Boolean> updateSeckillStockIncr(Long id, Long skuId, Integer count) {\n        activityService.updateSeckillStockIncr(id, skuId, count);\n        return success(true);\n    }\n\n    @Override\n    public CommonResult<SeckillValidateJoinRespDTO> validateJoinSeckill(Long activityId, Long skuId, Integer count) {\n        return success(activityService.validateJoinSeckill(activityId, skuId, count));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/ArticleCategoryController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.*;\nimport cn.iocoder.yudao.module.promotion.convert.article.ArticleCategoryConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryDO;\nimport cn.iocoder.yudao.module.promotion.service.article.ArticleCategoryService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 文章分类\")\n@RestController\n@RequestMapping(\"/promotion/article-category\")\n@Validated\npublic class ArticleCategoryController {\n\n    @Resource\n    private ArticleCategoryService articleCategoryService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建文章分类\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:article-category:create')\")\n    public CommonResult<Long> createArticleCategory(@Valid @RequestBody ArticleCategoryCreateReqVO createReqVO) {\n        return success(articleCategoryService.createArticleCategory(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新文章分类\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:article-category:update')\")\n    public CommonResult<Boolean> updateArticleCategory(@Valid @RequestBody ArticleCategoryUpdateReqVO updateReqVO) {\n        articleCategoryService.updateArticleCategory(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除文章分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:article-category:delete')\")\n    public CommonResult<Boolean> deleteArticleCategory(@RequestParam(\"id\") Long id) {\n        articleCategoryService.deleteArticleCategory(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得文章分类\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:article-category:query')\")\n    public CommonResult<ArticleCategoryRespVO> getArticleCategory(@RequestParam(\"id\") Long id) {\n        ArticleCategoryDO category = articleCategoryService.getArticleCategory(id);\n        return success(ArticleCategoryConvert.INSTANCE.convert(category));\n    }\n\n    @GetMapping(\"/list-all-simple\")\n    @Operation(summary = \"获取文章分类精简信息列表\", description = \"只包含被开启的文章分类，主要用于前端的下拉选项\")\n    public CommonResult<List<ArticleCategorySimpleRespVO>> getSimpleDeptList() {\n        // 获得分类列表，只要开启状态的\n        List<ArticleCategoryDO> list = articleCategoryService.getArticleCategoryListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        // 降序排序后，返回给前端\n        list.sort(Comparator.comparing(ArticleCategoryDO::getSort).reversed());\n        return success(ArticleCategoryConvert.INSTANCE.convertList03(list));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得文章分类分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:article-category:query')\")\n    public CommonResult<PageResult<ArticleCategoryRespVO>> getArticleCategoryPage(@Valid ArticleCategoryPageReqVO pageVO) {\n        PageResult<ArticleCategoryDO> pageResult = articleCategoryService.getArticleCategoryPage(pageVO);\n        return success(ArticleCategoryConvert.INSTANCE.convertPage(pageResult));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/ArticleController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.article.ArticleConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;\nimport cn.iocoder.yudao.module.promotion.service.article.ArticleService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 文章管理\")\n@RestController\n@RequestMapping(\"/promotion/article\")\n@Validated\npublic class ArticleController {\n\n    @Resource\n    private ArticleService articleService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建文章管理\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:article:create')\")\n    public CommonResult<Long> createArticle(@Valid @RequestBody ArticleCreateReqVO createReqVO) {\n        return success(articleService.createArticle(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新文章管理\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:article:update')\")\n    public CommonResult<Boolean> updateArticle(@Valid @RequestBody ArticleUpdateReqVO updateReqVO) {\n        articleService.updateArticle(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除文章管理\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:article:delete')\")\n    public CommonResult<Boolean> deleteArticle(@RequestParam(\"id\") Long id) {\n        articleService.deleteArticle(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得文章管理\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:article:query')\")\n    public CommonResult<ArticleRespVO> getArticle(@RequestParam(\"id\") Long id) {\n        ArticleDO article = articleService.getArticle(id);\n        return success(ArticleConvert.INSTANCE.convert(article));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得文章管理分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:article:query')\")\n    public CommonResult<PageResult<ArticleRespVO>> getArticlePage(@Valid ArticlePageReqVO pageVO) {\n        PageResult<ArticleDO> pageResult = articleService.getArticlePage(pageVO);\n        return success(ArticleConvert.INSTANCE.convertPage(pageResult));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/article/ArticleBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 文章管理 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class ArticleBaseVO {\n\n    @Schema(description = \"文章分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15458\")\n    @NotNull(message = \"文章分类编号不能为空\")\n    private Long categoryId;\n\n    @Schema(description = \"关联商品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22378\")\n    @NotNull(message = \"关联商品不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"文章标题\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"这是一个标题\")\n    @NotNull(message = \"文章标题不能为空\")\n    private String title;\n\n    @Schema(description = \"文章作者\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    private String author;\n\n    @Schema(description = \"文章封面图片地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn\")\n    @NotNull(message = \"文章封面图片地址不能为空\")\n    private String picUrl;\n\n    @Schema(description = \"文章简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"这是一个简介\")\n    private String introduction;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"是否热门(小程序)\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否热门(小程序)不能为空\")\n    private Boolean recommendHot;\n\n    @Schema(description = \"是否轮播图(小程序)\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否轮播图(小程序)不能为空\")\n    private Boolean recommendBanner;\n\n    @Schema(description = \"文章内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"这是文章内容\")\n    @NotNull(message = \"文章内容不能为空\")\n    private String content;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/article/ArticleCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 文章管理创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ArticleCreateReqVO extends ArticleBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/article/ArticlePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 文章管理分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ArticlePageReqVO extends PageParam {\n\n    @Schema(description = \"文章分类编号\", example = \"15458\")\n    private Long categoryId;\n\n    @Schema(description = \"关联商品编号\", example = \"22378\")\n    private Long spuId;\n\n    @Schema(description = \"文章标题\")\n    private String title;\n\n    @Schema(description = \"文章作者\")\n    private String author;\n\n    @Schema(description = \"状态\", example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"是否热门(小程序)\")\n    private Boolean recommendHot;\n\n    @Schema(description = \"是否轮播图(小程序)\")\n    private Boolean recommendBanner;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/article/ArticleRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 文章管理 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ArticleRespVO extends ArticleBaseVO {\n\n    @Schema(description = \"文章编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8606\")\n    private Long id;\n\n    @Schema(description = \"浏览量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"99999\")\n    private Integer browseCount;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/article/ArticleUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 文章管理更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ArticleUpdateReqVO extends ArticleBaseVO {\n\n    @Schema(description = \"文章编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8606\")\n    @NotNull(message = \"文章编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/category/ArticleCategoryBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 文章分类 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class ArticleCategoryBaseVO {\n\n    @Schema(description = \"文章分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"秒杀\")\n    @NotNull(message = \"文章分类名称不能为空\")\n    private String name;\n\n    @Schema(description = \"图标地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn\")\n    private String picUrl;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/category/ArticleCategoryCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 文章分类创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ArticleCategoryCreateReqVO extends ArticleCategoryBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/category/ArticleCategoryPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 文章分类分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ArticleCategoryPageReqVO extends PageParam {\n\n    @Schema(description = \"文章分类名称\", example = \"秒杀\")\n    private String name;\n\n    @Schema(description = \"状态\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/category/ArticleCategoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 文章分类 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ArticleCategoryRespVO extends ArticleCategoryBaseVO {\n\n    @Schema(description = \"文章分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19490\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/category/ArticleCategorySimpleRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 文章分类精简信息 Response VO\")\n@Data\npublic class ArticleCategorySimpleRespVO {\n\n    @Schema(description = \"文章分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19490\")\n    private Long id;\n\n    @Schema(description = \"文章分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"秒杀\")\n    private String name;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/category/ArticleCategoryUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 文章分类更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class ArticleCategoryUpdateReqVO extends ArticleCategoryBaseVO {\n\n    @Schema(description = \"文章分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19490\")\n    @NotNull(message = \"文章分类编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/BannerController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.banner;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.banner.BannerConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;\nimport cn.iocoder.yudao.module.promotion.service.banner.BannerService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - Banner 管理\")\n@RestController\n@RequestMapping(\"/promotion/banner\")\n@Validated\npublic class BannerController {\n\n    @Resource\n    private BannerService bannerService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建 Banner\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:banner:create')\")\n    public CommonResult<Long> createBanner(@Valid @RequestBody BannerCreateReqVO createReqVO) {\n        return success(bannerService.createBanner(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新 Banner\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:banner:update')\")\n    public CommonResult<Boolean> updateBanner(@Valid @RequestBody BannerUpdateReqVO updateReqVO) {\n        bannerService.updateBanner(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除 Banner\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:banner:delete')\")\n    public CommonResult<Boolean> deleteBanner(@RequestParam(\"id\") Long id) {\n        bannerService.deleteBanner(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得 Banner\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:banner:query')\")\n    public CommonResult<BannerRespVO> getBanner(@RequestParam(\"id\") Long id) {\n        BannerDO banner = bannerService.getBanner(id);\n        return success(BannerConvert.INSTANCE.convert(banner));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得 Banner 分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:banner:query')\")\n    public CommonResult<PageResult<BannerRespVO>> getBannerPage(@Valid BannerPageReqVO pageVO) {\n        PageResult<BannerDO> pageResult = bannerService.getBannerPage(pageVO);\n        return success(BannerConvert.INSTANCE.convertPage(pageResult));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.banner.BannerPositionEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * Banner Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n * @author xia\n */\n@Data\npublic class BannerBaseVO {\n\n    @Schema(description = \"标题\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"标题不能为空\")\n    private String title;\n\n    @Schema(description = \"跳转链接\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"跳转链接不能为空\")\n    private String url;\n\n    @Schema(description = \"图片地址\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"图片地址不能为空\")\n    private String picUrl;\n\n    @Schema(description = \"position\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"position 不能为空\")\n    @InEnum(BannerPositionEnum.class)\n    private Integer position;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"备注\")\n    private String memo;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n/**\n * @author xia\n */\n@Schema(description = \"管理后台 - Banner 创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BannerCreateReqVO extends BannerBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - Banner 分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BannerPageReqVO extends PageParam {\n\n    @Schema(description = \"标题\", example = \"这是一个标题\")\n    private String title;\n\n    @Schema(description = \"状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"创建时间\")\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - Banner Response VO\")\n@Data\n@ToString(callSuper = true)\npublic class BannerRespVO  extends BannerBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2022-07-01 23:59:59\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * @author xia\n */\n@Schema(description = \"管理后台 - Banner更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BannerUpdateReqVO extends BannerBaseVO {\n\n    @Schema(description = \"banner 编号\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"banner 编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/BargainActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.*;\nimport cn.iocoder.yudao.module.promotion.convert.bargain.BargainActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainHelpService;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainRecordService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"管理后台 - 砍价活动\")\n@RestController\n@RequestMapping(\"/promotion/bargain-activity\")\n@Validated\npublic class BargainActivityController {\n\n    @Resource\n    private BargainActivityService bargainActivityService;\n    @Resource\n    private BargainRecordService bargainRecordService;\n    @Resource\n    private BargainHelpService bargainHelpService;\n\n    @Resource\n    private ProductSpuApi spuApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建砍价活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:bargain-activity:create')\")\n    public CommonResult<Long> createBargainActivity(@Valid @RequestBody BargainActivityCreateReqVO createReqVO) {\n        return success(bargainActivityService.createBargainActivity(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新砍价活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:bargain-activity:update')\")\n    public CommonResult<Boolean> updateBargainActivity(@Valid @RequestBody BargainActivityUpdateReqVO updateReqVO) {\n        bargainActivityService.updateBargainActivity(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/close\")\n    @Operation(summary = \"关闭砍价活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:bargain-activity:close')\")\n    public CommonResult<Boolean> closeSeckillActivity(@RequestParam(\"id\") Long id) {\n        bargainActivityService.closeBargainActivityById(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除砍价活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:bargain-activity:delete')\")\n    public CommonResult<Boolean> deleteBargainActivity(@RequestParam(\"id\") Long id) {\n        bargainActivityService.deleteBargainActivity(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得砍价活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:bargain-activity:query')\")\n    public CommonResult<BargainActivityRespVO> getBargainActivity(@RequestParam(\"id\") Long id) {\n        return success(BargainActivityConvert.INSTANCE.convert(bargainActivityService.getBargainActivity(id)));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得砍价活动分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:bargain-activity:query')\")\n    public CommonResult<PageResult<BargainActivityPageItemRespVO>> getBargainActivityPage(\n            @Valid BargainActivityPageReqVO pageVO) {\n        // 查询砍价活动\n        PageResult<BargainActivityDO> pageResult = bargainActivityService.getBargainActivityPage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接数据\n        List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(pageResult.getList(), BargainActivityDO::getSpuId)).getCheckedData();\n        // 统计数据\n        Collection<Long> activityIds = convertList(pageResult.getList(), BargainActivityDO::getId);\n        Map<Long, Integer> recordUserCountMap = bargainRecordService.getBargainRecordUserCountMap(activityIds, null);\n        Map<Long, Integer> recordSuccessUserCountMap = bargainRecordService.getBargainRecordUserCountMap(activityIds,\n                BargainRecordStatusEnum.SUCCESS.getStatus());\n        Map<Long, Integer> helpUserCountMap = bargainHelpService.getBargainHelpUserCountMapByActivity(activityIds);\n        return success(BargainActivityConvert.INSTANCE.convertPage(pageResult, spuList,\n                recordUserCountMap, recordSuccessUserCountMap, helpUserCountMap));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/BargainHelpController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.help.BargainHelpPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.help.BargainHelpRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.bargain.BargainHelpConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainHelpService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 砍价助力\")\n@RestController\n@RequestMapping(\"/promotion/bargain-help\")\n@Validated\npublic class BargainHelpController {\n\n    @Resource\n    private BargainHelpService bargainHelpService;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得砍价助力分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:bargain-help:query')\")\n    public CommonResult<PageResult<BargainHelpRespVO>> getBargainHelpPage(@Valid BargainHelpPageReqVO pageVO) {\n        PageResult<BargainHelpDO> pageResult = bargainHelpService.getBargainHelpPage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接数据\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(\n                convertSet(pageResult.getList(), BargainHelpDO::getUserId));\n        return success(BargainHelpConvert.INSTANCE.convertPage(pageResult, userMap));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/BargainRecordController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.recrod.BargainRecordPageItemRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.recrod.BargainRecordPageReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.bargain.BargainRecordConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainHelpService;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainRecordService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 砍价记录\")\n@RestController\n@RequestMapping(\"/promotion/bargain-record\")\n@Validated\npublic class BargainRecordController {\n\n    @Resource\n    private BargainRecordService bargainRecordService;\n    @Resource\n    private BargainActivityService bargainActivityService;\n    @Resource\n    private BargainHelpService bargainHelpService;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得砍价记录分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:bargain-record:query')\")\n    public CommonResult<PageResult<BargainRecordPageItemRespVO>> getBargainRecordPage(@Valid BargainRecordPageReqVO pageVO) {\n        PageResult<BargainRecordDO> pageResult = bargainRecordService.getBargainRecordPage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接数据\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(\n                convertSet(pageResult.getList(), BargainRecordDO::getUserId));\n        List<BargainActivityDO> activityList = bargainActivityService.getBargainActivityList(\n                convertSet(pageResult.getList(), BargainRecordDO::getActivityId));\n        Map<Long, Integer> helpCountMap = bargainHelpService.getBargainHelpUserCountMapByRecord(\n                convertSet(pageResult.getList(), BargainRecordDO::getId));\n        return success(BargainRecordConvert.INSTANCE.convertPage(pageResult, helpCountMap, activityList, userMap));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n/**\n * 砍价活动 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n *\n * @author HUIHUI\n */\n@Data\npublic class BargainActivityBaseVO {\n\n    @Schema(description = \"砍价活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"砍得越多省得越多，是兄弟就来砍我\")\n    @NotNull(message = \"砍价名称不能为空\")\n    private String name;\n\n    @Schema(description = \"商品 SPU 编号\", example = \"1\")\n    @NotNull(message = \"砍价商品不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"商品 skuId\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23\")\n    @NotNull(message = \"商品 skuId 不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"砍价起始价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23\")\n    @NotNull(message = \"砍价起始价格不能为空\")\n    private Integer bargainFirstPrice;\n\n    @Schema(description = \"砍价底价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23\")\n    @NotNull(message = \"砍价底价不能为空\")\n    private Integer bargainMinPrice;\n\n    @Schema(description = \"活动库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23\")\n    @NotNull(message = \"活动库存不能为空\")\n    private Integer stock;\n\n    @Schema(description = \"总限购数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"16218\")\n    @NotNull(message = \"总限购数量不能为空\")\n    private Integer totalLimitCount;\n\n    @Schema(description = \"活动开始时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[2022-07-01 23:59:59]\")\n    @NotNull(message = \"活动开始时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"活动结束时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[2022-07-01 23:59:59]\")\n    @NotNull(message = \"活动结束时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"最大助力次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25222\")\n    @NotNull(message = \"最大助力次数不能为空\")\n    private Integer helpMaxCount;\n\n    @Schema(description = \"最大帮砍次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25222\")\n    @NotNull(message = \"最大帮砍次数不能为空\")\n    private Integer bargainCount;\n\n    @Schema(description = \"用户每次砍价的最小金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25222\")\n    @NotNull(message = \"用户每次砍价的最小金额不能为空\")\n    private Integer randomMinPrice;\n\n    @Schema(description = \"用户每次砍价的最大金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25222\")\n    @NotNull(message = \"用户每次砍价的最大金额不能为空\")\n    private Integer randomMaxPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 砍价活动创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BargainActivityCreateReqVO extends BargainActivityBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityPageItemRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 砍价活动的分页项 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BargainActivityPageItemRespVO extends BargainActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22901\")\n    private Long id;\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"618大促\")\n    private String spuName;\n    @Schema(description = \"商品主图\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"活动状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"活动总库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23\")\n    private Integer totalStock;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2022-07-01 23:59:59\")\n    private LocalDateTime createTime;\n\n    // ========== 统计字段 ==========\n\n    @Schema(description = \"总砍价的用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"999\")\n    private Integer recordUserCount;\n\n    @Schema(description = \"成功砍价的用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"500\")\n    private Integer recordSuccessUserCount;\n\n    @Schema(description = \"帮助砍价的用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n    private Integer helpUserCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 砍价活动分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BargainActivityPageReqVO extends PageParam {\n\n    @Schema(description = \"砍价名称\", example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"活动状态\", example = \"0\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n@Schema(description = \"管理后台 - 砍价活动 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BargainActivityRespVO extends BargainActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22901\")\n    private Long id;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2022-07-01 23:59:59\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 砍价活动更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BargainActivityUpdateReqVO extends BargainActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22901\")\n    @NotNull(message = \"活动编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/help/BargainHelpBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.help;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 砍价助力 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class BargainHelpBaseVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5402\")\n    private Long userId;\n\n    @Schema(description = \"砍价活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"16825\")\n    private Long activityId;\n\n    @Schema(description = \"砍价记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1800\")\n    private Long recordId;\n\n    @Schema(description = \"减少砍价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"32300\")\n    private Integer reducePrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/help/BargainHelpPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.help;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 砍价助力分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BargainHelpPageReqVO extends PageParam {\n\n    @Schema(description = \"砍价记录编号\", example = \"1800\")\n    private Long recordId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/help/BargainHelpRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.help;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 砍价助力 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BargainHelpRespVO extends BargainHelpBaseVO {\n\n    @Schema(description = \"砍价助力编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25860\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    // ========== 用户相关 ==========\n\n    @Schema(description = \"用户昵称\", example = \"老芋艿\")\n    private String nickname;\n\n    @Schema(description = \"用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xxx.jpg\")\n    private String avatar;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/recrod/BargainRecordBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.recrod;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n/**\n * 砍价记录 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class BargainRecordBaseVO {\n\n    @Schema(description = \"砍价活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22690\")\n    @NotNull(message = \"砍价活动名称不能为空\")\n    private Long activityId;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"9430\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23622\")\n    @NotNull(message = \"商品 SPU 编号不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29950\")\n    @NotNull(message = \"商品 SKU 编号不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"砍价起始价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31160\")\n    @NotNull(message = \"砍价起始价格，单位：分不能为空\")\n    private Integer bargainFirstPrice;\n\n    @Schema(description = \"当前砍价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22743\")\n    @NotNull(message = \"当前砍价，单位：分不能为空\")\n    private Integer bargainPrice;\n\n    @Schema(description = \"砍价状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"砍价状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"订单编号\", example = \"27845\")\n    private Long orderId;\n\n    @Schema(description = \"结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"结束时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime endTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/recrod/BargainRecordPageItemRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.recrod;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 砍价记录的分页项 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BargainRecordPageItemRespVO extends BargainRecordBaseVO {\n\n    @Schema(description = \"记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22901\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2022-07-01 23:59:59\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"帮砍次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    private Integer helpCount;\n\n    // ========== 用户相关 ==========\n\n    @Schema(description = \"用户昵称\", example = \"老芋艿\")\n    private String nickname;\n\n    @Schema(description = \"用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xxx.jpg\")\n    private String avatar;\n\n    // ========== 活动相关 ==========\n\n    private BargainActivityRespVO activity;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/recrod/BargainRecordPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.recrod;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 砍价记录分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BargainRecordPageReqVO extends PageParam {\n\n    @Schema(description = \"砍价状态\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.*;\nimport cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;\nimport cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;\nimport cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;\nimport cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.hutool.core.collection.CollectionUtil.newArrayList;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 拼团活动\")\n@RestController\n@RequestMapping(\"/promotion/combination-activity\")\n@Validated\npublic class CombinationActivityController {\n\n    @Resource\n    private CombinationActivityService combinationActivityService;\n    @Resource\n    private CombinationRecordService combinationRecordService;\n\n    @Resource\n    private ProductSpuApi productSpuApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建拼团活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:combination-activity:create')\")\n    public CommonResult<Long> createCombinationActivity(@Valid @RequestBody CombinationActivityCreateReqVO createReqVO) {\n        return success(combinationActivityService.createCombinationActivity(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新拼团活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:combination-activity:update')\")\n    public CommonResult<Boolean> updateCombinationActivity(@Valid @RequestBody CombinationActivityUpdateReqVO updateReqVO) {\n        combinationActivityService.updateCombinationActivity(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/close\")\n    @Operation(summary = \"关闭拼团活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:combination-activity:close')\")\n    public CommonResult<Boolean> closeCombinationActivity(@RequestParam(\"id\") Long id) {\n        combinationActivityService.closeCombinationActivityById(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除拼团活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:combination-activity:delete')\")\n    public CommonResult<Boolean> deleteCombinationActivity(@RequestParam(\"id\") Long id) {\n        combinationActivityService.deleteCombinationActivity(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得拼团活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:combination-activity:query')\")\n    public CommonResult<CombinationActivityRespVO> getCombinationActivity(@RequestParam(\"id\") Long id) {\n        CombinationActivityDO activity = combinationActivityService.getCombinationActivity(id);\n        List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(newArrayList(id));\n        return success(CombinationActivityConvert.INSTANCE.convert(activity, products));\n    }\n\n    @GetMapping(\"/list-by-ids\")\n    @Operation(summary = \"获得拼团活动列表，基于活动编号数组\")\n    @Parameter(name = \"ids\", description = \"活动编号数组\", required = true, example = \"[1024, 1025]\")\n    public CommonResult<List<CombinationActivityRespVO>> getCombinationActivityListByIds(@RequestParam(\"ids\") List<Long> ids) {\n        // 1. 获得开启的活动列表\n        List<CombinationActivityDO> activityList = combinationActivityService.getCombinationActivityListByIds(ids);\n        activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));\n        if (CollUtil.isEmpty(activityList)) {\n            return success(Collections.emptyList());\n        }\n        // 2. 拼接返回\n        List<CombinationProductDO> productList = combinationActivityService.getCombinationProductListByActivityIds(\n                convertList(activityList, CombinationActivityDO::getId));\n        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId)).getCheckedData();\n        return success(CombinationActivityConvert.INSTANCE.convertList(activityList, productList, spuList));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得拼团活动分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:combination-activity:query')\")\n    public CommonResult<PageResult<CombinationActivityPageItemRespVO>> getCombinationActivityPage(\n            @Valid CombinationActivityPageReqVO pageVO) {\n        // 查询拼团活动\n        PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 统计数据\n        Set<Long> activityIds = convertSet(pageResult.getList(), CombinationActivityDO::getId);\n        Map<Long, Integer> groupCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(\n                activityIds, null, CombinationRecordDO.HEAD_ID_GROUP);\n        Map<Long, Integer> groupSuccessCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(\n                activityIds, CombinationRecordStatusEnum.SUCCESS.getStatus(), CombinationRecordDO.HEAD_ID_GROUP);\n        Map<Long, Integer> recordCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(\n                activityIds, null, null);\n        // 拼接数据\n        List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(\n                convertSet(pageResult.getList(), CombinationActivityDO::getId));\n        List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(\n                convertSet(pageResult.getList(), CombinationActivityDO::getSpuId)).getCheckedData();\n        return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products,\n                groupCountMap, groupSuccessCountMap, recordCountMap, spus));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationRecordController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordPageItemRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordSummaryVO;\nimport cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;\nimport cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;\nimport cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;\nimport cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 拼团记录\")\n@RestController\n@RequestMapping(\"/promotion/combination-record\")\n@Validated\npublic class CombinationRecordController {\n\n    @Resource\n    private CombinationActivityService combinationActivityService;\n    @Resource\n    @Lazy\n    private CombinationRecordService combinationRecordService;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得拼团记录分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:combination-record:query')\")\n    public CommonResult<PageResult<CombinationRecordPageItemRespVO>> getCombinationRecordPage(\n            @Valid CombinationRecordReqPageVO pageVO) {\n        PageResult<CombinationRecordDO> recordPage = combinationRecordService.getCombinationRecordPage(pageVO);\n        // 拼接数据\n        List<CombinationActivityDO> activities = combinationActivityService.getCombinationActivityListByIds(\n                convertSet(recordPage.getList(), CombinationRecordDO::getActivityId));\n        List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(\n                convertSet(recordPage.getList(), CombinationRecordDO::getActivityId));\n        return success(CombinationActivityConvert.INSTANCE.convert(recordPage, activities, products));\n    }\n\n    @GetMapping(\"/get-summary\")\n    @Operation(summary = \"获得拼团记录的概要信息\", description = \"用于拼团记录页面展示\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:combination-record:query')\")\n    public CommonResult<CombinationRecordSummaryVO> getCombinationRecordSummary() {\n        CombinationRecordSummaryVO summaryVO = new CombinationRecordSummaryVO();\n        summaryVO.setUserCount(combinationRecordService.getCombinationUserCount()); // 获取拼团用户参与数量\n        summaryVO.setSuccessCount(combinationRecordService.getCombinationRecordCount( // 获取成团记录\n                CombinationRecordStatusEnum.SUCCESS.getStatus(), null, CombinationRecordDO.HEAD_ID_GROUP));\n        summaryVO.setVirtualGroupCount(combinationRecordService.getCombinationRecordCount(// 获取虚拟成团记录\n                null, Boolean.TRUE, CombinationRecordDO.HEAD_ID_GROUP));\n        return success(summaryVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n/**\n * 拼团活动 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n *\n * @author HUIHUI\n */\n@Data\npublic class CombinationActivityBaseVO {\n\n    @Schema(description = \"拼团名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"越拼越省钱\")\n    @NotNull(message = \"拼团名称不能为空\")\n    private String name;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"拼团商品不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"总限购数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"16218\")\n    @NotNull(message = \"总限购数量不能为空\")\n    private Integer totalLimitCount;\n\n    @Schema(description = \"单次限购数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28265\")\n    @NotNull(message = \"单次限购数量不能为空\")\n    private Integer singleLimitCount;\n\n    @Schema(description = \"活动时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[2022-07-01 23:59:59]\")\n    @NotNull(message = \"活动时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"活动时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[2022-07-01 23:59:59]\")\n    @NotNull(message = \"活动时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"开团人数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25222\")\n    @NotNull(message = \"开团人数不能为空\")\n    private Integer userSize;\n\n    @Schema(description = \"虚拟成团\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    @NotNull(message = \"虚拟成团不能为空\")\n    private Boolean virtualGroup;\n\n    @Schema(description = \"限制时长（小时）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"限制时长不能为空\")\n    private Integer limitDuration;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 拼团活动创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationActivityCreateReqVO extends CombinationActivityBaseVO {\n\n    @Schema(description = \"拼团商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @Valid\n    private List<CombinationProductBaseVO> products;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageItemRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 拼团活动的分页项 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationActivityPageItemRespVO extends CombinationActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22901\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer status;\n\n    @Schema(description = \"拼团商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<CombinationProductRespVO> products;\n\n    // ========== 商品字段 ==========\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取\n            example = \"618大促\")\n    private String spuName;\n    @Schema(description = \"商品主图\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取\n            example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n    @Schema(description = \"商品市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取\n            example = \"50\")\n    private Integer marketPrice;\n\n    // ========== 统计字段 ==========\n\n    @Schema(description = \"开团组数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"33\")\n    private Integer groupCount;\n\n    @Schema(description = \"成团组数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Integer groupSuccessCount;\n\n    @Schema(description = \"购买次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer recordCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 拼团活动分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationActivityPageReqVO extends PageParam {\n\n    @Schema(description = \"拼团名称\", example = \"赵六\")\n    private String name;\n\n    @Schema(description = \"活动状态\", example = \"0\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 拼团活动 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationActivityRespVO extends CombinationActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22901\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"开团人数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"666\")\n    private Integer userSize;\n\n    @Schema(description = \"拼团商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<CombinationProductRespVO> products;\n\n    @Schema(description = \"商品 SPU 名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"一个白菜\")\n    private String spuName; // 从 SPU 的 name 读取\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4096\")\n    private String picUrl; // 从 SPU 的 picUrl 读取\n    @Schema(description = \"商品市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n    private Integer marketPrice; // 从 SPU 的 marketPrice 读取\n\n    @Schema(description = \"拼团金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer combinationPrice; // 从 products 获取最小 price 读取\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 拼团活动更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationActivityUpdateReqVO extends CombinationActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22901\")\n    @NotNull(message = \"活动编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"拼团商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @Valid\n    private List<CombinationProductBaseVO> products;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 拼团商品 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class CombinationProductBaseVO {\n\n    @Schema(description = \"商品 spuId\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"30563\")\n    @NotNull(message = \"商品 spuId 不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"商品 skuId\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"30563\")\n    @NotNull(message = \"商品 skuId 不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"拼团价格，单位分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"27682\")\n    @NotNull(message = \"拼团价格不能为空\")\n    private Integer combinationPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 拼团商品分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationProductPageReqVO extends PageParam {\n\n    @Schema(description = \"拼团活动编号\", example = \"6829\")\n    private Long activityId;\n\n    @Schema(description = \"商品 SPU 编号\", example = \"18731\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SKU 编号\", example = \"31675\")\n    private Long skuId;\n\n    @Schema(description = \"拼团商品状态\", example = \"2\")\n    private Integer activityStatus;\n\n    @Schema(description = \"活动开始时间点\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] activityStartTime;\n\n    @Schema(description = \"活动结束时间点\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] activityEndTime;\n\n    @Schema(description = \"拼团价格，单位分\", example = \"27682\")\n    private Integer activePrice;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 拼团商品 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationProductRespVO extends CombinationProductBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28322\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n/**\n * 拼团记录 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n *\n * @author HUIHUI\n */\n@Data\npublic class CombinationRecordBaseVO {\n\n    @Schema(description = \"拼团记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"拼团活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long activityId;\n\n    @Schema(description = \"团长编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long headId;\n\n    // ========== 用户相关 ==========\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"9430\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    @Schema(description = \"用户昵称\", example = \"老芋艿\")\n    private String nickname;\n\n    @Schema(description = \"用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xxx.jpg\")\n    private String avatar;\n\n    // ========== 商品相关 ==========\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23622\")\n    @NotNull(message = \"商品 SPU 编号不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29950\")\n    @NotNull(message = \"商品 SKU 编号不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"商品名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是大黄豆\")\n    private String spuName;\n\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    private String picUrl;\n\n    @Schema(description = \"过期时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime expireTime;\n\n    @Schema(description = \"可参团人数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer userSize;\n\n    @Schema(description = \"已参团人数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    private Integer userCount;\n\n    @Schema(description = \"拼团状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"是否虚拟成团\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    private Boolean virtualGroup;\n\n    @Schema(description = \"开始时间 (订单付款后开始的时间)\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"结束时间（成团时间/失败时间）\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime endTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordPageItemRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 拼团记录的分页项 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationRecordPageItemRespVO extends CombinationRecordBaseVO {\n\n    // ========== 活动相关 ==========\n\n    private CombinationActivityRespVO activity;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordReqPage2VO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 拼团记录分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationRecordReqPage2VO extends PageParam {\n\n    @Schema(description = \"团长编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"团长编号不能为空\")\n    private Long headId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordReqPageVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 拼团记录分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CombinationRecordReqPageVO extends PageParam {\n\n    @Schema(description = \"活动状态\", example = \"1\")\n    @InEnum(CombinationRecordStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"团长编号\", example = \"1024\")\n    private Long headId;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordSummaryVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 拼团记录信息统计 Response VO\")\n@Data\npublic class CombinationRecordSummaryVO {\n\n    @Schema(description = \"所有拼团记录\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long userCount;\n\n    @Schema(description = \"成团记录\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long successCount;\n\n    @Schema(description = \"虚拟成团记录\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long virtualGroupCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponSendReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;\nimport cn.iocoder.yudao.module.promotion.service.coupon.CouponService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 优惠劵\")\n@RestController\n@RequestMapping(\"/promotion/coupon\")\n@Validated\npublic class CouponController {\n\n    @Resource\n    private CouponService couponService;\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"回收优惠劵\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon:delete')\")\n    public CommonResult<Boolean> deleteCoupon(@RequestParam(\"id\") Long id) {\n        couponService.deleteCoupon(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得优惠劵分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon:query')\")\n    public CommonResult<PageResult<CouponPageItemRespVO>> getCouponPage(@Valid CouponPageReqVO pageVO) {\n        PageResult<CouponDO> pageResult = couponService.getCouponPage(pageVO);\n        PageResult<CouponPageItemRespVO> pageResulVO = CouponConvert.INSTANCE.convertPage(pageResult);\n        if (CollUtil.isEmpty(pageResulVO.getList())) {\n            return success(pageResulVO);\n        }\n\n        // 读取用户信息，进行拼接\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), CouponDO::getUserId));\n        pageResulVO.getList().forEach(itemRespVO -> MapUtils.findAndThen(userMap, itemRespVO.getUserId(),\n                userRespDTO -> itemRespVO.setNickname(userRespDTO.getNickname())));\n        return success(pageResulVO);\n    }\n\n    @PostMapping(\"/send\")\n    @Operation(summary = \"发送优惠劵\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon:send')\")\n    public CommonResult<Boolean> sendCoupon(@Valid @RequestBody CouponSendReqVO reqVO) {\n        couponService.takeCouponByAdmin(reqVO.getTemplateId(), reqVO.getUserIds());\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.*;\nimport cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 优惠劵模板\")\n@RestController\n@RequestMapping(\"/promotion/coupon-template\")\n@Validated\npublic class CouponTemplateController {\n\n    @Resource\n    private CouponTemplateService couponTemplateService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建优惠劵模板\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon-template:create')\")\n    public CommonResult<Long> createCouponTemplate(@Valid @RequestBody CouponTemplateCreateReqVO createReqVO) {\n        return success(couponTemplateService.createCouponTemplate(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新优惠劵模板\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon-template:update')\")\n    public CommonResult<Boolean> updateCouponTemplate(@Valid @RequestBody CouponTemplateUpdateReqVO updateReqVO) {\n        couponTemplateService.updateCouponTemplate(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"更新优惠劵模板状态\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon-template:update')\")\n    public CommonResult<Boolean> updateCouponTemplateStatus(@Valid @RequestBody CouponTemplateUpdateStatusReqVO reqVO) {\n        couponTemplateService.updateCouponTemplateStatus(reqVO.getId(), reqVO.getStatus());\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除优惠劵模板\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon-template:delete')\")\n    public CommonResult<Boolean> deleteCouponTemplate(@RequestParam(\"id\") Long id) {\n        couponTemplateService.deleteCouponTemplate(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得优惠劵模板\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon-template:query')\")\n    public CommonResult<CouponTemplateRespVO> getCouponTemplate(@RequestParam(\"id\") Long id) {\n        CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(id);\n        return success(CouponTemplateConvert.INSTANCE.convert(couponTemplate));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得优惠劵模板分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon-template:query')\")\n    public CommonResult<PageResult<CouponTemplateRespVO>> getCouponTemplatePage(@Valid CouponTemplatePageReqVO pageVO) {\n        PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(pageVO);\n        return success(CouponTemplateConvert.INSTANCE.convertPage(pageResult));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得优惠劵模板列表\")\n    @Parameter(name = \"ids\", description = \"编号列表\", required = true, example = \"1024,2048\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:coupon-template:query')\")\n    public CommonResult<List<CouponTemplateRespVO>> getCouponTemplateList(@RequestParam(\"ids\") Collection<Long> ids) {\n        List<CouponTemplateDO> list = couponTemplateService.getCouponTemplateList(ids);\n        return success(CouponTemplateConvert.INSTANCE.convertList(list));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;\n\n/**\n* 优惠劵 Base VO，提供给添加、修改、详细的子 VO 使用\n* 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n*/\n@Data\npublic class CouponBaseVO {\n\n    // ========== 基本信息 BEGIN ==========\n    @Schema(description = \"优惠劵模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"优惠劵模板编号不能为空\")\n    private Long templateId;\n\n    @Schema(description = \"优惠劵名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"春节送送送\")\n    @NotNull(message = \"优惠劵名不能为空\")\n    private String name;\n\n    @Schema(description = \"优惠码状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    // ========== 基本信息 END ==========\n\n    // ========== 领取情况 BEGIN ==========\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    @Schema(description = \"领取方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"领取方式不能为空\")\n    private Integer takeType;\n    // ========== 领取情况 END ==========\n\n    // ========== 使用规则 BEGIN ==========\n    @Schema(description = \"是否设置满多少金额可用\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\") // 单位：分；0 - 不限制\n    @NotNull(message = \"是否设置满多少金额可用不能为空\")\n    private Integer usePrice;\n\n    @Schema(description = \"固定日期 - 生效开始时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)\n    private LocalDateTime validStartTime;\n\n    @Schema(description = \"固定日期 - 生效结束时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)\n    private LocalDateTime validEndTime;\n\n    @Schema(description = \"商品范围\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"商品范围不能为空\")\n    @InEnum(PromotionProductScopeEnum.class)\n    private Integer productScope;\n\n    @Schema(description = \"商品范围编号的数组\", example = \"1,3\")\n    private List<Long> productScopeValues;\n    // ========== 使用规则 END ==========\n\n    // ========== 使用效果 BEGIN ==========\n    @Schema(description = \"优惠类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"优惠类型不能为空\")\n    @InEnum(PromotionDiscountTypeEnum.class)\n    private Integer discountType;\n\n    @Schema(description = \"折扣百分比\", example = \"80\") // 例如说，80% 为 80\n    private Integer discountPercent;\n\n    @Schema(description = \"优惠金额\", example = \"10\")\n    @Min(value = 0, message = \"优惠金额需要大于等于 0\")\n    private Integer discountPrice;\n\n    @Schema(description = \"折扣上限\", example = \"100\") // 单位：分，仅在 discountType 为 PERCENT 使用\n    private Integer discountLimitPrice;\n    // ========== 使用效果 END ==========\n\n    // ========== 使用情况 BEGIN ==========\n\n    @Schema(description = \"使用订单号\", example = \"4096\")\n    private Long useOrderId;\n\n    @Schema(description = \"使用时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)\n    private LocalDateTime useTime;\n\n    // ========== 使用情况 END ==========\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageItemRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 优惠劵分页的每一项 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CouponPageItemRespVO extends CouponRespVO {\n\n    @Schema(description = \"用户昵称\", example = \"老芋艿\")\n    private String nickname;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\nimport java.util.Collection;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 优惠劵分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CouponPageReqVO extends PageParam {\n\n    @Schema(description = \"优惠劵模板编号\", example = \"2048\")\n    private Long templateId;\n\n    @Schema(description = \"优惠码状态\", example = \"1\")\n    @InEnum(value = CouponStatusEnum.class, message = \"优惠劵状态，必须是 {value}\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n    @Schema(description = \"用户昵称\", example = \"芋艿\")\n    private String nickname;\n\n    @Schema(description = \"用户编号\", example = \"1\")\n    private Collection<Long> userIds;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 优惠劵 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CouponRespVO extends CouponBaseVO {\n\n    @Schema(description = \"优惠劵编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponSendReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.Set;\n\n@Schema(description = \"管理后台 - 优惠劵发放 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class CouponSendReqVO {\n\n    @Schema(description = \"优惠劵模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"优惠劵模板编号不能为空\")\n    private Long templateId;\n\n    @Schema(description = \"用户编号列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1, 2]\")\n    @NotEmpty(message = \"用户编号列表不能为空\")\n    private Set<Long> userIds;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;\n\n/**\n * 优惠劵模板 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class CouponTemplateBaseVO {\n\n    @Schema(description = \"优惠劵名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"春节送送送\")\n    @NotNull(message = \"优惠劵名不能为空\")\n    private String name;\n\n    @Schema(description = \"优惠券说明\", example = \"优惠券使用说明\")\n    private String description;\n\n    @Schema(description = \"发行总量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\") // -1 - 则表示不限制发放数量\n    @NotNull(message = \"发行总量不能为空\")\n    private Integer totalCount;\n\n    @Schema(description = \"每人限领个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"66\") // -1 - 则表示不限制\n    @NotNull(message = \"每人限领个数不能为空\")\n    private Integer takeLimitCount;\n\n    @Schema(description = \"领取方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"领取方式不能为空\")\n    private Integer takeType;\n\n    @Schema(description = \"是否设置满多少金额可用\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\") // 单位：分；0 - 不限制\n    @NotNull(message = \"是否设置满多少金额可用不能为空\")\n    private Integer usePrice;\n\n    @Schema(description = \"商品范围\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"商品范围不能为空\")\n    @InEnum(PromotionProductScopeEnum.class)\n    private Integer productScope;\n\n    @Schema(description = \"商品范围编号的数组\", example = \"[1, 3]\")\n    private List<Long> productScopeValues;\n\n    @Schema(description = \"生效日期类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"生效日期类型不能为空\")\n    @InEnum(CouponTemplateValidityTypeEnum.class)\n    private Integer validityType;\n\n    @Schema(description = \"固定日期 - 生效开始时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)\n    private LocalDateTime validStartTime;\n\n    @Schema(description = \"固定日期 - 生效结束时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)\n    private LocalDateTime validEndTime;\n\n    @Schema(description = \"领取日期 - 开始天数\")\n    @Min(value = 0L, message = \"开始天数必须大于 0\")\n    private Integer fixedStartTerm;\n\n    @Schema(description = \"领取日期 - 结束天数\")\n    @Min(value = 1L, message = \"开始天数必须大于 1\")\n    private Integer fixedEndTerm;\n\n    @Schema(description = \"优惠类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"优惠类型不能为空\")\n    @InEnum(PromotionDiscountTypeEnum.class)\n    private Integer discountType;\n\n    @Schema(description = \"折扣百分比\", example = \"80\") //  例如说，80% 为 80\n    private Integer discountPercent;\n\n    @Schema(description = \"优惠金额\", example = \"10\")\n    @Min(value = 0, message = \"优惠金额需要大于等于 0\")\n    private Integer discountPrice;\n\n    @Schema(description = \"折扣上限\", example = \"100\") // 单位：分，仅在 discountType 为 PERCENT 使用\n    private Integer discountLimitPrice;\n\n    @AssertTrue(message = \"商品范围编号的数组不能为空\")\n    @JsonIgnore\n    public boolean isProductScopeValuesValid() {\n        return Objects.equals(productScope, PromotionProductScopeEnum.ALL.getScope()) // 全部范围时，可以为空\n                || CollUtil.isNotEmpty(productScopeValues);\n    }\n\n    @AssertTrue(message = \"生效开始时间不能为空\")\n    @JsonIgnore\n    public boolean isValidStartTimeValid() {\n        return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.DATE.getType())\n                || validStartTime != null;\n    }\n\n    @AssertTrue(message = \"生效结束时间不能为空\")\n    @JsonIgnore\n    public boolean isValidEndTimeValid() {\n        return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.DATE.getType())\n                || validEndTime != null;\n    }\n\n    @AssertTrue(message = \"开始天数不能为空\")\n    @JsonIgnore\n    public boolean isFixedStartTermValid() {\n        return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.TERM.getType())\n                || fixedStartTerm != null;\n    }\n\n    @AssertTrue(message = \"结束天数不能为空\")\n    @JsonIgnore\n    public boolean isFixedEndTermValid() {\n        return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.TERM.getType())\n                || fixedEndTerm != null;\n    }\n\n    @AssertTrue(message = \"折扣百分比需要大于等于 1，小于等于 99\")\n    @JsonIgnore\n    public boolean isDiscountPercentValid() {\n        return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType())\n                || (discountPercent != null && discountPercent >= 1 && discountPercent<= 99);\n    }\n\n    @AssertTrue(message = \"优惠金额不能为空\")\n    @JsonIgnore\n    public boolean isDiscountPriceValid() {\n        return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PRICE.getType())\n                || discountPrice != null;\n    }\n\n    @AssertTrue(message = \"折扣上限不能为空\")\n    @JsonIgnore\n    public boolean isDiscountLimitPriceValid() {\n        return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType())\n                || discountLimitPrice != null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 优惠劵模板创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CouponTemplateCreateReqVO extends CouponTemplateBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 优惠劵模板分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CouponTemplatePageReqVO extends PageParam {\n\n    @Schema(description = \"优惠劵名\", example = \"你好\")\n    private String name;\n\n    @Schema(description = \"状态\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"优惠类型\", example = \"1\")\n    private Integer discountType;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n    @Schema(description = \"可以领取的类型\", example = \"[1, 2, 3]\")\n    @InEnum(value = CouponTakeTypeEnum.class, message = \"可以领取的类型，必须是 {value}\")\n    private List<Integer> canTakeTypes;\n\n    @Schema(description = \"商品范围\", example = \"1\")\n    @InEnum(value = PromotionProductScopeEnum.class, message = \"商品范围，必须是 {value}\")\n    private Integer productScope;\n\n    @Schema(description = \"商品范围编号\", example = \"1\")\n    private Long productScopeValue;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 优惠劵模板 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CouponTemplateRespVO extends CouponTemplateBaseVO {\n\n    @Schema(description = \"模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"领取优惠券的数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer takeCount;\n\n    @Schema(description = \"使用优惠券的次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Integer useCount;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 优惠劵模板更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class CouponTemplateUpdateReqVO extends CouponTemplateBaseVO {\n\n    @Schema(description = \"模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"模板编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateStatusReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 优惠劵模板更新状态 Request VO\")\n@Data\npublic class CouponTemplateUpdateStatusReqVO {\n\n    @Schema(description = \"优惠劵模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"优惠劵模板编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"状态不能为空\")\n    @InEnum(value = CommonStatusEnum.class, message = \"修改状态必须是 {value}\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/DiscountActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.discount;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;\nimport cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 限时折扣活动\")\n@RestController\n@RequestMapping(\"/promotion/discount-activity\")\n@Validated\npublic class DiscountActivityController {\n\n    @Resource\n    private DiscountActivityService discountActivityService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建限时折扣活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:discount-activity:create')\")\n    public CommonResult<Long> createDiscountActivity(@Valid @RequestBody DiscountActivityCreateReqVO createReqVO) {\n        return success(discountActivityService.createDiscountActivity(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新限时折扣活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:discount-activity:update')\")\n    public CommonResult<Boolean> updateDiscountActivity(@Valid @RequestBody DiscountActivityUpdateReqVO updateReqVO) {\n        discountActivityService.updateDiscountActivity(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/close\")\n    @Operation(summary = \"关闭限时折扣活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:discount-activity:close')\")\n    public CommonResult<Boolean> closeRewardActivity(@RequestParam(\"id\") Long id) {\n        discountActivityService.closeDiscountActivity(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除限时折扣活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:discount-activity:delete')\")\n    public CommonResult<Boolean> deleteDiscountActivity(@RequestParam(\"id\") Long id) {\n        discountActivityService.deleteDiscountActivity(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得限时折扣活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:discount-activity:query')\")\n    public CommonResult<DiscountActivityRespVO> getDiscountActivity(@RequestParam(\"id\") Long id) {\n        DiscountActivityDO discountActivity = discountActivityService.getDiscountActivity(id);\n        if (discountActivity == null) {\n            return success(null);\n        }\n        // 拼接结果\n        List<DiscountProductDO> discountProducts = discountActivityService.getDiscountProductsByActivityId(id);\n        return success(DiscountActivityConvert.INSTANCE.convert(discountActivity, discountProducts));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得限时折扣活动分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:discount-activity:query')\")\n    public CommonResult<PageResult<DiscountActivityRespVO>> getDiscountActivityPage(@Valid DiscountActivityPageReqVO pageVO) {\n        PageResult<DiscountActivityDO> pageResult = discountActivityService.getDiscountActivityPage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接数据\n        List<DiscountProductDO> products = discountActivityService.getDiscountProductsByActivityId(\n                convertSet(pageResult.getList(), DiscountActivityDO::getId));\n        return success(DiscountActivityConvert.INSTANCE.convertPage(pageResult, products));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n/**\n * 限时折扣活动 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class DiscountActivityBaseVO {\n\n    @Schema(description = \"活动标题\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"一个标题\")\n    @NotNull(message = \"活动标题不能为空\")\n    private String name;\n\n    @Schema(description = \"开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"开始时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"结束时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"备注\", example = \"我是备注\")\n    private String remark;\n\n    @Schema(description = \"商品\")\n    @Data\n    public static class Product {\n\n        @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        @NotNull(message = \"商品 SPU 编号不能为空\")\n        private Long spuId;\n\n        @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        @NotNull(message = \"商品 SKU 编号不能为空\")\n        private Long skuId;\n\n        @Schema(description = \"优惠类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        @NotNull(message = \"优惠类型不能为空\")\n        @InEnum(PromotionDiscountTypeEnum.class)\n        private Integer discountType;\n\n        @Schema(description = \"折扣百分比\", example = \"80\") // 例如说，80% 为 80\n        private Integer discountPercent;\n\n        @Schema(description = \"优惠金额\", example = \"10\")\n        @Min(value = 0, message = \"优惠金额需要大于等于 0\")\n        private Integer discountPrice;\n\n        @AssertTrue(message = \"折扣百分比需要大于等于 0.01%，小于等于 99.99%\")\n        @JsonIgnore\n        public boolean isDiscountPercentValid() {\n            return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType())\n                    || (discountPercent != null && discountPercent >= 1 && discountPercent <= 9999);\n        }\n\n        @AssertTrue(message = \"优惠金额不能为空\")\n        @JsonIgnore\n        public boolean isDiscountPriceValid() {\n            return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PRICE.getType())\n                    || discountPrice != null;\n        }\n\n    }\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 限时折扣活动创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiscountActivityCreateReqVO extends DiscountActivityBaseVO {\n\n    /**\n     * 商品列表\n     */\n    @NotEmpty(message = \"商品列表不能为空\")\n    @Valid\n    private List<Product> products;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 限时折扣活动分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiscountActivityPageReqVO extends PageParam {\n\n    @Schema(description = \"活动标题\", example = \"一个标题\")\n    private String name;\n\n    @Schema(description = \"活动状态\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 限时折扣活动 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiscountActivityRespVO extends DiscountActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"活动状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"限时折扣商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Product> products;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 限时折扣活动更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiscountActivityUpdateReqVO extends DiscountActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"活动编号不能为空\")\n    private Long id;\n\n    /**\n     * 商品列表\n     */\n    @NotEmpty(message = \"商品列表不能为空\")\n    @Valid\n    private List<Product> products;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/DiyPageController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.*;\nimport cn.iocoder.yudao.module.promotion.convert.diy.DiyPageConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;\nimport cn.iocoder.yudao.module.promotion.service.diy.DiyPageService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 装修页面\")\n@RestController\n@RequestMapping(\"/promotion/diy-page\")\n@Validated\npublic class DiyPageController {\n\n    @Resource\n    private DiyPageService diyPageService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建装修页面\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-page:create')\")\n    public CommonResult<Long> createDiyPage(@Valid @RequestBody DiyPageCreateReqVO createReqVO) {\n        return success(diyPageService.createDiyPage(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新装修页面\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-page:update')\")\n    public CommonResult<Boolean> updateDiyPage(@Valid @RequestBody DiyPageUpdateReqVO updateReqVO) {\n        diyPageService.updateDiyPage(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除装修页面\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-page:delete')\")\n    public CommonResult<Boolean> deleteDiyPage(@RequestParam(\"id\") Long id) {\n        diyPageService.deleteDiyPage(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得装修页面\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-page:query')\")\n    public CommonResult<DiyPageRespVO> getDiyPage(@RequestParam(\"id\") Long id) {\n        DiyPageDO diyPage = diyPageService.getDiyPage(id);\n        return success(DiyPageConvert.INSTANCE.convert(diyPage));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得装修页面列表\")\n    @Parameter(name = \"ids\", description = \"编号列表\", required = true, example = \"1024,2048\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-page:query')\")\n    public CommonResult<List<DiyPageRespVO>> getDiyPageList(@RequestParam(\"ids\") Collection<Long> ids) {\n        List<DiyPageDO> list = diyPageService.getDiyPageList(ids);\n        return success(DiyPageConvert.INSTANCE.convertList(list));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得装修页面分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-page:query')\")\n    public CommonResult<PageResult<DiyPageRespVO>> getDiyPagePage(@Valid DiyPagePageReqVO pageVO) {\n        PageResult<DiyPageDO> pageResult = diyPageService.getDiyPagePage(pageVO);\n        return success(DiyPageConvert.INSTANCE.convertPage(pageResult));\n    }\n\n    @GetMapping(\"/get-property\")\n    @Operation(summary = \"获得装修页面属性\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-page:query')\")\n    public CommonResult<DiyPagePropertyRespVO> getDiyPageProperty(@RequestParam(\"id\") Long id) {\n        DiyPageDO diyPage = diyPageService.getDiyPage(id);\n        return success(DiyPageConvert.INSTANCE.convertPropertyVo(diyPage));\n    }\n\n    @PutMapping(\"/update-property\")\n    @Operation(summary = \"更新装修页面属性\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-page:update')\")\n    public CommonResult<Boolean> updateDiyPageProperty(@Valid @RequestBody DiyPagePropertyUpdateRequestVO updateReqVO) {\n        diyPageService.updateDiyPageProperty(updateReqVO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/DiyTemplateController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.*;\nimport cn.iocoder.yudao.module.promotion.convert.diy.DiyTemplateConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;\nimport cn.iocoder.yudao.module.promotion.service.diy.DiyPageService;\nimport cn.iocoder.yudao.module.promotion.service.diy.DiyTemplateService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 装修模板\")\n@RestController\n@RequestMapping(\"/promotion/diy-template\")\n@Validated\npublic class DiyTemplateController {\n\n    @Resource\n    private DiyTemplateService diyTemplateService;\n    @Resource\n    private DiyPageService diyPageService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建装修模板\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-template:create')\")\n    public CommonResult<Long> createDiyTemplate(@Valid @RequestBody DiyTemplateCreateReqVO createReqVO) {\n        return success(diyTemplateService.createDiyTemplate(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新装修模板\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-template:update')\")\n    public CommonResult<Boolean> updateDiyTemplate(@Valid @RequestBody DiyTemplateUpdateReqVO updateReqVO) {\n        diyTemplateService.updateDiyTemplate(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/use\")\n    @Operation(summary = \"使用装修模板\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-template:use')\")\n    public CommonResult<Boolean> useDiyTemplate(@RequestParam(\"id\") Long id) {\n        diyTemplateService.useDiyTemplate(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除装修模板\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-template:delete')\")\n    public CommonResult<Boolean> deleteDiyTemplate(@RequestParam(\"id\") Long id) {\n        diyTemplateService.deleteDiyTemplate(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得装修模板\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-template:query')\")\n    public CommonResult<DiyTemplateRespVO> getDiyTemplate(@RequestParam(\"id\") Long id) {\n        DiyTemplateDO diyTemplate = diyTemplateService.getDiyTemplate(id);\n        return success(DiyTemplateConvert.INSTANCE.convert(diyTemplate));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得装修模板分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-template:query')\")\n    public CommonResult<PageResult<DiyTemplateRespVO>> getDiyTemplatePage(@Valid DiyTemplatePageReqVO pageVO) {\n        PageResult<DiyTemplateDO> pageResult = diyTemplateService.getDiyTemplatePage(pageVO);\n        return success(DiyTemplateConvert.INSTANCE.convertPage(pageResult));\n    }\n\n    // TODO @疯狂：这个要不和 getDiyTemplate 合并，然后 DiyTemplateRespVO 里面直接把 DiyPagePropertyRespVO 也加上。减少 VO 好了，管理后台 get 多返回点数据，也问题不大的。目的，还是想尽可能降低大家的理解成本哈；\n    @GetMapping(\"/get-property\")\n    @Operation(summary = \"获得装修模板属性\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-template:query')\")\n    public CommonResult<DiyTemplatePropertyRespVO> getDiyTemplateProperty(@RequestParam(\"id\") Long id) {\n        DiyTemplateDO diyTemplate = diyTemplateService.getDiyTemplate(id);\n        List<DiyPageDO> pages = diyPageService.getDiyPageByTemplateId(id);\n        return success(DiyTemplateConvert.INSTANCE.convertPropertyVo(diyTemplate, pages));\n    }\n\n    // TODO @疯狂：这个接口，要不和 useDiyTemplate 合并成一个，然后 VO 改成我们新的 VO 规范。不改的字段，就不传递。\n    @PutMapping(\"/update-property\")\n    @Operation(summary = \"更新装修模板属性\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:diy-template:update')\")\n    public CommonResult<Boolean> updateDiyTemplateProperty(@Valid @RequestBody DiyTemplatePropertyUpdateRequestVO updateReqVO) {\n        diyTemplateService.updateDiyTemplateProperty(updateReqVO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n/**\n * 装修页面 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class DiyPageBaseVO {\n\n    @Schema(description = \"装修模板编号\", example = \"26179\")\n    private Long templateId;\n\n    @Schema(description = \"页面名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    @NotNull(message = \"页面名称不能为空\")\n    private String name;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"预览图\")\n    private List<String> previewPicUrls;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 装修页面创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyPageCreateReqVO extends DiyPageBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPagePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 装修页面分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyPagePageReqVO extends PageParam {\n\n    @Schema(description = \"页面名称\", example = \"王五\")\n    private String name;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPagePropertyRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 装修页面属性 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyPagePropertyRespVO extends DiyPageBaseVO {\n\n    @Schema(description = \"装修页面编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31209\")\n    private Long id;\n\n    @Schema(description = \"页面属性\", example = \"[]\")\n    private String property;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPagePropertyUpdateRequestVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 装修页面属性更新 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class DiyPagePropertyUpdateRequestVO {\n\n    @Schema(description = \"装修页面编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31209\")\n    @NotNull(message = \"装修页面编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"页面属性，JSON 格式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n    @NotBlank(message = \"页面属性不能为空\")\n    private String property;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 装修页面 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyPageRespVO extends DiyPageBaseVO {\n\n    @Schema(description = \"装修页面编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"12082\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 装修页面更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyPageUpdateReqVO extends DiyPageBaseVO {\n\n    @Schema(description = \"装修页面编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"12082\")\n    @NotNull(message = \"装修页面编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.util.List;\n\n/**\n * 装修模板 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class DiyTemplateBaseVO {\n\n    @Schema(description = \"模板名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"默认主题\")\n    @NotEmpty(message = \"模板名称不能为空\")\n    private String name;\n\n    @Schema(description = \"备注\", example = \"默认主题\")\n    private String remark;\n\n    @Schema(description = \"预览图\", example = \"[https://www.iocoder.cn/1.jpg]\")\n    private List<String> previewPicUrls;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 装修模板创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyTemplateCreateReqVO extends DiyTemplateBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplatePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 装修模板分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyTemplatePageReqVO extends PageParam {\n\n    @Schema(description = \"模板名称\", example = \"默认主题\")\n    private String name;\n\n    @Schema(description = \"是否使用\", example = \"true\")\n    private Boolean used;\n\n    @Schema(description = \"使用时间\", example = \"使用时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] usedTime;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplatePropertyRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePropertyRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 装修模板属性 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyTemplatePropertyRespVO extends DiyTemplateBaseVO {\n\n    @Schema(description = \"装修模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31209\")\n    private Long id;\n\n    @Schema(description = \"模板属性，JSON 格式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n    private String property;\n\n    @Schema(description = \"模板页面\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[]\")\n    private List<DiyPagePropertyRespVO> pages;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplatePropertyUpdateRequestVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 装修模板属性更新 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class DiyTemplatePropertyUpdateRequestVO {\n\n    @Schema(description = \"装修模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31209\")\n    @NotNull(message = \"装修模板编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"模板属性，JSON 格式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n    @NotBlank(message = \"模板属性不能为空\")\n    private String property;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 装修模板 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyTemplateRespVO extends DiyTemplateBaseVO {\n\n    @Schema(description = \"装修模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31209\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"是否使用\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean used;\n\n    @Schema(description = \"使用时间\", example = \"使用时间\")\n    private LocalDateTime usedTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 装修模板更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DiyTemplateUpdateReqVO extends DiyTemplateBaseVO {\n\n    @Schema(description = \"装修模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31209\")\n    @NotNull(message = \"装修模板编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuConversationController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.kefu;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;\nimport cn.iocoder.yudao.module.promotion.service.kefu.KeFuConversationService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\n\n@Tag(name = \"管理后台 - 客服会话\")\n@RestController\n@RequestMapping(\"/promotion/kefu-conversation\")\n@Validated\npublic class KeFuConversationController {\n\n    @Resource\n    private KeFuConversationService conversationService;\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得客服会话\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:kefu-conversation:query')\")\n    public CommonResult<KeFuConversationRespVO> getConversation(@RequestParam(\"id\") Long id) {\n        KeFuConversationDO conversation = conversationService.getConversation(id);\n        if (conversation == null) {\n            return success(null);\n        }\n\n        // 拼接数据\n        KeFuConversationRespVO result = BeanUtils.toBean(conversation, KeFuConversationRespVO.class);\n        MemberUserRespDTO memberUser = memberUserApi.getUser(conversation.getUserId()).getCheckedData();\n        if (memberUser != null) {\n            result.setUserAvatar(memberUser.getAvatar()).setUserNickname(memberUser.getNickname());\n        }\n        return success(result);\n    }\n\n    @PutMapping(\"/update-conversation-pinned\")\n    @Operation(summary = \"置顶/取消置顶客服会话\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:kefu-conversation:update')\")\n    public CommonResult<Boolean> updateConversationPinned(@Valid @RequestBody KeFuConversationUpdatePinnedReqVO updateReqVO) {\n        conversationService.updateConversationPinnedByAdmin(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除客服会话\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:kefu-conversation:delete')\")\n    public CommonResult<Boolean> deleteConversation(@RequestParam(\"id\") Long id) {\n        conversationService.deleteKefuConversation(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得客服会话列表\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:kefu-conversation:query')\")\n    public CommonResult<List<KeFuConversationRespVO>> getConversationList() {\n        // 查询会话列表\n        List<KeFuConversationRespVO> respList = BeanUtils.toBean(conversationService.getKefuConversationList(),\n                KeFuConversationRespVO.class);\n\n        // 拼接数据\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(respList, KeFuConversationRespVO::getUserId));\n        respList.forEach(item-> findAndThen(userMap, item.getUserId(),\n                memberUser-> item.setUserAvatar(memberUser.getAvatar()).setUserNickname(memberUser.getNickname())));\n        return success(respList);\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuMessageController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.kefu;\n\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageListReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;\nimport cn.iocoder.yudao.module.promotion.service.kefu.KeFuMessageService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - 客服消息\")\n@RestController\n@RequestMapping(\"/promotion/kefu-message\")\n@Validated\npublic class KeFuMessageController {\n\n    @Resource\n    private KeFuMessageService messageService;\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/send\")\n    @Operation(summary = \"发送客服消息\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:kefu-message:send')\")\n    public CommonResult<Long> sendKeFuMessage(@Valid @RequestBody KeFuMessageSendReqVO sendReqVO) {\n        sendReqVO.setSenderId(getLoginUserId()).setSenderType(UserTypeEnum.ADMIN.getValue()); // 设置用户编号和类型\n        return success(messageService.sendKefuMessage(sendReqVO));\n    }\n\n    @PutMapping(\"/update-read-status\")\n    @Operation(summary = \"更新客服消息已读状态\")\n    @Parameter(name = \"conversationId\", description = \"会话编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:kefu-message:update')\")\n    public CommonResult<Boolean> updateKeFuMessageReadStatus(@RequestParam(\"conversationId\") Long conversationId) {\n        messageService.updateKeFuMessageReadStatus(conversationId, getLoginUserId(), UserTypeEnum.ADMIN.getValue());\n        return success(true);\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得客服消息列表\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:kefu-message:query')\")\n    public CommonResult<List<KeFuMessageRespVO>> getKeFuMessageList(@Valid KeFuMessageListReqVO pageReqVO) {\n        // 获得数据\n        List<KeFuMessageDO> list = messageService.getKeFuMessageList(pageReqVO);\n\n        // 拼接数据\n        List<KeFuMessageRespVO> result = BeanUtils.toBean(list, KeFuMessageRespVO.class);\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(filterList(result,\n                item -> UserTypeEnum.ADMIN.getValue().equals(item.getSenderType())), KeFuMessageRespVO::getSenderId));\n        result.forEach(item -> findAndThen(userMap, item.getSenderId(), user -> item.setSenderAvatar(user.getAvatar())));\n        return success(result);\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/conversation/KeFuConversationRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 客服会话 Response VO\")\n@Data\npublic class KeFuConversationRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24988\")\n    private Long id;\n\n    @Schema(description = \"会话所属用户\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8300\")\n    private Long userId;\n    @Schema(description = \"会话所属用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://yudao.com/images/avatar.jpg\")\n    private String userAvatar;\n    @Schema(description = \"会话所属用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String userNickname;\n\n    @Schema(description = \"最后聊天时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime lastMessageTime;\n\n    @Schema(description = \"最后聊天内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"嗨，您好啊\")\n    private String lastMessageContent;\n\n    @Schema(description = \"最后发送的消息类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer lastMessageContentType;\n\n    @Schema(description = \"管理端置顶\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    private Boolean adminPinned;\n\n    @Schema(description = \"用户是否可见\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean userDeleted;\n\n    @Schema(description = \"管理员是否可见\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean adminDeleted;\n\n    @Schema(description = \"管理员未读消息数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6\")\n    private Integer adminUnreadMessageCount;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/conversation/KeFuConversationUpdatePinnedReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 客服会话置顶 Request VO\")\n@Data\npublic class KeFuConversationUpdatePinnedReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23202\")\n    @NotNull(message = \"会话编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"管理端置顶\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"false\")\n    @NotNull(message = \"管理端置顶不能为空\")\n    private Boolean adminPinned;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/message/KeFuMessageListReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 客服消息列表 Request VO\")\n@Data\npublic class KeFuMessageListReqVO {\n\n    private static final Integer LIMIT = 10;\n\n    @Schema(description = \"会话编号\", example = \"12580\")\n    @NotNull(message = \"会话编号不能为空\")\n    private Long conversationId;\n\n    @Schema(description = \"发送时间\", example = \"2024-03-27 12:00:00\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"每次查询条数，最大值为 100\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"每次查询条数不能为空\")\n    @Min(value = 1, message = \"每次查询条数最小值为 1\")\n    @Max(value = 200, message = \"每次查询最大值为 200\")\n    private Integer limit = LIMIT;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/message/KeFuMessageRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 客服消息 Response VO\")\n@Data\npublic class KeFuMessageRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23202\")\n    private Long id;\n\n    @Schema(description = \"会话编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"12580\")\n    private Long conversationId;\n\n    @Schema(description = \"发送人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24571\")\n    private Long senderId;\n    @Schema(description = \"发送人头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://yudao.com/images/avatar.jpg\")\n    private String senderAvatar;\n\n    @Schema(description = \"发送人类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer senderType;\n\n    @Schema(description = \"接收人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29124\")\n    private Long receiverId;\n\n    @Schema(description = \"接收人类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer receiverType;\n\n    @Schema(description = \"消息类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer contentType;\n\n    @Schema(description = \"消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private String content;\n\n    @Schema(description = \"是否已读\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Boolean readStatus;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/message/KeFuMessageSendReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 发送客服消息 Request VO\")\n@Data\npublic class KeFuMessageSendReqVO {\n\n    @Schema(description = \"会话编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"12580\")\n    @NotNull(message = \"会话编号不能为空\")\n    private Long conversationId;\n\n    @Schema(description = \"消息类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"消息类型不能为空\")\n    private Integer contentType;\n\n    @Schema(description = \"消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"消息不能为空\")\n    private String content;\n\n    // ========== 后端设置的参数，前端无需传递 ==========\n\n    @Schema(description = \"发送人编号\", example = \"24571\", hidden = true)\n    private Long senderId;\n    @Schema(description = \"发送人类型\", example = \"1\", hidden = true)\n    private Integer senderType;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/PointActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.point;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO;\nimport cn.iocoder.yudao.module.promotion.service.point.PointActivityService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\n\n@Tag(name = \"管理后台 - 积分商城活动\")\n@RestController\n@RequestMapping(\"/promotion/point-activity\")\n@Validated\npublic class PointActivityController {\n\n    @Resource\n    private PointActivityService pointActivityService;\n    @Resource\n    private ProductSpuApi productSpuApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建积分商城活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:point-activity:create')\")\n    public CommonResult<Long> createPointActivity(@Valid @RequestBody PointActivitySaveReqVO createReqVO) {\n        return success(pointActivityService.createPointActivity(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新积分商城活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:point-activity:update')\")\n    public CommonResult<Boolean> updatePointActivity(@Valid @RequestBody PointActivitySaveReqVO updateReqVO) {\n        pointActivityService.updatePointActivity(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/close\")\n    @Operation(summary = \"关闭积分商城活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:point-activity:close')\")\n    public CommonResult<Boolean> closeSeckillActivity(@RequestParam(\"id\") Long id) {\n        pointActivityService.closePointActivity(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除积分商城活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:point-activity:delete')\")\n    public CommonResult<Boolean> deletePointActivity(@RequestParam(\"id\") Long id) {\n        pointActivityService.deletePointActivity(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得积分商城活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:point-activity:query')\")\n    public CommonResult<PointActivityRespVO> getPointActivity(@RequestParam(\"id\") Long id) {\n        PointActivityDO pointActivity = pointActivityService.getPointActivity(id);\n        if (pointActivity == null) {\n            return success(null);\n        }\n\n        List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(Collections.singletonList(id));\n        PointActivityRespVO respVO = BeanUtils.toBean(pointActivity, PointActivityRespVO.class);\n        respVO.setProducts(BeanUtils.toBean(products, PointProductRespVO.class));\n        return success(respVO);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得积分商城活动分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:point-activity:query')\")\n    public CommonResult<PageResult<PointActivityRespVO>> getPointActivityPage(@Valid PointActivityPageReqVO pageReqVO) {\n        PageResult<PointActivityDO> pageResult = pointActivityService.getPointActivityPage(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接数据\n        List<PointActivityRespVO> resultList = buildPointActivityRespVOList(pageResult.getList());\n        return success(new PageResult<>(resultList, pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/list-by-ids\")\n    @Operation(summary = \"获得积分商城活动列表，基于活动编号数组\")\n    @Parameter(name = \"ids\", description = \"活动编号数组\", required = true, example = \"[1024, 1025]\")\n    public CommonResult<List<PointActivityRespVO>> getPointActivityListByIds(@RequestParam(\"ids\") List<Long> ids) {\n        // 1. 获得开启的活动列表\n        List<PointActivityDO> activityList = pointActivityService.getPointActivityListByIds(ids);\n        activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));\n        if (CollUtil.isEmpty(activityList)) {\n            return success(Collections.emptyList());\n        }\n        // 2. 拼接返回\n        List<PointActivityRespVO> result = buildPointActivityRespVOList(activityList);\n        return success(result);\n    }\n\n    private List<PointActivityRespVO> buildPointActivityRespVOList(List<PointActivityDO> activityList) {\n        List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(\n                convertSet(activityList, PointActivityDO::getId));\n        Map<Long, List<PointProductDO>> productsMap = convertMultiMap(products, PointProductDO::getActivityId);\n        Map<Long, ProductSpuRespDTO> spuMap = productSpuApi.getSpuMap(\n                convertSet(activityList, PointActivityDO::getSpuId));\n        List<PointActivityRespVO> result = BeanUtils.toBean(activityList, PointActivityRespVO.class);\n        result.forEach(activity -> {\n            // 设置 product 信息\n            PointProductDO minProduct = getMinObject(productsMap.get(activity.getId()), PointProductDO::getPoint);\n            assert minProduct != null;\n            activity.setPoint(minProduct.getPoint()).setPrice(minProduct.getPrice());\n            findAndThen(spuMap, activity.getSpuId(),\n                    spu -> activity.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n        });\n        return result;\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 积分商城活动分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class PointActivityPageReqVO extends PageParam {\n\n    @Schema(description = \"活动状态\", example = \"2\")\n    private Integer status;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductRespVO;\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 积分商城活动 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class PointActivityRespVO {\n\n    @Schema(description = \"积分商城活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11373\")\n    @ExcelProperty(\"积分商城活动编号\")\n    private Long id;\n\n    @Schema(description = \"积分商城活动商品\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19509\")\n    @ExcelProperty(\"积分商城活动商品\")\n    private Long spuId;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"活动状态\")\n    private Integer status;\n\n    @Schema(description = \"积分商城活动库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"积分商城活动库存\")\n    private Integer stock; // 剩余库存积分兑换时扣减\n\n    @Schema(description = \"积分商城活动总库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"积分商城活动总库存\")\n    private Integer totalStock;\n\n    @Schema(description = \"备注\", example = \"你说的对\")\n    @ExcelProperty(\"备注\")\n    private String remark;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"排序\")\n    private Integer sort;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n    @Schema(description = \"积分商城商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<PointProductRespVO> products;\n\n    // ========== 商品字段 ==========\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取\n            example = \"618大促\")\n    private String spuName;\n    @Schema(description = \"商品主图\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取\n            example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n    @Schema(description = \"商品市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取\n            example = \"50\")\n    private Integer marketPrice;\n\n    //======================= 显示所需兑换积分最少的 sku 信息 =======================\n\n    @Schema(description = \"兑换积分\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Integer point;\n\n    @Schema(description = \"兑换金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15860\")\n    private Integer price;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivitySaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductSaveReqVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 积分商城活动新增/修改 Request VO\")\n@Data\npublic class PointActivitySaveReqVO {\n\n    @Schema(description = \"积分商城活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11373\")\n    private Long id;\n\n    @Schema(description = \"积分商城活动商品\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19509\")\n    @NotNull(message = \"积分商城活动商品不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"备注\", example = \"你说的对\")\n    private String remark;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"积分商城商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<PointProductSaveReqVO> products;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/product/PointProductRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 积分商城商品 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class PointProductRespVO {\n\n    @Schema(description = \"积分商城商品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31718\")\n    private Long id;\n\n    @Schema(description = \"积分商城活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29388\")\n    private Long activityId;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8112\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2736\")\n    private Long skuId;\n\n    @Schema(description = \"可兑换数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3926\")\n    private Integer count;\n\n    @Schema(description = \"兑换积分\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Integer point;\n\n    @Schema(description = \"兑换金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15860\")\n    private Integer price;\n\n    @Schema(description = \"积分商城商品库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer stock;\n\n    @Schema(description = \"积分商城商品状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer activityStatus;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/product/PointProductSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 积分商城商品新增/修改 Request VO\")\n@Data\npublic class PointProductSaveReqVO {\n\n    @Schema(description = \"积分商城商品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31718\")\n    private Long id;\n\n    @Schema(description = \"积分商城活动 id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"29388\")\n    @NotNull(message = \"积分商城活动 id不能为空\")\n    private Long activityId;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"8112\")\n    @NotNull(message = \"商品 SPU 编号不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2736\")\n    @NotNull(message = \"商品 SKU 编号不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"可兑换数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3926\")\n    @NotNull(message = \"可兑换数量不能为空\")\n    private Integer count;\n\n    @Schema(description = \"兑换积分\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"兑换积分不能为空\")\n    private Integer point;\n\n    @Schema(description = \"兑换金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15860\")\n    @NotNull(message = \"兑换金额，单位：分不能为空\")\n    private Integer price;\n\n    @Schema(description = \"积分商城商品库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    @NotNull(message = \"积分商城商品不能为空\")\n    private Integer stock;\n\n    @Schema(description = \"积分商城商品状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @NotNull(message = \"积分商城商品状态不能为空\")\n    private Integer activityStatus;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/RewardActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.reward;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;\nimport cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 满减送活动\")\n@RestController\n@RequestMapping(\"/promotion/reward-activity\")\n@Validated\npublic class RewardActivityController {\n\n    @Resource\n    private RewardActivityService rewardActivityService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建满减送活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:reward-activity:create')\")\n    public CommonResult<Long> createRewardActivity(@Valid @RequestBody RewardActivityCreateReqVO createReqVO) {\n        return success(rewardActivityService.createRewardActivity(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新满减送活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:reward-activity:update')\")\n    public CommonResult<Boolean> updateRewardActivity(@Valid @RequestBody RewardActivityUpdateReqVO updateReqVO) {\n        rewardActivityService.updateRewardActivity(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/close\")\n    @Operation(summary = \"关闭满减送活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:reward-activity:close')\")\n    public CommonResult<Boolean> closeRewardActivity(@RequestParam(\"id\") Long id) {\n        rewardActivityService.closeRewardActivity(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除满减送活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:reward-activity:delete')\")\n    public CommonResult<Boolean> deleteRewardActivity(@RequestParam(\"id\") Long id) {\n        rewardActivityService.deleteRewardActivity(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得满减送活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:reward-activity:query')\")\n    public CommonResult<RewardActivityRespVO> getRewardActivity(@RequestParam(\"id\") Long id) {\n        RewardActivityDO rewardActivity = rewardActivityService.getRewardActivity(id);\n        return success(BeanUtils.toBean(rewardActivity, RewardActivityRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得满减送活动分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:reward-activity:query')\")\n    public CommonResult<PageResult<RewardActivityRespVO>> getRewardActivityPage(@Valid RewardActivityPageReqVO pageVO) {\n        PageResult<RewardActivityDO> pageResult = rewardActivityService.getRewardActivityPage(pageVO);\n        return success(BeanUtils.toBean(pageResult, RewardActivityRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.Future;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * 满减送活动 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class RewardActivityBaseVO {\n\n    @Schema(description = \"活动标题\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"满啦满啦\")\n    @NotNull(message = \"活动标题不能为空\")\n    private String name;\n\n    @Schema(description = \"开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"开始时间不能为空\")\n    private LocalDateTime startTime;\n\n    @Schema(description = \"结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"结束时间不能为空\")\n    @Future(message = \"结束时间必须大于当前时间\")\n    private LocalDateTime endTime;\n\n    @Schema(description = \"备注\", example = \"biubiubiu\")\n    private String remark;\n\n    @Schema(description = \"条件类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"条件类型不能为空\")\n    @InEnum(value = PromotionConditionTypeEnum.class, message = \"条件类型必须是 {value}\")\n    private Integer conditionType;\n\n    @Schema(description = \"商品范围\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"商品范围不能为空\")\n    @InEnum(value = PromotionProductScopeEnum.class, message = \"商品范围必须是 {value}\")\n    private Integer productScope;\n\n    @Schema(description = \"商品范围编号的数组\", example = \"[1, 3]\")\n    private List<Long> productScopeValues;\n\n    /**\n     * 优惠规则的数组\n     */\n    @Valid // 校验下子对象\n    private List<Rule> rules;\n\n    @Schema(description = \"优惠规则\")\n    @Data\n    public static class Rule {\n\n        @Schema(description = \"优惠门槛\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\") // 1. 满 N 元，单位：分; 2. 满 N 件\n        @Min(value = 1L, message = \"优惠门槛必须大于等于 1\")\n        private Integer limit;\n\n        @Schema(description = \"优惠价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        @Min(value = 1L, message = \"优惠价格必须大于等于 1\")\n        private Integer discountPrice;\n\n        @Schema(description = \"是否包邮\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n        @NotNull(message = \"规则是否包邮不能为空\")\n        private Boolean freeDelivery;\n\n        @Schema(description = \"赠送的积分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer point;\n\n        @Schema(description = \"赠送的优惠劵编号的数组\")\n        private Map<Long, Integer> giveCouponTemplateCounts;\n\n        @AssertTrue(message = \"赠送的积分不能小于 0\")\n        @JsonIgnore\n        public boolean isPointValid() {\n            return point == null || point >= 0;\n        }\n\n    }\n\n    @AssertTrue(message = \"商品范围编号的数组不能为空\")\n    @JsonIgnore\n    public boolean isProductScopeValuesValid() {\n        return Objects.equals(productScope, PromotionProductScopeEnum.ALL.getScope()) // 全部范围时，可以为空\n                || CollUtil.isNotEmpty(productScopeValues);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 满减送活动创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class RewardActivityCreateReqVO extends RewardActivityBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 满减送活动分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class RewardActivityPageReqVO extends PageParam {\n\n    @Schema(description = \"活动标题\", example = \"满啦满啦\")\n    private String name;\n\n    @Schema(description = \"活动状态\", example = \"1\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 满减送活动 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class RewardActivityRespVO extends RewardActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 满减送活动更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class RewardActivityUpdateReqVO extends RewardActivityBaseVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"活动编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.*;\nimport cn.iocoder.yudao.module.promotion.convert.seckill.SeckillActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;\nimport cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 秒杀活动\")\n@RestController\n@RequestMapping(\"/promotion/seckill-activity\")\n@Validated\npublic class SeckillActivityController {\n\n    @Resource\n    private SeckillActivityService seckillActivityService;\n    @Resource\n    private ProductSpuApi productSpuApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建秒杀活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-activity:create')\")\n    public CommonResult<Long> createSeckillActivity(@Valid @RequestBody SeckillActivityCreateReqVO createReqVO) {\n        return success(seckillActivityService.createSeckillActivity(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新秒杀活动\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-activity:update')\")\n    public CommonResult<Boolean> updateSeckillActivity(@Valid @RequestBody SeckillActivityUpdateReqVO updateReqVO) {\n        seckillActivityService.updateSeckillActivity(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/close\")\n    @Operation(summary = \"关闭秒杀活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-activity:close')\")\n    public CommonResult<Boolean> closeSeckillActivity(@RequestParam(\"id\") Long id) {\n        seckillActivityService.closeSeckillActivity(id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除秒杀活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-activity:delete')\")\n    public CommonResult<Boolean> deleteSeckillActivity(@RequestParam(\"id\") Long id) {\n        seckillActivityService.deleteSeckillActivity(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得秒杀活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-activity:query')\")\n    public CommonResult<SeckillActivityDetailRespVO> getSeckillActivity(@RequestParam(\"id\") Long id) {\n        SeckillActivityDO activity = seckillActivityService.getSeckillActivity(id);\n        List<SeckillProductDO> products = seckillActivityService.getSeckillProductListByActivityId(id);\n        return success(SeckillActivityConvert.INSTANCE.convert(activity, products));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得秒杀活动分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-activity:query')\")\n    public CommonResult<PageResult<SeckillActivityRespVO>> getSeckillActivityPage(@Valid SeckillActivityPageReqVO pageVO) {\n        // 查询活动列表\n        PageResult<SeckillActivityDO> pageResult = seckillActivityService.getSeckillActivityPage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接数据\n        List<SeckillProductDO> products = seckillActivityService.getSeckillProductListByActivityIds(\n                convertSet(pageResult.getList(), SeckillActivityDO::getId));\n        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(\n                convertSet(pageResult.getList(), SeckillActivityDO::getSpuId)).getCheckedData();\n        return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult, products, spuList));\n    }\n\n    @GetMapping(\"/list-by-ids\")\n    @Operation(summary = \"获得秒杀活动列表，基于活动编号数组\")\n    @Parameter(name = \"ids\", description = \"活动编号数组\", required = true, example = \"[1024, 1025]\")\n    public CommonResult<List<SeckillActivityRespVO>> getSeckillActivityListByIds(@RequestParam(\"ids\") List<Long> ids) {\n        // 1. 获得开启的活动列表\n        List<SeckillActivityDO> activityList = seckillActivityService.getSeckillActivityListByIds(ids);\n        activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));\n        if (CollUtil.isEmpty(activityList)) {\n            return success(Collections.emptyList());\n        }\n        // 2. 拼接返回\n        List<SeckillProductDO> productList = seckillActivityService.getSeckillProductListByActivityIds(\n                convertList(activityList, SeckillActivityDO::getId));\n        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId)).getCheckedData();\n        return success(SeckillActivityConvert.INSTANCE.convertList(activityList, productList, spuList));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.*;\nimport cn.iocoder.yudao.module.promotion.convert.seckill.SeckillConfigConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;\nimport cn.iocoder.yudao.module.promotion.service.seckill.SeckillConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 秒杀时段\")\n@RestController\n@RequestMapping(\"/promotion/seckill-config\")\n@Validated\npublic class SeckillConfigController {\n\n    @Resource\n    private SeckillConfigService seckillConfigService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建秒杀时段\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-config:create')\")\n    public CommonResult<Long> createSeckillConfig(@Valid @RequestBody SeckillConfigCreateReqVO createReqVO) {\n        return success(seckillConfigService.createSeckillConfig(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新秒杀时段\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-config:update')\")\n    public CommonResult<Boolean> updateSeckillConfig(@Valid @RequestBody SeckillConfigUpdateReqVO updateReqVO) {\n        seckillConfigService.updateSeckillConfig(updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-status\")\n    @Operation(summary = \"修改时段配置状态\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-config:update')\")\n    public CommonResult<Boolean> updateSeckillConfigStatus(@Valid @RequestBody SeckillConfigUpdateStatusReqVo reqVO) {\n        seckillConfigService.updateSeckillConfigStatus(reqVO.getId(), reqVO.getStatus());\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除秒杀时段\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-config:delete')\")\n    public CommonResult<Boolean> deleteSeckillConfig(@RequestParam(\"id\") Long id) {\n        seckillConfigService.deleteSeckillConfig(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得秒杀时段\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-config:query')\")\n    public CommonResult<SeckillConfigRespVO> getSeckillConfig(@RequestParam(\"id\") Long id) {\n        SeckillConfigDO seckillConfig = seckillConfigService.getSeckillConfig(id);\n        return success(SeckillConfigConvert.INSTANCE.convert(seckillConfig));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得所有秒杀时段列表\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-config:query')\")\n    public CommonResult<List<SeckillConfigRespVO>> getSeckillConfigList() {\n        List<SeckillConfigDO> list = seckillConfigService.getSeckillConfigList();\n        return success(SeckillConfigConvert.INSTANCE.convertList(list));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得所有开启状态的秒杀时段精简列表\", description = \"主要用于前端的下拉选项\")\n    public CommonResult<List<SeckillConfigSimpleRespVO>> getSeckillConfigSimpleList() {\n        List<SeckillConfigDO> list = seckillConfigService.getSeckillConfigListByStatus(\n                CommonStatusEnum.ENABLE.getStatus());\n        return success(SeckillConfigConvert.INSTANCE.convertList1(list));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得秒杀时间段分页\")\n    @PreAuthorize(\"@ss.hasPermission('promotion:seckill-config:query')\")\n    public CommonResult<PageResult<SeckillConfigRespVO>> getSeckillActivityPage(@Valid SeckillConfigPageReqVO pageVO) {\n        PageResult<SeckillConfigDO> pageResult = seckillConfigService.getSeckillConfigPage(pageVO);\n        return success(SeckillConfigConvert.INSTANCE.convertPage(pageResult));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n/**\n * 秒杀活动基地签证官\n * 秒杀活动 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n *\n * @author HUIHUI\n */\n@Data\npublic class SeckillActivityBaseVO {\n\n    @Schema(description = \"秒杀活动商品 id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[121,1212]\")\n    @NotNull(message = \"秒杀活动商品不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"秒杀活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"618大促\")\n    @NotNull(message = \"秒杀活动名称不能为空\")\n    private String name;\n\n    @Schema(description = \"备注\", example = \"清仓大甩卖割韭菜\")\n    private String remark;\n\n    @Schema(description = \"活动开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"活动开始时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"活动结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"活动结束时间不能为空\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"秒杀时段 id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1,2,3]\")\n    @NotNull(message = \"秒杀时段不能为空\")\n    private List<Long> configIds;\n\n    @Schema(description = \"总限购数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"12877\")\n    private Integer totalLimitCount;\n\n    @Schema(description = \"单次限够数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31683\")\n    private Integer singleLimitCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;\n\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 秒杀活动创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillActivityCreateReqVO extends SeckillActivityBaseVO {\n\n    @Schema(description = \"秒杀商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<SeckillProductBaseVO> products;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 秒杀活动的详细 Response VO\")\n@Data\n@ToString(callSuper = true)\npublic class SeckillActivityDetailRespVO extends SeckillActivityBaseVO{\n\n    @Schema(description = \"秒杀活动id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"秒杀商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<SeckillProductRespVO> products;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;\n\n@Schema(description = \"管理后台 - 秒杀活动分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillActivityPageReqVO extends PageParam {\n\n    @Schema(description = \"秒杀活动名称\", example = \"晚九点限时秒杀\")\n    private String name;\n\n    @Schema(description = \"活动状态\", example = \"进行中\")\n    private Integer status;\n\n    @Schema(description = \"秒杀时段id\", example = \"1\")\n    private Long configId;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 秒杀活动 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillActivityRespVO extends SeckillActivityBaseVO {\n\n    @Schema(description = \"秒杀活动 id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"秒杀商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<SeckillProductRespVO> products;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer status;\n\n    @Schema(description = \"订单实付金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22354\")\n    private Integer totalPrice;\n\n    @Schema(description = \"秒杀库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer stock;\n\n    @Schema(description = \"秒杀总库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Integer totalStock;\n\n    @Schema(description = \"新增订单数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Integer orderCount;\n\n    @Schema(description = \"付款人数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Integer userCount;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    // ========== 商品字段 ==========\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取\n            example = \"618大促\")\n    private String spuName;\n    @Schema(description = \"商品主图\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取\n            example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n    @Schema(description = \"商品市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取\n            example = \"50\")\n    private Integer marketPrice;\n\n    @Schema(description = \"秒杀金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer seckillPrice; // 从 products 获取最小 price 读取\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 秒杀活动更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillActivityUpdateReqVO extends SeckillActivityBaseVO {\n\n    @Schema(description = \"秒杀活动id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"秒杀商品\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<SeckillProductBaseVO> products;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalTime;\nimport java.util.List;\n\n/**\n * 秒杀时段 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n *\n * @author HUIHUI\n */\n@Data\npublic class SeckillConfigBaseVO {\n\n    @Schema(description = \"秒杀时段名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"早上场\")\n    @NotNull(message = \"秒杀时段名称不能为空\")\n    private String name;\n\n    @Schema(description = \"开始时间点\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"09:00:00\")\n    @NotNull(message = \"开始时间点不能为空\")\n    private String startTime;\n\n    @Schema(description = \"结束时间点\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"16:00:00\")\n    @NotNull(message = \"结束时间点不能为空\")\n    private String endTime;\n\n    @Schema(description = \"秒杀轮播图\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[https://www.iocoder.cn/xx.png]\")\n    @NotNull(message = \"秒杀轮播图不能为空\")\n    private List<String> sliderPicUrls;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @AssertTrue(message = \"秒杀时段开始时间和结束时间不能相等\")\n    @JsonIgnore\n    public boolean isValidStartTimeValid() {\n        return !LocalTime.parse(startTime).equals(LocalTime.parse(endTime));\n    }\n\n    @AssertTrue(message = \"秒杀时段开始时间不能在结束时间之后\")\n    @JsonIgnore\n    public boolean isValidEndTimeValid() {\n        return !LocalTime.parse(startTime).isAfter(LocalTime.parse(endTime));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 秒杀时段创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillConfigCreateReqVO extends SeckillConfigBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 秒杀时段分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillConfigPageReqVO extends PageParam {\n\n    @Schema(description = \"秒杀时段名称\", example = \"上午场\")\n    private String name;\n\n    @Schema(description = \"状态\", example = \"0\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 秒杀时段 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillConfigRespVO extends SeckillConfigBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"秒杀活动数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer seckillActivityCount;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigSimpleRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 秒杀时段配置精简信息 Response VO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SeckillConfigSimpleRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"秒杀时段名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"早上场\")\n    @NotNull(message = \"秒杀时段名称不能为空\")\n    private String name;\n\n    @Schema(description = \"开始时间点\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"09:00:00\")\n    private String startTime;\n\n    @Schema(description = \"结束时间点\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"16:00:00\")\n    private String endTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 秒杀时段更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillConfigUpdateReqVO extends SeckillConfigBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigUpdateStatusReqVo.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 修改时段配置状态 Request VO\")\n@Data\npublic class SeckillConfigUpdateStatusReqVo {\n\n    @Schema(description = \"时段配置编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"时段配置编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"状态，见 CommonStatusEnum 枚举\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"状态不能为空\")\n    @InEnum(value = CommonStatusEnum.class, message = \"修改状态必须是 {value}\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductBaseVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 秒杀参与商品 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n *\n * @author HUIHUI\n */\n@Data\npublic class SeckillProductBaseVO {\n\n    @Schema(description = \"商品sku_id\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"30563\")\n    @NotNull(message = \"商品sku_id不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"秒杀金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6689\")\n    @NotNull(message = \"秒杀金额，单位：分不能为空\")\n    private Integer seckillPrice;\n\n    @Schema(description = \"秒杀库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    @NotNull(message = \"秒杀库存不能为空\")\n    private Integer stock;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 秒杀参与商品 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillProductRespVO extends SeckillProductBaseVO {\n\n    @Schema(description = \"秒杀参与商品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"256\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.http",
    "content": "### /promotion/activity/list-by-spu-ids 获得多个商品，近期参与的每个活动\nGET {{appApi}}/promotion/activity/list-by-spu-ids?spuIds=222&spuIds=633\nAuthorization: Bearer {{appToken}}\nContent-Type: application/json\ntenant-id: {{appTenantId}}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.activity;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.controller.app.activity.vo.AppActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;\nimport cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;\nimport cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 APP - 营销活动\") // 用于提供跨多个活动的 HTTP 接口\n@RestController\n@RequestMapping(\"/promotion/activity\")\n@Validated\npublic class AppActivityController {\n\n    @Resource\n    private CombinationActivityService combinationActivityService;\n    @Resource\n    private SeckillActivityService seckillActivityService;\n    @Resource\n    private BargainActivityService bargainActivityService;\n\n    @GetMapping(\"/list-by-spu-id\")\n    @Operation(summary = \"获得单个商品，进行中的拼团、秒杀、砍价活动信息\", description = \"每种活动，只返回一个\")\n    @Parameter(name = \"spuId\", description = \"商品编号\", required = true)\n    @PermitAll\n    public CommonResult<List<AppActivityRespVO>> getActivityListBySpuId(@RequestParam(\"spuId\") Long spuId) {\n        List<AppActivityRespVO> activityVOList = new ArrayList<>();\n        // 1. 拼团活动\n        CombinationActivityDO combinationActivity = combinationActivityService.getMatchCombinationActivityBySpuId(spuId);\n        if (combinationActivity != null) {\n            activityVOList.add(new AppActivityRespVO(combinationActivity.getId(), PromotionTypeEnum.COMBINATION_ACTIVITY.getType(),\n                    combinationActivity.getName(), combinationActivity.getSpuId(), combinationActivity.getStartTime(), combinationActivity.getEndTime()));\n        }\n        // 2. 秒杀活动\n        SeckillActivityDO seckillActivity = seckillActivityService.getMatchSeckillActivityBySpuId(spuId);\n        if (seckillActivity != null) {\n            activityVOList.add(new AppActivityRespVO(seckillActivity.getId(), PromotionTypeEnum.SECKILL_ACTIVITY.getType(),\n                    seckillActivity.getName(), seckillActivity.getSpuId(), seckillActivity.getStartTime(), seckillActivity.getEndTime()));\n        }\n        // 3. 砍价活动\n        BargainActivityDO bargainActivity = bargainActivityService.getMatchBargainActivityBySpuId(spuId);\n        if (bargainActivity != null) {\n            activityVOList.add(new AppActivityRespVO(bargainActivity.getId(), PromotionTypeEnum.BARGAIN_ACTIVITY.getType(),\n                    bargainActivity.getName(), bargainActivity.getSpuId(), bargainActivity.getStartTime(), bargainActivity.getEndTime()));\n        }\n        return success(activityVOList);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/vo/AppActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.activity.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"用户 App - 营销活动 Response VO\")\n@AllArgsConstructor\n@NoArgsConstructor\n@Data\npublic class AppActivityRespVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"活动类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type; // 对应 PromotionTypeEnum 枚举\n\n    @Schema(description = \"活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"618 大促\")\n    private String name;\n\n    @Schema(description = \"spu 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"618\")\n    private Long spuId;\n\n    @Schema(description = \"活动开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"活动结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/AppArticleCategoryController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.article;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.controller.app.article.vo.category.AppArticleCategoryRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.article.ArticleCategoryConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryDO;\nimport cn.iocoder.yudao.module.promotion.service.article.ArticleCategoryService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 APP - 文章分类\")\n@RestController\n@RequestMapping(\"/promotion/article-category\")\n@Validated\npublic class AppArticleCategoryController {\n\n    @Resource\n    private ArticleCategoryService articleCategoryService;\n\n    @RequestMapping(\"/list\")\n    @Operation(summary = \"获得文章分类列表\")\n    public CommonResult<List<AppArticleCategoryRespVO>> getArticleCategoryList() {\n        List<ArticleCategoryDO> categoryList = articleCategoryService.getArticleCategoryListByStatus(\n                CommonStatusEnum.ENABLE.getStatus());\n        categoryList.sort(Comparator.comparing(ArticleCategoryDO::getSort)); // 按 sort 降序排列\n        return success(ArticleCategoryConvert.INSTANCE.convertList04(categoryList));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/AppArticleController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticlePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticleRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.article.ArticleConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;\nimport cn.iocoder.yudao.module.promotion.service.article.ArticleService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 APP - 文章\")\n@RestController\n@RequestMapping(\"/promotion/article\")\n@Validated\npublic class AppArticleController {\n\n    @Resource\n    private ArticleService articleService;\n\n    @RequestMapping(\"/list\")\n    @Operation(summary = \"获得文章详情列表\")\n    @Parameters({\n            @Parameter(name = \"recommendHot\", description = \"是否热门\", example = \"false\"), // 场景一：查看指定的文章\n            @Parameter(name = \"recommendBanner\", description = \"是否轮播图\", example = \"false\") // 场景二：查看指定的文章\n    })\n    @PermitAll\n    public CommonResult<List<AppArticleRespVO>> getArticleList(\n            @RequestParam(value = \"recommendHot\", required = false) Boolean recommendHot,\n            @RequestParam(value = \"recommendBanner\", required = false) Boolean recommendBanner) {\n        return success(ArticleConvert.INSTANCE.convertList03(\n                articleService.getArticleCategoryListByRecommend(recommendHot, recommendBanner)));\n    }\n\n    @RequestMapping(\"/page\")\n    @Operation(summary = \"获得文章详情分页\")\n    @PermitAll\n    public CommonResult<PageResult<AppArticleRespVO>> getArticlePage(AppArticlePageReqVO pageReqVO) {\n        return success(ArticleConvert.INSTANCE.convertPage02(articleService.getArticlePage(pageReqVO)));\n    }\n\n    @RequestMapping(\"/get\")\n    @Operation(summary = \"获得文章详情\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"文章编号\", example = \"1024\"),\n            @Parameter(name = \"title\", description = \"文章标题\", example = \"1024\"),\n    })\n    @PermitAll\n    public CommonResult<AppArticleRespVO> getArticle(@RequestParam(value = \"id\", required = false) Long id,\n                                                     @RequestParam(value = \"title\", required = false) String title) {\n        ArticleDO article = id != null ? articleService.getArticle(id)\n                : articleService.getLastArticleByTitle(title);\n        return success(BeanUtils.toBean(article, AppArticleRespVO.class));\n    }\n\n    @PutMapping(\"/add-browse-count\")\n    @Operation(summary = \"增加文章浏览量\")\n    @Parameter(name = \"id\", description = \"文章编号\", example = \"1024\")\n    @PermitAll\n    public CommonResult<Boolean> addBrowseCount(@RequestParam(\"id\") Long id) {\n        articleService.addArticleBrowseCount(id);\n        return success(true);\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/vo/article/AppArticlePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.article.vo.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"应用 App - 文章的分页 Request VO\")\n@Data\npublic class AppArticlePageReqVO extends PageParam {\n\n    @Schema(description = \"分类编号\", example = \"2048\")\n    private Long categoryId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/vo/article/AppArticleRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.article.vo.article;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"应用 App - 文章 Response VO\")\n@Data\npublic class AppArticleRespVO {\n\n    @Schema(description = \"文章编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"文章标题\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码 - 促销模块\")\n    private String title;\n\n    @Schema(description = \"文章作者\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String author;\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long categoryId;\n\n    @Schema(description = \"图文封面\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    private String picUrl;\n\n    @Schema(description = \"文章简介\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是简介\")\n    private String introduction;\n\n    @Schema(description = \"文章内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是详细\")\n    private String content;\n\n    @Schema(description = \"发布时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"浏览量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer browseCount;\n\n    @Schema(description = \"关联的商品 SPU 编号\", example = \"1024\")\n    private Long spuId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/vo/category/AppArticleCategoryRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.article.vo.category;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"应用 App - 文章分类 Response VO\")\n@Data\npublic class AppArticleCategoryRespVO {\n\n    @Schema(description = \"分类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"分类名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"技术\")\n    private String name;\n\n    @Schema(description = \"分类图标\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    private String picUrl;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/AppBannerController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.banner;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.controller.app.banner.vo.AppBannerRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.banner.BannerConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;\nimport cn.iocoder.yudao.module.promotion.service.banner.BannerService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@RestController\n@RequestMapping(\"/promotion/banner\")\n@Tag(name = \"用户 APP - 首页 Banner\")\n@Validated\npublic class AppBannerController {\n\n    @Resource\n    private BannerService bannerService;\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得 banner 列表\")\n    @Parameter(name = \"position\", description = \"Banner position\", example = \"1\")\n    @PermitAll\n    public CommonResult<List<AppBannerRespVO>> getBannerList(@RequestParam(\"position\") Integer position) {\n        List<BannerDO> bannerList = bannerService.getBannerListByPosition(position);\n        return success(BannerConvert.INSTANCE.convertList01(bannerList));\n    }\n\n    @PutMapping(\"/add-browse-count\")\n    @Operation(summary = \"增加 Banner 点击量\")\n    @Parameter(name = \"id\", description = \"Banner 编号\", example = \"1024\")\n    @PermitAll\n    public CommonResult<Boolean> addBrowseCount(@RequestParam(\"id\") Long id) {\n        bannerService.addBannerBrowseCount(id);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/vo/AppBannerRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.banner.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - Banner Response VO\")\n@Data\npublic class AppBannerRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Long id;\n\n    @Schema(description = \"标题\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"标题不能为空\")\n    private String title;\n\n    @Schema(description = \"跳转链接\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"跳转链接不能为空\")\n    private String url;\n\n    @Schema(description = \"图片地址\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"图片地址不能为空\")\n    private String picUrl;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.activity.AppBargainActivityDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.activity.AppBargainActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.bargain.BargainActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainRecordService;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.time.Duration;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"用户 App - 砍价活动\")\n@RestController\n@RequestMapping(\"/promotion/bargain-activity\")\n@Validated\npublic class AppBargainActivityController {\n\n    /**\n     * {@link AppBargainActivityRespVO} 缓存，通过它异步刷新 {@link #getBargainActivityList0(Integer)} 所要的首页数据\n     */\n    private final LoadingCache<Integer, List<AppBargainActivityRespVO>> bargainActivityListCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),\n            new CacheLoader<Integer, List<AppBargainActivityRespVO>>() {\n\n                @Override\n                public List<AppBargainActivityRespVO> load(Integer count) {\n                    return getBargainActivityList0(count);\n                }\n\n            });\n\n    @Resource\n    private BargainActivityService bargainActivityService;\n    @Resource\n    private BargainRecordService bargainRecordService;\n\n    @Resource\n    private ProductSpuApi spuApi;\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得砍价活动列表\", description = \"用于小程序首页\")\n    @Parameter(name = \"count\", description = \"需要展示的数量\", example = \"6\")\n    @PermitAll\n    public CommonResult<List<AppBargainActivityRespVO>> getBargainActivityList(\n            @RequestParam(name = \"count\", defaultValue = \"6\") Integer count) {\n        return success(bargainActivityListCache.getUnchecked(count));\n    }\n\n    private List<AppBargainActivityRespVO>getBargainActivityList0(Integer count) {\n        List<BargainActivityDO> list = bargainActivityService.getBargainActivityListByCount(count);\n        if (CollUtil.isEmpty(list)) {\n            return Collections.emptyList();\n        }\n        // 拼接数据\n        List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(list, BargainActivityDO::getSpuId)).getCheckedData();\n        return BargainActivityConvert.INSTANCE.convertAppList(list, spuList);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得砍价活动分页\")\n    @PermitAll\n    public CommonResult<PageResult<AppBargainActivityRespVO>> getBargainActivityPage(PageParam pageReqVO) {\n        PageResult<BargainActivityDO> result = bargainActivityService.getBargainActivityPage(pageReqVO);\n        if (CollUtil.isEmpty(result.getList())) {\n            return success(PageResult.empty(result.getTotal()));\n        }\n        // 拼接数据\n        List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(result.getList(), BargainActivityDO::getSpuId)).getCheckedData();\n        return success(BargainActivityConvert.INSTANCE.convertAppPage(result, spuList));\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得砍价活动详情\")\n    @Parameter(name = \"id\", description = \"活动编号\", example = \"1\")\n    @PermitAll\n    public CommonResult<AppBargainActivityDetailRespVO> getBargainActivityDetail(@RequestParam(\"id\") Long id) {\n        BargainActivityDO activity = bargainActivityService.getBargainActivity(id);\n        if (activity == null) {\n            return success(null);\n        }\n        // 拼接数据\n        Integer successUserCount = bargainRecordService.getBargainRecordUserCount(id, BargainRecordStatusEnum.SUCCESS.getStatus());\n        ProductSpuRespDTO spu = spuApi.getSpu(activity.getSpuId()).getCheckedData();\n        return success(BargainActivityConvert.INSTANCE.convert(activity, successUserCount, spu));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.http",
    "content": "### /promotion/bargain-record/create 创建砍价助力\nPOST {{appApi}}/promotion/bargain-help/create\nAuthorization: Bearer test248\nContent-Type: application/json\ntenant-id: {{appTenantId}}\n\n{\n  \"recordId\": 26\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.bargain.BargainHelpConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainHelpService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 App - 砍价助力\")\n@RestController\n@RequestMapping(\"/promotion/bargain-help\")\n@Validated\npublic class AppBargainHelpController {\n\n    @Resource\n    private BargainHelpService bargainHelpService;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建砍价助力\", description = \"给拼团记录砍一刀\") // 返回结果为砍价金额，单位：分\n    public CommonResult<Integer> createBargainHelp(@RequestBody AppBargainHelpCreateReqVO reqVO) {\n        BargainHelpDO help = bargainHelpService.createBargainHelp(getLoginUserId(), reqVO);\n        return success(help.getReducePrice());\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得砍价助力列表\")\n    @Parameter(name = \"recordId\", description = \"砍价记录编号\", required = true, example = \"111\")\n    public CommonResult<List<AppBargainHelpRespVO>> getBargainHelpList(@RequestParam(\"recordId\") Long recordId) {\n        List<BargainHelpDO> helps = bargainHelpService.getBargainHelpListByRecordId(recordId);\n        if (CollUtil.isEmpty(helps)) {\n            return success(Collections.emptyList());\n        }\n        helps.sort((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime())); // 倒序展示\n\n        // 拼接数据\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(\n                convertSet(helps, BargainHelpDO::getUserId));\n        return success(BargainHelpConvert.INSTANCE.convertList(helps, userMap));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.http",
    "content": "### /promotion/bargain-record/create 创建砍价记录\nPOST {{appApi}}/promotion/bargain-record/create\nAuthorization: Bearer {{appToken}}\nContent-Type: application/json\ntenant-id: {{appTenantId}}\n\n{\n  \"activityId\": 1\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordSummaryRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.bargain.BargainRecordConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;\nimport cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainHelpService;\nimport cn.iocoder.yudao.module.promotion.service.bargain.BargainRecordService;\nimport cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;\nimport cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 App - 砍价记录\")\n@RestController\n@RequestMapping(\"/promotion/bargain-record\")\n@Validated\npublic class AppBargainRecordController {\n\n    @Resource\n    private BargainHelpService bargainHelpService;\n    @Resource\n    private BargainRecordService bargainRecordService;\n    @Resource\n    private BargainActivityService bargainActivityService;\n\n    @Resource\n    private TradeOrderApi tradeOrderApi;\n    @Resource\n    private MemberUserApi memberUserApi;\n    @Resource\n    private ProductSpuApi productSpuApi;\n\n    @GetMapping(\"/get-summary\")\n    @Operation(summary = \"获得砍价记录的概要信息\", description = \"用于小程序首页\")\n    @PermitAll\n    public CommonResult<AppBargainRecordSummaryRespVO> getBargainRecordSummary() {\n        // 砍价成功的用户数量\n        Integer successUserCount = bargainRecordService.getBargainRecordUserCount(\n                BargainRecordStatusEnum.SUCCESS.getStatus());\n        if (successUserCount == 0) {\n            return success(new AppBargainRecordSummaryRespVO().setSuccessUserCount(0)\n                    .setSuccessList(Collections.emptyList()));\n        }\n        // 砍价成功的用户列表\n        List<BargainRecordDO> successList = bargainRecordService.getBargainRecordList(\n                BargainRecordStatusEnum.SUCCESS.getStatus(), 7);\n        List<BargainActivityDO> activityList = bargainActivityService.getBargainActivityList(\n                convertSet(successList, BargainRecordDO::getActivityId));\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(\n                convertSet(successList, BargainRecordDO::getUserId));\n        // 拼接返回\n        return success(BargainRecordConvert.INSTANCE.convert(successUserCount, successList, activityList, userMap));\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得砍价记录的明细\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"砍价记录编号\", example = \"111\"), // 场景一：查看指定的砍价记录\n            @Parameter(name = \"activityId\", description = \"砍价活动编号\", example = \"222\") // 场景二：查看指定的砍价活动\n    })\n    @PermitAll\n    public CommonResult<AppBargainRecordDetailRespVO> getBargainRecordDetail(\n            @RequestParam(value = \"id\", required = false) Long id,\n            @RequestParam(value = \"activityId\", required = false) Long activityId) {\n        // 1. 查询砍价记录 + 砍价活动\n        Assert.isTrue(id != null || activityId != null, \"砍价记录编号和活动编号不能同时为空\");\n        BargainRecordDO record = id != null ? bargainRecordService.getBargainRecord(id)\n                : bargainRecordService.getLastBargainRecord(getLoginUserId(), activityId);\n        if (activityId == null || record != null) {\n            activityId = record.getActivityId();\n        }\n        // 2. 查询助力记录\n        Long userId = getLoginUserId();\n        Integer helpAction = getHelpAction(userId, record, activityId);\n        // 3. 如果是自己的订单，则查询订单信息\n        TradeOrderRespDTO order = record != null && record.getOrderId() != null && record.getUserId().equals(getLoginUserId())\n                ? tradeOrderApi.getOrder(record.getOrderId()).getCheckedData() : null;\n        // TODO 继续查询别的字段\n\n        // 拼接返回\n        return success(BargainRecordConvert.INSTANCE.convert02(record, helpAction, order));\n    }\n\n    private Integer getHelpAction(Long userId, BargainRecordDO record, Long activityId) {\n        // 0.1 如果没有活动，无法帮砍\n        if (activityId == null) {\n            return null;\n        }\n        // 0.2 如果是自己的砍价记录，无法帮砍\n        if (record != null && record.getUserId().equals(userId)) {\n            return null;\n        }\n\n        // 1. 判断是否已经助力\n        if (record != null\n            && bargainHelpService.getBargainHelp(record.getId(), userId) != null) {\n            return AppBargainRecordDetailRespVO.HELP_ACTION_SUCCESS;\n        }\n        // 2. 判断是否满助力\n        BargainActivityDO activity = bargainActivityService.getBargainActivity(activityId);\n        if (activity != null\n            && bargainHelpService.getBargainHelpCountByActivity(activityId, userId) >= activity.getBargainCount()) {\n            return AppBargainRecordDetailRespVO.HELP_ACTION_FULL;\n        }\n        // 3. 允许助力\n        return AppBargainRecordDetailRespVO.HELP_ACTION_NONE;\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得砍价记录的分页\")\n    public CommonResult<PageResult<AppBargainRecordRespVO>> getBargainRecordPage(PageParam pageParam) {\n        PageResult<BargainRecordDO> pageResult = bargainRecordService.getBargainRecordPage(getLoginUserId(), pageParam);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 拼接数据\n        List<BargainActivityDO> activityList = bargainActivityService.getBargainActivityList(\n                convertSet(pageResult.getList(), BargainRecordDO::getActivityId));\n        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(\n                convertSet(pageResult.getList(), BargainRecordDO::getSpuId)).getCheckedData();\n        List<TradeOrderRespDTO> orderList = tradeOrderApi.getOrderList(\n                convertSet(pageResult.getList(), BargainRecordDO::getOrderId)).getCheckedData();\n        return success(BargainRecordConvert.INSTANCE.convertPage02(pageResult, activityList, spuList, orderList));\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建砍价记录\", description = \"参与砍价活动\")\n    public CommonResult<Long> createBargainRecord(@RequestBody AppBargainRecordCreateReqVO reqVO) {\n        Long recordId = bargainRecordService.createBargainRecord(getLoginUserId(), reqVO);\n        return success(recordId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/activity/AppBargainActivityDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"用户 App - 砍价活动的明细 Response VO\")\n@Data\npublic class AppBargainActivityDetailRespVO {\n\n    @Schema(description = \"砍价活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"砍价活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"618 大砍价\")\n    private String name;\n\n    @Schema(description = \"活动开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"活动结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long skuId;\n\n    @Schema(description = \"商品价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer price;\n\n    @Schema(description = \"商品描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我要吃西红柿\")\n    private String description;\n\n    @Schema(description = \"砍价库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"512\")\n    private Integer stock;\n\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4096\") // 从 SPU 的 picUrl 读取\n    private String picUrl;\n\n    @Schema(description = \"商品市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\") // 从 SPU 的 marketPrice 读取\n    private Integer marketPrice;\n\n    @Schema(description = \"砍价起始价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"200\")\n    private Integer bargainFirstPrice;\n\n    @Schema(description = \"砍价最低金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer bargainMinPrice;\n\n    @Schema(description = \"砍价成功数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer successUserCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/activity/AppBargainActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"用户 App - 砍价活动 Response VO\")\n@Data\npublic class AppBargainActivityRespVO {\n\n    @Schema(description = \"砍价活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"砍价活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"618 大砍价\")\n    private String name;\n\n    @Schema(description = \"活动开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"活动结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long skuId;\n\n    @Schema(description = \"砍价库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"512\")\n    private Integer stock;\n\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED,  // 从 SPU 的 picUrl 读取\n            example = \"4096\")\n    private String picUrl;\n    @Schema(description = \"商品市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED,  // 从 SPU 的 marketPrice 读取\n            example = \"50\")\n    private Integer marketPrice;\n\n    @Schema(description = \"砍价最低金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer bargainMinPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/help/AppBargainHelpCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 砍价助力的创建 Request VO\")\n@Data\npublic class AppBargainHelpCreateReqVO {\n\n    @Schema(description = \"砍价记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"砍价记录编号不能为空\")\n    private Long recordId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/help/AppBargainHelpRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"用户 App - 砍价助力 Response VO\")\n@Data\npublic class AppBargainHelpRespVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long userId;\n\n    @Schema(description = \"助力用户的昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String nickname;\n\n    @Schema(description = \"助力用户的头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String avatar;\n\n    @Schema(description = \"助力用户的砍价金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer reducePrice;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/record/AppBargainRecordCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 砍价记录的创建 Request VO\")\n@Data\npublic class AppBargainRecordCreateReqVO {\n\n    @Schema(description = \"砍价活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"砍价活动编号不能为空\")\n    private Long activityId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/record/AppBargainRecordDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 砍价记录的明细 Response VO\")\n@Data\npublic class AppBargainRecordDetailRespVO {\n\n    public static final int HELP_ACTION_NONE = 1; // 帮砍动作 - 未帮砍，可以帮砍\n    public static final int HELP_ACTION_FULL = 2; // 帮砍动作 - 未帮砍，无法帮砍（可帮砍次数已满)\n    public static final int HELP_ACTION_SUCCESS = 3; // 帮砍动作 - 已帮砍\n\n    // ========== 砍价记录 ==========\n\n    @Schema(description = \"砍价记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"666\")\n    private Long userId;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long spuId;\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long skuId;\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    private Long activityId;\n\n    @Schema(description = \"砍价起始价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23\")\n    private Integer bargainFirstPrice;\n\n    @Schema(description = \"当前砍价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23\")\n    private Integer bargainPrice;\n\n    @Schema(description = \"砍价记录状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    // ========== 订单相关 ========== 注意：只有是自己的砍价记录，才会返回，保证隐私性\n\n    @Schema(description = \"订单编号\", example = \"1024\")\n    private Long orderId;\n\n    @Schema(description = \"支付状态\", example = \"true\")\n    private Boolean payStatus;\n\n    @Schema(description = \"支付订单编号\", example = \"1024\")\n    private Long payOrderId;\n\n    // ========== 助力记录 ==========\n\n    private Integer helpAction;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/record/AppBargainRecordRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"用户 App - 砍价记录的 Response VO\")\n@Data\npublic class AppBargainRecordRespVO {\n\n    @Schema(description = \"砍价记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long spuId;\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long skuId;\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22901\")\n    private Long activityId;\n\n    @Schema(description = \"砍价记录状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"当前价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"102\")\n    private Integer bargainPrice;\n\n    // ========== 活动相关 ==========\n\n    @Schema(description = \"活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"赵六\")\n    private String activityName;\n\n    @Schema(description = \"活动结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED,  // 从 SPU 的 picUrl 读取\n            example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n\n    // ========== 订单相关 ==========\n\n    @Schema(description = \"订单编号\", example = \"1024\")\n    private Long orderId;\n\n    @Schema(description = \"支付状态\", example = \"true\")\n    private Boolean payStatus;\n\n    @Schema(description = \"支付订单编号\", example = \"1024\")\n    private Long payOrderId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/record/AppBargainRecordSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 砍价记录的简要概括 Response VO\")\n@Data\npublic class AppBargainRecordSummaryRespVO {\n\n    @Schema(description = \"砍价用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer successUserCount;\n\n    @Schema(description = \"成功砍价的记录\", requiredMode = Schema.RequiredMode.REQUIRED) // 只返回最近的 7 个\n    private List<Record> successList;\n\n    @Schema(description = \"成功砍价记录\")\n    @Data\n    public static class Record {\n\n        @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王**\")\n        private String nickname;\n\n        @Schema(description = \"用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xxx.jpg\")\n        private String avatar;\n\n        @Schema(description = \"活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"天蚕土豆\")\n        private String activityName;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.combination;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;\nimport cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\n\n@Tag(name = \"用户 APP - 拼团活动\")\n@RestController\n@RequestMapping(\"/promotion/combination-activity\")\n@Validated\npublic class AppCombinationActivityController {\n\n    @Resource\n    private CombinationActivityService activityService;\n\n    @Resource\n    private ProductSpuApi spuApi;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得拼团活动分页\")\n    @PermitAll\n    public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) {\n        PageResult<CombinationActivityDO> pageResult = activityService.getCombinationActivityPage(pageParam);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n        // 拼接返回\n        List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(\n                convertList(pageResult.getList(), CombinationActivityDO::getId));\n        List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(pageResult.getList(), CombinationActivityDO::getSpuId)).getCheckedData();\n        return success(CombinationActivityConvert.INSTANCE.convertAppPage(pageResult, productList, spuList));\n    }\n\n    @GetMapping(\"/list-by-ids\")\n    @Operation(summary = \"获得拼团活动列表，基于活动编号数组\")\n    @Parameter(name = \"ids\", description = \"活动编号数组\", required = true, example = \"[1024, 1025]\")\n    @PermitAll\n    public CommonResult<List<AppCombinationActivityRespVO>> getCombinationActivityListByIds(@RequestParam(\"ids\") List<Long> ids) {\n        // 1. 获得开启的活动列表\n        List<CombinationActivityDO> activityList = activityService.getCombinationActivityListByIds(ids);\n        activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));\n        if (CollUtil.isEmpty(activityList)) {\n            return success(Collections.emptyList());\n        }\n        // 2. 拼接返回\n        List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(\n                convertList(activityList, CombinationActivityDO::getId));\n        List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId)).getCheckedData();\n        return success(CombinationActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList));\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得拼团活动明细\")\n    @Parameter(name = \"id\", description = \"活动编号\", required = true, example = \"1024\")\n    @PermitAll\n    public CommonResult<AppCombinationActivityDetailRespVO> getCombinationActivityDetail(@RequestParam(\"id\") Long id) {\n        // 1. 获取活动\n        CombinationActivityDO activity = activityService.getCombinationActivity(id);\n        if (activity == null\n                || ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {\n            return success(null);\n        }\n\n        // 2. 获取活动商品\n        List<CombinationProductDO> products = activityService.getCombinationProductsByActivityId(activity.getId());\n        return success(CombinationActivityConvert.INSTANCE.convert3(activity, products));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.combination;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordSummaryRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;\nimport cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.validation.Valid;\nimport javax.validation.constraints.Max;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 APP - 拼团活动\")\n@RestController\n@RequestMapping(\"/promotion/combination-record\")\n@Validated\npublic class AppCombinationRecordController {\n\n    @Resource\n    private CombinationRecordService combinationRecordService;\n\n    @GetMapping(\"/get-summary\")\n    @Operation(summary = \"获得拼团记录的概要信息\", description = \"用于小程序首页\")\n    @PermitAll\n    public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationRecordSummary() {\n        AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO();\n        // 1. 获得拼团参与用户数量\n        Long userCount = combinationRecordService.getCombinationUserCount();\n        if (userCount == 0) {\n            summary.setAvatars(Collections.emptyList());\n            summary.setUserCount(userCount);\n            return success(summary);\n        }\n        summary.setUserCount(userCount);\n\n        // 2. 获得拼团记录头像\n        List<CombinationRecordDO> records = combinationRecordService.getLatestCombinationRecordList(\n                AppCombinationRecordSummaryRespVO.AVATAR_COUNT);\n        summary.setAvatars(convertList(records, CombinationRecordDO::getAvatar));\n        return success(summary);\n    }\n\n    @GetMapping(\"/get-head-list\")\n    @Operation(summary = \"获得最近 n 条拼团记录（团长发起的）\")\n    @Parameters({\n            @Parameter(name = \"activityId\", description = \"拼团活动编号\"),\n            @Parameter(name = \"status\", description = \"拼团状态\"), // 对应 CombinationRecordStatusEnum 枚举\n            @Parameter(name = \"count\", description = \"数量\")\n    })\n    @PermitAll\n    public CommonResult<List<AppCombinationRecordRespVO>> getHeadCombinationRecordList(\n            @RequestParam(value = \"activityId\", required = false) Long activityId,\n            @RequestParam(\"status\") Integer status,\n            @RequestParam(value = \"count\", defaultValue = \"20\") @Max(20) Integer count) {\n        List<CombinationRecordDO> list = combinationRecordService.getHeadCombinationRecordList(activityId, status, count);\n        return success(BeanUtils.toBean(list, AppCombinationRecordRespVO.class));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得我的拼团记录分页\")\n    public CommonResult<PageResult<AppCombinationRecordRespVO>> getCombinationRecordPage(\n            @Valid AppCombinationRecordPageReqVO pageReqVO) {\n        PageResult<CombinationRecordDO> pageResult = combinationRecordService.getCombinationRecordPage(\n                getLoginUserId(), pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AppCombinationRecordRespVO.class));\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得拼团记录明细\")\n    @Parameter(name = \"id\", description = \"拼团记录编号\", required = true, example = \"1024\")\n    @PermitAll\n    public CommonResult<AppCombinationRecordDetailRespVO> getCombinationRecordDetail(@RequestParam(\"id\") Long id) {\n        // 1. 查找这条拼团记录\n        CombinationRecordDO record = combinationRecordService.getCombinationRecordById(id);\n        if (record == null) {\n            return success(null);\n        }\n\n        // 2. 查找该拼团的参团记录\n        CombinationRecordDO headRecord;\n        List<CombinationRecordDO> memberRecords;\n        if (Objects.equals(record.getHeadId(), CombinationRecordDO.HEAD_ID_GROUP)) { // 情况一：团长\n            headRecord = record;\n            memberRecords = combinationRecordService.getCombinationRecordListByHeadId(record.getId());\n        } else { // 情况二：团员\n            headRecord = combinationRecordService.getCombinationRecordById(record.getHeadId());\n            memberRecords = combinationRecordService.getCombinationRecordListByHeadId(headRecord.getId());\n        }\n\n        // 3. 拼接数据\n        return success(CombinationActivityConvert.INSTANCE.convert(getLoginUserId(), headRecord, memberRecords));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/activity/AppCombinationActivityDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 拼团活动明细 Response VO\")\n@Data\npublic class AppCombinationActivityDetailRespVO {\n\n    @Schema(description = \"拼团活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"拼团活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"618 大拼团\")\n    private String name;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"活动开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"活动结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"拼团人数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3\")\n    private Integer userSize;\n\n    @Schema(description = \"成功的拼团数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer successCount;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long spuId;\n\n    @Schema(description = \"总共限购数量\", example = \"10\")\n    private Integer totalLimitCount;\n\n    @Schema(description = \"单次限购数量\", example = \"5\")\n    private Integer singleLimitCount;\n\n    @Schema(description = \"商品信息数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Product> products;\n\n    @Schema(description = \"商品信息\")\n    @Data\n    public static class Product {\n\n        @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4096\")\n        private Long skuId;\n\n        @Schema(description = \"拼团金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer combinationPrice;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/activity/AppCombinationActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 拼团活动 Response VO\")\n@Data\npublic class AppCombinationActivityRespVO {\n\n    @Schema(description = \"拼团活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"拼团活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"618 大拼团\")\n    private String name;\n\n    @Schema(description = \"拼团人数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3\")\n    private Integer userSize;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SPU 名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"一个白菜\")\n    private String spuName; // 从 SPU 的 name 读取\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4096\")\n    private String picUrl; // 从 SPU 的 picUrl 读取\n    @Schema(description = \"商品市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n    private Integer marketPrice; // 从 SPU 的 marketPrice 读取\n\n    @Schema(description = \"拼团金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer combinationPrice; // 从 products 获取最小 price 读取\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 拼团记录详细 Response VO\")\n@Data\npublic class AppCombinationRecordDetailRespVO {\n\n    @Schema(description = \"团长的拼团记录\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private AppCombinationRecordRespVO headRecord;\n\n    @Schema(description = \"成员的拼团记录\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<AppCombinationRecordRespVO> memberRecords;\n\n    @Schema(description = \"当前用户参团记录对应的订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long orderId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"用户 App - 拼团记录分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AppCombinationRecordPageReqVO extends PageParam {\n\n    @Schema(description = \"拼团状态\", example = \"1\")\n    @InEnum(value = CombinationRecordStatusEnum.class, message = \"拼团状态必须是 {value}\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"用户 App - 拼团记录 Response VO\")\n@Data\npublic class AppCombinationRecordRespVO {\n\n    @Schema(description = \"拼团记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"拼团活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long activityId;\n\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String nickname;\n\n    @Schema(description = \"用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String avatar;\n\n    @Schema(description = \"过期时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime expireTime;\n\n    @Schema(description = \"可参团人数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer userSize;\n\n    @Schema(description = \"已参团人数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    private Integer userCount;\n\n    @Schema(description = \"拼团状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long orderId;\n\n    @Schema(description = \"商品名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"我是大黄豆\")\n    private String spuName;\n\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    private String picUrl;\n\n    @Schema(description = \"购买的商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer count;\n\n    @Schema(description = \"拼团金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer combinationPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 拼团记录的简要概括 Response VO\")\n@Data\npublic class AppCombinationRecordSummaryRespVO {\n\n    /**\n     * 加载 {@link #avatars} 的数量\n     */\n    public static final Integer AVATAR_COUNT = 7;\n\n    @Schema(description = \"拼团用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long userCount;\n\n    @Schema(description = \"拼团用户头像列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> avatars;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.coupon;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponTakeReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\nimport cn.iocoder.yudao.module.promotion.service.coupon.CouponService;\nimport cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collections;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 App - 优惠劵\")\n@RestController\n@RequestMapping(\"/promotion/coupon\")\n@Validated\npublic class AppCouponController {\n\n    @Resource\n    private CouponService couponService;\n    @Resource\n    private CouponTemplateService couponTemplateService;\n\n    @PostMapping(\"/take\")\n    @Operation(summary = \"领取优惠劵\")\n    @Parameter(name = \"templateId\", description = \"优惠券模板编号\", required = true, example = \"1024\")\n    public CommonResult<Boolean> takeCoupon(@Valid @RequestBody AppCouponTakeReqVO reqVO) {\n        // 1. 领取优惠劵\n        Long userId = getLoginUserId();\n        couponService.takeCoupon(reqVO.getTemplateId(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);\n\n        // 2. 检查是否可以继续领取\n        CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(reqVO.getTemplateId());\n        boolean canTakeAgain = true;\n        if (couponTemplate.getTakeLimitCount() != null && couponTemplate.getTakeLimitCount() > 0) {\n            Integer takeCount = couponService.getTakeCount(reqVO.getTemplateId(), userId);\n            canTakeAgain = takeCount < couponTemplate.getTakeLimitCount();\n        }\n        return success(canTakeAgain);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"我的优惠劵列表\")\n    public CommonResult<PageResult<AppCouponRespVO>> getCouponPage(AppCouponPageReqVO pageReqVO) {\n        PageResult<CouponDO> pageResult = couponService.getCouponPage(\n                CouponConvert.INSTANCE.convert(pageReqVO, Collections.singleton(getLoginUserId())));\n        return success(BeanUtils.toBean(pageResult, AppCouponRespVO.class));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得优惠劵\")\n    @Parameter(name = \"id\", description = \"优惠劵编号\", required = true, example = \"1024\")\n    public CommonResult<AppCouponRespVO> getCoupon(@RequestParam(\"id\") Long id) {\n        CouponDO coupon = couponService.getCoupon(getLoginUserId(), id);\n        return success(BeanUtils.toBean(coupon, AppCouponRespVO.class));\n    }\n\n    @GetMapping(value = \"/get-unused-count\")\n    @Operation(summary = \"获得未使用的优惠劵数量\")\n    public CommonResult<Long> getUnusedCouponCount() {\n        return success(couponService.getUnusedCouponCount(getLoginUserId()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponTemplateController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.coupon;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplateRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\nimport cn.iocoder.yudao.module.promotion.service.coupon.CouponService;\nimport cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;\nimport static java.util.Collections.singletonList;\n\n@Tag(name = \"用户 App - 优惠劵模板\")\n@RestController\n@RequestMapping(\"/promotion/coupon-template\")\n@Validated\npublic class AppCouponTemplateController {\n\n    @Resource\n    private CouponTemplateService couponTemplateService;\n    @Resource\n    private CouponService couponService;\n\n    @Resource\n    private ProductSpuApi productSpuApi;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得优惠劵模版\")\n    @Parameter(name = \"id\", description = \"优惠券模板编号\", required = true, example = \"1024\")\n    @PermitAll\n    public CommonResult<AppCouponTemplateRespVO> getCouponTemplate(Long id) {\n        CouponTemplateDO template = couponTemplateService.getCouponTemplate(id);\n        if (template == null) {\n            return success(null);\n        }\n        // 处理是否可领取\n        Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(getLoginUserId(), singletonList(template));\n        return success(BeanUtils.toBean(template, AppCouponTemplateRespVO.class)\n                .setCanTake(canCanTakeMap.get(template.getId())));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得优惠劵模版列表\")\n    @Parameters({\n            @Parameter(name = \"spuId\", description = \"商品 SPU 编号\"), // 目前主要给商品详情使用\n            @Parameter(name = \"productScope\", description = \"使用类型\"),\n            @Parameter(name = \"count\", description = \"数量\", required = true)\n    })\n    @PermitAll\n    public CommonResult<List<AppCouponTemplateRespVO>> getCouponTemplateList(\n            @RequestParam(value = \"spuId\", required = false) Long spuId,\n            @RequestParam(value = \"productScope\", required = false) Integer productScope,\n            @RequestParam(value = \"count\", required = false, defaultValue = \"10\") Integer count) {\n        // 1.1 处理查询条件：商品范围编号\n        Long productScopeValue = getProductScopeValue(productScope, spuId);\n        // 1.2 处理查询条件：领取方式 = 直接领取\n        List<Integer> canTakeTypes = singletonList(CouponTakeTypeEnum.USER.getType());\n\n        // 2. 查询\n        List<CouponTemplateDO> list = couponTemplateService.getCouponTemplateList(canTakeTypes, productScope,\n                productScopeValue, count);\n\n        // 3.1 领取数量\n        Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(getLoginUserId(), list);\n        // 3.2 拼接返回\n        return success(CouponTemplateConvert.INSTANCE.convertAppList(list, canCanTakeMap));\n    }\n\n    @GetMapping(\"/list-by-ids\")\n    @Operation(summary = \"获得优惠劵模版列表\")\n    @Parameter(name = \"ids\", description = \"优惠券模板编号列表\")\n    @PermitAll\n    public CommonResult<List<AppCouponTemplateRespVO>> getCouponTemplateList(\n            @RequestParam(value = \"ids\", required = false) Set<Long> ids) {\n        // 1. 查询\n        List<CouponTemplateDO> list = couponTemplateService.getCouponTemplateList(ids);\n\n        // 2.1 领取数量\n        Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(getLoginUserId(), list);\n        // 2.2 拼接返回\n        return success(CouponTemplateConvert.INSTANCE.convertAppList(list, canCanTakeMap));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得优惠劵模版分页\")\n    @PermitAll\n    public CommonResult<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) {\n        // 1.1 处理查询条件：商品范围编号\n        Long productScopeValue = getProductScopeValue(pageReqVO.getProductScope(), pageReqVO.getSpuId());\n        // 1.2 处理查询条件：领取方式 = 直接领取\n        List<Integer> canTakeTypes = singletonList(CouponTakeTypeEnum.USER.getType());\n\n        // 2. 分页查询\n        PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(\n                CouponTemplateConvert.INSTANCE.convert(pageReqVO, canTakeTypes, pageReqVO.getProductScope(), productScopeValue));\n\n        // 3.1 领取数量\n        Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(getLoginUserId(), pageResult.getList());\n        // 3.2 拼接返回\n        return success(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, canCanTakeMap));\n    }\n\n    /**\n     * 获得商品的使用范围编号\n     *\n     * @param productScope 商品范围\n     * @param spuId        商品 SPU 编号\n     * @return 商品范围编号\n     */\n    private Long getProductScopeValue(Integer productScope, Long spuId) {\n        // 通用券：没有商品范围\n        if (ObjectUtils.equalsAny(productScope, PromotionProductScopeEnum.ALL.getScope(), null)) {\n            return null;\n        }\n        // 品类券：查询商品的品类编号\n        if (Objects.equals(productScope, PromotionProductScopeEnum.CATEGORY.getScope()) && spuId != null) {\n            ProductSpuRespDTO spu = productSpuApi.getSpu(spuId).getCheckedData();\n            return spu != null ? spu.getCategoryId() : null;\n        }\n        // 商品劵：直接返回\n        return spuId;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/coupon/AppCouponPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"用户 App - 优惠劵分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AppCouponPageReqVO extends PageParam {\n\n    @Schema(description = \"优惠劵状态\", example = \"1\")\n    @InEnum(value = CouponStatusEnum.class, message = \"优惠劵状态，必须是 {value}\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/coupon/AppCouponRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 优惠劵 Response VO\")\n@Data\npublic class AppCouponRespVO {\n\n    @Schema(description = \"优惠劵编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"优惠劵名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"春节送送送\")\n    private String name;\n\n    @Schema(description = \"优惠劵状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\") // 参见 CouponStatusEnum 枚举\n    private Integer status;\n\n    @Schema(description = \"是否设置满多少金额可用\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\") // 单位：分；0 - 不限制\n    private Integer usePrice;\n\n    @Schema(description = \"商品范围\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer productScope;\n\n    @Schema(description = \"商品范围编号的数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private List<Long> productScopeValues;\n\n    @Schema(description = \"固定日期 - 生效开始时间\")\n    private LocalDateTime validStartTime;\n\n    @Schema(description = \"固定日期 - 生效结束时间\")\n    private LocalDateTime validEndTime;\n\n    @Schema(description = \"优惠类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer discountType;\n\n    @Schema(description = \"折扣百分比\", example = \"80\") //  例如说，80% 为 80\n    private Integer discountPercent;\n\n    @Schema(description = \"优惠金额\", example = \"10\")\n    private Integer discountPrice;\n\n    @Schema(description = \"折扣上限\", example = \"100\") // 单位：分，仅在 discountType 为 PERCENT 使用\n    private Integer discountLimitPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/coupon/AppCouponTakeReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 优惠劵领取 Request VO\")\n@Data\npublic class AppCouponTakeReqVO {\n\n    @Schema(description = \"优惠劵模板编号\", example = \"1\")\n    @NotNull(message = \"优惠劵模板编号不能为空\")\n    private Long templateId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/template/AppCouponTemplatePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"用户 App - 优惠劵模板分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AppCouponTemplatePageReqVO extends PageParam {\n\n    @Schema(description = \"商品范围\", example = \"1\")\n    @InEnum(value = PromotionProductScopeEnum.class, message = \"商品范围，必须是 {value}\")\n    private Integer productScope;\n\n    @Schema(description = \"商品标号\", example = \"1\")\n    private Long spuId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/template/AppCouponTemplateRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 优惠劵模板 Response VO\")\n@Data\npublic class AppCouponTemplateRespVO {\n\n    @Schema(description = \"优惠劵模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"优惠劵名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"春节送送送\")\n    private String name;\n\n    @Schema(description = \"优惠券说明\", example = \"优惠券使用说明\")\n    private String description;\n\n    @Schema(description = \"发行总量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\") // -1 - 则表示不限制发放数量\n    private Integer totalCount;\n\n    @Schema(description = \"每人限领个数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"66\") // -1 - 则表示不限制\n    private Integer takeLimitCount;\n\n    @Schema(description = \"是否设置满多少金额可用\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\") // 单位：分；0 - 不限制\n    private Integer usePrice;\n\n    @Schema(description = \"商品范围\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer productScope;\n\n    @Schema(description = \"商品范围编号的数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private List<Long> productScopeValues;\n\n    @Schema(description = \"生效日期类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer validityType;\n\n    @Schema(description = \"固定日期 - 生效开始时间\")\n    private LocalDateTime validStartTime;\n\n    @Schema(description = \"固定日期 - 生效结束时间\")\n    private LocalDateTime validEndTime;\n\n    @Schema(description = \"领取日期 - 开始天数\")\n    @Min(value = 0L, message = \"开始天数必须大于 0\")\n    private Integer fixedStartTerm;\n\n    @Schema(description = \"领取日期 - 结束天数\")\n    @Min(value = 1L, message = \"开始天数必须大于 1\")\n    private Integer fixedEndTerm;\n\n    @Schema(description = \"优惠类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer discountType;\n\n    @Schema(description = \"折扣百分比\", example = \"80\") //  例如说，80% 为 80\n    private Integer discountPercent;\n\n    @Schema(description = \"优惠金额\", example = \"10\")\n    @Min(value = 0, message = \"优惠金额需要大于等于 0\")\n    private Integer discountPrice;\n\n    @Schema(description = \"折扣上限\", example = \"100\") // 单位：分，仅在 discountType 为 PERCENT 使用\n    private Integer discountLimitPrice;\n\n    @Schema(description = \"领取优惠券的数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer takeCount;\n\n    // ========== 用户相关字段 ==========\n\n    @Schema(description = \"是否可以领取\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean canTake;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/diy/AppDiyPageController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.controller.app.diy.vo.AppDiyPagePropertyRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;\nimport cn.iocoder.yudao.module.promotion.service.diy.DiyPageService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 APP - 装修页面\")\n@RestController\n@RequestMapping(\"/promotion/diy-page\")\n@Validated\npublic class AppDiyPageController {\n\n    @Resource\n    private DiyPageService diyPageService;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得装修页面\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PermitAll\n    public CommonResult<AppDiyPagePropertyRespVO> getDiyPage(@RequestParam(\"id\") Long id) {\n        DiyPageDO diyPage = diyPageService.getDiyPage(id);\n        return success(BeanUtils.toBean(diyPage, AppDiyPagePropertyRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/diy/AppDiyTemplateController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.controller.app.diy.vo.AppDiyTemplatePropertyRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.diy.DiyTemplateConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;\nimport cn.iocoder.yudao.module.promotion.enums.diy.DiyPageEnum;\nimport cn.iocoder.yudao.module.promotion.service.diy.DiyPageService;\nimport cn.iocoder.yudao.module.promotion.service.diy.DiyTemplateService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;\n\n@Tag(name = \"用户 APP - 装修模板\")\n@RestController\n@RequestMapping(\"/promotion/diy-template\")\n@Validated\npublic class AppDiyTemplateController {\n\n    @Resource\n    private DiyTemplateService diyTemplateService;\n    @Resource\n    private DiyPageService diyPageService;\n\n    // TODO @疯狂：要不要把 used 和 get 接口合并哈；不传递 id，直接拿默认；\n    @GetMapping(\"/used\")\n    @Operation(summary = \"使用中的装修模板\")\n    @PermitAll\n    public CommonResult<AppDiyTemplatePropertyRespVO> getUsedDiyTemplate() {\n        DiyTemplateDO diyTemplate = diyTemplateService.getUsedDiyTemplate();\n        return success(buildVo(diyTemplate));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得装修模板\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PermitAll\n    public CommonResult<AppDiyTemplatePropertyRespVO> getDiyTemplate(@RequestParam(\"id\") Long id) {\n        DiyTemplateDO diyTemplate = diyTemplateService.getDiyTemplate(id);\n        return success(buildVo(diyTemplate));\n    }\n\n    private AppDiyTemplatePropertyRespVO buildVo(DiyTemplateDO diyTemplate) {\n        if (diyTemplate == null) {\n            return null;\n        }\n        // 查询模板下的页面\n        List<DiyPageDO> pages = diyPageService.getDiyPageByTemplateId(diyTemplate.getId());\n        String home = findFirst(pages, page -> DiyPageEnum.INDEX.getName().equals(page.getName()), DiyPageDO::getProperty);\n        String user = findFirst(pages, page -> DiyPageEnum.MY.getName().equals(page.getName()), DiyPageDO::getProperty);\n        // 拼接返回\n        return DiyTemplateConvert.INSTANCE.convertPropertyVo2(diyTemplate, home, user);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/diy/vo/AppDiyPagePropertyRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.diy.vo;\n\nimport com.fasterxml.jackson.annotation.JsonRawValue;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\n@Schema(description = \"用户 App - 装修页面属性 Response VO\")\n@Data\n@ToString(callSuper = true)\npublic class AppDiyPagePropertyRespVO {\n\n    @Schema(description = \"装修页面编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31209\")\n    private Long id;\n\n    @Schema(description = \"页面名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    private String name;\n\n    @Schema(description = \"页面属性\", example = \"[]\")\n    @JsonRawValue\n    private String property;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/diy/vo/AppDiyTemplatePropertyRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.diy.vo;\n\nimport com.fasterxml.jackson.annotation.JsonRawValue;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\n@Schema(description = \"用户 App - 装修模板属性 Response VO\")\n@Data\n@ToString(callSuper = true)\npublic class AppDiyTemplatePropertyRespVO {\n\n    @Schema(description = \"装修模板编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31209\")\n    private Long id;\n\n    @Schema(description = \"模板名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"默认主题\")\n    private String name;\n\n    @Schema(description = \"模板属性\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n    @JsonRawValue\n    private String property;\n\n    @Schema(description = \"首页\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n    @JsonRawValue\n    private String home;\n\n    @Schema(description = \"我的\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{}\")\n    @JsonRawValue\n    private String user;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/AppKeFuMessageController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.kefu;\n\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;\nimport cn.iocoder.yudao.module.promotion.service.kefu.KeFuMessageService;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 APP - 客服消息\")\n@RestController\n@RequestMapping(\"/promotion/kefu-message\")\n@Validated\npublic class AppKeFuMessageController {\n\n    @Resource\n    private KeFuMessageService kefuMessageService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/send\")\n    @Operation(summary = \"发送客服消息\")\n    public CommonResult<Long> sendKefuMessage(@Valid @RequestBody AppKeFuMessageSendReqVO sendReqVO) {\n        sendReqVO.setSenderId(getLoginUserId()).setSenderType(UserTypeEnum.MEMBER.getValue()); // 设置用户编号和类型\n        return success(kefuMessageService.sendKefuMessage(sendReqVO));\n    }\n\n    @PutMapping(\"/update-read-status\")\n    @Operation(summary = \"更新客服消息已读状态\")\n    @Parameter(name = \"conversationId\", description = \"会话编号\", required = true)\n    public CommonResult<Boolean> updateKefuMessageReadStatus(@RequestParam(\"conversationId\") Long conversationId) {\n        kefuMessageService.updateKeFuMessageReadStatus(conversationId, getLoginUserId(), UserTypeEnum.MEMBER.getValue());\n        return success(true);\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得客服消息列表\")\n    public CommonResult<List<KeFuMessageRespVO>> getKefuMessageList(@Valid AppKeFuMessagePageReqVO pageReqVO) {\n        List<KeFuMessageDO> list = kefuMessageService.getKeFuMessageList(pageReqVO, getLoginUserId());\n\n        // 拼接数据\n        List<KeFuMessageRespVO> result = BeanUtils.toBean(list, KeFuMessageRespVO.class);\n        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(filterList(result,\n                item -> UserTypeEnum.ADMIN.getValue().equals(item.getSenderType())), KeFuMessageRespVO::getSenderId));\n        result.forEach(item -> findAndThen(userMap, item.getSenderId(), user -> item.setSenderAvatar(user.getAvatar())));\n        return success(result);\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/message/AppKeFuMessagePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"用户 App - 客服消息 Request VO\")\n@Data\npublic class AppKeFuMessagePageReqVO {\n\n    private static final Integer LIMIT = 10;\n\n    @Schema(description = \"会话编号\", example = \"12580\")\n    private Long conversationId;\n\n    @Schema(description = \"发送时间\", example = \"2024-03-27 12:00:00\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"每次查询条数，最大值为 100\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"每次查询条数不能为空\")\n    @Min(value = 1, message = \"每次查询条数最小值为 1\")\n    @Max(value = 200, message = \"每次查询最大值为 200\")\n    private Integer limit = LIMIT;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/message/AppKeFuMessageSendReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 发送客服消息 Request VO\")\n@Data\npublic class AppKeFuMessageSendReqVO {\n\n    @Schema(description = \"消息类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"消息类型不能为空\")\n    private Integer contentType;\n    @Schema(description = \"消息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"消息不能为空\")\n    private String content;\n\n    // ========== 后端设置的参数，前端无需传递 ==========\n\n    @Schema(description = \"发送人编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"24571\", hidden = true)\n    private Long senderId;\n    @Schema(description = \"发送人类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\", hidden = true)\n    private Integer senderType;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/package-info.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.kefu.vo;"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/AppPointActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.point;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.point.vo.AppPointActivityDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.point.vo.AppPointActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.point.vo.AppPointActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO;\nimport cn.iocoder.yudao.module.promotion.service.point.PointActivityService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\n\n@Tag(name = \"用户 App - 积分商城活动\")\n@RestController\n@RequestMapping(\"/promotion/point-activity\")\n@Validated\npublic class AppPointActivityController {\n\n    @Resource\n    private PointActivityService pointActivityService;\n\n    @Resource\n    private ProductSpuApi productSpuApi;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得积分商城活动分页\")\n    @PermitAll\n    public CommonResult<PageResult<AppPointActivityRespVO>> getPointActivityPage(AppPointActivityPageReqVO pageReqVO) {\n        // 1. 查询满足当前阶段的活动\n        PageResult<PointActivityDO> pageResult = pointActivityService.getPointActivityPage(\n                BeanUtils.toBean(pageReqVO, PointActivityPageReqVO.class));\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n\n        // 2. 拼接数据\n        List<AppPointActivityRespVO> resultList = buildAppPointActivityRespVOList(pageResult.getList());\n        return success(new PageResult<>(resultList, pageResult.getTotal()));\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得积分商城活动明细\")\n    @Parameter(name = \"id\", description = \"活动编号\", required = true, example = \"1024\")\n    @PermitAll\n    public CommonResult<AppPointActivityDetailRespVO> getPointActivity(@RequestParam(\"id\") Long id) {\n        // 1. 获取活动\n        PointActivityDO activity = pointActivityService.getPointActivity(id);\n        if (activity == null\n                || ObjUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {\n            return success(null);\n        }\n\n        // 2. 拼接数据\n        List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(Collections.singletonList(id));\n        PointProductDO minProduct = getMinObject(products, PointProductDO::getPoint);\n        assert minProduct != null;\n        AppPointActivityDetailRespVO respVO = BeanUtils.toBean(activity, AppPointActivityDetailRespVO.class)\n                .setProducts(BeanUtils.toBean(products, AppPointActivityDetailRespVO.Product.class))\n                .setPoint(minProduct.getPoint()).setPrice(minProduct.getPrice());\n        return success(respVO);\n    }\n\n    @GetMapping(\"/list-by-ids\")\n    @Operation(summary = \"获得积分商城活动列表，基于活动编号数组\")\n    @Parameter(name = \"ids\", description = \"活动编号数组\", required = true, example = \"[1024, 1025]\")\n    @PermitAll\n    public CommonResult<List<AppPointActivityRespVO>> getCombinationActivityListByIds(@RequestParam(\"ids\") List<Long> ids) {\n        // 1. 获得开启的活动列表\n        List<PointActivityDO> activityList = pointActivityService.getPointActivityListByIds(ids);\n        activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));\n        if (CollUtil.isEmpty(activityList)) {\n            return success(Collections.emptyList());\n        }\n        // 2. 拼接返回\n        List<AppPointActivityRespVO> result = buildAppPointActivityRespVOList(activityList);\n        return success(result);\n    }\n\n    private List<AppPointActivityRespVO> buildAppPointActivityRespVOList(List<PointActivityDO> activityList) {\n        List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(\n                convertSet(activityList, PointActivityDO::getId));\n        Map<Long, List<PointProductDO>> productsMap = convertMultiMap(products, PointProductDO::getActivityId);\n        Map<Long, ProductSpuRespDTO> spuMap = productSpuApi.getSpuMap(\n                convertSet(activityList, PointActivityDO::getSpuId));\n        List<AppPointActivityRespVO> result = BeanUtils.toBean(activityList, AppPointActivityRespVO.class);\n        result.forEach(activity -> {\n            // 设置 product 信息\n            PointProductDO minProduct = getMinObject(productsMap.get(activity.getId()), PointProductDO::getPoint);\n            assert minProduct != null;\n            activity.setPoint(minProduct.getPoint()).setPrice(minProduct.getPrice());\n            findAndThen(spuMap, activity.getSpuId(),\n                    spu -> activity.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n        });\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.point.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 积分商城活动的详细 Response VO\")\n@Data\npublic class AppPointActivityDetailRespVO {\n\n    @Schema(description = \"积分商城活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11373\")\n    private Long id;\n\n    @Schema(description = \"积分商城活动商品\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19509\")\n    private Long spuId;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer status;\n\n    @Schema(description = \"积分商城活动库存(剩余库存积分兑换时扣减)\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer stock;\n\n    @Schema(description = \"积分商城活动总库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer totalStock;\n\n    @Schema(description = \"备注\", example = \"你说的对\")\n    private String remark;\n\n    @Schema(description = \"商品信息数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Product> products;\n\n    //======================= 显示所需兑换积分最少的 SKU 信息 =======================\n\n    @Schema(description = \"兑换积分\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Integer point;\n\n    @Schema(description = \"兑换金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15860\")\n    private Integer price;\n\n    @Schema(description = \"商品信息\")\n    @Data\n    public static class Product {\n\n        @Schema(description = \"积分商城商品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"31718\")\n        private Long id;\n\n        @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2736\")\n        private Long skuId;\n\n        @Schema(description = \"可兑换数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3926\")\n        private Integer count;\n\n        @Schema(description = \"兑换积分\", requiredMode = Schema.RequiredMode.REQUIRED)\n        private Integer point;\n\n        @Schema(description = \"兑换金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15860\")\n        private Integer price;\n\n        @Schema(description = \"积分商城商品库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer stock;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.point.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"用户 App - 积分商城活动分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AppPointActivityPageReqVO extends PageParam {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.point.vo;\n\nimport cn.idev.excel.annotation.ExcelProperty;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 积分商城活动 Response VO\")\n@Data\npublic class AppPointActivityRespVO {\n\n    @Schema(description = \"积分商城活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11373\")\n    @ExcelProperty(\"积分商城活动编号\")\n    private Long id;\n\n    @Schema(description = \"积分商城活动商品\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19509\")\n    @ExcelProperty(\"积分商城活动商品\")\n    private Long spuId;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"活动状态\")\n    private Integer status;\n\n    @Schema(description = \"积分商城活动库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"积分商城活动库存\")\n    private Integer stock; // 剩余库存积分兑换时扣减\n\n    @Schema(description = \"积分商城活动总库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    @ExcelProperty(\"积分商城活动总库存\")\n    private Integer totalStock;\n\n    // ========== 商品字段 ==========\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取\n            example = \"618大促\")\n    private String spuName;\n    @Schema(description = \"商品主图\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取\n            example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n    @Schema(description = \"商品市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取\n            example = \"50\")\n    private Integer marketPrice;\n\n    //======================= 显示所需兑换积分最少的 sku 信息 =======================\n\n    @Schema(description = \"兑换积分\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Integer point;\n\n    @Schema(description = \"兑换金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15860\")\n    private Integer price;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/reward/AppRewardActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.reward;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.controller.app.reward.vo.AppRewardActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;\nimport cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 App - 满减送活动\")\n@RestController\n@RequestMapping(\"/promotion/reward-activity\")\n@Validated\npublic class AppRewardActivityController {\n\n    @Resource\n    private RewardActivityService rewardActivityService;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得满减送活动\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PermitAll\n    public CommonResult<AppRewardActivityRespVO> getRewardActivity(@RequestParam(\"id\") Long id) {\n        RewardActivityDO activity = rewardActivityService.getRewardActivity(id);\n        if (activity == null) {\n            return success(null);\n        }\n        // 拼接 Rule 描述\n        AppRewardActivityRespVO activityVO = BeanUtils.toBean(activity, AppRewardActivityRespVO.class);\n        for (int i = 0; i < activityVO.getRules().size(); i++) {\n            AppRewardActivityRespVO.Rule ruleVO = activityVO.getRules().get(i);\n            RewardActivityDO.Rule rule = activity.getRules().get(i);\n            ruleVO.setDescription(rewardActivityService.getRewardActivityRuleDescription(activity.getConditionType(), rule));\n        }\n        return success(activityVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/reward/vo/AppRewardActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.reward.vo;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityBaseVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 满减送活动 Response VO\")\n@Data\npublic class AppRewardActivityRespVO {\n\n    @Schema(description = \"活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer id;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"活动标题\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"满啦满啦\")\n    private String name;\n\n    @Schema(description = \"开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"条件类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer conditionType;\n\n    @Schema(description = \"商品范围\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer productScope;\n\n    @Schema(description = \"商品 SPU 编号的数组\", example = \"1,2,3\")\n    private List<Long> productScopeValues;\n\n    @Schema(description = \"优惠规则的数组\")\n    private List<Rule> rules;\n\n    @Schema(description = \"优惠规则\")\n    @Data\n    public static class Rule extends RewardActivityBaseVO.Rule {\n\n        @Schema(description = \"规则描述\")\n        private String description; // 通过 {@link #limit}、{@link #discountPrice} 等字段进行拼接\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.seckill;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityNowRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.seckill.SeckillActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;\nimport cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;\nimport cn.iocoder.yudao.module.promotion.service.seckill.SeckillConfigService;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.time.Duration;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBetween;\n\n@Tag(name = \"用户 App - 秒杀活动\")\n@RestController\n@RequestMapping(\"/promotion/seckill-activity\")\n@Validated\npublic class AppSeckillActivityController {\n\n    /**\n     * {@link AppSeckillActivityNowRespVO} 缓存，通过它异步刷新 {@link #getNowSeckillActivity()} 所要的首页数据\n     */\n    private final LoadingCache<String, AppSeckillActivityNowRespVO> nowSeckillActivityCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),\n            new CacheLoader<String, AppSeckillActivityNowRespVO>() {\n\n                @Override\n                public AppSeckillActivityNowRespVO load(String key) {\n                     return getNowSeckillActivity0();\n                }\n\n            });\n\n    @Resource\n    private SeckillActivityService activityService;\n    @Resource\n    @Lazy\n    private SeckillConfigService configService;\n\n    @Resource\n    private ProductSpuApi spuApi;\n\n    @GetMapping(\"/get-now\")\n    @Operation(summary = \"获得当前秒杀活动\", description = \"获取当前正在进行的活动，提供给首页使用\")\n    @PermitAll\n    public CommonResult<AppSeckillActivityNowRespVO> getNowSeckillActivity() {\n        return success(nowSeckillActivityCache.getUnchecked(\"\")); // 缓存\n    }\n\n    private AppSeckillActivityNowRespVO getNowSeckillActivity0() {\n        // 1. 获取当前时间处在哪个秒杀阶段\n        SeckillConfigDO config = configService.getCurrentSeckillConfig();\n        if (config == null) { // 时段不存在直接返回 null\n            return new AppSeckillActivityNowRespVO();\n        }\n\n        // 2.1 查询满足当前阶段的活动\n        List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(config.getId(), CommonStatusEnum.ENABLE.getStatus());\n        List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(\n                convertList(activityList, SeckillActivityDO::getId));\n        // 2.2 获取 spu 信息\n        List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId)).getCheckedData();\n        return SeckillActivityConvert.INSTANCE.convert(config, activityList, productList, spuList);\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得秒杀活动分页\")\n    @PermitAll\n    public CommonResult<PageResult<AppSeckillActivityRespVO>> getSeckillActivityPage(AppSeckillActivityPageReqVO pageReqVO) {\n        // 1. 查询满足当前阶段的活动\n        PageResult<SeckillActivityDO> pageResult = activityService.getSeckillActivityAppPageByConfigId(pageReqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty(pageResult.getTotal()));\n        }\n        List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(\n                convertList(pageResult.getList(), SeckillActivityDO::getId));\n\n        // 2. 拼接数据\n        List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(pageResult.getList(), SeckillActivityDO::getSpuId)).getCheckedData();\n        return success(SeckillActivityConvert.INSTANCE.convertPage02(pageResult, productList, spuList));\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得秒杀活动明细\")\n    @Parameter(name = \"id\", description = \"活动编号\", required = true, example = \"1024\")\n    @PermitAll\n    public CommonResult<AppSeckillActivityDetailRespVO> getSeckillActivity(@RequestParam(\"id\") Long id) {\n        // 1. 获取活动\n        SeckillActivityDO activity = activityService.getSeckillActivity(id);\n        if (activity == null\n                || ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {\n            return success(null);\n        }\n\n        // 2. 获取时间段\n        List<SeckillConfigDO> configs = configService.getSeckillConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        configs.removeIf(config -> !CollUtil.contains(activity.getConfigIds(), config.getId()));\n        // 2.1 优先使用当前时间段\n        SeckillConfigDO config = findFirst(configs, config0 -> isBetween(config0.getStartTime(), config0.getEndTime()));\n        // 2.2 如果没有，则获取最后一个，因为倾向优先展示“未开始” > “已结束”\n        if (config == null) {\n            config = CollUtil.getLast(configs);\n        }\n        if (config == null) {\n            return null;\n        }\n        // 3. 计算开始时间、结束时间\n        LocalDate nowDate;\n        // 3.1 如果在活动日期范围内，则以今天为 nowDate\n        if (LocalDateTimeUtils.isBetween(activity.getStartTime(), activity.getEndTime())) {\n            nowDate = LocalDate.now();\n        } else {\n            // 3.2 如果不在活动时间范围内，则直接以活动的 endTime 作为 nowDate，因为还是倾向优先展示“未开始” > “已结束”\n            nowDate = activity.getEndTime().toLocalDate();\n        }\n        LocalDateTime startTime = LocalDateTime.of(nowDate, LocalTime.parse(config.getStartTime()));\n        LocalDateTime endTime = LocalDateTime.of(nowDate, LocalTime.parse(config.getEndTime()));\n\n        // 4. 拼接数据\n        List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId(activity.getId());\n        return success(SeckillActivityConvert.INSTANCE.convert3(activity, productList, startTime, endTime));\n    }\n\n    @GetMapping(\"/list-by-ids\")\n    @Operation(summary = \"获得秒杀活动列表，基于活动编号数组\")\n    @Parameter(name = \"ids\", description = \"活动编号数组\", required = true, example = \"[1024, 1025]\")\n    @PermitAll\n    public CommonResult<List<AppSeckillActivityRespVO>> getCombinationActivityListByIds(@RequestParam(\"ids\") List<Long> ids) {\n        // 1. 获得开启的活动列表\n        List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByIds(ids);\n        activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));\n        if (CollUtil.isEmpty(activityList)) {\n            return success(Collections.emptyList());\n        }\n        // 2. 拼接返回\n        List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(\n                convertList(activityList, SeckillActivityDO::getId));\n        List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId)).getCheckedData();\n        return success(SeckillActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillConfigController.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.seckill;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config.AppSeckillConfigRespVO;\nimport cn.iocoder.yudao.module.promotion.convert.seckill.SeckillConfigConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;\nimport cn.iocoder.yudao.module.promotion.service.seckill.SeckillConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 App - 秒杀时间段\")\n@RestController\n@RequestMapping(\"/promotion/seckill-config\")\n@Validated\npublic class AppSeckillConfigController {\n    @Resource\n    private SeckillConfigService configService;\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得秒杀时间段列表\")\n    @PermitAll\n    public CommonResult<List<AppSeckillConfigRespVO>> getSeckillConfigList() {\n        List<SeckillConfigDO> list = configService.getSeckillConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(SeckillConfigConvert.INSTANCE.convertList2(list));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/vo/activity/AppSeckillActivityDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 秒杀活动的详细 Response VO\")\n@Data\npublic class AppSeckillActivityDetailRespVO {\n\n    @Schema(description = \"秒杀活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"秒杀活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"晚九点限时秒杀\")\n    private String name;\n\n    @Schema(description = \"活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"活动开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime startTime;\n\n    @Schema(description = \"活动结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime endTime;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long spuId;\n\n    @Schema(description = \"总共限购数量\", example = \"10\")\n    private Integer totalLimitCount;\n\n    @Schema(description = \"单次限购数量\", example = \"5\")\n    private Integer singleLimitCount;\n\n    @Schema(description = \"秒杀库存（剩余）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n    private Integer stock;\n\n    @Schema(description = \"秒杀库存（总计）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer totalStock;\n\n    @Schema(description = \"商品信息数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Product> products;\n\n    @Schema(description = \"商品信息\")\n    @Data\n    public static class Product {\n\n        @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4096\")\n        private Long skuId;\n\n        @Schema(description = \"秒杀金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer seckillPrice;\n\n        @Schema(description = \"秒杀限量库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n        private Integer stock;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/vo/activity/AppSeckillActivityNowRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity;\n\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config.AppSeckillConfigRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 当前秒杀活动 Response VO\")\n@Data\npublic class AppSeckillActivityNowRespVO {\n\n    @Schema(description = \"秒杀时间段\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private AppSeckillConfigRespVO config;\n\n    @Schema(description = \"秒杀活动数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<AppSeckillActivityRespVO> activities;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/vo/activity/AppSeckillActivityPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"用户 App - 商品评价分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AppSeckillActivityPageReqVO extends PageParam {\n\n    @Schema(description = \"秒杀配置编号\", example = \"1024\")\n    private Long configId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/vo/activity/AppSeckillActivityRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 秒杀活动 Response VO\")\n@Data\npublic class AppSeckillActivityRespVO {\n\n    @Schema(description = \"秒杀活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"秒杀活动名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"晚九点限时秒杀\")\n    private String name;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SPU 名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"一个白菜\")\n    private String spuName; // 从 SPU 的 name 读取\n\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取\n            example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n\n    @Schema(description = \"商品市场价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取\n            example = \"50\")\n    private Integer marketPrice;\n\n    @Schema(description = \"秒杀活动状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"秒杀库存（剩余）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer stock;\n    @Schema(description = \"秒杀库存（总共）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"200\")\n    private Integer totalStock;\n\n    @Schema(description = \"秒杀金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer seckillPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/vo/config/AppSeckillConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 秒杀时间段 Response VO\")\n@Data\npublic class AppSeckillConfigRespVO {\n\n    @Schema(description = \"秒杀时间段编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"开始时间点\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"09:00\")\n    private String startTime;\n    @Schema(description = \"结束时间点\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"09:59\")\n    private String endTime;\n\n    @Schema(description = \"轮播图\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> sliderPicUrls;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/article/ArticleCategoryConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategorySimpleRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.article.vo.category.AppArticleCategoryRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 文章分类 Convert\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface ArticleCategoryConvert {\n\n    ArticleCategoryConvert INSTANCE = Mappers.getMapper(ArticleCategoryConvert.class);\n\n    ArticleCategoryDO convert(ArticleCategoryCreateReqVO bean);\n\n    ArticleCategoryDO convert(ArticleCategoryUpdateReqVO bean);\n\n    ArticleCategoryRespVO convert(ArticleCategoryDO bean);\n\n    List<ArticleCategoryRespVO> convertList(List<ArticleCategoryDO> list);\n\n    PageResult<ArticleCategoryRespVO> convertPage(PageResult<ArticleCategoryDO> page);\n\n    List<ArticleCategorySimpleRespVO> convertList03(List<ArticleCategoryDO> list);\n\n    List<AppArticleCategoryRespVO> convertList04(List<ArticleCategoryDO> categoryList);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/article/ArticleConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticleRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 文章管理 Convert\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface ArticleConvert {\n\n    ArticleConvert INSTANCE = Mappers.getMapper(ArticleConvert.class);\n\n    ArticleDO convert(ArticleCreateReqVO bean);\n\n    ArticleDO convert(ArticleUpdateReqVO bean);\n\n    ArticleRespVO convert(ArticleDO bean);\n\n    List<ArticleRespVO> convertList(List<ArticleDO> list);\n\n    PageResult<ArticleRespVO> convertPage(PageResult<ArticleDO> page);\n\n    AppArticleRespVO convert01(ArticleDO article);\n\n    PageResult<AppArticleRespVO> convertPage02(PageResult<ArticleDO> articlePage);\n\n    List<AppArticleRespVO> convertList03(List<ArticleDO> articleCategoryListByRecommendHotAndRecommendBanner);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/banner/BannerConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.banner;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.banner.vo.AppBannerRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n@Mapper\npublic interface BannerConvert {\n\n    BannerConvert INSTANCE = Mappers.getMapper(BannerConvert.class);\n\n    List<BannerRespVO> convertList(List<BannerDO> list);\n\n    PageResult<BannerRespVO> convertPage(PageResult<BannerDO> pageResult);\n\n    BannerRespVO convert(BannerDO banner);\n\n    BannerDO convert(BannerCreateReqVO createReqVO);\n\n    BannerDO convert(BannerUpdateReqVO updateReqVO);\n\n    List<AppBannerRespVO> convertList01(List<BannerDO> bannerList);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/bargain/BargainActivityConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityBaseVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageItemRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.activity.AppBargainActivityDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.activity.AppBargainActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\n\n/**\n * 拼团活动 Convert\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface BargainActivityConvert {\n\n    BargainActivityConvert INSTANCE = Mappers.getMapper(BargainActivityConvert.class);\n\n    BargainActivityDO convert(BargainActivityBaseVO bean);\n\n    BargainActivityDO convert(BargainActivityUpdateReqVO bean);\n\n    BargainActivityRespVO convert(BargainActivityDO bean);\n\n    List<BargainActivityRespVO> convertList(List<BargainActivityDO> list);\n\n    PageResult<BargainActivityPageItemRespVO> convertPage(PageResult<BargainActivityDO> page);\n\n    default PageResult<BargainActivityPageItemRespVO> convertPage(PageResult<BargainActivityDO> page, List<ProductSpuRespDTO> spuList,\n                                                                  Map<Long, Integer> recordUserCountMap, Map<Long, Integer> recordSuccessUserCountMap,\n                                                                  Map<Long, Integer> helpUserCountMap) {\n        PageResult<BargainActivityPageItemRespVO> result = convertPage(page);\n        // 拼接关联属性\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        result.getList().forEach(item -> {\n            findAndThen(spuMap, item.getSpuId(), spu -> {\n                item.setPicUrl(spu.getPicUrl()).setSpuName(spu.getName());\n            });\n            // 设置统计字段\n            item.setRecordUserCount(recordUserCountMap.getOrDefault(item.getId(), 0))\n                    .setRecordSuccessUserCount(recordSuccessUserCountMap.getOrDefault(item.getId(), 0))\n                    .setHelpUserCount(helpUserCountMap.getOrDefault(item.getId(), 0));\n        });\n        return result;\n    }\n\n    AppBargainActivityDetailRespVO convert1(BargainActivityDO bean);\n\n    default AppBargainActivityDetailRespVO convert(BargainActivityDO bean, Integer successUserCount, ProductSpuRespDTO spu) {\n        AppBargainActivityDetailRespVO detail = convert1(bean).setSuccessUserCount(successUserCount);\n        if (spu != null) {\n            detail.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice());\n        }\n        return detail;\n    }\n\n    PageResult<AppBargainActivityRespVO> convertAppPage(PageResult<BargainActivityDO> page);\n\n    default PageResult<AppBargainActivityRespVO> convertAppPage(PageResult<BargainActivityDO> page, List<ProductSpuRespDTO> spuList) {\n        PageResult<AppBargainActivityRespVO> result = convertAppPage(page);\n        // 拼接关联属性\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        List<AppBargainActivityRespVO> list = CollectionUtils.convertList(result.getList(), item -> {\n            findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n            return item;\n        });\n        result.setList(list);\n        return result;\n    }\n\n    List<AppBargainActivityRespVO> convertAppList(List<BargainActivityDO> list);\n\n    default List<AppBargainActivityRespVO> convertAppList(List<BargainActivityDO> list, List<ProductSpuRespDTO> spuList) {\n        List<AppBargainActivityRespVO> activityList = convertAppList(list);\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        return CollectionUtils.convertList(activityList, item -> {\n            findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n            return item;\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/bargain/BargainHelpConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.help.BargainHelpRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 砍价助力 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface BargainHelpConvert {\n\n    BargainHelpConvert INSTANCE = Mappers.getMapper(BargainHelpConvert.class);\n\n    default PageResult<BargainHelpRespVO> convertPage(PageResult<BargainHelpDO> page,\n                                                      Map<Long, MemberUserRespDTO> userMap) {\n        PageResult<BargainHelpRespVO> pageResult = convertPage(page);\n        // 拼接数据\n        pageResult.getList().forEach(record ->\n                MapUtils.findAndThen(userMap, record.getUserId(),\n                        user -> record.setNickname(user.getNickname()).setAvatar(user.getAvatar())));\n        return pageResult;\n    }\n    PageResult<BargainHelpRespVO> convertPage(PageResult<BargainHelpDO> page);\n\n    default List<AppBargainHelpRespVO> convertList(List<BargainHelpDO> helps,\n                                                   Map<Long, MemberUserRespDTO> userMap) {\n        List<AppBargainHelpRespVO> helpVOs = convertList02(helps);\n        helpVOs.forEach(help ->\n                MapUtils.findAndThen(userMap, help.getUserId(),\n                        user -> help.setNickname(user.getNickname()).setAvatar(user.getAvatar())));\n        return helpVOs;\n    }\n    List<AppBargainHelpRespVO> convertList02(List<BargainHelpDO> helps);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/bargain/BargainRecordConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.recrod.BargainRecordPageItemRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordSummaryRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;\nimport cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 砍价记录 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface BargainRecordConvert {\n\n    BargainRecordConvert INSTANCE = Mappers.getMapper(BargainRecordConvert.class);\n\n    default PageResult<BargainRecordPageItemRespVO> convertPage(PageResult<BargainRecordDO> page,\n                                                                Map<Long, Integer> helpCountMap,\n                                                                List<BargainActivityDO> activityList,\n                                                                Map<Long, MemberUserRespDTO> userMap) {\n        PageResult<BargainRecordPageItemRespVO> pageResult = convertPage(page);\n        // 拼接数据\n        Map<Long, BargainActivityDO> activityMap = convertMap(activityList, BargainActivityDO::getId);\n        pageResult.getList().forEach(record -> {\n            MapUtils.findAndThen(userMap, record.getUserId(),\n                    user -> record.setNickname(user.getNickname()).setAvatar(user.getAvatar()));\n            record.setActivity(BargainActivityConvert.INSTANCE.convert(activityMap.get(record.getActivityId())))\n                    .setHelpCount(helpCountMap.getOrDefault(record.getId(), 0));\n        });\n        return pageResult;\n    }\n    PageResult<BargainRecordPageItemRespVO> convertPage(PageResult<BargainRecordDO> page);\n\n    default PageResult<AppBargainRecordRespVO> convertPage02(PageResult<BargainRecordDO> page,\n                                                             List<BargainActivityDO> activityList,\n                                                             List<ProductSpuRespDTO> spuList,\n                                                             List<TradeOrderRespDTO> orderList) {\n        PageResult<AppBargainRecordRespVO> pageResult = convertPage02(page);\n        // 拼接数据\n        Map<Long, BargainActivityDO> activityMap = convertMap(activityList, BargainActivityDO::getId);\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        Map<Long, TradeOrderRespDTO> orderMap = convertMap(orderList, TradeOrderRespDTO::getId);\n        pageResult.getList().forEach(record -> {\n            MapUtils.findAndThen(activityMap, record.getActivityId(),\n                    activity -> record.setActivityName(activity.getName()).setEndTime(activity.getEndTime()));\n            MapUtils.findAndThen(spuMap, record.getSpuId(),\n                    spu -> record.setPicUrl(record.getPicUrl()));\n            MapUtils.findAndThen(orderMap, record.getOrderId(),\n                    order -> record.setPayStatus(order.getPayStatus()).setPayOrderId(order.getPayOrderId()));\n        });\n        return pageResult;\n    }\n    PageResult<AppBargainRecordRespVO> convertPage02(PageResult<BargainRecordDO> page);\n\n    default AppBargainRecordSummaryRespVO convert(Integer successUserCount, List<BargainRecordDO> successList,\n                                                  List<BargainActivityDO> activityList, Map<Long, MemberUserRespDTO> userMap) {\n        AppBargainRecordSummaryRespVO summary = new AppBargainRecordSummaryRespVO().setSuccessUserCount(successUserCount);\n        Map<Long, BargainActivityDO> activityMap = convertMap(activityList, BargainActivityDO::getId);\n        summary.setSuccessList(CollectionUtils.convertList(successList, record -> {\n            AppBargainRecordSummaryRespVO.Record recordVO = new AppBargainRecordSummaryRespVO.Record();\n            MapUtils.findAndThen(userMap, record.getUserId(),\n                    user -> recordVO.setNickname(user.getNickname()).setAvatar(user.getAvatar()));\n            MapUtils.findAndThen(activityMap, record.getActivityId(),\n                    activity -> recordVO.setActivityName(activity.getName()));\n            return recordVO;\n        }));\n        return summary;\n    }\n\n    @Mapping(source = \"record.id\", target = \"id\")\n    @Mapping(source = \"record.userId\", target = \"userId\")\n    @Mapping(source = \"record.status\", target = \"status\")\n    AppBargainRecordDetailRespVO convert02(BargainRecordDO record, Integer helpAction, TradeOrderRespDTO order);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.combination;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageItemRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordPageItemRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;\nimport cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\n\n/**\n * 拼团活动 Convert\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface CombinationActivityConvert {\n\n    CombinationActivityConvert INSTANCE = Mappers.getMapper(CombinationActivityConvert.class);\n\n    CombinationActivityDO convert(CombinationActivityCreateReqVO bean);\n\n    CombinationActivityDO convert(CombinationActivityUpdateReqVO bean);\n\n    CombinationActivityRespVO convert(CombinationActivityDO bean);\n\n    CombinationProductRespVO convert(CombinationProductDO bean);\n\n    default CombinationActivityRespVO convert(CombinationActivityDO activity, List<CombinationProductDO> products) {\n        return convert(activity).setProducts(convertList2(products));\n    }\n\n    List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list);\n\n    default PageResult<CombinationActivityPageItemRespVO> convertPage(PageResult<CombinationActivityDO> page,\n                                                                      List<CombinationProductDO> productList,\n                                                                      Map<Long, Integer> groupCountMap,\n                                                                      Map<Long, Integer> groupSuccessCountMap,\n                                                                      Map<Long, Integer> recordCountMap,\n                                                                      List<ProductSpuRespDTO> spuList) {\n        PageResult<CombinationActivityPageItemRespVO> pageResult = convertPage(page);\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);\n        pageResult.getList().forEach(item -> {\n            MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl())\n                    .setMarketPrice(spu.getMarketPrice()));\n            item.setProducts(convertList2(productMap.get(item.getId())));\n            // 设置统计字段\n            item.setGroupCount(groupCountMap.getOrDefault(item.getId(), 0))\n                    .setGroupSuccessCount(groupSuccessCountMap.getOrDefault(item.getId(), 0))\n                    .setRecordCount(recordCountMap.getOrDefault(item.getId(), 0));\n        });\n        return pageResult;\n    }\n\n    PageResult<CombinationActivityPageItemRespVO> convertPage(PageResult<CombinationActivityDO> page);\n\n    List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs);\n\n    @Mappings({\n            @Mapping(target = \"id\", ignore = true),\n            @Mapping(target = \"activityId\", source = \"activity.id\"),\n            @Mapping(target = \"spuId\", source = \"activity.spuId\"),\n            @Mapping(target = \"skuId\", source = \"product.skuId\"),\n            @Mapping(target = \"combinationPrice\", source = \"product.combinationPrice\"),\n            @Mapping(target = \"activityStartTime\", source = \"activity.startTime\"),\n            @Mapping(target = \"activityEndTime\", source = \"activity.endTime\")\n    })\n    CombinationProductDO convert(CombinationActivityDO activity, CombinationProductBaseVO product);\n\n    default List<CombinationProductDO> convertList(List<? extends CombinationProductBaseVO> products, CombinationActivityDO activity) {\n        return CollectionUtils.convertList(products, item -> convert(activity, item).setActivityStatus(activity.getStatus()));\n    }\n\n    default List<CombinationProductDO> convertList(List<CombinationProductBaseVO> updateProductVOs,\n                                                   List<CombinationProductDO> products, CombinationActivityDO activity) {\n        Map<Long, Long> productMap = convertMap(products, CombinationProductDO::getSkuId, CombinationProductDO::getId);\n        return CollectionUtils.convertList(updateProductVOs, updateProductVO -> convert(activity, updateProductVO)\n                .setId(productMap.get(updateProductVO.getSkuId()))\n                .setActivityStatus(activity.getStatus()));\n    }\n\n    CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO);\n\n    default CombinationRecordCreateRespDTO convert4(CombinationRecordDO combinationRecord) {\n        return new CombinationRecordCreateRespDTO().setCombinationActivityId(combinationRecord.getActivityId())\n                .setCombinationRecordId(combinationRecord.getId()).setCombinationHeadId(combinationRecord.getHeadId());\n    }\n\n    default CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO,\n                                        CombinationActivityDO activity, MemberUserRespDTO user,\n                                        ProductSpuRespDTO spu, ProductSkuRespDTO sku) {\n        return convert(reqDTO).setVirtualGroup(false)\n                .setStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus()) // 创建后默认状态为进行中\n                .setUserSize(activity.getUserSize()).setUserCount(1) // 默认就是 1 插入后会接着更新一次所有的拼团记录\n                .setNickname(user.getNickname()).setAvatar(user.getAvatar()) // 用户信息\n                .setSpuName(spu.getName()).setPicUrl(ObjectUtil.defaultIfBlank(sku.getPicUrl(), spu.getPicUrl())); // 商品信息\n    }\n\n    default List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list,\n                                                        List<CombinationProductDO> productList,\n                                                        List<ProductSpuRespDTO> spuList) {\n        List<CombinationActivityRespVO> activityList = BeanUtils.toBean(list, CombinationActivityRespVO.class);\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);\n        return CollectionUtils.convertList(activityList, item -> {\n            // 设置 product 信息\n            item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));\n            // 设置 SPU 信息\n            findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())\n                    .setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n            return item;\n        });\n    }\n\n    default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list,\n                                                              List<CombinationProductDO> productList,\n                                                              List<ProductSpuRespDTO> spuList) {\n        List<AppCombinationActivityRespVO> activityList = BeanUtils.toBean(list, AppCombinationActivityRespVO.class);\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);\n        return CollectionUtils.convertList(activityList, item -> {\n            // 设置 product 信息\n            item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));\n            // 设置 SPU 信息\n            findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())\n                    .setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n            return item;\n        });\n    }\n\n    default PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result,\n                                                                    List<CombinationProductDO> productList,\n                                                                    List<ProductSpuRespDTO> spuList) {\n        return new PageResult<>(convertAppList(result.getList(), productList, spuList), result.getTotal());\n    }\n\n    AppCombinationActivityDetailRespVO convert2(CombinationActivityDO combinationActivity);\n\n    List<AppCombinationActivityDetailRespVO.Product> convertList1(List<CombinationProductDO> products);\n\n    default AppCombinationActivityDetailRespVO convert3(CombinationActivityDO combinationActivity, List<CombinationProductDO> products) {\n        return convert2(combinationActivity).setProducts(convertList1(products));\n    }\n\n    List<AppCombinationRecordRespVO> convertList3(List<CombinationRecordDO> records);\n\n    AppCombinationRecordRespVO convert(CombinationRecordDO record);\n\n    PageResult<CombinationRecordPageItemRespVO> convert(PageResult<CombinationRecordDO> result);\n\n    default PageResult<CombinationRecordPageItemRespVO> convert(PageResult<CombinationRecordDO> recordPage, List<CombinationActivityDO> activities, List<CombinationProductDO> products) {\n        PageResult<CombinationRecordPageItemRespVO> result = convert(recordPage);\n        // 拼接关联属性\n        Map<Long, CombinationActivityDO> activityMap = convertMap(activities, CombinationActivityDO::getId);\n        Map<Long, List<CombinationProductDO>> productsMap = convertMultiMap(products, CombinationProductDO::getActivityId);\n        result.setList(CollectionUtils.convertList(result.getList(), item -> {\n            findAndThen(activityMap, item.getActivityId(), activity -> {\n                item.setActivity(convert(activity).setProducts(convertList2(productsMap.get(item.getActivityId()))));\n            });\n            return item;\n        }));\n        return result;\n    }\n\n    default AppCombinationRecordDetailRespVO convert(Long userId, CombinationRecordDO headRecord, List<CombinationRecordDO> memberRecords) {\n        AppCombinationRecordDetailRespVO respVO = new AppCombinationRecordDetailRespVO()\n                .setHeadRecord(convert(headRecord)).setMemberRecords(convertList3(memberRecords));\n        // 处理自己参与拼团的 orderId\n        CombinationRecordDO userRecord = CollectionUtils.findFirst(memberRecords, r -> ObjectUtil.equal(r.getUserId(), userId));\n        if (userRecord == null && ObjectUtil.equal(headRecord.getUserId(), userId)) {\n            userRecord = headRecord;\n        }\n        respVO.setOrderId(userRecord == null ? null : userRecord.getOrderId());\n        return respVO;\n    }\n\n    /**\n     * 转换生成虚拟成团虚拟记录\n     *\n     * @param headRecord 虚拟成团团长记录\n     * @return 虚拟记录列表\n     */\n    default List<CombinationRecordDO> convertVirtualRecordList(CombinationRecordDO headRecord) {\n        int count = headRecord.getUserSize() - headRecord.getUserCount();\n        List<CombinationRecordDO> createRecords = new ArrayList<>(count);\n        for (int i = 0; i < count; i++) {\n            // 基础信息和团长保持一致\n            CombinationRecordDO newRecord = convert5(headRecord).setHeadId(headRecord.getId());\n            // 虚拟信息\n            newRecord.setCount(0) // 会单独更新下，在后续的 Service 逻辑里\n                    .setUserId(0L).setNickname(\"\").setAvatar(\"\").setOrderId(0L);\n            createRecords.add(newRecord);\n        }\n        return createRecords;\n    }\n    @Mapping(target = \"id\", ignore = true)\n    CombinationRecordDO convert5(CombinationRecordDO headRecord);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.coupon;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.time.LocalDateTime;\nimport java.util.Collection;\n\n/**\n * 优惠劵 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface CouponConvert {\n\n    CouponConvert INSTANCE = Mappers.getMapper(CouponConvert.class);\n\n    PageResult<CouponPageItemRespVO> convertPage(PageResult<CouponDO> page);\n\n    CouponRespDTO convert(CouponDO bean);\n\n    default CouponDO convert(CouponTemplateDO template, Long userId) {\n        CouponDO coupon = new CouponDO()\n                .setTemplateId(template.getId())\n                .setName(template.getName())\n                .setTakeType(template.getTakeType())\n                .setUsePrice(template.getUsePrice())\n                .setProductScope(template.getProductScope())\n                .setProductScopeValues(template.getProductScopeValues())\n                .setDiscountType(template.getDiscountType())\n                .setDiscountPercent(template.getDiscountPercent())\n                .setDiscountPrice(template.getDiscountPrice())\n                .setDiscountLimitPrice(template.getDiscountLimitPrice())\n                .setStatus(CouponStatusEnum.UNUSED.getStatus())\n                .setUserId(userId);\n        if (CouponTemplateValidityTypeEnum.DATE.getType().equals(template.getValidityType())) {\n            coupon.setValidStartTime(template.getValidStartTime());\n            coupon.setValidEndTime(template.getValidEndTime());\n        } else if (CouponTemplateValidityTypeEnum.TERM.getType().equals(template.getValidityType())) {\n            coupon.setValidStartTime(LocalDateTime.now().plusDays(template.getFixedStartTerm()));\n            coupon.setValidEndTime(coupon.getValidStartTime().plusDays(template.getFixedEndTerm()));\n        }\n        return coupon;\n    }\n\n    CouponPageReqVO convert(AppCouponPageReqVO pageReqVO, Collection<Long> userIds);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponTemplateConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.coupon;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplateRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 优惠劵模板 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface CouponTemplateConvert {\n\n    CouponTemplateConvert INSTANCE = Mappers.getMapper(CouponTemplateConvert.class);\n\n    CouponTemplateDO convert(CouponTemplateCreateReqVO bean);\n\n    CouponTemplateDO convert(CouponTemplateUpdateReqVO bean);\n\n    CouponTemplateRespVO convert(CouponTemplateDO bean);\n\n    PageResult<CouponTemplateRespVO> convertPage(PageResult<CouponTemplateDO> page);\n\n    CouponTemplatePageReqVO convert(AppCouponTemplatePageReqVO pageReqVO, List<Integer> canTakeTypes, Integer productScope, Long productScopeValue);\n\n    PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult);\n\n    List<AppCouponTemplateRespVO> convertAppList(List<CouponTemplateDO> list);\n\n    default PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult, Map<Long, Boolean> userCanTakeMap) {\n        PageResult<AppCouponTemplateRespVO> result = convertAppPage(pageResult);\n        copyTo(result.getList(), userCanTakeMap);\n        return result;\n    }\n\n    default List<AppCouponTemplateRespVO> convertAppList(List<CouponTemplateDO> list, Map<Long, Boolean> userCanTakeMap) {\n        List<AppCouponTemplateRespVO> result = convertAppList(list);\n        copyTo(result, userCanTakeMap);\n        return result;\n    }\n\n    default void copyTo(List<AppCouponTemplateRespVO> list, Map<Long, Boolean> userCanTakeMap) {\n        for (AppCouponTemplateRespVO template : list) {\n            // 检查已领取数量是否超过限领数量\n            template.setCanTake(MapUtil.getBool(userCanTakeMap, template.getId(), false));\n        }\n    }\n\n    List<CouponTemplateRespVO> convertList(List<CouponTemplateDO> list);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.discount;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 限时折扣活动 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface DiscountActivityConvert {\n\n    DiscountActivityConvert INSTANCE = Mappers.getMapper(DiscountActivityConvert.class);\n\n    DiscountActivityDO convert(DiscountActivityCreateReqVO bean);\n\n    DiscountActivityDO convert(DiscountActivityUpdateReqVO bean);\n\n    DiscountActivityRespVO convert(DiscountActivityDO bean);\n\n    List<DiscountActivityRespVO> convertList(List<DiscountActivityDO> list);\n\n    List<DiscountActivityBaseVO.Product> convertList2(List<DiscountProductDO> list);\n\n    PageResult<DiscountActivityRespVO> convertPage(PageResult<DiscountActivityDO> page);\n\n    default PageResult<DiscountActivityRespVO> convertPage(PageResult<DiscountActivityDO> page,\n                                                           List<DiscountProductDO> discountProductDOList) {\n        PageResult<DiscountActivityRespVO> pageResult = convertPage(page);\n        pageResult.getList().forEach(item -> item.setProducts(convertList2(discountProductDOList)));\n        return pageResult;\n    }\n\n    DiscountProductDO convert(DiscountActivityBaseVO.Product bean);\n\n    default DiscountActivityRespVO convert(DiscountActivityDO activity, List<DiscountProductDO> products) {\n        return BeanUtils.toBean(activity, DiscountActivityRespVO.class).setProducts(convertList2(products));\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/diy/DiyPageConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.*;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 装修页面 Convert\n *\n * @author owen\n */\n@Mapper\npublic interface DiyPageConvert {\n\n    DiyPageConvert INSTANCE = Mappers.getMapper(DiyPageConvert.class);\n\n    DiyPageDO convert(DiyPageCreateReqVO bean);\n\n    DiyPageDO convert(DiyPageUpdateReqVO bean);\n\n    DiyPageRespVO convert(DiyPageDO bean);\n\n    List<DiyPageRespVO> convertList(List<DiyPageDO> list);\n\n    PageResult<DiyPageRespVO> convertPage(PageResult<DiyPageDO> page);\n\n    DiyPageCreateReqVO convertCreateVo(Long templateId, String name, String remark);\n\n    DiyPagePropertyRespVO convertPropertyVo(DiyPageDO diyPage);\n\n    DiyPageDO convert(DiyPagePropertyUpdateRequestVO updateReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/diy/DiyTemplateConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.*;\nimport cn.iocoder.yudao.module.promotion.controller.app.diy.vo.AppDiyTemplatePropertyRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 装修模板 Convert\n *\n * @author owen\n */\n@Mapper\npublic interface DiyTemplateConvert {\n\n    DiyTemplateConvert INSTANCE = Mappers.getMapper(DiyTemplateConvert.class);\n\n    DiyTemplateDO convert(DiyTemplateCreateReqVO bean);\n\n    DiyTemplateDO convert(DiyTemplateUpdateReqVO bean);\n\n    DiyTemplateRespVO convert(DiyTemplateDO bean);\n\n    List<DiyTemplateRespVO> convertList(List<DiyTemplateDO> list);\n\n    PageResult<DiyTemplateRespVO> convertPage(PageResult<DiyTemplateDO> page);\n\n    DiyTemplatePropertyRespVO convertPropertyVo(DiyTemplateDO diyTemplate, List<DiyPageDO> pages);\n\n    AppDiyTemplatePropertyRespVO convertPropertyVo2(DiyTemplateDO diyTemplate, String home, String user);\n\n    DiyTemplateDO convert(DiyTemplatePropertyUpdateRequestVO updateReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/SeckillActivityConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.seckill;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.collection.MapUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityDetailRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityNowRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\nimport org.mapstruct.factory.Mappers;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\n\n/**\n * 秒杀活动 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface SeckillActivityConvert {\n\n    SeckillActivityConvert INSTANCE = Mappers.getMapper(SeckillActivityConvert.class);\n\n    SeckillActivityDO convert(SeckillActivityCreateReqVO bean);\n\n    SeckillActivityDO convert(SeckillActivityUpdateReqVO bean);\n\n    SeckillActivityRespVO convert(SeckillActivityDO bean);\n\n    List<SeckillActivityRespVO> convertList(List<SeckillActivityDO> list);\n\n    PageResult<SeckillActivityRespVO> convertPage(PageResult<SeckillActivityDO> page);\n\n    default PageResult<SeckillActivityRespVO> convertPage(PageResult<SeckillActivityDO> page,\n                                                          List<SeckillProductDO> seckillProducts,\n                                                          List<ProductSpuRespDTO> spuList) {\n        PageResult<SeckillActivityRespVO> pageResult = convertPage(page);\n        // 拼接商品\n        Map<Long, ProductSpuRespDTO> spuMap = CollectionUtils.convertMap(spuList, ProductSpuRespDTO::getId);\n        Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(seckillProducts, SeckillProductDO::getActivityId);\n        pageResult.getList().forEach(activity -> {\n            activity.setProducts(convertList2(productMap.get(activity.getId())));\n            MapUtils.findAndThen(spuMap, activity.getSpuId(),\n                    spu -> activity.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n        });\n        return pageResult;\n    }\n\n    SeckillActivityDetailRespVO convert1(SeckillActivityDO activity);\n\n    default SeckillActivityDetailRespVO convert(SeckillActivityDO activity, List<SeckillProductDO> products) {\n        return convert1(activity).setProducts(convertList2(products));\n    }\n\n    @Mappings({\n            @Mapping(target = \"id\", ignore = true),\n            @Mapping(target = \"activityId\", source = \"activity.id\"),\n            @Mapping(target = \"configIds\", source = \"activity.configIds\"),\n            @Mapping(target = \"spuId\", source = \"activity.spuId\"),\n            @Mapping(target = \"skuId\", source = \"product.skuId\"),\n            @Mapping(target = \"seckillPrice\", source = \"product.seckillPrice\"),\n            @Mapping(target = \"stock\", source = \"product.stock\"),\n            @Mapping(target = \"activityStartTime\", source = \"activity.startTime\"),\n            @Mapping(target = \"activityEndTime\", source = \"activity.endTime\")\n    })\n    SeckillProductDO convert(SeckillActivityDO activity, SeckillProductBaseVO product);\n\n    default List<SeckillProductDO> convertList(List<? extends SeckillProductBaseVO> products, SeckillActivityDO activity) {\n        return CollectionUtils.convertList(products, item -> convert(activity, item).setActivityStatus(activity.getStatus()));\n    }\n\n    default List<SeckillActivityRespVO> convertList(List<SeckillActivityDO> list,\n                                                        List<SeckillProductDO> productList,\n                                                        List<ProductSpuRespDTO> spuList) {\n        List<SeckillActivityRespVO> activityList = BeanUtils.toBean(list, SeckillActivityRespVO.class);\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(productList, SeckillProductDO::getActivityId);\n        return CollectionUtils.convertList(activityList, item -> {\n            // 设置 product 信息\n            item.setSeckillPrice(getMinValue(productMap.get(item.getId()), SeckillProductDO::getSeckillPrice));\n            // 设置 SPU 信息\n            findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())\n                    .setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n            return item;\n        });\n    }\n\n    default List<AppSeckillActivityRespVO> convertAppList(List<SeckillActivityDO> list,\n                                                              List<SeckillProductDO> productList,\n                                                              List<ProductSpuRespDTO> spuList) {\n        List<AppSeckillActivityRespVO> activityList = BeanUtils.toBean(list, AppSeckillActivityRespVO.class);\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(productList, SeckillProductDO::getActivityId);\n        return CollectionUtils.convertList(activityList, item -> {\n            // 设置 product 信息\n            item.setSeckillPrice(getMinValue(productMap.get(item.getId()), SeckillProductDO::getSeckillPrice));\n            // 设置 SPU 信息\n            findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())\n                    .setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n            return item;\n        });\n    }\n\n    List<SeckillProductRespVO> convertList2(List<SeckillProductDO> list);\n\n    List<AppSeckillActivityRespVO> convertList3(List<SeckillActivityDO> activityList);\n\n    default AppSeckillActivityNowRespVO convert(SeckillConfigDO filteredConfig, List<SeckillActivityDO> activityList,\n                                                List<SeckillProductDO> productList, List<ProductSpuRespDTO> spuList) {\n        AppSeckillActivityNowRespVO respVO = new AppSeckillActivityNowRespVO();\n        respVO.setConfig(SeckillConfigConvert.INSTANCE.convert1(filteredConfig));\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(productList, SeckillProductDO::getActivityId);\n        respVO.setActivities(CollectionUtils.convertList(convertList3(activityList), item -> {\n            // product 信息\n            item.setSeckillPrice(getMinValue(productMap.get(item.getId()), SeckillProductDO::getSeckillPrice));\n            // spu 信息\n            findAndThen(spuMap, item.getSpuId(), spu ->\n                    item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n            return item;\n        }));\n        return respVO;\n    }\n\n    PageResult<AppSeckillActivityRespVO> convertPage1(PageResult<SeckillActivityDO> pageResult);\n\n    default PageResult<AppSeckillActivityRespVO> convertPage02(PageResult<SeckillActivityDO> pageResult, List<SeckillProductDO> productList, List<ProductSpuRespDTO> spuList) {\n        PageResult<AppSeckillActivityRespVO> result = convertPage1(pageResult);\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n        Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(productList, SeckillProductDO::getActivityId);\n        List<AppSeckillActivityRespVO> list = CollectionUtils.convertList(result.getList(), item -> {\n            // product 信息\n            item.setSeckillPrice(getMinValue(productMap.get(item.getId()), SeckillProductDO::getSeckillPrice));\n            // spu 信息\n            findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));\n            return item;\n        });\n        result.setList(list);\n        return result;\n    }\n\n    AppSeckillActivityDetailRespVO convert2(SeckillActivityDO seckillActivity);\n\n    List<AppSeckillActivityDetailRespVO.Product> convertList1(List<SeckillProductDO> products);\n\n    default AppSeckillActivityDetailRespVO convert3(SeckillActivityDO activity, List<SeckillProductDO> products,\n                                                    LocalDateTime startTime, LocalDateTime endTime) {\n        return convert2(activity)\n                .setProducts(convertList1(products))\n                .setStartTime(startTime).setEndTime(endTime);\n    }\n\n    SeckillValidateJoinRespDTO convert02(SeckillActivityDO activity, SeckillProductDO product);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/SeckillConfigConvert.java",
    "content": "package cn.iocoder.yudao.module.promotion.convert.seckill;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigSimpleRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config.AppSeckillConfigRespVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 秒杀时段 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface SeckillConfigConvert {\n\n    SeckillConfigConvert INSTANCE = Mappers.getMapper(SeckillConfigConvert.class);\n\n    SeckillConfigDO convert(SeckillConfigCreateReqVO bean);\n\n    SeckillConfigDO convert(SeckillConfigUpdateReqVO bean);\n\n    SeckillConfigRespVO convert(SeckillConfigDO bean);\n\n    List<SeckillConfigRespVO> convertList(List<SeckillConfigDO> list);\n\n    List<SeckillConfigSimpleRespVO> convertList1(List<SeckillConfigDO> list);\n\n    PageResult<SeckillConfigRespVO> convertPage(PageResult<SeckillConfigDO> page);\n\n    List<AppSeckillConfigRespVO> convertList2(List<SeckillConfigDO> list);\n\n    AppSeckillConfigRespVO convert1(SeckillConfigDO filteredConfig);\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/article/ArticleCategoryDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.article;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 文章分类 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_article_category\")\n@KeySequence(\"promotion_article_category_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ArticleCategoryDO extends BaseDO {\n\n    /**\n     * 文章分类编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 文章分类名称\n     */\n    private String name;\n    /**\n     * 图标地址\n     */\n    private String picUrl;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/article/ArticleDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.article;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 文章管理 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_article\")\n@KeySequence(\"promotion_article_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ArticleDO extends BaseDO {\n\n    /**\n     * 文章管理编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 分类编号 ArticleCategoryDO#id\n     */\n    private Long categoryId;\n    /**\n     * 关联商品编号 ProductSpuDO#id\n     */\n    private Long spuId;\n    /**\n     * 文章标题\n     */\n    private String title;\n    /**\n     * 文章作者\n     */\n    private String author;\n    /**\n     * 文章封面图片地址\n     */\n    private String picUrl;\n    /**\n     * 文章简介\n     */\n    private String introduction;\n    /**\n     * 浏览次数\n     */\n    private Integer browseCount;\n    /**\n     * 排序\n     */\n    private Integer sort;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 是否热门(小程序)\n     */\n    private Boolean recommendHot;\n    /**\n     * 是否轮播图(小程序)\n     */\n    private Boolean recommendBanner;\n    /**\n     * 文章内容\n     */\n    private String content;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/banner/BannerDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.banner;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.promotion.enums.banner.BannerPositionEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * banner DO\n *\n * @author xia\n */\n@TableName(\"promotion_banner\")\n@KeySequence(\"promotion_banner_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BannerDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    private Long id;\n    /**\n     * 标题\n     */\n    private String title;\n    /**\n     * 跳转链接\n     */\n    private String url;\n    /**\n     * 图片链接\n     */\n    private String picUrl;\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    /**\n     * 状态 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n    /**\n     * 定位 {@link BannerPositionEnum}\n     */\n    private Integer position;\n\n    /**\n     * 备注\n     */\n    private String memo;\n\n    /**\n     * 点击次数\n     */\n    private Integer browseCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainActivityDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.bargain;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * 砍价活动 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_bargain_activity\")\n@KeySequence(\"promotion_bargain_activity_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BargainActivityDO extends BaseDO {\n\n    /**\n     * 砍价活动编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 砍价活动名称\n     */\n    private String name;\n\n    /**\n     * 活动开始时间\n     */\n    private LocalDateTime startTime;\n    /**\n     * 活动结束时间\n     */\n    private LocalDateTime endTime;\n\n    /**\n     * 活动状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n\n    /**\n     * 商品 SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 商品 SKU 编号\n     */\n    private Long skuId;\n    /**\n     * 砍价起始价格，单位：分\n     */\n    private Integer bargainFirstPrice;\n    /**\n     * 砍价底价，单位：分\n     */\n    private Integer bargainMinPrice;\n\n    /**\n     * 砍价库存(剩余库存砍价时扣减)\n     */\n    private Integer stock;\n    /**\n     * 砍价总库存\n     */\n    private Integer totalStock;\n\n    /**\n     * 砍价人数\n     *\n     * 需要多少人，砍价才能成功，即 {@link BargainRecordDO#getStatus()} 更新为 {@link BargainRecordDO#getStatus()} 成功状态\n     */\n    private Integer helpMaxCount;\n    /**\n     * 帮砍次数\n     *\n     * 单个活动，用户可以帮砍的次数。\n     * 例如说：帮砍次数为 1 时，A 和 B 同时将该活动链接发给 C，C 只能帮其中一个人砍价。\n     */\n    private Integer bargainCount;\n\n    /**\n     * 总限购数量\n     */\n    private Integer totalLimitCount;\n    /**\n     * 用户每次砍价的最小金额，单位：分\n     */\n    private Integer randomMinPrice;\n    /**\n     * 用户每次砍价的最大金额，单位：分\n     */\n    private Integer randomMaxPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainHelpDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.bargain;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 砍价助力 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_bargain_help\")\n@KeySequence(\"promotion_bargain_help_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BargainHelpDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 砍价活动编号\n     *\n     * 关联 {@link BargainActivityDO#getId()} 字段\n     */\n    private Long activityId;\n    /**\n     * 砍价记录编号\n     *\n     * 关联 {@link BargainRecordDO#getId()} 字段\n     */\n    private Long recordId;\n\n    /**\n     * 用户编号\n     */\n    private Long userId;\n    /**\n     * 减少价格，单位：分\n     */\n    private Integer reducePrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainRecordDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.bargain;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * 砍价记录 DO TODO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_bargain_record\")\n@KeySequence(\"promotion_bargain_record_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BargainRecordDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 砍价活动编号\n     *\n     * 关联 {@link BargainActivityDO#getId()} 字段\n     */\n    private Long activityId;\n    /**\n     * 商品 SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 商品 SKU 编号\n     */\n    private Long skuId;\n\n    /**\n     * 砍价起始价格，单位：分\n     */\n    private Integer bargainFirstPrice;\n    /**\n     * 当前砍价，单位：分\n     */\n    private Integer bargainPrice;\n\n    /**\n     * 砍价状态\n     *\n     * 砍价成功的条件是：（2 选 1）\n     *  1. 砍价到 {@link BargainActivityDO#getBargainMinPrice()} 底价\n     *  2. 助力人数到达 {@link BargainActivityDO#getHelpMaxCount()} 人\n     *\n     * 枚举 {@link BargainRecordStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 结束时间\n     */\n    private LocalDateTime endTime;\n\n    /**\n     * 订单编号\n     */\n    private Long orderId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationActivityDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * 拼团活动 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_combination_activity\")\n@KeySequence(\"promotion_combination_activity_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CombinationActivityDO extends BaseDO {\n\n    /**\n     * 活动编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 拼团名称\n     */\n    private String name;\n    /**\n     * 商品 SPU 编号\n     *\n     * 关联 ProductSpuDO 的 id\n     */\n    private Long spuId;\n    /**\n     * 总限购数量\n     */\n    private Integer totalLimitCount;\n    /**\n     * 单次限购数量\n     */\n    private Integer singleLimitCount;\n    /**\n     * 开始时间\n     */\n    private LocalDateTime startTime;\n    /**\n     * 结束时间\n     */\n    private LocalDateTime endTime;\n    /**\n     * 几人团\n     */\n    private Integer userSize;\n    /**\n     * 虚拟成团\n     */\n    private Boolean virtualGroup;\n    /**\n     * 活动状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 限制时长（小时）\n     */\n    private Integer limitDuration;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationProductDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * 拼团商品 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_combination_product\")\n@KeySequence(\"promotion_combination_product_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CombinationProductDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 拼团活动编号\n     */\n    private Long activityId;\n    /**\n     * 商品 SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 商品 SKU 编号\n     */\n    private Long skuId;\n    /**\n     * 拼团价格，单位分\n     */\n    private Integer combinationPrice;\n\n    /**\n     * 拼团商品状态\n     *\n     * 关联 {@link CombinationActivityDO#getStatus()}\n     */\n    private Integer activityStatus;\n    /**\n     * 活动开始时间点\n     *\n     * 冗余 {@link CombinationActivityDO#getStartTime()}\n     */\n    private LocalDateTime activityStartTime;\n    /**\n     * 活动结束时间点\n     *\n     * 冗余 {@link CombinationActivityDO#getEndTime()}\n     */\n    private LocalDateTime activityEndTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationRecordDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n// TODO 芋艿：把字段的顺序，和 do 顺序对齐下\n/**\n * 拼团记录 DO\n *\n * 1. 用户参与拼团时，会创建一条记录\n * 2. 团长的拼团记录，和参团人的拼团记录，通过 {@link #headId} 关联\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_combination_record\")\n@KeySequence(\"promotion_combination_record_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CombinationRecordDO extends BaseDO {\n\n    /**\n     * 团长编号 - 团长\n     */\n    public static final Long HEAD_ID_GROUP = 0L;\n\n    /**\n     * 编号，主键自增\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 拼团活动编号\n     *\n     * 关联 {@link CombinationActivityDO#getId()}\n     */\n    private Long activityId;\n    /**\n     * 拼团商品单价\n     *\n     * 冗余 {@link CombinationProductDO#getCombinationPrice()}\n     */\n    private Integer combinationPrice;\n    /**\n     * SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 商品名字\n     */\n    private String spuName;\n    /**\n     * 商品图片\n     */\n    private String picUrl;\n    /**\n     * SKU 编号\n     */\n    private Long skuId;\n    /**\n     * 购买的商品数量\n     */\n    private Integer count;\n\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 用户昵称\n     */\n    private String nickname;\n    /**\n     * 用户头像\n     */\n    private String avatar;\n\n    /**\n     * 团长编号\n     *\n     * 关联 {@link CombinationRecordDO#getId()}\n     *\n     * 如果是团长，则它的值是 {@link #HEAD_ID_GROUP}\n     */\n    private Long headId;\n    /**\n     * 开团状态\n     *\n     * 关联 {@link CombinationRecordStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 订单编号\n     */\n    private Long orderId;\n    /**\n     * 开团需要人数\n     *\n     * 关联 {@link CombinationActivityDO#getUserSize()}\n     */\n    private Integer userSize;\n    /**\n     * 已加入拼团人数\n     */\n    private Integer userCount;\n    /**\n     * 是否虚拟成团\n     *\n     * 默认为 false。\n     * 拼团过期都还没有成功，如果 {@link CombinationActivityDO#getVirtualGroup()} 为 true，则执行虚拟成团的逻辑，才会更新该字段为 true\n     */\n    private Boolean virtualGroup;\n\n    /**\n     * 过期时间\n     *\n     * 基于 {@link CombinationRecordDO#getStartTime()} + {@link CombinationActivityDO#getLimitDuration()} 计算\n     */\n    private LocalDateTime expireTime;\n    /**\n     * 开始时间 (订单付款后开始的时间)\n     */\n    private LocalDateTime startTime;\n    /**\n     * 结束时间（成团时间/失败时间）\n     */\n    private LocalDateTime endTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.coupon;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 优惠劵 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"promotion_coupon\", autoResultMap = true)\n@KeySequence(\"promotion_coupon_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class CouponDO extends BaseDO {\n\n    // ========== 基本信息 BEGIN ==========\n    /**\n     * 优惠劵编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 优惠劵模板编号\n     *\n     * 关联 {@link CouponTemplateDO#getId()}\n     */\n    private Long templateId;\n    /**\n     * 优惠劵名\n     *\n     * 冗余 {@link CouponTemplateDO#getName()}\n     */\n    private String name;\n    /**\n     * 优惠码状态\n     *\n     * 枚举 {@link CouponStatusEnum}\n     */\n    private Integer status;\n\n    // TODO 芋艿：发放 adminid？\n\n    // ========== 基本信息 END ==========\n\n    // ========== 领取情况 BEGIN ==========\n    /**\n     * 用户编号\n     *\n     * 关联 MemberUserDO 的 id 字段\n     */\n    private Long userId;\n    /**\n     * 领取类型\n     *\n     * 枚举 {@link CouponTakeTypeEnum}\n     */\n    private Integer takeType;\n    // ========== 领取情况 END ==========\n\n    // ========== 使用规则 BEGIN ==========\n    /**\n     * 是否设置满多少金额可用，单位：分\n     *\n     * 冗余 {@link CouponTemplateDO#getUsePrice()}\n     */\n    private Integer usePrice;\n    /**\n     * 生效开始时间\n     */\n    private LocalDateTime validStartTime;\n    /**\n     * 生效结束时间\n     */\n    private LocalDateTime validEndTime;\n    /**\n     * 商品范围\n     *\n     * 枚举 {@link PromotionProductScopeEnum}\n     */\n    private Integer productScope;\n    /**\n     * 商品范围编号的数组\n     *\n     * 冗余 {@link CouponTemplateDO#getProductScopeValues()}\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> productScopeValues;\n    // ========== 使用规则 END ==========\n\n    // ========== 使用效果 BEGIN ==========\n    /**\n     * 折扣类型\n     *\n     * 冗余 {@link CouponTemplateDO#getDiscountType()}\n     */\n    private Integer discountType;\n    /**\n     * 折扣百分比\n     *\n     * 冗余 {@link CouponTemplateDO#getDiscountPercent()}\n     */\n    private Integer discountPercent;\n    /**\n     * 优惠金额，单位：分\n     *\n     * 冗余 {@link CouponTemplateDO#getDiscountPrice()}\n     */\n    private Integer discountPrice;\n    /**\n     * 折扣上限，仅在 {@link #discountType} 等于 {@link PromotionDiscountTypeEnum#PERCENT} 时生效\n     *\n     * 冗余 {@link CouponTemplateDO#getDiscountLimitPrice()}\n     */\n    private Integer discountLimitPrice;\n    // ========== 使用效果 END ==========\n\n    // ========== 使用情况 BEGIN ==========\n    /**\n     * 使用订单号\n     */\n    private Long useOrderId;\n    /**\n     * 使用时间\n     */\n    private LocalDateTime useTime;\n\n    // ========== 使用情况 END ==========\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.coupon;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 优惠劵模板 DO\n *\n * 当用户领取时，会生成 {@link CouponDO} 优惠劵\n *\n * @author 芋道源码\n */\n@TableName(value = \"promotion_coupon_template\", autoResultMap = true)\n@KeySequence(\"promotion_coupon_template_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class CouponTemplateDO extends BaseDO {\n\n    /**\n     * 领取数量 - 不限制\n     */\n    public static final Integer TAKE_LIMIT_COUNT_MAX = -1;\n    /**\n     * 发放数量 - 不限制\n     */\n    public static final Integer TOTAL_COUNT_MAX = -1;\n\n    // ========== 基本信息 BEGIN ==========\n    /**\n     * 模板编号，自增唯一\n     */\n    @TableId\n    private Long id;\n    /**\n     * 优惠劵名\n     */\n    private String name;\n    /**\n     * 优惠券说明\n     */\n    private String description;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    // TODO 芋艿：要不要改成 3 个状态？？\n    private Integer status;\n\n    // ========== 基本信息 END ==========\n\n    // ========== 领取规则 BEGIN ==========\n    /**\n     * 发放数量\n     *\n     * -1 - 则表示不限制发放数量\n     */\n    private Integer totalCount;\n    /**\n     * 每人限领个数\n     *\n     * -1 - 则表示不限制\n     */\n    private Integer takeLimitCount;\n    /**\n     * 领取方式\n     *\n     * 枚举 {@link CouponTakeTypeEnum}\n     */\n    private Integer takeType;\n    // ========== 领取规则 END ==========\n\n    // ========== 使用规则 BEGIN ==========\n    /**\n     * 是否设置满多少金额可用，单位：分\n     *\n     * 0 - 不限制\n     * 大于 0 - 多少金额可用\n     */\n    private Integer usePrice;\n    /**\n     * 商品范围\n     *\n     * 枚举 {@link PromotionProductScopeEnum}\n     */\n    private Integer productScope;\n    /**\n     * 商品范围编号的数组\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> productScopeValues;\n    /**\n     * 生效日期类型\n     *\n     * 枚举 {@link CouponTemplateValidityTypeEnum}\n     */\n    private Integer validityType;\n    /**\n     * 固定日期 - 生效开始时间\n     *\n     * 当 {@link #validityType} 为 {@link CouponTemplateValidityTypeEnum#DATE}\n     */\n    private LocalDateTime validStartTime;\n    /**\n     * 固定日期 - 生效结束时间\n     *\n     * 当 {@link #validityType} 为 {@link CouponTemplateValidityTypeEnum#DATE}\n     */\n    private LocalDateTime validEndTime;\n    /**\n     * 领取日期 - 开始天数\n     *\n     * 当 {@link #validityType} 为 {@link CouponTemplateValidityTypeEnum#TERM}\n     */\n    private Integer fixedStartTerm;\n    /**\n     * 领取日期 - 结束天数\n     *\n     * 当 {@link #validityType} 为 {@link CouponTemplateValidityTypeEnum#TERM}\n     */\n    private Integer fixedEndTerm;\n    // ========== 使用规则 END ==========\n\n    // ========== 使用效果 BEGIN ==========\n    /**\n     * 折扣类型\n     *\n     * 枚举 {@link PromotionDiscountTypeEnum}\n     */\n    private Integer discountType;\n    /**\n     * 折扣百分比\n     *\n     * 例如，80% 为 80\n     */\n    private Integer discountPercent;\n    /**\n     * 优惠金额，单位：分\n     *\n     * 当 {@link #discountType} 为 {@link PromotionDiscountTypeEnum#PRICE} 生效\n     */\n    private Integer discountPrice;\n    /**\n     * 折扣上限，仅在 {@link #discountType} 等于 {@link PromotionDiscountTypeEnum#PERCENT} 时生效\n     *\n     * 例如，折扣上限为 20 元，当使用 8 折优惠券，订单金额为 1000 元时，最高只可折扣 20 元，而非 80  元。\n     */\n    private Integer discountLimitPrice;\n    // ========== 使用效果 END ==========\n\n    // ========== 统计信息 BEGIN ==========\n    /**\n     * 领取优惠券的数量\n     */\n    private Integer takeCount;\n    /**\n     * 使用优惠券的次数\n     */\n    private Integer useCount;\n\n    // ========== 统计信息 END ==========\n\n    // TODO 芋艿：领取开始时间、领取结束时间\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountActivityDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.discount;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.time.LocalDateTime;\n\n/**\n * 限时折扣活动 DO\n *\n * 一个活动下，可以有 {@link DiscountProductDO} 商品；\n * 一个商品，在指定时间段内，只能属于一个活动；\n *\n * @author 芋道源码\n */\n@TableName(value = \"promotion_discount_activity\", autoResultMap = true)\n@KeySequence(\"promotion_discount_activity_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class DiscountActivityDO extends BaseDO {\n\n    /**\n     * 活动编号，主键自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * 活动标题\n     */\n    private String name;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     *\n     * 活动被关闭后，不允许再次开启。\n     */\n    private Integer status;\n    /**\n     * 开始时间\n     */\n    private LocalDateTime startTime;\n    /**\n     * 结束时间\n     */\n    private LocalDateTime endTime;\n    /**\n     * 备注\n     */\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountProductDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.discount;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.time.LocalDateTime;\n\n/**\n * 限时折扣商品 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"promotion_discount_product\", autoResultMap = true)\n@KeySequence(\"promotion_discount_product_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class DiscountProductDO extends BaseDO {\n\n    /**\n     * 编号，主键自增\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 限时折扣活动的编号\n     *\n     * 关联 {@link DiscountActivityDO#getId()}\n     */\n    private Long activityId;\n\n    /**\n     * 商品 SPU 编号\n     *\n     * 关联 ProductSpuDO 的 id 编号\n     */\n    private Long spuId;\n    /**\n     * 商品 SKU 编号\n     *\n     * 关联 ProductSkuDO 的 id 编号\n     */\n    private Long skuId;\n\n    /**\n     * 折扣类型\n     *\n     * 枚举 {@link PromotionDiscountTypeEnum}\n     */\n    private Integer discountType;\n    /**\n     * 折扣百分比\n     *\n     * 例如，80% 为 80\n     */\n    private Integer discountPercent;\n    /**\n     * 优惠金额，单位：分\n     *\n     * 当 {@link #discountType} 为 {@link PromotionDiscountTypeEnum#PRICE} 生效\n     */\n    private Integer discountPrice;\n\n    /**\n     * 活动标题\n     *\n     * 冗余 {@link DiscountActivityDO#getName()}\n     */\n    private String activityName;\n    /**\n     * 活动状态\n     *\n     * 冗余 {@link DiscountActivityDO#getStatus()}\n     */\n    private Integer activityStatus;\n    /**\n     * 活动开始时间点\n     *\n     * 冗余 {@link DiscountActivityDO#getStartTime()}\n     */\n    private LocalDateTime activityStartTime;\n    /**\n     * 活动结束时间点\n     *\n     * 冗余 {@link DiscountActivityDO#getEndTime()}\n     */\n    private LocalDateTime activityEndTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyPageDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.diy;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.util.List;\n\n/**\n * 装修页面 DO\n *\n * @author owen\n */\n@TableName(value = \"promotion_diy_page\", autoResultMap = true)\n@KeySequence(\"promotion_diy_page_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DiyPageDO extends BaseDO {\n\n    /**\n     * 装修页面编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 装修模板编号\n     *\n     * 关联 {@link DiyTemplateDO#getId()}\n     */\n    private Long templateId;\n    /**\n     * 页面名称\n     */\n    private String name;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 预览图，多个逗号分隔\n     */\n    @TableField(typeHandler = StringListTypeHandler.class)\n    private List<String> previewPicUrls;\n    /**\n     * 页面属性，JSON 格式\n     */\n    private String property;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyTemplateDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.diy;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 装修模板 DO\n *\n * 1. 新建一个模版，下面可以包含多个 {@link DiyPageDO} 页面，例如说首页、我的\n * 2. 如果需要使用某个模版，则将 {@link #used} 设置为 true，表示已使用，有且仅有一个\n *\n * @author owen\n */\n@TableName(value = \"promotion_diy_template\", autoResultMap = true)\n@KeySequence(\"promotion_diy_template_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DiyTemplateDO extends BaseDO {\n\n    /**\n     * 装修模板编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 模板名称\n     */\n    private String name;\n    /**\n     * 是否使用\n     */\n    private Boolean used;\n    /**\n     * 使用时间\n     */\n    private LocalDateTime usedTime;\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 预览图\n     */\n    @TableField(typeHandler = StringListTypeHandler.class)\n    private List<String> previewPicUrls;\n    /**\n     * uni-app 底部导航属性，JSON 格式\n     */\n    private String property;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/kefu/KeFuConversationDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.kefu;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.promotion.enums.kefu.KeFuMessageContentTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * 客服会话 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_kefu_conversation\")\n@KeySequence(\"promotion_kefu_conversation_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class KeFuConversationDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 会话所属用户\n     *\n     * 关联 {@link MemberUserRespDTO#getId()}\n     */\n    private Long userId;\n\n    /**\n     * 最后聊天时间\n     */\n    private LocalDateTime lastMessageTime;\n    /**\n     * 最后聊天内容\n     */\n    private String lastMessageContent;\n    /**\n     * 最后发送的消息类型\n     *\n     * 枚举 {@link KeFuMessageContentTypeEnum}\n     */\n    private Integer lastMessageContentType;\n\n    //======================= 会话操作相关 =======================\n\n    /**\n     * 管理端置顶\n     */\n    private Boolean adminPinned;\n    /**\n     * 用户是否可见\n     *\n     * false - 可见，默认值\n     * true - 不可见，用户删除时设置为 true\n     */\n    private Boolean userDeleted;\n    /**\n     * 管理员是否可见\n     *\n     * false - 可见，默认值\n     * true - 不可见，管理员删除时设置为 true\n     */\n    private Boolean adminDeleted;\n\n    /**\n     * 管理员未读消息数\n     *\n     * 用户发送消息时增加，管理员查看后扣减\n     */\n    private Integer adminUnreadMessageCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/kefu/KeFuMessageDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.kefu;\n\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.module.promotion.enums.kefu.KeFuMessageContentTypeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 客服消息 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_kefu_message\")\n@KeySequence(\"promotion_kefu_message_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class KeFuMessageDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 会话编号\n     *\n     * 关联 {@link KeFuConversationDO#getId()}\n     */\n    private Long conversationId;\n\n    /**\n     * 发送人编号\n     *\n     * 存储的是用户编号\n     */\n    private Long senderId;\n    /**\n     * 发送人类型\n     *\n     * 枚举，{@link UserTypeEnum}\n     */\n    private Integer senderType;\n    /**\n     * 接收人编号\n     *\n     * 存储的是用户编号\n     */\n    private Long receiverId;\n    /**\n     * 接收人类型\n     *\n     * 枚举，{@link UserTypeEnum}\n     */\n    private Integer receiverType;\n\n    /**\n     * 消息类型\n     *\n     * 枚举 {@link KeFuMessageContentTypeEnum}\n     */\n    private Integer contentType;\n    /**\n     * 消息\n     */\n    private String content;\n\n    //======================= 消息相关状态 =======================\n\n    /**\n     * 是/否已读\n     */\n    private Boolean readStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/point/PointActivityDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.point;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n/**\n * 积分商城活动 DO\n *\n * @author HUIHUI\n */\n@TableName(value = \"promotion_point_activity\", autoResultMap = true)\n@KeySequence(\"promotion_point_activity_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class PointActivityDO extends BaseDO {\n\n    /**\n     * 积分商城活动编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 积分商城活动商品\n     */\n    private Long spuId;\n    /**\n     * 活动状态\n     *\n     * 枚举 {@link CommonStatusEnum 对应的类}\n     */\n    private Integer status;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    /**\n     * 积分商城活动库存(剩余库存积分兑换时扣减)\n     */\n    private Integer stock;\n    /**\n     * 积分商城活动总库存\n     */\n    private Integer totalStock;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/point/PointProductDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.point;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\n/**\n * 积分商城商品 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_point_product\")\n@KeySequence(\"promotion_point_product_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class PointProductDO extends BaseDO {\n\n    /**\n     * 积分商城商品编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 积分商城活动 id\n     *\n     * 关联 {@link PointActivityDO#getId()}\n     */\n    private Long activityId;\n    /**\n     * 商品 SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 商品 SKU 编号\n     */\n    private Long skuId;\n    /**\n     * 可兑换次数\n     */\n    private Integer count;\n    /**\n     * 所需兑换积分\n     */\n    private Integer point;\n    /**\n     * 所需兑换金额，单位：分\n     */\n    private Integer price;\n    /**\n     * 积分商城商品库存\n     */\n    private Integer stock;\n    /**\n     * 积分商城商品状态\n     *\n     * 枚举 {@link CommonStatusEnum 对应的类}\n     */\n    private Integer activityStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/reward/RewardActivityDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.reward;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 满减送活动 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"promotion_reward_activity\", autoResultMap = true)\n@KeySequence(\"promotion_reward_activity_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class RewardActivityDO extends BaseDO {\n\n    /**\n     * 活动编号，主键自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * 活动标题\n     */\n    private String name;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum}\n     */\n    private Integer status;\n    /**\n     * 开始时间\n     */\n    private LocalDateTime startTime;\n    /**\n     * 结束时间\n     */\n    private LocalDateTime endTime;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 条件类型\n     *\n     * 枚举 {@link PromotionConditionTypeEnum}\n     */\n    private Integer conditionType;\n    /**\n     * 商品范围\n     *\n     * 枚举 {@link PromotionProductScopeEnum}\n     */\n    private Integer productScope;\n    /**\n     * 商品 SPU 编号的数组\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> productScopeValues;\n    /**\n     * 优惠规则的数组\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<Rule> rules;\n\n    /**\n     * 优惠规则\n     */\n    @Data\n    public static class Rule implements Serializable {\n\n        /**\n         * 优惠门槛\n         *\n         * 1. 满 N 元，单位：分\n         * 2. 满 N 件\n         */\n        private Integer limit;\n        /**\n         * 优惠价格，单位：分\n         */\n        private Integer discountPrice;\n        /**\n         * 是否包邮\n         */\n        private Boolean freeDelivery;\n        /**\n         * 赠送的积分\n         */\n        private Integer point;\n        /**\n         * 赠送的优惠劵\n         *\n         * key: 优惠劵模版编号\n         * value：对应的优惠券数量\n         *\n         * 目的：用于订单支付后赠送优惠券\n         */\n        private Map<Long, Integer> giveCouponTemplateCounts;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/SeckillActivityDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 秒杀活动 DO\n *\n * @author halfninety\n */\n@TableName(value = \"promotion_seckill_activity\", autoResultMap = true)\n@KeySequence(\"promotion_seckill_activity_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class SeckillActivityDO extends BaseDO {\n\n    /**\n     * 秒杀活动编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 秒杀活动商品\n     */\n    private Long spuId;\n    /**\n     * 秒杀活动名称\n     */\n    private String name;\n    /**\n     * 活动状态\n     *\n     * 枚举 {@link CommonStatusEnum 对应的类}\n     */\n    private Integer status;\n    /**\n     * 备注\n     */\n    private String remark;\n    /**\n     * 活动开始时间\n     */\n    private LocalDateTime startTime;\n    /**\n     * 活动结束时间\n     */\n    private LocalDateTime endTime;\n    /**\n     * 排序\n     */\n    private Integer sort;\n    /**\n     * 秒杀时段 id\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> configIds;\n\n    /**\n     * 总限购数量\n     */\n    private Integer totalLimitCount;\n    /**\n     * 单次限够数量\n     */\n    private Integer singleLimitCount;\n\n    /**\n     * 秒杀库存(剩余库存秒杀时扣减)\n     */\n    private Integer stock;\n    /**\n     * 秒杀总库存\n     */\n    private Integer totalStock;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/SeckillConfigDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.*;\n\nimport java.util.List;\n\n/**\n * 秒杀时段 DO\n *\n * @author 芋道源码\n */\n@TableName(value = \"promotion_seckill_config\", autoResultMap = true)\n@KeySequence(\"promotion_seckill_config_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SeckillConfigDO extends BaseDO {\n\n    /**\n     * 编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 秒杀时段名称\n     */\n    private String name;\n    /**\n     * 开始时间点\n     */\n    private String startTime;\n    /**\n     * 结束时间点\n     */\n    private String endTime;\n    /**\n     * 秒杀轮播图\n     */\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<String> sliderPicUrls;\n    /**\n     * 状态\n     *\n     * 枚举 {@link CommonStatusEnum 对应的类}\n     */\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/SeckillProductDO.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 秒杀参与商品 DO\n *\n * @author HUIHUI\n */\n@TableName(\"promotion_seckill_product\")\n@KeySequence(\"promotion_seckill_product_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SeckillProductDO extends BaseDO {\n\n    /**\n     * 秒杀参与商品编号\n     */\n    @TableId\n    private Long id;\n    /**\n     * 秒杀活动 id\n     *\n     * 关联 {@link SeckillActivityDO#getId()}\n     */\n    private Long activityId;\n    /**\n     * 秒杀时段 id\n     *\n     * 关联 {@link SeckillConfigDO#getId()}\n     */\n    @TableField(typeHandler = LongListTypeHandler.class)\n    private List<Long> configIds;\n    /**\n     * 商品 SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 商品 SKU 编号\n     */\n    private Long skuId;\n    /**\n     * 秒杀金额，单位：分\n     */\n    private Integer seckillPrice;\n    /**\n     * 秒杀库存\n     */\n    private Integer stock;\n\n    /**\n     * 秒杀商品状态\n     *\n     * 枚举 {@link CommonStatusEnum 对应的类}\n     */\n    private Integer activityStatus;\n    /**\n     * 活动开始时间点\n     */\n    private LocalDateTime activityStartTime;\n    /**\n     * 活动结束时间点\n     */\n    private LocalDateTime activityEndTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/article/ArticleCategoryMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 文章分类 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface ArticleCategoryMapper extends BaseMapperX<ArticleCategoryDO> {\n\n    default PageResult<ArticleCategoryDO> selectPage(ArticleCategoryPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ArticleCategoryDO>()\n                .likeIfPresent(ArticleCategoryDO::getName, reqVO.getName())\n                .eqIfPresent(ArticleCategoryDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(ArticleCategoryDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(ArticleCategoryDO::getSort));\n    }\n\n    default List<ArticleCategoryDO> selectListByStatus(Integer status) {\n        return selectList(ArticleCategoryDO::getStatus, status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/article/ArticleMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticlePageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 文章管理 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface ArticleMapper extends BaseMapperX<ArticleDO> {\n\n    default PageResult<ArticleDO> selectPage(ArticlePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<ArticleDO>()\n                .eqIfPresent(ArticleDO::getCategoryId, reqVO.getCategoryId())\n                .eqIfPresent(ArticleDO::getTitle, reqVO.getTitle())\n                .eqIfPresent(ArticleDO::getAuthor, reqVO.getAuthor())\n                .eqIfPresent(ArticleDO::getStatus, reqVO.getStatus())\n                .eqIfPresent(ArticleDO::getSpuId, reqVO.getSpuId())\n                .eqIfPresent(ArticleDO::getRecommendHot, reqVO.getRecommendHot())\n                .eqIfPresent(ArticleDO::getRecommendBanner, reqVO.getRecommendBanner())\n                .betweenIfPresent(ArticleDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(ArticleDO::getId));\n    }\n\n    default List<ArticleDO> selectList(Boolean recommendHot, Boolean recommendBanner) {\n        return selectList(new LambdaQueryWrapperX<ArticleDO>()\n                .eqIfPresent(ArticleDO::getRecommendHot, recommendHot)\n                .eqIfPresent(ArticleDO::getRecommendBanner, recommendBanner));\n    }\n\n    default List<ArticleDO> selectListByTitle(String title) {\n        return selectList(ArticleDO::getTitle, title);\n    }\n\n    default PageResult<ArticleDO> selectPage(AppArticlePageReqVO pageReqVO) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<ArticleDO>()\n                .eqIfPresent(ArticleDO::getCategoryId, pageReqVO.getCategoryId()));\n    }\n\n    default void updateBrowseCount(Long id) {\n        update(null, new LambdaUpdateWrapper<ArticleDO>()\n                .eq(ArticleDO::getId, id)\n                .setSql(\"browse_count = browse_count + 1\"));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/banner/BannerMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.banner;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * Banner Mapper\n *\n * @author xia\n */\n@Mapper\npublic interface BannerMapper extends BaseMapperX<BannerDO> {\n\n    default PageResult<BannerDO> selectPage(BannerPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BannerDO>()\n                .likeIfPresent(BannerDO::getTitle, reqVO.getTitle())\n                .eqIfPresent(BannerDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(BannerDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(BannerDO::getSort));\n    }\n\n    default void updateBrowseCount(Long id) {\n        update(null, new LambdaUpdateWrapper<BannerDO>()\n                .eq(BannerDO::getId, id)\n                .setSql(\"browse_count = browse_count + 1\"));\n    }\n\n    default List<BannerDO> selectBannerListByPosition(Integer position) {\n        return selectList(new LambdaQueryWrapperX<BannerDO>().eq(BannerDO::getPosition, position));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 砍价活动 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {\n\n    default PageResult<BargainActivityDO> selectPage(BargainActivityPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BargainActivityDO>()\n                .likeIfPresent(BargainActivityDO::getName, reqVO.getName())\n                .eqIfPresent(BargainActivityDO::getStatus, reqVO.getStatus())\n                .orderByDesc(BargainActivityDO::getId));\n    }\n\n    default List<BargainActivityDO> selectListByStatus(Integer status) {\n        return selectList(BargainActivityDO::getStatus, status);\n    }\n\n    /**\n     * 更新活动库存\n     *\n     * @param id    活动编号\n     * @param count 扣减的库存数量\n     * @return 影响的行数\n     */\n    default int updateStock(Long id, int count) {\n        // 情况一：增加库存\n        if (count > 0) {\n            return update(null, new LambdaUpdateWrapper<BargainActivityDO>()\n                    .eq(BargainActivityDO::getId, id)\n                    .setSql(\"stock = stock + \" + count));\n        }\n        // 情况二：扣减库存\n        count = -count; // 取正\n        return update(null, new LambdaUpdateWrapper<BargainActivityDO>()\n                .eq(BargainActivityDO::getId, id)\n                .ge(BargainActivityDO::getStock, count)\n                .setSql(\"stock = stock - \" + count));\n    }\n\n    /**\n     * 查询处在 now 日期时间且是 status 状态的活动分页\n     *\n     * @param pageReqVO 分页参数\n     * @param status    状态\n     * @param now       当前日期时间\n     * @return 活动分页\n     */\n    default PageResult<BargainActivityDO> selectPage(PageParam pageReqVO, Integer status, LocalDateTime now) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<BargainActivityDO>()\n                .eq(BargainActivityDO::getStatus, status)\n                .le(BargainActivityDO::getStartTime, now)\n                .ge(BargainActivityDO::getEndTime, now));\n    }\n\n    /**\n     * 查询处在 now 日期时间且是 status 状态的活动分页\n     *\n     * @param status 状态\n     * @param now    当前日期时间\n     * @return 活动分页\n     */\n    default List<BargainActivityDO> selectList(Integer count, Integer status, LocalDateTime now) {\n        return selectList(new LambdaQueryWrapperX<BargainActivityDO>()\n                .eq(BargainActivityDO::getStatus, status)\n                .le(BargainActivityDO::getStartTime, now)\n                .ge(BargainActivityDO::getEndTime, now)\n                .last(\"LIMIT \" + count));\n    }\n\n    default BargainActivityDO selectBySpuIdAndStatusAndNow(Long spuId, Integer status) {\n        LocalDateTime now = LocalDateTime.now();\n        return selectOne(new LambdaQueryWrapperX<BargainActivityDO>()\n                .eq(BargainActivityDO::getSpuId, spuId)\n                .eq(BargainActivityDO::getStatus, status)\n                .lt(BargainActivityDO::getStartTime, now)\n                .gt(BargainActivityDO::getEndTime, now)); // 开始时间 < now < 结束时间，也就是说获取指定时间段的活动\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainHelpMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.bargain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.help.BargainHelpPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n@Mapper\npublic interface BargainHelpMapper extends BaseMapperX<BargainHelpDO> {\n\n    default Long selectCountByUserIdAndActivityId(Long userId, Long activityId) {\n        return selectCount(new LambdaQueryWrapper<>(BargainHelpDO.class)\n                .eq(BargainHelpDO::getUserId, userId)\n                .eq(BargainHelpDO::getActivityId, activityId));\n    }\n\n    default Long selectUserCountMapByRecordId(Long recordId) {\n        return selectCount(BargainHelpDO::getRecordId, recordId);\n    }\n\n    default BargainHelpDO selectByUserIdAndRecordId(Long userId, Long recordId) {\n        return selectOne(new LambdaQueryWrapper<>(BargainHelpDO.class)\n                .eq(BargainHelpDO::getUserId, userId)\n                .eq(BargainHelpDO::getRecordId, recordId));\n    }\n\n    default Map<Long, Integer> selectUserCountMapByActivityId(Collection<Long> activityIds) {\n        // SQL count 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<BargainHelpDO>()\n                .select(\"COUNT(DISTINCT(user_id)) AS userCount, activity_id AS activityId\")\n                .in(\"activity_id\", activityIds)\n                .groupBy(\"activity_id\"));\n        if (CollUtil.isEmpty(result)) {\n            return Collections.emptyMap();\n        }\n        // 转换数据\n        return CollectionUtils.convertMap(result,\n                record -> MapUtil.getLong(record, \"activityId\"),\n                record -> MapUtil.getInt(record, \"userCount\" ));\n    }\n\n    default Map<Long, Integer> selectUserCountMapByRecordId(Collection<Long> recordIds) {\n        // SQL count 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<BargainHelpDO>()\n                .select(\"COUNT(1) AS userCount, record_id AS recordId\")\n                .in(\"record_id\", recordIds)\n                .groupBy(\"record_id\"));\n        if (CollUtil.isEmpty(result)) {\n            return Collections.emptyMap();\n        }\n        // 转换数据\n        return CollectionUtils.convertMap(result,\n                record -> MapUtil.getLong(record, \"recordId\"),\n                record -> MapUtil.getInt(record, \"userCount\" ));\n    }\n\n    default PageResult<BargainHelpDO> selectPage(BargainHelpPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BargainHelpDO>()\n                .eqIfPresent(BargainHelpDO::getRecordId, reqVO.getRecordId())\n                .orderByDesc(BargainHelpDO::getId));\n    }\n\n    default List<BargainHelpDO> selectListByRecordId(Long recordId) {\n        return selectList(new LambdaQueryWrapperX<BargainHelpDO>()\n                .eq(BargainHelpDO::getRecordId, recordId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainRecordMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.bargain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.recrod.BargainRecordPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 砍价记录 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface BargainRecordMapper extends BaseMapperX<BargainRecordDO> {\n\n    default BargainRecordDO selectByIdAndUserId(Long id, Long userId) {\n        return selectOne(BargainRecordDO::getId, id,\n                BargainRecordDO::getUserId, userId);\n    }\n\n    default List<BargainRecordDO> selectListByUserIdAndActivityIdAndStatus(\n            Long userId, Long activityId, Integer status) {\n        return selectList(new LambdaQueryWrapper<>(BargainRecordDO.class)\n                .eq(BargainRecordDO::getUserId, userId)\n                .eq(BargainRecordDO::getActivityId, activityId)\n                .eq(BargainRecordDO::getStatus, status));\n    }\n\n    default BargainRecordDO selectLastByUserIdAndActivityId(Long userId, Long activityId) {\n        return selectOne(new LambdaQueryWrapper<>(BargainRecordDO.class)\n                .eq(BargainRecordDO::getUserId, userId)\n                .eq(BargainRecordDO::getActivityId, activityId)\n                .orderByDesc(BargainRecordDO::getId)\n                .last(\"LIMIT 1\"));\n    }\n\n    default Long selectCountByUserIdAndActivityIdAndStatus(\n            Long userId, Long activityId, Integer status) {\n        return selectCount(new LambdaQueryWrapper<>(BargainRecordDO.class)\n                .eq(BargainRecordDO::getUserId, userId)\n                .eq(BargainRecordDO::getActivityId, activityId)\n                .eq(BargainRecordDO::getStatus, status));\n    }\n\n    default int updateByIdAndBargainPrice(Long id, Integer whereBargainPrice, BargainRecordDO updateObj) {\n        return update(updateObj, new LambdaQueryWrapper<>(BargainRecordDO.class)\n                .eq(BargainRecordDO::getId, id)\n                .eq(BargainRecordDO::getBargainPrice, whereBargainPrice));\n    }\n\n    default Map<Long, Integer> selectUserCountByActivityIdsAndStatus(Collection<Long> activityIds, Integer status) {\n        // SQL count 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<BargainRecordDO>()\n                .select(\"COUNT(DISTINCT(user_id)) AS userCount, activity_id AS activityId\")\n                .in(\"activity_id\", activityIds)\n                .eq(status != null, \"status\", status)\n                .groupBy(\"activity_id\"));\n        if (CollUtil.isEmpty(result)) {\n            return Collections.emptyMap();\n        }\n        // 转换数据\n        return CollectionUtils.convertMap(result,\n                record -> MapUtil.getLong(record, \"activityId\"),\n                record -> MapUtil.getInt(record, \"userCount\" ));\n    }\n\n    @Select(\"SELECT COUNT(DISTINCT(user_id)) FROM promotion_bargain_record \" +\n            \"WHERE status = #{status}\")\n    Integer selectUserCountByStatus(@Param(\"status\") Integer status);\n\n    @Select(\"SELECT COUNT(DISTINCT(user_id)) FROM promotion_bargain_record \" +\n            \"WHERE activity_id = #{activityId} \" +\n            \"AND status = #{status}\")\n    Integer selectUserCountByActivityIdAndStatus(@Param(\"activityId\") Long activityId,\n                                                 @Param(\"status\") Integer status);\n\n    default PageResult<BargainRecordDO> selectPage(BargainRecordPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<BargainRecordDO>()\n                .eqIfPresent(BargainRecordDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(BargainRecordDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(BargainRecordDO::getId));\n    }\n\n    default PageResult<BargainRecordDO> selectBargainRecordPage(Long userId, PageParam pageParam) {\n        return selectPage(pageParam, new LambdaQueryWrapperX<BargainRecordDO>()\n                .eq(BargainRecordDO::getUserId, userId)\n                .orderByDesc(BargainRecordDO::getId));\n    }\n\n    default List<BargainRecordDO> selectListByStatusAndCount(Integer status, Integer count) {\n        return selectList(new LambdaQueryWrapper<>(BargainRecordDO.class)\n                .eq(BargainRecordDO::getStatus, status)\n                .last(\"LIMIT \" + count));\n    }\n\n    /**\n     * 更新砍价的订单编号，前提是 orderId 原本是空的\n     *\n     * @param id 砍价记录编号\n     * @param orderId 订单编号\n     * @return 更新数量\n     */\n    default int updateOrderIdById(Long id, Long orderId) {\n        return update(new BargainRecordDO().setOrderId(orderId).setEndTime(LocalDateTime.now()),\n                new LambdaQueryWrapper<>(BargainRecordDO.class)\n                        .eq(BargainRecordDO::getId, id)\n                        .isNull(BargainRecordDO::getOrderId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationActivityMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.combination;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 拼团活动 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface CombinationActivityMapper extends BaseMapperX<CombinationActivityDO> {\n\n    default PageResult<CombinationActivityDO> selectPage(CombinationActivityPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<CombinationActivityDO>()\n                .likeIfPresent(CombinationActivityDO::getName, reqVO.getName())\n                .eqIfPresent(CombinationActivityDO::getStatus, reqVO.getStatus())\n                .orderByDesc(CombinationActivityDO::getId));\n    }\n\n    default List<CombinationActivityDO> selectListByStatus(Integer status) {\n        return selectList(CombinationActivityDO::getStatus, status);\n    }\n\n    default PageResult<CombinationActivityDO> selectPage(PageParam pageParam, Integer status) {\n        return selectPage(pageParam, new LambdaQueryWrapperX<CombinationActivityDO>()\n                .eq(CombinationActivityDO::getStatus, status));\n    }\n\n    default CombinationActivityDO selectBySpuIdAndStatusAndNow(Long spuId, Integer status) {\n        LocalDateTime now = LocalDateTime.now();\n        return selectOne(new LambdaQueryWrapperX<CombinationActivityDO>()\n                .eq(CombinationActivityDO::getSpuId, spuId)\n                .eq(CombinationActivityDO::getStatus, status)\n                .lt(CombinationActivityDO::getStartTime, now)\n                .gt(CombinationActivityDO::getEndTime, now)); // 开始时间 < now < 结束时间，也就是说获取指定时间段的活动\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationProductMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.combination;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 拼团商品 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface CombinationProductMapper extends BaseMapperX<CombinationProductDO> {\n\n    default PageResult<CombinationProductDO> selectPage(CombinationProductPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<CombinationProductDO>()\n                .eqIfPresent(CombinationProductDO::getActivityId, reqVO.getActivityId())\n                .eqIfPresent(CombinationProductDO::getSpuId, reqVO.getSpuId())\n                .eqIfPresent(CombinationProductDO::getSkuId, reqVO.getSkuId())\n                .eqIfPresent(CombinationProductDO::getActivityStatus, reqVO.getActivityStatus())\n                .betweenIfPresent(CombinationProductDO::getActivityStartTime, reqVO.getActivityStartTime())\n                .betweenIfPresent(CombinationProductDO::getActivityEndTime, reqVO.getActivityEndTime())\n                .eqIfPresent(CombinationProductDO::getCombinationPrice, reqVO.getActivePrice())\n                .betweenIfPresent(CombinationProductDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(CombinationProductDO::getId));\n    }\n\n    default List<CombinationProductDO> selectListByActivityIds(Collection<Long> ids) {\n        return selectList(CombinationProductDO::getActivityId, ids);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationRecordMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.combination;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 拼团记录 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO> {\n\n    default CombinationRecordDO selectByUserIdAndOrderId(Long userId, Long orderId) {\n        return selectOne(CombinationRecordDO::getUserId, userId,\n                CombinationRecordDO::getOrderId, orderId);\n    }\n\n    /**\n     * 查询拼团记录\n     *\n     * @param headId 团长编号\n     * @return 拼团记录\n     */\n    default CombinationRecordDO selectByHeadId(Long headId, Integer status) {\n        return selectOne(new LambdaQueryWrapperX<CombinationRecordDO>()\n                .eq(CombinationRecordDO::getId, headId)\n                .eq(CombinationRecordDO::getStatus, status));\n    }\n\n    /**\n     * 查询拼团记录\n     *\n     * @param userId     用户 id\n     * @param activityId 活动 id\n     * @return 拼团记录\n     */\n    default List<CombinationRecordDO> selectListByUserIdAndActivityId(Long userId, Long activityId) {\n        return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()\n                .eq(CombinationRecordDO::getUserId, userId)\n                .eq(CombinationRecordDO::getActivityId, activityId));\n    }\n\n    /**\n     * 获取最近的 count 条数据\n     *\n     * @param count 数量\n     * @return 拼团记录列表\n     */\n    default List<CombinationRecordDO> selectLatestList(int count) {\n        return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()\n                .orderByDesc(CombinationRecordDO::getId)\n                .last(\"LIMIT \" + count));\n    }\n\n    default List<CombinationRecordDO> selectListByActivityIdAndStatusAndHeadId(Long activityId, Integer status,\n                                                                               Long headId, Integer count) {\n        return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()\n                .eqIfPresent(CombinationRecordDO::getActivityId, activityId)\n                .eqIfPresent(CombinationRecordDO::getStatus, status)\n                .eq(CombinationRecordDO::getHeadId, headId)\n                .orderByDesc(CombinationRecordDO::getId)\n                .last(\"LIMIT \" + count));\n    }\n\n    default Map<Long, Integer> selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(Collection<Long> activityIds,\n                                                                                             Integer status, Long headId) {\n        // SQL count 查询\n        List<Map<String, Object>> result = selectMaps(new QueryWrapper<CombinationRecordDO>()\n                .select(\"COUNT(DISTINCT(user_id)) AS recordCount, activity_id AS activityId\")\n                .in(\"activity_id\", activityIds)\n                .eq(status != null, \"status\", status)\n                .eq(headId != null, \"head_id\", headId)\n                .groupBy(\"activity_id\"));\n        if (CollUtil.isEmpty(result)) {\n            return Collections.emptyMap();\n        }\n        // 转换数据\n        return CollectionUtils.convertMap(result,\n                record -> MapUtil.getLong(record, \"activityId\"),\n                record -> MapUtil.getInt(record, \"recordCount\"));\n    }\n\n    default PageResult<CombinationRecordDO> selectPage(CombinationRecordReqPageVO pageVO) {\n        LambdaQueryWrapperX<CombinationRecordDO> queryWrapper = new LambdaQueryWrapperX<CombinationRecordDO>()\n                .eqIfPresent(CombinationRecordDO::getStatus, pageVO.getStatus())\n                .betweenIfPresent(CombinationRecordDO::getCreateTime, pageVO.getCreateTime());\n        // 如果 headId 非空，说明查询指定团的团长 + 团员的拼团记录\n        if (pageVO.getHeadId() != null) {\n            queryWrapper.eq(CombinationRecordDO::getId, pageVO.getHeadId()) // 团长\n                    .or().eq(CombinationRecordDO::getHeadId, pageVO.getHeadId()); // 团员\n        }\n        return selectPage(pageVO, queryWrapper);\n    }\n\n    /**\n     * 查询指定条件的记录数\n     *\n     * @param status       状态，可为 null\n     * @param virtualGroup 是否虚拟成团，可为 null\n     * @param headId       团长编号，可为 null\n     * @return 记录数\n     */\n    default Long selectCountByHeadAndStatusAndVirtualGroup(Integer status, Boolean virtualGroup, Long headId) {\n        return selectCount(new LambdaQueryWrapperX<CombinationRecordDO>()\n                .eqIfPresent(CombinationRecordDO::getStatus, status)\n                .eqIfPresent(CombinationRecordDO::getVirtualGroup, virtualGroup)\n                .eqIfPresent(CombinationRecordDO::getHeadId, headId));\n    }\n\n    /**\n     * 查询用户拼团记录（DISTINCT 去重），也就是说查询会员表中的用户有多少人参与过拼团活动每个人只统计一次\n     *\n     * @return 参加过拼团的用户数\n     */\n    default Long selectUserCount() {\n        return selectCount(new QueryWrapper<CombinationRecordDO>()\n                .select(\"DISTINCT (user_id)\"));\n    }\n\n    default List<CombinationRecordDO> selectListByHeadIdAndStatusAndExpireTimeLt(Long headId, Integer status, LocalDateTime dateTime) {\n        return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()\n                .eq(CombinationRecordDO::getHeadId, headId)\n                .eq(CombinationRecordDO::getStatus, status)\n                .lt(CombinationRecordDO::getExpireTime, dateTime));\n    }\n\n    default List<CombinationRecordDO> selectListByHeadId(Long headId) {\n        return selectList(CombinationRecordDO::getHeadId, headId);\n    }\n\n    default PageResult<CombinationRecordDO> selectPage(Long userId, AppCombinationRecordPageReqVO pageReqVO) {\n        LambdaQueryWrapperX<CombinationRecordDO> queryWrapper = new LambdaQueryWrapperX<CombinationRecordDO>()\n                .eq(CombinationRecordDO::getUserId, userId)\n                .eqIfPresent(CombinationRecordDO::getStatus, pageReqVO.getStatus());\n        return selectPage(pageReqVO, queryWrapper);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.github.yulichang.toolkit.MPJWrappers;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 优惠劵 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface CouponMapper extends BaseMapperX<CouponDO> {\n\n    default PageResult<CouponDO> selectPage(CouponPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<CouponDO>()\n                .eqIfPresent(CouponDO::getTemplateId, reqVO.getTemplateId())\n                .eqIfPresent(CouponDO::getStatus, reqVO.getStatus())\n                .inIfPresent(CouponDO::getUserId, reqVO.getUserIds())\n                .betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(CouponDO::getId));\n    }\n\n    default List<CouponDO> selectListByUserIdAndStatus(Long userId, Integer status) {\n        return selectList(new LambdaQueryWrapperX<CouponDO>()\n                .eq(CouponDO::getUserId, userId).eq(CouponDO::getStatus, status));\n    }\n\n    default CouponDO selectByIdAndUserId(Long id, Long userId) {\n        return selectOne(new LambdaQueryWrapperX<CouponDO>()\n                .eq(CouponDO::getId, id).eq(CouponDO::getUserId, userId));\n    }\n\n    default int delete(Long id, Collection<Integer> whereStatuses) {\n        return update(null, new LambdaUpdateWrapper<CouponDO>()\n                .eq(CouponDO::getId, id).in(CouponDO::getStatus, whereStatuses)\n                .set(CouponDO::getDeleted, 1));\n    }\n\n    default int updateByIdAndStatus(Long id, Integer status, CouponDO updateObj) {\n        return update(updateObj, new LambdaUpdateWrapper<CouponDO>()\n                .eq(CouponDO::getId, id).eq(CouponDO::getStatus, status));\n    }\n\n    default Long selectCountByUserIdAndStatus(Long userId, Integer status) {\n        return selectCount(new LambdaQueryWrapperX<CouponDO>()\n                .eq(CouponDO::getUserId, userId)\n                .eq(CouponDO::getStatus, status));\n    }\n\n    default List<CouponDO> selectListByTemplateIdAndUserId(Long templateId, Collection<Long> userIds) {\n        return selectList(new LambdaQueryWrapperX<CouponDO>()\n                .eq(CouponDO::getTemplateId, templateId)\n                .in(CouponDO::getUserId, userIds)\n        );\n    }\n\n    default Map<Long, Integer> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) {\n        String templateIdAlias = \"templateId\";\n        String countAlias = \"count\";\n        List<Map<String, Object>> list = selectMaps(MPJWrappers.lambdaJoin(CouponDO.class)\n                .selectAs(CouponDO::getTemplateId, templateIdAlias)\n                .selectCount(CouponDO::getId, countAlias)\n                .eq(CouponDO::getUserId, userId)\n                .in(CouponDO::getTemplateId, templateIds)\n                .groupBy(CouponDO::getTemplateId));\n        return convertMap(list, map -> MapUtil.getLong(map, templateIdAlias), map -> MapUtil.getInt(map, countAlias));\n    }\n\n    default List<CouponDO> selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime) {\n        return selectList(new LambdaQueryWrapperX<CouponDO>()\n                .eq(CouponDO::getStatus, status)\n                .le(CouponDO::getValidEndTime, validEndTime)\n        );\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.function.Consumer;\n\n/**\n * 优惠劵模板 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> {\n\n    default PageResult<CouponTemplateDO> selectPage(CouponTemplatePageReqVO reqVO) {\n        // 构建可领取的查询条件\n        Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = buildCanTakeQueryConsumer(reqVO.getCanTakeTypes());\n        // 执行分页查询\n        return selectPage(reqVO, new LambdaQueryWrapperX<CouponTemplateDO>()\n                .likeIfPresent(CouponTemplateDO::getName, reqVO.getName())\n                .eqIfPresent(CouponTemplateDO::getStatus, reqVO.getStatus())\n                .eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType())\n                .betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime())\n                .eqIfPresent(CouponTemplateDO::getProductScope, reqVO.getProductScope())\n                .and(reqVO.getProductScopeValue() != null, w -> w.apply(\"FIND_IN_SET({0}, product_scope_values)\",\n                        reqVO.getProductScopeValue()))\n                .and(canTakeConsumer != null, canTakeConsumer)\n                .orderByDesc(CouponTemplateDO::getId));\n    }\n\n    default int updateTakeCount(Long id, Integer incrCount) {\n        LambdaUpdateWrapper<CouponTemplateDO> updateWrapper = new LambdaUpdateWrapper<CouponTemplateDO>()\n                .eq(CouponTemplateDO::getId, id)\n                .setSql(\"take_count = take_count + \" + incrCount);\n        // 增加已领取的数量（incrCount 为正数），需要考虑发放数量 totalCount 的限制\n        if (incrCount > 0) {\n            updateWrapper.and(i -> i.apply(\"take_count < total_count\")\n                    .or().eq(CouponTemplateDO::getTotalCount, CouponTemplateDO.TOTAL_COUNT_MAX));\n        }\n        return update(updateWrapper);\n    }\n\n    default List<CouponTemplateDO> selectListByTakeType(Integer takeType) {\n        return selectList(CouponTemplateDO::getTakeType, takeType, CouponTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus());\n    }\n\n    default List<CouponTemplateDO> selectList(List<Integer> canTakeTypes, Integer productScope, Long productScopeValue, Integer count) {\n        // 构建可领取的查询条件\n        Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = buildCanTakeQueryConsumer(canTakeTypes);\n        return selectList(new LambdaQueryWrapperX<CouponTemplateDO>()\n                .eqIfPresent(CouponTemplateDO::getProductScope, productScope)\n                .and(productScopeValue != null, w -> w.apply(\"FIND_IN_SET({0}, product_scope_values)\",\n                        productScopeValue))\n                .and(canTakeConsumer != null, canTakeConsumer)\n                .last(\" LIMIT \" + count)\n                .orderByDesc(CouponTemplateDO::getId));\n    }\n\n    static Consumer<LambdaQueryWrapper<CouponTemplateDO>> buildCanTakeQueryConsumer(List<Integer> canTakeTypes) {\n        Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = null;\n        if (CollUtil.isNotEmpty(canTakeTypes)) {\n            canTakeConsumer = w ->\n                    w.eq(CouponTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) // 1. 状态为可用的\n                            .in(CouponTemplateDO::getTakeType, canTakeTypes) // 2. 领取方式一致\n                            .and(ww -> ww.gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now())  // 3.1 未过期\n                                    .or().eq(CouponTemplateDO::getValidityType, CouponTemplateValidityTypeEnum.TERM.getType())) // 3.2 领取之后\n                            .apply(\" (take_count < total_count OR total_count = \" + CouponTemplateDO.TOTAL_COUNT_MAX + \")\"); // 4. 剩余数量大于 0，或者无限领取\n        }\n        return canTakeConsumer;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountActivityMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.discount;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 限时折扣活动 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface DiscountActivityMapper extends BaseMapperX<DiscountActivityDO> {\n\n    default PageResult<DiscountActivityDO> selectPage(DiscountActivityPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<DiscountActivityDO>()\n                .likeIfPresent(DiscountActivityDO::getName, reqVO.getName())\n                .eqIfPresent(DiscountActivityDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(DiscountActivityDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(DiscountActivityDO::getId));\n    }\n\n    /**\n     * 获取指定活动编号的活动列表且\n     * 开始时间和结束时间小于给定时间 dateTime 的活动列表\n     *\n     * @param ids      活动编号\n     * @param dateTime 指定日期\n     * @return 活动列表\n     */\n    default List<DiscountActivityDO> selectListByIdsAndDateTimeLt(Collection<Long> ids, LocalDateTime dateTime) {\n        return selectList(new LambdaQueryWrapperX<DiscountActivityDO>()\n                .in(DiscountActivityDO::getId, ids)\n                .lt(DiscountActivityDO::getStartTime, dateTime)\n                .gt(DiscountActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间，也就是说获取指定时间段的活动\n                .orderByDesc(DiscountActivityDO::getCreateTime));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.discount;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 限时折扣商城 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface DiscountProductMapper extends BaseMapperX<DiscountProductDO> {\n\n    default List<DiscountProductDO> selectListByActivityId(Long activityId) {\n        return selectList(DiscountProductDO::getActivityId, activityId);\n    }\n\n    default List<DiscountProductDO> selectListByActivityId(Collection<Long> activityIds) {\n        return selectList(DiscountProductDO::getActivityId, activityIds);\n    }\n\n    default void updateByActivityId(DiscountProductDO discountProductDO) {\n        update(discountProductDO, new LambdaUpdateWrapper<DiscountProductDO>()\n                .eq(DiscountProductDO::getActivityId, discountProductDO.getActivityId()));\n    }\n\n    default void deleteByActivityId(Long activityId) {\n        delete(DiscountProductDO::getActivityId, activityId);\n    }\n\n    default List<DiscountProductDO> selectListBySkuIdsAndStatusAndNow(Collection<Long> skuIds, Integer status) {\n        LocalDateTime now = LocalDateTime.now();\n        return selectList(new LambdaQueryWrapperX<DiscountProductDO>()\n                .in(DiscountProductDO::getSkuId, skuIds)\n                .eq(DiscountProductDO::getActivityStatus,status)\n                .lt(DiscountProductDO::getActivityStartTime, now)\n                .gt(DiscountProductDO::getActivityEndTime, now));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/diy/DiyPageMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 装修页面 Mapper\n *\n * @author owen\n */\n@Mapper\npublic interface DiyPageMapper extends BaseMapperX<DiyPageDO> {\n\n    default PageResult<DiyPageDO> selectPage(DiyPagePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<DiyPageDO>()\n                .likeIfPresent(DiyPageDO::getName, reqVO.getName())\n                .betweenIfPresent(DiyPageDO::getCreateTime, reqVO.getCreateTime())\n                // 模板下面的页面，在模板中管理\n                .isNull(DiyPageDO::getTemplateId)\n                .orderByDesc(DiyPageDO::getId));\n    }\n\n    default List<DiyPageDO> selectListByTemplateId(Long templateId) {\n        return selectList(DiyPageDO::getTemplateId, templateId);\n    }\n\n    default DiyPageDO selectByNameAndTemplateIdIsNull(String name) {\n        return selectOne(new LambdaQueryWrapperX<DiyPageDO>()\n                .eq(DiyPageDO::getName, name)\n                .isNull(DiyPageDO::getTemplateId));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/diy/DiyTemplateMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 装修模板 Mapper\n *\n * @author owen\n */\n@Mapper\npublic interface DiyTemplateMapper extends BaseMapperX<DiyTemplateDO> {\n\n    default PageResult<DiyTemplateDO> selectPage(DiyTemplatePageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<DiyTemplateDO>()\n                .likeIfPresent(DiyTemplateDO::getName, reqVO.getName())\n                .eqIfPresent(DiyTemplateDO::getUsed, reqVO.getUsed())\n                .betweenIfPresent(DiyTemplateDO::getUsedTime, reqVO.getUsedTime())\n                .betweenIfPresent(DiyTemplateDO::getCreateTime, reqVO.getCreateTime())\n                .orderByDesc(DiyTemplateDO::getUsed) // 排序规则1：已使用的排到最前面\n                .orderByDesc(DiyTemplateDO::getId)); // 排序规则2：新创建的排到前面\n    }\n\n    default DiyTemplateDO selectByUsed(boolean used) {\n        return selectOne(DiyTemplateDO::getUsed, used);\n    }\n\n    default DiyTemplateDO selectByName(String name) {\n        return selectOne(DiyTemplateDO::getName, name);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuConversationMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.kefu;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 客服会话 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface KeFuConversationMapper extends BaseMapperX<KeFuConversationDO> {\n\n    default List<KeFuConversationDO> selectConversationList() {\n        return selectList(new LambdaQueryWrapperX<KeFuConversationDO>()\n                .eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)\n                .orderByDesc(KeFuConversationDO::getCreateTime));\n    }\n\n    default void updateAdminUnreadMessageCountIncrement(Long id) {\n        update(new LambdaUpdateWrapper<KeFuConversationDO>()\n                .eq(KeFuConversationDO::getId, id)\n                .setSql(\"admin_unread_message_count = admin_unread_message_count + 1\"));\n    }\n\n    default KeFuConversationDO selectByUserId(Long userId) {\n        return selectOne(KeFuConversationDO::getUserId, userId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuMessageMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.kefu;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageListReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 客服消息 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface KeFuMessageMapper extends BaseMapperX<KeFuMessageDO> {\n\n    /**\n     * 获得消息列表\n     * 1. 第一次查询时，不带时间，默认查询最新的十条消息\n     * 2. 第二次查询时，带时间，查询历史消息\n     *\n     * @param reqVO 列表请求\n     * @return 消息列表\n     */\n    default List<KeFuMessageDO> selectList(KeFuMessageListReqVO reqVO) {\n        return selectList(new QueryWrapperX<KeFuMessageDO>()\n                .eqIfPresent(\"conversation_id\", reqVO.getConversationId())\n                .ltIfPresent(\"create_time\", reqVO.getCreateTime())\n                .orderByDesc(\"create_time\")\n                .limitN(reqVO.getLimit()));\n    }\n\n    default List<KeFuMessageDO> selectListByConversationIdAndUserTypeAndReadStatus(Long conversationId, Integer userType,\n                                                                                   Boolean readStatus) {\n        return selectList(new LambdaQueryWrapper<KeFuMessageDO>()\n                .eq(KeFuMessageDO::getConversationId, conversationId)\n                .ne(KeFuMessageDO::getSenderType, userType) // 管理员：查询出未读的会员消息，会员：查询出未读的客服消息\n                .eq(KeFuMessageDO::getReadStatus, readStatus));\n    }\n\n    default void updateReadStatusBatchByIds(Collection<Long> ids, KeFuMessageDO keFuMessageDO) {\n        update(keFuMessageDO, new LambdaUpdateWrapper<KeFuMessageDO>()\n                .in(KeFuMessageDO::getId, ids));\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointActivityMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.point;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 积分商城活动 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface PointActivityMapper extends BaseMapperX<PointActivityDO> {\n\n    default PageResult<PointActivityDO> selectPage(PointActivityPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<PointActivityDO>()\n                .eqIfPresent(PointActivityDO::getStatus, reqVO.getStatus())\n                .orderByDesc(PointActivityDO::getSort));\n    }\n\n    /**\n     * 更新活动库存(减少)\n     *\n     * @param id    活动编号\n     * @param count 扣减的库存数量(正数)\n     * @return 影响的行数\n     */\n    default int updateStockDecr(Long id, int count) {\n        Assert.isTrue(count > 0);\n        return update(null, new LambdaUpdateWrapper<PointActivityDO>()\n                .eq(PointActivityDO::getId, id)\n                .ge(PointActivityDO::getStock, count)\n                .setSql(\"stock = stock - \" + count));\n    }\n\n    /**\n     * 更新活动库存（增加）\n     *\n     * @param id    活动编号\n     * @param count 增加的库存数量(正数)\n     * @return 影响的行数\n     */\n    default int updateStockIncr(Long id, int count) {\n        Assert.isTrue(count > 0);\n        return update(null, new LambdaUpdateWrapper<PointActivityDO>()\n                .eq(PointActivityDO::getId, id)\n                .setSql(\"stock = stock + \" + count));\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.point;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 积分商城商品 Mapper\n *\n * @author HUIHUI\n */\n@Mapper\npublic interface PointProductMapper extends BaseMapperX<PointProductDO> {\n\n    default List<PointProductDO> selectListByActivityId(Collection<Long> activityIds) {\n        return selectList(PointProductDO::getActivityId, activityIds);\n    }\n\n    default List<PointProductDO> selectListByActivityId(Long activityId) {\n        return selectList(PointProductDO::getActivityId, activityId);\n    }\n\n    default void updateByActivityId(PointProductDO pointProductDO) {\n        update(pointProductDO, new LambdaUpdateWrapper<PointProductDO>()\n                .eq(PointProductDO::getActivityId, pointProductDO.getActivityId()));\n    }\n\n    default PointProductDO selectListByActivityIdAndSkuId(Long activityId, Long skuId) {\n        return selectOne(PointProductDO::getActivityId, activityId,\n                PointProductDO::getSkuId, skuId);\n    }\n\n    /**\n     * 更新活动库存（减少）\n     *\n     * @param id    活动编号\n     * @param count 扣减的库存数量(减少库存)\n     * @return 影响的行数\n     */\n    default int updateStockDecr(Long id, int count) {\n        Assert.isTrue(count > 0);\n        return update(null, new LambdaUpdateWrapper<PointProductDO>()\n                .eq(PointProductDO::getId, id)\n                .ge(PointProductDO::getStock, count)\n                .setSql(\"stock = stock - \" + count));\n    }\n\n    /**\n     * 更新活动库存（增加）\n     *\n     * @param id    活动编号\n     * @param count 需要增加的库存（增加库存）\n     * @return 影响的行数\n     */\n    default int updateStockIncr(Long id, int count) {\n        Assert.isTrue(count > 0);\n        return update(null, new LambdaUpdateWrapper<PointProductDO>()\n                .eq(PointProductDO::getId, id)\n                .setSql(\"stock = stock + \" + count));\n    }\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.reward;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 满减送活动 Mapper\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface RewardActivityMapper extends BaseMapperX<RewardActivityDO> {\n\n    default PageResult<RewardActivityDO> selectPage(RewardActivityPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<RewardActivityDO>()\n                .likeIfPresent(RewardActivityDO::getName, reqVO.getName())\n                .eqIfPresent(RewardActivityDO::getStatus, reqVO.getStatus())\n                .orderByDesc(RewardActivityDO::getId));\n    }\n\n    default List<RewardActivityDO> selectListBySpuIdAndStatusAndNow(Collection<Long> spuIds,\n                                                                    Collection<Long> categoryIds,\n                                                                    Integer status) {\n        LocalDateTime now = LocalDateTime.now();\n        Function<Collection<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()\n                .map(id -> StrUtil.format(\"FIND_IN_SET({}, product_scope_values) \", id))\n                .collect(Collectors.joining(\" OR \"));\n        return selectList(new LambdaQueryWrapperX<RewardActivityDO>()\n                .eq(RewardActivityDO::getStatus, status)\n                .lt(RewardActivityDO::getStartTime, now)\n                .gt(RewardActivityDO::getEndTime, now)\n                .and(i -> i.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.SPU.getScope())\n                            .and(i1 -> i1.apply(productScopeValuesFindInSetFunc.apply(spuIds)))\n                        .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()))\n                        .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope())\n                                .and(i2 -> i2.apply(productScopeValuesFindInSetFunc.apply(categoryIds)))))\n                .orderByDesc(RewardActivityDO::getId)\n        );\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 秒杀活动 Mapper\n *\n * @author halfninety\n */\n@Mapper\npublic interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {\n\n    default PageResult<SeckillActivityDO> selectPage(SeckillActivityPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<SeckillActivityDO>()\n                .likeIfPresent(SeckillActivityDO::getName, reqVO.getName())\n                .eqIfPresent(SeckillActivityDO::getStatus, reqVO.getStatus())\n                .betweenIfPresent(SeckillActivityDO::getCreateTime, reqVO.getCreateTime())\n                .apply(ObjectUtil.isNotNull(reqVO.getConfigId()), \"FIND_IN_SET(\" + reqVO.getConfigId() + \", config_ids) > 0\")\n                .orderByDesc(SeckillActivityDO::getId));\n    }\n\n    default List<SeckillActivityDO> selectListBySpuIdAndStatus(Long spuId, Integer status) {\n        return selectList(SeckillActivityDO::getSpuId, spuId,\n                SeckillActivityDO::getStatus, status);\n    }\n\n    /**\n     * 更新活动库存(减少)\n     *\n     * @param id    活动编号\n     * @param count 扣减的库存数量(正数)\n     * @return 影响的行数\n     */\n    default int updateStockDecr(Long id, int count) {\n        Assert.isTrue(count > 0);\n        return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()\n                .eq(SeckillActivityDO::getId, id)\n                .ge(SeckillActivityDO::getStock, count)\n                .setSql(\"stock = stock - \" + count));\n    }\n\n    /**\n     * 更新活动库存（增加）\n     *\n     * @param id    活动编号\n     * @param count 增加的库存数量(正数)\n     * @return 影响的行数\n     */\n    default int updateStockIncr(Long id, int count) {\n        Assert.isTrue(count > 0);\n        return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()\n                .eq(SeckillActivityDO::getId, id)\n                .setSql(\"stock = stock + \" + count));\n    }\n\n    default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status, LocalDateTime dateTime) {\n        return selectPage(pageReqVO, new LambdaQueryWrapperX<SeckillActivityDO>()\n                .eqIfPresent(SeckillActivityDO::getStatus, status)\n                .lt(SeckillActivityDO::getStartTime, dateTime)\n                .gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间，也就是说获取指定时间段的活动\n                .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), \"FIND_IN_SET(\" + pageReqVO.getConfigId() + \",config_ids) > 0\"));\n    }\n\n    default SeckillActivityDO selectBySpuIdAndStatusAndNow(Long spuId, Integer status) {\n        LocalDateTime now = LocalDateTime.now();\n        return selectOne(new LambdaQueryWrapperX<SeckillActivityDO>()\n                .eq(SeckillActivityDO::getSpuId, spuId)\n                .eq(SeckillActivityDO::getStatus, status)\n                .lt(SeckillActivityDO::getStartTime, now)\n                .gt(SeckillActivityDO::getEndTime, now)); // 开始时间 < now < 结束时间，也就是说获取指定时间段的活动\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 秒杀活动商品 Mapper\n *\n * @author halfninety\n */\n@Mapper\npublic interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> {\n\n    default List<SeckillProductDO> selectListByActivityId(Long activityId) {\n        return selectList(SeckillProductDO::getActivityId, activityId);\n    }\n\n    default SeckillProductDO selectByActivityIdAndSkuId(Long activityId, Long skuId) {\n        return selectOne(SeckillProductDO::getActivityId, activityId,\n                SeckillProductDO::getSkuId, skuId);\n    }\n\n    default List<SeckillProductDO> selectListByActivityId(Collection<Long> ids) {\n        return selectList(SeckillProductDO::getActivityId, ids);\n    }\n\n    /**\n     * 更新活动库存（减少）\n     *\n     * @param id    活动编号\n     * @param count 扣减的库存数量(减少库存)\n     * @return 影响的行数\n     */\n    default int updateStockDecr(Long id, int count) {\n        Assert.isTrue(count > 0);\n        return update(null, new LambdaUpdateWrapper<SeckillProductDO>()\n                .eq(SeckillProductDO::getId, id)\n                .ge(SeckillProductDO::getStock, count)\n                .setSql(\"stock = stock - \" + count));\n    }\n\n    /**\n     * 更新活动库存（增加）\n     *\n     * @param id    活动编号\n     * @param count 需要增加的库存（增加库存）\n     * @return 影响的行数\n     */\n    default int updateStockIncr(Long id, int count) {\n        Assert.isTrue(count > 0);\n        return update(null, new LambdaUpdateWrapper<SeckillProductDO>()\n                .eq(SeckillProductDO::getId, id)\n                .setSql(\"stock = stock + \" + count));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java",
    "content": "package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n@Mapper\npublic interface SeckillConfigMapper extends BaseMapperX<SeckillConfigDO> {\n\n    default PageResult<SeckillConfigDO> selectPage(SeckillConfigPageReqVO reqVO) {\n        return selectPage(reqVO, new LambdaQueryWrapperX<SeckillConfigDO>()\n                .likeIfPresent(SeckillConfigDO::getName, reqVO.getName())\n                .eqIfPresent(SeckillConfigDO::getStatus, reqVO.getStatus())\n                .orderByAsc(SeckillConfigDO::getStartTime));\n    }\n\n    default List<SeckillConfigDO> selectListByStatus(Integer status) {\n        return selectList(SeckillConfigDO::getStatus, status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/framework/package-info.java",
    "content": "/**\n * 属于 promotion 模块的 framework 封装\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.promotion.framework;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/framework/rpc/config/RpcConfiguration.java",
    "content": "package cn.iocoder.yudao.module.promotion.framework.rpc.config;\n\nimport cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.product.api.category.ProductCategoryApi;\nimport cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.system.api.social.SocialClientApi;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(value = \"promotionRpcConfiguration\", proxyBeanMethods = false)\n@EnableFeignClients(clients = {ProductSkuApi.class, ProductSpuApi.class, ProductCategoryApi.class,\n        MemberUserApi.class, TradeOrderApi.class, AdminUserApi.class, SocialClientApi.class,\n        WebSocketSenderApi.class})\npublic class RpcConfiguration {\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/framework/rpc/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.promotion.framework.rpc;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/framework/security/config/SecurityConfiguration.java",
    "content": "package cn.iocoder.yudao.module.promotion.framework.security.config;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport cn.iocoder.yudao.module.promotion.enums.ApiConstants;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\n/**\n * Promotion 模块的 Security 配置\n */\n@Configuration(\"promotionSecurityConfiguration\")\npublic class SecurityConfiguration {\n\n    @Bean(\"promotionAuthorizeRequestsCustomizer\")\n    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {\n        return new AuthorizeRequestsCustomizer() {\n\n            @Override\n            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n                // Swagger 接口文档\n                registry.requestMatchers(\"/v3/api-docs/**\").permitAll()\n                        .requestMatchers(\"/webjars/**\").permitAll()\n                        .requestMatchers(\"/swagger-ui\").permitAll()\n                        .requestMatchers(\"/swagger-ui/**\").permitAll();\n                // Spring Boot Actuator 的安全配置\n                registry.requestMatchers(\"/actuator\").permitAll()\n                        .requestMatchers(\"/actuator/**\").permitAll();\n                // Druid 监控\n                registry.requestMatchers(\"/druid/**\").permitAll();\n                // RPC 服务的安全配置\n                registry.requestMatchers(ApiConstants.PREFIX + \"/**\").permitAll();\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/framework/security/core/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.promotion.framework.security.core;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/job/combination/CombinationRecordExpireJob.java",
    "content": "package cn.iocoder.yudao.module.promotion.job.combination;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.tenant.core.job.TenantJob;\nimport cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n// TODO 芋艿：配置一个 Job\n/**\n * 拼团过期 Job\n *\n * @author HUIHUI\n */\n@Component\npublic class CombinationRecordExpireJob {\n\n    @Resource\n    private CombinationRecordService combinationRecordService;\n\n    @XxlJob(\"combinationRecordExpireJob\")\n    @TenantJob // 多租户\n    public String execute() {\n        KeyValue<Integer, Integer> keyValue = combinationRecordService.expireCombinationRecord();\n        return StrUtil.format(\"过期拼团 {} 个, 虚拟成团 {} 个\", keyValue.getKey(), keyValue.getValue());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/job/coupon/CouponExpireJob.java",
    "content": "package cn.iocoder.yudao.module.promotion.job.coupon;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.tenant.core.job.TenantJob;\nimport cn.iocoder.yudao.module.promotion.service.coupon.CouponService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n// TODO 芋艿：配置一个 Job\n/**\n * 优惠券过期 Job\n *\n * @author owen\n */\n@Component\npublic class CouponExpireJob {\n\n    @Resource\n    private CouponService couponService;\n\n    @XxlJob(\"couponExpireJob\")\n    @TenantJob // 多租户\n    public String execute() {\n        int count = couponService.expireCoupon();\n        return StrUtil.format(\"过期优惠券 {} 个\", count);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/job/package-info.java",
    "content": "/**\n * TODO 占位，无具体含义\n */\npackage cn.iocoder.yudao.module.promotion.job;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/mq/consumer/coupon/CouponTakeByRegisterConsumer.java",
    "content": "package cn.iocoder.yudao.module.promotion.mq.consumer.coupon;\n\nimport cn.iocoder.yudao.module.member.api.message.user.MemberUserCreateMessage;\nimport cn.iocoder.yudao.module.promotion.service.coupon.CouponService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * 用户注册时，发送优惠劵的消费者，基 {@link MemberUserCreateMessage} 消息\n *\n * @author owen\n */\n@Component\n@Slf4j\npublic class CouponTakeByRegisterConsumer {\n\n    @Resource\n    private CouponService couponService;\n\n    @EventListener\n    @Async // Spring Event 默认在 Producer 发送的线程，通过 @Async 实现异步\n    public void onMessage(MemberUserCreateMessage message) {\n        log.info(\"[onMessage][消息内容({})]\", message);\n        couponService.takeCouponByRegister(message.getUserId());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/mq/consumer/package-info.java",
    "content": "/**\n * 消息队列的消费者\n */\npackage cn.iocoder.yudao.module.promotion.mq.consumer;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/mq/message/package-info.java",
    "content": "/**\n * 消息队列的消息\n */\npackage cn.iocoder.yudao.module.promotion.mq.message;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/mq/producer/package-info.java",
    "content": "/**\n * 消息队列的生产者\n */\npackage cn.iocoder.yudao.module.promotion.mq.producer;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/package-info.java",
    "content": "/**\n * promotion 模块，我们放营销业务。\n * 例如说：营销活动、banner、优惠券等等\n *\n * 1. Controller URL：以 /promotion/ 开头，避免和其它 Module 冲突\n * 2. DataObject 表名：以 promotion_ 开头，方便在数据库中区分\n */\npackage cn.iocoder.yudao.module.promotion;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleCategoryService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 文章分类 Service 接口\n *\n * @author HUIHUI\n */\npublic interface ArticleCategoryService {\n\n    /**\n     * 创建文章分类\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createArticleCategory(@Valid ArticleCategoryCreateReqVO createReqVO);\n\n    /**\n     * 更新文章分类\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateArticleCategory(@Valid ArticleCategoryUpdateReqVO updateReqVO);\n\n    /**\n     * 删除文章分类\n     *\n     * @param id 编号\n     */\n    void deleteArticleCategory(Long id);\n\n    /**\n     * 获得文章分类\n     *\n     * @param id 编号\n     * @return 文章分类\n     */\n    ArticleCategoryDO getArticleCategory(Long id);\n\n    /**\n     * 获得文章分类分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 文章分类分页\n     */\n    PageResult<ArticleCategoryDO> getArticleCategoryPage(ArticleCategoryPageReqVO pageReqVO);\n\n    /**\n     * 获得指定状态的文章分类列表\n     *\n     * @param status 状态\n     * @return 文章分类列表\n     */\n    List<ArticleCategoryDO> getArticleCategoryListByStatus(Integer status);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleCategoryServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.article.ArticleCategoryConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.article.ArticleCategoryMapper;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.ARTICLE_CATEGORY_DELETE_FAIL_HAVE_ARTICLES;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.ARTICLE_CATEGORY_NOT_EXISTS;\n\n/**\n * 文章分类 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class ArticleCategoryServiceImpl implements ArticleCategoryService {\n\n    @Resource\n    private ArticleCategoryMapper articleCategoryMapper;\n\n    @Resource\n    @Lazy // 延迟加载，解决循环依赖问题\n    private ArticleService articleService;\n\n    @Override\n    public Long createArticleCategory(ArticleCategoryCreateReqVO createReqVO) {\n        // 插入\n        ArticleCategoryDO category = ArticleCategoryConvert.INSTANCE.convert(createReqVO);\n        articleCategoryMapper.insert(category);\n        // 返回\n        return category.getId();\n    }\n\n    @Override\n    public void updateArticleCategory(ArticleCategoryUpdateReqVO updateReqVO) {\n        // 校验存在\n        validateArticleCategoryExists(updateReqVO.getId());\n        // 更新\n        ArticleCategoryDO updateObj = ArticleCategoryConvert.INSTANCE.convert(updateReqVO);\n        articleCategoryMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteArticleCategory(Long id) {\n        // 校验存在\n        validateArticleCategoryExists(id);\n        // 校验是不是存在关联文章\n        Long count = articleService.getArticleCountByCategoryId(id);\n        if (count > 0) {\n            throw exception(ARTICLE_CATEGORY_DELETE_FAIL_HAVE_ARTICLES);\n        }\n\n        // 删除\n        articleCategoryMapper.deleteById(id);\n    }\n\n    private void validateArticleCategoryExists(Long id) {\n        if (articleCategoryMapper.selectById(id) == null) {\n            throw exception(ARTICLE_CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ArticleCategoryDO getArticleCategory(Long id) {\n        return articleCategoryMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<ArticleCategoryDO> getArticleCategoryPage(ArticleCategoryPageReqVO pageReqVO) {\n        return articleCategoryMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<ArticleCategoryDO> getArticleCategoryListByStatus(Integer status) {\n        return articleCategoryMapper.selectListByStatus(status);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.article;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticlePageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 文章 Service 接口\n *\n * @author HUIHUI\n */\npublic interface ArticleService {\n\n    /**\n     * 创建文章\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createArticle(@Valid ArticleCreateReqVO createReqVO);\n\n    /**\n     * 更新文章\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateArticle(@Valid ArticleUpdateReqVO updateReqVO);\n\n    /**\n     * 删除文章\n     *\n     * @param id 编号\n     */\n    void deleteArticle(Long id);\n\n    /**\n     * 获得文章\n     *\n     * @param id 编号\n     * @return 文章\n     */\n    ArticleDO getArticle(Long id);\n\n    /**\n     * 基于标题，获得文章\n     *\n     * 如果有重名的文章，获取最后发布的\n     *\n     * @param title 标题\n     * @return 文章\n     */\n    ArticleDO getLastArticleByTitle(String title);\n\n    /**\n     * 获得文章分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 文章分页\n     */\n    PageResult<ArticleDO> getArticlePage(ArticlePageReqVO pageReqVO);\n\n    /**\n     * 获得文章列表\n     *\n     * @param recommendHot    是否热门\n     * @param recommendBanner 是否轮播图\n     * @return 文章列表\n     */\n    List<ArticleDO> getArticleCategoryListByRecommend(Boolean recommendHot, Boolean recommendBanner);\n\n    /**\n     * 获得文章分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 文章分页\n     */\n    PageResult<ArticleDO> getArticlePage(AppArticlePageReqVO pageReqVO);\n\n    /**\n     * 获得指定分类的文章数量\n     *\n     * @param categoryId 文章分类编号\n     * @return 文章数量\n     */\n    Long getArticleCountByCategoryId(Long categoryId);\n\n    /**\n     * 增加文章浏览量\n     *\n     * @param id 文章编号\n     */\n    void addArticleBrowseCount(Long id);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.article;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticlePageReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.article.ArticleConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.article.ArticleMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.ARTICLE_CATEGORY_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.ARTICLE_NOT_EXISTS;\n\n/**\n * 文章管理 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class ArticleServiceImpl implements ArticleService {\n\n    @Resource\n    private ArticleMapper articleMapper;\n\n    @Resource\n    private ArticleCategoryService articleCategoryService;\n\n    @Override\n    public Long createArticle(ArticleCreateReqVO createReqVO) {\n        // 校验分类存在\n        validateArticleCategoryExists(createReqVO.getCategoryId());\n\n        // 插入\n        ArticleDO article = ArticleConvert.INSTANCE.convert(createReqVO);\n        article.setBrowseCount(0); // 初始浏览量\n        articleMapper.insert(article);\n        // 返回\n        return article.getId();\n    }\n\n    @Override\n    public void updateArticle(ArticleUpdateReqVO updateReqVO) {\n        // 校验存在\n        validateArticleExists(updateReqVO.getId());\n        // 校验分类存在\n        validateArticleCategoryExists(updateReqVO.getCategoryId());\n\n        // 更新\n        ArticleDO updateObj = ArticleConvert.INSTANCE.convert(updateReqVO);\n        articleMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteArticle(Long id) {\n        // 校验存在\n        validateArticleExists(id);\n        // 删除\n        articleMapper.deleteById(id);\n    }\n\n    private void validateArticleExists(Long id) {\n        if (articleMapper.selectById(id) == null) {\n            throw exception(ARTICLE_NOT_EXISTS);\n        }\n    }\n\n    private void validateArticleCategoryExists(Long categoryId) {\n        ArticleCategoryDO articleCategory = articleCategoryService.getArticleCategory(categoryId);\n        if (articleCategory == null) {\n            throw exception(ARTICLE_CATEGORY_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public ArticleDO getArticle(Long id) {\n        return articleMapper.selectById(id);\n    }\n\n    @Override\n    public ArticleDO getLastArticleByTitle(String title) {\n        List<ArticleDO> articles = articleMapper.selectListByTitle(title);\n        return CollUtil.getLast(articles);\n    }\n\n    @Override\n    public PageResult<ArticleDO> getArticlePage(ArticlePageReqVO pageReqVO) {\n        return articleMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<ArticleDO> getArticleCategoryListByRecommend(Boolean recommendHot, Boolean recommendBanner) {\n        return articleMapper.selectList(recommendHot, recommendBanner);\n    }\n\n    @Override\n    public PageResult<ArticleDO> getArticlePage(AppArticlePageReqVO pageReqVO) {\n        return articleMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public Long getArticleCountByCategoryId(Long categoryId) {\n        return articleMapper.selectCount(ArticleDO::getCategoryId, categoryId);\n    }\n\n    @Override\n    public void addArticleBrowseCount(Long id) {\n        // 校验文章是否存在\n        validateArticleExists(id);\n        // 增加浏览次数\n        articleMapper.updateBrowseCount(id);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.banner;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 首页 Banner Service 接口\n *\n * @author xia\n */\npublic interface BannerService {\n\n    /**\n     * 创建 Banner\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createBanner(@Valid BannerCreateReqVO createReqVO);\n\n    /**\n     * 更新 Banner\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateBanner(@Valid BannerUpdateReqVO updateReqVO);\n\n    /**\n     * 删除 Banner\n     *\n     * @param id 编号\n     */\n    void deleteBanner(Long id);\n\n    /**\n     * 获得 Banner\n     *\n     * @param id 编号\n     * @return Banner\n     */\n    BannerDO getBanner(Long id);\n\n    /**\n     * 获得 Banner 分页\n     *\n     * @param pageReqVO 分页查询\n     * @return Banner分页\n     */\n    PageResult<BannerDO> getBannerPage(BannerPageReqVO pageReqVO);\n\n    /**\n     * 增加 Banner 点击量\n     *\n     * @param id Banner编号\n     */\n    void addBannerBrowseCount(Long id);\n\n    /**\n     * 获得 Banner 列表\n     *\n     * @param position 定位\n     * @return Banner 列表\n     */\n    List<BannerDO> getBannerListByPosition(Integer position);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.banner;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.banner.BannerConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.banner.BannerMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.BANNER_NOT_EXISTS;\n\n/**\n * 首页 banner 实现类\n *\n * @author xia\n */\n@Service\n@Validated\npublic class BannerServiceImpl implements BannerService {\n\n    @Resource\n    private BannerMapper bannerMapper;\n\n    @Override\n    public Long createBanner(BannerCreateReqVO createReqVO) {\n        // 插入\n        BannerDO banner = BannerConvert.INSTANCE.convert(createReqVO);\n        bannerMapper.insert(banner);\n        // 返回\n        return banner.getId();\n    }\n\n    @Override\n    public void updateBanner(BannerUpdateReqVO updateReqVO) {\n        // 校验存在\n        this.validateBannerExists(updateReqVO.getId());\n        // 更新\n        BannerDO updateObj = BannerConvert.INSTANCE.convert(updateReqVO);\n        bannerMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void deleteBanner(Long id) {\n        // 校验存在\n        this.validateBannerExists(id);\n        // 删除\n        bannerMapper.deleteById(id);\n    }\n\n    private void validateBannerExists(Long id) {\n        if (bannerMapper.selectById(id) == null) {\n            throw exception(BANNER_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public BannerDO getBanner(Long id) {\n        return bannerMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<BannerDO> getBannerPage(BannerPageReqVO pageReqVO) {\n        return bannerMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public void addBannerBrowseCount(Long id) {\n        // 校验 Banner 是否存在\n        validateBannerExists(id);\n        // 增加点击次数\n        bannerMapper.updateBrowseCount(id);\n    }\n\n    @Override\n    public List<BannerDO> getBannerListByPosition(Integer position) {\n        return bannerMapper.selectBannerListByPosition(position);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\n\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 砍价活动 Service 接口\n *\n * @author HUIHUI\n */\npublic interface BargainActivityService {\n\n    /**\n     * 创建砍价活动\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createBargainActivity(@Valid BargainActivityCreateReqVO createReqVO);\n\n    /**\n     * 更新砍价活动\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateBargainActivity(@Valid BargainActivityUpdateReqVO updateReqVO);\n\n    /**\n     * 更新砍价活动库存\n     *\n     * 如果更新失败（库存不足），则抛出业务异常\n     *\n     * @param id    砍价活动编号\n     * @param count 购买数量\n     */\n    void updateBargainActivityStock(Long id, Integer count);\n\n    /**\n     * 关闭砍价活动\n     *\n     * @param id 砍价活动编号\n     */\n    void closeBargainActivityById(Long id);\n\n    /**\n     * 删除砍价活动\n     *\n     * @param id 编号\n     */\n    void deleteBargainActivity(Long id);\n\n    /**\n     * 获得砍价活动\n     *\n     * @param id 编号\n     * @return 砍价活动\n     */\n    BargainActivityDO getBargainActivity(Long id);\n\n    /**\n     * 获得砍价活动列表\n     *\n     * @param ids 编号数组\n     * @return 砍价活动列表\n     */\n    List<BargainActivityDO> getBargainActivityList(Set<Long> ids);\n\n    /**\n     * 校验砍价活动，是否可以参与（发起砍价、下单、帮好友砍价）\n     *\n     * @param id 编号\n     * @return 砍价活动\n     */\n    BargainActivityDO validateBargainActivityCanJoin(Long id);\n\n    /**\n     * 获得砍价活动分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 砍价活动分页\n     */\n    PageResult<BargainActivityDO> getBargainActivityPage(BargainActivityPageReqVO pageReqVO);\n\n    /**\n     * 获取正在进行的活动分页数据\n     *\n     * @param pageReqVO 分页请求\n     * @return 砍价活动分页\n     */\n    PageResult<BargainActivityDO> getBargainActivityPage(PageParam pageReqVO);\n\n    /**\n     * 获取正在进行的活动分页数据\n     *\n     * @param count 需要的数量\n     * @return 砍价活动分页\n     */\n    List<BargainActivityDO> getBargainActivityListByCount(Integer count);\n\n    /**\n     * 获得 SPU 进行中的砍价活动\n     *\n     * @param spuId SPU 编号数组\n     * @return 砍价活动\n     */\n    BargainActivityDO getMatchBargainActivityBySpuId(Long spuId);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.bargain;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.bargain.BargainActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.bargain.BargainActivityMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\n\n/**\n * 砍价活动 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class BargainActivityServiceImpl implements BargainActivityService {\n\n    @Resource\n    private BargainActivityMapper bargainActivityMapper;\n\n    @Resource\n    private ProductSkuApi productSkuApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createBargainActivity(BargainActivityCreateReqVO createReqVO) {\n        // 校验商品 SPU 是否存在是否参加的别的活动\n        validateBargainConflict(createReqVO.getSpuId(), null);\n        // 校验商品 sku 是否存在\n        validateSku(createReqVO.getSkuId());\n\n        // 插入砍价活动\n        BargainActivityDO activityDO = BargainActivityConvert.INSTANCE.convert(createReqVO)\n                .setTotalStock(createReqVO.getStock())\n                .setStatus(CommonStatusEnum.ENABLE.getStatus());\n        bargainActivityMapper.insert(activityDO);\n        return activityDO.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateBargainActivity(BargainActivityUpdateReqVO updateReqVO) {\n        // 校验存在\n        BargainActivityDO activity = validateBargainActivityExists(updateReqVO.getId());\n        // 校验状态\n        if (ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {\n            throw exception(BARGAIN_ACTIVITY_STATUS_DISABLE);\n        }\n        // 校验商品冲突\n        validateBargainConflict(updateReqVO.getSpuId(), updateReqVO.getId());\n        // 校验商品 sku 是否存在\n        validateSku(updateReqVO.getSkuId());\n\n        // 更新\n        BargainActivityDO updateObj = BargainActivityConvert.INSTANCE.convert(updateReqVO);\n        if (updateObj.getStock() > activity.getTotalStock()) { // 如果更新的库存大于原来的库存，则更新总库存\n            updateObj.setTotalStock(updateObj.getStock());\n        }\n        bargainActivityMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void updateBargainActivityStock(Long id, Integer count) {\n        if (count < 0) {\n            // 更新库存。如果更新失败，则抛出异常\n            int updateCount = bargainActivityMapper.updateStock(id, count);\n            if (updateCount == 0) {\n                throw exception(BARGAIN_ACTIVITY_STOCK_NOT_ENOUGH);\n            }\n        } else if (count > 0) {\n            bargainActivityMapper.updateStock(id, count);\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void closeBargainActivityById(Long id) {\n        // 校验砍价活动是否存在\n        BargainActivityDO activity = validateBargainActivityExists(id);\n        if (CommonStatusEnum.isDisable(activity.getStatus())) {\n            throw exception(BARGAIN_ACTIVITY_STATUS_DISABLE);\n        }\n\n        bargainActivityMapper.updateById(new BargainActivityDO().setId(id)\n                .setStatus(CommonStatusEnum.DISABLE.getStatus()));\n    }\n\n    private void validateBargainConflict(Long spuId, Long activityId) {\n        // 查询所有开启的砍价活动\n        List<BargainActivityDO> activityList = bargainActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        if (activityId != null) { // 更新时排除自己\n            activityList.removeIf(item -> ObjectUtil.equal(item.getId(), activityId));\n        }\n        // 校验商品 spu 是否参加了其它活动\n        if (anyMatch(activityList, activity -> ObjectUtil.equal(activity.getSpuId(), spuId))) {\n            throw exception(BARGAIN_ACTIVITY_SPU_CONFLICTS);\n        }\n    }\n\n    private void validateSku(Long skuId) {\n        ProductSkuRespDTO sku = productSkuApi.getSku(skuId).getCheckedData();\n        if (sku == null) {\n            throw exception(SKU_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteBargainActivity(Long id) {\n        // 校验存在\n        BargainActivityDO activityDO = validateBargainActivityExists(id);\n        // 校验状态\n        if (CommonStatusEnum.isEnable(activityDO.getStatus())) {\n            throw exception(BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END);\n        }\n\n        // 删除\n        bargainActivityMapper.deleteById(id);\n    }\n\n    private BargainActivityDO validateBargainActivityExists(Long id) {\n        BargainActivityDO activityDO = bargainActivityMapper.selectById(id);\n        if (activityDO == null) {\n            throw exception(BARGAIN_ACTIVITY_NOT_EXISTS);\n        }\n        return activityDO;\n    }\n\n    @Override\n    public BargainActivityDO getBargainActivity(Long id) {\n        return bargainActivityMapper.selectById(id);\n    }\n\n    @Override\n    public List<BargainActivityDO> getBargainActivityList(Set<Long> ids) {\n        return bargainActivityMapper.selectByIds(ids);\n    }\n\n    @Override\n    public BargainActivityDO validateBargainActivityCanJoin(Long id) {\n        BargainActivityDO activity = bargainActivityMapper.selectById(id);\n        if (activity == null) {\n            throw exception(BARGAIN_ACTIVITY_NOT_EXISTS);\n        }\n        if (CommonStatusEnum.isDisable(activity.getStatus())) {\n            throw exception(BARGAIN_ACTIVITY_STATUS_CLOSED);\n        }\n        if (activity.getStock() <= 0) {\n            throw exception(BARGAIN_ACTIVITY_STOCK_NOT_ENOUGH);\n        }\n        if (!LocalDateTimeUtils.isBetween(activity.getStartTime(), activity.getEndTime())) {\n            throw exception(BARGAIN_ACTIVITY_TIME_END);\n        }\n        return activity;\n    }\n\n    @Override\n    public PageResult<BargainActivityDO> getBargainActivityPage(BargainActivityPageReqVO pageReqVO) {\n        return bargainActivityMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public PageResult<BargainActivityDO> getBargainActivityPage(PageParam pageReqVO) {\n        // 只查询进行中，且在时间范围内的\n        return bargainActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now());\n    }\n\n    @Override\n    public List<BargainActivityDO> getBargainActivityListByCount(Integer count) {\n        return bargainActivityMapper.selectList(count, CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now());\n    }\n\n    @Override\n    public BargainActivityDO getMatchBargainActivityBySpuId(Long spuId) {\n        return bargainActivityMapper.selectBySpuIdAndStatusAndNow(spuId, CommonStatusEnum.ENABLE.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainHelpService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.bargain;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.help.BargainHelpPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 砍价助力 Service 接口\n *\n * @author 芋道源码\n */\npublic interface BargainHelpService {\n\n    /**\n     * 创建砍价助力（帮人砍价）\n     *\n     * @param userId 用户编号\n     * @param reqVO 请求信息\n     * @return 砍价助力记录\n     */\n    BargainHelpDO createBargainHelp(Long userId, AppBargainHelpCreateReqVO reqVO);\n\n    /**\n     * 【砍价活动】获得助力人数 Map\n     *\n     * @param activityIds 活动编号\n     * @return 助力人数 Map\n     */\n    Map<Long, Integer> getBargainHelpUserCountMapByActivity(Collection<Long> activityIds);\n\n    /**\n     * 【砍价记录】获得助力人数 Map\n     *\n     * @param recordIds 记录编号\n     * @return 助力人数 Map\n     */\n    Map<Long, Integer> getBargainHelpUserCountMapByRecord(Collection<Long> recordIds);\n\n    /**\n     * 【砍价活动】获得用户的助力次数\n     *\n     * @param activityId 活动编号\n     * @param userId 用户编号\n     * @return 助力次数\n     */\n    Long getBargainHelpCountByActivity(Long activityId, Long userId);\n\n    /**\n     * 获得砍价助力分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 砍价助力分页\n     */\n    PageResult<BargainHelpDO> getBargainHelpPage(BargainHelpPageReqVO pageReqVO);\n\n    /**\n     * 获得指定砍价记录编号，对应的砍价助力列表\n     *\n     * @param recordId 砍价记录编号\n     * @return 砍价助力列表\n     */\n    List<BargainHelpDO> getBargainHelpListByRecordId(Long recordId);\n\n    /**\n     * 获得助力记录\n     *\n     * @param recordId 砍价记录编号\n     * @param userId 用户编号\n     * @return 助力记录\n     */\n    BargainHelpDO getBargainHelp(Long recordId, Long userId);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainHelpServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.bargain;\n\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.help.BargainHelpPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.bargain.BargainHelpMapper;\nimport cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;\nimport jodd.util.MathUtil;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\n\n/**\n * 砍价助力 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class BargainHelpServiceImpl implements BargainHelpService {\n\n    @Resource\n    private BargainHelpMapper bargainHelpMapper;\n\n    @Resource\n    private BargainRecordService bargainRecordService;\n    @Resource\n    private BargainActivityService bargainActivityService;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public BargainHelpDO createBargainHelp(Long userId, AppBargainHelpCreateReqVO reqVO) {\n        // 1.1 校验砍价记录存在，并且处于进行中\n        BargainRecordDO record = bargainRecordService.getBargainRecord(reqVO.getRecordId());\n        if (record == null) {\n            throw exception(BARGAIN_RECORD_NOT_EXISTS);\n        }\n        if (ObjUtil.notEqual(record.getStatus(), BargainRecordStatusEnum.IN_PROGRESS.getStatus())) {\n            throw exception(BARGAIN_HELP_CREATE_FAIL_RECORD_NOT_IN_PROCESS);\n        }\n        // 1.2 不能自己给自己砍价\n        if (ObjUtil.equal(record.getUserId(), userId)) {\n            throw exception(BARGAIN_HELP_CREATE_FAIL_RECORD_SELF);\n        }\n\n        // 2.1 校验砍价活动\n        BargainActivityDO activity = bargainActivityService.getBargainActivity(record.getActivityId());\n        // 2.2 校验自己是否助力次数上限\n        if (bargainHelpMapper.selectCountByUserIdAndActivityId(userId, activity.getId())\n                >= activity.getBargainCount()) {\n            throw exception(BARGAIN_HELP_CREATE_FAIL_LIMIT);\n        }\n        // 2.3 特殊情况：砍价已经砍到最低价，不能再砍了\n        if (record.getBargainPrice() <= activity.getBargainMinPrice()) {\n            throw exception(BARGAIN_HELP_CREATE_FAIL_RECORD_NOT_IN_PROCESS);\n        }\n\n        // 3. 已经助力\n        if (bargainHelpMapper.selectByUserIdAndRecordId(userId, record.getId()) != null) {\n            throw exception(BARGAIN_HELP_CREATE_FAIL_HELP_EXISTS);\n        }\n\n        // 4.1 计算砍价金额\n        Integer reducePrice = calculateReducePrice(activity, record);\n        Assert.isTrue(reducePrice > 0, \"砍价金额必须大于 0 元\");\n        // 4.2 创建助力记录\n        BargainHelpDO help = BargainHelpDO.builder().userId(userId).activityId(activity.getId())\n                .recordId(record.getId()).reducePrice(reducePrice).build();\n        bargainHelpMapper.insert(help);\n\n        // 5. 判断砍价记录是否完成\n        Boolean success = record.getBargainPrice() - reducePrice <= activity.getBargainMinPrice() // 情况一：砍价已经砍到最低价\n                || bargainHelpMapper.selectUserCountMapByRecordId(reqVO.getRecordId()) >= activity.getHelpMaxCount(); // 情况二：砍价助力已经达到上限\n        if (!bargainRecordService.updateBargainRecordBargainPrice(\n                record.getId(), record.getBargainPrice(), reducePrice, success)) {\n            // 多人一起砍价，需要重试\n            throw exception(BARGAIN_HELP_CREATE_FAIL_CONFLICT);\n        }\n        return help;\n    }\n\n    // TODO 芋艿：优化点：实现一个更随机的逻辑，可以按照你自己的业务；\n    private Integer calculateReducePrice(BargainActivityDO activity, BargainRecordDO record) {\n        // 1. 随机金额\n        Integer reducePrice = MathUtil.randomInt(activity.getBargainMinPrice(),\n                activity.getRandomMaxPrice() + 1); // + 1 的原因是，randomInt 默认不包含第二个参数\n        // 2. 校验是否超过砍价上限\n        if (record.getBargainPrice() - reducePrice < activity.getBargainMinPrice()) {\n            reducePrice = record.getBargainPrice() - activity.getBargainMinPrice();\n        }\n        return reducePrice;\n    }\n\n    @Override\n    public Map<Long, Integer> getBargainHelpUserCountMapByActivity(Collection<Long> activityIds) {\n        return bargainHelpMapper.selectUserCountMapByActivityId(activityIds);\n    }\n\n    @Override\n    public Map<Long, Integer> getBargainHelpUserCountMapByRecord(Collection<Long> recordIds) {\n        return bargainHelpMapper.selectUserCountMapByRecordId(recordIds);\n    }\n\n    @Override\n    public Long getBargainHelpCountByActivity(Long activityId, Long userId) {\n        return bargainHelpMapper.selectCountByUserIdAndActivityId(userId, activityId);\n    }\n\n    @Override\n    public PageResult<BargainHelpDO> getBargainHelpPage(BargainHelpPageReqVO pageReqVO) {\n        return bargainHelpMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<BargainHelpDO> getBargainHelpListByRecordId(Long recordId) {\n        return bargainHelpMapper.selectListByRecordId(recordId);\n    }\n\n    @Override\n    public BargainHelpDO getBargainHelp(Long recordId, Long userId) {\n        return bargainHelpMapper.selectByUserIdAndRecordId(userId, recordId);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainRecordService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.bargain;\n\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.api.bargain.dto.BargainValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.recrod.BargainRecordPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 砍价记录 service 接口\n *\n * @author HUIHUI\n */\npublic interface BargainRecordService {\n\n    /**\n     * 【会员】创建砍价记录（参与参加活动）\n     *\n     * @param userId 用户编号\n     * @param reqVO 创建信息\n     * @return 砍价记录编号\n     */\n    Long createBargainRecord(Long userId, AppBargainRecordCreateReqVO reqVO);\n\n    /**\n     * 更新砍价记录的砍价金额\n     *\n     * 如果满足砍价成功的条件，则更新砍价记录的状态为成功\n     *\n     * @param id 砍价记录编号\n     * @param whereBargainPrice 当前的砍价金额\n     * @param reducePrice 减少的砍价金额\n     * @param success 是否砍价成功\n     * @return 是否更新成功。注意，如果并发更新时，会更新失败\n     */\n    Boolean updateBargainRecordBargainPrice(Long id, Integer whereBargainPrice,\n                                            Integer reducePrice, Boolean success);\n\n    /**\n     * 【下单前】校验是否参与砍价活动\n     * <p>\n     * 如果校验失败，则抛出业务异常\n     *\n     * @param userId          用户编号\n     * @param bargainRecordId 砍价活动编号\n     * @param skuId           SKU 编号\n     * @return 砍价信息\n     */\n    BargainValidateJoinRespDTO validateJoinBargain(Long userId, Long bargainRecordId, Long skuId);\n\n    /**\n     * 更新砍价记录的订单编号\n     *\n     * 在砍价成功后，用户发起订单后，会记录该订单编号\n     *\n     * @param id     砍价记录编号\n     * @param orderId 订单编号\n     */\n    void updateBargainRecordOrderId(Long id, Long orderId);\n\n    /**\n     * 获得砍价记录\n     *\n     * @param id 砍价记录编号\n     * @return 砍价记录\n     */\n    BargainRecordDO getBargainRecord(Long id);\n\n    /**\n     * 获得用户在当前砍价活动中的最后一条砍价记录\n     *\n     * @param userId 用户编号\n     * @param activityId 砍价记录编号\n     * @return 砍价记录\n     */\n    BargainRecordDO getLastBargainRecord(Long userId, Long activityId);\n\n    /**\n     * 获得砍价人数 Map\n     *\n     * @param activityIds 活动编号\n     * @param status 砍价记录状态\n     * @return 砍价人数 Map\n     */\n    Map<Long, Integer> getBargainRecordUserCountMap(Collection<Long> activityIds, @Nullable Integer status);\n\n    /**\n     * 获得砍价人数\n     *\n     * @param status 砍价记录状态\n     * @return 砍价人数\n     */\n    Integer getBargainRecordUserCount(Integer status);\n\n    /**\n     * 获得砍价人数\n     *\n     * @param activityId 砍价活动编号\n     * @param status 砍价记录状态\n     * @return 砍价人数\n     */\n    Integer getBargainRecordUserCount(Long activityId, Integer status);\n\n    /**\n     * 【管理员】获得砍价记录分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 砍价记录分页\n     */\n    PageResult<BargainRecordDO> getBargainRecordPage(BargainRecordPageReqVO pageReqVO);\n\n    /**\n     * 【会员】获得砍价记录分页\n     *\n     * @param userId 用户编号\n     * @param pageParam 分页查询\n     * @return 砍价记录分页\n     */\n    PageResult<BargainRecordDO> getBargainRecordPage(Long userId, PageParam pageParam);\n\n    /**\n     * 获得砍价记录列表\n     *\n     * @param status 砍价记录状态\n     * @param count 条数\n     * @return 砍价记录列表\n     */\n    List<BargainRecordDO> getBargainRecordList(Integer status, Integer count);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainRecordServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.bargain;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.Assert;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.api.bargain.dto.BargainValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.recrod.BargainRecordPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.bargain.BargainRecordMapper;\nimport cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Nullable;\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\n\n/**\n * 砍价记录 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class BargainRecordServiceImpl implements BargainRecordService {\n\n    @Resource\n    private BargainActivityService bargainActivityService;\n\n    @Resource\n    private BargainRecordMapper bargainRecordMapper;\n\n    @Override\n    public Long createBargainRecord(Long userId, AppBargainRecordCreateReqVO reqVO) {\n        // 1. 校验砍价活动（包括库存）\n        BargainActivityDO activity = bargainActivityService.validateBargainActivityCanJoin(reqVO.getActivityId());\n\n        // 2.1 校验当前是否已经有参与中的砍价活动\n        if (CollUtil.isNotEmpty(bargainRecordMapper.selectListByUserIdAndActivityIdAndStatus(\n                userId, reqVO.getActivityId(), BargainRecordStatusEnum.IN_PROGRESS.getStatus()))) {\n            throw exception(BARGAIN_RECORD_CREATE_FAIL_EXISTS);\n        }\n        // 2.2 是否超过参与的上限\n        if (bargainRecordMapper.selectCountByUserIdAndActivityIdAndStatus(\n                userId, reqVO.getActivityId(), BargainRecordStatusEnum.SUCCESS.getStatus()) >= activity.getTotalLimitCount()) {\n            throw exception(BARGAIN_RECORD_CREATE_FAIL_LIMIT);\n        }\n\n        // 3. 创建砍价记录\n        BargainRecordDO record = BargainRecordDO.builder().userId(userId)\n                .activityId(reqVO.getActivityId()).spuId(activity.getSpuId()).skuId(activity.getSkuId())\n                .bargainFirstPrice(activity.getBargainFirstPrice()).bargainPrice(activity.getBargainFirstPrice())\n                .status(BargainRecordStatusEnum.IN_PROGRESS.getStatus()).build();\n        bargainRecordMapper.insert(record);\n        return record.getId();\n    }\n\n    @Override\n    public Boolean updateBargainRecordBargainPrice(Long id, Integer whereBargainPrice,\n                                                   Integer reducePrice, Boolean success) {\n        BargainRecordDO updateObj = new BargainRecordDO().setBargainPrice(whereBargainPrice - reducePrice);\n        if (success) {\n            updateObj.setStatus(BargainRecordStatusEnum.SUCCESS.getStatus());\n        }\n        return bargainRecordMapper.updateByIdAndBargainPrice(id, whereBargainPrice, updateObj) > 0;\n    }\n\n    @Override\n    public BargainValidateJoinRespDTO validateJoinBargain(Long userId, Long bargainRecordId, Long skuId) {\n        // 1.1 砍价记录不存在\n        BargainRecordDO record = bargainRecordMapper.selectByIdAndUserId(bargainRecordId, userId);\n        if (record == null) {\n            throw exception(BARGAIN_RECORD_NOT_EXISTS);\n        }\n        // 1.2 砍价记录未在进行中\n        if (ObjUtil.notEqual(record.getStatus(), BargainRecordStatusEnum.SUCCESS.getStatus())) {\n            throw exception(BARGAIN_JOIN_RECORD_NOT_SUCCESS);\n        }\n        // 1.3 砍价记录已经下单\n        if (record.getOrderId() != null) {\n            throw exception(BARGAIN_JOIN_RECORD_ALREADY_ORDER);\n        }\n\n        // 2.1 校验砍价活动（包括库存）\n        BargainActivityDO activity = bargainActivityService.validateBargainActivityCanJoin(record.getActivityId());\n        Assert.isTrue(Objects.equals(skuId, activity.getSkuId()), \"砍价商品不匹配\"); // 防御性校验\n        return new BargainValidateJoinRespDTO().setActivityId(activity.getId()).setName(activity.getName())\n                .setBargainPrice(record.getBargainPrice());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateBargainRecordOrderId(Long id, Long orderId) {\n        // 更新失败，说明已经下单\n        int updateCount = bargainRecordMapper.updateOrderIdById(id, orderId);\n        if (updateCount == 0) {\n            throw exception(BARGAIN_JOIN_RECORD_ALREADY_ORDER);\n        }\n    }\n\n    @Override\n    public BargainRecordDO getBargainRecord(Long id) {\n        return bargainRecordMapper.selectById(id);\n    }\n\n    @Override\n    public BargainRecordDO getLastBargainRecord(Long userId, Long activityId) {\n        return bargainRecordMapper.selectLastByUserIdAndActivityId(userId, activityId);\n    }\n\n    @Override\n    public Map<Long, Integer> getBargainRecordUserCountMap(Collection<Long> activityIds, @Nullable Integer status) {\n        return bargainRecordMapper.selectUserCountByActivityIdsAndStatus(activityIds, status);\n    }\n\n    @Override\n    public Integer getBargainRecordUserCount(Integer status) {\n        return bargainRecordMapper.selectUserCountByStatus(status);\n    }\n\n    @Override\n    public Integer getBargainRecordUserCount(Long activityId, Integer status) {\n        return bargainRecordMapper.selectUserCountByActivityIdAndStatus(activityId, status);\n    }\n\n    @Override\n    public PageResult<BargainRecordDO> getBargainRecordPage(BargainRecordPageReqVO pageReqVO) {\n        return bargainRecordMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public PageResult<BargainRecordDO> getBargainRecordPage(Long userId, PageParam pageParam) {\n        return bargainRecordMapper.selectBargainRecordPage(userId, pageParam);\n    }\n\n    @Override\n    public List<BargainRecordDO> getBargainRecordList(Integer status, Integer count) {\n        return bargainRecordMapper.selectListByStatusAndCount(status, count);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.combination;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * 拼团活动 Service 接口\n *\n * @author HUIHUI\n */\npublic interface CombinationActivityService {\n\n    /**\n     * 创建拼团活动\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createCombinationActivity(@Valid CombinationActivityCreateReqVO createReqVO);\n\n    /**\n     * 更新拼团活动\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCombinationActivity(@Valid CombinationActivityUpdateReqVO updateReqVO);\n\n    /**\n     * 关闭拼团活动\n     *\n     * @param id 拼团活动编号\n     */\n    void closeCombinationActivityById(Long id);\n\n    /**\n     * 删除拼团活动\n     *\n     * @param id 编号\n     */\n    void deleteCombinationActivity(Long id);\n\n    /**\n     * 校验拼团活动是否存在\n     *\n     * @param id 编号\n     * @return 拼团活动\n     */\n    CombinationActivityDO validateCombinationActivityExists(Long id);\n\n    /**\n     * 获得拼团活动\n     *\n     * @param id 编号\n     * @return 拼团活动\n     */\n    CombinationActivityDO getCombinationActivity(Long id);\n\n    /**\n     * 获得拼团活动分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 拼团活动分页\n     */\n    PageResult<CombinationActivityDO> getCombinationActivityPage(CombinationActivityPageReqVO pageReqVO);\n\n    /**\n     * 获得拼团活动商品列表\n     *\n     * @param activityId 拼团活动 id\n     * @return 拼团活动的商品列表\n     */\n    default List<CombinationProductDO> getCombinationProductsByActivityId(Long activityId) {\n        return getCombinationProductListByActivityIds(Collections.singletonList(activityId));\n    }\n\n    /**\n     * 获得拼团活动商品列表\n     *\n     * @param activityIds 拼团活动 ids\n     * @return 拼团活动的商品列表\n     */\n    List<CombinationProductDO> getCombinationProductListByActivityIds(Collection<Long> activityIds);\n\n    /**\n     * 获得拼团活动列表\n     *\n     * @param ids 拼团活动 ids\n     * @return 拼团活动的列表\n     */\n    List<CombinationActivityDO> getCombinationActivityListByIds(Collection<Long> ids);\n\n    /**\n     * 获取正在进行的活动分页数据\n     *\n     * @param pageParam 分页请求\n     * @return 拼团活动分页\n     */\n    PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam);\n\n    /**\n     * 获取指定活动、指定 SKU 编号的商品\n     *\n     * @param activityId 活动编号\n     * @param skuId      SKU 编号\n     * @return 活动商品信息\n     */\n    CombinationProductDO selectByActivityIdAndSkuId(Long activityId, Long skuId);\n\n    /**\n     * 获得 SPU 进行中的拼团活动\n     *\n     * @param spuId SPU 编号数组\n     * @return 拼团活动\n     */\n    CombinationActivityDO getMatchCombinationActivityBySpuId(Long spuId);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.combination;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO;\nimport cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\nimport static java.util.Collections.singletonList;\n\n/**\n * 拼团活动 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class CombinationActivityServiceImpl implements CombinationActivityService {\n\n    @Resource\n    private CombinationActivityMapper combinationActivityMapper;\n    @Resource\n    private CombinationProductMapper combinationProductMapper;\n\n    @Resource\n    private ProductSpuApi productSpuApi;\n    @Resource\n    private ProductSkuApi productSkuApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createCombinationActivity(CombinationActivityCreateReqVO createReqVO) {\n        // 校验商品 SPU 是否存在是否参加的别的活动\n        validateProductConflict(createReqVO.getSpuId(), null);\n        // 校验商品是否存在\n        validateProductExists(createReqVO.getSpuId(), createReqVO.getProducts());\n\n        // 插入拼团活动\n        CombinationActivityDO activity = CombinationActivityConvert.INSTANCE.convert(createReqVO)\n                .setStatus(CommonStatusEnum.ENABLE.getStatus());\n        combinationActivityMapper.insert(activity);\n        // 插入商品\n        List<CombinationProductDO> products = CombinationActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activity);\n        combinationProductMapper.insertBatch(products);\n        return activity.getId();\n    }\n\n    /**\n     * 校验拼团商品参与的活动是否存在冲突\n     *\n     * @param spuId      商品 SPU 编号\n     * @param activityId 拼团活动编号\n     */\n    private void validateProductConflict(Long spuId, Long activityId) {\n        // 查询所有开启的拼团活动\n        List<CombinationActivityDO> activityList = combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        if (activityId != null) { // 时排除自己\n            activityList.removeIf(item -> ObjectUtil.equal(item.getId(), activityId));\n        }\n        // 查找是否有其它活动，选择了该产品\n        List<CombinationActivityDO> matchActivityList = filterList(activityList, activity -> ObjectUtil.equal(activity.getSpuId(), spuId));\n        if (CollUtil.isNotEmpty(matchActivityList)) {\n            throw exception(COMBINATION_ACTIVITY_SPU_CONFLICTS);\n        }\n    }\n\n    /**\n     * 校验拼团商品是否都存在\n     *\n     * @param spuId    商品 SPU 编号\n     * @param products 拼团商品\n     */\n    private void validateProductExists(Long spuId, List<CombinationProductBaseVO> products) {\n        // 1. 校验商品 spu 是否存在\n        ProductSpuRespDTO spu = productSpuApi.getSpu(spuId).getCheckedData();\n        if (spu == null) {\n            throw exception(SPU_NOT_EXISTS);\n        }\n\n        // 2. 校验商品 sku 都存在\n        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(singletonList(spuId)).getCheckedData();\n        Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);\n        products.forEach(product -> {\n            if (!skuMap.containsKey(product.getSkuId())) {\n                throw exception(SKU_NOT_EXISTS);\n            }\n        });\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateCombinationActivity(CombinationActivityUpdateReqVO updateReqVO) {\n        // 校验存在\n        CombinationActivityDO activityDO = validateCombinationActivityExists(updateReqVO.getId());\n        // 校验状态\n        if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {\n            throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE_NOT_UPDATE);\n        }\n        // 校验商品冲突\n        validateProductConflict(updateReqVO.getSpuId(), updateReqVO.getId());\n        // 校验商品是否存在\n        validateProductExists(updateReqVO.getSpuId(), updateReqVO.getProducts());\n\n        // 更新活动\n        CombinationActivityDO updateObj = CombinationActivityConvert.INSTANCE.convert(updateReqVO);\n        combinationActivityMapper.updateById(updateObj);\n        // 更新商品\n        updateCombinationProduct(updateObj, updateReqVO.getProducts());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void closeCombinationActivityById(Long id) {\n        // 校验活动是否存在\n        CombinationActivityDO activity = validateCombinationActivityExists(id);\n        if (CommonStatusEnum.isDisable(activity.getStatus())) {\n            throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE_NOT_UPDATE);\n        }\n\n        // 关闭活动\n        combinationActivityMapper.updateById(new CombinationActivityDO().setId(id)\n                .setStatus(CommonStatusEnum.DISABLE.getStatus()));\n    }\n\n    /**\n     * 更新拼团商品\n     *\n     * @param activity 拼团活动\n     * @param products 该活动的最新商品配置\n     */\n    private void updateCombinationProduct(CombinationActivityDO activity, List<CombinationProductBaseVO> products) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<CombinationProductDO> newList = CombinationActivityConvert.INSTANCE.convertList(products, activity);\n        List<CombinationProductDO> oldList = combinationProductMapper.selectListByActivityIds(CollUtil.newArrayList(activity.getId()));\n        List<List<CombinationProductDO>> diffList = CollectionUtils.diffList(oldList, newList, (oldVal, newVal) -> {\n            boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId());\n            if (same) {\n                newVal.setId(oldVal.getId());\n            }\n            return same;\n        });\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            combinationProductMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            combinationProductMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            combinationProductMapper.deleteByIds(CollectionUtils.convertList(diffList.get(2), CombinationProductDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteCombinationActivity(Long id) {\n        // 校验存在\n        CombinationActivityDO activity = validateCombinationActivityExists(id);\n        // 校验状态\n        if (CommonStatusEnum.isEnable(activity.getStatus())) {\n            throw exception(COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END);\n        }\n\n        // 删除\n        combinationActivityMapper.deleteById(id);\n    }\n\n    @Override\n    public CombinationActivityDO validateCombinationActivityExists(Long id) {\n        CombinationActivityDO activityDO = combinationActivityMapper.selectById(id);\n        if (activityDO == null) {\n            throw exception(COMBINATION_ACTIVITY_NOT_EXISTS);\n        }\n        return activityDO;\n    }\n\n    @Override\n    public CombinationActivityDO getCombinationActivity(Long id) {\n        return validateCombinationActivityExists(id);\n    }\n\n    @Override\n    public PageResult<CombinationActivityDO> getCombinationActivityPage(CombinationActivityPageReqVO pageReqVO) {\n        return combinationActivityMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<CombinationProductDO> getCombinationProductListByActivityIds(Collection<Long> activityIds) {\n        return combinationProductMapper.selectListByActivityIds(activityIds);\n    }\n\n    @Override\n    public List<CombinationActivityDO> getCombinationActivityListByIds(Collection<Long> ids) {\n        return combinationActivityMapper.selectList(CombinationActivityDO::getId, ids);\n    }\n\n    @Override\n    public PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam) {\n        return combinationActivityMapper.selectPage(pageParam, CommonStatusEnum.ENABLE.getStatus());\n    }\n\n    @Override\n    public CombinationProductDO selectByActivityIdAndSkuId(Long activityId, Long skuId) {\n        return combinationProductMapper.selectOne(\n                CombinationProductDO::getActivityId, activityId,\n                CombinationProductDO::getSkuId, skuId);\n    }\n\n    @Override\n    public CombinationActivityDO getMatchCombinationActivityBySpuId(Long spuId) {\n        return combinationActivityMapper.selectBySpuIdAndStatusAndNow(spuId, CommonStatusEnum.ENABLE.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.combination;\n\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 拼团记录 Service 接口\n *\n * @author HUIHUI\n */\npublic interface CombinationRecordService {\n\n    /**\n     * 【下单前】校验是否满足拼团活动条件\n     *\n     * 如果校验失败，则抛出业务异常\n     *\n     * @param userId     用户编号\n     * @param activityId 活动编号\n     * @param headId     团长编号\n     * @param skuId      sku 编号\n     * @param count      数量\n     * @return 拼团信息\n     */\n    KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(Long userId, Long activityId, Long headId,\n                                                                                    Long skuId, Integer count);\n\n    /**\n     * 创建拼团记录\n     *\n     * @param reqDTO 创建信息\n     * @return 团信息\n     */\n    CombinationRecordDO createCombinationRecord(CombinationRecordCreateReqDTO reqDTO);\n\n    /**\n     * 获得拼团记录\n     *\n     * @param userId  用户编号\n     * @param orderId 订单编号\n     * @return 拼团记录\n     */\n    CombinationRecordDO getCombinationRecord(Long userId, Long orderId);\n\n    /**\n     * 【下单前】校验是否满足拼团活动条件\n     *\n     * 如果校验失败，则抛出业务异常\n     *\n     * @param userId     用户编号\n     * @param activityId 活动编号\n     * @param headId     团长编号\n     * @param skuId      sku 编号\n     * @param count      数量\n     * @return 拼团信息\n     */\n    CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count);\n\n    /**\n     * 获取拼团记录数\n     *\n     * @param status       状态-允许为空\n     * @param virtualGroup 是否虚拟成团-允许为空\n     * @param headId       团长编号，允许空。目的 headId 设置为 {@link CombinationRecordDO#HEAD_ID_GROUP} 时，可以设置\n     * @return 记录数\n     */\n    Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup, Long headId);\n\n    /**\n     * 查询用户拼团记录（DISTINCT 去重），也就是说查询会员表中的用户有多少人参与过拼团活动每个人只统计一次\n     *\n     * @return 参加过拼团的用户数\n     */\n    Long getCombinationUserCount();\n\n    /**\n     * 获取最近的 count 条拼团记录\n     *\n     * @param count 限制数量\n     * @return 拼团记录列表\n     */\n    List<CombinationRecordDO> getLatestCombinationRecordList(int count);\n\n    /**\n     * 获得最近 n 条拼团记录（团长发起的）\n     *\n     * @param activityId 拼团活动编号\n     * @param status     状态\n     * @param count      数量\n     * @return 拼团记录列表\n     */\n    List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count);\n\n    /**\n     * 获取指定编号的拼团记录\n     *\n     * @param id 拼团记录编号\n     * @return 拼团记录\n     */\n    CombinationRecordDO getCombinationRecordById(Long id);\n\n    /**\n     * 获取指定团长编号的拼团记录\n     *\n     * @param headId 团长编号\n     * @return 拼团记录列表\n     */\n    List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId);\n\n    /**\n     * 获取拼团记录分页数据\n     *\n     * @param pageVO 分页请求\n     * @return 拼团记录分页数据\n     */\n    PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO);\n\n    /**\n     * 【拼团活动】获得拼团记录数量 Map\n     *\n     * @param activityIds 活动记录编号数组\n     * @param status      拼团状态，允许空\n     * @param headId      团长编号，允许空。目的 headId 设置为 {@link CombinationRecordDO#HEAD_ID_GROUP} 时，可以设置\n     * @return 拼团记录数量 Map\n     */\n    Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds,\n                                                              @Nullable Integer status,\n                                                              @Nullable Long headId);\n\n    /**\n     * 处理过期拼团\n     *\n     * @return key 过期拼团数量, value 虚拟成团数量\n     */\n    KeyValue<Integer, Integer> expireCombinationRecord();\n\n    /**\n     * 获得拼团记录分页数据\n     *\n     * @param userId 用户编号\n     * @param pageReqVO 分页请求\n     * @return 拼团记录分页数据\n     */\n    PageResult<CombinationRecordDO> getCombinationRecordPage(Long userId, AppCombinationRecordPageReqVO pageReqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.combination;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.core.KeyValue;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.json.JsonUtils;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordPageReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;\nimport cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;\nimport cn.iocoder.yudao.module.system.api.social.SocialClientApi;\nimport cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;\nimport cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;\nimport cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Nullable;\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.afterNow;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeNow;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\nimport static cn.iocoder.yudao.module.promotion.enums.MessageTemplateConstants.COMBINATION_SUCCESS;\n\n/**\n * 拼团记录 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Slf4j\n@Validated\npublic class CombinationRecordServiceImpl implements CombinationRecordService {\n\n    @Resource\n    private CombinationActivityService combinationActivityService;\n    @Resource\n    private CombinationRecordMapper combinationRecordMapper;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n    @Resource\n    private ProductSpuApi productSpuApi;\n    @Resource\n    private ProductSkuApi productSkuApi;\n    @Resource\n    @Lazy // 延迟加载，避免循环依赖\n    private TradeOrderApi tradeOrderApi;\n    @Resource\n    public SocialClientApi socialClientApi;\n\n    // TODO @芋艿：在详细预览下；\n    @Override\n    public KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(\n            Long userId, Long activityId, Long headId, Long skuId, Integer count) {\n        // 1. 校验拼团活动是否存在\n        CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(activityId);\n        // 1.1 校验活动是否开启\n        if (ObjUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {\n            throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);\n        }\n        // 1.2. 校验活动开始时间\n        if (afterNow(activity.getStartTime())) {\n            throw exception(COMBINATION_RECORD_FAILED_TIME_NOT_START);\n        }\n        // 1.3 校验是否超出单次限购数量\n        if (count > activity.getSingleLimitCount()) {\n            throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);\n        }\n\n        // 2. 父拼团是否存在,是否已经满了\n        if (headId != null) {\n            // 2.1. 查询进行中的父拼团\n            CombinationRecordDO record = combinationRecordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());\n            if (record == null) {\n                throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);\n            }\n            // 2.2. 校验拼团是否已满\n            if (ObjUtil.equal(record.getUserCount(), record.getUserSize())) {\n                throw exception(COMBINATION_RECORD_USER_FULL);\n            }\n            // 2.3 校验拼团是否过期（有父拼团的时候只校验父拼团的过期时间）\n            if (beforeNow(record.getExpireTime())) {\n                throw exception(COMBINATION_RECORD_FAILED_TIME_END);\n            }\n        } else {\n            // 3. 校验当前活动是否结束(自己是父拼团的时候才校验活动是否结束)\n            if (beforeNow(activity.getEndTime())) {\n                throw exception(COMBINATION_RECORD_FAILED_TIME_END);\n            }\n        }\n\n        // 4.1 校验活动商品是否存在\n        CombinationProductDO product = combinationActivityService.selectByActivityIdAndSkuId(activityId, skuId);\n        if (product == null) {\n            throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);\n        }\n        // 4.2 校验 sku 是否存在\n        ProductSkuRespDTO sku = productSkuApi.getSku(skuId).getCheckedData();\n        if (sku == null) {\n            throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);\n        }\n        // 4.3 校验库存是否充足\n        if (count >= sku.getStock()) {\n            throw exception(COMBINATION_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n\n        // 6.1 校验是否有拼团记录\n        List<CombinationRecordDO> recordList = combinationRecordMapper.selectListByUserIdAndActivityId(userId, activityId);\n        recordList.removeIf(record -> CombinationRecordStatusEnum.isFailed(record.getStatus())); // 取消的订单，不算数\n        if (CollUtil.isEmpty(recordList)) { // 如果为空，说明可以参与，直接返回\n            return new KeyValue<>(activity, product);\n        }\n        // 6.2 校验用户是否有该活动正在进行的拼团\n        CombinationRecordDO inProgressRecord = findFirst(recordList,\n                record -> CombinationRecordStatusEnum.isInProgress(record.getStatus()));\n        if (inProgressRecord != null) {\n            throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED);\n        }\n        // 6.3 校验是否超出总限购数量\n        Integer sumValue = getSumValue(recordList, CombinationRecordDO::getCount, Integer::sum);\n        if (sumValue != null && sumValue + count > activity.getTotalLimitCount()) {\n            throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);\n        }\n        return new KeyValue<>(activity, product);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public CombinationRecordDO createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {\n        // 1. 校验拼团活动\n        KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(reqDTO.getUserId(),\n                reqDTO.getActivityId(), reqDTO.getHeadId(), reqDTO.getSkuId(), reqDTO.getCount());\n\n        // 2. 组合数据创建拼团记录\n        MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId()).getCheckedData();\n        ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId()).getCheckedData();\n        ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId()).getCheckedData();\n        CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO, keyValue.getKey(), user, spu, sku);\n        // 2.1. 如果是团长需要设置 headId 为 CombinationRecordDO#HEAD_ID_GROUP\n        if (record.getHeadId() == null) {\n            record.setStartTime(LocalDateTime.now())\n                    .setExpireTime(LocalDateTime.now().plusHours(keyValue.getKey().getLimitDuration()))\n                    .setHeadId(CombinationRecordDO.HEAD_ID_GROUP);\n        } else {\n            // 2.2.有团长的情况下需要设置开始时间和过期时间为团长的\n            CombinationRecordDO headRecord = combinationRecordMapper.selectByHeadId(record.getHeadId(),\n                    CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); // 查询进行中的父拼团\n            record.setStartTime(headRecord.getStartTime()).setExpireTime(headRecord.getExpireTime());\n        }\n        combinationRecordMapper.insert(record);\n\n        // 3. 更新拼团记录\n        if (ObjUtil.notEqual(CombinationRecordDO.HEAD_ID_GROUP, record.getHeadId())) {\n            updateCombinationRecordWhenCreate(reqDTO.getHeadId(), keyValue.getKey());\n        }\n        return record;\n    }\n\n    /**\n     * 当新增拼团时，更新拼团记录的进展\n     *\n     * @param headId   团长编号\n     * @param activity 活动\n     */\n    private void updateCombinationRecordWhenCreate(Long headId, CombinationActivityDO activity) {\n        // 1. 团长 + 团员\n        List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId);\n        if (CollUtil.isEmpty(records)) {\n            return;\n        }\n        CombinationRecordDO headRecord = combinationRecordMapper.selectById(headId);\n\n        // 2. 批量更新记录\n        List<CombinationRecordDO> updateRecords = new ArrayList<>();\n        records.add(headRecord); // 加入团长，团长也需要更新\n        boolean isFull = records.size() >= activity.getUserSize();\n        LocalDateTime now = LocalDateTime.now();\n        records.forEach(item -> {\n            CombinationRecordDO updateRecord = new CombinationRecordDO();\n            updateRecord.setId(item.getId()).setUserCount(records.size());\n            if (isFull) {\n                updateRecord.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());\n                updateRecord.setEndTime(now);\n            }\n            updateRecords.add(updateRecord);\n        });\n        Boolean updateSuccess = combinationRecordMapper.updateBatch(updateRecords);\n\n        // 3. 拼团成功发送订阅消息\n        if (updateSuccess && isFull) {\n            records.forEach(item -> getSelf().sendCombinationResultMessage(item));\n        }\n    }\n\n    @Async\n    public void sendCombinationResultMessage(CombinationRecordDO record) {\n        // 构建并发送模版消息\n        socialClientApi.sendWxaSubscribeMessage(new SocialWxaSubscribeMessageSendReqDTO()\n                .setUserId(record.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue())\n                .setTemplateTitle(COMBINATION_SUCCESS)\n                .setPage(\"pages/order/detail?id=\" + record.getOrderId()) // 订单详情页\n                .addMessage(\"thing1\", \"商品拼团活动\") // 活动标题\n                .addMessage(\"thing2\", \"恭喜您拼团成功！我们将尽快为您发货。\")).checkError(); // 温馨提示\n    }\n\n    @Override\n    public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) {\n        return combinationRecordMapper.selectByUserIdAndOrderId(userId, orderId);\n    }\n\n    @Override\n    public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId,\n                                                                  Long skuId, Integer count) {\n        KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(userId, activityId,\n                headId, skuId, count);\n        return new CombinationValidateJoinRespDTO().setActivityId(keyValue.getKey().getId())\n                .setName(keyValue.getKey().getName()).setCombinationPrice(keyValue.getValue().getCombinationPrice());\n    }\n\n    @Override\n    public Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup, @Nullable Long headId) {\n        return combinationRecordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup, headId);\n    }\n\n    @Override\n    public Long getCombinationUserCount() {\n        return combinationRecordMapper.selectUserCount();\n    }\n\n    @Override\n    public List<CombinationRecordDO> getLatestCombinationRecordList(int count) {\n        return combinationRecordMapper.selectLatestList(count);\n    }\n\n    @Override\n    public List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count) {\n        return combinationRecordMapper.selectListByActivityIdAndStatusAndHeadId(activityId, status,\n                CombinationRecordDO.HEAD_ID_GROUP, count);\n    }\n\n    @Override\n    public CombinationRecordDO getCombinationRecordById(Long id) {\n        return combinationRecordMapper.selectById(id);\n    }\n\n    @Override\n    public List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId) {\n        return combinationRecordMapper.selectList(CombinationRecordDO::getHeadId, headId);\n    }\n\n    @Override\n    public PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO) {\n        return combinationRecordMapper.selectPage(pageVO);\n    }\n\n    @Override\n    public Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds,\n                                                                     @Nullable Integer status, @Nullable Long headId) {\n        return combinationRecordMapper.selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(activityIds, status, headId);\n    }\n\n    @Override\n    public KeyValue<Integer, Integer> expireCombinationRecord() {\n        // 1. 获取所有正在进行中的过期的父拼团\n        List<CombinationRecordDO> headExpireRecords = combinationRecordMapper.selectListByHeadIdAndStatusAndExpireTimeLt(\n                CombinationRecordDO.HEAD_ID_GROUP, CombinationRecordStatusEnum.IN_PROGRESS.getStatus(), LocalDateTime.now());\n        if (CollUtil.isEmpty(headExpireRecords)) {\n            return new KeyValue<>(0, 0);\n        }\n\n        // 2. 获取拼团活动\n        List<CombinationActivityDO> activities = combinationActivityService.getCombinationActivityListByIds(\n                convertSet(headExpireRecords, CombinationRecordDO::getActivityId));\n        Map<Long, CombinationActivityDO> activityMap = convertMap(activities, CombinationActivityDO::getId);\n\n        // 3. 逐个处理拼团，过期 or 虚拟成团\n        KeyValue<Integer, Integer> keyValue = new KeyValue<>(0, 0); // 统计过期拼团和虚拟成团\n        for (CombinationRecordDO record : headExpireRecords) {\n            try {\n                CombinationActivityDO activity = activityMap.get(record.getActivityId());\n                if (activity == null || !activity.getVirtualGroup()) { // 取不到活动的或者不是虚拟拼团的\n                    // 3.1. 处理过期的拼团\n                    getSelf().handleExpireRecord(record);\n                    keyValue.setKey(keyValue.getKey() + 1);\n                } else {\n                    // 3.2. 处理虚拟成团\n                    getSelf().handleVirtualGroupRecord(record);\n                    keyValue.setValue(keyValue.getValue() + 1);\n                }\n            } catch (Exception ignored) { // 处理异常继续循环\n                log.error(\"[expireCombinationRecord][record({}) 处理异常，请进行处理！record 数据是：{}]\",\n                        record.getId(), JsonUtils.toJsonString(record));\n            }\n        }\n        return keyValue;\n    }\n\n    /**\n     * 处理过期拼团\n     *\n     * @param headRecord 过期拼团团长记录\n     */\n    @Transactional(rollbackFor = Exception.class)\n    public void handleExpireRecord(CombinationRecordDO headRecord) {\n        // 1. 更新拼团记录\n        List<CombinationRecordDO> headAndRecords = updateBatchCombinationRecords(headRecord,\n                CombinationRecordStatusEnum.FAILED);\n        // 2. 订单取消\n        headAndRecords.forEach(item -> tradeOrderApi.cancelPaidOrder(item.getUserId(), item.getOrderId(),\n                TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getType()).checkError());\n    }\n\n    /**\n     * 处理虚拟拼团\n     *\n     * @param headRecord 虚拟成团团长记录\n     */\n    @Transactional(rollbackFor = Exception.class)\n    public void handleVirtualGroupRecord(CombinationRecordDO headRecord) {\n        // 1. 团员补齐\n        combinationRecordMapper.insertBatch(CombinationActivityConvert.INSTANCE.convertVirtualRecordList(headRecord));\n        // 2. 更新拼团记录\n        updateBatchCombinationRecords(headRecord, CombinationRecordStatusEnum.SUCCESS);\n    }\n\n    /**\n     * 更新拼团记录\n     *\n     * @param headRecord 团长记录\n     * @param status     状态-拼团失败 FAILED 成功 SUCCESS\n     * @return 整团记录（包含团长和团成员）\n     */\n    private List<CombinationRecordDO> updateBatchCombinationRecords(CombinationRecordDO headRecord, CombinationRecordStatusEnum status) {\n        // 1. 查询团成员（包含团长）\n        List<CombinationRecordDO> records = combinationRecordMapper.selectListByHeadId(headRecord.getId());\n        records.add(headRecord);// 把团长加进去\n\n        // 2. 批量更新拼团记录 status 和 endTime\n        List<CombinationRecordDO> updateRecords = new ArrayList<>(records.size());\n        LocalDateTime now = LocalDateTime.now();\n        records.forEach(item -> {\n            CombinationRecordDO updateRecord = new CombinationRecordDO().setId(item.getId())\n                    .setStatus(status.getStatus()).setEndTime(now);\n            if (CombinationRecordStatusEnum.isSuccess(status.getStatus())) { // 虚拟成团完事更改状态成功后还需要把参与人数修改为成团需要人数\n                updateRecord.setUserCount(records.size()).setVirtualGroup(Boolean.TRUE); // 标记为虚拟成团\n            }\n            updateRecords.add(updateRecord);\n        });\n        combinationRecordMapper.updateBatch(updateRecords);\n        return records;\n    }\n\n    @Override\n    public PageResult<CombinationRecordDO> getCombinationRecordPage(Long userId, AppCombinationRecordPageReqVO pageReqVO) {\n        return combinationRecordMapper.selectPage(userId, pageReqVO);\n    }\n\n    /**\n     * 获得自身的代理对象，解决 AOP 生效问题\n     *\n     * @return 自己\n     */\n    private CombinationRecordServiceImpl getSelf() {\n        return SpringUtil.getBean(getClass());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.coupon;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\n\nimport java.util.*;\n\n/**\n * 优惠劵 Service 接口\n *\n * @author 芋道源码\n */\npublic interface CouponService {\n\n    /**\n     * 使用优惠劵\n     *\n     * @param id      优惠劵编号\n     * @param userId  用户编号\n     * @param orderId 订单编号\n     */\n    void useCoupon(Long id, Long userId, Long orderId);\n\n    /**\n     * 退还已使用的优惠券\n     *\n     * @param id 优惠券编号\n     */\n    void returnUsedCoupon(Long id);\n\n    /**\n     * 回收优惠劵\n     *\n     * @param id 优惠劵编号\n     */\n    void deleteCoupon(Long id);\n\n    /**\n     * 领取优惠券\n     *\n     * @param templateId 优惠券模板编号\n     * @param userIds    用户编号列表\n     * @param takeType   领取方式\n     * @return key: userId, value: 优惠券编号列表\n     */\n    Map<Long, List<Long>> takeCoupon(Long templateId, Set<Long> userIds, CouponTakeTypeEnum takeType);\n\n    /**\n     * 【管理员】给用户发送优惠券\n     *\n     * @param templateId 优惠券模板编号\n     * @param userIds    用户编号列表\n     * @return key: userId, value: 优惠券编号列表\n     */\n    default Map<Long, List<Long>> takeCouponByAdmin(Long templateId, Set<Long> userIds) {\n        return takeCoupon(templateId, userIds, CouponTakeTypeEnum.ADMIN);\n    }\n\n    /**\n     * 【管理员】给指定用户批量发送优惠券\n     *\n     * @param giveCoupons  key: 优惠劵模版编号，value：对应的数量\n     * @param userId      用户编号\n     * @return 优惠券编号列表\n     */\n    List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId);\n\n    /**\n     * 【管理员】作废指定用户的指定优惠劵\n     *\n     * @param giveCouponIds  赠送的优惠券编号\n     * @param userId         用户编号\n     */\n    void invalidateCouponsByAdmin(List<Long> giveCouponIds, Long userId);\n\n    /**\n     * 【会员】领取优惠券\n     *\n     * @param templateId 优惠券模板编号\n     * @param userId     用户编号\n     */\n    default void takeCouponByUser(Long templateId, Long userId) {\n        takeCoupon(templateId, CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);\n    }\n\n    /**\n     * 【系统】给用户发送新人券\n     *\n     * @param userId 用户编号\n     */\n    void takeCouponByRegister(Long userId);\n\n    /**\n     * 过期优惠券\n     *\n     * @return 过期数量\n     */\n    int expireCoupon();\n\n    // ======================= 查询相关 =======================\n\n    /**\n     * 获得未使用的优惠劵数量\n     *\n     * @param userId 用户编号\n     * @return 未使用的优惠劵数量\n     */\n    Long getUnusedCouponCount(Long userId);\n\n    /**\n     * 获得优惠劵分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 优惠劵分页\n     */\n    PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO);\n\n    /**\n     * 获得用户的优惠劵列表\n     *\n     * @param userId 用户编号\n     * @param status 优惠劵状态\n     * @return 优惠劵列表\n     */\n    List<CouponDO> getCouponList(Long userId, Integer status);\n\n    /**\n     * 统计会员领取优惠券的数量\n     *\n     * @param templateIds 优惠券模板编号列表\n     * @param userId      用户编号\n     * @return 领取优惠券的数量\n     */\n    Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId);\n\n    /**\n     * 获取会员领取指定优惠券的数量\n     *\n     * @param templateId 优惠券模板编号\n     * @param userId     用户编号\n     * @return 领取优惠券的数量\n     */\n    default Integer getTakeCount(Long templateId, Long userId) {\n        Map<Long, Integer> map = getTakeCountMapByTemplateIds(Collections.singleton(templateId), userId);\n        return MapUtil.getInt(map, templateId, 0);\n    }\n\n    /**\n     * 获取用户是否可以领取优惠券\n     *\n     * @param userId    用户编号\n     * @param templates 优惠券列表\n     * @return 是否可以领取\n     */\n    Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates);\n\n    /**\n     * 获得优惠劵\n     *\n     * @param userId 用户编号\n     * @param id     编号\n     * @return 优惠劵\n     */\n    CouponDO getCoupon(Long userId, Long id);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.coupon;\n\nimport cn.hutool.core.collection.CollStreamUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\nimport static java.util.Arrays.asList;\n\n/**\n * 优惠劵 Service 实现类\n *\n * @author 芋道源码\n */\n@Slf4j\n@Service\n@Validated\npublic class CouponServiceImpl implements CouponService {\n\n    @Resource\n    private CouponTemplateService couponTemplateService;\n\n    @Resource\n    private CouponMapper couponMapper;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @Override\n    public void useCoupon(Long id, Long userId, Long orderId) {\n        // 校验状态\n        CouponDO coupon = couponMapper.selectByIdAndUserId(id, userId);\n        if (ObjectUtil.notEqual(coupon.getStatus(), CouponStatusEnum.UNUSED.getStatus())) {\n            throw exception(COUPON_STATUS_NOT_UNUSED);\n        }\n        // 校验有效期；为避免定时器没跑，实际优惠劵已经过期\n        if (!LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime())) {\n            throw exception(COUPON_VALID_TIME_NOT_NOW);\n        }\n\n        // 更新状态\n        int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),\n                new CouponDO().setStatus(CouponStatusEnum.USED.getStatus())\n                        .setUseOrderId(orderId).setUseTime(LocalDateTime.now()));\n        if (updateCount == 0) {\n            throw exception(COUPON_STATUS_NOT_UNUSED);\n        }\n    }\n\n    @Override\n    public void returnUsedCoupon(Long id) {\n        // 校验存在\n        CouponDO coupon = couponMapper.selectById(id);\n        if (coupon == null) {\n            throw exception(COUPON_NOT_EXISTS);\n        }\n        // 校验状态\n        if (ObjectUtil.notEqual(coupon.getStatus(), CouponStatusEnum.USED.getStatus())) {\n            throw exception(COUPON_STATUS_NOT_USED);\n        }\n\n        // 退还\n        Integer status = LocalDateTimeUtils.beforeNow(coupon.getValidEndTime())\n                ? CouponStatusEnum.EXPIRE.getStatus() // 退还时可能已经过期了\n                : CouponStatusEnum.UNUSED.getStatus();\n        int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.USED.getStatus(),\n                new CouponDO().setStatus(status));\n        if (updateCount == 0) {\n            throw exception(COUPON_STATUS_NOT_USED);\n        }\n\n        // TODO 增加优惠券变动记录？\n    }\n\n    @Override\n    @Transactional\n    public void deleteCoupon(Long id) {\n        // 校验存在\n        CouponDO coupon = validateCouponExists(id);\n\n        // 更新优惠劵\n        int deleteCount = couponMapper.delete(id,\n                asList(CouponStatusEnum.UNUSED.getStatus(), CouponStatusEnum.EXPIRE.getStatus()));\n        if (deleteCount == 0) {\n            throw exception(COUPON_DELETE_FAIL_USED);\n        }\n\n        // 减少优惠劵模板的领取数量 -1\n        couponTemplateService.updateCouponTemplateTakeCount(coupon.getTemplateId(), -1);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Map<Long, List<Long>> takeCoupon(Long templateId, Set<Long> userIds, CouponTakeTypeEnum takeType) {\n        CouponTemplateDO template = couponTemplateService.getCouponTemplate(templateId);\n        return takeCoupon(template, userIds, takeType);\n    }\n\n    private Map<Long, List<Long>> takeCoupon(CouponTemplateDO template, Set<Long> userIds, CouponTakeTypeEnum takeType) {\n        // 1. 过滤掉达到领取限制的用户\n        removeTakeLimitUser(userIds, template);\n        // 2. 校验优惠劵是否可以领取\n        validateCouponTemplateCanTake(template, userIds, takeType);\n\n        // 3. 批量保存优惠劵\n        List<CouponDO> couponList = convertList(userIds, userId -> CouponConvert.INSTANCE.convert(template, userId));\n        couponMapper.insertBatch(couponList);\n\n        // 4. 增加优惠劵模板的领取数量\n        couponTemplateService.updateCouponTemplateTakeCount(template.getId(), userIds.size());\n        return convertMultiMap(couponList, CouponDO::getUserId, CouponDO::getId);\n    }\n\n    @Override\n    public List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId) {\n        if (CollUtil.isEmpty(giveCoupons)) {\n            return Collections.emptyList();\n        }\n\n        List<Long> couponIds = new ArrayList<>();\n        // 循环发放\n        for (Map.Entry<Long, Integer> entry : giveCoupons.entrySet()) {\n            try {\n                for (int i = 0; i < entry.getValue(); i++) {\n                    Map<Long, List<Long>> userCouponIdsMap = getSelf().takeCoupon(entry.getKey(), CollUtil.newHashSet(userId),\n                            CouponTakeTypeEnum.ADMIN);\n                    findAndThen(userCouponIdsMap, userId, couponIds::addAll);\n                }\n            } catch (Exception e) {\n                log.error(\"[takeCouponsByAdmin][coupon({}) 优惠券发放失败 userId({})]\", entry, userId, e);\n            }\n        }\n        return couponIds;\n    }\n\n    @Override\n    public void invalidateCouponsByAdmin(List<Long> giveCouponIds, Long userId) {\n        // 循环收回\n        for (Long couponId : giveCouponIds) {\n            // couponId 为空或 0 则跳过\n            if (null == couponId || couponId <= 0) {\n                continue;\n            }\n            try {\n                getSelf().invalidateCoupon(couponId, userId);\n            } catch (Exception e) {\n                log.error(\"[invalidateCouponsByAdmin][couponId({}) 收回优惠券失败]\", couponId, e);\n            }\n        }\n    }\n\n    /**\n     * 【管理员】收回优惠券\n     *\n     * @param couponId 模版编号\n     * @param userId   用户编号\n     */\n    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) // 每次调用开启一个新的事务，避免在一个大的事务里面\n    public void invalidateCoupon(Long couponId, Long userId) {\n        if (couponId == null || couponId <= 0) {\n            return;\n        }\n        // 1.1 校验优惠券\n        CouponDO coupon = couponMapper.selectByIdAndUserId(couponId, userId);\n        if (coupon == null) {\n            throw exception(COUPON_NOT_EXISTS);\n        }\n        // 1.2 校验模板\n        CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(coupon.getTemplateId());\n        if (couponTemplate == null) {\n            throw exception(COUPON_TEMPLATE_NOT_EXISTS);\n        }\n        // 1.3 校验优惠券是否已经使用，如若使用则先不管\n        if (ObjUtil.equal(coupon.getStatus(), CouponStatusEnum.USED.getStatus())) {\n            log.info(\"[invalidateCoupon][coupon({}) 已经使用，无法作废]\", couponId);\n            return;\n        }\n\n        // 2.1 减少优惠劵模板的领取数量\n        couponTemplateService.updateCouponTemplateTakeCount(couponTemplate.getId(), -1);\n        // 2.2 作废优惠劵\n        couponMapper.deleteById(couponId);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void takeCouponByRegister(Long userId) {\n        List<CouponTemplateDO> templates = couponTemplateService.getCouponTemplateListByTakeType(CouponTakeTypeEnum.REGISTER);\n        for (CouponTemplateDO template : templates) {\n            takeCoupon(template, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER);\n        }\n    }\n\n    @Override\n    public int expireCoupon() {\n        // 1. 查询待过期的优惠券\n        List<CouponDO> list = couponMapper.selectListByStatusAndValidEndTimeLe(\n                CouponStatusEnum.UNUSED.getStatus(), LocalDateTime.now());\n        if (CollUtil.isEmpty(list)) {\n            return 0;\n        }\n\n        // 2. 遍历执行\n        int count = 0;\n        for (CouponDO coupon : list) {\n            try {\n                boolean success = expireCoupon(coupon);\n                if (success) {\n                    count++;\n                }\n            } catch (Exception e) {\n                log.error(\"[expireCoupon][coupon({}) 更新为已过期失败]\", coupon.getId(), e);\n            }\n        }\n        return count;\n    }\n\n    /**\n     * 过期单个优惠劵\n     *\n     * @param coupon 优惠劵\n     * @return 是否过期成功\n     */\n    private boolean expireCoupon(CouponDO coupon) {\n        // 更新记录状态\n        int updateRows = couponMapper.updateByIdAndStatus(coupon.getId(), CouponStatusEnum.UNUSED.getStatus(),\n                new CouponDO().setStatus(CouponStatusEnum.EXPIRE.getStatus()));\n        if (updateRows == 0) {\n            log.error(\"[expireCoupon][coupon({}) 更新为已过期失败]\", coupon.getId());\n            return false;\n        }\n        log.info(\"[expireCoupon][coupon({}) 更新为已过期成功]\", coupon.getId());\n        return true;\n    }\n\n    /**\n     * 校验优惠券是否可以领取\n     *\n     * @param couponTemplate 优惠券模板\n     * @param userIds        领取人列表\n     * @param takeType       领取方式\n     */\n    private void validateCouponTemplateCanTake(CouponTemplateDO couponTemplate, Set<Long> userIds, CouponTakeTypeEnum takeType) {\n        // 如果所有用户都领取过，则抛出异常\n        if (CollUtil.isEmpty(userIds)) {\n            throw exception(COUPON_TEMPLATE_USER_ALREADY_TAKE);\n        }\n        // 校验模板\n        if (couponTemplate == null) {\n            throw exception(COUPON_TEMPLATE_NOT_EXISTS);\n        }\n        // 校验领取方式\n        if (ObjUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) {\n            throw exception(COUPON_TEMPLATE_CANNOT_TAKE);\n        }\n        // 校验剩余发放数量是否充足（仅在 CouponTakeTypeEnum.USER 用户领取时）\n        // 关联案例：https://t.zsxq.com/mElGQ、https://t.zsxq.com/6pLzr\n        if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeType())\n                && !couponTemplateService.isTotalCountUnlimited(couponTemplate.getTotalCount()) // 校验不限制总发放数量\n                && couponTemplate.getTakeCount() > couponTemplate.getTotalCount()) { // 已领取数量 > 总发放数量\n            throw exception(COUPON_TEMPLATE_NOT_ENOUGH);\n        }\n        // 校验\"固定日期\"的有效期类型是否过期\n        if (CouponTemplateValidityTypeEnum.DATE.getType().equals(couponTemplate.getValidityType())) {\n            if (LocalDateTimeUtils.beforeNow(couponTemplate.getValidEndTime())) {\n                throw exception(COUPON_TEMPLATE_EXPIRED);\n            }\n        }\n    }\n\n    /**\n     * 过滤掉达到领取上线的用户\n     *\n     * @param userIds        用户编号数组\n     * @param couponTemplate 优惠劵模版\n     */\n    private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {\n        if (couponTemplateService.isTakeLimitCountUnlimited(couponTemplate.getTakeLimitCount())) {\n            return;\n        }\n        // 查询已领过券的用户\n        List<CouponDO> alreadyTakeCoupons = couponMapper.selectListByTemplateIdAndUserId(couponTemplate.getId(), userIds);\n        if (CollUtil.isEmpty(alreadyTakeCoupons)) {\n            return;\n        }\n        // 移除达到领取限制的用户\n        Map<Long, Integer> userTakeCountMap = CollStreamUtil.groupBy(alreadyTakeCoupons, CouponDO::getUserId, Collectors.summingInt(c -> 1));\n        userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= couponTemplate.getTakeLimitCount());\n    }\n\n    //======================= 查询相关 =======================\n\n    @Override\n    public Long getUnusedCouponCount(Long userId) {\n        return couponMapper.selectCountByUserIdAndStatus(userId, CouponStatusEnum.UNUSED.getStatus());\n    }\n\n    @Override\n    public PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO) {\n        // 获得用户编号\n        if (StrUtil.isNotEmpty(pageReqVO.getNickname())) {\n            List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(pageReqVO.getNickname()).getCheckedData();\n            if (CollUtil.isEmpty(users)) {\n                return PageResult.empty();\n            }\n            pageReqVO.setUserIds(convertSet(users, MemberUserRespDTO::getId));\n        }\n        // 分页查询\n        return couponMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<CouponDO> getCouponList(Long userId, Integer status) {\n        return couponMapper.selectListByUserIdAndStatus(userId, status);\n    }\n\n    @Override\n    public Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) {\n        if (CollUtil.isEmpty(templateIds)) {\n            return Collections.emptyMap();\n        }\n        return couponMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);\n    }\n\n    @Override\n    public Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates) {\n        // 1. 未登录时，都显示可以领取\n        Map<Long, Boolean> userCanTakeMap = convertMap(templates, CouponTemplateDO::getId, templateId -> true);\n        if (userId == null) {\n            return userCanTakeMap;\n        }\n\n        // 2.1 过滤领取数量无限制的\n        Set<Long> templateIds = convertSet(templates, CouponTemplateDO::getId,\n                template -> !couponTemplateService.isTakeLimitCountUnlimited(template.getTakeLimitCount()));\n        // 2.2 检查用户领取的数量是否超过限制\n        if (CollUtil.isNotEmpty(templateIds)) {\n            Map<Long, Integer> couponTakeCountMap = this.getTakeCountMapByTemplateIds(templateIds, userId);\n            for (CouponTemplateDO template : templates) {\n                Integer takeCount = couponTakeCountMap.get(template.getId());\n                userCanTakeMap.put(template.getId(), takeCount == null || takeCount < template.getTakeLimitCount());\n            }\n        }\n        return userCanTakeMap;\n    }\n\n    @Override\n    public CouponDO getCoupon(Long userId, Long id) {\n        return couponMapper.selectByIdAndUserId(id, userId);\n    }\n\n    private CouponDO validateCouponExists(Long id) {\n        CouponDO coupon = couponMapper.selectById(id);\n        if (coupon == null) {\n            throw exception(COUPON_NOT_EXISTS);\n        }\n        return coupon;\n    }\n\n    /**\n     * 获得自身的代理对象，解决 AOP 生效问题\n     *\n     * @return 自己\n     */\n    private CouponServiceImpl getSelf() {\n        return SpringUtil.getBean(getClass());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.coupon;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 优惠劵模板 Service 接口\n *\n * @author 芋道源码\n */\npublic interface CouponTemplateService {\n\n    /**\n     * 判断是否不限制每人领取数量\n     *\n     * @param takeLimitCount 每人限领个数\n     * @return 是否不限制\n     */\n    boolean isTakeLimitCountUnlimited(Integer takeLimitCount);\n\n    /**\n     * 判断是否不限制总发放数量\n     *\n     * @param totalCount 发放数量\n     * @return 是否不限制\n     */\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    boolean isTotalCountUnlimited(Integer totalCount);\n\n    /**\n     * 创建优惠劵模板\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createCouponTemplate(@Valid CouponTemplateCreateReqVO createReqVO);\n\n    /**\n     * 更新优惠劵模板\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateCouponTemplate(@Valid CouponTemplateUpdateReqVO updateReqVO);\n\n    /**\n     * 更新优惠劵模板的状态\n     *\n     * @param id     编号\n     * @param status 状态\n     */\n    void updateCouponTemplateStatus(Long id, Integer status);\n\n    /**\n     * 删除优惠劵模板\n     *\n     * @param id 编号\n     */\n    void deleteCouponTemplate(Long id);\n\n    /**\n     * 获得优惠劵模板\n     *\n     * @param id 编号\n     * @return 优惠劵模板\n     */\n    CouponTemplateDO getCouponTemplate(Long id);\n\n    /**\n     * 获得优惠劵模板分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 优惠劵模板分页\n     */\n    PageResult<CouponTemplateDO> getCouponTemplatePage(CouponTemplatePageReqVO pageReqVO);\n\n    /**\n     * 更新优惠劵模板的领取数量\n     *\n     * @param id        优惠劵模板编号\n     * @param incrCount 增加数量\n     */\n    void updateCouponTemplateTakeCount(Long id, int incrCount);\n\n    /**\n     * 获得指定领取方式的优惠券模板\n     *\n     * @param takeType 领取方式\n     * @return 优惠券模板列表\n     */\n    List<CouponTemplateDO> getCouponTemplateListByTakeType(CouponTakeTypeEnum takeType);\n\n    /**\n     * 获得优惠券模板列表\n     *\n     * @param canTakeTypes      可领取的类型列表\n     * @param productScope      商品使用范围类型\n     * @param productScopeValue 商品使用范围编号\n     * @param count             查询数量\n     * @return 优惠券模板列表\n     */\n    List<CouponTemplateDO> getCouponTemplateList(List<Integer> canTakeTypes, Integer productScope,\n                                                 Long productScopeValue, Integer count);\n\n    /**\n     * 获得优惠券模版列表\n     *\n     * @param ids 优惠券模版编号\n     * @return 优惠券模版列表\n     */\n    List<CouponTemplateDO> getCouponTemplateList(Collection<Long> ids);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.coupon;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.product.api.category.ProductCategoryApi;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\n\n/**\n * 优惠劵模板 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class CouponTemplateServiceImpl implements CouponTemplateService {\n\n    @Resource\n    private CouponTemplateMapper couponTemplateMapper;\n\n    @Resource\n    private ProductCategoryApi productCategoryApi;\n    @Resource\n    private ProductSpuApi productSpuApi;\n\n    @Override\n    public boolean isTakeLimitCountUnlimited(Integer takeLimitCount) {\n        return CouponTemplateDO.TAKE_LIMIT_COUNT_MAX.equals(takeLimitCount);\n    }\n\n    @Override\n    public boolean isTotalCountUnlimited(Integer totalCount) {\n        return CouponTemplateDO.TOTAL_COUNT_MAX.equals(totalCount);\n    }\n\n    @Override\n    public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) {\n        // 校验商品范围\n        validateProductScope(createReqVO.getProductScope(), createReqVO.getProductScopeValues());\n        // 插入\n        CouponTemplateDO couponTemplate = CouponTemplateConvert.INSTANCE.convert(createReqVO)\n                .setStatus(CommonStatusEnum.ENABLE.getStatus());\n        couponTemplateMapper.insert(couponTemplate);\n        // 返回\n        return couponTemplate.getId();\n    }\n\n    @Override\n    public void updateCouponTemplate(CouponTemplateUpdateReqVO updateReqVO) {\n        // 校验存在\n        CouponTemplateDO couponTemplate = validateCouponTemplateExists(updateReqVO.getId());\n        // 校验发放数量不能过小（仅在 CouponTakeTypeEnum.USER 用户领取时）\n        if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeType())\n                && !isTotalCountUnlimited(updateReqVO.getTotalCount()) // 非不限制总发放数量\n                && updateReqVO.getTotalCount() < couponTemplate.getTakeCount()) {\n            throw exception(COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL, couponTemplate.getTakeCount());\n        }\n        // 校验商品范围\n        validateProductScope(updateReqVO.getProductScope(), updateReqVO.getProductScopeValues());\n\n        // 更新\n        CouponTemplateDO updateObj = CouponTemplateConvert.INSTANCE.convert(updateReqVO);\n        couponTemplateMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void updateCouponTemplateStatus(Long id, Integer status) {\n        // 校验存在\n        validateCouponTemplateExists(id);\n        // 更新\n        couponTemplateMapper.updateById(new CouponTemplateDO().setId(id).setStatus(status));\n    }\n\n    @Override\n    public void deleteCouponTemplate(Long id) {\n        // 校验存在\n        validateCouponTemplateExists(id);\n        // 删除\n        couponTemplateMapper.deleteById(id);\n    }\n\n    private CouponTemplateDO validateCouponTemplateExists(Long id) {\n        CouponTemplateDO couponTemplate = couponTemplateMapper.selectById(id);\n        if (couponTemplate == null) {\n            throw exception(COUPON_TEMPLATE_NOT_EXISTS);\n        }\n        return couponTemplate;\n    }\n\n    private void validateProductScope(Integer productScope, List<Long> productScopeValues) {\n        if (Objects.equals(PromotionProductScopeEnum.SPU.getScope(), productScope)) {\n            productSpuApi.validateSpuList(productScopeValues).checkError();\n        } else if (Objects.equals(PromotionProductScopeEnum.CATEGORY.getScope(), productScope)) {\n            productCategoryApi.validateCategoryList(productScopeValues).checkError();\n        }\n    }\n\n    @Override\n    public CouponTemplateDO getCouponTemplate(Long id) {\n        return couponTemplateMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<CouponTemplateDO> getCouponTemplatePage(CouponTemplatePageReqVO pageReqVO) {\n        return couponTemplateMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public void updateCouponTemplateTakeCount(Long id, int incrCount) {\n        int updateCount = couponTemplateMapper.updateTakeCount(id, incrCount);\n        if (updateCount == 0) {\n            throw exception(COUPON_TEMPLATE_NOT_ENOUGH);\n        }\n    }\n\n    @Override\n    public List<CouponTemplateDO> getCouponTemplateListByTakeType(CouponTakeTypeEnum takeType) {\n        return couponTemplateMapper.selectListByTakeType(takeType.getType());\n    }\n\n    @Override\n    public List<CouponTemplateDO> getCouponTemplateList(List<Integer> canTakeTypes, Integer productScope,\n                                                        Long productScopeValue, Integer count) {\n        return couponTemplateMapper.selectList(canTakeTypes, productScope, productScopeValue, count);\n    }\n\n    @Override\n    public List<CouponTemplateDO> getCouponTemplateList(Collection<Long> ids) {\n        return couponTemplateMapper.selectByIds(ids);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.discount;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 限时折扣 Service 接口\n *\n * @author 芋道源码\n */\npublic interface DiscountActivityService {\n\n    /**\n     * 基于指定 SKU 编号数组，获得匹配的限时折扣商品\n     *\n     * 注意，匹配的条件，仅仅是日期符合，并且处于开启状态\n     *\n     * @param skuIds SKU 编号数组\n     * @return 匹配的限时折扣商品\n     */\n    List<DiscountProductDO> getMatchDiscountProductListBySkuIds(Collection<Long> skuIds);\n\n    /**\n     * 创建限时折扣活动\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDiscountActivity(@Valid DiscountActivityCreateReqVO createReqVO);\n\n    /**\n     * 更新限时折扣活动\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDiscountActivity(@Valid DiscountActivityUpdateReqVO updateReqVO);\n\n    /**\n     * 关闭限时折扣活动\n     *\n     * @param id 编号\n     */\n    void closeDiscountActivity(Long id);\n\n    /**\n     * 删除限时折扣活动\n     *\n     * @param id 编号\n     */\n    void deleteDiscountActivity(Long id);\n\n    /**\n     * 获得限时折扣活动\n     *\n     * @param id 编号\n     * @return 限时折扣活动\n     */\n    DiscountActivityDO getDiscountActivity(Long id);\n\n    /**\n     * 获得限时折扣活动分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 限时折扣活动分页\n     */\n    PageResult<DiscountActivityDO> getDiscountActivityPage(DiscountActivityPageReqVO pageReqVO);\n\n    /**\n     * 获得活动编号，对应对应的商品列表\n     *\n     * @param activityId 活动编号\n     * @return 活动的商品列表\n     */\n    List<DiscountProductDO> getDiscountProductsByActivityId(Long activityId);\n\n    /**\n     * 获得活动编号，对应对应的商品列表\n     *\n     * @param activityIds 活动编号\n     * @return 活动的商品列表\n     */\n    List<DiscountProductDO> getDiscountProductsByActivityId(Collection<Long> activityIds);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.discount;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountActivityMapper;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.hutool.core.collection.CollUtil.intersectionDistinct;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\n\n/**\n * 限时折扣 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class DiscountActivityServiceImpl implements DiscountActivityService {\n\n    @Resource\n    private DiscountActivityMapper discountActivityMapper;\n    @Resource\n    private DiscountProductMapper discountProductMapper;\n\n    @Resource\n    private ProductSkuApi productSkuApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createDiscountActivity(DiscountActivityCreateReqVO createReqVO) {\n        // 校验商品是否冲突\n        validateDiscountActivityProductConflicts(null, createReqVO.getProducts());\n        // 校验商品是否存在\n        validateProductExists(createReqVO.getProducts());\n\n        // 插入活动\n        DiscountActivityDO discountActivity = DiscountActivityConvert.INSTANCE.convert(createReqVO)\n                .setStatus(CommonStatusEnum.ENABLE.getStatus());\n        discountActivityMapper.insert(discountActivity);\n        // 插入商品\n        List<DiscountProductDO> discountProducts = BeanUtils.toBean(createReqVO.getProducts(), DiscountProductDO.class,\n                product -> product.setActivityId(discountActivity.getId())\n                        .setActivityName(discountActivity.getName()).setActivityStatus(discountActivity.getStatus())\n                        .setActivityStartTime(createReqVO.getStartTime()).setActivityEndTime(createReqVO.getEndTime()));\n        discountProductMapper.insertBatch(discountProducts);\n        // 返回\n        return discountActivity.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateDiscountActivity(DiscountActivityUpdateReqVO updateReqVO) {\n        // 校验存在\n        DiscountActivityDO discountActivity = validateDiscountActivityExists(updateReqVO.getId());\n        if (discountActivity.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { // 已关闭的活动，不能修改噢\n            throw exception(DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);\n        }\n        // 校验商品是否冲突\n        validateDiscountActivityProductConflicts(updateReqVO.getId(), updateReqVO.getProducts());\n        // 校验商品是否存在\n        validateProductExists(updateReqVO.getProducts());\n\n        // 更新活动\n        DiscountActivityDO updateObj = DiscountActivityConvert.INSTANCE.convert(updateReqVO);\n        discountActivityMapper.updateById(updateObj);\n        // 更新商品\n        updateDiscountProduct(updateObj, updateReqVO.getProducts());\n    }\n\n    private void updateDiscountProduct(DiscountActivityDO activity, List<DiscountActivityCreateReqVO.Product> products) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<DiscountProductDO> newList = BeanUtils.toBean(products, DiscountProductDO.class,\n                product -> product.setActivityId(activity.getId())\n                        .setActivityName(activity.getName()).setActivityStatus(activity.getStatus())\n                        .setActivityStartTime(activity.getStartTime()).setActivityEndTime(activity.getEndTime()));\n        List<DiscountProductDO> oldList = discountProductMapper.selectListByActivityId(activity.getId());\n        List<List<DiscountProductDO>> diffList = CollectionUtils.diffList(oldList, newList, (oldVal, newVal) -> {\n            boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId());\n            if (same) {\n                newVal.setId(oldVal.getId());\n            }\n            return same;\n        });\n\n        // 第二步，批量添加、修改、删除\n        if (CollUtil.isNotEmpty(diffList.get(0))) {\n            discountProductMapper.insertBatch(diffList.get(0));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(1))) {\n            discountProductMapper.updateBatch(diffList.get(1));\n        }\n        if (CollUtil.isNotEmpty(diffList.get(2))) {\n            discountProductMapper.deleteByIds(convertList(diffList.get(2), DiscountProductDO::getId));\n        }\n    }\n\n    /**\n     * 校验商品是否冲突\n     *\n     * @param id       编号\n     * @param products 商品列表\n     */\n    private void validateDiscountActivityProductConflicts(Long id, List<DiscountActivityBaseVO.Product> products) {\n        // 1.1 查询所有开启的折扣活动\n        List<DiscountActivityDO> activityList = discountActivityMapper.selectList(DiscountActivityDO::getStatus,\n                CommonStatusEnum.ENABLE.getStatus());\n        if (id != null) { // 时排除自己\n            activityList.removeIf(item -> ObjectUtil.equal(item.getId(), id));\n        }\n        // 1.2 查询活动下的所有商品\n        List<DiscountProductDO> productList = discountProductMapper.selectListByActivityId(\n                convertList(activityList, DiscountActivityDO::getId));\n        Map<Long, List<DiscountProductDO>> productListMap = convertMultiMap(productList, DiscountProductDO::getActivityId);\n\n        // 2. 校验商品是否冲突\n        activityList.forEach(item -> {\n            findAndThen(productListMap, item.getId(), discountProducts -> {\n                if (!intersectionDistinct(convertList(discountProducts, DiscountProductDO::getSpuId),\n                        convertList(products, DiscountActivityBaseVO.Product::getSpuId)).isEmpty()) {\n                    throw exception(DISCOUNT_ACTIVITY_SPU_CONFLICTS, item.getName());\n                }\n            });\n        });\n    }\n\n    /**\n     * 校验活动商品是否都存在\n     *\n     * @param products 活动商品\n     */\n    private void validateProductExists(List<DiscountActivityBaseVO.Product> products) {\n        // 1.获得商品所有的 sku\n        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(\n                convertList(products, DiscountActivityBaseVO.Product::getSpuId)).getCheckedData();\n        Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);\n        // 2. 校验商品 sku 都存在\n        products.forEach(product -> {\n            if (!skuMap.containsKey(product.getSkuId())) {\n                throw exception(SKU_NOT_EXISTS);\n            }\n        });\n    }\n\n    @Override\n    public void closeDiscountActivity(Long id) {\n        // 校验存在\n        DiscountActivityDO activity = validateDiscountActivityExists(id);\n        if (activity.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { // 已关闭的活动，不能关闭噢\n            throw exception(DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);\n        }\n\n        // 更新活动状态\n        discountActivityMapper.updateById(new DiscountActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus()));\n        // 更新活动商品状态\n        discountProductMapper.updateByActivityId(new DiscountProductDO().setActivityId(id).setActivityStatus(\n                CommonStatusEnum.DISABLE.getStatus()));\n    }\n\n    @Override\n    public void deleteDiscountActivity(Long id) {\n        // 校验存在\n        DiscountActivityDO activity = validateDiscountActivityExists(id);\n        if (CommonStatusEnum.isEnable(activity.getStatus())) { // 未关闭的活动，不能删除噢\n            throw exception(DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED);\n        }\n\n        // 删除活动\n        discountActivityMapper.deleteById(id);\n        // 删除活动商品\n        discountProductMapper.deleteByActivityId(id);\n    }\n\n    private DiscountActivityDO validateDiscountActivityExists(Long id) {\n        DiscountActivityDO discountActivity = discountActivityMapper.selectById(id);\n        if (discountActivity == null) {\n            throw exception(DISCOUNT_ACTIVITY_NOT_EXISTS);\n        }\n        return discountActivity;\n    }\n\n    @Override\n    public DiscountActivityDO getDiscountActivity(Long id) {\n        return discountActivityMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<DiscountActivityDO> getDiscountActivityPage(DiscountActivityPageReqVO pageReqVO) {\n        return discountActivityMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<DiscountProductDO> getDiscountProductsByActivityId(Long activityId) {\n        return discountProductMapper.selectListByActivityId(activityId);\n    }\n\n    @Override\n    public List<DiscountProductDO> getDiscountProductsByActivityId(Collection<Long> activityIds) {\n        if (CollUtil.isEmpty(activityIds)) {\n            return CollUtil.newArrayList();\n        }\n        return discountProductMapper.selectList(DiscountProductDO::getActivityId, activityIds);\n    }\n\n    @Override\n    public List<DiscountProductDO> getMatchDiscountProductListBySkuIds(Collection<Long> skuIds) {\n        if (CollUtil.isEmpty(skuIds)) {\n            return CollUtil.newArrayList();\n        }\n        return discountProductMapper.selectListBySkuIdsAndStatusAndNow(skuIds, CommonStatusEnum.ENABLE.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePropertyUpdateRequestVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 装修页面 Service 接口\n *\n * @author owen\n */\npublic interface DiyPageService {\n\n    /**\n     * 创建装修页面\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDiyPage(@Valid DiyPageCreateReqVO createReqVO);\n\n    /**\n     * 更新装修页面\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDiyPage(@Valid DiyPageUpdateReqVO updateReqVO);\n\n    /**\n     * 删除装修页面\n     *\n     * @param id 编号\n     */\n    void deleteDiyPage(Long id);\n\n    /**\n     * 获得装修页面\n     *\n     * @param id 编号\n     * @return 装修页面\n     */\n    DiyPageDO getDiyPage(Long id);\n\n    /**\n     * 获得装修页面列表\n     *\n     * @param ids 编号\n     * @return 装修页面列表\n     */\n    List<DiyPageDO> getDiyPageList(Collection<Long> ids);\n\n    /**\n     * 获得装修页面分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 装修页面分页\n     */\n    PageResult<DiyPageDO> getDiyPagePage(DiyPagePageReqVO pageReqVO);\n\n    /**\n     * 更新装修页面属性\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDiyPageProperty(DiyPagePropertyUpdateRequestVO updateReqVO);\n\n    /**\n     * 获得模板所属的页面列表\n     *\n     * @param templateId 模板编号\n     * @return 装修页面列表\n     */\n    List<DiyPageDO> getDiyPageByTemplateId(Long templateId);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.diy;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.collection.ListUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePropertyUpdateRequestVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.diy.DiyPageConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyPageMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_PAGE_NAME_USED;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_PAGE_NOT_EXISTS;\n\n/**\n * 装修页面 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class DiyPageServiceImpl implements DiyPageService {\n\n    @Resource\n    private DiyPageMapper diyPageMapper;\n\n    @Override\n    public Long createDiyPage(DiyPageCreateReqVO createReqVO) {\n        // 校验名称唯一\n        validateNameUnique(null, createReqVO.getTemplateId(), createReqVO.getName());\n        // 插入\n        DiyPageDO diyPage = DiyPageConvert.INSTANCE.convert(createReqVO);\n        diyPage.setProperty(\"{}\");\n        diyPageMapper.insert(diyPage);\n        return diyPage.getId();\n    }\n\n    @Override\n    public void updateDiyPage(DiyPageUpdateReqVO updateReqVO) {\n        // 校验存在\n        validateDiyPageExists(updateReqVO.getId());\n        // 校验名称唯一\n        validateNameUnique(updateReqVO.getId(), updateReqVO.getTemplateId(), updateReqVO.getName());\n        // 更新\n        DiyPageDO updateObj = DiyPageConvert.INSTANCE.convert(updateReqVO);\n        diyPageMapper.updateById(updateObj);\n    }\n\n    /**\n     * 校验 Page 页面，在一个 template 模版下的名字是唯一的\n     *\n     * @param id Page 编号\n     * @param templateId 模版编号\n     * @param name Page 名字\n     */\n    void validateNameUnique(Long id, Long templateId, String name) {\n        if (templateId != null || StrUtil.isBlank(name)) {\n            return;\n        }\n        DiyPageDO page = diyPageMapper.selectByNameAndTemplateIdIsNull(name);\n        if (page == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的页面\n        if (id == null) {\n            throw exception(DIY_PAGE_NAME_USED, name);\n        }\n        if (!page.getId().equals(id)) {\n            throw exception(DIY_PAGE_NAME_USED, name);\n        }\n    }\n\n    @Override\n    public void deleteDiyPage(Long id) {\n        // 校验存在\n        validateDiyPageExists(id);\n        // 删除\n        diyPageMapper.deleteById(id);\n    }\n\n    private void validateDiyPageExists(Long id) {\n        if (diyPageMapper.selectById(id) == null) {\n            throw exception(DIY_PAGE_NOT_EXISTS);\n        }\n    }\n\n    @Override\n    public DiyPageDO getDiyPage(Long id) {\n        return diyPageMapper.selectById(id);\n    }\n\n    @Override\n    public List<DiyPageDO> getDiyPageList(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return ListUtil.empty();\n        }\n        return diyPageMapper.selectByIds(ids);\n    }\n\n    @Override\n    public PageResult<DiyPageDO> getDiyPagePage(DiyPagePageReqVO pageReqVO) {\n        return diyPageMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<DiyPageDO> getDiyPageByTemplateId(Long templateId) {\n        return diyPageMapper.selectListByTemplateId(templateId);\n    }\n\n    @Override\n    public void updateDiyPageProperty(DiyPagePropertyUpdateRequestVO updateReqVO) {\n        // 校验存在\n        validateDiyPageExists(updateReqVO.getId());\n        // 更新\n        DiyPageDO updateObj = DiyPageConvert.INSTANCE.convert(updateReqVO);\n        diyPageMapper.updateById(updateObj);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.diy;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePropertyUpdateRequestVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;\n\nimport javax.validation.Valid;\n\n/**\n * 装修模板 Service 接口\n *\n * @author owen\n */\npublic interface DiyTemplateService {\n\n    /**\n     * 创建装修模板\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createDiyTemplate(@Valid DiyTemplateCreateReqVO createReqVO);\n\n    /**\n     * 更新装修模板\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDiyTemplate(@Valid DiyTemplateUpdateReqVO updateReqVO);\n\n    /**\n     * 删除装修模板\n     *\n     * @param id 编号\n     */\n    void deleteDiyTemplate(Long id);\n\n    /**\n     * 获得装修模板\n     *\n     * @param id 编号\n     * @return 装修模板\n     */\n    DiyTemplateDO getDiyTemplate(Long id);\n\n    /**\n     * 获得装修模板分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 装修模板分页\n     */\n    PageResult<DiyTemplateDO> getDiyTemplatePage(DiyTemplatePageReqVO pageReqVO);\n\n    /**\n     * 使用装修模板\n     *\n     * @param id 编号\n     */\n    void useDiyTemplate(Long id);\n\n    /**\n     * 更新装修模板属性\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateDiyTemplateProperty(DiyTemplatePropertyUpdateRequestVO updateReqVO);\n\n    /**\n     * 获取使用中的装修模板\n     *\n     * @return 装修模板\n     */\n    DiyTemplateDO getUsedDiyTemplate();\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.diy;\n\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePropertyUpdateRequestVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.diy.DiyPageConvert;\nimport cn.iocoder.yudao.module.promotion.convert.diy.DiyTemplateConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyTemplateMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\n\n/**\n * 装修模板 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class DiyTemplateServiceImpl implements DiyTemplateService {\n\n    @Resource\n    private DiyTemplateMapper diyTemplateMapper;\n\n    @Resource\n    private DiyPageService diyPageService;\n\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public Long createDiyTemplate(DiyTemplateCreateReqVO createReqVO) {\n        // 校验名称唯一\n        validateNameUnique(null, createReqVO.getName());\n        // 插入\n        DiyTemplateDO diyTemplate = DiyTemplateConvert.INSTANCE.convert(createReqVO);\n        diyTemplate.setProperty(\"{}\");\n        diyTemplateMapper.insert(diyTemplate);\n        // 创建默认页面\n        createDefaultPage(diyTemplate);\n        // 返回\n        return diyTemplate.getId();\n    }\n\n    /**\n     * 创建模板下面的默认页面\n     * 默认创建两个页面：首页、我的\n     *\n     * @param diyTemplate 模板对象\n     */\n    private void createDefaultPage(DiyTemplateDO diyTemplate) {\n        String remark = String.format(\"模板【%s】自动创建\", diyTemplate.getName());\n        diyPageService.createDiyPage(DiyPageConvert.INSTANCE.convertCreateVo(diyTemplate.getId(), \"首页\", remark));\n        diyPageService.createDiyPage(DiyPageConvert.INSTANCE.convertCreateVo(diyTemplate.getId(), \"我的\", remark));\n    }\n\n    @Override\n    public void updateDiyTemplate(DiyTemplateUpdateReqVO updateReqVO) {\n        // 校验存在\n        validateDiyTemplateExists(updateReqVO.getId());\n        // 校验名称唯一\n        validateNameUnique(updateReqVO.getId(), updateReqVO.getName());\n        // 更新\n        DiyTemplateDO updateObj = DiyTemplateConvert.INSTANCE.convert(updateReqVO);\n        diyTemplateMapper.updateById(updateObj);\n    }\n\n    void validateNameUnique(Long id, String name) {\n        if (StrUtil.isBlank(name)) {\n            return;\n        }\n        DiyTemplateDO template = diyTemplateMapper.selectByName(name);\n        if (template == null) {\n            return;\n        }\n        // 如果 id 为空，说明不用比较是否为相同 id 的模板\n        if (id == null) {\n            throw exception(DIY_TEMPLATE_NAME_USED, name);\n        }\n        if (!template.getId().equals(id)) {\n            throw exception(DIY_TEMPLATE_NAME_USED, name);\n        }\n    }\n\n    @Override\n    public void deleteDiyTemplate(Long id) {\n        // 校验存在\n        DiyTemplateDO diyTemplateDO = validateDiyTemplateExists(id);\n        // 校验使用中\n        if (BooleanUtil.isTrue(diyTemplateDO.getUsed())) {\n            throw exception(DIY_TEMPLATE_USED_CANNOT_DELETE);\n        }\n        // 删除\n        diyTemplateMapper.deleteById(id);\n    }\n\n    private DiyTemplateDO validateDiyTemplateExists(Long id) {\n        DiyTemplateDO diyTemplateDO = diyTemplateMapper.selectById(id);\n        if (diyTemplateDO == null) {\n            throw exception(DIY_TEMPLATE_NOT_EXISTS);\n        }\n        return diyTemplateDO;\n    }\n\n    @Override\n    public DiyTemplateDO getDiyTemplate(Long id) {\n        return diyTemplateMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<DiyTemplateDO> getDiyTemplatePage(DiyTemplatePageReqVO pageReqVO) {\n        return diyTemplateMapper.selectPage(pageReqVO);\n    }\n\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public void useDiyTemplate(Long id) {\n        // 校验存在\n        validateDiyTemplateExists(id);\n        // TODO @疯狂：要不已使用的情况，抛个业务异常？\n        // 已使用的更新为未使用\n        DiyTemplateDO used = diyTemplateMapper.selectByUsed(true);\n        if (used != null) {\n            // 如果 id 相同，说明未发生变化\n            if (used.getId().equals(id)) {\n                return;\n            }\n            this.updateTemplateUsed(used.getId(), false, null);\n        }\n        // 更新为已使用\n        this.updateTemplateUsed(id, true, LocalDateTime.now());\n    }\n\n    /**\n     * 更新模板是否使用\n     *\n     * @param id       模板编号\n     * @param used     是否使用\n     * @param usedTime 使用时间\n     */\n    private void updateTemplateUsed(Long id, Boolean used, LocalDateTime usedTime) {\n        DiyTemplateDO updateObj = new DiyTemplateDO().setId(id)\n                .setUsed(used).setUsedTime(usedTime);\n        diyTemplateMapper.updateById(updateObj);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateDiyTemplateProperty(DiyTemplatePropertyUpdateRequestVO updateReqVO) {\n        // 校验存在\n        validateDiyTemplateExists(updateReqVO.getId());\n        // 更新模板属性\n        DiyTemplateDO updateObj = DiyTemplateConvert.INSTANCE.convert(updateReqVO);\n        diyTemplateMapper.updateById(updateObj);\n    }\n\n    @Override\n    public DiyTemplateDO getUsedDiyTemplate() {\n        return diyTemplateMapper.selectByUsed(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.kefu;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;\n\nimport java.util.List;\n\n/**\n * 客服会话 Service 接口\n *\n * @author HUIHUI\n */\npublic interface KeFuConversationService {\n\n    /**\n     * 获得客服会话\n     *\n     * @param id 编号\n     * @return 客服会话\n     */\n    KeFuConversationDO getConversation(Long id);\n\n    /**\n     * 【管理员】删除客服会话\n     *\n     * @param id 编号\n     */\n    void deleteKefuConversation(Long id);\n\n    /**\n     * 【管理员】客服会话置顶\n     *\n     * @param updateReqVO 请求\n     */\n    void updateConversationPinnedByAdmin(KeFuConversationUpdatePinnedReqVO updateReqVO);\n\n    /**\n     * 更新会话客服消息冗余信息\n     *\n     * @param kefuMessage 消息\n     */\n    void updateConversationLastMessage(KeFuMessageDO kefuMessage);\n\n    /**\n     * 【管理员】将管理员未读消息计数更新为零\n     *\n     * @param id 编号\n     */\n    void updateAdminUnreadMessageCountToZero(Long id);\n\n    /**\n     * 【管理员】更新会话对于管理员是否可见\n     *\n     * @param id           编号\n     * @param adminDeleted 管理员是否可见\n     */\n    void updateConversationAdminDeleted(Long id, Boolean adminDeleted);\n\n    /**\n     * 【管理员】获得客服会话列表\n     *\n     * @return 会话列表\n     */\n    List<KeFuConversationDO> getKefuConversationList();\n\n    /**\n     * 【会员】获得或创建会话\n     *\n     * 对于【会员】来说，有且仅有一个对话\n     *\n     * @param userId 用户编号\n     * @return 客服会话\n     */\n    KeFuConversationDO getOrCreateConversation(Long userId);\n\n    /**\n     * 校验客服会话是否存在\n     *\n     * @param id 编号\n     * @return 客服会话\n     */\n    KeFuConversationDO validateKefuConversationExists(Long id);\n\n    /**\n     * 【会员】获得客服会话\n     *\n     * @param userId 用户编号\n     * @return 客服会话\n     */\n    KeFuConversationDO getConversationByUserId(Long userId);\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.kefu;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuConversationMapper;\nimport cn.iocoder.yudao.module.promotion.enums.kefu.KeFuMessageContentTypeEnum;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.KEFU_CONVERSATION_NOT_EXISTS;\n\n/**\n * 客服会话 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class KeFuConversationServiceImpl implements KeFuConversationService {\n\n    @Resource\n    private KeFuConversationMapper conversationMapper;\n\n    @Override\n    public KeFuConversationDO getConversation(Long id) {\n        return conversationMapper.selectById(id);\n    }\n\n    @Override\n    public void deleteKefuConversation(Long id) {\n        // 校验存在\n        validateKefuConversationExists(id);\n\n        // 只有管理员端可以删除会话，也不真的删，只是管理员端看不到啦\n        conversationMapper.updateById(new KeFuConversationDO().setId(id).setAdminDeleted(Boolean.TRUE));\n    }\n\n    @Override\n    public void updateConversationPinnedByAdmin(KeFuConversationUpdatePinnedReqVO updateReqVO) {\n        // 校验存在\n        validateKefuConversationExists(updateReqVO.getId());\n\n        // 更新管理员会话置顶状态\n        conversationMapper.updateById(new KeFuConversationDO().setId(updateReqVO.getId()).setAdminPinned(updateReqVO.getAdminPinned()));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateConversationLastMessage(KeFuMessageDO kefuMessage) {\n        // 1.1 校验会话是否存在\n        KeFuConversationDO conversation = validateKefuConversationExists(kefuMessage.getConversationId());\n        // 1.2 更新会话消息冗余\n        conversationMapper.updateById(new KeFuConversationDO().setId(kefuMessage.getConversationId())\n                .setLastMessageTime(kefuMessage.getCreateTime()).setLastMessageContent(kefuMessage.getContent())\n                .setLastMessageContentType(kefuMessage.getContentType()));\n\n        // 2.1 更新管理员未读消息数\n        if (UserTypeEnum.MEMBER.getValue().equals(kefuMessage.getSenderType())) {\n            conversationMapper.updateAdminUnreadMessageCountIncrement(kefuMessage.getConversationId());\n        }\n        // 2.2 会员用户发送消息时，如果管理员删除过会话则进行恢复\n        if (Boolean.TRUE.equals(conversation.getAdminDeleted())) {\n            updateConversationAdminDeleted(kefuMessage.getConversationId(), Boolean.FALSE);\n        }\n    }\n\n    @Override\n    public void updateAdminUnreadMessageCountToZero(Long id) {\n        // 校验存在\n        validateKefuConversationExists(id);\n\n        // 管理员未读消息数归零\n        conversationMapper.updateById(new KeFuConversationDO().setId(id).setAdminUnreadMessageCount(0));\n    }\n\n    @Override\n    public void updateConversationAdminDeleted(Long id, Boolean adminDeleted) {\n        conversationMapper.updateById(new KeFuConversationDO().setId(id).setAdminDeleted(adminDeleted));\n    }\n\n    @Override\n    public List<KeFuConversationDO> getKefuConversationList() {\n        return conversationMapper.selectConversationList();\n    }\n\n    @Override\n    public KeFuConversationDO getOrCreateConversation(Long userId) {\n        KeFuConversationDO conversation = conversationMapper.selectOne(KeFuConversationDO::getUserId, userId);\n        // 没有历史会话，则初始化一个新会话\n        if (conversation == null) {\n            conversation = new KeFuConversationDO().setUserId(userId).setLastMessageTime(LocalDateTime.now())\n                    .setLastMessageContent(StrUtil.EMPTY).setLastMessageContentType(KeFuMessageContentTypeEnum.TEXT.getType())\n                    .setAdminPinned(Boolean.FALSE).setUserDeleted(Boolean.FALSE).setAdminDeleted(Boolean.FALSE)\n                    .setAdminUnreadMessageCount(0);\n            conversationMapper.insert(conversation);\n        }\n        return conversation;\n    }\n\n    @Override\n    public KeFuConversationDO validateKefuConversationExists(Long id) {\n        KeFuConversationDO conversation = conversationMapper.selectById(id);\n        if (conversation == null) {\n            throw exception(KEFU_CONVERSATION_NOT_EXISTS);\n        }\n        return conversation;\n    }\n\n    @Override\n    public KeFuConversationDO getConversationByUserId(Long userId) {\n        return conversationMapper.selectByUserId(userId);\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.kefu;\n\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageListReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;\n\nimport javax.validation.Valid;\n\nimport java.util.List;\n\n/**\n * 客服消息 Service 接口\n *\n * @author HUIHUI\n */\npublic interface KeFuMessageService {\n\n    /**\n     * 【管理员】发送客服消息\n     *\n     * @param sendReqVO 信息\n     * @return 编号\n     */\n    Long sendKefuMessage(@Valid KeFuMessageSendReqVO sendReqVO);\n\n    /**\n     * 【会员】发送客服消息\n     *\n     * @param sendReqVO 信息\n     * @return 编号\n     */\n    Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO);\n\n    /**\n     * 【管理员】更新消息已读状态\n     *\n     * @param conversationId 会话编号\n     * @param userId         用户编号\n     * @param userType       用户类型\n     */\n    void updateKeFuMessageReadStatus(Long conversationId, Long userId, Integer userType);\n\n    /**\n     * 获得客服消息分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 客服消息分页\n     */\n    List<KeFuMessageDO> getKeFuMessageList(KeFuMessageListReqVO pageReqVO);\n\n    /**\n     * 【会员】获得客服消息分页\n     *\n     * @param pageReqVO 请求\n     * @param userId    用户编号\n     * @return 客服消息分页\n     */\n    List<KeFuMessageDO> getKeFuMessageList(AppKeFuMessagePageReqVO pageReqVO, Long userId);\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.kefu;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageListReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageRespVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuMessageMapper;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport javax.annotation.Resource;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.KEFU_CONVERSATION_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.promotion.enums.WebSocketMessageTypeConstants.KEFU_MESSAGE_ADMIN_READ;\nimport static cn.iocoder.yudao.module.promotion.enums.WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE;\n\n/**\n * 客服消息 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class KeFuMessageServiceImpl implements KeFuMessageService {\n\n    @Resource\n    private KeFuMessageMapper keFuMessageMapper;\n    @Resource\n    private KeFuConversationService conversationService;\n    @Resource\n    private AdminUserApi adminUserApi;\n    @Resource\n    private MemberUserApi memberUserApi;\n    @Resource\n    private WebSocketSenderApi webSocketSenderApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long sendKefuMessage(KeFuMessageSendReqVO sendReqVO) {\n        // 1.1 校验会话是否存在\n        KeFuConversationDO conversation = conversationService.validateKefuConversationExists(sendReqVO.getConversationId());\n        // 1.2 校验接收人是否存在\n        validateReceiverExist(conversation.getUserId(), UserTypeEnum.MEMBER.getValue());\n\n        // 2.1 保存消息\n        KeFuMessageDO kefuMessage = BeanUtils.toBean(sendReqVO, KeFuMessageDO.class);\n        kefuMessage.setReceiverId(conversation.getUserId()).setReceiverType(UserTypeEnum.MEMBER.getValue()); // 设置接收人\n        keFuMessageMapper.insert(kefuMessage);\n        // 2.2 更新会话消息冗余\n        conversationService.updateConversationLastMessage(kefuMessage);\n\n        // 3.1 发送消息给会员\n        AdminUserRespDTO user = adminUserApi.getUser(kefuMessage.getSenderId()).getCheckedData();\n        KeFuMessageRespVO message = BeanUtils.toBean(kefuMessage, KeFuMessageRespVO.class).setSenderAvatar(user.getAvatar());\n        getSelf().sendAsyncMessageToMember(conversation.getUserId(), KEFU_MESSAGE_TYPE, message);\n        // 3.2 通知所有管理员更新对话\n        getSelf().sendAsyncMessageToAdmin(KEFU_MESSAGE_TYPE, message);\n        return kefuMessage.getId();\n    }\n\n    @Override\n    public Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO) {\n        // 1.1 设置会话编号\n        KeFuMessageDO kefuMessage = BeanUtils.toBean(sendReqVO, KeFuMessageDO.class);\n        KeFuConversationDO conversation = conversationService.getOrCreateConversation(sendReqVO.getSenderId());\n        kefuMessage.setConversationId(conversation.getId());\n        // 1.2 保存消息\n        keFuMessageMapper.insert(kefuMessage);\n\n        // 2. 更新会话消息冗余\n        conversationService.updateConversationLastMessage(kefuMessage);\n        // 3. 通知所有管理员更新对话\n        MemberUserRespDTO user = memberUserApi.getUser(kefuMessage.getSenderId()).getCheckedData();\n        KeFuMessageRespVO message = BeanUtils.toBean(kefuMessage, KeFuMessageRespVO.class).setSenderAvatar(user.getAvatar());\n        getSelf().sendAsyncMessageToAdmin(KEFU_MESSAGE_TYPE, message);\n        getSelf().sendAsyncMessageToMember(conversation.getUserId(), KEFU_MESSAGE_TYPE, message);\n        return kefuMessage.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateKeFuMessageReadStatus(Long conversationId, Long userId, Integer userType) {\n        // 1.1 校验会话是否存在\n        KeFuConversationDO conversation = conversationService.validateKefuConversationExists(conversationId);\n        // 1.2 如果是会员端处理已读，需要传递 userId；万一用户模拟一个 conversationId\n        if (UserTypeEnum.MEMBER.getValue().equals(userType) && ObjUtil.notEqual(conversation.getUserId(), userId)) {\n            throw exception(KEFU_CONVERSATION_NOT_EXISTS);\n        }\n        // 1.3 查询会话所有的未读消息 (tips: 多个客服，一个人点了，就都点了)\n        List<KeFuMessageDO> messageList = keFuMessageMapper.selectListByConversationIdAndUserTypeAndReadStatus(conversationId, userType, Boolean.FALSE);\n        if (CollUtil.isEmpty(messageList)) {\n            return;\n        }\n\n        // 2.1 情况二：更新未读消息状态为已读\n        keFuMessageMapper.updateReadStatusBatchByIds(convertSet(messageList, KeFuMessageDO::getId),\n                new KeFuMessageDO().setReadStatus(Boolean.TRUE));\n        // 2.2 将管理员未读消息计数更新为零\n        conversationService.updateAdminUnreadMessageCountToZero(conversationId);\n\n        // 2.3 发送消息通知会员，管理员已读 -> 会员更新发送的消息状态\n        KeFuMessageDO keFuMessage = getFirst(filterList(messageList, message -> UserTypeEnum.MEMBER.getValue().equals(message.getSenderType())));\n        assert keFuMessage != null; // 断言避免警告\n        getSelf().sendAsyncMessageToMember(keFuMessage.getSenderId(), KEFU_MESSAGE_ADMIN_READ,\n                new KeFuMessageRespVO().setConversationId(keFuMessage.getConversationId()));\n        // 2.4 通知所有管理员消息已读\n        getSelf().sendAsyncMessageToAdmin(KEFU_MESSAGE_ADMIN_READ,\n                new KeFuMessageRespVO().setConversationId(keFuMessage.getConversationId()));\n    }\n\n    private void validateReceiverExist(Long receiverId, Integer receiverType) {\n        if (UserTypeEnum.ADMIN.getValue().equals(receiverType)) {\n            adminUserApi.validateUser(receiverId);\n        }\n        if (UserTypeEnum.MEMBER.getValue().equals(receiverType)) {\n            memberUserApi.validateUser(receiverId);\n        }\n    }\n\n    @Async\n    public void sendAsyncMessageToMember(Long userId, String messageType, Object content) {\n        webSocketSenderApi.sendObject(UserTypeEnum.MEMBER.getValue(), userId, messageType, content);\n    }\n\n    @Async\n    public void sendAsyncMessageToAdmin(String messageType, Object content) {\n        webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), messageType, content);\n    }\n\n    @Override\n    public List<KeFuMessageDO> getKeFuMessageList(KeFuMessageListReqVO pageReqVO) {\n        return keFuMessageMapper.selectList(pageReqVO);\n    }\n\n    @Override\n    public List<KeFuMessageDO> getKeFuMessageList(AppKeFuMessagePageReqVO pageReqVO, Long userId) {\n        // 1. 获得客服会话\n        KeFuConversationDO conversation = conversationService.getConversationByUserId(userId);\n        if (conversation == null) {\n            return Collections.emptyList();\n        }\n        // 2. 设置会话编号\n        pageReqVO.setConversationId(conversation.getId());\n        return keFuMessageMapper.selectList(BeanUtils.toBean(pageReqVO, KeFuMessageListReqVO.class));\n    }\n\n    private KeFuMessageServiceImpl getSelf() {\n        return SpringUtil.getBean(getClass());\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.point;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 积分商城活动 Service 接口\n *\n * @author HUIHUI\n */\npublic interface PointActivityService {\n\n    /**\n     * 创建积分商城活动\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createPointActivity(@Valid PointActivitySaveReqVO createReqVO);\n\n    /**\n     * 更新积分商城活动\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updatePointActivity(@Valid PointActivitySaveReqVO updateReqVO);\n\n    /**\n     * 更新积分商城商品库存（减少）\n     *\n     * @param id    活动编号\n     * @param skuId sku 编号\n     * @param count 数量(正数)\n     */\n    void updatePointStockDecr(Long id, Long skuId, Integer count);\n\n    /**\n     * 更新积分商城商品库存（增加）\n     *\n     * @param id    活动编号\n     * @param skuId sku 编号\n     * @param count 数量(正数)\n     */\n    void updatePointStockIncr(Long id, Long skuId, Integer count);\n\n    /**\n     * 关闭积分商城活动\n     *\n     * @param id 编号\n     */\n    void closePointActivity(Long id);\n\n    /**\n     * 删除积分商城活动\n     *\n     * @param id 编号\n     */\n    void deletePointActivity(Long id);\n\n    /**\n     * 获得积分商城活动\n     *\n     * @param id 编号\n     * @return 积分商城活动\n     */\n    PointActivityDO getPointActivity(Long id);\n\n    /**\n     * 获得积分商城活动分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 积分商城活动分页\n     */\n    PageResult<PointActivityDO> getPointActivityPage(PointActivityPageReqVO pageReqVO);\n\n    /**\n     * 获得积分商城活动列表\n     *\n     * @param ids 活动编号\n     * @return 积分商城活动列表\n     */\n    List<PointActivityDO> getPointActivityListByIds(Collection<Long> ids);\n\n    /**\n     * 获得活动商品\n     *\n     * @param activityIds 活动编号\n     * @return 获得活动商品\n     */\n    List<PointProductDO> getPointProductListByActivityIds(Collection<Long> activityIds);\n\n    /**\n     * 【下单前】校验是否参与积分商城活动\n     *\n     * 如果校验失败，则抛出业务异常\n     *\n     * @param activityId 活动编号\n     * @param skuId      SKU 编号\n     * @param count      数量\n     * @return 积分商城商品信息\n     */\n    PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count);\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.point;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductSaveReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.point.PointActivityMapper;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.point.PointProductMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.hutool.core.collection.CollUtil.intersectionDistinct;\nimport static cn.hutool.core.collection.CollUtil.isNotEmpty;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\nimport static java.util.Collections.singletonList;\n\n/**\n * 积分商城活动 Service 实现类\n *\n * @author HUIHUI\n */\n@Service\n@Validated\npublic class PointActivityServiceImpl implements PointActivityService {\n\n    @Resource\n    private PointActivityMapper pointActivityMapper;\n    @Resource\n    private PointProductMapper pointProductMapper;\n\n    @Resource\n    private ProductSpuApi productSpuApi;\n    @Resource\n    private ProductSkuApi productSkuApi;\n\n    private static List<PointProductDO> buildPointProductDO(PointActivityDO pointActivity, List<PointProductSaveReqVO> products) {\n        return BeanUtils.toBean(products, PointProductDO.class, product ->\n                product.setSpuId(pointActivity.getSpuId()).setActivityId(pointActivity.getId())\n                        .setActivityStatus(pointActivity.getStatus()));\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createPointActivity(PointActivitySaveReqVO createReqVO) {\n        // 1.1 校验商品是否存在\n        validateProductExists(createReqVO.getSpuId(), createReqVO.getProducts());\n        // 1.2 校验商品是否已经参加别的活动\n        validatePointActivityProductConflicts(null, createReqVO.getProducts());\n\n        // 2.1 插入积分商城活动\n        PointActivityDO pointActivity = BeanUtils.toBean(createReqVO, PointActivityDO.class)\n                .setStatus(CommonStatusEnum.ENABLE.getStatus())\n                .setStock(getSumValue(createReqVO.getProducts(), PointProductSaveReqVO::getStock, Integer::sum));\n        pointActivity.setTotalStock(pointActivity.getStock());\n        pointActivityMapper.insert(pointActivity);\n        // 2.2 插入积分商城活动商品\n        pointProductMapper.insertBatch(buildPointProductDO(pointActivity, createReqVO.getProducts()));\n        return pointActivity.getId();\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePointActivity(PointActivitySaveReqVO updateReqVO) {\n        // 1.1 校验存在\n        PointActivityDO activity = validatePointActivityExists(updateReqVO.getId());\n        if (CommonStatusEnum.DISABLE.getStatus().equals(activity.getStatus())) {\n            throw exception(POINT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);\n        }\n        // 1.2 校验商品是否存在\n        validateProductExists(updateReqVO.getSpuId(), updateReqVO.getProducts());\n        // 1.3 校验商品是否已经参加别的活动\n        validatePointActivityProductConflicts(updateReqVO.getId(), updateReqVO.getProducts());\n\n        // 2.1 更新积分商城活动\n        PointActivityDO updateObj = BeanUtils.toBean(updateReqVO, PointActivityDO.class)\n                .setStock(getSumValue(updateReqVO.getProducts(), PointProductSaveReqVO::getStock, Integer::sum));\n        if (updateObj.getStock() > activity.getTotalStock()) { // 如果更新的库存大于原来的库存，则更新总库存\n            updateObj.setTotalStock(updateObj.getStock());\n        }\n        pointActivityMapper.updateById(updateObj);\n        // 2.2 更新商品\n        updatePointProduct(updateObj, updateReqVO.getProducts());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePointStockDecr(Long id, Long skuId, Integer count) {\n        // 1.1 校验活动库存是否充足\n        PointActivityDO activity = validatePointActivityExists(id);\n        if (count > activity.getStock()) {\n            throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n        // 1.2 校验商品库存是否充足\n        PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(id, skuId);\n        if (product == null || count > product.getStock()) {\n            throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n\n        // 2.1 更新活动商品库存\n        int updateCount = pointProductMapper.updateStockDecr(product.getId(), count);\n        if (updateCount == 0) {\n            throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n\n        // 2.2 更新活动库存\n        updateCount = pointActivityMapper.updateStockDecr(id, count);\n        if (updateCount == 0) {\n            throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updatePointStockIncr(Long id, Long skuId, Integer count) {\n        PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(id, skuId);\n        // 更新活动商品库存\n        pointProductMapper.updateStockIncr(product.getId(), count);\n        // 更新活动库存\n        pointActivityMapper.updateStockIncr(id, count);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void closePointActivity(Long id) {\n        // 校验存在\n        PointActivityDO pointActivity = validatePointActivityExists(id);\n        if (CommonStatusEnum.DISABLE.getStatus().equals(pointActivity.getStatus())) {\n            throw exception(POINT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);\n        }\n\n        // 更新\n        pointActivityMapper.updateById(new PointActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus()));\n        // 更新活动商品状态\n        pointProductMapper.updateByActivityId(new PointProductDO().setActivityId(id).setActivityStatus(\n                CommonStatusEnum.DISABLE.getStatus()));\n    }\n\n    /**\n     * 更新积分商品\n     *\n     * @param activity 积分活动\n     * @param products 该活动的最新商品配置\n     */\n    private void updatePointProduct(PointActivityDO activity, List<PointProductSaveReqVO> products) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<PointProductDO> newList = buildPointProductDO(activity, products);\n        List<PointProductDO> oldList = pointProductMapper.selectListByActivityId(activity.getId());\n        List<List<PointProductDO>> diffList = diffList(oldList, newList, (oldVal, newVal) -> {\n            boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId());\n            if (same) {\n                newVal.setId(oldVal.getId());\n            }\n            return same;\n        });\n\n        // 第二步，批量添加、修改、删除\n        if (isNotEmpty(diffList.get(0))) {\n            pointProductMapper.insertBatch(diffList.get(0));\n        }\n        if (isNotEmpty(diffList.get(1))) {\n            pointProductMapper.updateBatch(diffList.get(1));\n        }\n        if (isNotEmpty(diffList.get(2))) {\n            pointProductMapper.deleteByIds(convertList(diffList.get(2), PointProductDO::getId));\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deletePointActivity(Long id) {\n        // 校验存在\n        PointActivityDO pointActivity = validatePointActivityExists(id);\n        if (CommonStatusEnum.ENABLE.getStatus().equals(pointActivity.getStatus())) {\n            throw exception(POINT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END);\n        }\n\n        // 删除商城活动\n        pointActivityMapper.deleteById(id);\n        // 删除活动商品\n        List<PointProductDO> products = pointProductMapper.selectListByActivityId(id);\n        pointProductMapper.deleteByIds(convertSet(products, PointProductDO::getId));\n    }\n\n    private PointActivityDO validatePointActivityExists(Long id) {\n        PointActivityDO pointActivityDO = pointActivityMapper.selectById(id);\n        if (pointActivityDO == null) {\n            throw exception(POINT_ACTIVITY_NOT_EXISTS);\n        }\n        return pointActivityDO;\n    }\n\n    /**\n     * 校验积分商品是否都存在\n     *\n     * @param spuId    商品 SPU 编号\n     * @param products 积分商品\n     */\n    private void validateProductExists(Long spuId, List<PointProductSaveReqVO> products) {\n        // 1. 校验商品 spu 是否存在\n        ProductSpuRespDTO spu = productSpuApi.getSpu(spuId).getCheckedData();\n        if (spu == null) {\n            throw exception(SPU_NOT_EXISTS);\n        }\n        products.forEach(product -> product.setSpuId(spuId));\n\n        // 2. 校验商品 sku 都存在\n        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(singletonList(spuId)).getCheckedData();\n        Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);\n        products.forEach(product -> {\n            if (!skuMap.containsKey(product.getSkuId())) {\n                throw exception(SKU_NOT_EXISTS);\n            }\n        });\n    }\n\n    /**\n     * 校验商品是否冲突\n     *\n     * @param id       编号\n     * @param products 商品列表\n     */\n    private void validatePointActivityProductConflicts(Long id, List<PointProductSaveReqVO> products) {\n        // 1.1 查询所有开启的积分商城活动\n        List<PointActivityDO> activityList = pointActivityMapper.selectList(PointActivityDO::getStatus,\n                CommonStatusEnum.ENABLE.getStatus());\n        if (id != null) { // 更新时排除自己\n            activityList.removeIf(item -> ObjectUtil.equal(item.getId(), id));\n        }\n        // 1.2 查询活动下的所有商品\n        List<PointProductDO> productList = pointProductMapper.selectListByActivityId(\n                convertList(activityList, PointActivityDO::getId));\n        Map<Long, List<PointProductDO>> productListMap = convertMultiMap(productList, PointProductDO::getActivityId);\n\n        // 2. 校验商品是否冲突\n        activityList.forEach(item -> {\n            findAndThen(productListMap, item.getId(), discountProducts -> {\n                if (!intersectionDistinct(convertList(discountProducts, PointProductDO::getSpuId),\n                        convertList(products, PointProductSaveReqVO::getSpuId)).isEmpty()) {\n                    throw exception(POINT_ACTIVITY_SPU_CONFLICTS);\n                }\n            });\n        });\n    }\n\n    @Override\n    public PointActivityDO getPointActivity(Long id) {\n        return pointActivityMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<PointActivityDO> getPointActivityPage(PointActivityPageReqVO pageReqVO) {\n        return pointActivityMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<PointActivityDO> getPointActivityListByIds(Collection<Long> ids) {\n        return pointActivityMapper.selectList(PointActivityDO::getId, ids);\n    }\n\n    @Override\n    public List<PointProductDO> getPointProductListByActivityIds(Collection<Long> activityIds) {\n        return pointProductMapper.selectListByActivityId(activityIds);\n    }\n\n    @Override\n    public PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count) {\n        // 1. 校验积分商城活动是否存在\n        PointActivityDO activity = validatePointActivityExists(activityId);\n        if (CommonStatusEnum.isDisable(activity.getStatus())) {\n            throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_STATUS_CLOSED);\n        }\n\n        // 2.1 校验积分商城商品是否存在\n        PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(activityId, skuId);\n        if (product == null) {\n            throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);\n        }\n        // 2.2 超过单次购买限制\n        if (count > product.getCount()) {\n            throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED);\n        }\n        // 2.2 校验库存是否充足\n        if (count > product.getStock()) {\n            throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n        return BeanUtils.toBean(product, PointValidateJoinRespDTO.class);\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.reward;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;\n\nimport javax.validation.Valid;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;\n\n/**\n * 满减送活动 Service 接口\n *\n * @author 芋道源码\n */\npublic interface RewardActivityService {\n\n    /**\n     * 创建满减送活动\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createRewardActivity(@Valid RewardActivityCreateReqVO createReqVO);\n\n    /**\n     * 更新满减送活动\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateRewardActivity(@Valid RewardActivityUpdateReqVO updateReqVO);\n\n    /**\n     * 关闭满减送活动\n     *\n     * @param id 活动编号\n     */\n    void closeRewardActivity(Long id);\n\n    /**\n     * 删除满减送活动\n     *\n     * @param id 编号\n     */\n    void deleteRewardActivity(Long id);\n\n    /**\n     * 获得满减送活动\n     *\n     * @param id 编号\n     * @return 满减送活动\n     */\n    RewardActivityDO getRewardActivity(Long id);\n\n    /**\n     * 获得满减送活动分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 满减送活动分页\n     */\n    PageResult<RewardActivityDO> getRewardActivityPage(RewardActivityPageReqVO pageReqVO);\n\n    /**\n     * 获得 spuId 商品匹配的的满减送活动列表\n     *\n     * @param spuIds   SPU 编号数组\n     * @return 满减送活动列表\n     */\n    List<RewardActivityMatchRespDTO> getMatchRewardActivityListBySpuIds(Collection<Long> spuIds);\n\n    default String getRewardActivityRuleDescription(Integer conditionType, RewardActivityDO.Rule rule) {\n        String description = \"\";\n        if (PromotionConditionTypeEnum.PRICE.getType().equals(conditionType)) {\n            description += StrUtil.format(\"满 {} 元\", MoneyUtils.fenToYuanStr(rule.getLimit()));\n        } else {\n            description += StrUtil.format(\"满 {} 件\", rule.getLimit());\n        }\n        List<String> tips = new ArrayList<>(10);\n        if (rule.getDiscountPrice() != null) {\n            tips.add(StrUtil.format(\"减 {}\", MoneyUtils.fenToYuanStr(rule.getDiscountPrice())));\n        }\n        if (Boolean.TRUE.equals(rule.getFreeDelivery())) {\n            tips.add(\"包邮\");\n        }\n        if (rule.getPoint() != null && rule.getPoint() > 0) {\n            tips.add(StrUtil.format(\"送 {} 积分\", rule.getPoint()));\n        }\n        if (CollUtil.isNotEmpty(rule.getGiveCouponTemplateCounts())) {\n            tips.add(StrUtil.format(\"送 {} 张优惠券\",\n                    getSumValue(rule.getGiveCouponTemplateCounts().values(), count -> count, Integer::sum)));\n        }\n        return description + StrUtil.join(\"、\", tips);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.reward;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.category.ProductCategoryApi;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityBaseVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper;\nimport cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.util.*;\n\nimport static cn.hutool.core.collection.CollUtil.intersectionDistinct;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\n\n/**\n * 满减送活动 Service 实现类\n *\n * @author 芋道源码\n */\n@Service\n@Validated\npublic class RewardActivityServiceImpl implements RewardActivityService {\n\n    @Resource\n    private RewardActivityMapper rewardActivityMapper;\n\n    @Resource\n    private ProductCategoryApi productCategoryApi;\n    @Resource\n    private ProductSpuApi productSpuApi;\n\n    @Override\n    public Long createRewardActivity(RewardActivityCreateReqVO createReqVO) {\n        // 1.1 校验商品范围\n        validateProductScope(createReqVO.getProductScope(), createReqVO.getProductScopeValues());\n        // 1.2 校验商品是否冲突\n        validateRewardActivitySpuConflicts(null, createReqVO);\n\n        // 插入\n        RewardActivityDO rewardActivity = BeanUtils.toBean(createReqVO, RewardActivityDO.class)\n                .setStatus(CommonStatusEnum.ENABLE.getStatus());\n        rewardActivityMapper.insert(rewardActivity);\n        // 返回\n        return rewardActivity.getId();\n    }\n\n    @Override\n    public void updateRewardActivity(RewardActivityUpdateReqVO updateReqVO) {\n        // 1.1 校验存在\n        RewardActivityDO dbRewardActivity = validateRewardActivityExists(updateReqVO.getId());\n        if (dbRewardActivity.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { // 已关闭的活动，不能修改噢\n            throw exception(REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);\n        }\n        // 1.2 校验商品范围\n        validateProductScope(updateReqVO.getProductScope(), updateReqVO.getProductScopeValues());\n        // 1.3 校验商品是否冲突\n        validateRewardActivitySpuConflicts(updateReqVO.getId(), updateReqVO);\n\n        // 2. 更新\n        RewardActivityDO updateObj = BeanUtils.toBean(updateReqVO, RewardActivityDO.class);\n        rewardActivityMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void closeRewardActivity(Long id) {\n        // 校验存在\n        RewardActivityDO dbRewardActivity = validateRewardActivityExists(id);\n        if (dbRewardActivity.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { // 已关闭的活动，不能关闭噢\n            throw exception(REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);\n        }\n\n        // 更新\n        rewardActivityMapper.updateById(new RewardActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus()));\n    }\n\n    @Override\n    public void deleteRewardActivity(Long id) {\n        // 校验存在\n        RewardActivityDO dbRewardActivity = validateRewardActivityExists(id);\n        if (dbRewardActivity.getStatus().equals(CommonStatusEnum.ENABLE.getStatus())) { // 未关闭的活动，不能删除噢\n            throw exception(REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED);\n        }\n\n        // 删除\n        rewardActivityMapper.deleteById(id);\n    }\n\n    private RewardActivityDO validateRewardActivityExists(Long id) {\n        RewardActivityDO activity = rewardActivityMapper.selectById(id);\n        if (activity == null) {\n            throw exception(REWARD_ACTIVITY_NOT_EXISTS);\n        }\n        return activity;\n    }\n\n    /**\n     * 校验商品参加的活动是否冲突\n     *\n     * @param id             活动编号\n     * @param rewardActivity 请求\n     */\n    private void validateRewardActivitySpuConflicts(Long id, RewardActivityBaseVO rewardActivity) {\n        // 1. 获得开启的所有的活动\n        List<RewardActivityDO> list = rewardActivityMapper.selectList(RewardActivityDO::getStatus, CommonStatusEnum.ENABLE.getStatus());\n        if (id != null) { // 排除自己这个活动\n            list.removeIf(activity -> id.equals(activity.getId()));\n        }\n\n        // 2. 完全不允许重叠\n        for (RewardActivityDO item : list) {\n            // 2.1 校验满减送活动时间是否冲突，如果时段不冲突那么不同的时间段内则可以存在相同的商品范围\n            if (!LocalDateTimeUtil.isOverlap(item.getStartTime(), item.getEndTime(),\n                    rewardActivity.getStartTime(), rewardActivity.getEndTime())) {\n                continue;\n            }\n            // 2.2 校验商品范围是否重叠\n            // 情况一：如果与该时间段内商品范围为全部的活动冲突，或 rewardActivity 商品范围为全部，那么则直接校验不通过\n            // 例如说，rewardActivity 是全部活动，结果有个 db 里的 activity 是某个分类，它也是冲突的。也就是说，当前时间段内，有且仅有只能有一个活动！\n            if (PromotionProductScopeEnum.isAll(item.getProductScope()) ||\n                    PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {\n                throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(),\n                        PromotionProductScopeEnum.isAll(item.getProductScope()) ?\n                                \"该活动商品范围为全部已覆盖包含本活动范围\" : \"本活动商品范围为全部已覆盖包含了该活动商品范围\");\n            }\n            // 情况二：如果与该时间段内商品范围为类别的活动冲突\n            if (PromotionProductScopeEnum.isCategory(item.getProductScope())) {\n                // 校验分类是否冲突\n                if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {\n                    if (!intersectionDistinct(item.getProductScopeValues(), rewardActivity.getProductScopeValues()).isEmpty()) {\n                        throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), \"商品分类范围重叠\");\n                    }\n                }\n                // 校验商品分类是否冲突\n                if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {\n                    List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(rewardActivity.getProductScopeValues()).getCheckedData();\n                    if (!intersectionDistinct(item.getProductScopeValues(),\n                            convertSet(spuList, ProductSpuRespDTO::getCategoryId)).isEmpty()) {\n                        throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), \"该活动商品分类范围已包含本活动所选商品\");\n                    }\n                }\n            }\n            // 情况三：如果与该时间段内商品范围为商品的活动冲突\n            if (PromotionProductScopeEnum.isSpu(item.getProductScope())) {\n                // 校验商品是否冲突\n                if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {\n                    if (!intersectionDistinct(item.getProductScopeValues(), rewardActivity.getProductScopeValues()).isEmpty()) {\n                        throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), \"活动商品范围所选商品重叠\");\n                    }\n                }\n                // 校验商品分类是否冲突\n                if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {\n                    List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(item.getProductScopeValues()).getCheckedData();\n                    if (!intersectionDistinct(rewardActivity.getProductScopeValues(),\n                            convertSet(spuList, ProductSpuRespDTO::getCategoryId)).isEmpty()) {\n                        throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), \"本活动商品分类范围包含了该活动所选商品\");\n                    }\n                }\n            }\n        }\n    }\n\n    private void validateProductScope(Integer productScope, List<Long> productScopeValues) {\n        if (Objects.equals(PromotionProductScopeEnum.SPU.getScope(), productScope)) {\n            productSpuApi.validateSpuList(productScopeValues).checkError();\n        } else if (Objects.equals(PromotionProductScopeEnum.CATEGORY.getScope(), productScope)) {\n            productCategoryApi.validateCategoryList(productScopeValues).checkError();\n        }\n    }\n\n    @Override\n    public RewardActivityDO getRewardActivity(Long id) {\n        return rewardActivityMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<RewardActivityDO> getRewardActivityPage(RewardActivityPageReqVO pageReqVO) {\n        return rewardActivityMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<RewardActivityMatchRespDTO> getMatchRewardActivityListBySpuIds(Collection<Long> spuIds) {\n        // 1. 查询商品分类\n        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds).getCheckedData();\n        if (CollUtil.isEmpty(spuList)) {\n            return Collections.emptyList();\n        }\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);\n\n        // 2. 查询出指定 spuId 的 spu 参加的活动\n        List<RewardActivityDO> activityList = rewardActivityMapper.selectListBySpuIdAndStatusAndNow(\n                spuIds, convertSet(spuList, ProductSpuRespDTO::getCategoryId), CommonStatusEnum.ENABLE.getStatus());\n        if (CollUtil.isEmpty(activityList)) {\n            return Collections.emptyList();\n        }\n\n        // 3. 转换成 Response DTO\n        return convertList(activityList, activity -> {\n            RewardActivityMatchRespDTO activityDTO = BeanUtils.toBean(activity, RewardActivityMatchRespDTO.class);\n            // 3.1 设置对应匹配的 spuIds\n            activityDTO.setSpuIds(new ArrayList<>());\n            for (Long spuId : spuIds) {\n                if (PromotionProductScopeEnum.isAll(activityDTO.getProductScope())) {\n                    activityDTO.getSpuIds().add(spuId);\n                } else if (PromotionProductScopeEnum.isSpu(activityDTO.getProductScope())) {\n                    if (CollUtil.contains(activityDTO.getProductScopeValues(), spuId)) {\n                        activityDTO.getSpuIds().add(spuId);\n                    }\n                } else if (PromotionProductScopeEnum.isCategory(activityDTO.getProductScope())) {\n                    ProductSpuRespDTO spu = spuMap.get(spuId);\n                    if (spu != null && CollUtil.contains(activityDTO.getProductScopeValues(), spu.getCategoryId())) {\n                        activityDTO.getSpuIds().add(spuId);\n                    }\n                }\n            }\n\n            // 3.2 设置每个 Rule 的描述\n            activityDTO.setRules(convertList(activity.getRules(), rule ->\n                    BeanUtils.toBean(rule, RewardActivityMatchRespDTO.Rule.class)\n                            .setDescription(getRewardActivityRuleDescription(activityDTO.getConditionType(), rule))));\n            return activityDTO;\n        });\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.seckill;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 秒杀活动 Service 接口\n *\n * @author halfninety\n */\npublic interface SeckillActivityService {\n\n    /**\n     * 创建秒杀活动\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createSeckillActivity(@Valid SeckillActivityCreateReqVO createReqVO);\n\n    /**\n     * 更新秒杀活动\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO);\n\n    /**\n     * 更新秒杀库存（减少）\n     *\n     * @param id    活动编号\n     * @param skuId sku 编号\n     * @param count 数量（正数）\n     */\n    void updateSeckillStockDecr(Long id, Long skuId, Integer count);\n\n    /**\n     * 更新秒杀库存（增加）\n     *\n     * @param id    活动编号\n     * @param skuId sku 编号\n     * @param count 数量（正数）\n     */\n    void updateSeckillStockIncr(Long id, Long skuId, Integer count);\n\n    /**\n     * 关闭秒杀活动\n     *\n     * @param id 编号\n     */\n    void closeSeckillActivity(Long id);\n\n    /**\n     * 删除秒杀活动\n     *\n     * @param id 编号\n     */\n    void deleteSeckillActivity(Long id);\n\n    /**\n     * 获得秒杀活动\n     *\n     * @param id 编号\n     * @return 秒杀活动\n     */\n    SeckillActivityDO getSeckillActivity(Long id);\n\n    /**\n     * 获得秒杀活动分页\n     *\n     * @param pageReqVO 分页查询\n     * @return 秒杀活动分页\n     */\n    PageResult<SeckillActivityDO> getSeckillActivityPage(SeckillActivityPageReqVO pageReqVO);\n\n    /**\n     * 通过活动编号获取活动商品\n     *\n     * @param activityId 活动编号\n     * @return 活动商品列表\n     */\n    List<SeckillProductDO> getSeckillProductListByActivityId(Long activityId);\n\n    /**\n     * 通过活动编号获取活动商品\n     *\n     * @param activityIds 活动编号\n     * @return 活动商品列表\n     */\n    List<SeckillProductDO> getSeckillProductListByActivityIds(Collection<Long> activityIds);\n\n    /**\n     * 通过活动时段编号获取指定 status 的秒杀活动\n     *\n     * @param configId 时段配置编号\n     * @param status   状态\n     * @return 秒杀活动列表\n     */\n    List<SeckillActivityDO> getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status);\n\n    /**\n     * 通过活动时段获取开始的秒杀活动\n     *\n     * @param pageReqVO 请求\n     * @return 秒杀活动列表\n     */\n    PageResult<SeckillActivityDO> getSeckillActivityAppPageByConfigId(AppSeckillActivityPageReqVO pageReqVO);\n\n    /**\n     * 校验是否参与秒杀商品\n     *\n     * 如果校验失败，则抛出业务异常\n     *\n     * @param activityId 活动编号\n     * @param skuId      SKU 编号\n     * @param count      数量\n     * @return 秒杀信息\n     */\n    SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count);\n\n    /**\n     * 获得 SPU 进行中的秒杀活动\n     *\n     * @param spuId SPU 编号数组\n     * @return 秒杀活动\n     */\n    SeckillActivityDO getMatchSeckillActivityBySpuId(Long spuId);\n\n    /**\n     * 获得拼团活动列表\n     *\n     * @param ids 拼团活动编号数组\n     * @return 拼团活动的列表\n     */\n    List<SeckillActivityDO> getSeckillActivityListByIds(Collection<Long> ids);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.seckill;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO;\nimport cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.seckill.SeckillActivityConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillProductMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.hutool.core.collection.CollUtil.isNotEmpty;\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBetween;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\nimport static java.util.Collections.singletonList;\n\n/**\n * 秒杀活动 Service 实现类\n *\n * @author halfninety\n */\n@Service\n@Validated\npublic class SeckillActivityServiceImpl implements SeckillActivityService {\n\n    @Resource\n    private SeckillActivityMapper seckillActivityMapper;\n    @Resource\n    private SeckillProductMapper seckillProductMapper;\n\n    @Resource\n    private SeckillConfigService seckillConfigService;\n\n    @Resource\n    private ProductSpuApi productSpuApi;\n    @Resource\n    private ProductSkuApi productSkuApi;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public Long createSeckillActivity(SeckillActivityCreateReqVO createReqVO) {\n        // 1.1 校验商品秒杀时段是否冲突\n        validateProductConflict(createReqVO.getConfigIds(), createReqVO.getSpuId(), null);\n        // 1.2 校验商品是否存在\n        validateProductExists(createReqVO.getSpuId(), createReqVO.getProducts());\n\n        // 2.1 插入秒杀活动\n        SeckillActivityDO activity = SeckillActivityConvert.INSTANCE.convert(createReqVO)\n                .setStatus(CommonStatusEnum.ENABLE.getStatus())\n                .setStock(getSumValue(createReqVO.getProducts(), SeckillProductBaseVO::getStock, Integer::sum));\n        activity.setTotalStock(activity.getStock());\n        seckillActivityMapper.insert(activity);\n        // 2.2 插入商品\n        List<SeckillProductDO> products = SeckillActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activity);\n        seckillProductMapper.insertBatch(products);\n        return activity.getId();\n    }\n\n    /**\n     * 校验秒杀商品参与的活动是否存在冲突\n     *\n     * 1. 校验秒杀时段是否存在\n     * 2. 秒杀商品是否参加其它活动\n     *\n     * @param configIds  秒杀时段数组\n     * @param spuId      商品 SPU 编号\n     * @param activityId 秒杀活动编号\n     */\n    private void validateProductConflict(List<Long> configIds, Long spuId, Long activityId) {\n        // 1. 校验秒杀时段是否存在\n        seckillConfigService.validateSeckillConfigExists(configIds);\n\n        // 2.1 查询所有开启的秒杀活动\n        List<SeckillActivityDO> activityList = seckillActivityMapper.selectListBySpuIdAndStatus(spuId, CommonStatusEnum.ENABLE.getStatus());\n        if (activityId != null) { // 排除自己\n            activityList.removeIf(item -> ObjectUtil.equal(item.getId(), activityId));\n        }\n        // 2.2 过滤出所有 configIds 有交集的活动，判断是否存在重叠\n        List<SeckillActivityDO> conflictActivityList = filterList(activityList, s -> containsAny(s.getConfigIds(), configIds));\n        if (isNotEmpty(conflictActivityList)) {\n            throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);\n        }\n    }\n\n    /**\n     * 校验秒杀商品是否都存在\n     *\n     * @param spuId    商品 SPU 编号\n     * @param products 秒杀商品\n     */\n    private void validateProductExists(Long spuId, List<SeckillProductBaseVO> products) {\n        // 1. 校验商品 spu 是否存在\n        ProductSpuRespDTO spu = productSpuApi.getSpu(spuId).getCheckedData();\n        if (spu == null) {\n            throw exception(SPU_NOT_EXISTS);\n        }\n\n        // 2. 校验商品 sku 都存在\n        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(singletonList(spuId)).getCheckedData();\n        Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);\n        products.forEach(product -> {\n            if (!skuMap.containsKey(product.getSkuId())) {\n                throw exception(SKU_NOT_EXISTS);\n            }\n        });\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSeckillActivity(SeckillActivityUpdateReqVO updateReqVO) {\n        // 1.1 校验存在\n        SeckillActivityDO activity = validateSeckillActivityExists(updateReqVO.getId());\n        if (CommonStatusEnum.DISABLE.getStatus().equals(activity.getStatus())) {\n            throw exception(SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);\n        }\n        // 1.2 校验商品是否冲突\n        validateProductConflict(updateReqVO.getConfigIds(), updateReqVO.getSpuId(), updateReqVO.getId());\n        // 1.3 校验商品是否存在\n        validateProductExists(updateReqVO.getSpuId(), updateReqVO.getProducts());\n\n        // 2.1 更新活动\n        SeckillActivityDO updateObj = SeckillActivityConvert.INSTANCE.convert(updateReqVO)\n                .setStock(getSumValue(updateReqVO.getProducts(), SeckillProductBaseVO::getStock, Integer::sum));\n        if (updateObj.getStock() > activity.getTotalStock()) { // 如果更新的库存大于原来的库存，则更新总库存\n            updateObj.setTotalStock(updateObj.getStock());\n        }\n        seckillActivityMapper.updateById(updateObj);\n        // 2.2 更新商品\n        updateSeckillProduct(updateObj, updateReqVO.getProducts());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSeckillStockDecr(Long id, Long skuId, Integer count) {\n        // 1.1 校验活动库存是否充足\n        SeckillActivityDO seckillActivity = validateSeckillActivityExists(id);\n        if (count > seckillActivity.getStock()) {\n            throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n        // 1.2 校验商品库存是否充足\n        SeckillProductDO product = seckillProductMapper.selectByActivityIdAndSkuId(id, skuId);\n        if (product == null || count > product.getStock()) {\n            throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n\n        // 2.1 更新活动商品库存\n        int updateCount = seckillProductMapper.updateStockDecr(product.getId(), count);\n        if (updateCount == 0) {\n            throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n\n        // 2.2 更新活动库存\n        updateCount = seckillActivityMapper.updateStockDecr(seckillActivity.getId(), count);\n        if (updateCount == 0) {\n            throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void updateSeckillStockIncr(Long id, Long skuId, Integer count) {\n        SeckillProductDO product = seckillProductMapper.selectByActivityIdAndSkuId(id, skuId);\n        // 更新活动商品库存\n        seckillProductMapper.updateStockIncr(product.getId(), count);\n        // 更新活动库存\n        seckillActivityMapper.updateStockIncr(id, count);\n    }\n\n    /**\n     * 更新秒杀商品\n     *\n     * @param activity 秒杀活动\n     * @param products 该活动的最新商品配置\n     */\n    private void updateSeckillProduct(SeckillActivityDO activity, List<SeckillProductBaseVO> products) {\n        // 第一步，对比新老数据，获得添加、修改、删除的列表\n        List<SeckillProductDO> newList = SeckillActivityConvert.INSTANCE.convertList(products, activity);\n        List<SeckillProductDO> oldList = seckillProductMapper.selectListByActivityId(activity.getId());\n        List<List<SeckillProductDO>> diffList = diffList(oldList, newList, (oldVal, newVal) -> {\n            boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId());\n            if (same) {\n                newVal.setId(oldVal.getId());\n            }\n            return same;\n        });\n\n        // 第二步，批量添加、修改、删除\n        if (isNotEmpty(diffList.get(0))) {\n            seckillProductMapper.insertBatch(diffList.get(0));\n        }\n        if (isNotEmpty(diffList.get(1))) {\n            seckillProductMapper.updateBatch(diffList.get(1));\n        }\n        if (isNotEmpty(diffList.get(2))) {\n            seckillProductMapper.deleteByIds(convertList(diffList.get(2), SeckillProductDO::getId));\n        }\n    }\n\n    @Override\n    public void closeSeckillActivity(Long id) {\n        // 校验存在\n        SeckillActivityDO activity = validateSeckillActivityExists(id);\n        if (CommonStatusEnum.DISABLE.getStatus().equals(activity.getStatus())) {\n            throw exception(SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);\n        }\n\n        // 更新\n        SeckillActivityDO updateObj = new SeckillActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus());\n        seckillActivityMapper.updateById(updateObj);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void deleteSeckillActivity(Long id) {\n        // 校验存在\n        SeckillActivityDO seckillActivity = this.validateSeckillActivityExists(id);\n        if (CommonStatusEnum.ENABLE.getStatus().equals(seckillActivity.getStatus())) {\n            throw exception(SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END);\n        }\n\n        // 删除活动\n        seckillActivityMapper.deleteById(id);\n        // 删除活动商品\n        List<SeckillProductDO> products = seckillProductMapper.selectListByActivityId(id);\n        seckillProductMapper.deleteByIds(convertSet(products, SeckillProductDO::getId));\n    }\n\n    private SeckillActivityDO validateSeckillActivityExists(Long id) {\n        SeckillActivityDO seckillActivity = seckillActivityMapper.selectById(id);\n        if (seckillActivity == null) {\n            throw exception(SECKILL_ACTIVITY_NOT_EXISTS);\n        }\n        return seckillActivity;\n    }\n\n    @Override\n    public SeckillActivityDO getSeckillActivity(Long id) {\n        return seckillActivityMapper.selectById(id);\n    }\n\n    @Override\n    public PageResult<SeckillActivityDO> getSeckillActivityPage(SeckillActivityPageReqVO pageReqVO) {\n        return seckillActivityMapper.selectPage(pageReqVO);\n    }\n\n    @Override\n    public List<SeckillProductDO> getSeckillProductListByActivityId(Long activityId) {\n        return seckillProductMapper.selectListByActivityId(activityId);\n    }\n\n    @Override\n    public List<SeckillProductDO> getSeckillProductListByActivityIds(Collection<Long> activityIds) {\n        return seckillProductMapper.selectListByActivityId(activityIds);\n    }\n\n    @Override\n    public List<SeckillActivityDO> getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status) {\n        return filterList(seckillActivityMapper.selectList(SeckillActivityDO::getStatus, status),\n                item -> anyMatch(item.getConfigIds(), id -> ObjectUtil.equal(id, configId)) // 校验时段\n                        && isBetween(item.getStartTime(), item.getEndTime())); // 追加当前日期是否处在活动日期之间的校验条件\n    }\n\n    @Override\n    public PageResult<SeckillActivityDO> getSeckillActivityAppPageByConfigId(AppSeckillActivityPageReqVO pageReqVO) {\n        return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now());\n    }\n\n    @Override\n    public SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count) {\n        // 1.1 校验秒杀活动是否存在\n        SeckillActivityDO activity = validateSeckillActivityExists(activityId);\n        if (CommonStatusEnum.isDisable(activity.getStatus())) {\n            throw exception(SECKILL_JOIN_ACTIVITY_STATUS_CLOSED);\n        }\n        // 1.2 是否在活动时间范围内\n        if (!LocalDateTimeUtils.isBetween(activity.getStartTime(), activity.getEndTime())) {\n            throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR);\n        }\n        SeckillConfigDO config = seckillConfigService.getCurrentSeckillConfig();\n        if (config == null\n                || !CollectionUtil.contains(activity.getConfigIds(), config.getId())\n                || !LocalDateTimeUtils.isBetween(config.getStartTime(), config.getEndTime())) {\n            throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR);\n        }\n        // 1.3 超过单次购买限制\n        if (count > activity.getSingleLimitCount()) {\n            throw exception(SECKILL_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED);\n        }\n\n        // 2.1 校验秒杀商品是否存在\n        SeckillProductDO product = seckillProductMapper.selectByActivityIdAndSkuId(activityId, skuId);\n        if (product == null) {\n            throw exception(SECKILL_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);\n        }\n        // 2.2 校验库存是否充足\n        if (count > product.getStock()) {\n            throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);\n        }\n        return SeckillActivityConvert.INSTANCE.convert02(activity, product);\n    }\n\n    @Override\n    public SeckillActivityDO getMatchSeckillActivityBySpuId(Long spuId) {\n        return seckillActivityMapper.selectBySpuIdAndStatusAndNow(spuId, CommonStatusEnum.ENABLE.getStatus());\n    }\n\n    @Override\n    public List<SeckillActivityDO> getSeckillActivityListByIds(Collection<Long> ids) {\n        return seckillActivityMapper.selectList(SeckillActivityDO::getId, ids);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillConfigService.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.seckill;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;\n\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * 秒杀时段 Service 接口\n *\n * @author halfninety\n */\npublic interface SeckillConfigService {\n\n    /**\n     * 创建秒杀时段\n     *\n     * @param createReqVO 创建信息\n     * @return 编号\n     */\n    Long createSeckillConfig(@Valid SeckillConfigCreateReqVO createReqVO);\n\n    /**\n     * 更新秒杀时段\n     *\n     * @param updateReqVO 更新信息\n     */\n    void updateSeckillConfig(@Valid SeckillConfigUpdateReqVO updateReqVO);\n\n    /**\n     * 删除秒杀时段\n     *\n     * @param id 编号\n     */\n    void deleteSeckillConfig(Long id);\n\n    /**\n     * 获得秒杀时段\n     *\n     * @param id 编号\n     * @return 秒杀时段\n     */\n    SeckillConfigDO getSeckillConfig(Long id);\n\n    /**\n     * 获得所有秒杀时段列表\n     *\n     * @return 所有秒杀时段列表\n     */\n    List<SeckillConfigDO> getSeckillConfigList();\n\n    /**\n     * 校验秒杀时段是否存在\n     *\n     * @param ids 秒杀时段 id 集合\n     */\n    void validateSeckillConfigExists(Collection<Long> ids);\n\n    /**\n     * 获得秒杀时间段配置分页数据\n     *\n     * @param pageVO 分页请求参数\n     * @return 秒杀时段分页列表\n     */\n    PageResult<SeckillConfigDO> getSeckillConfigPage(SeckillConfigPageReqVO pageVO);\n\n    /**\n     * 获得所有正常状态的时段配置列表\n     *\n     * @param status 状态\n     * @return 秒杀时段列表\n     */\n    List<SeckillConfigDO> getSeckillConfigListByStatus(Integer status);\n\n    /**\n     * 更新秒杀时段配置状态\n     *\n     * @param id     id\n     * @param status 状态\n     */\n    void updateSeckillConfigStatus(Long id, Integer status);\n\n    /**\n     * 获得当前的秒杀时段\n     *\n     * 要求必须处于开启状态、且在当前时间段内\n     *\n     * @return 时段\n     */\n    SeckillConfigDO getCurrentSeckillConfig();\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillConfigServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.promotion.service.seckill;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigCreateReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigPageReqVO;\nimport cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigUpdateReqVO;\nimport cn.iocoder.yudao.module.promotion.convert.seckill.SeckillConfigConvert;\nimport cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;\nimport cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig.SeckillConfigMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalTime;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBetween;\nimport static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;\n\n/**\n * 秒杀时段 Service 实现类\n *\n * @author halfninety\n */\n@Service\n@Validated\npublic class SeckillConfigServiceImpl implements SeckillConfigService {\n\n    @Resource\n    private SeckillConfigMapper seckillConfigMapper;\n\n    @Override\n    public Long createSeckillConfig(SeckillConfigCreateReqVO createReqVO) {\n        // 校验时间段是否冲突\n        validateSeckillConfigConflict(createReqVO.getStartTime(), createReqVO.getEndTime(), null);\n\n        // 插入\n        SeckillConfigDO seckillConfig = SeckillConfigConvert.INSTANCE.convert(createReqVO);\n        seckillConfigMapper.insert(seckillConfig);\n        // 返回\n        return seckillConfig.getId();\n    }\n\n    @Override\n    public void updateSeckillConfig(SeckillConfigUpdateReqVO updateReqVO) {\n        // 校验存在\n        validateSeckillConfigExists(updateReqVO.getId());\n        // 校验时间段是否冲突\n        validateSeckillConfigConflict(updateReqVO.getStartTime(), updateReqVO.getEndTime(), updateReqVO.getId());\n\n        // 更新\n        SeckillConfigDO updateObj = SeckillConfigConvert.INSTANCE.convert(updateReqVO);\n        seckillConfigMapper.updateById(updateObj);\n    }\n\n    @Override\n    public void updateSeckillConfigStatus(Long id, Integer status) {\n        // 校验秒杀时段是否存在\n        validateSeckillConfigExists(id);\n\n        // 更新状态\n        seckillConfigMapper.updateById(new SeckillConfigDO().setId(id).setStatus(status));\n    }\n\n    @Override\n    public SeckillConfigDO getCurrentSeckillConfig() {\n        List<SeckillConfigDO> list = seckillConfigMapper.selectList(SeckillConfigDO::getStatus, CommonStatusEnum.ENABLE.getStatus());\n        return findFirst(list, config -> isBetween(config.getStartTime(), config.getEndTime()));\n    }\n\n    @Override\n    public void deleteSeckillConfig(Long id) {\n        // 校验存在\n        validateSeckillConfigExists(id);\n\n        // 删除\n        seckillConfigMapper.deleteById(id);\n    }\n\n    private void validateSeckillConfigExists(Long id) {\n        if (seckillConfigMapper.selectById(id) == null) {\n            throw exception(SECKILL_CONFIG_NOT_EXISTS);\n        }\n    }\n\n    /**\n     * 校验时间是否存在冲突\n     *\n     * @param startTimeStr 开始时间\n     * @param endTimeStr   结束时间\n     */\n    private void validateSeckillConfigConflict(String startTimeStr, String endTimeStr, Long id) {\n        // 1. 查询出所有的时段配置\n        LocalTime startTime = LocalTime.parse(startTimeStr);\n        LocalTime endTime = LocalTime.parse(endTimeStr);\n        List<SeckillConfigDO> configs = seckillConfigMapper.selectList();\n        // 更新时排除自己\n        if (id != null) {\n            configs.removeIf(item -> ObjectUtil.equal(item.getId(), id));\n        }\n\n        // 2. 判断是否有重叠的时间\n        boolean hasConflict = configs.stream().anyMatch(config -> LocalDateTimeUtils.isOverlap(startTime, endTime,\n                LocalTime.parse(config.getStartTime()), LocalTime.parse(config.getEndTime())));\n        if (hasConflict) {\n            throw exception(SECKILL_CONFIG_TIME_CONFLICTS);\n        }\n    }\n\n\n    @Override\n    public SeckillConfigDO getSeckillConfig(Long id) {\n        return seckillConfigMapper.selectById(id);\n    }\n\n    @Override\n    public List<SeckillConfigDO> getSeckillConfigList() {\n        return seckillConfigMapper.selectList();\n    }\n\n    @Override\n    public void validateSeckillConfigExists(Collection<Long> ids) {\n        if (CollUtil.isEmpty(ids)) {\n            return;\n        }\n        // 1. 如果有数量不匹配，说明有不存在的，则抛出 SECKILL_CONFIG_NOT_EXISTS 业务异常\n        List<SeckillConfigDO> configs = seckillConfigMapper.selectByIds(ids);\n        if (configs.size() != ids.size()) {\n            throw exception(SECKILL_CONFIG_NOT_EXISTS);\n        }\n\n        // 2. 如果存在关闭，则抛出 SECKILL_CONFIG_DISABLE 业务异常\n        configs.forEach(config -> {\n            if (ObjectUtil.equal(config.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {\n                throw exception(SECKILL_CONFIG_DISABLE);\n            }\n        });\n    }\n\n    @Override\n    public PageResult<SeckillConfigDO> getSeckillConfigPage(SeckillConfigPageReqVO pageVO) {\n        return seckillConfigMapper.selectPage(pageVO);\n    }\n\n    @Override\n    public List<SeckillConfigDO> getSeckillConfigListByStatus(Integer status) {\n        List<SeckillConfigDO> list = seckillConfigMapper.selectListByStatus(status);\n        list.sort(Comparator.comparing(SeckillConfigDO::getStartTime));\n        return list;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 5 # 初始连接数\n        min-idle: 10 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n        slave: # 模拟从库，可根据自己需要修改 # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 400-infra.server.iocoder.cn # 地址\n    port: 6379 # 端口\n    database: 1 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n      # Spring Boot Admin Server 服务端的相关配置\n      context-path: /admin # 配置 Spring\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  demo: true # 开启演示模式\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 1 # 初始连接数\n        min-idle: 1 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例\n          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例\n          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例\n          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例\n          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例\n          username: root\n          password: 123456\n        #          username: sa # SQL Server 连接的示例\n        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例\n        #          username: SYSDBA # DM 连接的示例\n        #          password: SYSDBA # DM 连接的示例\n        slave: # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 6379 # 端口\n    database: 0 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n# 日志文件配置\nlogging:\n  level:\n    # 配置自己写的 MyBatis Mapper 打印日志\n    cn.iocoder.yudao.module.promotion.dal.mysql: debug\n    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿：先禁用，Spring Boot 3.X 存在部分错误的 WARN 提示\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  env: # 多环境的配置项\n    tag: ${HOSTNAME}\n  security:\n    mock-enable: true\n  access-log: # 访问日志的配置项\n    enable: false\n  demo: false # 关闭演示模式\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: promotion-server\n\n  profiles:\n    active: local\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n    allow-bean-definition-overriding: true # 允许 Bean 覆盖，例如说 Feign 等会存在重复定义的服务\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  # Servlet 配置\n  servlet:\n    # 文件上传相关配置项\n    multipart:\n      max-file-size: 16MB # 单个文件大小\n      max-request-size: 32MB # 设置总上传的文件大小\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  # Cache 配置项\n  cache:\n    type: REDIS\n    redis:\n      time-to-live: 1h # 设置过期时间为 1 小时\n\nserver:\n  port: 48101\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\n--- #################### 接口文档配置 ####################\n\nspringdoc:\n  api-docs:\n    enabled: true # 1. 是否开启 Swagger 接文档的元数据\n    path: /v3/api-docs\n  swagger-ui:\n    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面\n    path: /swagger-ui\n  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档\n\nknife4j:\n  enable: true\n  setting:\n    language: zh_cn\n\n# MyBatis Plus 的配置项\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: NONE # “智能”模式，基于 IdTypeEnvironmentPostProcessor + 数据源的类型，自动适配成 AUTO、INPUT 模式。\n      #      id-type: AUTO # 自增 ID，适合 MySQL 等直接自增的数据库\n      #      id-type: INPUT # 用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n      #      id-type: ASSIGN_ID # 分配 ID，默认使用雪花算法。注意，Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时，需要去除实体类上的 @KeySequence 注解\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n    banner: false # 关闭控制台的 Banner 打印\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  encryptor:\n    password: XDV71a+xqStEA3WH # 加解密的秘钥，可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成\n\nmybatis-plus-join:\n  banner: false # 关闭控制台的 Banner 打印\n\n# Spring Data Redis 配置\nspring:\n  data:\n    redis:\n      repositories:\n        enabled: false # 项目未使用到 Spring Data Redis 的 Repository，所以直接禁用，保证启动速度\n\n# VO 转换（数据翻译）相关\neasy-trans:\n  is-enable-global: false # 【默认禁用，对性能确认压力大】启用全局翻译（拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置，或通过 @IgnoreTrans 忽略某个接口\n\n--- #################### RPC 远程调用相关配置 ####################\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    executor:\n      appname: ${spring.application.name} # 执行器 AppName\n      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径\n    accessToken: default_token # 执行器通讯TOKEN\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0\n    base-package: cn.iocoder.yudao.module.promotion\n  web:\n    admin-ui:\n      url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址\n  xss:\n    enable: false\n    exclude-urls: # 如下 url，仅仅是为了演示，去掉配置也没关系\n      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求\n  swagger:\n    title: 管理后台\n    description: 提供管理员管理的所有功能\n    version: ${yudao.info.version}\n  tenant: # 多租户相关配置项\n    enable: true\n    ignore-urls:\n    ignore-tables:\n\ndebug: false\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/test/resources/application-unit-test.yaml",
    "content": "spring:\n  main:\n    lazy-initialization: true # 开启懒加载，加快速度\n    banner-mode: off # 单元测试，禁用 Banner\n\n--- #################### 数据库相关配置 ####################\n\nspring:\n  # 数据源配置项\n  datasource:\n    name: ruoyi-vue-pro\n    url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式；DATABASE_TO_UPPER 配置表和字段使用小写\n    driver-class-name: org.h2.Driver\n    username: sa\n    password:\n    druid:\n      async-init: true # 单元测试，异步初始化 Druid 连接池，提升启动速度\n      initial-size: 1 # 单元测试，配置为 1，提升启动速度\n  sql:\n    init:\n      schema-locations: classpath:/sql/create_tables.sql\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 16379 # 端口（单元测试，使用 16379 端口）\n    database: 0 # 数据库索引\n\nmybatis:\n  lazy-initialization: true # 单元测试，设置 MyBatis Mapper 延迟加载，加速每个单元测试\n\n--- #################### 定时任务相关配置 ####################\n\n--- #################### 配置中心相关配置 ####################\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项（单元测试，禁用 Lock4j）\n\n--- #################### 监控相关配置 ####################\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  info:\n    base-package: cn.iocoder.yudao.module\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/test/resources/logback.xml",
    "content": "<configuration>\n    <!-- 引用 Spring Boot 的 logback 基础配置 -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n</configuration>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/test/resources/sql/clean.sql",
    "content": "DELETE FROM \"market_activity\";\nDELETE FROM \"promotion_coupon_template\";\nDELETE FROM \"promotion_coupon\";\nDELETE FROM \"promotion_reward_activity\";\nDELETE FROM \"promotion_discount_activity\";\nDELETE FROM \"promotion_discount_product\";\nDELETE FROM \"promotion_seckill_config\";\nDELETE FROM \"promotion_combination_activity\";\nDELETE FROM \"promotion_article_category\";\nDELETE FROM \"promotion_article\";\nDELETE FROM \"promotion_diy_template\";\nDELETE FROM \"promotion_diy_page\";"
  },
  {
    "path": "yudao-module-mall/yudao-module-promotion-server/src/test/resources/sql/create_tables.sql",
    "content": "CREATE TABLE IF NOT EXISTS \"market_activity\"\n(\n    \"id\"                    bigint(20)  NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"title\"                 varchar(50) NOT NULL,\n    \"activity_type\"         tinyint(4)  NOT NULL,\n    \"status\"                tinyint(4)  NOT NULL,\n    \"start_time\"            datetime    NOT NULL,\n    \"end_time\"              datetime    NOT NULL,\n    \"invalid_time\"          datetime,\n    \"delete_time\"           datetime,\n    \"time_limited_discount\" varchar(2000),\n    \"full_privilege\"        varchar(2000),\n    \"creator\"               varchar(64)          DEFAULT '',\n    \"create_time\"           datetime    NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"               varchar(64)          DEFAULT '',\n    \"update_time\"           datetime    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"               bit         NOT NULL DEFAULT FALSE,\n    \"tenant_id\"             bigint(20)  NOT NULL,\n    PRIMARY KEY (\"id\")\n) COMMENT '促销活动';\n\nCREATE TABLE IF NOT EXISTS \"promotion_coupon_template\"\n(\n    \"id\"                   bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\"                 varchar  NOT NULL,\n    \"status\"               int      NOT NULL,\n    \"total_count\"          int      NOT NULL,\n    \"take_limit_count\"     int      NOT NULL,\n    \"take_type\"            int      NOT NULL,\n    \"use_price\"            int      NOT NULL,\n    \"product_scope\"        int      NOT NULL,\n    \"product_spu_ids\"      varchar,\n    \"validity_type\"        int      NOT NULL,\n    \"valid_start_time\"     datetime,\n    \"valid_end_time\"       datetime,\n    \"fixed_start_term\"     int,\n    \"fixed_end_term\"       int,\n    \"discount_type\"        int      NOT NULL,\n    \"discount_percent\"     int,\n    \"discount_price\"       int,\n    \"discount_limit_price\" int,\n    \"take_count\"           int      NOT NULL DEFAULT 0,\n    \"use_count\"            int      NOT NULL DEFAULT 0,\n    \"creator\"              varchar           DEFAULT '',\n    \"create_time\"          datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"              varchar           DEFAULT '',\n    \"update_time\"          datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"              bit      NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '优惠劵模板';\n\nCREATE TABLE IF NOT EXISTS \"promotion_coupon\"\n(\n    \"id\"                   bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"template_id\"          bigint   NOT NULL,\n    \"name\"                 varchar  NOT NULL,\n    \"status\"               int      NOT NULL,\n    \"user_id\"              bigint   NOT NULL,\n    \"take_type\"            int      NOT NULL,\n    \"useprice\"             int      NOT NULL,\n    \"valid_start_time\"     datetime NOT NULL,\n    \"valid_end_time\"       datetime NOT NULL,\n    \"product_scope\"        int      NOT NULL,\n    \"product_spu_ids\"      varchar,\n    \"discount_type\"        int      NOT NULL,\n    \"discount_percent\"     int,\n    \"discount_price\"       int,\n    \"discount_limit_price\" int,\n    \"use_order_id\"         bigint,\n    \"use_time\"             datetime,\n    \"creator\"              varchar           DEFAULT '',\n    \"create_time\"          datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"              varchar           DEFAULT '',\n    \"update_time\"          datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"              bit      NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '优惠劵';\n\nCREATE TABLE IF NOT EXISTS \"promotion_reward_activity\"\n(\n    \"id\"              bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\"            varchar  NOT NULL,\n    \"status\"          int      NOT NULL,\n    \"start_time\"      datetime NOT NULL,\n    \"end_time\"        datetime NOT NULL,\n    \"remark\"          varchar,\n    \"condition_type\"  int      NOT NULL,\n    \"product_scope\"   int      NOT NULL,\n    \"product_spu_ids\" varchar,\n    \"rules\"           varchar,\n    \"creator\"         varchar           DEFAULT '',\n    \"create_time\"     datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"         varchar           DEFAULT '',\n    \"update_time\"     datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"         bit      NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '满减送活动';\n\nCREATE TABLE IF NOT EXISTS \"promotion_discount_activity\"\n(\n    \"id\"          bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\"        varchar  NOT NULL,\n    \"status\"      int      NOT NULL,\n    \"start_time\"  datetime NOT NULL,\n    \"end_time\"    datetime NOT NULL,\n    \"remark\"      varchar,\n    \"creator\"     varchar           DEFAULT '',\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"     varchar           DEFAULT '',\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"     bit      NOT NULL DEFAULT FALSE,\n    PRIMARY KEY (\"id\")\n) COMMENT '限时折扣活动';\n\nCREATE TABLE IF NOT EXISTS \"promotion_seckill_activity\"\n(\n    \"id\"                 bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"spu_id\"             bigint   NOT NULL,\n    \"name\"               varchar  NOT NULL,\n    \"status\"             int      NOT NULL,\n    \"remark\"             varchar,\n    \"start_time\"         varchar  NOT NULL,\n    \"end_time\"           varchar  NOT NULL,\n    \"sort\"               int      NOT NULL,\n    \"config_ids\"         varchar  NOT NULL,\n    \"order_count\"        int      NOT NULL,\n    \"user_count\"         int      NOT NULL,\n    \"total_price\"        int      NOT NULL,\n    \"total_limit_count\"  int,\n    \"single_limit_count\" int,\n    \"stock\"              int,\n    \"total_stock\"        int,\n    \"creator\"            varchar           DEFAULT '',\n    \"create_time\"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"            varchar           DEFAULT '',\n    \"update_time\"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"            bit      NOT NULL DEFAULT FALSE,\n    \"tenant_id\"          bigint   NOT NULL,\n    PRIMARY KEY (\"id\")\n) COMMENT '秒杀活动';\n\nCREATE TABLE IF NOT EXISTS \"promotion_seckill_config\"\n(\n    \"id\"          bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\"        varchar  NOT NULL,\n    \"start_time\"  varchar  NOT NULL,\n    \"end_time\"    varchar  NOT NULL,\n    \"pic_url\"     varchar  NOT NULL,\n    \"status\"      int      NOT NULL,\n    \"creator\"     varchar           DEFAULT '',\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"     varchar           DEFAULT '',\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"     bit      NOT NULL DEFAULT FALSE,\n    \"tenant_id\"   bigint   NOT NULL,\n    PRIMARY KEY (\"id\")\n) COMMENT '秒杀时段配置';\n\nCREATE TABLE IF NOT EXISTS \"promotion_combination_activity\"\n(\n    \"id\"                 bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\"               varchar  NOT NULL,\n    \"spu_id\"             bigint,\n    \"total_limit_count\"  int      NOT NULL,\n    \"single_limit_count\" int      NOT NULL,\n    \"start_time\"         varchar  NOT NULL,\n    \"end_time\"           varchar  NOT NULL,\n    \"user_size\"          int      NOT NULL,\n    \"total_num\"          int      NOT NULL,\n    \"success_num\"        int      NOT NULL,\n    \"order_user_count\"   int      NOT NULL,\n    \"virtual_group\"      int      NOT NULL,\n    \"status\"             int      NOT NULL,\n    \"limit_duration\"     int      NOT NULL,\n    \"creator\"            varchar           DEFAULT '',\n    \"create_time\"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"            varchar           DEFAULT '',\n    \"update_time\"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"            bit      NOT NULL DEFAULT FALSE,\n    \"tenant_id\"          bigint   NOT NULL,\n    PRIMARY KEY (\"id\")\n) COMMENT '拼团活动';\n\nCREATE TABLE IF NOT EXISTS \"promotion_article_category\"\n(\n    \"id\"          bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\"        varchar  NOT NULL,\n    \"pic_url\"     varchar,\n    \"status\"      int      NOT NULL,\n    \"sort\"        int      NOT NULL,\n    \"creator\"     varchar           DEFAULT '',\n    \"create_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"     varchar           DEFAULT '',\n    \"update_time\" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"     bit      NOT NULL DEFAULT FALSE,\n    \"tenant_id\"   bigint   NOT NULL,\n    PRIMARY KEY (\"id\")\n) COMMENT '文章分类表';\n\nCREATE TABLE IF NOT EXISTS \"promotion_article\"\n(\n    \"id\"               bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"category_id\"      bigint   NOT NULL,\n    \"title\"            varchar  NOT NULL,\n    \"author\"           varchar,\n    \"pic_url\"          varchar  NOT NULL,\n    \"introduction\"     varchar,\n    \"browse_count\"     varchar,\n    \"sort\"             int      NOT NULL,\n    \"status\"           int      NOT NULL,\n    \"spu_id\"           bigint   NOT NULL,\n    \"recommend_hot\"    bit      NOT NULL,\n    \"recommend_banner\" bit      NOT NULL,\n    \"content\"          varchar  NOT NULL,\n    \"creator\"          varchar           DEFAULT '',\n    \"create_time\"      datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"          varchar           DEFAULT '',\n    \"update_time\"      datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"          bit      NOT NULL DEFAULT FALSE,\n    \"tenant_id\"        bigint   NOT NULL,\n    PRIMARY KEY (\"id\")\n) COMMENT '文章管理表';\n\nCREATE TABLE IF NOT EXISTS \"promotion_diy_template\"\n(\n    \"id\"                 bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"name\"               varchar  NOT NULL,\n    \"used\"               bit      NOT NULL,\n    \"used_time\"          varchar,\n    \"remark\"             varchar,\n    \"preview_pic_urls\"   varchar,\n    \"property\"           varchar  NOT NULL,\n    \"creator\"            varchar           DEFAULT '',\n    \"create_time\"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"            varchar           DEFAULT '',\n    \"update_time\"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"            bit      NOT NULL DEFAULT FALSE,\n    \"tenant_id\"          bigint   NOT NULL DEFAULT 0,\n    PRIMARY KEY (\"id\")\n) COMMENT '装修模板';\nCREATE TABLE IF NOT EXISTS \"promotion_diy_page\"\n(\n    \"id\"                 bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n    \"template_id\"        bigint   NOT NULL,\n    \"name\"               varchar  NOT NULL,\n    \"remark\"             varchar,\n    \"preview_pic_urls\"   varchar,\n    \"property\"           varchar,\n    \"creator\"            varchar           DEFAULT '',\n    \"create_time\"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updater\"            varchar           DEFAULT '',\n    \"update_time\"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    \"deleted\"            bit      NOT NULL DEFAULT FALSE,\n    \"tenant_id\"          bigint   NOT NULL,\n    PRIMARY KEY (\"id\")\n) COMMENT '装修页面';"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-mall</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-statistics-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        statistics 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springdoc</groupId>\n            <artifactId>springdoc-openapi-ui</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-api/src/main/java/cn/iocoder/yudao/module/statistics/api/package-info.java",
    "content": "/**\n * TODO 占位，无特殊含义\n */\npackage cn.iocoder.yudao.module.statistics.api;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-api/src/main/java/cn/iocoder/yudao/module/statistics/enums/TimeRangeTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.statistics.enums;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 时间范围类型的枚举\n *\n * @author owen\n */\n@AllArgsConstructor\n@Getter\npublic enum TimeRangeTypeEnum implements ArrayValuable<Integer> {\n\n    /**\n     * 天\n     */\n    DAY(1),\n    /**\n     * 周\n     */\n    WEEK(7),\n    /**\n     * 月\n     */\n    MONTH(30),\n    /**\n     * 年\n     */\n    YEAR(365),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(TimeRangeTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-api/src/main/java/cn/iocoder/yudao/module/statistics/enums/package-info.java",
    "content": "/**\n * TODO 占位，无特殊含义\n */\npackage cn.iocoder.yudao.module.statistics.enums;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:21-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-module-statistics-server\nWORKDIR /yudao-module-statistics-server\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-module-statistics-server.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48080 端口\nEXPOSE 48101\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-mall</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-statistics-server</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        statistics 模块，主要实现统计相关功能\n        例如：统计商品、会员、交易等功能。\n    </description>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-statistics-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-promotion-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-product-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-trade-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-member-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-pay-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- Job 定时任务相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-job</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/StatisticsServerApplication.java",
    "content": "package cn.iocoder.yudao.module.statistics;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n *\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication\npublic class StatisticsServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(StatisticsServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/common/vo/DataComparisonRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.common.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Schema(description = \"管理后台 - 数据对照 Response VO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataComparisonRespVO<T> {\n\n    @Schema(description = \"当前数据\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private T value;\n\n    @Schema(description = \"参照数据\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private T reference;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/MemberStatisticsController.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.NumberUtil;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.*;\nimport cn.iocoder.yudao.module.statistics.convert.member.MemberStatisticsConvert;\nimport cn.iocoder.yudao.module.statistics.service.infra.ApiAccessLogStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.member.MemberStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.trade.TradeOrderStatisticsService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 会员统计\")\n@RestController\n@RequestMapping(\"/statistics/member\")\n@Validated\n@Slf4j\npublic class MemberStatisticsController {\n\n    @Resource\n    private MemberStatisticsService memberStatisticsService;\n    @Resource\n    private TradeOrderStatisticsService tradeOrderStatisticsService;\n    @Resource\n    private ApiAccessLogStatisticsService apiAccessLogStatisticsService;\n\n    @GetMapping(\"/summary\")\n    @Operation(summary = \"获得会员统计（实时统计）\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:member:query')\")\n    public CommonResult<MemberSummaryRespVO> getMemberSummary() {\n        return success(memberStatisticsService.getMemberSummary());\n    }\n\n    @GetMapping(\"/analyse\")\n    @Operation(summary = \"获得会员分析数据\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:member:query')\")\n    public CommonResult<MemberAnalyseRespVO> getMemberAnalyse(MemberAnalyseReqVO reqVO) {\n        // 1. 查询数据\n        LocalDateTime beginTime = ArrayUtil.get(reqVO.getTimes(), 0);\n        LocalDateTime endTime = ArrayUtil.get(reqVO.getTimes(), 1);\n        // 1.1 查询分析对照数据\n        DataComparisonRespVO<MemberAnalyseDataRespVO> comparisonData = memberStatisticsService.getMemberAnalyseComparisonData(beginTime, endTime);\n        // TODO @疯狂：这个可能有点特殊，要按照 create_time 来查询；不然它的漏斗就不统一；因为是访问数量 > 今日下单人 > 今日支付人；是一个统一的维度；\n        // 1.2 查询成交用户数量\n        Integer payUserCount = tradeOrderStatisticsService.getPayUserCount(beginTime, endTime);\n        // 1.3 计算客单价\n        int atv = 0;\n        if (payUserCount != null && payUserCount > 0) {\n            // TODO @疯狂：类似上面的 payUserCount\n            Integer payPrice = tradeOrderStatisticsService.getOrderPayPrice(beginTime, endTime);\n            atv = NumberUtil.div(payPrice, payUserCount).intValue();\n        }\n        // 1.4 查询访客数量\n        Integer visitUserCount = apiAccessLogStatisticsService.getIpCount(UserTypeEnum.MEMBER.getValue(), beginTime, endTime);\n        // 1.5 下单用户数量\n        Integer orderUserCount = tradeOrderStatisticsService.getOrderUserCount(beginTime, endTime);\n\n        // 2. 拼接返回\n        return success(MemberStatisticsConvert.INSTANCE.convert(visitUserCount, orderUserCount, payUserCount, atv, comparisonData));\n    }\n\n    @GetMapping(\"/area-statistics-list\")\n    @Operation(summary = \"按照省份，获得会员统计列表\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:member:query')\")\n    public CommonResult<List<MemberAreaStatisticsRespVO>> getMemberAreaStatisticsList() {\n        return success(memberStatisticsService.getMemberAreaStatisticsList());\n    }\n\n    @GetMapping(\"/sex-statistics-list\")\n    @Operation(summary = \"按照性别，获得会员统计列表\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:member:query')\")\n    public CommonResult<List<MemberSexStatisticsRespVO>> getMemberSexStatisticsList() {\n        return success(memberStatisticsService.getMemberSexStatisticsList());\n    }\n\n    @GetMapping(\"/terminal-statistics-list\")\n    @Operation(summary = \"按照终端，获得会员统计列表\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:member:query')\")\n    public CommonResult<List<MemberTerminalStatisticsRespVO>> getMemberTerminalStatisticsList() {\n        return success(memberStatisticsService.getMemberTerminalStatisticsList());\n    }\n\n    // TODO @疯狂：要注意 date 的排序；\n    @GetMapping(\"/user-count-comparison\")\n    @Operation(summary = \"获得用户数量对照\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:member:query')\")\n    public CommonResult<DataComparisonRespVO<MemberCountRespVO>> getUserCountComparison() {\n        return success(memberStatisticsService.getUserCountComparison());\n    }\n\n    @GetMapping(\"/register-count-list\")\n    @Operation(summary = \"获得会员注册数量列表\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:member:query')\")\n    public CommonResult<List<MemberRegisterCountRespVO>> getMemberRegisterCountList(MemberAnalyseReqVO reqVO) {\n        return success(memberStatisticsService.getMemberRegisterCountList(\n                ArrayUtil.get(reqVO.getTimes(), 0), ArrayUtil.get(reqVO.getTimes(), 1)));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberAnalyseDataRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 会员分析数据 Response VO\")\n@Data\npublic class MemberAnalyseDataRespVO {\n\n    @Schema(description = \"会员数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer registerUserCount;\n\n    @Schema(description = \"活跃用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer visitUserCount;\n\n    @Schema(description = \"充值会员数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"221\")\n    private Integer rechargeUserCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberAnalyseReqVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 会员分析 Request VO\")\n@Data\npublic class MemberAnalyseReqVO {\n\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"时间范围\")\n    private LocalDateTime[] times;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberAnalyseRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;\n\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 会员分析 Response VO\")\n@Data\npublic class MemberAnalyseRespVO {\n\n    @Schema(description = \"访客数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer visitUserCount;\n\n    @Schema(description = \"下单用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer orderUserCount;\n\n    @Schema(description = \"成交用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer payUserCount;\n\n    @Schema(description = \"客单价，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer atv;\n\n    @Schema(description = \"对照数据\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private DataComparisonRespVO<MemberAnalyseDataRespVO> comparison;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberAreaStatisticsRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 会员地区统计 Response VO\")\n@Data\npublic class MemberAreaStatisticsRespVO {\n\n    @Schema(description = \"省份编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer areaId;\n    @Schema(description = \"省份名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"浙江省\")\n    private String areaName;\n\n    @Schema(description = \"会员数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer userCount;\n\n    @Schema(description = \"下单的会员数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer orderCreateUserCount;\n    @Schema(description = \"支付订单的会员数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"512\")\n    private Integer orderPayUserCount;\n\n    @Schema(description = \"订单支付金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"622\")\n    private Integer orderPayPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberCountRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 会员数量统计 Response VO\")\n@Data\npublic class MemberCountRespVO {\n\n    @Schema(description = \"用户访问量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer visitUserCount;\n\n    @Schema(description = \"注册用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer registerUserCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberRegisterCountRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDate;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;\n\n@Schema(description = \"管理后台 - 会员注册数量 Response VO\")\n@Data\npublic class MemberRegisterCountRespVO {\n\n    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT)\n    @Schema(description = \"日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private LocalDate date;\n\n    @Schema(description = \"数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer count;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberSexStatisticsRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 会员性别统计 Response VO\")\n@Data\npublic class MemberSexStatisticsRespVO {\n\n    @Schema(description = \"性别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer sex;\n\n    // TODO @疯狂：要不还是其它字段，我们也补全，这样方便使用的用户，做定制化；就保持和 MemberAreaStatisticsRespVO 一致；\n    @Schema(description = \"会员数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer userCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 会员统计 Response VO\")\n@Data\npublic class MemberSummaryRespVO {\n\n    @Schema(description = \"会员数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer userCount;\n\n    @Schema(description = \"充值会员数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"221\")\n    private Integer rechargeUserCount;\n\n    @Schema(description = \"充值金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer rechargePrice;\n\n    // TODO @疯狂：要不干脆这个字段改成：orderPayPrice？？\n    @Schema(description = \"支出金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer expensePrice; // 只计算 mall 交易订单的支付金额，不考虑退款\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberTerminalStatisticsRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 会员终端统计 Response VO\")\n@Data\npublic class MemberTerminalStatisticsRespVO {\n\n    @Schema(description = \"终端\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer terminal;\n\n    // TODO @疯狂：要不 orderCreateUserCount 和 orderPayUserCount 貌似更统一一些；\n    @Schema(description = \"会员数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer userCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/pay/PayStatisticsController.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.pay;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.statistics.controller.admin.pay.vo.PaySummaryRespVO;\nimport cn.iocoder.yudao.module.statistics.convert.pay.PayStatisticsConvert;\nimport cn.iocoder.yudao.module.statistics.service.pay.PayWalletStatisticsService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 支付统计\")\n@RestController\n@RequestMapping(\"/statistics/pay\")\n@Validated\n@Slf4j\npublic class PayStatisticsController {\n\n    @Resource\n    private PayWalletStatisticsService payWalletStatisticsService;\n\n    @GetMapping(\"/summary\")\n    @Operation(summary = \"获取充值金额\")\n    public CommonResult<PaySummaryRespVO> getWalletRechargePrice() {\n        Integer rechargePrice = payWalletStatisticsService.getRechargePriceSummary();\n        return success(PayStatisticsConvert.INSTANCE.convert(rechargePrice));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/pay/vo/PaySummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.pay.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 支付统计 Response VO\")\n@Data\npublic class PaySummaryRespVO {\n\n    @Schema(description = \"充值金额，单位分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer rechargePrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/product/ProductStatisticsController.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.SortablePageParam;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;\nimport cn.iocoder.yudao.module.statistics.service.product.ProductStatisticsService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 商品统计\")\n@RestController\n@RequestMapping(\"/statistics/product\")\n@Validated\npublic class ProductStatisticsController {\n\n    @Resource\n    private ProductStatisticsService productStatisticsService;\n\n    @Resource\n    private ProductSpuApi productSpuApi;\n\n    @GetMapping(\"/analyse\")\n    @Operation(summary = \"获得商品统计分析\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:product:query')\")\n    public CommonResult<DataComparisonRespVO<ProductStatisticsRespVO>> getProductStatisticsAnalyse(ProductStatisticsReqVO reqVO) {\n        return success(productStatisticsService.getProductStatisticsAnalyse(reqVO));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得商品统计明细（日期维度）\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:product:query')\")\n    public CommonResult<List<ProductStatisticsRespVO>> getProductStatisticsList(ProductStatisticsReqVO reqVO) {\n        List<ProductStatisticsDO> list = productStatisticsService.getProductStatisticsList(reqVO);\n        return success(BeanUtils.toBean(list, ProductStatisticsRespVO.class));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出获得商品统计明细 Excel（日期维度）\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:product:export')\")\n    public void exportProductStatisticsExcel(ProductStatisticsReqVO reqVO, HttpServletResponse response) throws IOException {\n        List<ProductStatisticsDO> list = productStatisticsService.getProductStatisticsList(reqVO);\n        // 导出 Excel\n        List<ProductStatisticsRespVO> voList = BeanUtils.toBean(list, ProductStatisticsRespVO.class);\n        ExcelUtils.write(response, \"商品状况.xls\", \"数据\", ProductStatisticsRespVO.class, voList);\n    }\n\n    @GetMapping(\"/rank-page\")\n    @Operation(summary = \"获得商品统计排行榜分页（商品维度）\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:product:query')\")\n    public CommonResult<PageResult<ProductStatisticsRespVO>> getProductStatisticsRankPage(@Valid ProductStatisticsReqVO reqVO,\n                                                                                          @Valid SortablePageParam pageParam) {\n        PageResult<ProductStatisticsDO> pageResult = productStatisticsService.getProductStatisticsRankPage(reqVO, pageParam);\n        // 处理商品信息\n        Set<Long> spuIds = convertSet(pageResult.getList(), ProductStatisticsDO::getSpuId);\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(productSpuApi.getSpuList(spuIds).getCheckedData(), ProductSpuRespDTO::getId);\n        return success(BeanUtils.toBean(pageResult, ProductStatisticsRespVO.class,\n                item -> Optional.ofNullable(spuMap.get(item.getSpuId()))\n                        .ifPresent(spu -> item.setName(spu.getName()).setPicUrl(spu.getPicUrl()))));\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/product/vo/ProductStatisticsReqVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.product.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 商品统计分析 Request VO\")\n@Data\n@ToString(callSuper = true)\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ProductStatisticsReqVO {\n\n    @Schema(description = \"统计时间范围\", example = \"[2022-07-01 00:00:00, 2022-07-01 23:59:59]\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] times;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/product/vo/ProductStatisticsRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.product.vo;\n\nimport cn.idev.excel.annotation.ExcelIgnoreUnannotated;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDate;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;\n\n@Schema(description = \"管理后台 - 商品统计 Response VO\")\n@Data\n@ExcelIgnoreUnannotated\npublic class ProductStatisticsRespVO {\n\n    @Schema(description = \"编号，主键自增\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"12393\")\n    private Long id;\n\n    @Schema(description = \"统计日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2023-12-16\")\n    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)\n    @ExcelProperty(\"统计日期\")\n    private LocalDate time;\n\n    @Schema(description = \"商品SPU编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15114\")\n    @ExcelProperty(\"商品SPU编号\")\n    private Long spuId;\n\n    // region 商品信息\n\n    @Schema(description = \"商品名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"商品名称\")\n    @ExcelProperty(\"商品名称\")\n    private String name;\n\n    @Schema(description = \"商品封面图\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15114\")\n    @ExcelProperty(\"商品封面图\")\n    private String picUrl;\n\n    // endregion\n\n    @Schema(description = \"浏览量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"17505\")\n    @ExcelProperty(\"浏览量\")\n    private Integer browseCount;\n\n    @Schema(description = \"访客量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11814\")\n    @ExcelProperty(\"访客量\")\n    private Integer browseUserCount;\n\n    @Schema(description = \"收藏数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20950\")\n    @ExcelProperty(\"收藏数量\")\n    private Integer favoriteCount;\n\n    @Schema(description = \"加购数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28493\")\n    @ExcelProperty(\"加购数量\")\n    private Integer cartCount;\n\n    @Schema(description = \"下单件数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18966\")\n    @ExcelProperty(\"下单件数\")\n    private Integer orderCount;\n\n    @Schema(description = \"支付件数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15142\")\n    @ExcelProperty(\"支付件数\")\n    private Integer orderPayCount;\n\n    @Schema(description = \"支付金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11595\")\n    @ExcelProperty(\"支付金额，单位：分\")\n    private Integer orderPayPrice;\n\n    @Schema(description = \"退款件数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2591\")\n    @ExcelProperty(\"退款件数\")\n    private Integer afterSaleCount;\n\n    @Schema(description = \"退款金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"21709\")\n    @ExcelProperty(\"退款金额，单位：分\")\n    private Integer afterSaleRefundPrice;\n\n    @Schema(description = \"访客支付转化率（百分比）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15\")\n    private Integer browseConvertPercent;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/TradeStatisticsController.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.trade;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.*;\nimport cn.iocoder.yudao.module.statistics.convert.trade.TradeStatisticsConvert;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;\nimport cn.iocoder.yudao.module.statistics.service.trade.AfterSaleStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.trade.BrokerageStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.trade.TradeOrderStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.trade.TradeStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;\nimport cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;\nimport cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;\nimport cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 交易统计\")\n@RestController\n@RequestMapping(\"/statistics/trade\")\n@Validated\n@Slf4j\npublic class TradeStatisticsController {\n\n    @Resource\n    private TradeStatisticsService tradeStatisticsService;\n    @Resource\n    private TradeOrderStatisticsService tradeOrderStatisticsService;\n    @Resource\n    private AfterSaleStatisticsService afterSaleStatisticsService;\n    @Resource\n    private BrokerageStatisticsService brokerageStatisticsService;\n\n    @GetMapping(\"/summary\")\n    @Operation(summary = \"获得交易统计\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:trade:query')\")\n    public CommonResult<DataComparisonRespVO<TradeSummaryRespVO>> getTradeSummaryComparison() {\n        // 1.1 昨天的数据\n        TradeSummaryRespBO yesterdayData = tradeStatisticsService.getTradeSummaryByDays(-1);\n        // 1.2 前天的数据（用于对照昨天的数据）\n        TradeSummaryRespBO beforeYesterdayData = tradeStatisticsService.getTradeSummaryByDays(-2);\n\n        // 2.1 本月数据\n        TradeSummaryRespBO monthData = tradeStatisticsService.getTradeSummaryByMonths(0);\n        // 2.2 上月数据（用于对照本月的数据）\n        TradeSummaryRespBO lastMonthData = tradeStatisticsService.getTradeSummaryByMonths(-1);\n        // 拼接数据\n        return success(TradeStatisticsConvert.INSTANCE.convert(yesterdayData, beforeYesterdayData, monthData, lastMonthData));\n    }\n\n    @GetMapping(\"/analyse\")\n    @Operation(summary = \"获得交易状况统计\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:trade:query')\")\n    public CommonResult<DataComparisonRespVO<TradeTrendSummaryRespVO>> getTradeStatisticsAnalyse(TradeTrendReqVO reqVO) {\n        return success(tradeStatisticsService.getTradeStatisticsAnalyse(ArrayUtil.get(reqVO.getTimes(), 0),\n                ArrayUtil.get(reqVO.getTimes(), 1)));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得交易状况明细\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:trade:query')\")\n    public CommonResult<List<TradeTrendSummaryRespVO>> getTradeStatisticsList(TradeTrendReqVO reqVO) {\n        List<TradeStatisticsDO> list = tradeStatisticsService.getTradeStatisticsList(ArrayUtil.get(reqVO.getTimes(), 0),\n                ArrayUtil.get(reqVO.getTimes(), 1));\n        return success(TradeStatisticsConvert.INSTANCE.convertList(list));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出获得交易状况明细 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:trade:export')\")\n    public void exportTradeStatisticsExcel(TradeTrendReqVO reqVO, HttpServletResponse response) throws IOException {\n        List<TradeStatisticsDO> list = tradeStatisticsService.getTradeStatisticsList(ArrayUtil.get(reqVO.getTimes(), 0),\n                ArrayUtil.get(reqVO.getTimes(), 1));\n        // 导出 Excel\n        List<TradeTrendSummaryRespVO> voList = TradeStatisticsConvert.INSTANCE.convertList(list);\n        List<TradeTrendSummaryExcelVO> data = TradeStatisticsConvert.INSTANCE.convertList02(voList);\n        ExcelUtils.write(response, \"交易状况.xls\", \"数据\", TradeTrendSummaryExcelVO.class, data);\n    }\n\n    @GetMapping(\"/order-count\")\n    @Operation(summary = \"获得交易订单数量\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:trade:query')\")\n    public CommonResult<TradeOrderCountRespVO> getOrderCount() {\n        // 订单统计\n        Long undeliveredCount = tradeOrderStatisticsService.getCountByStatusAndDeliveryType(\n                TradeOrderStatusEnum.UNDELIVERED.getStatus(), DeliveryTypeEnum.EXPRESS.getType());\n        // TODO @疯狂：订单支付后，如果是门店自提的，需要 update 成 DELIVERED；；目前还没搞~~突然反应过来\n        Long pickUpCount = tradeOrderStatisticsService.getCountByStatusAndDeliveryType(\n                TradeOrderStatusEnum.DELIVERED.getStatus(), DeliveryTypeEnum.PICK_UP.getType());\n        // 售后统计\n        Long afterSaleApplyCount = afterSaleStatisticsService.getCountByStatus(AfterSaleStatusEnum.APPLY);\n        Long auditingWithdrawCount = brokerageStatisticsService.getWithdrawCountByStatus(BrokerageWithdrawStatusEnum.AUDITING);\n        // 拼接返回\n        return success(TradeStatisticsConvert.INSTANCE.convert(undeliveredCount, pickUpCount, afterSaleApplyCount, auditingWithdrawCount));\n    }\n\n    @GetMapping(\"/order-comparison\")\n    @Operation(summary = \"获得交易订单数量\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:trade:query')\")\n    public CommonResult<DataComparisonRespVO<TradeOrderSummaryRespVO>> getOrderComparison() {\n        return success(tradeOrderStatisticsService.getOrderComparison());\n    }\n\n    @GetMapping(\"/order-count-trend\")\n    @Operation(summary = \"获得订单量趋势统计\")\n    @PreAuthorize(\"@ss.hasPermission('statistics:trade:query')\")\n    public CommonResult<List<DataComparisonRespVO<TradeOrderTrendRespVO>>> getOrderCountTrendComparison(@Valid TradeOrderTrendReqVO reqVO) {\n        // TODO @疯狂：要注意 date 的排序；\n        return success(tradeOrderStatisticsService.getOrderCountTrendComparison(reqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeOrderCountRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 交易订单数量 Response VO\")\n@Data\npublic class TradeOrderCountRespVO {\n\n    @Schema(description = \"待发货\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long undelivered;\n\n    @Schema(description = \"待核销\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long pickUp;\n\n    @Schema(description = \"退款中\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long afterSaleApply;\n\n    @Schema(description = \"提现待审核\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long auditingWithdraw;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeOrderSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 交易订单统计 Response VO\")\n@Data\npublic class TradeOrderSummaryRespVO {\n\n    @Schema(description = \"支付订单商品数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer orderPayCount;\n\n    @Schema(description = \"总支付金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer orderPayPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeOrderTrendReqVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.statistics.enums.TimeRangeTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 交易订单量趋势统计 Request VO\")\n@Data\npublic class TradeOrderTrendReqVO {\n\n    @Schema(description = \"日期范围类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"日期范围类型不能为空\")\n    @InEnum(value = TimeRangeTypeEnum.class, message = \"日期范围类型，必须是 {value}\")\n    private Integer type;\n\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"起始时间\")\n    private LocalDateTime beginTime;\n\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"截止时间\")\n    private LocalDateTime endTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeOrderTrendRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 订单量趋势统计 Response VO\")\n@Data\npublic class TradeOrderTrendRespVO {\n\n    @Schema(description = \"日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private String date;\n\n    @Schema(description = \"订单数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer orderPayCount;\n\n    @Schema(description = \"订单支付金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer orderPayPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 交易统计 Response VO\")\n@Data\npublic class TradeSummaryRespVO {\n\n    @Schema(description = \"昨日订单数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer yesterdayOrderCount;\n    @Schema(description = \"昨日支付金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer yesterdayPayPrice;\n\n    @Schema(description = \"本月订单数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer monthOrderCount;\n    @Schema(description = \"本月支付金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer monthPayPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeTrendReqVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 交易状况 Request VO\")\n@Data\npublic class TradeTrendReqVO {\n\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @Schema(description = \"时间范围\")\n    private LocalDateTime[] times;\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeTrendSummaryExcelVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;\n\nimport cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport cn.idev.excel.annotation.format.DateTimeFormat;\nimport lombok.Data;\n\nimport java.time.LocalDate;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;\n\n/**\n * 交易状况统计 Excel VO\n *\n * @author owen\n */\n@Data\npublic class TradeTrendSummaryExcelVO {\n\n    @ExcelProperty(value = \"日期\")\n    @DateTimeFormat(FORMAT_YEAR_MONTH_DAY)\n    private LocalDate date;\n\n    @ExcelProperty(value = \"营业额\", converter = MoneyConvert.class)\n    private Integer turnoverPrice;\n\n    @ExcelProperty(value = \"商品支付金额\", converter = MoneyConvert.class)\n    private Integer orderPayPrice;\n\n    @ExcelProperty(value = \"充值金额\", converter = MoneyConvert.class)\n    private Integer rechargePrice;\n\n    @ExcelProperty(value = \"支出金额\", converter = MoneyConvert.class)\n    private Integer expensePrice;\n\n    @ExcelProperty(value = \"余额支付金额\", converter = MoneyConvert.class)\n    private Integer walletPayPrice;\n\n    @ExcelProperty(value = \"支付佣金金额\", converter = MoneyConvert.class)\n    private Integer brokerageSettlementPrice;\n\n    @ExcelProperty(value = \"商品退款金额\", converter = MoneyConvert.class)\n    private Integer afterSaleRefundPrice;\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeTrendSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDate;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;\n\n@Schema(description = \"管理后台 - 交易状况统计 Response VO\")\n@Data\npublic class TradeTrendSummaryRespVO {\n\n    @Schema(description = \"日期\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2023-12-16\")\n    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)\n    private LocalDate date;\n\n    @Schema(description = \"营业额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer turnoverPrice; // 营业额 = 商品支付金额 + 充值金额\n\n    @Schema(description = \"订单支付金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer orderPayPrice;\n\n    @Schema(description = \"余额支付金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer walletPayPrice;\n\n    @Schema(description = \"订单退款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer afterSaleRefundPrice;\n\n    @Schema(description = \"支付佣金金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer brokerageSettlementPrice;\n\n    @Schema(description = \"充值金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer rechargePrice;\n\n    @Schema(description = \"支出金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer expensePrice; // 余额支付金额 + 支付佣金金额 + 商品退款金额\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/controller/app/package-info.java",
    "content": "/**\n * TODO 芋艿：占位\n */\npackage cn.iocoder.yudao.module.statistics.controller.app;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/convert/member/MemberStatisticsConvert.java",
    "content": "package cn.iocoder.yudao.module.statistics.convert.member;\n\nimport cn.hutool.core.map.MapUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAnalyseDataRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAnalyseRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSummaryRespVO;\nimport cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;\nimport cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * 会员统计 Convert\n *\n * @author owen\n */\n@Mapper\npublic interface MemberStatisticsConvert {\n\n    MemberStatisticsConvert INSTANCE = Mappers.getMapper(MemberStatisticsConvert.class);\n\n    default List<MemberAreaStatisticsRespVO> convertList(List<Area> areaList,\n                                                         Map<Integer, Integer> userCountMap,\n                                                         Map<Integer, MemberAreaStatisticsRespBO> orderMap) {\n        return CollectionUtils.convertList(areaList, area -> {\n            MemberAreaStatisticsRespBO orderVo = Optional.ofNullable(orderMap.get(area.getId()))\n                    .orElseGet(MemberAreaStatisticsRespBO::new);\n            return new MemberAreaStatisticsRespVO()\n                    .setAreaId(area.getId()).setAreaName(area.getName())\n                    .setUserCount(MapUtil.getInt(userCountMap, area.getId(), 0))\n                    .setOrderCreateUserCount(ObjUtil.defaultIfNull(orderVo.getOrderCreateUserCount(), 0))\n                    .setOrderPayUserCount(ObjUtil.defaultIfNull(orderVo.getOrderPayUserCount(), 0))\n                    .setOrderPayPrice(ObjUtil.defaultIfNull(orderVo.getOrderPayPrice(), 0));\n        });\n    }\n\n    MemberSummaryRespVO convert(RechargeSummaryRespBO rechargeSummary, Integer expensePrice, Integer userCount);\n\n    MemberAnalyseRespVO convert(Integer visitUserCount, Integer orderUserCount, Integer payUserCount, int atv,\n                                DataComparisonRespVO<MemberAnalyseDataRespVO> comparison);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/convert/pay/PayStatisticsConvert.java",
    "content": "package cn.iocoder.yudao.module.statistics.convert.pay;\n\nimport cn.iocoder.yudao.module.statistics.controller.admin.pay.vo.PaySummaryRespVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n/**\n * 支付统计 Convert\n *\n * @author owen\n */\n@Mapper\npublic interface PayStatisticsConvert {\n\n    PayStatisticsConvert INSTANCE = Mappers.getMapper(PayStatisticsConvert.class);\n\n    PaySummaryRespVO convert(Integer rechargePrice);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/convert/trade/TradeStatisticsConvert.java",
    "content": "package cn.iocoder.yudao.module.statistics.convert.trade;\n\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderCountRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeSummaryRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryExcelVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;\nimport org.mapstruct.IterableMapping;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Named;\nimport org.mapstruct.factory.Mappers;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 交易统计 Convert\n *\n * @author owen\n */\n@Mapper\npublic interface TradeStatisticsConvert {\n\n    TradeStatisticsConvert INSTANCE = Mappers.getMapper(TradeStatisticsConvert.class);\n\n    default DataComparisonRespVO<TradeSummaryRespVO> convert(TradeSummaryRespBO yesterdayData,\n                                                             TradeSummaryRespBO beforeYesterdayData,\n                                                             TradeSummaryRespBO monthData,\n                                                             TradeSummaryRespBO lastMonthData) {\n        return convert(convert(yesterdayData, monthData), convert(beforeYesterdayData, lastMonthData));\n    }\n\n\n    default TradeSummaryRespVO convert(TradeSummaryRespBO yesterdayData, TradeSummaryRespBO monthData) {\n        return new TradeSummaryRespVO()\n                .setYesterdayOrderCount(yesterdayData.getCount()).setYesterdayPayPrice(yesterdayData.getSummary())\n                .setMonthOrderCount(monthData.getCount()).setMonthPayPrice(monthData.getSummary());\n    }\n\n    DataComparisonRespVO<TradeSummaryRespVO> convert(TradeSummaryRespVO value, TradeSummaryRespVO reference);\n\n    DataComparisonRespVO<TradeTrendSummaryRespVO> convert(TradeTrendSummaryRespVO value,\n                                                          TradeTrendSummaryRespVO reference);\n\n    List<TradeTrendSummaryExcelVO> convertList02(List<TradeTrendSummaryRespVO> list);\n\n    TradeStatisticsDO convert(LocalDateTime time, TradeOrderSummaryRespBO orderSummary,\n                              AfterSaleSummaryRespBO afterSaleSummary, Integer brokerageSettlementPrice,\n                              WalletSummaryRespBO walletSummary);\n\n    @IterableMapping(qualifiedByName = \"convert\")\n    List<TradeTrendSummaryRespVO> convertList(List<TradeStatisticsDO> list);\n\n    TradeTrendSummaryRespVO convertA(TradeStatisticsDO tradeStatistics);\n\n    @Named(\"convert\")\n    default TradeTrendSummaryRespVO convert(TradeStatisticsDO tradeStatistics) {\n        TradeTrendSummaryRespVO vo = convertA(tradeStatistics);\n        return vo\n                .setDate(tradeStatistics.getTime().toLocalDate())\n                // 营业额 = 商品支付金额 + 充值金额\n                .setTurnoverPrice(tradeStatistics.getOrderPayPrice() + tradeStatistics.getRechargePayPrice())\n                // 支出金额 = 余额支付金额 + 支付佣金金额 + 商品退款金额\n                .setExpensePrice(tradeStatistics.getWalletPayPrice() + tradeStatistics.getBrokerageSettlementPrice() + tradeStatistics.getAfterSaleRefundPrice());\n    }\n\n    TradeOrderCountRespVO convert(Long undelivered, Long pickUp, Long afterSaleApply, Long auditingWithdraw);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/dataobject/package-info.java",
    "content": "/**\n * 占位 todo\n */\npackage cn.iocoder.yudao.module.statistics.dal.dataobject;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/dataobject/product/ProductStatisticsDO.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.dataobject.product;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDate;\n\n/**\n * 商品统计 DO\n *\n * @author owen\n */\n@TableName(\"product_statistics\")\n@KeySequence(\"product_statistics_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ProductStatisticsDO extends BaseDO {\n\n    /**\n     * 编号，主键自增\n     */\n    @TableId\n    private Long id;\n    /**\n     * 统计日期\n     */\n    private LocalDate time;\n    /**\n     * 商品 SPU 编号\n     */\n    private Long spuId;\n    /**\n     * 浏览量\n     */\n    private Integer browseCount;\n    /**\n     * 访客量\n     */\n    private Integer browseUserCount;\n    /**\n     * 收藏数量\n     */\n    private Integer favoriteCount;\n    /**\n     * 加购数量\n     */\n    private Integer cartCount;\n    /**\n     * 下单件数\n     */\n    private Integer orderCount;\n    /**\n     * 支付件数\n     */\n    private Integer orderPayCount;\n    /**\n     * 支付金额，单位：分\n     */\n    private Integer orderPayPrice;\n    /**\n     * 退款件数\n     */\n    private Integer afterSaleCount;\n    /**\n     * 退款金额，单位：分\n     */\n    private Integer afterSaleRefundPrice;\n    /**\n     * 访客支付转化率（百分比）\n     */\n    private Integer browseConvertPercent;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/dataobject/trade/TradeStatisticsDO.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.dataobject.trade;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * 交易统计 DO\n * <p>\n * 以天为维度，统计全部的数据\n *\n * @author 芋道源码\n */\n@TableName(\"trade_statistics\")\n@KeySequence(\"trade_statistics_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TradeStatisticsDO extends BaseDO {\n\n    /**\n     * 编号，主键自增\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 统计日期\n     */\n    private LocalDateTime time;\n\n    /**\n     * 创建订单数\n     */\n    private Integer orderCreateCount;\n    /**\n     * 支付订单商品数\n     */\n    private Integer orderPayCount;\n    /**\n     * 总支付金额，单位：分\n     */\n    private Integer orderPayPrice;\n\n    /**\n     * 退款订单数\n     */\n    private Integer afterSaleCount;\n    /**\n     * 总退款金额，单位：分\n     */\n    private Integer afterSaleRefundPrice;\n\n    /**\n     * 佣金金额（已结算），单位：分\n     */\n    private Integer brokerageSettlementPrice;\n\n    /**\n     * 总支付金额（余额），单位：分\n     */\n    private Integer walletPayPrice;\n    /**\n     * 充值订单数\n     * <p>\n     * 从 PayWalletRechargeDO 计算\n     */\n    private Integer rechargePayCount;\n    /**\n     * 充值金额，单位：分\n     */\n    private Integer rechargePayPrice;\n    /**\n     * 充值退款订单数\n     */\n    private Integer rechargeRefundCount;\n    /**\n     * 充值退款金额，单位：分\n     */\n    private Integer rechargeRefundPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/infra/ApiAccessLogStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.mysql.infra;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\n\n// TODO @芋艿：api 访问日志，现在会清理，可能要单独有个偏业务的访问表；\n/**\n * API 访问日志的统计 Mapper\n *\n * @author owen\n */\n@Mapper\n@SuppressWarnings(\"rawtypes\")\npublic interface ApiAccessLogStatisticsMapper extends BaseMapperX {\n\n    Integer selectIpCountByUserTypeAndCreateTimeBetween(@Param(\"userType\") Integer userType,\n                                                        @Param(\"beginTime\") LocalDateTime beginTime,\n                                                        @Param(\"endTime\") LocalDateTime endTime);\n\n    Integer selectUserCountByUserTypeAndCreateTimeBetween(@Param(\"userType\") Integer userType,\n                                                          @Param(\"beginTime\") LocalDateTime beginTime,\n                                                          @Param(\"endTime\") LocalDateTime endTime);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/member/MemberStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.mysql.member;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberRegisterCountRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSexStatisticsRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberTerminalStatisticsRespVO;\nimport cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 会员信息的统计 Mapper\n *\n * @author owen\n */\n@Mapper\n@SuppressWarnings(\"rawtypes\")\npublic interface MemberStatisticsMapper extends BaseMapperX {\n\n    List<MemberAreaStatisticsRespBO> selectSummaryListByAreaId();\n\n    List<MemberSexStatisticsRespVO> selectSummaryListBySex();\n\n    List<MemberTerminalStatisticsRespVO> selectSummaryListByRegisterTerminal();\n\n    Integer selectUserCount(@Param(\"beginTime\") LocalDateTime beginTime,\n                            @Param(\"endTime\") LocalDateTime endTime);\n\n    /**\n     * 获得用户的每天注册数量列表\n     *\n     * @param beginTime 开始时间\n     * @param endTime 结束时间\n     * @return 每天注册数量列表\n     */\n    List<MemberRegisterCountRespVO> selectListByCreateTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                                  @Param(\"endTime\") LocalDateTime endTime);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/pay/PayWalletStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.mysql.pay;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\n\n/**\n * 支付钱包的统计 Mapper\n *\n * @author owen\n */\n@Mapper\n@SuppressWarnings(\"rawtypes\")\npublic interface PayWalletStatisticsMapper extends BaseMapperX {\n\n    WalletSummaryRespBO selectRechargeSummaryByPayTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                              @Param(\"endTime\") LocalDateTime endTime,\n                                                              @Param(\"payStatus\") Boolean payStatus);\n\n    WalletSummaryRespBO selectRechargeSummaryByRefundTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                                 @Param(\"endTime\") LocalDateTime endTime,\n                                                                 @Param(\"refundStatus\") Integer refundStatus);\n\n    Integer selectPriceSummaryByBizTypeAndCreateTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                            @Param(\"endTime\") LocalDateTime endTime,\n                                                            @Param(\"bizType\") Integer bizType);\n\n    RechargeSummaryRespBO selectRechargeSummaryGroupByWalletId(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                               @Param(\"endTime\") LocalDateTime endTime,\n                                                               @Param(\"payStatus\") Boolean payStatus);\n\n    Integer selectRechargePriceSummary(@Param(\"payStatus\") Boolean payStatus);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.mysql.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.SortablePageParam;\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;\nimport cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils.toUnderlineCase;\n\n/**\n * 商品统计 Mapper\n *\n * @author owen\n */\n@Mapper\npublic interface ProductStatisticsMapper extends BaseMapperX<ProductStatisticsDO> {\n\n    default PageResult<ProductStatisticsDO> selectPageGroupBySpuId(ProductStatisticsReqVO reqVO, SortablePageParam pageParam) {\n        return selectPage(pageParam, buildWrapper(reqVO)\n                .groupBy(ProductStatisticsDO::getSpuId)\n                .select(ProductStatisticsDO::getSpuId)\n        );\n    }\n\n    default List<ProductStatisticsDO> selectListByTimeBetween(ProductStatisticsReqVO reqVO) {\n        return selectList(buildWrapper(reqVO)\n                .groupBy(ProductStatisticsDO::getTime)\n                .select(ProductStatisticsDO::getTime));\n    }\n\n    default ProductStatisticsRespVO selectVoByTimeBetween(ProductStatisticsReqVO reqVO) {\n        return selectJoinOne(ProductStatisticsRespVO.class, buildWrapper(reqVO));\n    }\n\n    /**\n     * 构建 LambdaWrapper\n     *\n     * @param reqVO 查询参数\n     * @return LambdaWrapper\n     */\n    static MPJLambdaWrapperX<ProductStatisticsDO> buildWrapper(ProductStatisticsReqVO reqVO) {\n        return new MPJLambdaWrapperX<ProductStatisticsDO>()\n                .betweenIfPresent(ProductStatisticsDO::getTime, reqVO.getTimes())\n                .selectSum(ProductStatisticsDO::getBrowseCount, toUnderlineCase(ProductStatisticsDO::getBrowseCount))\n                .selectSum(ProductStatisticsDO::getBrowseUserCount, toUnderlineCase(ProductStatisticsDO::getBrowseUserCount))\n                .selectSum(ProductStatisticsDO::getFavoriteCount, toUnderlineCase(ProductStatisticsDO::getFavoriteCount))\n                .selectSum(ProductStatisticsDO::getCartCount, toUnderlineCase(ProductStatisticsDO::getCartCount))\n                .selectSum(ProductStatisticsDO::getOrderCount, toUnderlineCase(ProductStatisticsDO::getOrderCount))\n                .selectSum(ProductStatisticsDO::getOrderPayCount, toUnderlineCase(ProductStatisticsDO::getOrderPayCount))\n                .selectSum(ProductStatisticsDO::getOrderPayPrice, toUnderlineCase(ProductStatisticsDO::getOrderPayPrice))\n                .selectSum(ProductStatisticsDO::getAfterSaleCount, toUnderlineCase(ProductStatisticsDO::getAfterSaleCount))\n                .selectSum(ProductStatisticsDO::getAfterSaleRefundPrice, toUnderlineCase(ProductStatisticsDO::getAfterSaleRefundPrice))\n                .selectAvg(ProductStatisticsDO::getBrowseConvertPercent, toUnderlineCase(ProductStatisticsDO::getBrowseConvertPercent));\n    }\n\n    /**\n     * 根据时间范围统计商品信息\n     *\n     * @param page      分页参数\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 统计\n     */\n    IPage<ProductStatisticsDO> selectStatisticsResultPageByTimeBetween(IPage<ProductStatisticsDO> page,\n                                                                       @Param(\"beginTime\") LocalDateTime beginTime,\n                                                                       @Param(\"endTime\") LocalDateTime endTime);\n\n    default Long selectCountByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {\n        return selectCount(new LambdaQueryWrapperX<ProductStatisticsDO>().between(ProductStatisticsDO::getTime, beginTime, endTime));\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/AfterSaleStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.mysql.trade;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\n\n/**\n * 售后订单的统计 Mapper\n *\n * @author owen\n */\n@Mapper\npublic interface AfterSaleStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {\n\n    AfterSaleSummaryRespBO selectSummaryByRefundTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                            @Param(\"endTime\") LocalDateTime endTime);\n\n    Long selectCountByStatus(@Param(\"status\") Integer status);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/BrokerageStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.mysql.trade;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\n\n/**\n * 订单分销的统计 Mapper\n *\n * @author owen\n */\n@Mapper\npublic interface BrokerageStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {\n\n    Integer selectSummaryPriceByStatusAndUnfreezeTimeBetween(@Param(\"bizType\") Integer bizType,\n                                                             @Param(\"status\") Integer status,\n                                                             @Param(\"beginTime\") LocalDateTime beginTime,\n                                                             @Param(\"endTime\") LocalDateTime endTime);\n\n    Long selectWithdrawCountByStatus(@Param(\"status\") Integer status);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeOrderStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.mysql.trade;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderSummaryRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendRespVO;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;\nimport cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 交易订单的统计 Mapper\n *\n * @author owen\n */\n@Mapper\npublic interface TradeOrderStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {\n\n    List<MemberAreaStatisticsRespBO> selectSummaryListByAreaId();\n\n    Integer selectCountByCreateTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                           @Param(\"endTime\") LocalDateTime endTime);\n\n    Integer selectCountByPayTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                        @Param(\"endTime\") LocalDateTime endTime);\n\n    Integer selectSummaryPriceByPayTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                               @Param(\"endTime\") LocalDateTime endTime);\n\n    Integer selectUserCountByCreateTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                               @Param(\"endTime\") LocalDateTime endTime);\n\n    Integer selectUserCountByPayTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                            @Param(\"endTime\") LocalDateTime endTime);\n\n    /**\n     * 按照支付时间统计订单（按天分组）\n     *\n     * @param beginTime 支付起始时间\n     * @param endTime   支付截止时间\n     * @return 订单统计列表\n     */\n    List<TradeOrderTrendRespVO> selectListByPayTimeBetweenAndGroupByDay(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                                        @Param(\"endTime\") LocalDateTime endTime);\n\n    /**\n     * 按照支付时间统计订单（按月分组）\n     *\n     * @param beginTime 支付起始时间\n     * @param endTime   支付截止时间\n     * @return 订单统计列表\n     */\n    List<TradeOrderTrendRespVO> selectListByPayTimeBetweenAndGroupByMonth(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                                          @Param(\"endTime\") LocalDateTime endTime);\n\n    Long selectCountByStatusAndDeliveryType(@Param(\"status\") Integer status, @Param(\"deliveryType\") Integer deliveryType);\n\n    TradeOrderSummaryRespVO selectPaySummaryByPayStatusAndPayTimeBetween(@Param(\"payStatus\") Boolean payStatus,\n                                                                         @Param(\"beginTime\") LocalDateTime beginTime,\n                                                                         @Param(\"endTime\") LocalDateTime endTime);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeStatisticsMapper.java",
    "content": "package cn.iocoder.yudao.module.statistics.dal.mysql.trade;\n\nimport cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;\nimport cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 交易统计 Mapper\n *\n * @author owen\n */\n@Mapper\npublic interface TradeStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {\n\n    TradeSummaryRespBO selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                                                 @Param(\"endTime\") LocalDateTime endTime);\n\n    TradeTrendSummaryRespVO selectVoByTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                                  @Param(\"endTime\") LocalDateTime endTime);\n\n    default List<TradeStatisticsDO> selectListByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {\n        return selectList(new LambdaQueryWrapperX<TradeStatisticsDO>()\n                .between(TradeStatisticsDO::getTime, beginTime, endTime));\n    }\n\n    Integer selectExpensePriceByTimeBetween(@Param(\"beginTime\") LocalDateTime beginTime,\n                                            @Param(\"endTime\") LocalDateTime endTime);\n\n    default TradeStatisticsDO selectByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {\n        return selectOne(new LambdaQueryWrapperX<TradeStatisticsDO>()\n                .between(TradeStatisticsDO::getTime, beginTime, endTime));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/framework/package-info.java",
    "content": "/**\n * 属于 statistics 模块的 framework 封装\n *\n * @author 芋道源码\n */\npackage cn.iocoder.yudao.module.statistics.framework;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/framework/rpc/config/RpcConfiguration.java",
    "content": "package cn.iocoder.yudao.module.statistics.framework.rpc.config;\n\nimport cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration(value = \"statisticsRpcConfiguration\", proxyBeanMethods = false)\n@EnableFeignClients(clients = {ProductSpuApi.class})\npublic class RpcConfiguration {\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/framework/rpc/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.statistics.framework.rpc;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/framework/security/config/SecurityConfiguration.java",
    "content": "package cn.iocoder.yudao.module.statistics.framework.security.config;\n\nimport cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\n\n/**\n * Statistics 模块的 Security 配置\n */\n@Configuration(\"reportSecurityConfiguration\")\npublic class SecurityConfiguration {\n\n    @Bean(\"reportAuthorizeRequestsCustomizer\")\n    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {\n        return new AuthorizeRequestsCustomizer() {\n\n            @Override\n            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {\n                // Swagger 接口文档\n                registry.requestMatchers(\"/v3/api-docs/**\").permitAll()\n                        .requestMatchers(\"/webjars/**\").permitAll()\n                        .requestMatchers(\"/swagger-ui\").permitAll()\n                        .requestMatchers(\"/swagger-ui/**\").permitAll();\n                // Spring Boot Actuator 的安全配置\n                registry.requestMatchers(\"/actuator\").permitAll()\n                        .requestMatchers(\"/actuator/**\").permitAll();\n                // Druid 监控\n                registry.requestMatchers(\"/druid/**\").permitAll();\n                // 积木报表\n                registry.requestMatchers(\"/jmreport/**\").permitAll();\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/framework/security/core/package-info.java",
    "content": "/**\n * 占位\n */\npackage cn.iocoder.yudao.module.statistics.framework.security.core;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/job/package-info.java",
    "content": "/**\n * TODO 芋艿，占坑，无特殊含义\n */\npackage cn.iocoder.yudao.module.statistics.job;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/job/product/ProductStatisticsJob.java",
    "content": "package cn.iocoder.yudao.module.statistics.job.product;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.util.NumberUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.tenant.core.job.TenantJob;\nimport cn.iocoder.yudao.module.statistics.service.product.ProductStatisticsService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * 商品统计 Job\n *\n * @author owen\n */\n@Component\npublic class ProductStatisticsJob {\n\n    @Resource\n    private ProductStatisticsService productStatisticsService;\n\n    /**\n     * 执行商品统计任务\n     *\n     * @param param 要统计的天数，只能是正整数，1 代表昨日数据\n     * @return 统计结果\n     */\n    @XxlJob(\"productStatisticsJob\")\n    @TenantJob\n    public String execute(String param) {\n        // 默认昨日\n        param = ObjUtil.defaultIfBlank(param, \"1\");\n        // 校验参数的合理性\n        if (!NumberUtil.isInteger(param)) {\n            throw new RuntimeException(\"商品统计任务的参数只能为是正整数\");\n        }\n        Integer days = Convert.toInt(param, 0);\n        if (days < 1) {\n            throw new RuntimeException(\"商品统计任务的参数只能为是正整数\");\n        }\n        String result = productStatisticsService.statisticsProduct(days);\n        return StrUtil.format(\"商品统计:\\n{}\", result);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/job/trade/TradeStatisticsJob.java",
    "content": "package cn.iocoder.yudao.module.statistics.job.trade;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.util.NumberUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.tenant.core.job.TenantJob;\nimport cn.iocoder.yudao.module.statistics.service.trade.TradeStatisticsService;\nimport com.xxl.job.core.handler.annotation.XxlJob;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * 交易统计 Job\n *\n * @author owen\n */\n@Component\npublic class TradeStatisticsJob {\n\n    @Resource\n    private TradeStatisticsService tradeStatisticsService;\n\n    /**\n     * 执行交易统计任务\n     *\n     * @param param 要统计的天数，只能是正整数，1 代表昨日数据\n     * @return 统计结果\n     */\n    @XxlJob(\"tradeStatisticsJob\")\n    @TenantJob\n    public String execute(String param) {\n        // 默认昨日\n        param = ObjUtil.defaultIfBlank(param, \"1\");\n        // 校验参数的合理性\n        if (!NumberUtil.isInteger(param)) {\n            throw new RuntimeException(\"交易统计任务的参数只能为是正整数\");\n        }\n        Integer days = Convert.toInt(param, 0);\n        if (days < 1) {\n            throw new RuntimeException(\"交易统计任务的参数只能为是正整数\");\n        }\n        String result = tradeStatisticsService.statisticsTrade(days);\n        return StrUtil.format(\"交易统计:\\n{}\", result);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/package-info.java",
    "content": "/**\n * statistics 模块，主要实现统计相关功能。\n * 例如：统计商品、会员、交易等功能。\n *\n * 1. Controller URL：以 /statistics/ 开头，避免和其它 Module 冲突\n * 2. DataObject 表名：以 statistics_ 为后缀，方便在数据库中区分【特殊】\n */\npackage cn.iocoder.yudao.module.statistics;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/infra/ApiAccessLogStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.infra;\n\nimport java.time.LocalDateTime;\n\n/**\n * API 访问日志的统计 Service 接口\n *\n * @author owen\n */\npublic interface ApiAccessLogStatisticsService {\n\n    /**\n     * 获取活跃用户数量\n     *\n     * @param userType  用户类型\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 活跃用户数量\n     */\n    Integer getUserCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获取访问用户数量\n     *\n     * @param userType  用户类型\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 访问用户数量\n     */\n    Integer getIpCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/infra/ApiAccessLogStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.infra;\n\nimport cn.iocoder.yudao.module.statistics.dal.mysql.infra.ApiAccessLogStatisticsMapper;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\n/**\n * API 访问日志的统计 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class ApiAccessLogStatisticsServiceImpl implements ApiAccessLogStatisticsService {\n\n    @Resource\n    private ApiAccessLogStatisticsMapper apiAccessLogStatisticsMapper;\n\n    @Override\n    public Integer getUserCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime) {\n        return apiAccessLogStatisticsMapper.selectUserCountByUserTypeAndCreateTimeBetween(userType, beginTime, endTime);\n    }\n\n    @Override\n    public Integer getIpCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime) {\n        return apiAccessLogStatisticsMapper.selectIpCountByUserTypeAndCreateTimeBetween(userType, beginTime, endTime);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/member/MemberStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.member;\n\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.*;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 会员信息的统计 Service 接口\n *\n * @author owen\n */\npublic interface MemberStatisticsService {\n\n    /**\n     * 获取会员统计（实时统计）\n     *\n     * @return 会员统计\n     */\n    MemberSummaryRespVO getMemberSummary();\n\n    /**\n     * 获取会员分析对照数据\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 会员分析对照数据\n     */\n    DataComparisonRespVO<MemberAnalyseDataRespVO> getMemberAnalyseComparisonData(LocalDateTime beginTime,\n                                                                                 LocalDateTime endTime);\n\n    /**\n     * 按照省份，获得会员统计列表\n     *\n     * @return 会员统计列表\n     */\n    List<MemberAreaStatisticsRespVO> getMemberAreaStatisticsList();\n\n    /**\n     * 按照性别，获得会员统计列表\n     *\n     * @return 会员统计列表\n     */\n    List<MemberSexStatisticsRespVO> getMemberSexStatisticsList();\n\n    /**\n     * 按照终端，获得会员统计列表\n     *\n     * @return 会员统计列表\n     */\n    List<MemberTerminalStatisticsRespVO> getMemberTerminalStatisticsList();\n\n    /**\n     * 获取用户注册数量列表\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 注册数量列表\n     */\n    List<MemberRegisterCountRespVO> getMemberRegisterCountList(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获得用户数量量统计对照\n     *\n     * @return 用户数量量统计对照\n     */\n    DataComparisonRespVO<MemberCountRespVO> getUserCountComparison();\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/member/MemberStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.member;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.ip.core.Area;\nimport cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.member.vo.*;\nimport cn.iocoder.yudao.module.statistics.convert.member.MemberStatisticsConvert;\nimport cn.iocoder.yudao.module.statistics.dal.mysql.member.MemberStatisticsMapper;\nimport cn.iocoder.yudao.module.statistics.service.infra.ApiAccessLogStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;\nimport cn.iocoder.yudao.module.statistics.service.pay.PayWalletStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.TradeOrderStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.trade.TradeStatisticsService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n/**\n * 会员信息的统计 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class MemberStatisticsServiceImpl implements MemberStatisticsService {\n\n    @Resource\n    private MemberStatisticsMapper memberStatisticsMapper;\n\n    @Resource\n    private PayWalletStatisticsService payWalletStatisticsService;\n    @Resource\n    private TradeStatisticsService tradeStatisticsService;\n    @Resource\n    private TradeOrderStatisticsService tradeOrderStatisticsService;\n    @Resource\n    private ApiAccessLogStatisticsService apiAccessLogStatisticsService;\n\n    @Override\n    public MemberSummaryRespVO getMemberSummary() {\n        RechargeSummaryRespBO rechargeSummary = payWalletStatisticsService.getUserRechargeSummary(null, null);\n        // TODO @疯狂：1）这里是实时统计，不好走走 TradeStatistics 表；2）因为这个放在商城下，所以只考虑订单数据，即按照 trade_order 的 pay_price 并且已支付来计算；\n        Integer expensePrice = tradeStatisticsService.getExpensePrice(null, null);\n        Integer userCount = memberStatisticsMapper.selectUserCount(null, null);\n        return MemberStatisticsConvert.INSTANCE.convert(rechargeSummary, expensePrice, userCount);\n    }\n\n    @Override\n    public List<MemberAreaStatisticsRespVO> getMemberAreaStatisticsList() {\n        // 统计用户\n        // TODO @疯狂：可能得把每个省的用户，都查询出来，然后去 order 那边 in；因为要按照这些人为基础来计算；；用户规模量大可能不太好，但是暂时就先这样搞吧 = =\n        Map<Integer, Integer> userCountMap = convertMap(memberStatisticsMapper.selectSummaryListByAreaId(),\n                vo -> AreaUtils.getParentIdByType(vo.getAreaId(), AreaTypeEnum.PROVINCE),\n                MemberAreaStatisticsRespBO::getUserCount, Integer::sum);\n        // 统计订单\n        Map<Integer, MemberAreaStatisticsRespBO> orderMap = convertMap(tradeOrderStatisticsService.getSummaryListByAreaId(),\n                bo -> AreaUtils.getParentIdByType(bo.getAreaId(), AreaTypeEnum.PROVINCE),\n                bo -> bo,\n                (a, b) -> new MemberAreaStatisticsRespBO()\n                        .setOrderCreateUserCount(ObjectUtil.defaultIfNull(a.getOrderCreateUserCount(), 0)\n                                + ObjectUtil.defaultIfNull(b.getOrderCreateUserCount(), 0))\n                        .setOrderPayUserCount(ObjectUtil.defaultIfNull(a.getOrderPayUserCount(), 0)\n                                + ObjectUtil.defaultIfNull(b.getOrderPayUserCount(), 0))\n                        .setOrderPayPrice(ObjectUtil.defaultIfNull(a.getOrderPayPrice(), 0)\n                                + ObjectUtil.defaultIfNull(b.getOrderPayPrice(), 0)));\n        // 拼接数据\n        List<Area> areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area);\n        areaList.add(new Area().setId(null).setName(\"未知\"));\n        return MemberStatisticsConvert.INSTANCE.convertList(areaList, userCountMap, orderMap);\n    }\n\n    @Override\n    public DataComparisonRespVO<MemberAnalyseDataRespVO> getMemberAnalyseComparisonData(LocalDateTime beginTime, LocalDateTime endTime) {\n        // 当前数据\n        MemberAnalyseDataRespVO vo = getMemberAnalyseData(beginTime, endTime);\n        // 对照数据\n        LocalDateTime referenceEndDate = beginTime.minusDays(1); // 减少1天，防止出现时间重叠\n        LocalDateTime referenceBeginDate = referenceEndDate.minus(Duration.between(beginTime, endTime));\n        MemberAnalyseDataRespVO reference = getMemberAnalyseData(\n                LocalDateTimeUtil.beginOfDay(referenceBeginDate), LocalDateTimeUtil.endOfDay(referenceEndDate));\n        return new DataComparisonRespVO<>(vo, reference);\n    }\n\n    private MemberAnalyseDataRespVO getMemberAnalyseData(LocalDateTime beginTime, LocalDateTime endTime) {\n        Integer rechargeUserCount = Optional.ofNullable(payWalletStatisticsService.getUserRechargeSummary(beginTime, endTime))\n                .map(RechargeSummaryRespBO::getRechargeUserCount).orElse(0);\n        return new MemberAnalyseDataRespVO()\n                .setRegisterUserCount(memberStatisticsMapper.selectUserCount(beginTime, endTime))\n                .setVisitUserCount(apiAccessLogStatisticsService.getUserCount(UserTypeEnum.MEMBER.getValue(), beginTime, endTime))\n                .setRechargeUserCount(rechargeUserCount);\n    }\n\n    @Override\n    public List<MemberSexStatisticsRespVO> getMemberSexStatisticsList() {\n        return memberStatisticsMapper.selectSummaryListBySex();\n    }\n\n    @Override\n    public List<MemberTerminalStatisticsRespVO> getMemberTerminalStatisticsList() {\n        return memberStatisticsMapper.selectSummaryListByRegisterTerminal();\n    }\n\n    @Override\n    public List<MemberRegisterCountRespVO> getMemberRegisterCountList(LocalDateTime beginTime, LocalDateTime endTime) {\n        return memberStatisticsMapper.selectListByCreateTimeBetween(beginTime, endTime);\n    }\n\n    @Override\n    public DataComparisonRespVO<MemberCountRespVO> getUserCountComparison() {\n        // 今日时间范围\n        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());\n        LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(beginOfToday);\n        // 昨日时间范围\n        LocalDateTime beginOfYesterday = LocalDateTimeUtil.beginOfDay(beginOfToday.minusDays(1));\n        LocalDateTime endOfYesterday = LocalDateTimeUtil.endOfDay(beginOfYesterday);\n        return new DataComparisonRespVO<MemberCountRespVO>()\n                .setValue(getUserCount(beginOfToday, endOfToday))\n                .setReference(getUserCount(beginOfYesterday, endOfYesterday));\n    }\n\n    private MemberCountRespVO getUserCount(LocalDateTime beginTime, LocalDateTime endTime) {\n        return new MemberCountRespVO()\n                .setRegisterUserCount(memberStatisticsMapper.selectUserCount(beginTime, endTime))\n                .setVisitUserCount(apiAccessLogStatisticsService.getIpCount(UserTypeEnum.MEMBER.getValue(), beginTime, endTime));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/member/bo/MemberAreaStatisticsRespBO.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.member.bo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 会员地区统计 Response BO\")\n@Data\npublic class MemberAreaStatisticsRespBO {\n\n    /**\n     * 省份编号\n     */\n    private Integer areaId;\n    /**\n     * 省份名称\n     */\n    private String areaName;\n\n    /**\n     * 会员数量\n     */\n    private Integer userCount;\n\n    /**\n     * 下单的会员数量\n     */\n    private Integer orderCreateUserCount;\n    /**\n     * 支付订单的会员数量\n     */\n    private Integer orderPayUserCount;\n\n    /**\n     * 订单支付金额，单位：分\n     */\n    private Integer orderPayPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/pay/PayWalletStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.pay;\n\nimport cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;\n\nimport java.time.LocalDateTime;\n\n/**\n * 钱包的统计 Service 接口\n *\n * @author owen\n */\npublic interface PayWalletStatisticsService {\n\n    /**\n     * 获取钱包统计\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 钱包统计\n     */\n    WalletSummaryRespBO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获取钱包充值统计\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 钱包充值统计\n     */\n    RechargeSummaryRespBO getUserRechargeSummary(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获取充值金额合计\n     *\n     * @return 充值金额合计\n     */\n    Integer getRechargePriceSummary();\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/pay/PayWalletStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.pay;\n\nimport cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;\nimport cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;\nimport cn.iocoder.yudao.module.statistics.dal.mysql.pay.PayWalletStatisticsMapper;\nimport cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\n/**\n * 钱包的统计 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class PayWalletStatisticsServiceImpl implements PayWalletStatisticsService {\n\n    @Resource\n    private PayWalletStatisticsMapper payWalletStatisticsMapper;\n\n    @Override\n    public WalletSummaryRespBO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime) {\n        WalletSummaryRespBO paySummary = payWalletStatisticsMapper.selectRechargeSummaryByPayTimeBetween(\n                beginTime, endTime, true);\n        WalletSummaryRespBO refundSummary = payWalletStatisticsMapper.selectRechargeSummaryByRefundTimeBetween(\n                beginTime, endTime, PayRefundStatusEnum.SUCCESS.getStatus());\n        Integer walletPayPrice = payWalletStatisticsMapper.selectPriceSummaryByBizTypeAndCreateTimeBetween(\n                beginTime, endTime, PayWalletBizTypeEnum.PAYMENT.getType());\n        // 拼接\n        paySummary.setWalletPayPrice(walletPayPrice)\n                .setRechargeRefundCount(refundSummary.getRechargeRefundCount())\n                .setRechargeRefundPrice(refundSummary.getRechargeRefundPrice());\n        return paySummary;\n    }\n\n    @Override\n    public RechargeSummaryRespBO getUserRechargeSummary(LocalDateTime beginTime, LocalDateTime endTime) {\n        return payWalletStatisticsMapper.selectRechargeSummaryGroupByWalletId(beginTime, endTime, true);\n    }\n\n    @Override\n    public Integer getRechargePriceSummary() {\n        return payWalletStatisticsMapper.selectRechargePriceSummary(Boolean.TRUE);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/pay/bo/RechargeSummaryRespBO.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.pay.bo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n/**\n * 充值统计 Response BO\n */\n@Data\npublic class RechargeSummaryRespBO {\n\n    /**\n     * 充值会员数量\n     */\n    private Integer rechargeUserCount;\n\n    /**\n     * 充值金额\n     */\n    private Integer rechargePrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.product;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.SortablePageParam;\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;\n\nimport java.util.List;\n\n/**\n * 商品统计 Service 接口\n *\n * @author owen\n */\npublic interface ProductStatisticsService {\n\n    /**\n     * 获得商品统计排行榜分页\n     *\n     * @param reqVO     查询条件\n     * @param pageParam 分页排序查询\n     * @return 商品统计分页\n     */\n    PageResult<ProductStatisticsDO> getProductStatisticsRankPage(ProductStatisticsReqVO reqVO, SortablePageParam pageParam);\n\n    /**\n     * 获得商品状况统计分析\n     *\n     * @param reqVO 查询条件\n     * @return 统计数据对照\n     */\n    DataComparisonRespVO<ProductStatisticsRespVO> getProductStatisticsAnalyse(ProductStatisticsReqVO reqVO);\n\n    /**\n     * 获得商品状况明细\n     *\n     * @param reqVO 查询条件\n     * @return 统计数据对照\n     */\n    List<ProductStatisticsDO> getProductStatisticsList(ProductStatisticsReqVO reqVO);\n\n    /**\n     * 统计指定天数的商品数据\n     *\n     * @return 统计结果\n     */\n    String statisticsProduct(Integer days);\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.product;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.pojo.SortablePageParam;\nimport cn.iocoder.yudao.framework.common.util.object.PageUtils;\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;\nimport cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.StopWatch;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\n\n/**\n * 商品统计 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class ProductStatisticsServiceImpl implements ProductStatisticsService {\n\n    @Resource\n    private ProductStatisticsMapper productStatisticsMapper;\n\n\n    @Override\n    public PageResult<ProductStatisticsDO> getProductStatisticsRankPage(ProductStatisticsReqVO reqVO, SortablePageParam pageParam) {\n        PageUtils.buildDefaultSortingField(pageParam, ProductStatisticsDO::getBrowseCount); // 默认浏览量倒序\n        return productStatisticsMapper.selectPageGroupBySpuId(reqVO, pageParam);\n    }\n\n    @Override\n    public DataComparisonRespVO<ProductStatisticsRespVO> getProductStatisticsAnalyse(ProductStatisticsReqVO reqVO) {\n        LocalDateTime beginTime = ArrayUtil.get(reqVO.getTimes(), 0);\n        LocalDateTime endTime = ArrayUtil.get(reqVO.getTimes(), 1);\n\n        // 统计数据\n        ProductStatisticsRespVO value = productStatisticsMapper.selectVoByTimeBetween(reqVO);\n        // 对照数据\n        LocalDateTime referenceBeginTime = beginTime.minus(Duration.between(beginTime, endTime));\n        ProductStatisticsReqVO referenceReqVO = new ProductStatisticsReqVO(new LocalDateTime[]{referenceBeginTime, beginTime});\n        ProductStatisticsRespVO reference = productStatisticsMapper.selectVoByTimeBetween(referenceReqVO);\n        return new DataComparisonRespVO<>(value, reference);\n    }\n\n    @Override\n    public List<ProductStatisticsDO> getProductStatisticsList(ProductStatisticsReqVO reqVO) {\n        return productStatisticsMapper.selectListByTimeBetween(reqVO);\n    }\n\n    @Override\n    public String statisticsProduct(Integer days) {\n        LocalDateTime today = LocalDateTime.now();\n        return IntStream.rangeClosed(1, days)\n                .mapToObj(day -> statisticsProduct(today.minusDays(day)))\n                .sorted()\n                .collect(Collectors.joining(\"\\n\"));\n    }\n\n    /**\n     * 统计商品数据\n     *\n     * @param date 需要统计的日期\n     * @return 统计结果\n     */\n    private String statisticsProduct(LocalDateTime date) {\n        // 1. 处理统计时间范围\n        LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date);\n        LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date);\n        String dateStr = DatePattern.NORM_DATE_FORMATTER.format(date);\n        // 2. 检查该日是否已经统计过\n        Long count = productStatisticsMapper.selectCountByTimeBetween(beginTime, endTime);\n        if (count != null && count > 0) {\n            return dateStr + \" 数据已存在，如果需要重新统计，请先删除对应的数据\";\n        }\n\n        StopWatch stopWatch = new StopWatch(dateStr);\n        stopWatch.start();\n        // 4. 分页统计，避免商品表数据较多时，出现超时问题\n        final int pageSize = 100;\n        for (int pageNo = 1; ; pageNo++) {\n            IPage<ProductStatisticsDO> page = productStatisticsMapper.selectStatisticsResultPageByTimeBetween(\n                    Page.of(pageNo, pageSize, false), beginTime, endTime);\n            if (CollUtil.isEmpty(page.getRecords())) {\n                break;\n            }\n            // 4.1 计算访客支付转化率（百分比）\n            for (ProductStatisticsDO record : page.getRecords()) {\n                record.setTime(date.toLocalDate());\n                if (record.getBrowseUserCount() != null && ObjUtil.notEqual(record.getBrowseUserCount(), 0)) {\n                    record.setBrowseConvertPercent(100 * record.getOrderPayCount() / record.getBrowseUserCount());\n                }\n            }\n            // 4.2 插入数据\n            productStatisticsMapper.insertBatch(page.getRecords());\n        }\n        return stopWatch.prettyPrint();\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/AfterSaleStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade;\n\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;\nimport cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum;\n\nimport java.time.LocalDateTime;\n\n/**\n * 售后统计 Service 接口\n *\n * @author owen\n */\npublic interface AfterSaleStatisticsService {\n\n    /**\n     * 获取售后单统计\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 售后统计结果\n     */\n    AfterSaleSummaryRespBO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获取指定状态的售后订单数量\n     *\n     * @param status 售后状态\n     * @return 售后订单数量\n     */\n    Long getCountByStatus(AfterSaleStatusEnum status);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/AfterSaleStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade;\n\nimport cn.iocoder.yudao.module.statistics.dal.mysql.trade.AfterSaleStatisticsMapper;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;\nimport cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\n/**\n * 售后统计 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class AfterSaleStatisticsServiceImpl implements AfterSaleStatisticsService {\n\n    @Resource\n    private AfterSaleStatisticsMapper afterSaleStatisticsMapper;\n\n    @Override\n    public AfterSaleSummaryRespBO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime) {\n        return afterSaleStatisticsMapper.selectSummaryByRefundTimeBetween(beginTime, endTime);\n    }\n\n    @Override\n    public Long getCountByStatus(AfterSaleStatusEnum status) {\n        return afterSaleStatisticsMapper.selectCountByStatus(status.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/BrokerageStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade;\n\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;\n\nimport java.time.LocalDateTime;\n\n/**\n * 分销统计 Service 接口\n *\n * @author owen\n */\npublic interface BrokerageStatisticsService {\n\n    /**\n     * 获取已结算的佣金金额\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 已结算的佣金金额\n     */\n    Integer getBrokerageSettlementPriceSummary(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获取指定状态的提现记录数量\n     *\n     * @param status 提现记录状态\n     * @return 提现记录数量\n     */\n    Long getWithdrawCountByStatus(BrokerageWithdrawStatusEnum status);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/BrokerageStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade;\n\nimport cn.iocoder.yudao.module.statistics.dal.mysql.trade.BrokerageStatisticsMapper;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.LocalDateTime;\n\n/**\n * 分销统计 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class BrokerageStatisticsServiceImpl implements BrokerageStatisticsService {\n\n    @Resource\n    private BrokerageStatisticsMapper brokerageStatisticsMapper;\n\n    @Override\n    public Integer getBrokerageSettlementPriceSummary(LocalDateTime beginTime, LocalDateTime endTime) {\n        return brokerageStatisticsMapper.selectSummaryPriceByStatusAndUnfreezeTimeBetween(\n                BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(),\n                beginTime, endTime);\n    }\n\n    @Override\n    public Long getWithdrawCountByStatus(BrokerageWithdrawStatusEnum status) {\n        return brokerageStatisticsMapper.selectWithdrawCountByStatus(status.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeOrderStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade;\n\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.*;\nimport cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 交易订单的统计 Service 接口\n *\n * @author owen\n */\npublic interface TradeOrderStatisticsService {\n\n    /**\n     * 获取订单统计\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 订单统计结果\n     */\n    TradeOrderSummaryRespBO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获取地区订单统计\n     *\n     * @return 订单统计结果\n     */\n    List<MemberAreaStatisticsRespBO> getSummaryListByAreaId();\n\n    /**\n     * 获取下单用户数量\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 下单用户数量\n     */\n    Integer getOrderUserCount(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获取支付用户数量\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 支付用户数量\n     */\n    Integer getPayUserCount(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获取支付金额\n     *\n     * @param beginTime 起始时间\n     * @param endTime   截止时间\n     * @return 支付用户金额\n     */\n    Integer getOrderPayPrice(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 根据订单状态、物流类型，获得交易订单数量\n     *\n     * @return 订单数量\n     */\n    Long getCountByStatusAndDeliveryType(Integer status, Integer deliveryType);\n\n    /**\n     * 交易订单销售额对照\n     *\n     * @return 销售额对照\n     */\n    DataComparisonRespVO<TradeOrderSummaryRespVO> getOrderComparison();\n\n    /**\n     * 获得订单量趋势统计\n     *\n     * @param reqVO 统计参数\n     * @return 订单量趋势统计\n     */\n    List<DataComparisonRespVO<TradeOrderTrendRespVO>> getOrderCountTrendComparison(TradeOrderTrendReqVO reqVO);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeOrderStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderSummaryRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendReqVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendRespVO;\nimport cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeOrderStatisticsMapper;\nimport cn.iocoder.yudao.module.statistics.enums.TimeRangeTypeEnum;\nimport cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\n/**\n * 交易订单统计 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class TradeOrderStatisticsServiceImpl implements TradeOrderStatisticsService {\n\n    @Resource\n    private TradeOrderStatisticsMapper tradeOrderStatisticsMapper;\n\n    @Override\n    public TradeOrderSummaryRespBO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime) {\n        return new TradeOrderSummaryRespBO()\n                .setOrderCreateCount(tradeOrderStatisticsMapper.selectCountByCreateTimeBetween(beginTime, endTime))\n                .setOrderPayCount(tradeOrderStatisticsMapper.selectCountByPayTimeBetween(beginTime, endTime))\n                .setOrderPayPrice(tradeOrderStatisticsMapper.selectSummaryPriceByPayTimeBetween(beginTime, endTime));\n    }\n\n    @Override\n    public List<MemberAreaStatisticsRespBO> getSummaryListByAreaId() {\n        return tradeOrderStatisticsMapper.selectSummaryListByAreaId();\n    }\n\n    @Override\n    public Integer getOrderUserCount(LocalDateTime beginTime, LocalDateTime endTime) {\n        return tradeOrderStatisticsMapper.selectUserCountByCreateTimeBetween(beginTime, endTime);\n    }\n\n    @Override\n    public Integer getPayUserCount(LocalDateTime beginTime, LocalDateTime endTime) {\n        return tradeOrderStatisticsMapper.selectUserCountByPayTimeBetween(beginTime, endTime);\n    }\n\n    @Override\n    public Integer getOrderPayPrice(LocalDateTime beginTime, LocalDateTime endTime) {\n        return tradeOrderStatisticsMapper.selectSummaryPriceByPayTimeBetween(beginTime, endTime);\n    }\n\n    @Override\n    public Long getCountByStatusAndDeliveryType(Integer status, Integer deliveryType) {\n        return tradeOrderStatisticsMapper.selectCountByStatusAndDeliveryType(status, deliveryType);\n    }\n\n    @Override\n    public DataComparisonRespVO<TradeOrderSummaryRespVO> getOrderComparison() {\n        return new DataComparisonRespVO<TradeOrderSummaryRespVO>()\n                .setValue(getPayPriceSummary(LocalDateTime.now()))\n                .setReference(getPayPriceSummary(LocalDateTime.now().minusDays(1)));\n    }\n\n    private TradeOrderSummaryRespVO getPayPriceSummary(LocalDateTime date) {\n        LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date);\n        LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date);\n        return tradeOrderStatisticsMapper.selectPaySummaryByPayStatusAndPayTimeBetween(\n                Boolean.TRUE, beginTime, endTime);\n    }\n\n    @Override\n    public List<DataComparisonRespVO<TradeOrderTrendRespVO>> getOrderCountTrendComparison(TradeOrderTrendReqVO reqVO) {\n        // 查询当前数据\n        List<TradeOrderTrendRespVO> value = getOrderCountTrend(reqVO.getType(), reqVO.getBeginTime(), reqVO.getEndTime());\n        // 查询对照数据\n        LocalDateTime referenceEndTime = reqVO.getBeginTime().minusDays(1);\n        LocalDateTime referenceBeginTime = referenceEndTime.minus(Duration.between(reqVO.getBeginTime(), reqVO.getEndTime()));\n        List<TradeOrderTrendRespVO> reference = getOrderCountTrend(reqVO.getType(), referenceBeginTime, referenceEndTime);\n        // 顺序对比返回\n        return IntStream.range(0, value.size())\n                .mapToObj(index -> new DataComparisonRespVO<TradeOrderTrendRespVO>()\n                        .setValue(CollUtil.get(value, index))\n                        .setReference(CollUtil.get(reference, index)))\n                .collect(Collectors.toList());\n    }\n\n    private List<TradeOrderTrendRespVO> getOrderCountTrend(Integer timeRangeType, LocalDateTime beginTime, LocalDateTime endTime) {\n        // 情况一：按年统计时，以月份分组\n        if (TimeRangeTypeEnum.YEAR.getType().equals(timeRangeType)) {\n            return tradeOrderStatisticsMapper.selectListByPayTimeBetweenAndGroupByMonth(beginTime, endTime);\n        }\n        // 情况二：其它以天分组（天、周、月）\n        return tradeOrderStatisticsMapper.selectListByPayTimeBetweenAndGroupByDay(beginTime, endTime);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsService.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade;\n\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * 交易统计 Service 接口\n *\n * @author owen\n */\npublic interface TradeStatisticsService {\n\n    /**\n     * 获得交易状况统计对照\n     *\n     * @return 统计数据对照\n     */\n    DataComparisonRespVO<TradeTrendSummaryRespVO> getTradeStatisticsAnalyse(\n            LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获得交易状况统计\n     *\n     * @param beginTime 开始时间\n     * @param endTime   结束时间\n     * @return 统计数据对照\n     */\n    Integer getExpensePrice(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 获得交易状况明细\n     *\n     * @param beginTime 开始时间\n     * @param endTime   结束时间\n     * @return 统计数据列表\n     */\n    List<TradeStatisticsDO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime);\n\n    /**\n     * 统计指定天数的交易数据\n     *\n     * @return 统计结果\n     */\n    String statisticsTrade(Integer days);\n\n    /**\n     * 统计指定日期的交易数据\n     *\n     * @param days 增加的天数\n     * @return 交易数据\n     */\n    TradeSummaryRespBO getTradeSummaryByDays(int days);\n\n    /**\n     * 统计指定月份的交易数据\n     *\n     * @param months 增加的月数\n     * @return 交易数据\n     */\n    TradeSummaryRespBO getTradeSummaryByMonths(int months);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsServiceImpl.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;\nimport cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;\nimport cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;\nimport cn.iocoder.yudao.module.statistics.convert.trade.TradeStatisticsConvert;\nimport cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;\nimport cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeStatisticsMapper;\nimport cn.iocoder.yudao.module.statistics.service.pay.PayWalletStatisticsService;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;\nimport cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.StopWatch;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.annotation.Resource;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\n/**\n * 交易统计 Service 实现类\n *\n * @author owen\n */\n@Service\n@Validated\npublic class TradeStatisticsServiceImpl implements TradeStatisticsService {\n\n    @Resource\n    private TradeStatisticsMapper tradeStatisticsMapper;\n\n    @Resource\n    private TradeOrderStatisticsService tradeOrderStatisticsService;\n    @Resource\n    private AfterSaleStatisticsService afterSaleStatisticsService;\n    @Resource\n    private BrokerageStatisticsService brokerageStatisticsService;\n    @Resource\n    private PayWalletStatisticsService payWalletStatisticsService;\n\n    @Override\n    public TradeSummaryRespBO getTradeSummaryByDays(int days) {\n        LocalDateTime date = LocalDateTime.now().plusDays(days);\n        return tradeStatisticsMapper.selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(\n                LocalDateTimeUtil.beginOfDay(date), LocalDateTimeUtil.endOfDay(date));\n    }\n\n    @Override\n    public TradeSummaryRespBO getTradeSummaryByMonths(int months) {\n        LocalDateTime monthDate = LocalDateTime.now().plusMonths(months);\n        return tradeStatisticsMapper.selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(\n                LocalDateTimeUtils.beginOfMonth(monthDate), LocalDateTimeUtils.endOfMonth(monthDate));\n    }\n\n    @Override\n    public DataComparisonRespVO<TradeTrendSummaryRespVO> getTradeStatisticsAnalyse(LocalDateTime beginTime,\n                                                                                        LocalDateTime endTime) {\n        // 统计数据\n        TradeTrendSummaryRespVO value = tradeStatisticsMapper.selectVoByTimeBetween(beginTime, endTime);\n        // 对照数据\n        LocalDateTime referenceBeginTime = beginTime.minus(Duration.between(beginTime, endTime));\n        TradeTrendSummaryRespVO reference = tradeStatisticsMapper.selectVoByTimeBetween(referenceBeginTime, beginTime);\n        return TradeStatisticsConvert.INSTANCE.convert(value, reference);\n    }\n\n    @Override\n    public Integer getExpensePrice(LocalDateTime beginTime, LocalDateTime endTime) {\n        return tradeStatisticsMapper.selectExpensePriceByTimeBetween(beginTime, endTime);\n    }\n\n    @Override\n    public List<TradeStatisticsDO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime) {\n        return tradeStatisticsMapper.selectListByTimeBetween(beginTime, endTime);\n    }\n\n    @Override\n    public String statisticsTrade(Integer days) {\n        LocalDateTime today = LocalDateTime.now();\n        return IntStream.rangeClosed(1, days)\n                .mapToObj(day -> statisticsTrade(today.minusDays(day)))\n                .sorted()\n                .collect(Collectors.joining(\"\\n\"));\n    }\n\n    /**\n     * 统计交易数据\n     *\n     * @param date 需要统计的日期\n     * @return 统计结果\n     */\n    private String statisticsTrade(LocalDateTime date) {\n        // 1. 处理统计时间范围\n        LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date);\n        LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date);\n        String dateStr = DatePattern.NORM_DATE_FORMATTER.format(date);\n        // 2. 检查该日是否已经统计过\n        TradeStatisticsDO entity = tradeStatisticsMapper.selectByTimeBetween(beginTime, endTime);\n        if (entity != null) {\n            return dateStr + \" 数据已存在，如果需要重新统计，请先删除对应的数据\";\n        }\n\n        // 3. 从各个数据表，统计对应数据\n        StopWatch stopWatch = new StopWatch(dateStr);\n        // 3.1 统计订单\n        stopWatch.start(\"统计订单\");\n        TradeOrderSummaryRespBO orderSummary = tradeOrderStatisticsService.getOrderSummary(beginTime, endTime);\n        stopWatch.stop();\n        // 3.2 统计售后\n        stopWatch.start(\"统计售后\");\n        AfterSaleSummaryRespBO afterSaleSummary = afterSaleStatisticsService.getAfterSaleSummary(beginTime, endTime);\n        stopWatch.stop();\n        // 3.3 统计佣金\n        stopWatch.start(\"统计佣金\");\n        Integer brokerageSettlementPrice = brokerageStatisticsService.getBrokerageSettlementPriceSummary(beginTime, endTime);\n        stopWatch.stop();\n        // 3.4 统计充值\n        stopWatch.start(\"统计充值\");\n        WalletSummaryRespBO walletSummary = payWalletStatisticsService.getWalletSummary(beginTime, endTime);\n        stopWatch.stop();\n\n        // 4. 插入数据\n        entity = TradeStatisticsConvert.INSTANCE.convert(date, orderSummary, afterSaleSummary, brokerageSettlementPrice,\n                walletSummary);\n        tradeStatisticsMapper.insert(entity);\n        return stopWatch.prettyPrint();\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/bo/AfterSaleSummaryRespBO.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade.bo;\n\nimport lombok.Data;\n\n/**\n * 售后统计 Response DTO\n *\n * @author owen\n */\n@Data\npublic class AfterSaleSummaryRespBO {\n\n    /**\n     * 退款订单数\n     */\n    private Integer afterSaleCount;\n    /**\n     * 总退款金额，单位：分\n     */\n    private Integer afterSaleRefundPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/bo/MemberAreaStatisticsRespBO.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade.bo;\n\nimport lombok.Data;\n\n/**\n * 会员地区统计 Response BO\n *\n * @author owen\n */\n@Data\npublic class MemberAreaStatisticsRespBO {\n\n    /**\n     * 省份编号\n     */\n    private Integer areaId;\n    /**\n     * 省份名称\n     */\n    private String areaName;\n\n    /**\n     * 会员数量\n     */\n    private Integer userCount;\n\n    /**\n     * 下单的会员数量\n     */\n    private Integer orderCreateUserCount;\n    /**\n     * 支付订单的会员数量\n     */\n    private Integer orderPayUserCount;\n\n    /**\n     * 订单支付金额，单位：分\n     */\n    private Integer orderPayPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/bo/TradeOrderSummaryRespBO.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade.bo;\n\nimport lombok.Data;\n\n/**\n * 订单统计 Response BO\n *\n * @author owen\n */\n@Data\npublic class TradeOrderSummaryRespBO {\n\n    /**\n     * 创建订单数\n     */\n    private Integer orderCreateCount;\n    /**\n     * 支付订单商品数\n     */\n    private Integer orderPayCount;\n    /**\n     * 总支付金额，单位：分\n     */\n    private Integer orderPayPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/bo/TradeSummaryRespBO.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade.bo;\n\nimport lombok.Data;\n\n/**\n * 交易统计 Resp BO\n *\n * @author owen\n */\n@Data\npublic class TradeSummaryRespBO {\n\n    /**\n     * 数量\n     */\n    private Integer count;\n\n    /**\n     * 合计\n     */\n    private Integer summary;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/bo/WalletSummaryRespBO.java",
    "content": "package cn.iocoder.yudao.module.statistics.service.trade.bo;\n\nimport lombok.Data;\n\n/**\n * 钱包统计 Response DTO\n *\n * @author owen\n */\n@Data\npublic class WalletSummaryRespBO {\n\n    /**\n     * 总支付金额（余额），单位：分\n     */\n    private Integer walletPayPrice;\n\n    /**\n     * 充值订单数\n     */\n    private Integer rechargePayCount;\n    /**\n     * 充值金额，单位：分\n     */\n    private Integer rechargePayPrice;\n    /**\n     * 充值退款订单数\n     */\n    private Integer rechargeRefundCount;\n    /**\n     * 充值退款金额，单位：分\n     */\n    private Integer rechargeRefundPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-dev.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 5 # 初始连接数\n        min-idle: 10 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n        slave: # 模拟从库，可根据自己需要修改 # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 400-infra.server.iocoder.cn # 地址\n    port: 6379 # 端口\n    database: 1 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\nxxl:\n  job:\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n      # Spring Boot Admin Server 服务端的相关配置\n      context-path: /admin # 配置 Spring\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  demo: true # 开启演示模式\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-local.yaml",
    "content": "--- #################### 注册中心 + 配置中心相关配置 ####################\n\nspring:\n  cloud:\n    nacos:\n      server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n      username: # Nacos 账号\n      password: # Nacos 密码\n      discovery: # 【配置中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        metadata:\n          version: 1.0.0 # 服务实例的版本号，可用于灰度发布\n      config: # 【注册中心】配置项\n        namespace: dev # 命名空间。这里使用 dev 开发环境\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n\n--- #################### 数据库相关配置 ####################\nspring:\n  # 数据源配置项\n  autoconfigure:\n    exclude:\n      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置，使用 dynamic-datasource-spring-boot-starter 配置多数据源\n  datasource:\n    druid: # Druid 【监控】相关的全局配置\n      web-stat-filter:\n        enabled: true\n      stat-view-servlet:\n        enabled: true\n        allow: # 设置白名单，不填则允许所有访问\n        url-pattern: /druid/*\n        login-username: # 控制台管理用户名和密码\n        login-password:\n      filter:\n        stat:\n          enabled: true\n          log-slow-sql: true # 慢 SQL 记录\n          slow-sql-millis: 100\n          merge-sql: true\n        wall:\n          config:\n            multi-statement-allow: true\n    dynamic: # 多数据源配置\n      druid: # Druid 【连接池】相关的全局配置\n        initial-size: 1 # 初始连接数\n        min-idle: 1 # 最小连接池数量\n        max-active: 20 # 最大连接池数量\n        max-wait: 60000 # 配置获取连接等待超时的时间，单位：毫秒（1 分钟）\n        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位：毫秒\n        min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间，单位：毫秒（10 分钟）\n        max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间，单位：毫秒（30 分钟）\n        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效\n        test-while-idle: true\n        test-on-borrow: false\n        test-on-return: false\n        pool-prepared-statements: true # 是否开启 PreparedStatement 缓存\n        max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量\n      primary: master\n      datasource:\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例\n          #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例\n          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例\n          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例\n          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例\n          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例\n          username: root\n          password: 123456\n        #          username: sa # SQL Server 连接的示例\n        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例\n        #          username: SYSDBA # DM 连接的示例\n        #          password: SYSDBA # DM 连接的示例\n        slave: # 模拟从库，可根据自己需要修改\n          lazy: true # 开启懒加载，保证启动速度\n          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true\n          username: root\n          password: 123456\n\n  # Redis 配置。Redisson 默认的配置足够使用，一般不需要进行调优\n  redis:\n    host: 127.0.0.1 # 地址\n    port: 6379 # 端口\n    database: 0 # 数据库索引\n#    password: 123456 # 密码，建议生产环境开启\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    enabled: false # 是否开启调度中心，默认为 true 开启\n    admin:\n      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址\n\n--- #################### 服务保障相关配置 ####################\n\n# Lock4j 配置项\nlock4j:\n  acquire-timeout: 3000 # 获取分布式锁超时时间，默认为 3000 毫秒\n  expire: 30000 # 分布式锁的超时时间，默认为 30 毫秒\n\n--- #################### 监控相关配置 ####################\n\n# Actuator 监控端点的配置项\nmanagement:\n  endpoints:\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n# Spring Boot Admin 配置项\nspring:\n  boot:\n    admin:\n      # Spring Boot Admin Client 客户端的相关配置\n      client:\n        instance:\n          service-host-type: IP # 注册实例时，优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]\n        username: admin\n        password: admin\n\n# 日志文件配置\nlogging:\n  level:\n    # 配置自己写的 MyBatis Mapper 打印日志\n    cn.iocoder.yudao.module.statistics.dal.mysql: debug\n    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿：先禁用，Spring Boot 3.X 存在部分错误的 WARN 提示\n\n--- #################### 芋道相关配置 ####################\n\n# 芋道配置项，设置当前项目所有自定义的配置\nyudao:\n  env: # 多环境的配置项\n    tag: ${HOSTNAME}\n  security:\n    mock-enable: true\n  access-log: # 访问日志的配置项\n    enable: false\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: statistics-server\n\n  profiles:\n    active: local\n\n  main:\n    allow-circular-references: true # 允许循环依赖，因为项目是三层架构，无法避免这个情况。\n    allow-bean-definition-overriding: true # 允许 Bean 覆盖，例如说 Feign 等会存在重复定义的服务\n\n  config:\n    import:\n      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置\n      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置\n\n  # Servlet 配置\n  servlet:\n    # 文件上传相关配置项\n    multipart:\n      max-file-size: 16MB # 单个文件大小\n      max-request-size: 32MB # 设置总上传的文件大小\n\n  # Jackson 配置项\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式，使用时间戳\n      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401，而是直接 1611460870401\n      write-durations-as-timestamps: true # 设置 Duration 的格式，使用时间戳\n      fail-on-empty-beans: false # 允许序列化无属性的 Bean\n\n  # Cache 配置项\n  cache:\n    type: REDIS\n    redis:\n      time-to-live: 1h # 设置过期时间为 1 小时\n\nserver:\n  port: 48103\n\nlogging:\n  file:\n    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名，全路径\n\n--- #################### 接口文档配置 ####################\n\nspringdoc:\n  api-docs:\n    enabled: true # 1. 是否开启 Swagger 接文档的元数据\n    path: /v3/api-docs\n  swagger-ui:\n    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面\n    path: /swagger-ui\n  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档\n\nknife4j:\n  enable: true\n  setting:\n    language: zh_cn\n\n# MyBatis Plus 的配置项\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: NONE # “智能”模式，基于 IdTypeEnvironmentPostProcessor + 数据源的类型，自动适配成 AUTO、INPUT 模式。\n      #      id-type: AUTO # 自增 ID，适合 MySQL 等直接自增的数据库\n      #      id-type: INPUT # 用户输入 ID，适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库\n      #      id-type: ASSIGN_ID # 分配 ID，默认使用雪花算法。注意，Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时，需要去除实体类上的 @KeySequence 注解\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n    banner: false # 关闭控制台的 Banner 打印\n  type-aliases-package: ${yudao.info.base-package}.dal.dataobject\n  encryptor:\n    password: XDV71a+xqStEA3WH # 加解密的秘钥，可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成\n\nmybatis-plus-join:\n  banner: false # 关闭控制台的 Banner 打印\n\n# Spring Data Redis 配置\nspring:\n  data:\n    redis:\n      repositories:\n        enabled: false # 项目未使用到 Spring Data Redis 的 Repository，所以直接禁用，保证启动速度\n\n# VO 转换（数据翻译）相关\neasy-trans:\n  is-enable-global: false # 【默认禁用，对性能确认压力大】启用全局翻译（拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置，或通过 @IgnoreTrans 忽略某个接口\n\n--- #################### RPC 远程调用相关配置 ####################\n\n--- #################### MQ 消息队列相关配置 ####################\n\n--- #################### 定时任务相关配置 ####################\n\nxxl:\n  job:\n    executor:\n      appname: ${spring.application.name} # 执行器 AppName\n      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径\n    accessToken: default_token # 执行器通讯TOKEN\n\n--- #################### 芋道相关配置 ####################\n\nyudao:\n  info:\n    version: 1.0.0\n    base-package: cn.iocoder.yudao.module.statistics\n  web:\n    admin-ui:\n      url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址\n  xss:\n    enable: false\n    exclude-urls: # 如下 url，仅仅是为了演示，去掉配置也没关系\n      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求\n  swagger:\n    title: 管理后台\n    description: 提供管理员管理的所有功能\n    version: ${yudao.info.version}\n  tenant: # 多租户相关配置项\n    enable: true\n    ignore-urls:\n    ignore-tables:\n\ndebug: false\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/logback-spring.xml",
    "content": "<configuration>\n    <!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置，优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->\n    <!-- 格式化输出：%d 表示日期，%thread 表示线程名，%-5level：级别从左显示 5 个字符宽度，%msg：日志消息，%n是换行符 -->\n    <!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">　　　　　\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 文件 Appender -->\n    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->\n    <appender name=\"FILE\"  class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <!-- 日志文件名 -->\n        <file>${LOG_FILE}</file>\n        <!-- 滚动策略：基于【每天 + 大小】创建日志文件 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->\n            <maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->\n            <maxFileSize>10MB</maxFileSize> <!-- 日志文件，到达多少容量，进行滚动 -->\n        </rollingPolicy>\n    </appender>\n    <!-- 异步写入日志，提升性能 -->\n    <appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">\n        <discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的，如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->\n        <queueSize>512</queueSize> <!-- 更改默认的队列的深度，该值会影响性能。默认值为 256 -->\n        <appender-ref ref=\"FILE\"/>\n    </appender>\n\n    <!-- SkyWalking Appender：GRPC 日志收集，实现日志中心 -->\n    <!--\n    <appender name=\"SKYWALKING\" class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender\">\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>\n            </layout>\n        </encoder>\n    </appender>\n    -->\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\"/>\n        <!-- 本地环境下，如果不想【FILE】打印日志，可以注释掉本行 -->\n        <appender-ref ref=\"ASYNC\"/>\n        <!-- 如果想接入【SkyWalking 日志服务】，可以取消注释掉本行 -->\n        <!-- <appender-ref ref=\"SKYWALKING\"/> -->\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/mapper/infra/ApiAccessLogStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.statistics.dal.mysql.infra.ApiAccessLogStatisticsMapper\">\n\n    <select id=\"selectIpCountByUserTypeAndCreateTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT COUNT(DISTINCT user_ip)\n        FROM infra_api_access_log\n        WHERE user_type = #{userType}\n        AND create_time BETWEEN #{beginTime} AND #{endTime}\n        AND deleted = FALSE\n    </select>\n\n    <select id=\"selectUserCountByUserTypeAndCreateTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT COUNT(DISTINCT user_id)\n        FROM infra_api_access_log\n        WHERE user_id > 0\n          AND user_type = #{userType}\n          AND create_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/mapper/member/MemberStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.statistics.dal.mysql.member.MemberStatisticsMapper\">\n\n    <select id=\"selectSummaryListByAreaId\"\n            resultType=\"cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO\">\n        SELECT area_id, COUNT(1) AS userCount\n        FROM member_user\n        WHERE deleted = FALSE\n        GROUP BY area_id\n    </select>\n\n    <select id=\"selectSummaryListBySex\"\n            resultType=\"cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSexStatisticsRespVO\">\n        SELECT sex, COUNT(1) AS userCount\n        FROM member_user\n        WHERE deleted = FALSE\n        GROUP BY sex\n    </select>\n\n    <select id=\"selectSummaryListByRegisterTerminal\"\n                         resultType=\"cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberTerminalStatisticsRespVO\">\n        SELECT register_terminal as terminal, COUNT(1) AS userCount\n        FROM member_user\n        WHERE deleted = FALSE\n        GROUP BY register_terminal\n    </select>\n\n    <select id=\"selectUserCount\" resultType=\"java.lang.Integer\">\n        SELECT COUNT(1)\n        FROM member_user\n        WHERE deleted = FALSE\n        <if test=\"beginTime != null\">\n            AND create_time >= #{beginTime}\n        </if>\n        <if test=\"endTime != null\">\n            AND create_time &lt;= #{endTime}\n        </if>\n    </select>\n\n    <select id=\"selectListByCreateTimeBetween\"\n            resultType=\"cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberRegisterCountRespVO\">\n        SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS date,\n               count(1)                             AS count\n        FROM member_user\n        WHERE create_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n        GROUP BY date\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/mapper/pay/PayWalletStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.statistics.dal.mysql.pay.PayWalletStatisticsMapper\">\n\n    <select id=\"selectRechargeSummaryByPayTimeBetween\"\n            resultType=\"cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO\">\n        SELECT COUNT(1)       AS rechargePayCount,\n               SUM(pay_price) AS rechargePayPrice\n        FROM pay_wallet_recharge\n        WHERE pay_status = #{payStatus}\n          AND pay_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectRechargeSummaryByRefundTimeBetween\"\n            resultType=\"cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO\">\n        SELECT COUNT(1)       AS rechargeRefundCount,\n               SUM(pay_price) AS rechargeRefundPrice\n        FROM pay_wallet_recharge\n        WHERE refund_status = #{refundStatus}\n          AND refund_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectPriceSummaryByBizTypeAndCreateTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT SUM(price)\n        FROM pay_wallet_transaction\n        WHERE biz_type = #{bizType}\n          AND create_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectRechargeSummaryGroupByWalletId\"\n            resultType=\"cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO\">\n        SELECT COUNT(DISTINCT wallet_id) AS rechargeUserCount,\n               SUM(pay_price)            AS rechargePrice\n        FROM pay_wallet_recharge\n        WHERE pay_status = #{payStatus}\n        <if test=\"beginTime != null\">\n            AND pay_time >= #{beginTime}\n        </if>\n        <if test=\"endTime != null\">\n            AND pay_time &lt;= #{endTime}\n        </if>\n            AND deleted = FALSE\n    </select>\n\n    <select id=\"selectRechargePriceSummary\" resultType=\"java.lang.Integer\">\n        SELECT IFNULL(SUM(pay_price), 0)\n        FROM pay_wallet_recharge\n        WHERE pay_status = #{payStatus}\n          AND deleted = FALSE\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/mapper/product/ProductStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsMapper\">\n\n    <select id=\"selectStatisticsResultPageByTimeBetween\"\n            resultType=\"cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO\">\n        SELECT spu.id                                                       AS spuId\n             -- 浏览量：一个用户可以有多次\n             , (SELECT COUNT(1)\n                FROM product_browse_history\n                WHERE spu_id = spu.id\n                  AND create_time BETWEEN #{beginTime} AND #{endTime})      AS browse_count\n             -- 访客量：按用户去重计数\n             , (SELECT COUNT(DISTINCT user_id)\n                FROM product_browse_history\n                WHERE spu_id = spu.id\n                  AND create_time BETWEEN #{beginTime} AND #{endTime})      AS browse_user_count\n             -- 收藏数量：按用户去重计数\n             , (SELECT COUNT(DISTINCT user_id)\n                FROM product_favorite\n                WHERE spu_id = spu.id\n                  AND create_time BETWEEN #{beginTime} AND #{endTime})      AS favorite_count\n             -- 加购数量：按用户去重计数\n             , (SELECT COUNT(DISTINCT user_id)\n                FROM trade_cart\n                WHERE spu_id = spu.id\n                  AND create_time BETWEEN #{beginTime} AND #{endTime})      AS cart_count\n             -- 下单件数\n             , (SELECT IFNULL(SUM(count), 0)\n                FROM trade_order_item\n                WHERE spu_id = spu.id\n                  AND create_time BETWEEN #{beginTime} AND #{endTime})      AS order_count\n             -- 支付件数\n             , (SELECT IFNULL(SUM(item.count), 0)\n                FROM trade_order_item item\n                         JOIN trade_order o ON item.order_id = o.id\n                WHERE spu_id = spu.id\n                  AND o.pay_status = TRUE\n                  AND item.create_time BETWEEN #{beginTime} AND #{endTime}) AS order_pay_count\n             -- 支付金额\n             , (SELECT IFNULL(SUM(item.pay_price), 0)\n                FROM trade_order_item item\n                         JOIN trade_order o ON item.order_id = o.id\n                WHERE spu_id = spu.id\n                  AND o.pay_status = TRUE\n                  AND item.create_time BETWEEN #{beginTime} AND #{endTime}) AS order_pay_price\n             -- 退款件数\n             , (SELECT IFNULL(SUM(count), 0)\n                FROM trade_after_sale\n                WHERE spu_id = spu.id\n                  AND refund_time IS NOT NULL\n                  AND create_time BETWEEN #{beginTime} AND #{endTime})      AS after_sale_count\n             -- 退款金额\n             , (SELECT IFNULL(SUM(refund_price), 0)\n                FROM trade_after_sale\n                WHERE spu_id = spu.id\n                  AND refund_time IS NOT NULL\n                  AND create_time BETWEEN #{beginTime} AND #{endTime})      AS after_sale_refund_price\n        FROM product_spu spu\n        WHERE spu.deleted = FALSE\n        ORDER BY spu.id\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/mapper/trade/AfterSaleStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.statistics.dal.mysql.trade.AfterSaleStatisticsMapper\">\n\n    <select id=\"selectSummaryByRefundTimeBetween\"\n            resultType=\"cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO\">\n        SELECT COUNT(1)          AS afterSaleCount,\n               SUM(refund_price) AS afterSaleRefundPrice\n        FROM trade_after_sale\n        WHERE refund_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectCountByStatus\" resultType=\"java.lang.Long\">\n        SELECT COUNT(1)\n        FROM trade_after_sale\n        WHERE status = #{status}\n          AND deleted = FALSE\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/mapper/trade/BrokerageStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.statistics.dal.mysql.trade.BrokerageStatisticsMapper\">\n\n    <select id=\"selectSummaryPriceByStatusAndUnfreezeTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT SUM(price)\n        FROM trade_brokerage_record\n        WHERE biz_type = #{bizType}\n          AND status = #{status}\n          AND unfreeze_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectWithdrawCountByStatus\" resultType=\"java.lang.Long\">\n        SELECT COUNT(1)\n        FROM trade_brokerage_withdraw\n        WHERE status = #{status}\n          AND deleted = FALSE\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/mapper/trade/TradeOrderStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeOrderStatisticsMapper\">\n\n    <select id=\"selectSummaryListByAreaId\"\n            resultType=\"cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO\">\n        SELECT receiver_area_id                                AS areaId,\n               (SELECT COUNT(DISTINCT s.user_id)\n                FROM trade_order AS s\n                WHERE s.receiver_area_id = m.receiver_area_id) AS orderCreateUserCount,\n               (SELECT COUNT(DISTINCT s.user_id)\n                FROM trade_order AS s\n                WHERE s.receiver_area_id = m.receiver_area_id\n                  AND s.pay_status = TRUE\n                  AND s.deleted = FALSE)                       AS orderPayUserCount,\n               (SELECT SUM(s.pay_price)\n                FROM trade_order AS s\n                WHERE s.receiver_area_id = m.receiver_area_id\n                  AND s.pay_status = TRUE\n                  AND s.deleted = FALSE)                       AS orderPayPrice\n        FROM trade_order m\n        WHERE deleted = FALSE\n        GROUP BY receiver_area_id\n    </select>\n\n    <select id=\"selectUserCountByCreateTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT COUNT(DISTINCT (user_id))\n        FROM trade_order\n        WHERE deleted = FALSE\n          AND create_time BETWEEN #{beginTime} AND #{endTime}\n    </select>\n\n    <select id=\"selectUserCountByPayTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT COUNT(DISTINCT (user_id))\n        FROM trade_order\n        WHERE pay_time BETWEEN #{beginTime} AND #{endTime}\n          AND pay_status = TRUE\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectCountByCreateTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT COUNT(1)\n        FROM trade_order\n        WHERE create_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectCountByPayTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT COUNT(1)\n        FROM trade_order\n        WHERE pay_status = TRUE\n          AND create_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectSummaryPriceByPayTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT SUM(pay_price)\n        FROM trade_order AS s\n        WHERE s.pay_status = TRUE\n          AND deleted = FALSE\n          AND create_time BETWEEN #{beginTime} AND #{endTime}\n    </select>\n\n    <select id=\"selectListByPayTimeBetweenAndGroupByDay\"\n            resultType=\"cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendRespVO\">\n        SELECT DATE_FORMAT(pay_time, '%Y-%m-%d') AS date,\n               COUNT(1)                          AS orderPayCount,\n               SUM(pay_price)                    AS orderPayPrice\n        FROM trade_order\n        WHERE pay_status = TRUE\n          AND create_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n        GROUP BY date\n    </select>\n\n    <select id=\"selectListByPayTimeBetweenAndGroupByMonth\"\n            resultType=\"cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendRespVO\">\n        SELECT DATE_FORMAT(pay_time, '%Y-%m') AS date,\n               COUNT(1)                       AS order_pay_count,\n               SUM(pay_price)                 AS order_pay_price\n        FROM trade_order\n        WHERE pay_status = TRUE\n          AND create_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n        GROUP BY date\n    </select>\n\n    <select id=\"selectCountByStatusAndDeliveryType\" resultType=\"java.lang.Long\">\n        SELECT COUNT(1)\n        FROM trade_order\n        WHERE status = #{status}\n          ANd delivery_type = #{deliveryType}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectPaySummaryByPayStatusAndPayTimeBetween\"\n            resultType=\"cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderSummaryRespVO\">\n        SELECT IFNULL(SUM(pay_price), 0) AS order_pay_price,\n               COUNT(1)                  AS order_pay_count\n        FROM trade_order\n        WHERE pay_status = #{payStatus}\n          AND pay_time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-statistics-server/src/main/resources/mapper/trade/TradeStatisticsMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeStatisticsMapper\">\n\n    <select id=\"selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween\"\n            resultType=\"cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO\">\n        SELECT IFNULL(SUM(order_create_count), 0) AS count,\n               IFNULL(SUM(order_pay_price), 0) AS summary\n        FROM trade_statistics\n        WHERE time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectVoByTimeBetween\"\n            resultType=\"cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO\">\n        SELECT\n            -- 营业额 = 商品支付金额 + 充值金额\n            SUM(order_pay_price + recharge_pay_price)                                    AS turnoverPrice,\n            SUM(order_pay_price)                                                         AS orderPayPrice,\n            SUM(recharge_pay_price)                                                      AS rechargePrice,\n            -- 支出金额 = 余额支付金额 + 支付佣金金额 + 商品退款金额\n            SUM(wallet_pay_price + brokerage_settlement_price + after_sale_refund_price) AS expensePrice,\n            SUM(wallet_pay_price)                                                        AS walletPayPrice,\n            SUM(brokerage_settlement_price)                                              AS brokerageSettlementPrice,\n            SUM(after_sale_refund_price)                                                 AS afterSaleRefundPrice\n        FROM trade_statistics\n        WHERE time BETWEEN #{beginTime} AND #{endTime}\n          AND deleted = FALSE\n    </select>\n\n    <select id=\"selectExpensePriceByTimeBetween\" resultType=\"java.lang.Integer\">\n        SELECT -- 支出金额 = 余额支付金额 + 支付佣金金额 + 商品退款金额\n               SUM(wallet_pay_price + brokerage_settlement_price + after_sale_refund_price) AS expensePrice\n        FROM trade_statistics\n        WHERE deleted = FALSE\n        <if test=\"beginTime != null\">\n            AND time >= #{beginTime}\n        </if>\n        <if test=\"endTime != null\">\n            AND time &lt;= #{endTime}\n        </if>\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-mall</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-trade-api</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        trade 模块 API，暴露给其它模块调用\n    </description>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-common</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>org.springdoc</groupId>\n            <artifactId>springdoc-openapi-ui</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java",
    "content": "package cn.iocoder.yudao.module.trade.api.order;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;\nimport cn.iocoder.yudao.module.trade.enums.ApiConstants;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.Collection;\nimport java.util.List;\n\n@FeignClient(name = ApiConstants.NAME) // TODO 芋艿：fallbackFactory =\n@Tag(name = \"RPC 服务 - 订单\")\npublic interface TradeOrderApi {\n\n    String PREFIX = ApiConstants.PREFIX + \"/order\";\n\n    @GetMapping(PREFIX + \"/list\")\n    @Operation(summary = \"获得订单列表\")\n    @Parameter(name = \"ids\", description = \"订单编号数组\", required = true)\n    CommonResult<List<TradeOrderRespDTO>> getOrderList(@RequestParam(\"ids\") Collection<Long> ids);\n\n    @GetMapping(PREFIX + \"/get\")\n    @Operation(summary = \"获得订单\")\n    @Parameter(name = \"id\", description = \"订单编号\", required = true)\n    CommonResult<TradeOrderRespDTO> getOrder(@RequestParam(\"id\") Long id);\n\n    @PutMapping(PREFIX + \"/cancel-paid\")\n    @Parameters({\n            @Parameter(name = \"userId\", description = \"用户编号\", required = true, example = \"1024\"),\n            @Parameter(name = \"orderId\", description = \"订单编号\", required = true, example = \"2048\"),\n    })\n    CommonResult<Boolean> cancelPaidOrder(@RequestParam(\"userId\") Long userId,\n                                          @RequestParam(\"orderId\") Long orderId,\n                                          @RequestParam(\"cancelType\") Integer cancelType);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/dto/TradeOrderRespDTO.java",
    "content": "package cn.iocoder.yudao.module.trade.api.order.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n/**\n * 订单信息 Response DTO\n *\n * @author HUIHUI\n */\n@Schema(description = \"RPC 服务 - 订单信息 Response DTO\")\n@Data\npublic class TradeOrderRespDTO {\n\n    // ========== 订单基本信息 ==========\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"订单流水号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1146347329394184195\")\n    private String no;\n\n    @Schema(description = \"订单类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type; // 参见 TradeOrderTypeEnum 枚举\n\n    @Schema(description = \"订单来源\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer terminal; // 参见 TerminalEnum 枚举\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long userId;\n\n    @Schema(description = \"用户 IP\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"127.0.0.1\")\n    private String userIp;\n\n    @Schema(description = \"用户备注\", example = \"这个商品不错哦\")\n    private String userRemark;\n\n    @Schema(description = \"订单状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer status; // 参见 TradeOrderStatusEnum 枚举\n\n    @Schema(description = \"购买的商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer productCount;\n\n    @Schema(description = \"订单完成时间\")\n    private LocalDateTime finishTime;\n\n    @Schema(description = \"订单取消时间\")\n    private LocalDateTime cancelTime;\n\n    @Schema(description = \"取消类型\", example = \"1\")\n    private Integer cancelType; // 参见 TradeOrderCancelTypeEnum 枚举\n\n    @Schema(description = \"商家备注\", example = \"这个用户很喜欢退货\")\n    private String remark;\n\n    @Schema(description = \"是否评价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean commentStatus;\n\n    // ========== 价格 + 支付基本信息 ==========\n\n    @Schema(description = \"支付订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long payOrderId;\n\n    @Schema(description = \"是否已支付\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean payStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/package-info.java",
    "content": "package cn.iocoder.yudao.module.trade.api;"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ApiConstants.java",
    "content": "package cn.iocoder.yudao.module.trade.enums;\n\nimport cn.iocoder.yudao.framework.common.enums.RpcConstants;\n\n/**\n * API 相关的枚举\n *\n * @author 芋道源码\n */\npublic class ApiConstants {\n\n    /**\n     * 服务名\n     *\n     * 注意，需要保证和 spring.application.name 保持一致\n     */\n    public static final String NAME = \"trade-server\";\n\n    public static final String PREFIX = RpcConstants.RPC_API_PREFIX +  \"/trade\";\n\n    public static final String VERSION = \"1.0.0\";\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/DictTypeConstants.java",
    "content": "package cn.iocoder.yudao.module.trade.enums;\n\n/**\n * Trade 字典类型的枚举类\n *\n * @author owen\n */\npublic interface DictTypeConstants {\n\n    String BROKERAGE_RECORD_STATUS = \"brokerage_record_status\"; // 佣金记录状态\n\n    String BROKERAGE_WITHDRAW_TYPE = \"brokerage_withdraw_type\"; // 佣金提现类型\n    String BROKERAGE_WITHDRAW_STATUS = \"brokerage_withdraw_status\"; // 佣金提现状态\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java",
    "content": "package cn.iocoder.yudao.module.trade.enums;\n\nimport cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n/**\n * Trade 错误码枚举类\n * trade 系统，使用 1-011-000-000 段\n *\n * @author LeeYan9\n * @since 2022-08-26\n */\npublic interface ErrorCodeConstants {\n\n    // ========== Order 模块 1-011-000-000 ==========\n    ErrorCode ORDER_ITEM_NOT_FOUND = new ErrorCode(1_011_000_010, \"交易订单项不存在\");\n    ErrorCode ORDER_NOT_FOUND = new ErrorCode(1_011_000_011, \"交易订单不存在\");\n    ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1_011_000_012, \"交易订单项更新售后状态失败，请重试\");\n    ErrorCode ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1_011_000_013, \"交易订单更新支付状态失败，订单不是【未支付】状态\");\n    ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1_011_000_014, \"交易订单更新支付状态失败，支付单编号不匹配\");\n    ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_015, \"交易订单更新支付状态失败，支付单状态不是【支付成功】状态\");\n    ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1_011_000_016, \"交易订单更新支付状态失败，支付单金额不匹配\");\n    ErrorCode ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED = new ErrorCode(1_011_000_017, \"交易订单发货失败，订单不是【待发货】状态\");\n    ErrorCode ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_018, \"交易订单收货失败，订单不是【待收货】状态\");\n    ErrorCode ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED = new ErrorCode(1_011_000_019, \"创建交易订单项的评价失败，订单不是【已完成】状态\");\n    ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1_011_000_020, \"创建交易订单项的评价失败，订单已评价\");\n    ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1_011_000_021, \"交易订单发货失败，订单已退款或部分退款\");\n    ErrorCode ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_022, \"交易订单发货失败，拼团未成功\");\n    ErrorCode ORDER_DELIVERY_FAIL_BARGAIN_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_023, \"交易订单发货失败，砍价未成功\");\n    ErrorCode ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS = new ErrorCode(1_011_000_024, \"交易订单发货失败，发货类型不是快递\");\n    ErrorCode ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID = new ErrorCode(1_011_000_025, \"交易订单取消失败，订单不是【待支付】状态\");\n    ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1_011_000_026, \"支付订单调价失败，原因：支付订单已付款,不能调价\");\n    ErrorCode ORDER_UPDATE_PRICE_FAIL_ALREADY = new ErrorCode(1_011_000_027, \"支付订单调价失败，原因：已经修改过价格\");\n    ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1_011_000_028, \"支付订单调价失败，原因：调整后支付价格不能小于 0.01 元\");\n    ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1_011_000_029, \"交易订单删除失败，订单不是【已取消】状态\");\n    ErrorCode ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP = new ErrorCode(1_011_000_030, \"交易订单自提失败，收货方式不是【用户自提】\");\n    ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, \"交易订单修改收货地址失败，原因：订单不是【待发货】状态\");\n    ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, \"交易订单创建失败，原因：存在未付款订单\");\n    ErrorCode ORDER_CANCEL_PAID_FAIL = new ErrorCode(1_011_000_033, \"交易订单取消支付失败，原因：订单不是【{}】状态\");\n    ErrorCode ORDER_UPDATE_PAID_ORDER_REFUNDED_FAIL_REFUND_NOT_FOUND = new ErrorCode(1_011_000_034, \"交易订单更新支付订单退款状态失败，原因：退款单不存在\");\n    ErrorCode ORDER_UPDATE_PAID_ORDER_REFUNDED_FAIL_REFUND_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_035, \"交易订单更新支付订单退款状态失败，原因：退款单状态不是【退款成功】\");\n    ErrorCode ORDER_PICK_UP_FAIL_NOT_VERIFY_USER = new ErrorCode(1_011_000_036, \"交易订单自提失败，原因：你没有核销该门店订单的权限\");\n    ErrorCode ORDER_PICK_UP_FAIL_COMBINATION_NOT_SUCCESS = new ErrorCode(1_011_000_037, \"交易订单自提失败，原因：商品拼团记录不是【成功】状态\");\n    ErrorCode ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS = new ErrorCode(1_011_000_038, \"交易订单创建失败，原因：用户积分不足\");\n    ErrorCode ORDER_PICK_UP_FAIL_STATUS_NOT_UNDELIVERED = new ErrorCode(1_011_000_039, \"交易订单自提失败，订单不是【待核销】状态\");\n\n    // ========== After Sale 模块 1-011-000-100 ==========\n    ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, \"售后单不存在\");\n    ErrorCode AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR = new ErrorCode(1_011_000_101, \"申请退款金额错误\");\n    ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED = new ErrorCode(1_011_000_102, \"订单已关闭，无法申请售后\");\n    ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID = new ErrorCode(1_011_000_103, \"订单未支付，无法申请售后\");\n    ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED = new ErrorCode(1_011_000_104, \"订单未发货，无法申请【退货退款】售后\");\n    ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED = new ErrorCode(1_011_000_105, \"订单项已申请售后，无法重复申请\");\n    ErrorCode AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY = new ErrorCode(1_011_000_106, \"审批失败，售后状态不处于审批中\");\n    ErrorCode AFTER_SALE_UPDATE_STATUS_FAIL = new ErrorCode(1_011_000_107, \"操作售后单失败，请刷新后重试\");\n    ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1_011_000_108, \"退货失败，售后单状态不处于【待买家退货】\");\n    ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1_011_000_109, \"确认收货失败，售后单状态不处于【待确认收货】\");\n    ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, \"退款失败，售后单状态不是【待退款】\");\n    ErrorCode AFTER_SALE_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1_011_000_111, \"退款失败，退款单不存在\");\n    ErrorCode AFTER_SALE_REFUND_FAIL_REFUND_NOT_SUCCESS_OR_FAILURE = new ErrorCode(1_011_000_112, \"退款失败，退款单未退款\");\n    ErrorCode AFTER_SALE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1_011_000_113, \"退款失败，退款金额不匹配\");\n    ErrorCode AFTER_SALE_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1_011_000_114, \"退款失败，退款单不匹配\");\n    ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY =\n            new ErrorCode(1_011_000_115, \"取消售后单失败，售后单状态不是【待审核】或【卖家同意】或【商家待收货】\");\n    ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_COMBINATION_IN_PROGRESS = new ErrorCode(1_011_000_116, \"订单拼团中，无法申请售后\");\n\n    // ========== Cart 模块 1-011-002-000 ==========\n    ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, \"购物车项不存在\");\n\n    // ========== Price 相关 1-011-003-000 ============\n    ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1_011_003_000, \"支付价格计算异常，原因：价格小于等于 0\");\n    ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1_011_003_001, \"计算快递运费异常，找不到对应的运费模板\");\n    ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_002, \"参与秒杀、拼团、砍价的营销商品，无法使用优惠劵\");\n    ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_003, \"参与秒杀的商品，超过了秒杀总限购数量\");\n    ErrorCode PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_004, \"参与积分活动的商品，超过了积分活动商品总限购数量\");\n    ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL = new ErrorCode(1_011_003_005, \"计算快递运费异常，配送方式不匹配\");\n    ErrorCode PRICE_CALCULATE_COUPON_CAN_NOT_USE = new ErrorCode(1_011_003_006, \"该优惠劵无法使用，原因：{}」\");\n\n    // ========== 物流 Express 模块 1-011-004-000 ==========\n    ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, \"快递公司不存在\");\n    ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1_011_004_001, \"已经存在该编码的快递公司\");\n    ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1_011_004_002, \"需要接入快递服务商，比如【快递100】\");\n    ErrorCode EXPRESS_STATUS_NOT_ENABLE = new ErrorCode(1_011_004_003, \"快递公司未启用\");\n\n    ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1_011_004_101, \"快递查询接口异常\");\n    ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1_011_004_102, \"快递查询返回失败，原因：{}\");\n\n    // ========== 物流 Template 模块 1-011-005-000 ==========\n    ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1_011_005_000, \"已经存在该运费模板名\");\n    ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_011_005_001, \"运费模板不存在\");\n\n    // ==========  物流 PICK_UP 模块 1-011-006-000 ==========\n    ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1_011_006_000, \"自提门店不存在\");\n    ErrorCode PICK_UP_STORE_STAFF_NOT_EXISTS = new ErrorCode(1_011_006_000, \"自提门店店员不存在\");\n\n    // ========== 分销用户 模块 1-011-007-000 ==========\n    ErrorCode BROKERAGE_USER_NOT_EXISTS = new ErrorCode(1_011_007_000, \"分销用户不存在\");\n    ErrorCode BROKERAGE_USER_FROZEN_PRICE_NOT_ENOUGH = new ErrorCode(1_011_007_001, \"用户冻结佣金({})数量不足\");\n    ErrorCode BROKERAGE_BIND_SELF = new ErrorCode(1_011_007_002, \"不能绑定自己\");\n    ErrorCode BROKERAGE_BIND_USER_NOT_ENABLED = new ErrorCode(1_011_007_003, \"绑定用户没有推广资格\");\n    ErrorCode BROKERAGE_BIND_CONDITION_ADMIN = new ErrorCode(1_011_007_004, \"仅可在后台绑定推广员\");\n    ErrorCode BROKERAGE_BIND_MODE_REGISTER = new ErrorCode(1_011_007_005, \"只有在注册时可以绑定\");\n    ErrorCode BROKERAGE_BIND_OVERRIDE = new ErrorCode(1_011_007_006, \"已绑定了推广人\");\n    ErrorCode BROKERAGE_BIND_LOOP = new ErrorCode(1_011_007_007, \"下级不能绑定自己的上级\");\n    ErrorCode BROKERAGE_USER_LEVEL_NOT_SUPPORT = new ErrorCode(1_011_007_008, \"目前只支持 level 小于等于 2\");\n    ErrorCode BROKERAGE_CREATE_USER_EXISTS = new ErrorCode(1_011_007_009, \"分销用户已存在\");\n\n    // ========== 分销提现 模块 1-011-008-000 ==========\n    ErrorCode BROKERAGE_WITHDRAW_NOT_EXISTS = new ErrorCode(1_011_008_000, \"佣金提现记录不存在\");\n    ErrorCode BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING = new ErrorCode(1_011_008_001, \"佣金提现记录状态不是审核中\");\n    ErrorCode BROKERAGE_WITHDRAW_MIN_PRICE = new ErrorCode(1_011_008_002, \"提现金额不能低于 {} 元\");\n    ErrorCode BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH = new ErrorCode(1_011_008_003, \"您当前最多可提现 {} 元\");\n    ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_ID_ERROR = new ErrorCode(1_011_008_005, \"提现单更新转账状态失败，转账单不匹配\");\n    ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_STATUS_NOT_SUCCESS_OR_CLOSED = new ErrorCode(1_011_008_006, \"提现单更新转账状态失败，转账单状态不是成功或关闭状态\");\n    ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1_011_008_007, \"提现单更新转账状态失败，转账单金额不匹配\");\n    ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_MERCHANT_EXISTS = new ErrorCode(1_011_008_008, \"提现单更新转账状态失败，转账单的商户订单不匹配\");\n    ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_CHANNEL_NOT_MATCH = new ErrorCode(1_011_008_009, \"提现单更新转账状态失败，转账渠道不匹配\");\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java",
    "content": "package cn.iocoder.yudao.module.trade.enums;\n\n/**\n * 通知模板枚举类\n *\n * @author HUIHUI\n */\npublic interface MessageTemplateConstants {\n\n    // ======================= 短信消息模版 =======================\n\n    String SMS_ORDER_DELIVERY = \"order_delivery\"; // 短信模版编号\n\n    // ======================= 小程序订阅消息模版 =======================\n\n    String WXA_ORDER_DELIVERY = \"订单发货通知\";\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/AfterSaleOperateTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.aftersale;\n\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\n/**\n * 售后操作类型的枚举\n *\n * @author 陈賝\n * @since 2023/6/13 13:53\n */\n@RequiredArgsConstructor\n@Getter\npublic enum AfterSaleOperateTypeEnum {\n\n    MEMBER_CREATE(10, \"会员申请退款\"),\n    ADMIN_AGREE_APPLY(11, \"商家同意退款\"),\n    ADMIN_DISAGREE_APPLY(12, \"商家拒绝退款\"),\n    MEMBER_DELIVERY(20, \"会员填写退货物流信息，快递公司：{deliveryName}，快递单号：{logisticsNo}\"),\n    ADMIN_AGREE_RECEIVE(21, \"商家收货\"),\n    ADMIN_DISAGREE_RECEIVE(22, \"商家拒绝收货，原因：{reason}\"),\n    ADMIN_REFUND(30, \"商家发起退款\"),\n    SYSTEM_REFUND_SUCCESS(31, \"退款成功\"),\n    SYSTEM_REFUND_FAIL(32, \"退款失败\"),\n    MEMBER_CANCEL(40, \"会员取消退款\"),\n    ;\n\n    /**\n     * 操作类型\n     */\n    private final Integer type;\n    /**\n     * 操作描述\n     */\n    private final String content;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/AfterSaleStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.aftersale;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport static cn.hutool.core.util.ArrayUtil.firstMatch;\n\n/**\n * 售后状态的枚举\n *\n * <a href=\"https://www.processon.com/view/link/63731a270e3e742ce7b7c194\">状态流转</a>\n *\n * @author 芋道源码\n */\n@AllArgsConstructor\n@Getter\npublic enum AfterSaleStatusEnum implements ArrayValuable<Integer> {\n\n    /**\n     * 【申请售后】\n     */\n    APPLY(10,\"申请中\", \"会员申请退款\"), // 有赞的状态提示：退款申请待商家处理\n    /**\n     * 卖家通过售后；【商品待退货】\n     */\n    SELLER_AGREE(20, \"卖家通过\", \"商家同意退款\"), // 有赞的状态提示：请退货并填写物流信息\n    /**\n     * 买家已退货，等待卖家收货；【商家待收货】\n     */\n    BUYER_DELIVERY(30,\"待卖家收货\", \"会员填写退货物流信息\"), // 有赞的状态提示：退货退款申请待商家处理\n    /**\n     * 卖家已收货，等待平台退款；等待退款【等待退款】\n     */\n    WAIT_REFUND(40, \"等待平台退款\", \"商家收货\"), // 有赞的状态提示：无（有赞无该状态）\n    /**\n     * 完成退款【退款成功】\n     */\n    COMPLETE(50, \"完成\", \"商家确认退款\"), // 有赞的状态提示：退款成功\n    /**\n     * 【买家取消】\n     */\n    BUYER_CANCEL(61, \"买家取消售后\", \"会员取消退款\"), // 有赞的状态提示：退款关闭\n    /**\n     * 卖家拒绝售后；商家拒绝【商家拒绝】\n     */\n    SELLER_DISAGREE(62,\"卖家拒绝\", \"商家拒绝退款\"), // 有赞的状态提示：商家不同意退款申请\n    /**\n     * 卖家拒绝收货，终止售后；【商家拒收货】\n     */\n    SELLER_REFUSE(63,\"卖家拒绝收货\", \"商家拒绝收货\"), // 有赞的状态提示：商家拒绝收货，不同意退款\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AfterSaleStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 进行中的售后状态\n     *\n     * 不包括已经结束的状态\n     */\n    public static final Collection<Integer> APPLYING_STATUSES = Arrays.asList(\n            APPLY.getStatus(),\n            SELLER_AGREE.getStatus(),\n            BUYER_DELIVERY.getStatus(),\n            WAIT_REFUND.getStatus()\n    );\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n    /**\n     * 操作内容\n     *\n     * 目的：记录售后日志的内容\n     */\n    private final String content;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static AfterSaleStatusEnum valueOf(Integer status) {\n        return firstMatch(value -> value.getStatus().equals(status), values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/AfterSaleTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.aftersale;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * 交易售后 - 类型\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum AfterSaleTypeEnum implements ArrayValuable<Integer> {\n\n    IN_SALE(10, \"售中退款\"), // 交易完成前买家申请退款\n    AFTER_SALE(20, \"售后退款\"); // 交易完成后买家申请退款\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AfterSaleTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 类型名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/AfterSaleWayEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.aftersale;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * 交易售后 - 方式\n *\n * @author Sin\n */\n@RequiredArgsConstructor\n@Getter\npublic enum AfterSaleWayEnum implements ArrayValuable<Integer> {\n\n    REFUND(10, \"仅退款\"),\n    RETURN_AND_REFUND(20, \"退货退款\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AfterSaleWayEnum::getWay).toArray(Integer[]::new);\n\n    /**\n     * 方式\n     */\n    private final Integer way;\n    /**\n     * 方式名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageBindModeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.brokerage;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 分销关系绑定模式枚举\n *\n * @author owen\n */\n@AllArgsConstructor\n@Getter\npublic enum BrokerageBindModeEnum implements ArrayValuable<Integer> {\n\n    /**\n     * 只要用户没有推广人，随时都可以绑定分销关系\n     */\n    ANYTIME(1, \"首次绑定\"),\n    /**\n     * 仅新用户注册时才能绑定推广关系\n     */\n    REGISTER(2, \"注册绑定\"),\n    /**\n     * 每次扫码都覆盖\n     */\n    OVERRIDE(3, \"覆盖绑定\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageBindModeEnum::getMode).toArray(Integer[]::new);\n\n    /**\n     * 模式\n     */\n    private final Integer mode;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageEnabledConditionEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.brokerage;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 分佣模式枚举\n *\n * @author owen\n */\n@AllArgsConstructor\n@Getter\npublic enum BrokerageEnabledConditionEnum implements ArrayValuable<Integer> {\n\n    /**\n     * 所有用户都可以分销\n     */\n    ALL(1, \"人人分销\"),\n    /**\n     * 仅可后台手动设置推广员\n     */\n    ADMIN(2, \"指定分销\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageEnabledConditionEnum::getCondition).toArray(Integer[]::new);\n\n    /**\n     * 模式\n     */\n    private final Integer condition;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordBizTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.brokerage;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 佣金记录业务类型枚举\n *\n * @author owen\n */\n@AllArgsConstructor\n@Getter\npublic enum BrokerageRecordBizTypeEnum implements ArrayValuable<Integer> {\n\n    ORDER(1, \"获得推广佣金\", \"获得推广佣金 {}\", true),\n    WITHDRAW(2, \"提现申请\", \"提现申请扣除佣金 {}\", false),\n    WITHDRAW_REJECT(3, \"提现申请驳回\", \"提现申请驳回，返还佣金 {}\", true),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageRecordBizTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 标题\n     */\n    private final String title;\n    /**\n     * 描述\n     */\n    private final String description;\n    /**\n     * 是否为增加佣金\n     */\n    private final boolean add;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.brokerage;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 佣金记录状态枚举\n *\n * @author owen\n */\n@AllArgsConstructor\n@Getter\npublic enum BrokerageRecordStatusEnum implements ArrayValuable<Integer> {\n\n    WAIT_SETTLEMENT(0, \"待结算\"),\n    SETTLEMENT(1, \"已结算\"),\n    CANCEL(2, \"已取消\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageRecordStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.brokerage;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 佣金提现状态枚举\n *\n * @author owen\n */\n@AllArgsConstructor\n@Getter\npublic enum BrokerageWithdrawStatusEnum implements ArrayValuable<Integer> {\n\n    AUDITING(0, \"审核中\"),\n    AUDIT_SUCCESS(10, \"审核通过\"),\n    WITHDRAW_SUCCESS(11, \"提现成功\"),\n    AUDIT_FAIL(20, \"审核不通过\"),\n    WITHDRAW_FAIL(21, \"提现失败\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageWithdrawStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态\n     */\n    private final Integer status;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.brokerage;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 佣金提现类型枚举\n *\n * @author owen\n */\n@AllArgsConstructor\n@Getter\npublic enum BrokerageWithdrawTypeEnum implements ArrayValuable<Integer> {\n\n    WALLET(1, \"钱包\"),\n    BANK(2, \"银行卡\"),\n    WECHAT_QR(3, \"微信收款码\"), // 手动打款\n    ALIPAY_QR(4, \"支付宝收款码\"), // 手动打款\n    WECHAT_API(5, \"微信零钱\"), // 自动打款，通过微信转账 API\n    ALIPAY_API(6, \"支付宝余额\"), // 自动打款，通过支付宝转账 API\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageWithdrawTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 名字\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    /**\n     * 是否通过支付平台的 API 打款\n     *\n     * @param type 类型\n     * @return 是否\n     */\n    public static boolean isApi(Integer type) {\n        return ObjectUtils.equalsAny(type, WALLET.getType(), ALIPAY_API.getType(), WECHAT_API.getType());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryExpressChargeModeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.delivery;\n\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 快递配送计费方式枚举\n *\n * @author jason\n */\n@AllArgsConstructor\n@Getter\npublic enum DeliveryExpressChargeModeEnum implements ArrayValuable<Integer> {\n\n    COUNT(1, \"按件\"),\n    WEIGHT(2,\"按重量\"),\n    VOLUME(3, \"按体积\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(DeliveryExpressChargeModeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 描述\n     */\n    private final String desc;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static DeliveryExpressChargeModeEnum valueOf(Integer value) {\n        return ArrayUtil.firstMatch(chargeMode -> chargeMode.getType().equals(value), DeliveryExpressChargeModeEnum.values());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.delivery;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.Arrays;\n\n/**\n * 配送方式枚举\n *\n * @author 芋道源码\n */\n@Getter\n@AllArgsConstructor\npublic enum DeliveryTypeEnum implements ArrayValuable<Integer> {\n\n    EXPRESS(1, \"快递发货\"),\n    PICK_UP(2, \"用户自提\"),;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(DeliveryTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 配送方式\n     */\n    private final Integer type;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/notify/TradeNotifyEnums.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.notify;\n\n// TODO @芋艿：这个枚举的作用？\npublic interface TradeNotifyEnums {\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderCancelTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.order;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * 交易订单 - 关闭类型\n *\n * @author Sin\n */\n@RequiredArgsConstructor\n@Getter\npublic enum TradeOrderCancelTypeEnum implements ArrayValuable<Integer> {\n\n    PAY_TIMEOUT(10, \"超时未支付\"),\n    AFTER_SALE_CLOSE(20, \"退款关闭\"),\n    MEMBER_CANCEL(30, \"买家取消\"),\n    COMBINATION_CLOSE(40, \"拼团关闭\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderCancelTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 关闭类型\n     */\n    private final Integer type;\n    /**\n     * 关闭类型名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderItemAfterSaleStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.order;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * 交易订单项 - 售后状态\n *\n * @author 芋道源码\n */\n@RequiredArgsConstructor\n@Getter\npublic enum TradeOrderItemAfterSaleStatusEnum implements ArrayValuable<Integer> {\n\n    NONE(0, \"未售后\"),\n    APPLY(10, \"售后中\"),\n    SUCCESS(20, \"售后成功\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderItemAfterSaleStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态值\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    /**\n     * 判断指定状态，是否正处于【未申请】状态\n     *\n     * @param status 指定状态\n     * @return 是否\n     */\n    public static boolean isNone(Integer status) {\n        return ObjectUtil.equals(status, NONE.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderOperateTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.order;\n\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\n/**\n * 订单操作类型的枚举\n *\n * @author 陈賝\n * @since 2023/7/6 15:31\n */\n@RequiredArgsConstructor\n@Getter\npublic enum TradeOrderOperateTypeEnum {\n\n    MEMBER_CREATE(1, \"用户下单\"),\n    ADMIN_UPDATE_PRICE(2, \"订单价格 {oldPayPrice} 修改，调整价格 {adjustPrice}，实际支付金额为 {newPayPrice} 元\"),\n    MEMBER_PAY(10, \"用户付款成功\"),\n    ADMIN_UPDATE_ADDRESS(11, \"收货地址修改\"),\n    ADMIN_DELIVERY(20, \"已发货，快递公司：{expressName}，快递单号：{logisticsNo}\"),\n    MEMBER_RECEIVE(30, \"用户已收货\"),\n    SYSTEM_RECEIVE(31, \"到期未收货，系统自动确认收货\"),\n    ADMIN_PICK_UP_RECEIVE(32, \"管理员自提收货\"),\n    MEMBER_COMMENT(33, \"用户评价\"),\n    SYSTEM_COMMENT(34, \"到期未评价，系统自动评价\"),\n    MEMBER_CANCEL(40, \"取消订单\"),\n    SYSTEM_CANCEL(41, \"到期未支付，系统自动取消订单\"),\n    // 42 预留：管理员取消订单\n    ADMIN_CANCEL_AFTER_SALE(43, \"订单全部售后，管理员自动取消订单\"),\n    MEMBER_DELETE(49, \"删除订单\"),\n    ;\n\n    /**\n     * 操作类型\n     */\n    private final Integer type;\n    /**\n     * 操作描述\n     */\n    private final String content;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderRefundStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.order;\n\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * 交易订单 - 退款状态\n *\n * @author Sin\n */\n@RequiredArgsConstructor\n@Getter\npublic enum TradeOrderRefundStatusEnum implements ArrayValuable<Integer> {\n\n    NONE(0, \"未退款\"),\n    PART(10, \"部分退款\"),\n    ALL(20, \"全部退款\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderRefundStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态值\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.order;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport cn.iocoder.yudao.framework.common.util.object.ObjectUtils;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * 交易订单 - 状态\n *\n * @author Sin\n */\n@RequiredArgsConstructor\n@Getter\npublic enum TradeOrderStatusEnum implements ArrayValuable<Integer> {\n\n    UNPAID(0, \"待支付\"),\n    UNDELIVERED(10, \"待发货\"),\n    DELIVERED(20, \"已发货\"),\n    COMPLETED(30, \"已完成\"),\n    CANCELED(40, \"已取消\");\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderStatusEnum::getStatus).toArray(Integer[]::new);\n\n    /**\n     * 状态值\n     */\n    private final Integer status;\n    /**\n     * 状态名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    // ========== 问：为什么写了很多 isXXX 和 haveXXX 的判断逻辑呢？ ==========\n    // ========== 答：方便找到某一类判断，哪些业务正在使用 ==========\n\n    /**\n     * 判断指定状态，是否正处于【未付款】状态\n     *\n     * @param status 指定状态\n     * @return 是否\n     */\n    public static boolean isUnpaid(Integer status) {\n        return ObjectUtil.equal(UNPAID.getStatus(), status);\n    }\n\n    /**\n     * 判断指定状态，是否正处于【待发货】状态\n     *\n     * @param status 指定状态\n     * @return 是否\n     */\n    public static boolean isUndelivered(Integer status) {\n        return ObjectUtil.equal(UNDELIVERED.getStatus(), status);\n    }\n\n    /**\n     * 判断指定状态，是否正处于【已发货】状态\n     *\n     * @param status 指定状态\n     * @return 是否\n     */\n    public static boolean isDelivered(Integer status) {\n        return ObjectUtil.equals(status, DELIVERED.getStatus());\n    }\n\n    /**\n     * 判断指定状态，是否正处于【已取消】状态\n     *\n     * @param status 指定状态\n     * @return 是否\n     */\n    public static boolean isCanceled(Integer status) {\n        return ObjectUtil.equals(status, CANCELED.getStatus());\n    }\n\n    /**\n     * 判断指定状态，是否正处于【已完成】状态\n     *\n     * @param status 指定状态\n     * @return 是否\n     */\n    public static boolean isCompleted(Integer status) {\n        return ObjectUtil.equals(status, COMPLETED.getStatus());\n    }\n\n    /**\n     * 判断指定状态，是否有过【已付款】状态\n     *\n     * @param status 指定状态\n     * @return 是否\n     */\n    public static boolean havePaid(Integer status) {\n        return ObjectUtils.equalsAny(status, UNDELIVERED.getStatus(),\n                DELIVERED.getStatus(), COMPLETED.getStatus());\n    }\n\n    /**\n     * 判断指定状态，是否有过【已发货】状态\n     *\n     * @param status 指定状态\n     * @return 是否\n     */\n    public static boolean haveDelivered(Integer status) {\n        return ObjectUtils.equalsAny(status, DELIVERED.getStatus(), COMPLETED.getStatus());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java",
    "content": "package cn.iocoder.yudao.module.trade.enums.order;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.iocoder.yudao.framework.common.core.ArrayValuable;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Arrays;\n\n/**\n * 交易订单 - 类型\n *\n * @author Sin\n */\n@RequiredArgsConstructor\n@Getter\npublic enum TradeOrderTypeEnum implements ArrayValuable<Integer> {\n\n    NORMAL(0, \"普通订单\"),\n    SECKILL(1, \"秒杀订单\"),\n    BARGAIN(2, \"砍价订单\"),\n    COMBINATION(3, \"拼团订单\"),\n    POINT(4, \"积分商城\"),\n    ;\n\n    public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderTypeEnum::getType).toArray(Integer[]::new);\n\n    /**\n     * 类型\n     */\n    private final Integer type;\n    /**\n     * 类型名\n     */\n    private final String name;\n\n    @Override\n    public Integer[] array() {\n        return ARRAYS;\n    }\n\n    public static boolean isNormal(Integer type) {\n        return ObjectUtil.equal(type, NORMAL.getType());\n    }\n\n    public static boolean isSeckill(Integer type) {\n        return ObjectUtil.equal(type, SECKILL.getType());\n    }\n\n    public static boolean isBargain(Integer type) {\n        return ObjectUtil.equal(type, BARGAIN.getType());\n    }\n\n    public static boolean isCombination(Integer type) {\n        return ObjectUtil.equal(type, COMBINATION.getType());\n    }\n\n    public static boolean isPoint(Integer type) {\n        return ObjectUtil.equal(type, POINT.getType());\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/Dockerfile",
    "content": "## AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\n## 感谢复旦核博士的建议！灰子哥，牛皮！\nFROM eclipse-temurin:8-jre\n\n## 创建目录，并使用它作为工作目录\nRUN mkdir -p /yudao-module-trade-biz\nWORKDIR /yudao-module-trade-biz\n## 将后端项目的 Jar 文件，复制到镜像中\nCOPY ./target/yudao-module-trade-biz.jar app.jar\n\n## 设置 TZ 时区\n## 设置 JAVA_OPTS 环境变量，可通过 docker run -e \"JAVA_OPTS=\" 进行覆盖\nENV TZ=Asia/Shanghai JAVA_OPTS=\"-Xms512m -Xmx512m\"\n\n## 暴露后端项目的 48080 端口\nEXPOSE 48102\n\n## 启动后端项目\nCMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>cn.iocoder.cloud</groupId>\n        <artifactId>yudao-module-mall</artifactId>\n        <version>${revision}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>yudao-module-trade-server</artifactId>\n    <packaging>jar</packaging>\n\n    <name>${project.artifactId}</name>\n    <description>\n        trade 模块，主要实现交易相关功能\n        例如：订单、退款、购物车等功能。\n    </description>\n\n    <dependencies>\n        <!-- Spring Cloud 基础 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-env</artifactId>\n        </dependency>\n\n        <!-- 依赖服务 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-trade-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-product-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-pay-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-promotion-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-member-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-module-system-api</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <!-- 业务组件 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>\n        </dependency>\n\n        <!-- Web 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- DB 相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-redis</artifactId>\n        </dependency>\n\n        <!-- RPC 远程调用相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-rpc</artifactId>\n        </dependency>\n\n        <!-- Registry 注册中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- Config 配置中心相关 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- Job 定时任务相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-job</artifactId>\n        </dependency>\n\n        <!-- Test 测试相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <!-- 工具类相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-excel</artifactId>\n        </dependency>\n\n        <!-- 监控相关 -->\n        <dependency>\n            <groupId>cn.iocoder.cloud</groupId>\n            <artifactId>yudao-spring-boot-starter-monitor</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n            <!-- 打包 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/TradeServerApplication.java",
    "content": "package cn.iocoder.yudao.module.trade;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 项目的启动类\n *\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n * 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n *\n * @author 芋道源码\n */\n@SpringBootApplication\npublic class TradeServerApplication {\n\n    public static void main(String[] args) {\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n\n        SpringApplication.run(TradeServerApplication.class, args);\n\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n        // 如果你碰到启动的问题，请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java",
    "content": "package cn.iocoder.yudao.module.trade.api.order;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;\nimport cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;\nimport cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;\nimport cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n/**\n * 订单 API 接口实现类\n *\n * @author HUIHUI\n */\n@RestController // 提供 RESTful API 接口，给 Feign 调用\n@Validated\npublic class TradeOrderApiImpl implements TradeOrderApi {\n\n    @Resource\n    private TradeOrderUpdateService tradeOrderUpdateService;\n    @Resource\n    private TradeOrderQueryService tradeOrderQueryService;\n\n    @Override\n    public CommonResult<List<TradeOrderRespDTO>> getOrderList(Collection<Long> ids) {\n        return success(TradeOrderConvert.INSTANCE.convertList04(tradeOrderQueryService.getOrderList(ids)));\n    }\n\n    @Override\n    public CommonResult<TradeOrderRespDTO> getOrder(Long id) {\n        return success(TradeOrderConvert.INSTANCE.convert(tradeOrderQueryService.getOrder(id)));\n    }\n\n    @Override\n    public CommonResult<Boolean> cancelPaidOrder(Long userId, Long orderId, Integer cancelType) {\n        tradeOrderUpdateService.cancelPaidOrder(userId, orderId, cancelType);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/api/package-info.java",
    "content": "package cn.iocoder.yudao.module.trade.api;"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/AfterSaleController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.aftersale;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.*;\nimport cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;\nimport cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleLogService;\nimport cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;\nimport cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;\nimport cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - 售后订单\")\n@RestController\n@RequestMapping(\"/trade/after-sale\")\n@Validated\n@Slf4j\npublic class AfterSaleController {\n\n    @Resource\n    private AfterSaleService afterSaleService;\n    @Resource\n    private TradeOrderQueryService tradeOrderQueryService;\n    @Resource\n    private TradeOrderUpdateService tradeOrderUpdateService;\n    @Resource\n    private AfterSaleLogService afterSaleLogService;\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得售后订单分页\")\n    @PreAuthorize(\"@ss.hasPermission('trade:after-sale:query')\")\n    public CommonResult<PageResult<AfterSaleRespPageItemVO>> getAfterSalePage(@Valid AfterSalePageReqVO pageVO) {\n        // 查询售后\n        PageResult<AfterSaleDO> pageResult = afterSaleService.getAfterSalePage(pageVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n\n        // 查询会员\n        Map<Long, MemberUserRespDTO> memberUsers = memberUserApi.getUserMap(\n                convertSet(pageResult.getList(), AfterSaleDO::getUserId));\n        return success(AfterSaleConvert.INSTANCE.convertPage(pageResult, memberUsers));\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得售后订单详情\")\n    @Parameter(name = \"id\", description = \"售后编号\", required = true, example = \"1\")\n    @PreAuthorize(\"@ss.hasPermission('trade:after-sale:query')\")\n    public CommonResult<AfterSaleDetailRespVO> getOrderDetail(@RequestParam(\"id\") Long id) {\n        // 查询订单\n        AfterSaleDO afterSale = afterSaleService.getAfterSale(id);\n        if (afterSale == null) {\n            return success(null);\n        }\n\n        // 查询订单\n        TradeOrderDO order = tradeOrderQueryService.getOrder(afterSale.getOrderId());\n        // 查询订单项\n        TradeOrderItemDO orderItem = tradeOrderQueryService.getOrderItem(afterSale.getOrderItemId());\n        // 拼接数据\n        MemberUserRespDTO user = memberUserApi.getUser(afterSale.getUserId()).getCheckedData();\n        List<AfterSaleLogDO> logs = afterSaleLogService.getAfterSaleLogList(afterSale.getId());\n        return success(AfterSaleConvert.INSTANCE.convert(afterSale, order, orderItem, user, logs));\n    }\n\n    @PutMapping(\"/agree\")\n    @Operation(summary = \"同意售后\")\n    @Parameter(name = \"id\", description = \"售后编号\", required = true, example = \"1\")\n    @PreAuthorize(\"@ss.hasPermission('trade:after-sale:agree')\")\n    public CommonResult<Boolean> agreeAfterSale(@RequestParam(\"id\") Long id) {\n        afterSaleService.agreeAfterSale(getLoginUserId(), id);\n        return success(true);\n    }\n\n    @PutMapping(\"/disagree\")\n    @Operation(summary = \"拒绝售后\")\n    @PreAuthorize(\"@ss.hasPermission('trade:after-sale:disagree')\")\n    public CommonResult<Boolean> disagreeAfterSale(@RequestBody AfterSaleDisagreeReqVO confirmReqVO) {\n        afterSaleService.disagreeAfterSale(getLoginUserId(), confirmReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/receive\")\n    @Operation(summary = \"确认收货\")\n    @Parameter(name = \"id\", description = \"售后编号\", required = true, example = \"1\")\n    @PreAuthorize(\"@ss.hasPermission('trade:after-sale:receive')\")\n    public CommonResult<Boolean> receiveAfterSale(@RequestParam(\"id\") Long id) {\n        afterSaleService.receiveAfterSale(getLoginUserId(), id);\n        return success(true);\n    }\n\n    @PutMapping(\"/refuse\")\n    @Operation(summary = \"拒绝收货\")\n    @Parameter(name = \"id\", description = \"售后编号\", required = true, example = \"1\")\n    @PreAuthorize(\"@ss.hasPermission('trade:after-sale:receive')\")\n    public CommonResult<Boolean> refuseAfterSale(AfterSaleRefuseReqVO refuseReqVO) {\n        afterSaleService.refuseAfterSale(getLoginUserId(), refuseReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/refund\")\n    @Operation(summary = \"确认退款\")\n    @Parameter(name = \"id\", description = \"售后编号\", required = true, example = \"1\")\n    @PreAuthorize(\"@ss.hasPermission('trade:after-sale:refund')\")\n    public CommonResult<Boolean> refundAfterSale(@RequestParam(\"id\") Long id) {\n        afterSaleService.refundAfterSale(getLoginUserId(), getClientIP(), id);\n        return success(true);\n    }\n\n    @PostMapping(\"/update-refunded\")\n    @Operation(summary = \"更新售后订单为已退款\") // 由 pay-module 支付服务，进行回调，可见 PayNotifyJob\n    @PermitAll // 无需登录，安全由 AfterSaleService 内部校验实现\n    public CommonResult<Boolean> updateAfterSaleRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) {\n        log.info(\"[updateAfterRefund][notifyReqDTO({})]\", notifyReqDTO);\n        if (StrUtil.startWithAny(notifyReqDTO.getMerchantRefundId(), \"order-\")) {\n            Long orderId = Long.parseLong(StrUtil.subAfter(notifyReqDTO.getMerchantRefundId(), \"order-\", true));\n            tradeOrderUpdateService.updatePaidOrderRefunded(orderId, notifyReqDTO.getPayRefundId());\n        } else {\n            afterSaleService.updateAfterSaleRefunded(\n                    Long.parseLong(notifyReqDTO.getMerchantRefundId()),\n                    Long.parseLong(notifyReqDTO.getMerchantOrderId()),\n                    notifyReqDTO.getPayRefundId());\n        }\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.http",
    "content": "### 获得交易售后分页 => 成功\nGET {{baseUrl}}/trade/after-sale/page?pageNo=1&pageSize=10\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 同意售后 => 成功\nPUT {{baseUrl}}/trade/after-sale/agree?id=7\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\nContent-Type: application/json\n\n### 拒绝售后 => 成功\nPUT {{baseUrl}}/trade/after-sale/disagree\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\nContent-Type: application/json\n\n{\n  \"id\": 6,\n  \"auditReason\": \"阿巴巴\"\n}\n\n### 确认退款 => 成功\nPUT {{baseUrl}}/trade/after-sale/refund?id=6\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\nContent-Type: application/json\n\n### 确认收货 => 成功\nPUT {{baseUrl}}/trade/after-sale/receive?id=7\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\nContent-Type: application/json\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/AfterSaleBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n/**\n* 交易售后 Base VO，提供给添加、修改、详细的子 VO 使用\n* 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n*/\n@Data\npublic class AfterSaleBaseVO {\n\n    @Schema(description = \"售后流水号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"202211190847450020500077\")\n    @NotNull(message = \"售后流水号不能为空\")\n    private String no;\n\n    @Schema(description = \"售后状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"售后状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"售后类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    @NotNull(message = \"售后类型不能为空\")\n    private Integer type;\n\n    @Schema(description = \"售后方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"售后方式不能为空\")\n    private Integer way;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"30337\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    @Schema(description = \"申请原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"不喜欢\")\n    @NotNull(message = \"申请原因不能为空\")\n    private String applyReason;\n\n    @Schema(description = \"补充描述\", example = \"你说的对\")\n    private String applyDescription;\n\n    @Schema(description = \"补充凭证图片\", example = \"https://www.iocoder.cn/1.png\")\n    private List<String> applyPicUrls;\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18078\")\n    @NotNull(message = \"订单编号不能为空\")\n    private Long orderId;\n\n    @Schema(description = \"订单流水号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2022111917190001\")\n    @NotNull(message = \"订单流水号不能为空\")\n    private String orderNo;\n\n    @Schema(description = \"订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"572\")\n    @NotNull(message = \"订单项编号不能为空\")\n    private Long orderItemId;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2888\")\n    @NotNull(message = \"商品 SPU 编号不能为空\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SPU 名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotNull(message = \"商品 SPU 名称不能为空\")\n    private String spuName;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15657\")\n    @NotNull(message = \"商品 SKU 编号不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"商品图片\", example = \"https://www.iocoder.cn/2.png\")\n    private String picUrl;\n\n    @Schema(description = \"购买数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20012\")\n    @NotNull(message = \"购买数量不能为空\")\n    private Integer count;\n\n    @Schema(description = \"审批时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime auditTime;\n\n    @Schema(description = \"审批人\", example = \"30835\")\n    private Long auditUserId;\n\n    @Schema(description = \"审批备注\", example = \"不香\")\n    private String auditReason;\n\n    @Schema(description = \"退款金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18077\")\n    @NotNull(message = \"退款金额，单位：分不能为空\")\n    private Integer refundPrice;\n\n    @Schema(description = \"支付退款编号\", example = \"10271\")\n    private Long payRefundId;\n\n    @Schema(description = \"退款时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime refundTime;\n\n    @Schema(description = \"退货物流公司编号\", example = \"10\")\n    private Long logisticsId;\n\n    @Schema(description = \"退货物流单号\", example = \"610003952009\")\n    private String logisticsNo;\n\n    @Schema(description = \"退货时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime deliveryTime;\n\n    @Schema(description = \"收货时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime receiveTime;\n\n    @Schema(description = \"收货备注\", example = \"不喜欢\")\n    private String receiveReason;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/AfterSaleDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log.AfterSaleLogRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderBaseVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderItemBaseVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 售后订单的详情 Response VO\")\n@Data\npublic class AfterSaleDetailRespVO extends AfterSaleBaseVO {\n\n    @Schema(description = \"售后编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n\n\n    /**\n     * 订单基本信息\n     */\n    private TradeOrderBaseVO order;\n    /**\n     * 订单项列表\n     */\n    private OrderItem orderItem;\n\n    /**\n     * 用户信息\n     */\n    private MemberUserRespVO user;\n\n    /**\n     * 售后日志\n     */\n    private List<AfterSaleLogRespVO> logs;\n\n    @Schema(description = \"管理后台 - 交易订单的详情的订单项目\")\n    @Data\n    public static class OrderItem extends TradeOrderItemBaseVO {\n\n        /**\n         * 属性数组\n         */\n        private List<ProductPropertyValueDetailRespVO> properties;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/AfterSaleDisagreeReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 交易售后拒绝 Request VO\")\n@Data\npublic class AfterSaleDisagreeReqVO {\n\n    @Schema(description = \"售后编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"售后编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"审批备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你猜\")\n    @NotEmpty(message = \"审批备注不能为空\")\n    private String auditReason;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/AfterSalePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum;\nimport cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleTypeEnum;\nimport cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 交易售后分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AfterSalePageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"1024\")\n    private Long userId;\n\n    @Schema(description = \"售后流水号\", example = \"202211190847450020500077\")\n    private String no;\n\n    @Schema(description = \"售后状态\", example = \"10\")\n    @InEnum(value = AfterSaleStatusEnum.class, message = \"售后状态必须是 {value}\")\n    private Integer status;\n\n    @Schema(description = \"售后类型\", example = \"20\")\n    @InEnum(value = AfterSaleTypeEnum.class, message = \"售后类型必须是 {value}\")\n    private Integer type;\n\n    @Schema(description = \"售后方式\", example = \"10\")\n    @InEnum(value = AfterSaleWayEnum.class, message = \"售后方式必须是 {value}\")\n    private Integer way;\n\n    @Schema(description = \"订单编号\", example = \"18078\")\n    private String orderNo;\n\n    @Schema(description = \"商品 SPU 名称\", example = \"李四\")\n    private String spuName;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/AfterSaleRefuseReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 交易售后拒绝收货 Request VO\")\n@Data\npublic class AfterSaleRefuseReqVO {\n\n    @Schema(description = \"售后编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"售后编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"收货备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你猜\")\n    @NotNull(message = \"收货备注不能为空\")\n    private String refuseMemo;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/AfterSaleRespPageItemVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 交易售后分页的每一条记录 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AfterSaleRespPageItemVO extends AfterSaleBaseVO {\n\n    @Schema(description = \"售后编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"27630\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    /**\n     * 商品属性数组\n     */\n    private List<ProductPropertyValueDetailRespVO> properties;\n\n    /**\n     * 用户信息\n     */\n    private MemberUserRespVO user;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/log/AfterSaleLogRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 交易售后日志 Response VO\")\n@Data\npublic class AfterSaleLogRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20669\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"22634\")\n    private Long userId;\n\n    @Schema(description = \"用户类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2\")\n    private Integer userType;\n\n    @Schema(description = \"售后编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"3023\")\n    private Long afterSaleId;\n\n    @Schema(description = \"售后状态（之前）\", example = \"2\")\n    private Integer beforeStatus;\n\n    @Schema(description = \"售后状态（之后）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer afterStatus;\n\n    @Schema(description = \"操作明细\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"维权完成，退款金额：¥37776.00\")\n    private String content;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/base/member/package-info.java",
    "content": "/**\n * 占位符，可忽略\n */\npackage cn.iocoder.yudao.module.trade.controller.admin.base.member;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/base/member/user/MemberUserRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.base.member.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 会员用户 Response VO\")\n@Data\npublic class MemberUserRespVO {\n\n    @Schema(description = \"用户 ID\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String nickname;\n\n    @Schema(description = \"用户头像\", example = \"https://www.iocoder.cn/xxx.png\")\n    private String avatar;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/base/package-info.java",
    "content": "/**\n * 放置该模块通用的 VO 类\n */\npackage cn.iocoder.yudao.module.trade.controller.admin.base;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/base/product/property/ProductPropertyValueDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.base.product.property;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 商品属性值的明细 Response VO\")\n@Data\npublic class ProductPropertyValueDetailRespVO {\n\n    @Schema(description = \"属性的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long propertyId;\n\n    @Schema(description = \"属性的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"颜色\")\n    private String propertyName;\n\n    @Schema(description = \"属性值的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long valueId;\n\n    @Schema(description = \"属性值的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"红色\")\n    private String valueName;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/base/system/package-info.java",
    "content": "/**\n * 占位符，可忽略\n */\npackage cn.iocoder.yudao.module.trade.controller.admin.base.system;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/base/system/user/UserSimpleBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.base.system.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户精简信息 VO\")\n@Data\npublic class UserSimpleBaseVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    private String nickname;\n\n    @Schema(description = \"用户头像\", example = \"https://www.iocoder.cn/1.png\")\n    private String avatar;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageRecordController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record.BrokerageRecordRespVO;\nimport cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageRecordConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\n\n@Tag(name = \"管理后台 - 佣金记录\")\n@RestController\n@RequestMapping(\"/trade/brokerage-record\")\n@Validated\npublic class BrokerageRecordController {\n\n    @Resource\n    private BrokerageRecordService brokerageRecordService;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得佣金记录\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-record:query')\")\n    public CommonResult<BrokerageRecordRespVO> getBrokerageRecord(@RequestParam(\"id\") Long id) {\n        BrokerageRecordDO brokerageRecord = brokerageRecordService.getBrokerageRecord(id);\n        return success(BrokerageRecordConvert.INSTANCE.convert(brokerageRecord));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得佣金记录分页\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-record:query')\")\n    public CommonResult<PageResult<BrokerageRecordRespVO>> getBrokerageRecordPage(@Valid BrokerageRecordPageReqVO pageVO) {\n        PageResult<BrokerageRecordDO> pageResult = brokerageRecordService.getBrokerageRecordPage(pageVO);\n\n        // 查询用户信息\n        Set<Long> userIds = convertSet(pageResult.getList(), BrokerageRecordDO::getUserId);\n        userIds.addAll(convertList(pageResult.getList(), BrokerageRecordDO::getSourceUserId));\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);\n        // 拼接数据\n        return success(BrokerageRecordConvert.INSTANCE.convertPage(pageResult, userMap));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageUserController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.*;\nimport cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageUserConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;\nimport cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;\nimport cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static java.util.Arrays.asList;\n\n@Tag(name = \"管理后台 - 分销用户\")\n@RestController\n@RequestMapping(\"/trade/brokerage-user\")\n@Validated\npublic class BrokerageUserController {\n\n    @Resource\n    private BrokerageUserService brokerageUserService;\n    @Resource\n    private BrokerageRecordService brokerageRecordService;\n    @Resource\n    private BrokerageWithdrawService brokerageWithdrawService;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建分销用户\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-user:create')\")\n    public CommonResult<Long> createBrokerageUser(@Valid @RequestBody BrokerageUserCreateReqVO createReqVO) {\n        return success(brokerageUserService.createBrokerageUser(createReqVO));\n    }\n\n    @PutMapping(\"/update-bind-user\")\n    @Operation(summary = \"修改推广员\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-user:update-bind-user')\")\n    public CommonResult<Boolean> updateBindUser(@Valid @RequestBody BrokerageUserUpdateBrokerageUserReqVO updateReqVO) {\n        brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), updateReqVO.getBindUserId());\n        return success(true);\n    }\n\n    @PutMapping(\"/clear-bind-user\")\n    @Operation(summary = \"清除推广员\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-user:clear-bind-user')\")\n    public CommonResult<Boolean> clearBindUser(@Valid @RequestBody BrokerageUserClearBrokerageUserReqVO updateReqVO) {\n        brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), null);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-brokerage-enable\")\n    @Operation(summary = \"修改推广资格\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-user:update-brokerage-enable')\")\n    public CommonResult<Boolean> updateBrokerageEnabled(@Valid @RequestBody BrokerageUserUpdateBrokerageEnabledReqVO updateReqVO) {\n        brokerageUserService.updateBrokerageUserEnabled(updateReqVO.getId(), updateReqVO.getEnabled());\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得分销用户\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-user:query')\")\n    public CommonResult<BrokerageUserRespVO> getBrokerageUser(@RequestParam(\"id\") Long id) {\n        BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(id);\n        // TODO @疯狂：是不是搞成一个统一的 convert？\n        BrokerageUserRespVO respVO = BrokerageUserConvert.INSTANCE.convert(brokerageUser);\n        return success(BrokerageUserConvert.INSTANCE.copyTo(memberUserApi.getUser(id).getCheckedData(), respVO));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得分销用户分页\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-user:query')\")\n    public CommonResult<PageResult<BrokerageUserRespVO>> getBrokerageUserPage(@Valid BrokerageUserPageReqVO pageVO) {\n        // 分页查询\n        PageResult<BrokerageUserDO> pageResult = brokerageUserService.getBrokerageUserPage(pageVO);\n\n        // 查询用户信息\n        Set<Long> userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId);\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);\n        // 合计分佣的推广订单\n        Map<Long, UserBrokerageSummaryRespBO> brokerageOrderSummaryMap = brokerageRecordService.getUserBrokerageSummaryMapByUserId(\n                userIds, BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus());\n        // 合计分佣的推广用户\n        // TODO @疯狂：转成 map 批量读取\n        Map<Long, Long> brokerageUserCountMap = convertMap(userIds,\n                userId -> userId,\n                userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId, null));\n        // 合计分佣的提现\n        // TODO @疯狂：如果未来支持了打款这个动作，可能 status 会不对；\n        Map<Long, BrokerageWithdrawSummaryRespBO> withdrawMap = brokerageWithdrawService.getWithdrawSummaryMapByUserId(\n                userIds, asList(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS));\n        // 拼接返回\n        return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap,\n                brokerageOrderSummaryMap, withdrawMap));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageWithdrawController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.pay.api.notify.dto.PayTransferNotifyReqDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRejectReqVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRespVO;\nimport cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageWithdrawConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.validation.Valid;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;\n\n@Tag(name = \"管理后台 - 佣金提现\")\n@RestController\n@RequestMapping(\"/trade/brokerage-withdraw\")\n@Validated\n@Slf4j\npublic class BrokerageWithdrawController {\n\n    @Resource\n    private BrokerageWithdrawService brokerageWithdrawService;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @PutMapping(\"/approve\")\n    @Operation(summary = \"通过申请\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-withdraw:audit')\")\n    public CommonResult<Boolean> approveBrokerageWithdraw(@RequestParam(\"id\") Long id) {\n        brokerageWithdrawService.auditBrokerageWithdraw(id,\n                BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, \"\", getClientIP());\n        return success(true);\n    }\n\n    @PutMapping(\"/reject\")\n    @Operation(summary = \"驳回申请\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-withdraw:audit')\")\n    public CommonResult<Boolean> rejectBrokerageWithdraw(@Valid @RequestBody BrokerageWithdrawRejectReqVO reqVO) {\n        brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(),\n                BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason(), getClientIP());\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得佣金提现\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-withdraw:query')\")\n    public CommonResult<BrokerageWithdrawRespVO> getBrokerageWithdraw(@RequestParam(\"id\") Long id) {\n        BrokerageWithdrawDO brokerageWithdraw = brokerageWithdrawService.getBrokerageWithdraw(id);\n        return success(BrokerageWithdrawConvert.INSTANCE.convert(brokerageWithdraw));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得佣金提现分页\")\n    @PreAuthorize(\"@ss.hasPermission('trade:brokerage-withdraw:query')\")\n    public CommonResult<PageResult<BrokerageWithdrawRespVO>> getBrokerageWithdrawPage(@Valid BrokerageWithdrawPageReqVO pageVO) {\n        // 分页查询\n        PageResult<BrokerageWithdrawDO> pageResult = brokerageWithdrawService.getBrokerageWithdrawPage(pageVO);\n\n        // 拼接信息\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(\n                convertSet(pageResult.getList(), BrokerageWithdrawDO::getUserId));\n        return success(BrokerageWithdrawConvert.INSTANCE.convertPage(pageResult, userMap));\n    }\n\n    @PostMapping(\"/update-transferred\")\n    @Operation(summary = \"更新佣金提现的转账结果\") // 由 pay-module 支付服务，进行回调，可见 PayNotifyJob\n    @PermitAll // 无需登录，安全由 BrokerageWithdrawService 内部校验实现\n    public CommonResult<Boolean> updateBrokerageWithdrawTransferred(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) {\n        log.info(\"[updateAfterRefund][notifyReqDTO({})]\", notifyReqDTO);\n        brokerageWithdrawService.updateBrokerageWithdrawTransferred(\n                Long.parseLong(notifyReqDTO.getMerchantTransferId()), notifyReqDTO.getPayTransferId());\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/record/BrokerageRecordBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n/**\n * 佣金记录 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class BrokerageRecordBaseVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"25973\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long userId;\n\n    @Schema(description = \"业务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23353\")\n    @NotEmpty(message = \"业务编号不能为空\")\n    private String bizId;\n\n    @Schema(description = \"业务类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"业务类型不能为空\")\n    private Integer bizType;\n\n    @Schema(description = \"标题\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"标题不能为空\")\n    private String title;\n\n    @Schema(description = \"金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28731\")\n    @NotNull(message = \"金额不能为空\")\n    private Integer price;\n\n    @Schema(description = \"当前总佣金\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13226\")\n    @NotNull(message = \"当前总佣金不能为空\")\n    private Integer totalPrice;\n\n    @Schema(description = \"说明\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你说的对\")\n    @NotNull(message = \"说明不能为空\")\n    private String description;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @Schema(description = \"冻结时间（天）\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"冻结时间（天）不能为空\")\n    private Integer frozenDays;\n\n    @Schema(description = \"解冻时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime unfreezeTime;\n\n    @Schema(description = \"来源用户等级\")\n    private Integer sourceUserLevel;\n\n    @Schema(description = \"来源用户编号\")\n    private Long sourceUserId;\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/record/BrokerageRecordPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 佣金记录分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BrokerageRecordPageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"25973\")\n    private Long userId;\n\n    @Schema(description = \"业务类型\", example = \"1\")\n    private Integer bizType;\n\n    @Schema(description = \"状态\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n    @Schema(description = \"用户类型\", example = \"1\")\n    private Integer sourceUserLevel;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/record/BrokerageRecordRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 佣金记录 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BrokerageRecordRespVO extends BrokerageRecordBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"28896\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n\n    // ========== 用户信息 ==========\n\n    @Schema(description = \"用户头像\", example = \"https://www.iocoder.cn/xxx.png\")\n    private String userAvatar;\n    @Schema(description = \"用户昵称\", example = \"李四\")\n    private String userNickname;\n\n\n    // ========== 来源用户信息 ==========\n\n    @Schema(description = \"来源用户头像\", example = \"https://www.iocoder.cn/xxx.png\")\n    private String sourceUserAvatar;\n    @Schema(description = \"来源用户昵称\", example = \"李四\")\n    private String sourceUserNickname;\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/user/BrokerageUserBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n/**\n * 分销用户 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class BrokerageUserBaseVO {\n\n    @Schema(description = \"推广员编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4587\")\n    @NotNull(message = \"推广员编号不能为空\")\n    private Long bindUserId;\n\n    @Schema(description = \"推广员绑定时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime bindUserTime;\n\n    @Schema(description = \"推广资格\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"推广资格不能为空\")\n    private Boolean brokerageEnabled;\n\n    @Schema(description = \"成为分销员时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime brokerageTime;\n\n    @Schema(description = \"可用佣金\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11089\")\n    @NotNull(message = \"可用佣金不能为空\")\n    private Integer price;\n\n    @Schema(description = \"冻结佣金\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"30916\")\n    @NotNull(message = \"冻结佣金不能为空\")\n    private Integer frozenPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/user/BrokerageUserClearBrokerageUserReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 分销用户 - 清除推广员 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class BrokerageUserClearBrokerageUserReqVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20019\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/user/BrokerageUserCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 分销用户创建 Request VO\")\n@Data\npublic class BrokerageUserCreateReqVO {\n\n    @Schema(description = \"分销用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"分销用户编号不能为空\")\n    private Long userId;\n\n    @Schema(description = \"推广员编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4587\")\n    @NotNull(message = \"推广员编号不能为空\")\n    private Long bindUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/user/BrokerageUserPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 分销用户分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BrokerageUserPageReqVO extends PageParam {\n\n    @Schema(description = \"推广员编号\", example = \"4587\")\n    private Long bindUserId;\n\n    @Schema(description = \"推广资格\", example = \"true\")\n    private Boolean brokerageEnabled;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n    @Schema(description = \"用户等级\", example = \"1\") // 注意，这了不是用户的会员等级，而是过滤推广的层级\n    private Integer level;\n\n    @Schema(description = \"绑定时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] bindUserTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/user/BrokerageUserRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 分销用户 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BrokerageUserRespVO extends BrokerageUserBaseVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20019\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    // ========== 用户信息 ==========\n\n    @Schema(description = \"用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xxx.png\")\n    private String avatar;\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    private String nickname;\n\n    // ========== 推广信息 ========== 注意：是包括 1 + 2 级的数据\n\n    @Schema(description = \"推广用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20019\")\n    private Integer brokerageUserCount;\n    @Schema(description = \"推广订单数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20019\")\n    private Integer brokerageOrderCount;\n    @Schema(description = \"推广订单金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20019\")\n    private Integer brokerageOrderPrice;\n\n    // ========== 提现信息 ==========\n\n    @Schema(description = \"已提现金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20019\")\n    private Integer withdrawPrice;\n    @Schema(description = \"已提现次数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20019\")\n    private Integer withdrawCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageEnabledReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 分销用户 - 修改推广员 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class BrokerageUserUpdateBrokerageEnabledReqVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20019\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"推广资格\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"推广资格不能为空\")\n    private Boolean enabled;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageUserReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 分销用户 - 修改推广员 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class BrokerageUserUpdateBrokerageUserReqVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20019\")\n    @NotNull(message = \"用户编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"推广员编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4587\")\n    @NotNull(message = \"推广员编号不能为空\")\n    private Long bindUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 佣金提现分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class BrokerageWithdrawPageReqVO extends PageParam {\n\n    @Schema(description = \"用户编号\", example = \"11436\")\n    private Long userId;\n\n    @Schema(description = \"提现类型\", example = \"1\")\n    @InEnum(value = BrokerageWithdrawTypeEnum.class, message = \"提现类型必须是 {value}\")\n    private Integer type;\n\n    @Schema(description = \"真实姓名\", example = \"赵六\")\n    private String userName;\n\n    @Schema(description = \"账号\", example = \"886779132\")\n    private String userAccount;\n\n    @Schema(description = \"银行名称\", example = \"1\")\n    private String bankName;\n\n    @Schema(description = \"状态\", example = \"1\")\n    @InEnum(value = BrokerageWithdrawStatusEnum.class, message = \"状态必须是 {value}\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRejectReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 驳回申请 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class BrokerageWithdrawRejectReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7161\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"审核驳回原因\", example = \"不对\")\n    @NotEmpty(message = \"审核驳回原因不能为空\")\n    private String auditReason;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 佣金提现 Response VO\")\n@Data\npublic class BrokerageWithdrawRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7161\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11436\")\n    private Long userId;\n\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋艿\")\n    private String userNickname;\n\n    @Schema(description = \"提现金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18781\")\n    private Integer price;\n\n    @Schema(description = \"提现手续费\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"11417\")\n    private Integer feePrice;\n\n    @Schema(description = \"当前总佣金\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18576\")\n    private Integer totalPrice;\n\n    @Schema(description = \"提现类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"真实姓名\", example = \"赵六\")\n    private String userName;\n\n    @Schema(description = \"收款账号\", example = \"88677912132\")\n    private String userAccount;\n\n    @Schema(description = \"银行名称\", example = \"1\")\n    private String bankName;\n\n    @Schema(description = \"开户地址\", example = \"海淀支行\")\n    private String bankAddress;\n\n    @Schema(description = \"收款码\", example = \"https://www.iocoder.cn\")\n    private String qrCodeUrl;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"审核驳回原因\", example = \"不对\")\n    private String auditReason;\n\n    @Schema(description = \"审核时间\")\n    private LocalDateTime auditTime;\n\n    @Schema(description = \"备注\", example = \"随便\")\n    private String remark;\n\n    @Schema(description = \"转账单编号\", example = \"1024\")\n    private Long payTransferId;\n\n    @Schema(description = \"转账错误提示\", example = \"余额不足\")\n    private String transferErrorMsg;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/TradeConfigController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.config;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigSaveReqVO;\nimport cn.iocoder.yudao.module.trade.convert.config.TradeConfigConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;\nimport cn.iocoder.yudao.module.trade.service.config.TradeConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 交易中心配置\")\n@RestController\n@RequestMapping(\"/trade/config\")\n@Validated\npublic class TradeConfigController {\n\n    @Resource\n    private TradeConfigService tradeConfigService;\n\n    @Value(\"${yudao.tencent-lbs-key}\")\n    private String tencentLbsKey;\n\n    @PutMapping(\"/save\")\n    @Operation(summary = \"更新交易中心配置\")\n    @PreAuthorize(\"@ss.hasPermission('trade:config:save')\")\n    public CommonResult<Boolean> updateConfig(@Valid @RequestBody TradeConfigSaveReqVO updateReqVO) {\n        tradeConfigService.saveTradeConfig(updateReqVO);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得交易中心配置\")\n    @PreAuthorize(\"@ss.hasPermission('trade:config:query')\")\n    public CommonResult<TradeConfigRespVO> getConfig() {\n        TradeConfigDO config = tradeConfigService.getTradeConfig();\n        TradeConfigRespVO configVO = TradeConfigConvert.INSTANCE.convert(config);\n        if (configVO != null) {\n            configVO.setTencentLbsKey(tencentLbsKey);\n        }\n        return success(configVO);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.config.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.Range;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.PositiveOrZero;\nimport java.util.List;\n\n/**\n * 交易中心配置 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class TradeConfigBaseVO {\n\n    // ========== 售后相关 ==========\n\n    @Schema(description = \"售后的退款理由\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"售后的退款理由不能为空\")\n    private List<String> afterSaleRefundReasons;\n\n    @Schema(description = \"售后的退货理由\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"售后的退货理由不能为空\")\n    private List<String> afterSaleReturnReasons;\n\n    // ========== 配送相关 ==========\n\n    /**\n     * 是否启用全场包邮\n     */\n    @Schema(description = \"是否启用全场包邮\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否启用全场包邮不能为空\")\n    private Boolean deliveryExpressFreeEnabled;\n\n    @Schema(description = \"全场包邮的最小金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    @NotNull(message = \"全场包邮的最小金额不能为空\")\n    @PositiveOrZero(message = \"全场包邮的最小金额不能是负数\")\n    private Integer deliveryExpressFreePrice;\n\n    @Schema(description = \"是否开启自提\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否开启自提不能为空\")\n    private Boolean deliveryPickUpEnabled;\n\n    // ========== 分销相关 ==========\n\n    @Schema(description = \"是否启用分佣\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否启用分佣不能为空\")\n    private Boolean brokerageEnabled;\n\n    @Schema(description = \"分佣模式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"分佣模式不能为空\")\n    @InEnum(value = BrokerageEnabledConditionEnum.class, message = \"分佣模式必须是 {value}\")\n    private Integer brokerageEnabledCondition;\n\n    @Schema(description = \"分销关系绑定模式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    @NotNull(message = \"分销关系绑定模式不能为空\")\n    @InEnum(value = BrokerageBindModeEnum.class, message = \"分销关系绑定模式必须是 {value}\")\n    private Integer brokerageBindMode;\n\n    @Schema(description = \"分销海报图地址数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[https://www.iocoder.cn/yudao.jpg]\")\n    private List<String> brokeragePosterUrls;\n\n    @Schema(description = \"一级返佣比例\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"一级返佣比例不能为空\")\n    @Range(min = 0, max = 100, message = \"一级返佣比例必须在 0 - 100 之间\")\n    private Integer brokerageFirstPercent;\n\n    @Schema(description = \"二级返佣比例\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"二级返佣比例不能为空\")\n    @Range(min = 0, max = 100, message = \"二级返佣比例必须在 0 - 100 之间\")\n    private Integer brokerageSecondPercent;\n\n    @Schema(description = \"用户提现最低金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    @NotNull(message = \"用户提现最低金额不能为空\")\n    @PositiveOrZero(message = \"用户提现最低金额不能是负数\")\n    private Integer brokerageWithdrawMinPrice;\n\n    @Schema(description = \"用户提现手续费百分比\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    @NotNull(message = \"用户提现手续费百分比不能为空\")\n    @PositiveOrZero(message = \"用户提现手续费百分比不能是负数\")\n    private Integer brokerageWithdrawFeePercent;\n\n    @Schema(description = \"佣金冻结时间(天)\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7\")\n    @NotNull(message = \"佣金冻结时间(天)不能为空\")\n    @PositiveOrZero(message = \"佣金冻结时间不能是负数\")\n    private Integer brokerageFrozenDays;\n\n    @Schema(description = \"提现方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[0, 1]\")\n    @NotEmpty(message = \"提现方式不能为空\")\n    @InEnum(value = BrokerageWithdrawTypeEnum.class, message = \"提现方式必须是 {value}\")\n    private List<Integer> brokerageWithdrawTypes;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.config.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 交易中心配置 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class TradeConfigRespVO extends TradeConfigBaseVO {\n\n    @Schema(description = \"自增主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"腾讯地图 KEY\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123456\")\n    private String tencentLbsKey;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigSaveReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.config.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 交易中心配置更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class TradeConfigSaveReqVO extends TradeConfigBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery;\n\nimport cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;\nimport cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.*;\nimport cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;\nimport cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.Valid;\nimport java.io.IOException;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 快递公司\")\n@RestController\n@RequestMapping(\"/trade/delivery/express\")\n@Validated\npublic class DeliveryExpressController {\n\n    @Resource\n    private DeliveryExpressService deliveryExpressService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建快递公司\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express:create')\")\n    public CommonResult<Long> createDeliveryExpress(@Valid @RequestBody DeliveryExpressCreateReqVO createReqVO) {\n        return success(deliveryExpressService.createDeliveryExpress(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新快递公司\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express:update')\")\n    public CommonResult<Boolean> updateDeliveryExpress(@Valid @RequestBody DeliveryExpressUpdateReqVO updateReqVO) {\n        deliveryExpressService.updateDeliveryExpress(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除快递公司\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express:delete')\")\n    public CommonResult<Boolean> deleteDeliveryExpress(@RequestParam(\"id\") Long id) {\n        deliveryExpressService.deleteDeliveryExpress(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得快递公司\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express:query')\")\n    public CommonResult<DeliveryExpressRespVO> getDeliveryExpress(@RequestParam(\"id\") Long id) {\n        DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(id);\n        return success(DeliveryExpressConvert.INSTANCE.convert(deliveryExpress));\n    }\n\n    @GetMapping(\"/list-all-simple\")\n    @Operation(summary = \"获取快递公司精简信息列表\", description = \"主要用于前端的下拉选项\")\n    public CommonResult<List<DeliveryExpressSimpleRespVO>> getSimpleDeliveryExpressList() {\n        List<DeliveryExpressDO> list = deliveryExpressService.getDeliveryExpressListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        return success(DeliveryExpressConvert.INSTANCE.convertList1(list));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得快递公司分页\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express:query')\")\n    public CommonResult<PageResult<DeliveryExpressRespVO>> getDeliveryExpressPage(@Valid DeliveryExpressPageReqVO pageVO) {\n        PageResult<DeliveryExpressDO> pageResult = deliveryExpressService.getDeliveryExpressPage(pageVO);\n        return success(DeliveryExpressConvert.INSTANCE.convertPage(pageResult));\n    }\n\n    @GetMapping(\"/export-excel\")\n    @Operation(summary = \"导出快递公司 Excel\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express:export')\")\n    @ApiAccessLog(operateType = EXPORT)\n    public void exportDeliveryExpressExcel(@Valid DeliveryExpressExportReqVO exportReqVO,\n              HttpServletResponse response) throws IOException {\n        List<DeliveryExpressDO> list = deliveryExpressService.getDeliveryExpressList(exportReqVO);\n        // 导出 Excel\n        List<DeliveryExpressExcelVO> dataList = DeliveryExpressConvert.INSTANCE.convertList02(list);\n        ExcelUtils.write(response, \"快递公司.xls\", \"数据\", DeliveryExpressExcelVO.class, dataList);\n    }\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressTemplateController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.*;\nimport cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressTemplateConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;\nimport cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 快递运费模板\")\n@RestController\n@RequestMapping(\"/trade/delivery/express-template\")\n@Validated\npublic class DeliveryExpressTemplateController {\n\n    @Resource\n    private DeliveryExpressTemplateService deliveryExpressTemplateService;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建快递运费模板\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express-template:create')\")\n    public CommonResult<Long> createDeliveryExpressTemplate(@Valid @RequestBody DeliveryExpressTemplateCreateReqVO createReqVO) {\n        return success(deliveryExpressTemplateService.createDeliveryExpressTemplate(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新快递运费模板\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express-template:update')\")\n    public CommonResult<Boolean> updateDeliveryExpressTemplate(@Valid @RequestBody DeliveryExpressTemplateUpdateReqVO updateReqVO) {\n        deliveryExpressTemplateService.updateDeliveryExpressTemplate(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除快递运费模板\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express-template:delete')\")\n    public CommonResult<Boolean> deleteDeliveryExpressTemplate(@RequestParam(\"id\") Long id) {\n        deliveryExpressTemplateService.deleteDeliveryExpressTemplate(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得快递运费模板\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express-template:query')\")\n    public CommonResult<DeliveryExpressTemplateDetailRespVO> getDeliveryExpressTemplate(@RequestParam(\"id\") Long id) {\n        return success(deliveryExpressTemplateService.getDeliveryExpressTemplate(id));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得快递运费模板列表\")\n    @Parameter(name = \"ids\", description = \"编号列表\", required = true, example = \"1024,2048\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express-template:query')\")\n    public CommonResult<List<DeliveryExpressTemplateRespVO>> getDeliveryExpressTemplateList(@RequestParam(\"ids\") Collection<Long> ids) {\n        List<DeliveryExpressTemplateDO> list = deliveryExpressTemplateService.getDeliveryExpressTemplateList(ids);\n        return success(DeliveryExpressTemplateConvert.INSTANCE.convertList(list));\n    }\n\n    @GetMapping(\"/list-all-simple\")\n    @Operation(summary = \"获取快递模版精简信息列表\", description = \"主要用于前端的下拉选项\")\n    public CommonResult<List<DeliveryExpressTemplateSimpleRespVO>> getSimpleTemplateList() {\n        // 获取运费模版列表，只要开启状态的\n        List<DeliveryExpressTemplateDO> list = deliveryExpressTemplateService.getDeliveryExpressTemplateList();\n        // 排序后，返回给前端\n        return success(DeliveryExpressTemplateConvert.INSTANCE.convertList1(list));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得快递运费模板分页\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:express-template:query')\")\n    public CommonResult<PageResult<DeliveryExpressTemplateRespVO>> getDeliveryExpressTemplatePage(@Valid DeliveryExpressTemplatePageReqVO pageVO) {\n        PageResult<DeliveryExpressTemplateDO> pageResult = deliveryExpressTemplateService.getDeliveryExpressTemplatePage(pageVO);\n        return success(DeliveryExpressTemplateConvert.INSTANCE.convertPage(pageResult));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryPickUpStoreController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.system.api.user.AdminUserApi;\nimport cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.system.user.UserSimpleBaseVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.*;\nimport cn.iocoder.yudao.module.trade.convert.delivery.DeliveryPickUpStoreConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;\nimport cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.Collection;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"管理后台 - 自提门店\")\n@RestController\n@RequestMapping(\"/trade/delivery/pick-up-store\")\n@Validated\npublic class DeliveryPickUpStoreController {\n\n    @Resource\n    private DeliveryPickUpStoreService deliveryPickUpStoreService;\n\n    @Resource\n    private AdminUserApi adminUserApi;\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建自提门店\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:pick-up-store:create')\")\n    public CommonResult<Long> createDeliveryPickUpStore(@Valid @RequestBody DeliveryPickUpStoreCreateReqVO createReqVO) {\n        return success(deliveryPickUpStoreService.createDeliveryPickUpStore(createReqVO));\n    }\n\n    @PutMapping(\"/update\")\n    @Operation(summary = \"更新自提门店\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:pick-up-store:update')\")\n    public CommonResult<Boolean> updateDeliveryPickUpStore(@Valid @RequestBody DeliveryPickUpStoreUpdateReqVO updateReqVO) {\n        deliveryPickUpStoreService.updateDeliveryPickUpStore(updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除自提门店\")\n    @Parameter(name = \"id\", description = \"编号\", required = true)\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:pick-up-store:delete')\")\n    public CommonResult<Boolean> deleteDeliveryPickUpStore(@RequestParam(\"id\") Long id) {\n        deliveryPickUpStoreService.deleteDeliveryPickUpStore(id);\n        return success(true);\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得自提门店\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:pick-up-store:query')\")\n    public CommonResult<DeliveryPickUpStoreRespVO> getDeliveryPickUpStore(@RequestParam(\"id\") Long id) {\n        DeliveryPickUpStoreDO deliveryPickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(id);\n        if (deliveryPickUpStore == null) {\n            return success(null);\n        }\n        List<AdminUserRespDTO> verifyUsers = CollUtil.isNotEmpty(deliveryPickUpStore.getVerifyUserIds()) ?\n                adminUserApi.getUserList(deliveryPickUpStore.getVerifyUserIds()).getCheckedData() : null;\n        return success(BeanUtils.toBean(deliveryPickUpStore, DeliveryPickUpStoreRespVO.class)\n                .setVerifyUsers(BeanUtils.toBean(verifyUsers, UserSimpleBaseVO.class)));\n    }\n\n    @GetMapping(\"/simple-list\")\n    @Operation(summary = \"获得自提门店精简信息列表\")\n    public CommonResult<List<DeliveryPickUpStoreSimpleRespVO>> getSimpleDeliveryPickUpStoreList() {\n        List<DeliveryPickUpStoreDO> list = deliveryPickUpStoreService.getDeliveryPickUpStoreListByStatus(\n                CommonStatusEnum.ENABLE.getStatus());\n        return success(DeliveryPickUpStoreConvert.INSTANCE.convertList1(list));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得自提门店列表\")\n    @Parameter(name = \"ids\", description = \"编号列表\", required = true, example = \"1024,2048\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:pick-up-store:query')\")\n    public CommonResult<List<DeliveryPickUpStoreRespVO>> getDeliveryPickUpStoreList(@RequestParam(\"ids\") Collection<Long> ids) {\n        List<DeliveryPickUpStoreDO> list = deliveryPickUpStoreService.getDeliveryPickUpStoreList(ids);\n        return success(DeliveryPickUpStoreConvert.INSTANCE.convertList(list));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得自提门店分页\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:pick-up-store:query')\")\n    public CommonResult<PageResult<DeliveryPickUpStoreRespVO>> getDeliveryPickUpStorePage(@Valid DeliveryPickUpStorePageReqVO pageVO) {\n        PageResult<DeliveryPickUpStoreDO> pageResult = deliveryPickUpStoreService.getDeliveryPickUpStorePage(pageVO);\n        return success(DeliveryPickUpStoreConvert.INSTANCE.convertPage(pageResult));\n    }\n\n    @PostMapping(\"/bind\")\n    @Operation(summary = \"绑定自提店员\")\n    @PreAuthorize(\"@ss.hasPermission('trade:delivery:pick-up-store:create')\")\n    public CommonResult<Boolean> bindDeliveryPickUpStore(@Valid @RequestBody DeliveryPickUpBindReqVO bindReqVO) {\n        deliveryPickUpStoreService.bindDeliveryPickUpStore(bindReqVO);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n* 快递公司 Base VO，提供给添加、修改、详细的子 VO 使用\n* 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n*/\n@Data\npublic class DeliveryExpressBaseVO {\n\n    @Schema(description = \"快递公司编码\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"快递公司编码不能为空\")\n    private String code;\n\n    @Schema(description = \"快递公司名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotNull(message = \"快递公司名称不能为空\")\n    private String name;\n\n    @Schema(description = \"快递公司logo\")\n    private String logo;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express;\n\nimport lombok.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\n\n@Schema(description = \"管理后台 - 快递公司创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryExpressCreateReqVO extends DeliveryExpressBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressExcelVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express;\n\nimport cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;\nimport cn.iocoder.yudao.framework.excel.core.convert.DictConvert;\nimport cn.iocoder.yudao.module.system.enums.DictTypeConstants;\nimport cn.idev.excel.annotation.ExcelProperty;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n/**\n * 快递公司 Excel VO\n */\n@Data\npublic class DeliveryExpressExcelVO {\n\n    @ExcelProperty(\"编号\")\n    private Long id;\n\n    @ExcelProperty(\"快递公司编码\")\n    private String code;\n\n    @ExcelProperty(\"快递公司名称\")\n    private String name;\n\n    @ExcelProperty(\"快递公司 logo\")\n    private String logo;\n\n    @ExcelProperty(\"排序\")\n    private Integer sort;\n\n    @ExcelProperty(value = \"状态\", converter = DictConvert.class)\n    @DictFormat(DictTypeConstants.COMMON_STATUS)\n    private Integer status;\n\n    @ExcelProperty(\"创建时间\")\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressExportReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 快递公司 Excel 导出 Request VO\")\n@Data\npublic class DeliveryExpressExportReqVO {\n\n    @Schema(description = \"快递公司编码\")\n    private String code;\n\n    @Schema(description = \"快递公司名称\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"状态（0正常 1停用）\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express;\n\nimport lombok.*;\nimport java.util.*;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 快递公司分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryExpressPageReqVO extends PageParam {\n\n    @Schema(description = \"快递公司编码\")\n    private String code;\n\n    @Schema(description = \"快递公司名称\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"状态（0正常 1停用）\", example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 快递公司 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryExpressRespVO extends DeliveryExpressBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6592\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressSimpleRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 快递公司精简信息 Response VO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DeliveryExpressSimpleRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6592\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"快递公司名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"顺丰速运\")\n    @NotNull(message = \"快递公司名称不能为空\")\n    private String name;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 快递公司更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryExpressUpdateReqVO extends DeliveryExpressBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6592\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n* 快递运费模板 Base VO，提供给添加、修改、详细的子 VO 使用\n* 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n*/\n@Data\npublic class DeliveryExpressTemplateBaseVO {\n\n    @Schema(description = \"模板名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"王五\")\n    @NotNull(message = \"模板名称不能为空\")\n    private String name;\n\n    @Schema(description = \"配送计费方式 1:按件 2:按重量 3:按体积\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"配送计费方式 1:按件 2:按重量 3:按体积不能为空\")\n    private Integer chargeMode;\n\n    @Schema(description = \"排序\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateChargeBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n/**\n * 快递运费模板运费设置 Base VO，提供给添加运费模板使用\n */\n@Data\npublic class DeliveryExpressTemplateChargeBaseVO {\n\n    @Schema(description = \"编号\", example = \"6592\", hidden = true) // 由于想简单一点，复用这个 VO 在更新操作，所以 hidden 为 false\n    private Long id;\n\n    @Schema(description = \"区域编号列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1,120000]\")\n    @NotEmpty(message = \"区域编号列表不能为空\")\n    private List<Integer> areaIds;\n\n    @Schema(description = \"首件数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"首件数量不能为空\")\n    private Double startCount;\n\n    @Schema(description = \"起步价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    @NotNull(message = \"起步价不能为空\")\n    private Integer startPrice;\n\n    @Schema(description = \"续件数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    @NotNull(message = \"续件数量不能为空\")\n    private Double extraCount;\n\n    @Schema(description = \"额外价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2000\")\n    @NotNull(message = \"额外价不能为空\")\n    private Integer extraPrice;\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 快递运费模板创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryExpressTemplateCreateReqVO extends DeliveryExpressTemplateBaseVO {\n\n    @Schema(description = \"区域运费列表\")\n    @Valid\n    private List<DeliveryExpressTemplateChargeBaseVO> charges;\n\n    @Schema(description = \"包邮区域列表\")\n    @Valid\n    private List<DeliveryExpressTemplateFreeBaseVO> frees;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 快递运费模板的详细 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryExpressTemplateDetailRespVO extends DeliveryExpressTemplateBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"371\")\n    private Long id;\n\n    @Schema(description = \"运费模板运费设置\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<DeliveryExpressTemplateChargeBaseVO> charges;\n\n    @Schema(description = \"运费模板包邮区域\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<DeliveryExpressTemplateFreeBaseVO> frees;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateFreeBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n/**\n * 快递运费模板包邮 Base VO，提供给添加运费模板使用\n */\n@Data\npublic class DeliveryExpressTemplateFreeBaseVO {\n\n    @Schema(description = \"区域编号列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1,120000]\")\n    @NotEmpty(message = \"区域编号列表不能为空\")\n    private List<Integer> areaIds;\n\n    @Schema(description = \"包邮金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5000\")\n    @NotNull(message = \"包邮金额不能为空\")\n    private Integer freePrice;\n\n    @Schema(description = \"包邮件数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"包邮件数不能为空\")\n    private Integer freeCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplatePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 快递运费模板分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryExpressTemplatePageReqVO extends PageParam {\n\n    @Schema(description = \"模板名称\", example = \"王五\")\n    private String name;\n\n    @Schema(description = \"配送计费方式 1:按件 2:按重量 3:按体积\")\n    private Integer chargeMode;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - 快递运费模板 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryExpressTemplateRespVO extends DeliveryExpressTemplateBaseVO {\n\n    @Schema(description = \"编号，自增\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"371\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n\n@Schema(description = \"管理后台 - 模版精简信息 Response VO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DeliveryExpressTemplateSimpleRespVO {\n\n    @Schema(description = \"模版编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"模板名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"测试模版\")\n    private String name;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 快递运费模板更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryExpressTemplateUpdateReqVO extends DeliveryExpressTemplateBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"371\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"区域运费列表\")\n    @Valid\n    private List<DeliveryExpressTemplateChargeBaseVO> charges;\n\n    @Schema(description = \"包邮区域列表\")\n    @Valid\n    private List<DeliveryExpressTemplateFreeBaseVO> frees;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpBindReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 自提门店绑定核销人 Request VO\")\n@Data\n@ToString(callSuper = true)\npublic class DeliveryPickUpBindReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23128\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"绑定用户编号组数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23128\")\n    @NotEmpty(message = \"绑定用户编号组数不能未空\")\n    private List<Long> verifyUserIds;\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.framework.common.validation.Mobile;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalTime;\n\n/**\n* 自提门店 Base VO，提供给添加、修改、详细的子 VO 使用\n* 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n*/\n@Data\npublic class DeliveryPickUpStoreBaseVO {\n\n    @Schema(description = \"门店名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    @NotBlank(message = \"门店名称不能为空\")\n    private String name;\n\n    @Schema(description = \"门店简介\", example = \"我是门店简介\")\n    private String introduction;\n\n    @Schema(description = \"门店手机\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15601892312\")\n    @NotBlank(message = \"门店手机不能为空\")\n    @Mobile\n    private String phone;\n\n    @Schema(description = \"区域编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18733\")\n    @NotNull(message = \"区域编号不能为空\")\n    private Integer areaId;\n\n    @Schema(description = \"门店详细地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"复旦大学路 188 号\")\n    @NotBlank(message = \"门店详细地址不能为空\")\n    private String detailAddress;\n\n    @Schema(description = \"门店 logo\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    @NotBlank(message = \"门店 logo 不能为空\")\n    private String logo;\n\n    @Schema(description = \"营业开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"营业开始时间不能为空\")\n    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = \"HH:mm\")\n    private LocalTime openingTime;\n\n    @Schema(description = \"营业结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"营业结束时间不能为空\")\n    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = \"HH:mm\")\n    private LocalTime closingTime;\n\n    @Schema(description = \"纬度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5.88\")\n    @NotNull(message = \"纬度不能为空\")\n    private Double latitude;\n\n    @Schema(description = \"经度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6.99\")\n    @NotNull(message = \"经度不能为空\")\n    private Double longitude;\n\n    @Schema(description = \"门店状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"门店状态不能为空\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Schema(description = \"管理后台 - 自提门店创建 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryPickUpStoreCreateReqVO extends DeliveryPickUpStoreBaseVO {\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStorePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 自提门店分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryPickUpStorePageReqVO extends PageParam {\n\n    @Schema(description = \"门店名称\", example = \"李四\")\n    private String name;\n\n    @Schema(description = \"门店手机\")\n    private String phone;\n\n    @Schema(description = \"区域编号\", example = \"18733\")\n    private Integer areaId;\n\n    @Schema(description = \"门店状态\", example = \"1\")\n    @InEnum(CommonStatusEnum.class)\n    private Integer status;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;\n\nimport cn.iocoder.yudao.module.trade.controller.admin.base.system.user.UserSimpleBaseVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 自提门店 Response VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryPickUpStoreRespVO extends DeliveryPickUpStoreBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23128\")\n    private Long id;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"核销用户数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<UserSimpleBaseVO> verifyUsers;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreSimpleRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 自提门店精简信息 Response VO\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DeliveryPickUpStoreSimpleRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23128\")\n    private Long id;\n\n    @Schema(description = \"门店名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    private String name;\n\n    @Schema(description = \"门店手机\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15601892312\")\n    private String phone;\n\n    @Schema(description = \"区域编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18733\")\n    private Integer areaId;\n\n    @Schema(description = \"区域名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"xx市\")\n    private String areaName;\n\n    @Schema(description = \"门店详细地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"复旦大学路 188 号\")\n    private String detailAddress;\n\n    @Schema(description = \"绑定用户编号组数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23128\")\n    private List<Long> verifyUserIds;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreUpdateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 自提门店更新 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class DeliveryPickUpStoreUpdateReqVO extends DeliveryPickUpStoreBaseVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23128\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.http",
    "content": "### 获得交易订单分页 => 成功\nGET {{baseUrl}}/trade/order/page?pageNo=1&pageSize=10\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 获得交易订单分页 => 成功\nGET {{baseUrl}}/trade/order/get-detail?id=21\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}\n\n### 获得交易订单的物流轨迹 => 成功\nGET {{baseUrl}}/trade/order/get-express-track-list?id=21\nAuthorization: Bearer {{token}}\ntenant-id: {{adminTenantId}}"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.order.vo.*;\nimport cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO;\nimport cn.iocoder.yudao.module.trade.service.order.TradeOrderLogService;\nimport cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;\nimport cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"管理后台 - 交易订单\")\n@RestController\n@RequestMapping(\"/trade/order\")\n@Validated\n@Slf4j\npublic class TradeOrderController {\n\n    @Resource\n    private TradeOrderUpdateService tradeOrderUpdateService;\n    @Resource\n    private TradeOrderQueryService tradeOrderQueryService;\n    @Resource\n    private TradeOrderLogService tradeOrderLogService;\n\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得交易订单分页\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:query')\")\n    public CommonResult<PageResult<TradeOrderPageItemRespVO>> getOrderPage(TradeOrderPageReqVO reqVO) {\n        // 查询订单\n        PageResult<TradeOrderDO> pageResult = tradeOrderQueryService.getOrderPage(reqVO);\n        if (CollUtil.isEmpty(pageResult.getList())) {\n            return success(PageResult.empty());\n        }\n\n        // 查询用户信息\n        Set<Long> userIds = CollUtil.unionDistinct(convertList(pageResult.getList(), TradeOrderDO::getUserId),\n                convertList(pageResult.getList(), TradeOrderDO::getBrokerageUserId, Objects::nonNull));\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);\n        // 查询订单项\n        List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(\n                convertSet(pageResult.getList(), TradeOrderDO::getId));\n        // 最终组合\n        return success(TradeOrderConvert.INSTANCE.convertPage(pageResult, orderItems, userMap));\n    }\n\n    @GetMapping(\"/summary\")\n    @Operation(summary = \"获得交易订单统计\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:query')\")\n    public CommonResult<TradeOrderSummaryRespVO> getOrderSummary(TradeOrderPageReqVO reqVO) {\n        return success(tradeOrderQueryService.getOrderSummary(reqVO));\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得交易订单详情\")\n    @Parameter(name = \"id\", description = \"订单编号\", required = true, example = \"1\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:query')\")\n    public CommonResult<TradeOrderDetailRespVO> getOrderDetail(@RequestParam(\"id\") Long id) {\n        // 查询订单\n        TradeOrderDO order = tradeOrderQueryService.getOrder(id);\n        if (order == null) {\n            return success(null);\n        }\n        // 查询订单项\n        List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id);\n\n        // 拼接数据\n        MemberUserRespDTO user = memberUserApi.getUser(order.getUserId()).getCheckedData();\n        MemberUserRespDTO brokerageUser = order.getBrokerageUserId() != null ?\n                memberUserApi.getUser(order.getBrokerageUserId()).getCheckedData() : null;\n        List<TradeOrderLogDO> orderLogs = tradeOrderLogService.getOrderLogListByOrderId(id);\n        return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, orderLogs, user, brokerageUser));\n    }\n\n    @GetMapping(\"/get-express-track-list\")\n    @Operation(summary = \"获得交易订单的物流轨迹\")\n    @Parameter(name = \"id\", description = \"交易订单编号\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:query')\")\n    public CommonResult<List<?>> getOrderExpressTrackList(@RequestParam(\"id\") Long id) {\n        return success(TradeOrderConvert.INSTANCE.convertList02(\n                tradeOrderQueryService.getExpressTrackList(id)));\n    }\n\n    @PutMapping(\"/delivery\")\n    @Operation(summary = \"订单发货\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:update')\")\n    public CommonResult<Boolean> deliveryOrder(@RequestBody TradeOrderDeliveryReqVO deliveryReqVO) {\n        tradeOrderUpdateService.deliveryOrder(deliveryReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-remark\")\n    @Operation(summary = \"订单备注\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:update')\")\n    public CommonResult<Boolean> updateOrderRemark(@RequestBody TradeOrderRemarkReqVO reqVO) {\n        tradeOrderUpdateService.updateOrderRemark(reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-price\")\n    @Operation(summary = \"订单调价\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:update')\")\n    public CommonResult<Boolean> updateOrderPrice(@RequestBody TradeOrderUpdatePriceReqVO reqVO) {\n        tradeOrderUpdateService.updateOrderPrice(reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-address\")\n    @Operation(summary = \"修改订单收货地址\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:update')\")\n    public CommonResult<Boolean> updateOrderAddress(@RequestBody TradeOrderUpdateAddressReqVO reqVO) {\n        tradeOrderUpdateService.updateOrderAddress(reqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/pick-up-by-id\")\n    @Operation(summary = \"订单核销\")\n    @Parameter(name = \"id\", description = \"交易订单编号\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:pick-up')\")\n    public CommonResult<Boolean> pickUpOrderById(@RequestParam(\"id\") Long id) {\n        tradeOrderUpdateService.pickUpOrderByAdmin(getLoginUserId(), id);\n        return success(true);\n    }\n\n    @PutMapping(\"/pick-up-by-verify-code\")\n    @Operation(summary = \"订单核销\")\n    @Parameter(name = \"pickUpVerifyCode\", description = \"自提核销码\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:pick-up')\")\n    public CommonResult<Boolean> pickUpOrderByVerifyCode(@RequestParam(\"pickUpVerifyCode\") String pickUpVerifyCode) {\n        tradeOrderUpdateService.pickUpOrderByAdmin(getLoginUserId(), pickUpVerifyCode);\n        return success(true);\n    }\n\n    @GetMapping(\"/get-by-pick-up-verify-code\")\n    @Operation(summary = \"查询核销码对应的订单\")\n    @Parameter(name = \"pickUpVerifyCode\", description = \"自提核销码\")\n    @PreAuthorize(\"@ss.hasPermission('trade:order:query')\")\n    public CommonResult<TradeOrderDetailRespVO> getByPickUpVerifyCode(@RequestParam(\"pickUpVerifyCode\") String pickUpVerifyCode) {\n        TradeOrderDO tradeOrder = tradeOrderUpdateService.getByPickUpVerifyCode(pickUpVerifyCode);\n        return success(TradeOrderConvert.INSTANCE.convert2(tradeOrder, null));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n/**\n * 交易订单 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class TradeOrderBaseVO {\n\n    // ========== 订单基本信息 ==========\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"订单流水号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1146347329394184195\")\n    private String no;\n\n    @Schema(description = \"下单时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"订单类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"订单来源\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer terminal;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n    private Long userId;\n\n    @Schema(description = \"用户 IP\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"127.0.0.1\")\n    private String userIp;\n\n    @Schema(description = \"用户备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你猜\")\n    private String userRemark;\n\n    @Schema(description = \"订单状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"购买的商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer productCount;\n\n    @Schema(description = \"订单完成时间\")\n    private LocalDateTime finishTime;\n\n    @Schema(description = \"订单取消时间\")\n    private LocalDateTime cancelTime;\n\n    @Schema(description = \"取消类型\", example = \"10\")\n    private Integer cancelType;\n\n    @Schema(description = \"商家备注\", example = \"你猜一下\")\n    private String remark;\n\n    // ========== 价格 + 支付基本信息 ==========\n\n    @Schema(description = \"支付订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long payOrderId;\n\n    @Schema(description = \"是否已支付\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean payStatus;\n\n    @Schema(description = \"付款时间\")\n    private LocalDateTime payTime;\n\n    @Schema(description = \"支付渠道\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"wx_lite\")\n    private String payChannelCode;\n\n    @Schema(description = \"商品原价（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    private Integer totalPrice;\n\n    @Schema(description = \"订单优惠（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer discountPrice;\n\n    @Schema(description = \"运费金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer deliveryPrice;\n\n    @Schema(description = \"订单调价（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer adjustPrice;\n\n    @Schema(description = \"应付金额（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    private Integer payPrice;\n\n    // ========== 收件 + 物流基本信息 ==========\n\n    @Schema(description = \"配送方式\", example = \"10\")\n    private Integer deliveryType;\n\n    @Schema(description = \"自提门店\", example = \"10\")\n    private Long pickUpStoreId;\n\n    @Schema(description = \"自提核销码\", example = \"10\")\n    private Long pickUpVerifyCode;\n\n    @Schema(description = \"配送模板编号\", example = \"1024\")\n    private Long deliveryTemplateId;\n\n    @Schema(description = \"发货物流公司编号\", example = \"1024\")\n    private Long logisticsId;\n\n    @Schema(description = \"发货物流单号\", example = \"1024\")\n    private String logisticsNo;\n\n    @Schema(description = \"发货时间\")\n    private LocalDateTime deliveryTime;\n\n    @Schema(description = \"收货时间\")\n    private LocalDateTime receiveTime;\n\n    @Schema(description = \"收件人名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    private String receiverName;\n\n    @Schema(description = \"收件人手机\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13800138000\")\n    private String receiverMobile;\n\n    @Schema(description = \"收件人地区编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"110000\")\n    private Integer receiverAreaId;\n\n    @Schema(description = \"收件人详细地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"中关村大街 1 号\")\n    private String receiverDetailAddress;\n\n    // ========== 售后基本信息 ==========\n\n    @Schema(description = \"售后状态\", example = \"1\")\n    private Integer afterSaleStatus;\n\n    @Schema(description = \"退款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer refundPrice;\n\n    // ========== 营销基本信息 ==========\n\n    @Schema(description = \"优惠劵编号\", example = \"1024\")\n    private Long couponId;\n\n    @Schema(description = \"优惠劵减免金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer couponPrice;\n\n    @Schema(description = \"积分抵扣的金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer pointPrice;\n\n    @Schema(description = \"VIP 减免金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n    private Integer vipPrice;\n\n    @Schema(description = \"推广人编号\", example = \"1\")\n    private Long brokerageUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 订单发货 Request VO\")\n@Data\npublic class TradeOrderDeliveryReqVO {\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"订单编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"发货物流公司编号\", example = \"1\")\n    @NotNull(message = \"发货物流公司不能为空\")\n    private Long logisticsId;\n\n    @Schema(description = \"发货物流单号\", example = \"SF123456789\")\n    private String logisticsNo;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 交易订单的详情 Response VO\")\n@Data\npublic class TradeOrderDetailRespVO extends TradeOrderBaseVO {\n\n    /**\n     * 订单项列表\n     */\n    private List<Item> items;\n\n    /**\n     * 下单用户信息\n     */\n    private MemberUserRespVO user;\n    /**\n     * 推广用户信息\n     */\n    private MemberUserRespVO brokerageUser;\n\n    /**\n     * 操作日志列表\n     */\n    private List<OrderLog> logs;\n\n    @Schema(description = \"收件人地区名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"上海 上海市 普陀区\")\n    private String receiverAreaName;\n\n    @Schema(description = \"管理后台 - 交易订单的操作日志\")\n    @Data\n    public static class OrderLog {\n\n        @Schema(description = \"操作详情\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"订单发货\")\n        private String content;\n\n        @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2023-06-01 10:50:20\")\n        private LocalDateTime createTime;\n\n        @Schema(description = \"用户类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer userType;\n\n    }\n\n    @Schema(description = \"管理后台 - 交易订单的详情的订单项目\")\n    @Data\n    public static class Item extends TradeOrderItemBaseVO {\n\n        /**\n         * 属性数组\n         */\n        private List<ProductPropertyValueDetailRespVO> properties;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderItemBaseVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n/**\n * 交易订单项 Base VO，提供给添加、修改、详细的子 VO 使用\n * 如果子 VO 存在差异的字段，请不要添加到这里，影响 Swagger 文档生成\n */\n@Data\npublic class TradeOrderItemBaseVO {\n\n    // ========== 订单项基本信息 ==========\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long userId;\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long orderId;\n\n    // ========== 商品基本信息 ==========\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SPU 名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String spuName;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long skuId;\n\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    private String picUrl;\n\n    @Schema(description = \"购买数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer count;\n\n    // ========== 价格 + 支付基本信息 ==========\n\n    @Schema(description = \"商品原价（单）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer price;\n\n    @Schema(description = \"商品优惠（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer discountPrice;\n\n    @Schema(description = \"商品实付金额（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer payPrice;\n\n    @Schema(description = \"子订单分摊金额（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer orderPartPrice;\n\n    @Schema(description = \"分摊后子订单实付金额（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer orderDividePrice;\n\n    // ========== 营销基本信息 ==========\n\n    // TODO 芋艿：在捉摸一下\n\n    // ========== 售后基本信息 ==========\n\n    @Schema(description = \"售后状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer afterSaleStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderPageItemRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"管理后台 - 交易订单的分页项 Response VO\")\n@Data\npublic class TradeOrderPageItemRespVO extends TradeOrderBaseVO {\n\n    @Schema(description = \"收件人地区名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"上海 上海市 普陀区\")\n    private String receiverAreaName;\n\n    @Schema(description = \"订单项列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"用户信息\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private MemberUserRespVO user;\n\n    @Schema(description = \"推广人信息\")\n    private MemberUserRespVO brokerageUser;\n\n    @Schema(description = \"管理后台 - 交易订单的分页项的订单项目\")\n    @Data\n    public static class Item extends TradeOrderItemBaseVO {\n\n        @Schema(description = \"属性列表\", requiredMode = Schema.RequiredMode.REQUIRED)\n        private List<ProductPropertyValueDetailRespVO> properties;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport cn.iocoder.yudao.framework.common.enums.TerminalEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.framework.common.validation.Mobile;\nimport cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"管理后台 - 交易订单的分页 Request VO\")\n@Data\npublic class TradeOrderPageReqVO extends PageParam {\n\n    @Schema(description = \"订单号\", example = \"88888888\")\n    private String no;\n\n    @Schema(description = \"用户编号\", example = \"1024\")\n    private Long userId;\n\n    @Schema(description = \"用户昵称\", example = \"小王\")\n    private String userNickname;\n\n    @Schema(description = \"用户手机号\", example = \"小王\")\n    @Mobile\n    private String userMobile;\n\n    @Schema(description = \"配送方式\", example = \"1\")\n    private Integer deliveryType;\n\n    @Schema(description = \"发货物流公司编号\", example = \"1\")\n    private Long logisticsId;\n\n    @Schema(description = \"自提门店编号\", example = \"[1,2]\")\n    private List<Long> pickUpStoreIds;\n\n    @Schema(description = \"自提核销码\", example = \"12345678\")\n    private String pickUpVerifyCode;\n\n    @Schema(description = \"订单类型\", example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"订单状态\", example = \"1\")\n    @InEnum(value = TradeOrderStatusEnum.class, message = \"订单状态必须是 {value}\")\n    private Integer status;\n\n    @Schema(description = \"支付渠道\", example = \"wx_lite\")\n    private String payChannelCode;\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n    @Schema(description = \"订单来源\", example = \"10\")\n    @InEnum(value = TerminalEnum.class, message = \"订单来源 {value}\")\n    private Integer terminal;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderRemarkReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 订单备注 Request VO\")\n@Data\npublic class TradeOrderRemarkReqVO {\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"订单编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"商家备注\", example = \"你猜一下\")\n    @NotEmpty(message = \"订单备注不能为空\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"管理后台 - 交易订单统计 Response VO\")\n@Data\npublic class TradeOrderSummaryRespVO {\n\n    @Schema(description = \"订单数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long orderCount;\n\n    @Schema(description = \"订单金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long orderPayPrice;\n\n    @Schema(description = \"退款单数\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long afterSaleCount;\n\n    @Schema(description = \"退款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long afterSalePrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderUpdateAddressReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 订单修改地址 Request VO\")\n@Data\npublic class TradeOrderUpdateAddressReqVO {\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"订单编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"收件人名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"z张三\")\n    @NotEmpty(message = \"收件人名称不能为空\")\n    private String receiverName;\n\n    @Schema(description = \"收件人手机\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"19988188888\")\n    @NotEmpty(message = \"收件人手机不能为空\")\n    private String receiverMobile;\n\n    @Schema(description = \"收件人地区编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"7310\")\n    @NotNull(message = \"收件人地区编号不能为空\")\n    private Integer receiverAreaId;\n\n    @Schema(description = \"收件人详细地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"昆明市五华区xxx小区xxx\")\n    @NotEmpty(message = \"收件人详细地址不能为空\")\n    private String receiverDetailAddress;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderUpdatePriceReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.admin.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"管理后台 - 订单改价 Request VO\")\n@Data\npublic class TradeOrderUpdatePriceReqVO {\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"订单编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"订单调价，单位：分。正数，加价；负数，减价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"-100\")\n    @NotNull(message = \"订单调价价格不能为空\")\n    private Integer adjustPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.aftersale;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleRespVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;\nimport cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 App - 交易售后\")\n@RestController\n@RequestMapping(\"/trade/after-sale\")\n@Validated\n@Slf4j\npublic class AppAfterSaleController {\n\n    @Resource\n    private AfterSaleService afterSaleService;\n\n    @GetMapping(value = \"/page\")\n    @Operation(summary = \"获得售后分页\")\n    public CommonResult<PageResult<AppAfterSaleRespVO>> getAfterSalePage(AppAfterSalePageReqVO pageReqVO) {\n        PageResult<AfterSaleDO> pageResult = afterSaleService.getAfterSalePage(getLoginUserId(), pageReqVO);\n        return success(BeanUtils.toBean(pageResult, AppAfterSaleRespVO.class));\n    }\n\n    @GetMapping(value = \"/get\")\n    @Operation(summary = \"获得售后订单\")\n    @Parameter(name = \"id\", description = \"售后编号\", required = true, example = \"1\")\n    public CommonResult<AppAfterSaleRespVO> getAfterSale(@RequestParam(\"id\") Long id) {\n        AfterSaleDO afterSale = afterSaleService.getAfterSale(getLoginUserId(), id);\n        return success(BeanUtils.toBean(afterSale, AppAfterSaleRespVO.class));\n    }\n\n    @PostMapping(value = \"/create\")\n    @Operation(summary = \"申请售后\")\n    public CommonResult<Long> createAfterSale(@RequestBody AppAfterSaleCreateReqVO createReqVO) {\n        return success(afterSaleService.createAfterSale(getLoginUserId(), createReqVO));\n    }\n\n    @PutMapping(value = \"/delivery\")\n    @Operation(summary = \"退回货物\")\n    public CommonResult<Boolean> deliveryAfterSale(@RequestBody AppAfterSaleDeliveryReqVO deliveryReqVO) {\n        afterSaleService.deliveryAfterSale(getLoginUserId(), deliveryReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(value = \"/cancel\")\n    @Operation(summary = \"取消售后\")\n    @Parameter(name = \"id\", description = \"售后编号\", required = true, example = \"1\")\n    public CommonResult<Boolean> cancelAfterSale(@RequestParam(\"id\") Long id) {\n        afterSaleService.cancelAfterSale(getLoginUserId(), id);\n        return success(true);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleLogController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.aftersale;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.log.AppAfterSaleLogRespVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO;\nimport cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleLogService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 App - 售后日志\")\n@RestController\n@RequestMapping(\"/trade/after-sale-log\")\n@Validated\n@Slf4j\npublic class AppAfterSaleLogController {\n\n    @Resource\n    private AfterSaleLogService afterSaleLogService;\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得售后日志列表\")\n    @Parameter(name = \"afterSaleId\", description = \"售后编号\", required = true, example = \"1\")\n    public CommonResult<List<AppAfterSaleLogRespVO>> getAfterSaleLogList(\n            @RequestParam(\"afterSaleId\") Long afterSaleId) {\n        List<AfterSaleLogDO> logs = afterSaleLogService.getAfterSaleLogList(afterSaleId);\n        return success(BeanUtils.toBean(logs, AppAfterSaleLogRespVO.class));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppAfterSaleCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;\n\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 交易售后创建 Request VO\")\n@Data\npublic class AppAfterSaleCreateReqVO {\n\n    @Schema(description = \"订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"订单项编号不能为空\")\n    private Long orderItemId;\n\n    @Schema(description = \"售后方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"售后方式不能为空\")\n    @InEnum(value = AfterSaleWayEnum.class, message = \"售后方式必须是 {value}\")\n    private Integer way;\n\n    @Schema(description = \"退款金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    @NotNull(message = \"退款金额不能为空\")\n    @Min(value = 1, message = \"退款金额必须大于 0\")\n    private Integer refundPrice;\n\n    @Schema(description = \"申请原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"申请原因不能为空\")\n    private String applyReason;\n\n    @Schema(description = \"补充描述\", example = \"商品质量不好\")\n    private String applyDescription;\n\n    @Schema(description = \"补充凭证图片\", example = \"https://www.iocoder.cn/1.png, https://www.iocoder.cn/2.png\")\n    private List<String> applyPicUrls;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppAfterSaleDeliveryReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 交易售后退回货物 Request VO\")\n@Data\npublic class AppAfterSaleDeliveryReqVO {\n\n    @Schema(description = \"售后编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"售后编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"退货物流公司编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"退货物流公司编号不能为空\")\n    private Long logisticsId;\n\n    @Schema(description = \"退货物流单号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"SF123456789\")\n    @NotNull(message = \"退货物流单号不能为空\")\n    private String logisticsNo;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppAfterSalePageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.util.Set;\n\n@Schema(description = \"用户 App - 交易售后分页 Request VO\")\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\npublic class AppAfterSalePageReqVO extends PageParam {\n\n    @Schema(description = \"售后状态\", example = \"10, 20\")\n    private Set<Integer> statuses;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppAfterSaleRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 交易售后 Response VO\")\n@Data\npublic class AppAfterSaleRespVO {\n\n    @Schema(description = \"售后编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"售后流水号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1146347329394184195\")\n    private String no;\n\n    @Schema(description = \"售后状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"售后方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer way;\n\n    @Schema(description = \"售后类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"申请原因\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private String applyReason;\n\n    @Schema(description = \"补充描述\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private String applyDescription;\n\n    @Schema(description = \"补充凭证图片\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private List<String> applyPicUrls;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"更新时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime updateTime;\n\n    // ========== 交易订单相关 ==========\n\n    @Schema(description = \"交易订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long orderId;\n\n    @Schema(description = \"交易订单流水号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private String orderNo;\n\n    @Schema(description = \"交易订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long orderItemId;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long spuId;\n\n    @Schema(description = \"商品 SPU 名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private String spuName;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long skuId;\n\n    /**\n     * 属性数组\n     */\n    private List<AppProductPropertyValueDetailRespVO> properties;\n\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/01.jpg\")\n    private String picUrl;\n\n    @Schema(description = \"退货商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer count;\n\n    // ========== 审批相关 ==========\n\n    /**\n     * 审批备注\n     *\n     * 注意，只有审批不通过才会填写\n     */\n    private String auditReason;\n\n    // ========== 退款相关 ==========\n\n    @Schema(description = \"退款金额，单位：分\", example = \"100\")\n    private Integer refundPrice;\n\n    @Schema(description = \"退款时间\")\n    private LocalDateTime refundTime;\n\n    // ========== 退货相关 ==========\n\n    @Schema(description = \"退货物流公司编号\", example = \"1\")\n    private Long logisticsId;\n\n    @Schema(description = \"退货物流单号\", example = \"SF123456789\")\n    private String logisticsNo;\n\n    @Schema(description = \"退货时间\")\n    private LocalDateTime deliveryTime;\n\n    @Schema(description = \"收货时间\")\n    private LocalDateTime receiveTime;\n\n    @Schema(description = \"收货备注\")\n    private String receiveReason;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/log/AppAfterSaleLogRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.log;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"管理后台 - App 交易售后日志 Response VO\")\n@Data\npublic class AppAfterSaleLogRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20669\")\n    private Long id;\n\n    @Schema(description = \"操作明细\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"维权完成，退款金额：¥37776.00\")\n    private String content;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/package-info.java",
    "content": "/**\n * 基础包，放一些通用的 VO 类\n */\npackage cn.iocoder.yudao.module.trade.controller.app.base;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/property/AppProductPropertyValueDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.base.property;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 商品属性值的明细 Response VO\")\n@Data\npublic class AppProductPropertyValueDetailRespVO {\n\n    @Schema(description = \"属性的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long propertyId;\n\n    @Schema(description = \"属性的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"颜色\")\n    private String propertyName;\n\n    @Schema(description = \"属性值的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long valueId;\n\n    @Schema(description = \"属性值的名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"红色\")\n    private String valueName;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.base.sku;\n\nimport cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * 商品 SKU 基础 Response VO\n *\n * @author 芋道源码\n */\n@Data\npublic class AppProductSkuBaseRespVO {\n\n    @Schema(description = \"主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"图片地址\", example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n\n    @Schema(description = \"销售价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer price;\n\n    @Schema(description = \"库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer stock;\n\n    /**\n     * 属性数组\n     */\n    private List<AppProductPropertyValueDetailRespVO> properties;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.base.spu;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n/**\n * 商品 SPU 基础 Response VO\n *\n * @author 芋道源码\n */\n@Data\npublic class AppProductSpuBaseRespVO {\n\n    @Schema(description = \"主键\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"商品 SPU 名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道\")\n    private String name;\n\n    @Schema(description = \"商品主图地址\", example = \"https://www.iocoder.cn/xx.png\")\n    private String picUrl;\n\n    @Schema(description = \"商品分类编号\", example = \"1\")\n    private Long categoryId;\n\n    @Schema(description = \"商品库存\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10000\")\n    private Integer stock;\n\n    @Schema(description = \"商品状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageRecordController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO;\nimport cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;\nimport cn.iocoder.yudao.module.trade.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 APP - 分销用户\")\n@RestController\n@RequestMapping(\"/trade/brokerage-record\")\n@Validated\n@Slf4j\npublic class AppBrokerageRecordController {\n\n    @Resource\n    private BrokerageRecordService brokerageRecordService;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得分销记录分页\")\n    public CommonResult<PageResult<AppBrokerageRecordRespVO>> getBrokerageRecordPage(@Valid AppBrokerageRecordPageReqVO pageReqVO) {\n        PageResult<BrokerageRecordDO> pageResult = brokerageRecordService.getBrokerageRecordPage(\n                BeanUtils.toBean(pageReqVO, BrokerageRecordPageReqVO.class).setUserId(getLoginUserId()));\n        return success(BeanUtils.toBean(pageResult, AppBrokerageRecordRespVO.class, recordVO ->\n                recordVO.setStatusName(DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.BROKERAGE_RECORD_STATUS, recordVO.getStatus()))));\n    }\n\n    @GetMapping(\"/get-product-brokerage-price\")\n    @Operation(summary = \"获得商品的分销金额\")\n    public CommonResult<AppBrokerageProductPriceRespVO> getProductBrokeragePrice(@RequestParam(\"spuId\") Long spuId) {\n        return success(brokerageRecordService.calculateProductBrokeragePrice(getLoginUserId(), spuId));\n    }\n\n}"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage;\n\nimport cn.hutool.core.date.LocalDateTimeUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.MemberUserApi;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.*;\nimport cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageRecordConvert;\nimport cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageUserConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;\nimport cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.format.annotation.DateTimeFormat;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.time.LocalDateTime;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\nimport static java.util.Arrays.asList;\n\n@Tag(name = \"用户 APP - 分销用户\")\n@RestController\n@RequestMapping(\"/trade/brokerage-user\")\n@Validated\n@Slf4j\npublic class AppBrokerageUserController {\n\n    @Resource\n    private BrokerageUserService brokerageUserService;\n    @Resource\n    private BrokerageRecordService brokerageRecordService;\n    @Resource\n    private BrokerageWithdrawService brokerageWithdrawService;\n    @Resource\n    private MemberUserApi memberUserApi;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得个人分销信息\")\n    public CommonResult<AppBrokerageUserRespVO> getBrokerageUser() {\n        Optional<BrokerageUserDO> user = Optional.ofNullable(brokerageUserService.getOrCreateBrokerageUser(getLoginUserId()));\n        // 返回数据\n        AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO()\n                .setBrokerageEnabled(user.map(BrokerageUserDO::getBrokerageEnabled).orElse(false))\n                .setBrokeragePrice(user.map(BrokerageUserDO::getBrokeragePrice).orElse(0))\n                .setFrozenPrice(user.map(BrokerageUserDO::getFrozenPrice).orElse(0));\n        return success(respVO);\n    }\n\n    @PutMapping(\"/bind\")\n    @Operation(summary = \"绑定推广员\")\n    public CommonResult<Boolean> bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) {\n        return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId()));\n    }\n\n    @GetMapping(\"/get-summary\")\n    @Operation(summary = \"获得个人分销统计\")\n    public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() {\n        // 查询当前登录用户信息\n        Long userId = getLoginUserId();\n        BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(userId);\n        // 统计用户昨日的佣金\n        LocalDateTime yesterday = LocalDateTime.now().minusDays(1);\n        LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday);\n        LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday);\n        Integer yesterdayPrice = brokerageRecordService.getSummaryPriceByUserId(userId,\n                BrokerageRecordBizTypeEnum.ORDER, BrokerageRecordStatusEnum.SETTLEMENT, beginTime, endTime);\n        // 统计用户提现的佣金\n        Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryListByUserId(Collections.singleton(userId),\n                        asList(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS)).stream()\n                .findFirst().map(BrokerageWithdrawSummaryRespBO::getPrice).orElse(0);\n        // 统计分销用户数量（一级）\n        Long firstBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(userId, 1);\n        // 统计分销用户数量（二级）\n        Long secondBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(userId, 2);\n\n        // 拼接返回\n        return success(BrokerageUserConvert.INSTANCE.convert(yesterdayPrice, withdrawPrice, firstBrokerageUserCount, secondBrokerageUserCount, brokerageUser));\n    }\n\n    @GetMapping(\"/rank-page-by-user-count\")\n    @Operation(summary = \"获得分销用户排行分页（基于用户量）\")\n    public CommonResult<PageResult<AppBrokerageUserRankByUserCountRespVO>> getBrokerageUserRankPageByUserCount(AppBrokerageUserRankPageReqVO pageReqVO) {\n        // 分页查询\n        PageResult<AppBrokerageUserRankByUserCountRespVO> pageResult = brokerageUserService.getBrokerageUserRankPageByUserCount(pageReqVO);\n        // 拼接数据\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), AppBrokerageUserRankByUserCountRespVO::getId));\n        return success(BrokerageUserConvert.INSTANCE.convertPage03(pageResult, userMap));\n    }\n\n    @GetMapping(\"/rank-page-by-price\")\n    @Operation(summary = \"获得分销用户排行分页（基于佣金）\")\n    public CommonResult<PageResult<AppBrokerageUserRankByPriceRespVO>> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {\n        // 分页查询\n        PageResult<AppBrokerageUserRankByPriceRespVO> pageResult = brokerageRecordService.getBrokerageUserChildSummaryPageByPrice(pageReqVO);\n        // 拼接数据\n        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), AppBrokerageUserRankByPriceRespVO::getId));\n        return success(BrokerageRecordConvert.INSTANCE.convertPage03(pageResult, userMap));\n    }\n\n    @GetMapping(\"/child-summary-page\")\n    @Operation(summary = \"获得下级分销统计分页\")\n    public CommonResult<PageResult<AppBrokerageUserChildSummaryRespVO>> getBrokerageUserChildSummaryPage(\n            AppBrokerageUserChildSummaryPageReqVO pageReqVO) {\n        PageResult<AppBrokerageUserChildSummaryRespVO> pageResult = brokerageUserService.getBrokerageUserChildSummaryPage(pageReqVO, getLoginUserId());\n        return success(pageResult);\n    }\n\n    @GetMapping(\"/get-rank-by-price\")\n    @Operation(summary = \"获得分销用户排行（基于佣金）\")\n    @Parameter(name = \"times\", description = \"时间段\", required = true)\n    public CommonResult<Integer> getRankByPrice(\n            @RequestParam(\"times\") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] times) {\n        return success(brokerageRecordService.getUserRankByPrice(getLoginUserId(), times));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageWithdrawController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi;\nimport cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawPageReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawRespVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;\nimport cn.iocoder.yudao.module.trade.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;\nimport cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.Objects;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 APP - 分销提现\")\n@RestController\n@RequestMapping(\"/trade/brokerage-withdraw\")\n@Validated\n@Slf4j\npublic class AppBrokerageWithdrawController {\n\n    @Resource\n    private BrokerageWithdrawService brokerageWithdrawService;\n\n    @Resource\n    private PayTransferApi payTransferApi;\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得分销提现分页\")\n    public CommonResult<PageResult<AppBrokerageWithdrawRespVO>> getBrokerageWithdrawPage(AppBrokerageWithdrawPageReqVO pageReqVO) {\n        PageResult<BrokerageWithdrawDO> pageResult = brokerageWithdrawService.getBrokerageWithdrawPage(\n                BeanUtils.toBean(pageReqVO, BrokerageWithdrawPageReqVO.class).setUserId(getLoginUserId()));\n        return success(BeanUtils.toBean(pageResult, AppBrokerageWithdrawRespVO.class, withdrawVO ->\n                withdrawVO.setTypeName(DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.BROKERAGE_WITHDRAW_TYPE, withdrawVO.getType()))\n                        .setStatusName(DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.BROKERAGE_WITHDRAW_STATUS, withdrawVO.getStatus()))));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得佣金提现\")\n    @Parameter(name = \"id\", description = \"编号\", required = true, example = \"1024\")\n    public CommonResult<AppBrokerageWithdrawRespVO> getBrokerageWithdraw(@RequestParam(\"id\") Long id) {\n        BrokerageWithdrawDO withdraw = brokerageWithdrawService.getBrokerageWithdraw(id);\n        if (withdraw == null || ObjUtil.notEqual(withdraw.getUserId(), getLoginUserId())) {\n            return success(null);\n        }\n        // 审核中（转账中），并且是微信转账，需要返回 mchId 用于确认收款\n        AppBrokerageWithdrawRespVO withdrawVO = BeanUtils.toBean(withdraw, AppBrokerageWithdrawRespVO.class);\n        if (Objects.equals(withdraw.getStatus(), BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.getStatus())\n                && Objects.equals(withdraw.getType(), BrokerageWithdrawTypeEnum.WECHAT_API.getType())\n                && withdraw.getPayTransferId() != null) {\n            PayTransferRespDTO transfer = payTransferApi.getTransfer(withdraw.getPayTransferId()).getCheckedData();\n            if (transfer != null) {\n                withdrawVO.setTransferChannelPackageInfo(transfer.getChannelPackageInfo())\n                        .setTransferChannelMchId(transfer.getChannelMchId());\n            }\n        }\n        return success(withdrawVO);\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建分销提现\")\n    public CommonResult<Long> createBrokerageWithdraw(@RequestBody @Valid AppBrokerageWithdrawCreateReqVO createReqVO) {\n        return success(brokerageWithdrawService.createBrokerageWithdraw(getLoginUserId(), createReqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 商品的分销金额 Response VO\")\n@Data\npublic class AppBrokerageProductPriceRespVO {\n\n    @Schema(description = \"是否开启\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Boolean enabled;\n\n    @Schema(description = \"分销最小金额，单位：分\", example = \"100\")\n    private Integer brokerageMinPrice;\n\n    @Schema(description = \"分销最大金额，单位：分\", example = \"100\")\n    private Integer brokerageMaxPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"应用 App - 分销记录分页 Request VO\")\n@Data\npublic class AppBrokerageRecordPageReqVO extends PageParam {\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n    @Schema(description = \"业务类型\", example = \"1\")\n    @InEnum(value = BrokerageRecordBizTypeEnum.class, message = \"业务类型必须是 {value}\")\n    private Integer bizType;\n\n    @Schema(description = \"状态\", example = \"1\")\n    @InEnum(value = BrokerageRecordStatusEnum.class, message = \"状态必须是 {value}\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"用户 App - 分销记录 Response VO\")\n@Data\npublic class AppBrokerageRecordRespVO {\n\n    @Schema(description = \"记录编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long id;\n\n    @Schema(description = \"业务编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private String bizId;\n\n    @Schema(description = \"分销标题\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"用户下单\")\n    private String title;\n\n    @Schema(description = \"分销金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    private Integer price;\n\n    @Schema(description = \"状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"状态名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"待结算\")\n    private String statusName;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"完成时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime finishTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserBindReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"应用 App - 绑定推广员 Request VO\")\n@Data\npublic class AppBrokerageUserBindReqVO {\n\n    @Schema(description = \"推广员编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"推广员编号不能为空\")\n    private Long bindUserId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.pojo.SortingField;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.Range;\n\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 下级分销统计分页 Request VO\")\n@Data\npublic class AppBrokerageUserChildSummaryPageReqVO extends PageParam {\n\n    public static final String SORT_FIELD_USER_COUNT = \"userCount\";\n    public static final String SORT_FIELD_ORDER_COUNT = \"orderCount\";\n    public static final String SORT_FIELD_PRICE = \"price\";\n\n    @Schema(description = \"用户昵称\", example = \"李\") // 模糊匹配\n    private String nickname;\n\n    @Schema(description = \"排序字段\", example = \"userCount\")\n    private SortingField sortingField;\n\n    @Schema(description = \"下级的级别\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\") // 1 - 直接下级；2 - 间接下级\n    @NotNull(message = \"下级的级别不能为空\")\n    @Range(min = 1, max = 2, message = \"下级的级别只能是 {min} 或者 {max}\")\n    private Integer level;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"用户 App - 下级分销统计 Response VO\")\n@Data\npublic class AppBrokerageUserChildSummaryRespVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long id;\n\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"小王\")\n    private String nickname;\n\n    @Schema(description = \"用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xxx.jpg\")\n    private String avatar;\n\n    @Schema(description = \"佣金金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer brokeragePrice;\n\n    @Schema(description = \"分销订单数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"20\")\n    private Integer brokerageOrderCount;\n\n    @Schema(description = \"分销用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"30\")\n    private Integer brokerageUserCount;\n\n    @Schema(description = \"绑定推广员的时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime brokerageTime;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 个人分销统计 Response VO\")\n@Data\npublic class AppBrokerageUserMySummaryRespVO {\n\n    @Schema(description = \"昨天的佣金，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer yesterdayPrice;\n\n    @Schema(description = \"提现的佣金，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Integer withdrawPrice;\n\n    @Schema(description = \"可用的佣金，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2408\")\n    private Integer brokeragePrice;\n\n    @Schema(description = \"冻结的佣金，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"234\")\n    private Integer frozenPrice;\n\n    @Schema(description = \"分销用户数量（一级）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long firstBrokerageUserCount;\n\n    @Schema(description = \"分销用户数量（二级）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long secondBrokerageUserCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 分销排行用户（基于用户量） Response VO\")\n@Data\npublic class AppBrokerageUserRankByPriceRespVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long id;\n\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"小王\")\n    private String nickname;\n\n    @Schema(description = \"用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xxx.jpg\")\n    private String avatar;\n\n    @Schema(description = \"佣金金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer brokeragePrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByUserCountRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 分销排行用户（基于用户量） Response VO\")\n@Data\npublic class AppBrokerageUserRankByUserCountRespVO {\n\n    @Schema(description = \"用户编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long id;\n\n    @Schema(description = \"用户昵称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"小王\")\n    private String nickname;\n\n    @Schema(description = \"用户头像\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/xxx.jpg\")\n    private String avatar;\n\n    @Schema(description = \"邀请用户数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer brokerageUserCount;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"应用 App - 分销用户排行 Request VO\")\n@Data\npublic class AppBrokerageUserRankPageReqVO extends PageParam {\n\n    @Schema(description = \"开始 + 结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    @NotEmpty(message = \"时间不能为空\")\n    private LocalDateTime[] times;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 分销用户信息 Response VO\")\n@Data\npublic class AppBrokerageUserRespVO {\n\n    @Schema(description = \"是否有分销资格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean brokerageEnabled;\n\n    @Schema(description = \"可用的佣金，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2408\")\n    private Integer brokeragePrice;\n\n    @Schema(description = \"冻结的佣金，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"234\")\n    private Integer frozenPrice;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw;\n\nimport cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.pay.enums.PayChannelEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport javax.validation.constraints.Min;\nimport lombok.Data;\nimport org.hibernate.validator.constraints.URL;\n\nimport javax.validation.Validator;\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.PositiveOrZero;\n\n@Schema(description = \"用户 App - 分销提现创建 Request VO\")\n@Data\npublic class AppBrokerageWithdrawCreateReqVO {\n\n    @Schema(description = \"提现方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @InEnum(value = BrokerageWithdrawTypeEnum.class, message = \"提现方式必须是 {value}\")\n    private Integer type;\n\n    @Schema(description = \"提现金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    @PositiveOrZero(message = \"提现金额不能小于 0\")\n    @Min(value = 30, message = \"微信提现金额不能小于 0.3\", groups = {WechatApi.class})\n    @NotNull(message = \"提现金额不能为空\")\n    private Integer price;\n\n    @Schema(description = \"提现账号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123456789\")\n    @NotBlank(message = \"提现账号不能为空\", groups = {Bank.class, WechatApi.class, AlipayApi.class})\n    private String userAccount;\n\n    @Schema(description = \"提现姓名\", example = \"张三\")\n    @NotBlank(message = \"提现姓名不能为空\", groups = {Bank.class, WechatApi.class, AlipayApi.class})\n    private String userName;\n\n    @Schema(description = \"收款码的图片\", example = \"https://www.iocoder.cn/1.png\")\n    @URL(message = \"收款码的图片，必须是一个 URL\", groups = {WechatQR.class, AlipayQR.class})\n    private String qrCodeUrl;\n\n    @Schema(description = \"提现银行\", example = \"1\")\n    @NotNull(message = \"提现银行不能为空\", groups = {Bank.class})\n    private String bankName;\n    @Schema(description = \"开户地址\", example = \"海淀支行\")\n    private String bankAddress;\n\n    @Schema(description = \"转账渠道\", example = \"wx_lite\")\n    @NotNull(message = \"转账渠道不能为空\", groups = {WechatApi.class})\n    @InEnum(PayChannelEnum.class)\n    private String transferChannelCode;\n\n    public interface Wallet {\n    }\n\n    public interface Bank {\n    }\n\n    public interface WechatQR {\n    }\n\n    public interface WechatApi {\n    }\n\n    public interface AlipayQR {\n    }\n\n    public interface AlipayApi {\n    }\n\n    public void validate(Validator validator) {\n        if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(type)) {\n            ValidationUtils.validate(validator, this, Wallet.class);\n        } else if (BrokerageWithdrawTypeEnum.BANK.getType().equals(type)) {\n            ValidationUtils.validate(validator, this, Bank.class);\n        } else if (BrokerageWithdrawTypeEnum.WECHAT_QR.getType().equals(type)) {\n            ValidationUtils.validate(validator, this, WechatQR.class);\n        } else if (BrokerageWithdrawTypeEnum.WECHAT_API.getType().equals(type)) {\n            ValidationUtils.validate(validator, this, WechatApi.class);\n        } else if (BrokerageWithdrawTypeEnum.ALIPAY_QR.getType().equals(type)) {\n            ValidationUtils.validate(validator, this, AlipayQR.class);\n        } else if (BrokerageWithdrawTypeEnum.ALIPAY_API.getType().equals(type)) {\n            ValidationUtils.validate(validator, this, AlipayApi.class);\n        }\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.time.LocalDateTime;\n\nimport static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;\n\n@Schema(description = \"应用 App - 分销提现分页 Request VO\")\n@Data\npublic class AppBrokerageWithdrawPageReqVO extends PageParam {\n\n    @Schema(description = \"创建时间\")\n    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)\n    private LocalDateTime[] createTime;\n\n\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Schema(description = \"用户 App - 分销提现 Response VO\")\n@Data\npublic class AppBrokerageWithdrawRespVO {\n\n    @Schema(description = \"提现编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Long id;\n\n    @Schema(description = \"提现类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer type;\n\n    @Schema(description = \"提现类型名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"微信\")\n    private String typeName;\n\n    @Schema(description = \"提现状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer status;\n\n    @Schema(description = \"提现状态名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"审核中\")\n    private String statusName;\n\n    @Schema(description = \"提现金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    private Integer price;\n\n    @Schema(description = \"提现时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    // ========== 微信转账专属 ==========\n\n    @Schema(description = \"转账单编号\", example = \"1024\")\n    private Long payTransferId;\n\n    @Schema(description = \"渠道 package 信息\")\n    private String transferChannelPackageInfo;\n\n    @Schema(description = \"渠道商户号\")\n    private String transferChannelMchId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.http",
    "content": "### 请求 /trade/cart/add 接口 => 成功\nPOST {{appApi}}/trade/cart/add\ntenant-id: {{appTenantId}}\nAuthorization: Bearer {{appToken}}\nContent-Type: application/json\n\n{\n  \"skuId\": 1,\n  \"count\": 10,\n  \"addStatus\": true\n}\n\n### 请求 /trade/cart/update 接口 => 成功\nPUT {{appApi}}/trade/cart/update\ntenant-id: {{appTenantId}}\nAuthorization: Bearer {{appToken}}\nContent-Type: application/json\n\n{\n  \"id\": 35,\n  \"count\": 5\n}\n\n### 请求 /trade/cart/delete 接口 => 成功\nDELETE {{appApi}}/trade/cart/delete?ids=1\ntenant-id: {{appTenantId}}\nAuthorization: Bearer {{appToken}}\n\n### 请求 /trade/cart/get-count 接口 => 成功\nGET {{appApi}}/trade/cart/get-count\ntenant-id: {{appTenantId}}\nAuthorization: Bearer {{appToken}}\n\n### 请求 /trade/cart/get-count-map 接口 => 成功\nGET {{appApi}}/trade/cart/get-count-map\ntenant-id: {{appTenantId}}\nAuthorization: Bearer {{appToken}}\n\n### 请求 /trade/cart/list 接口 => 成功\nGET {{appApi}}/trade/cart/list\ntenant-id: {{appTenantId}}\nAuthorization: Bearer {{appToken}}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.cart;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.trade.controller.app.cart.vo.*;\nimport cn.iocoder.yudao.module.trade.service.cart.CartService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.validation.Valid;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 App - 购物车\")\n@RestController\n@RequestMapping(\"/trade/cart\")\n@RequiredArgsConstructor\n@Validated\n@Slf4j\npublic class AppCartController {\n\n    @Resource\n    private CartService cartService;\n\n    @PostMapping(\"/add\")\n    @Operation(summary = \"添加购物车商品\")\n    public CommonResult<Long> addCart(@Valid @RequestBody AppCartAddReqVO addCountReqVO) {\n        return success(cartService.addCart(getLoginUserId(), addCountReqVO));\n    }\n\n    @PutMapping(\"/update-count\")\n    @Operation(summary = \"更新购物车商品数量\")\n    public CommonResult<Boolean> updateCartCount(@Valid @RequestBody AppCartUpdateCountReqVO updateReqVO) {\n        cartService.updateCartCount(getLoginUserId(), updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/update-selected\")\n    @Operation(summary = \"更新购物车商品选中\")\n    public CommonResult<Boolean> updateCartSelected(@Valid @RequestBody AppCartUpdateSelectedReqVO updateReqVO) {\n        cartService.updateCartSelected(getLoginUserId(), updateReqVO);\n        return success(true);\n    }\n\n    @PutMapping(\"/reset\")\n    @Operation(summary = \"重置购物车商品\")\n    public CommonResult<Boolean> resetCart(@Valid @RequestBody AppCartResetReqVO updateReqVO) {\n        cartService.resetCart(getLoginUserId(), updateReqVO);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除购物车商品\")\n    @Parameter(name = \"ids\", description = \"购物车商品编号\", required = true, example = \"1024,2048\")\n    public CommonResult<Boolean> deleteCart(@RequestParam(\"ids\") List<Long> ids) {\n        cartService.deleteCart(getLoginUserId(), ids);\n        return success(true);\n    }\n\n    @GetMapping(\"get-count\")\n    @Operation(summary = \"查询用户在购物车中的商品数量\")\n    public CommonResult<Integer> getCartCount() {\n        return success(cartService.getCartCount(getLoginUserId()));\n    }\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"查询用户的购物车列表\")\n    public CommonResult<AppCartListRespVO> getCartList() {\n        return success(cartService.getCartList(getLoginUserId()));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartAddReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.cart.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 购物车添加购物项 Request VO\")\n@Data\npublic class AppCartAddReqVO {\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED,example = \"1024\")\n    @NotNull(message = \"商品 SKU 编号不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"新增商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"数量不能为空\")\n    @Min(value = 1, message = \"商品数量必须大于等于 1\")\n    private Integer count;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.cart.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 用户的购物车明细 Response VO\")\n@Data\npublic class AppCartDetailRespVO {\n\n    /**\n     * 商品分组数组\n     */\n    private List<ItemGroup> itemGroups;\n\n    /**\n     * 费用\n     */\n    private Order order;\n\n    @Schema(description = \"商品分组\") // 多个商品，参加同一个活动，从而形成分组\n    @Data\n    public static class ItemGroup {\n\n        /**\n         * 商品数组\n         */\n        private List<Sku> items;\n        /**\n         * 营销活动，订单级别\n         */\n        private Promotion promotion;\n\n    }\n\n    @Schema(description = \"商品 SKU\")\n    @Data\n    public static class Sku extends AppProductSkuBaseRespVO {\n\n        /**\n         * SPU 信息\n         */\n        private AppProductSkuBaseRespVO spu;\n\n        // ========== 购物车相关的字段 ==========\n\n        @Schema(description = \"商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer count;\n        @Schema(description = \"是否选中\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n        private Boolean selected;\n\n        // ========== 价格相关的字段，对应 PriceCalculateRespDTO.OrderItem 的属性 ==========\n\n        // TODO 芋艿：后续可以去除一些无用的字段\n\n        @Schema(description = \"商品原价（单）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer originalPrice;\n        @Schema(description = \"商品原价（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"200\")\n        private Integer totalOriginalPrice;\n        @Schema(description = \"商品级优惠（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"300\")\n        private Integer totalPromotionPrice;\n        @Schema(description = \"最终购买金额（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"400\")\n        private Integer totalPresentPrice;\n        @Schema(description = \"最终购买金额（单）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"500\")\n        private Integer presentPrice;\n        @Schema(description = \"应付金额（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"600\")\n        private Integer totalPayPrice;\n\n        // ========== 营销相关的字段 ==========\n        /**\n         * 营销活动，商品级别\n         */\n        private Promotion promotion;\n\n    }\n\n    @Schema(description = \"订单\") // 对应 PriceCalculateRespDTO.Order 类，用于费用（合计）\n    @Data\n    public static class Order {\n\n        // TODO 芋艿：后续可以去除一些无用的字段\n\n        @Schema(description = \"商品原价（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer skuOriginalPrice;\n        @Schema(description = \"商品优惠（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"200\")\n        private Integer skuPromotionPrice;\n        @Schema(description = \"订单优惠（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"300\")\n        private Integer orderPromotionPrice;\n        @Schema(description = \"运费金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"400\")\n        private Integer deliveryPrice;\n        @Schema(description = \"应付金额（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"500\")\n        private Integer payPrice;\n\n    }\n\n    @Schema(description = \"营销活动\") // 对应 PriceCalculateRespDTO.Promotion 类的属性\n    @Data\n    public static class Promotion {\n\n        @Schema(description = \"营销编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\") // 营销活动的编号、优惠劵的编号\n        private Long id;\n        @Schema(description = \"营销名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"xx 活动\")\n        private String name;\n        @Schema(description = \"营销类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer type;\n\n        // ========== 匹配情况 ==========\n        @Schema(description = \"是否满足优惠条件\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n        private Boolean meet;\n        @Schema(description = \"满足条件的提示\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"圣诞价:省 150.00 元\")\n        private String meetTip;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartListRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.cart.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;\nimport cn.iocoder.yudao.module.trade.controller.app.base.spu.AppProductSpuBaseRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 用户的购物列表 Response VO\")\n@Data\npublic class AppCartListRespVO {\n\n    /**\n     * 有效的购物项数组\n     */\n    private List<Cart> validList;\n\n    /**\n     * 无效的购物项数组\n     */\n    private List<Cart> invalidList;\n\n    @Schema(description = \"购物项\")\n    @Data\n    public static class Cart {\n\n        @Schema(description = \"购物项的编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private Long id;\n\n        @Schema(description = \"商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer count;\n\n        @Schema(description = \"是否选中\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n        private Boolean selected;\n\n        /**\n         * 商品 SPU\n         */\n        private AppProductSpuBaseRespVO spu;\n        /**\n         * 商品 SKU\n         */\n        private AppProductSkuBaseRespVO sku;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartResetReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.cart.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 购物车重置 Request VO\")\n@Data\npublic class AppCartResetReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED,example = \"1024\")\n    @NotNull(message = \"商品 SKU 编号不能为空\")\n    private Long skuId;\n\n    @Schema(description = \"商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"数量不能为空\")\n    @Min(message = \"数量必须大于 0\", value = 1L)\n    private Integer count;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartUpdateCountReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.cart.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\n\n@Schema(description = \"用户 App - 购物车更新数量 Request VO\")\n@Data\npublic class AppCartUpdateCountReqVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    @NotNull(message = \"编号不能为空\")\n    private Long id;\n\n    @Schema(description = \"商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @NotNull(message = \"数量不能为空\")\n    @Min(message = \"数量必须大于 0\", value = 1L)\n    private Integer count;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartUpdateSelectedReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.cart.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.Collection;\n\n@Schema(description = \"用户 App - 购物车更新是否选中 Request VO\")\n@Data\npublic class AppCartUpdateSelectedReqVO {\n\n    @Schema(description = \"编号列表\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024,2048\")\n    @NotNull(message = \"编号列表不能为空\")\n    private Collection<Long> ids;\n\n    @Schema(description = \"是否选中\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否选中不能为空\")\n    private Boolean selected;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/config/AppTradeConfigController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.config;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.trade.controller.app.config.vo.AppTradeConfigRespVO;\nimport cn.iocoder.yudao.module.trade.convert.config.TradeConfigConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;\nimport cn.iocoder.yudao.module.trade.service.config.TradeConfigService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 App - 交易配置\")\n@RestController\n@RequestMapping(\"/trade/config\")\n@RequiredArgsConstructor\n@Validated\n@Slf4j\npublic class AppTradeConfigController {\n\n    @Resource\n    private TradeConfigService tradeConfigService;\n\n    @Value(\"${yudao.tencent-lbs-key}\")\n    private String tencentLbsKey;\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得交易配置\")\n    @PermitAll\n    public CommonResult<AppTradeConfigRespVO> getTradeConfig() {\n        TradeConfigDO config = ObjUtil.defaultIfNull(tradeConfigService.getTradeConfig(), new TradeConfigDO());\n        return success(TradeConfigConvert.INSTANCE.convert02(config).setTencentLbsKey(tencentLbsKey));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/config/vo/AppTradeConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.config.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 交易配置 Response VO\")\n@Data\npublic class AppTradeConfigRespVO {\n\n    @Schema(description = \"腾讯地图 KEY\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123456\")\n    private String tencentLbsKey;\n\n    // ========== 配送相关 ==========\n\n    @Schema(description = \"是否开启自提\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否开启自提不能为空\")\n    private Boolean deliveryPickUpEnabled;\n\n    // ========== 售后相关 ==========\n\n    @Schema(description = \"售后的退款理由\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> afterSaleRefundReasons;\n\n    @Schema(description = \"售后的退货理由\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> afterSaleReturnReasons;\n\n    // ========== 分销相关 ==========\n\n    @Schema(description = \"分销海报地址数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<String> brokeragePosterUrls;\n\n    @Schema(description = \"佣金冻结时间（天）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer brokerageFrozenDays;\n\n    @Schema(description = \"佣金提现最小金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer brokerageWithdrawMinPrice;\n\n    @Schema(description = \"提现方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[1, 2]\")\n    private List<Integer> brokerageWithdrawTypes;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/AppDeliverExpressController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.delivery;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.trade.controller.app.delivery.vo.express.AppDeliveryExpressRespVO;\nimport cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;\nimport cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 App - 快递公司\")\n@RestController\n@RequestMapping(\"/trade/delivery/express\")\n@Validated\npublic class AppDeliverExpressController {\n\n    @Resource\n    private DeliveryExpressService deliveryExpressService;\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得快递公司列表\")\n    @PermitAll\n    public CommonResult<List<AppDeliveryExpressRespVO>> getDeliveryExpressList() {\n        List<DeliveryExpressDO> list = deliveryExpressService.getDeliveryExpressListByStatus(CommonStatusEnum.ENABLE.getStatus());\n        list.sort(Comparator.comparing(DeliveryExpressDO::getSort));\n        return success(DeliveryExpressConvert.INSTANCE.convertList03(list));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/AppDeliverPickUpStoreController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.delivery;\n\nimport cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.module.trade.controller.app.delivery.vo.pickup.AppDeliveryPickUpStoreRespVO;\nimport cn.iocoder.yudao.module.trade.convert.delivery.DeliveryPickUpStoreConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;\nimport cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport java.util.List;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\n\n@Tag(name = \"用户 App - 自提门店\")\n@RestController\n@RequestMapping(\"/trade/delivery/pick-up-store\")\n@Validated\npublic class AppDeliverPickUpStoreController {\n\n    @Resource\n    private DeliveryPickUpStoreService deliveryPickUpStoreService;\n\n    @GetMapping(\"/list\")\n    @Operation(summary = \"获得自提门店列表\")\n    @Parameters({\n            @Parameter(name = \"latitude\", description = \"精度\", example = \"110\"),\n            @Parameter(name = \"longitude\", description = \"纬度\", example = \"120\")\n    })\n    @PermitAll\n    public CommonResult<List<AppDeliveryPickUpStoreRespVO>> getDeliveryPickUpStoreList(\n            @RequestParam(value = \"latitude\", required = false) Double latitude,\n            @RequestParam(value = \"longitude\", required = false) Double longitude) {\n        List<DeliveryPickUpStoreDO> list = deliveryPickUpStoreService.getDeliveryPickUpStoreListByStatus(\n                CommonStatusEnum.ENABLE.getStatus());\n        return success(DeliveryPickUpStoreConvert.INSTANCE.convertList(list, latitude, longitude));\n    }\n\n    @GetMapping(\"/get\")\n    @Operation(summary = \"获得自提门店\")\n    @Parameter(name = \"id\", description = \"门店编号\")\n    @PermitAll\n    public CommonResult<AppDeliveryPickUpStoreRespVO> getOrder(@RequestParam(\"id\") Long id) {\n        DeliveryPickUpStoreDO store = deliveryPickUpStoreService.getDeliveryPickUpStore(id);\n        return success(DeliveryPickUpStoreConvert.INSTANCE.convert03(store));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/vo/config/AppDeliveryConfigRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.config;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n// TODO 芋艿：后续要实现下，配送配置；后续融合到 AppTradeConfigRespVO 中\n@Schema(description = \"用户 App - 配送配置 Response VO\")\n@Data\npublic class AppDeliveryConfigRespVO {\n\n    @Schema(description = \"腾讯地图 KEY\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"123456\")\n    private String tencentLbsKey;\n\n    @Schema(description = \"是否开启自提\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean pickUpEnable;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/vo/express/AppDeliveryExpressRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.express;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 快递公司 Response VO\")\n@Data\npublic class AppDeliveryExpressRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"门店名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"顺丰\")\n    private String name;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.pickup;\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.time.LocalTime;\n\n@Schema(description = \"用户 App - 自提门店 Response VO\")\n@Data\npublic class AppDeliveryPickUpStoreRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"23128\")\n    private Long id;\n\n    @Schema(description = \"门店名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"李四\")\n    private String name;\n\n    @Schema(description = \"门店 logo\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    private String logo;\n\n    @Schema(description = \"门店手机\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15601892312\")\n    private String phone;\n\n    @Schema(description = \"区域编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"18733\")\n    private Integer areaId;\n\n    @Schema(description = \"地区名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"上海上海市普陀区\")\n    private String areaName;\n\n    @Schema(description = \"门店详细地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"复旦大学路 188 号\")\n    private String detailAddress;\n\n    @Schema(description = \"营业开始时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"营业开始时间不能为空\")\n    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = \"HH:mm\")\n    private LocalTime openingTime;\n\n    @Schema(description = \"营业结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotNull(message = \"营业结束时间不能为空\")\n    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = \"HH:mm\")\n    private LocalTime closingTime;\n\n    @Schema(description = \"纬度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5.88\")\n    private Double latitude;\n\n    @Schema(description = \"经度\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"6.99\")\n    private Double longitude;\n\n    @Schema(description = \"距离，单位：千米\", example = \"100\") // 只有在用户传递了经纬度时，才进行计算\n    private Double distance;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http",
    "content": "### /trade-order/settlement 获得订单结算信息（基于商品）\nGET {{appApi}}/trade/order/settlement?type=0&items[0].skuId=1&items[0].count=2&items[1].skuId=2&items[1].count=3&couponId=1\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n### /trade-order/settlement 获得订单结算信息（基于购物车）\nGET {{appApi}}/trade/order/settlement?type=0&items[0].cartId=50&couponId=1\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n### /trade-order/create 创建订单（基于商品）【快递】\nPOST {{appApi}}/trade/order/create\nContent-Type: application/json\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n{\n  \"pointStatus\": true,\n  \"deliveryType\": 1,\n  \"addressId\": 21,\n  \"items\": [\n    {\n      \"skuId\": 1,\n      \"count\": 2\n    }\n  ],\n  \"remark\": \"我是备注\"\n}\n\n### /trade-order/create 创建订单（基于商品）【自提】\nPOST {{appApi}}/trade/order/create\nContent-Type: application/json\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n{\n  \"pointStatus\": true,\n  \"deliveryType\": 2,\n  \"pickUpStoreId\": 1,\n  \"items\": [\n    {\n      \"skuId\": 1,\n      \"count\": 2\n    }\n  ],\n  \"remark\": \"我是备注\",\n  \"receiverName\": \"土豆\",\n  \"receiverMobile\": \"15601691300\"\n}\n\n### 获得订单交易的分页\nGET {{appApi}}/trade/order/page?pageNo=1&pageSize=10\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n### 获得订单交易的详细\nGET {{appApi}}/trade/order/get-detail?id=21\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n### 获得交易订单的物流轨迹\nGET {{appApi}}/trade/order/get-express-track-list?id=70\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}\n\n### /trade-order/settlement-product 获得商品结算信息\nGET {{appApi}}/trade/order/settlement-product?spuIds=633\nAuthorization: Bearer {{appToken}}\ntenant-id: {{appTenantId}}"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order;\n\nimport cn.iocoder.yudao.framework.common.pojo.CommonResult;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;\nimport cn.iocoder.yudao.module.trade.controller.app.order.vo.*;\nimport cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;\nimport cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;\nimport cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;\nimport cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;\nimport cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;\nimport cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;\nimport cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;\nimport cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;\nimport cn.iocoder.yudao.module.trade.service.price.TradePriceService;\nimport com.google.common.collect.Maps;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.Parameters;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport javax.annotation.security.PermitAll;\nimport javax.validation.Valid;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;\nimport static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;\n\n@Tag(name = \"用户 App - 交易订单\")\n@RestController\n@RequestMapping(\"/trade/order\")\n@Validated\n@Slf4j\npublic class AppTradeOrderController {\n\n    @Resource\n    private TradeOrderUpdateService tradeOrderUpdateService;\n    @Resource\n    private TradeOrderQueryService tradeOrderQueryService;\n    @Resource\n    private DeliveryExpressService deliveryExpressService;\n    @Resource\n    private AfterSaleService afterSaleService;\n    @Resource\n    private TradePriceService priceService;\n\n    @Resource\n    private TradeOrderProperties tradeOrderProperties;\n\n    @GetMapping(\"/settlement\")\n    @Operation(summary = \"获得订单结算信息\")\n    public CommonResult<AppTradeOrderSettlementRespVO> settlementOrder(@Valid AppTradeOrderSettlementReqVO settlementReqVO) {\n        return success(tradeOrderUpdateService.settlementOrder(getLoginUserId(), settlementReqVO));\n    }\n\n    @GetMapping(\"/settlement-product\")\n    @Operation(summary = \"获得商品结算信息\", description = \"用于商品列表、商品详情，获得参与活动后的价格信息\")\n    @Parameter(name = \"spuIds\", description = \"商品 SPU 编号数组\")\n    @PermitAll\n    public CommonResult<List<AppTradeProductSettlementRespVO>> settlementProduct(@RequestParam(\"spuIds\") List<Long> spuIds) {\n        return success(priceService.calculateProductPrice(getLoginUserId(), spuIds));\n    }\n\n    @PostMapping(\"/create\")\n    @Operation(summary = \"创建订单\")\n    public CommonResult<AppTradeOrderCreateRespVO> createOrder(@Valid @RequestBody AppTradeOrderCreateReqVO createReqVO) {\n        TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), createReqVO);\n        return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId()));\n    }\n\n    @PostMapping(\"/update-paid\")\n    @Operation(summary = \"更新订单为已支付\") // 由 pay-module 支付服务，进行回调，可见 PayNotifyJob\n    @PermitAll\n    public CommonResult<Boolean> updateOrderPaid(@RequestBody PayOrderNotifyReqDTO notifyReqDTO) {\n        tradeOrderUpdateService.updateOrderPaid(Long.valueOf(notifyReqDTO.getMerchantOrderId()),\n                notifyReqDTO.getPayOrderId());\n        return success(true);\n    }\n\n    @GetMapping(\"/get-detail\")\n    @Operation(summary = \"获得交易订单\")\n    @Parameters({\n            @Parameter(name = \"id\", description = \"交易订单编号\"),\n            @Parameter(name = \"sync\", description = \"是否同步支付状态\", example = \"true\")\n    })\n    public CommonResult<AppTradeOrderDetailRespVO> getOrderDetail(@RequestParam(\"id\") Long id,\n                                                                  @RequestParam(value = \"sync\", required = false) Boolean sync) {\n        // 1.1 查询订单\n        TradeOrderDO order = tradeOrderQueryService.getOrder(getLoginUserId(), id);\n        if (order == null) {\n            return success(null);\n        }\n        // 1.2 sync 仅在等待支付\n        if (Boolean.TRUE.equals(sync)\n                && TradeOrderStatusEnum.isUnpaid(order.getStatus()) && !order.getPayStatus()) {\n            tradeOrderUpdateService.syncOrderPayStatusQuietly(order.getId(), order.getPayOrderId());\n            // 重新查询，因为同步后，可能会有变化\n            order = tradeOrderQueryService.getOrder(id);\n        }\n\n        // 2.1 查询订单项\n        List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(order.getId());\n        // 2.2 查询物流公司\n        DeliveryExpressDO express = order.getLogisticsId() != null && order.getLogisticsId() > 0 ?\n                deliveryExpressService.getDeliveryExpress(order.getLogisticsId()) : null;\n        // 2.3 最终组合\n        return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express));\n    }\n\n    @GetMapping(\"/get-express-track-list\")\n    @Operation(summary = \"获得交易订单的物流轨迹\")\n    @Parameter(name = \"id\", description = \"交易订单编号\")\n    public CommonResult<List<AppOrderExpressTrackRespDTO>> getOrderExpressTrackList(@RequestParam(\"id\") Long id) {\n        return success(TradeOrderConvert.INSTANCE.convertList02(\n                tradeOrderQueryService.getExpressTrackList(id, getLoginUserId())));\n    }\n\n    @GetMapping(\"/page\")\n    @Operation(summary = \"获得交易订单分页\")\n    public CommonResult<PageResult<AppTradeOrderPageItemRespVO>> getOrderPage(AppTradeOrderPageReqVO reqVO) {\n        // 查询订单\n        PageResult<TradeOrderDO> pageResult = tradeOrderQueryService.getOrderPage(getLoginUserId(), reqVO);\n        // 查询订单项\n        List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(\n                convertSet(pageResult.getList(), TradeOrderDO::getId));\n        // 最终组合\n        return success(TradeOrderConvert.INSTANCE.convertPage02(pageResult, orderItems));\n    }\n\n    @GetMapping(\"/get-count\")\n    @Operation(summary = \"获得交易订单数量\")\n    public CommonResult<Map<String, Long>> getOrderCount() {\n        Map<String, Long> orderCount = Maps.newLinkedHashMapWithExpectedSize(5);\n        // 全部\n        orderCount.put(\"allCount\", tradeOrderQueryService.getOrderCount(getLoginUserId(), null, null));\n        // 待付款（未支付）\n        orderCount.put(\"unpaidCount\", tradeOrderQueryService.getOrderCount(getLoginUserId(),\n                TradeOrderStatusEnum.UNPAID.getStatus(), null));\n        // 待发货\n        orderCount.put(\"undeliveredCount\", tradeOrderQueryService.getOrderCount(getLoginUserId(),\n                TradeOrderStatusEnum.UNDELIVERED.getStatus(), null));\n        // 待收货\n        orderCount.put(\"deliveredCount\", tradeOrderQueryService.getOrderCount(getLoginUserId(),\n                TradeOrderStatusEnum.DELIVERED.getStatus(), null));\n        // 待评价\n        orderCount.put(\"uncommentedCount\", tradeOrderQueryService.getOrderCount(getLoginUserId(),\n                TradeOrderStatusEnum.COMPLETED.getStatus(), false));\n        // 售后数量\n        orderCount.put(\"afterSaleCount\", afterSaleService.getApplyingAfterSaleCount(getLoginUserId()));\n        return success(orderCount);\n    }\n\n    @PutMapping(\"/receive\")\n    @Operation(summary = \"确认交易订单收货\")\n    @Parameter(name = \"id\", description = \"交易订单编号\")\n    public CommonResult<Boolean> receiveOrder(@RequestParam(\"id\") Long id) {\n        tradeOrderUpdateService.receiveOrderByMember(getLoginUserId(), id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/cancel\")\n    @Operation(summary = \"取消交易订单\")\n    @Parameter(name = \"id\", description = \"交易订单编号\")\n    public CommonResult<Boolean> cancelOrder(@RequestParam(\"id\") Long id) {\n        tradeOrderUpdateService.cancelOrderByMember(getLoginUserId(), id);\n        return success(true);\n    }\n\n    @DeleteMapping(\"/delete\")\n    @Operation(summary = \"删除交易订单\")\n    @Parameter(name = \"id\", description = \"交易订单编号\")\n    public CommonResult<Boolean> deleteOrder(@RequestParam(\"id\") Long id) {\n        tradeOrderUpdateService.deleteOrder(getLoginUserId(), id);\n        return success(true);\n    }\n\n    // ========== 订单项 ==========\n\n    @GetMapping(\"/item/get\")\n    @Operation(summary = \"获得交易订单项\")\n    @Parameter(name = \"id\", description = \"交易订单项编号\")\n    public CommonResult<AppTradeOrderItemRespVO> getOrderItem(@RequestParam(\"id\") Long id) {\n        TradeOrderItemDO item = tradeOrderQueryService.getOrderItem(getLoginUserId(), id);\n        return success(TradeOrderConvert.INSTANCE.convert03(item));\n    }\n\n    @PostMapping(\"/item/create-comment\")\n    @Operation(summary = \"创建交易订单项的评价\")\n    public CommonResult<Long> createOrderItemComment(@RequestBody AppTradeOrderItemCommentCreateReqVO createReqVO) {\n        return success(tradeOrderUpdateService.createOrderItemCommentByMember(getLoginUserId(), createReqVO));\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppOrderExpressTrackRespDTO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n/**\n * 快递查询的轨迹 Resp DTO\n *\n * @author jason\n */\n@Schema(description = \"用户 App - 快递查询的轨迹 Response VO\")\n@Data\npublic class AppOrderExpressTrackRespDTO {\n\n    @Schema(description = \"发生时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime time;\n\n    @Schema(description = \"快递状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"已签收\")\n    private String content;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.AssertTrue;\n\n@Schema(description = \"用户 App - 交易订单创建 Request VO\")\n@Data\npublic class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO {\n\n    @Schema(description = \"备注\", example = \"这个是我的订单哟\")\n    private String remark;\n\n    @AssertTrue(message = \"配送方式不能为空\")\n    @JsonIgnore\n    public boolean isDeliveryTypeNotNull() {\n        return getDeliveryType() != null;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"用户 App - 交易订单创建 Response VO\")\n@Data\npublic class AppTradeOrderCreateRespVO {\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"支付订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long payOrderId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderDetailRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 订单交易的明细 Response VO\")\n@Data\npublic class AppTradeOrderDetailRespVO {\n\n    // ========== 订单基本信息 ==========\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"订单流水号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1146347329394184195\")\n    private String no;\n\n    @Schema(description = \"订单类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer type;\n\n    @Schema(description = \"下单时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    @Schema(description = \"用户备注\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"你猜\")\n    private String userRemark;\n\n    @Schema(description = \"订单状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"购买的商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer productCount;\n\n    @Schema(description = \"订单完成时间\")\n    private LocalDateTime finishTime;\n\n    @Schema(description = \"订单取消时间\")\n    private LocalDateTime cancelTime;\n\n    @Schema(description = \"是否评价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean commentStatus;\n\n    // ========== 价格 + 支付基本信息 ==========\n\n    @Schema(description = \"是否已支付\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean payStatus;\n\n    @Schema(description = \"支付订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long payOrderId;\n\n    @Schema(description = \"付款时间\")\n    private LocalDateTime payTime;\n\n    @Schema(description = \"付款超时时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime payExpireTime;\n\n    @Schema(description = \"支付渠道\", example = \"wx_lite_pay\")\n    private String payChannelCode;\n    @Schema(description = \"支付渠道名\", example = \"微信小程序支付\")\n    private String payChannelName;\n\n    @Schema(description = \"商品原价（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    private Integer totalPrice;\n\n    @Schema(description = \"订单优惠（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer discountPrice;\n\n    @Schema(description = \"运费金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer deliveryPrice;\n\n    @Schema(description = \"订单调价（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer adjustPrice;\n\n    @Schema(description = \"应付金额（总）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    private Integer payPrice;\n\n    // ========== 收件 + 物流基本信息 ==========\n\n    @Schema(description = \"配送方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer deliveryType;\n\n    @Schema(description = \"发货物流公司编号\", example = \"10\")\n    private Long logisticsId;\n\n    @Schema(description = \"发货物流名称\", example = \"顺丰快递\")\n    private String logisticsName;\n\n    @Schema(description = \"发货物流单号\", example = \"1024\")\n    private String logisticsNo;\n\n    @Schema(description = \"发货时间\")\n    private LocalDateTime deliveryTime;\n\n    @Schema(description = \"收货时间\")\n    private LocalDateTime receiveTime;\n\n    @Schema(description = \"收件人名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"张三\")\n    private String receiverName;\n\n    @Schema(description = \"收件人手机\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"13800138000\")\n    private String receiverMobile;\n\n    @Schema(description = \"收件人地区编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"110000\")\n    private Integer receiverAreaId;\n\n    @Schema(description = \"收件人地区名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"上海 上海市 普陀区\")\n    private String receiverAreaName;\n\n    @Schema(description = \"收件人详细地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"中关村大街 1 号\")\n    private String receiverDetailAddress;\n\n    @Schema(description = \"自提门店编号\", example = \"1088\")\n    private Long pickUpStoreId;\n\n    @Schema(description = \"自提核销码\", example = \"40964096\")\n    private String pickUpVerifyCode;\n\n    // ========== 售后基本信息 ==========\n\n    @Schema(description = \"售后状态\", example = \"0\")\n    private Integer refundStatus;\n\n    @Schema(description = \"退款金额，单位：分\", example = \"100\")\n    private Integer refundPrice;\n\n    // ========== 营销基本信息 ==========\n\n    @Schema(description = \"优惠劵编号\", example = \"1024\")\n    private Long couponId;\n\n    @Schema(description = \"优惠劵减免金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer couponPrice;\n\n    @Schema(description = \"积分抵扣的金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer pointPrice;\n\n    @Schema(description = \"VIP 减免金额\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"888\")\n    private Integer vipPrice;\n\n    @Schema(description = \"拼团记录编号\", example = \"100\")\n    private Long combinationRecordId;\n\n    /**\n     * 订单项数组\n     */\n    private List<AppTradeOrderItemRespVO> items;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderPageItemRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 订单交易的分页项 Response VO\")\n@Data\npublic class AppTradeOrderPageItemRespVO {\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long id;\n\n    @Schema(description = \"订单流水号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1146347329394184195\")\n    private String no;\n\n    @Schema(description = \"订单类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"0\")\n    private Integer type;\n\n    @Schema(description = \"订单状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer status;\n\n    @Schema(description = \"购买的商品数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer productCount;\n\n    @Schema(description = \"是否评价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean commentStatus;\n\n    @Schema(description = \"创建时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private LocalDateTime createTime;\n\n    // ========== 价格 + 支付基本信息 ==========\n\n    @Schema(description = \"支付订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long payOrderId;\n\n    @Schema(description = \"应付金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1000\")\n    private Integer payPrice;\n\n    // ========== 收件 + 物流基本信息 ==========\n\n    @Schema(description = \"配送方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer deliveryType;\n\n    /**\n     * 订单项数组\n     */\n    private List<AppTradeOrderItemRespVO> items;\n\n    // ========== 营销基本信息 ==========\n\n    @Schema(description = \"拼团记录编号\", example = \"100\")\n    private Long combinationRecordId;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderPageReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageParam;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n@Schema(description = \"交易订单分页 Request VO\")\n@Data\npublic class AppTradeOrderPageReqVO extends PageParam {\n\n    @Schema(description = \"订单状态\", example = \"1\")\n    @InEnum(value = TradeOrderStatusEnum.class, message = \"订单状态必须是 {value}\")\n    private Integer status;\n\n    @Schema(description = \"是否评价\", example = \"true\")\n    private Boolean commentStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo;\n\nimport cn.hutool.core.util.ObjUtil;\nimport cn.iocoder.yudao.framework.common.validation.InEnum;\nimport cn.iocoder.yudao.framework.common.validation.Mobile;\nimport cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.AssertTrue;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 交易订单结算 Request VO\")\n@Data\n@Valid\npublic class AppTradeOrderSettlementReqVO {\n\n    @Schema(description = \"商品项数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    @NotEmpty(message = \"商品不能为空\")\n    private List<Item> items;\n\n    @Schema(description = \"优惠劵编号\", example = \"1024\")\n    private Long couponId;\n\n    @Schema(description = \"是否使用积分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否使用积分不能为空\")\n    private Boolean pointStatus;\n\n    // ========== 配送相关相关字段 ==========\n    @Schema(description = \"配送方式\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    @InEnum(value = DeliveryTypeEnum.class, message = \"配送方式不正确\")\n    private Integer deliveryType;\n\n    @Schema(description = \"收件地址编号\", example = \"1\")\n    private Long addressId;\n\n    @Schema(description = \"自提门店编号\", example = \"1088\")\n    private Long pickUpStoreId;\n    @Schema(description = \"收件人名称\", example = \"芋艿\") // 选择门店自提时，该字段为联系人名\n    private String receiverName;\n    @Schema(description = \"收件人手机\", example = \"15601691300\") // 选择门店自提时，该字段为联系人手机\n    @Mobile(message = \"收件人手机格式不正确\")\n    private String receiverMobile;\n\n    // ========== 秒杀活动相关字段 ==========\n    @Schema(description = \"秒杀活动编号\", example = \"1024\")\n    private Long seckillActivityId;\n\n    // ========== 拼团活动相关字段 ==========\n    @Schema(description = \"拼团活动编号\", example = \"1024\")\n    private Long combinationActivityId;\n\n    @Schema(description = \"拼团团长编号\", example = \"2048\")\n    private Long combinationHeadId;\n\n    // ========== 砍价活动相关字段 ==========\n    @Schema(description = \"砍价记录编号\", example = \"123\")\n    private Long bargainRecordId;\n\n    // ========== 积分商城活动相关字段 ==========\n    @Schema(description = \"积分商城活动编号\", example = \"123\")\n    private Long pointActivityId;\n\n    @AssertTrue(message = \"活动商品每次只能购买一种规格\")\n    @JsonIgnore\n    public boolean isValidActivityItems() {\n        // 校验是否是活动订单\n        if (ObjUtil.isAllEmpty(seckillActivityId, combinationActivityId, combinationHeadId, bargainRecordId)) {\n            return true;\n        }\n        // 校验订单项是否超出\n        return items.size() == 1;\n    }\n\n    @Data\n    @Schema(description = \"用户 App - 商品项\")\n    @Valid\n    public static class Item {\n\n        @Schema(description = \"商品 SKU 编号\", example = \"2048\")\n        @NotNull(message = \"商品 SKU 编号不能为空\")\n        private Long skuId;\n\n        @Schema(description = \"购买数量\", example = \"1\")\n        @Min(value = 1, message = \"购买数量最小值为 {value}\")\n        private Integer count;\n\n        @Schema(description = \"购物车项的编号\", example = \"1024\")\n        private Long cartId;\n\n        @AssertTrue(message = \"商品不正确\")\n        @JsonIgnore\n        public boolean isValid() {\n            // 组合一：skuId + count 使用商品 SKU\n            if (skuId != null && count != null) {\n                return true;\n            }\n            // 组合二：cartId 使用购物车项\n            return cartId != null;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo;\n\nimport cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;\nimport cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 交易订单结算信息 Response VO\")\n@Data\npublic class AppTradeOrderSettlementRespVO {\n\n    @Schema(description = \"交易类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\") // 对应 TradeOrderTypeEnum 枚举\n    private Integer type;\n\n    @Schema(description = \"购物项数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Item> items;\n\n    @Schema(description = \"优惠劵数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private List<Coupon> coupons; // 可用 + 不可用\n\n    @Schema(description = \"费用\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Price price;\n\n    @Schema(description = \"收件地址\", requiredMode = Schema.RequiredMode.REQUIRED)\n    private Address address;\n\n    @Schema(description = \"已使用的积分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer usePoint;\n\n    @Schema(description = \"总积分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"10\")\n    private Integer totalPoint;\n\n    /**\n     * 营销活动数组\n     *\n     * 只对应 {@link TradePriceCalculateRespBO.Price#items} 商品匹配的活动\n     */\n    private List<TradePriceCalculateRespBO.Promotion> promotions;\n\n    @Schema(description = \"购物项\")\n    @Data\n    public static class Item {\n\n        // ========== SPU 信息 ==========\n\n        @Schema(description = \"品类编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n        private Long categoryId;\n        @Schema(description = \"SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2048\")\n        private Long spuId;\n        @Schema(description = \"SPU 名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"Apple iPhone 12\")\n        private String spuName;\n\n        // ========== SKU 信息 ==========\n\n        @Schema(description = \"SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n        private Integer skuId;\n        @Schema(description = \"价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer price;\n        @Schema(description = \"图片地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n        private String picUrl;\n\n        @Schema(description = \"属性数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private List<AppProductPropertyValueDetailRespVO> properties;\n\n        // ========== 购物车信息 ==========\n\n        @Schema(description = \"购物车编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Long cartId;\n\n        @Schema(description = \"购买数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer count;\n\n    }\n\n    @Schema(description = \"费用（合计）\")\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Price {\n\n        @Schema(description = \"商品原价（总），单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"500\")\n        private Integer totalPrice;\n\n        @Schema(description = \"订单优惠（总），单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"66\")\n        private Integer discountPrice;\n\n        @Schema(description = \"运费金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n        private Integer deliveryPrice;\n\n        @Schema(description = \"优惠劵减免金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer couponPrice;\n\n        @Schema(description = \"积分抵扣的金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n        private Integer pointPrice;\n\n        @Schema(description = \"VIP 减免金额，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"30\")\n        private Integer vipPrice;\n\n        @Schema(description = \"实际支付金额（总），单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"450\")\n        private Integer payPrice;\n\n    }\n\n    @Schema(description = \"地址信息\")\n    @Data\n    public static class Address {\n\n        @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Long id;\n\n        @Schema(description = \"收件人名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"小王\")\n        private String name;\n\n        @Schema(description = \"手机号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"15601691300\")\n        private String mobile;\n\n        @Schema(description = \"地区编号\", requiredMode = Schema.RequiredMode.REQUIRED)\n        private Long areaId;\n        @Schema(description = \"地区名字\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"上海上海市普陀区\")\n        private String areaName;\n\n        @Schema(description = \"详细地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"望京悠乐汇 A 座\")\n        private String detailAddress;\n\n        @Schema(description = \"是否默认收件地址\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n        private Boolean defaultStatus;\n\n    }\n\n    @Schema(description = \"优惠劵信息\")\n    @Data\n    public static class Coupon {\n\n        @Schema(description = \"优惠劵编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Long id;\n\n        @Schema(description = \"优惠劵名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"春节送送送\")\n        private String name;\n\n        @Schema(description = \"是否设置满多少金额可用\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\") // 单位：分；0 - 不限制\n        private Integer usePrice;\n\n        @Schema(description = \"固定日期 - 生效开始时间\")\n        private LocalDateTime validStartTime;\n\n        @Schema(description = \"固定日期 - 生效结束时间\")\n        private LocalDateTime validEndTime;\n\n        @Schema(description = \"优惠类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer discountType;\n\n        @Schema(description = \"折扣百分比\", example = \"80\") //  例如说，80% 为 80\n        private Integer discountPercent;\n\n        @Schema(description = \"优惠金额\", example = \"10\")\n        private Integer discountPrice;\n\n        @Schema(description = \"折扣上限\", example = \"100\") // 单位：分，仅在 discountType 为 PERCENT 使用\n        private Integer discountLimitPrice;\n\n        @Schema(description = \"是否可用\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n        private Boolean match;\n\n        @Schema(description = \"不可用原因\", example = \"优惠劵已过期\")\n        private String mismatchReason;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\n\n@Schema(description = \"用户 App - 商品结算信息 Response VO\")\n@Data\npublic class AppTradeProductSettlementRespVO {\n\n    @Schema(description = \"SPU 商品编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long spuId;\n\n    @Schema(description = \"SKU 价格信息数组\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private List<Sku> skus;\n\n    @Schema(description = \"满减送活动信息\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private RewardActivity rewardActivity;\n\n    @Schema(description = \"SKU 价格信息\")\n    @Data\n    public static class Sku implements Serializable {\n\n        @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Long id;\n\n        @Schema(description = \"优惠后价格，单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer promotionPrice;\n\n        @Schema(description = \"营销类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"4\")\n        private Integer promotionType; // 对应 PromotionTypeEnum 枚举，目前只有 4 和 6 两种\n\n        @Schema(description = \"营销编号\", requiredMode = Schema.RequiredMode.REQUIRED)\n        private Long promotionId; // 目前只有限时折扣活动的编号\n\n        @Schema(description = \"活动结束时间\", requiredMode = Schema.RequiredMode.REQUIRED)\n        private LocalDateTime promotionEndTime;\n\n    }\n\n    @Schema(description = \"满减送活动信息\")\n    @Data\n    public static class RewardActivity {\n\n        @Schema(description = \"满减活动编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Long id;\n\n        @Schema(description = \"条件类型\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n        private Integer conditionType;\n\n        @Schema(description = \"优惠规则的数组\", requiredMode = Schema.RequiredMode.REQUIRED)\n        private List<RewardActivityRule> rules;\n\n    }\n\n    @Schema(description = \"优惠规则\")\n    @Data\n    public static class RewardActivityRule {\n\n        @Schema(description = \"优惠门槛\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\") // 1. 满 N 元，单位：分; 2. 满 N 件\n        private Integer limit;\n\n        @Schema(description = \"优惠价格\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer discountPrice;\n\n        @Schema(description = \"是否包邮\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n        private Boolean freeDelivery;\n\n        @Schema(description = \"赠送的积分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n        private Integer point;\n\n        @Schema(description = \"赠送的优惠劵编号的数组\")\n        private Map<Long, Integer> giveCouponTemplateCounts;\n\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/item/AppTradeOrderItemCommentCreateReqVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo.item;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.util.List;\n\n@Schema(description = \"用户 App - 商品评价创建 Request VO\")\n@Data\npublic class AppTradeOrderItemCommentCreateReqVO {\n\n    @Schema(description = \"是否匿名\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    @NotNull(message = \"是否匿名不能为空\")\n    private Boolean anonymous;\n\n    @Schema(description = \"交易订单项编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"2312312\")\n    @NotNull(message = \"交易订单项编号不能为空\")\n    private Long orderItemId;\n\n    @Schema(description = \"描述星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"描述星级 1-5 分不能为空\")\n    private Integer descriptionScores;\n\n    @Schema(description = \"服务星级 1-5 分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"5\")\n    @NotNull(message = \"服务星级 1-5 分不能为空\")\n    private Integer benefitScores;\n\n    @Schema(description = \"评论内容\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"穿身上很漂亮诶(*^▽^*)\")\n    @NotNull(message = \"评论内容不能为空\")\n    private String content;\n\n    @Schema(description = \"评论图片地址数组，以逗号分隔最多上传 9 张\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"[https://www.iocoder.cn/xx.png]\")\n    @Size(max = 9, message = \"评论图片地址数组长度不能超过 9 张\")\n    private List<String> picUrls;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/item/AppTradeOrderItemRespVO.java",
    "content": "package cn.iocoder.yudao.module.trade.controller.app.order.vo.item;\n\nimport cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Schema(description = \"用户 App - 订单交易项 Response VO\")\n@Data\npublic class AppTradeOrderItemRespVO {\n\n    @Schema(description = \"编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long id;\n\n    @Schema(description = \"订单编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1024\")\n    private Long orderId;\n\n    @Schema(description = \"商品 SPU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long spuId;\n    @Schema(description = \"商品 SPU 名称\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"芋道源码\")\n    private String spuName;\n\n    @Schema(description = \"商品 SKU 编号\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Long skuId;\n\n    /**\n     * 属性数组\n     */\n    private List<AppProductPropertyValueDetailRespVO> properties;\n\n    @Schema(description = \"商品图片\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"https://www.iocoder.cn/1.png\")\n    private String picUrl;\n\n    @Schema(description = \"购买数量\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer count;\n\n    @Schema(description = \"是否评价\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"true\")\n    private Boolean commentStatus;\n\n    // ========== 价格 + 支付基本信息 ==========\n\n    @Schema(description = \"商品原价（单）\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"100\")\n    private Integer price;\n\n    @Schema(description = \"应付金额（总），单位：分\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"50\")\n    private Integer payPrice;\n\n    // ========== 营销基本信息 ==========\n\n    // TODO 芋艿：在捉摸一下\n\n    // ========== 售后基本信息 ==========\n\n    @Schema(description = \"售后编号\", example = \"1024\")\n    private Long afterSaleId;\n\n    @Schema(description = \"售后状态\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"1\")\n    private Integer afterSaleStatus;\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/package-info.java",
    "content": "/**\n * 提供 RESTful API 给前端：\n * 1. admin 包：提供给管理后台 yudao-ui-admin 前端项目\n * 2. app 包：提供给用户 APP yudao-ui-app 前端项目，它的 Controller 和 VO 都要添加 App 前缀，用于和管理后台进行区分\n */\npackage cn.iocoder.yudao.module.trade.controller;\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/AfterSaleConvert.java",
    "content": "package cn.iocoder.yudao.module.trade.convert.aftersale;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;\nimport cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDetailRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRespPageItemVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log.AfterSaleLogRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderBaseVO;\nimport cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;\nimport cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\n\n@Mapper\npublic interface AfterSaleConvert {\n\n    AfterSaleConvert INSTANCE = Mappers.getMapper(AfterSaleConvert.class);\n\n    @Mappings({\n            @Mapping(target = \"id\", ignore = true),\n            @Mapping(target = \"createTime\", ignore = true),\n            @Mapping(target = \"updateTime\", ignore = true),\n            @Mapping(target = \"creator\", ignore = true),\n            @Mapping(target = \"updater\", ignore = true),\n    })\n    AfterSaleDO convert(AppAfterSaleCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItem);\n\n    @Mappings({\n            @Mapping(source = \"afterSale.orderId\", target = \"merchantOrderId\"),\n            @Mapping(source = \"afterSale.id\", target = \"merchantRefundId\"),\n            @Mapping(source = \"afterSale.applyReason\", target = \"reason\"),\n            @Mapping(source = \"afterSale.refundPrice\", target = \"price\"),\n            @Mapping(source = \"orderProperties.payAppKey\", target = \"appKey\"),\n    })\n    PayRefundCreateReqDTO convert(String userIp, AfterSaleDO afterSale, TradeOrderProperties orderProperties);\n\n    MemberUserRespVO convert(MemberUserRespDTO bean);\n\n    PageResult<AfterSaleRespPageItemVO> convertPage(PageResult<AfterSaleDO> page);\n\n    default PageResult<AfterSaleRespPageItemVO> convertPage(PageResult<AfterSaleDO> pageResult,\n                                                            Map<Long, MemberUserRespDTO> memberUsers) {\n        PageResult<AfterSaleRespPageItemVO> voPageResult = convertPage(pageResult);\n        // 处理会员\n        voPageResult.getList().forEach(afterSale -> afterSale.setUser(\n                convert(memberUsers.get(afterSale.getUserId()))));\n        return voPageResult;\n    }\n\n    ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);\n\n    default AfterSaleDetailRespVO convert(AfterSaleDO afterSale, TradeOrderDO order, TradeOrderItemDO orderItem,\n                                          MemberUserRespDTO user, List<AfterSaleLogDO> logs) {\n        AfterSaleDetailRespVO respVO = convert02(afterSale);\n        // 处理用户信息\n        respVO.setUser(convert(user));\n        // 处理订单信息\n        respVO.setOrder(convert(order));\n        respVO.setOrderItem(convert02(orderItem));\n        // 处理售后日志\n        respVO.setLogs(convertList1(logs));\n        return respVO;\n    }\n\n    List<AfterSaleLogRespVO> convertList1(List<AfterSaleLogDO> list);\n    AfterSaleDetailRespVO convert02(AfterSaleDO bean);\n    AfterSaleDetailRespVO.OrderItem convert02(TradeOrderItemDO bean);\n    TradeOrderBaseVO convert(TradeOrderDO bean);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/brokerage/BrokerageRecordConvert.java",
    "content": "package cn.iocoder.yudao.module.trade.convert.brokerage;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.number.MoneyUtils;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record.BrokerageRecordRespVO;\nimport cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByPriceRespVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;\nimport cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.MappingTarget;\nimport org.mapstruct.factory.Mappers;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * 佣金记录 Convert\n *\n * @author owen\n */\n@Mapper\npublic interface BrokerageRecordConvert {\n\n    BrokerageRecordConvert INSTANCE = Mappers.getMapper(BrokerageRecordConvert.class);\n\n    BrokerageRecordRespVO convert(BrokerageRecordDO bean);\n\n    List<BrokerageRecordRespVO> convertList(List<BrokerageRecordDO> list);\n\n    PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> page);\n\n    default BrokerageRecordDO convert(BrokerageUserDO user, BrokerageRecordBizTypeEnum bizType, String bizId,\n                                      Integer brokerageFrozenDays, int brokeragePrice, LocalDateTime unfreezeTime,\n                                      String title, Long sourceUserId, Integer sourceUserLevel) {\n        brokerageFrozenDays = ObjectUtil.defaultIfNull(brokerageFrozenDays, 0);\n        // 不冻结时，佣金直接就是结算状态\n        Integer status = brokerageFrozenDays > 0\n                ? BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus()\n                : BrokerageRecordStatusEnum.SETTLEMENT.getStatus();\n        return new BrokerageRecordDO().setUserId(user.getId())\n                .setBizType(bizType.getType()).setBizId(bizId)\n                .setPrice(brokeragePrice).setTotalPrice(user.getBrokeragePrice())\n                .setTitle(title)\n                .setDescription(StrUtil.format(bizType.getDescription(), MoneyUtils.fenToYuanStr(Math.abs(brokeragePrice))))\n                .setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime)\n                .setSourceUserLevel(sourceUserLevel).setSourceUserId(sourceUserId);\n    }\n\n    default PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> pageResult, Map<Long, MemberUserRespDTO> userMap) {\n        PageResult<BrokerageRecordRespVO> result = convertPage(pageResult);\n        for (BrokerageRecordRespVO respVO : result.getList()) {\n            Optional.ofNullable(userMap.get(respVO.getUserId())).ifPresent(user ->\n                    respVO.setUserNickname(user.getNickname()).setUserAvatar(user.getAvatar()));\n            Optional.ofNullable(userMap.get(respVO.getSourceUserId())).ifPresent(user ->\n                    respVO.setSourceUserNickname(user.getNickname()).setSourceUserAvatar(user.getAvatar()));\n        }\n        return result;\n    }\n\n    default PageResult<AppBrokerageUserRankByPriceRespVO> convertPage03(PageResult<AppBrokerageUserRankByPriceRespVO> pageResult, Map<Long, MemberUserRespDTO> userMap) {\n        for (AppBrokerageUserRankByPriceRespVO vo : pageResult.getList()) {\n            copyTo(userMap.get(vo.getId()), vo);\n        }\n        return pageResult;\n    }\n\n    void copyTo(MemberUserRespDTO from, @MappingTarget AppBrokerageUserRankByPriceRespVO to);\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/brokerage/BrokerageWithdrawConvert.java",
    "content": "package cn.iocoder.yudao.module.trade.convert.brokerage;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRespVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * 佣金提现 Convert\n *\n * @author 芋道源码\n */\n@Mapper\npublic interface BrokerageWithdrawConvert {\n\n    BrokerageWithdrawConvert INSTANCE = Mappers.getMapper(BrokerageWithdrawConvert.class);\n\n    BrokerageWithdrawRespVO convert(BrokerageWithdrawDO bean);\n\n    List<BrokerageWithdrawRespVO> convertList(List<BrokerageWithdrawDO> list);\n\n    PageResult<BrokerageWithdrawRespVO> convertPage(PageResult<BrokerageWithdrawDO> page);\n\n    default PageResult<BrokerageWithdrawRespVO> convertPage(PageResult<BrokerageWithdrawDO> pageResult, Map<Long, MemberUserRespDTO> userMap) {\n        PageResult<BrokerageWithdrawRespVO> result = convertPage(pageResult);\n        for (BrokerageWithdrawRespVO vo : result.getList()) {\n            vo.setUserNickname(Optional.ofNullable(userMap.get(vo.getUserId())).map(MemberUserRespDTO::getNickname).orElse(null));\n        }\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java",
    "content": "package cn.iocoder.yudao.module.trade.convert.cart;\n\nimport cn.iocoder.yudao.framework.common.util.object.BeanUtils;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;\nimport cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;\nimport cn.iocoder.yudao.module.trade.controller.app.base.spu.AppProductSpuBaseRespVO;\nimport cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppCartListRespVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\n\n@Mapper\npublic interface TradeCartConvert {\n\n    TradeCartConvert INSTANCE = Mappers.getMapper(TradeCartConvert.class);\n\n    default AppCartListRespVO convertList(List<CartDO> carts,\n                                          List<ProductSpuRespDTO> spus, List<ProductSkuRespDTO> skus) {\n        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spus, ProductSpuRespDTO::getId);\n        Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);\n        // 遍历，开始转换\n        List<AppCartListRespVO.Cart> validList = new ArrayList<>(carts.size());\n        List<AppCartListRespVO.Cart> invalidList = new ArrayList<>();\n        carts.forEach(cart -> {\n            AppCartListRespVO.Cart cartVO = new AppCartListRespVO.Cart();\n            cartVO.setId(cart.getId()).setCount(cart.getCount()).setSelected(cart.getSelected());\n            ProductSpuRespDTO spu = spuMap.get(cart.getSpuId());\n            ProductSkuRespDTO sku = skuMap.get(cart.getSkuId());\n            cartVO.setSpu(BeanUtils.toBean(spu, AppProductSpuBaseRespVO.class))\n                    .setSku(BeanUtils.toBean(sku, AppProductSkuBaseRespVO.class));\n            // 如果 SPU 不存在，或者下架，或者库存不足，说明是无效的\n            if (spu == null\n                || !ProductSpuStatusEnum.isEnable(spu.getStatus())\n                || spu.getStock() <= 0) {\n                invalidList.add(cartVO);\n            } else {\n                validList.add(cartVO);\n            }\n        });\n        return new AppCartListRespVO().setValidList(validList).setInvalidList(invalidList);\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java",
    "content": "package cn.iocoder.yudao.module.trade.convert.delivery;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.*;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;\nimport cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;\nimport com.google.common.collect.Maps;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;\n\n@Mapper\npublic interface DeliveryExpressTemplateConvert {\n\n    DeliveryExpressTemplateConvert INSTANCE = Mappers.getMapper(DeliveryExpressTemplateConvert.class);\n\n    // ========== Template ==========\n\n    DeliveryExpressTemplateDO convert(DeliveryExpressTemplateCreateReqVO bean);\n\n    DeliveryExpressTemplateDO convert(DeliveryExpressTemplateUpdateReqVO bean);\n\n    DeliveryExpressTemplateRespVO convert(DeliveryExpressTemplateDO bean);\n\n    DeliveryExpressTemplateDetailRespVO convert2(DeliveryExpressTemplateDO bean);\n\n    List<DeliveryExpressTemplateRespVO> convertList(List<DeliveryExpressTemplateDO> list);\n\n    List<DeliveryExpressTemplateSimpleRespVO> convertList1(List<DeliveryExpressTemplateDO> list);\n\n    PageResult<DeliveryExpressTemplateRespVO> convertPage(PageResult<DeliveryExpressTemplateDO> page);\n\n    default DeliveryExpressTemplateDetailRespVO convert(DeliveryExpressTemplateDO bean,\n                                                        List<DeliveryExpressTemplateChargeDO> chargeList,\n                                                        List<DeliveryExpressTemplateFreeDO> freeList) {\n        DeliveryExpressTemplateDetailRespVO respVO = convert2(bean);\n        respVO.setCharges(convertTemplateChargeList(chargeList));\n        respVO.setFrees(convertTemplateFreeList(freeList));\n        return respVO;\n    }\n\n    // ========== Template Charge ==========\n\n    DeliveryExpressTemplateChargeDO convertTemplateCharge(Long templateId, Integer chargeMode, DeliveryExpressTemplateChargeBaseVO vo);\n\n    DeliveryExpressTemplateRespBO.Charge convertTemplateCharge(DeliveryExpressTemplateChargeDO bean);\n\n    default List<DeliveryExpressTemplateChargeDO> convertTemplateChargeList(Long templateId, Integer chargeMode, List<DeliveryExpressTemplateChargeBaseVO> list) {\n        return CollectionUtils.convertList(list, vo -> convertTemplateCharge(templateId, chargeMode, vo));\n    }\n\n    // ========== Template Free ==========\n\n    DeliveryExpressTemplateFreeDO convertTemplateFree(Long templateId, DeliveryExpressTemplateFreeBaseVO vo);\n\n    DeliveryExpressTemplateRespBO.Free convertTemplateFree(DeliveryExpressTemplateFreeDO bean);\n\n    List<DeliveryExpressTemplateChargeBaseVO> convertTemplateChargeList(List<DeliveryExpressTemplateChargeDO> list);\n\n    List<DeliveryExpressTemplateFreeBaseVO> convertTemplateFreeList(List<DeliveryExpressTemplateFreeDO> list);\n\n    default List<DeliveryExpressTemplateFreeDO> convertTemplateFreeList(Long templateId, List<DeliveryExpressTemplateFreeBaseVO> list) {\n        return CollectionUtils.convertList(list, vo -> convertTemplateFree(templateId, vo));\n    }\n\n    default Map<Long, DeliveryExpressTemplateRespBO> convertMap(Integer areaId, List<DeliveryExpressTemplateDO> templateList,\n                                                                List<DeliveryExpressTemplateChargeDO> chargeList,\n                                                                List<DeliveryExpressTemplateFreeDO> freeList) {\n        Map<Long, List<DeliveryExpressTemplateChargeDO>> templateIdChargeMap = convertMultiMap(chargeList,\n                DeliveryExpressTemplateChargeDO::getTemplateId);\n        Map<Long, List<DeliveryExpressTemplateFreeDO>> templateIdFreeMap = convertMultiMap(freeList,\n                DeliveryExpressTemplateFreeDO::getTemplateId);\n        // 组合运费模板配置 RespBO\n        Map<Long, DeliveryExpressTemplateRespBO> result = Maps.newHashMapWithExpectedSize(templateList.size());\n        templateList.forEach(template -> {\n            DeliveryExpressTemplateRespBO bo = new DeliveryExpressTemplateRespBO()\n                    .setChargeMode(template.getChargeMode())\n                    .setCharge(convertTemplateCharge(findFirst(templateIdChargeMap.get(template.getId()), charge -> charge.getAreaIds().contains(areaId))))\n                    .setFree(convertTemplateFree(findFirst(templateIdFreeMap.get(template.getId()), free -> free.getAreaIds().contains(areaId))));\n            if (bo.getCharge() != null || bo.getFree() != null) {\n                result.put(template.getId(), bo);\n            }\n        });\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryPickUpStoreConvert.java",
    "content": "package cn.iocoder.yudao.module.trade.convert.delivery;\n\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.number.NumberUtils;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreCreateReqVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreSimpleRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreUpdateReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.delivery.vo.pickup.AppDeliveryPickUpStoreRespVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Named;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n@Mapper\npublic interface DeliveryPickUpStoreConvert {\n\n    DeliveryPickUpStoreConvert INSTANCE = Mappers.getMapper(DeliveryPickUpStoreConvert.class);\n\n    DeliveryPickUpStoreDO convert(DeliveryPickUpStoreCreateReqVO bean);\n\n    DeliveryPickUpStoreDO convert(DeliveryPickUpStoreUpdateReqVO bean);\n\n    List<DeliveryPickUpStoreRespVO> convertList(List<DeliveryPickUpStoreDO> list);\n\n    PageResult<DeliveryPickUpStoreRespVO> convertPage(PageResult<DeliveryPickUpStoreDO> page);\n\n    List<DeliveryPickUpStoreSimpleRespVO> convertList1(List<DeliveryPickUpStoreDO> list);\n    @Mapping(source = \"areaId\", target = \"areaName\", qualifiedByName = \"convertAreaIdToAreaName\")\n    DeliveryPickUpStoreSimpleRespVO convert02(DeliveryPickUpStoreDO bean);\n\n    @Named(\"convertAreaIdToAreaName\")\n    default String convertAreaIdToAreaName(Integer areaId) {\n        return AreaUtils.format(areaId);\n    }\n\n    default List<AppDeliveryPickUpStoreRespVO> convertList(List<DeliveryPickUpStoreDO> list,\n                                                           Double latitude, Double longitude) {\n        return CollectionUtils.convertList(list, store -> {\n            AppDeliveryPickUpStoreRespVO storeVO = convert03(store);\n            if (latitude != null && longitude != null) {\n                storeVO.setDistance(NumberUtils.getDistance(latitude, longitude, storeVO.getLatitude(), storeVO.getLongitude()));\n            }\n            return storeVO;\n        });\n    }\n    @Mapping(source = \"areaId\", target = \"areaName\", qualifiedByName = \"convertAreaIdToAreaName\")\n    AppDeliveryPickUpStoreRespVO convert03(DeliveryPickUpStoreDO bean);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java",
    "content": "package cn.iocoder.yudao.module.trade.convert.order;\n\nimport cn.hutool.core.util.BooleanUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.iocoder.yudao.framework.common.enums.UserTypeEnum;\nimport cn.iocoder.yudao.framework.common.pojo.PageResult;\nimport cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;\nimport cn.iocoder.yudao.framework.common.util.string.StrUtils;\nimport cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;\nimport cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;\nimport cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO;\nimport cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;\nimport cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;\nimport cn.iocoder.yudao.module.pay.enums.DictTypeConstants;\nimport cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;\nimport cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;\nimport cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;\nimport cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;\nimport cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;\nimport cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;\nimport cn.iocoder.yudao.module.trade.controller.admin.order.vo.*;\nimport cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;\nimport cn.iocoder.yudao.module.trade.controller.app.order.vo.*;\nimport cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;\nimport cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;\nimport cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO;\nimport cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;\nimport cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;\nimport cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;\nimport cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;\nimport cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;\nimport cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\nimport org.mapstruct.Named;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;\nimport static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;\nimport static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;\n\n@Mapper\npublic interface TradeOrderConvert {\n\n    TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);\n\n    @Mappings({\n            @Mapping(target = \"id\", ignore = true),\n            @Mapping(source = \"userId\", target = \"userId\"),\n            @Mapping(source = \"createReqVO.couponId\", target = \"couponId\"),\n            @Mapping(target = \"remark\", ignore = true),\n            @Mapping(source = \"createReqVO.remark\", target = \"userRemark\"),\n            @Mapping(source = \"calculateRespBO.price.totalPrice\", target = \"totalPrice\"),\n            @Mapping(source = \"calculateRespBO.price.discountPrice\", target = \"discountPrice\"),\n            @Mapping(source = \"calculateRespBO.price.deliveryPrice\", target = \"deliveryPrice\"),\n            @Mapping(source = \"calculateRespBO.price.couponPrice\", target = \"couponPrice\"),\n            @Mapping(source = \"calculateRespBO.price.pointPrice\", target = \"pointPrice\"),\n            @Mapping(source = \"calculateRespBO.price.vipPrice\", target = \"vipPrice\"),\n            @Mapping(source = \"calculateRespBO.price.payPrice\", target = \"payPrice\")\n    })\n    TradeOrderDO convert(Long userId, AppTradeOrderCreateReqVO createReqVO, TradePriceCalculateRespBO calculateRespBO);\n\n    TradeOrderRespDTO convert(TradeOrderDO orderDO);\n\n    default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) {\n        return CollectionUtils.convertList(calculateRespBO.getItems(), item -> {\n            TradeOrderItemDO orderItem = convert(item);\n            orderItem.setOrderId(tradeOrderDO.getId());\n            orderItem.setUserId(tradeOrderDO.getUserId());\n            orderItem.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());\n            orderItem.setCommentStatus(false);\n            return orderItem;\n        });\n    }\n\n    TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item);\n\n    default ProductSkuUpdateStockReqDTO convert(List<TradeOrderItemDO> list) {\n        List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->\n                new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(item.getCount()));\n        return new ProductSkuUpdateStockReqDTO(items);\n    }\n\n    default ProductSkuUpdateStockReqDTO convertNegative(List<TradeOrderItemDO> list) {\n        List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->\n                new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(-item.getCount()));\n        return new ProductSkuUpdateStockReqDTO(items);\n    }\n\n    default PayOrderCreateReqDTO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,\n                                         TradeOrderProperties orderProperties) {\n        PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO()\n                .setAppKey(orderProperties.getPayAppKey()).setUserIp(order.getUserIp())\n                .setUserId(order.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue());\n        // 商户相关字段\n        createReqDTO.setMerchantOrderId(String.valueOf(order.getId()));\n        String subject = orderItems.get(0).getSpuName();\n        subject = StrUtils.maxLength(subject, PayOrderCreateReqDTO.SUBJECT_MAX_LENGTH); // 避免超过 32 位\n        createReqDTO.setSubject(subject);\n        createReqDTO.setBody(subject); // TODO 芋艿：临时写死\n        // 订单相关字段\n        createReqDTO.setPrice(order.getPayPrice()).setExpireTime(addTime(orderProperties.getPayExpireTime()));\n        return createReqDTO;\n    }\n\n    default PageResult<TradeOrderPageItemRespVO> convertPage(PageResult<TradeOrderDO> pageResult,\n                                                             List<TradeOrderItemDO> orderItems,\n                                                             Map<Long, MemberUserRespDTO> memberUserMap) {\n        Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);\n        // 转化 List\n        List<TradeOrderPageItemRespVO> orderVOs = CollectionUtils.convertList(pageResult.getList(), order -> {\n            List<TradeOrderItemDO> xOrderItems = orderItemMap.get(order.getId());\n            TradeOrderPageItemRespVO orderVO = convert(order, xOrderItems);\n            // 处理收货地址\n            orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));\n            // 增加用户信息\n            orderVO.setUser(convertUser(memberUserMap.get(orderVO.getUserId())));\n            // 增加推广人信息\n            orderVO.setBrokerageUser(convertUser(memberUserMap.get(orderVO.getBrokerageUserId())));\n            return orderVO;\n        });\n        return new PageResult<>(orderVOs, pageResult.getTotal());\n    }\n\n    MemberUserRespVO convertUser(MemberUserRespDTO memberUserRespDTO);\n\n    TradeOrderPageItemRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> items);\n\n    ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);\n\n    default TradeOrderDetailRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,\n                                           List<TradeOrderLogDO> orderLogs,\n                                           MemberUserRespDTO user, MemberUserRespDTO brokerageUser) {\n        TradeOrderDetailRespVO orderVO = convert2(order, orderItems);\n        // 处理收货地址\n        orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));\n        // 处理用户信息\n        orderVO.setUser(convert(user));\n        orderVO.setBrokerageUser(convert(brokerageUser));\n        // 处理日志\n        orderVO.setLogs(convertList03(orderLogs));\n        return orderVO;\n    }\n    List<TradeOrderDetailRespVO.OrderLog> convertList03(List<TradeOrderLogDO> orderLogs);\n\n    TradeOrderDetailRespVO convert2(TradeOrderDO order, List<TradeOrderItemDO> items);\n\n    MemberUserRespVO convert(MemberUserRespDTO bean);\n\n    default PageResult<AppTradeOrderPageItemRespVO> convertPage02(PageResult<TradeOrderDO> pageResult,\n                                                                  List<TradeOrderItemDO> orderItems) {\n        Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);\n        // 转化 List\n        List<AppTradeOrderPageItemRespVO> orderVOs = CollectionUtils.convertList(pageResult.getList(), order -> {\n            List<TradeOrderItemDO> xOrderItems = orderItemMap.get(order.getId());\n            return convert02(order, xOrderItems);\n        });\n        return new PageResult<>(orderVOs, pageResult.getTotal());\n    }\n\n    AppTradeOrderPageItemRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> items);\n\n    AppProductPropertyValueDetailRespVO convert02(ProductPropertyValueDetailRespDTO bean);\n\n    default AppTradeOrderDetailRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> orderItems,\n                                                TradeOrderProperties tradeOrderProperties,\n                                                DeliveryExpressDO express) {\n        AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems);\n        orderVO.setPayExpireTime(order.getCreateTime().plus(tradeOrderProperties.getPayExpireTime()));\n        if (StrUtil.isNotEmpty(order.getPayChannelCode())) {\n            orderVO.setPayChannelName(DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.CHANNEL_CODE, order.getPayChannelCode()));\n        }\n        // 处理收货地址\n        orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));\n        if (express != null) {\n            orderVO.setLogisticsId(express.getId()).setLogisticsName(express.getName());\n        }\n        return orderVO;\n    }\n\n    AppTradeOrderDetailRespVO convert3(TradeOrderDO order, List<TradeOrderItemDO> items);\n\n    AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean);\n\n    @Mappings({\n            @Mapping(target = \"skuId\", source = \"tradeOrderItemDO.skuId\"),\n            @Mapping(target = \"orderId\", source = \"tradeOrderItemDO.orderId\"),\n            @Mapping(target = \"orderItemId\", source = \"tradeOrderItemDO.id\"),\n            @Mapping(target = \"descriptionScores\", source = \"createReqVO.descriptionScores\"),\n            @Mapping(target = \"benefitScores\", source = \"createReqVO.benefitScores\"),\n            @Mapping(target = \"content\", source = \"createReqVO.content\"),\n            @Mapping(target = \"picUrls\", source = \"createReqVO.picUrls\"),\n            @Mapping(target = \"anonymous\", source = \"createReqVO.anonymous\"),\n            @Mapping(target = \"userId\", source = \"tradeOrderItemDO.userId\")\n    })\n    ProductCommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItemDO);\n\n    TradePriceCalculateReqBO convert(AppTradeOrderSettlementReqVO settlementReqVO);\n\n    default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,\n                                             List<CartDO> cartList) {\n        TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId)\n                .setItems(new ArrayList<>(settlementReqVO.getItems().size()))\n                .setCouponId(settlementReqVO.getCouponId()).setPointStatus(settlementReqVO.getPointStatus())\n                // 物流信息\n                .setDeliveryType(settlementReqVO.getDeliveryType()).setAddressId(settlementReqVO.getAddressId())\n                .setPickUpStoreId(settlementReqVO.getPickUpStoreId())\n                // 各种活动\n                .setSeckillActivityId(settlementReqVO.getSeckillActivityId())\n                .setBargainRecordId(settlementReqVO.getBargainRecordId())\n                .setCombinationActivityId(settlementReqVO.getCombinationActivityId())\n                .setCombinationHeadId(settlementReqVO.getCombinationHeadId())\n                .setPointActivityId(settlementReqVO.getPointActivityId());\n        // 商品项的构建\n        Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId);\n        for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) {\n            // 情况一：skuId + count\n            if (item.getSkuId() != null) {\n                reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(item.getSkuId()).setCount(item.getCount())\n                        .setSelected(true)); // true 的原因，下单一定选中\n                continue;\n            }\n            // 情况二：cartId\n            CartDO cart = cartMap.get(item.getCartId());\n            if (cart == null) {\n                continue;\n            }\n            reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(cart.getSkuId()).setCount(cart.getCount())\n                    .setCartId(item.getCartId()).setSelected(true)); // true 的原因，下单一定选中\n        }\n        return reqBO;\n    }\n\n    default AppTradeOrderSettlementRespVO convert(TradePriceCalculateRespBO calculate, MemberAddressRespDTO address) {\n        AppTradeOrderSettlementRespVO respVO = convert0(calculate, address);\n        if (address != null) {\n            respVO.getAddress().setAreaName(AreaUtils.format(address.getAreaId()));\n        }\n        return respVO;\n    }\n\n    AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, MemberAddressRespDTO address);\n\n    List<AppOrderExpressTrackRespDTO> convertList02(List<ExpressTrackRespDTO> list);\n\n    TradeOrderDO convert(TradeOrderUpdateAddressReqVO reqVO);\n\n    TradeOrderDO convert(TradeOrderUpdatePriceReqVO reqVO);\n\n    TradeOrderDO convert(TradeOrderRemarkReqVO reqVO);\n\n    default BrokerageAddReqBO convert(MemberUserRespDTO user, TradeOrderItemDO item,\n                                      ProductSpuRespDTO spu, ProductSkuRespDTO sku) {\n        BrokerageAddReqBO bo = new BrokerageAddReqBO().setBizId(String.valueOf(item.getId())).setSourceUserId(item.getUserId())\n                .setBasePrice(item.getPayPrice())\n                .setTitle(StrUtil.format(\"{}成功购买{}\", user.getNickname(), item.getSpuName()));\n        if (BooleanUtil.isTrue(spu.getSubCommissionType())) {\n            // 特殊：单独设置的佣金需要乘以购买数量。关联 https://gitee.com/yudaocode/yudao-mall-uniapp/issues/ICY7SJ\n            bo.setFirstFixedPrice(sku.getFirstBrokeragePrice() * item.getCount())\n                    .setSecondFixedPrice(sku.getSecondBrokeragePrice() * item.getCount());\n        }\n        return bo;\n    }\n\n    @Named(\"convertList04\")\n    List<TradeOrderRespDTO> convertList04(List<TradeOrderDO> list);\n\n    @Mappings({\n            @Mapping(target = \"activityId\", source = \"order.combinationActivityId\"),\n            @Mapping(target = \"spuId\", source = \"item.spuId\"),\n            @Mapping(target = \"skuId\", source = \"item.skuId\"),\n            @Mapping(target = \"count\", source = \"item.count\"),\n            @Mapping(target = \"orderId\", source = \"order.id\"),\n            @Mapping(target = \"userId\", source = \"order.userId\"),\n            @Mapping(target = \"headId\", source = \"order.combinationHeadId\"),\n            @Mapping(target = \"combinationPrice\", source = \"item.payPrice\"),\n    })\n    CombinationRecordCreateReqDTO convert(TradeOrderDO order, TradeOrderItemDO item);\n\n}\n"
  },
  {
    "path": "yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/brokerage/BrokerageUserDO.java",
    "content": "package cn.iocoder.yudao.module.trade.dal.dataobject.brokerage;\n\nimport cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.*;\n\nimport java.time.LocalDateTime;\n\n/**\n * 分销用户 DO\n *\n * @author owen\n */\n@TableName(\"trade_brokerage_user\")\n@KeySequence(\"trade_brokerage_user_seq\") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BrokerageUserDO extends BaseDO {\n\n    /**\n     * 用户编号\n     * <p>\n     * 对应 MemberUserDO 的 id 字段\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 推广员编号\n     * <p>\n     * 关联 MemberUserDO 的 id 字段\n     */\n    private Long bindUserId;\n    /**\n     * 推广员绑定时间\n     */\n    private LocalDateTime bindUserTime;\n\n    /**\n     * 是否有分销资格\n     */\n    private Boolean brokerageEnabled;\n    /**\n     * 成为分销员时间\n     */\n    private LocalDateTime brokerageTime;\n\n    /**\n     * 可用佣金\n     */\n    private Integer brokeragePrice;\n    /**\n     * 冻结佣金\n     */\n    private Integer frozenPrice;\n}\n"
  }
]